diff --git a/ui/src/ui/views/chat.ts b/ui/src/ui/views/chat.ts
index 0ca7ca671..bec69a724 100644
--- a/ui/src/ui/views/chat.ts
+++ b/ui/src/ui/views/chat.ts
@@ -1,10 +1,9 @@
import { html, nothing } from "lit";
import { repeat } from "lit/directives/repeat.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
-
-import type { SessionsListResult } from "../types";
import { toSanitizedMarkdownHtml } from "../markdown";
-import { resolveToolDisplay, formatToolDetail } from "../tool-display";
+import { formatToolDetail, resolveToolDisplay } from "../tool-display";
+import type { SessionsListResult } from "../types";
export type ChatProps = {
sessionKey: string;
@@ -34,7 +33,7 @@ export function renderChat(props: ChatProps) {
const canCompose = props.connected && !props.sending;
const sessionOptions = resolveSessionOptions(props.sessionKey, props.sessions);
const composePlaceholder = props.connected
- ? "Message (⌘↩ to send)"
+ ? "Message (Shift+↩ for line breaks)"
: "Connect to the gateway to start chatting…";
return html`
@@ -53,7 +52,7 @@ export function renderChat(props: ChatProps) {
(entry) =>
html``,
+ `
)}
@@ -70,33 +69,41 @@ export function renderChat(props: ChatProps) {
- ${props.disabledReason
- ? html`
+ ${
+ props.disabledReason
+ ? html`
${props.disabledReason}
`
- : nothing}
+ : nothing
+ }
- ${props.error
- ? html`
${props.error}
`
- : nothing}
+ ${
+ props.error
+ ? html`
${props.error}
`
+ : nothing
+ }
${props.loading ? html`
Loading chat…
` : nothing}
- ${repeat(buildChatItems(props), (item) => item.key, (item) => {
- if (item.kind === "reading-indicator") return renderReadingIndicator();
- if (item.kind === "stream") {
- return renderMessage(
- {
- role: "assistant",
- content: [{ type: "text", text: item.text }],
- timestamp: item.startedAt,
- },
- props,
- { streaming: true },
- );
+ ${repeat(
+ buildChatItems(props),
+ (item) => item.key,
+ (item) => {
+ if (item.kind === "reading-indicator") return renderReadingIndicator();
+ if (item.kind === "stream") {
+ return renderMessage(
+ {
+ role: "assistant",
+ content: [{ type: "text", text: item.text }],
+ timestamp: item.startedAt,
+ },
+ props,
+ { streaming: true }
+ );
+ }
+ return renderMessage(item.message, props);
}
- return renderMessage(item.message, props);
- })}
+ )}
@@ -107,12 +114,11 @@ export function renderChat(props: ChatProps) {
?disabled=${!props.connected}
@keydown=${(e: KeyboardEvent) => {
if (e.key !== "Enter") return;
- if (!e.metaKey && !e.ctrlKey) return;
+ if (e.shiftKey) return; // Allow Shift+Enter for line breaks
e.preventDefault();
if (canCompose) props.onSend();
}}
- @input=${(e: Event) =>
- props.onDraftChange((e.target as HTMLTextAreaElement).value)}
+ @input=${(e: Event) => props.onDraftChange((e.target as HTMLTextAreaElement).value)}
placeholder=${composePlaceholder}
>
@@ -231,16 +237,11 @@ type SessionOption = {
displayName?: string;
};
-function resolveSessionOptions(
- currentKey: string,
- sessions: SessionsListResult | null,
-) {
+function resolveSessionOptions(currentKey: string, sessions: SessionsListResult | null) {
const now = Date.now();
const cutoff = now - 24 * 60 * 60 * 1000;
- const entries = Array.isArray(sessions?.sessions) ? sessions?.sessions ?? [] : [];
- const sorted = [...entries].sort(
- (a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0),
- );
+ const entries = Array.isArray(sessions?.sessions) ? (sessions?.sessions ?? []) : [];
+ const sorted = [...entries].sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
const recent: SessionOption[] = [];
const seen = new Set
();
for (const entry of sorted) {
@@ -292,7 +293,7 @@ function renderReadingIndicator() {
function renderMessage(
message: unknown,
props?: Pick,
- opts?: { streaming?: boolean },
+ opts?: { streaming?: boolean }
) {
const m = message as Record;
const role = typeof m.role === "string" ? m.role : "unknown";
@@ -314,7 +315,7 @@ function renderMessage(
const markdown =
display?.kind === "json"
? ["```json", display.value, "```"].join("\n")
- : display?.value ?? null;
+ : (display?.value ?? null);
const timestamp =
typeof m.timestamp === "number" ? new Date(m.timestamp).toLocaleTimeString() : "";
@@ -330,9 +331,11 @@ function renderMessage(
- ${markdown
- ? html`
${unsafeHTML(toSanitizedMarkdownHtml(markdown))}
`
- : nothing}
+ ${
+ markdown
+ ? html`
${unsafeHTML(toSanitizedMarkdownHtml(markdown))}
`
+ : nothing
+ }
${toolCards.map((card, index) =>
renderToolCard(card, {
id: `${toolCardBase}:${index}`,
@@ -340,7 +343,7 @@ function renderMessage(
? props.isToolOutputExpanded(`${toolCardBase}:${index}`)
: false,
onToggle: props?.onToolOutputToggle,
- }),
+ })
)}
@@ -421,7 +424,7 @@ function renderToolCard(
id: string;
expanded: boolean;
onToggle?: (id: string, expanded: boolean) => void;
- },
+ }
) {
const display = resolveToolDisplay({ name: card.name, args: card.args });
const detail = formatToolDetail(display);
@@ -431,11 +434,10 @@ function renderToolCard(
return html`