@xemahq/ui-kernel 0.11.0 → 0.11.2

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 (237) hide show
  1. package/dist/ui/chrome/page-shell.d.ts +1 -1
  2. package/dist/ui/chrome/page-shell.d.ts.map +1 -1
  3. package/dist/ui/chrome/page-shell.js +9 -4
  4. package/dist/ui/chrome/page-shell.js.map +1 -1
  5. package/package.json +6 -4
  6. package/src/index.ts +0 -17
  7. package/src/lib/biome-host/agent-validation.ts +0 -239
  8. package/src/lib/biome-host/biome-builders.ts +0 -112
  9. package/src/lib/biome-host/biome-mode.ts +0 -6
  10. package/src/lib/biome-host/biome-navigation.ts +0 -37
  11. package/src/lib/biome-host/biome-registry.ts +0 -299
  12. package/src/lib/biome-host/biome-scope-hooks.ts +0 -41
  13. package/src/lib/biome-host/biome-scope.tsx +0 -52
  14. package/src/lib/biome-host/biome-scoped-query.ts +0 -134
  15. package/src/lib/biome-host/create-biome-orval-config.ts +0 -82
  16. package/src/lib/biome-host/define-web-biome.spec.ts +0 -221
  17. package/src/lib/biome-host/define-web-biome.ts +0 -222
  18. package/src/lib/biome-host/errors.ts +0 -220
  19. package/src/lib/biome-host/frontend-biome.ts +0 -256
  20. package/src/lib/biome-host/host-bridge.ts +0 -281
  21. package/src/lib/biome-host/host-sources.ts +0 -54
  22. package/src/lib/biome-host/i18n-hooks.ts +0 -51
  23. package/src/lib/biome-host/i18n-port.test.ts +0 -50
  24. package/src/lib/biome-host/i18n-port.ts +0 -96
  25. package/src/lib/biome-host/index.ts +0 -35
  26. package/src/lib/biome-host/realtime-hooks.ts +0 -75
  27. package/src/lib/biome-host/realtime-port.ts +0 -115
  28. package/src/lib/biome-host/session-contributions.ts +0 -296
  29. package/src/lib/biome-host/use-mutation-with-error-toast.ts +0 -90
  30. package/src/lib/biome-host/use-page-state.ts +0 -248
  31. package/src/lib/capabilities/capability-context.ts +0 -25
  32. package/src/lib/capabilities/capability-provider.tsx +0 -73
  33. package/src/lib/capabilities/index.ts +0 -17
  34. package/src/lib/capabilities/types.ts +0 -69
  35. package/src/lib/capabilities/use-capability.ts +0 -79
  36. package/src/lib/contribution-registry/contribution-registry.spec.ts +0 -141
  37. package/src/lib/contribution-registry/contribution-registry.ts +0 -204
  38. package/src/lib/contribution-registry/index.ts +0 -9
  39. package/src/lib/contribution-registry/renderable-registry.ts +0 -142
  40. package/src/lib/system-bus/capability-invoke.ts +0 -92
  41. package/src/lib/system-bus/deeplink.ts +0 -210
  42. package/src/lib/system-bus/enums.ts +0 -86
  43. package/src/lib/system-bus/host-ports.ts +0 -93
  44. package/src/lib/system-bus/index.ts +0 -16
  45. package/src/lib/system-bus/intent-registry.ts +0 -109
  46. package/src/lib/system-bus/intents.ts +0 -113
  47. package/src/lib/system-bus/palette.ts +0 -78
  48. package/src/lib/system-bus/system-bus-builder.ts +0 -159
  49. package/src/lib/system-bus/system-bus.ts +0 -37
  50. package/src/lib/system-bus/windows.ts +0 -51
  51. package/src/offline/id.ts +0 -16
  52. package/src/offline/index.ts +0 -54
  53. package/src/offline/mutation.ts +0 -75
  54. package/src/offline/offline-status-badge.tsx +0 -51
  55. package/src/offline/online-controller.ts +0 -105
  56. package/src/offline/outbox.test.ts +0 -172
  57. package/src/offline/outbox.ts +0 -348
  58. package/src/offline/react-hooks.ts +0 -40
  59. package/src/offline/react.tsx +0 -32
  60. package/src/offline/storage.ts +0 -112
  61. package/src/offline/types.ts +0 -77
  62. package/src/org-db/components/DbResultTable.tsx +0 -176
  63. package/src/org-db/index.ts +0 -1
  64. package/src/pdf/index.ts +0 -11
  65. package/src/registry/index.ts +0 -9
  66. package/src/registry/lib/agent-validation-host.ts +0 -36
  67. package/src/registry/lib/biome-slot.tsx +0 -50
  68. package/src/registry/lib/biomes-enabled-context.ts +0 -20
  69. package/src/registry/lib/extension-points.ts +0 -134
  70. package/src/registry/lib/primitives/SessionEventCard.tsx +0 -164
  71. package/src/registry/lib/primitives/SessionEventTimeline.tsx +0 -95
  72. package/src/registry/lib/primitives/SessionMutationBar.tsx +0 -76
  73. package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +0 -165
  74. package/src/registry/lib/primitives/index.ts +0 -18
  75. package/src/registry/lib/session-context-builder.ts +0 -84
  76. package/src/registry/lib/session-context-provider.tsx +0 -33
  77. package/src/registry/lib/session-context.ts +0 -21
  78. package/src/registry/lib/session-selectors.ts +0 -272
  79. package/src/session/comments/CommentRail.tsx +0 -164
  80. package/src/session/index.ts +0 -3
  81. package/src/session/lib/cn.ts +0 -11
  82. package/src/session/shell/SessionWorkspaceShell.tsx +0 -147
  83. package/src/session-kit/approvals/ApprovalButton.tsx +0 -89
  84. package/src/session-kit/approvals/ApprovalCard.tsx +0 -341
  85. package/src/session-kit/approvals/ApprovalsCenter.tsx +0 -336
  86. package/src/session-kit/approvals/CapabilityApprovalStickyBar.tsx +0 -118
  87. package/src/session-kit/approvals/Obligation.tsx +0 -111
  88. package/src/session-kit/approvals/ScopedBody.tsx +0 -392
  89. package/src/session-kit/approvals/approval-icons.ts +0 -31
  90. package/src/session-kit/approvals/approval-model.ts +0 -204
  91. package/src/session-kit/approvals/index.ts +0 -22
  92. package/src/session-kit/approvals/obligation-display.ts +0 -100
  93. package/src/session-kit/approvals/risk-accent.ts +0 -47
  94. package/src/session-kit/approvals/scope-icons.ts +0 -19
  95. package/src/session-kit/combobox/Combobox.tsx +0 -358
  96. package/src/session-kit/combobox/use-click-outside.ts +0 -21
  97. package/src/session-kit/display/ContextHeader.tsx +0 -148
  98. package/src/session-kit/display/FileDiffCard.tsx +0 -140
  99. package/src/session-kit/display/MD.tsx +0 -162
  100. package/src/session-kit/display/MessageTurn.tsx +0 -157
  101. package/src/session-kit/display/ThinkingPanel.tsx +0 -81
  102. package/src/session-kit/display/TodoChecklist.tsx +0 -120
  103. package/src/session-kit/display/TokenMeter.tsx +0 -89
  104. package/src/session-kit/display/ToolStrip.tsx +0 -278
  105. package/src/session-kit/display/TypingDots.tsx +0 -24
  106. package/src/session-kit/index.ts +0 -44
  107. package/src/session-kit/lib/enums.ts +0 -66
  108. package/src/session-kit/lib/portal-accent.ts +0 -30
  109. package/src/session-kit/lib/status-dot.ts +0 -68
  110. package/src/session-kit/primitives/Avatar.tsx +0 -44
  111. package/src/session-kit/primitives/PortalGlyph.tsx +0 -51
  112. package/src/session-kit/primitives/RiskPill.tsx +0 -95
  113. package/src/session-kit/primitives/ScopeDot.tsx +0 -47
  114. package/src/session-kit/primitives/Segmented.tsx +0 -71
  115. package/src/ui/branding/app-branding-context.ts +0 -20
  116. package/src/ui/branding/app-branding-provider.tsx +0 -123
  117. package/src/ui/branding/sanitize.ts +0 -89
  118. package/src/ui/breakpoints.ts +0 -14
  119. package/src/ui/capture/document-preview.tsx +0 -166
  120. package/src/ui/capture/field-spec-to-zod.test.ts +0 -81
  121. package/src/ui/capture/field-spec-to-zod.ts +0 -174
  122. package/src/ui/capture/field-spec-validate.ts +0 -236
  123. package/src/ui/capture/field-spec.test.ts +0 -49
  124. package/src/ui/capture/field-spec.ts +0 -274
  125. package/src/ui/capture/index.ts +0 -51
  126. package/src/ui/capture/schema-field-input.tsx +0 -620
  127. package/src/ui/capture/schema-form.tsx +0 -149
  128. package/src/ui/capture/submit-values.test.ts +0 -70
  129. package/src/ui/capture/submit-values.ts +0 -63
  130. package/src/ui/capture/use-prefill.ts +0 -186
  131. package/src/ui/capture/use-schema-validation.ts +0 -145
  132. package/src/ui/capture/use-wizard.ts +0 -180
  133. package/src/ui/capture/value-format.ts +0 -32
  134. package/src/ui/capture/wizard.tsx +0 -105
  135. package/src/ui/charts/bar-chart.tsx +0 -139
  136. package/src/ui/charts/chart-frame.tsx +0 -125
  137. package/src/ui/charts/chart-types.ts +0 -52
  138. package/src/ui/charts/chart-utils.ts +0 -190
  139. package/src/ui/charts/charts.test.tsx +0 -204
  140. package/src/ui/charts/donut-chart.tsx +0 -129
  141. package/src/ui/charts/index.ts +0 -27
  142. package/src/ui/charts/line-chart.tsx +0 -202
  143. package/src/ui/charts/sparkline.tsx +0 -109
  144. package/src/ui/chrome/AsyncBoundary.tsx +0 -74
  145. package/src/ui/chrome/EmptyState.tsx +0 -257
  146. package/src/ui/chrome/ErrorCard.tsx +0 -63
  147. package/src/ui/chrome/LoadingState.tsx +0 -61
  148. package/src/ui/chrome/PageHeader.tsx +0 -137
  149. package/src/ui/chrome/RecordCardList.tsx +0 -98
  150. package/src/ui/chrome/StateCard.tsx +0 -150
  151. package/src/ui/chrome/confirm-delete-dialog.tsx +0 -59
  152. package/src/ui/chrome/page-shell.tsx +0 -189
  153. package/src/ui/chrome/scope-badge.tsx +0 -40
  154. package/src/ui/chrome/status-badge.tsx +0 -79
  155. package/src/ui/cn.ts +0 -32
  156. package/src/ui/design-tokens.ts +0 -372
  157. package/src/ui/hooks/use-debounced-value.ts +0 -16
  158. package/src/ui/hooks/use-is-mobile.ts +0 -28
  159. package/src/ui/index.ts +0 -110
  160. package/src/ui/pdf/pdf-preview.tsx +0 -122
  161. package/src/ui/primitives/alert-dialog.tsx +0 -104
  162. package/src/ui/primitives/async-combobox.tsx +0 -182
  163. package/src/ui/primitives/badge-variants.ts +0 -21
  164. package/src/ui/primitives/badge.tsx +0 -25
  165. package/src/ui/primitives/button-variants.ts +0 -31
  166. package/src/ui/primitives/button.tsx +0 -29
  167. package/src/ui/primitives/card.tsx +0 -43
  168. package/src/ui/primitives/checkbox.tsx +0 -26
  169. package/src/ui/primitives/collapsible.tsx +0 -9
  170. package/src/ui/primitives/dialog.tsx +0 -103
  171. package/src/ui/primitives/dropdown-menu.tsx +0 -179
  172. package/src/ui/primitives/form-stepper.tsx +0 -94
  173. package/src/ui/primitives/form-stepper.types.ts +0 -15
  174. package/src/ui/primitives/input.tsx +0 -22
  175. package/src/ui/primitives/label.tsx +0 -17
  176. package/src/ui/primitives/overflow-tabs.tsx +0 -331
  177. package/src/ui/primitives/popover.tsx +0 -33
  178. package/src/ui/primitives/preview-device-frame.tsx +0 -213
  179. package/src/ui/primitives/radio-group.tsx +0 -36
  180. package/src/ui/primitives/resizable.tsx +0 -67
  181. package/src/ui/primitives/scroll-area.tsx +0 -38
  182. package/src/ui/primitives/select.tsx +0 -143
  183. package/src/ui/primitives/separator.tsx +0 -20
  184. package/src/ui/primitives/sheet.tsx +0 -107
  185. package/src/ui/primitives/skeleton.tsx +0 -99
  186. package/src/ui/primitives/switch.tsx +0 -27
  187. package/src/ui/primitives/table.tsx +0 -72
  188. package/src/ui/primitives/tabs.tsx +0 -53
  189. package/src/ui/primitives/tag-multi-select.tsx +0 -261
  190. package/src/ui/primitives/textarea.tsx +0 -21
  191. package/src/ui/primitives/tooltip.tsx +0 -30
  192. package/src/ui/primitives/url-preview-chrome.tsx +0 -103
  193. package/src/vista/GenericCatalogueSurface.tsx +0 -179
  194. package/src/vista/SurfaceFallbackCard.tsx +0 -109
  195. package/src/vista/VistaPanel.tsx +0 -163
  196. package/src/vista/form-payload.spec.ts +0 -139
  197. package/src/vista/html/CustomUiFrame.spec.tsx +0 -127
  198. package/src/vista/html/CustomUiFrame.tsx +0 -206
  199. package/src/vista/html/custom-ui-frame-bridge.spec.ts +0 -191
  200. package/src/vista/html/custom-ui-frame-bridge.ts +0 -98
  201. package/src/vista/html/custom-ui-frame-contract.ts +0 -88
  202. package/src/vista/html/custom-ui-intents.spec.ts +0 -135
  203. package/src/vista/html/custom-ui-intents.ts +0 -155
  204. package/src/vista/html/custom-ui-srcdoc.spec.ts +0 -151
  205. package/src/vista/html/custom-ui-srcdoc.ts +0 -193
  206. package/src/vista/html/index.ts +0 -13
  207. package/src/vista/index.ts +0 -52
  208. package/src/vista/surface-catalogue.spec.ts +0 -140
  209. package/src/vista/surface-catalogue.ts +0 -90
  210. package/src/vista/surface-icons.ts +0 -57
  211. package/src/vista/surface-registry.ts +0 -56
  212. package/src/vista/surfaces/ChartSurface.tsx +0 -34
  213. package/src/vista/surfaces/FilesSurface.tsx +0 -202
  214. package/src/vista/surfaces/FormSurface.tsx +0 -380
  215. package/src/vista/surfaces/HtmlSurface.tsx +0 -43
  216. package/src/vista/surfaces/TableSurface.tsx +0 -100
  217. package/src/vista/surfaces/UrlSurface.tsx +0 -40
  218. package/src/vista/surfaces/blob-surfaces.tsx +0 -139
  219. package/src/vista/surfaces/elicitation-transport.spec.ts +0 -369
  220. package/src/vista/surfaces/elicitation-transport.ts +0 -367
  221. package/src/vista/surfaces/file-surface-utils.ts +0 -69
  222. package/src/vista/surfaces/index.ts +0 -24
  223. package/src/vista/surfaces/register-surfaces.tsx +0 -139
  224. package/src/vista/surfaces/text-surfaces.tsx +0 -132
  225. package/src/vista/use-vista-tabs.ts +0 -196
  226. package/src/vista/vista-code-renderer-context.ts +0 -16
  227. package/src/vista/vista-code-renderer.tsx +0 -35
  228. package/src/vista/vista-file-context.ts +0 -14
  229. package/src/vista/vista-file-source.tsx +0 -141
  230. package/src/vista/vista-form-context.ts +0 -20
  231. package/src/vista/vista-form-host.tsx +0 -48
  232. package/src/vista/vista-model.ts +0 -137
  233. package/src/vista/vista-open-context.tsx +0 -27
  234. package/src/vista/vista-open-state.ts +0 -11
  235. package/src/vista/vista-payloads.ts +0 -635
  236. package/src/vista/vista-tabs-manifest.ts +0 -138
  237. package/src/vista/vista-tools.ts +0 -232
@@ -1,61 +0,0 @@
1
- import { Loader2 } from 'lucide-react';
2
-
3
- import { cn } from '../cn';
4
- import { Skeleton } from '../primitives/skeleton';
5
-
6
- export type LoadingStateVariant = 'spinner' | 'skeleton-list' | 'skeleton-card';
7
-
8
- interface LoadingStateProps {
9
- /**
10
- * `spinner` (default): centred spinner + optional label. `skeleton-list`:
11
- * N skeleton rows. `skeleton-card`: a single card-shaped skeleton block.
12
- */
13
- variant?: LoadingStateVariant;
14
- /** Accessible label / caption (spinner variant). */
15
- label?: string;
16
- /** Row count for `skeleton-list` (default 4). */
17
- rows?: number;
18
- className?: string;
19
- }
20
-
21
- /**
22
- * Canonical loading affordance shared across biomes. Replaces the per-biome
23
- * ad-hoc spinners and inline skeleton stacks.
24
- */
25
- export default function LoadingState({
26
- variant = 'spinner',
27
- label = 'Loading…',
28
- rows = 4,
29
- className,
30
- }: Readonly<LoadingStateProps>) {
31
- if (variant === 'skeleton-list') {
32
- return (
33
- <div className={cn('space-y-3', className)} role="status" aria-label={label}>
34
- {Array.from({ length: rows }).map((_, i) => (
35
- <Skeleton key={i} className="h-12 w-full rounded-xl" />
36
- ))}
37
- </div>
38
- );
39
- }
40
-
41
- if (variant === 'skeleton-card') {
42
- return (
43
- <div className={cn('space-y-3', className)} role="status" aria-label={label}>
44
- <Skeleton className="h-40 w-full rounded-2xl" />
45
- <Skeleton className="h-4 w-2/3 rounded" />
46
- <Skeleton className="h-4 w-1/2 rounded" />
47
- </div>
48
- );
49
- }
50
-
51
- return (
52
- <div
53
- className={cn('flex flex-col items-center justify-center py-14 text-center', className)}
54
- role="status"
55
- aria-label={label}
56
- >
57
- <Loader2 className="h-6 w-6 animate-spin text-ink-3" />
58
- {label && <p className="mt-3 text-body-2 text-ink-3">{label}</p>}
59
- </div>
60
- );
61
- }
@@ -1,137 +0,0 @@
1
- // ═══════════════════════════════════════════════════════════════════════════
2
- // ── PageHeader — INVISIBLE topbar registration ──
3
- //
4
- // PageHeader does NOT render a visible header bar. It registers the page's
5
- // title, description, eyebrow, help-actions, status pill, count, and
6
- // primary action buttons into the shared page-meta store. The global
7
- // `AppTopbar` reads from that store and renders them in the topbar
8
- // (mac-style — the topbar IS the page chrome).
9
- //
10
- // Why invisible: stacking a second header row beneath the topbar wastes
11
- // vertical space and competes with the topbar for attention. Pages get
12
- // the canvas back; the topbar carries context.
13
- //
14
- // When you need a thin in-page row (filter dropdowns, search input,
15
- // selection-mode toolbar), use `<PageToolbar>` from this same file —
16
- // that's a real visible row but carries NO title (the topbar still owns
17
- // it), so there's no visual duplication.
18
- //
19
- // Host-agnostic: reaches the page-meta store through the kernel `HostBridge`
20
- // singleton (NOT a host import), so it bundles cleanly into any biome remote.
21
- // ═══════════════════════════════════════════════════════════════════════════
22
-
23
- import { useMemo, type ReactNode } from 'react';
24
-
25
- import { useHostBridge, type PageBackTarget } from '../../lib/biome-host';
26
- import { cn } from '../cn';
27
-
28
- interface PageHeaderProps {
29
- readonly title: string;
30
- /**
31
- * Short secondary line. Renders in the topbar info popover (long-form
32
- * `topbarDescription` wins for the popover if both are set) AND
33
- * inline next to the title as `topbarMeta` when it's a string.
34
- */
35
- readonly description?: string;
36
- /**
37
- * Long-form text rendered ONLY in the topbar's info popover (the
38
- * (i) button). Use when `description` is a counts line and you still
39
- * want a richer popover blurb.
40
- */
41
- readonly topbarDescription?: string;
42
- /** Mono caption rendered before the title in the topbar info popover. */
43
- readonly eyebrow?: string;
44
- /** Bullet list of "what you can do here" shown in the topbar info popover. */
45
- readonly helpActions?: readonly string[];
46
- /**
47
- * Right-aligned topbar action cluster. Use small h-7 buttons / icon
48
- * buttons. Anything that needs more space (multi-input filter rows,
49
- * search inputs) belongs in a `<PageToolbar>` inside the page body
50
- * — not here.
51
- */
52
- readonly actions?: ReactNode;
53
- /**
54
- * Leftmost back affordance. Renders as an icon-only button before
55
- * the title in the topbar. Pages with a parent context (a detail
56
- * page under a list, etc.) set this so the user can return to where
57
- * they came from. See `app/page-meta.ts` for the full layout
58
- * contract — back always lives on the left, never inside the page
59
- * body.
60
- */
61
- readonly backTo?: PageBackTarget;
62
- }
63
-
64
- /**
65
- * Invisible primitive — registers page meta with the topbar. Renders
66
- * nothing visible. Mount once at the top of a page component.
67
- */
68
- export default function PageHeader({
69
- title,
70
- description,
71
- topbarDescription,
72
- eyebrow,
73
- helpActions,
74
- actions,
75
- backTo,
76
- }: Readonly<PageHeaderProps>) {
77
- // `topbarActions` is a ReactNode — wrap in useMemo so React doesn't see
78
- // a new identity every render and re-notify the store needlessly. Same
79
- // for the meta line.
80
- const topbarActions = useMemo(() => actions, [actions]);
81
- const topbarMeta = useMemo<ReactNode>(
82
- () => (description ? <span>{description}</span> : null),
83
- [description],
84
- );
85
-
86
- // Reach the host page-meta store through the shared bridge singleton — NOT
87
- // a module-level import — so this component bundles cleanly into an MF
88
- // remote and still drives the one host topbar store.
89
- useHostBridge().pageMeta.usePageMeta({
90
- title,
91
- eyebrow,
92
- description: topbarDescription ?? description,
93
- actions: helpActions,
94
- topbarActions,
95
- topbarMeta,
96
- backTo,
97
- });
98
- return null;
99
- }
100
-
101
- // ═══════════════════════════════════════════════════════════════════════════
102
- // ── PageToolbar — thin in-page row for filters / search / sub-tabs ──
103
- //
104
- // Use when the page needs a horizontal row of controls that don't fit
105
- // in the topbar (multi-select filter, search input, bulk-selection
106
- // toolbar). NEVER include the page title — that's the topbar's job.
107
- //
108
- // Default height is `h-9` to match the topbar visual rhythm without
109
- // looking like a duplicated header. Background is the same paper tone
110
- // as the page so it reads as a tool strip, not a banner.
111
- // ═══════════════════════════════════════════════════════════════════════════
112
-
113
- interface PageToolbarProps {
114
- /** Left cluster — filters, search, etc. */
115
- readonly children?: ReactNode;
116
- /** Right cluster — usually `null`; the primary action lives in the topbar. */
117
- readonly trailing?: ReactNode;
118
- readonly className?: string;
119
- }
120
-
121
- export function PageToolbar({
122
- children,
123
- trailing,
124
- className,
125
- }: Readonly<PageToolbarProps>) {
126
- return (
127
- <div
128
- className={cn(
129
- 'flex shrink-0 items-center gap-2 border-b border-rule/50 bg-paper px-4 py-1.5 sm:px-6',
130
- className,
131
- )}
132
- >
133
- <div className="flex min-w-0 flex-1 flex-wrap items-center gap-2">{children}</div>
134
- {trailing && <div className="flex shrink-0 items-center gap-1.5">{trailing}</div>}
135
- </div>
136
- );
137
- }
@@ -1,98 +0,0 @@
1
- import { cn } from '../cn';
2
-
3
- import type { ReactNode } from 'react';
4
-
5
- /**
6
- * One label/value row inside a record card — mirrors one table column.
7
- *
8
- * `key` exists because a card's rows are keyed by the COLUMN, and a column's
9
- * identity is its field key, not its user-facing label (two columns may carry
10
- * the same label). It defaults to `label` for the common case where the label
11
- * is already unique.
12
- */
13
- export interface RecordCardField {
14
- readonly key?: string;
15
- readonly label: string;
16
- readonly value: ReactNode;
17
- /**
18
- * `data-*` attributes for the row element — e.g. the same hook the equivalent
19
- * table cell carries, so a click-to-context resolver lands on the identical
20
- * node at every viewport width. Deliberately `data-*` only: styling stays in
21
- * this primitive, so a caller cannot re-open the drift it closed.
22
- */
23
- readonly rowAttributes?: Readonly<Record<`data-${string}`, string>>;
24
- }
25
-
26
- export interface RecordCardListProps<T> {
27
- readonly items: readonly T[];
28
- readonly getKey: (item: T) => string;
29
- /** Card header rendered above the field list (title, status, row toggle). */
30
- readonly renderHeader?: (item: T) => ReactNode;
31
- readonly getFields: (item: T) => readonly RecordCardField[];
32
- /** Row actions, rendered right-aligned below a divider. */
33
- readonly renderActions?: (item: T) => ReactNode;
34
- /** Free block appended after the actions (expanded detail). */
35
- readonly renderDetail?: (item: T) => ReactNode;
36
- /** Extra classes for the list element. */
37
- readonly className?: string;
38
- }
39
-
40
- /**
41
- * Small-viewport (below `md`) layout for a record list: one stacked card per
42
- * record with the table's columns rendered as label/value rows, instead of a
43
- * horizontal-scroll-only table.
44
- *
45
- * This is the ONE definition of that card chrome — spacing, border, divider,
46
- * label/value alignment. Callers name their columns and their actions; they do
47
- * not restate the chrome. Deliberately NOT parameterised by card class: a
48
- * per-caller class hook is how the three copies this replaced drifted apart in
49
- * the first place.
50
- */
51
- export function RecordCardList<T>(props: Readonly<RecordCardListProps<T>>) {
52
- const {
53
- items,
54
- getKey,
55
- renderHeader,
56
- getFields,
57
- renderActions,
58
- renderDetail,
59
- className,
60
- } = props;
61
-
62
- return (
63
- <ul className={cn('space-y-2', className)}>
64
- {items.map((item) => {
65
- const actions = renderActions?.(item);
66
- const detail = renderDetail?.(item);
67
- return (
68
- <li
69
- key={getKey(item)}
70
- className="space-y-2 rounded-lg border border-rule bg-paper-elev p-3"
71
- >
72
- {renderHeader?.(item)}
73
- <dl className="space-y-1.5">
74
- {getFields(item).map((field) => (
75
- <div
76
- key={field.key ?? field.label}
77
- {...field.rowAttributes}
78
- className="flex justify-between gap-3 text-sm"
79
- >
80
- <dt className="shrink-0 text-ink-3">{field.label}</dt>
81
- <dd className="text-right text-ink-2">{field.value}</dd>
82
- </div>
83
- ))}
84
- </dl>
85
- {actions ? (
86
- <div className="flex items-center justify-end gap-2 border-t border-rule/50 pt-2">
87
- {actions}
88
- </div>
89
- ) : null}
90
- {detail}
91
- </li>
92
- );
93
- })}
94
- </ul>
95
- );
96
- }
97
-
98
- export default RecordCardList;
@@ -1,150 +0,0 @@
1
- /**
2
- * Unified state display card for empty, error-simple, and error-detailed states.
3
- * Replaces ErrorCard, ErrorDisplay, and EmptyState components.
4
- * Supports collapsible error details and structured field extraction.
5
- */
6
- import { ChevronDown, ChevronRight } from 'lucide-react';
7
- import React, { useState } from 'react';
8
-
9
-
10
- import { cn } from '../cn';
11
-
12
- interface FieldEntry {
13
- label: string;
14
- value: string | React.ReactNode;
15
- }
16
-
17
- interface StateCardProps {
18
- /** State variant: 'empty' | 'error-simple' | 'error-detailed' */
19
- variant: 'empty' | 'error-simple' | 'error-detailed';
20
- /** Icon React element */
21
- icon: React.ReactNode;
22
- /** Primary title */
23
- title: string;
24
- /** Description or error message */
25
- description?: string;
26
- /** Optional action button */
27
- action?: {
28
- label: string;
29
- onClick: () => void;
30
- };
31
- /** Optional error details (for error-detailed variant) */
32
- errorDetails?: {
33
- fields?: FieldEntry[];
34
- stackTrace?: string;
35
- requestId?: string;
36
- };
37
- /** Optional CSS class */
38
- className?: string;
39
- }
40
-
41
- /**
42
- * Renders a state card (empty/error) with consistent appearance.
43
- * Supports collapsible error details for debugging.
44
- */
45
- export const StateCard: React.FC<StateCardProps> = ({
46
- variant,
47
- icon,
48
- title,
49
- description,
50
- action,
51
- errorDetails,
52
- className = '',
53
- }) => {
54
- const [detailsExpanded, setDetailsExpanded] = useState(false);
55
-
56
- const isError = variant.startsWith('error');
57
- const isDetailed = variant === 'error-detailed';
58
-
59
- return (
60
- <div className={cn(
61
- 'flex flex-col items-center justify-center py-14 px-6 rounded-xl border-2',
62
- isError ? 'border-destructive/20 bg-destructive/[0.03]' : 'border-dashed border-border/50 bg-paper-elev/60',
63
- className,
64
- )}>
65
- <div className={cn(
66
- 'h-14 w-14 rounded-2xl flex items-center justify-center mb-5',
67
- isError ? 'bg-destructive/10' : 'bg-primary/10',
68
- )}>
69
- <span className={isError ? 'text-destructive' : 'text-primary'}>{icon}</span>
70
- </div>
71
-
72
- <h2 className="text-subtitle font-semibold text-ink mb-2 text-center">
73
- {title}
74
- </h2>
75
-
76
- {description && (
77
- <p className="text-body-1 text-ink-3 max-w-md text-center mb-4 leading-relaxed">
78
- {description}
79
- </p>
80
- )}
81
-
82
- {/* Error details section */}
83
- {isDetailed && errorDetails && (
84
- <div className="mt-4 w-full max-w-md">
85
- <button
86
- type="button"
87
- onClick={() => setDetailsExpanded(!detailsExpanded)}
88
- className="text-body-1 font-medium text-destructive hover:text-destructive flex items-center gap-1.5 mb-2"
89
- >
90
- {detailsExpanded ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
91
- Error Details
92
- </button>
93
-
94
- {detailsExpanded && (
95
- <div className="p-4 bg-card border border-destructive/20 rounded-xl space-y-3">
96
- {/* Structured fields */}
97
- {errorDetails.fields && errorDetails.fields.length > 0 && (
98
- <div className="space-y-1.5">
99
- {errorDetails.fields.map((field, idx) => (
100
- <div key={idx} className="text-body-1">
101
- <span className="font-semibold text-destructive">{field.label}</span>
102
- <span className="text-ink/70">: {field.value}</span>
103
- </div>
104
- ))}
105
- </div>
106
- )}
107
-
108
- {/* Stack trace */}
109
- {errorDetails.stackTrace && (
110
- <details className="text-body-1">
111
- <summary className="font-medium text-destructive cursor-pointer mb-1">
112
- Stack Trace
113
- </summary>
114
- <pre className="bg-destructive/5 p-3 rounded-lg overflow-auto max-h-32 text-destructive whitespace-pre-wrap break-words text-body-1 font-mono">
115
- {errorDetails.stackTrace}
116
- </pre>
117
- </details>
118
- )}
119
-
120
- {/* Request ID */}
121
- {errorDetails.requestId && (
122
- <div className="text-body-1 pt-2 border-t border-destructive/15">
123
- <span className="font-medium text-ink-3">Request ID</span>
124
- <br />
125
- <code className="text-body-1 text-ink/70 font-mono break-all">{errorDetails.requestId}</code>
126
- </div>
127
- )}
128
- </div>
129
- )}
130
- </div>
131
- )}
132
-
133
- {/* Action button */}
134
- {action && (
135
- <button
136
- type="button"
137
- onClick={action.onClick}
138
- className={cn(
139
- 'mt-6 px-5 py-2.5 rounded-lg font-medium transition-colors h-10',
140
- isError
141
- ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'
142
- : 'bg-primary text-primary-foreground hover:bg-primary/90',
143
- )}
144
- >
145
- {action.label}
146
- </button>
147
- )}
148
- </div>
149
- );
150
- };
@@ -1,59 +0,0 @@
1
- import { Loader2 } from 'lucide-react';
2
-
3
- import {
4
- AlertDialog,
5
- AlertDialogAction,
6
- AlertDialogCancel,
7
- AlertDialogContent,
8
- AlertDialogDescription,
9
- AlertDialogFooter,
10
- AlertDialogHeader,
11
- AlertDialogTitle,
12
- } from '../primitives/alert-dialog';
13
-
14
- export interface ConfirmDeleteDialogProps {
15
- open: boolean;
16
- onOpenChange: (open: boolean) => void;
17
- onConfirm: () => void;
18
- title?: string;
19
- description?: string;
20
- isPending?: boolean;
21
- /** Confirm-button label. Defaults to 'Delete' for the destructive case. */
22
- confirmLabel?: string;
23
- }
24
-
25
- /**
26
- * Reusable delete confirmation dialog.
27
- * Used across CRUD surfaces to prevent accidental destructive actions.
28
- */
29
- export default function ConfirmDeleteDialog({
30
- open,
31
- onOpenChange,
32
- onConfirm,
33
- title = 'Delete item',
34
- description = 'This action cannot be undone. Are you sure you want to continue?',
35
- isPending,
36
- confirmLabel = 'Delete',
37
- }: ConfirmDeleteDialogProps) {
38
- return (
39
- <AlertDialog open={open} onOpenChange={onOpenChange}>
40
- <AlertDialogContent>
41
- <AlertDialogHeader>
42
- <AlertDialogTitle>{title}</AlertDialogTitle>
43
- <AlertDialogDescription>{description}</AlertDialogDescription>
44
- </AlertDialogHeader>
45
- <AlertDialogFooter>
46
- <AlertDialogCancel disabled={isPending}>Cancel</AlertDialogCancel>
47
- <AlertDialogAction
48
- onClick={onConfirm}
49
- disabled={isPending}
50
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
51
- >
52
- {isPending && <Loader2 className="h-4 w-4 mr-1.5 animate-spin" />}
53
- {confirmLabel}
54
- </AlertDialogAction>
55
- </AlertDialogFooter>
56
- </AlertDialogContent>
57
- </AlertDialog>
58
- );
59
- }