@sero-ai/ui 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +628 -0
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +38 -0
- package/dist/components/context-editor/CapabilitySection.cjs +57 -0
- package/dist/components/context-editor/CapabilitySection.d.cts +20 -0
- package/dist/components/context-editor/CapabilitySection.d.ts +20 -0
- package/dist/components/context-editor/CapabilitySection.js +57 -0
- package/dist/components/context-editor/ContextEditor.cjs +108 -0
- package/dist/components/context-editor/ContextEditor.d.cts +26 -0
- package/dist/components/context-editor/ContextEditor.d.ts +26 -0
- package/dist/components/context-editor/ContextEditor.js +108 -0
- package/dist/components/context-editor/PresetBar.cjs +71 -0
- package/dist/components/context-editor/PresetBar.d.cts +19 -0
- package/dist/components/context-editor/PresetBar.d.ts +19 -0
- package/dist/components/context-editor/PresetBar.js +71 -0
- package/dist/components/context-editor/SkillsSection.cjs +31 -0
- package/dist/components/context-editor/SkillsSection.d.cts +14 -0
- package/dist/components/context-editor/SkillsSection.d.ts +14 -0
- package/dist/components/context-editor/SkillsSection.js +31 -0
- package/dist/components/context-editor/SystemPromptSection.cjs +58 -0
- package/dist/components/context-editor/SystemPromptSection.d.cts +25 -0
- package/dist/components/context-editor/SystemPromptSection.d.ts +25 -0
- package/dist/components/context-editor/SystemPromptSection.js +58 -0
- package/dist/components/context-editor/ToolsSection.cjs +31 -0
- package/dist/components/context-editor/ToolsSection.d.cts +14 -0
- package/dist/components/context-editor/ToolsSection.d.ts +14 -0
- package/dist/components/context-editor/ToolsSection.js +31 -0
- package/dist/components/context-editor/index.cjs +8 -0
- package/dist/components/context-editor/index.d.cts +5 -0
- package/dist/components/context-editor/index.d.ts +5 -0
- package/dist/components/context-editor/index.js +8 -0
- package/dist/components/context-editor/parts.cjs +184 -0
- package/dist/components/context-editor/parts.d.cts +32 -0
- package/dist/components/context-editor/parts.d.ts +32 -0
- package/dist/components/context-editor/parts.js +184 -0
- package/dist/components/context-editor/use-context-editor-model.cjs +194 -0
- package/dist/components/context-editor/use-context-editor-model.d.cts +57 -0
- package/dist/components/context-editor/use-context-editor-model.d.ts +57 -0
- package/dist/components/context-editor/use-context-editor-model.js +194 -0
- package/dist/components/dashboard/activity-list.cjs +59 -0
- package/dist/components/dashboard/activity-list.d.cts +27 -0
- package/dist/components/dashboard/activity-list.d.ts +27 -0
- package/dist/components/dashboard/activity-list.js +59 -0
- package/dist/components/dashboard/catalog.cjs +322 -0
- package/dist/components/dashboard/catalog.d.cts +14 -0
- package/dist/components/dashboard/catalog.d.ts +14 -0
- package/dist/components/dashboard/catalog.js +322 -0
- package/dist/components/dashboard/data-boundary.cjs +29 -0
- package/dist/components/dashboard/data-boundary.d.cts +21 -0
- package/dist/components/dashboard/data-boundary.d.ts +21 -0
- package/dist/components/dashboard/data-boundary.js +29 -0
- package/dist/components/dashboard/empty-state.cjs +49 -0
- package/dist/components/dashboard/empty-state.d.cts +20 -0
- package/dist/components/dashboard/empty-state.d.ts +20 -0
- package/dist/components/dashboard/empty-state.js +49 -0
- package/dist/components/dashboard/icon-button.cjs +40 -0
- package/dist/components/dashboard/icon-button.d.cts +24 -0
- package/dist/components/dashboard/icon-button.d.ts +24 -0
- package/dist/components/dashboard/icon-button.js +40 -0
- package/dist/components/dashboard/index.cjs +15 -0
- package/dist/components/dashboard/index.d.cts +24 -0
- package/dist/components/dashboard/index.d.ts +24 -0
- package/dist/components/dashboard/index.js +15 -0
- package/dist/components/dashboard/item-list.cjs +71 -0
- package/dist/components/dashboard/item-list.d.cts +28 -0
- package/dist/components/dashboard/item-list.d.ts +28 -0
- package/dist/components/dashboard/item-list.js +71 -0
- package/dist/components/dashboard/key-value.cjs +39 -0
- package/dist/components/dashboard/key-value.d.cts +17 -0
- package/dist/components/dashboard/key-value.d.ts +17 -0
- package/dist/components/dashboard/key-value.js +39 -0
- package/dist/components/dashboard/layout.cjs +193 -0
- package/dist/components/dashboard/layout.d.cts +91 -0
- package/dist/components/dashboard/layout.d.ts +91 -0
- package/dist/components/dashboard/layout.js +193 -0
- package/dist/components/dashboard/metric.cjs +98 -0
- package/dist/components/dashboard/metric.d.cts +44 -0
- package/dist/components/dashboard/metric.d.ts +44 -0
- package/dist/components/dashboard/metric.js +98 -0
- package/dist/components/dashboard/progress-ring.cjs +77 -0
- package/dist/components/dashboard/progress-ring.d.cts +25 -0
- package/dist/components/dashboard/progress-ring.d.ts +25 -0
- package/dist/components/dashboard/progress-ring.js +77 -0
- package/dist/components/dashboard/skeletons.cjs +54 -0
- package/dist/components/dashboard/skeletons.d.cts +15 -0
- package/dist/components/dashboard/skeletons.d.ts +15 -0
- package/dist/components/dashboard/skeletons.js +54 -0
- package/dist/components/dashboard/spacing.cjs +10 -0
- package/dist/components/dashboard/spacing.d.cts +4 -0
- package/dist/components/dashboard/spacing.d.ts +4 -0
- package/dist/components/dashboard/spacing.js +10 -0
- package/dist/components/dashboard/stale-indicator.cjs +42 -0
- package/dist/components/dashboard/stale-indicator.d.cts +15 -0
- package/dist/components/dashboard/stale-indicator.d.ts +15 -0
- package/dist/components/dashboard/stale-indicator.js +42 -0
- package/dist/components/dashboard/status.cjs +62 -0
- package/dist/components/dashboard/status.d.cts +15 -0
- package/dist/components/dashboard/status.d.ts +15 -0
- package/dist/components/dashboard/status.js +62 -0
- package/dist/components/dashboard/tone.cjs +26 -0
- package/dist/components/dashboard/tone.d.cts +9 -0
- package/dist/components/dashboard/tone.d.ts +9 -0
- package/dist/components/dashboard/tone.js +26 -0
- package/dist/components/dashboard/typography.cjs +99 -0
- package/dist/components/dashboard/typography.d.cts +57 -0
- package/dist/components/dashboard/typography.d.ts +57 -0
- package/dist/components/dashboard/typography.js +99 -0
- package/dist/components/reference/ActivityExample.cjs +47 -0
- package/dist/components/reference/ActivityExample.d.cts +5 -0
- package/dist/components/reference/ActivityExample.d.ts +5 -0
- package/dist/components/reference/ActivityExample.js +47 -0
- package/dist/components/reference/ResourceExample.cjs +54 -0
- package/dist/components/reference/ResourceExample.d.cts +5 -0
- package/dist/components/reference/ResourceExample.d.ts +5 -0
- package/dist/components/reference/ResourceExample.js +54 -0
- package/dist/components/reference/SchedulerExample.cjs +68 -0
- package/dist/components/reference/SchedulerExample.d.cts +5 -0
- package/dist/components/reference/SchedulerExample.d.ts +5 -0
- package/dist/components/reference/SchedulerExample.js +68 -0
- package/dist/components/reference/StarterExample.cjs +34 -0
- package/dist/components/reference/StarterExample.d.cts +5 -0
- package/dist/components/reference/StarterExample.d.ts +5 -0
- package/dist/components/reference/StarterExample.js +34 -0
- package/dist/components/reference/index.cjs +10 -0
- package/dist/components/reference/index.d.cts +5 -0
- package/dist/components/reference/index.d.ts +5 -0
- package/dist/components/reference/index.js +10 -0
- package/dist/components/ui/badge.cjs +5 -1
- package/dist/components/ui/badge.d.cts +1 -1
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/badge.js +5 -1
- package/dist/dashboard-catalog.cjs +8 -0
- package/dist/dashboard-catalog.d.cts +1 -0
- package/dist/dashboard-catalog.d.ts +1 -0
- package/dist/dashboard-catalog.js +8 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +1 -0
- package/dist/reference.cjs +12 -0
- package/dist/reference.d.cts +5 -0
- package/dist/reference.d.ts +5 -0
- package/dist/reference.js +12 -0
- package/dist/styles/glass-board.css +43 -0
- package/dist/styles/globals.css +59 -0
- package/package.json +70 -41
- package/src/components/context-editor/CapabilitySection.tsx +85 -0
- package/src/components/context-editor/ContextEditor.tsx +138 -0
- package/src/components/context-editor/PresetBar.tsx +94 -0
- package/src/components/context-editor/SkillsSection.tsx +37 -0
- package/src/components/context-editor/SystemPromptSection.tsx +88 -0
- package/src/components/context-editor/ToolsSection.tsx +37 -0
- package/src/components/context-editor/index.ts +7 -0
- package/src/components/context-editor/parts.tsx +217 -0
- package/src/components/context-editor/use-context-editor-model.ts +296 -0
- package/src/components/dashboard/__tests__/catalog.test.ts +57 -0
- package/src/components/dashboard/__tests__/glass.test.tsx +42 -0
- package/src/components/dashboard/__tests__/state.test.tsx +110 -0
- package/src/components/dashboard/__tests__/variants.test.tsx +95 -0
- package/src/components/dashboard/activity-list.tsx +99 -0
- package/src/components/dashboard/catalog.ts +351 -0
- package/src/components/dashboard/data-boundary.tsx +54 -0
- package/src/components/dashboard/empty-state.tsx +70 -0
- package/src/components/dashboard/icon-button.tsx +58 -0
- package/src/components/dashboard/index.ts +21 -0
- package/src/components/dashboard/item-list.tsx +106 -0
- package/src/components/dashboard/key-value.tsx +54 -0
- package/src/components/dashboard/layout.tsx +286 -0
- package/src/components/dashboard/metric.tsx +139 -0
- package/src/components/dashboard/progress-ring.tsx +100 -0
- package/src/components/dashboard/skeletons.tsx +73 -0
- package/src/components/dashboard/spacing.ts +14 -0
- package/src/components/dashboard/stale-indicator.tsx +55 -0
- package/src/components/dashboard/status.tsx +75 -0
- package/src/components/dashboard/tone.ts +35 -0
- package/src/components/dashboard/typography.tsx +151 -0
- package/src/components/reference/ActivityExample.tsx +68 -0
- package/src/components/reference/ResourceExample.tsx +78 -0
- package/src/components/reference/SchedulerExample.tsx +88 -0
- package/src/components/reference/StarterExample.tsx +48 -0
- package/src/components/reference/index.ts +10 -0
- package/src/components/ui/badge.tsx +6 -0
- package/src/dashboard-catalog.ts +13 -0
- package/src/index.ts +5 -0
- package/src/reference.ts +15 -0
- package/src/styles/glass-board.css +43 -0
- package/src/styles/globals.css +59 -0
- package/tsup.config.ts +6 -2
- package/vitest.config.ts +8 -0
- package/LICENSE +0 -201
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// State-selection + accessibility-contract tests for dashboard components.
|
|
2
|
+
|
|
3
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
4
|
+
import { RotateCw, Clock } from "lucide-react";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
|
|
7
|
+
import { DataBoundary } from "../data-boundary";
|
|
8
|
+
import { EmptyState } from "../empty-state";
|
|
9
|
+
import { IconButton } from "../icon-button";
|
|
10
|
+
import { Icon } from "../typography";
|
|
11
|
+
import { ProgressRing } from "../progress-ring";
|
|
12
|
+
import { Status } from "../status";
|
|
13
|
+
|
|
14
|
+
describe("DataBoundary", () => {
|
|
15
|
+
const slots = {
|
|
16
|
+
loading: <div>LOADING</div>,
|
|
17
|
+
empty: <div>EMPTY</div>,
|
|
18
|
+
error: <div>ERROR</div>,
|
|
19
|
+
stale: <div>STALE</div>,
|
|
20
|
+
children: <div>READY</div>,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
it("renders only the loading slot when loading", () => {
|
|
24
|
+
const html = renderToStaticMarkup(
|
|
25
|
+
<DataBoundary state="loading" {...slots} />,
|
|
26
|
+
);
|
|
27
|
+
expect(html).toContain("LOADING");
|
|
28
|
+
expect(html).not.toContain("READY");
|
|
29
|
+
expect(html).not.toContain("EMPTY");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("renders only the empty slot when empty", () => {
|
|
33
|
+
const html = renderToStaticMarkup(<DataBoundary state="empty" {...slots} />);
|
|
34
|
+
expect(html).toContain("EMPTY");
|
|
35
|
+
expect(html).not.toContain("READY");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("renders only the error slot when error", () => {
|
|
39
|
+
const html = renderToStaticMarkup(<DataBoundary state="error" {...slots} />);
|
|
40
|
+
expect(html).toContain("ERROR");
|
|
41
|
+
expect(html).not.toContain("READY");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("renders the stale banner above ready content when stale", () => {
|
|
45
|
+
const html = renderToStaticMarkup(<DataBoundary state="stale" {...slots} />);
|
|
46
|
+
expect(html).toContain("STALE");
|
|
47
|
+
expect(html).toContain("READY");
|
|
48
|
+
expect(html.indexOf("STALE")).toBeLessThan(html.indexOf("READY"));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("renders ready children when ready", () => {
|
|
52
|
+
const html = renderToStaticMarkup(<DataBoundary state="ready" {...slots} />);
|
|
53
|
+
expect(html).toContain("READY");
|
|
54
|
+
expect(html).not.toContain("STALE");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("accessibility contracts", () => {
|
|
59
|
+
it("ProgressRing exposes progressbar value semantics", () => {
|
|
60
|
+
const html = renderToStaticMarkup(
|
|
61
|
+
<ProgressRing value={30} max={60} label="Usage" />,
|
|
62
|
+
);
|
|
63
|
+
expect(html).toContain('role="progressbar"');
|
|
64
|
+
expect(html).toContain('aria-label="Usage"');
|
|
65
|
+
expect(html).toContain('aria-valuenow="30"');
|
|
66
|
+
expect(html).toContain('aria-valuemax="60"');
|
|
67
|
+
// 30 / 60 = 50%
|
|
68
|
+
expect(html).toContain("50%");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("ProgressRing clamps out-of-range values", () => {
|
|
72
|
+
const html = renderToStaticMarkup(<ProgressRing value={999} max={100} />);
|
|
73
|
+
expect(html).toContain('aria-valuenow="100"');
|
|
74
|
+
expect(html).toContain("100%");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("Icon is hidden from assistive tech without a label", () => {
|
|
78
|
+
const html = renderToStaticMarkup(<Icon icon={Clock} />);
|
|
79
|
+
expect(html).toContain('aria-hidden="true"');
|
|
80
|
+
expect(html).not.toContain('role="img"');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("Icon is exposed as an image with a label", () => {
|
|
84
|
+
const html = renderToStaticMarkup(<Icon icon={Clock} label="Time" />);
|
|
85
|
+
expect(html).toContain('role="img"');
|
|
86
|
+
expect(html).toContain('aria-label="Time"');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("IconButton carries its required accessible label", () => {
|
|
90
|
+
const html = renderToStaticMarkup(
|
|
91
|
+
<IconButton icon={RotateCw} label="Refresh" />,
|
|
92
|
+
);
|
|
93
|
+
expect(html).toContain('aria-label="Refresh"');
|
|
94
|
+
expect(html).toContain('type="button"');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("a bare Status dot is announced as a status", () => {
|
|
98
|
+
const html = renderToStaticMarkup(<Status tone="success" />);
|
|
99
|
+
expect(html).toContain('role="status"');
|
|
100
|
+
expect(html).toContain('data-tone="success"');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("EmptyState renders its title and message", () => {
|
|
104
|
+
const html = renderToStaticMarkup(
|
|
105
|
+
<EmptyState title="Nothing here" message="Add an item to start" />,
|
|
106
|
+
);
|
|
107
|
+
expect(html).toContain("Nothing here");
|
|
108
|
+
expect(html).toContain("Add an item to start");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Variant / tone tests for dashboard components.
|
|
2
|
+
|
|
3
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { Badge } from "../../ui/badge";
|
|
7
|
+
import { Grid, Inline, Stack } from "../layout";
|
|
8
|
+
import { Metric } from "../metric";
|
|
9
|
+
import { Status } from "../status";
|
|
10
|
+
import { Text } from "../typography";
|
|
11
|
+
|
|
12
|
+
describe("Status tones", () => {
|
|
13
|
+
it("renders a tinted pill for a tone", () => {
|
|
14
|
+
const html = renderToStaticMarkup(
|
|
15
|
+
<Status tone="warning" variant="pill">
|
|
16
|
+
Degraded
|
|
17
|
+
</Status>,
|
|
18
|
+
);
|
|
19
|
+
expect(html).toContain("--status-warning-muted");
|
|
20
|
+
expect(html).toContain("Degraded");
|
|
21
|
+
expect(html).toContain('data-tone="warning"');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("maps neutral tone to the muted token", () => {
|
|
25
|
+
const html = renderToStaticMarkup(<Status tone="neutral">Idle</Status>);
|
|
26
|
+
expect(html).toContain("--text-muted");
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("Badge semantic tones", () => {
|
|
31
|
+
it.each([
|
|
32
|
+
["success", "--status-success"],
|
|
33
|
+
["warning", "--status-warning"],
|
|
34
|
+
["info", "--status-info"],
|
|
35
|
+
] as const)("badge %s uses its status token", (variant, token) => {
|
|
36
|
+
const html = renderToStaticMarkup(<Badge variant={variant}>x</Badge>);
|
|
37
|
+
expect(html).toContain(token);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("Text variants", () => {
|
|
42
|
+
it("numeric variant uses tabular numerals", () => {
|
|
43
|
+
const html = renderToStaticMarkup(<Text variant="numeric">42</Text>);
|
|
44
|
+
expect(html).toContain("tabular-nums");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("clamp overrides truncate", () => {
|
|
48
|
+
const html = renderToStaticMarkup(
|
|
49
|
+
<Text truncate clamp={2}>
|
|
50
|
+
long
|
|
51
|
+
</Text>,
|
|
52
|
+
);
|
|
53
|
+
expect(html).toContain("line-clamp-2");
|
|
54
|
+
expect(html).not.toContain("truncate");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("Metric", () => {
|
|
59
|
+
it("renders label, value and a trend", () => {
|
|
60
|
+
const html = renderToStaticMarkup(
|
|
61
|
+
<Metric
|
|
62
|
+
label="Jobs"
|
|
63
|
+
value={6}
|
|
64
|
+
trend={{ direction: "up", value: "+2", tone: "success" }}
|
|
65
|
+
/>,
|
|
66
|
+
);
|
|
67
|
+
expect(html).toContain("Jobs");
|
|
68
|
+
expect(html).toContain("6");
|
|
69
|
+
expect(html).toContain("+2");
|
|
70
|
+
expect(html).toContain("--status-success");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("layout spacing scale", () => {
|
|
75
|
+
it("Stack maps semantic gap to a utility", () => {
|
|
76
|
+
expect(renderToStaticMarkup(<Stack gap="lg" />)).toContain("gap-4");
|
|
77
|
+
});
|
|
78
|
+
it("Stack fill grows without scrolling; scroll adds overflow", () => {
|
|
79
|
+
const fill = renderToStaticMarkup(<Stack fill />);
|
|
80
|
+
expect(fill).toContain("flex-1");
|
|
81
|
+
expect(fill).not.toContain("overflow-auto");
|
|
82
|
+
expect(renderToStaticMarkup(<Stack scroll />)).toContain("overflow-auto");
|
|
83
|
+
});
|
|
84
|
+
it("Inline wraps when asked", () => {
|
|
85
|
+
expect(renderToStaticMarkup(<Inline wrap />)).toContain("flex-wrap");
|
|
86
|
+
});
|
|
87
|
+
it("Grid with fixed columns uses a column utility", () => {
|
|
88
|
+
expect(renderToStaticMarkup(<Grid columns={3} />)).toContain("grid-cols-3");
|
|
89
|
+
});
|
|
90
|
+
it("Grid auto uses an inline template", () => {
|
|
91
|
+
expect(renderToStaticMarkup(<Grid columns="auto" minColumnWidth={80} />)).toContain(
|
|
92
|
+
"minmax(80px",
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// ActivityList and ActivityListItem — a compact chronological event list.
|
|
2
|
+
//
|
|
3
|
+
// Each row carries a status marker or icon, a label, a timestamp and optional
|
|
4
|
+
// supporting detail. Timestamps are passed as pre-formatted strings — the
|
|
5
|
+
// component never computes relative time, so it stays stateless and the plugin
|
|
6
|
+
// owns any refresh.
|
|
7
|
+
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
|
|
10
|
+
import { cn } from "../../lib/utils";
|
|
11
|
+
import { Icon } from "./typography";
|
|
12
|
+
import { toneDot, type Tone } from "./tone";
|
|
13
|
+
|
|
14
|
+
export interface ActivityListItemProps
|
|
15
|
+
extends Omit<React.ComponentProps<"li">, "title"> {
|
|
16
|
+
label: React.ReactNode;
|
|
17
|
+
/** Pre-formatted timestamp, e.g. "08:00" or "in 2h". */
|
|
18
|
+
timestamp?: React.ReactNode;
|
|
19
|
+
/** Supporting detail under the label. */
|
|
20
|
+
detail?: React.ReactNode;
|
|
21
|
+
/** Status marker tone. Ignored when `icon` is provided. */
|
|
22
|
+
tone?: Tone;
|
|
23
|
+
/** Leading icon component, used instead of the status dot. */
|
|
24
|
+
icon?: React.ComponentType<{ className?: string }>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** A single chronological event row. */
|
|
28
|
+
function ActivityListItem({
|
|
29
|
+
className,
|
|
30
|
+
label,
|
|
31
|
+
timestamp,
|
|
32
|
+
detail,
|
|
33
|
+
tone = "neutral",
|
|
34
|
+
icon,
|
|
35
|
+
...props
|
|
36
|
+
}: ActivityListItemProps) {
|
|
37
|
+
return (
|
|
38
|
+
<li
|
|
39
|
+
data-slot="activity-list-item"
|
|
40
|
+
className={cn("flex items-start gap-2 py-1", className)}
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
<span className="mt-1 flex size-3.5 shrink-0 items-center justify-center">
|
|
44
|
+
{icon ? (
|
|
45
|
+
<Icon icon={icon} size="xs" tone={tone === "neutral" ? undefined : tone} />
|
|
46
|
+
) : (
|
|
47
|
+
<span className={cn("size-2 rounded-full", toneDot[tone])} />
|
|
48
|
+
)}
|
|
49
|
+
</span>
|
|
50
|
+
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
51
|
+
<div className="flex items-baseline justify-between gap-2">
|
|
52
|
+
<span className="truncate text-xs text-[var(--text-primary)]">
|
|
53
|
+
{label}
|
|
54
|
+
</span>
|
|
55
|
+
{timestamp && (
|
|
56
|
+
<span className="shrink-0 text-[11px] tabular-nums text-[var(--text-muted)]">
|
|
57
|
+
{timestamp}
|
|
58
|
+
</span>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
{detail && (
|
|
62
|
+
<span className="line-clamp-1 text-xs text-[var(--text-muted)]">
|
|
63
|
+
{detail}
|
|
64
|
+
</span>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
</li>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ActivityListProps extends React.ComponentProps<"ul"> {
|
|
72
|
+
/** Count of events not shown; renders a "+N more" footer when > 0. */
|
|
73
|
+
overflowCount?: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** A chronological list of ActivityListItem rows. */
|
|
77
|
+
function ActivityList({
|
|
78
|
+
className,
|
|
79
|
+
overflowCount = 0,
|
|
80
|
+
children,
|
|
81
|
+
...props
|
|
82
|
+
}: ActivityListProps) {
|
|
83
|
+
return (
|
|
84
|
+
<ul
|
|
85
|
+
data-slot="activity-list"
|
|
86
|
+
className={cn("flex min-w-0 flex-col", className)}
|
|
87
|
+
{...props}
|
|
88
|
+
>
|
|
89
|
+
{children}
|
|
90
|
+
{overflowCount > 0 && (
|
|
91
|
+
<li className="pl-5 pt-0.5 text-[11px] text-[var(--text-muted)]">
|
|
92
|
+
+{overflowCount} more
|
|
93
|
+
</li>
|
|
94
|
+
)}
|
|
95
|
+
</ul>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { ActivityList, ActivityListItem };
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
// Dashboard component catalogue — the source of truth for discovery.
|
|
2
|
+
//
|
|
3
|
+
// This is package metadata, not a runtime service. It changes through normal
|
|
4
|
+
// source review and ships with @sero-ai/ui. Widget code imports the React
|
|
5
|
+
// components from "@sero-ai/ui"; tooling and the agent read this metadata via
|
|
6
|
+
// "@sero-ai/ui/dashboard-catalog" to discover the supported vocabulary.
|
|
7
|
+
//
|
|
8
|
+
// Keep this in step with the two readable views on release:
|
|
9
|
+
// - packages/templates/skills/sero-dashboard-ui/references/component-catalog.md
|
|
10
|
+
// - the docs-site dashboard component catalogue page
|
|
11
|
+
// (Generation + drift validation is a deferred follow-up.)
|
|
12
|
+
|
|
13
|
+
export interface DashboardComponentCatalogEntry {
|
|
14
|
+
name: string;
|
|
15
|
+
category:
|
|
16
|
+
| "layout"
|
|
17
|
+
| "typography"
|
|
18
|
+
| "data-display"
|
|
19
|
+
| "state"
|
|
20
|
+
| "action"
|
|
21
|
+
| "filter";
|
|
22
|
+
/** `primitive` = existing @sero-ai/ui primitive; `composite` = new dashboard component. */
|
|
23
|
+
kind: "primitive" | "composite";
|
|
24
|
+
summary: string;
|
|
25
|
+
useWhen: string;
|
|
26
|
+
related?: string[];
|
|
27
|
+
status: "stable" | "experimental";
|
|
28
|
+
example?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const dashboardComponentCatalog: DashboardComponentCatalogEntry[] = [
|
|
32
|
+
// ── Layout and structure ──────────────────────────────────────
|
|
33
|
+
{
|
|
34
|
+
name: "WidgetContent",
|
|
35
|
+
category: "layout",
|
|
36
|
+
kind: "composite",
|
|
37
|
+
summary:
|
|
38
|
+
"Full-height widget frame with standard padding and a container-query boundary.",
|
|
39
|
+
useWhen: "As the outermost element of every dashboard widget.",
|
|
40
|
+
related: ["Stack", "Inline"],
|
|
41
|
+
status: "stable",
|
|
42
|
+
example: "<WidgetContent><Stack gap=\"sm\">…</Stack></WidgetContent>",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "Stack",
|
|
46
|
+
category: "layout",
|
|
47
|
+
kind: "composite",
|
|
48
|
+
summary: "Vertical layout with semantic spacing, alignment and optional scrolling.",
|
|
49
|
+
useWhen: "For the primary top-to-bottom content flow inside a widget.",
|
|
50
|
+
related: ["Inline", "WidgetContent"],
|
|
51
|
+
status: "stable",
|
|
52
|
+
example: "<Stack gap=\"sm\" scroll>…</Stack>",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "Inline",
|
|
56
|
+
category: "layout",
|
|
57
|
+
kind: "composite",
|
|
58
|
+
summary: "Horizontal layout with spacing, alignment, wrap and justify.",
|
|
59
|
+
useWhen: "For a row of items such as a header with a trailing action.",
|
|
60
|
+
related: ["Stack"],
|
|
61
|
+
status: "stable",
|
|
62
|
+
example: "<Inline justify=\"between\" align=\"center\">…</Inline>",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "Grid",
|
|
66
|
+
category: "layout",
|
|
67
|
+
kind: "composite",
|
|
68
|
+
summary: "Responsive grid with bounded columns and gap.",
|
|
69
|
+
useWhen: "For metric and summary layouts that reflow by width.",
|
|
70
|
+
related: ["MetricGroup"],
|
|
71
|
+
status: "stable",
|
|
72
|
+
example: "<Grid columns=\"auto\" minColumnWidth={120}>…</Grid>",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "Section",
|
|
76
|
+
category: "layout",
|
|
77
|
+
kind: "composite",
|
|
78
|
+
summary: "Compact section with optional heading, description and trailing action.",
|
|
79
|
+
useWhen: "To group related content under a labelled heading.",
|
|
80
|
+
related: ["Divider", "Heading"],
|
|
81
|
+
status: "stable",
|
|
82
|
+
example: "<Section heading=\"Summary\" action={…}>…</Section>",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "Divider",
|
|
86
|
+
category: "layout",
|
|
87
|
+
kind: "composite",
|
|
88
|
+
summary: "Compact horizontal or vertical divider built on Separator.",
|
|
89
|
+
useWhen: "To separate sections without a heavy border.",
|
|
90
|
+
related: ["Section"],
|
|
91
|
+
status: "stable",
|
|
92
|
+
example: "<Divider spacing=\"sm\" />",
|
|
93
|
+
},
|
|
94
|
+
// ── Typography and media ──────────────────────────────────────
|
|
95
|
+
{
|
|
96
|
+
name: "Text",
|
|
97
|
+
category: "typography",
|
|
98
|
+
kind: "composite",
|
|
99
|
+
summary:
|
|
100
|
+
"Semantic text variants (body, label, supporting, muted, numeric) with truncation.",
|
|
101
|
+
useWhen: "For any text, instead of ad-hoc font-size utilities.",
|
|
102
|
+
related: ["Heading"],
|
|
103
|
+
status: "stable",
|
|
104
|
+
example: "<Text variant=\"label\">Jobs</Text>",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "Heading",
|
|
108
|
+
category: "typography",
|
|
109
|
+
kind: "composite",
|
|
110
|
+
summary: "Compact heading levels and sizes that keep correct HTML semantics.",
|
|
111
|
+
useWhen: "For section and view titles.",
|
|
112
|
+
related: ["Section", "Text"],
|
|
113
|
+
status: "stable",
|
|
114
|
+
example: "<Heading level={2} size=\"md\">Overview</Heading>",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "Icon",
|
|
118
|
+
category: "typography",
|
|
119
|
+
kind: "composite",
|
|
120
|
+
summary: "Consistent icon wrapper for size, tone and accessibility.",
|
|
121
|
+
useWhen: "To render an icon component with a shared size and tone.",
|
|
122
|
+
related: ["IconButton"],
|
|
123
|
+
status: "stable",
|
|
124
|
+
example: "<Icon icon={Clock} size=\"sm\" tone=\"warning\" />",
|
|
125
|
+
},
|
|
126
|
+
// ── Data display ──────────────────────────────────────────────
|
|
127
|
+
{
|
|
128
|
+
name: "Metric",
|
|
129
|
+
category: "data-display",
|
|
130
|
+
kind: "composite",
|
|
131
|
+
summary: "Label + value with optional icon, supporting text and trend.",
|
|
132
|
+
useWhen: "To show a single number inline.",
|
|
133
|
+
related: ["MetricCard", "MetricGroup"],
|
|
134
|
+
status: "stable",
|
|
135
|
+
example: "<Metric label=\"Jobs\" value={6} />",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "MetricCard",
|
|
139
|
+
category: "data-display",
|
|
140
|
+
kind: "composite",
|
|
141
|
+
summary: "A contained surface for a Metric or compact summary.",
|
|
142
|
+
useWhen: "To give a metric its own bounded card.",
|
|
143
|
+
related: ["Metric", "MetricGroup"],
|
|
144
|
+
status: "stable",
|
|
145
|
+
example: "<MetricCard><Metric label=\"Jobs\" value={6} /></MetricCard>",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "MetricGroup",
|
|
149
|
+
category: "data-display",
|
|
150
|
+
kind: "composite",
|
|
151
|
+
summary: "Arranges multiple metrics responsively in a bounded grid.",
|
|
152
|
+
useWhen: "To show several metrics that reflow by width.",
|
|
153
|
+
related: ["Metric", "Grid"],
|
|
154
|
+
status: "stable",
|
|
155
|
+
example: "<MetricGroup><Metric … /><Metric … /></MetricGroup>",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "Status",
|
|
159
|
+
category: "data-display",
|
|
160
|
+
kind: "composite",
|
|
161
|
+
summary: "Semantic status dot, label or pill with neutral/success/warning/error/info tones.",
|
|
162
|
+
useWhen: "To show a health or state indicator. Map domain status onto a tone.",
|
|
163
|
+
related: ["Badge"],
|
|
164
|
+
status: "stable",
|
|
165
|
+
example: "<Status tone=\"success\">Active</Status>",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "Badge",
|
|
169
|
+
category: "data-display",
|
|
170
|
+
kind: "primitive",
|
|
171
|
+
summary: "Existing badge primitive, extended with success/warning/info tones.",
|
|
172
|
+
useWhen: "For compact counts, tags and category labels.",
|
|
173
|
+
related: ["Status"],
|
|
174
|
+
status: "stable",
|
|
175
|
+
example: "<Badge variant=\"success\">Passed</Badge>",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "KeyValue",
|
|
179
|
+
category: "data-display",
|
|
180
|
+
kind: "composite",
|
|
181
|
+
summary: "A single label/value row with predictable alignment and truncation.",
|
|
182
|
+
useWhen: "For metadata, totals and configuration summaries.",
|
|
183
|
+
related: ["KeyValueList"],
|
|
184
|
+
status: "stable",
|
|
185
|
+
example: "<KeyValue label=\"Region\" value=\"eu-west-1\" mono />",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: "KeyValueList",
|
|
189
|
+
category: "data-display",
|
|
190
|
+
kind: "composite",
|
|
191
|
+
summary: "A vertical list of KeyValue rows.",
|
|
192
|
+
useWhen: "For a block of metadata pairs.",
|
|
193
|
+
related: ["KeyValue"],
|
|
194
|
+
status: "stable",
|
|
195
|
+
example: "<KeyValueList><KeyValue … /></KeyValueList>",
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "ItemList",
|
|
199
|
+
category: "data-display",
|
|
200
|
+
kind: "composite",
|
|
201
|
+
summary: "Compact rows with leading media, text, trailing metadata, actions and overflow.",
|
|
202
|
+
useWhen: "For entity lists (files, notes, results).",
|
|
203
|
+
related: ["ItemListItem", "ActivityList"],
|
|
204
|
+
status: "stable",
|
|
205
|
+
example: "<ItemList overflowCount={3}><ItemListItem … /></ItemList>",
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "ItemListItem",
|
|
209
|
+
category: "data-display",
|
|
210
|
+
kind: "composite",
|
|
211
|
+
summary: "A single compact list row with media, primary/secondary text and actions.",
|
|
212
|
+
useWhen: "As the row inside an ItemList.",
|
|
213
|
+
related: ["ItemList"],
|
|
214
|
+
status: "stable",
|
|
215
|
+
example: "<ItemListItem primary=\"Report.pdf\" trailing=\"2h\" />",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "ActivityList",
|
|
219
|
+
category: "data-display",
|
|
220
|
+
kind: "composite",
|
|
221
|
+
summary: "A chronological list for recent events with a shared overflow footer.",
|
|
222
|
+
useWhen: "For time-ordered activity or upcoming events.",
|
|
223
|
+
related: ["ActivityListItem", "ItemList"],
|
|
224
|
+
status: "stable",
|
|
225
|
+
example: "<ActivityList><ActivityListItem … /></ActivityList>",
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: "ActivityListItem",
|
|
229
|
+
category: "data-display",
|
|
230
|
+
kind: "composite",
|
|
231
|
+
summary: "An event row with a status marker or icon, label, timestamp and detail.",
|
|
232
|
+
useWhen: "As the row inside an ActivityList.",
|
|
233
|
+
related: ["ActivityList", "Status"],
|
|
234
|
+
status: "stable",
|
|
235
|
+
example: "<ActivityListItem label=\"Backup\" timestamp=\"08:00\" tone=\"success\" />",
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "ProgressRing",
|
|
239
|
+
category: "data-display",
|
|
240
|
+
kind: "composite",
|
|
241
|
+
summary: "Accessible circular progress / donut gauge, no charting dependency.",
|
|
242
|
+
useWhen: "For a bounded value such as a success rate or usage percentage.",
|
|
243
|
+
related: ["Metric"],
|
|
244
|
+
status: "stable",
|
|
245
|
+
example: "<ProgressRing value={75} label=\"Success rate\" tone=\"success\" />",
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: "StaleIndicator",
|
|
249
|
+
category: "data-display",
|
|
250
|
+
kind: "composite",
|
|
251
|
+
summary: "Compact hint that data may be out of date, with optional refresh action.",
|
|
252
|
+
useWhen: "When displayed data can lag its source.",
|
|
253
|
+
related: ["DataBoundary"],
|
|
254
|
+
status: "stable",
|
|
255
|
+
example: "<StaleIndicator lastUpdated=\"Updated 5m ago\" onRefresh={…} />",
|
|
256
|
+
},
|
|
257
|
+
// ── Runtime states and feedback ───────────────────────────────
|
|
258
|
+
{
|
|
259
|
+
name: "DataBoundary",
|
|
260
|
+
category: "state",
|
|
261
|
+
kind: "composite",
|
|
262
|
+
summary: "Selects presentation for loading / ready / empty / stale / error states.",
|
|
263
|
+
useWhen: "To switch a widget's content by data state without owning fetching.",
|
|
264
|
+
related: ["EmptyState", "MetricSkeleton", "StaleIndicator"],
|
|
265
|
+
status: "stable",
|
|
266
|
+
example: "<DataBoundary state={state} loading={…} empty={…} error={…}>…</DataBoundary>",
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
name: "EmptyState",
|
|
270
|
+
category: "state",
|
|
271
|
+
kind: "composite",
|
|
272
|
+
summary: "Compact empty state built on the Empty primitives.",
|
|
273
|
+
useWhen: "When there is nothing to show yet.",
|
|
274
|
+
related: ["DataBoundary"],
|
|
275
|
+
status: "stable",
|
|
276
|
+
example: "<EmptyState icon={Inbox} title=\"No activity yet\" />",
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: "Alert",
|
|
280
|
+
category: "state",
|
|
281
|
+
kind: "primitive",
|
|
282
|
+
summary: "Existing alert primitive for inline warnings and errors.",
|
|
283
|
+
useWhen: "For a persistent inline message inside a widget or view.",
|
|
284
|
+
related: ["DataBoundary"],
|
|
285
|
+
status: "stable",
|
|
286
|
+
example: "<Alert variant=\"destructive\">Failed to load</Alert>",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: "MetricSkeleton",
|
|
290
|
+
category: "state",
|
|
291
|
+
kind: "composite",
|
|
292
|
+
summary: "Loading placeholder for a row of metrics.",
|
|
293
|
+
useWhen: "As the `loading` fallback for metric layouts.",
|
|
294
|
+
related: ["DataBoundary", "ListSkeleton"],
|
|
295
|
+
status: "stable",
|
|
296
|
+
example: "<MetricSkeleton count={3} />",
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: "ListSkeleton",
|
|
300
|
+
category: "state",
|
|
301
|
+
kind: "composite",
|
|
302
|
+
summary: "Loading placeholder for a list of rows.",
|
|
303
|
+
useWhen: "As the `loading` fallback for item lists.",
|
|
304
|
+
related: ["DataBoundary", "ItemList"],
|
|
305
|
+
status: "stable",
|
|
306
|
+
example: "<ListSkeleton count={4} />",
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: "ActivitySkeleton",
|
|
310
|
+
category: "state",
|
|
311
|
+
kind: "composite",
|
|
312
|
+
summary: "Loading placeholder for an activity list.",
|
|
313
|
+
useWhen: "As the `loading` fallback for activity feeds.",
|
|
314
|
+
related: ["DataBoundary", "ActivityList"],
|
|
315
|
+
status: "stable",
|
|
316
|
+
example: "<ActivitySkeleton count={4} />",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
name: "Skeleton",
|
|
320
|
+
category: "state",
|
|
321
|
+
kind: "primitive",
|
|
322
|
+
summary: "Existing skeleton primitive for bespoke loading shapes.",
|
|
323
|
+
useWhen: "When the prebuilt skeleton patterns do not fit.",
|
|
324
|
+
related: ["MetricSkeleton", "ListSkeleton", "ActivitySkeleton"],
|
|
325
|
+
status: "stable",
|
|
326
|
+
example: "<Skeleton className=\"h-4 w-24\" />",
|
|
327
|
+
},
|
|
328
|
+
// ── Actions ───────────────────────────────────────────────────
|
|
329
|
+
{
|
|
330
|
+
name: "Button",
|
|
331
|
+
category: "action",
|
|
332
|
+
kind: "primitive",
|
|
333
|
+
summary: "Existing button primitive, the standard text-action control.",
|
|
334
|
+
useWhen: "For a labelled action.",
|
|
335
|
+
related: ["IconButton"],
|
|
336
|
+
status: "stable",
|
|
337
|
+
example: "<Button size=\"sm\">Open</Button>",
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: "IconButton",
|
|
341
|
+
category: "action",
|
|
342
|
+
kind: "composite",
|
|
343
|
+
summary: "Small icon-only button with a required accessible label.",
|
|
344
|
+
useWhen: "For a compact action such as refresh or dismiss.",
|
|
345
|
+
related: ["Button", "Icon"],
|
|
346
|
+
status: "stable",
|
|
347
|
+
example: "<IconButton icon={RotateCw} label=\"Refresh\" size=\"xs\" />",
|
|
348
|
+
},
|
|
349
|
+
];
|
|
350
|
+
|
|
351
|
+
export default dashboardComponentCatalog;
|