This commit is contained in:
Timothy Jaeryang Baek 2025-02-13 22:37:01 -08:00
parent b7aeb432ed
commit fa92abce31
5 changed files with 128 additions and 50 deletions

View File

@ -18,6 +18,8 @@
export let floatingButtons = true; export let floatingButtons = true;
export let onSourceClick = () => {}; export let onSourceClick = () => {};
export let onTaskClick = () => {};
export let onAddMessages = () => {}; export let onAddMessages = () => {};
let contentContainerElement; let contentContainerElement;
@ -141,6 +143,7 @@
return acc.filter((item, index) => acc.indexOf(item) === index); return acc.filter((item, index) => acc.indexOf(item) === index);
}, [])} }, [])}
{onSourceClick} {onSourceClick}
{onTaskClick}
on:update={(e) => { on:update={(e) => {
dispatch('update', e.detail); dispatch('update', e.detail);
}} }}

View File

@ -17,7 +17,9 @@
export let save = false; export let save = false;
export let sourceIds = []; export let sourceIds = [];
export let onSourceClick = () => {}; export let onSourceClick = () => {};
export let onTaskClick = () => {};
let tokens = []; let tokens = [];
@ -45,6 +47,7 @@
{tokens} {tokens}
{id} {id}
{save} {save}
{onTaskClick}
{onSourceClick} {onSourceClick}
on:update={(e) => { on:update={(e) => {
dispatch('update', e.detail); dispatch('update', e.detail);

View File

@ -26,6 +26,8 @@
export let attributes = {}; export let attributes = {};
export let save = false; export let save = false;
export let onTaskClick: Function = () => {};
export let onSourceClick: Function = () => {}; export let onSourceClick: Function = () => {};
const headerComponent = (depth: number) => { const headerComponent = (depth: number) => {
@ -168,17 +170,37 @@
</div> </div>
{:else if token.type === 'blockquote'} {:else if token.type === 'blockquote'}
<blockquote> <blockquote>
<svelte:self id={`${id}-${tokenIdx}`} tokens={token.tokens} /> <svelte:self id={`${id}-${tokenIdx}`} tokens={token.tokens} {onTaskClick} {onSourceClick} />
</blockquote> </blockquote>
{:else if token.type === 'list'} {:else if token.type === 'list'}
{#if token.ordered} {#if token.ordered}
<ol start={token.start || 1}> <ol start={token.start || 1}>
{#each token.items as item, itemIdx} {#each token.items as item, itemIdx}
<li> <li>
{#if item?.task}
<input
class=" translate-y-[1px] -translate-x-1"
type="checkbox"
checked={item.checked}
on:change={(e) => {
onTaskClick({
id: id,
token: token,
tokenIdx: tokenIdx,
item: item,
itemIdx: itemIdx,
checked: e.target.checked
});
}}
/>
{/if}
<svelte:self <svelte:self
id={`${id}-${tokenIdx}-${itemIdx}`} id={`${id}-${tokenIdx}-${itemIdx}`}
tokens={item.tokens} tokens={item.tokens}
top={token.loose} top={token.loose}
{onTaskClick}
{onSourceClick}
/> />
</li> </li>
{/each} {/each}
@ -187,15 +209,40 @@
<ul> <ul>
{#each token.items as item, itemIdx} {#each token.items as item, itemIdx}
<li> <li>
{#if item?.task}
<input
class=" translate-y-[1px] -translate-x-1"
type="checkbox"
checked={item.checked}
on:change={(e) => {
onTaskClick({
id: id,
token: token,
tokenIdx: tokenIdx,
item: item,
itemIdx: itemIdx,
checked: e.target.checked
});
}}
/>
{/if}
<svelte:self <svelte:self
id={`${id}-${tokenIdx}-${itemIdx}`} id={`${id}-${tokenIdx}-${itemIdx}`}
tokens={item.tokens} tokens={item.tokens}
top={token.loose} top={token.loose}
{onTaskClick}
{onSourceClick}
/> />
</li> </li>
{/each} {/each}
</ul> </ul>
{/if} {/if}
{:else if token.type === 'list_item'}
{JSON.stringify(token)}
<p>
<MarkdownInlineTokens id={`${id}-${tokenIdx}-li`} tokens={token.tokens} {onSourceClick} />
</p>
{:else if token.type === 'details'} {:else if token.type === 'details'}
<Collapsible title={token.summary} attributes={token?.attributes} className="w-full space-y-1"> <Collapsible title={token.summary} attributes={token?.attributes} className="w-full space-y-1">
<div class=" mb-1.5" slot="content"> <div class=" mb-1.5" slot="content">
@ -203,6 +250,8 @@
id={`${id}-${tokenIdx}-d`} id={`${id}-${tokenIdx}-d`}
tokens={marked.lexer(token.text)} tokens={marked.lexer(token.text)}
attributes={token?.attributes} attributes={token?.attributes}
{onTaskClick}
{onSourceClick}
/> />
</div> </div>
</Collapsible> </Collapsible>

View File

@ -716,6 +716,9 @@
floatingButtons={message?.done} floatingButtons={message?.done}
save={!readOnly} save={!readOnly}
{model} {model}
onTaskClick={async (e) => {
console.log(e);
}}
onSourceClick={async (e) => { onSourceClick={async (e) => {
console.log(e); console.log(e);
let sourceButton = document.getElementById(`source-${e}`); let sourceButton = document.getElementById(`source-${e}`);

View File

@ -35,6 +35,8 @@
export let value = ''; export let value = '';
export let id = ''; export let id = '';
export let raw = false;
export let preserveBreaks = false; export let preserveBreaks = false;
export let generateAutoCompletion: Function = async () => null; export let generateAutoCompletion: Function = async () => null;
export let autocomplete = false; export let autocomplete = false;
@ -137,25 +139,29 @@
}); });
} }
async function tryParse(value, attempts = 3, interval = 100) { let content = value;
try {
// Try parsing the value
return marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), {
breaks: false
});
} catch (error) {
// If no attempts remain, fallback to plain text
if (attempts <= 1) {
return value;
}
// Wait for the interval, then retry
await new Promise((resolve) => setTimeout(resolve, interval));
return tryParse(value, attempts - 1, interval); // Recursive call
}
}
// Usage example if (!raw) {
let content = await tryParse(value); async function tryParse(value, attempts = 3, interval = 100) {
try {
// Try parsing the value
return marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), {
breaks: false
});
} catch (error) {
// If no attempts remain, fallback to plain text
if (attempts <= 1) {
return value;
}
// Wait for the interval, then retry
await new Promise((resolve) => setTimeout(resolve, interval));
return tryParse(value, attempts - 1, interval); // Recursive call
}
}
// Usage example
content = await tryParse(value);
}
editor = new Editor({ editor = new Editor({
element: element, element: element,
@ -191,28 +197,33 @@
onTransaction: () => { onTransaction: () => {
// force re-render so `editor.isActive` works as expected // force re-render so `editor.isActive` works as expected
editor = editor; editor = editor;
let newValue = turndownService
.turndown(
editor
.getHTML()
.replace(/<p><\/p>/g, '<br/>')
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
)
.replace(/\u00a0/g, ' ');
if (!preserveBreaks) { if (!raw) {
newValue = newValue.replace(/<br\/>/g, ''); let newValue = turndownService
} .turndown(
editor
.getHTML()
.replace(/<p><\/p>/g, '<br/>')
.replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
)
.replace(/\u00a0/g, ' ');
if (value !== newValue) { if (!preserveBreaks) {
value = newValue; newValue = newValue.replace(/<br\/>/g, '');
}
// check if the node is paragraph as well if (value !== newValue) {
if (editor.isActive('paragraph')) { value = newValue;
if (value === '') {
editor.commands.clearContent(); // check if the node is paragraph as well
if (editor.isActive('paragraph')) {
if (value === '') {
editor.commands.clearContent();
}
} }
} }
} else {
value = editor.getHTML();
} }
}, },
editorProps: { editorProps: {
@ -340,21 +351,30 @@
// Update the editor content if the external `value` changes // Update the editor content if the external `value` changes
$: if ( $: if (
editor && editor &&
value !== (raw
turndownService ? value !== editor.getHTML()
.turndown( : value !==
(preserveBreaks turndownService
? editor.getHTML().replace(/<p><\/p>/g, '<br/>') .turndown(
: editor.getHTML() (preserveBreaks
).replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0')) ? editor.getHTML().replace(/<p><\/p>/g, '<br/>')
) : editor.getHTML()
.replace(/\u00a0/g, ' ') ).replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
)
.replace(/\u00a0/g, ' '))
) { ) {
editor.commands.setContent( if (raw) {
marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), { editor.commands.setContent(value);
breaks: false } else {
}) preserveBreaks
); // Update editor content ? editor.commands.setContent(value)
: editor.commands.setContent(
marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), {
breaks: false
})
); // Update editor content
}
selectTemplate(); selectTemplate();
} }
</script> </script>