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

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