fix(frontend): refine activity layout and input state

This commit is contained in:
User
2026-07-14 10:32:11 +02:00
parent 6dbf93fff9
commit 3633a5ebd9
7 changed files with 87 additions and 7 deletions
+18 -4
View File
@@ -33,6 +33,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
export function AppShell() {
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
const [creatingSession, setCreatingSession] = useState(false);
const [awaitingQuestion, setAwaitingQuestion] = useState(false);
const { data: sessions = [] } = useQuery<SessionSummary[]>({
queryKey: ["sessions"], queryFn: listSessions, refetchInterval: 10_000,
});
@@ -104,6 +105,7 @@ export function AppShell() {
// working spinner shows straight away; the backend calls run after.
setPanelSession(null);
resetSession();
setAwaitingQuestion(false);
// Optimistic: the resume POST is about to hand the ball to the harness.
setAgentActive(true);
setActiveSessionId(id);
@@ -231,6 +233,7 @@ export function AppShell() {
// explicit “Stop & save” action is allowed to call /close.
resetSession();
setActiveSessionId(null);
setAwaitingQuestion(false);
}
// The final workflow turn ends with the session already finalized on disk:
// refetch now instead of waiting for the 10s poll, so the completed state
@@ -241,6 +244,7 @@ export function AppShell() {
function startNewSession() {
resetSession();
setAwaitingQuestion(true);
setCreatingSession(false);
setActiveSessionId(null);
// Best effort only: session creation keeps the authoritative readiness gate.
@@ -250,6 +254,7 @@ export function AppShell() {
}
function beginSessionCreation() {
setAwaitingQuestion(false);
setCreatingSession(true);
}
@@ -258,6 +263,7 @@ export function AppShell() {
// while useSessionStream opens the durable session's SSE channel.
setActiveSessionId(id);
setCreatingSession(false);
setAwaitingQuestion(false);
refresh();
}
@@ -274,11 +280,16 @@ export function AppShell() {
} finally {
resetSession();
setActiveSessionId(null);
setAwaitingQuestion(false);
}
}
return (
<div className="flex h-screen bg-background text-foreground">
<div
data-testid="app-shell"
data-activity-layout={showActivity ? "open" : "closed"}
className="flex h-screen bg-background text-foreground"
>
{panelSession && (
<SessionDocumentsPanel
session={panelSession}
@@ -289,7 +300,7 @@ export function AppShell() {
{showActivity && <ModelActivityPanel onClose={() => setShowActivity(false)} />}
{/* Conversation column */}
<div className="flex min-w-0 flex-1 flex-col">
<div className={["flex min-w-0 flex-col", showActivity ? "w-3/5 shrink-0" : "flex-1"].join(" ")}>
{activeSessionId && (
<div className="relative shrink-0 border-b border-border/70 bg-background/80 px-6 py-2.5 backdrop-blur">
{/* Arrow toggle for the left Model-activity panel: → opens it, ← closes it. */}
@@ -352,6 +363,7 @@ export function AppShell() {
onSessionCreateFailed={failSessionCreation}
onStop={() => setStopConfirm(true)}
inputRef={composerRef}
awaitingInput={awaitingQuestion || pendingWidget?.widget === "freetext"}
/>
</div>
<ComposerFooter />
@@ -361,7 +373,8 @@ export function AppShell() {
</div>
{/* Right session rail — symmetric to the portal's left sidebar */}
<aside className="flex w-[15vw] shrink-0 flex-col border-l border-border bg-sidebar">
{!showActivity && (
<aside className="flex w-[15vw] shrink-0 flex-col border-l border-border bg-sidebar">
<div className="relative px-4 pb-3 pt-5 text-center">
<h1 className="font-heading text-xl font-semibold leading-none tracking-tight text-foreground">
Thoth<span className="text-primary">II</span>
@@ -500,7 +513,8 @@ export function AppShell() {
/>
)}
</div>
</aside>
</aside>
)}
<Toaster />