diff --git a/apps/web/src/pages/library-page.css b/apps/web/src/pages/library-page.css index 4b464d62d..241d6f84a 100644 --- a/apps/web/src/pages/library-page.css +++ b/apps/web/src/pages/library-page.css @@ -1,10 +1,4 @@ /* Artifacts and Skills list pages (mockups: artifacts.html, skills.html). */ -.page-lede { - margin: 0 0 var(--space-4, 1rem); - color: var(--ink-soft); - font-size: 0.9375rem; -} - .library-row-title { display: inline-flex; align-items: center; diff --git a/apps/web/src/pages/library-page.tsx b/apps/web/src/pages/library-page.tsx index 377ad0a16..c005394f0 100644 --- a/apps/web/src/pages/library-page.tsx +++ b/apps/web/src/pages/library-page.tsx @@ -5,7 +5,6 @@ import { MenuContent, MenuItem, MenuTrigger, - PageShell, RichEmptyState, SelectionCheckbox, Skeleton, @@ -75,6 +74,7 @@ import { uploadArtifactFiles, uploadMimeTypeFromSource, } from "../shell/library-artifacts"; +import { PageLayout } from "../shell/page-layout"; import { StageTopBar } from "../shell/stage-top-bar"; const SORT_LABEL: Record = { @@ -499,8 +499,7 @@ export function LibraryPage({ ) : null}
- -

Everything your workers made. Yours to keep.

+ {artifacts.length === 0 ? ( } @@ -514,7 +513,7 @@ export function LibraryPage({ description={`No file matches "${activeQuery}".`} /> ) : ( -
+
)} - +
{activeSelected !== null ? (
@@ -622,13 +621,13 @@ export function LibraryRoute({ path }: { readonly path: string }) { return (
- + } title="Select a workbench" description="Open a workbench to browse the artifacts it owns." /> - +
); } @@ -637,13 +636,13 @@ export function LibraryRoute({ path }: { readonly path: string }) { return (
- + } title="Artifacts not configured" description="Artifacts isn't set up yet. Ask your workbench admin to finish setup." /> - +
); } @@ -652,7 +651,7 @@ export function LibraryRoute({ path }: { readonly path: string }) { return (
- + {page.kind === "loading" ? ( ) : page.kind === "unauthenticated" ? ( @@ -664,7 +663,7 @@ export function LibraryRoute({ path }: { readonly path: string }) { description={describeApiError({ status: page.status }, "loading your artifacts")} /> )} - +
); } diff --git a/apps/web/src/pages/routines-page.tsx b/apps/web/src/pages/routines-page.tsx index 4e2179942..be3c25550 100644 --- a/apps/web/src/pages/routines-page.tsx +++ b/apps/web/src/pages/routines-page.tsx @@ -10,6 +10,7 @@ import type { GlobalRoutineRow } from "../global-routines"; import { routineDetailPath } from "../global-routines"; import { Link } from "../navigation"; import { benchLink, useFromBench } from "../shell/page-crumbs"; +import { PageLayout } from "../shell/page-layout"; import { StageTopBar } from "../shell/stage-top-bar"; import { RoutinePill, formatWhen, routineState, useRoutineRuns } from "./routine-ui"; @@ -81,9 +82,10 @@ export function GlobalRoutinesList({ .includes(needle), ); return ( -
-

Workflows

-

Deployed workflows, when they run, and how the last run went.

+ ))} -
+ ); } diff --git a/apps/web/src/pages/routines.css b/apps/web/src/pages/routines.css index 06a990649..b5e2410cd 100644 --- a/apps/web/src/pages/routines.css +++ b/apps/web/src/pages/routines.css @@ -10,28 +10,6 @@ font-size: 13.5px; } -.routines-page { - display: flex; - flex-direction: column; - gap: 16px; - max-width: 960px; - width: 100%; - margin: 0 auto; - padding: 24px 16px; -} - -.routines-page h1 { - margin: 0; - font-size: 24px; - font-weight: 800; -} - -.routines-lede { - margin: 0; - color: var(--ink-3); - font-size: 13.5px; -} - .routine-rows { list-style: none; margin: 0; diff --git a/apps/web/src/pages/skills-page.tsx b/apps/web/src/pages/skills-page.tsx index 862c073fa..7da5bc353 100644 --- a/apps/web/src/pages/skills-page.tsx +++ b/apps/web/src/pages/skills-page.tsx @@ -3,7 +3,6 @@ // catalog — skills are authored here, private by default or shared. import { - PageShell, Button, EmptyState, RichEmptyState, @@ -29,6 +28,7 @@ import { CreateSkillDialog, type SkillCreateInput } from "./create-skill-dialog" import { useBench } from "../bench-context"; import { SKILLS_PATH_PREFIX } from "../path-ids"; import { skillDisplayName } from "../skill-display-name"; +import { PageLayout } from "../shell/page-layout"; import { StageTopBar } from "../shell/stage-top-bar"; type RegistryState = @@ -113,12 +113,15 @@ export function SkillsPage({
- + {body} - +
); @@ -189,8 +192,7 @@ export function SkillsPage({ description={`Nothing matches “${query.trim()}”.`} /> ) : ( -
-

Know-how your workers draw on. Ask for more in any bench.

+
diff --git a/apps/web/src/pages/workers-page.tsx b/apps/web/src/pages/workers-page.tsx index 359e627f4..f9533349e 100644 --- a/apps/web/src/pages/workers-page.tsx +++ b/apps/web/src/pages/workers-page.tsx @@ -2,8 +2,8 @@ // the sidebar's "+" menu. import { useState } from "react"; -import { Button, PageShell, RichEmptyState, toast } from "@corbits/react-ui"; -import { Robot } from "@/lib/icons"; +import { Button, RichEmptyState, toast } from "@corbits/react-ui"; +import { Plus, Robot } from "@/lib/icons"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { reportError } from "@corbits/error-sink"; @@ -15,7 +15,9 @@ import { describeRestartFailure, redeployWorkbenchAgent } from "../workbench-cre import { useBench } from "../bench-context"; import { Link } from "../navigation"; import { useTenantQuery } from "../routines-api"; +import { PageLayout } from "../shell/page-layout"; import { StageTopBar } from "../shell/stage-top-bar"; +import { CreateAgentPanel } from "./create-agent-panel"; import { WORKERS_PATH_PREFIX } from "../path-ids"; export type WorkerTone = "working" | "ready" | "idle"; @@ -94,7 +96,7 @@ export function WorkersRosterList({ } title="No workers yet" - description="Create a worker, a name and instructions, from the + menu and it appears here." + description="Create one with New worker and it appears here." /> ); } @@ -202,14 +204,24 @@ export function WorkersRoute() { benchMemberships.find((member) => member.tenantId === selectedTenantId)?.tenantName ?? "This workbench"; + const queryClient = useQueryClient(); + const [createOpen, setCreateOpen] = useState(false); + return (
- +
- + setCreateOpen(true)}> + New worker + + ) + } + > {selectedTenantId === null ? ( } @@ -217,7 +229,7 @@ export function WorkersRoute() { description="Pick a workbench from the switcher to see its workers." /> ) : ( -
+ <> {(agents) => ( )} -
+ { + void queryClient.invalidateQueries({ + queryKey: tenantKeys.agents(selectedTenantId), + }); + }} + /> + )} -
+
); diff --git a/apps/web/src/shell/page-layout.css b/apps/web/src/shell/page-layout.css new file mode 100644 index 000000000..671062caa --- /dev/null +++ b/apps/web/src/shell/page-layout.css @@ -0,0 +1,42 @@ +/* One page column for the library pages (mockup .page / .page-narrow / .page-head). */ +.page-layout { + width: 100%; + max-width: calc(864px + 48px); + margin: 0 auto; + padding: 32px 24px 64px; + box-sizing: border-box; +} + +.page-layout-head { + display: flex; + align-items: flex-end; + gap: 16px; + margin-bottom: 24px; +} + +.page-layout-head > div:first-child { + flex: 1; + min-width: 0; +} + +.page-layout-head h1 { + margin: 0; + font-size: 26px; + font-weight: 800; + letter-spacing: -0.02em; + line-height: 1.2; +} + +.page-layout-lede { + margin: 6px 0 0; + max-width: 60ch; + color: var(--ink-2); + font-size: 15px; +} + +.page-layout-actions { + display: flex; + flex: none; + align-items: center; + gap: 8px; +} diff --git a/apps/web/src/shell/page-layout.tsx b/apps/web/src/shell/page-layout.tsx new file mode 100644 index 000000000..012631104 --- /dev/null +++ b/apps/web/src/shell/page-layout.tsx @@ -0,0 +1,30 @@ +import type { ReactNode } from "react"; + +import "./page-layout.css"; + +export function PageLayout({ + title, + subtitle, + actions, + children, +}: { + readonly title: string; + readonly subtitle?: ReactNode; + readonly actions?: ReactNode; + readonly children: ReactNode; +}) { + return ( +
+
+
+

{title}

+ {subtitle !== undefined ?

{subtitle}

: null} +
+ {actions !== undefined && actions !== null ? ( +
{actions}
+ ) : null} +
+ {children} +
+ ); +}