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

1,106 lines 46.5 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-3.5-turbo', // 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 if ($('.block-editor-page').length > 0) {
566 const bdAIButton = $(`<div class="betterdocs-ai-button-container">
567 <button class="betterdocs-ai-button">
568 <img src="<?php echo esc_url(BETTERDOCS_ABSURL . 'assets/admin/images/betterdocs-icon-white.png'); ?>" alt="<?php echo esc_attr__('betterdocs icon', 'embedpress'); ?>" />
569 <span><?php echo esc_html__('Write with AI', 'embedpress'); ?></span>
570 </button>
571 </div>`);
572 const closeBtn = $('bd-close-button');
573
574 const formHtml = writeWithAIForm();
575 $(formHtml).addClass('hidden');
576
577 $(document).on('click', '.betterdocs-ai-button', function() {
578 $('.betterdocs-ai-autowrite-form-container').removeClass('hidden');
579 $('#betterdocs-ai-autowrite-form-container-overlay').removeClass('hidden');
580 });
581
582 // $(document).on('click', '.betterdocs-ai-button', function() {
583 if ($('.betterdocs-ai-autowrite-form-container').length < 1) {
584 $('body').append(formHtml);
585
586 // Add event listeners to input elements
587 document.getElementById('betterdocs-ai-title').addEventListener('input', updateTextarea);
588 document.getElementById('betterdocs-ai-keyword').addEventListener('input', updateTextarea);
589 // document.getElementById('bd-autowrtite-content-section').addEventListener('change', updateTextarea);
590 // document.getElementById('bd-autowrtite-content-paragraph').addEventListener('change', updateTextarea);
591 // document.getElementById('betterdocs-ai-language').addEventListener('input', updateTextarea);
592 }
593 // });
594 const btn = document.createElement('button');
595 wp.data.subscribe(function() {
596 setTimeout(() => {
597 if ($('.edit-post-header__settings').length > 0) {
598 $('.edit-post-header__settings').prepend(bdAIButton);
599 }
600 }, 1);
601 });
602 }
603
604 $(document).on('click', '#betterdocs-ai-autowrite-form-container-overlay', function() {
605 closeWriteWithAIForm();
606 });
607 });
608 </script>
609
610 <style>
611 .hidden {
612 display: none !important;
613 }
614
615 .disabled-input-field input,
616 .disabled-input-field textarea,
617 .disabled-input-field button,
618 .disabled-input-field label {
619 pointer-events: none;
620 opacity: 0.6;
621 }
622
623 .disabled-input-field label {
624 opacity: 1;
625 }
626
627 .betterdocs-ai-button-container {
628 margin-left: 10px;
629 }
630
631 .autowrite-icon {
632 display: flex;
633 align-items: center;
634 width: 55px;
635 height: 55px;
636 background: #FFE4E8;
637 justify-content: center;
638 border-radius: 50px;
639 }
640
641 .autowrite-icon svg {
642 width: 28px;
643 height: 28px;
644 }
645
646 .autowrite-heading {
647 display: flex;
648 gap: 10px;
649 align-items: center;
650 }
651
652 .autowrite-heading h1 {
653 color: #1D2939;
654 font-family: 'IBM Plex Sans', sans-serif;
655 font-size: 24px;
656 font-style: normal;
657 font-weight: 500;
658 line-height: normal;
659 margin: 0;
660 }
661
662 .autowrite-heading p,
663 form#betterdocs-ai-form p {
664 margin: 0;
665 margin-bottom: 24px;
666 }
667
668 .autowrite-subheadding p {
669 font-family: 'IBM Plex Sans', sans-serif;
670 font-size: 14px;
671 }
672
673 .prompt-field .input-description {
674 margin: 0 !important;
675 }
676
677 .autowrite-heading p {
678 color: #475467;
679 font-family: 'IBM Plex Sans', sans-serif;
680 font-size: 16px;
681 font-style: normal;
682 font-weight: 400;
683 }
684
685 .betterdocs-ai-button {
686 background: #00B884;
687 border: 1px solid transparent;
688 color: #fff;
689 box-shadow: none;
690 font-size: 12px;
691 margin-right: 8px;
692 padding: 0px 15px;
693 cursor: pointer;
694 border-radius: 3px;
695 height: 38px;
696 display: flex;
697 align-items: center;
698 justify-content: space-between;
699 gap: 10px;
700 }
701
702 .betterdocs-ai-button img {
703 width: 20px;
704 height: 20px;
705 }
706
707 .betterdocs-ai-autowrite-form-container {
708 position: fixed;
709 top: 50%;
710 left: 50%;
711 transform: translate(-50%, -50%);
712 z-index: 100001;
713 width: 650px;
714 margin: 0 auto;
715 background-color: #fff;
716 border-radius: 5px;
717 font-family: 'IBM Plex Sans', sans-serif;
718 /* max-height: 600px; */
719 max-height: 750px;
720 max-width: 100%;
721 }
722
723 .betterdocs-ai-autowrite-form-content {
724 background-color: #fff;
725 padding: 20px 0px;
726 border-radius: 5px;
727 padding-bottom: 0;
728 overflow: auto;
729 /* max-height: 700px; */
730 height: 100%;
731
732 }
733
734 .betterdocs-ai-autowrite-top-part {
735 /* padding-bottom: 20px; */
736 border-bottom: 1px solid #ddd;
737 /* margin-bottom: 20px; */
738 padding: 0 40px;
739 }
740
741 #betterdocs-ai-form {
742 display: flex;
743 flex-direction: column;
744 /* height: 100%; */
745 overflow: hidden;
746 }
747
748 .form-inner-content {
749 overflow: auto;
750 height: 100%;
751 max-height: 435px;
752 /* max-height: 330px; */
753 padding: 0 40px;
754 }
755
756 .form-inner-content>.form-group {
757 margin-top: 20px;
758 }
759
760
761 #betterdocs-ai-autowrite-form-container-overlay {
762 position: fixed;
763 top: 0;
764 right: 0;
765 bottom: 0;
766 left: 0;
767 background-color: rgba(0, 0, 0, .7);
768 z-index: 100000;
769 }
770
771
772
773 #betterdocs-ai-form {
774 display: flex;
775 flex-direction: column;
776 }
777
778 #betterdocs-ai-form .form-group {
779 margin-bottom: 24px;
780 }
781
782 #betterdocs-ai-form .bd-aiform-flex {
783 display: flex;
784 justify-content: space-between;
785 }
786
787 #betterdocs-ai-form label {
788 font-weight: 600;
789 margin-bottom: 5px;
790 display: block;
791 font-size: 14px;
792 line-height: 20px;
793 }
794
795 select#bd-autowrtite-content-section,
796 #bd-autowrtite-content-paragraph,
797 #betterdocs-ai-language {
798 width: 150px !important;
799 height: 40px;
800 }
801
802 #betterdocs-ai-form input[type="text"],
803 #betterdocs-ai-form textarea {
804 width: 100%;
805 padding: 8px;
806 box-sizing: border-box;
807 border: 1px solid #D0D5DD;
808 border-radius: 4px;
809 color: #667085;
810 font-weight: 400;
811 }
812
813 /* Override autofill text color */
814 #betterdocs-ai-form input:-webkit-autofill {
815 -webkit-text-fill-color: #667085 !important;
816 }
817
818 #betterdocs-ai-form input::placeholder {
819 color: #acb2bf;
820 }
821
822 #betterdocs-ai-form textarea {
823 color: #667085;
824 }
825
826 #betterdocs-ai-form input:focus,
827 #betterdocs-ai-form textarea:focus {
828 box-shadow: none;
829 }
830
831 #betterdocs-ai-form textarea:focus {
832 color: inherit;
833 box-shadow: none;
834
835 }
836
837
838 .generate-button-container {
839 position: sticky;
840 bottom: 0px;
841 width: 100%;
842 margin-left: 0;
843 z-index: 999999 !important;
844 padding: 20px 0;
845 display: flex;
846 justify-content: end;
847 border-top: 1px solid #ddd;
848 background: white;
849 border-bottom-right-radius: 5px;
850 border-bottom-left-radius: 5px;
851 }
852
853 .generate-button-container button {
854 display: flex;
855 gap: 8px;
856 align-items: center;
857 justify-content: center;
858 opacity: 1 !important;
859 margin-right: 40px;
860 }
861
862 .input-description {
863 font-size: 14px;
864 color: #667085;
865 }
866
867 .betterdocs-ai-checkbox-field .form-group {
868 display: flex;
869 align-items: start;
870 /* gap: 200px; */
871 justify-content: space-between;
872 }
873
874 .betterdocs-ai-checkbox-field input {
875 width: 20px;
876 height: 20px;
877 }
878
879 /* Add this CSS to your existing stylesheet or create a new one */
880
881 .betterdocs-ai-checkbox-field {
882 position: relative;
883 font-size: 16px;
884 /* Adjust font size as needed */
885 }
886
887 .betterdocs-ai-checkbox-field input[type=checkbox]:checked::before {
888 padding: 2px;
889 }
890
891 .betterdocs-ai-checkbox-field input[type=checkbox] {
892 border: 1px solid #00b884;
893 }
894
895 .betterdocs-ai-checkbox-field input[type=checkbox]:checked::before {
896 content: '\2713';
897 color: #00b884;
898 }
899
900 /* Change the default checkbox color */
901 .betterdocs-ai-checkbox-field input[type="checkbox"]:hover {
902 -webkit-appearance: none;
903 /* WebKit/Blink Browsers */
904 -moz-appearance: none;
905 /* Firefox */
906 appearance: none;
907 border: 1px solid #00b884;
908 /* Set the height of the checkbox */
909 /* Optional: Round the corners */
910 outline: none;
911 /* Remove the default outline */
912 }
913
914 /* Style the checked state */
915 .betterdocs-ai-checkbox-field input[type="checkbox"]:checked {
916 /* Set the background color when checked */
917 border: 1px solid #00b884;
918 /* Set the border color when checked */
919 }
920
921 .checkbox-field-label {
922 width: calc(100% - 150px);
923 }
924
925 .checkbox-field-label p {
926 margin: 0 !important;
927 }
928
929 .checkbox-input-field {
930 width: 300px;
931 text-align: right;
932 }
933
934 .checkbox-input-field {
935 position: relative;
936 display: inline-block;
937 width: 60px;
938 height: 34px;
939 }
940
941 .checkbox-input-field input {
942 display: none;
943 }
944
945 .toggle-label {
946 position: absolute;
947 cursor: pointer;
948 top: 0;
949 left: 0;
950 right: 0;
951 bottom: 0;
952 background-color: #ccc;
953 border-radius: 34px;
954 transition: background-color 0.3s;
955 scale: .9;
956 width: 52px;
957 height: 26px;
958 }
959
960
961 .checkbox-input-field input:checked+.toggle-label {
962 background-color: #00b884;
963 }
964
965 .toggle-label:after {
966 content: "";
967 position: absolute;
968 height: 22px;
969 width: 22px;
970 left: 2px;
971 bottom: 2px;
972 background-color: white;
973 border-radius: 50%;
974 transition: transform 0.3s;
975 }
976
977 .checkbox-input-field input:checked+.toggle-label:after {
978 transform: translateX(26px);
979 }
980
981 .generate-btn,
982 .prompt-btn,
983 .keep-btn {
984 background-color: #00b884;
985 color: #fff;
986 border: none;
987 padding: 10px 15px;
988 text-align: center;
989 text-decoration: none;
990 display: inline-block;
991 font-size: 16px;
992 cursor: pointer;
993 border-radius: 4px;
994 }
995
996 .generate-btn[disabled] {
997 cursor: unset;
998 }
999
1000 .bd-close-button {
1001 cursor: pointer;
1002 font-size: 18px;
1003 font-weight: bold;
1004 float: right;
1005 position: absolute;
1006 right: 1px;
1007 top: 1px;
1008 padding: 10px;
1009 background: #ffffff;
1010 border-radius: 25px;
1011 width: 20px;
1012 height: 20px;
1013 display: flex;
1014 align-items: center;
1015 justify-content: center;
1016 color: #281617;
1017 right: -60px;
1018 }
1019
1020 div#betterdocs-ai-error-message,
1021 #betterdocs-ai-message {
1022 font-size: 14px;
1023 font-family: 'IBM Plex Sans', sans-serif;
1024 color: #667085;
1025 margin-bottom: 20px;
1026 }
1027
1028 div#betterdocs-ai-error-message a,
1029 #betterdocs-ai-message a {
1030 text-decoration: none;
1031 font-weight: 600;
1032 }
1033
1034 div#betterdocs-ai-error-message a:focus,
1035 #betterdocs-ai-message a:focus {
1036 outline: none;
1037 box-shadow: none;
1038 color: #2271b1;
1039 }
1040
1041 #betterdocs-ai-message span {
1042 color: #d63638;
1043 }
1044
1045 .warning-message {
1046 display: flex;
1047 align-items: center;
1048 gap: 10px;
1049 background: #fbebed;
1050 padding: 12px;
1051 border-radius: 5px;
1052 font-size: 14px;
1053 line-height: 1.4em;
1054 border-left: 4px solid #d63638;
1055 }
1056
1057 .warning-message svg {
1058 width: 20px;
1059 height: 20px;
1060 }
1061
1062 .warning-message span {
1063 color: #d63638;
1064 }
1065
1066 .warning-message .footer-message {
1067 width: calc(100% - 20px);
1068 }
1069
1070 @media only screen and (max-width: 991px) {
1071 .betterdocs-ai-autowrite-form-container {
1072 left: 0;
1073 right: 0;
1074 transform: translate(0%, -50%);
1075 }
1076
1077 .betterdocs-ai-autowrite-form-content {
1078 overflow-x: auto;
1079 }
1080 }
1081
1082 @media only screen and (max-width: 740px) {
1083
1084 /* .bd-close-button {
1085 right: px;
1086 top: 1px;
1087 border-radius: 5px;
1088 width: 12px;
1089 height: 12px;
1090 color: #ffffff;
1091 background: #00b884;
1092 } */
1093 .bd-close-button {
1094 right: 0px;
1095 top: 0px;
1096 width: 12px;
1097 height: 12px;
1098 font-size: 14px;
1099 }
1100
1101 }
1102 </style>
1103 <?php
1104 }
1105 }
1106