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

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