dashboard: expand to 5 peer tiers — add Research + Loops with placeholder canvases
Fifth commit of the Research + Loops arc. Lights up the tier rail (both the chip column and the top-bar crumbs) with two new peer surfaces: [VIZ] [RESEARCH] [LOOPS] [AGENT] [INFRA] The stubs (ResearchList / ResearchCanvas / LoopsList / LoopsCanvas) are self-contained placeholder components — each renders a header with a "0 topics" / "0 loops" chip and a "coming soon" body plus a canvas hero with the tier motif. Real functionality (topic cards, wizard, canvas timelines, publish gate UI) arrives in the next two commits, and slots in by replacing the stub files without touching Dashboard.tsx again. Dashboard.tsx changes: - `Tier` type widened to include `"research" | "loops"`. - railIcon record grew two SVG entries — book+lens for Research, orbit arrows for Loops. Matches the placeholder canvas hero. - TIER_TABS reordered as Viz → Research → Loops → Agent → Infra to match the mental grouping (conceptual work first, machinery second). - Top-bar crumbs mirror the rail order. - New isResearch / isLoops selectors used everywhere the existing tiers were tested; the Agents-header conditional now also skips for the new tiers so ResearchList and LoopsList can supply their own headers. - Sidebar swap short-circuits to ResearchList / LoopsList; canvas swap short-circuits to ResearchCanvas / LoopsCanvas.
This commit is contained in:
@@ -23,6 +23,10 @@ import { type FlowItem } from "./flow/TopologyFlow";
|
|||||||
import { WorldCanvas } from "../world/WorldCanvas";
|
import { WorldCanvas } from "../world/WorldCanvas";
|
||||||
import { ObservePanel } from "../observe/ObservePanel";
|
import { ObservePanel } from "../observe/ObservePanel";
|
||||||
import { StructureTree, orgNode, clawNode, type TreeItem } from "./StructureTree";
|
import { StructureTree, orgNode, clawNode, type TreeItem } from "./StructureTree";
|
||||||
|
import { ResearchList } from "./ResearchList";
|
||||||
|
import { ResearchCanvas } from "./ResearchCanvas";
|
||||||
|
import { LoopsList } from "./LoopsList";
|
||||||
|
import { LoopsCanvas } from "./LoopsCanvas";
|
||||||
import { UserMenu } from "./UserMenu";
|
import { UserMenu } from "./UserMenu";
|
||||||
import { ToolPanel, type ToolKey } from "./ToolPanel";
|
import { ToolPanel, type ToolKey } from "./ToolPanel";
|
||||||
import { InfraNav, FleetConsole, FleetStatusBar, FleetPill } from "./fleet/FleetConsole";
|
import { InfraNav, FleetConsole, FleetStatusBar, FleetPill } from "./fleet/FleetConsole";
|
||||||
@@ -43,7 +47,7 @@ import { DeviceSizeToggle } from "@/components/computer/DeviceSizeToggle";
|
|||||||
|
|
||||||
const COMPUTER_WIDTH: Record<DeviceSize, string> = { phone: "448px", tablet: "67%", full: "100%" };
|
const COMPUTER_WIDTH: Record<DeviceSize, string> = { phone: "448px", tablet: "67%", full: "100%" };
|
||||||
|
|
||||||
type Tier = "world" | "claw" | "infra";
|
type Tier = "world" | "research" | "loops" | "claw" | "infra";
|
||||||
const mono = "'JetBrains Mono', ui-monospace, monospace";
|
const mono = "'JetBrains Mono', ui-monospace, monospace";
|
||||||
|
|
||||||
// Gradient palette for structure nodes that don't carry their own (companies,
|
// Gradient palette for structure nodes that don't carry their own (companies,
|
||||||
@@ -79,11 +83,19 @@ const COMPANY_TEMPLATES: Template[] = [
|
|||||||
|
|
||||||
const railIcon: Record<Tier, React.ReactNode> = {
|
const railIcon: Record<Tier, React.ReactNode> = {
|
||||||
world: (<svg width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="3.5" r="1.9" fill="currentColor" /><circle cx="3.8" cy="11" r="1.9" fill="currentColor" /><circle cx="16.2" cy="11" r="1.9" fill="currentColor" /><circle cx="10" cy="16.5" r="1.9" fill="currentColor" /><path d="M10 3.5 L3.8 11 M10 3.5 L16.2 11 M3.8 11 L10 16.5 M16.2 11 L10 16.5" stroke="currentColor" strokeWidth="1.1" opacity=".5" /></svg>),
|
world: (<svg width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="3.5" r="1.9" fill="currentColor" /><circle cx="3.8" cy="11" r="1.9" fill="currentColor" /><circle cx="16.2" cy="11" r="1.9" fill="currentColor" /><circle cx="10" cy="16.5" r="1.9" fill="currentColor" /><path d="M10 3.5 L3.8 11 M10 3.5 L16.2 11 M3.8 11 L10 16.5 M16.2 11 L10 16.5" stroke="currentColor" strokeWidth="1.1" opacity=".5" /></svg>),
|
||||||
|
// Book with a magnifying-lens tucked into the lower-right — research.
|
||||||
|
research: (<svg width="20" height="20" viewBox="0 0 24 24"><rect x="4" y="3.5" width="12" height="14" rx="1.5" stroke="currentColor" strokeWidth="1.4" fill="none" /><path d="M4 15.5 H16" stroke="currentColor" strokeWidth="1.4" fill="none" /><circle cx="17.5" cy="17.5" r="3.2" stroke="currentColor" strokeWidth="1.6" fill="none" /><path d="M19.9 19.9 L22 22" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" /></svg>),
|
||||||
|
// Circular arrow ⟳ with an inner dot — loops.
|
||||||
|
loops: (<svg width="20" height="20" viewBox="0 0 24 24"><path d="M20 12 A8 8 0 1 1 12 4" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinecap="round" /><path d="M13 3 L20 4 L18.5 10" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinejoin="round" strokeLinecap="round" /><circle cx="12" cy="12" r="1.8" fill="currentColor" /></svg>),
|
||||||
claw: (<svg width="20" height="20" viewBox="0 0 20 20"><rect x="4" y="4" width="12" height="12" rx="3.5" stroke="currentColor" strokeWidth="1.4" fill="none" /><circle cx="10" cy="10" r="2.4" fill="currentColor" /></svg>),
|
claw: (<svg width="20" height="20" viewBox="0 0 20 20"><rect x="4" y="4" width="12" height="12" rx="3.5" stroke="currentColor" strokeWidth="1.4" fill="none" /><circle cx="10" cy="10" r="2.4" fill="currentColor" /></svg>),
|
||||||
infra: (<svg width="20" height="20" viewBox="0 0 20 20"><rect x="3.5" y="4" width="13" height="5" rx="1.4" stroke="currentColor" strokeWidth="1.4" fill="none" /><rect x="3.5" y="11" width="13" height="5" rx="1.4" stroke="currentColor" strokeWidth="1.4" fill="none" /><circle cx="6.4" cy="6.5" r="1" fill="currentColor" /><circle cx="6.4" cy="13.5" r="1" fill="currentColor" /></svg>),
|
infra: (<svg width="20" height="20" viewBox="0 0 20 20"><rect x="3.5" y="4" width="13" height="5" rx="1.4" stroke="currentColor" strokeWidth="1.4" fill="none" /><rect x="3.5" y="11" width="13" height="5" rx="1.4" stroke="currentColor" strokeWidth="1.4" fill="none" /><circle cx="6.4" cy="6.5" r="1" fill="currentColor" /><circle cx="6.4" cy="13.5" r="1" fill="currentColor" /></svg>),
|
||||||
};
|
};
|
||||||
const TIER_TABS: { key: Tier; label: string }[] = [
|
const TIER_TABS: { key: Tier; label: string }[] = [
|
||||||
{ key: "world", label: "VIZ" }, { key: "claw", label: "AGENT" }, { key: "infra", label: "INFRA" },
|
{ key: "world", label: "VIZ" },
|
||||||
|
{ key: "research", label: "RESEARCH" },
|
||||||
|
{ key: "loops", label: "LOOPS" },
|
||||||
|
{ key: "claw", label: "AGENT" },
|
||||||
|
{ key: "infra", label: "INFRA" },
|
||||||
];
|
];
|
||||||
const companyIcon = (size: number) => (
|
const companyIcon = (size: number) => (
|
||||||
<svg width={size} height={size} viewBox="0 0 20 20"><rect x="3.5" y="5" width="6" height="11" rx="1" stroke="currentColor" strokeWidth="1.4" fill="none" /><rect x="10.5" y="2.5" width="6" height="13.5" rx="1" stroke="currentColor" strokeWidth="1.4" fill="none" /></svg>
|
<svg width={size} height={size} viewBox="0 0 20 20"><rect x="3.5" y="5" width="6" height="11" rx="1" stroke="currentColor" strokeWidth="1.4" fill="none" /><rect x="10.5" y="2.5" width="6" height="13.5" rx="1" stroke="currentColor" strokeWidth="1.4" fill="none" /></svg>
|
||||||
@@ -447,7 +459,11 @@ export function Dashboard({ user, orgs, claws }: { user?: { display_name?: strin
|
|||||||
const [reapOpen, setReapOpen] = useState(false);
|
const [reapOpen, setReapOpen] = useState(false);
|
||||||
const [toolOpen, setToolOpen] = useState<ToolKey | null>(null);
|
const [toolOpen, setToolOpen] = useState<ToolKey | null>(null);
|
||||||
|
|
||||||
const isWorld = tier === "world", isClaw = tier === "claw", isInfra = tier === "infra";
|
const isWorld = tier === "world",
|
||||||
|
isResearch = tier === "research",
|
||||||
|
isLoops = tier === "loops",
|
||||||
|
isClaw = tier === "claw",
|
||||||
|
isInfra = tier === "infra";
|
||||||
|
|
||||||
const allAgents = orgs.flatMap((o) => o.companies.flatMap((c) => c.teams.flatMap((t) => t.agents)));
|
const allAgents = orgs.flatMap((o) => o.companies.flatMap((c) => c.teams.flatMap((t) => t.agents)));
|
||||||
// World: the full expandable org→company→team→agent forest. Agents page: a flat list.
|
// World: the full expandable org→company→team→agent forest. Agents page: a flat list.
|
||||||
@@ -518,6 +534,10 @@ export function Dashboard({ user, orgs, claws }: { user?: { display_name?: strin
|
|||||||
<div style={{ display: "flex", alignItems: "center", gap: 6, fontFamily: mono, fontSize: 12 }}>
|
<div style={{ display: "flex", alignItems: "center", gap: 6, fontFamily: mono, fontSize: 12 }}>
|
||||||
<span style={crumbStyle(isWorld)} onClick={() => setTier("world")}>Visualizations</span>
|
<span style={crumbStyle(isWorld)} onClick={() => setTier("world")}>Visualizations</span>
|
||||||
<span style={{ color: "#3a3a40" }}>/</span>
|
<span style={{ color: "#3a3a40" }}>/</span>
|
||||||
|
<span style={crumbStyle(isResearch)} onClick={() => setTier("research")}>Research</span>
|
||||||
|
<span style={{ color: "#3a3a40" }}>/</span>
|
||||||
|
<span style={crumbStyle(isLoops)} onClick={() => setTier("loops")}>Loops</span>
|
||||||
|
<span style={{ color: "#3a3a40" }}>/</span>
|
||||||
<span style={crumbStyle(isClaw)} onClick={() => setTier("claw")}>Agents</span>
|
<span style={crumbStyle(isClaw)} onClick={() => setTier("claw")}>Agents</span>
|
||||||
<span style={{ color: "#3a3a40" }}>/</span>
|
<span style={{ color: "#3a3a40" }}>/</span>
|
||||||
<span style={crumbStyle(isInfra)} onClick={() => setTier("infra")}>Infrastructure</span>
|
<span style={crumbStyle(isInfra)} onClick={() => setTier("infra")}>Infrastructure</span>
|
||||||
@@ -564,7 +584,7 @@ export function Dashboard({ user, orgs, claws }: { user?: { display_name?: strin
|
|||||||
</div>
|
</div>
|
||||||
<button type="button" onClick={() => { setSelectMode((v) => { if (v) setSelectedAgents(new Set()); return !v; }); }} title="Select to manage" aria-label="Select to manage" style={{ flex: "none", width: 34, height: 34, borderRadius: 9, border: `1px solid ${selectMode ? "rgba(255,111,97,.5)" : "rgba(255,255,255,.12)"}`, background: selectMode ? "rgba(255,111,97,.12)" : "transparent", color: selectMode ? "#ff6f61" : "#9a9aa2", cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center" }}><Wrench aria-hidden size={16} /></button>
|
<button type="button" onClick={() => { setSelectMode((v) => { if (v) setSelectedAgents(new Set()); return !v; }); }} title="Select to manage" aria-label="Select to manage" style={{ flex: "none", width: 34, height: 34, borderRadius: 9, border: `1px solid ${selectMode ? "rgba(255,111,97,.5)" : "rgba(255,255,255,.12)"}`, background: selectMode ? "rgba(255,111,97,.12)" : "transparent", color: selectMode ? "#ff6f61" : "#9a9aa2", cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center" }}><Wrench aria-hidden size={16} /></button>
|
||||||
</div>
|
</div>
|
||||||
) : isInfra ? null : (
|
) : isInfra || isResearch || isLoops ? null : (
|
||||||
<div style={{ padding: "16px 16px 12px", borderBottom: "1px solid rgba(255,255,255,.06)", display: "flex", alignItems: "flex-start", gap: 8 }}>
|
<div style={{ padding: "16px 16px 12px", borderBottom: "1px solid rgba(255,255,255,.06)", display: "flex", alignItems: "flex-start", gap: 8 }}>
|
||||||
<div style={{ flex: 1, minWidth: 0 }}>
|
<div style={{ flex: 1, minWidth: 0 }}>
|
||||||
<div style={{ fontFamily: mono, fontSize: 10, letterSpacing: ".12em", color: "#5a5a62", marginBottom: 6 }}>{allAgents.length} AGENT{allAgents.length === 1 ? "" : "S"}</div>
|
<div style={{ fontFamily: mono, fontSize: 10, letterSpacing: ".12em", color: "#5a5a62", marginBottom: 6 }}>{allAgents.length} AGENT{allAgents.length === 1 ? "" : "S"}</div>
|
||||||
@@ -578,7 +598,11 @@ export function Dashboard({ user, orgs, claws }: { user?: { display_name?: strin
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isInfra ? (
|
{isResearch ? (
|
||||||
|
<ResearchList />
|
||||||
|
) : isLoops ? (
|
||||||
|
<LoopsList />
|
||||||
|
) : isInfra ? (
|
||||||
<InfraNav view={infraSel ?? "local"} onSelect={setInfraSel} onConnectHost={() => setInfraConnectOpen(true)} />
|
<InfraNav view={infraSel ?? "local"} onSelect={setInfraSel} onConnectHost={() => setInfraConnectOpen(true)} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -605,7 +629,11 @@ export function Dashboard({ user, orgs, claws }: { user?: { display_name?: strin
|
|||||||
|
|
||||||
{/* CANVAS */}
|
{/* CANVAS */}
|
||||||
<div ref={canvasRef} style={{ flex: 1, position: "relative", minWidth: 0, overflow: "hidden", ...(resizing ? { ["--duration-normal" as string]: "0ms" } : {}), ...(isClaw || isInfra ? { ["--computer-width" as string]: computerOpen ? (customWidth != null ? `${customWidth}px` : COMPUTER_WIDTH[device]) : "0px" } : isWorld ? { ["--world-width" as string]: worldPanelOpen ? COMPUTER_WIDTH[worldSize] : "0px" } : {}) }}>
|
<div ref={canvasRef} style={{ flex: 1, position: "relative", minWidth: 0, overflow: "hidden", ...(resizing ? { ["--duration-normal" as string]: "0ms" } : {}), ...(isClaw || isInfra ? { ["--computer-width" as string]: computerOpen ? (customWidth != null ? `${customWidth}px` : COMPUTER_WIDTH[device]) : "0px" } : isWorld ? { ["--world-width" as string]: worldPanelOpen ? COMPUTER_WIDTH[worldSize] : "0px" } : {}) }}>
|
||||||
{isWorld ? (
|
{isResearch ? (
|
||||||
|
<ResearchCanvas />
|
||||||
|
) : isLoops ? (
|
||||||
|
<LoopsCanvas />
|
||||||
|
) : isWorld ? (
|
||||||
<>
|
<>
|
||||||
{/* Graph stage — condensed by the right slide-out's width. */}
|
{/* Graph stage — condensed by the right slide-out's width. */}
|
||||||
<div style={{ position: "absolute", top: 0, bottom: 0, left: 0, right: "var(--world-width, 0px)", background: "radial-gradient(120% 90% at 55% 38%, #0e0e13 0%, #08080a 70%)", transition: "right var(--duration-normal) var(--ease-app)" }}>
|
<div style={{ position: "absolute", top: 0, bottom: 0, left: 0, right: "var(--world-width, 0px)", background: "radial-gradient(120% 90% at 55% 38%, #0e0e13 0%, #08080a 70%)", transition: "right var(--duration-normal) var(--ease-app)" }}>
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
// Canvas for the Loops tier — stub. The real canvas (topology preview +
|
||||||
|
// trigger config + iteration timeline + webhook signing card) arrives with
|
||||||
|
// the loops-frontend commit.
|
||||||
|
|
||||||
|
const mono =
|
||||||
|
"ui-monospace, SFMono-Regular, SF Mono, Menlo, Monaco, Consolas, monospace";
|
||||||
|
|
||||||
|
export function LoopsCanvas() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
inset: 0,
|
||||||
|
background:
|
||||||
|
"radial-gradient(120% 90% at 55% 38%, #0e0e13 0%, #08080a 70%)",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
padding: 48,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
maxWidth: 460,
|
||||||
|
textAlign: "center",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Orbit-arrows: the loops motif from the rail */}
|
||||||
|
<svg width="48" height="48" viewBox="0 0 24 24" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M20 12 A8 8 0 1 1 12 4"
|
||||||
|
stroke="#ff6f61"
|
||||||
|
strokeWidth="1.7"
|
||||||
|
fill="none"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M13 3 L20 4 L18.5 10"
|
||||||
|
stroke="#ff6f61"
|
||||||
|
strokeWidth="1.7"
|
||||||
|
fill="none"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
<circle cx="12" cy="12" r="2.2" fill="currentColor" opacity=".6" />
|
||||||
|
</svg>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: 700,
|
||||||
|
color: "#f3f3f5",
|
||||||
|
letterSpacing: "-.01em",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Loops
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: mono,
|
||||||
|
fontSize: 12,
|
||||||
|
color: "#8a8a92",
|
||||||
|
lineHeight: 1.6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Pick a loop on the left to see its topology, trigger config, and
|
||||||
|
iteration timeline. Full canvas ships next commit.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
// Sidebar for the Loops tier — stub. The real list (loop cards + status +
|
||||||
|
// next-fire chip + `+` opens the loop wizard) arrives in the loops-frontend
|
||||||
|
// commit.
|
||||||
|
|
||||||
|
const mono =
|
||||||
|
"ui-monospace, SFMono-Regular, SF Mono, Menlo, Monaco, Consolas, monospace";
|
||||||
|
|
||||||
|
export function LoopsList() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: "16px 16px 12px",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,.06)",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ flex: 1, minWidth: 0 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: mono,
|
||||||
|
fontSize: 10,
|
||||||
|
letterSpacing: ".12em",
|
||||||
|
color: "#5a5a62",
|
||||||
|
marginBottom: 6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
0 LOOPS
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 700,
|
||||||
|
color: "#f3f3f5",
|
||||||
|
letterSpacing: "-.01em",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Loops
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
minHeight: 0,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
padding: "40px 16px",
|
||||||
|
fontFamily: mono,
|
||||||
|
fontSize: 11,
|
||||||
|
color: "#5a5a62",
|
||||||
|
textAlign: "center",
|
||||||
|
lineHeight: 1.6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Loop cards land in the next commit. Recurring topology executions —
|
||||||
|
cron, on-completion, or webhook-triggered.
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
// Canvas for the Research tier — stub. The real canvas (selected topic's
|
||||||
|
// agents grid, run timeline, publish gate) arrives with the wizard commit.
|
||||||
|
|
||||||
|
const mono =
|
||||||
|
"ui-monospace, SFMono-Regular, SF Mono, Menlo, Monaco, Consolas, monospace";
|
||||||
|
|
||||||
|
export function ResearchCanvas() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
inset: 0,
|
||||||
|
background:
|
||||||
|
"radial-gradient(120% 90% at 55% 38%, #0e0e13 0%, #08080a 70%)",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
padding: 48,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
maxWidth: 460,
|
||||||
|
textAlign: "center",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Book + magnifying-lens: the research motif from the rail */}
|
||||||
|
<svg width="48" height="48" viewBox="0 0 24 24" aria-hidden>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="3.5"
|
||||||
|
width="12"
|
||||||
|
height="14"
|
||||||
|
rx="1.5"
|
||||||
|
stroke="#5a5a62"
|
||||||
|
strokeWidth="1.4"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4 15.5 H16"
|
||||||
|
stroke="#5a5a62"
|
||||||
|
strokeWidth="1.4"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="17.5"
|
||||||
|
cy="17.5"
|
||||||
|
r="3.2"
|
||||||
|
stroke="#ff6f61"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M19.9 19.9 L22 22"
|
||||||
|
stroke="#ff6f61"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: 700,
|
||||||
|
color: "#f3f3f5",
|
||||||
|
letterSpacing: "-.01em",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Research
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: mono,
|
||||||
|
fontSize: 12,
|
||||||
|
color: "#8a8a92",
|
||||||
|
lineHeight: 1.6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Pick a topic on the left to see the agents assembling around it, the
|
||||||
|
run timeline, and the publish gate. Full canvas ships next commit.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
// Sidebar for the Research tier — stub for the shell-expansion commit. The
|
||||||
|
// real list (topics + status pills + `+` opens the wizard) lands in the
|
||||||
|
// next commit that also brings the API client and canvas panels.
|
||||||
|
|
||||||
|
const mono =
|
||||||
|
"ui-monospace, SFMono-Regular, SF Mono, Menlo, Monaco, Consolas, monospace";
|
||||||
|
|
||||||
|
export function ResearchList() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: "16px 16px 12px",
|
||||||
|
borderBottom: "1px solid rgba(255,255,255,.06)",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ flex: 1, minWidth: 0 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: mono,
|
||||||
|
fontSize: 10,
|
||||||
|
letterSpacing: ".12em",
|
||||||
|
color: "#5a5a62",
|
||||||
|
marginBottom: 6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
0 TOPICS
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 700,
|
||||||
|
color: "#f3f3f5",
|
||||||
|
letterSpacing: "-.01em",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Research
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
minHeight: 0,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
padding: "40px 16px",
|
||||||
|
fontFamily: mono,
|
||||||
|
fontSize: 11,
|
||||||
|
color: "#5a5a62",
|
||||||
|
textAlign: "center",
|
||||||
|
lineHeight: 1.6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Research topic cards land in the next commit. Wizard: capture, refine
|
||||||
|
with LLM, assemble outputs.
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user