@cueplusplus/ui 0.20.0 → 0.21.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.
- package/CHANGELOG.md +134 -0
- package/dist/brand/particle-logo.js +11 -3
- package/dist/chat/agent-pile.js +2 -2
- package/dist/chrome/_status-bar.js +1 -1
- package/dist/chrome/app-shell.js +1 -1
- package/dist/chrome/navigation-menu.js +3 -3
- package/dist/chrome/row.js +1 -1
- package/dist/chrome/scroll-area.js +1 -1
- package/dist/chrome/section-header.js +1 -1
- package/dist/chrome/segmented-control.js +1 -1
- package/dist/chrome/tabs.js +1 -1
- package/dist/chrome/toolbar.js +1 -1
- package/dist/chrome/tree.js +3 -3
- package/dist/date/calendar.js +3 -3
- package/dist/dmx/channel-matrix.js +1 -1
- package/dist/dmx/universe-grid.js +1 -1
- package/dist/elements/agent-handoff.js +3 -3
- package/dist/elements/agent-plan.js +1 -1
- package/dist/elements/agent-status.js +1 -1
- package/dist/elements/agent-surface.d.ts +9 -6
- package/dist/elements/agent-surface.js +3 -2
- package/dist/elements/approval-card.d.ts +29 -3
- package/dist/elements/approval-card.js +18 -8
- package/dist/elements/artifact-card.js +2 -2
- package/dist/elements/canvas-split.js +3 -3
- package/dist/elements/chart.js +1 -1
- package/dist/elements/chat-panel.js +2 -2
- package/dist/elements/checkpoint-history.js +1 -1
- package/dist/elements/code-diff.js +1 -1
- package/dist/elements/code-runner.js +1 -1
- package/dist/elements/composer.d.ts +8 -2
- package/dist/elements/composer.js +20 -12
- package/dist/elements/computer-use.js +2 -2
- package/dist/elements/confidence-marker.js +1 -1
- package/dist/elements/connection-state.js +2 -2
- package/dist/elements/context-breakdown.js +1 -1
- package/dist/elements/context-usage.js +1 -1
- package/dist/elements/conversation-search.js +2 -2
- package/dist/elements/cost-meter.js +1 -1
- package/dist/elements/data-table.js +1 -1
- package/dist/elements/diagram.js +1 -1
- package/dist/elements/draft-restore.js +2 -2
- package/dist/elements/edit-message.js +1 -1
- package/dist/elements/elements.css +134 -0
- package/dist/elements/elicitation-form.d.ts +8 -1
- package/dist/elements/elicitation-form.js +24 -16
- package/dist/elements/empty-state.js +3 -3
- package/dist/elements/error-state.js +2 -2
- package/dist/elements/event-feed.d.ts +88 -0
- package/dist/elements/event-feed.js +190 -0
- package/dist/elements/feedback-dialog.js +2 -2
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/flow-graph.js +2 -2
- package/dist/elements/generative.css +13 -0
- package/dist/elements/generative.d.ts +8 -6
- package/dist/elements/generative.js +4 -3
- package/dist/elements/image-generation.js +2 -2
- package/dist/elements/index.d.ts +7 -3
- package/dist/elements/index.js +5 -2
- package/dist/elements/inline-citation.js +1 -1
- package/dist/elements/job-progress.js +1 -1
- package/dist/elements/launcher-bubble.js +3 -3
- package/dist/elements/loading-state.js +1 -1
- package/dist/elements/map-answer.js +1 -1
- package/dist/elements/math-block.js +1 -1
- package/dist/elements/mcp-server-panel.js +3 -3
- package/dist/elements/memory-chips.js +1 -1
- package/dist/elements/message-attachment.js +3 -3
- package/dist/elements/message-branches.js +1 -1
- package/dist/elements/message-pair.js +2 -2
- package/dist/elements/message-queue.js +1 -1
- package/dist/elements/message-timing.js +1 -1
- package/dist/elements/mobile-composer.js +1 -1
- package/dist/elements/model-picker.js +1 -1
- package/dist/elements/number-ticker.js +1 -1
- package/dist/elements/onboarding.js +3 -3
- package/dist/elements/permission-grant.js +3 -3
- package/dist/elements/prompt-library.js +1 -1
- package/dist/elements/question-card.d.ts +50 -0
- package/dist/elements/question-card.js +117 -0
- package/dist/elements/queue-dock.js +1 -1
- package/dist/elements/quota-banner.js +1 -1
- package/dist/elements/quote-reply.js +3 -3
- package/dist/elements/read-aloud.js +2 -2
- package/dist/elements/reasoning-effort.js +2 -2
- package/dist/elements/reasoning-panel.js +3 -3
- package/dist/elements/recommendation-card.js +2 -2
- package/dist/elements/regenerate-menu.js +1 -1
- package/dist/elements/request-delivery.js +31 -0
- package/dist/elements/research-report.js +1 -1
- package/dist/elements/retrieval-chunks.js +3 -3
- package/dist/elements/revert-dock.js +1 -1
- package/dist/elements/reviewable-diff.js +4 -4
- package/dist/elements/schedule-card.js +2 -2
- package/dist/elements/score-breakdown.js +2 -2
- package/dist/elements/scroll-anchor.js +2 -2
- package/dist/elements/settings-panel.js +3 -3
- package/dist/elements/sources.js +1 -1
- package/dist/elements/spec-sheet.js +1 -1
- package/dist/elements/stopped-run.js +2 -2
- package/dist/elements/streaming-text.js +2 -2
- package/dist/elements/subagent-list.js +4 -4
- package/dist/elements/suggestions.js +1 -1
- package/dist/elements/surfaces.d.ts +6 -6
- package/dist/elements/surfaces.js +7 -7
- package/dist/elements/tail-status.js +1 -1
- package/dist/elements/terminal-block.js +1 -1
- package/dist/elements/thinking-indicator.js +1 -1
- package/dist/elements/timeline.js +1 -1
- package/dist/elements/todo-list.js +1 -1
- package/dist/elements/tool-call.d.ts +18 -2
- package/dist/elements/tool-call.js +20 -11
- package/dist/elements/tool-error.js +2 -2
- package/dist/elements/tool-group.d.ts +11 -3
- package/dist/elements/tool-group.js +62 -38
- package/dist/elements/tool-state.d.ts +13 -0
- package/dist/elements/tool-state.js +44 -0
- package/dist/elements/tool-timeline.js +2 -2
- package/dist/elements/trace-waterfall.js +2 -2
- package/dist/elements/unsupported-request-card.d.ts +31 -0
- package/dist/elements/unsupported-request-card.js +80 -0
- package/dist/elements/vocabulary.d.ts +50 -1
- package/dist/elements/vocabulary.js +50 -1
- package/dist/elements/voice-conversation.js +5 -5
- package/dist/elements/web-preview.js +1 -1
- package/dist/elements/web-search.js +2 -2
- package/dist/elements/work-collapse.js +1 -1
- package/dist/forms/file-upload.js +1 -1
- package/dist/forms/rating.js +1 -1
- package/dist/forms/select.js +1 -1
- package/dist/forms/switch.js +2 -2
- package/dist/forms/toggle.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/instruments/_ledger-disclosure.js +1 -1
- package/dist/instruments/data-tree.js +1 -1
- package/dist/instruments/meter.js +1 -1
- package/dist/instruments/seam-grid.js +1 -1
- package/dist/layout/_collapse.js +3 -3
- package/dist/layout/bento.js +1 -1
- package/dist/layout/breadcrumb.js +1 -1
- package/dist/layout/carousel.js +1 -1
- package/dist/layout/collapsible.js +1 -1
- package/dist/layout/index.d.ts +3 -1
- package/dist/layout/index.js +3 -1
- package/dist/layout/item.js +1 -1
- package/dist/layout/link.js +1 -1
- package/dist/layout/pagination.js +1 -1
- package/dist/layout/pane-seam.d.ts +75 -0
- package/dist/layout/pane-seam.js +127 -0
- package/dist/layout/resizable.d.ts +2 -1
- package/dist/layout/resizable.js +3 -2
- package/dist/layout/reveal.js +10 -4
- package/dist/layout/sidebar.js +3 -3
- package/dist/layout/split-pane.d.ts +74 -0
- package/dist/layout/split-pane.js +135 -0
- package/dist/layout/stepper.js +1 -1
- package/dist/lib/auto-grow.js +42 -0
- package/dist/overlays/_surface.d.ts +4 -4
- package/dist/overlays/_surface.js +4 -4
- package/dist/overlays/drawer.js +1 -1
- package/dist/overlays/menubar.js +1 -1
- package/dist/overlays/sheet.js +1 -1
- package/dist/overlays/toast.js +1 -1
- package/dist/primitives/button.js +1 -1
- package/dist/primitives/chip.d.ts +1 -1
- package/dist/styles.css +12 -0
- package/dist/system/agent-skin.d.ts +11 -5
- package/dist/system/frame-clock.js +1 -0
- package/dist/system/theme-registry.d.ts +1 -1
- package/dist/system/theme-registry.js +1 -1
- package/manifest/components/agent-surface.json +5 -4
- package/manifest/components/approval-card.json +45 -7
- package/manifest/components/composer-input.json +16 -2
- package/manifest/components/elicitation-form.json +16 -1
- package/manifest/components/event-feed.json +171 -0
- package/manifest/components/event-row.json +116 -0
- package/manifest/components/generative-ui.json +4 -3
- package/manifest/components/pane-seam.json +171 -0
- package/manifest/components/question-card.json +157 -0
- package/manifest/components/resizable.json +1 -1
- package/manifest/components/reveal.json +3 -1
- package/manifest/components/split-pane.json +224 -0
- package/manifest/components/tool-call.json +46 -2
- package/manifest/components/tool-group.json +15 -3
- package/manifest/components/unsupported-request-card.json +117 -0
- package/manifest/fixtures.json +16 -0
- package/manifest/manifest.json +91 -13
- package/manifest/tokens.json +9 -2
- package/package.json +8 -6
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/layout/split-pane.d.ts
|
|
3
|
+
/** Whether the panes sit side by side or stack into a drill-in. */
|
|
4
|
+
type SplitPaneLayout = "split" | "stacked";
|
|
5
|
+
interface SplitPaneProps extends Omit<React.ComponentPropsWithoutRef<"div">, "children"> {
|
|
6
|
+
/** The master: a list of rows, a tree, a feed. It owns its own keyboard. */
|
|
7
|
+
list: React.ReactNode;
|
|
8
|
+
/** The detail for whatever the list has selected. */
|
|
9
|
+
detail: React.ReactNode;
|
|
10
|
+
/**
|
|
11
|
+
* What the list is, in a word or two: `events`. It names the seam (`Resize
|
|
12
|
+
* the events`), the Back button (`Back to events`) and the fold (`Hide
|
|
13
|
+
* events`), so each control says what it acts on.
|
|
14
|
+
*/
|
|
15
|
+
listLabel: string;
|
|
16
|
+
/** The list's width in pixels, when the caller holds it. */
|
|
17
|
+
listSize?: number;
|
|
18
|
+
/** The list's starting width when nothing controls it. Defaults to 320. */
|
|
19
|
+
defaultListSize?: number;
|
|
20
|
+
/** Called with every new list width, clamped to `minListSize` and `maxListSize`. */
|
|
21
|
+
onListSizeChange?: (size: number) => void;
|
|
22
|
+
/** The narrowest the list may be dragged, in pixels. Defaults to 200. */
|
|
23
|
+
minListSize?: number;
|
|
24
|
+
/** The widest the list may be dragged, in pixels. Defaults to 480. */
|
|
25
|
+
maxListSize?: number;
|
|
26
|
+
/**
|
|
27
|
+
* The frame width, in pixels, below which the panes stack. Unset, they stack
|
|
28
|
+
* once the detail would be narrower than twice the list.
|
|
29
|
+
*/
|
|
30
|
+
stackBelow?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Which pane shows when stacked: the detail when `true`, the list when not.
|
|
33
|
+
* Side by side, both always show.
|
|
34
|
+
*/
|
|
35
|
+
detailOpen?: boolean;
|
|
36
|
+
/** Called by the stacked detail's Back button and by Escape inside it. */
|
|
37
|
+
onBack?: () => void;
|
|
38
|
+
/** Whether the list can fold to a thin tab while side by side. */
|
|
39
|
+
collapsible?: boolean;
|
|
40
|
+
/** Whether the list is folded, when the caller holds it. */
|
|
41
|
+
collapsed?: boolean;
|
|
42
|
+
/** Whether the list starts folded when nothing controls it. */
|
|
43
|
+
defaultCollapsed?: boolean;
|
|
44
|
+
/** Called when the list folds (`true`) or opens (`false`). */
|
|
45
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A list beside its detail, which stacks into a drill-in when narrow (#102).
|
|
49
|
+
*
|
|
50
|
+
* The master-detail opinion on top of {@link PaneSeam}. Side by side while
|
|
51
|
+
* the detail would be at least twice the list, measured on the frame rather
|
|
52
|
+
* than the window, so it works in any column. Below that it stacks the way a
|
|
53
|
+
* phone does: the list, or the detail with a Back button, and Escape inside the
|
|
54
|
+
* detail goes back too. `detailOpen` says which shows; selection and the list's
|
|
55
|
+
* own keyboard stay with the list.
|
|
56
|
+
*
|
|
57
|
+
* The frame measures itself with a `ResizeObserver` and draws side by side
|
|
58
|
+
* until it has, so a server render and a browser without one get the split.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* const [selected, setSelected] = React.useState<string | null>(null);
|
|
62
|
+
*
|
|
63
|
+
* <SplitPane
|
|
64
|
+
* listLabel="events"
|
|
65
|
+
* list={<EventFeed events={events} selectedId={selected} onSelect={setSelected} />}
|
|
66
|
+
* detail={selected ? <EventDetail id={selected} /> : <EmptyState title="Pick an event" />}
|
|
67
|
+
* detailOpen={selected !== null}
|
|
68
|
+
* onBack={() => setSelected(null)}
|
|
69
|
+
* collapsible
|
|
70
|
+
* />
|
|
71
|
+
*/
|
|
72
|
+
declare const SplitPane: React.ForwardRefExoticComponent<SplitPaneProps & React.RefAttributes<HTMLDivElement>>;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { SplitPane, SplitPaneLayout, SplitPaneProps };
|
|
@@ -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 };
|
package/dist/layout/stepper.js
CHANGED
|
@@ -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-
|
|
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 };
|
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
package/dist/overlays/drawer.js
CHANGED
|
@@ -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-
|
|
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",
|
package/dist/overlays/menubar.js
CHANGED
|
@@ -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-
|
|
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
|
}),
|
package/dist/overlays/sheet.js
CHANGED
|
@@ -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-
|
|
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",
|
package/dist/overlays/toast.js
CHANGED
|
@@ -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-
|
|
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-
|
|
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?: "
|
|
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
|
@@ -115,6 +115,18 @@
|
|
|
115
115
|
clip-path: none;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/* `Reveal`'s server render (`layout/reveal.tsx`): pending content is hidden before hydration, so
|
|
119
|
+
* it is never shown visible and then hidden by the client. Only where scripting is on — a reader
|
|
120
|
+
* with no JavaScript sees it — and where motion is welcome, as the client's own entrance is. A
|
|
121
|
+
* staggered block stays visible and hides the items it marks, as the client does. */
|
|
122
|
+
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
|
|
123
|
+
.cue-reveal-pending:not([data-reveal-stagger]),
|
|
124
|
+
.cue-reveal-pending[data-reveal-stagger] [data-reveal-item] {
|
|
125
|
+
opacity: 0;
|
|
126
|
+
transform: translateY(var(--cue-reveal-distance, 28px));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
118
130
|
@media (forced-colors: active) {
|
|
119
131
|
.cue-cut,
|
|
120
132
|
.cue-cut-secondary {
|
|
@@ -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` —
|
|
21
|
-
* 12 / 11 px type
|
|
22
|
-
*
|
|
23
|
-
* element exactly as its authors
|
|
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
|
|
@@ -32,6 +32,7 @@ function useFrameClock({ fps = 30, durationInFrames, loop = true, playing = true
|
|
|
32
32
|
last = now;
|
|
33
33
|
const total = Math.floor(elapsed.current * fps / 1e3);
|
|
34
34
|
setFrame(loop ? total % durationInFrames : Math.min(total, durationInFrames - 1));
|
|
35
|
+
if (!loop && total >= durationInFrames - 1) return;
|
|
35
36
|
handle = requestAnimationFrame(step);
|
|
36
37
|
});
|
|
37
38
|
const onVisibility = () => {
|
|
@@ -41,7 +41,7 @@ declare function useThemes(): readonly ThemeManifest[];
|
|
|
41
41
|
*/
|
|
42
42
|
declare function useDensities(): readonly DensityEntry[];
|
|
43
43
|
/**
|
|
44
|
-
* The font pairings, by the same rule: the base
|
|
44
|
+
* The font pairings, by the same rule: the base fourteen, then the active theme's.
|
|
45
45
|
*
|
|
46
46
|
* @throws If called outside a `<ThemeProvider>`.
|
|
47
47
|
* @example
|
|
@@ -68,7 +68,7 @@ function useDensities() {
|
|
|
68
68
|
return useRegistry("useDensities", (registry) => registry.densities);
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
|
-
* The font pairings, by the same rule: the base
|
|
71
|
+
* The font pairings, by the same rule: the base fourteen, then the active theme's.
|
|
72
72
|
*
|
|
73
73
|
* @throws If called outside a `<ThemeProvider>`.
|
|
74
74
|
* @example
|
|
@@ -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,
|
|
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)
|
|
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
|
|
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": [
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"type": "(() => void)",
|
|
72
72
|
"required": false,
|
|
73
73
|
"defaultValue": null,
|
|
74
|
-
"description": "Called when the user clicks Always allow.",
|
|
74
|
+
"description": "Called when the user clicks Always allow. The button is drawn only when this is\nset, so a card never offers a standing grant nothing can honour.",
|
|
75
75
|
"control": {
|
|
76
76
|
"kind": "callback",
|
|
77
77
|
"fixture": "callback.log"
|
|
@@ -87,10 +87,46 @@
|
|
|
87
87
|
"kind": "callback",
|
|
88
88
|
"fixture": "callback.log"
|
|
89
89
|
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "options",
|
|
93
|
+
"type": "readonly ApprovalOption[]",
|
|
94
|
+
"required": false,
|
|
95
|
+
"defaultValue": null,
|
|
96
|
+
"description": "The decisions the agent offered, one button each and in this order. Replaces the\ndefault Deny, Always allow and Allow once.",
|
|
97
|
+
"control": {
|
|
98
|
+
"kind": "array"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"name": "onDecide",
|
|
103
|
+
"type": "((id: string) => void)",
|
|
104
|
+
"required": false,
|
|
105
|
+
"defaultValue": null,
|
|
106
|
+
"description": "Called with an option's `id` when the user picks it.",
|
|
107
|
+
"control": {
|
|
108
|
+
"kind": "callback",
|
|
109
|
+
"fixture": "callback.log"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "delivery",
|
|
114
|
+
"type": "\"failed\" | \"sending\" | \"sent\"",
|
|
115
|
+
"required": false,
|
|
116
|
+
"defaultValue": null,
|
|
117
|
+
"description": "Where the answer has got to: `sending`, `sent` or `failed`. While it is on its way\nand once it has arrived, the card offers nothing to press. `failed` offers the\ndecisions again. An outcome `state` wins over it.",
|
|
118
|
+
"control": {
|
|
119
|
+
"kind": "enum",
|
|
120
|
+
"members": [
|
|
121
|
+
"failed",
|
|
122
|
+
"sending",
|
|
123
|
+
"sent"
|
|
124
|
+
]
|
|
125
|
+
}
|
|
90
126
|
}
|
|
91
127
|
],
|
|
92
128
|
"typeReferences": [
|
|
93
|
-
"Omit<\n ComponentProps<\"div\">,\n | \"children\"\n | \"state\"\n | \"command\"\n | \"title\"\n | \"subtitle\"\n | \"onAllowOnce\"\n | \"onAlwaysAllow\"\n | \"onDeny\"\n> & {\n /** Lifecycle of the card: request with actions, running spinner, done check, or denied. */\n state: ApprovalState;\n /** Command shown in the mono field the agent wants to run. */\n command: string;\n /** Headline naming the action that needs approval. */\n title: string;\n /** Supporting line under the title explaining the request. */\n subtitle: string;\n /** Called when the user clicks Allow once. */\n onAllowOnce?: () => void;\n
|
|
129
|
+
"Omit<\n ComponentProps<\"div\">,\n | \"children\"\n | \"state\"\n | \"command\"\n | \"title\"\n | \"subtitle\"\n | \"onAllowOnce\"\n | \"onAlwaysAllow\"\n | \"onDeny\"\n | \"options\"\n | \"onDecide\"\n | \"delivery\"\n> & {\n /** Lifecycle of the card: request with actions, running spinner, done check, or denied. */\n state: ApprovalState;\n /** Command shown in the mono field the agent wants to run. */\n command: string;\n /** Headline naming the action that needs approval. */\n title: string;\n /** Supporting line under the title explaining the request. */\n subtitle: string;\n /** Called when the user clicks Allow once. */\n onAllowOnce?: () => void;\n /**\n * Called when the user clicks Always allow. The button is drawn only when this is\n * set, so a card never offers a standing grant nothing can honour.\n */\n onAlwaysAllow?: () => void;\n /** Called when the user clicks Deny. */\n onDeny?: () => void;\n /**\n * The decisions the agent offered, one button each and in this order. Replaces the\n * default Deny, Always allow and Allow once.\n */\n options?: readonly ApprovalOption[];\n /** Called with an option's `id` when the user picks it. */\n onDecide?: (id: string) => void;\n /**\n * Where the answer has got to: `sending`, `sent` or `failed`. While it is on its way\n * and once it has arrived, the card offers nothing to press. `failed` offers the\n * decisions again. An outcome `state` wins over it.\n */\n delivery?: RequestDelivery;\n}"
|
|
94
130
|
],
|
|
95
131
|
"subcomponents": [],
|
|
96
132
|
"parts": [],
|
|
@@ -122,14 +158,16 @@
|
|
|
122
158
|
"jsonUrl": "/r/components/approval-card.json",
|
|
123
159
|
"whenToUse": [
|
|
124
160
|
"A command with side effects that must not run until someone says so.",
|
|
125
|
-
"Carrying that request through its whole lifecycle, since one card covers request, running, done and denied."
|
|
161
|
+
"Carrying that request through its whole lifecycle, since one card covers request, running, done and denied.",
|
|
162
|
+
"Drawing exactly the decisions the agent offered. Pass `options` with `onDecide`, and only those buttons appear."
|
|
126
163
|
],
|
|
127
164
|
"whenNotToUse": [
|
|
128
165
|
"A decision with no command behind it. `command` is required and is shown in a mono field.",
|
|
129
166
|
"Reporting a command that failed. The four states are request, running, done and denied; a failure needs somewhere else to go."
|
|
130
167
|
],
|
|
131
168
|
"commonMistakes": [
|
|
132
|
-
"Leaving `state=\"request\"` after the user has answered
|
|
169
|
+
"Leaving `state=\"request\"` after the user has answered with no `delivery`, so Allow and Deny stay pressable. Set `delivery=\"sending\"` as the answer leaves.",
|
|
170
|
+
"Passing a no-op `onAlwaysAllow`. Always allow is drawn only when the handler is set, so set it only when a standing grant can be honoured.",
|
|
133
171
|
"Folding the explanation into `title`. `title` names the action that needs approval; `subtitle` is the supporting line that explains it."
|
|
134
172
|
],
|
|
135
173
|
"specimens": [
|
|
@@ -139,9 +177,9 @@
|
|
|
139
177
|
"components": [
|
|
140
178
|
"ApprovalCard"
|
|
141
179
|
],
|
|
142
|
-
"code": "<Specimens>\n {APPROVALS.map((state) => (\n <Specimen key={state} label={state} wide>\n <ApprovalCard\n state={approvals[state] ?? state}\n command=\"cue unpatch --universe 4 --all\"\n title=\"Unpatch universe 4\"\n subtitle=\"48 channels stop responding until they are patched again.\"\n onAllowOnce={settle(state, \"running\")}\n onAlwaysAllow={settle(state, \"running\")}\n onDeny={settle(state, \"denied\")}\n />\n </Specimen>\n ))}\n</Specimens>",
|
|
143
|
-
"note": "Human in the loop for one action: the command in mono exactly as it will run, and
|
|
144
|
-
"interaction": "Allow once and Always allow are separate answers, never one control with a modifier. Denying settles the card rather than hiding it."
|
|
180
|
+
"code": "<Specimens>\n {APPROVALS.map((state) => (\n <Specimen key={state} label={state} wide>\n <ApprovalCard\n state={approvals[state] ?? state}\n command=\"cue unpatch --universe 4 --all\"\n title=\"Unpatch universe 4\"\n subtitle=\"48 channels stop responding until they are patched again.\"\n onAllowOnce={settle(state, \"running\")}\n onAlwaysAllow={settle(state, \"running\")}\n onDeny={settle(state, \"denied\")}\n />\n </Specimen>\n ))}\n <Specimen label=\"the agent's own decisions\" wide>\n <ApprovalCard\n state=\"request\"\n command=\"cue patch --universe 3 --fixtures 6\"\n title=\"Patch universe 3\"\n subtitle=\"The agent offered two answers, so the card draws two.\"\n options={[\n { id: \"reject\", label: \"Reject\" },\n { id: \"approve\", label: \"Approve\", tone: \"primary\" },\n ]}\n onDecide={() => {}}\n />\n </Specimen>\n <Specimen label=\"sending\" wide>\n <ApprovalCard\n state=\"request\"\n delivery=\"sending\"\n command=\"cue unpatch --universe 4 --all\"\n title=\"Unpatch universe 4\"\n subtitle=\"48 channels stop responding until they are patched again.\"\n />\n </Specimen>\n <Specimen label=\"not sent\" wide>\n <ApprovalCard\n state=\"request\"\n delivery=\"failed\"\n command=\"cue unpatch --universe 4 --all\"\n title=\"Unpatch universe 4\"\n subtitle=\"48 channels stop responding until they are patched again.\"\n onAllowOnce={() => {}}\n onDeny={() => {}}\n />\n </Specimen>\n</Specimens>",
|
|
181
|
+
"note": "Human in the loop for one action: the command in mono exactly as it will run, and the answers the agent offered. The first four specimens are the four states of the union, and the first one is live: answer it and it moves. Then a card drawing the agent's own two decisions rather than the default three, and two deliveries: an answer on its way, which offers nothing to press, and one that did not arrive, which offers the answers again.",
|
|
182
|
+
"interaction": "Allow once and Always allow are separate answers, never one control with a modifier, and Always allow is drawn only when something can honour it. Denying settles the card rather than hiding it."
|
|
145
183
|
}
|
|
146
184
|
]
|
|
147
185
|
}
|