diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte
index f384c5b8f..31cf94e8b 100644
--- a/src/lib/components/chat/MessageInput.svelte
+++ b/src/lib/components/chat/MessageInput.svelte
@@ -754,6 +754,7 @@
bind:this={chatInputElement}
bind:value={prompt}
id="chat-input"
+ preserveBreaks={true}
messageInput={true}
shiftEnter={!($settings?.ctrlEnterToSend ?? false) &&
(!$mobile ||
diff --git a/src/lib/components/chat/MessageInput/Commands/Prompts.svelte b/src/lib/components/chat/MessageInput/Commands/Prompts.svelte
index c6d8bcb82..9d49cee25 100644
--- a/src/lib/components/chat/MessageInput/Commands/Prompts.svelte
+++ b/src/lib/components/chat/MessageInput/Commands/Prompts.svelte
@@ -22,9 +22,18 @@
let selectedPromptIdx = 0;
let filteredPrompts = [];
- $: filteredPrompts = $prompts
- .filter((p) => p.command.toLowerCase().includes(command.toLowerCase()))
- .sort((a, b) => a.title.localeCompare(b.title));
+ $: {
+ if (command && command.length > 0) {
+ const commandName = command.substring(1).toLowerCase();
+ const cleanedCommandName = commandName.replace(/<\/?p>/gi, '').trim();
+
+ filteredPrompts = $prompts
+ .filter((p) => p.command.toLowerCase().includes(cleanedCommandName))
+ .sort((a, b) => a.title.localeCompare(b.title));
+ } else {
+ filteredPrompts = [];
+ }
+ }
$: if (command) {
selectedPromptIdx = 0;
@@ -120,23 +129,42 @@
text = text.replaceAll('{{CURRENT_WEEKDAY}}', weekday);
}
- const lines = prompt.split('\n');
- const lastLine = lines.pop();
-
- const lastLineWords = lastLine.split(' ');
- const lastWord = lastLineWords.pop();
-
if ($settings?.richTextInput ?? true) {
- lastLineWords.push(
- `${text.replace(//g, '>').replaceAll('\n', '
')}`
- );
+ const allPromptLines = prompt.split('\n');
+ const lastLineWithTrigger = allPromptLines.pop() || ''; // Line where command was typed
+ const wordsInLastLine = lastLineWithTrigger.split(' ');
+ wordsInLastLine.pop(); // Remove the command trigger itself (e.g., /mycommand)
+
+ let fullPromptPrefix = '';
+ if (allPromptLines.length > 0) {
+ fullPromptPrefix = allPromptLines.join('\n');
+ }
+ if (wordsInLastLine.length > 0) {
+ if (fullPromptPrefix.length > 0) {
+ fullPromptPrefix += '\n';
+ }
+ fullPromptPrefix += wordsInLastLine.join(' ');
+ }
+ fullPromptPrefix = fullPromptPrefix.trimEnd();
+
+ if (text && text.trim().length > 0) { // If 'text' (the command's content) is not empty
+ if (fullPromptPrefix.length > 0) {
+ prompt = fullPromptPrefix + '\n\n' + text;
+ } else {
+ prompt = text;
+ }
+ } else {
+ prompt = fullPromptPrefix;
+ }
- lines.push(lastLineWords.join(' '));
- prompt = lines.join('
');
} else {
- lastLineWords.push(text);
- lines.push(lastLineWords.join(' '));
- prompt = lines.join('\n');
+ const currentInputLines = prompt.split('\n');
+ const lastCurrentInputLine = currentInputLines.pop() || '';
+ const lastCurrentInputLineWords = lastCurrentInputLine.split(' ');
+ lastCurrentInputLineWords.pop();
+ lastCurrentInputLineWords.push(command.content);
+ currentInputLines.push(lastCurrentInputLineWords.join(' '));
+ prompt = currentInputLines.join('\n');
}
const chatInputContainerElement = document.getElementById('chat-input-container');
diff --git a/src/lib/components/common/RichTextInput.svelte b/src/lib/components/common/RichTextInput.svelte
index eb7013e5e..b543241cf 100644
--- a/src/lib/components/common/RichTextInput.svelte
+++ b/src/lib/components/common/RichTextInput.svelte
@@ -163,25 +163,34 @@
}
if (!raw) {
- async function tryParse(value, attempts = 3, interval = 100) {
+ async function tryParse(value, attempts = 3, interval = 100, useMarkdownBreaks = false) {
try {
+ // Normalize all types of newlines to \n
+ const normalizedTextValue = (value || '').replace(/\r\n|\r/g, '\n');
// Try parsing the value
- return marked.parse(value.replaceAll(`\n
`, `
`), {
- breaks: false
+ return marked.parse(normalizedTextValue, {
+ breaks: useMarkdownBreaks,
+ gfm: true // Ensure GFM is active for paragraph handling
});
} catch (error) {
// If no attempts remain, fallback to plain text
if (attempts <= 1) {
- return value;
+ return (value || '').replace(/\r\n|\r/g, '\n');
}
// Wait for the interval, then retry
await new Promise((resolve) => setTimeout(resolve, interval));
- return tryParse(value, attempts - 1, interval); // Recursive call
+ return tryParse(value, attempts - 1, interval, useMarkdownBreaks);
}
}
// Usage example
- content = await tryParse(value);
+ content = await tryParse(value, 3, 100, preserveBreaks);
+
+ if (preserveBreaks && content) {
+ // Ensure truly empty paragraphs generated by marked get an
+ // so they are visually represented and can be saved back correctly.
+ content = content.replace(/
(?: |
)?<\/p>/gi, '
'); + } } } else { if (html && !content) { @@ -244,9 +253,7 @@ if (!raw) { let newValue = turndownService .turndown( - editor - .getHTML() - .replace(/<\/p>/g, '
')
+ html
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
)
.replace(/\u00a0/g, ' ');
@@ -438,14 +445,23 @@
)
.replace(/\u00a0/g, ' ')
) {
- preserveBreaks
- ? editor.commands.setContent(value)
- : editor.commands.setContent(
- marked.parse(value.replaceAll(`\n
`, `
`), {
- breaks: false
- })
- ); // Update editor content
-
+ if (preserveBreaks) {
+ const normalizedValue = (value || '').replace(/\r\n|\r/g, '\n');
+ // Parse Markdown with breaks:true to convert \n to
for Tiptap
+ let htmlToSet = marked.parse(normalizedValue, { breaks: true, gfm: true });
+ if (htmlToSet) {
+ // htmlToSet = htmlToSet.replace(/
<\/p>/g, '
'); // Old way + htmlToSet = htmlToSet.replace(/
(?: |
)?<\/p>/gi, '
'); + } + editor.commands.setContent( + marked.parse(normalizedValue, { breaks: true, gfm: true }) + ); + } else { + const normalizedValue = (value || '').replace(/\r\n|\r/g, '\n'); + editor.commands.setContent( + marked.parse(normalizedValue, { breaks: false, gfm: true }) + ); + } selectTemplate(); } } diff --git a/src/lib/components/workspace/Prompts/PromptEditor.svelte b/src/lib/components/workspace/Prompts/PromptEditor.svelte index 62d75fa65..34b66f33b 100644 --- a/src/lib/components/workspace/Prompts/PromptEditor.svelte +++ b/src/lib/components/workspace/Prompts/PromptEditor.svelte @@ -156,6 +156,7 @@ bind:value={content} rows={6} required + preserveBreaks={true} />