cursedbelt 5.56.0 → 5.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/react/charts/tokens.js +4 -4
  2. package/dist/react/code-editor/CodeEditor.d.ts.map +1 -1
  3. package/dist/react/code-editor/CodeEditor.js +2 -1
  4. package/dist/react/code-editor/CodeEditor.js.map +1 -1
  5. package/dist/react/code-editor/lineAbove.d.ts +17 -0
  6. package/dist/react/code-editor/lineAbove.d.ts.map +1 -0
  7. package/dist/react/code-editor/lineAbove.js +34 -0
  8. package/dist/react/code-editor/lineAbove.js.map +1 -0
  9. package/dist/react/companion-link/CompanionLinkStatus.d.ts.map +1 -1
  10. package/dist/react/companion-link/CompanionLinkStatus.js +2 -1
  11. package/dist/react/companion-link/CompanionLinkStatus.js.map +1 -1
  12. package/dist/react/components/Accordion.d.ts.map +1 -1
  13. package/dist/react/components/Accordion.js +2 -2
  14. package/dist/react/components/Accordion.js.map +1 -1
  15. package/dist/react/components/Badge.d.ts +20 -1
  16. package/dist/react/components/Badge.d.ts.map +1 -1
  17. package/dist/react/components/Badge.js +30 -3
  18. package/dist/react/components/Badge.js.map +1 -1
  19. package/dist/react/components/Button.d.ts.map +1 -1
  20. package/dist/react/components/Button.js +8 -2
  21. package/dist/react/components/Button.js.map +1 -1
  22. package/dist/react/components/Collapsible.d.ts.map +1 -1
  23. package/dist/react/components/Collapsible.js +2 -2
  24. package/dist/react/components/Collapsible.js.map +1 -1
  25. package/dist/react/components/DisclosureButton.d.ts +2 -2
  26. package/dist/react/components/DisclosureButton.d.ts.map +1 -1
  27. package/dist/react/components/DisclosureButton.js +9 -6
  28. package/dist/react/components/DisclosureButton.js.map +1 -1
  29. package/dist/react/components/EditorPage.d.ts +78 -0
  30. package/dist/react/components/EditorPage.d.ts.map +1 -0
  31. package/dist/react/components/EditorPage.js +133 -0
  32. package/dist/react/components/EditorPage.js.map +1 -0
  33. package/dist/react/components/IconPicker.d.ts +70 -0
  34. package/dist/react/components/IconPicker.d.ts.map +1 -0
  35. package/dist/react/components/IconPicker.js +175 -0
  36. package/dist/react/components/IconPicker.js.map +1 -0
  37. package/dist/react/components/PropertyList.d.ts +45 -0
  38. package/dist/react/components/PropertyList.d.ts.map +1 -0
  39. package/dist/react/components/PropertyList.js +12 -0
  40. package/dist/react/components/PropertyList.js.map +1 -0
  41. package/dist/react/components/Wizard/ReviewStep.d.ts +1 -1
  42. package/dist/react/components/Wizard/ReviewStep.d.ts.map +1 -1
  43. package/dist/react/components/Wizard/ReviewStep.js +3 -3
  44. package/dist/react/components/Wizard/ReviewStep.js.map +1 -1
  45. package/dist/react/components/editorPage.css +421 -0
  46. package/dist/react/components/formKit.d.ts +3 -1
  47. package/dist/react/components/formKit.d.ts.map +1 -1
  48. package/dist/react/components/formKit.js +3 -1
  49. package/dist/react/components/formKit.js.map +1 -1
  50. package/dist/react/index.d.ts +4 -1
  51. package/dist/react/index.d.ts.map +1 -1
  52. package/dist/react/index.js +3 -0
  53. package/dist/react/index.js.map +1 -1
  54. package/dist/react/line-keys/index.d.ts +2 -0
  55. package/dist/react/line-keys/index.d.ts.map +1 -0
  56. package/dist/react/line-keys/index.js +2 -0
  57. package/dist/react/line-keys/index.js.map +1 -0
  58. package/dist/react/line-keys/lineKeys.d.ts +62 -0
  59. package/dist/react/line-keys/lineKeys.d.ts.map +1 -0
  60. package/dist/react/line-keys/lineKeys.js +159 -0
  61. package/dist/react/line-keys/lineKeys.js.map +1 -0
  62. package/dist/react/master-detail/DetailPane.d.ts +3 -0
  63. package/dist/react/master-detail/DetailPane.d.ts.map +1 -1
  64. package/dist/react/master-detail/DetailPane.js +5 -2
  65. package/dist/react/master-detail/DetailPane.js.map +1 -1
  66. package/dist/react/nav/SideNav.js +1 -1
  67. package/dist/react/nav/SideNav.js.map +1 -1
  68. package/dist/scripts/guardrailsEnforce.d.ts.map +1 -1
  69. package/dist/styles-areas/analytics.css +14 -14
  70. package/dist/styles-areas/charts.css +3 -3
  71. package/dist/styles-areas/companion-link.css +2 -2
  72. package/dist/styles-areas/core.css +50 -45
  73. package/dist/styles-areas/data-table.css +15 -16
  74. package/dist/styles-areas/disk-usage.css +4 -4
  75. package/dist/styles-areas/fields.css +11 -11
  76. package/dist/styles-areas/folder-tree.css +3 -3
  77. package/dist/styles-areas/layout-engine.css +2 -2
  78. package/dist/styles-areas/master-detail.css +4 -5
  79. package/dist/styles-areas/media-gallery.css +9 -9
  80. package/dist/styles-areas/media.css +3 -3
  81. package/dist/styles-areas/nav.css +5 -5
  82. package/dist/styles-areas/notifications.css +3 -3
  83. package/dist/styles-areas/problem-detail.css +3 -3
  84. package/dist/styles-areas/sharing.css +2 -3
  85. package/dist/styles-areas/stats.css +3 -3
  86. package/dist/styles-areas/wizard.css +7 -8
  87. package/dist/styles-areas/workbench.css +4 -4
  88. package/dist/styles-utilities.css +49 -10
  89. package/dist/theme.css +38 -22
  90. package/package.json +14 -2
  91. package/scripts/guardrails-baseline.json +2 -1
  92. package/scripts/guardrailsEnforce.spec.ts +32 -0
  93. package/scripts/guardrailsEnforce.ts +67 -1
  94. package/scripts/styleAreas.ts +3 -0
  95. package/src/react/charts/tokens.ts +4 -4
  96. package/src/react/code-editor/CodeEditor.tsx +2 -1
  97. package/src/react/code-editor/lineAbove.spec.ts +36 -0
  98. package/src/react/code-editor/lineAbove.ts +35 -0
  99. package/src/react/companion-link/CompanionLinkStatus.tsx +10 -12
  100. package/src/react/components/Accordion.tsx +5 -4
  101. package/src/react/components/Badge.capsule.spec.ts +1 -1
  102. package/src/react/components/Badge.spec.tsx +22 -1
  103. package/src/react/components/Badge.tsx +66 -11
  104. package/src/react/components/Button.spec.tsx +5 -4
  105. package/src/react/components/Button.tsx +8 -2
  106. package/src/react/components/Collapsible.tsx +5 -4
  107. package/src/react/components/DisclosureButton.spec.tsx +47 -0
  108. package/src/react/components/DisclosureButton.tsx +11 -7
  109. package/src/react/components/EditorPage.spec.tsx +106 -0
  110. package/src/react/components/EditorPage.tsx +281 -0
  111. package/src/react/components/IconPicker.spec.tsx +84 -0
  112. package/src/react/components/IconPicker.tsx +289 -0
  113. package/src/react/components/PropertyList.spec.tsx +110 -0
  114. package/src/react/components/PropertyList.tsx +92 -0
  115. package/src/react/components/Wizard/ReviewStep.tsx +7 -7
  116. package/src/react/components/editorPage.css +421 -0
  117. package/src/react/components/formKit.ts +10 -1
  118. package/src/react/components/formKitStylesheets.spec.ts +47 -0
  119. package/src/react/index.ts +11 -1
  120. package/src/react/line-keys/index.ts +1 -0
  121. package/src/react/line-keys/lineKeys.spec.tsx +176 -0
  122. package/src/react/line-keys/lineKeys.ts +176 -0
  123. package/src/react/master-detail/DetailPane.tsx +14 -18
  124. package/src/react/nav/SideNav.tsx +1 -1
  125. package/src/styles-areas/analytics.css +14 -14
  126. package/src/styles-areas/charts.css +3 -3
  127. package/src/styles-areas/companion-link.css +2 -2
  128. package/src/styles-areas/core.css +50 -45
  129. package/src/styles-areas/data-table.css +15 -16
  130. package/src/styles-areas/disk-usage.css +4 -4
  131. package/src/styles-areas/fields.css +11 -11
  132. package/src/styles-areas/folder-tree.css +3 -3
  133. package/src/styles-areas/layout-engine.css +2 -2
  134. package/src/styles-areas/master-detail.css +4 -5
  135. package/src/styles-areas/media-gallery.css +9 -9
  136. package/src/styles-areas/media.css +3 -3
  137. package/src/styles-areas/nav.css +5 -5
  138. package/src/styles-areas/notifications.css +3 -3
  139. package/src/styles-areas/problem-detail.css +3 -3
  140. package/src/styles-areas/sharing.css +2 -3
  141. package/src/styles-areas/stats.css +3 -3
  142. package/src/styles-areas/wizard.css +7 -8
  143. package/src/styles-areas/workbench.css +4 -4
  144. package/src/styles-utilities.css +49 -10
  145. package/src/theme.css +38 -22
@@ -0,0 +1,133 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowLeft } from 'lucide-react';
3
+ import { useCallback, useEffect, useId, useRef, useState, } from 'react';
4
+ import { useDiscardGuard } from '../hooks/useDiscardGuard.js';
5
+ import { cn } from '../lib/cn.js';
6
+ import { UnstyledButton } from './UnstyledButton.js';
7
+ import './formKit.css';
8
+ import './editorPage.css';
9
+ /** What answers Escape itself, before the page does. */
10
+ const OWNS_ESCAPE = '[role="listbox"],[role="menu"],[role="combobox"][aria-expanded="true"],[aria-haspopup]:not([aria-haspopup="false"])[aria-expanded="true"]';
11
+ const MIN_SPLIT = 0.25;
12
+ const MAX_SPLIT = 0.75;
13
+ const clampSplit = (value) => Math.min(MAX_SPLIT, Math.max(MIN_SPLIT, value));
14
+ function readSplit(key, fallback) {
15
+ if (!key || typeof localStorage === 'undefined')
16
+ return fallback;
17
+ const stored = Number(localStorage.getItem(key));
18
+ return Number.isFinite(stored) && stored > 0 ? clampSplit(stored) : fallback;
19
+ }
20
+ /**
21
+ * A creation page: the form on the left, a live mock of what it makes on the right, and a
22
+ * divider between them that drags.
23
+ *
24
+ * The owner, 2026-10-05, on desk's template editor: *"instead of opening a modal I want to open a
25
+ * page with a split view where I see the forms and fields … on the left and I see what it looks
26
+ * like so far and can tinker with a mock version of it on the right. I should be able to drag to
27
+ * resize a vertical divider … On mobile it will need an appropriate narrow layout."*
28
+ *
29
+ * - **Wide** (its container ≥ 48rem): two columns and a `separator` between them — drag it,
30
+ * or focus it and use ← / → (Home / End for the limits); a double-click puts it back. The
31
+ * split is kept under `storageKey`. Each side scrolls on its own, so a long form never pushes
32
+ * the preview out of sight.
33
+ * - **Narrow**: one side at a time, chosen by an Edit / Preview switch in the header — a phone
34
+ * has no room for both, and a pinned half-height preview leaves neither usable.
35
+ *
36
+ * The header is the page's own: a Back arrow with the crumb, the title, and the actions
37
+ * top-right where Linear and every desktop editor put Save. Escape anywhere is Back, as it was
38
+ * when the same form was a dialog.
39
+ *
40
+ * Plain stylesheet (`formKit.css`) — draws without Tailwind; `cursedbelt/react/form-kit`.
41
+ */
42
+ export function EditorPage({ title, backLabel = 'Back', onBack, isDirty, actions, children, preview, previewLabel = 'Preview', previewTools, defaultSplit = 0.5, storageKey, quiet = true, presentation = 'overlay', className, }) {
43
+ const [split, setSplit] = useState(() => readSplit(storageKey, clampSplit(defaultSplit)));
44
+ const [mode, setMode] = useState('edit');
45
+ const [dragging, setDragging] = useState(false);
46
+ const splitRef = useRef(null);
47
+ const rootRef = useRef(null);
48
+ const titleId = useId();
49
+ const formId = useId();
50
+ const previewId = useId();
51
+ const { guardedClose, discardOverlay, isConfirming, cancelDiscard } = useDiscardGuard({ onClose: onBack, isDirty });
52
+ const commit = useCallback((value) => {
53
+ const next = clampSplit(value);
54
+ setSplit(next);
55
+ if (storageKey && typeof localStorage !== 'undefined')
56
+ localStorage.setItem(storageKey, String(next));
57
+ }, [storageKey]);
58
+ // An overlay takes focus on open and gives it back on close, so the keyboard is where the
59
+ // eye is; the first field's own `autoFocus` still wins, because it runs first.
60
+ useEffect(() => {
61
+ if (presentation !== 'overlay')
62
+ return;
63
+ const before = document.activeElement;
64
+ const root = rootRef.current;
65
+ if (root && !root.contains(document.activeElement))
66
+ root.focus({ preventScroll: true });
67
+ return () => {
68
+ if (before?.isConnected)
69
+ before.focus({ preventScroll: true });
70
+ };
71
+ }, [presentation]);
72
+ const onPointerDown = (event) => {
73
+ if (event.button !== 0)
74
+ return;
75
+ event.preventDefault();
76
+ event.currentTarget.setPointerCapture(event.pointerId);
77
+ setDragging(true);
78
+ };
79
+ const onPointerMove = (event) => {
80
+ if (!dragging)
81
+ return;
82
+ const box = splitRef.current?.getBoundingClientRect();
83
+ if (!box || box.width === 0)
84
+ return;
85
+ commit((event.clientX - box.left) / box.width);
86
+ };
87
+ const onPointerUp = (event) => {
88
+ if (event.currentTarget.hasPointerCapture(event.pointerId))
89
+ event.currentTarget.releasePointerCapture(event.pointerId);
90
+ setDragging(false);
91
+ };
92
+ const onDividerKey = (event) => {
93
+ const step = event.shiftKey ? 0.1 : 0.02;
94
+ const next = event.key === 'ArrowLeft' ? split - step
95
+ : event.key === 'ArrowRight' ? split + step
96
+ : event.key === 'Home' ? MIN_SPLIT
97
+ : event.key === 'End' ? MAX_SPLIT
98
+ : null;
99
+ if (next === null)
100
+ return;
101
+ event.preventDefault();
102
+ commit(next);
103
+ };
104
+ const onKeyDown = (event) => {
105
+ if (event.key !== 'Escape' || event.defaultPrevented)
106
+ return;
107
+ // A menu, a listbox or an open combobox inside the form closes itself on Escape first. Only a
108
+ // POPUP's trigger counts: a disclosure (a folded row's ▸, `aria-expanded` with no popup) has
109
+ // nothing to close, and Escape on it is the page's.
110
+ if (event.target.closest(OWNS_ESCAPE))
111
+ return;
112
+ event.preventDefault();
113
+ if (isConfirming)
114
+ cancelDiscard();
115
+ else
116
+ guardedClose();
117
+ };
118
+ return (_jsxs("div", { ref: rootRef, className: cn('cbfk-page', className), "data-presentation": presentation, "data-mode": mode, "data-dragging": dragging ? 'true' : undefined, role: presentation === 'overlay' ? 'dialog' : 'region', "aria-modal": presentation === 'overlay' ? true : undefined, "aria-labelledby": titleId, tabIndex: -1, onKeyDown: onKeyDown, children: [_jsxs("header", { className: 'cbfk-page-head', children: [_jsxs(UnstyledButton, { className: 'cbfk-page-back', onClick: guardedClose, "aria-label": backLabel, tooltip: backLabel, children: [_jsx(ArrowLeft, { "aria-hidden": true }), _jsx("span", { className: 'cbfk-page-crumb', children: backLabel })] }), _jsx("span", { className: 'cbfk-page-sep', "aria-hidden": true, children: "/" }), _jsx("h1", { id: titleId, className: 'cbfk-page-title', children: title }), _jsx("div", { className: 'cbfk-page-modes', role: 'tablist', "aria-label": 'Show', children: ['edit', 'preview'].map((each) => (_jsx(UnstyledButton, { role: 'tab', className: 'cbfk-page-mode', "aria-selected": mode === each, "aria-controls": each === 'edit' ? formId : previewId, onClick: () => setMode(each), children: each === 'edit' ? 'Edit' : previewLabel }, each))) }), actions ? _jsx("div", { className: 'cbfk-page-actions', children: actions }) : null] }), _jsxs("div", { ref: splitRef, className: 'cbfk-page-split', style: { '--cbfk-page-split': `${(split * 100).toFixed(2)}%` }, children: [_jsx("div", { id: formId, className: cn('cbfk-page-form', quiet && 'cbfk-quiet'), children: _jsx("div", { className: 'cbfk-page-form-inner', children: children }) }), _jsx("div", { className: 'cbfk-page-divider', role: 'separator', "aria-orientation": 'vertical', "aria-label": 'Resize the form and the preview', "aria-valuemin": MIN_SPLIT * 100, "aria-valuemax": MAX_SPLIT * 100, "aria-valuenow": Math.round(split * 100), "aria-controls": formId, tabIndex: 0, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerUp, onKeyDown: onDividerKey, onDoubleClick: () => commit(defaultSplit) }), _jsxs("section", { id: previewId, className: 'cbfk-page-preview', "aria-label": previewLabel, children: [_jsxs("div", { className: 'cbfk-page-preview-head', children: [_jsx("p", { className: 'cbfk-page-preview-label', children: previewLabel }), previewTools ? _jsx("div", { className: 'cbfk-page-preview-tools', children: previewTools }) : null] }), _jsx("div", { className: 'cbfk-page-preview-body', children: preview })] })] }), discardOverlay] }));
119
+ }
120
+ /**
121
+ * Linear's form, for any form: inputs, textareas and selects drawn with no box until they are
122
+ * pointed at or focused, labels muted and small, so the values are the only full-strength text.
123
+ *
124
+ * The owner, 2026-10-05, beside a Linear project page: *"I like how it doesn't have boxes around
125
+ * all the fields and doesn't fill in extra text or explanations. I want this minimal look in our
126
+ * apps too."* `EditorPage` puts its form inside one; wrap any other form to get the same.
127
+ * The words are the caller's half: a field that needs a description to be understood has the
128
+ * wrong label or the wrong placeholder.
129
+ */
130
+ export function QuietForm({ children, className }) {
131
+ return _jsx("div", { className: cn('cbfk-quiet', className), children: children });
132
+ }
133
+ //# sourceMappingURL=EditorPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorPage.js","sourceRoot":"","sources":["../../../src/react/components/EditorPage.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import './formKit.css';
2
+ import './editorPage.css';
3
+ /**
4
+ * An icon somebody picked, with its drawing — so whoever stores it can draw it again with no
5
+ * icon library and no network: `iconDataUrl(icon, color)`.
6
+ */
7
+ export interface PickedIcon {
8
+ /** Iconify's name, `set:icon` — `lucide:code`, `simple-icons:kubernetes`. */
9
+ name: string;
10
+ /** The SVG's inner markup, every colour `currentColor`. */
11
+ body: string;
12
+ width: number;
13
+ height: number;
14
+ }
15
+ export interface IconSet {
16
+ prefix: string;
17
+ label: string;
18
+ }
19
+ /**
20
+ * The sets searched, all single-colour (they tint): the general-purpose four, and Simple Icons
21
+ * for the brands a runbook names — Kubernetes, Datadog, GitHub. Multi-colour logo sets are left
22
+ * out because a colour of the owner's choosing is the point of the mark.
23
+ */
24
+ export declare const ICON_SETS: readonly IconSet[];
25
+ export declare const ICONIFY_API = "https://api.iconify.design";
26
+ /**
27
+ * A picked icon as an `<img>` source, in `color`. An image rather than inline SVG on purpose: a
28
+ * stored body is drawn wherever the record goes, and an SVG inside an `<img>` cannot run script
29
+ * or reach the page, whatever the stored markup says. `color` must be a hex or a colour's name —
30
+ * anything else is left out, and the icon draws in the SVG's default black.
31
+ */
32
+ export declare function iconDataUrl(icon: Pick<PickedIcon, 'body' | 'width' | 'height'>, color: string): string;
33
+ /** Iconify's search, then every hit's drawing, one request per set. */
34
+ export declare function searchIcons(query: string, prefixes: readonly string[], fetcher?: typeof fetch, signal?: AbortSignal): Promise<PickedIcon[]>;
35
+ export interface IconPickerProps {
36
+ /** The chosen icon's name, drawn as selected. */
37
+ value?: string | null;
38
+ onPick: (icon: PickedIcon) => void;
39
+ /**
40
+ * The colour the results are drawn in — the mark's own, so the choice is made in it. A hex
41
+ * or a colour's name: the hits are images, which cannot inherit the page's `currentColor`.
42
+ */
43
+ color: string;
44
+ /**
45
+ * What to search for until somebody types — the thing's name is a good first guess. Followed
46
+ * as it changes (a name typed after the picker opened still searches), and let go of for good
47
+ * the moment the search field is typed in.
48
+ */
49
+ initialQuery?: string;
50
+ sets?: readonly IconSet[];
51
+ /** The request function — a test's seam. */
52
+ fetcher?: typeof fetch;
53
+ className?: string;
54
+ }
55
+ /**
56
+ * Search big icon sets and pick one: a search field, a row of sets, and a grid of hits drawn in
57
+ * the mark's own colour.
58
+ *
59
+ * The owner, 2026-10-05: *"For the mark I want a way to search in icon lists to choose a good
60
+ * icon … if I make a code snippet template I want to be able to pick an icon instead of letters
61
+ * like "CS". If we can have a search field and access to big icon sets like at shadcn.io/icons or
62
+ * iconify that will be good."* So Iconify's public API: 200,000 icons across the sets above,
63
+ * searched where they live rather than shipped in every app's bundle. A picked icon carries its
64
+ * own drawing (`PickedIcon.body`), so drawing it later needs neither the API nor the network.
65
+ *
66
+ * Keys: type to search; ↓ from the field enters the grid; arrows move in it; Enter or Space
67
+ * picks. Plain stylesheet (`formKit.css`); `cursedbelt/react/form-kit`.
68
+ */
69
+ export declare function IconPicker({ value, onPick, color, initialQuery, sets, fetcher, className, }: IconPickerProps): import("react").JSX.Element;
70
+ //# sourceMappingURL=IconPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IconPicker.d.ts","sourceRoot":"","sources":["../../../src/react/components/IconPicker.tsx"],"names":[],"mappings":"AAIA,OAAO,eAAe,CAAC;AACvB,OAAO,kBAAkB,CAAC;AAE1B;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,SAAS,OAAO,EAMvC,CAAC;AAEF,eAAO,MAAM,WAAW,+BAA+B,CAAC;AAKxD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAItG;AASD,uEAAuE;AACvE,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,OAAO,GAAE,OAAO,KAAa,EAC7B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAoCvB;AAED,MAAM,WAAW,eAAe;IAC9B,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACnC;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC1B,4CAA4C;IAC5C,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAiB,EACjB,IAAgB,EAChB,OAAO,EACP,SAAS,GACV,EAAE,eAAe,+BA2IjB"}
@@ -0,0 +1,175 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from 'react';
3
+ import { cn } from '../lib/cn.js';
4
+ import { SearchInput } from './SearchInput.js';
5
+ import { UnstyledButton } from './UnstyledButton.js';
6
+ import './formKit.css';
7
+ import './editorPage.css';
8
+ /**
9
+ * The sets searched, all single-colour (they tint): the general-purpose four, and Simple Icons
10
+ * for the brands a runbook names — Kubernetes, Datadog, GitHub. Multi-colour logo sets are left
11
+ * out because a colour of the owner's choosing is the point of the mark.
12
+ */
13
+ export const ICON_SETS = [
14
+ { prefix: 'lucide', label: 'Lucide' },
15
+ { prefix: 'tabler', label: 'Tabler' },
16
+ { prefix: 'ph', label: 'Phosphor' },
17
+ { prefix: 'mdi', label: 'Material' },
18
+ { prefix: 'simple-icons', label: 'Brands' },
19
+ ];
20
+ export const ICONIFY_API = 'https://api.iconify.design';
21
+ const RESULTS = 96;
22
+ /** A hex, or a named colour — nothing that could close the attribute it is spliced into. */
23
+ const SAFE_COLOR = /^(?:#[0-9a-f]{3,8}|[a-z]+)$/i;
24
+ /**
25
+ * A picked icon as an `<img>` source, in `color`. An image rather than inline SVG on purpose: a
26
+ * stored body is drawn wherever the record goes, and an SVG inside an `<img>` cannot run script
27
+ * or reach the page, whatever the stored markup says. `color` must be a hex or a colour's name —
28
+ * anything else is left out, and the icon draws in the SVG's default black.
29
+ */
30
+ export function iconDataUrl(icon, color) {
31
+ const tint = SAFE_COLOR.test(color) ? ` color="${color}"` : '';
32
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${icon.width} ${icon.height}" width="${icon.width}" height="${icon.height}"${tint}>${icon.body}</svg>`;
33
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
34
+ }
35
+ /** Iconify's search, then every hit's drawing, one request per set. */
36
+ export async function searchIcons(query, prefixes, fetcher = fetch, signal) {
37
+ const q = query.trim();
38
+ if (q === '')
39
+ return [];
40
+ const search = new URL('/search', ICONIFY_API);
41
+ search.searchParams.set('query', q);
42
+ search.searchParams.set('limit', String(RESULTS));
43
+ search.searchParams.set('prefixes', prefixes.join(','));
44
+ const found = await fetcher(search.toString(), { signal });
45
+ if (!found.ok)
46
+ throw new Error(`The icon search answered ${found.status}.`);
47
+ const names = (await found.json()).icons ?? [];
48
+ const bySet = new Map();
49
+ for (const name of names) {
50
+ const [prefix, icon] = name.split(':');
51
+ if (!prefix || !icon)
52
+ continue;
53
+ bySet.set(prefix, [...(bySet.get(prefix) ?? []), icon]);
54
+ }
55
+ const drawn = new Map();
56
+ await Promise.all([...bySet].map(async ([prefix, icons]) => {
57
+ const url = new URL(`/${prefix}.json`, ICONIFY_API);
58
+ url.searchParams.set('icons', icons.join(','));
59
+ const answer = await fetcher(url.toString(), { signal });
60
+ if (!answer.ok)
61
+ return;
62
+ const set = (await answer.json());
63
+ for (const [icon, data] of Object.entries(set.icons ?? {})) {
64
+ drawn.set(`${prefix}:${icon}`, {
65
+ name: `${prefix}:${icon}`,
66
+ body: data.body,
67
+ width: data.width ?? set.width ?? 24,
68
+ height: data.height ?? set.height ?? 24,
69
+ });
70
+ }
71
+ }));
72
+ // In the search's own order — its ranking is the useful part of it.
73
+ return names.map((name) => drawn.get(name)).filter((icon) => icon !== undefined);
74
+ }
75
+ /**
76
+ * Search big icon sets and pick one: a search field, a row of sets, and a grid of hits drawn in
77
+ * the mark's own colour.
78
+ *
79
+ * The owner, 2026-10-05: *"For the mark I want a way to search in icon lists to choose a good
80
+ * icon … if I make a code snippet template I want to be able to pick an icon instead of letters
81
+ * like "CS". If we can have a search field and access to big icon sets like at shadcn.io/icons or
82
+ * iconify that will be good."* So Iconify's public API: 200,000 icons across the sets above,
83
+ * searched where they live rather than shipped in every app's bundle. A picked icon carries its
84
+ * own drawing (`PickedIcon.body`), so drawing it later needs neither the API nor the network.
85
+ *
86
+ * Keys: type to search; ↓ from the field enters the grid; arrows move in it; Enter or Space
87
+ * picks. Plain stylesheet (`formKit.css`); `cursedbelt/react/form-kit`.
88
+ */
89
+ export function IconPicker({ value, onPick, color, initialQuery = '', sets = ICON_SETS, fetcher, className, }) {
90
+ const [query, setQuery] = useState(initialQuery);
91
+ const [typed, setTyped] = useState(false);
92
+ const [only, setOnly] = useState(null);
93
+ const [results, setResults] = useState([]);
94
+ const [state, setState] = useState('idle');
95
+ const [active, setActive] = useState(0);
96
+ const grid = useRef(null);
97
+ useEffect(() => {
98
+ if (!typed)
99
+ setQuery(initialQuery);
100
+ }, [initialQuery, typed]);
101
+ const prefixes = useMemo(() => (only ? [only] : sets.map((set) => set.prefix)), [only, sets]);
102
+ useEffect(() => {
103
+ if (query.trim() === '') {
104
+ setResults([]);
105
+ setState('idle');
106
+ return;
107
+ }
108
+ const abort = new AbortController();
109
+ setState('loading');
110
+ const timer = setTimeout(() => {
111
+ searchIcons(query, prefixes, fetcher, abort.signal).then((found) => {
112
+ setResults(found);
113
+ setActive(0);
114
+ setState('done');
115
+ }, (cause) => {
116
+ if (cause?.name === 'AbortError')
117
+ return;
118
+ setState('error');
119
+ });
120
+ }, 200);
121
+ return () => {
122
+ clearTimeout(timer);
123
+ abort.abort();
124
+ };
125
+ }, [query, prefixes, fetcher]);
126
+ const focusAt = (index) => {
127
+ const buttons = grid.current?.querySelectorAll('[data-icon]');
128
+ if (!buttons || buttons.length === 0)
129
+ return;
130
+ const next = Math.max(0, Math.min(buttons.length - 1, index));
131
+ setActive(next);
132
+ buttons[next]?.focus();
133
+ };
134
+ const columns = () => {
135
+ const buttons = grid.current?.querySelectorAll('[data-icon]');
136
+ if (!buttons || buttons.length === 0)
137
+ return 1;
138
+ const top = buttons[0].offsetTop;
139
+ let count = 0;
140
+ for (const button of buttons) {
141
+ if (button.offsetTop !== top)
142
+ break;
143
+ count += 1;
144
+ }
145
+ return Math.max(1, count);
146
+ };
147
+ const onGridKey = (event) => {
148
+ const step = event.key === 'ArrowRight' ? 1
149
+ : event.key === 'ArrowLeft' ? -1
150
+ : event.key === 'ArrowDown' ? columns()
151
+ : event.key === 'ArrowUp' ? -columns()
152
+ : null;
153
+ if (step === null)
154
+ return;
155
+ event.preventDefault();
156
+ focusAt(active + step);
157
+ };
158
+ return (_jsxs("div", { className: cn('cbfk-icons', className), children: [_jsx(SearchInput, { value: query, onChange: (next) => {
159
+ setTyped(true);
160
+ setQuery(next);
161
+ }, syntax: false, placeholder: 'Search icons \u2014 code, server, chart, kubernetes\u2026', "aria-label": 'Search icons', onKeyDown: (event) => {
162
+ if (event.key === 'ArrowDown' && results.length > 0) {
163
+ event.preventDefault();
164
+ focusAt(0);
165
+ }
166
+ } }), _jsx("fieldset", { className: 'cbfk-icons-sets', "aria-label": 'Icon sets', children: [{ prefix: null, label: 'All' }, ...sets].map((set) => (_jsx(UnstyledButton, { className: 'cbfk-icons-set', "aria-pressed": only === set.prefix, onClick: () => setOnly(set.prefix), children: set.label }, set.prefix ?? 'all'))) }), _jsx("div", { ref: grid, className: 'cbfk-icons-grid', role: 'radiogroup', "aria-label": 'Icons', "aria-busy": state === 'loading', onKeyDown: onGridKey, children: results.map((icon, index) => {
167
+ const [set, name] = icon.name.split(':');
168
+ return (_jsx(UnstyledButton, { role: 'radio', "data-icon": icon.name, className: 'cbfk-icons-cell', "aria-checked": icon.name === value, "aria-label": `${name} (${sets.find((each) => each.prefix === set)?.label ?? set})`, tooltip: icon.name, tabIndex: index === active ? 0 : -1, onFocus: () => setActive(index), onClick: () => onPick(icon), children: _jsx("img", { src: iconDataUrl(icon, color), alt: '', width: 22, height: 22, draggable: false }) }, icon.name));
169
+ }) }), _jsx("p", { className: 'cbfk-icons-note', role: 'status', children: state === 'idle' ? 'Type to search Lucide, Tabler, Phosphor, Material and brand icons.'
170
+ : state === 'loading' ? 'Searching…'
171
+ : state === 'error' ? 'Icon search needs the internet — api.iconify.design did not answer.'
172
+ : results.length === 0 ? `No icon matches “${query.trim()}”.`
173
+ : `${results.length} icons${results.length === RESULTS ? ' — the closest; type more to narrow' : ''}.` })] }));
174
+ }
175
+ //# sourceMappingURL=IconPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IconPicker.js","sourceRoot":"","sources":["../../../src/react/components/IconPicker.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAsB,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjF,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,eAAe,CAAC;AACvB,OAAO,kBAAkB,CAAC;AAoB1B;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAuB;IAC3C,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;IACrC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE;IACrC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE;IACnC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE;IACpC,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE;CAC5C,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,4BAA4B,CAAC;AACxD,MAAM,OAAO,GAAG,EAAE,CAAC;AACnB,4FAA4F;AAC5F,MAAM,UAAU,GAAG,8BAA8B,CAAC;AAElD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAmD,EAAE,KAAa;IAC5F,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/D,MAAM,GAAG,GAAG,wDAAwD,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,YAAY,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,MAAM,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,QAAQ,CAAC;IACzK,OAAO,oCAAoC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;AACvE,CAAC;AASD,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAa,EACb,QAA2B,EAC3B,UAAwB,KAAK,EAC7B,MAAoB;IAEpB,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACvB,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC/C,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5E,MAAM,KAAK,GAAI,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAA0B,CAAC,KAAK,IAAI,EAAE,CAAC;IACzE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI;YAAE,SAAS;QAC/B,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC5C,MAAM,OAAO,CAAC,GAAG,CACf,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE;QACvC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,MAAM,OAAO,EAAE,WAAW,CAAC,CAAC;QACpD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO;QACvB,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAmB,CAAC;QACpD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;YAC3D,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,EAAE;gBAC7B,IAAI,EAAE,GAAG,MAAM,IAAI,IAAI,EAAE;gBACzB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE;gBACpC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE;aACxC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IACF,oEAAoE;IACpE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAsB,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;AACvG,CAAC;AAuBD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAY,GAAG,EAAE,EACjB,IAAI,GAAG,SAAS,EAChB,OAAO,EACP,SAAS,GACO;IAChB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACtD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAe,EAAE,CAAC,CAAC;IACzD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAwC,MAAM,CAAC,CAAC;IAClF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC1C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,KAAK;YAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IACrC,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;IAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAE9F,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACxB,UAAU,CAAC,EAAE,CAAC,CAAC;YACf,QAAQ,CAAC,MAAM,CAAC,CAAC;YACjB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;QACpC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACpB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CACtD,CAAC,KAAK,EAAE,EAAE;gBACR,UAAU,CAAC,KAAK,CAAC,CAAC;gBAClB,SAAS,CAAC,CAAC,CAAC,CAAC;gBACb,QAAQ,CAAC,MAAM,CAAC,CAAC;YACnB,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;gBACjB,IAAK,KAA2B,EAAE,IAAI,KAAK,YAAY;oBAAE,OAAO;gBAChE,QAAQ,CAAC,OAAO,CAAC,CAAC;YACpB,CAAC,CACF,CAAC;QACJ,CAAC,EAAE,GAAG,CAAC,CAAC;QACR,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAE/B,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAoB,aAAa,CAAC,CAAC;QACjF,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QAC9D,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;IACzB,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,GAAW,EAAE;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAc,aAAa,CAAC,CAAC;QAC3E,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC,SAAS,CAAC;QAClC,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,SAAS,KAAK,GAAG;gBAAE,MAAM;YACpC,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5B,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,CAAC,KAAoC,EAAE,EAAE;QACzD,MAAM,IAAI,GACR,KAAK,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChC,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,EAAE;oBACvC,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE;wBACtC,CAAC,CAAC,IAAI,CAAC;QACT,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO;QAC1B,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC,aACzC,KAAC,WAAW,IACV,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;oBACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;oBACf,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACjB,CAAC,EACD,MAAM,EAAE,KAAK,EACb,WAAW,EAAC,2DAAiD,gBAClD,cAAc,EACzB,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;oBACnB,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACpD,KAAK,CAAC,cAAc,EAAE,CAAC;wBACvB,OAAO,CAAC,CAAC,CAAC,CAAC;oBACb,CAAC;gBACH,CAAC,GACD,EACF,mBAAU,SAAS,EAAC,iBAAiB,gBAAY,WAAW,YACzD,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACtD,KAAC,cAAc,IAEb,SAAS,EAAC,gBAAgB,kBACZ,IAAI,KAAK,GAAG,CAAC,MAAM,EACjC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,YAEjC,GAAG,CAAC,KAAK,IALL,GAAG,CAAC,MAAM,IAAI,KAAK,CAMT,CAClB,CAAC,GACO,EACX,cACE,GAAG,EAAE,IAAI,EACT,SAAS,EAAC,iBAAiB,EAC3B,IAAI,EAAC,YAAY,gBACN,OAAO,eACP,KAAK,KAAK,SAAS,EAC9B,SAAS,EAAE,SAAS,YAEnB,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;oBAC3B,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBACzC,OAAO,CACL,KAAC,cAAc,IAEb,IAAI,EAAC,OAAO,eACD,IAAI,CAAC,IAAI,EACpB,SAAS,EAAC,iBAAiB,kBACb,IAAI,CAAC,IAAI,KAAK,KAAK,gBACrB,GAAG,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,GAAG,GAAG,EACjF,OAAO,EAAE,IAAI,CAAC,IAAI,EAClB,QAAQ,EAAE,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACnC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAC/B,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,YAE3B,cAAK,GAAG,EAAE,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,GAAG,EAAC,EAAE,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,GAAI,IAXjF,IAAI,CAAC,IAAI,CAYC,CAClB,CAAC;gBACJ,CAAC,CAAC,GACE,EACN,YAAG,SAAS,EAAC,iBAAiB,EAAC,IAAI,EAAC,QAAQ,YACzC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,oEAAoE;oBACxF,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY;wBACpC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,qEAAqE;4BAC3F,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,oBAAoB,KAAK,CAAC,IAAI,EAAE,IAAI;gCAC7D,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,SAAS,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC,EAAE,GAAG,GACpG,IACA,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `PropertyList` — one record's facts as Linear draws them: a quiet label column, the values in
3
+ * a second column, and nothing else. No row fills, no boxes, no rules between rows.
4
+ *
5
+ * ── The failure it exists to stop ───────────────────────────────────────────
6
+ * The owner put desk's Components page under Linear's project page and asked a judge which had
7
+ * the better UI (2026-10-05). desk lost, and the judge's words for its facts were "tightly boxed
8
+ * container shapes, stark block borders": five label/value rows, each in its own filled,
9
+ * rounded box, with the label in bold. Linear's Properties panel carries nine rows in less ink:
10
+ * the label is muted and regular weight, the value is the only full-strength text in the row,
11
+ * and the rows are separated by nothing but their own height. Every app that hand-rolls a `<dl>`
12
+ * makes desk's choice again, so the shape lives here once and `no-hand-rolled-property-list`
13
+ * refuses the raw element in app source.
14
+ *
15
+ * The numbers are the design law's: one shared label column (FRM-1, FRM-2) sized by a token,
16
+ * rows on the control height so a value that IS a control (a select, a button) lines up with
17
+ * one that is text (FND-4), two type sizes at most (FND-5), and text that never drops under
18
+ * 12px (FND-6).
19
+ */
20
+ import type { HTMLAttributes, ReactNode } from "react";
21
+ export interface PropertyListProps extends HTMLAttributes<HTMLDListElement> {
22
+ /**
23
+ * `comfortable` (default) is Linear's panel: 14px text on 32px rows. `compact` is 12px text
24
+ * on 24px rows, for a dense detail pane that lists paths and ids.
25
+ */
26
+ density?: "comfortable" | "compact";
27
+ children: ReactNode;
28
+ }
29
+ export declare function PropertyList({ density, className, children, ...props }: PropertyListProps): import("react").JSX.Element;
30
+ export interface PropertyProps {
31
+ /** What the fact is. Plain words, sentence case (CPY-9). */
32
+ label: ReactNode;
33
+ /** A 16px mark before the value — Linear's status ring, a person, a calendar. */
34
+ icon?: ReactNode;
35
+ /** The value is not set: drawn muted, the way Linear draws "Add lead". */
36
+ empty?: boolean;
37
+ /** A monospace value with no word to break at (a path, an id) breaks anywhere. */
38
+ mono?: boolean;
39
+ /** Trailing controls that belong to this fact — a copy button, a reset (FRM-6). */
40
+ action?: ReactNode;
41
+ children: ReactNode;
42
+ }
43
+ /** One fact. Renders nothing when its value is absent, so a caller never filters by hand. */
44
+ export declare function Property({ label, icon, empty, mono, action, children }: PropertyProps): import("react").JSX.Element | null;
45
+ //# sourceMappingURL=PropertyList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PropertyList.d.ts","sourceRoot":"","sources":["../../../src/react/components/PropertyList.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvD,MAAM,WAAW,iBAAkB,SAAQ,cAAc,CAAC,gBAAgB,CAAC;IAC1E;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACpC,QAAQ,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAuB,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,iBAAiB,+BAczG;AAED,MAAM,WAAW,aAAa;IAC7B,4DAA4D;IAC5D,KAAK,EAAE,SAAS,CAAC;IACjB,iFAAiF;IACjF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kFAAkF;IAClF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,mFAAmF;IACnF,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACpB;AAED,6FAA6F;AAC7F,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAa,EAAE,IAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,aAAa,sCA6BrG"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn.js";
3
+ export function PropertyList({ density = "comfortable", className, children, ...props }) {
4
+ return (_jsx("dl", { "data-density": density, className: cn("group/properties m-0 grid grid-cols-[minmax(0,var(--property-label-w))_minmax(0,1fr)] gap-x-4", density === "compact" ? "gap-y-1 text-xs" : "gap-y-0 text-sm", className), ...props, children: children }));
5
+ }
6
+ /** One fact. Renders nothing when its value is absent, so a caller never filters by hand. */
7
+ export function Property({ label, icon, empty = false, mono = false, action, children }) {
8
+ if (children === undefined || children === null || children === "" || children === false)
9
+ return null;
10
+ return (_jsxs("div", { className: "contents", children: [_jsx("dt", { className: "m-0 flex min-h-[var(--control-h-sm)] min-w-0 items-center font-normal text-[var(--muted-foreground)] group-data-[density=compact]/properties:min-h-6 group-data-[density=compact]/properties:items-start", children: _jsx("span", { className: "truncate", children: label }) }), _jsxs("dd", { className: "m-0 flex min-h-[var(--control-h-sm)] min-w-0 items-center gap-2 group-data-[density=compact]/properties:min-h-6 group-data-[density=compact]/properties:items-start", children: [icon ? (_jsx("span", { "aria-hidden": "true", className: "flex size-4 flex-none items-center justify-center text-[var(--muted-foreground)]", children: icon })) : null, _jsx("span", { "data-slot": "property-value", className: cn("min-w-0", mono ? "break-all font-mono" : "break-words", empty ? "text-[var(--muted-foreground)]" : "text-[var(--foreground)]"), children: children }), action ? _jsx("span", { className: "flex flex-none items-center gap-1", children: action }) : null] })] }));
11
+ }
12
+ //# sourceMappingURL=PropertyList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PropertyList.js","sourceRoot":"","sources":["../../../src/react/components/PropertyList.tsx"],"names":[],"mappings":";AAoBA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAWlC,MAAM,UAAU,YAAY,CAAC,EAAE,OAAO,GAAG,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAqB;IACzG,OAAO,CACN,6BACe,OAAO,EACrB,SAAS,EAAE,EAAE,CACZ,+FAA+F,EAC/F,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,iBAAiB,EAC7D,SAAS,CACT,KACG,KAAK,YAER,QAAQ,GACL,CACL,CAAC;AACH,CAAC;AAgBD,6FAA6F;AAC7F,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAiB;IACrG,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACtG,OAAO,CACN,eAAK,SAAS,EAAC,UAAU,aACxB,aAAI,SAAS,EAAC,0MAA0M,YACvN,eAAM,SAAS,EAAC,UAAU,YAAE,KAAK,GAAQ,GACrC,EACL,cAAI,SAAS,EAAC,qKAAqK,aACjL,IAAI,CAAC,CAAC,CAAC,CACP,8BAAkB,MAAM,EAAC,SAAS,EAAC,kFAAkF,YACnH,IAAI,GACC,CACP,CAAC,CAAC,CAAC,IAAI,EAGR,4BACW,gBAAgB,EAC1B,SAAS,EAAE,EAAE,CACZ,SAAS,EACT,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,aAAa,EAC5C,KAAK,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,0BAA0B,CACrE,YAEA,QAAQ,GACH,EACN,MAAM,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,mCAAmC,YAAE,MAAM,GAAQ,CAAC,CAAC,CAAC,IAAI,IAChF,IACA,CACN,CAAC;AACH,CAAC"}
@@ -1,4 +1,4 @@
1
- import { type ReactNode } from 'react';
1
+ import type { ReactNode } from 'react';
2
2
  import { type FieldValues, type UseFormReturn } from 'react-hook-form';
3
3
  import type { FieldSpec } from '../FieldInput.js';
4
4
  import type { StepDef } from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"ReviewStep.d.ts","sourceRoot":"","sources":["../../../../src/react/components/Wizard/ReviewStep.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,KAAK,WAAW,EAAO,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIlD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,WAAW;IACpD,gFAAgF;IAChF,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACpB,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACvB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACvC;uCACmC;IACnC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC;CAC3D;AAED;;yEAEyE;AACzE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAS7D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,EAAE,EAChD,KAAK,EACL,IAAI,EACJ,UAAU,EACV,YAAY,GACb,EAAE,eAAe,CAAC,CAAC,CAAC,+BA6BpB"}
1
+ {"version":3,"file":"ReviewStep.d.ts","sourceRoot":"","sources":["../../../../src/react/components/Wizard/ReviewStep.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,KAAK,WAAW,EAAO,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAKlD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,WAAW;IACpD,gFAAgF;IAChF,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACpB,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACvB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACvC;uCACmC;IACnC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC;CAC3D;AAED;;yEAEyE;AACzE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAS7D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,EAAE,EAChD,KAAK,EACL,IAAI,EACJ,UAAU,EACV,YAAY,GACb,EAAE,eAAe,CAAC,CAAC,CAAC,+BA4BpB"}
@@ -1,7 +1,7 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Fragment } from 'react';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
3
2
  import { get } from 'react-hook-form';
4
3
  import { Stack } from '../layout.js';
4
+ import { Property, PropertyList } from '../PropertyList.js';
5
5
  import { Section } from '../Section.js';
6
6
  import { Surface } from '../Surface.js';
7
7
  /** A generic, type-aware fallback formatter for review rows. Apps override the hard
@@ -37,6 +37,6 @@ export function ReviewStep({ steps, form, fieldSpecs, displayValue, }) {
37
37
  const raw = get(values, key);
38
38
  return displayValue ? displayValue(key, raw) : defaultDisplayValue(raw);
39
39
  };
40
- return (_jsx(Stack, { gap: 6, children: steps.map((s) => (_jsx(Surface, { level: 'flat', pad: 'sm', children: _jsx(Section, { title: s.title, children: _jsx("dl", { className: 'grid grid-cols-[auto_1fr] gap-x-4 gap-y-2 text-sm', children: s.fields.map((key) => (_jsxs(Fragment, { children: [_jsx("dt", { className: 'font-medium text-muted-foreground', children: labelFor(key) }), _jsx("dd", { className: 'min-w-0 break-words text-foreground', children: valueFor(key) })] }, key))) }) }) }, s.id))) }));
40
+ return (_jsx(Stack, { gap: 6, children: steps.map((s) => (_jsx(Surface, { level: 'flat', pad: 'sm', children: _jsx(Section, { title: s.title, children: _jsx(PropertyList, { children: s.fields.map((key) => (_jsx(Property, { label: labelFor(key), children: valueFor(key) }, key))) }) }) }, s.id))) }));
41
41
  }
42
42
  //# sourceMappingURL=ReviewStep.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ReviewStep.js","sourceRoot":"","sources":["../../../../src/react/components/Wizard/ReviewStep.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AACjD,OAAO,EAAoB,GAAG,EAAsB,MAAM,iBAAiB,CAAC;AAE5E,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAcxC;;yEAEyE;AACzE,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,eAAM,SAAS,EAAC,uBAAuB,uBAAS,CAAC;IAC3F,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,eAAM,SAAS,EAAC,uBAAuB,uBAAS,CAAC;QAChF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAwB,EAChD,KAAK,EACL,IAAI,EACJ,UAAU,EACV,YAAY,GACO;IACnB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAa,EAAE;QAC1C,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;QAC3C,OAAO,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;IAC7C,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAa,EAAE;QAC1C,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC1E,CAAC,CAAC;IAEF,OAAO,CACL,KAAC,KAAK,IAAC,GAAG,EAAE,CAAC,YACV,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAChB,KAAC,OAAO,IAAY,KAAK,EAAC,MAAM,EAAC,GAAG,EAAC,IAAI,YACvC,KAAC,OAAO,IAAC,KAAK,EAAE,CAAC,CAAC,KAAK,YACrB,aAAI,SAAS,EAAC,mDAAmD,YAC9D,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACrB,MAAC,QAAQ,eACP,aAAI,SAAS,EAAC,mCAAmC,YAAE,QAAQ,CAAC,GAAG,CAAC,GAAM,EACtE,aAAI,SAAS,EAAC,qCAAqC,YAAE,QAAQ,CAAC,GAAG,CAAC,GAAM,KAF3D,GAAG,CAGP,CACZ,CAAC,GACC,GACG,IAVE,CAAC,CAAC,EAAE,CAWR,CACX,CAAC,GACI,CACT,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ReviewStep.js","sourceRoot":"","sources":["../../../../src/react/components/Wizard/ReviewStep.tsx"],"names":[],"mappings":";AACA,OAAO,EAAoB,GAAG,EAAsB,MAAM,iBAAiB,CAAC;AAE5E,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAcxC;;yEAEyE;AACzE,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,eAAM,SAAS,EAAC,uBAAuB,uBAAS,CAAC;IAC3F,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,eAAM,SAAS,EAAC,uBAAuB,uBAAS,CAAC;QAChF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAwB,EAChD,KAAK,EACL,IAAI,EACJ,UAAU,EACV,YAAY,GACO;IACnB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAa,EAAE;QAC1C,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;QAC3C,OAAO,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;IAC7C,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAa,EAAE;QAC1C,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC1E,CAAC,CAAC;IAEF,OAAO,CACL,KAAC,KAAK,IAAC,GAAG,EAAE,CAAC,YACV,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAChB,KAAC,OAAO,IAAY,KAAK,EAAC,MAAM,EAAC,GAAG,EAAC,IAAI,YACvC,KAAC,OAAO,IAAC,KAAK,EAAE,CAAC,CAAC,KAAK,YACrB,KAAC,YAAY,cACV,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACrB,KAAC,QAAQ,IAAW,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,YACrC,QAAQ,CAAC,GAAG,CAAC,IADD,GAAG,CAEP,CACZ,CAAC,GACW,GACP,IATE,CAAC,CAAC,EAAE,CAUR,CACX,CAAC,GACI,CACT,CAAC;AACJ,CAAC"}