Merge feat/ui-polish: three-phase session model, plan graph, layout restructure
chat.tsx woven by hand: UI's session-surface layout (transcript column + right sidebar as full-height siblings, h-10 header with PhaseChip) wrapped in sub-agents' JobsContext.Provider; the message map carries both the steer ternary (steers bypass the Message wrapper — the model-facing glue text never renders) and UI's PhaseDivider emission (steers are verified phase-boundary no-ops). Right sidebar = sub-agents' fleet panel column (SubAgentsPanel above TreePanel, xl-only) carried onto UI's skeleton. contracts.ts auto-merged (ChatSummary.phase/pendingQuestion + ChatListItem, additive above the async-protocol section). Smoke-verified on the merged tree: steer renders bare as its artifact badge with no learner bubble, no rendered glue text, and no spurious phase divider at the steer boundary.
This commit is contained in:
+11
-2
@@ -1,7 +1,16 @@
|
||||
import { createChat, listChats, titleFromPrompt } from "@/src/server";
|
||||
import { createChat, isTurnRunning, listChats, listJobs, titleFromPrompt } from "@/src/server";
|
||||
import type { ChatListItem } from "@/src/core";
|
||||
|
||||
/** The session picker's list: record-derived summaries (phase, pending
|
||||
* question) + live liveness — a running turn OR a background job marks
|
||||
* the session busy (contracts.ts ChatListItem; the store can't see
|
||||
* liveness, the route is the merge point). */
|
||||
export async function GET() {
|
||||
return Response.json({ chats: listChats() });
|
||||
const chats: ChatListItem[] = listChats().map((c) => ({
|
||||
...c,
|
||||
busy: isTurnRunning(c.id) || listJobs(c.id).some((j) => j.status === "running"),
|
||||
}));
|
||||
return Response.json({ chats });
|
||||
}
|
||||
|
||||
export async function POST(req: Request) {
|
||||
|
||||
@@ -157,3 +157,55 @@ body {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Session status — phase colors + status dots
|
||||
* (picker strips, header chip, transcript dividers: one language)
|
||||
* ============================================================ */
|
||||
|
||||
/* Phase colors: blue = probing, violet = planning, green = teaching.
|
||||
Tuned per theme (light needs darker, dark lighter). These color the
|
||||
session dots, header chip and transcript dividers — one language. */
|
||||
:root {
|
||||
--phase-probe: oklch(0.55 0.17 253);
|
||||
--phase-plan: oklch(0.54 0.19 300);
|
||||
--phase-teach: oklch(0.55 0.14 160);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--phase-probe: oklch(0.72 0.13 250);
|
||||
--phase-plan: oklch(0.72 0.14 300);
|
||||
--phase-teach: oklch(0.74 0.12 160);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-phase-probe: var(--phase-probe);
|
||||
--color-phase-plan: var(--phase-plan);
|
||||
--color-phase-teach: var(--phase-teach);
|
||||
}
|
||||
|
||||
/* Session dot motion (picker): the ONLY animated state is "teacher is
|
||||
working" — a quiet phase-colored breathing. Pending questions are the
|
||||
dominant resting state and stay perfectly still (solid dot). */
|
||||
@keyframes lh-dot-breathe {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.35;
|
||||
transform: scale(0.75);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.lh-dot-breathe {
|
||||
animation: lh-dot-breathe 1.4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.lh-dot-breathe {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
||||
import "katex/dist/katex.min.css";
|
||||
import "./globals.css";
|
||||
import { AppShell, SidebarProvider } from "@/components/sidebar";
|
||||
import { TreePanelModeProvider } from "@/components/tree-panel";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Learning Harness",
|
||||
@@ -13,7 +14,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
<html lang="en" className="dark">
|
||||
<body className="min-h-dvh antialiased">
|
||||
<SidebarProvider>
|
||||
<AppShell>{children}</AppShell>
|
||||
<TreePanelModeProvider>
|
||||
<AppShell>{children}</AppShell>
|
||||
</TreePanelModeProvider>
|
||||
</SidebarProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user