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

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