@cueplusplus/ui 0.20.1 → 0.22.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 (236) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/dist/chat/agent-pile.js +2 -2
  3. package/dist/chrome/_status-bar.js +1 -1
  4. package/dist/chrome/app-shell.js +1 -1
  5. package/dist/chrome/navigation-menu.js +3 -3
  6. package/dist/chrome/row.js +1 -1
  7. package/dist/chrome/scroll-area.js +1 -1
  8. package/dist/chrome/section-header.js +1 -1
  9. package/dist/chrome/segmented-control.js +1 -1
  10. package/dist/chrome/tabs.js +1 -1
  11. package/dist/chrome/toolbar.js +1 -1
  12. package/dist/chrome/tree.js +3 -3
  13. package/dist/date/calendar.js +3 -3
  14. package/dist/dmx/channel-matrix.js +1 -1
  15. package/dist/dmx/universe-grid.js +1 -1
  16. package/dist/elements/agent-handoff.js +3 -3
  17. package/dist/elements/agent-plan.js +1 -1
  18. package/dist/elements/agent-status.js +1 -1
  19. package/dist/elements/agent-surface.d.ts +9 -6
  20. package/dist/elements/agent-surface.js +3 -2
  21. package/dist/elements/approval-card.d.ts +29 -3
  22. package/dist/elements/approval-card.js +18 -8
  23. package/dist/elements/artifact-card.js +2 -2
  24. package/dist/elements/canvas-split.js +3 -3
  25. package/dist/elements/chart.js +1 -1
  26. package/dist/elements/chat-panel.js +2 -2
  27. package/dist/elements/checkpoint-history.js +1 -1
  28. package/dist/elements/code-diff.js +1 -1
  29. package/dist/elements/code-runner.js +1 -1
  30. package/dist/elements/composer.d.ts +8 -2
  31. package/dist/elements/composer.js +20 -12
  32. package/dist/elements/computer-use.js +2 -2
  33. package/dist/elements/confidence-marker.js +1 -1
  34. package/dist/elements/connection-state.js +2 -2
  35. package/dist/elements/context-breakdown.js +1 -1
  36. package/dist/elements/context-usage.js +1 -1
  37. package/dist/elements/conversation-search.js +2 -2
  38. package/dist/elements/cost-meter.js +1 -1
  39. package/dist/elements/data-table.js +1 -1
  40. package/dist/elements/diagram.js +1 -1
  41. package/dist/elements/draft-restore.js +2 -2
  42. package/dist/elements/edit-message.js +1 -1
  43. package/dist/elements/elements.css +134 -0
  44. package/dist/elements/elicitation-form.d.ts +8 -1
  45. package/dist/elements/elicitation-form.js +24 -16
  46. package/dist/elements/empty-state.js +3 -3
  47. package/dist/elements/error-state.js +2 -2
  48. package/dist/elements/event-feed.d.ts +88 -0
  49. package/dist/elements/event-feed.js +190 -0
  50. package/dist/elements/feedback-dialog.js +2 -2
  51. package/dist/elements/file-tree.js +1 -1
  52. package/dist/elements/flow-graph.js +2 -2
  53. package/dist/elements/generative.css +13 -0
  54. package/dist/elements/generative.d.ts +8 -6
  55. package/dist/elements/generative.js +4 -3
  56. package/dist/elements/image-generation.js +2 -2
  57. package/dist/elements/index.d.ts +7 -3
  58. package/dist/elements/index.js +5 -2
  59. package/dist/elements/inline-citation.js +1 -1
  60. package/dist/elements/job-progress.js +1 -1
  61. package/dist/elements/launcher-bubble.js +3 -3
  62. package/dist/elements/loading-state.js +1 -1
  63. package/dist/elements/map-answer.js +1 -1
  64. package/dist/elements/math-block.js +1 -1
  65. package/dist/elements/mcp-server-panel.js +3 -3
  66. package/dist/elements/memory-chips.js +1 -1
  67. package/dist/elements/message-attachment.js +3 -3
  68. package/dist/elements/message-branches.js +1 -1
  69. package/dist/elements/message-pair.js +2 -2
  70. package/dist/elements/message-queue.js +1 -1
  71. package/dist/elements/message-timing.js +1 -1
  72. package/dist/elements/mobile-composer.js +1 -1
  73. package/dist/elements/model-picker.js +1 -1
  74. package/dist/elements/number-ticker.js +1 -1
  75. package/dist/elements/onboarding.js +3 -3
  76. package/dist/elements/permission-grant.js +3 -3
  77. package/dist/elements/prompt-library.js +1 -1
  78. package/dist/elements/question-card.d.ts +50 -0
  79. package/dist/elements/question-card.js +117 -0
  80. package/dist/elements/queue-dock.js +1 -1
  81. package/dist/elements/quota-banner.js +1 -1
  82. package/dist/elements/quote-reply.js +3 -3
  83. package/dist/elements/read-aloud.js +2 -2
  84. package/dist/elements/reasoning-effort.js +2 -2
  85. package/dist/elements/reasoning-panel.js +3 -3
  86. package/dist/elements/recommendation-card.js +2 -2
  87. package/dist/elements/regenerate-menu.js +1 -1
  88. package/dist/elements/request-delivery.js +31 -0
  89. package/dist/elements/research-report.js +1 -1
  90. package/dist/elements/retrieval-chunks.js +3 -3
  91. package/dist/elements/revert-dock.js +1 -1
  92. package/dist/elements/reviewable-diff.js +4 -4
  93. package/dist/elements/schedule-card.js +2 -2
  94. package/dist/elements/score-breakdown.js +2 -2
  95. package/dist/elements/scroll-anchor.js +2 -2
  96. package/dist/elements/settings-panel.js +3 -3
  97. package/dist/elements/sources.js +1 -1
  98. package/dist/elements/spec-sheet.js +1 -1
  99. package/dist/elements/stopped-run.js +2 -2
  100. package/dist/elements/streaming-text.js +2 -2
  101. package/dist/elements/subagent-list.js +4 -4
  102. package/dist/elements/suggestions.js +1 -1
  103. package/dist/elements/surfaces.d.ts +6 -6
  104. package/dist/elements/surfaces.js +7 -7
  105. package/dist/elements/tail-status.js +1 -1
  106. package/dist/elements/terminal-block.js +1 -1
  107. package/dist/elements/thinking-indicator.js +1 -1
  108. package/dist/elements/timeline.js +1 -1
  109. package/dist/elements/todo-list.js +1 -1
  110. package/dist/elements/tool-call.d.ts +18 -2
  111. package/dist/elements/tool-call.js +20 -11
  112. package/dist/elements/tool-error.js +2 -2
  113. package/dist/elements/tool-group.d.ts +11 -3
  114. package/dist/elements/tool-group.js +62 -38
  115. package/dist/elements/tool-state.d.ts +13 -0
  116. package/dist/elements/tool-state.js +44 -0
  117. package/dist/elements/tool-timeline.js +2 -2
  118. package/dist/elements/trace-waterfall.js +2 -2
  119. package/dist/elements/unsupported-request-card.d.ts +31 -0
  120. package/dist/elements/unsupported-request-card.js +80 -0
  121. package/dist/elements/vocabulary.d.ts +50 -1
  122. package/dist/elements/vocabulary.js +50 -1
  123. package/dist/elements/voice-conversation.js +5 -5
  124. package/dist/elements/web-preview.js +1 -1
  125. package/dist/elements/web-search.js +2 -2
  126. package/dist/elements/work-collapse.js +1 -1
  127. package/dist/forms/_chassis.d.ts +6 -1
  128. package/dist/forms/_chassis.js +8 -3
  129. package/dist/forms/file-upload.js +1 -1
  130. package/dist/forms/rating.js +1 -1
  131. package/dist/forms/select.js +1 -1
  132. package/dist/forms/switch.js +2 -2
  133. package/dist/forms/toggle.js +1 -1
  134. package/dist/index.d.ts +3 -1
  135. package/dist/index.js +3 -1
  136. package/dist/instruments/_ledger-disclosure.js +1 -1
  137. package/dist/instruments/data-tree.js +1 -1
  138. package/dist/instruments/meter.js +1 -1
  139. package/dist/instruments/seam-grid.js +1 -1
  140. package/dist/layout/_collapse.js +3 -3
  141. package/dist/layout/bento.js +1 -1
  142. package/dist/layout/breadcrumb.js +1 -1
  143. package/dist/layout/carousel.js +1 -1
  144. package/dist/layout/collapsible.js +1 -1
  145. package/dist/layout/index.d.ts +3 -1
  146. package/dist/layout/index.js +3 -1
  147. package/dist/layout/item.js +1 -1
  148. package/dist/layout/link.js +1 -1
  149. package/dist/layout/pagination.js +1 -1
  150. package/dist/layout/pane-seam.d.ts +75 -0
  151. package/dist/layout/pane-seam.js +127 -0
  152. package/dist/layout/resizable.d.ts +2 -1
  153. package/dist/layout/resizable.js +3 -2
  154. package/dist/layout/sidebar.js +3 -3
  155. package/dist/layout/split-pane.d.ts +74 -0
  156. package/dist/layout/split-pane.js +135 -0
  157. package/dist/layout/stepper.js +1 -1
  158. package/dist/lib/auto-grow.js +42 -0
  159. package/dist/overlays/_surface.d.ts +5 -5
  160. package/dist/overlays/_surface.js +4 -4
  161. package/dist/overlays/drawer.js +1 -1
  162. package/dist/overlays/menubar.js +1 -1
  163. package/dist/overlays/sheet.js +1 -1
  164. package/dist/overlays/toast.js +1 -1
  165. package/dist/primitives/button.js +1 -1
  166. package/dist/primitives/chip.d.ts +1 -1
  167. package/dist/styles.css +52 -0
  168. package/dist/system/_paint-stroke-sunken.js +22 -2
  169. package/dist/system/_paint-stroke-surface-1.js +5 -1
  170. package/dist/system/_paint-stroke-surface-2.js +5 -1
  171. package/dist/system/_style-disabled.js +1 -7
  172. package/dist/system/agent-skin.d.ts +11 -5
  173. package/manifest/components/agent-surface.json +5 -4
  174. package/manifest/components/alert-dialog.json +1 -0
  175. package/manifest/components/approval-card.json +45 -7
  176. package/manifest/components/autocomplete.json +2 -0
  177. package/manifest/components/button.json +0 -3
  178. package/manifest/components/card.json +1 -0
  179. package/manifest/components/channel-matrix.json +1 -0
  180. package/manifest/components/chart-tooltip-content.json +1 -0
  181. package/manifest/components/checkbox-group.json +0 -3
  182. package/manifest/components/checkbox.json +0 -3
  183. package/manifest/components/color-field.json +1 -0
  184. package/manifest/components/color-picker.json +1 -0
  185. package/manifest/components/combobox.json +2 -0
  186. package/manifest/components/command-palette.json +1 -0
  187. package/manifest/components/composer-input.json +16 -2
  188. package/manifest/components/composer.json +1 -0
  189. package/manifest/components/context-menu.json +1 -0
  190. package/manifest/components/date-field.json +1 -0
  191. package/manifest/components/date-picker.json +1 -0
  192. package/manifest/components/date-range-picker.json +1 -0
  193. package/manifest/components/dialog.json +1 -0
  194. package/manifest/components/dmx-bar.json +1 -0
  195. package/manifest/components/dmx-strip.json +1 -0
  196. package/manifest/components/drawer.json +1 -0
  197. package/manifest/components/dropdown-menu.json +1 -0
  198. package/manifest/components/elicitation-form.json +16 -1
  199. package/manifest/components/env-var-input.json +1 -0
  200. package/manifest/components/event-feed.json +171 -0
  201. package/manifest/components/event-row.json +116 -0
  202. package/manifest/components/generative-ui.json +4 -3
  203. package/manifest/components/hover-card.json +1 -0
  204. package/manifest/components/input-group.json +1 -0
  205. package/manifest/components/input.json +1 -0
  206. package/manifest/components/menubar.json +1 -0
  207. package/manifest/components/multi-select.json +2 -0
  208. package/manifest/components/musical-time-input.json +1 -0
  209. package/manifest/components/navigation-menu.json +1 -0
  210. package/manifest/components/number-field.json +1 -0
  211. package/manifest/components/otp-field.json +1 -0
  212. package/manifest/components/pane-seam.json +171 -0
  213. package/manifest/components/password-input.json +1 -0
  214. package/manifest/components/popover.json +1 -0
  215. package/manifest/components/question-card.json +157 -0
  216. package/manifest/components/resizable.json +1 -1
  217. package/manifest/components/scrub-input.json +1 -0
  218. package/manifest/components/search-input.json +1 -0
  219. package/manifest/components/select.json +2 -0
  220. package/manifest/components/sheet.json +1 -0
  221. package/manifest/components/split-pane.json +224 -0
  222. package/manifest/components/switch.json +0 -3
  223. package/manifest/components/tags-input.json +2 -0
  224. package/manifest/components/textarea.json +1 -0
  225. package/manifest/components/theme-configurator.json +1 -0
  226. package/manifest/components/time-field.json +1 -0
  227. package/manifest/components/toast.json +1 -0
  228. package/manifest/components/toggle.json +0 -3
  229. package/manifest/components/tool-call.json +46 -2
  230. package/manifest/components/tool-group.json +15 -3
  231. package/manifest/components/tooltip.json +1 -0
  232. package/manifest/components/unsupported-request-card.json +117 -0
  233. package/manifest/fixtures.json +16 -0
  234. package/manifest/manifest.json +134 -56
  235. package/manifest/tokens.json +12 -2
  236. package/package.json +3 -3
@@ -0,0 +1,135 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { FOCUS_RING_INSET_2 } from "../system/_focus-ring.js";
4
+ import { ChevronLeftGlyph, ChevronRightGlyph } from "./_glyphs.js";
5
+ import { PaneSeam } from "./pane-seam.js";
6
+ import * as React from "react";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/layout/split-pane.tsx
9
+ /**
10
+ * A list beside its detail, which stacks into a drill-in when narrow (#102).
11
+ *
12
+ * The master-detail opinion on top of {@link PaneSeam}. Side by side while
13
+ * the detail would be at least twice the list, measured on the frame rather
14
+ * than the window, so it works in any column. Below that it stacks the way a
15
+ * phone does: the list, or the detail with a Back button, and Escape inside the
16
+ * detail goes back too. `detailOpen` says which shows; selection and the list's
17
+ * own keyboard stay with the list.
18
+ *
19
+ * The frame measures itself with a `ResizeObserver` and draws side by side
20
+ * until it has, so a server render and a browser without one get the split.
21
+ *
22
+ * @example
23
+ * const [selected, setSelected] = React.useState<string | null>(null);
24
+ *
25
+ * <SplitPane
26
+ * listLabel="events"
27
+ * list={<EventFeed events={events} selectedId={selected} onSelect={setSelected} />}
28
+ * detail={selected ? <EventDetail id={selected} /> : <EmptyState title="Pick an event" />}
29
+ * detailOpen={selected !== null}
30
+ * onBack={() => setSelected(null)}
31
+ * collapsible
32
+ * />
33
+ */
34
+ const SplitPane = React.forwardRef(function SplitPane({ list, detail, listLabel, listSize, defaultListSize = 320, onListSizeChange, minListSize = 200, maxListSize = 480, stackBelow, detailOpen = false, onBack, collapsible = false, collapsed, defaultCollapsed = false, onCollapsedChange, className, ...elementProps }, ref) {
35
+ const node = React.useRef(null);
36
+ const setNode = React.useCallback((element) => {
37
+ node.current = element;
38
+ if (typeof ref === "function") ref(element);
39
+ else if (ref) ref.current = element;
40
+ }, [ref]);
41
+ const [ownSize, setOwnSize] = React.useState(defaultListSize);
42
+ const size = listSize ?? ownSize;
43
+ const [ownCollapsed, setOwnCollapsed] = React.useState(defaultCollapsed);
44
+ const folded = collapsible && (collapsed ?? ownCollapsed);
45
+ const [width, setWidth] = React.useState(null);
46
+ React.useLayoutEffect(() => {
47
+ const element = node.current;
48
+ if (element === null || typeof ResizeObserver === "undefined") return;
49
+ const measure = () => setWidth(element.getBoundingClientRect().width);
50
+ const observer = new ResizeObserver(measure);
51
+ observer.observe(element);
52
+ measure();
53
+ return () => observer.disconnect();
54
+ }, []);
55
+ const threshold = stackBelow ?? size * 3;
56
+ const layout = width !== null && width < threshold ? "stacked" : "split";
57
+ const resize = (next) => {
58
+ if (listSize === void 0) setOwnSize(next);
59
+ onListSizeChange?.(next);
60
+ };
61
+ const fold = (next) => {
62
+ if (collapsed === void 0) setOwnCollapsed(next);
63
+ onCollapsedChange?.(next);
64
+ };
65
+ const quiet = `flex items-center justify-center rounded-control text-fg-muted transition-colors duration-fast outline-none hover:bg-surface-2 hover:text-fg ${FOCUS_RING_INSET_2} motion-reduce:transition-none`;
66
+ return /* @__PURE__ */ jsx("div", {
67
+ ref: setNode,
68
+ "data-slot": "split-pane",
69
+ "data-layout": layout,
70
+ "data-detail-open": detailOpen || void 0,
71
+ "data-collapsed": folded || void 0,
72
+ className: cn("flex min-h-0 min-w-0", className),
73
+ ...elementProps,
74
+ children: layout === "stacked" ? detailOpen ? /* @__PURE__ */ jsxs("div", {
75
+ "data-slot": "split-pane-detail",
76
+ className: "flex min-h-0 min-w-0 flex-1 flex-col",
77
+ onKeyDown: (event) => {
78
+ if (event.key !== "Escape" || event.defaultPrevented) return;
79
+ event.preventDefault();
80
+ onBack?.();
81
+ },
82
+ children: [/* @__PURE__ */ jsx("div", {
83
+ className: "flex shrink-0 items-center border-b border-border px-(--cue-space-2) py-(--cue-space-1)",
84
+ children: /* @__PURE__ */ jsxs("button", {
85
+ type: "button",
86
+ onClick: onBack,
87
+ className: cn(quiet, "gap-(--cue-space-1) px-(--cue-space-2) py-(--cue-space-1) text-(length:--cue-text-ui)"),
88
+ children: [/* @__PURE__ */ jsx(ChevronLeftGlyph, { className: "size-3" }), `Back to ${listLabel}`]
89
+ })
90
+ }), /* @__PURE__ */ jsx("div", {
91
+ className: "min-h-0 min-w-0 flex-1",
92
+ children: detail
93
+ })]
94
+ }) : /* @__PURE__ */ jsx("div", {
95
+ "data-slot": "split-pane-list",
96
+ className: "min-h-0 min-w-0 flex-1",
97
+ children: list
98
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [folded ? /* @__PURE__ */ jsxs("button", {
99
+ type: "button",
100
+ "aria-expanded": false,
101
+ onClick: () => fold(false),
102
+ className: cn(quiet, "w-(--cue-space-6) shrink-0 self-stretch rounded-none border-e border-border"),
103
+ children: [/* @__PURE__ */ jsx(ChevronRightGlyph, { className: "size-3" }), /* @__PURE__ */ jsx("span", {
104
+ className: "sr-only",
105
+ children: `Show ${listLabel}`
106
+ })]
107
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
108
+ "data-slot": "split-pane-list",
109
+ className: "relative min-h-0 shrink-0",
110
+ style: { width: size },
111
+ children: [list, collapsible ? /* @__PURE__ */ jsxs("button", {
112
+ type: "button",
113
+ "aria-expanded": true,
114
+ onClick: () => fold(true),
115
+ className: cn(quiet, "absolute end-(--cue-space-1) top-(--cue-space-1) size-(--cue-space-6)"),
116
+ children: [/* @__PURE__ */ jsx(ChevronLeftGlyph, { className: "size-3" }), /* @__PURE__ */ jsx("span", {
117
+ className: "sr-only",
118
+ children: `Hide ${listLabel}`
119
+ })]
120
+ }) : null]
121
+ }), /* @__PURE__ */ jsx(PaneSeam, {
122
+ label: `Resize the ${listLabel}`,
123
+ min: minListSize,
124
+ max: maxListSize,
125
+ value: Math.min(maxListSize, Math.max(minListSize, size)),
126
+ onValueChange: resize
127
+ })] }), /* @__PURE__ */ jsx("div", {
128
+ "data-slot": "split-pane-detail",
129
+ className: "min-h-0 min-w-0 flex-1",
130
+ children: detail
131
+ })] })
132
+ });
133
+ });
134
+ //#endregion
135
+ export { SplitPane };
@@ -19,7 +19,7 @@ const INDEX_TONES = {
19
19
  current: "text-accent",
20
20
  upcoming: "text-fg-subtle"
21
21
  };
22
- const STEP_BUTTON_CLASSES = `inline-flex cursor-pointer items-center gap-(--cue-space-2) rounded-control border-0 bg-transparent p-0 ${LABEL_FACE} text-[length:inherit] tracking-[inherit] text-inherit ${LABEL_CASE} transition-colors duration-150 outline-none hover:text-fg ${FOCUS_RING_OFFSET_2}`;
22
+ const STEP_BUTTON_CLASSES = `inline-flex cursor-pointer items-center gap-(--cue-space-2) rounded-control border-0 bg-transparent p-0 ${LABEL_FACE} text-[length:inherit] tracking-[inherit] text-inherit ${LABEL_CASE} transition-colors duration-fast outline-none hover:text-fg ${FOCUS_RING_OFFSET_2}`;
23
23
  /**
24
24
  * The step trail: `1 target · 2 preview · 3 apply`.
25
25
  *
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { useIsomorphicLayoutEffect } from "../system/use-isomorphic-layout-effect.js";
3
+ import * as React from "react";
4
+ //#region src/lib/auto-grow.ts
5
+ /** Whether this browser sizes a textarea to its content itself (`field-sizing: content`). */
6
+ function sizesItself() {
7
+ return typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("field-sizing", "content");
8
+ }
9
+ /**
10
+ * Grow a textarea with what it holds, where CSS cannot.
11
+ *
12
+ * `field-sizing: content` sizes a textarea to its text in the browsers that
13
+ * have it, and a `max-height` caps it, so in those this does nothing. Where the
14
+ * property is missing, the textarea is measured instead: its height is reset
15
+ * and set to its `scrollHeight` on every input, and again whenever `value`
16
+ * changes from outside, so a composer the parent clears after sending shrinks
17
+ * back to one line. The cap stays the stylesheet's.
18
+ *
19
+ * Returns a ref for the textarea. A caller that passes its own `ref` after this
20
+ * one replaces it, and the field then sizes with CSS alone.
21
+ *
22
+ * @param value The textarea's value, so a change made from outside re-measures it.
23
+ */
24
+ function useAutoGrow(value) {
25
+ const node = React.useRef(null);
26
+ const fit = React.useCallback(() => {
27
+ const element = node.current;
28
+ if (element === null || sizesItself()) return;
29
+ element.style.height = "auto";
30
+ element.style.height = `${element.scrollHeight}px`;
31
+ }, []);
32
+ const ref = React.useCallback((element) => {
33
+ node.current?.removeEventListener("input", fit);
34
+ node.current = element;
35
+ element?.addEventListener("input", fit);
36
+ fit();
37
+ }, [fit]);
38
+ useIsomorphicLayoutEffect(fit, [value, fit]);
39
+ return { ref };
40
+ }
41
+ //#endregion
42
+ export { useAutoGrow };
@@ -18,7 +18,7 @@
18
18
  * @example
19
19
  * <Popover.Popup className={cn(overlaySurfaceClasses, className)} />
20
20
  */
21
- declare const overlaySurfaceClasses = "rounded-overlay border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box] shadow-[0_0_0_var(--cue-paint-rim-width)_var(--cue-paint-shade-hairline,transparent),0_calc(2px*var(--cue-paint-shadow-scale))_calc(6px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-near,transparent),0_calc(8px*var(--cue-paint-shadow-scale))_calc(24px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-far,rgba(0,0,0,0.4)),inset_0_1px_0_0_var(--cue-paint-sheen-ink,transparent)]";
21
+ declare const overlaySurfaceClasses = "rounded-overlay border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box] [--cue-ground:var(--cue-style-overlay-ground,var(--cue-surface-2))] shadow-[0_0_0_var(--cue-paint-rim-width)_var(--cue-paint-shade-hairline,transparent),0_calc(2px*var(--cue-paint-shadow-scale))_calc(6px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-near,transparent),0_calc(8px*var(--cue-paint-shadow-scale))_calc(24px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-far,rgba(0,0,0,0.4)),inset_0_1px_0_0_var(--cue-paint-sheen-ink,transparent)]";
22
22
  /**
23
23
  * The full-viewport dim behind a modal surface. `--cue-scrim` is theme-owned —
24
24
  * dusk and hivehub tune their own — so the value is never hard-coded here.
@@ -73,9 +73,9 @@ declare const overlaySurfaceClasses = "rounded-overlay border-transparent border
73
73
  * composed declaration without entering it; at rest the property is theirs.
74
74
  * The pseudo's `transition` and timing are its own classes: Tailwind
75
75
  * registers `--tw-duration` and `--tw-ease` with `inherits: false`, so the
76
- * scrim's `duration-150 ease-out` never reach it.
76
+ * scrim's `duration-fast ease-out` never reach it.
77
77
  */
78
- declare const scrimClasses = "fixed inset-0 z-50 bg-(--cue-scrim) before:absolute before:inset-0 before:content-[''] before:pointer-events-none before:[backdrop-filter:var(--cue-paint-scrim-filter,none)] transition-[background-color,backdrop-filter] duration-150 ease-out before:transition-opacity before:duration-150 before:ease-out motion-reduce:transition-none motion-reduce:before:transition-none data-[starting-style]:bg-transparent data-[ending-style]:bg-transparent data-[starting-style]:[backdrop-filter:none] data-[ending-style]:[backdrop-filter:none] data-[starting-style]:before:opacity-0 data-[ending-style]:before:opacity-0";
78
+ declare const scrimClasses = "fixed inset-0 z-50 bg-(--cue-scrim) before:absolute before:inset-0 before:content-[''] before:pointer-events-none before:[backdrop-filter:var(--cue-paint-scrim-filter,none)] transition-[background-color,backdrop-filter] duration-fast ease-out before:transition-opacity before:duration-fast before:ease-out motion-reduce:transition-none motion-reduce:before:transition-none data-[starting-style]:bg-transparent data-[ending-style]:bg-transparent data-[starting-style]:[backdrop-filter:none] data-[ending-style]:[backdrop-filter:none] data-[starting-style]:before:opacity-0 data-[ending-style]:before:opacity-0";
79
79
  /**
80
80
  * What a Base UI `Positioner` needs and nothing more: it is a layout box, not a
81
81
  * painted one. The stacking context lives here so the popup itself can stay
@@ -88,14 +88,14 @@ declare const overlayPositionerClasses = "z-50 outline-none";
88
88
  * transitions — no animation library, and nothing to run on the main thread
89
89
  * when the user has asked for reduced motion.
90
90
  */
91
- declare const overlayPopupTransitionClasses = "origin-(--transform-origin) transition-[opacity,scale] duration-100 ease-out motion-reduce:transition-none data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0";
91
+ declare const overlayPopupTransitionClasses = "origin-(--transform-origin) transition-[opacity,scale] duration-instant ease-out motion-reduce:transition-none data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0";
92
92
  /**
93
93
  * Enter/exit for a centred modal surface. Split from
94
94
  * {@link overlayPopupTransitionClasses} because a dialog is centred with a
95
95
  * translate, and animating `scale` alongside a static `translate` needs the
96
96
  * transition property list to name both.
97
97
  */
98
- declare const overlayModalTransitionClasses = "transition-[opacity,scale] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0";
98
+ declare const overlayModalTransitionClasses = "transition-[opacity,scale] duration-fast ease-out motion-reduce:transition-none data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0";
99
99
  /**
100
100
  * Body copy inside an overlay: the body voice (mono unless a style set names
101
101
  * another face — `_style-body.ts`), one rung under the panel's own text.
@@ -88,9 +88,9 @@ const overlaySurfaceClasses = `rounded-overlay ${STROKE_SURFACE_2} ${ELEVATION_2
88
88
  * composed declaration without entering it; at rest the property is theirs.
89
89
  * The pseudo's `transition` and timing are its own classes: Tailwind
90
90
  * registers `--tw-duration` and `--tw-ease` with `inherits: false`, so the
91
- * scrim's `duration-150 ease-out` never reach it.
91
+ * scrim's `duration-fast ease-out` never reach it.
92
92
  */
93
- const scrimClasses = "fixed inset-0 z-50 bg-(--cue-scrim) before:absolute before:inset-0 before:content-[''] before:pointer-events-none before:[backdrop-filter:var(--cue-paint-scrim-filter,none)] transition-[background-color,backdrop-filter] duration-150 ease-out before:transition-opacity before:duration-150 before:ease-out motion-reduce:transition-none motion-reduce:before:transition-none data-[starting-style]:bg-transparent data-[ending-style]:bg-transparent data-[starting-style]:[backdrop-filter:none] data-[ending-style]:[backdrop-filter:none] data-[starting-style]:before:opacity-0 data-[ending-style]:before:opacity-0";
93
+ const scrimClasses = "fixed inset-0 z-50 bg-(--cue-scrim) before:absolute before:inset-0 before:content-[''] before:pointer-events-none before:[backdrop-filter:var(--cue-paint-scrim-filter,none)] transition-[background-color,backdrop-filter] duration-fast ease-out before:transition-opacity before:duration-fast before:ease-out motion-reduce:transition-none motion-reduce:before:transition-none data-[starting-style]:bg-transparent data-[ending-style]:bg-transparent data-[starting-style]:[backdrop-filter:none] data-[ending-style]:[backdrop-filter:none] data-[starting-style]:before:opacity-0 data-[ending-style]:before:opacity-0";
94
94
  /**
95
95
  * What a Base UI `Positioner` needs and nothing more: it is a layout box, not a
96
96
  * painted one. The stacking context lives here so the popup itself can stay
@@ -103,14 +103,14 @@ const overlayPositionerClasses = "z-50 outline-none";
103
103
  * transitions — no animation library, and nothing to run on the main thread
104
104
  * when the user has asked for reduced motion.
105
105
  */
106
- const overlayPopupTransitionClasses = "origin-(--transform-origin) transition-[opacity,scale] duration-100 ease-out motion-reduce:transition-none data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0";
106
+ const overlayPopupTransitionClasses = "origin-(--transform-origin) transition-[opacity,scale] duration-instant ease-out motion-reduce:transition-none data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0";
107
107
  /**
108
108
  * Enter/exit for a centred modal surface. Split from
109
109
  * {@link overlayPopupTransitionClasses} because a dialog is centred with a
110
110
  * translate, and animating `scale` alongside a static `translate` needs the
111
111
  * transition property list to name both.
112
112
  */
113
- const overlayModalTransitionClasses = "transition-[opacity,scale] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0";
113
+ const overlayModalTransitionClasses = "transition-[opacity,scale] duration-fast ease-out motion-reduce:transition-none data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0 data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0";
114
114
  /**
115
115
  * Body copy inside an overlay: the body voice (mono unless a style set names
116
116
  * another face — `_style-body.ts`), one rung under the panel's own text.
@@ -106,7 +106,7 @@ const Drawer$1 = {
106
106
  children: /* @__PURE__ */ jsxs(Drawer.Popup, {
107
107
  ref,
108
108
  "data-slot": "drawer-popup",
109
- className: cn(overlaySurfaceClasses, DRAWER_RADIUS, "flex flex-col overflow-y-auto overscroll-contain outline-none transition-transform duration-200 ease-out data-[swiping]:duration-0 motion-reduce:transition-none", POPUP_GEOMETRY[direction], className),
109
+ className: cn(overlaySurfaceClasses, DRAWER_RADIUS, "flex flex-col overflow-y-auto overscroll-contain outline-none transition-transform duration-brisk ease-out data-[swiping]:duration-0 motion-reduce:transition-none", POPUP_GEOMETRY[direction], className),
110
110
  ...popupProps,
111
111
  children: [grabHandle ? /* @__PURE__ */ jsx("div", {
112
112
  "data-slot": "drawer-grab-handle",
@@ -68,7 +68,7 @@ const Menubar$1 = {
68
68
  return /* @__PURE__ */ jsx(Menu.Trigger, {
69
69
  ref,
70
70
  "data-slot": "menubar-trigger",
71
- className: cn(`inline-flex h-control-sm shrink-0 cursor-default items-center rounded-control px-(--cue-space-3) text-(length:--cue-text-ui) whitespace-nowrap text-fg-muted transition-colors duration-150 outline-none select-none hover:text-fg ${FOCUS_RING_INSET} data-[popup-open]:bg-accent-soft data-[popup-open]:text-fg data-[disabled]:pointer-events-none data-[disabled]:opacity-40`, CONTROL_FACE, className),
71
+ className: cn(`inline-flex h-control-sm shrink-0 cursor-default items-center rounded-control px-(--cue-space-3) text-(length:--cue-text-ui) whitespace-nowrap text-fg-muted transition-colors duration-fast outline-none select-none hover:text-fg ${FOCUS_RING_INSET} data-[popup-open]:bg-accent-soft data-[popup-open]:text-fg data-[disabled]:pointer-events-none data-[disabled]:opacity-40`, CONTROL_FACE, className),
72
72
  ...elementProps
73
73
  });
74
74
  }),
@@ -23,7 +23,7 @@ import { Dialog } from "@base-ui/react/dialog";
23
23
  * into a corner; killing the outer edge is what makes it read as part of the
24
24
  * window.
25
25
  */
26
- const sheetPopupVariants = cva(`${overlaySurfaceClasses} fixed z-50 flex flex-col gap-(--cue-space-4) overflow-y-auto p-(--cue-space-6) outline-none transition-transform duration-200 ease-out motion-reduce:transition-none`, {
26
+ const sheetPopupVariants = cva(`${overlaySurfaceClasses} fixed z-50 flex flex-col gap-(--cue-space-4) overflow-y-auto p-(--cue-space-6) outline-none transition-transform duration-brisk ease-out motion-reduce:transition-none`, {
27
27
  variants: { side: {
28
28
  right: "inset-y-0 right-0 w-[min(28rem,calc(100vw-2rem))] rounded-r-none border-y-0 border-r-0 data-[starting-style]:translate-x-full data-[ending-style]:translate-x-full",
29
29
  left: "inset-y-0 left-0 w-[min(28rem,calc(100vw-2rem))] rounded-l-none border-y-0 border-l-0 data-[starting-style]:-translate-x-full data-[ending-style]:-translate-x-full",
@@ -83,7 +83,7 @@ function ToastItem({ toast, className, closeLabel }) {
83
83
  return /* @__PURE__ */ jsxs(Toast.Root, {
84
84
  toast,
85
85
  "data-slot": "toast-root",
86
- className: cn(overlaySurfaceClasses, `relative flex flex-col gap-(--cue-space-1) border-l-2 ${TOAST_PAD_Y} pr-(--cue-space-6) ${TOAST_PAD_X} outline-none`, "transition-[opacity,translate] duration-150 ease-out motion-reduce:transition-none", "data-[starting-style]:translate-x-2 data-[starting-style]:opacity-0", "data-[ending-style]:translate-x-2 data-[ending-style]:opacity-0", "data-[limited]:hidden", TOAST_TONE_RAIL[toToastTone(toast.type)], className),
86
+ className: cn(overlaySurfaceClasses, `relative flex flex-col gap-(--cue-space-1) border-l-2 ${TOAST_PAD_Y} pr-(--cue-space-6) ${TOAST_PAD_X} outline-none`, "transition-[opacity,translate] duration-fast ease-out motion-reduce:transition-none", "data-[starting-style]:translate-x-2 data-[starting-style]:opacity-0", "data-[ending-style]:translate-x-2 data-[ending-style]:opacity-0", "data-[limited]:hidden", TOAST_TONE_RAIL[toToastTone(toast.type)], className),
87
87
  children: [
88
88
  /* @__PURE__ */ jsx(Toast.Title, {
89
89
  "data-slot": "toast-title",
@@ -38,7 +38,7 @@ import { useRender } from "@base-ui/react/use-render";
38
38
  *
39
39
  * @internal Not part of the public API; imported by `icon-button.tsx`.
40
40
  */
41
- const buttonChassis = `inline-flex select-none items-center justify-center ${CONTROL_GAP} whitespace-nowrap rounded-control ${CONTROL_FACE} ${CONTROL_WEIGHT} ${CONTROL_TRACKING} ${CONTROL_TRIM} ${CONTROL_CASE} transition-colors duration-150 outline-none ${FOCUS_RING_PAINT} focus-visible:outline-offset-[var(--cue-style-focus-offset,1px)] disabled:pointer-events-none ${DISABLED_OPACITY}`;
41
+ const buttonChassis = `inline-flex select-none items-center justify-center ${CONTROL_GAP} whitespace-nowrap rounded-control ${CONTROL_FACE} ${CONTROL_WEIGHT} ${CONTROL_TRACKING} ${CONTROL_TRIM} ${CONTROL_CASE} transition-colors duration-fast outline-none ${FOCUS_RING_PAINT} focus-visible:outline-offset-[var(--cue-style-focus-offset,1px)] disabled:pointer-events-none ${DISABLED_OPACITY}`;
42
42
  /**
43
43
  * A style set's chevron cut (structural Looks, spec 2026-10-02 §3.3), for the tones
44
44
  * that are a filled shape: primary and danger always, secondary only where the set
@@ -2,7 +2,7 @@ import { ClassProp, VariantProps } from "../lib/cva.js";
2
2
  import * as React from "react";
3
3
  //#region src/primitives/chip.d.ts
4
4
  declare const chipVariants: (props?: ({
5
- tone?: "danger" | "info" | "warn" | "accent" | "ok" | "busy" | "neutral" | null | undefined;
5
+ tone?: "danger" | "info" | "warn" | "busy" | "accent" | "ok" | "neutral" | null | undefined;
6
6
  variant?: "outline" | "tinted" | "tag" | null | undefined;
7
7
  interactive?: boolean | null | undefined;
8
8
  } & ClassProp) | undefined) => string;
package/dist/styles.css CHANGED
@@ -133,3 +133,55 @@
133
133
  clip-path: none;
134
134
  }
135
135
  }
136
+
137
+ /* A container's ground, for the relative field fill (spec 2026-10-08-field-ground R3).
138
+ *
139
+ * Every fill utility a container can paint with says what it painted, as
140
+ * `--cue-ground`, and a control inside `[data-field-ground="relative"]` mixes
141
+ * its fill from the nearest one (`system/_field-ground.ts`). Declaring it is
142
+ * free: nothing outside the opt-in reads it. In `base` so a consumer's own
143
+ * utility or inline `--cue-ground` wins; a container painted any other way — a
144
+ * gradient, an image, a translucent fill — sets `--cue-ground` itself. */
145
+ @layer base {
146
+ .bg-bg {
147
+ --cue-ground: var(--cue-bg);
148
+ }
149
+ .bg-sunken {
150
+ --cue-ground: var(--cue-sunken);
151
+ }
152
+ .bg-surface-1 {
153
+ --cue-ground: var(--cue-surface-1);
154
+ }
155
+ .bg-surface-2 {
156
+ --cue-ground: var(--cue-surface-2);
157
+ }
158
+ .bg-surface-3 {
159
+ --cue-ground: var(--cue-surface-3);
160
+ }
161
+ }
162
+
163
+ /* The relative field fill (spec 2026-10-08-field-ground R3), and opt-in.
164
+ *
165
+ * Nothing happens until an ancestor carries `data-field-ground="relative"`.
166
+ * Inside one, a field that wears `field-ground` — every control the recessed
167
+ * chassis draws does — re-declares its OWN `--cue-sunken` one step off the
168
+ * nearest container's `--cue-ground` (declared above, and by the raised and
169
+ * overlay strokes), toward its text colour: lighter on a dark ground, deeper on
170
+ * a light one, 6% unless `--cue-style-field-step` says otherwise.
171
+ * `--cue-style-field-toward` changes the direction.
172
+ *
173
+ * On the field, never on the ancestor: a custom property resolves its `var()`s
174
+ * where it is declared, so declared on the opt-in element it would freeze at
175
+ * that element's ground and every field below — in a card, in a popover —
176
+ * would fill as if it stood on the page.
177
+ *
178
+ * Re-declaring `sunken` rather than adding a term to the field's fill keeps
179
+ * the chain the stroke already reads, `var(--cue-style-field-fill,
180
+ * var(--cue-sunken))`: a named fill still wins, a disabled field follows, and
181
+ * no field's class grows by more than this utility's name. A consumer's own
182
+ * field can wear `field-ground` too. */
183
+ @utility field-ground {
184
+ :where([data-field-ground="relative"]) & {
185
+ --cue-sunken: color-mix(in oklab, var(--cue-ground, var(--cue-bg)), var(--cue-style-field-toward, var(--cue-fg)) var(--cue-style-field-step, 6%));
186
+ }
187
+ }
@@ -12,8 +12,28 @@
12
12
  * The recipe itself, and why its off state is a flat gradient rather than
13
13
  * `none`, is documented once in `./_paint`.
14
14
  *
15
+ * **The fill is a name first** (spec 2026-10-08-field-ground R2):
16
+ * `--cue-style-field-fill`, declared on no `:root` for the reason
17
+ * `_style-input-face.ts` gives, so with nothing set this resolves to exactly
18
+ * the `--cue-sunken` it always painted. A style set, or any subtree, names it.
19
+ *
15
20
  * @internal Not part of the public API.
16
21
  */
17
- const STROKE_SUNKEN = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-sunken),var(--cue-sunken)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box]";
22
+ const STROKE_SUNKEN = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-field-fill,var(--cue-sunken)),var(--cue-style-field-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box]";
23
+ /**
24
+ * The same two layers when the field is disabled: the fill read through
25
+ * `style-disabled-fill`, then the field's own fill, then `sunken`, on
26
+ * `:disabled` and on `[data-disabled]`.
27
+ *
28
+ * **Here, beside the resting recipe, and not in `_style-disabled.ts`** with the
29
+ * other disabled recipes (spec 2026-10-08-field-ground R2). Docgen credits every
30
+ * `--cue-*` in a module to each component that imports it, and the button, the
31
+ * checkbox and the switch import that module for their own disabled recipes:
32
+ * left there, all of them would claim a field's fill they never paint. This
33
+ * module is imported by the field chassis alone, so the credit is exact, and
34
+ * the library ships one file per module, so a module of its own would have
35
+ * cost every page that draws a field one more request.
36
+ */
37
+ const FIELD_DISABLED_FILL = "disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))]";
18
38
  //#endregion
19
- export { STROKE_SUNKEN };
39
+ export { FIELD_DISABLED_FILL, STROKE_SUNKEN };
@@ -12,8 +12,12 @@
12
12
  * The recipe itself, and why its off state is a flat gradient rather than
13
13
  * `none`, is documented once in `./_paint`.
14
14
  *
15
+ * It also says what it painted, as `--cue-ground`, for the relative field fill
16
+ * (spec 2026-10-08-field-ground R3): a field inside `[data-field-ground="relative"]`
17
+ * takes its fill one step off the nearest container's ground.
18
+ *
15
19
  * @internal Not part of the public API.
16
20
  */
17
- const STROKE_SURFACE_1 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-surface-1),var(--cue-surface-1)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-raised-from,var(--cue-paint-stroke-from,var(--cue-border))),var(--cue-paint-stroke-raised-to,var(--cue-paint-stroke-to,var(--cue-border))))] bg-origin-border [background-clip:padding-box,border-box]";
21
+ const STROKE_SURFACE_1 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-surface-1),var(--cue-surface-1)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-raised-from,var(--cue-paint-stroke-from,var(--cue-border))),var(--cue-paint-stroke-raised-to,var(--cue-paint-stroke-to,var(--cue-border))))] bg-origin-border [background-clip:padding-box,border-box] [--cue-ground:var(--cue-surface-1)]";
18
22
  //#endregion
19
23
  export { STROKE_SURFACE_1 };
@@ -33,8 +33,12 @@
33
33
  * The recipe itself, and why its off state is a flat gradient rather than
34
34
  * `none`, is documented once in `./_paint`.
35
35
  *
36
+ * It also says what it painted, as `--cue-ground`, for the relative field fill
37
+ * (spec 2026-10-08-field-ground R3): a field inside `[data-field-ground="relative"]`
38
+ * takes its fill one step off the nearest container's ground.
39
+ *
36
40
  * @internal Not part of the public API.
37
41
  */
38
- const STROKE_SURFACE_2 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box]";
42
+ const STROKE_SURFACE_2 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box] [--cue-ground:var(--cue-style-overlay-ground,var(--cue-surface-2))]";
39
43
  //#endregion
40
44
  export { STROKE_SURFACE_2 };
@@ -33,12 +33,6 @@ const LABEL_DISABLED_OPACITY = "has-[[data-disabled]]:opacity-[var(--cue-style-d
33
33
  /** A disabled control label's ink, falling back to the label ink it drew (`LABEL_INK`). */
34
34
  const LABEL_DISABLED_INK = "has-[[data-disabled]]:text-[color:var(--cue-style-disabled-ink,var(--cue-style-label-ink,var(--cue-fg-muted)))]";
35
35
  /**
36
- * The field chassis's fill when disabled: its own two layers (`STROKE_SUNKEN`),
37
- * with the fill layer's `sunken` read through `style-disabled-fill`, on
38
- * `:disabled` and on `[data-disabled]`.
39
- */
40
- const FIELD_DISABLED_FILL = "disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))]";
41
- /**
42
36
  * The button tones' fill and ink when disabled, each falling back to the tone's own resting fill and ink.
43
37
  * The two transparent tones keep their ground and take an edge instead (`style-disabled-edge`, falling
44
38
  * back to the edge each drew): stripe.com's disabled icon button stays on its ground and draws a 1px rim
@@ -67,4 +61,4 @@ const CHECK_DISABLED = "data-[disabled]:bg-[color:var(--cue-style-disabled-fill,
67
61
  /** The switch's track when disabled, unchecked and checked, each state keeping its own fallback. */
68
62
  const SWITCH_DISABLED_TRACK = "data-[disabled]:bg-[color:var(--cue-style-disabled-fill,var(--cue-surface-3))] data-[disabled]:data-[checked]:bg-[color:var(--cue-style-disabled-fill,var(--cue-accent))]";
69
63
  //#endregion
70
- export { CHECK_DISABLED, DATA_DISABLED_OPACITY, DISABLED_OPACITY, DISABLED_TONES, FIELD_DISABLED_FILL, LABEL_DISABLED_INK, LABEL_DISABLED_OPACITY, SWITCH_DISABLED_TRACK };
64
+ export { CHECK_DISABLED, DATA_DISABLED_OPACITY, DISABLED_OPACITY, DISABLED_TONES, LABEL_DISABLED_INK, LABEL_DISABLED_OPACITY, SWITCH_DISABLED_TRACK };
@@ -9,18 +9,24 @@ import "react";
9
9
  * - `aui` — supersede mode. `@cueplusplus/ui/elements.css` re-points those same
10
10
  * tokens to assistant-ui's own values under `[data-cue-skin="aui"]`, so one
11
11
  * DOM paints two ways with no branching in any component.
12
+ * - `cursor` — the same mechanism with Cursor's agent-pane values, under
13
+ * `[data-cue-skin="cursor"]`: the gallery study's palette, published so a
14
+ * consumer wears it rather than copying it. Its light and dark halves follow
15
+ * `data-mode`, as assistant-ui's do.
12
16
  */
13
- type AgentSkin = "cue" | "aui";
17
+ type AgentSkin = "cue" | "aui" | "cursor";
14
18
  /**
15
19
  * How far the supersede island goes: colour only, or colour and geometry.
16
20
  *
17
21
  * - `cue-metrics` — the default. Colour is re-pointed; cue's type scale, radius
18
22
  * ladder and density ladder still apply, so an island tracks the density of
19
23
  * the console around it.
20
- * - `upstream` — assistant-ui's metrics too: their radii, their literal 13.5 /
21
- * 12 / 11 px type, `--spacing` pinned at Tailwind's default. Density stops at
22
- * the island edge, which is the point — this is the mode for showing an
23
- * element exactly as its authors drew it.
24
+ * - `upstream` — the skin's own geometry too. Under `aui` that is
25
+ * assistant-ui's radii, their literal 13.5 / 12 / 11 px type and `--spacing`
26
+ * pinned at Tailwind's default, so density stops at the island edge, which is
27
+ * the point: this is the mode for showing an element exactly as its authors
28
+ * drew it. Under `cursor` it is Cursor's corners and its two faces. Cursor's
29
+ * type sizes and spacing were never recorded, so those stay cue's.
24
30
  */
25
31
  type AgentFidelity = "upstream" | "cue-metrics";
26
32
  //#endregion
@@ -5,17 +5,18 @@
5
5
  "importPath": "@cueplusplus/ui/elements",
6
6
  "peerDependencies": [],
7
7
  "clientOnly": true,
8
- "description": "The supersede island: one subtree, painted by a different set of values.\n\nRenders a plain `<div data-slot=\"agent-surface\" data-cue-skin=\"…\"\ndata-cue-fidelity=\"…\">` and publishes the same pair on React context. The attributes do the work —\n`@cueplusplus/ui/elements.css` re-declares, under them, the `--cue-*` tokens\nthe whole elements family reads, and custom properties inherit, so every\nelement inside repaints with no prop threading and no branch in any\ncomponent. The context exists for the one place that cannot see the DOM: an\noverlay portaled to `<body>` from inside the island, which\n{@link useCuePortalProps } stamps from the same values.\n\nTwo things this is not. It is not a theme — `data-theme`, `data-mode` and\n`data-density` are untouched, and a light app keeps a light island. And it is\nnot needed for cue mode: the elements were rewritten onto cue's tokens by the\nvendoring codemod, so `skin=\"cue\"` is what they already do. Reach for this to\nshow an element the way assistant-ui drew it, or to hand a chat panel back to\nupstream's design language inside a console that is otherwise cue's.\n\nWhat an `aui` island re-points is this family's own vocabulary rather than the\nwhole token contract: `--cue-busy`, `--cue-info`, `--cue-selection`,\n`--cue-focus`, `--cue-scrim` and every derived `*-soft` / `*-border` stay on\nthe console's theme. Nothing in the elements group reads them, so the island\nis complete for what it is for — but a *cue* component placed inside one (a\n`Chip tone=\"busy\"`, a `Toast`) paints half in each palette, and the\nderivations cannot be made to follow at all: they are `color-mix()` values\nresolved on the theme block and inherited as answers. `elements.css` carries\nthe full accounting beside the values themselves.\n\n`elements.css` is a consumer import: `@import \"@cueplusplus/ui/elements.css\";`\nafter the main stylesheet. Without it a `skin=\"aui\"` island simply stays in\ncue's palette.",
8
+ "description": "The supersede island: one subtree, painted by a different set of values.\n\nRenders a plain `<div data-slot=\"agent-surface\" data-cue-skin=\"…\"\ndata-cue-fidelity=\"…\">` and publishes the same pair on React context. The attributes do the work —\n`@cueplusplus/ui/elements.css` re-declares, under them, the `--cue-*` tokens\nthe whole elements family reads, and custom properties inherit, so every\nelement inside repaints with no prop threading and no branch in any\ncomponent. The context exists for the one place that cannot see the DOM: an\noverlay portaled to `<body>` from inside the island, which\n{@link useCuePortalProps } stamps from the same values.\n\nTwo things this is not. It is not a theme — `data-theme`, `data-mode` and\n`data-density` are untouched, and a light app keeps a light island. And it is\nnot needed for cue mode: the elements were rewritten onto cue's tokens by the\nvendoring codemod, so `skin=\"cue\"` is what they already do. Reach for this to\nshow an element the way assistant-ui drew it, to hand a chat panel back to\nupstream's design language inside a console that is otherwise cue's, or to\nwear Cursor's agent pane with `skin=\"cursor\"` rather than copying its palette.\n\nWhat an `aui` island re-points is this family's own vocabulary rather than the\nwhole token contract: `--cue-busy`, `--cue-info`, `--cue-selection`,\n`--cue-focus`, `--cue-scrim` and every derived `*-soft` / `*-border` stay on\nthe console's theme. Nothing in the elements group reads them, so the island\nis complete for what it is for — but a *cue* component placed inside one (a\n`Chip tone=\"busy\"`, a `Toast`) paints half in each palette, and the\nderivations cannot be made to follow at all: they are `color-mix()` values\nresolved on the theme block and inherited as answers. `elements.css` carries\nthe full accounting beside the values themselves.\n\n`elements.css` is a consumer import: `@import \"@cueplusplus/ui/elements.css\";`\nafter the main stylesheet. Without it a `skin=\"aui\"` island simply stays in\ncue's palette.",
9
9
  "props": [
10
10
  {
11
11
  "name": "skin",
12
- "type": "\"cue\" | \"aui\"",
12
+ "type": "\"cursor\" | \"cue\" | \"aui\"",
13
13
  "required": false,
14
14
  "defaultValue": "cue",
15
- "description": "Which value-set paints the subtree: `cue` (the default) or `aui`, which\ninstalls assistant-ui's own inks, grounds and hues.",
15
+ "description": "Which value-set paints the subtree: `cue` (the default); `aui`, which\ninstalls assistant-ui's own inks, grounds and hues; or `cursor`, Cursor's\nagent-pane values. Both halves of each follow `data-mode`.",
16
16
  "control": {
17
17
  "kind": "enum",
18
18
  "members": [
19
+ "cursor",
19
20
  "cue",
20
21
  "aui"
21
22
  ]
@@ -26,7 +27,7 @@
26
27
  "type": "\"upstream\" | \"cue-metrics\"",
27
28
  "required": false,
28
29
  "defaultValue": "cue-metrics",
29
- "description": "How much a supersede island re-points: `cue-metrics` (the default, colour\nonly) or `upstream`, which also pins assistant-ui's radii, literal type\nsizes and spacing so the density ladder stops at the island edge.",
30
+ "description": "How much a supersede island re-points: `cue-metrics` (the default, colour\nonly) or `upstream`, which also pins the skin's own geometry. For `aui`\nthat is its radii, literal type sizes and spacing, so the density ladder\nstops at the island edge; for `cursor`, its corners and faces.",
30
31
  "control": {
31
32
  "kind": "enum",
32
33
  "members": [
@@ -172,6 +172,7 @@
172
172
  "--cue-fg",
173
173
  "--cue-fg-muted",
174
174
  "--cue-font-mono",
175
+ "--cue-ground",
175
176
  "--cue-leading-ui",
176
177
  "--cue-paint-rim-width",
177
178
  "--cue-paint-scrim-filter",