PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.2.3
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.2.3
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
betterdocs / includes / Core / WriteWithAI.php

WriteWithAI.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.2.3, at includes/Core/WriteWithAI.php

1,124 lines 36.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Core;
4
5 use WPDeveloper\BetterDocs\Utils\Base;
6 use WPDeveloper\BetterDocs\Core\Settings;
7 use WPDeveloper\BetterDocs\Core\PostType;
8
9 use WPDeveloper\BetterDocs\Utils\Helper;
10
11 class WriteWithAI extends Base {
12
13 public $settings;
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
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 }
27
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
33 }
34
35 public function isEnabledWriteWithAI() {
36 $isEnableAutoWrite = $this->settings->get( 'enable_write_with_ai', true );
37 return $isEnableAutoWrite;
38 }
39
40 public function isValidAPIKey( $apiKey ) {
41 if ( empty( $apiKey ) ) {
42 $api_response['valid'] = false;
43 $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
45 return $api_response;
46 }
47
48 $ch = curl_init( 'https://api.openai.com/v1/engines' ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_init
49 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt
50 curl_setopt(
51 $ch,
52 CURLOPT_HTTPHEADER,
53 [ //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt
54 'Content-Type: application/json',
55 'Authorization: Bearer ' . $apiKey,
56 ]
57 );
58
59 $api_response = [];
60
61 $response = curl_exec( $ch ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_exec
62 $httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_getinfo
63
64 curl_close( $ch ); //phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_close
65
66 if ( $httpCode == 200 ) {
67 $api_response['valid'] = true;
68 $api_response['message'] = 'Valid API Key';
69 } else {
70 $responseData = json_decode( $response, true );
71 // Access the message data (replace 'data' with the actual key used in the response)
72 $messageData = $responseData['error'] ? $responseData['error'] : '';
73 $api_response['valid'] = false;
74 $api_response['message'] = $messageData['message'] ? $messageData['message'] : 'Invalid API Key';
75 }
76
77 // print_r($response);
78
79 return $api_response;
80 }
81
82 public function get_api_key() {
83 $api_key = $this->settings->get( 'ai_autowrite_api_key', '' );
84 return $api_key;
85 }
86
87
88 public function generate_openai_response( $prompt, $keywords ) {
89 try {
90 $api_key = $this->settings->get( 'ai_autowrite_api_key', '' );
91 $max_tokens = $this->settings->get( 'ai_autowrite_max_token', 1500 );
92 $model = $this->settings->get( 'write_with_ai_model', 'gpt-4o-mini' );
93
94 $api_endpoint = 'https://api.openai.com/v1/chat/completions'; // Update the endpoint based on OpenAI API version
95
96 $request_options = array(
97 'headers' => array(
98 'Content-Type' => 'application/json',
99 'Authorization' => 'Bearer ' . $api_key,
100 ),
101 'body' => json_encode(
102 array( //phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode
103 'model' => $model, // Add the model parameter here
104 'messages' => array(
105 array(
106 'role' => 'system',
107 'content' => 'You are a helpful assistant who writes documentation for users.'
108 ),
109 array(
110 'role' => 'user',
111 'content' => $prompt
112 ),
113 ),
114 'max_tokens' => $max_tokens,
115
116 )
117 ),
118 'timeout' => 50,
119 );
120
121 $response = wp_remote_post( $api_endpoint, $request_options );
122
123 if ( is_wp_error( $response ) ) {
124 return 'Error: ' . $response->get_error_message();
125 } else {
126 $body = wp_remote_retrieve_body( $response );
127
128 $data = json_decode( $body, true );
129
130 if ( ! empty( $data['error'] ) ) {
131 return $data['error']['message'];
132 }
133
134 return $data['choices'][0]['message']['content']; // Update this line to get the assistant's message
135 }
136 } catch ( Exception $error ) {
137 return 'Error: ' . $error->getMessage();
138 }
139 }
140
141
142
143 public function generate_openai_content_callback() {
144 // Verify the nonce
145 if (!isset($_POST['ai_nonce']) || !wp_verify_nonce($_POST['ai_nonce'], 'generate_openai_content_nonce')) { //phpcs:ignore
146 wp_send_json_error( 'Invalid nonce' );
147 wp_die();
148 }
149
150 $prompt = sanitize_text_field($_POST['prompt']); //phpcs:ignore
151 // $num_of_sections = sanitize_text_field($_POST['numOfSections']);
152 // $num_of_paragraphs = sanitize_text_field($_POST['numOfParagraphs']);
153 $keywords = sanitize_text_field($_POST['keywords']); //phpcs:ignore
154
155 $ai_instance = new WriteWithAI( $this->settings );
156
157 $generated_content = $ai_instance->generate_openai_response( $prompt, $keywords );
158
159 // Send the generated content as the AJAX response
160 wp_send_json_success( $generated_content );
161 wp_die();
162 }
163
164
165
166 public function ai_autowrite_button() {
167
168 ?>
169 <script>
170 var docsTitle;
171
172 function responseMessage(message) {
173 return `<div class="warning-message">
174 <span class="dashicons dashicons-warning"></span>
175 <div class="footer-message">
176 ${message}
177 </div>
178 </div>`;
179 }
180
181 function generateArticle(e) {
182
183
184 const title = document.getElementById('betterdocs-ai-title').value;
185 const keywords = document.getElementById('betterdocs-ai-keyword').value;
186 // const sectionOptions = document.getElementById('bd-autowrtite-content-section');
187 // const numOfSections = sectionOptions.options[sectionOptions.selectedIndex].value;
188
189 // const paragraphOptions = document.getElementById('bd-autowrtite-content-paragraph');
190 // const numOfParagraphs = paragraphOptions.options[paragraphOptions.selectedIndex].value;
191
192 const contentTextArea = document.getElementById('betterdocs-ai-content');
193 const docGenerateBtnTxt = document.querySelector('.generate-btn span');
194
195 // Add nonce value to the data being sent
196 const nonce = document.querySelector('input[name="ai_nonce"]').value;
197 const isOverwrite = document.getElementById('betterdocs-ai-checkbox').checked;
198 const generateDocLabel = "<?php echo esc_html__( 'Generate Doc', 'betterdocs' ); ?>";
199 const reGenerateDocLabel = "<?php echo esc_html__( 'Regenerate Doc', 'betterdocs' ); ?>";
200
201
202 // contentTextArea.value = `Write an article about ${title} in English. The article is organized by the following exact ${numOfSections} headings. Write exact ${numOfParagraphs} number of paragraphs per heading.${keywords} Use Markdown for formatting. Add an introduction and a conclusion. Style: creative. Tone: cheerful.`;
203
204
205 // Check if the title is not empty
206 if (title.trim() !== '' && keywords.trim() !== '') {
207 e.preventDefault();
208
209 if (!jQuery('#betterdocs-ai-error-message').parent().hasClass('hidden')) {
210 jQuery('#betterdocs-ai-error-message').parent().addClass('hidden');
211 }
212
213 docGenerateBtnTxt.innerHTML = "<?php echo esc_html__( 'Generating...', 'betterdocs' ); ?>";
214 jQuery('.generate-btn').prop('disabled', true);
215
216 jQuery.post({
217 url: ajaxurl, // Make sure ajaxurl is defined in your script
218 type: 'POST',
219 data: {
220 action: 'generate_openai_content',
221 prompt: document.querySelector('#betterdocs-ai-content').value,
222 // numOfSections: numOfSections,
223 // numOfParagraphs: numOfParagraphs,
224 keywords: keywords,
225 ai_nonce: nonce, // Pass the nonce value
226 },
227 success: function(response) {
228 // Update the content in the textarea
229
230 if (response.success && (typeof response.data === 'string')) {
231 docGenerateBtnTxt.innerHTML = "<?php echo esc_html__( 'Generated', 'betterdocs' ); ?>";
232 setTimeout(() => {
233 insertContentToEditor(title, response.data, isOverwrite, keywords);
234 docGenerateBtnTxt.innerHTML = reGenerateDocLabel;
235 jQuery('.generate-btn').removeAttr('disabled');
236
237 // console.log(jQuery('.betterdocs-ai-autowrite-form-container').data('new-doc-page'));
238
239 // if(jQuery('.betterdocs-ai-autowrite-form-container')?.data('new-doc-page')) {
240 // docGenerateBtnTxt.innerHTML = reGenerateDocLabel;
241 // }
242 }, 2000);
243 } else {
244 // alert(response.data.message);
245 jQuery('#betterdocs-ai-error-message').parent().removeClass('hidden');
246 jQuery('#betterdocs-ai-error-message').html(responseMessage(response.data.message));
247 docGenerateBtnTxt.innerHTML = generateDocLabel;
248 jQuery('.generate-btn').removeAttr('disabled');
249
250 }
251 },
252 error: function(error) {
253 console.error(error);
254 },
255 });
256
257 }
258 }
259
260 // Function to update the textarea
261 function updateTextarea() {
262 const title = document.getElementById('betterdocs-ai-title').value;
263 const keywords = document.getElementById('betterdocs-ai-keyword').value;
264 const sectionOptions = document.getElementById('bd-autowrtite-content-section');
265 let language = 'English';
266
267 if (language === '') {
268 language = 'English';
269 }
270
271 let keywordsText = keywords !== '' ? ` and incorporate the keywords: ${keywords}` : '';
272
273 const contentTextArea = document.getElementById('betterdocs-ai-content');
274
275 if (!jQuery('#betterdocs-ai-error-message').parent().hasClass('hidden')) {
276 jQuery('#betterdocs-ai-error-message').parent().addClass('hidden');
277 }
278
279 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.`;
280 }
281
282 function convertMarkdownToHTML(markdownContent) {
283 // Simple Markdown to HTML conversion (you may need to enhance this based on your needs)
284 const htmlContent = markdownContent
285 .replace(/^# (.+)$/gm, '<h1>$1</h1>')
286 .replace(/^## (.+)$/gm, '<h2>$1</h2>')
287 .replace(/^### (.+)$/gm, '<h3>$1</h3>')
288 .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
289 .replace(/\*(.+?)\*/g, '<em>$1</em>');
290
291 return htmlContent;
292 }
293
294 function replaceHeadingTitle(content, title, overviewTitle) {
295 let regex = new RegExp(title, 'g');
296 let updateContent = content.replace(regex, overviewTitle);
297 return updateContent;
298 }
299
300 function removeFirstHeading(htmlString) {
301 var newDiv = document.createElement('div');
302 newDiv.innerHTML = htmlString;
303 var firstHeading = newDiv.querySelector('h1');
304 if (firstHeading) {
305 firstHeading.parentNode.removeChild(firstHeading);
306 }
307 return newDiv.innerHTML;
308 }
309
310
311 function wrapwithHeighlight(inputString, keywords) {
312 let modifiedString = inputString.replace(/<(h\d)(.*?)>(.*?)<\/\1>/g, '<$1$2><span class="highlight">$3</span></$1>');
313
314 const keywordArray = keywords.split(',').map(keyword => keyword.trim());
315
316 keywordArray.forEach(keyword => {
317 modifiedString = modifiedString.replace(new RegExp(`\\b(${keyword})\\b`, 'gi'), '<span class="highlight">$1</span>');
318 });
319
320 return modifiedString;
321 }
322
323 function getBodyContent(htmlString) {
324 var match = htmlString.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
325
326 // Check if match is null before accessing match[1]
327 if (match) {
328 console.log(match[1].replace(/<p>\s+/g, '<p>'));
329 return match[1].replace(/<p>\s+/g, '<p>');
330 } else {
331 return '';
332 }
333 }
334
335
336
337 function insertContentToEditor(title, htmlContent, isOverwrite, keywords) {
338
339 const {
340 dispatch,
341 select
342 } = wp.data;
343
344 htmlContent = getBodyContent(htmlContent);
345 htmlContent = removeFirstHeading(htmlContent);
346 htmlContent = wrapwithHeighlight(htmlContent, keywords);
347 const isPostContent = `<?php echo isset($_GET['post']) ? esc_html(get_the_content($_GET['post'])) : ''; // phpcs:ignore ?>`;
348
349 const blocks = wp.blocks.rawHandler({
350 HTML: htmlContent
351 });
352
353 dispatch('core/editor').editPost({
354 title: title,
355 });
356
357 const selectedBlockClientId = select('core/block-editor').getSelectedBlockClientId();
358
359 const allBlocks = select('core/block-editor').getBlocks();
360
361 if (isOverwrite || isPostContent === '') {
362
363 allBlocks.forEach((block) => {
364 dispatch('core/block-editor').removeBlock(block.clientId);
365 });
366 dispatch('core/block-editor').insertBlocks(blocks);
367
368 } else if (selectedBlockClientId) {
369 const selectedIndex = select('core/block-editor').getBlockIndex(selectedBlockClientId);
370 dispatch('core/block-editor').insertBlocks(blocks, selectedIndex + 1);
371 } else {
372 dispatch('core/block-editor').insertBlocks(blocks);
373 }
374
375 closeWriteWithAIForm('afterInsertContent');
376 }
377
378 function closeWriteWithAIForm(after) {
379 jQuery('.betterdocs-ai-autowrite-form-container').addClass('hidden');
380 jQuery('#betterdocs-ai-autowrite-form-container-overlay').addClass('hidden');
381 jQuery('.betterdocs-ai-button').addClass('regenerate-btn');
382 }
383
384
385 document.addEventListener('DOMContentLoaded', function() {
386
387 // Subscribe to changes in the editor state
388 let previousDocsTitle = '';
389
390 wp.data.subscribe(() => {
391 // Get the updated post title
392 // const docsTitle = wp.data.select('core/editor').getEditedPostAttribute('title');
393
394 const docsTitle = wp.data.select('core/editor').getEditedPostAttribute('title');
395
396 // Check if the title has changed
397 if (docsTitle !== previousDocsTitle) {
398 let currentKeywords = jQuery('#betterdocs-ai-keyword').val();
399 if (!currentKeywords) {
400 currentKeywords = '{Documentation Keywords}';
401 }
402 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.`;
403 jQuery('#betterdocs-ai-content').val(currentPrompt);
404
405 jQuery('#betterdocs-ai-title').val(docsTitle);
406
407 previousDocsTitle = docsTitle;
408 }
409
410
411 });
412 });
413
414
415 // This example assumes that this code is executed when your script is loaded.
416 // You may want to place it within the appropriate context in your application.
417
418
419 function writeWithAIForm() {
420
421 let title = `<?php echo isset($_GET['post']) ? esc_html(get_the_title($_GET['post'])) : ''; // phpcs:ignore ?>`;
422 if (docsTitle) {
423 title = docsTitle;
424 }
425
426 const promtTitle = `<?php echo isset($_GET['post']) ? esc_html(get_the_title($_GET['post'])) : '{Documentation Title}'; // phpcs:ignore ?>`;
427 const titlePlaceholder = "<?php echo esc_attr__( 'Enter a descriptive title for your documentation.', 'betterdocs' ); ?>";
428 const keywords = "<?php echo esc_attr( '{Documentation Keywords}' ); ?>";
429 const keywordsPlaceholder = "<?php echo esc_attr__( 'Add keywords to generate precise & relevant documentation (comma-separated).', 'betterdocs' ); ?>";
430 const language = "<?php echo esc_attr( 'English' ); ?>";
431 const titleLabel = "<?php echo esc_html__( 'Documentation Title:', 'betterdocs' ); ?>";
432 const keywordLabel = "<?php echo esc_html__( 'Keywords:', 'betterdocs' ); ?>";
433 const secLabel = "<?php echo esc_html__( '# of Sections:', 'betterdocs' ); ?>";
434 const paraLabel = "<?php echo esc_html__( '# of Paragraph Per Section: ', 'betterdocs' ); ?>";
435 const langLabel = "<?php echo esc_html__( 'Language:', 'betterdocs' ); ?>";
436 const promtLabel = "<?php echo esc_html__( 'Prompt:', 'betterdocs' ); ?>";
437 const promtBtnLabel = "<?php echo esc_html__( 'Generate Prompt', 'betterdocs' ); ?>";
438 let promtArticleLabel = "<?php echo esc_html__( 'Generate Doc', 'betterdocs' ); ?>";
439 const checkboxLabel = "<?php echo esc_html__( 'Overwrite your existing Doc:', 'betterdocs' ); ?>";
440 const nonce = "<?php echo esc_attr( wp_create_nonce( 'generate_openai_content_nonce' ) ); ?>";
441
442 <?php
443 $is_valid = $this->get_api_key();
444 $disable_field = 'disabled-input-field';
445 if ( $this->get_api_key() ) {
446 $disable_field = '';
447 }
448 ?>
449
450 let hiddenClass = 'hidden';
451 let newDocPageAtt = 'data-new-doc-page="true"';
452 const isPostContent = `<?php echo isset($_GET['post']) ? esc_html(get_the_content($_GET['post'])) : ''; // phpcs:ignore ?>`;
453
454 if (isPostContent !== '') {
455 hiddenClass = '';
456 newDocPageAtt = '';
457 }
458
459
460 // 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.`;
461
462 // 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.`;
463
464 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.`;
465
466 const aiIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="21" height="20" viewBox="0 0 21 20" fill="none">
467 <g clip-path="url(#clip0_2460_1729)">
468 <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"/>
469 </g>
470 <defs>
471 <clipPath id="clip0_2460_1729">
472 <rect width="20" height="20" fill="white" transform="translate(0.5)"/>
473 </clipPath>
474 </defs>
475 </svg>`;
476
477
478
479 return `
480 <div class="betterdocs-ai-autowrite-form-container hidden" ${newDocPageAtt}>
481 <div class="betterdocs-ai-autowrite-form-content">
482 <div class="betterdocs-ai-autowrite-top-part">
483 <div class="autowrite-heading">
484 <span class="autowrite-icon">
485 <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
486 <g clip-path="url(#clip0_2453_20882)">
487 <path d="M15.8322 30.1765L8.97508 30.7708L9.56936 23.9136L23.8322 9.74219C24.0451 9.52448 24.2993 9.3515 24.58 9.23341C24.8606 9.11531 25.162 9.05447 25.4665 9.05447C25.771 9.05447 26.0724 9.11531 26.3531 9.23341C26.6337 9.3515 26.8879 9.52448 27.1008 9.74219L30.0037 12.6679C30.2176 12.8805 30.3874 13.1334 30.5033 13.4119C30.6192 13.6904 30.6788 13.9891 30.6788 14.2908C30.6788 14.5924 30.6192 14.8911 30.5033 15.1696C30.3874 15.4481 30.2176 15.701 30.0037 15.9136L15.8322 30.1765ZM1.92593 9.07933C1.12365 8.9399 1.12365 7.7879 1.92593 7.64848C3.34647 7.40124 4.6612 6.73658 5.70251 5.73923C6.74382 4.74188 7.46455 3.45703 7.77279 2.04848L7.82079 1.82676C7.99451 1.03591 9.12365 1.02905 9.30651 1.82219L9.36365 2.07819C9.68319 3.48048 10.4099 4.75709 11.4526 5.74772C12.4953 6.73834 13.8074 7.39881 15.2242 7.64619C16.0311 7.78333 16.0311 8.94219 15.2242 9.0839C13.8073 9.33071 12.4949 9.99066 11.4518 10.9809C10.4087 11.9711 9.68146 13.2474 9.36136 14.6496L9.30193 14.9079C9.12136 15.6988 7.99222 15.6942 7.81851 14.901L7.77279 14.6793C7.46424 13.2702 6.74284 11.9849 5.70064 10.9874C4.65845 9.99003 3.34273 9.32574 1.92136 9.07933H1.92593Z" stroke="#E31B54" stroke-width="2.28571" stroke-linecap="round" stroke-linejoin="round"/>
488 </g>
489 <defs>
490 <clipPath id="clip0_2453_20882">
491 <rect width="32" height="32" fill="white"/>
492 </clipPath>
493 </defs>
494 </svg>
495 </span>
496 <h1><?php echo esc_html__( 'Write Documentation with BetterDocs AI', 'betterdocs' ); ?></h1>
497
498 </div>
499 <div class="autowrite-subheadding">
500 <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>
501 </div>
502
503 <?php if ( empty( $this->get_api_key() ) ) : ?>
504 <div id="betterdocs-ai-message">
505 <div class="warning-message">
506 <span class="dashicons dashicons-warning"></span>
507 <div>
508 <?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' ); ?>
509 </div>
510 </div>
511
512 </div>
513 <?php endif; ?>
514
515 <div class="form-group hidden">
516 <div id="betterdocs-ai-error-message"></div>
517 </div>
518
519 </div>
520 <form id="betterdocs-ai-form" class="<?php echo esc_attr( $disable_field ); ?>">
521 <div class="form-inner-content">
522 <div class="form-group">
523 <label for="betterdocs-ai-title">${titleLabel}</label>
524 <input type="text" placeholder="${titlePlaceholder}" id="betterdocs-ai-title" name="betterdocs-ai-title" required value="${title}">
525 </div>
526
527 <div class="form-group">
528 <label for="betterdocs-ai-keyword">${keywordLabel}</label>
529 <input type="text" placeholder="${keywordsPlaceholder}" id="betterdocs-ai-keyword" name="betterdocs-ai-keyword" required>
530 </div>
531
532 <div class="form-group prompt-field">
533 <label for="betterdocs-ai-content">${promtLabel}</label>
534 <textarea id="betterdocs-ai-content" name="betterdocs-ai-content" rows="6" required>${prompt}</textarea>
535 <p class="input-description"><?php echo esc_html__( 'Ensure you include a clear and detailed prompt to receive the desired output. Follow the guidelines provided for the best results.', 'betterdocs' ); ?></p>
536 </div>
537 <div class="betterdocs-ai-checkbox-field ${hiddenClass}">
538 <div class="form-group">
539 <div class="checkbox-field-label">
540 <label for="betterdocs-ai-checkbox">${checkboxLabel}</label>
541 <p class="input-description"><?php echo esc_html__( 'Overwrite the existing doc with the AI Generated Content. If Disabled, new AI Generated content will be added in the section you are currently editing.', 'betterdocs' ); ?></p>
542 </div>
543
544 <div class="checkbox-input-field">
545 <input type="checkbox" id="betterdocs-ai-checkbox" name="betterdocs-ai-checkbox" data-overwrite="false">
546 <label for="betterdocs-ai-checkbox" class="toggle-label"></label>
547 </div>
548 </div>
549 </div>
550
551 <input type="hidden" name="ai_nonce" value="${nonce}" />
552 </div>
553 <div class="generate-button-container">
554 <button type="submit" class="generate-btn" onclick="generateArticle(event)">
555 ${aiIcon} <span>${promtArticleLabel}</span>
556 </button>
557 </div>
558 </form>
559 </div>
560
561 <div class="bd-close-button" onclick="closeWriteWithAIForm('afterClosedClicked')">✕</div>
562 </div>
563
564 <div id="betterdocs-ai-autowrite-form-container-overlay" class="hidden"></div>
565 `;
566 }
567
568 jQuery(document).on('click', '.regenerate-btn', function() {
569 jQuery('.betterdocs-ai-autowrite-form-container').removeClass('hidden');
570 jQuery('#betterdocs-ai-autowrite-form-container-overlay').removeClass('hidden');
571 });
572
573 jQuery(document).ready(function($) {
574 // Check if we are in the Edit Post screen
575
576 if ($('.block-editor-page').length > 0) {
577
578 const bdAIButton = $(`<div class="betterdocs-ai-button-container">
579 <button class="betterdocs-ai-button">
580 <img src="<?php echo esc_url( BETTERDOCS_ABSURL . 'assets/admin/images/betterdocs-icon-white.png' ); ?>" alt="<?php echo esc_attr__( 'betterdocs icon', 'betterdocs' ); ?>" />
581 <span><?php echo esc_html__( 'Write with AI', 'betterdocs' ); ?></span>
582 </button>
583 </div>`);
584
585
586 const closeBtn = $('bd-close-button');
587
588 const formHtml = writeWithAIForm();
589 $(formHtml).addClass('hidden');
590
591 $(document).on('click', '.betterdocs-ai-button', function() {
592 $('.betterdocs-ai-autowrite-form-container').removeClass('hidden');
593 $('#betterdocs-ai-autowrite-form-container-overlay').removeClass('hidden');
594 });
595
596 // $(document).on('click', '.betterdocs-ai-button', function() {
597 if ($('.betterdocs-ai-autowrite-form-container').length < 1) {
598 $('body').append(formHtml);
599
600 // Add event listeners to input elements
601 document.getElementById('betterdocs-ai-title').addEventListener('input', updateTextarea);
602 document.getElementById('betterdocs-ai-keyword').addEventListener('input', updateTextarea);
603 // document.getElementById('bd-autowrtite-content-section').addEventListener('change', updateTextarea);
604 // document.getElementById('bd-autowrtite-content-paragraph').addEventListener('change', updateTextarea);
605 // document.getElementById('betterdocs-ai-language').addEventListener('input', updateTextarea);
606 }
607 // });
608 const btn = document.createElement('button');
609 wp.data.subscribe(function() {
610 setTimeout(() => {
611 if ($('.edit-post-header__settings').length > 0) {
612 $('.edit-post-header__settings').prepend(bdAIButton);
613 } else if ($('.editor-header__settings').length > 0) {
614 $('.editor-header__settings').prepend(bdAIButton);
615 }
616
617 }, 1);
618 });
619 }
620
621 $(document).on('click', '#betterdocs-ai-autowrite-form-container-overlay', function() {
622 closeWriteWithAIForm();
623 });
624 });
625 </script>
626
627 <style>
628 .hidden {
629 display: none !important;
630 }
631
632 .disabled-input-field input,
633 .disabled-input-field textarea,
634 .disabled-input-field button,
635 .disabled-input-field label {
636 pointer-events: none;
637 opacity: 0.6;
638 }
639
640 .disabled-input-field label {
641 opacity: 1;
642 }
643
644 .betterdocs-ai-button-container {
645 margin-left: 10px;
646 white-space: nowrap;
647 max-width: 145px;
648 }
649
650 .autowrite-icon {
651 display: flex;
652 align-items: center;
653 width: 55px;
654 height: 55px;
655 background: #FFE4E8;
656 justify-content: center;
657 border-radius: 50px;
658 }
659
660 .autowrite-icon svg {
661 width: 28px;
662 height: 28px;
663 }
664
665 .autowrite-heading {
666 display: flex;
667 gap: 10px;
668 align-items: center;
669 }
670
671 .autowrite-heading h1 {
672 color: #1D2939;
673 font-family: 'IBM Plex Sans', sans-serif;
674 font-size: 24px;
675 font-style: normal;
676 font-weight: 500;
677 line-height: normal;
678 margin: 0;
679 }
680
681 .autowrite-heading p,
682 form#betterdocs-ai-form p {
683 margin: 0;
684 margin-bottom: 24px;
685 }
686
687 .autowrite-subheadding p {
688 font-family: 'IBM Plex Sans', sans-serif;
689 font-size: 14px;
690 }
691
692 .prompt-field .input-description {
693 margin: 0 !important;
694 }
695
696 .autowrite-heading p {
697 color: #475467;
698 font-family: 'IBM Plex Sans', sans-serif;
699 font-size: 16px;
700 font-style: normal;
701 font-weight: 400;
702 }
703
704 .betterdocs-ai-button {
705 background: #00B884;
706 border: 1px solid transparent;
707 color: #fff;
708 box-shadow: none;
709 font-size: 12px;
710 margin-right: 8px;
711 padding: 0px 15px;
712 cursor: pointer;
713 border-radius: 3px;
714 height: 38px;
715 display: flex;
716 align-items: center;
717 justify-content: space-between;
718 gap: 10px;
719 width: 135px;
720 }
721
722 .betterdocs-ai-button img {
723 width: 20px;
724 height: 20px;
725 }
726
727 .betterdocs-ai-autowrite-form-container {
728 position: fixed;
729 top: 50%;
730 left: 50%;
731 transform: translate(-50%, -50%);
732 z-index: 100001;
733 width: 650px;
734 margin: 0 auto;
735 background-color: #fff;
736 border-radius: 5px;
737 font-family: 'IBM Plex Sans', sans-serif;
738 /* max-height: 600px; */
739 max-height: 750px;
740 max-width: 100%;
741 }
742
743 .betterdocs-ai-autowrite-form-content {
744 background-color: #fff;
745 padding: 20px 0px;
746 border-radius: 5px;
747 padding-bottom: 0;
748 overflow: auto;
749 /* max-height: 700px; */
750 height: 100%;
751
752 }
753
754 .betterdocs-ai-autowrite-top-part {
755 /* padding-bottom: 20px; */
756 border-bottom: 1px solid #ddd;
757 /* margin-bottom: 20px; */
758 padding: 0 40px;
759 }
760
761 #betterdocs-ai-form {
762 display: flex;
763 flex-direction: column;
764 /* height: 100%; */
765 overflow: hidden;
766 }
767
768 .form-inner-content {
769 overflow: auto;
770 height: 100%;
771 max-height: 435px;
772 /* max-height: 330px; */
773 padding: 0 40px;
774 }
775
776 .form-inner-content>.form-group {
777 margin-top: 20px;
778 }
779
780
781 #betterdocs-ai-autowrite-form-container-overlay {
782 position: fixed;
783 top: 0;
784 right: 0;
785 bottom: 0;
786 left: 0;
787 background-color: rgba(0, 0, 0, .7);
788 z-index: 100000;
789 }
790
791
792
793 #betterdocs-ai-form {
794 display: flex;
795 flex-direction: column;
796 }
797
798 #betterdocs-ai-form .form-group {
799 margin-bottom: 24px;
800 }
801
802 #betterdocs-ai-form .bd-aiform-flex {
803 display: flex;
804 justify-content: space-between;
805 }
806
807 #betterdocs-ai-form label {
808 font-weight: 600;
809 margin-bottom: 5px;
810 display: block;
811 font-size: 14px;
812 line-height: 20px;
813 }
814
815 select#bd-autowrtite-content-section,
816 #bd-autowrtite-content-paragraph,
817 #betterdocs-ai-language {
818 width: 150px !important;
819 height: 40px;
820 }
821
822 #betterdocs-ai-form input[type="text"],
823 #betterdocs-ai-form textarea {
824 width: 100%;
825 padding: 8px;
826 box-sizing: border-box;
827 border: 1px solid #D0D5DD;
828 border-radius: 4px;
829 color: #667085;
830 font-weight: 400;
831 }
832
833 /* Override autofill text color */
834 #betterdocs-ai-form input:-webkit-autofill {
835 -webkit-text-fill-color: #667085 !important;
836 }
837
838 #betterdocs-ai-form input::placeholder {
839 color: #acb2bf;
840 }
841
842 #betterdocs-ai-form textarea {
843 color: #667085;
844 }
845
846 #betterdocs-ai-form input:focus,
847 #betterdocs-ai-form textarea:focus {
848 box-shadow: none;
849 }
850
851 #betterdocs-ai-form textarea:focus {
852 color: inherit;
853 box-shadow: none;
854
855 }
856
857
858 .generate-button-container {
859 position: sticky;
860 bottom: 0px;
861 width: 100%;
862 margin-left: 0;
863 z-index: 999999 !important;
864 padding: 20px 0;
865 display: flex;
866 justify-content: end;
867 border-top: 1px solid #ddd;
868 background: white;
869 border-bottom-right-radius: 5px;
870 border-bottom-left-radius: 5px;
871 }
872
873 .generate-button-container button {
874 display: flex;
875 gap: 8px;
876 align-items: center;
877 justify-content: center;
878 opacity: 1 !important;
879 margin-right: 40px;
880 }
881
882 .input-description {
883 font-size: 14px;
884 color: #667085;
885 }
886
887 .betterdocs-ai-checkbox-field .form-group {
888 display: flex;
889 align-items: start;
890 /* gap: 200px; */
891 justify-content: space-between;
892 }
893
894 .betterdocs-ai-checkbox-field input {
895 width: 20px;
896 height: 20px;
897 }
898
899 /* Add this CSS to your existing stylesheet or create a new one */
900
901 .betterdocs-ai-checkbox-field {
902 position: relative;
903 font-size: 16px;
904 /* Adjust font size as needed */
905 }
906
907 .betterdocs-ai-checkbox-field input[type=checkbox]:checked::before {
908 padding: 2px;
909 }
910
911 .betterdocs-ai-checkbox-field input[type=checkbox] {
912 border: 1px solid #00b884;
913 }
914
915 .betterdocs-ai-checkbox-field input[type=checkbox]:checked::before {
916 content: '\2713';
917 color: #00b884;
918 }
919
920 /* Change the default checkbox color */
921 .betterdocs-ai-checkbox-field input[type="checkbox"]:hover {
922 -webkit-appearance: none;
923 /* WebKit/Blink Browsers */
924 -moz-appearance: none;
925 /* Firefox */
926 appearance: none;
927 border: 1px solid #00b884;
928 /* Set the height of the checkbox */
929 /* Optional: Round the corners */
930 outline: none;
931 /* Remove the default outline */
932 }
933
934 /* Style the checked state */
935 .betterdocs-ai-checkbox-field input[type="checkbox"]:checked {
936 /* Set the background color when checked */
937 border: 1px solid #00b884;
938 /* Set the border color when checked */
939 }
940
941 .checkbox-field-label {
942 width: calc(100% - 150px);
943 }
944
945 .checkbox-field-label p {
946 margin: 0 !important;
947 }
948
949 .checkbox-input-field {
950 width: 300px;
951 text-align: right;
952 }
953
954 .checkbox-input-field {
955 position: relative;
956 display: inline-block;
957 width: 60px;
958 height: 34px;
959 }
960
961 .checkbox-input-field input {
962 display: none;
963 }
964
965 .toggle-label {
966 position: absolute;
967 cursor: pointer;
968 top: 0;
969 left: 0;
970 right: 0;
971 bottom: 0;
972 background-color: #ccc;
973 border-radius: 34px;
974 transition: background-color 0.3s;
975 scale: .9;
976 width: 52px;
977 height: 26px;
978 }
979
980
981 .checkbox-input-field input:checked+.toggle-label {
982 background-color: #00b884;
983 }
984
985 .toggle-label:after {
986 content: "";
987 position: absolute;
988 height: 22px;
989 width: 22px;
990 left: 2px;
991 bottom: 2px;
992 background-color: white;
993 border-radius: 50%;
994 transition: transform 0.3s;
995 }
996
997 .checkbox-input-field input:checked+.toggle-label:after {
998 transform: translateX(26px);
999 }
1000
1001 .generate-btn,
1002 .prompt-btn,
1003 .keep-btn {
1004 background-color: #00b884;
1005 color: #fff;
1006 border: none;
1007 padding: 10px 15px;
1008 text-align: center;
1009 text-decoration: none;
1010 display: inline-block;
1011 font-size: 16px;
1012 cursor: pointer;
1013 border-radius: 4px;
1014 }
1015
1016 .generate-btn[disabled] {
1017 cursor: unset;
1018 }
1019
1020 .bd-close-button {
1021 cursor: pointer;
1022 font-size: 18px;
1023 font-weight: bold;
1024 float: right;
1025 position: absolute;
1026 top: -16px;
1027 border-radius: 50%;
1028 background: #ffffff;
1029 width: 40px;
1030 height: 40px;
1031 display: flex;
1032 align-items: center;
1033 justify-content: center;
1034 color: #281617;
1035 right: -42px;
1036 }
1037
1038 div#betterdocs-ai-error-message,
1039 #betterdocs-ai-message {
1040 font-size: 14px;
1041 font-family: 'IBM Plex Sans', sans-serif;
1042 color: #667085;
1043 margin-bottom: 20px;
1044 }
1045
1046 div#betterdocs-ai-error-message a,
1047 #betterdocs-ai-message a {
1048 text-decoration: none;
1049 font-weight: 600;
1050 }
1051
1052 div#betterdocs-ai-error-message a:focus,
1053 #betterdocs-ai-message a:focus {
1054 outline: none;
1055 box-shadow: none;
1056 color: #2271b1;
1057 }
1058
1059 #betterdocs-ai-message span {
1060 color: #d63638;
1061 }
1062
1063 .warning-message {
1064 display: flex;
1065 align-items: center;
1066 gap: 10px;
1067 background: #fbebed;
1068 padding: 12px;
1069 border-radius: 5px;
1070 font-size: 14px;
1071 line-height: 1.4em;
1072 border-left: 4px solid #d63638;
1073 }
1074
1075 .warning-message svg {
1076 width: 20px;
1077 height: 20px;
1078 }
1079
1080 .warning-message span {
1081 color: #d63638;
1082 }
1083
1084 .warning-message .footer-message {
1085 width: calc(100% - 20px);
1086 }
1087
1088 @media only screen and (max-width: 991px) {
1089 .betterdocs-ai-autowrite-form-container {
1090 left: 0;
1091 right: 0;
1092 transform: translate(0%, -50%);
1093 }
1094
1095 .betterdocs-ai-autowrite-form-content {
1096 overflow-x: auto;
1097 }
1098 }
1099
1100 @media only screen and (max-width: 740px) {
1101
1102 /* .bd-close-button {
1103 right: px;
1104 top: 1px;
1105 border-radius: 5px;
1106 width: 12px;
1107 height: 12px;
1108 color: #ffffff;
1109 background: #00b884;
1110 } */
1111 .bd-close-button {
1112 right: 0px;
1113 top: 0px;
1114 width: 12px;
1115 height: 12px;
1116 font-size: 14px;
1117 }
1118
1119 }
1120 </style>
1121 <?php
1122 }
1123 }
1124