This commit is contained in:
Timothy Jaeryang Baek
2026-07-23 04:45:27 -04:00
parent bb12b1a18b
commit d0f7da4f45
11 changed files with 343 additions and 110 deletions
+2
View File
@@ -43,6 +43,7 @@
export let forkHandler: Function | null = null;
export let readOnly = false;
export let preview = false;
export let editCodeBlock = true;
export let topPadding = false;
@@ -543,6 +544,7 @@
{forkHandler}
{triggerScroll}
{readOnly}
{preview}
{editCodeBlock}
{topPadding}
{onInsertToNote}
@@ -307,7 +307,7 @@
{#if extracted.detailsContent}
<!-- Render structural blocks (tool calls, reasoning, etc.) through Markdown -->
<div class="markdown-prose">
<Markdown {id} content={extracted.detailsContent} {done} />
<Markdown {id} content={extracted.detailsContent} {preview} {done} />
</div>
{/if}
{#if extracted.plainContent}
@@ -31,6 +31,7 @@
export let messageDone = true;
export let allowEmbeds = true;
export let preview = false;
let open = $settings?.expandDetails ?? false;
@@ -112,7 +113,9 @@
<div {id} class="w-full">
<!-- svelte-ignore a11y-no-static-element-interactions -->
<button
class="w-fit py-0.5 text-left text-[0.9375rem] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition cursor-pointer"
class="w-fit py-0.5 text-left {preview
? 'text-xs'
: 'text-[0.9375rem]'} text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition cursor-pointer"
aria-label={$i18n.t('Toggle details')}
aria-expanded={open}
on:click={() => {
@@ -97,6 +97,10 @@
.trim();
};
$: detailButtonClassName = `w-fit py-0.5 ${
preview ? 'text-xs' : 'text-[0.9375rem]'
} text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition`;
$: displayTokens = getDisplayTokens(tokens);
$: singlePlainBlock =
displayTokens.length === 1 &&
@@ -278,6 +282,7 @@
id={`${id}-${tokenIdx}`}
tokens={token.tokens}
{done}
{preview}
{editCodeBlock}
{onTaskClick}
{sourceIds}
@@ -313,6 +318,7 @@
tokens={item.tokens}
top={token.loose}
{done}
{preview}
{editCodeBlock}
{onTaskClick}
{sourceIds}
@@ -348,6 +354,7 @@
tokens={item.tokens}
top={token.loose}
{done}
{preview}
{editCodeBlock}
{onTaskClick}
{sourceIds}
@@ -360,6 +367,7 @@
tokens={item.tokens}
top={token.loose}
{done}
{preview}
{editCodeBlock}
{onTaskClick}
{sourceIds}
@@ -375,6 +383,7 @@
id={`${id}-${tokenIdx}-detail-group`}
tokens={token.items}
messageDone={done}
{preview}
{allowEmbeds}
>
<div slot="content">
@@ -389,7 +398,7 @@
grouped={true}
open={$settings?.expandDetails ?? false}
className="w-full"
buttonClassName="w-fit py-0.5 text-[0.9375rem] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
buttonClassName={detailButtonClassName}
/>
{:else if textContent.length > 0}
<Collapsible
@@ -398,7 +407,7 @@
attributes={detailToken?.attributes}
messageDone={done}
className="w-full"
buttonClassName="w-fit py-0.5 text-[0.9375rem] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
buttonClassName={detailButtonClassName}
dir="auto"
>
<div class="mb-1.5" slot="content">
@@ -407,6 +416,7 @@
tokens={marked.lexer(decode(detailToken.text))}
attributes={detailToken?.attributes}
{done}
{preview}
{editCodeBlock}
{onTaskClick}
{sourceIds}
@@ -422,7 +432,7 @@
attributes={detailToken?.attributes}
messageDone={done}
className="w-full"
buttonClassName="w-fit py-0.5 text-[0.9375rem] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
buttonClassName={detailButtonClassName}
dir="auto"
/>
{/if}
@@ -440,6 +450,7 @@
resultContent={getDetailTextContent(token)}
open={$settings?.expandDetails ?? false}
className="w-full space-y-2"
buttonClassName={detailButtonClassName}
/>
{:else if textContent.length > 0}
<Collapsible
@@ -448,6 +459,7 @@
attributes={token?.attributes}
messageDone={done}
className="w-full space-y-2"
buttonClassName={detailButtonClassName}
dir="auto"
>
<div class=" mb-1.5" slot="content">
@@ -456,6 +468,7 @@
tokens={marked.lexer(decode(token.text))}
attributes={token?.attributes}
{done}
{preview}
{editCodeBlock}
{onTaskClick}
{sourceIds}
@@ -471,6 +484,7 @@
attributes={token?.attributes}
messageDone={done}
className="w-full space-y-2"
buttonClassName={detailButtonClassName}
dir="auto"
/>
{/if}
@@ -42,6 +42,7 @@
export let forkHandler: Function | null = null;
export let triggerScroll;
export let readOnly = false;
export let preview = false;
export let editCodeBlock = true;
export let topPadding = false;
export let onInsertToNote: ((content: string) => void) | null = null;
@@ -72,6 +73,7 @@
{editMessage}
{deleteMessage}
{readOnly}
{preview}
{editCodeBlock}
{topPadding}
{onInsertToNote}
@@ -100,6 +102,7 @@
{addMessages}
{forkHandler}
{readOnly}
{preview}
{editCodeBlock}
{topPadding}
/>
@@ -126,6 +129,7 @@
{addMessages}
{forkHandler}
{readOnly}
{preview}
{editCodeBlock}
{topPadding}
{onInsertToNote}
@@ -28,6 +28,7 @@
export let isLastMessage;
export let readOnly = false;
export let preview = false;
export let editCodeBlock = true;
export let setInputText: Function = () => {};
@@ -251,7 +252,11 @@
>
{#if $settings?.displayMultiModelResponsesInTabs ?? false}
<div class="w-full">
<div class=" flex w-full mb-4.5 border-b border-gray-200 dark:border-gray-850">
<div
class=" flex w-full mb-4.5 border-b border-gray-200 dark:border-gray-850 {preview
? 'hidden'
: ''}"
>
<div
class="flex gap-2 scrollbar-none overflow-x-auto w-fit text-center font-normal bg-transparent pt-1 text-sm"
on:wheel|preventDefault={(e) => {
@@ -322,6 +327,7 @@
{addMessages}
{forkHandler}
{readOnly}
{preview}
{topPadding}
{onInsertToNote}
/>
@@ -338,14 +344,17 @@
groupedMessageIds[modelIdx].messageIds[groupedMessageIdsIdx[modelIdx]]}
<div
class=" snap-center w-full max-w-full m-1 border {history.messages[messageId]
?.modelIdx == modelIdx
? `bg-gray-50 dark:bg-gray-850 border-gray-100 dark:border-gray-800 border-2 ${
$mobile ? 'min-w-full' : 'min-w-80'
}`
: `border-gray-100/30 dark:border-gray-850/30 border-dashed ${
$mobile ? 'min-w-full' : 'min-w-80'
}`} transition-all p-5 rounded-2xl"
class="snap-center w-full max-w-full transition-all {preview
? ''
: `m-1 border p-5 rounded-2xl ${
history.messages[messageId]?.modelIdx == modelIdx
? `bg-gray-50 dark:bg-gray-850 border-gray-100 dark:border-gray-800 border-2 ${
$mobile ? 'min-w-full' : 'min-w-80'
}`
: `border-gray-100/30 dark:border-gray-850/30 border-dashed ${
$mobile ? 'min-w-full' : 'min-w-80'
}`
}`}"
on:click={async () => {
onGroupClick(_messageId, modelIdx);
}}
@@ -380,6 +389,7 @@
{addMessages}
{forkHandler}
{readOnly}
{preview}
{editCodeBlock}
{topPadding}
{onInsertToNote}
@@ -392,7 +402,7 @@
{/if}
</div>
{#if !readOnly}
{#if !preview && !readOnly}
{#if !Object.keys(groupedMessageIds).find((modelIdx) => {
const { messageIds } = groupedMessageIds[modelIdx];
const _messageId = messageIds[groupedMessageIdsIdx[modelIdx]];
@@ -165,6 +165,7 @@
export let isLastMessage = true;
export let readOnly = false;
export let preview = false;
export let editCodeBlock = true;
export let topPadding = false;
export let onInsertToNote: ((content: string) => void) | null = null;
@@ -665,13 +666,15 @@
</div>
<div class="flex-auto w-0 pl-1 relative">
<Name>
<Tooltip content={model?.name ?? message.model} placement="top-start">
<span id="response-message-model-name" class="line-clamp-1 text-black dark:text-white">
{model?.name ?? message.model}
</span>
</Tooltip>
</Name>
{#if !preview}
<Name>
<Tooltip content={model?.name ?? message.model} placement="top-start">
<span id="response-message-model-name" class="line-clamp-1 text-black dark:text-white">
{model?.name ?? message.model}
</span>
</Tooltip>
</Name>
{/if}
<div>
<div class="chat-{message.role} w-full min-w-full">
@@ -822,7 +825,7 @@
!readOnly &&
($settings?.showFloatingActionButtons ?? true)}
save={!readOnly}
preview={!readOnly}
preview={preview || !readOnly}
{editCodeBlock}
{topPadding}
done={($settings?.chatFadeStreamingText ?? true)
@@ -891,7 +894,22 @@
</div>
</div>
{#if !edit}
{#if preview && message.timestamp}
<div class="mt-0.5 flex justify-start whitespace-nowrap text-gray-600 dark:text-gray-500">
<Tooltip
className="flex self-center"
content={formatMessageTimestampFull(message.timestamp * 1000)}
placement="bottom"
>
<time
datetime={new Date(message.timestamp * 1000).toISOString()}
class="ml-1 shrink-0 whitespace-nowrap text-[0.6875rem] tabular-nums text-gray-400 dark:text-gray-600 select-none"
>
{formatMessageTimestamp(message.timestamp * 1000)}
</time>
</Tooltip>
</div>
{:else if !edit}
<div
bind:this={buttonsContainerElement}
class="flex items-center justify-start overflow-x-auto whitespace-nowrap buttons text-gray-600 dark:text-gray-500 mt-0.5 [&>*]:shrink-0"
@@ -32,6 +32,10 @@
const getDetailTitle = (detailToken: OutputDetailToken): any => detailToken.summary;
const getDetailAttributes = (detailToken: OutputDetailToken): any => detailToken.attributes;
$: detailButtonClassName = `w-fit py-0.5 ${
preview ? 'text-xs' : 'text-[0.9375rem]'
} text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition`;
$: displayItems = buildOutputDisplayItems(output) as OutputDisplayItem[];
</script>
@@ -64,6 +68,7 @@
id={`${id}-${displayItem.id}`}
tokens={displayItem.tokens}
messageDone={done}
{preview}
>
<div slot="content">
{#each displayItem.tokens as detailToken, detailIndex}
@@ -75,7 +80,7 @@
grouped={true}
open={$settings?.expandDetails ?? false}
className="w-full"
buttonClassName="w-fit py-0.5 text-[0.9375rem] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
buttonClassName={detailButtonClassName}
/>
{:else if detailToken.text?.length > 0}
<Collapsible
@@ -84,7 +89,7 @@
attributes={getDetailAttributes(detailToken)}
messageDone={done}
className="w-full"
buttonClassName="w-fit py-0.5 text-[0.9375rem] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
buttonClassName={detailButtonClassName}
>
<div class="mb-1.5" slot="content">
<div class="markdown-prose">
@@ -92,6 +97,7 @@
id={`${id}-${displayItem.id}-${detailIndex}-detail`}
content={detailToken.text}
{done}
{preview}
{editCodeBlock}
/>
</div>
@@ -105,7 +111,7 @@
attributes={getDetailAttributes(detailToken)}
messageDone={done}
className="w-full"
buttonClassName="w-fit py-0.5 text-[0.9375rem] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
buttonClassName={detailButtonClassName}
/>
{/if}
{/each}
@@ -120,6 +126,7 @@
resultContent={detailToken.text}
open={$settings?.expandDetails ?? false}
className="w-full space-y-2"
buttonClassName={detailButtonClassName}
/>
{:else if detailToken.text?.length > 0}
<Collapsible
@@ -128,6 +135,7 @@
attributes={getDetailAttributes(detailToken)}
messageDone={done}
className="w-full space-y-2"
buttonClassName={detailButtonClassName}
>
<div class="mb-1.5" slot="content">
<div class="markdown-prose">
@@ -135,6 +143,7 @@
id={`${id}-${displayItem.id}-detail`}
content={detailToken.text}
{done}
{preview}
{editCodeBlock}
/>
</div>
@@ -148,6 +157,7 @@
attributes={getDetailAttributes(detailToken)}
messageDone={done}
className="w-full space-y-2"
buttonClassName={detailButtonClassName}
/>
{/if}
{/if}
@@ -39,6 +39,7 @@
export let isFirstMessage: boolean;
export let readOnly: boolean;
export let preview = false;
export let editCodeBlock = true;
export let topPadding = false;
@@ -415,7 +416,8 @@
>
<time
datetime={new Date(message.timestamp * 1000).toISOString()}
class="invisible group-hover:visible {($settings?.chatBubble ?? true)
class="{preview ? '' : 'invisible group-hover:visible'} {($settings?.chatBubble ??
true)
? 'mr-1'
: 'ml-1 shrink-0 whitespace-nowrap'} text-[0.6875rem] tabular-nums text-gray-400 dark:text-gray-600 select-none"
>
@@ -424,7 +426,7 @@
</Tooltip>
{/if}
{#if !($settings?.chatBubble ?? true)}
{#if !preview && !($settings?.chatBubble ?? true)}
{#if siblings.length > 1}
<div class="flex self-center" dir="ltr">
<button
@@ -518,7 +520,7 @@
</div>
{/if}
{/if}
{#if !readOnly}
{#if !preview && !readOnly}
<Tooltip content={$i18n.t('Edit')} placement="bottom">
<button
class="{($settings?.highContrastMode ?? false)
@@ -546,7 +548,7 @@
</Tooltip>
{/if}
{#if message?.content}
{#if !preview && message?.content}
<Tooltip content={$i18n.t('Copy')} placement="bottom">
<button
class="{($settings?.highContrastMode ?? false)
@@ -575,7 +577,7 @@
{/if}
{#if $_user?.role === 'admin' || ($_user?.permissions?.chat?.delete_message ?? false)}
{#if !readOnly && (!isFirstMessage || siblings.length > 1)}
{#if !preview && !readOnly && (!isFirstMessage || siblings.length > 1)}
<Tooltip content={$i18n.t('Delete')} placement="bottom">
<button
class="{($settings?.highContrastMode ?? false)
@@ -608,7 +610,7 @@
{/if}
{/if}
{#if $settings?.chatBubble ?? true}
{#if !preview && ($settings?.chatBubble ?? true)}
{#if siblings.length > 1}
<div class="flex self-center" dir="ltr">
<button