@@ -386,6 +137,6 @@
cancelText="Keep editing"
variant="destructive"
icon={AlertTriangle}
- onConfirm={onCancelEdit}
+ onConfirm={editCtx.cancel}
onCancel={() => (showDiscardDialog = false)}
/>
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageMcpPrompt.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageMcpPrompt.svelte
new file mode 100644
index 00000000000..72ece2b8e22
--- /dev/null
+++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageMcpPrompt.svelte
@@ -0,0 +1,83 @@
+
+
+
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageMcpPromptContent.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageMcpPromptContent.svelte
new file mode 100644
index 00000000000..3d5dec3b6ac
--- /dev/null
+++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageMcpPromptContent.svelte
@@ -0,0 +1,197 @@
+
+
+
+
+
+
+
+ {#if serverFavicon}
+
{
+ (e.currentTarget as HTMLImageElement).style.display = 'none';
+ }}
+ />
+ {/if}
+
+
+
+ {serverDisplayName}
+
+
+
+
+
+
+ {#if showArgBadges}
+
+ {#each argumentEntries as [key, value] (key)}
+
+
+
+ (hoveredArgKey = key)}
+ onmouseleave={() => (hoveredArgKey = null)}
+ >
+ {key}
+
+
+
+
+ {value}
+
+
+ {/each}
+
+ {/if}
+
+
+ {#if loadError}
+
+
+ {loadError}
+
+
+ {:else if isLoading}
+
+
+
+ {:else if hasContent}
+
+
+
+
+
+ {#each contentParts as part, i (i)}{#if part.argKey} (hoveredArgKey = part.argKey)}
+ onmouseleave={() => (hoveredArgKey = null)}>{part.text}{:else}{part.text}{/if}{/each}
+
+
+ {/if}
+
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics.svelte
index b53e82aaf9c..4bde1567a1d 100644
--- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics.svelte
+++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageStatistics.svelte
@@ -1,21 +1,23 @@
@@ -97,9 +128,11 @@
onclick={() => (activeView = ChatMessageStatsView.READING)}
>
+
Reading
+
Reading (prompt processing)
@@ -119,9 +152,11 @@
disabled={isGenerationDisabled}
>
+
Generation
+
{isGenerationDisabled
@@ -130,6 +165,52 @@
+
+ {#if hasAgenticStats}
+
+
+
+
+
+
+ Tool calls
+
+
+
+ {#if !hideSummary}
+
+
+
+
+
+
+ Agentic summary
+
+
+ {/if}
+ {/if}
@@ -140,18 +221,62 @@
value="{predictedTokens?.toLocaleString()} tokens"
tooltipLabel="Generated tokens"
/>
+
+
+ {:else if activeView === ChatMessageStatsView.TOOLS && hasAgenticStats}
+
+
+
+
+
+ {:else if activeView === ChatMessageStatsView.SUMMARY && hasAgenticStats}
+
+
+
+
+
{:else if hasPromptStats}
+
+
void;
- onSaveEdit: () => void;
- onEditKeydown: (event: KeyboardEvent) => void;
- onEditedContentChange: (content: string) => void;
onCopy: () => void;
onEdit: () => void;
onDelete: () => void;
@@ -36,15 +33,9 @@
let {
class: className = '',
message,
- isEditing,
- editedContent,
siblingInfo = null,
showDeleteDialog,
deletionInfo,
- onCancelEdit,
- onSaveEdit,
- onEditKeydown,
- onEditedContentChange,
onCopy,
onEdit,
onDelete,
@@ -54,10 +45,25 @@
textareaElement = $bindable()
}: Props = $props();
+ const editCtx = getMessageEditContext();
+
+ function handleEditKeydown(event: KeyboardEvent) {
+ if (event.key === KeyboardKey.ENTER && !event.shiftKey && !isIMEComposing(event)) {
+ event.preventDefault();
+
+ editCtx.save();
+ } else if (event.key === KeyboardKey.ESCAPE) {
+ event.preventDefault();
+
+ editCtx.cancel();
+ }
+ }
+
let isMultiline = $state(false);
let messageElement: HTMLElement | undefined = $state();
let isExpanded = $state(false);
let contentHeight = $state(0);
+
const MAX_HEIGHT = 200; // pixels
const currentConfig = config();
@@ -97,25 +103,32 @@
class="group flex flex-col items-end gap-3 md:gap-2 {className}"
role="group"
>
- {#if isEditing}
+ {#if editCtx.isEditing}
-
@@ -131,12 +144,12 @@
type="button"
>
{#if currentConfig.renderUserContentAsMarkdown}
-
+
{:else}
+
@@ -208,7 +225,7 @@
{onShowDeleteDialogChange}
{siblingInfo}
{showDeleteDialog}
- role="user"
+ role={MessageRole.USER}
/>
{/if}
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageThinkingBlock.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageThinkingBlock.svelte
deleted file mode 100644
index 9245ad51533..00000000000
--- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageThinkingBlock.svelte
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
- {isStreaming ? 'Reasoning...' : 'Reasoning'}
-
-
-
-
-
-
- Toggle reasoning content
-
-
-
-
-
-
-
- {reasoningContent ?? ''}
-
-
-
-
-
-
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageUser.svelte b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageUser.svelte
index 041c6bd2513..05a02e27281 100644
--- a/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageUser.svelte
+++ b/tools/server/webui/src/lib/components/app/chat/ChatMessages/ChatMessageUser.svelte
@@ -1,67 +1,48 @@
-
- {#each displayMessages as { message, siblingInfo } (message.id)}
+
+ {#each displayMessages as { message, isLastAssistantMessage, siblingInfo } (message.id)}
{/each}
diff --git a/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte b/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte
index 3d432e26bc7..ceecf03e54d 100644
--- a/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte
+++ b/tools/server/webui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte
@@ -1,7 +1,7 @@
@@ -406,11 +363,8 @@
class="mb-16 md:mb-24"
messages={activeMessages()}
onUserAction={() => {
- if (!disableAutoScroll) {
- userScrolledUp = false;
- autoScrollEnabled = true;
- scrollChatToBottom();
- }
+ autoScroll.enable();
+ autoScroll.scrollToBottom();
}}
/>
@@ -444,7 +398,7 @@
{/if}