@sero-ai/ui 0.3.2 → 0.4.1
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 +615 -0
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +58 -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 +1 -0
- package/dist/components/dashboard/catalog.d.cts +13 -0
- package/dist/components/dashboard/catalog.d.ts +13 -0
- package/dist/components/dashboard/catalog.js +0 -0
- package/dist/components/dashboard/catalog.json +312 -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 +66 -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 +66 -0
- package/dist/components/dashboard/tone.cjs +34 -0
- package/dist/components/dashboard/tone.d.cts +15 -0
- package/dist/components/dashboard/tone.d.ts +15 -0
- package/dist/components/dashboard/tone.js +34 -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/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/index.cjs +1 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1 -0
- package/dist/reference.cjs +1260 -0
- package/dist/reference.d.cts +11 -0
- package/dist/reference.d.ts +11 -0
- package/dist/reference.js +1230 -0
- package/dist/styles/glass-board.css +43 -0
- package/dist/styles/globals.css +59 -0
- package/package.json +16 -4
- package/scripts/ensure-pnpm-publish.mjs +23 -0
- package/scripts/smoke-dist.mjs +68 -0
- 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 +62 -0
- package/src/components/dashboard/__tests__/glass.test.tsx +42 -0
- package/src/components/dashboard/__tests__/state.test.tsx +127 -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.json +312 -0
- package/src/components/dashboard/catalog.ts +29 -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 +88 -0
- package/src/components/dashboard/tone.ts +48 -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/index.ts +7 -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 +18 -4
- package/tsup.reference.config.ts +15 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// StaleIndicator — a compact hint that displayed data may be out of date.
|
|
2
|
+
//
|
|
3
|
+
// Shows an optional last-updated value and an optional refresh action. The
|
|
4
|
+
// component owns no fetching; the plugin passes the label and handles refresh.
|
|
5
|
+
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { Clock, RotateCw } from "lucide-react";
|
|
8
|
+
|
|
9
|
+
import { cn } from "../../lib/utils";
|
|
10
|
+
import { Icon } from "./typography";
|
|
11
|
+
import { IconButton } from "./icon-button";
|
|
12
|
+
|
|
13
|
+
export interface StaleIndicatorProps extends React.ComponentProps<"div"> {
|
|
14
|
+
/** Pre-formatted last-updated value, e.g. "Updated 5m ago". */
|
|
15
|
+
lastUpdated?: React.ReactNode;
|
|
16
|
+
/** Refresh handler. When provided, a refresh button is shown. */
|
|
17
|
+
onRefresh?: () => void;
|
|
18
|
+
/** Spin the refresh icon while a refresh is in flight. */
|
|
19
|
+
refreshing?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Compact "data may be stale" indicator with optional refresh. */
|
|
23
|
+
function StaleIndicator({
|
|
24
|
+
className,
|
|
25
|
+
lastUpdated,
|
|
26
|
+
onRefresh,
|
|
27
|
+
refreshing = false,
|
|
28
|
+
...props
|
|
29
|
+
}: StaleIndicatorProps) {
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
data-slot="stale-indicator"
|
|
33
|
+
className={cn(
|
|
34
|
+
"flex items-center gap-1.5 text-[11px] text-[var(--text-muted)]",
|
|
35
|
+
className,
|
|
36
|
+
)}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
<Icon icon={Clock} size="xs" tone="warning" />
|
|
40
|
+
<span className="truncate">{lastUpdated ?? "Data may be out of date"}</span>
|
|
41
|
+
{onRefresh && (
|
|
42
|
+
<IconButton
|
|
43
|
+
icon={RotateCw}
|
|
44
|
+
label="Refresh"
|
|
45
|
+
size="xs"
|
|
46
|
+
onClick={onRefresh}
|
|
47
|
+
className={cn("ml-auto", refreshing && "animate-spin")}
|
|
48
|
+
disabled={refreshing}
|
|
49
|
+
/>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { StaleIndicator };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Status — a semantic status dot, label or pill.
|
|
2
|
+
//
|
|
3
|
+
// Replaces the hand-rolled `size-2 rounded-full` dots with inline hex colours
|
|
4
|
+
// that appear across the existing widgets. Encodes only neutral semantic tones,
|
|
5
|
+
// never plugin-specific status names — callers map their domain status onto a
|
|
6
|
+
// tone.
|
|
7
|
+
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
|
|
10
|
+
import { cn } from "../../lib/utils";
|
|
11
|
+
import { toneColor, toneDot, toneLabel, tonePill, type Tone } from "./tone";
|
|
12
|
+
|
|
13
|
+
export interface StatusProps extends React.ComponentProps<"span"> {
|
|
14
|
+
tone?: Tone;
|
|
15
|
+
/** Presentation: coloured dot + label, a tinted pill, or a bare dot. */
|
|
16
|
+
variant?: "dot" | "pill";
|
|
17
|
+
/** Add a soft glow to the dot (e.g. a live/active indicator). */
|
|
18
|
+
pulse?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Semantic status dot, label or pill with neutral/success/warning/error/info tones. */
|
|
22
|
+
function Status({
|
|
23
|
+
className,
|
|
24
|
+
tone = "neutral",
|
|
25
|
+
variant = "dot",
|
|
26
|
+
pulse = false,
|
|
27
|
+
children,
|
|
28
|
+
...props
|
|
29
|
+
}: StatusProps) {
|
|
30
|
+
const hasLabel = children != null && children !== "";
|
|
31
|
+
const hasAccessibleName =
|
|
32
|
+
props["aria-label"] != null || props["aria-labelledby"] != null;
|
|
33
|
+
|
|
34
|
+
// A bare indicator (no visible children) is otherwise silent to assistive
|
|
35
|
+
// tech. Give it a text alternative — the caller's aria-label if provided,
|
|
36
|
+
// else the generic tone name — so its state is announced, not an empty region.
|
|
37
|
+
const hiddenLabel =
|
|
38
|
+
!hasLabel && !hasAccessibleName ? (
|
|
39
|
+
<span className="sr-only">{toneLabel[tone]}</span>
|
|
40
|
+
) : null;
|
|
41
|
+
|
|
42
|
+
if (variant === "pill") {
|
|
43
|
+
return (
|
|
44
|
+
<span
|
|
45
|
+
data-slot="status"
|
|
46
|
+
data-tone={tone}
|
|
47
|
+
className={cn(
|
|
48
|
+
"inline-flex w-fit items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium",
|
|
49
|
+
tonePill[tone],
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
<span className={cn("size-1.5 shrink-0 rounded-full", toneDot[tone])} />
|
|
55
|
+
{children}
|
|
56
|
+
{hiddenLabel}
|
|
57
|
+
</span>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<span
|
|
63
|
+
data-slot="status"
|
|
64
|
+
data-tone={tone}
|
|
65
|
+
// A bare dot carries no visible text, so expose its state to assistive
|
|
66
|
+
// tech via role="status" plus the hidden label below.
|
|
67
|
+
role={hasLabel ? undefined : "status"}
|
|
68
|
+
className={cn(
|
|
69
|
+
"inline-flex items-center gap-1.5 text-xs font-medium text-[var(--text-secondary)]",
|
|
70
|
+
className,
|
|
71
|
+
)}
|
|
72
|
+
{...props}
|
|
73
|
+
>
|
|
74
|
+
<span
|
|
75
|
+
className={cn(
|
|
76
|
+
"size-2 shrink-0 rounded-full",
|
|
77
|
+
toneDot[tone],
|
|
78
|
+
// ring-current picks up the tone's text colour for a matching glow.
|
|
79
|
+
pulse && cn(toneColor[tone], "ring-2 ring-current/25"),
|
|
80
|
+
)}
|
|
81
|
+
/>
|
|
82
|
+
{children}
|
|
83
|
+
{hiddenLabel}
|
|
84
|
+
</span>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { Status };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Shared semantic tone vocabulary for dashboard components.
|
|
2
|
+
//
|
|
3
|
+
// Tones map to the `--status-*` design tokens in styles/globals.css so every
|
|
4
|
+
// dashboard component speaks one status language. Components must never encode
|
|
5
|
+
// plugin-specific status names (e.g. "running", "queued") — they map their
|
|
6
|
+
// domain status onto one of these neutral tones.
|
|
7
|
+
|
|
8
|
+
export type Tone = "neutral" | "success" | "warning" | "error" | "info";
|
|
9
|
+
|
|
10
|
+
/** Solid foreground colour for a tone (dots, icons, emphasised text). */
|
|
11
|
+
export const toneColor: Record<Tone, string> = {
|
|
12
|
+
neutral: "text-[var(--text-muted)]",
|
|
13
|
+
success: "text-[var(--status-success)]",
|
|
14
|
+
warning: "text-[var(--status-warning)]",
|
|
15
|
+
error: "text-[var(--status-error)]",
|
|
16
|
+
info: "text-[var(--status-info)]",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** Solid background colour for a tone (status dots). */
|
|
20
|
+
export const toneDot: Record<Tone, string> = {
|
|
21
|
+
neutral: "bg-[var(--text-muted)]",
|
|
22
|
+
success: "bg-[var(--status-success)]",
|
|
23
|
+
warning: "bg-[var(--status-warning)]",
|
|
24
|
+
error: "bg-[var(--status-error)]",
|
|
25
|
+
info: "bg-[var(--status-info)]",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Default accessible name for a tone, used as the text alternative when a
|
|
30
|
+
* status indicator carries no visible label. Generic by design — callers with
|
|
31
|
+
* a domain-specific meaning should pass their own `aria-label`.
|
|
32
|
+
*/
|
|
33
|
+
export const toneLabel: Record<Tone, string> = {
|
|
34
|
+
neutral: "Neutral",
|
|
35
|
+
success: "Success",
|
|
36
|
+
warning: "Warning",
|
|
37
|
+
error: "Error",
|
|
38
|
+
info: "Info",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** Muted tinted background + matching text for a tone (pills, chips). */
|
|
42
|
+
export const tonePill: Record<Tone, string> = {
|
|
43
|
+
neutral: "bg-[var(--surface-flat)] text-[var(--text-secondary)]",
|
|
44
|
+
success: "bg-[var(--status-success-muted)] text-[var(--status-success)]",
|
|
45
|
+
warning: "bg-[var(--status-warning-muted)] text-[var(--status-warning)]",
|
|
46
|
+
error: "bg-[var(--status-error-muted)] text-[var(--status-error)]",
|
|
47
|
+
info: "bg-[var(--status-info-muted)] text-[var(--status-info)]",
|
|
48
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Typography and media primitives for dashboard widgets.
|
|
2
|
+
//
|
|
3
|
+
// Text, Heading and Icon give one type scale and one icon treatment so
|
|
4
|
+
// equivalent text and icons render identically across widgets, replacing the
|
|
5
|
+
// scattered `text-[9px]`/`text-[10px]` and per-widget muted colours.
|
|
6
|
+
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
9
|
+
|
|
10
|
+
import { cn } from "../../lib/utils";
|
|
11
|
+
import { toneColor, type Tone } from "./tone";
|
|
12
|
+
|
|
13
|
+
// ── Text ─────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
const textVariants = cva("min-w-0", {
|
|
16
|
+
variants: {
|
|
17
|
+
variant: {
|
|
18
|
+
body: "text-sm text-[var(--text-primary)]",
|
|
19
|
+
label:
|
|
20
|
+
"text-[11px] font-semibold uppercase tracking-wider text-[var(--text-muted)]",
|
|
21
|
+
supporting: "text-xs text-[var(--text-secondary)]",
|
|
22
|
+
muted: "text-xs text-[var(--text-muted)]",
|
|
23
|
+
numeric:
|
|
24
|
+
"text-sm font-semibold tabular-nums text-[var(--text-primary)]",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: { variant: "body" },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export interface TextProps
|
|
31
|
+
extends Omit<React.ComponentProps<"span">, "color">,
|
|
32
|
+
VariantProps<typeof textVariants> {
|
|
33
|
+
/** Truncate to a single line with an ellipsis. */
|
|
34
|
+
truncate?: boolean;
|
|
35
|
+
/** Clamp to N lines. Overrides `truncate`. */
|
|
36
|
+
clamp?: 1 | 2 | 3;
|
|
37
|
+
/** Semantic colour override (e.g. a numeric value that turns red on error). */
|
|
38
|
+
tone?: Tone;
|
|
39
|
+
asChild?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const clampClass = {
|
|
43
|
+
1: "line-clamp-1",
|
|
44
|
+
2: "line-clamp-2",
|
|
45
|
+
3: "line-clamp-3",
|
|
46
|
+
} as const;
|
|
47
|
+
|
|
48
|
+
/** Semantic text with truncation, line-clamp and optional tone. */
|
|
49
|
+
function Text({
|
|
50
|
+
className,
|
|
51
|
+
variant,
|
|
52
|
+
truncate,
|
|
53
|
+
clamp,
|
|
54
|
+
tone,
|
|
55
|
+
...props
|
|
56
|
+
}: TextProps) {
|
|
57
|
+
return (
|
|
58
|
+
<span
|
|
59
|
+
data-slot="text"
|
|
60
|
+
className={cn(
|
|
61
|
+
textVariants({ variant }),
|
|
62
|
+
clamp ? clampClass[clamp] : truncate && "truncate",
|
|
63
|
+
tone && toneColor[tone],
|
|
64
|
+
className,
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── Heading ──────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
const headingSize = {
|
|
74
|
+
sm: "text-xs font-semibold",
|
|
75
|
+
md: "text-sm font-semibold",
|
|
76
|
+
lg: "text-base font-semibold",
|
|
77
|
+
} as const;
|
|
78
|
+
|
|
79
|
+
export interface HeadingProps extends React.ComponentProps<"h2"> {
|
|
80
|
+
/** HTML heading level for correct document semantics (1–4). */
|
|
81
|
+
level?: 1 | 2 | 3 | 4;
|
|
82
|
+
/** Visual size, decoupled from the semantic level. */
|
|
83
|
+
size?: keyof typeof headingSize;
|
|
84
|
+
truncate?: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Compact heading with correct HTML semantics decoupled from visual size. */
|
|
88
|
+
function Heading({
|
|
89
|
+
className,
|
|
90
|
+
level = 2,
|
|
91
|
+
size = "md",
|
|
92
|
+
truncate,
|
|
93
|
+
...props
|
|
94
|
+
}: HeadingProps) {
|
|
95
|
+
const Tag = `h${level}` as "h1" | "h2" | "h3" | "h4";
|
|
96
|
+
return (
|
|
97
|
+
<Tag
|
|
98
|
+
data-slot="heading"
|
|
99
|
+
className={cn(
|
|
100
|
+
"leading-snug text-[var(--text-primary)]",
|
|
101
|
+
headingSize[size],
|
|
102
|
+
truncate && "truncate",
|
|
103
|
+
className,
|
|
104
|
+
)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── Icon ─────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
const iconSize = {
|
|
113
|
+
xs: "size-3",
|
|
114
|
+
sm: "size-3.5",
|
|
115
|
+
md: "size-4",
|
|
116
|
+
lg: "size-5",
|
|
117
|
+
} as const;
|
|
118
|
+
|
|
119
|
+
export interface IconProps extends React.ComponentProps<"span"> {
|
|
120
|
+
/** The icon component to render (e.g. a lucide-react icon). */
|
|
121
|
+
icon: React.ComponentType<{ className?: string }>;
|
|
122
|
+
size?: keyof typeof iconSize;
|
|
123
|
+
tone?: Tone;
|
|
124
|
+
/**
|
|
125
|
+
* Accessible label. When omitted the icon is decorative and hidden from
|
|
126
|
+
* assistive tech; when provided it is exposed as an image with this name.
|
|
127
|
+
*/
|
|
128
|
+
label?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Consistent icon wrapper for size, tone and accessibility. */
|
|
132
|
+
function Icon({ className, icon: IconCmp, size = "md", tone, label, ...props }: IconProps) {
|
|
133
|
+
return (
|
|
134
|
+
<span
|
|
135
|
+
data-slot="icon"
|
|
136
|
+
role={label ? "img" : undefined}
|
|
137
|
+
aria-label={label}
|
|
138
|
+
aria-hidden={label ? undefined : true}
|
|
139
|
+
className={cn(
|
|
140
|
+
"inline-flex shrink-0 items-center justify-center",
|
|
141
|
+
tone ? toneColor[tone] : "text-[var(--text-muted)]",
|
|
142
|
+
className,
|
|
143
|
+
)}
|
|
144
|
+
{...props}
|
|
145
|
+
>
|
|
146
|
+
<IconCmp className={iconSize[size]} />
|
|
147
|
+
</span>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { Text, Heading, Icon };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// ActivityExample — a scrollable item list with a header count and a
|
|
2
|
+
// footer status row.
|
|
3
|
+
//
|
|
4
|
+
// Pure static presentation; a real feed would wire its rows through a
|
|
5
|
+
// DataBoundary (loading / empty / error) driven by plugin state. See the
|
|
6
|
+
// runtime-state components in the catalogue for those slots.
|
|
7
|
+
|
|
8
|
+
import { FileText } from "lucide-react";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
Inline,
|
|
12
|
+
ItemList,
|
|
13
|
+
ItemListItem,
|
|
14
|
+
Stack,
|
|
15
|
+
StaleIndicator,
|
|
16
|
+
Status,
|
|
17
|
+
Text,
|
|
18
|
+
WidgetContent,
|
|
19
|
+
} from "../dashboard";
|
|
20
|
+
import { Badge } from "../ui/badge";
|
|
21
|
+
|
|
22
|
+
const ITEMS = [
|
|
23
|
+
{ id: "1", title: "quarterly-report.pdf", meta: "2h", badge: "PDF" },
|
|
24
|
+
{ id: "2", title: "design-tokens.json", meta: "5h", badge: "JSON" },
|
|
25
|
+
{ id: "3", title: "release-notes.md", meta: "1d", badge: "MD" },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export function ActivityExample() {
|
|
29
|
+
return (
|
|
30
|
+
<WidgetContent>
|
|
31
|
+
<Stack gap="sm" fill>
|
|
32
|
+
<Inline justify="between" align="center">
|
|
33
|
+
<Inline gap="xs" align="center">
|
|
34
|
+
<Text variant="label">Recent files</Text>
|
|
35
|
+
<Badge variant="info">{ITEMS.length}</Badge>
|
|
36
|
+
</Inline>
|
|
37
|
+
</Inline>
|
|
38
|
+
|
|
39
|
+
<Stack gap="none" scroll>
|
|
40
|
+
<ItemList>
|
|
41
|
+
{ITEMS.map((f) => (
|
|
42
|
+
<ItemListItem
|
|
43
|
+
key={f.id}
|
|
44
|
+
leading={<FileText className="size-4 text-[var(--text-muted)]" />}
|
|
45
|
+
primary={f.title}
|
|
46
|
+
trailing={
|
|
47
|
+
<Inline gap="xs" align="center">
|
|
48
|
+
<Badge variant="secondary">{f.badge}</Badge>
|
|
49
|
+
<span>{f.meta}</span>
|
|
50
|
+
</Inline>
|
|
51
|
+
}
|
|
52
|
+
/>
|
|
53
|
+
))}
|
|
54
|
+
</ItemList>
|
|
55
|
+
</Stack>
|
|
56
|
+
|
|
57
|
+
<Inline justify="between" align="center">
|
|
58
|
+
<Status tone="info" variant="pill">
|
|
59
|
+
Synced
|
|
60
|
+
</Status>
|
|
61
|
+
<StaleIndicator lastUpdated="Synced 30s ago" />
|
|
62
|
+
</Inline>
|
|
63
|
+
</Stack>
|
|
64
|
+
</WidgetContent>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default ActivityExample;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// ResourceExample — a grid, gauges, key/value metadata and headings.
|
|
2
|
+
//
|
|
3
|
+
// Pure static presentation; a real resource monitor would read live host
|
|
4
|
+
// metrics from its plugin.
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Divider,
|
|
8
|
+
Grid,
|
|
9
|
+
Heading,
|
|
10
|
+
KeyValue,
|
|
11
|
+
KeyValueList,
|
|
12
|
+
Metric,
|
|
13
|
+
MetricGroup,
|
|
14
|
+
ProgressRing,
|
|
15
|
+
Section,
|
|
16
|
+
Stack,
|
|
17
|
+
Status,
|
|
18
|
+
Text,
|
|
19
|
+
WidgetContent,
|
|
20
|
+
} from "../dashboard";
|
|
21
|
+
|
|
22
|
+
function Ring({
|
|
23
|
+
value,
|
|
24
|
+
label,
|
|
25
|
+
tone,
|
|
26
|
+
}: {
|
|
27
|
+
value: number;
|
|
28
|
+
label: string;
|
|
29
|
+
tone: "success" | "info" | "warning";
|
|
30
|
+
}) {
|
|
31
|
+
return (
|
|
32
|
+
<Stack gap="xs" align="center">
|
|
33
|
+
<ProgressRing value={value} label={label} tone={tone} />
|
|
34
|
+
<Text variant="muted">{label}</Text>
|
|
35
|
+
</Stack>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function ResourceExample() {
|
|
40
|
+
return (
|
|
41
|
+
<WidgetContent scroll>
|
|
42
|
+
<Stack gap="sm">
|
|
43
|
+
<Heading level={2} size="md">
|
|
44
|
+
Resources
|
|
45
|
+
</Heading>
|
|
46
|
+
|
|
47
|
+
<MetricGroup>
|
|
48
|
+
<Metric label="CPU" value="42%" tone="success" />
|
|
49
|
+
<Metric label="Memory" value="6.1 GB" />
|
|
50
|
+
<Metric label="Disk" value="71%" tone="warning" />
|
|
51
|
+
</MetricGroup>
|
|
52
|
+
|
|
53
|
+
<Divider />
|
|
54
|
+
|
|
55
|
+
<Section heading="Usage">
|
|
56
|
+
<Grid columns={3} gap="sm">
|
|
57
|
+
<Ring value={42} label="CPU" tone="success" />
|
|
58
|
+
<Ring value={61} label="Memory" tone="info" />
|
|
59
|
+
<Ring value={71} label="Disk" tone="warning" />
|
|
60
|
+
</Grid>
|
|
61
|
+
</Section>
|
|
62
|
+
|
|
63
|
+
<Divider />
|
|
64
|
+
|
|
65
|
+
<Section heading="Host">
|
|
66
|
+
<KeyValueList>
|
|
67
|
+
<KeyValue label="Region" value="eu-west-1" mono />
|
|
68
|
+
<KeyValue label="Instance" value="c7g.xlarge" mono />
|
|
69
|
+
<KeyValue label="Uptime" value="14d 6h" mono />
|
|
70
|
+
<KeyValue label="Status" value={<Status tone="success">Healthy</Status>} />
|
|
71
|
+
</KeyValueList>
|
|
72
|
+
</Section>
|
|
73
|
+
</Stack>
|
|
74
|
+
</WidgetContent>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default ResourceExample;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// SchedulerExample — layout, metric cards, a gauge and an activity group.
|
|
2
|
+
//
|
|
3
|
+
// Pure static presentation (fixed sample data, no state or controls); a real
|
|
4
|
+
// scheduler widget would read jobs and run stats from its plugin.
|
|
5
|
+
|
|
6
|
+
import { ExternalLink } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
ActivityList,
|
|
10
|
+
ActivityListItem,
|
|
11
|
+
DataBoundary,
|
|
12
|
+
Divider,
|
|
13
|
+
Inline,
|
|
14
|
+
Metric,
|
|
15
|
+
MetricCard,
|
|
16
|
+
ProgressRing,
|
|
17
|
+
Section,
|
|
18
|
+
Stack,
|
|
19
|
+
StaleIndicator,
|
|
20
|
+
Status,
|
|
21
|
+
WidgetContent,
|
|
22
|
+
} from "../dashboard";
|
|
23
|
+
import { Button } from "../ui/button";
|
|
24
|
+
|
|
25
|
+
const UPCOMING = [
|
|
26
|
+
{ id: "a", label: "Daily backup", timestamp: "08:00", tone: "success" as const },
|
|
27
|
+
{ id: "b", label: "Publish report", timestamp: "in 2h", tone: "info" as const },
|
|
28
|
+
{ id: "c", label: "Rotate API keys", timestamp: "tomorrow", tone: "warning" as const },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export function SchedulerExample() {
|
|
32
|
+
return (
|
|
33
|
+
<WidgetContent>
|
|
34
|
+
<Stack gap="sm">
|
|
35
|
+
<Inline justify="between" align="center">
|
|
36
|
+
<Status tone="success" pulse>
|
|
37
|
+
Scheduler active
|
|
38
|
+
</Status>
|
|
39
|
+
<ProgressRing value={92} label="Success rate" tone="success" size={40} />
|
|
40
|
+
</Inline>
|
|
41
|
+
|
|
42
|
+
<Section heading="Summary">
|
|
43
|
+
<Inline gap="sm" wrap>
|
|
44
|
+
<MetricCard className="flex-1">
|
|
45
|
+
<Metric label="Jobs" value={6} />
|
|
46
|
+
</MetricCard>
|
|
47
|
+
<MetricCard className="flex-1">
|
|
48
|
+
<Metric
|
|
49
|
+
label="Runs today"
|
|
50
|
+
value={18}
|
|
51
|
+
trend={{ direction: "up", value: "+4", tone: "success" }}
|
|
52
|
+
/>
|
|
53
|
+
</MetricCard>
|
|
54
|
+
</Inline>
|
|
55
|
+
</Section>
|
|
56
|
+
|
|
57
|
+
<Divider />
|
|
58
|
+
|
|
59
|
+
<Section
|
|
60
|
+
heading="Next up"
|
|
61
|
+
action={
|
|
62
|
+
<Button size="xs" variant="ghost">
|
|
63
|
+
<ExternalLink className="size-3" />
|
|
64
|
+
All
|
|
65
|
+
</Button>
|
|
66
|
+
}
|
|
67
|
+
>
|
|
68
|
+
<DataBoundary state="ready">
|
|
69
|
+
<ActivityList>
|
|
70
|
+
{UPCOMING.map((e) => (
|
|
71
|
+
<ActivityListItem
|
|
72
|
+
key={e.id}
|
|
73
|
+
tone={e.tone}
|
|
74
|
+
label={e.label}
|
|
75
|
+
timestamp={e.timestamp}
|
|
76
|
+
/>
|
|
77
|
+
))}
|
|
78
|
+
</ActivityList>
|
|
79
|
+
</DataBoundary>
|
|
80
|
+
</Section>
|
|
81
|
+
|
|
82
|
+
<StaleIndicator lastUpdated="Updated 2m ago" />
|
|
83
|
+
</Stack>
|
|
84
|
+
</WidgetContent>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default SchedulerExample;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// StarterExample — the smallest useful widget: a headline metric and a
|
|
2
|
+
// short list. Copy this as the starting point for a new dashboard widget.
|
|
3
|
+
//
|
|
4
|
+
// Reference widgets are pure static presentation (fixed sample data, no
|
|
5
|
+
// state or controls) so they render identically anywhere. A real widget
|
|
6
|
+
// swaps the sample data for state read from its plugin.
|
|
7
|
+
|
|
8
|
+
import { CheckCircle2 } from "lucide-react";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
ActivityList,
|
|
12
|
+
ActivityListItem,
|
|
13
|
+
Metric,
|
|
14
|
+
Section,
|
|
15
|
+
Stack,
|
|
16
|
+
WidgetContent,
|
|
17
|
+
} from "../dashboard";
|
|
18
|
+
|
|
19
|
+
const RECENT = [
|
|
20
|
+
{ id: "a", label: "Nightly build", timestamp: "12m", tone: "success" as const },
|
|
21
|
+
{ id: "b", label: "Deploy to staging", timestamp: "1h", tone: "info" as const },
|
|
22
|
+
{ id: "c", label: "Dependency audit", timestamp: "3h", tone: "neutral" as const },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export function StarterExample() {
|
|
26
|
+
return (
|
|
27
|
+
<WidgetContent>
|
|
28
|
+
<Stack gap="sm">
|
|
29
|
+
<Metric label="Tasks done" value={12} icon={CheckCircle2} tone="success" />
|
|
30
|
+
|
|
31
|
+
<Section heading="Recent">
|
|
32
|
+
<ActivityList>
|
|
33
|
+
{RECENT.map((e) => (
|
|
34
|
+
<ActivityListItem
|
|
35
|
+
key={e.id}
|
|
36
|
+
tone={e.tone}
|
|
37
|
+
label={e.label}
|
|
38
|
+
timestamp={e.timestamp}
|
|
39
|
+
/>
|
|
40
|
+
))}
|
|
41
|
+
</ActivityList>
|
|
42
|
+
</Section>
|
|
43
|
+
</Stack>
|
|
44
|
+
</WidgetContent>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default StarterExample;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Reference dashboard widgets — worked examples of the @sero-ai/ui dashboard
|
|
2
|
+
// components, glass-styled and ready to copy. Pure static presentation.
|
|
3
|
+
//
|
|
4
|
+
// Exposed to widget authors via the `@sero-ai/ui/reference` subpath, not the
|
|
5
|
+
// main barrel.
|
|
6
|
+
|
|
7
|
+
export { StarterExample } from "./StarterExample";
|
|
8
|
+
export { SchedulerExample } from "./SchedulerExample";
|
|
9
|
+
export { ResourceExample } from "./ResourceExample";
|
|
10
|
+
export { ActivityExample } from "./ActivityExample";
|
|
@@ -18,6 +18,12 @@ const badgeVariants = cva(
|
|
|
18
18
|
"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
19
19
|
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
20
20
|
link: "text-primary underline-offset-4 [a&]:hover:underline",
|
|
21
|
+
// Semantic status tones (error is covered by `destructive`).
|
|
22
|
+
success:
|
|
23
|
+
"bg-[var(--status-success-muted)] text-[var(--status-success)]",
|
|
24
|
+
warning:
|
|
25
|
+
"bg-[var(--status-warning-muted)] text-[var(--status-warning)]",
|
|
26
|
+
info: "bg-[var(--status-info-muted)] text-[var(--status-info)]",
|
|
21
27
|
},
|
|
22
28
|
},
|
|
23
29
|
defaultVariants: {
|
package/src/index.ts
CHANGED
|
@@ -131,6 +131,13 @@ export * from "./components/ai-elements/voice-selector-accent";
|
|
|
131
131
|
export * from "./components/ai-elements/voice-selector";
|
|
132
132
|
export * from "./components/ai-elements/web-preview";
|
|
133
133
|
|
|
134
|
+
// ── Dashboard Components ──
|
|
135
|
+
// Compact presentation components for dashboard widgets and full plugin views.
|
|
136
|
+
// The discovery catalogue is plain data at "@sero-ai/ui/dashboard-catalog.json";
|
|
137
|
+
// its entry type is re-exported here for callers that type the parsed JSON.
|
|
138
|
+
export * from "./components/dashboard";
|
|
139
|
+
export type { DashboardComponentCatalogEntry } from "./components/dashboard/catalog";
|
|
140
|
+
|
|
134
141
|
// ── Model Selection Components ──
|
|
135
142
|
export * from "./components/model-selection/available-model-picker";
|
|
136
143
|
export * from "./components/model-selection/model-warning-list";
|
package/src/reference.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Stable subpath for the reference dashboard widgets.
|
|
2
|
+
//
|
|
3
|
+
// import { SchedulerExample } from "@sero-ai/ui/reference";
|
|
4
|
+
//
|
|
5
|
+
// These are worked, glass-styled examples of the dashboard components —
|
|
6
|
+
// importable to preview, or copy-pasteable as a starting point for a real
|
|
7
|
+
// widget. Kept out of the main barrel so they don't ship with app bundles
|
|
8
|
+
// that only need the components.
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
StarterExample,
|
|
12
|
+
SchedulerExample,
|
|
13
|
+
ResourceExample,
|
|
14
|
+
ActivityExample,
|
|
15
|
+
} from "./components/reference";
|