@ciridae/ui 1.0.4 → 1.0.9

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 (147) hide show
  1. package/README.md +3 -2
  2. package/dist/chat/conversation-source.d.ts +1 -3
  3. package/dist/chat/message-text.js +6 -6
  4. package/dist/chat/use-chat-conversation.d.ts +7 -2
  5. package/dist/chat/use-chat-conversation.js +2 -1
  6. package/dist/components/activity-feed.d.ts +0 -1
  7. package/dist/components/activity-feed.js +6 -7
  8. package/dist/components/agent-work.d.ts +5 -1
  9. package/dist/components/agent-work.js +27 -13
  10. package/dist/components/app-frame.d.ts +3 -17
  11. package/dist/components/app-frame.js +11 -30
  12. package/dist/components/app-navigation.d.ts +5 -6
  13. package/dist/components/app-navigation.js +70 -40
  14. package/dist/components/badge.d.ts +0 -4
  15. package/dist/components/badge.js +17 -27
  16. package/dist/components/bulk-action-bar.js +2 -2
  17. package/dist/components/button.js +41 -50
  18. package/dist/components/calendar.js +5 -8
  19. package/dist/components/centered-page.js +4 -4
  20. package/dist/components/chart.js +3 -4
  21. package/dist/components/chat-client-source.js +55 -34
  22. package/dist/components/chat-page.js +1 -1
  23. package/dist/components/chat-sheet.d.ts +30 -20
  24. package/dist/components/chat-sheet.js +112 -54
  25. package/dist/components/chat.d.ts +27 -56
  26. package/dist/components/chat.js +153 -132
  27. package/dist/components/checkbox-item.js +4 -4
  28. package/dist/components/checkbox-list.js +6 -6
  29. package/dist/components/chip.js +2 -2
  30. package/dist/components/collapsible.js +3 -3
  31. package/dist/components/color-theme.d.ts +3 -6
  32. package/dist/components/color-theme.js +10 -11
  33. package/dist/components/column-settings.js +3 -3
  34. package/dist/components/command-menu.d.ts +1 -2
  35. package/dist/components/command-menu.js +56 -77
  36. package/dist/components/command-palette.js +5 -2
  37. package/dist/components/copyable-id.js +5 -5
  38. package/dist/components/data-display.d.ts +1 -7
  39. package/dist/components/data-display.js +32 -55
  40. package/dist/components/data-grid.d.ts +4 -7
  41. package/dist/components/data-grid.js +106 -90
  42. package/dist/components/date-range-picker.js +4 -5
  43. package/dist/components/document-viewer.js +23 -23
  44. package/dist/components/drawer.d.ts +2 -1
  45. package/dist/components/drawer.js +45 -20
  46. package/dist/components/dropzone.js +13 -13
  47. package/dist/components/editable-field.d.ts +2 -7
  48. package/dist/components/editable-field.js +14 -27
  49. package/dist/components/email-input.js +2 -3
  50. package/dist/components/environment-frame.js +7 -7
  51. package/dist/components/expandable-text.js +2 -2
  52. package/dist/components/feedback-screenshot.js +7 -7
  53. package/dist/components/feedback-sheet.js +8 -8
  54. package/dist/components/filter-bar.js +41 -27
  55. package/dist/components/form-controls.d.ts +3 -6
  56. package/dist/components/form-controls.js +15 -22
  57. package/dist/components/form-layout.d.ts +6 -6
  58. package/dist/components/form-layout.js +10 -16
  59. package/dist/components/glass-menu.d.ts +2 -0
  60. package/dist/components/glass-menu.js +14 -18
  61. package/dist/components/gradient-edge.d.ts +0 -1
  62. package/dist/components/gradient-edge.js +8 -9
  63. package/dist/components/icon.js +1 -1
  64. package/dist/components/input.js +18 -17
  65. package/dist/components/keycap.d.ts +2 -6
  66. package/dist/components/keycap.js +4 -12
  67. package/dist/components/layout.js +11 -10
  68. package/dist/components/lightbox.js +8 -8
  69. package/dist/components/link.js +1 -1
  70. package/dist/components/locale.d.ts +23 -7
  71. package/dist/components/locale.js +19 -7
  72. package/dist/components/log-entry.d.ts +2 -14
  73. package/dist/components/log-entry.js +11 -22
  74. package/dist/components/log-list.js +1 -1
  75. package/dist/components/mention-input.js +13 -14
  76. package/dist/components/menu.d.ts +7 -4
  77. package/dist/components/menu.js +23 -10
  78. package/dist/components/meter.d.ts +2 -3
  79. package/dist/components/meter.js +12 -16
  80. package/dist/components/navigation.d.ts +0 -7
  81. package/dist/components/navigation.js +3 -12
  82. package/dist/components/notification-tray.js +7 -7
  83. package/dist/components/overlays.d.ts +5 -2
  84. package/dist/components/overlays.js +24 -24
  85. package/dist/components/panel.js +3 -3
  86. package/dist/components/progress.js +2 -2
  87. package/dist/components/radio-card.d.ts +1 -1
  88. package/dist/components/radio-card.js +9 -9
  89. package/dist/components/record.d.ts +2 -4
  90. package/dist/components/record.js +19 -32
  91. package/dist/components/scroll-area.js +10 -10
  92. package/dist/components/segmented-bar.js +9 -9
  93. package/dist/components/select.d.ts +3 -5
  94. package/dist/components/select.js +26 -44
  95. package/dist/components/settings.js +1 -1
  96. package/dist/components/shell-skeleton.js +1 -1
  97. package/dist/components/shell.d.ts +1 -1
  98. package/dist/components/shell.js +69 -24
  99. package/dist/components/side-panel.js +3 -3
  100. package/dist/components/sidebar.d.ts +0 -5
  101. package/dist/components/sidebar.js +25 -33
  102. package/dist/components/single-line-text.js +3 -4
  103. package/dist/components/site-banner.js +4 -4
  104. package/dist/components/skeleton.js +19 -22
  105. package/dist/components/skeletons.d.ts +0 -3
  106. package/dist/components/skeletons.js +5 -10
  107. package/dist/components/stages.d.ts +6 -3
  108. package/dist/components/stages.js +77 -105
  109. package/dist/components/stat-card.d.ts +2 -5
  110. package/dist/components/stat-card.js +23 -31
  111. package/dist/components/status-banner.js +6 -6
  112. package/dist/components/status.js +3 -3
  113. package/dist/components/steps.js +2 -2
  114. package/dist/components/switch.js +1 -1
  115. package/dist/components/tabs.js +15 -18
  116. package/dist/components/theme-customizer.d.ts +5 -8
  117. package/dist/components/theme-customizer.js +9 -41
  118. package/dist/components/theme-toggle.d.ts +0 -1
  119. package/dist/components/theme-toggle.js +6 -11
  120. package/dist/components/toast.d.ts +1 -7
  121. package/dist/components/toast.js +19 -22
  122. package/dist/components/user-chip.js +8 -8
  123. package/dist/components/wizard.js +6 -4
  124. package/dist/preset.d.ts +1 -2
  125. package/dist/preset.js +41 -89
  126. package/dist/styled-system/css/conditions.mjs +1 -1
  127. package/dist/styled-system/css/css.mjs +1 -1
  128. package/dist/styled-system/styles.css +820 -1297
  129. package/dist/styled-system/tokens/index.mjs +78 -1926
  130. package/dist/styled-system/tokens/tokens.d.ts +18 -33
  131. package/dist/styled-system/types/conditions.d.ts +0 -74
  132. package/dist/styled-system/types/prop-type.d.ts +15 -40
  133. package/dist/styled-system/types/style-props.d.ts +705 -705
  134. package/dist/theme/color.d.ts +0 -1
  135. package/dist/theme/color.js +0 -16
  136. package/dist/theme/derive.d.ts +3 -3
  137. package/dist/theme/derive.js +9 -32
  138. package/dist/theme/index.d.ts +1 -2
  139. package/dist/theme/index.js +3 -5
  140. package/dist/theme/options.d.ts +2 -4
  141. package/dist/theme/options.js +0 -1
  142. package/dist/theme/{palettes.d.ts → palette.d.ts} +12 -23
  143. package/dist/theme/palette.js +127 -0
  144. package/dist/theme/tokens.d.ts +8 -13
  145. package/dist/theme/tokens.js +39 -92
  146. package/package.json +1 -1
  147. package/dist/theme/palettes.js +0 -254
@@ -1,25 +1,24 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /*
3
3
  * The app's sidebar from data. `AppNavigation` draws its sections twice from the same list: as the
4
- * expanded sidebar, and as the icon rail the shell shows under 1000px or while `collapsed`. A
4
+ * expanded sidebar, and as the icon rail the shell shows under 1000px or while `collapsed`, where
5
+ * each icon shows its label in a tooltip beside it on hover and keyboard focus. A
5
6
  * section can fold past `maxVisible` behind a More row, and unfolds on its own while the item it
6
7
  * holds is open or, with `autoUnfold`, while the sidebar has room for every row. Pinned and recent
7
8
  * records are sections like any other, which the caller builds from its own state.
8
- * `WorkspaceSwitcher` heads the sidebar: the current workspace's mark and name, opening a menu of
9
- * the others.
10
9
  */
11
10
  import { CaretDownIcon, CaretLineRightIcon, CaretUpDownIcon, CaretUpIcon, } from "@phosphor-icons/react";
12
11
  import { Fragment, useLayoutEffect, useRef, useState, } from "react";
13
12
  import { css, cx } from "#primitives/css";
14
13
  import { IconButton } from "./button.js";
15
- import { icon } from "./icon.js";
14
+ import { directional, icon } from "./icon.js";
16
15
  import { useCiridaeLocale } from "./locale.js";
17
16
  import { Menu } from "./menu.js";
17
+ import { Tooltip } from "./overlays.js";
18
18
  import { AppSidebar, AppSidebarFooter, AppSidebarNav, AppSidebarRail } from "./shell.js";
19
19
  import { SidebarItem, SidebarPanel, SidebarRailRule, SidebarSection } from "./sidebar.js";
20
20
  import { singleLineTextClass } from "./single-line-text.js";
21
21
  const navCount = css({ color: "fg.subtle", fontSize: "code", fontVariantNumeric: "tabular-nums" });
22
- // The rail's items, a section at a time: the sections stand apart by the rail's gap.
23
22
  const railSection = css({
24
23
  display: "flex",
25
24
  flexDirection: "column",
@@ -35,17 +34,21 @@ const sectionFooter = css({
35
34
  const foldScroll = css({
36
35
  display: "grid",
37
36
  gap: "control.trackInset",
38
- maxHeight: "200px",
37
+ maxHeight: "[200px]",
39
38
  overflowY: "auto",
40
39
  });
41
- /** The tallest row, so an estimate of the folded rows' height never overshoots. */
42
- const SIDEBAR_ROW_HEIGHT = 32;
40
+ // The kit draws the collapsed sidebar's expand button as its 32px round button, a step under
41
+ // the rail's other round buttons; the child selector outranks the button's own size.
42
+ const expandButtonRail = css({
43
+ "& > button": { width: "control.roundRail", height: "control.roundRail" },
44
+ });
43
45
  /**
44
- * Whether the sidebar has room to list `foldedRows` more rows: the free space between its
45
- * navigation and its footer covers them. Once it has, it loses it only when the navigation runs
46
- * out of room, so it does not flicker at the threshold.
46
+ * Whether the sidebar has room to unfold every section: the free space between its navigation and
47
+ * its footer covers the `hiddenRows` its folds hide, less the More rows unfolding removes. Once it
48
+ * has, it loses it only when the navigation runs out of room, so it does not flicker at the
49
+ * threshold.
47
50
  */
48
- export function useSidebarRoom(foldedRows) {
51
+ export function useSidebarRoom(hiddenRows) {
49
52
  const navRef = useRef(null);
50
53
  const footerRef = useRef(null);
51
54
  const [roomy, setRoomy] = useState(false);
@@ -58,28 +61,56 @@ export function useSidebarRoom(foldedRows) {
58
61
  const free = footer.getBoundingClientRect().top - nav.getBoundingClientRect().bottom;
59
62
  setRoomy((current) => current
60
63
  ? free >= 0 && nav.scrollHeight <= nav.clientHeight + 1
61
- : free >= foldedRows * SIDEBAR_ROW_HEIGHT);
64
+ : free >= measureHeightUnfoldingAdds(nav, hiddenRows));
62
65
  };
63
66
  measure();
64
67
  const observer = new ResizeObserver(measure);
65
68
  observer.observe(nav);
66
69
  observer.observe(document.documentElement);
67
70
  return () => observer.disconnect();
68
- }, [foldedRows]);
71
+ }, [hiddenRows]);
69
72
  return { navRef, footerRef, roomy };
70
73
  }
74
+ /**
75
+ * How much taller unfolding every section makes the navigation, from the rows it renders: each
76
+ * hidden row as tall as the tallest row shown, with the gap a section leaves between rows, less
77
+ * the More rows that unfolding removes.
78
+ */
79
+ function measureHeightUnfoldingAdds(nav, hiddenRows) {
80
+ const moreRows = Array.from(nav.querySelectorAll("[data-navigation-more]"));
81
+ const moreRowSection = moreRows[0]?.parentElement;
82
+ // Without a More row, every section already shows all of its rows.
83
+ if (!moreRowSection)
84
+ return 0;
85
+ const rowGap = parseFloat(getComputedStyle(moreRowSection).rowGap);
86
+ const tallestRowHeight = Math.max(...Array.from(nav.querySelectorAll("[data-sidebar-row]"), (row) => row.getBoundingClientRect().height));
87
+ const moreRowsHeight = moreRows.reduce((total, row) => total + row.getBoundingClientRect().height + rowGap, 0);
88
+ return hiddenRows * (tallestRowHeight + rowGap) - moreRowsHeight;
89
+ }
71
90
  function NavRow({ item, activeIds, renderLink, }) {
72
91
  return (_jsx(SidebarItem, { size: item.size, icon: item.icon, active: activeIds.includes(item.id), trailing: item.count !== undefined && _jsx("span", { className: navCount, children: item.count }), href: item.href, render: item.render ?? renderLink?.(item), onClick: item.onSelect, action: item.action, children: item.label }));
73
92
  }
74
- function SectionItems({ section, activeIds, renderLink, roomy, labels, }) {
75
- const [expanded, setExpanded] = useState(false);
93
+ /**
94
+ * How many of a section's rows its fold hides: none when it has no fold, or when it is unfolded
95
+ * because it was expanded or holds the active item past `maxVisible`.
96
+ */
97
+ function countRowsHiddenByFold(section, activeIds, expanded) {
98
+ const { items, maxVisible } = section;
99
+ if (maxVisible === undefined || items.length <= maxVisible)
100
+ return 0;
101
+ const unfolded = expanded || items.slice(maxVisible).some((item) => activeIds.includes(item.id));
102
+ return unfolded ? 0 : items.length - maxVisible;
103
+ }
104
+ function SectionItems({ section, activeIds, renderLink, roomy, labels, expanded, onExpandedChange, }) {
76
105
  const { items, maxVisible } = section;
77
106
  const rows = (shown) => shown.map((item) => (_jsx(NavRow, { item: item, activeIds: activeIds, renderLink: renderLink }, item.id)));
78
107
  if (maxVisible === undefined || items.length <= maxVisible || roomy)
79
108
  return rows(items);
80
- const unfolded = expanded || items.slice(maxVisible).some((item) => activeIds.includes(item.id));
109
+ const unfolded = countRowsHiddenByFold(section, activeIds, expanded) === 0;
110
+ // The open page keeps its section unfolded, so a Less row could not fold it.
111
+ const holdsActiveItem = items.slice(maxVisible).some((item) => activeIds.includes(item.id));
81
112
  const Caret = unfolded ? CaretUpIcon : CaretDownIcon;
82
- return (_jsxs(_Fragment, { children: [!unfolded ? (rows(items.slice(0, maxVisible))) : section.scrollWhenUnfolded ? (_jsx("div", { className: foldScroll, children: rows(items) })) : (rows(items)), _jsx(SidebarItem, { size: "compact", icon: _jsx(Caret, { className: icon({ size: "small" }) }), "aria-expanded": unfolded, onClick: () => setExpanded(!unfolded), children: unfolded ? labels.less : labels.more })] }));
113
+ return (_jsxs(_Fragment, { children: [!unfolded ? (rows(items.slice(0, maxVisible))) : section.scrollWhenUnfolded ? (_jsx("div", { className: foldScroll, children: rows(items) })) : (rows(items)), !holdsActiveItem && (_jsx(SidebarItem, { size: "compact", icon: _jsx(Caret, { className: icon({ size: "small" }) }), "aria-expanded": unfolded, onClick: () => onExpandedChange(!unfolded), "data-navigation-more": "", "data-sidebar-action": "", children: unfolded ? labels.less : labels.more }))] }));
83
114
  }
84
115
  /** Runs of sections: each main section alone, consecutive panel sections together. */
85
116
  function sectionRuns(sections) {
@@ -100,23 +131,26 @@ export function AppNavigation({ sections, activeId: ownActiveId = null, renderLi
100
131
  const locale = useCiridaeLocale();
101
132
  const labels = { ...locale.labels.navigation, ...ownLabels };
102
133
  const activeIds = ownActiveId === null ? [] : typeof ownActiveId === "string" ? [ownActiveId] : ownActiveId;
103
- // Every row a fold hides, less the More row it trades them for.
104
- const foldedRows = sections.reduce((rows, section) => rows +
105
- (section.maxVisible === undefined
106
- ? 0
107
- : Math.max(0, section.items.length - section.maxVisible - 1)), 0);
108
- const room = useSidebarRoom(foldedRows);
134
+ // The navigation owns which sections were expanded, so the room estimate counts only the rows
135
+ // that are still folded away.
136
+ const [expandedSectionIds, setExpandedSectionIds] = useState(() => new Set());
137
+ const setSectionExpanded = (sectionId, expanded) => setExpandedSectionIds((current) => {
138
+ const next = new Set(current);
139
+ if (expanded)
140
+ next.add(sectionId);
141
+ else
142
+ next.delete(sectionId);
143
+ return next;
144
+ });
145
+ const hiddenRows = sections.reduce((rows, section) => rows + countRowsHiddenByFold(section, activeIds, expandedSectionIds.has(section.id)), 0);
146
+ const room = useSidebarRoom(hiddenRows);
109
147
  const roomy = autoUnfold && room.roomy;
110
- const sectionBlock = (section) => (_jsxs(SidebarSection, { label: section.label, children: [_jsx(SectionItems, { section: section, activeIds: activeIds, renderLink: renderLink, roomy: roomy, labels: labels }), section.footer && _jsx("div", { className: sectionFooter, children: section.footer })] }, section.id));
148
+ const sectionBlock = (section) => (_jsxs(SidebarSection, { label: section.label, children: [_jsx(SectionItems, { section: section, activeIds: activeIds, renderLink: renderLink, roomy: roomy, labels: labels, expanded: expandedSectionIds.has(section.id), onExpandedChange: (expanded) => setSectionExpanded(section.id, expanded) }), section.footer && _jsx("div", { className: sectionFooter, children: section.footer })] }, section.id));
111
149
  return (_jsxs(AppSidebar, { "aria-label": labels.navigation, collapsed: collapsed, ...props, children: [_jsxs(AppSidebarNav, { ref: room.navRef, children: [header, sectionRuns(sections).map((run) => run.panel ? (_jsx(SidebarPanel, { children: run.sections.map(sectionBlock) }, run.sections[0].id)) : (_jsx(Fragment, { children: run.sections.map(sectionBlock) }, run.sections[0].id)))] }), _jsxs(AppSidebarRail, { children: [railHeader, sections
112
150
  .filter((section) => section.rail !== false && section.items.length > 0)
113
- .map((section) => (_jsxs("div", { className: railSection, children: [section.label && _jsx(SidebarRailRule, {}), section.items.map((item) => (_jsx(SidebarItem, { "aria-label": item.label, icon: item.icon, active: activeIds.includes(item.id), href: item.href, render: item.render ?? renderLink?.(item), onClick: item.onSelect }, item.id)))] }, section.id)))] }), _jsxs(AppSidebarFooter, { ref: room.footerRef, children: [collapsed && onExpand && (_jsx(AppSidebarRail, { children: _jsx(IconButton, { "aria-label": locale.labels.common.expand, size: "medium", onClick: onExpand, children: _jsx(CaretLineRightIcon, { className: cx(icon({ size: "small" }), css({ _rtl: { transform: "scaleX(-1)" } })) }) }) })), footer] })] }));
151
+ .map((section) => (_jsxs("div", { className: railSection, children: [section.label && _jsx(SidebarRailRule, {}), section.items.map((item) => (_jsx(Tooltip, { content: item.label, side: "inline-end", children: _jsx(SidebarItem, { "aria-label": item.label, icon: item.icon, active: activeIds.includes(item.id), href: item.href, render: item.render ?? renderLink?.(item), onClick: item.onSelect }) }, item.id)))] }, section.id)))] }), _jsxs(AppSidebarFooter, { ref: room.footerRef, children: [collapsed && onExpand && (_jsx(AppSidebarRail, { className: expandButtonRail, children: _jsx(Tooltip, { content: locale.labels.common.expand, side: "inline-end", children: _jsx(IconButton, { "aria-label": locale.labels.common.expand, size: "medium", onClick: onExpand, children: _jsx(CaretLineRightIcon, { className: cx(icon({ size: "small" }), directional) }) }) }) })), footer] })] }));
114
152
  }
115
- /*
116
- * A workspace's square mark with its initial: the primary face for the current one, the raised
117
- * face with a quieter initial for the others. Its edge is a gradient, so it is drawn under the face
118
- * through a transparent border.
119
- */
153
+ /* A workspace's mark: its edge is a gradient, so it is drawn under the face through a transparent border. */
120
154
  const workspaceMark = css({
121
155
  display: "inline-grid",
122
156
  placeItems: "center",
@@ -125,29 +159,27 @@ const workspaceMark = css({
125
159
  height: "workspace.mark",
126
160
  border: "1px solid transparent",
127
161
  borderRadius: "keycap",
128
- background: "token(gradients.raised) padding-box, token(gradients.border.otherWorkspaceMark) border-box",
162
+ background: "[token(gradients.raised) padding-box, token(gradients.border.otherWorkspaceMark) border-box]",
129
163
  color: "fg.label",
130
164
  fontSize: "code",
131
165
  fontWeight: "medium",
132
166
  letterSpacing: "compact",
133
167
  lineHeight: "tight",
134
168
  "&[data-current]": {
135
- background: "token(gradients.primary) padding-box, token(gradients.border.primary) border-box",
169
+ background: "[token(gradients.primary) padding-box, token(gradients.border.primary) border-box]",
136
170
  color: "bg.canvas",
137
171
  _light: { color: "fg.onAccent" },
138
172
  },
139
173
  "&[data-size=small]": { width: "control.mark", height: "control.mark", borderRadius: "control" },
140
174
  });
141
- /** A workspace's mark: its name's first letter on a square tile. */
142
175
  export function WorkspaceMark({ name, current = false, size = "regular", className, }) {
143
176
  return (_jsx("span", { "aria-hidden": "true", className: cx(workspaceMark, className), "data-ciridae-ui": "", "data-current": current || undefined, "data-size": size, children: name.trim().charAt(0).toLocaleUpperCase() }));
144
177
  }
145
- // The sidebar's first row: hovered it takes the nav row's wash, open a grey well in the canvas's edge.
146
178
  const workspaceSwitcher = css({
147
179
  display: "flex",
148
180
  alignItems: "center",
149
181
  gap: "layout.controlGap",
150
- width: "100%",
182
+ width: "full",
151
183
  height: "control.default",
152
184
  padding: "control.compactBlock",
153
185
  border: "0",
@@ -163,12 +195,11 @@ const workspaceSwitcher = css({
163
195
  cursor: "pointer",
164
196
  transitionProperty: "background",
165
197
  transitionDuration: "fast",
166
- _hover: { background: "token(gradients.selected)", _light: { background: "bg.rowHover" } },
167
- // Open, it keeps its open face under the pointer too.
198
+ _hover: { background: "[token(gradients.selected)]", _light: { background: "bg.rowHover" } },
168
199
  "&[data-popup-open], &[data-popup-open]:hover": {
169
200
  background: "bg.openTrigger",
170
201
  _light: { background: "bg.openTrigger" },
171
- boxShadow: "inset 0 0 0 1px token(colors.border.canvas)",
202
+ boxShadow: "[inset 0 0 0 1px token(colors.border.canvas)]",
172
203
  },
173
204
  _focusVisible: { focusRing: "outer" },
174
205
  "& > [data-name]": { flex: "1" },
@@ -180,7 +211,6 @@ const workspaceSwitcher = css({
180
211
  padding: "none",
181
212
  },
182
213
  });
183
- /** The current workspace, opening a menu of every workspace the reader belongs to, current checked. */
184
214
  export function WorkspaceSwitcher({ workspaces, currentId, onSelect, compact = false, }) {
185
215
  const current = workspaces.find((workspace) => workspace.id === currentId);
186
216
  const name = current?.name ?? "";
@@ -21,19 +21,15 @@ export type BadgeGlyphKind = "notReached" | "hereNow" | "terminal" | "contested"
21
21
  export type BadgeGlyphProps = Omit<ComponentProps<"svg">, "children"> & {
22
22
  kind: BadgeGlyphKind;
23
23
  };
24
- /** A badge's drawn glyph: a lifecycle position, a contested verdict, an unchanged outcome. */
25
24
  export declare function BadgeGlyph({ kind, className, ...props }: BadgeGlyphProps): import("react").JSX.Element;
26
25
  /** How strongly a value has been checked: none, by a model, by a person, against the world. */
27
26
  export type AssuranceLevel = 0 | 1 | 2 | 3;
28
27
  export type AssuranceGlyphProps = Omit<ComponentProps<"svg">, "children"> & {
29
28
  level: AssuranceLevel;
30
29
  };
31
- /** A three-bar meter, lit to the level, so a value's assurance reads without color. */
32
30
  export declare function AssuranceGlyph({ level, className, ...props }: AssuranceGlyphProps): import("react").JSX.Element;
33
- /** Where a record sits in its stages. The glyph is fixed per position; the words name the stage. */
34
31
  export type LifecycleState = "notReached" | "hereNow" | "done" | "blocked" | "agentWaiting" | "expired" | "terminal" | "awaiting";
35
32
  export type LifecycleBadgeProps = Omit<BadgeProps, "tone" | "shape" | "appearance" | "icon"> & {
36
33
  state: LifecycleState;
37
34
  };
38
- /** A lifecycle pill: the state picks the glyph and the hue; the words are the stage's name. */
39
35
  export declare function LifecycleBadge({ state, ...props }: LifecycleBadgeProps): import("react").JSX.Element;
@@ -11,11 +11,7 @@ import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-run
11
11
  import { useRender } from "@base-ui/react/use-render";
12
12
  import { CheckIcon, ClockIcon, HourglassIcon, SparkleIcon, StopIcon } from "@phosphor-icons/react";
13
13
  import { css, cva, cx } from "#primitives/css";
14
- /*
15
- * A tinted pill is its hue's glass, falling from light to its hue, inside that hue's gradient
16
- * hairline, which the `[data-edge="gradient"]` ring draws from `--edge`. Neutral is the raised
17
- * glass, warning the tinted button's warm glass, and ink the primary face.
18
- */
14
+ /* The `[data-edge="gradient"]` ring draws the hairline from `--edge`. */
19
15
  const badge = cva({
20
16
  base: {
21
17
  display: "inline-flex",
@@ -47,11 +43,6 @@ const badge = cva({
47
43
  _light: { background: "bg.card" },
48
44
  },
49
45
  },
50
- /*
51
- * `filled` is the tone's glass; `outline` the card's face inside the tone's edge, as an
52
- * outcome that changed nothing; `bare` a fact told in passing: the icon and the words, muted,
53
- * with no box, the tone on the icon alone.
54
- */
55
46
  appearance: {
56
47
  filled: {},
57
48
  outline: {},
@@ -60,7 +51,7 @@ const badge = cva({
60
51
  gap: "layout.tightGap",
61
52
  paddingInline: "none",
62
53
  border: "0",
63
- borderRadius: "0",
54
+ borderRadius: "none",
64
55
  background: "transparent",
65
56
  flexShrink: "0",
66
57
  _light: { background: "transparent" },
@@ -81,7 +72,7 @@ const badge = cva({
81
72
  shape: "pill",
82
73
  tone: "neutral",
83
74
  css: {
84
- background: "token(gradients.raised)",
75
+ background: "[token(gradients.raised)]",
85
76
  color: "fg.label",
86
77
  "--edge": "token(gradients.border.raised)",
87
78
  },
@@ -91,7 +82,7 @@ const badge = cva({
91
82
  shape: "pill",
92
83
  tone: "info",
93
84
  css: {
94
- background: "token(gradients.infoGlass)",
85
+ background: "[token(gradients.infoGlass)]",
95
86
  "--edge": "token(gradients.border.infoGlass)",
96
87
  },
97
88
  },
@@ -100,7 +91,7 @@ const badge = cva({
100
91
  shape: "pill",
101
92
  tone: "success",
102
93
  css: {
103
- background: "token(gradients.successGlass)",
94
+ background: "[token(gradients.successGlass)]",
104
95
  "--edge": "token(gradients.border.successGlass)",
105
96
  },
106
97
  },
@@ -109,7 +100,7 @@ const badge = cva({
109
100
  shape: "pill",
110
101
  tone: "warning",
111
102
  css: {
112
- background: "token(gradients.tinted)",
103
+ background: "[token(gradients.tinted)]",
113
104
  "--edge": "token(gradients.border.raised)",
114
105
  _light: { "--edge": "token(gradients.border.tinted)" },
115
106
  },
@@ -119,7 +110,7 @@ const badge = cva({
119
110
  shape: "pill",
120
111
  tone: "danger",
121
112
  css: {
122
- background: "token(gradients.dangerGlass)",
113
+ background: "[token(gradients.dangerGlass)]",
123
114
  "--edge": "token(gradients.border.dangerGlass)",
124
115
  },
125
116
  },
@@ -128,7 +119,7 @@ const badge = cva({
128
119
  shape: "pill",
129
120
  tone: "ink",
130
121
  css: {
131
- background: "token(gradients.primary)",
122
+ background: "[token(gradients.primary)]",
132
123
  color: "bg.canvas",
133
124
  "--edge": "token(gradients.border.primary)",
134
125
  _light: { color: "fg.onAccent" },
@@ -138,7 +129,7 @@ const badge = cva({
138
129
  appearance: "outline",
139
130
  shape: "pill",
140
131
  css: {
141
- background: "token(gradients.raised)",
132
+ background: "[token(gradients.raised)]",
142
133
  color: "fg.primary",
143
134
  "--edge": "token(gradients.border.raised)",
144
135
  _light: { background: "bg.card", "--edge": "token(gradients.border.primary)" },
@@ -180,13 +171,14 @@ export function Badge({ tone = "neutral", shape = "pill", appearance = "filled",
180
171
  },
181
172
  });
182
173
  }
183
- /*
184
- * The glyphs the kit draws itself, in a 14px box (12px for the progress step's check and stop),
185
- * in the color of the badge's or field's label.
186
- */
187
- const glyph = css({ flexShrink: "0", width: "1em", height: "1em", fontSize: "icon.small" });
188
- const smallGlyph = css({ flexShrink: "0", width: "1em", height: "1em", fontSize: "icon.compact" });
189
- /** A badge's drawn glyph: a lifecycle position, a contested verdict, an unchanged outcome. */
174
+ /* The glyphs are drawn in a 14px box (12px for the progress step's check and stop). */
175
+ const glyph = css({ flexShrink: "0", width: "[1em]", height: "[1em]", fontSize: "icon.small" });
176
+ const smallGlyph = css({
177
+ flexShrink: "0",
178
+ width: "[1em]",
179
+ height: "[1em]",
180
+ fontSize: "icon.compact",
181
+ });
190
182
  export function BadgeGlyph({ kind, className, ...props }) {
191
183
  const small = kind === "check" || kind === "stop";
192
184
  return (_jsxs("svg", { "aria-hidden": "true", viewBox: small ? "0 0 12 12" : "0 0 14 14", fill: "none", ...props, className: cx(small ? smallGlyph : glyph, className), children: [kind === "notReached" && (_jsx("circle", { cx: "7", cy: "7", r: "3.875", stroke: "currentColor", strokeWidth: "1.25", strokeDasharray: "2 1.6" })), kind === "hereNow" && _jsx("circle", { cx: "7", cy: "7", r: "3", fill: "currentColor" }), kind === "terminal" && (_jsx("rect", { x: "3.5", y: "3.5", width: "7", height: "7", rx: "1.5", fill: "currentColor" })), kind === "contested" && (_jsx("circle", { cx: "7", cy: "7", r: "3.875", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" })), kind === "noChange" && (_jsx("circle", { cx: "7", cy: "7", r: "3.375", stroke: "currentColor", strokeWidth: "1.25" })), kind === "check" && (_jsx("path", { d: "M2.4 6.2 4.6 8.5 9.7 3.4", stroke: "currentColor", strokeWidth: "0.857", strokeLinecap: "round", strokeLinejoin: "round" })), kind === "stop" && (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "6", cy: "6", r: "3.95", stroke: "currentColor", strokeWidth: "0.857" }), _jsx("path", { d: "M4.1 6h3.8", stroke: "currentColor", strokeWidth: "0.857", strokeLinecap: "round" })] }))] }));
@@ -196,7 +188,6 @@ const ASSURANCE_BARS = [
196
188
  { x: 5.8, y: 4.5, height: 8 },
197
189
  { x: 9.5, y: 1.5, height: 11 },
198
190
  ];
199
- /** A three-bar meter, lit to the level, so a value's assurance reads without color. */
200
191
  export function AssuranceGlyph({ level, className, ...props }) {
201
192
  return (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 14 14", fill: "currentColor", ...props, className: cx(glyph, className), children: ASSURANCE_BARS.map((bar, index) => (_jsx("rect", { x: bar.x, y: bar.y, width: "2.5", height: bar.height, rx: "0.75", opacity: index < level ? 1 : 0.28 }, bar.x))) }));
202
193
  }
@@ -220,7 +211,6 @@ const LIFECYCLE_GLYPHS = {
220
211
  terminal: _jsx(BadgeGlyph, { kind: "terminal" }),
221
212
  awaiting: _jsx(HourglassIcon, { weight: "bold" }),
222
213
  };
223
- /** A lifecycle pill: the state picks the glyph and the hue; the words are the stage's name. */
224
214
  export function LifecycleBadge({ state, ...props }) {
225
215
  return _jsx(Badge, { ...props, tone: LIFECYCLE_TONES[state], icon: LIFECYCLE_GLYPHS[state] });
226
216
  }
@@ -10,13 +10,13 @@ const bar = css({
10
10
  justifyContent: "space-between",
11
11
  flexWrap: "wrap",
12
12
  gap: "layout.contentGap",
13
- minHeight: "56px",
13
+ minHeight: "[56px]",
14
14
  paddingBlock: "panel.compact",
15
15
  paddingInlineStart: "panel.comfortable",
16
16
  paddingInlineEnd: "panel.compact",
17
17
  border: "1px solid token(colors.border.divider)",
18
18
  borderRadius: "action",
19
- background: "token(gradients.card)",
19
+ background: "[token(gradients.card)]",
20
20
  fontSize: "body",
21
21
  fontWeight: "medium",
22
22
  letterSpacing: "compact",
@@ -8,9 +8,8 @@ import { formatForDisplay } from "./hotkeys.js";
8
8
  import { icon as iconStyle, Spinner } from "./icon.js";
9
9
  import { ShortcutHint } from "./keycap.js";
10
10
  import { useCiridaeLocale } from "./locale.js";
11
- /* The hover wash laid over a gradient face, as the design's state layer sits over it. */
12
- const raisedHoverLight = "linear-gradient(token(colors.bg.hover), token(colors.bg.hover)), token(gradients.raised)";
13
- const tintedHover = "linear-gradient(token(colors.bg.hover), token(colors.bg.hover)), token(gradients.tinted)";
11
+ const raisedHoverLight = "[linear-gradient(token(colors.bg.hover), token(colors.bg.hover)), token(gradients.raised)]";
12
+ const tintedHover = "[linear-gradient(token(colors.bg.hover), token(colors.bg.hover)), token(gradients.tinted)]";
14
13
  const action = cva({
15
14
  base: {
16
15
  display: "inline-flex",
@@ -20,7 +19,6 @@ const action = cva({
20
19
  gap: "control.iconGap",
21
20
  // The design lays a button's edge over its padding rather than inside it, so no border.
22
21
  border: "0",
23
- // Buttons are pills at both heights.
24
22
  borderRadius: "pill",
25
23
  height: "control.default",
26
24
  paddingInline: "control.inline",
@@ -30,44 +28,41 @@ const action = cva({
30
28
  letterSpacing: "compact",
31
29
  lineHeight: "tight",
32
30
  cursor: "pointer",
33
- transitionProperty: "translate, background, color",
31
+ transitionProperty: "[translate, background, color]",
34
32
  transitionDuration: "fast",
35
33
  _focusVisible: { focusRing: "outer" },
36
34
  "&:disabled, &[data-disabled]": {
37
35
  opacity: "disabled",
38
36
  cursor: "not-allowed",
39
37
  // A control that cannot be pressed should not look like it is waiting to be.
40
- translate: "none",
38
+ translate: "[none]",
41
39
  },
42
40
  },
43
41
  variants: {
44
42
  variant: {
45
43
  /*
46
44
  * Buttons cast no shadow: each face is told apart by its gradient fill and its gradient
47
- * edge alone. Light has no hover on the primary face; dark lets a little of the surface
48
- * through. A raised face takes the hover wash over its gradient in light, and thins in dark.
45
+ * edge alone. The primary face's hover lets a little of the surface through in both
46
+ * themes.
49
47
  */
50
48
  primary: {
51
- background: "token(gradients.primary)",
49
+ background: "[token(gradients.primary)]",
52
50
  color: "bg.canvas",
53
51
  _light: { color: "fg.onAccent" },
54
- _hover: {
55
- background: "bg.primaryHover",
56
- _light: { background: "token(gradients.primary)" },
57
- },
58
- _active: { translate: "0 1px" },
52
+ _hover: { background: "[token(gradients.primaryHover)]" },
53
+ _active: { translate: "[0 1px]" },
59
54
  },
60
55
  secondary: {
61
- background: "token(gradients.raised)",
56
+ background: "[token(gradients.raised)]",
62
57
  color: "fg.primary",
63
58
  _hover: { background: "bg.raisedHover", _light: { background: raisedHoverLight } },
64
- _active: { translate: "0 1px" },
59
+ _active: { translate: "[0 1px]" },
65
60
  },
66
61
  subtle: {
67
62
  background: "bg.fill",
68
63
  color: "fg.muted",
69
64
  _hover: { color: "fg.primary" },
70
- _active: { translate: "0 1px" },
65
+ _active: { translate: "[0 1px]" },
71
66
  },
72
67
  // White in dark, the secondary grey in light: the one face whose text follows the theme
73
68
  // differently.
@@ -79,41 +74,41 @@ const action = cva({
79
74
  },
80
75
  // Warm glass with an amber edge and the warm accent for its text.
81
76
  tinted: {
82
- background: "token(gradients.tinted)",
77
+ background: "[token(gradients.tinted)]",
83
78
  color: "accent.yellow",
84
79
  _light: { color: "accent.bronze" },
85
80
  _hover: { background: tintedHover },
86
- _active: { translate: "0 1px" },
81
+ _active: { translate: "[0 1px]" },
87
82
  },
88
83
  // The secondary face with red text, for a destructive action that asks for confirmation:
89
84
  // it names the risk without being the loudest thing on the page. The confirmation's own
90
85
  // button is `danger`.
91
86
  secondaryDanger: {
92
- background: "token(gradients.raised)",
87
+ background: "[token(gradients.raised)]",
93
88
  color: "accent.red",
94
89
  _hover: { background: "bg.raisedHover", _light: { background: raisedHoverLight } },
95
- _active: { translate: "0 1px" },
90
+ _active: { translate: "[0 1px]" },
96
91
  },
97
92
  danger: {
98
- background: "token(gradients.danger)",
93
+ background: "[token(gradients.danger)]",
99
94
  color: "fg.onAccent",
100
- _hover: { filter: "brightness(1.05)" },
101
- _active: { translate: "0 1px" },
95
+ _hover: { filter: "[brightness(1.05)]" },
96
+ _active: { translate: "[0 1px]" },
102
97
  },
103
98
  success: {
104
99
  background: "accent.green",
105
100
  color: "bg.canvas",
106
- _hover: { filter: "brightness(1.05)" },
107
- _active: { translate: "0 1px" },
101
+ _hover: { filter: "[brightness(1.05)]" },
102
+ _active: { translate: "[0 1px]" },
108
103
  },
109
104
  // Words only, in the text around it: no face, no height, underlined on hover.
110
105
  link: {
111
106
  background: "transparent",
112
107
  color: "fg.primary",
113
- fontFamily: "inherit",
114
- fontWeight: "inherit",
115
- letterSpacing: "inherit",
116
- lineHeight: "inherit",
108
+ fontFamily: "[inherit]",
109
+ fontWeight: "[inherit]",
110
+ letterSpacing: "[inherit]",
111
+ lineHeight: "[inherit]",
117
112
  _hover: { textDecoration: "underline" },
118
113
  },
119
114
  },
@@ -126,19 +121,18 @@ const action = cva({
126
121
  medium: {},
127
122
  large: { height: "control.field", paddingInline: "control.largeInline", fontSize: "heading" },
128
123
  },
129
- fullWidth: { true: { width: "100%" } },
124
+ fullWidth: { true: { width: "full" } },
130
125
  },
131
126
  compoundVariants: [
132
127
  {
133
128
  variant: "link",
134
129
  size: ["small", "medium", "large"],
135
- css: { height: "auto", paddingInline: "none", fontSize: "inherit", flexShrink: "0" },
130
+ css: { height: "auto", paddingInline: "none", fontSize: "[inherit]", flexShrink: "0" },
136
131
  },
137
132
  ],
138
133
  // Small is the everyday button; medium and large are for a page's one main action.
139
134
  defaultVariants: { variant: "primary", size: "small" },
140
135
  });
141
- // Each gradient face draws its own gradient edge; the flat faces draw a hairline or none.
142
136
  const buttonEdges = {
143
137
  primary: "primary",
144
138
  secondary: "raised",
@@ -151,7 +145,7 @@ const buttonEdges = {
151
145
  link: undefined,
152
146
  };
153
147
  // In the button's own color, quieter than its label.
154
- const buttonHint = css({ color: "inherit", opacity: "0.6" });
148
+ const buttonHint = css({ color: "[inherit]", opacity: "[0.6]" });
155
149
  /**
156
150
  * The size a `Button` takes where it does not set its own, so the container decides: a dialog's
157
151
  * footer holds the default 38px buttons, where everywhere else the small one is the everyday.
@@ -199,42 +193,40 @@ const round = cva({
199
193
  border: "1px solid transparent",
200
194
  borderRadius: "pill",
201
195
  color: "fg.muted",
202
- background: "token(gradients.raised)",
196
+ background: "[token(gradients.raised)]",
203
197
  cursor: "pointer",
204
198
  flexShrink: "0",
205
- transitionProperty: "translate, background, color",
199
+ transitionProperty: "[translate, background, color]",
206
200
  transitionDuration: "fast",
207
- // Hover keeps the face in light and thins it in dark; the icon takes the primary ink.
208
- _hover: { color: "fg.primary", background: "token(gradients.raisedHover)" },
209
- _active: { translate: "0 1px" },
201
+ _hover: { color: "fg.primary", background: "[token(gradients.raisedHover)]" },
202
+ _active: { translate: "[0 1px]" },
210
203
  _focusVisible: { focusRing: "outer" },
211
204
  // A round face fades further than other controls when it cannot be pressed.
212
205
  "&:disabled, &[data-disabled]": {
213
206
  opacity: "disabledRound",
214
207
  cursor: "not-allowed",
215
- translate: "none",
208
+ translate: "[none]",
216
209
  },
217
210
  },
218
211
  variants: {
219
212
  variant: {
220
213
  raised: {},
221
- // The bare icon button: no face until hovered, and disabled it fades as other controls do.
222
214
  ghost: {
223
215
  border: "0",
224
216
  background: "transparent",
225
- _hover: { background: "bg.hover", _light: { background: "bg.hover" } },
226
- _active: { translate: "none" },
217
+ _hover: { background: "bg.hover" },
218
+ _active: { translate: "[none]" },
227
219
  "&:disabled, &[data-disabled]": { opacity: "disabled" },
228
220
  },
229
221
  // The primary face as a round button: a composer's send.
230
222
  primary: {
231
- background: "token(gradients.primary)",
223
+ background: "[token(gradients.primary)]",
232
224
  color: "bg.canvas",
233
225
  _light: { color: "fg.onAccent" },
234
226
  _hover: {
235
227
  color: "bg.canvas",
236
- background: "bg.primaryHover",
237
- _light: { color: "fg.onAccent", background: "token(gradients.primary)" },
228
+ background: "[token(gradients.primaryHover)]",
229
+ _light: { color: "fg.onAccent" },
238
230
  },
239
231
  },
240
232
  },
@@ -247,7 +239,6 @@ const round = cva({
247
239
  medium: { width: "control.round", height: "control.round" },
248
240
  large: { width: "control.default", height: "control.default" },
249
241
  },
250
- // A page's leading icon button is a rounded square rather than a circle.
251
242
  shape: { round: {}, square: { borderRadius: "action" } },
252
243
  },
253
244
  compoundVariants: [
@@ -271,8 +262,8 @@ const badgeDot = css({
271
262
  /* A count sits on the button's corner, where the dot alone would not say how many. */
272
263
  const badgeCount = css({
273
264
  position: "absolute",
274
- top: "calc(token(spacing.layout.tightGap) * -1)",
275
- insetInlineEnd: "calc(token(spacing.layout.tightGap) * -1)",
265
+ top: "[calc(token(spacing.layout.tightGap) * -1)]",
266
+ insetInlineEnd: "[calc(token(spacing.layout.tightGap) * -1)]",
276
267
  display: "inline-flex",
277
268
  alignItems: "center",
278
269
  justifyContent: "center",
@@ -286,7 +277,7 @@ const badgeCount = css({
286
277
  color: "bg.canvas",
287
278
  fontSize: "avatar.small",
288
279
  fontWeight: "medium",
289
- lineHeight: "1",
280
+ lineHeight: "tight",
290
281
  fontVariantNumeric: "tabular-nums",
291
282
  });
292
283
  /**