diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index e987733221..b2dc86dfe0 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -465,6 +465,15 @@ return; } + if (event.data.type === 'action:submit') { + console.debug(event.data.text); + + if (prompt !== '') { + await tick(); + submitPrompt(prompt); + } + } + // Replace with your iframe's origin if (event.data.type === 'input:prompt') { console.debug(event.data.text); @@ -477,15 +486,6 @@ } } - if (event.data.type === 'action:submit') { - console.debug(event.data.text); - - if (prompt !== '') { - await tick(); - submitPrompt(prompt); - } - } - if (event.data.type === 'input:prompt:submit') { console.debug(event.data.text); diff --git a/src/lib/components/common/Collapsible.svelte b/src/lib/components/common/Collapsible.svelte index 83522d7006..49c6e3f1fd 100644 --- a/src/lib/components/common/Collapsible.svelte +++ b/src/lib/components/common/Collapsible.svelte @@ -89,54 +89,55 @@
- {#if title !== null} - - -
{ - if (!disabled) { - open = !open; - } - }} - > -
- {#if attributes?.done && attributes?.done !== 'true'} -
- -
- {/if} + {#if attributes?.type === 'tool_calls'} + {@const args = decode(attributes?.arguments)} + {@const result = decode(attributes?.result ?? '')} + {@const files = parseJSONString(decode(attributes?.files ?? ''))} + {@const embeds = parseJSONString(decode(attributes?.embeds ?? ''))} -
- {#if attributes?.type === 'reasoning'} - {#if attributes?.done === 'true' && attributes?.duration} - {#if attributes.duration < 1} - {$i18n.t('Thought for less than a second')} - {:else if attributes.duration < 60} - {$i18n.t('Thought for {{DURATION}} seconds', { - DURATION: attributes.duration - })} - {:else} - {$i18n.t('Thought for {{DURATION}}', { - DURATION: dayjs.duration(attributes.duration, 'seconds').humanize() - })} - {/if} - {:else} - {$i18n.t('Thinking...')} - {/if} - {:else if attributes?.type === 'code_interpreter'} - {#if attributes?.done === 'true'} - {$i18n.t('Analyzed')} - {:else} - {$i18n.t('Analyzing...')} - {/if} - {:else if attributes?.type === 'tool_calls'} + {#if embeds && Array.isArray(embeds) && embeds.length > 0} +
+
+
+ {attributes.name} +
+
+ + {#each embeds as embed, idx} +
+ +
+ {/each} +
+ {:else} +
{ + if (!disabled) { + open = !open; + } + }} + > +
+ {#if attributes?.done && attributes?.done !== 'true'} +
+ +
+ {/if} + +
{#if attributes?.done === 'true'} {/if} - {:else} - {title} - {/if} -
+
-
- {#if open} - - {:else} - - {/if} +
+ {#if open} + + {:else} + + {/if} +
-
- {:else} - - -
{ - e.stopPropagation(); - }} - on:pointerup={(e) => { - if (!disabled) { - open = !open; - } - }} - > -
-
- - {#if chevron} -
- {#if open} - + {#if !grow} + {#if open && !hide} +
+ {#if attributes?.type === 'tool_calls'} + {#if attributes?.done === 'true'} + \`\`\`json +> ${formatJSONString(args)} +> ${formatJSONString(result)} +> \`\`\``} + /> {:else} - + \`\`\`json +> ${formatJSONString(args)} +> \`\`\``} + /> {/if} -
- {/if} -
- - {#if grow} - {#if open && !hide} -
{ - e.stopPropagation(); - }} - > + {:else} -
+ {/if} +
+ {/if} + + {#if attributes?.done === 'true'} + {#if typeof files === 'object'} + {#each files ?? [] as file, idx} + {#if file.startsWith('data:image/')} + Image + {/if} + {/each} {/if} {/if} -
-
- {/if} + {/if} + {/if} + {:else} + {#if title !== null} + + +
{ + if (!disabled) { + open = !open; + } + }} + > +
+ {#if attributes?.done && attributes?.done !== 'true'} +
+ +
+ {/if} - {#if attributes?.type === 'tool_calls'} - {@const args = decode(attributes?.arguments)} - {@const result = decode(attributes?.result ?? '')} - {@const files = parseJSONString(decode(attributes?.files ?? ''))} - {@const embeds = parseJSONString(decode(attributes?.embeds ?? ''))} +
+ {#if attributes?.type === 'reasoning'} + {#if attributes?.done === 'true' && attributes?.duration} + {#if attributes.duration < 1} + {$i18n.t('Thought for less than a second')} + {:else if attributes.duration < 60} + {$i18n.t('Thought for {{DURATION}} seconds', { + DURATION: attributes.duration + })} + {:else} + {$i18n.t('Thought for {{DURATION}}', { + DURATION: dayjs.duration(attributes.duration, 'seconds').humanize() + })} + {/if} + {:else} + {$i18n.t('Thinking...')} + {/if} + {:else if attributes?.type === 'code_interpreter'} + {#if attributes?.done === 'true'} + {$i18n.t('Analyzed')} + {:else} + {$i18n.t('Analyzing...')} + {/if} + {:else} + {title} + {/if} +
- {#if embeds && Array.isArray(embeds) && embeds.length > 0} - {#each embeds as embed, idx} -
- +
+ {#if open} + + {:else} + + {/if} +
- {/each} +
+ {:else} + + +
{ + e.stopPropagation(); + }} + on:pointerup={(e) => { + if (!disabled) { + open = !open; + } + }} + > +
+
+ + + {#if chevron} +
+ {#if open} + + {:else} + + {/if} +
+ {/if} +
+ + {#if grow} + {#if open && !hide} +
{ + e.stopPropagation(); + }} + > + +
+ {/if} + {/if} +
+
{/if} {#if !grow} {#if open && !hide}
- {#if attributes?.type === 'tool_calls'} - {#if attributes?.done === 'true'} - \`\`\`json -> ${formatJSONString(args)} -> ${formatJSONString(result)} -> \`\`\``} - /> - {:else} - \`\`\`json -> ${formatJSONString(args)} -> \`\`\``} - /> - {/if} - {:else} - - {/if} +
{/if} - - {#if attributes?.done === 'true'} - {#if typeof files === 'object'} - {#each files ?? [] as file, idx} - {#if file.startsWith('data:image/')} - Image - {/if} - {/each} - {/if} - {/if} - {/if} - {:else if !grow} - {#if open && !hide} -
- -
{/if} {/if}
diff --git a/src/lib/components/common/FullHeightIframe.svelte b/src/lib/components/common/FullHeightIframe.svelte index ec9d49c5c3..7757e4d780 100644 --- a/src/lib/components/common/FullHeightIframe.svelte +++ b/src/lib/components/common/FullHeightIframe.svelte @@ -4,7 +4,8 @@ // Props export let src: string | null = null; // URL or raw HTML (auto-detected) export let title = 'Embedded Content'; - export let initialHeight = 400; // fallback height if we can't measure + export let initialHeight: number | null = null; // initial height in px, null = auto + export let allowScripts = true; export let allowForms = false; @@ -33,13 +34,14 @@ // Detect URL vs raw HTML and prep src/srcdoc $: isUrl = typeof src === 'string' && /^(https?:)?\/\//i.test(src); $: iframeSrc = isUrl ? (src as string) : null; - $: iframeDoc = !isUrl && src ? ensureAutosizer(src) : null; + $: iframeDoc = !isUrl ? src : null; // Try to measure same-origin content safely function resizeSameOrigin() { if (!iframe) return; try { const doc = iframe.contentDocument || iframe.contentWindow?.document; + console.log('iframe doc:', doc); if (!doc) return; const h = Math.max(doc.documentElement?.scrollHeight ?? 0, doc.body?.scrollHeight ?? 0); if (h > 0) iframe.style.height = h + 20 + 'px'; @@ -57,6 +59,11 @@ } } + // When the iframe loads, try same-origin resize (cross-origin will noop) + function onLoad() { + requestAnimationFrame(resizeSameOrigin); + } + // Ensure event listener bound only while component lives onMount(() => { window.addEventListener('message', onMessage); @@ -65,52 +72,6 @@ onDestroy(() => { window.removeEventListener('message', onMessage); }); - - // When the iframe loads, try same-origin resize (cross-origin will noop) - function onLoad() { - // schedule after layout - requestAnimationFrame(resizeSameOrigin); - } - - /** - * If user passes raw HTML, we inject a tiny autosizer that posts height. - * This helps both same-origin and "about:srcdoc" cases. - * (No effect if the caller already includes their own autosizer.) - */ - function ensureAutosizer(html: string): string { - const hasOurHook = /iframe:height/.test(html) || /postMessage\(.+height/i.test(html); - if (hasOurHook) return html; - - // This script uses ResizeObserver to post the document height - const autosizer = ` - {#if iframeDoc} @@ -118,12 +79,11 @@ bind:this={iframe} srcdoc={iframeDoc} {title} - class="w-full rounded-xl" - style={`height:${initialHeight}px;`} + class="w-full rounded-2xl" + style={`${initialHeight ? `height:${initialHeight}px;` : ''}`} width="100%" frameborder="0" {sandbox} - referrerpolicy={referrerPolicy} {allowFullscreen} on:load={onLoad} /> @@ -132,8 +92,8 @@ bind:this={iframe} src={iframeSrc} {title} - class="w-full rounded-xl" - style={`height:${initialHeight}px;`} + class="w-full rounded-2xl" + style={`${initialHeight ? `height:${initialHeight}px;` : ''}`} width="100%" frameborder="0" {sandbox}