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

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