diff --git a/src/lib/components/channel/MessageInput.svelte b/src/lib/components/channel/MessageInput.svelte index 0f3f551b83..9cb99337bc 100644 --- a/src/lib/components/channel/MessageInput.svelte +++ b/src/lib/components/channel/MessageInput.svelte @@ -31,6 +31,7 @@ let content = ''; let files = []; + let chatInputElement; let filesInputElement; let inputFiles; @@ -287,8 +288,9 @@ await tick(); - const chatInputElement = document.getElementById(`chat-input-${id}`); - chatInputElement?.focus(); + if (chatInputElement) { + chatInputElement.focus(); + } }; $: if (content) { @@ -297,9 +299,10 @@ onMount(async () => { window.setTimeout(() => { - const chatInput = document.getElementById(`chat-input-${id}`); - chatInput?.focus(); - }, 0); + if (chatInputElement) { + chatInputElement.focus(); + } + }, 100); window.addEventListener('keydown', handleKeyDown); await tick(); @@ -402,7 +405,10 @@ recording = false; await tick(); - document.getElementById(`chat-input-${id}`)?.focus(); + + if (chatInputElement) { + chatInputElement.focus(); + } }} onConfirm={async (data) => { const { text, filename } = data; @@ -410,7 +416,10 @@ recording = false; await tick(); - document.getElementById(`chat-input-${id}`)?.focus(); + + if (chatInputElement) { + chatInputElement.focus(); + } }} /> {:else} @@ -485,17 +494,21 @@ class="scrollbar-hidden font-primary text-left bg-transparent dark:text-gray-100 outline-hidden w-full pt-3 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto" > 0 || - navigator.msMaxTouchPoints > 0 - )} - {placeholder} + shiftEnter={!($settings?.ctrlEnterToSend ?? false) && + (!$mobile || + !( + 'ontouchstart' in window || + navigator.maxTouchPoints > 0 || + navigator.msMaxTouchPoints > 0 + ))} largeTextAsFile={$settings?.largeTextAsFile ?? false} + onChange={(e) => { + const { md } = e; + content = md; + }} on:keydown={async (e) => { e = e.detail.event; const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index af7c227719..046e31099b 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -385,7 +385,6 @@ }; onMount(async () => { - console.log('codeblock', lang, code); if (token) { onUpdate(token); } diff --git a/src/lib/components/common/RichTextInput.svelte b/src/lib/components/common/RichTextInput.svelte index 8aec839ed2..d811a1675e 100644 --- a/src/lib/components/common/RichTextInput.svelte +++ b/src/lib/components/common/RichTextInput.svelte @@ -176,35 +176,34 @@ if (!editor) return; text = text.replaceAll('\n\n', '\n'); const { state, view } = editor; + const { schema, tr } = state; if (text.includes('\n')) { // Multiple lines: make paragraphs - const { schema, tr } = state; const lines = text.split('\n'); - // Map each line to a paragraph node (empty lines -> empty paragraph) const nodes = lines.map((line) => schema.nodes.paragraph.create({}, line ? schema.text(line) : undefined) ); - // Create a document fragment containing all parsed paragraphs const fragment = Fragment.fromArray(nodes); - // Replace current selection with these paragraphs tr.replaceSelectionWith(fragment, false /* don't select new */); - - // You probably want to move the cursor after the inserted content - // tr.setSelection(Selection.near(tr.doc.resolve(tr.selection.to))); - view.dispatch(tr); } else if (text === '') { - // Empty: delete selection or paragraph - editor.commands.clearContent(); + // Empty: replace with empty paragraph using tr + const emptyParagraph = schema.nodes.paragraph.create(); + tr.replaceSelectionWith(emptyParagraph, false); + view.dispatch(tr); } else { - editor.commands.setContent(editor.state.schema.text(text)); + // Single line: create paragraph with text + const paragraph = schema.nodes.paragraph.create({}, schema.text(text)); + tr.replaceSelectionWith(paragraph, false); + view.dispatch(tr); } selectNextTemplate(editor.view.state, editor.view.dispatch); + focus(); }; export const replaceVariables = (variables) => { @@ -253,7 +252,7 @@ export const focus = () => { if (editor) { editor.view.focus(); - // Scroll to the top of the editor + // Scroll to the current selection editor.view.dispatch(editor.view.state.tr.scrollIntoView()); } }; @@ -326,11 +325,7 @@ setTimeout(() => { const templateFound = selectNextTemplate(editor.view.state, editor.view.dispatch); if (!templateFound) { - // If no template found, set cursor at the end - const endPos = editor.view.state.doc.content.size; - editor.view.dispatch( - editor.view.state.tr.setSelection(TextSelection.create(editor.view.state.doc, endPos)) - ); + editor.commands.focus('end'); } }, 0); } @@ -339,7 +334,11 @@ onMount(async () => { let content = value; - if (!json) { + if (json) { + if (!content) { + content = html ? html : null; + } + } else { if (preserveBreaks) { turndownService.addRule('preserveBreaks', { filter: 'br', // Target
elements @@ -370,10 +369,6 @@ // Usage example content = await tryParse(value); } - } else { - if (html && !content) { - content = html; - } } console.log('content', content); @@ -417,40 +412,34 @@ // force re-render so `editor.isActive` works as expected editor = editor; - html = editor.getHTML(); + const htmlValue = editor.getHTML(); + const jsonValue = editor.getJSON(); + let mdValue = turndownService + .turndown( + htmlValue + .replace(/

<\/p>/g, '
') + .replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0')) + ) + .replace(/\u00a0/g, ' '); onChange({ - html: editor.getHTML(), - json: editor.getJSON(), - md: turndownService - .turndown( - editor - .getHTML() - .replace(/

<\/p>/g, '
') - .replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0')) - ) - .replace(/\u00a0/g, ' ') + html: htmlValue, + json: jsonValue, + md: mdValue }); if (json) { - value = editor.getJSON(); + value = jsonValue; } else { - if (!raw) { - let newValue = turndownService - .turndown( - editor - .getHTML() - .replace(/

<\/p>/g, '
') - .replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0')) - ) - .replace(/\u00a0/g, ' '); - + if (raw) { + value = htmlValue; + } else { if (!preserveBreaks) { - newValue = newValue.replace(//g, ''); + mdValue = mdValue.replace(//g, ''); } - if (value !== newValue) { - value = newValue; + if (value !== mdValue) { + value = mdValue; // check if the node is paragraph as well if (editor.isActive('paragraph')) { @@ -459,8 +448,6 @@ } } } - } else { - value = editor.getHTML(); } } }, @@ -609,36 +596,44 @@ const onValueChange = () => { if (!editor) return; + const jsonValue = editor.getJSON(); + const htmlValue = editor.getHTML(); + let mdValue = turndownService + .turndown( + (preserveBreaks ? htmlValue.replace(/

<\/p>/g, '
') : htmlValue).replace( + / {2,}/g, + (m) => m.replace(/ /g, '\u00a0') + ) + ) + .replace(/\u00a0/g, ' '); + + if (value === '') { + editor.commands.clearContent(); // Clear content if value is empty + selectTemplate(); + + return; + } + if (json) { - if (JSON.stringify(value) !== JSON.stringify(editor.getJSON())) { + if (JSON.stringify(value) !== JSON.stringify(jsonValue)) { editor.commands.setContent(value); selectTemplate(); } } else { if (raw) { - if (value !== editor.getHTML()) { + if (value !== htmlValue) { editor.commands.setContent(value); selectTemplate(); } } else { - if ( - value !== - turndownService - .turndown( - (preserveBreaks - ? editor.getHTML().replace(/

<\/p>/g, '
') - : editor.getHTML() - ).replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0')) - ) - .replace(/\u00a0/g, ' ') - ) { - preserveBreaks - ? editor.commands.setContent(value) - : editor.commands.setContent( - marked.parse(value.replaceAll(`\n
`, `
`), { + if (value !== mdValue) { + editor.commands.setContent( + preserveBreaks + ? value + : marked.parse(value.replaceAll(`\n
`, `
`), { breaks: false }) - ); // Update editor content + ); selectTemplate(); }