From 9f7ef4af3a42dce7a7214671f3281d7d0866ce79 Mon Sep 17 00:00:00 2001 From: Luka Trovic Date: Mon, 25 Nov 2024 19:32:24 +0100 Subject: [PATCH] feat: render text inserted from assistant Signed-off-by: Luka Trovic --- src/components/Assistant.vue | 6 +++- .../Suggestion/LinkPicker/suggestions.js | 28 ++----------------- src/markdownit/hasMarkdownSyntax.js | 25 +++++++++++++++++ src/markdownit/isValidMarkdown.js | 19 +++++++++++++ src/markdownit/shouldInterpretAsMarkdown.js | 16 +++++++++++ 5 files changed, 67 insertions(+), 27 deletions(-) create mode 100644 src/markdownit/hasMarkdownSyntax.js create mode 100644 src/markdownit/isValidMarkdown.js create mode 100644 src/markdownit/shouldInterpretAsMarkdown.js diff --git a/src/components/Assistant.vue b/src/components/Assistant.vue index 1b09040693c..f6cd5cb36bb 100644 --- a/src/components/Assistant.vue +++ b/src/components/Assistant.vue @@ -133,6 +133,8 @@ import { } from './Editor.provider.js' import { FloatingMenu } from '@tiptap/vue-2' import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus' +import markdownit from '../markdownit/index.js' +import shouldInterpretAsMarkdown from '../markdownit/shouldInterpretAsMarkdown.js' const limitInRange = (num, min, max) => { return Math.min(Math.max(parseInt(num), parseInt(min)), parseInt(max)) @@ -302,7 +304,9 @@ export default { }) }, async insertResult(task) { - this.$editor.commands.insertContent(task.output.output) + const isMarkdown = shouldInterpretAsMarkdown(task.output.output) + const content = isMarkdown ? markdownit.render(task.output.output) : task.output.output + this.$editor.commands.insertContent(content) this.showTaskList = false }, async copyResult(task) { diff --git a/src/components/Suggestion/LinkPicker/suggestions.js b/src/components/Suggestion/LinkPicker/suggestions.js index 2d5829d9101..dea7d3b674c 100644 --- a/src/components/Suggestion/LinkPicker/suggestions.js +++ b/src/components/Suggestion/LinkPicker/suggestions.js @@ -9,6 +9,7 @@ import { searchProvider, getLinkWithPicker } from '@nextcloud/vue/dist/Component import menuEntries from './../../Menu/entries.js' import { getIsActive } from '../../Menu/utils.js' import markdownit from '../../../markdownit/index.js' +import shouldInterpretAsMarkdown from '../../../markdownit/shouldInterpretAsMarkdown.js' const suggestGroupFormat = t('text', 'Formatting') const suggestGroupPicker = t('text', 'Smart picker') @@ -19,31 +20,6 @@ const filterOut = (e) => { const important = ['task-list', 'table'] -const hasMarkdownSyntax = (content) => { - // Regular expressions for common Markdown patterns - const markdownPatterns = [ - /\*\*.*?\*\*/, // Bold: **text** - /\*.*?\*/, // Italics: *text* - /\[.*?\(.*?\)/, // Links: [text](url) - /^#{1,6}\s.*$/, // Headings: # text - /^\s*[-+*]\s.*/m, // Unordered list: - item - /^\s\d\..*/m, // Ordered list: 1. item - /^>+\s.*/, // Blockquote: > text - /`.*?`/, // Code: `code` - ] - - return markdownPatterns.some(pattern => pattern.test(content)) -} - -const isValidMarkdown = (content) => { - try { - markdownit.parse(content) - return true - } catch (e) { - return false - } -} - const isValidUrl = (url) => { try { return Boolean(new URL(url)) @@ -88,7 +64,7 @@ export default () => createSuggestions({ .then(link => { const isUrl = isValidUrl(link) if (!isUrl) { - const isMarkdown = hasMarkdownSyntax(link) && isValidMarkdown(link) + const isMarkdown = shouldInterpretAsMarkdown(link) // Insert markdown content (e.g. from `text_templates` app) const content = isMarkdown ? markdownit.render(link) : link editor diff --git a/src/markdownit/hasMarkdownSyntax.js b/src/markdownit/hasMarkdownSyntax.js new file mode 100644 index 00000000000..61e59ea62ab --- /dev/null +++ b/src/markdownit/hasMarkdownSyntax.js @@ -0,0 +1,25 @@ +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * Check if the content has Markdown syntax + * + * @param {string} content Markdown object + */ +export default function hasMarkdownSyntax(content) { + // Regular expressions for common Markdown patterns + const markdownPatterns = [ + /\*\*.*?\*\*/, // Bold: **text** + /\*.*?\*/, // Italics: *text* + /\[.*?\(.*?\)/, // Links: [text](url) + /^#{1,6}\s.*$/, // Headings: # text + /^\s*[-+*]\s.*/m, // Unordered list: - item + /^\s\d\..*/m, // Ordered list: 1. item + /^>+\s.*/, // Blockquote: > text + /`.*?`/, // Code: `code` + ] + + return markdownPatterns.some(pattern => pattern.test(content)) +} diff --git a/src/markdownit/isValidMarkdown.js b/src/markdownit/isValidMarkdown.js new file mode 100644 index 00000000000..c722b7d5e26 --- /dev/null +++ b/src/markdownit/isValidMarkdown.js @@ -0,0 +1,19 @@ +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import markdownit from './index.js' + +/** + * Check if the content is valid Markdown syntax + * + * @param {string} content Markdown object + */ +export default function isValidMarkdown(content) { + try { + markdownit.parse(content) + return true + } catch (e) { + return false + } +} diff --git a/src/markdownit/shouldInterpretAsMarkdown.js b/src/markdownit/shouldInterpretAsMarkdown.js new file mode 100644 index 00000000000..25fa6022974 --- /dev/null +++ b/src/markdownit/shouldInterpretAsMarkdown.js @@ -0,0 +1,16 @@ +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import hasMarkdownSyntax from './hasMarkdownSyntax.js' +import isValidMarkdown from './isValidMarkdown.js' + +/** + * Check if the content has Markdown syntax + * + * @param {string} content Markdown object + */ +export default function shouldInterpretAsMarkdown(content) { + return hasMarkdownSyntax(content) && isValidMarkdown(content) +}