@ciridae/ui 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/conversation-source.d.ts +15 -1
- package/dist/chat/describe-tool-call.d.ts +3 -3
- package/dist/chat/describe-tool-call.js +3 -3
- package/dist/chat/message-text.js +2 -2
- package/dist/ciridae.css +16 -4
- package/dist/components/activity-feed.d.ts +1 -0
- package/dist/components/activity-feed.js +1 -0
- package/dist/components/app-frame.d.ts +13 -1
- package/dist/components/app-frame.js +45 -3
- package/dist/components/app-navigation.d.ts +28 -1
- package/dist/components/app-navigation.js +110 -14
- package/dist/components/badge.d.ts +29 -4
- package/dist/components/badge.js +134 -15
- package/dist/components/bulk-action-bar.js +3 -3
- package/dist/components/button.d.ts +10 -3
- package/dist/components/button.js +77 -44
- package/dist/components/calendar.js +44 -17
- package/dist/components/centered-page.js +2 -2
- package/dist/components/chart.d.ts +1 -1
- package/dist/components/chart.js +16 -11
- package/dist/components/chat-client-source.js +47 -6
- package/dist/components/chat-sheet-controller.d.ts +1 -0
- package/dist/components/chat-sheet-controller.js +1 -0
- package/dist/components/chat-sheet.d.ts +10 -2
- package/dist/components/chat-sheet.js +74 -37
- package/dist/components/chat.d.ts +90 -14
- package/dist/components/chat.js +401 -101
- package/dist/components/checkbox-item.d.ts +5 -2
- package/dist/components/checkbox-item.js +9 -5
- package/dist/components/chip.d.ts +5 -0
- package/dist/components/chip.js +27 -1
- package/dist/components/column-settings.js +1 -1
- package/dist/components/command-menu.d.ts +5 -1
- package/dist/components/command-menu.js +98 -32
- package/dist/components/command-palette.d.ts +6 -2
- package/dist/components/command-palette.js +20 -10
- package/dist/components/data-display.d.ts +35 -4
- package/dist/components/data-display.js +138 -19
- package/dist/components/data-grid.d.ts +14 -2
- package/dist/components/data-grid.js +61 -26
- package/dist/components/date-range-picker.js +9 -20
- package/dist/components/drawer.d.ts +5 -0
- package/dist/components/drawer.js +91 -54
- package/dist/components/editable-field.d.ts +17 -3
- package/dist/components/editable-field.js +57 -27
- package/dist/components/email-input.js +6 -5
- package/dist/components/environment-frame.d.ts +1 -1
- package/dist/components/environment-frame.js +37 -6
- package/dist/components/filter-bar.js +39 -28
- package/dist/components/form-controls.d.ts +9 -0
- package/dist/components/form-controls.js +46 -22
- package/dist/components/form-layout.d.ts +7 -3
- package/dist/components/form-layout.js +68 -23
- package/dist/components/glass-menu.d.ts +3 -0
- package/dist/components/glass-menu.js +63 -0
- package/dist/components/gradient-edge.d.ts +9 -0
- package/dist/components/gradient-edge.js +31 -0
- package/dist/components/input.d.ts +4 -2
- package/dist/components/input.js +26 -15
- package/dist/components/keycap.d.ts +9 -5
- package/dist/components/keycap.js +57 -19
- package/dist/components/lightbox.js +2 -1
- package/dist/components/locale.d.ts +10 -0
- package/dist/components/locale.js +18 -2
- package/dist/components/log-entry.d.ts +40 -0
- package/dist/components/log-entry.js +146 -0
- package/dist/components/mention-input.js +4 -3
- package/dist/components/menu.d.ts +6 -2
- package/dist/components/menu.js +34 -37
- package/dist/components/meter.d.ts +9 -1
- package/dist/components/meter.js +64 -6
- package/dist/components/navigation.d.ts +21 -4
- package/dist/components/navigation.js +84 -19
- package/dist/components/notification-tray.js +5 -4
- package/dist/components/overlays.d.ts +3 -1
- package/dist/components/overlays.js +69 -40
- package/dist/components/page-frame.js +1 -1
- package/dist/components/panel.d.ts +1 -1
- package/dist/components/panel.js +18 -12
- package/dist/components/radio-card.js +10 -9
- package/dist/components/record.d.ts +25 -1
- package/dist/components/record.js +147 -18
- package/dist/components/segmented-bar.d.ts +3 -1
- package/dist/components/segmented-bar.js +16 -6
- package/dist/components/select.d.ts +5 -2
- package/dist/components/select.js +103 -127
- package/dist/components/shell-skeleton.js +1 -1
- package/dist/components/shell.d.ts +2 -2
- package/dist/components/shell.js +43 -45
- package/dist/components/sidebar.d.ts +7 -2
- package/dist/components/sidebar.js +55 -27
- package/dist/components/single-line-text.d.ts +2 -0
- package/dist/components/single-line-text.js +16 -0
- package/dist/components/site-banner.d.ts +5 -2
- package/dist/components/site-banner.js +17 -6
- package/dist/components/skeleton.d.ts +7 -6
- package/dist/components/skeleton.js +28 -16
- package/dist/components/skeletons.d.ts +12 -0
- package/dist/components/skeletons.js +71 -8
- package/dist/components/stages.d.ts +78 -6
- package/dist/components/stages.js +512 -156
- package/dist/components/stat-card.d.ts +10 -2
- package/dist/components/stat-card.js +92 -37
- package/dist/components/status-banner.js +1 -1
- package/dist/components/tabs.js +26 -13
- package/dist/components/theme-toggle.d.ts +4 -0
- package/dist/components/theme-toggle.js +67 -0
- package/dist/components/toast.d.ts +9 -2
- package/dist/components/toast.js +78 -29
- package/dist/preset.js +291 -33
- package/dist/styled-system/styles.css +3107 -1039
- package/dist/styled-system/tokens/index.mjs +602 -22
- package/dist/styled-system/tokens/tokens.d.ts +11 -11
- package/dist/styled-system/types/prop-type.d.ts +2 -2
- package/dist/styles.css +16 -4
- package/dist/theme/color.d.ts +3 -0
- package/dist/theme/color.js +5 -0
- package/dist/theme/derive.d.ts +9 -2
- package/dist/theme/derive.js +72 -12
- package/dist/theme/palettes.d.ts +49 -6
- package/dist/theme/palettes.js +84 -15
- package/dist/theme/tokens.d.ts +3 -3
- package/dist/theme/tokens.js +108 -32
- package/package.json +9 -1
|
@@ -9,55 +9,51 @@ import { HotkeyLayer } from "./hotkeys.js";
|
|
|
9
9
|
import { icon } from "./icon.js";
|
|
10
10
|
import { Eyebrow, heading } from "./layout.js";
|
|
11
11
|
import { useCiridaeLocale } from "./locale.js";
|
|
12
|
-
const backdrop = css({
|
|
13
|
-
position: "fixed",
|
|
14
|
-
inset: "none",
|
|
15
|
-
zIndex: "drawer",
|
|
16
|
-
background: "bg.backdrop",
|
|
17
|
-
transitionProperty: "opacity",
|
|
18
|
-
transitionDuration: "fast",
|
|
19
|
-
"&[data-starting-style], &[data-ending-style]": { opacity: "0" },
|
|
20
|
-
_motionReduce: { transitionDuration: "0ms" },
|
|
21
|
-
});
|
|
22
12
|
const viewport = css({ position: "fixed", inset: "none", zIndex: "drawer", pointerEvents: "none" });
|
|
13
|
+
/*
|
|
14
|
+
* A drawer sits beside the page, which stays usable: no scrim, docked to the window's right edge
|
|
15
|
+
* at full height, a glass shell holding the sheet. The shell's padding is what keeps the sheet off
|
|
16
|
+
* the window's edges, level with the main panel; a phone takes the sheet to the edges.
|
|
17
|
+
*/
|
|
23
18
|
const popup = cva({
|
|
24
19
|
base: {
|
|
25
20
|
position: "absolute",
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
insetBlock: "panel.compact",
|
|
29
|
-
insetInlineEnd: "panel.compact",
|
|
21
|
+
insetBlock: "none",
|
|
22
|
+
insetInlineEnd: "none",
|
|
30
23
|
display: "flex",
|
|
31
|
-
|
|
24
|
+
padding: "panel.compact",
|
|
32
25
|
pointerEvents: "auto",
|
|
33
26
|
outline: "none",
|
|
34
27
|
textStyle: "body",
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
border: "1px solid token(colors.border.divider)",
|
|
29
|
+
borderRadius: "shell",
|
|
30
|
+
background: "bg.drawerShell",
|
|
31
|
+
backdropFilter: "blur(token(blurs.drawerShell))",
|
|
32
|
+
boxShadow: "drawerShell",
|
|
33
|
+
_phone: { padding: "none", border: "0", borderRadius: "0" },
|
|
40
34
|
// Base UI drives live swipe drag through this custom property; the resting
|
|
41
35
|
// transform must reference it or dragging won't visually track the pointer.
|
|
42
36
|
transform: "translateX(var(--drawer-swipe-movement-x, 0px))",
|
|
43
37
|
transitionProperty: "transform",
|
|
44
38
|
transitionDuration: "default",
|
|
45
39
|
"&[data-starting-style], &[data-ending-style]": {
|
|
46
|
-
transform: "translateX(
|
|
47
|
-
_rtl: { transform: "translateX(
|
|
40
|
+
transform: "translateX(100%)",
|
|
41
|
+
_rtl: { transform: "translateX(-100%)" },
|
|
48
42
|
},
|
|
49
43
|
"&[data-swiping]": { transitionDuration: "0ms" },
|
|
50
44
|
_motionReduce: { transitionDuration: "0ms" },
|
|
51
45
|
},
|
|
46
|
+
// The sheet's width, plus the shell's padding and its 1px edge on each side.
|
|
52
47
|
variants: {
|
|
53
48
|
size: {
|
|
54
|
-
//
|
|
49
|
+
// The assistant and a preview.
|
|
55
50
|
narrow: {
|
|
56
|
-
width: "min(calc(
|
|
51
|
+
width: "min(100vw, calc(token(sizes.overlay.sheetNarrow) + token(spacing.panel.compact) * 2 + 2px))",
|
|
57
52
|
_phone: { width: "100vw" },
|
|
58
53
|
},
|
|
54
|
+
// Create, edit and act.
|
|
59
55
|
regular: {
|
|
60
|
-
width: "min(calc(
|
|
56
|
+
width: "min(100vw, calc(token(sizes.overlay.sheet) + token(spacing.panel.compact) * 2 + 2px))",
|
|
61
57
|
_phone: { width: "100vw" },
|
|
62
58
|
},
|
|
63
59
|
// Near the full viewport, for content such as logs that needs the room.
|
|
@@ -72,26 +68,51 @@ const panel = css({
|
|
|
72
68
|
minWidth: "0",
|
|
73
69
|
minHeight: "0",
|
|
74
70
|
flexDirection: "column",
|
|
71
|
+
overflow: "hidden",
|
|
72
|
+
border: "1px solid token(colors.border.divider)",
|
|
73
|
+
borderRadius: "action",
|
|
74
|
+
background: "bg.drawerSheet",
|
|
75
|
+
boxShadow: "soft",
|
|
75
76
|
color: "fg.primary",
|
|
77
|
+
_phone: { border: "0", borderRadius: "0" },
|
|
76
78
|
});
|
|
77
|
-
// At least the app header's height, so on a desktop
|
|
78
|
-
// panel's beside it: both start one shell gap from the window's top.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
// At least the app header's height, so on a desktop a title-only sheet's header rule lines up with
|
|
80
|
+
// the main panel's beside it: both start one shell gap from the window's top. Under a subtitle it
|
|
81
|
+
// takes the design's even 16px padding, its close button at the top. The narrow sheet's header is
|
|
82
|
+
// one line exactly the app header's height, its 34px round buttons centred in it.
|
|
83
|
+
const header = cva({
|
|
84
|
+
base: {
|
|
85
|
+
display: "flex",
|
|
86
|
+
flexDirection: "column",
|
|
87
|
+
justifyContent: "center",
|
|
88
|
+
gap: "layout.contentGap",
|
|
89
|
+
minHeight: "shell.header",
|
|
90
|
+
paddingBlock: "panel.default",
|
|
91
|
+
paddingInlineStart: "panel.comfortable",
|
|
92
|
+
paddingInlineEnd: "panel.default",
|
|
93
|
+
borderBottom: "1px solid token(colors.border.divider)",
|
|
94
|
+
"&:has([data-sheet-subtitle])": {
|
|
95
|
+
paddingBlock: "panel.comfortable",
|
|
96
|
+
"& > [data-header-line]": { alignItems: "flex-start" },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
// The narrow header is the Ask Ciridae drawer's one line: 8px between its title and actions.
|
|
100
|
+
variants: {
|
|
101
|
+
size: {
|
|
102
|
+
narrow: { gap: "layout.controlGap", height: "shell.header", paddingBlock: "none" },
|
|
103
|
+
regular: { paddingInlineEnd: "panel.comfortable" },
|
|
104
|
+
large: { paddingInlineEnd: "panel.comfortable" },
|
|
105
|
+
},
|
|
106
|
+
},
|
|
89
107
|
});
|
|
90
|
-
const headerLine =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
const headerLine = cva({
|
|
109
|
+
base: {
|
|
110
|
+
display: "flex",
|
|
111
|
+
alignItems: "center",
|
|
112
|
+
justifyContent: "space-between",
|
|
113
|
+
gap: "layout.contentGap",
|
|
114
|
+
},
|
|
115
|
+
variants: { size: { narrow: { gap: "layout.controlGap" }, regular: {}, large: {} } },
|
|
95
116
|
});
|
|
96
117
|
const titleBlock = css({
|
|
97
118
|
display: "flex",
|
|
@@ -107,12 +128,21 @@ const titleLine = css({
|
|
|
107
128
|
gap: "layout.contentGap",
|
|
108
129
|
minWidth: "0",
|
|
109
130
|
});
|
|
110
|
-
const titleStyle =
|
|
111
|
-
|
|
131
|
+
const titleStyle = css({ minWidth: "0", lineHeight: "tight" });
|
|
132
|
+
const subtitleStyle = css({
|
|
133
|
+
margin: "none",
|
|
134
|
+
color: "fg.label",
|
|
135
|
+
fontSize: "code",
|
|
136
|
+
fontWeight: "medium",
|
|
137
|
+
letterSpacing: "compact",
|
|
138
|
+
lineHeight: "tight",
|
|
139
|
+
});
|
|
140
|
+
// The sheet's mark before its title, muted like the agent sheet's sparkle. A flex row of its own,
|
|
141
|
+
// so the heading stays the title's line high rather than an inline box's.
|
|
112
142
|
const titleMark = css({
|
|
113
|
-
display: "
|
|
143
|
+
display: "flex",
|
|
114
144
|
alignItems: "center",
|
|
115
|
-
gap: "
|
|
145
|
+
gap: "layout.controlGap",
|
|
116
146
|
minWidth: "0",
|
|
117
147
|
"& > svg": { flexShrink: "0", color: "fg.muted" },
|
|
118
148
|
});
|
|
@@ -154,10 +184,9 @@ const footer = css({
|
|
|
154
184
|
alignItems: "center",
|
|
155
185
|
justifyContent: "flex-end",
|
|
156
186
|
flexWrap: "wrap",
|
|
157
|
-
gap: "layout.
|
|
187
|
+
gap: "layout.tightGap",
|
|
158
188
|
padding: "panel.comfortable",
|
|
159
|
-
borderTop: "1px solid token(colors.border.
|
|
160
|
-
background: "bg.card",
|
|
189
|
+
borderTop: "1px solid token(colors.border.divider)",
|
|
161
190
|
});
|
|
162
191
|
const footerNote = css({
|
|
163
192
|
flex: "1",
|
|
@@ -166,12 +195,20 @@ const footerNote = css({
|
|
|
166
195
|
fontWeight: "medium",
|
|
167
196
|
letterSpacing: "compact",
|
|
168
197
|
});
|
|
169
|
-
const footerActions = css({ display: "flex", alignItems: "center", gap: "layout.
|
|
198
|
+
const footerActions = css({ display: "flex", alignItems: "center", gap: "layout.tightGap" });
|
|
199
|
+
/**
|
|
200
|
+
* A sheet docked at the window's right edge beside the page, which stays usable: it is not modal
|
|
201
|
+
* and a click outside leaves it open, so Escape or its close button closes it. Pass `modal` or
|
|
202
|
+
* `disablePointerDismissal` to change either.
|
|
203
|
+
*/
|
|
170
204
|
export function Drawer({ title, icon: titleIcon, eyebrow, subtitle, titleAccessory, actions, size = "regular", children, ...rootProps }) {
|
|
171
205
|
const { labels, dir } = useCiridaeLocale();
|
|
172
|
-
|
|
206
|
+
// The narrow sheet's title is body size, as the app header's is, beside a 34px close.
|
|
207
|
+
const narrow = size === "narrow";
|
|
208
|
+
const titleClassName = cx(heading({ size: narrow ? "small" : "medium" }), titleStyle);
|
|
209
|
+
const close = (_jsx(BaseDrawer.Close, { render: _jsx(IconButton, { "aria-label": labels.common.close, size: narrow ? "medium" : "small" }), children: _jsx(XIcon, { className: icon({ size: "small" }) }) }));
|
|
173
210
|
const titleContent = titleIcon ? (_jsxs("span", { className: titleMark, children: [titleIcon, title] })) : (title);
|
|
174
|
-
return (_jsx(BaseDrawer.Root, { ...rootProps, swipeDirection: dir === "rtl" ? "left" : "right", children: _jsx(BaseDrawer.Portal, { children:
|
|
211
|
+
return (_jsx(BaseDrawer.Root, { modal: false, disablePointerDismissal: true, ...rootProps, swipeDirection: dir === "rtl" ? "left" : "right", children: _jsx(BaseDrawer.Portal, { children: _jsx(HotkeyLayer, { children: _jsx(BaseDrawer.Viewport, { className: viewport, children: _jsx(BaseDrawer.Popup, { className: popup({ size }), "data-ciridae-ui": "", children: _jsxs("div", { className: panel, children: [_jsx("header", { className: header({ size }), children: eyebrow ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: eyebrowLine, children: [eyebrow, _jsxs("div", { className: headerActions, children: [actions, close] })] }), _jsxs("div", { className: css({ display: "grid", gap: "layout.controlGap" }), children: [_jsx(BaseDrawer.Title, { className: displayTitle, children: title }), subtitle && _jsx("p", { className: displaySubtitle, children: subtitle })] })] })) : (_jsxs("div", { className: headerLine({ size }), "data-header-line": "", children: [_jsxs("div", { className: titleBlock, children: [titleAccessory ? (_jsxs("div", { className: titleLine, children: [_jsx(BaseDrawer.Title, { className: titleClassName, children: titleContent }), titleAccessory] })) : (_jsx(BaseDrawer.Title, { className: titleClassName, children: titleContent })), subtitle && (_jsx("p", { className: subtitleStyle, "data-sheet-subtitle": "", children: subtitle }))] }), _jsxs("div", { className: headerActions, children: [actions, close] })] })) }), children] }) }) }) }) }) }));
|
|
175
212
|
}
|
|
176
213
|
export function DrawerBody({ flush = false, className, ...props }) {
|
|
177
214
|
return (_jsx(BaseDrawer.Content, { ...props, className: cx(body({ flush }), className), "data-ciridae-ui": "" }));
|
|
@@ -186,7 +223,7 @@ export function DrawerFooter({ note, className, children, ...props }) {
|
|
|
186
223
|
const field = css({
|
|
187
224
|
display: "flex",
|
|
188
225
|
flexDirection: "column",
|
|
189
|
-
gap: "
|
|
226
|
+
gap: "layout.controlGap",
|
|
190
227
|
minWidth: "0",
|
|
191
228
|
});
|
|
192
229
|
const fieldLabelRow = css({
|
|
@@ -198,7 +235,7 @@ const fieldLabelRow = css({
|
|
|
198
235
|
letterSpacing: "compact",
|
|
199
236
|
lineHeight: "tight",
|
|
200
237
|
});
|
|
201
|
-
const fieldHint = css({ color: "fg.
|
|
238
|
+
const fieldHint = css({ color: "fg.subtle" });
|
|
202
239
|
const fieldError = css({
|
|
203
240
|
margin: "none",
|
|
204
241
|
color: "accent.red",
|
|
@@ -65,16 +65,28 @@ export type EditableFieldProps = Omit<ComponentProps<"div">, "children"> & {
|
|
|
65
65
|
badge?: ReactNode;
|
|
66
66
|
/** Under the value while the row is idle. */
|
|
67
67
|
note?: ReactNode;
|
|
68
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* The field needs the reader: the row lifts on the selected glass and this button sits at its
|
|
70
|
+
* end. With `emphasis="highlighted"` the row keeps the yellow band, as the kit's Highlighted field
|
|
71
|
+
* does with its action.
|
|
72
|
+
*/
|
|
69
73
|
action?: {
|
|
70
74
|
label: ReactNode;
|
|
71
75
|
onClick: () => void;
|
|
72
76
|
disabled?: boolean;
|
|
73
77
|
};
|
|
78
|
+
/** `bottom` by default; `none` for the last row of a list whose container draws its own edge. */
|
|
79
|
+
divider?: "bottom" | "none";
|
|
80
|
+
/**
|
|
81
|
+
* `selected` washes the row with the selected glass, such as the field a side panel is
|
|
82
|
+
* showing; `inset` sets it into the list between hairlines, such as a field set apart;
|
|
83
|
+
* `highlighted` is the yellow band while the agent points at the field.
|
|
84
|
+
*/
|
|
85
|
+
emphasis?: "selected" | "inset" | "highlighted";
|
|
74
86
|
edit?: FieldEdit;
|
|
75
87
|
labels?: Partial<CiridaeLabels["editableField"]>;
|
|
76
88
|
};
|
|
77
|
-
export declare function EditableField({ label, value, provenance, badge, note, action, edit, labels: ownLabels, className, ...props }: EditableFieldProps): import("react").JSX.Element;
|
|
89
|
+
export declare function EditableField({ label, value, provenance, badge, note, action, divider, emphasis, edit, labels: ownLabels, className, ...props }: EditableFieldProps): import("react").JSX.Element;
|
|
78
90
|
export type ConflictingSource = {
|
|
79
91
|
id: string;
|
|
80
92
|
label: ReactNode;
|
|
@@ -92,8 +104,10 @@ export type SourceConflictProps = Omit<ComponentProps<"div">, "children"> & {
|
|
|
92
104
|
};
|
|
93
105
|
/** At the effective row's end, such as a Resolve button. */
|
|
94
106
|
action?: ReactNode;
|
|
107
|
+
/** `highlighted` while the agent points at the field; `neutral` by default. */
|
|
108
|
+
tone?: "highlighted" | "neutral";
|
|
95
109
|
labels?: Partial<{
|
|
96
110
|
effectiveValue: string;
|
|
97
111
|
}>;
|
|
98
112
|
};
|
|
99
|
-
export declare function SourceConflict({ sources, effective, action, labels: ownLabels, className, ...props }: SourceConflictProps): import("react").JSX.Element;
|
|
113
|
+
export declare function SourceConflict({ sources, effective, action, tone, labels: ownLabels, className, ...props }: SourceConflictProps): import("react").JSX.Element;
|
|
@@ -10,6 +10,7 @@ import { useEffect, useId, useRef, useState, } from "react";
|
|
|
10
10
|
import { css, cx } from "#primitives/css";
|
|
11
11
|
import { Button } from "./button.js";
|
|
12
12
|
import { Calendar } from "./calendar.js";
|
|
13
|
+
import { glassMenuSurface } from "./glass-menu.js";
|
|
13
14
|
import { useHotkey } from "./hotkeys.js";
|
|
14
15
|
import { icon, Spinner } from "./icon.js";
|
|
15
16
|
import { Input } from "./input.js";
|
|
@@ -18,24 +19,48 @@ import { Tooltip } from "./overlays.js";
|
|
|
18
19
|
import { Combobox } from "./select.js";
|
|
19
20
|
/* The row stacks its label over the value in a narrow box, so each field is its own container. */
|
|
20
21
|
const fieldBox = css({ containerType: "inline-size" });
|
|
22
|
+
/*
|
|
23
|
+
* The kit's Detail field: 12 · 16 padding, a hairline under it. A field that needs the reader
|
|
24
|
+
* (the inline edit's Needs confirm) and an inset row wear the selected glass between the selected
|
|
25
|
+
* edge's hairlines; a selected row wears the glass alone; a highlighted row, while the agent points
|
|
26
|
+
* at it, is the flat yellow band between hairlines.
|
|
27
|
+
*/
|
|
21
28
|
const fieldRow = css({
|
|
22
29
|
position: "relative",
|
|
23
30
|
display: "grid",
|
|
24
31
|
gridTemplateColumns: "150px minmax(0, 1fr) auto",
|
|
25
|
-
|
|
32
|
+
// The label sits on the value's first line, as the kit's does; the trailing control centres.
|
|
33
|
+
alignItems: "baseline",
|
|
26
34
|
gap: "layout.contentGap",
|
|
27
35
|
paddingBlock: "panel.default",
|
|
28
36
|
paddingInline: "panel.comfortable",
|
|
29
|
-
borderBottom: "1px solid token(colors.border.
|
|
37
|
+
borderBottom: "1px solid token(colors.border.divider)",
|
|
30
38
|
fontSize: "caption",
|
|
31
39
|
letterSpacing: "compact",
|
|
40
|
+
"& > :last-child": { alignSelf: "center" },
|
|
32
41
|
"&[data-needs]": {
|
|
33
|
-
background: "
|
|
34
|
-
borderBlock: "1px solid
|
|
42
|
+
background: "token(gradients.selected)",
|
|
43
|
+
borderBlock: "1px solid",
|
|
44
|
+
borderImage: "token(gradients.border.selected) 1",
|
|
45
|
+
},
|
|
46
|
+
// After the band's hairlines, so a last highlighted row keeps only its top one, as the kit's.
|
|
47
|
+
"&[data-divider=none]": { borderBottom: "0" },
|
|
48
|
+
"&[data-emphasis=selected]": { background: "token(gradients.selected)" },
|
|
49
|
+
"&[data-emphasis=inset]": {
|
|
50
|
+
background: "token(gradients.selected)",
|
|
51
|
+
borderBlock: "1px solid",
|
|
52
|
+
borderImage: "token(gradients.border.selected) 1",
|
|
53
|
+
},
|
|
54
|
+
// The kit's Highlighted detail field: the yellow band while the agent points at the field.
|
|
55
|
+
"&[data-emphasis=highlighted]": {
|
|
56
|
+
background: "bg.warningTint",
|
|
57
|
+
borderBlock: "1px solid token(colors.border.divider)",
|
|
58
|
+
},
|
|
59
|
+
"&[data-editable]": {
|
|
60
|
+
cursor: "pointer",
|
|
61
|
+
_hover: { background: "token(gradients.card)", _light: { background: "bg.rowHover" } },
|
|
35
62
|
},
|
|
36
|
-
"&[data-editable]": { cursor: "pointer", _hover: { background: "bg.rowHover" } },
|
|
37
63
|
"&[data-editable]:is(:hover, :focus-within) [data-affordance], &[data-editing] [data-affordance]": { opacity: "1" },
|
|
38
|
-
"&[data-editable]:hover > [data-label]": { color: "fg.secondary" },
|
|
39
64
|
"@container (max-width: 520px)": {
|
|
40
65
|
gridTemplateColumns: "minmax(0, 1fr) auto",
|
|
41
66
|
rowGap: "layout.tightGap",
|
|
@@ -43,10 +68,10 @@ const fieldRow = css({
|
|
|
43
68
|
},
|
|
44
69
|
});
|
|
45
70
|
const fieldLabel = css({
|
|
46
|
-
color: "fg.
|
|
71
|
+
color: "fg.secondary",
|
|
47
72
|
fontSize: "code",
|
|
48
73
|
fontWeight: "medium",
|
|
49
|
-
|
|
74
|
+
lineHeight: "tight",
|
|
50
75
|
});
|
|
51
76
|
const fieldValue = css({
|
|
52
77
|
display: "flex",
|
|
@@ -101,9 +126,10 @@ const provenanceLine = css({
|
|
|
101
126
|
gap: "layout.contentGap",
|
|
102
127
|
marginTop: "control.iconGap",
|
|
103
128
|
minHeight: "control.knob",
|
|
104
|
-
color: "fg.
|
|
129
|
+
color: "fg.label",
|
|
105
130
|
fontSize: "code",
|
|
106
131
|
fontWeight: "medium",
|
|
132
|
+
lineHeight: "tight",
|
|
107
133
|
"& > span, & > button, & > a": {
|
|
108
134
|
display: "inline-flex",
|
|
109
135
|
alignItems: "center",
|
|
@@ -133,31 +159,29 @@ const fieldNote = css({
|
|
|
133
159
|
color: "fg.secondary",
|
|
134
160
|
fontSize: "code",
|
|
135
161
|
fontWeight: "medium",
|
|
162
|
+
lineHeight: "tight",
|
|
136
163
|
});
|
|
137
|
-
const editor = css({ width: "200px", maxWidth: "100%"
|
|
164
|
+
const editor = css({ width: "200px", maxWidth: "100%" });
|
|
138
165
|
const editorValue = css({ position: "relative", display: "flex", alignItems: "center" });
|
|
139
166
|
const tooltipTitle = css({ display: "block", fontWeight: "medium" });
|
|
140
167
|
const tooltipDetail = css({ display: "block", fontSize: "code", opacity: "0.8" });
|
|
141
168
|
const trailingActions = css({ display: "inline-flex", gap: "layout.tightGap" });
|
|
142
|
-
/* The calendar opens above the date editor. */
|
|
143
|
-
const calendarPicker = css({
|
|
169
|
+
/* The calendar opens above the date editor, on the menus' glass sheet. */
|
|
170
|
+
const calendarPicker = css(glassMenuSurface, {
|
|
144
171
|
position: "absolute",
|
|
145
172
|
bottom: "calc(100% + token(spacing.control.iconGap))",
|
|
146
173
|
insetInlineStart: "0",
|
|
147
174
|
zIndex: "popover",
|
|
148
175
|
cursor: "default",
|
|
149
|
-
border: "1px solid token(colors.border.control)",
|
|
150
|
-
borderRadius: "inset",
|
|
151
|
-
background: "bg.card",
|
|
152
|
-
boxShadow: "card",
|
|
153
|
-
"& > section": { border: "0", background: "transparent" },
|
|
154
176
|
});
|
|
177
|
+
// Under the days, inset as the calendar is, such as the value it had before.
|
|
155
178
|
const calendarFooter = css({
|
|
156
|
-
paddingInline: "panel.
|
|
179
|
+
paddingInline: "panel.compact",
|
|
157
180
|
paddingBottom: "panel.compact",
|
|
158
181
|
color: "fg.muted",
|
|
159
182
|
fontSize: "code",
|
|
160
183
|
fontWeight: "medium",
|
|
184
|
+
lineHeight: "body",
|
|
161
185
|
});
|
|
162
186
|
/** An editor opens with its text selected, so typing replaces the value. */
|
|
163
187
|
function selectTyped(event) {
|
|
@@ -167,7 +191,7 @@ function selectTyped(event) {
|
|
|
167
191
|
function localMidnight(date) {
|
|
168
192
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
|
|
169
193
|
}
|
|
170
|
-
export function EditableField({ label, value, provenance, badge, note, action, edit, labels: ownLabels, className, ...props }) {
|
|
194
|
+
export function EditableField({ label, value, provenance, badge, note, action, divider = "bottom", emphasis, edit, labels: ownLabels, className, ...props }) {
|
|
171
195
|
const locale = useCiridaeLocale();
|
|
172
196
|
const labels = { ...locale.labels.editableField, ...ownLabels };
|
|
173
197
|
const formatDate = (timestamp) => locale.formatDate(timestamp);
|
|
@@ -307,7 +331,7 @@ export function EditableField({ label, value, provenance, badge, note, action, e
|
|
|
307
331
|
cancel();
|
|
308
332
|
} }) }) }));
|
|
309
333
|
}
|
|
310
|
-
return (_jsxs("span", { className: editorValue, onFocus: selectTyped, children: [_jsx(Input, { ref: inputRef, className: editor, "aria-labelledby": labelId, "aria-invalid": status.mode === "error" || undefined, value: draft, onChange: (event) => {
|
|
334
|
+
return (_jsxs("span", { className: editorValue, onFocus: selectTyped, children: [_jsx(Input, { ref: inputRef, size: "inline", className: editor, "aria-labelledby": labelId, "aria-invalid": status.mode === "error" || undefined, value: draft, onChange: (event) => {
|
|
311
335
|
setDraft(event.target.value);
|
|
312
336
|
if (status.mode === "error")
|
|
313
337
|
setStatus({ mode: "editing" });
|
|
@@ -364,18 +388,24 @@ export function EditableField({ label, value, provenance, badge, note, action, e
|
|
|
364
388
|
}
|
|
365
389
|
return _jsx("span", {});
|
|
366
390
|
};
|
|
367
|
-
return (_jsx("div", { ...props, className: cx(fieldBox, className), "data-ciridae-ui": "", children: _jsxs("div", { ref: rowRef, className: fieldRow, "data-needs": action && !editing ? "" : undefined, "data-editable": editable && !editing ? "" : undefined, "data-editing": editing ? "" : undefined, onClick: begin, children: [_jsx("span", { className: fieldLabel, id: labelId, "data-label": "", children: label }), _jsxs("span", { className: css({ minWidth: "0" }), children: [valueLine(), _jsx("span", { role: "status", children: statusLine() }), note !== undefined && status.mode === "idle" && (_jsx("span", { className: fieldNote, children: note }))] }), trailing()] }) }));
|
|
391
|
+
return (_jsx("div", { ...props, className: cx(fieldBox, className), "data-ciridae-ui": "", children: _jsxs("div", { ref: rowRef, className: fieldRow, "data-needs": action && !editing && emphasis !== "highlighted" ? "" : undefined, "data-divider": divider, "data-emphasis": emphasis, "data-editable": editable && !editing ? "" : undefined, "data-editing": editing ? "" : undefined, onClick: begin, children: [_jsx("span", { className: fieldLabel, id: labelId, "data-label": "", children: label }), _jsxs("span", { className: css({ minWidth: "0" }), children: [valueLine(), _jsx("span", { role: "status", children: statusLine() }), note !== undefined && status.mode === "idle" && (_jsx("span", { className: fieldNote, children: note }))] }), trailing()] }) }));
|
|
368
392
|
}
|
|
369
|
-
/*
|
|
393
|
+
/*
|
|
394
|
+
* Source conflict: each source's value under a disputed field, then the value in effect. The
|
|
395
|
+
* kit's Contested sources: flat and square between hairlines, the yellow band while the agent
|
|
396
|
+
* points at the field, the panel's face otherwise (the grey well in light).
|
|
397
|
+
*/
|
|
370
398
|
const conflictBlock = css({
|
|
371
399
|
display: "grid",
|
|
372
400
|
gap: "layout.controlGap",
|
|
373
401
|
paddingBlock: "panel.default",
|
|
374
402
|
paddingInline: "panel.comfortable",
|
|
375
|
-
|
|
376
|
-
background: "bg.panel",
|
|
403
|
+
borderBlock: "1px solid token(colors.border.divider)",
|
|
377
404
|
letterSpacing: "compact",
|
|
405
|
+
lineHeight: "tight",
|
|
378
406
|
containerType: "inline-size",
|
|
407
|
+
"&[data-tone=neutral]": { background: "bg.panel", _light: { background: "bg.field" } },
|
|
408
|
+
"&[data-tone=highlighted]": { background: "bg.warningTint" },
|
|
379
409
|
});
|
|
380
410
|
const conflictRow = css({
|
|
381
411
|
display: "grid",
|
|
@@ -406,7 +436,7 @@ const sourceMeta = css({
|
|
|
406
436
|
});
|
|
407
437
|
const effectiveRow = css({
|
|
408
438
|
paddingTop: "panel.default",
|
|
409
|
-
borderTop: "1px solid token(colors.border.
|
|
439
|
+
borderTop: "1px solid token(colors.border.divider)",
|
|
410
440
|
});
|
|
411
441
|
const effectiveLabel = css({ fontSize: "code", fontWeight: "medium" });
|
|
412
442
|
const effectiveNote = css({
|
|
@@ -416,7 +446,7 @@ const effectiveNote = css({
|
|
|
416
446
|
fontSize: "code",
|
|
417
447
|
fontWeight: "medium",
|
|
418
448
|
});
|
|
419
|
-
export function SourceConflict({ sources, effective, action, labels: ownLabels, className, ...props }) {
|
|
449
|
+
export function SourceConflict({ sources, effective, action, tone = "neutral", labels: ownLabels, className, ...props }) {
|
|
420
450
|
const labels = { ...useCiridaeLocale().labels.editableField, ...ownLabels };
|
|
421
|
-
return (_jsxs("div", { ...props, className: cx(conflictBlock, className), "data-ciridae-ui": "", children: [sources.map((source) => (_jsxs("div", { className: conflictRow, children: [_jsxs("span", { className: sourceName, children: [source.icon, source.label] }), _jsx("span", { className: sourceValue, children: source.value }), _jsx("span", { className: sourceMeta, children: source.meta })] }, source.id))), _jsxs("div", { className: cx(conflictRow, effectiveRow), children: [_jsx("span", { className: effectiveLabel, children: labels.effectiveValue }), _jsxs("span", { children: [_jsx("span", { className: sourceValue, children: effective.value }), effective.note !== undefined && _jsx("span", { className: effectiveNote, children: effective.note })] }), action ?? _jsx("span", {})] })] }));
|
|
451
|
+
return (_jsxs("div", { ...props, className: cx(conflictBlock, className), "data-ciridae-ui": "", "data-tone": tone, children: [sources.map((source) => (_jsxs("div", { className: conflictRow, children: [_jsxs("span", { className: sourceName, children: [source.icon, source.label] }), _jsx("span", { className: sourceValue, children: source.value }), _jsx("span", { className: sourceMeta, children: source.meta })] }, source.id))), _jsxs("div", { className: cx(conflictRow, effectiveRow), children: [_jsx("span", { className: effectiveLabel, children: labels.effectiveValue }), _jsxs("span", { children: [_jsx("span", { className: sourceValue, children: effective.value }), effective.note !== undefined && _jsx("span", { className: effectiveNote, children: effective.note })] }), action ?? _jsx("span", {})] })] }));
|
|
422
452
|
}
|
|
@@ -2,18 +2,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { css, cx } from "#primitives/css";
|
|
3
3
|
import { Chip } from "./chip.js";
|
|
4
4
|
import { useFieldControl } from "./field-context.js";
|
|
5
|
+
// The field face `Input` wears, holding chips that wrap onto more lines as they fill it.
|
|
5
6
|
const emailField = css({
|
|
6
7
|
display: "flex",
|
|
7
8
|
flexWrap: "wrap",
|
|
8
9
|
alignItems: "center",
|
|
9
10
|
gap: "layout.tightGap",
|
|
10
11
|
padding: "control.compactBlock",
|
|
11
|
-
minHeight: "control.
|
|
12
|
+
minHeight: "control.default",
|
|
12
13
|
border: "1px solid token(colors.border.control)",
|
|
13
|
-
borderRadius: "
|
|
14
|
-
background: "bg.
|
|
14
|
+
borderRadius: "control",
|
|
15
|
+
background: "bg.formField",
|
|
15
16
|
_focusWithin: { focusRing: "outer" },
|
|
16
|
-
"&[data-invalid]": { borderColor: "
|
|
17
|
+
"&[data-invalid]": { borderColor: "border.error" },
|
|
17
18
|
"&[data-disabled]": { opacity: "disabled" },
|
|
18
19
|
});
|
|
19
20
|
const draftInput = css({
|
|
@@ -28,7 +29,7 @@ const draftInput = css({
|
|
|
28
29
|
height: "control.shortcut",
|
|
29
30
|
paddingInline: "control.compactInline",
|
|
30
31
|
outline: "none",
|
|
31
|
-
_placeholder: { color: "fg.
|
|
32
|
+
_placeholder: { color: "fg.subtle" },
|
|
32
33
|
_focusVisible: { outline: "none", boxShadow: "none" },
|
|
33
34
|
});
|
|
34
35
|
/** Comma, semicolon, whitespace, Enter, Tab, or blur commits addresses; duplicates are ignored. */
|
|
@@ -9,5 +9,5 @@ export type EnvironmentFrameProps = ComponentProps<"div"> & {
|
|
|
9
9
|
/** The strip's tint: `warning` for a staging build, `info` for dev. */
|
|
10
10
|
tone?: SiteBannerProps["tone"];
|
|
11
11
|
};
|
|
12
|
-
/**
|
|
12
|
+
/** An environment strip, which the reader can dismiss for the session, and an inset AppShell viewport. */
|
|
13
13
|
export declare function EnvironmentFrame({ label, tone, children, className, ...props }: EnvironmentFrameProps): import("react").JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
2
3
|
import { css, cx } from "#primitives/css";
|
|
3
4
|
import { useCiridaeLocale } from "./locale.js";
|
|
4
5
|
import { SiteBanner } from "./site-banner.js";
|
|
@@ -17,26 +18,56 @@ const viewport = css({
|
|
|
17
18
|
flex: "1",
|
|
18
19
|
minHeight: "0",
|
|
19
20
|
overflow: "hidden",
|
|
20
|
-
background: "
|
|
21
|
+
background: "token(gradients.canvas)",
|
|
21
22
|
// Only under the environment strip does the app read as inset below it.
|
|
22
23
|
"&[data-under-banner]": {
|
|
23
24
|
borderTopRadius: "field",
|
|
24
25
|
boxShadow: "inset 0 2px 4px rgba(0, 0, 0, 0.18)",
|
|
25
26
|
},
|
|
26
|
-
// Keep the shell and its phone layout within the space below the site banner.
|
|
27
|
+
// Keep the shell and its phone layout within the space below the site banner. On a desktop the
|
|
28
|
+
// shell's row sizes the main panel inside its block margins, so a full height would push it past
|
|
29
|
+
// them; a phone's main panel is a full window tall, so there it takes the space under the strip.
|
|
27
30
|
"& > [data-ciridae-ui]": { height: "100%", background: "transparent" },
|
|
28
31
|
"& > [data-ciridae-ui] > main": {
|
|
29
|
-
height: "100%",
|
|
30
32
|
borderRadius: "inset",
|
|
31
|
-
_phone: { borderRadius: "0" },
|
|
33
|
+
_phone: { borderRadius: "0", height: "100%" },
|
|
32
34
|
},
|
|
33
35
|
"& > [data-ciridae-ui] > main > header": {
|
|
34
36
|
paddingInlineStart: "page.inline",
|
|
35
37
|
_phone: { paddingInlineStart: "panel.default" },
|
|
36
38
|
},
|
|
37
39
|
});
|
|
38
|
-
|
|
40
|
+
/*
|
|
41
|
+
* Dismissing the strip hides it for the rest of the browser tab's session. Where the browser blocks
|
|
42
|
+
* storage, such as a sandboxed frame or site data turned off, even reading `sessionStorage` throws;
|
|
43
|
+
* the strip then shows on each load and a dismissal lasts until the page reloads.
|
|
44
|
+
*/
|
|
45
|
+
const dismissedStorageKey = "ciridae-ui-environment-banner-dismissed";
|
|
46
|
+
function readDismissedForSession() {
|
|
47
|
+
try {
|
|
48
|
+
return sessionStorage.getItem(dismissedStorageKey) === "1";
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.warn(`Showing the environment strip: could not read ${dismissedStorageKey}`, error);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function rememberDismissedForSession() {
|
|
56
|
+
try {
|
|
57
|
+
sessionStorage.setItem(dismissedStorageKey, "1");
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.warn(`Could not save ${dismissedStorageKey}; the strip stays hidden until the page reloads`, error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** An environment strip, which the reader can dismiss for the session, and an inset AppShell viewport. */
|
|
39
64
|
export function EnvironmentFrame({ label, tone = "info", children, className, ...props }) {
|
|
40
65
|
const { labels } = useCiridaeLocale();
|
|
41
|
-
|
|
66
|
+
const [dismissed, setDismissed] = useState(readDismissedForSession);
|
|
67
|
+
const shown = label !== undefined && !dismissed;
|
|
68
|
+
const dismiss = () => {
|
|
69
|
+
rememberDismissedForSession();
|
|
70
|
+
setDismissed(true);
|
|
71
|
+
};
|
|
72
|
+
return (_jsxs("div", { ...props, className: cx(frame, className), children: [shown && (_jsx(SiteBanner, { className: environmentBanner, tone: tone, onDismiss: dismiss, children: typeof label === "string" ? labels.shell.environment(label) : label })), _jsx("div", { className: viewport, "data-under-banner": shown || undefined, children: children })] }));
|
|
42
73
|
}
|