@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.
Files changed (169) hide show
  1. package/.turbo/turbo-build.log +615 -0
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +58 -0
  4. package/dist/components/context-editor/CapabilitySection.cjs +57 -0
  5. package/dist/components/context-editor/CapabilitySection.d.cts +20 -0
  6. package/dist/components/context-editor/CapabilitySection.d.ts +20 -0
  7. package/dist/components/context-editor/CapabilitySection.js +57 -0
  8. package/dist/components/context-editor/ContextEditor.cjs +108 -0
  9. package/dist/components/context-editor/ContextEditor.d.cts +26 -0
  10. package/dist/components/context-editor/ContextEditor.d.ts +26 -0
  11. package/dist/components/context-editor/ContextEditor.js +108 -0
  12. package/dist/components/context-editor/PresetBar.cjs +71 -0
  13. package/dist/components/context-editor/PresetBar.d.cts +19 -0
  14. package/dist/components/context-editor/PresetBar.d.ts +19 -0
  15. package/dist/components/context-editor/PresetBar.js +71 -0
  16. package/dist/components/context-editor/SkillsSection.cjs +31 -0
  17. package/dist/components/context-editor/SkillsSection.d.cts +14 -0
  18. package/dist/components/context-editor/SkillsSection.d.ts +14 -0
  19. package/dist/components/context-editor/SkillsSection.js +31 -0
  20. package/dist/components/context-editor/SystemPromptSection.cjs +58 -0
  21. package/dist/components/context-editor/SystemPromptSection.d.cts +25 -0
  22. package/dist/components/context-editor/SystemPromptSection.d.ts +25 -0
  23. package/dist/components/context-editor/SystemPromptSection.js +58 -0
  24. package/dist/components/context-editor/ToolsSection.cjs +31 -0
  25. package/dist/components/context-editor/ToolsSection.d.cts +14 -0
  26. package/dist/components/context-editor/ToolsSection.d.ts +14 -0
  27. package/dist/components/context-editor/ToolsSection.js +31 -0
  28. package/dist/components/context-editor/index.cjs +8 -0
  29. package/dist/components/context-editor/index.d.cts +5 -0
  30. package/dist/components/context-editor/index.d.ts +5 -0
  31. package/dist/components/context-editor/index.js +8 -0
  32. package/dist/components/context-editor/parts.cjs +184 -0
  33. package/dist/components/context-editor/parts.d.cts +32 -0
  34. package/dist/components/context-editor/parts.d.ts +32 -0
  35. package/dist/components/context-editor/parts.js +184 -0
  36. package/dist/components/context-editor/use-context-editor-model.cjs +194 -0
  37. package/dist/components/context-editor/use-context-editor-model.d.cts +57 -0
  38. package/dist/components/context-editor/use-context-editor-model.d.ts +57 -0
  39. package/dist/components/context-editor/use-context-editor-model.js +194 -0
  40. package/dist/components/dashboard/activity-list.cjs +59 -0
  41. package/dist/components/dashboard/activity-list.d.cts +27 -0
  42. package/dist/components/dashboard/activity-list.d.ts +27 -0
  43. package/dist/components/dashboard/activity-list.js +59 -0
  44. package/dist/components/dashboard/catalog.cjs +1 -0
  45. package/dist/components/dashboard/catalog.d.cts +13 -0
  46. package/dist/components/dashboard/catalog.d.ts +13 -0
  47. package/dist/components/dashboard/catalog.js +0 -0
  48. package/dist/components/dashboard/catalog.json +312 -0
  49. package/dist/components/dashboard/data-boundary.cjs +29 -0
  50. package/dist/components/dashboard/data-boundary.d.cts +21 -0
  51. package/dist/components/dashboard/data-boundary.d.ts +21 -0
  52. package/dist/components/dashboard/data-boundary.js +29 -0
  53. package/dist/components/dashboard/empty-state.cjs +49 -0
  54. package/dist/components/dashboard/empty-state.d.cts +20 -0
  55. package/dist/components/dashboard/empty-state.d.ts +20 -0
  56. package/dist/components/dashboard/empty-state.js +49 -0
  57. package/dist/components/dashboard/icon-button.cjs +40 -0
  58. package/dist/components/dashboard/icon-button.d.cts +24 -0
  59. package/dist/components/dashboard/icon-button.d.ts +24 -0
  60. package/dist/components/dashboard/icon-button.js +40 -0
  61. package/dist/components/dashboard/index.cjs +15 -0
  62. package/dist/components/dashboard/index.d.cts +24 -0
  63. package/dist/components/dashboard/index.d.ts +24 -0
  64. package/dist/components/dashboard/index.js +15 -0
  65. package/dist/components/dashboard/item-list.cjs +71 -0
  66. package/dist/components/dashboard/item-list.d.cts +28 -0
  67. package/dist/components/dashboard/item-list.d.ts +28 -0
  68. package/dist/components/dashboard/item-list.js +71 -0
  69. package/dist/components/dashboard/key-value.cjs +39 -0
  70. package/dist/components/dashboard/key-value.d.cts +17 -0
  71. package/dist/components/dashboard/key-value.d.ts +17 -0
  72. package/dist/components/dashboard/key-value.js +39 -0
  73. package/dist/components/dashboard/layout.cjs +193 -0
  74. package/dist/components/dashboard/layout.d.cts +91 -0
  75. package/dist/components/dashboard/layout.d.ts +91 -0
  76. package/dist/components/dashboard/layout.js +193 -0
  77. package/dist/components/dashboard/metric.cjs +98 -0
  78. package/dist/components/dashboard/metric.d.cts +44 -0
  79. package/dist/components/dashboard/metric.d.ts +44 -0
  80. package/dist/components/dashboard/metric.js +98 -0
  81. package/dist/components/dashboard/progress-ring.cjs +77 -0
  82. package/dist/components/dashboard/progress-ring.d.cts +25 -0
  83. package/dist/components/dashboard/progress-ring.d.ts +25 -0
  84. package/dist/components/dashboard/progress-ring.js +77 -0
  85. package/dist/components/dashboard/skeletons.cjs +54 -0
  86. package/dist/components/dashboard/skeletons.d.cts +15 -0
  87. package/dist/components/dashboard/skeletons.d.ts +15 -0
  88. package/dist/components/dashboard/skeletons.js +54 -0
  89. package/dist/components/dashboard/spacing.cjs +10 -0
  90. package/dist/components/dashboard/spacing.d.cts +4 -0
  91. package/dist/components/dashboard/spacing.d.ts +4 -0
  92. package/dist/components/dashboard/spacing.js +10 -0
  93. package/dist/components/dashboard/stale-indicator.cjs +42 -0
  94. package/dist/components/dashboard/stale-indicator.d.cts +15 -0
  95. package/dist/components/dashboard/stale-indicator.d.ts +15 -0
  96. package/dist/components/dashboard/stale-indicator.js +42 -0
  97. package/dist/components/dashboard/status.cjs +66 -0
  98. package/dist/components/dashboard/status.d.cts +15 -0
  99. package/dist/components/dashboard/status.d.ts +15 -0
  100. package/dist/components/dashboard/status.js +66 -0
  101. package/dist/components/dashboard/tone.cjs +34 -0
  102. package/dist/components/dashboard/tone.d.cts +15 -0
  103. package/dist/components/dashboard/tone.d.ts +15 -0
  104. package/dist/components/dashboard/tone.js +34 -0
  105. package/dist/components/dashboard/typography.cjs +99 -0
  106. package/dist/components/dashboard/typography.d.cts +57 -0
  107. package/dist/components/dashboard/typography.d.ts +57 -0
  108. package/dist/components/dashboard/typography.js +99 -0
  109. package/dist/components/ui/badge.cjs +5 -1
  110. package/dist/components/ui/badge.d.cts +1 -1
  111. package/dist/components/ui/badge.d.ts +1 -1
  112. package/dist/components/ui/badge.js +5 -1
  113. package/dist/index.cjs +1 -0
  114. package/dist/index.d.cts +16 -0
  115. package/dist/index.d.ts +16 -0
  116. package/dist/index.js +1 -0
  117. package/dist/reference.cjs +1260 -0
  118. package/dist/reference.d.cts +11 -0
  119. package/dist/reference.d.ts +11 -0
  120. package/dist/reference.js +1230 -0
  121. package/dist/styles/glass-board.css +43 -0
  122. package/dist/styles/globals.css +59 -0
  123. package/package.json +16 -4
  124. package/scripts/ensure-pnpm-publish.mjs +23 -0
  125. package/scripts/smoke-dist.mjs +68 -0
  126. package/src/components/context-editor/CapabilitySection.tsx +85 -0
  127. package/src/components/context-editor/ContextEditor.tsx +138 -0
  128. package/src/components/context-editor/PresetBar.tsx +94 -0
  129. package/src/components/context-editor/SkillsSection.tsx +37 -0
  130. package/src/components/context-editor/SystemPromptSection.tsx +88 -0
  131. package/src/components/context-editor/ToolsSection.tsx +37 -0
  132. package/src/components/context-editor/index.ts +7 -0
  133. package/src/components/context-editor/parts.tsx +217 -0
  134. package/src/components/context-editor/use-context-editor-model.ts +296 -0
  135. package/src/components/dashboard/__tests__/catalog.test.ts +62 -0
  136. package/src/components/dashboard/__tests__/glass.test.tsx +42 -0
  137. package/src/components/dashboard/__tests__/state.test.tsx +127 -0
  138. package/src/components/dashboard/__tests__/variants.test.tsx +95 -0
  139. package/src/components/dashboard/activity-list.tsx +99 -0
  140. package/src/components/dashboard/catalog.json +312 -0
  141. package/src/components/dashboard/catalog.ts +29 -0
  142. package/src/components/dashboard/data-boundary.tsx +54 -0
  143. package/src/components/dashboard/empty-state.tsx +70 -0
  144. package/src/components/dashboard/icon-button.tsx +58 -0
  145. package/src/components/dashboard/index.ts +21 -0
  146. package/src/components/dashboard/item-list.tsx +106 -0
  147. package/src/components/dashboard/key-value.tsx +54 -0
  148. package/src/components/dashboard/layout.tsx +286 -0
  149. package/src/components/dashboard/metric.tsx +139 -0
  150. package/src/components/dashboard/progress-ring.tsx +100 -0
  151. package/src/components/dashboard/skeletons.tsx +73 -0
  152. package/src/components/dashboard/spacing.ts +14 -0
  153. package/src/components/dashboard/stale-indicator.tsx +55 -0
  154. package/src/components/dashboard/status.tsx +88 -0
  155. package/src/components/dashboard/tone.ts +48 -0
  156. package/src/components/dashboard/typography.tsx +151 -0
  157. package/src/components/reference/ActivityExample.tsx +68 -0
  158. package/src/components/reference/ResourceExample.tsx +78 -0
  159. package/src/components/reference/SchedulerExample.tsx +88 -0
  160. package/src/components/reference/StarterExample.tsx +48 -0
  161. package/src/components/reference/index.ts +10 -0
  162. package/src/components/ui/badge.tsx +6 -0
  163. package/src/index.ts +7 -0
  164. package/src/reference.ts +15 -0
  165. package/src/styles/glass-board.css +43 -0
  166. package/src/styles/globals.css +59 -0
  167. package/tsup.config.ts +18 -4
  168. package/tsup.reference.config.ts +15 -0
  169. 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";
@@ -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";