PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.9
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
← All changes | includes/Core/WriteWithAI.php +166 -365 4.5.13.8.9 View file →
@@ -1,295 +1,168 @@
1 1 <?php
2 2
3 - namespace WPDeveloper\BetterDocs\Core;
3 +namespace WPDeveloper\BetterDocs\Core;
4 4
5 - use WPDeveloper\BetterDocs\Utils\Base;
6 - use WPDeveloper\BetterDocs\Core\Settings;
7 - use WPDeveloper\BetterDocs\Core\PostType;
5 +use WPDeveloper\BetterDocs\Utils\Base;
6 +use WPDeveloper\BetterDocs\Core\Settings;
7 +use WPDeveloper\BetterDocs\Core\PostType;
8 8
9 - use WPDeveloper\BetterDocs\Utils\Helper;
9 +use WPDeveloper\BetterDocs\Utils\Helper;
10 10
11 - class WriteWithAI extends Base {
11 +class WriteWithAI extends Base {
12 12
13 - public $settings;
13 + public $settings;
14 14
15 - public function __construct( Settings $settings ) {
16 - $this->settings = $settings;
17 - // Get the post ID from the URL
18 - $post_id = isset( $_GET[ 'post' ] ) ? intval( $_GET[ 'post' ] ) : 0; // phpcs:ignore
15 + public function __construct( Settings $settings ) {
16 + $this->settings = $settings;
17 + // Get the post ID from the URL
18 + $post_id = isset($_GET['post']) ? intval($_GET['post']) : 0; // phpcs:ignore
19 19
20 - if ( ! empty( $_GET[ 'post_type' ] ) ) { // phpcs:ignore
21 - $post_type = $_GET[ 'post_type' ]; // phpcs:ignore
22 - } elseif ( $post_id > 0 ) {
23 - $post_type = get_post_type( $post_id );
24 - } else {
25 - $post_type = '';
26 - }
20 + if (!empty($_GET['post_type'])) { // phpcs:ignore
21 + $post_type = $_GET['post_type']; // phpcs:ignore
22 + } elseif ( $post_id > 0 ) {
23 + $post_type = get_post_type( $post_id );
24 + } else {
25 + $post_type = '';
26 + }
27 27
28 - if ( ! empty( $this->isEnabledWriteWithAI() ) && 'docs' == $post_type ) {
29 - add_action( 'admin_footer', array( $this, 'ai_autowrite_button' ) );
30 - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_ai_edit_assets' ) );
31 - }
32 - add_action( 'wp_ajax_generate_openai_content', array( $this, 'generate_openai_content_callback' ) );
33 - }
28 + if ( ! empty( $this->isEnabledWriteWithAI() ) && $post_type == 'docs' ) {
29 + add_action( 'admin_footer', [ $this, 'ai_autowrite_button' ] );
30 + }
31 + add_action( 'wp_ajax_generate_openai_content', [ $this, 'generate_openai_content_callback' ] );
32 + }
34 33
35 - public function enqueue_ai_edit_assets( $hook ) {
36 - if ( $hook !== 'post.php' && $hook !== 'post-new.php' ) {
37 - return;
38 - }
34 + public function isEnabledWriteWithAI() {
35 + $isEnableAutoWrite = $this->settings->get( 'enable_write_with_ai', true );
36 + return $isEnableAutoWrite;
37 + }
39 38
40 - global $post_type;
41 - if ( $post_type !== 'docs' ) {
42 - return;
43 - }
39 + public function isValidAPIKey( $apiKey ) {
40 + if ( empty( $apiKey ) ) {
41 + $api_response['valid'] = false;
42 + $api_response['message'] = 'Please Insert your <a href="/admin.php?page=betterdocs-settings">OpenAI API Key</a> to use this Write with AI feature.';
44 43
45 - if ( empty( $this->get_api_key() ) ) {
46 - return;
47 - }
44 + return $api_response;
45 + }
48 46
49 - betterdocs()->assets->enqueue( 'betterdocs-ai-edit', 'blocks/ai-edit.js' );
50 - betterdocs()->assets->enqueue( 'betterdocs-ai-edit-style', 'blocks/ai-edit-style.css' );
47 + $ch = curl_init( 'https://api.openai.com/v1/engines' ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_init
48 + curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt
49 + curl_setopt(
50 + $ch,
51 + CURLOPT_HTTPHEADER,
52 + [ //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt
53 + 'Content-Type: application/json',
54 + 'Authorization: Bearer ' . $apiKey,
55 + ]
56 + );
51 57
52 - wp_localize_script(
53 - 'betterdocs-ai-edit',
54 - 'betterdocsAIEdit',
55 - array(
56 - 'rest_url' => esc_url_raw( rest_url( 'betterdocs/v1/ai-edit' ) ),
57 - 'rest_nonce' => wp_create_nonce( 'wp_rest' ),
58 - 'post_id' => get_the_ID(),
59 - )
60 - );
61 - }
58 + $api_response = [];
62 59
63 - public function isEnabledWriteWithAI() {
64 - $isEnableAutoWrite = $this->settings->get( 'enable_write_with_ai', true );
65 - return $isEnableAutoWrite;
66 - }
60 + $response = curl_exec( $ch ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_exec
61 + $httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_getinfo
67 62
68 - public function isValidAPIKey( $apiKey ) {
69 - if ( empty( $apiKey ) ) {
70 - $api_response[ 'valid' ] = false;
71 - $api_response[ 'message' ] = 'Please Insert your <a href="/admin.php?page=betterdocs-settings#betterdocs-ai">OpenAI API Key</a> to use this Write with AI feature.';
63 + curl_close( $ch ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_close
72 64
73 - return $api_response;
74 - }
65 + if ( $httpCode == 200 ) {
66 + $api_response['valid'] = true;
67 + $api_response['message'] = 'Valid API Key';
68 + } else {
69 + $responseData = json_decode( $response, true );
70 + // Access the message data (replace 'data' with the actual key used in the response)
71 + $messageData = $responseData['error'] ? $responseData['error'] : '';
72 + $api_response['valid'] = false;
73 + $api_response['message'] = $messageData['message'] ? $messageData['message'] : 'Invalid API Key';
74 + }
75 75
76 - $ch = curl_init( 'https://api.openai.com/v1/engines' ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_init
77 - curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt
78 - curl_setopt(
79 - $ch,
80 - CURLOPT_HTTPHEADER,
81 - array( //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt
82 - 'Content-Type: application/json',
83 - 'Authorization: Bearer ' . $apiKey
84 - )
85 - );
76 + // print_r($response);
86 77
87 - $api_response = array();
78 + return $api_response;
79 + }
88 80
89 - $response = curl_exec( $ch ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_exec
90 - $httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_getinfo
81 + public function get_api_key() {
82 + $api_key = $this->settings->get( 'ai_autowrite_api_key', '' );
83 + return $api_key;
84 + }
91 85
92 - curl_close( $ch ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_close
93 86
94 - if ( 200 == $httpCode ) {
95 - $api_response[ 'valid' ] = true;
96 - $api_response[ 'message' ] = 'Valid API Key';
97 - } else {
98 - $responseData = json_decode( $response, true );
99 - // Access the message data (replace 'data' with the actual key used in the response)
100 - $messageData = $responseData[ 'error' ] ? $responseData[ 'error' ] : '';
101 - $api_response[ 'valid' ] = false;
102 - $api_response[ 'message' ] = $messageData[ 'message' ] ? $messageData[ 'message' ] : 'Invalid API Key';
103 - }
87 + public function generate_openai_response( $prompt, $keywords ) {
88 + try {
89 + $api_key = $this->settings->get( 'ai_autowrite_api_key', '' );
90 + $max_tokens = $this->settings->get( 'ai_autowrite_max_token', 1500 );
104 91
105 - // print_r($response);
92 + $api_endpoint = 'https://api.openai.com/v1/chat/completions'; // Update the endpoint based on OpenAI API version
106 93
107 - return $api_response;
108 - }
94 + $request_options = array(
95 + 'headers' => array(
96 + 'Content-Type' => 'application/json',
97 + 'Authorization' => 'Bearer ' . $api_key,
98 + ),
99 + 'body' => json_encode(
100 + array( //phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode
101 + 'model' => 'gpt-4o-mini', // Add the model parameter here
102 + 'messages' => array(
103 + array(
104 + 'role' => 'system',
105 + 'content' => 'You are a helpful assistant who writes documentation for users.'
106 + ),
107 + array(
108 + 'role' => 'user',
109 + 'content' => $prompt
110 + ),
111 + ),
112 + 'max_tokens' => $max_tokens,
109 113
110 - public function get_api_key() {
111 - $api_key = $this->settings->get( 'ai_autowrite_api_key', '' );
112 - return $api_key;
113 - }
114 + )
115 + ),
116 + 'timeout' => 50,
117 + );
114 118
115 - public function get_system_prompt() {
116 - $prompt = <<<'PROMPT'
117 -You are a Senior Technical Writer specializing in comprehensive, high-quality documentation. Your goal is to produce documentation that scores 100/100 on clarity, completeness, and structure.
119 + $response = wp_remote_post( $api_endpoint, $request_options );
118 120
119 -## Output format
121 + if ( is_wp_error( $response ) ) {
122 + return 'Error: ' . $response->get_error_message();
123 + } else {
124 + $body = wp_remote_retrieve_body( $response );
120 125
121 -Return only HTML body content. Never wrap output in `<!doctype>`, `<html>`, `<head>`, `<body>`, or markdown code fences (no ```html ... ```).
126 + $data = json_decode( $body, true );
122 127
123 -Use semantic, Gutenberg-friendly tags only:
124 -- Headings: `<h2>`, `<h3>`, `<h4>` (do not emit `<h1>` — it is reserved for the document title)
125 -- Paragraphs: `<p>` for prose
126 -- Lists: `<ul>`/`<ol>` with `<li>` for any list of items, steps, or bullet points — never fake a list with paragraphs or `<br>`
127 -- Links: `<a href="https://...">link text</a>` with absolute URLs
128 -- Inline emphasis: `<strong>`, `<em>`, `<code>`
129 -- Code blocks: `<pre><code>...</code></pre>` for multi-line code or commands
130 -- Quotes: `<blockquote>`
131 -- Tables: `<table>` with `<thead>`, `<tbody>`, `<tr>`, `<th>`, `<td>`
132 -- Images: `<img src="..." alt="...">`
128 + if ( ! empty( $data['error'] ) ) {
129 + return $data['error']['message'];
130 + }
133 131
134 -Apply `<span class="highlight">key term</span>` to important topic terms inside headings and to the first occurrence of each keyword in body text. Use it sparingly — never wrap whole sentences or wrap text inside `href`, `src`, `alt`, or other attributes.
132 + return $data['choices'][0]['message']['content']; // Update this line to get the assistant's message
133 + }
134 + } catch ( Exception $error ) {
135 + return 'Error: ' . $error->getMessage();
136 + }
137 + }
135 138
136 -Do not emit `<style>`, `<script>`, `<iframe>`, `<form>`, or inline `style=""` / `class=""` attributes (other than the `highlight` class above).
137 139
138 -If — and only if — the user's prompt explicitly asks for raw/custom HTML, embed code, or a non-standard structure, follow that request instead of these defaults.
139 -PROMPT;
140 140
141 - return apply_filters( 'betterdocs_write_with_ai_system_prompt', $prompt );
142 - }
141 + public function generate_openai_content_callback() {
142 + // Verify the nonce
143 + if (!isset($_POST['ai_nonce']) || !wp_verify_nonce($_POST['ai_nonce'], 'generate_openai_content_nonce')) { //phpcs:ignore
144 + wp_send_json_error( 'Invalid nonce' );
145 + wp_die();
146 + }
143 147
144 - public function generate_openai_response( $prompt, $keywords ) {
145 - try {
146 - $api_key = $this->settings->get( 'ai_autowrite_api_key', '' );
147 - $max_tokens = $this->settings->get( 'ai_autowrite_max_token', 1500 );
148 - $model = $this->settings->get( 'write_with_ai_model', 'gpt-4o-mini' );
148 + $prompt = sanitize_text_field($_POST['prompt']); //phpcs:ignore
149 + // $num_of_sections = sanitize_text_field($_POST['numOfSections']);
150 + // $num_of_paragraphs = sanitize_text_field($_POST['numOfParagraphs']);
151 + $keywords = sanitize_text_field($_POST['keywords']); //phpcs:ignore
149 152
150 - $api_endpoint = 'https://api.openai.com/v1/chat/completions'; // Update the endpoint based on OpenAI API version
153 + $ai_instance = new WriteWithAI( $this->settings );
151 154
152 - $request_body = array(
153 - 'model' => $model,
154 - 'messages' => array(
155 - array(
156 - 'role' => 'system',
157 - 'content' => $this->get_system_prompt()
158 - ),
159 - array(
160 - 'role' => 'user',
161 - 'content' => $prompt
162 - )
163 - ),
164 - 'max_tokens' => $max_tokens
165 - );
155 + $generated_content = $ai_instance->generate_openai_response( $prompt, $keywords );
166 156
167 - $request_options = array(
168 - 'headers' => array(
169 - 'Content-Type' => 'application/json',
170 - 'Authorization' => 'Bearer ' . $api_key
171 - ),
172 - 'body' => json_encode( $request_body ),
173 - 'timeout' => 50
174 - );
157 + // Send the generated content as the AJAX response
158 + wp_send_json_success( $generated_content );
159 + wp_die();
160 + }
175 161
176 - $response = wp_remote_post( $api_endpoint, $request_options );
162 + public function ai_autowrite_button() {
177 163
178 - if ( is_wp_error( $response ) ) {
179 - return 'Error: ' . $response->get_error_message();
180 - } else {
181 - $body = wp_remote_retrieve_body( $response );
182 -
183 - $data = json_decode( $body, true );
184 -
185 - if ( ! empty( $data[ 'error' ] ) ) {
186 - return $data[ 'error' ][ 'message' ];
187 - }
188 -
189 - return $data[ 'choices' ][ 0 ][ 'message' ][ 'content' ]; // Update this line to get the assistant's message
190 - }
191 - } catch ( Exception $error ) {
192 - return 'Error: ' . $error->getMessage();
193 - }
194 - }
195 -
196 - public function generate_openai_response_full( $prompt ) {
197 - $api_key = $this->settings->get( 'ai_autowrite_api_key', '' );
198 - $max_tokens = $this->settings->get( 'ai_autowrite_max_token', 1500 );
199 - $model = $this->settings->get( 'write_with_ai_model', 'gpt-4o-mini' );
200 -
201 - $api_endpoint = 'https://api.openai.com/v1/chat/completions';
202 -
203 - $payload = array(
204 - 'model' => $model,
205 - 'messages' => array(
206 - array(
207 - 'role' => 'system',
208 - 'content' => $this->get_system_prompt()
209 - ),
210 - array(
211 - 'role' => 'user',
212 - 'content' => $prompt
213 - )
214 - ),
215 - 'max_tokens' => $max_tokens
216 - );
217 -
218 - $request_options = array(
219 - 'headers' => array(
220 - 'Content-Type' => 'application/json',
221 - 'Authorization' => 'Bearer ' . $api_key
222 - ),
223 - 'body' => wp_json_encode( $payload ),
224 - 'timeout' => 50
225 - );
226 -
227 - $response = wp_remote_post( $api_endpoint, $request_options );
228 -
229 - if ( is_wp_error( $response ) ) {
230 - return array(
231 - 'success' => false,
232 - 'error' => $response->get_error_message(),
233 - 'model' => $model,
234 - );
235 - }
236 -
237 - $body = wp_remote_retrieve_body( $response );
238 - $data = json_decode( $body, true );
239 -
240 - if ( ! empty( $data['error'] ) ) {
241 - return array(
242 - 'success' => false,
243 - 'error' => isset( $data['error']['message'] ) ? $data['error']['message'] : 'OpenAI error',
244 - 'model' => $model,
245 - 'raw' => $data,
246 - );
247 - }
248 -
249 - $content = isset( $data['choices'][0]['message']['content'] ) ? $data['choices'][0]['message']['content'] : '';
250 - $usage = isset( $data['usage'] ) && is_array( $data['usage'] ) ? $data['usage'] : array();
251 -
252 - return array(
253 - 'success' => true,
254 - 'content' => $content,
255 - 'model' => $model,
256 - 'prompt_tokens' => isset( $usage['prompt_tokens'] ) ? (int) $usage['prompt_tokens'] : null,
257 - 'completion_tokens' => isset( $usage['completion_tokens'] ) ? (int) $usage['completion_tokens'] : null,
258 - 'total_tokens' => isset( $usage['total_tokens'] ) ? (int) $usage['total_tokens'] : null,
259 - 'finish_reason' => isset( $data['choices'][0]['finish_reason'] ) ? $data['choices'][0]['finish_reason'] : null,
260 - );
261 - }
262 -
263 - public function generate_openai_content_callback() {
264 - // Verify the nonce
265 - if ( ! isset( $_POST[ 'ai_nonce' ] ) || ! wp_verify_nonce( $_POST[ 'ai_nonce' ], 'generate_openai_content_nonce' ) ) { //phpcs:ignore
266 - wp_send_json_error( 'Invalid nonce' );
267 - wp_die();
268 - }
269 -
270 - if ( ! current_user_can( 'edit_posts' ) ) {
271 - wp_send_json_error( 'Insufficient permissions' );
272 - wp_die();
273 - }
274 -
275 - $prompt = sanitize_text_field( $_POST[ 'prompt' ] ); //phpcs:ignore
276 - // $num_of_sections = sanitize_text_field($_POST['numOfSections']);
277 - // $num_of_paragraphs = sanitize_text_field($_POST['numOfParagraphs']);
278 - $keywords = sanitize_text_field( $_POST[ 'keywords' ] ); //phpcs:ignore
279 -
280 - $ai_instance = new WriteWithAI( $this->settings );
281 -
282 - $generated_content = $ai_instance->generate_openai_response( $prompt, $keywords );
283 -
284 - // Send the generated content as the AJAX response
285 - wp_send_json_success( $generated_content );
286 - wp_die();
287 - }
288 -
289 - public function ai_autowrite_button() {
290 -
291 - ?>
164 + ?>
292 165 <script>
293 166 var docsTitle;
294 167
295 168 function responseMessage(message) {
@@ -355,8 +228,14 @@
355 228 setTimeout(() => {
356 229 insertContentToEditor(title, response.data, isOverwrite, keywords);
357 230 docGenerateBtnTxt.innerHTML = reGenerateDocLabel;
358 231 jQuery('.generate-btn').removeAttr('disabled');
232 +
233 + // console.log(jQuery('.betterdocs-ai-autowrite-form-container').data('new-doc-page'));
234 +
235 + // if(jQuery('.betterdocs-ai-autowrite-form-container')?.data('new-doc-page')) {
236 + // docGenerateBtnTxt.innerHTML = reGenerateDocLabel;
237 + // }
359 238 }, 2000);
360 239 } else {
361 240 // alert(response.data.message);
362 241 jQuery('#betterdocs-ai-error-message').parent().removeClass('hidden');
@@ -392,9 +271,9 @@
392 271 if (!jQuery('#betterdocs-ai-error-message').parent().hasClass('hidden')) {
393 272 jQuery('#betterdocs-ai-error-message').parent().addClass('hidden');
394 273 }
395 274
396 - contentTextArea.value = `Write documentation for '${title}'. Cover these topics in depth: ${keywords}. Use headings, paragraphs, lists, links, code blocks, and tables wherever they help the reader. Follow the output-format rules in the system instructions.`;
275 + contentTextArea.value = `Generate a documentation using HTML heading tags for '${title}'. Include relevant details on ${keywords} in the documentation. Ensure all content is enclosed with <p> tags and apply a <span> tag with the class 'highlight' to headings and topic tags for emphasis.`;
397 276 }
398 277
399 278 function convertMarkdownToHTML(markdownContent) {
400 279 // Simple Markdown to HTML conversion (you may need to enhance this based on your needs)
@@ -424,107 +303,30 @@
424 303 return newDiv.innerHTML;
425 304 }
426 305
427 306
428 - function escapeHtml(str) {
429 - return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
430 - }
431 -
432 - function escapeRegex(str) {
433 - return String(str).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
434 - }
435 -
436 - function stripCodeFences(htmlString) {
437 - if (!htmlString) {
438 - return '';
439 - }
440 - return htmlString
441 - .replace(/^\s*```(?:html|HTML)?\s*\n?/, '')
442 - .replace(/\n?\s*```\s*$/, '');
443 - }
444 -
445 307 function wrapwithHeighlight(inputString, keywords) {
446 - if (!inputString) {
447 - return '';
448 - }
308 + let modifiedString = inputString.replace(/<(h\d)(.*?)>(.*?)<\/\1>/g, '<$1$2><span class="highlight">$3</span></$1>');
449 309
450 - const container = document.createElement('div');
451 - container.innerHTML = inputString;
310 + const keywordArray = keywords.split(',').map(keyword => keyword.trim());
452 311
453 - // Wrap each heading's visible text with the highlight span (skip if already present)
454 - container.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(function (heading) {
455 - if (heading.querySelector('.highlight')) {
456 - return;
457 - }
458 - const text = heading.textContent;
459 - if (!text || !text.trim()) {
460 - return;
461 - }
462 - heading.innerHTML = '<span class="highlight">' + escapeHtml(text) + '</span>';
312 + keywordArray.forEach(keyword => {
313 + modifiedString = modifiedString.replace(new RegExp(`\\b(${keyword})\\b`, 'gi'), '<span class="highlight">$1</span>');
463 314 });
464 315
465 - // Wrap keywords only inside text nodes — never inside attributes, code, or existing highlights
466 - const keywordList = (keywords || '')
467 - .split(',')
468 - .map(function (k) { return k.trim(); })
469 - .filter(Boolean);
470 -
471 - if (keywordList.length) {
472 - const pattern = new RegExp('\\b(' + keywordList.map(escapeRegex).join('|') + ')\\b', 'gi');
473 - const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, {
474 - acceptNode: function (node) {
475 - let parent = node.parentNode;
476 - while (parent && parent !== container) {
477 - if (parent.nodeType === 1) {
478 - const tag = parent.tagName.toLowerCase();
479 - if (tag === 'code' || tag === 'pre') {
480 - return NodeFilter.FILTER_REJECT;
481 - }
482 - if (parent.classList && parent.classList.contains('highlight')) {
483 - return NodeFilter.FILTER_REJECT;
484 - }
485 - }
486 - parent = parent.parentNode;
487 - }
488 - return NodeFilter.FILTER_ACCEPT;
489 - }
490 - });
491 -
492 - const textNodes = [];
493 - let current;
494 - while ((current = walker.nextNode())) {
495 - textNodes.push(current);
496 - }
497 -
498 - textNodes.forEach(function (textNode) {
499 - pattern.lastIndex = 0;
500 - if (!pattern.test(textNode.nodeValue)) {
501 - return;
502 - }
503 - pattern.lastIndex = 0;
504 - const fragmentHost = document.createElement('span');
505 - fragmentHost.innerHTML = escapeHtml(textNode.nodeValue).replace(pattern, '<span class="highlight">$1</span>');
506 - const parent = textNode.parentNode;
507 - while (fragmentHost.firstChild) {
508 - parent.insertBefore(fragmentHost.firstChild, textNode);
509 - }
510 - parent.removeChild(textNode);
511 - });
512 - }
513 -
514 - return container.innerHTML;
316 + return modifiedString;
515 317 }
516 318
517 319 function getBodyContent(htmlString) {
518 - if (!htmlString) {
519 - return '';
520 - }
521 - const cleaned = stripCodeFences(htmlString);
522 - const match = cleaned.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
320 + var match = htmlString.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
321 +
322 + // Check if match is null before accessing match[1]
523 323 if (match) {
324 + console.log(match[1].replace(/<p>\s+/g, '<p>'));
524 325 return match[1].replace(/<p>\s+/g, '<p>');
326 + } else {
327 + return '';
525 328 }
526 - return cleaned;
527 329 }
528 330
529 331
530 332
@@ -537,9 +339,9 @@
537 339
538 340 htmlContent = getBodyContent(htmlContent);
539 341 htmlContent = removeFirstHeading(htmlContent);
540 342 htmlContent = wrapwithHeighlight(htmlContent, keywords);
541 - const isPostContent = `<?php echo isset( $_GET[ 'post' ] ) ? esc_html( get_the_content( $_GET[ 'post' ] ) ) : ''; // phpcs:ignore ?>`;
343 + const isPostContent = `<?php echo isset($_GET['post']) ? esc_html(get_the_content($_GET['post'])) : ''; // phpcs:ignore ?>`;
542 344
543 345 const blocks = wp.blocks.rawHandler({
544 346 HTML: htmlContent
545 347 });
@@ -592,9 +394,9 @@
592 394 let currentKeywords = jQuery('#betterdocs-ai-keyword').val();
593 395 if (!currentKeywords) {
594 396 currentKeywords = '{Documentation Keywords}';
595 397 }
596 - const currentPrompt = `Write documentation for '${docsTitle?docsTitle:'{Documentation title}'}'. Cover these topics in depth: ${currentKeywords}. Use headings, paragraphs, lists, links, code blocks, and tables wherever they help the reader. Follow the output-format rules in the system instructions.`;
398 + const currentPrompt = `Generate documentation using HTML heading tags for '${docsTitle?docsTitle:'{Documentation title}'}'. Include relevant details on ${currentKeywords} in the documentation. Ensure all content is enclosed with <p> tags and apply a <span> tag with the class 'highlight' to headings and topic tags for emphasis.`;
597 399 jQuery('#betterdocs-ai-content').val(currentPrompt);
598 400
599 401 jQuery('#betterdocs-ai-title').val(docsTitle);
600 402
@@ -611,14 +413,14 @@
611 413
612 414
613 415 function writeWithAIForm() {
614 416
615 - let title = `<?php echo isset( $_GET[ 'post' ] ) ? esc_html( get_the_title( $_GET[ 'post' ] ) ) : ''; // phpcs:ignore ?>`;
417 + let title = `<?php echo isset($_GET['post']) ? esc_html(get_the_title($_GET['post'])) : ''; // phpcs:ignore ?>`;
616 418 if (docsTitle) {
617 419 title = docsTitle;
618 420 }
619 421
620 - const promtTitle = `<?php echo isset( $_GET[ 'post' ] ) ? esc_html( get_the_title( $_GET[ 'post' ] ) ) : '{Documentation Title}'; // phpcs:ignore ?>`;
422 + const promtTitle = `<?php echo isset($_GET['post']) ? esc_html(get_the_title($_GET['post'])) : '{Documentation Title}'; // phpcs:ignore ?>`;
621 423 const titlePlaceholder = "<?php echo esc_attr__( 'Enter a descriptive title for your documentation.', 'betterdocs' ); ?>";
622 424 const keywords = "<?php echo esc_attr( '{Documentation Keywords}' ); ?>";
623 425 const keywordsPlaceholder = "<?php echo esc_attr__( 'Add keywords to generate precise & relevant documentation (comma-separated).', 'betterdocs' ); ?>";
624 426 const language = "<?php echo esc_attr( 'English' ); ?>";
@@ -633,18 +435,18 @@
633 435 const checkboxLabel = "<?php echo esc_html__( 'Overwrite your existing Doc:', 'betterdocs' ); ?>";
634 436 const nonce = "<?php echo esc_attr( wp_create_nonce( 'generate_openai_content_nonce' ) ); ?>";
635 437
636 438 <?php
637 - $is_valid = $this->get_api_key();
638 - $disable_field = 'disabled-input-field';
639 - if ( $this->get_api_key() ) {
640 - $disable_field = '';
641 - }
642 - ?>
439 + $is_valid = $this->get_api_key();
440 + $disable_field = 'disabled-input-field';
441 + if ( $this->get_api_key() ) {
442 + $disable_field = '';
443 + }
444 + ?>
643 445
644 446 let hiddenClass = 'hidden';
645 447 let newDocPageAtt = 'data-new-doc-page="true"';
646 - const isPostContent = `<?php echo isset( $_GET[ 'post' ] ) ? esc_html( get_the_content( $_GET[ 'post' ] ) ) : ''; // phpcs:ignore ?>`;
448 + const isPostContent = `<?php echo isset($_GET['post']) ? esc_html(get_the_content($_GET['post'])) : ''; // phpcs:ignore ?>`;
647 449
648 450 if (isPostContent !== '') {
649 451 hiddenClass = '';
650 452 newDocPageAtt = '';
@@ -654,9 +456,9 @@
654 456 // const prompt = `Make a knowledge base article with html heading tags about [${title}] in [${language}]. Here is some topic to describe the article: [${keywords}]. and wrap the content with p tag also add a span tag with a class named 'highlight' to all the heading and topic tags in the article.`;
655 457
656 458 // const prompt = `Create a details knowledge base article in [${language}] about [${title}] with html heading tags. Here is some topic to describe the article: [${keywords}]. And wrap the content with p tag also add a span tag with a class named 'highlight' to all the heading and topic tags in the article.`;
657 459
658 - const prompt = `Write documentation for '${promtTitle}'. Cover these topics in depth: ${keywords}. Use headings, paragraphs, lists, links, code blocks, and tables wherever they help the reader. Follow the output-format rules in the system instructions.`;
460 + const prompt = `Generate a documentation using HTML heading tags for '${promtTitle}'. Include relevant details on ${keywords} in the documentation. Ensure all content is enclosed with <p> tags and apply a <span> tag with the class 'highlight' to headings and topic tags for emphasis.`;
659 461
660 462 const aiIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="21" height="20" viewBox="0 0 21 20" fill="none">
661 463 <g clip-path="url(#clip0_2460_1729)">
662 464 <path d="M10.3956 18.8608L6.10991 19.2322L6.48134 14.9465L15.3956 6.08936C15.5287 5.95329 15.6876 5.84518 15.863 5.77137C16.0384 5.69756 16.2268 5.65954 16.4171 5.65954C16.6074 5.65954 16.7957 5.69756 16.9711 5.77137C17.1466 5.84518 17.3054 5.95329 17.4385 6.08936L19.2528 7.91793C19.3865 8.05083 19.4926 8.20886 19.565 8.38293C19.6375 8.55701 19.6747 8.74368 19.6747 8.93221C19.6747 9.12075 19.6375 9.30742 19.565 9.48149C19.4926 9.65557 19.3865 9.8136 19.2528 9.9465L10.3956 18.8608ZM1.7042 5.67507C1.20277 5.58793 1.20277 4.86793 1.7042 4.78079C2.59203 4.62626 3.41374 4.21085 4.06456 3.58751C4.71538 2.96417 5.16583 2.16113 5.35848 1.28079L5.38848 1.14221C5.49705 0.647929 6.20277 0.643643 6.31705 1.13936L6.35277 1.29936C6.55248 2.17579 7.0067 2.97367 7.65837 3.59281C8.31005 4.21195 9.13013 4.62475 10.0156 4.77936C10.5199 4.86507 10.5199 5.58936 10.0156 5.67793C9.13005 5.83218 8.3098 6.24465 7.65787 6.86354C7.00594 7.48243 6.5514 8.28014 6.35134 9.1565L6.3142 9.31793C6.20134 9.81221 5.49563 9.80936 5.38705 9.31364L5.35848 9.17507C5.16564 8.29433 4.71476 7.49102 4.06339 6.86764C3.41202 6.24426 2.58969 5.82908 1.70134 5.67507H1.7042Z" stroke="white" stroke-width="1.42857" stroke-linecap="round" stroke-linejoin="round"/>
@@ -693,14 +495,14 @@
693 495 <div class="autowrite-subheadding">
694 496 <p><?php echo esc_html__( 'Generate documentation effortlessly with BetterDocs AI. Simply input your doc title, keywords, prompt and let the system automatically generate comprehensive documentation tailored to your needs.', 'betterdocs' ); ?></p>
695 497 </div>
696 498
697 - <?php if ( empty( $this->get_api_key() ) ): ?>
499 + <?php if ( empty( $this->get_api_key() ) ) : ?>
698 500 <div id="betterdocs-ai-message">
699 501 <div class="warning-message">
700 502 <span class="dashicons dashicons-warning"></span>
701 503 <div>
702 - <?php echo wp_kses_post( 'Please Insert your <a target="_blank" href="' . esc_url( admin_url( 'admin.php?page=betterdocs-settings#betterdocs-ai' ) ) . '">OpenAI API Key</a> to use this Write with AI feature.', 'betterdocs' ); ?>
504 + <?php echo wp_kses_post( 'Please Insert your <a target="_blank" href="' . esc_url( admin_url( 'admin.php?page=betterdocs-settings&tab=tab-ai-autowrite' ) ) . '">OpenAI API Key</a> to use this Write with AI feature.', 'betterdocs' ); ?>
703 505 </div>
704 506 </div>
705 507
706 508 </div>
@@ -836,10 +638,8 @@
836 638 }
837 639
838 640 .betterdocs-ai-button-container {
839 641 margin-left: 10px;
840 - white-space: nowrap;
841 - max-width: 145px;
842 642 }
843 643
844 644 .autowrite-icon {
845 645 display: flex;
@@ -909,9 +709,8 @@
909 709 display: flex;
910 710 align-items: center;
911 711 justify-content: space-between;
912 712 gap: 10px;
913 - width: 135px;
914 713 }
915 714
916 715 .betterdocs-ai-button img {
917 716 width: 20px;
@@ -1216,18 +1015,20 @@
1216 1015 font-size: 18px;
1217 1016 font-weight: bold;
1218 1017 float: right;
1219 1018 position: absolute;
1220 - top: -16px;
1221 - border-radius: 50%;
1019 + right: 1px;
1020 + top: 1px;
1021 + padding: 10px;
1222 1022 background: #ffffff;
1223 - width: 40px;
1224 - height: 40px;
1023 + border-radius: 25px;
1024 + width: 20px;
1025 + height: 20px;
1225 1026 display: flex;
1226 1027 align-items: center;
1227 1028 justify-content: center;
1228 1029 color: #281617;
1229 - right: -42px;
1030 + right: -60px;
1230 1031 }
1231 1032
1232 1033 div#betterdocs-ai-error-message,
1233 1034 #betterdocs-ai-message {
@@ -1312,6 +1113,6 @@
1312 1113
1313 1114 }
1314 1115 </style>
1315 1116 <?php
1316 - }
1317 - }
1117 + }
1118 +}