Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/screenshots/assistant.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshots/chat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshots/mobile-chat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 12 additions & 6 deletions server/scripts/mock-rpc-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,12 @@ function streamCannedTurn() {
];
let index = 0;
const tick = () => {
if (index >= steps.length) return;
if (index >= steps.length) {
// Turn finished: drop the timer so get_state reports isStreaming
// false again — the UI keys its queue/stop mode off that flag.
streamTimer = undefined;
return;
}
steps[index]!();
index++;
streamTimer = setTimeout(tick, 250);
Expand Down Expand Up @@ -253,7 +258,9 @@ function handleFrame(frame: unknown) {
pendingPrompt = { id, command: type };
write({ id, type: "response", command: type, success: true });
write({ type: "turn_start" });
// Echo the prompt back as a user message, images included.
// Echo the prompt back as a user message, images included. Real omp
// finalizes the entry with message_end; without it the UI keeps the
// message in streaming state forever ("agent is working…" never clears).
const images = Array.isArray(obj.images) ? (obj.images as unknown[]) : [];
const content =
images.length > 0
Expand All @@ -262,10 +269,9 @@ function handleFrame(frame: unknown) {
...images.map(image => ({ type: "image", ...(image as Record<string, unknown>) })),
]
: String(obj.message ?? "");
write({
type: "message_start",
message: { role: "user", content, timestamp: Date.now() },
});
const echo = { role: "user", content, timestamp: Date.now() };
write({ type: "message_start", message: echo });
write({ type: "message_end", message: echo });
streamCannedTurn();
return;
}
Expand Down
13 changes: 8 additions & 5 deletions web/src/components/chat/Composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -506,11 +506,14 @@ export function Composer({ store, snapshot, assistant = false }: ComposerProps)
</div>
)}

<div className="ml-auto flex shrink-0 items-center gap-2">
{/* min-w-0 (not shrink-0): the model chip is the elastic item —
it truncates so the row still fits narrow screens instead of
pushing the primary button out of the composer. */}
<div className="ml-auto flex min-w-0 items-center gap-2">
{/* Queued message count */}
{queuedCount > 0 ? (
<span
className="flex h-7 items-center gap-1 rounded-md border border-border bg-surface-2 px-2 font-mono text-[10px] text-fg-2"
className="flex h-7 shrink-0 items-center gap-1 rounded-md border border-border bg-surface-2 px-2 font-mono text-[10px] text-fg-2"
title={`${queuedCount} message(s) queued`}
>
<ListPlus className="h-3 w-3" />
Expand All @@ -527,10 +530,10 @@ export function Composer({ store, snapshot, assistant = false }: ComposerProps)
type="button"
onClick={() => toggleMenu("model")}
title="Switch model"
className="flex h-7 items-center gap-1.5 rounded-md border border-border bg-surface-2 px-2.5 font-mono text-[10.5px] text-fg-1 hover:border-border-strong"
className="flex h-7 min-w-0 items-center gap-1.5 rounded-md border border-border bg-surface-2 px-2.5 font-mono text-[10.5px] text-fg-1 hover:border-border-strong"
>
<Sparkles className="h-3 w-3 shrink-0 text-cat-meta" />
<span className="max-w-[7.5rem] truncate sm:max-w-44">
<span className="min-w-0 max-w-[7.5rem] truncate sm:max-w-44">
{modelLabel ??
(currentModel ? `${currentModel.provider}/${currentModel.id}` : "model")}
</span>
Expand Down Expand Up @@ -562,7 +565,7 @@ export function Composer({ store, snapshot, assistant = false }: ComposerProps)
</div>

{/* Thinking level */}
<div className="relative">
<div className="relative shrink-0">
<button
type="button"
onClick={() => toggleMenu("think")}
Expand Down
10 changes: 7 additions & 3 deletions web/src/pages/SessionDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,13 @@ export function SessionDetailPage() {
) : null}

<div className="ml-auto flex items-center gap-2">
<ThemeToggle />
<SettingsLink />
<LogoutButton />
{/* The mobile top bar already carries these three — a second copy
here would show them twice on narrow screens. */}
<div className="hidden items-center gap-2 lg:flex">
<ThemeToggle />
<SettingsLink />
<LogoutButton />
</div>
<button
type="button"
onClick={() => setShowSide(s => !s)}
Expand Down
Loading