@ctrl-ui/react 0.9.5 → 0.9.6

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 (64) hide show
  1. package/README.md +2 -2
  2. package/dist/action-bar.js +16 -8
  3. package/dist/activity.d.ts +1 -1
  4. package/dist/activity.js +3 -1
  5. package/dist/audio-visualizer.js +1 -1
  6. package/dist/blocks/agent-run-clock.d.ts +5 -0
  7. package/dist/blocks/agent-run-clock.js +52 -0
  8. package/dist/blocks/agent-run-sidebar.d.ts +13 -0
  9. package/dist/blocks/agent-run-sidebar.js +100 -0
  10. package/dist/blocks/agent-run-status.d.ts +32 -0
  11. package/dist/blocks/agent-run-status.js +27 -0
  12. package/dist/blocks/agent-run.d.ts +80 -0
  13. package/dist/blocks/agent-run.js +254 -0
  14. package/dist/blocks/coding-agent.d.ts +1 -1
  15. package/dist/blocks/design-canvas-data.d.ts +1 -1
  16. package/dist/blocks/design-canvas.d.ts +2 -1
  17. package/dist/blocks/design-canvas.js +15 -8
  18. package/dist/chat-message.js +33 -39
  19. package/dist/knob-contracts/infinite-canvas-knobs.d.ts +1 -1
  20. package/dist/knob-contracts/infinite-canvas-knobs.js +1 -7
  21. package/dist/knob-contracts/swap-knobs.d.ts +4 -0
  22. package/dist/knob-contracts/swap-knobs.js +9 -0
  23. package/dist/knob-contracts/timeline-knobs.d.ts +1 -1
  24. package/dist/knob-contracts/timeline-knobs.js +1 -0
  25. package/dist/knob-contracts/tool-approval-knobs.d.ts +4 -0
  26. package/dist/knob-contracts/tool-approval-knobs.js +32 -0
  27. package/dist/lib/infinite-canvas-camera-store.d.ts +65 -0
  28. package/dist/lib/infinite-canvas-camera-store.js +233 -0
  29. package/dist/lib/infinite-canvas-camera.d.ts +33 -0
  30. package/dist/lib/infinite-canvas-camera.js +98 -0
  31. package/dist/lib/infinite-canvas-navigation.d.ts +19 -0
  32. package/dist/lib/infinite-canvas-navigation.js +395 -0
  33. package/dist/markdown-editor/bubble-menu.d.ts +6 -0
  34. package/dist/markdown-editor/bubble-menu.js +45 -0
  35. package/dist/markdown-editor/toolbar.d.ts +17 -1
  36. package/dist/markdown-editor/toolbar.js +103 -81
  37. package/dist/styles/index.css +10 -8
  38. package/dist/styles/recipes/activity.css +8 -0
  39. package/dist/styles/recipes/button-group.css +12 -6
  40. package/dist/styles/recipes/button.css +12 -2
  41. package/dist/styles/recipes/chat-message-edit.css +6 -21
  42. package/dist/styles/recipes/infinite-canvas.css +17 -57
  43. package/dist/styles/recipes/markdown-editor.css +12 -0
  44. package/dist/styles/recipes/swap.css +143 -0
  45. package/dist/styles/recipes/timeline.css +11 -0
  46. package/dist/styles/recipes/tool-approval.css +267 -0
  47. package/dist/styles/recipes/track-highlight.css +19 -6
  48. package/dist/styles/theme.css +6 -5
  49. package/dist/styles/type-scale.css +1 -1
  50. package/dist/tool-approval.d.ts +49 -0
  51. package/dist/tool-approval.js +260 -0
  52. package/dist/ui/code.js +13 -3
  53. package/dist/ui/command.js +1 -0
  54. package/dist/ui/infinite-canvas.d.ts +19 -22
  55. package/dist/ui/infinite-canvas.js +264 -325
  56. package/dist/ui/rich-tooltip.d.ts +2 -1
  57. package/dist/ui/rich-tooltip.js +32 -29
  58. package/dist/ui/swap.d.ts +14 -0
  59. package/dist/ui/swap.js +69 -0
  60. package/dist/ui/timeline.d.ts +1 -1
  61. package/dist/ui/timeline.js +1 -1
  62. package/dist/user-ask.js +1 -1
  63. package/lint/no-raw-type-classes.grit +2 -2
  64. package/package.json +2 -2
@@ -0,0 +1,260 @@
1
+ "use client";
2
+ import { cn } from "./lib/cn.js";
3
+ import { Button } from "./ui/button.js";
4
+ import { LiveStatus } from "./ui/live-status.js";
5
+ import { Badge } from "./ui/badge.js";
6
+ import { Kbd } from "./ui/kbd.js";
7
+ import { createContext, useContext, useEffect, useId, useRef } from "react";
8
+ import { useRender } from "@base-ui/react/use-render";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
+ import { Check, ShieldAlert, X } from "lucide-react";
11
+ //#region src/tool-approval.tsx
12
+ const ToolApprovalContext = createContext(null);
13
+ function useToolApprovalContext() {
14
+ const context = useContext(ToolApprovalContext);
15
+ if (!context) throw new Error("ToolApproval compound components must be rendered inside <ToolApproval>.");
16
+ return context;
17
+ }
18
+ const decisionAnnouncements = {
19
+ pending: "",
20
+ approved: "Action approved",
21
+ denied: "Action denied"
22
+ };
23
+ function ToolApproval({ state = "pending", risk = "medium", onApprove, onDeny, autoFocus = false, className, children, onKeyDown, ...props }) {
24
+ const titleId = useId();
25
+ const rootRef = useRef(null);
26
+ const isPending = state === "pending";
27
+ const approve = () => {
28
+ if (isPending) onApprove?.();
29
+ };
30
+ const deny = () => {
31
+ if (isPending) onDeny?.();
32
+ };
33
+ useEffect(() => {
34
+ if (autoFocus) rootRef.current?.focus();
35
+ }, [autoFocus]);
36
+ function handleKeyDown(event) {
37
+ onKeyDown?.(event);
38
+ if (event.defaultPrevented || !isPending || event.nativeEvent.isComposing) return;
39
+ if (event.key === "Escape") {
40
+ event.preventDefault();
41
+ deny();
42
+ return;
43
+ }
44
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
45
+ event.preventDefault();
46
+ approve();
47
+ }
48
+ }
49
+ return /* @__PURE__ */ jsx(ToolApprovalContext.Provider, {
50
+ value: {
51
+ state,
52
+ risk,
53
+ titleId,
54
+ approve,
55
+ deny
56
+ },
57
+ children: /* @__PURE__ */ jsxs("section", {
58
+ ref: rootRef,
59
+ "data-control-ui": "tool-approval",
60
+ "data-control-family": "tool-approval",
61
+ "data-slot": "root",
62
+ "data-state": state,
63
+ "data-risk": risk,
64
+ "aria-labelledby": titleId,
65
+ tabIndex: -1,
66
+ onKeyDown: handleKeyDown,
67
+ className: cn("relative w-full min-w-0", className),
68
+ ...props,
69
+ children: [children, /* @__PURE__ */ jsx(LiveStatus, { message: decisionAnnouncements[state] })]
70
+ })
71
+ });
72
+ }
73
+ function ToolApprovalBody({ className, ...props }) {
74
+ return /* @__PURE__ */ jsx("div", {
75
+ "data-control-ui": "tool-approval",
76
+ "data-control-family": "tool-approval",
77
+ "data-slot": "body",
78
+ className: cn("min-w-0", className),
79
+ ...props
80
+ });
81
+ }
82
+ function ToolApprovalHeader({ className, ...props }) {
83
+ return /* @__PURE__ */ jsx("div", {
84
+ "data-control-ui": "tool-approval",
85
+ "data-control-family": "tool-approval",
86
+ "data-slot": "header",
87
+ className: cn("flex items-start gap-2", className),
88
+ ...props
89
+ });
90
+ }
91
+ function ToolApprovalIcon({ className, children, ...props }) {
92
+ return /* @__PURE__ */ jsx("span", {
93
+ "data-control-ui": "tool-approval",
94
+ "data-control-family": "tool-approval",
95
+ "data-slot": "icon",
96
+ "aria-hidden": "true",
97
+ className: cn("inline-flex size-5 shrink-0 items-center justify-center [&_svg]:size-4", className),
98
+ ...props,
99
+ children: children ?? /* @__PURE__ */ jsx(ShieldAlert, {})
100
+ });
101
+ }
102
+ function ToolApprovalTitle({ render, className, ...props }) {
103
+ const { titleId } = useToolApprovalContext();
104
+ return useRender({
105
+ defaultTagName: "div",
106
+ render,
107
+ props: {
108
+ ...props,
109
+ id: titleId,
110
+ "data-control-ui": "tool-approval",
111
+ "data-control-family": "tool-approval",
112
+ "data-slot": "title",
113
+ className: cn("min-w-0 flex-1 font-medium", className)
114
+ }
115
+ });
116
+ }
117
+ const riskColors = {
118
+ low: "neutral",
119
+ medium: "yellow",
120
+ high: "red"
121
+ };
122
+ const riskLabels = {
123
+ low: "Low risk",
124
+ medium: "Medium risk",
125
+ high: "High risk"
126
+ };
127
+ function ToolApprovalRisk({ children, ...props }) {
128
+ const { state, risk } = useToolApprovalContext();
129
+ if (state !== "pending") return null;
130
+ return /* @__PURE__ */ jsx(Badge, {
131
+ "data-control-ui": "tool-approval",
132
+ "data-slot": "risk",
133
+ size: "sm",
134
+ color: riskColors[risk],
135
+ ...props,
136
+ children: children ?? riskLabels[risk]
137
+ });
138
+ }
139
+ const decisionLabels = {
140
+ approved: "Approved",
141
+ denied: "Denied"
142
+ };
143
+ function ToolApprovalStatus({ className, children, ...props }) {
144
+ const { state } = useToolApprovalContext();
145
+ if (state === "pending") return null;
146
+ const StatusIcon = state === "approved" ? Check : X;
147
+ return /* @__PURE__ */ jsxs("div", {
148
+ "data-control-ui": "tool-approval",
149
+ "data-control-family": "tool-approval",
150
+ "data-slot": "status",
151
+ className: cn("shrink-0", className),
152
+ ...props,
153
+ children: [/* @__PURE__ */ jsx("span", {
154
+ "data-control-ui": "tool-approval",
155
+ "data-control-family": "tool-approval",
156
+ "data-slot": "status-icon",
157
+ "aria-hidden": "true",
158
+ children: /* @__PURE__ */ jsx(StatusIcon, { className: "size-3.5" })
159
+ }), children ?? decisionLabels[state]]
160
+ });
161
+ }
162
+ function ToolApprovalDescription({ className, ...props }) {
163
+ const { state } = useToolApprovalContext();
164
+ if (state !== "pending") return null;
165
+ return /* @__PURE__ */ jsx("p", {
166
+ "data-control-ui": "tool-approval",
167
+ "data-control-family": "tool-approval",
168
+ "data-slot": "description",
169
+ className,
170
+ ...props
171
+ });
172
+ }
173
+ function ToolApprovalPreview({ className, ...props }) {
174
+ const { state } = useToolApprovalContext();
175
+ if (state !== "pending") return null;
176
+ return /* @__PURE__ */ jsx("dl", {
177
+ "data-control-ui": "tool-approval",
178
+ "data-control-family": "tool-approval",
179
+ "data-slot": "preview",
180
+ className: cn("min-w-0", className),
181
+ ...props
182
+ });
183
+ }
184
+ function ToolApprovalField({ label, children, className, ...props }) {
185
+ return /* @__PURE__ */ jsxs("div", {
186
+ "data-control-ui": "tool-approval",
187
+ "data-control-family": "tool-approval",
188
+ "data-slot": "field",
189
+ className,
190
+ ...props,
191
+ children: [/* @__PURE__ */ jsx("dt", {
192
+ "data-control-ui": "tool-approval",
193
+ "data-control-family": "tool-approval",
194
+ "data-slot": "field-label",
195
+ children: label
196
+ }), /* @__PURE__ */ jsx("dd", {
197
+ "data-control-ui": "tool-approval",
198
+ "data-control-family": "tool-approval",
199
+ "data-slot": "field-value",
200
+ className: "min-w-0 break-words",
201
+ children
202
+ })]
203
+ });
204
+ }
205
+ function ToolApprovalFooter({ className, ...props }) {
206
+ const { state } = useToolApprovalContext();
207
+ if (state !== "pending") return null;
208
+ return /* @__PURE__ */ jsx("div", {
209
+ "data-control-ui": "tool-approval",
210
+ "data-control-family": "tool-approval",
211
+ "data-slot": "footer",
212
+ className,
213
+ ...props
214
+ });
215
+ }
216
+ function ToolApprovalDeny({ children, onClick, ...props }) {
217
+ const { deny } = useToolApprovalContext();
218
+ function handleClick(event) {
219
+ onClick?.(event);
220
+ if (!event.defaultPrevented) deny();
221
+ }
222
+ return /* @__PURE__ */ jsx(Button, {
223
+ "data-control-ui": "tool-approval",
224
+ "data-slot": "deny",
225
+ type: "button",
226
+ variant: "quiet",
227
+ size: "xs",
228
+ "aria-keyshortcuts": "Escape",
229
+ onClick: handleClick,
230
+ ...props,
231
+ children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: ["Deny", /* @__PURE__ */ jsx(Kbd, {
232
+ "aria-hidden": "true",
233
+ children: "Esc"
234
+ })] })
235
+ });
236
+ }
237
+ function ToolApprovalApprove({ children, onClick, ...props }) {
238
+ const { approve } = useToolApprovalContext();
239
+ function handleClick(event) {
240
+ onClick?.(event);
241
+ if (!event.defaultPrevented) approve();
242
+ }
243
+ return /* @__PURE__ */ jsx(Button, {
244
+ "data-control-ui": "tool-approval",
245
+ "data-slot": "approve",
246
+ type: "button",
247
+ variant: "solid",
248
+ tone: "primary",
249
+ size: "xs",
250
+ "aria-keyshortcuts": "Meta+Enter Control+Enter",
251
+ onClick: handleClick,
252
+ ...props,
253
+ children: children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: ["Approve", /* @__PURE__ */ jsx(Kbd, {
254
+ "aria-hidden": "true",
255
+ children: "⌘⏎"
256
+ })] })
257
+ });
258
+ }
259
+ //#endregion
260
+ export { ToolApproval, ToolApprovalApprove, ToolApprovalBody, ToolApprovalDeny, ToolApprovalDescription, ToolApprovalField, ToolApprovalFooter, ToolApprovalHeader, ToolApprovalIcon, ToolApprovalPreview, ToolApprovalRisk, ToolApprovalStatus, ToolApprovalTitle };
package/dist/ui/code.js CHANGED
@@ -3,6 +3,7 @@ import { useCopyToClipboard } from "../hooks/use-copy-to-clipboard.js";
3
3
  import { cn } from "../lib/cn.js";
4
4
  import { Button } from "./button.js";
5
5
  import { LiveStatus } from "./live-status.js";
6
+ import { Swap, SwapItem } from "./swap.js";
6
7
  import { ScrollArea } from "./scroll-area.js";
7
8
  import { highlightToTokens } from "../lib/code-tokens.js";
8
9
  import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
@@ -99,9 +100,18 @@ function CodeCopy({ value, copiedLabel, copiedAriaLabel = "Copied to clipboard",
99
100
  className,
100
101
  ...props,
101
102
  onClick: handleCopy,
102
- children: status === "copied" ? copied : children ?? /* @__PURE__ */ jsx(CopyIcon, {
103
- "aria-hidden": "true",
104
- className: "size-3.5"
103
+ children: /* @__PURE__ */ jsxs(Swap, {
104
+ value: status === "copied" ? "copied" : "idle",
105
+ children: [/* @__PURE__ */ jsx(SwapItem, {
106
+ value: "idle",
107
+ children: children ?? /* @__PURE__ */ jsx(CopyIcon, {
108
+ "aria-hidden": "true",
109
+ className: "size-3.5"
110
+ })
111
+ }), /* @__PURE__ */ jsx(SwapItem, {
112
+ value: "copied",
113
+ children: copied
114
+ })]
105
115
  })
106
116
  });
107
117
  if (!isIconOnly) return /* @__PURE__ */ jsxs(Fragment$1, { children: [button, liveStatus] });
@@ -61,6 +61,7 @@ function CommandInput({ className, ...props }) {
61
61
  "data-slot": "input-wrapper",
62
62
  "data-control-family": "popup",
63
63
  "data-control": "true",
64
+ "data-focus-ring": "none",
64
65
  "data-size": "lg",
65
66
  className: "flex items-center",
66
67
  children: [/* @__PURE__ */ jsxs("svg", {
@@ -1,31 +1,24 @@
1
+ import { Button } from "./button.js";
1
2
  import { InfiniteCanvasKnobStyle } from "../knob-contracts/infinite-canvas-knobs.js";
3
+ import { InfiniteCanvasBounds, InfiniteCanvasPoint, InfiniteCanvasTransform } from "../lib/infinite-canvas-camera.js";
4
+ import { InfiniteCanvasCamera, InfiniteCanvasFitOptions, InfiniteCanvasMoveDetails, InfiniteCanvasMoveOptions, InfiniteCanvasMoveReason, InfiniteCanvasRevealOptions, InfiniteCanvasTransformUpdate, InfiniteCanvasZoomOptions } from "../lib/infinite-canvas-camera-store.js";
5
+ import { InfiniteCanvasPanOnDrag } from "../lib/infinite-canvas-navigation.js";
2
6
  import { CSSProperties, ComponentProps } from "react";
3
7
  //#region src/ui/infinite-canvas.d.ts
4
- export type InfiniteCanvasTransform = {
5
- x: number;
6
- y: number;
7
- scale: number;
8
- };
9
- export type InfiniteCanvasMoveReason = "pointer" | "wheel" | "keyboard" | "control";
10
- export type InfiniteCanvasMoveDetails = {
11
- reason: InfiniteCanvasMoveReason;
12
- };
13
- export type InfiniteCanvasProps = Omit<ComponentProps<"section">, "onChange" | "onWheel" | "ref" | "style"> & {
8
+ export type InfiniteCanvasProps = Omit<ComponentProps<"section">, "onChange" | "onWheel" | "style"> & {
14
9
  transform?: InfiniteCanvasTransform;
15
10
  defaultTransform?: InfiniteCanvasTransform;
16
11
  onTransformChange?: (transform: InfiniteCanvasTransform, details: InfiniteCanvasMoveDetails) => void;
17
12
  minScale?: number;
18
13
  maxScale?: number;
14
+ panOnDrag?: InfiniteCanvasPanOnDrag;
15
+ keyboardNavigation?: boolean;
19
16
  onWheel?: (event: WheelEvent) => void;
20
17
  style?: CSSProperties & InfiniteCanvasKnobStyle;
21
18
  };
22
- export type InfiniteCanvasContentProps = ComponentProps<"div"> & {
19
+ export type InfiniteCanvasContentProps = Omit<ComponentProps<"div">, "ref" | "style"> & {
23
20
  style?: CSSProperties & InfiniteCanvasKnobStyle;
24
21
  };
25
- export type InfiniteCanvasPoint = {
26
- x: number;
27
- y: number;
28
- };
29
22
  export type InfiniteCanvasItemProps = Omit<ComponentProps<"div">, "style"> & {
30
23
  x: number;
31
24
  y: number;
@@ -33,19 +26,23 @@ export type InfiniteCanvasItemProps = Omit<ComponentProps<"div">, "style"> & {
33
26
  movableItemLabel?: string;
34
27
  style?: CSSProperties & InfiniteCanvasKnobStyle;
35
28
  };
29
+ export type InfiniteCanvasItemHandleProps = ComponentProps<typeof Button> & {
30
+ onPositionChange: (position: InfiniteCanvasPoint) => void;
31
+ };
36
32
  export type InfiniteCanvasControlsProps = Omit<ComponentProps<"div">, "children" | "style"> & {
37
33
  style?: CSSProperties & InfiniteCanvasKnobStyle;
38
34
  };
39
- type InfiniteCanvasContextValue = {
40
- transform: InfiniteCanvasTransform;
35
+ export type InfiniteCanvasContextValue = InfiniteCanvasCamera & {
41
36
  minScale: number;
42
37
  maxScale: number;
43
- reset: (reason: InfiniteCanvasMoveReason) => void;
44
- zoomBy: (factor: number, reason: InfiniteCanvasMoveReason) => void;
45
38
  };
46
39
  export declare function useInfiniteCanvas(): InfiniteCanvasContextValue;
47
- export declare function InfiniteCanvas({ transform: controlledTransform, defaultTransform, onTransformChange, minScale: minScaleProp, maxScale: maxScaleProp, className, children, style, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onWheel, onKeyDown, "aria-label": ariaLabel, "aria-keyshortcuts": ariaKeyShortcuts, ...props }: InfiniteCanvasProps): import("react").JSX.Element;
40
+ export declare function useInfiniteCanvasTransform(): InfiniteCanvasTransform;
41
+ export declare function useInfiniteCanvasTransform<Selected>(select: (transform: InfiniteCanvasTransform) => Selected): Selected;
42
+ export declare function InfiniteCanvas({ transform, defaultTransform, onTransformChange, minScale: minScaleProp, maxScale: maxScaleProp, panOnDrag, keyboardNavigation, ref, className, children, style, onPointerDown, onWheel, onKeyDown, "aria-label": ariaLabel, "aria-keyshortcuts": ariaKeyShortcuts, ...props }: InfiniteCanvasProps): import("react").JSX.Element;
48
43
  export declare function InfiniteCanvasContent({ className, style, ...props }: InfiniteCanvasContentProps): import("react").JSX.Element;
49
- export declare function InfiniteCanvasItem({ x, y, onPositionChange, movableItemLabel, className, style, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onKeyDown, ...props }: InfiniteCanvasItemProps): import("react").JSX.Element;
44
+ export declare function InfiniteCanvasItem({ x, y, onPositionChange, movableItemLabel, className, style, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onLostPointerCapture, onKeyDown, ...props }: InfiniteCanvasItemProps): import("react").JSX.Element;
45
+ export declare function InfiniteCanvasItemHandle({ onPositionChange, className, children, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onLostPointerCapture, onKeyDown, "aria-label": ariaLabel, ...props }: InfiniteCanvasItemHandleProps): import("react").JSX.Element;
50
46
  export declare function InfiniteCanvasControls({ className, ...props }: InfiniteCanvasControlsProps): import("react").JSX.Element;
51
- //#endregion
47
+ //#endregion
48
+ export type { InfiniteCanvasBounds, InfiniteCanvasCamera, InfiniteCanvasFitOptions, InfiniteCanvasMoveDetails, InfiniteCanvasMoveOptions, InfiniteCanvasMoveReason, InfiniteCanvasPanOnDrag, InfiniteCanvasPoint, InfiniteCanvasRevealOptions, InfiniteCanvasTransform, InfiniteCanvasTransformUpdate, InfiniteCanvasZoomOptions };