feat(web): W3-1 shared console routes, nav, api/sse/query-keys and shared components

- routes.ts registers all workspaces under /console (+ detail routes, engine, audit, login)
- api.ts: cookie session first (credentials include, CSRF header), Bearer only with stored key, 401 -> /login
- sse.ts: shared EventSource with ?after=seq catch-up, backoff reconnect, per-type subscribe
- shared components: data-table, filter-bar, detail-sheet, timeline, json-view, confirm-dialog,
  form-field, kv-list, stat-tile, error/loading states; ui tabs and textarea

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-27 20:21:29 +08:00
1 parent a24bda952b
commit e5e42b1f0d
32 files changed
+1286 -71

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
import { Tabs as TabsPrimitive } from "radix-ui";
import type * as React from "react";
import { cn } from "~/lib/utils.ts";
// 分段标签:列表横向可滚(scroll-bleed 保留描边不被裁),选中态用静态 pill-selected
function Tabs({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>) {
return <TabsPrimitive.Root data-slot="tabs" className={cn("flex flex-col gap-4", className)} {...props} />;
}
function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {
return (
<TabsPrimitive.List
data-slot="tabs-list"
className={cn("scroll-bleed flex max-w-full items-center gap-1 overflow-x-auto pb-1", className)}
{...props}
/>
);
}
function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return (
<TabsPrimitive.Trigger
data-slot="tabs-trigger"
className={cn(
"inline-flex h-8 shrink-0 cursor-pointer items-center gap-1.5 rounded-full border border-transparent px-3 text-sm font-medium whitespace-nowrap text-muted-foreground transition-[background-color,color] duration-micro outline-none hover:bg-foreground/[0.06] hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:border-(--mat-pill-selected-border) data-[state=active]:bg-(--mat-pill-selected-bg) data-[state=active]:text-foreground [&_svg]:size-4",
className,
)}
{...props}
/>
);
}
function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {
return <TabsPrimitive.Content data-slot="tabs-content" className={cn("outline-none", className)} {...props} />;
}
export { Tabs, TabsContent, TabsList, TabsTrigger };
+20
View File
@@ -0,0 +1,20 @@
import type * as React from "react";
import { cn } from "~/lib/utils.ts";
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
return (
<textarea
data-slot="textarea"
className={cn(
"placeholder:text-muted-foreground dark:bg-input/30 border-input flex min-h-16 w-full min-w-0 rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className,
)}
{...props}
/>
);
}
export { Textarea };