Skip to content

Commit 94f877f

Browse files
committed
refac
1 parent aa2f7fb commit 94f877f

1 file changed

Lines changed: 30 additions & 41 deletions

File tree

src/lib/components/common/RichTextInput.svelte

Lines changed: 30 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -437,50 +437,39 @@
437437
if (!editor || !editor.view) return;
438438
text = text.replaceAll('\n\n', '\n');
439439
440-
// reset the editor content
441-
editor.commands.clearContent();
442-
443-
const { state, view } = editor;
444-
const { schema, tr } = state;
445-
446-
// Build a paragraph node from a line of text, reconstructing any
447-
// serialized mention syntax (e.g. <@model>, <$skill|Label>) into
448-
// proper TipTap Mention nodes via DOMParser.
449-
const toParagraph = (line: string) => {
450-
if (!line) return schema.nodes.paragraph.create();
451-
if (/<[@#$][\w.\-:/]+(?:\|[^>]*)?>/.test(line)) {
452-
const html = line.replace(
453-
/<([@#$])([\w.\-:/]+)(?:\|([^>]*))?>/g,
454-
(_, ch, id, label) => {
455-
const display = label?.length ? label : id;
456-
return `<span class="mention" data-type="mention" data-id="${id}" data-mention-suggestion-char="${ch}">${ch}${display}</span>`;
457-
}
458440
);
459-
const el = document.createElement('p');
460-
el.innerHTML = html;
461-
return DOMParser.fromSchema(schema).parse(el, {
462-
topNode: schema.nodes.paragraph
463-
});
464-
}
465-
return schema.nodes.paragraph.create({}, schema.text(line));
466-
};
467-
468-
if (text.includes('\n')) {
469-
// Multiple lines: make paragraphs
470-
const lines = text.split('\n');
471-
const nodes = lines.map(toParagraph);
472-
// Create a document fragment containing all parsed paragraphs
473-
const fragment = Fragment.fromArray(nodes);
474-
// Replace current selection with these paragraphs
475-
tr.replaceSelectionWith(fragment, false /* don't select new */);
476-
view.dispatch(tr);
477-
} else if (text === '') {
478-
// Empty: replace with empty paragraph using tr
441+
if (text === '') {
479442
editor.commands.clearContent();
480443
} else {
481-
// Single line: create paragraph with text
482-
tr.replaceSelectionWith(toParagraph(text), false);
483-
view.dispatch(tr);
444+
// Regex to find serialized mention tags: <@id>, <#id>, <$id|label>
445+
const mentionReG = /<([@#$])([\w.\-:/]+)(?:\|([^>]*))?>/g;
446+
447+
// Convert each line to a <p>, replacing mention tags with proper
448+
// TipTap mention spans that the editor's DOMParser will recognise.
449+
const lines = text.split('\n');
450+
const htmlContent = lines
451+
.map((line) => {
452+
if (!line) return '<p></p>';
453+
// Escape HTML entities in the line FIRST so we don't corrupt
454+
// user text that happens to contain < or >, then re-inject
455+
// the mention spans.
456+
const escaped = line
457+
.replace(/&/g, '&amp;')
458+
.replace(/</g, '&lt;')
459+
.replace(/>/g, '&gt;');
460+
// Now replace the escaped mention patterns back into real spans
461+
const withMentions = escaped.replace(
462+
/&lt;([@#$])([\w.\-:/]+)(?:\|([^&]*?))?&gt;/g,
463+
(_, ch, id, label) => {
464+
const display = label?.length ? label : id;
465+
return `<span class="mention" data-type="mention" data-id="${id}" data-label="${display}" data-mention-suggestion-char="${ch}">${ch}${display}</span>`;
466+
}
467+
);
468+
return `<p>${withMentions}</p>`;
469+
})
470+
.join('');
471+
472+
editor.commands.setContent(htmlContent);
484473
}
485474
486475
selectNextTemplate(editor.view.state, editor.view.dispatch);

0 commit comments

Comments
 (0)