@ctrl-ui/react 0.8.18 → 0.8.20

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 (106) hide show
  1. package/README.md +7 -0
  2. package/dist/action-bar.js +2 -2
  3. package/dist/audio-device-select.js +5 -2
  4. package/dist/blocks/coding-agent.js +22 -12
  5. package/dist/blocks/design-canvas-inspector.js +6 -3
  6. package/dist/blocks/design-canvas.js +20 -10
  7. package/dist/blocks/discussion-markdown-input.d.ts +14 -0
  8. package/dist/blocks/discussion-markdown-input.js +36 -0
  9. package/dist/blocks/discussion.d.ts +9 -3
  10. package/dist/blocks/discussion.js +82 -16
  11. package/dist/blocks/file-explorer.js +46 -24
  12. package/dist/blocks/settings.js +10 -6
  13. package/dist/chat-composer-editor/extensions/mention.d.ts +1 -1
  14. package/dist/chat-message.d.ts +13 -0
  15. package/dist/chat-message.js +198 -3
  16. package/dist/filter-bar/editor.js +5 -2
  17. package/dist/knob-contracts/flow-knobs.d.ts +4 -0
  18. package/dist/knob-contracts/flow-knobs.js +32 -0
  19. package/dist/knob-contracts/item-knobs.d.ts +1 -1
  20. package/dist/knob-contracts/item-knobs.js +4 -1
  21. package/dist/knob-contracts/label-knobs.d.ts +4 -0
  22. package/dist/knob-contracts/label-knobs.js +4 -0
  23. package/dist/knob-contracts/markdown-editor-knobs.d.ts +4 -0
  24. package/dist/knob-contracts/markdown-editor-knobs.js +17 -0
  25. package/dist/knob-contracts/morphing-panel-knobs.d.ts +1 -1
  26. package/dist/knob-contracts/morphing-panel-knobs.js +2 -1
  27. package/dist/knob-contracts/resize-handle-knobs.d.ts +4 -0
  28. package/dist/knob-contracts/resize-handle-knobs.js +12 -0
  29. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  30. package/dist/knob-contracts/sidebar-knobs.js +10 -0
  31. package/dist/knob-contracts/switch-knobs.d.ts +1 -1
  32. package/dist/knob-contracts/switch-knobs.js +1 -0
  33. package/dist/lib/canvas-grid.d.ts +4 -0
  34. package/dist/lib/canvas-grid.js +5 -0
  35. package/dist/markdown-editor/context.d.ts +22 -0
  36. package/dist/markdown-editor/context.js +10 -0
  37. package/dist/markdown-editor/drag-handle.d.ts +6 -0
  38. package/dist/markdown-editor/drag-handle.js +126 -0
  39. package/dist/markdown-editor/extensions.d.ts +10 -0
  40. package/dist/markdown-editor/extensions.js +139 -0
  41. package/dist/markdown-editor/suggestions.d.ts +12 -0
  42. package/dist/markdown-editor/suggestions.js +235 -0
  43. package/dist/markdown-editor/toolbar.d.ts +9 -0
  44. package/dist/markdown-editor/toolbar.js +339 -0
  45. package/dist/markdown-editor/uploads.d.ts +23 -0
  46. package/dist/markdown-editor/uploads.js +32 -0
  47. package/dist/markdown-editor/use-image-uploads.d.ts +12 -0
  48. package/dist/markdown-editor/use-image-uploads.js +120 -0
  49. package/dist/markdown-editor.d.ts +27 -0
  50. package/dist/markdown-editor.js +202 -0
  51. package/dist/styles/index.css +9 -4
  52. package/dist/styles/recipes/chat-message-edit.css +105 -0
  53. package/dist/styles/recipes/code.css +4 -0
  54. package/dist/styles/recipes/field.css +1 -1
  55. package/dist/styles/recipes/flow.css +304 -0
  56. package/dist/styles/recipes/infinite-canvas.css +1 -1
  57. package/dist/styles/recipes/item.css +39 -2
  58. package/dist/styles/recipes/label.css +19 -0
  59. package/dist/styles/recipes/markdown-editor.css +234 -0
  60. package/dist/styles/recipes/morphing-panel.css +53 -12
  61. package/dist/styles/recipes/page-layout.css +11 -1
  62. package/dist/styles/recipes/resize-handle.css +126 -0
  63. package/dist/styles/recipes/sidebar-mobile.css +171 -0
  64. package/dist/styles/recipes/switch.css +17 -5
  65. package/dist/styles/recipes/toolbar.css +8 -1
  66. package/dist/styles/theme.css +1 -0
  67. package/dist/ui/autocomplete.js +1 -1
  68. package/dist/ui/code.js +1 -0
  69. package/dist/ui/color-picker.js +1 -1
  70. package/dist/ui/combobox.d.ts +1 -1
  71. package/dist/ui/combobox.js +1 -1
  72. package/dist/ui/dockable-panel.js +1 -1
  73. package/dist/ui/flow.d.ts +51 -0
  74. package/dist/ui/flow.js +153 -0
  75. package/dist/ui/infinite-canvas.js +3 -3
  76. package/dist/ui/item.d.ts +5 -3
  77. package/dist/ui/item.js +2 -1
  78. package/dist/ui/label.d.ts +8 -0
  79. package/dist/ui/label.js +14 -0
  80. package/dist/ui/markdown.d.ts +2 -1
  81. package/dist/ui/markdown.js +3 -3
  82. package/dist/ui/morphing-panel.d.ts +21 -4
  83. package/dist/ui/morphing-panel.js +152 -36
  84. package/dist/ui/resize-handle.d.ts +11 -0
  85. package/dist/ui/resize-handle.js +26 -0
  86. package/dist/ui/sidebar-mobile.d.ts +17 -0
  87. package/dist/ui/sidebar-mobile.js +70 -0
  88. package/dist/ui/sidebar-provider.d.ts +1 -0
  89. package/dist/ui/sidebar-provider.js +3 -0
  90. package/dist/ui/sidebar.d.ts +3 -1
  91. package/dist/ui/sidebar.js +101 -49
  92. package/dist/ui/toolbar.d.ts +3 -1
  93. package/dist/ui/toolbar.js +2 -1
  94. package/dist/ui/typography.d.ts +47 -0
  95. package/dist/ui/typography.js +61 -0
  96. package/dist/ui/use-switch-drag.js +1 -1
  97. package/package.json +41 -1
  98. package/dist/environment-variables.d.ts +0 -125
  99. package/dist/environment-variables.js +0 -512
  100. package/dist/hooks/use-environment-variables.d.ts +0 -50
  101. package/dist/hooks/use-environment-variables.js +0 -188
  102. package/dist/knob-contracts/environment-variables-knobs.d.ts +0 -4
  103. package/dist/knob-contracts/environment-variables-knobs.js +0 -12
  104. package/dist/lib/env-file.d.ts +0 -27
  105. package/dist/lib/env-file.js +0 -218
  106. package/dist/styles/recipes/environment-variables.css +0 -170
@@ -0,0 +1,202 @@
1
+ "use client";
2
+ import { cn } from "./lib/cn.js";
3
+ import { Textarea } from "./ui/textarea.js";
4
+ import { MarkdownEditorContext, useMarkdownEditor } from "./markdown-editor/context.js";
5
+ import { MarkdownEditorDragHandle } from "./markdown-editor/drag-handle.js";
6
+ import { createMarkdownCodec, hasMarkdownContent, markdownExtensions, requiresMarkdownSource } from "./markdown-editor/extensions.js";
7
+ import { useImageUploads } from "./markdown-editor/use-image-uploads.js";
8
+ import { useEffect, useMemo, useRef, useState } from "react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { Slice } from "@tiptap/pm/model";
11
+ import { EditorState } from "@tiptap/pm/state";
12
+ import { EditorContent, useEditor } from "@tiptap/react";
13
+ //#region src/markdown-editor.tsx
14
+ function MarkdownEditor({ value, defaultValue = "", onValueChange, disabled = false, onUploadImage, maxImageBytes = 10485760, onStatusChange, children, className, ...props }) {
15
+ const [internalValue, setInternalValue] = useState(defaultValue);
16
+ const markdown = value ?? internalValue;
17
+ const [sourceMode, setSourceMode] = useState(false);
18
+ const [contentAttributes, setContentAttributes] = useState({
19
+ role: "textbox",
20
+ "aria-multiline": "true"
21
+ });
22
+ const [codec] = useState(createMarkdownCodec);
23
+ const sourceRequired = useMemo(() => requiresMarkdownSource(markdown, codec), [markdown, codec]);
24
+ const source = sourceMode || sourceRequired;
25
+ const lastValue = useRef(markdown);
26
+ const [extensions] = useState(markdownExtensions);
27
+ const handlers = useRef(null);
28
+ function setMarkdown(next) {
29
+ lastValue.current = next;
30
+ setInternalValue(next);
31
+ onValueChange?.(next);
32
+ }
33
+ const editor = useEditor({
34
+ extensions,
35
+ immediatelyRender: false,
36
+ shouldRerenderOnTransaction: false,
37
+ content: sourceRequired ? "" : markdown,
38
+ contentType: "markdown",
39
+ editable: !disabled,
40
+ onUpdate: ({ editor: currentEditor }) => setMarkdown(currentEditor.getMarkdown()),
41
+ editorProps: {
42
+ attributes: contentAttributes,
43
+ handlePaste: (view, event) => {
44
+ if (disabled) return false;
45
+ const files = Array.from(event.clipboardData?.files ?? []);
46
+ if (onUploadImage && files.length > 0) {
47
+ event.preventDefault();
48
+ handlers.current?.upload(files);
49
+ return true;
50
+ }
51
+ const text = event.clipboardData?.getData("text/plain");
52
+ if (!text || event.clipboardData?.getData("text/html") || requiresMarkdownSource(text, codec)) return false;
53
+ const document = view.state.schema.nodeFromJSON(codec.parse(text));
54
+ event.preventDefault();
55
+ view.dispatch(view.state.tr.replaceSelection(Slice.maxOpen(document.content)).scrollIntoView());
56
+ return true;
57
+ },
58
+ handleDrop: (view, event, _slice, moved) => {
59
+ const files = Array.from(event.dataTransfer?.files ?? []);
60
+ if (moved || !onUploadImage || disabled || files.length === 0) return false;
61
+ event.preventDefault();
62
+ const position = view.posAtCoords({
63
+ left: event.clientX,
64
+ top: event.clientY
65
+ })?.pos;
66
+ handlers.current?.upload(files, position);
67
+ return true;
68
+ }
69
+ }
70
+ });
71
+ const imageUploads = useImageUploads(editor, onUploadImage, maxImageBytes);
72
+ useEffect(() => {
73
+ handlers.current = imageUploads;
74
+ });
75
+ useEffect(() => {
76
+ if (!editor) return;
77
+ editor.setEditable(!disabled, false);
78
+ }, [editor, disabled]);
79
+ useEffect(() => {
80
+ if (!editor) return;
81
+ const externalReset = lastValue.current !== markdown;
82
+ if (externalReset) handlers.current?.cancelAll();
83
+ if (source) {
84
+ lastValue.current = markdown;
85
+ return;
86
+ }
87
+ if (editor.getMarkdown() !== markdown) {
88
+ editor.commands.setContent(markdown, {
89
+ contentType: "markdown",
90
+ emitUpdate: false
91
+ });
92
+ if (externalReset) {
93
+ editor.view.updateState(EditorState.create({
94
+ schema: editor.schema,
95
+ doc: editor.state.doc,
96
+ plugins: editor.state.plugins
97
+ }));
98
+ editor.view.dispatch(editor.state.tr);
99
+ }
100
+ }
101
+ lastValue.current = markdown;
102
+ }, [
103
+ editor,
104
+ markdown,
105
+ source
106
+ ]);
107
+ const isEmpty = source ? markdown.trim() === "" : !hasMarkdownContent(codec.parse(markdown));
108
+ const hasPendingUploads = imageUploads.uploads.length > 0;
109
+ useEffect(() => {
110
+ onStatusChange?.({
111
+ isEmpty,
112
+ hasPendingUploads
113
+ });
114
+ }, [
115
+ onStatusChange,
116
+ isEmpty,
117
+ hasPendingUploads
118
+ ]);
119
+ return /* @__PURE__ */ jsx(MarkdownEditorContext, {
120
+ value: {
121
+ editor,
122
+ disabled,
123
+ source,
124
+ markdown,
125
+ sourceRequired,
126
+ setSource: setSourceMode,
127
+ setMarkdown,
128
+ setContentAttributes,
129
+ canUpload: Boolean(onUploadImage),
130
+ ...imageUploads
131
+ },
132
+ children: /* @__PURE__ */ jsx("div", {
133
+ "data-control-ui": "markdown-editor",
134
+ "data-control-family": "markdown-editor",
135
+ "data-slot": "root",
136
+ "data-disabled": disabled ? "" : void 0,
137
+ className: cn("min-w-0", className),
138
+ ...props,
139
+ children
140
+ })
141
+ });
142
+ }
143
+ function MarkdownEditorContent({ label, placeholder, autoFocus = false, className }) {
144
+ const container = useRef(null);
145
+ const { editor, disabled, source, sourceRequired, markdown, setMarkdown, setContentAttributes } = useMarkdownEditor();
146
+ useEffect(() => {
147
+ setContentAttributes({
148
+ role: "textbox",
149
+ "aria-multiline": "true",
150
+ "aria-label": label,
151
+ "aria-disabled": String(disabled),
152
+ "data-placeholder": placeholder ?? ""
153
+ });
154
+ }, [
155
+ setContentAttributes,
156
+ label,
157
+ disabled,
158
+ placeholder
159
+ ]);
160
+ useEffect(() => {
161
+ if (editor && autoFocus && !source && !disabled) editor.commands.focus("end");
162
+ }, [
163
+ editor,
164
+ autoFocus,
165
+ source,
166
+ disabled
167
+ ]);
168
+ if (source || !editor) return /* @__PURE__ */ jsxs("div", {
169
+ "data-control-ui": "markdown-editor",
170
+ "data-control-family": "markdown-editor",
171
+ "data-slot": "source",
172
+ className,
173
+ children: [sourceRequired ? /* @__PURE__ */ jsx("p", {
174
+ role: "status",
175
+ children: "This content uses Markdown that is only available in source mode. Its source is preserved."
176
+ }) : null, /* @__PURE__ */ jsx(Textarea, {
177
+ "aria-label": label,
178
+ placeholder,
179
+ value: markdown,
180
+ disabled: disabled || !editor,
181
+ onChange: (event) => setMarkdown(event.target.value),
182
+ rows: 5,
183
+ autoFocus,
184
+ style: {
185
+ "--cui-field-background": "transparent",
186
+ "--cui-field-border-width": "0px",
187
+ "--cui-field-radius": "0px",
188
+ "--cui-field-shadow": "none"
189
+ }
190
+ })]
191
+ });
192
+ return /* @__PURE__ */ jsxs("div", {
193
+ ref: container,
194
+ "data-control-ui": "markdown-editor",
195
+ "data-control-family": "markdown-editor",
196
+ "data-slot": "content",
197
+ className,
198
+ children: [/* @__PURE__ */ jsx(EditorContent, { editor }), /* @__PURE__ */ jsx(MarkdownEditorDragHandle, { container })]
199
+ });
200
+ }
201
+ //#endregion
202
+ export { MarkdownEditor, MarkdownEditorContent };
@@ -14,13 +14,16 @@
14
14
  @import "./recipes/popup-lists.css";
15
15
  @import "./recipes/popup-motion.css";
16
16
  @import "./recipes/page-layout.css";
17
+ @import "./recipes/popup-drawer.css";
17
18
  @import "./recipes/track-highlight.css";
18
19
  @import "./recipes/sidebar.css";
19
20
  @import "./recipes/sidebar-menu.css";
21
+ @import "./recipes/sidebar-mobile.css";
20
22
  @import "./recipes/sidebar-parts.css";
21
23
  @import "./recipes/app-shell-header.css";
22
24
  @import "./recipes/field.css";
23
25
  @import "./recipes/field-parts.css";
26
+ @import "./type-scale.css";
24
27
  @import "./recipes/audio-device-select.css";
25
28
  @import "./recipes/audio-visualizer.css";
26
29
  @import "./recipes/audio-recorder.css";
@@ -31,6 +34,7 @@
31
34
  @import "./recipes/calendar.css";
32
35
  @import "./recipes/card.css";
33
36
  @import "./recipes/chat-message.css";
37
+ @import "./recipes/chat-message-edit.css";
34
38
  @import "./recipes/chat-message-social.css";
35
39
  @import "./recipes/chat-layout.css";
36
40
  @import "./recipes/spinner.css";
@@ -46,12 +50,14 @@
46
50
  @import "./recipes/color-picker-values.css";
47
51
  @import "./recipes/empty.css";
48
52
  @import "./recipes/context.css";
49
- @import "./recipes/popup-drawer.css";
50
53
  @import "./recipes/infinite-canvas.css";
51
54
  @import "./recipes/resizable.css";
52
55
  @import "./recipes/separator.css";
53
56
  @import "./recipes/toolbar.css";
54
57
  @import "./recipes/kbd.css";
58
+ @import "./recipes/markdown.css";
59
+ @import "./recipes/label.css";
60
+ @import "./recipes/markdown-editor.css";
55
61
  @import "./recipes/dockable-panel.css";
56
62
  @import "./recipes/item.css";
57
63
  @import "./recipes/dropzone.css";
@@ -62,18 +68,18 @@
62
68
  @import "./recipes/icon-picker.css";
63
69
  @import "./recipes/tabs.css";
64
70
  @import "./recipes/tabs-motion.css";
65
- @import "./recipes/environment-variables.css";
66
71
  @import "./recipes/filter-bar.css";
72
+ @import "./recipes/flow.css";
67
73
  @import "./recipes/gradient-editor.css";
68
74
  @import "./recipes/inline-attachment.css";
69
75
  @import "./recipes/source-badge.css";
70
76
  @import "./recipes/inline-citation.css";
71
- @import "./recipes/markdown.css";
72
77
  @import "./recipes/markdown-block.css";
73
78
  @import "./recipes/range.css";
74
79
  @import "./recipes/morphing-panel.css";
75
80
  @import "./recipes/pagination.css";
76
81
  @import "./recipes/phone-input.css";
82
+ @import "./recipes/resize-handle.css";
77
83
  @import "./recipes/rich-tooltip.css";
78
84
  @import "./recipes/skeleton.css";
79
85
  @import "./recipes/stepper.css";
@@ -86,6 +92,5 @@
86
92
  @import "./recipes/popup-toast.css";
87
93
  @import "./recipes/transcript-divider.css";
88
94
  @import "./recipes/tree.css";
89
- @import "./type-scale.css";
90
95
  @import "./recipes/user-ask.css";
91
96
  @import "./recipes/user-ask-details.css";
@@ -0,0 +1,105 @@
1
+ @layer components {
2
+ :where([data-control-family="chat-message"][data-slot="editable"]) {
3
+ min-inline-size: 0;
4
+ max-inline-size: 100%;
5
+ }
6
+
7
+ :where([data-control-family="chat-message"][data-slot="content"][data-editable]) {
8
+ position: relative;
9
+ display: grid;
10
+ outline: var(--focus-ring-width) var(--focus-ring-style) transparent;
11
+ outline-offset: calc(var(--focus-ring-width) * -1);
12
+ transition: outline-color var(--duration-fast) var(--ease-standard);
13
+ }
14
+
15
+ :where([data-control-family="chat-message"][data-slot="content"][data-editable]:focus-within) {
16
+ outline-color: var(--focus-ring);
17
+ }
18
+
19
+ :where([data-control-family="chat-message"][data-slot="editable"] :is([data-slot="edit-value"], [data-slot="edit-sizer"])) {
20
+ grid-area: 1 / 1;
21
+ white-space: pre-wrap;
22
+ overflow-wrap: anywhere;
23
+ min-inline-size: 0;
24
+ }
25
+
26
+ :where([data-control-family="chat-message"][data-slot="editable"] [data-slot="edit-sizer"]) {
27
+ visibility: hidden;
28
+ contain: inline-size;
29
+ min-inline-size: 100%;
30
+ inline-size: 0;
31
+ block-size: 0;
32
+ max-block-size: 40dvh;
33
+ overflow: hidden;
34
+ }
35
+
36
+ :where([data-control-family="chat-message"][data-slot="editable"][data-editing] [data-slot="edit-sizer"]) {
37
+ block-size: auto;
38
+ }
39
+
40
+ @supports (interpolate-size: allow-keywords) {
41
+ :where([data-control-family="chat-message"][data-slot="editable"] [data-slot="edit-sizer"]) {
42
+ interpolate-size: allow-keywords;
43
+ transition: block-size var(--duration-fast) var(--ease-standard);
44
+ }
45
+ }
46
+
47
+ :where([data-control-family="chat-message"][data-slot="editable"] [data-slot="edit-input"]) {
48
+ position: absolute;
49
+ inset: 0;
50
+ box-sizing: border-box;
51
+ inline-size: 100%;
52
+ block-size: 100%;
53
+ min-inline-size: 0;
54
+ margin: 0;
55
+ border: 0;
56
+ padding: inherit;
57
+ resize: none;
58
+ background: transparent;
59
+ color: inherit;
60
+ font: inherit;
61
+ letter-spacing: inherit;
62
+ text-align: inherit;
63
+ white-space: pre-wrap;
64
+ overflow-wrap: anywhere;
65
+ scrollbar-width: thin;
66
+ outline: none;
67
+ visibility: hidden;
68
+ }
69
+
70
+ :where([data-control-family="chat-message"][data-slot="editable"][data-editing] [data-slot="edit-input"]) {
71
+ visibility: visible;
72
+ }
73
+
74
+ :where([data-control-family="chat-message"][data-slot="editable"][data-editing] [data-slot="edit-value"]) {
75
+ visibility: hidden;
76
+ }
77
+
78
+ :where([data-control-family="chat-message"][data-slot="editable"] [data-slot="edit-toolbars"]) {
79
+ display: grid;
80
+ }
81
+
82
+ :where([data-control-family="chat-message"][data-slot="editable"] [data-slot="edit-toolbar"]) {
83
+ grid-area: 1 / 1;
84
+ min-inline-size: 0;
85
+ visibility: hidden;
86
+ opacity: 0;
87
+ transition: opacity var(--duration-fast) var(--ease-standard);
88
+ }
89
+
90
+ :where([data-control-family="chat-message"][data-slot="editable"] [data-slot="edit-toolbar"][data-active]) {
91
+ visibility: visible;
92
+ opacity: 1;
93
+ }
94
+
95
+ :where([data-control-family="chat-message"][data-slot="editable"] [data-slot="edit-error"]) {
96
+ display: block;
97
+ margin-block-start: calc(var(--spacing) * 1);
98
+ color: var(--destructive);
99
+ font-size: var(--text-caption);
100
+ }
101
+
102
+ :where([data-control-family="chat-message"][data-slot="editable"] [data-slot="edit-error"][hidden]) {
103
+ display: none;
104
+ }
105
+ }
@@ -59,6 +59,10 @@
59
59
  margin-inline-start: auto;
60
60
  }
61
61
 
62
+ :where([data-control-family="code"][data-slot="floating-frame"]) {
63
+ padding-block-start: calc(var(--spacing) * 9);
64
+ }
65
+
62
66
  :where([data-control-family="code"][data-slot="line"]) {
63
67
  min-block-size: calc(var(--spacing) * 5);
64
68
  }
@@ -187,7 +187,7 @@
187
187
  }
188
188
 
189
189
  :where([data-control-family="field"][data-field-kind="field"][data-slot="root"]) {
190
- gap: calc(var(--spacing) * 3);
190
+ gap: calc(var(--spacing) * 1.5) calc(var(--spacing) * 3);
191
191
  }
192
192
 
193
193
  :where([data-control-family="field"][data-field-kind="field"][data-slot="content"]) {
@@ -0,0 +1,304 @@
1
+ @layer components {
2
+ :where([data-control-family="flow"][data-slot="root"]) {
3
+ --cui-flow-background: var(--canvas);
4
+ --cui-flow-grid-dot-color: var(--canvas-grid-dot);
5
+ --cui-flow-edge-stroke: oklch(from var(--muted-foreground) l c h / 0.6);
6
+ --cui-flow-selected-edge-stroke: var(--primary);
7
+ --cui-flow-edge-size: 1.5px;
8
+ --cui-flow-node-background: var(--card);
9
+ --cui-flow-node-foreground: var(--card-foreground);
10
+ --cui-flow-node-border-color: var(--border);
11
+ --cui-flow-node-hover-border-color: oklch(from var(--foreground) l c h / 0.2);
12
+ --cui-flow-node-selected-border-color: var(--primary);
13
+ --cui-flow-node-border-width: var(--control-rim-width);
14
+ --cui-flow-node-radius: var(--radius-lg);
15
+ --cui-flow-node-shadow: var(--shadow-sm);
16
+ --cui-flow-handle-size: calc(var(--spacing) * 2);
17
+ --cui-flow-handle-background: var(--background);
18
+ --cui-flow-handle-border-color: var(--muted-foreground);
19
+ --cui-flow-handle-border-width: var(--control-rim-width);
20
+ --cui-flow-edge-label-background: var(--background);
21
+ --cui-flow-edge-label-foreground: var(--muted-foreground);
22
+ --cui-flow-edge-label-border-color: var(--border);
23
+ --cui-flow-edge-label-border-width: var(--control-rim-width);
24
+ --cui-flow-controls-item-radius: min(var(--radius-sm), calc(var(--control-h-sm) / 2));
25
+ --cui-flow-controls-padding: calc(var(--spacing) * 1);
26
+ --cui-flow-controls-radius: calc(
27
+ var(--cui-flow-controls-item-radius) +
28
+ clamp(0px, calc(var(--cui-flow-controls-item-radius) * 1000), var(--cui-flow-controls-padding))
29
+ );
30
+ --cui-flow-controls-background: var(--card);
31
+ --cui-flow-controls-border-color: var(--border);
32
+ --cui-flow-controls-shadow: var(--shadow-sm);
33
+ --xy-edge-stroke: var(--cui-flow-edge-stroke);
34
+ --xy-connectionline-stroke: var(--cui-flow-edge-stroke);
35
+ --xy-connectionline-stroke-width: var(--cui-flow-edge-size);
36
+ --xy-selection-background-color: oklch(from var(--primary) l c h / 0.08);
37
+ --xy-selection-border: var(--control-rim-width) solid oklch(from var(--primary) l c h / 0.4);
38
+ --xy-attribution-background-color: oklch(from var(--background) l c h / 0.5);
39
+ background-color: var(--cui-flow-background);
40
+ color: var(--foreground);
41
+ }
42
+
43
+ :where([data-control-family="flow"][data-slot="root"] .react-flow__background-pattern) {
44
+ fill: var(--cui-flow-grid-dot-color);
45
+ }
46
+
47
+ :where([data-control-family="flow"][data-slot="root"] .react-flow__attribution a) {
48
+ color: var(--muted-foreground);
49
+ }
50
+
51
+ :where([data-control-family="flow"][data-slot="panel"]),
52
+ :where([data-control-family="flow"][data-slot="controls"]) {
53
+ margin: calc(var(--spacing) * 3);
54
+ }
55
+
56
+ :where([data-control-family="flow"][data-slot="controls"]) {
57
+ --nest-radius: var(--cui-flow-controls-item-radius);
58
+ gap: calc(var(--spacing) * 0.5);
59
+ padding: var(--cui-flow-controls-padding);
60
+ border-radius: var(--cui-flow-controls-radius);
61
+ background-color: var(--cui-flow-controls-background);
62
+ box-shadow:
63
+ var(--cui-flow-controls-shadow),
64
+ 0 0 0 var(--control-rim-width) var(--cui-flow-controls-border-color);
65
+ }
66
+
67
+ :where([data-control-family="flow"][data-slot="node"]) {
68
+ overflow: hidden;
69
+ border: var(--cui-flow-node-border-width) solid var(--cui-flow-node-border-color);
70
+ border-radius: var(--cui-flow-node-radius);
71
+ background-color: var(--cui-flow-node-background);
72
+ color: var(--cui-flow-node-foreground);
73
+ box-shadow: var(--cui-flow-node-shadow);
74
+ transition-property: border-color, box-shadow;
75
+ transition-duration: var(--duration-fast);
76
+ transition-timing-function: var(--ease-standard);
77
+ }
78
+
79
+ @media (hover: hover) {
80
+ :where([data-control-family="flow"][data-slot="node"]:hover) {
81
+ border-color: var(--cui-flow-node-hover-border-color);
82
+ }
83
+ }
84
+
85
+ :where([data-control-family="flow"][data-slot="node"][data-selected]) {
86
+ border-color: var(--cui-flow-node-selected-border-color);
87
+ box-shadow:
88
+ var(--cui-flow-node-shadow),
89
+ 0 0 0 calc(var(--control-rim-width) * 3) oklch(from var(--cui-flow-node-selected-border-color) l c h / 0.15);
90
+ }
91
+
92
+ :where([data-control-family="flow"][data-slot="node-header"]) {
93
+ gap: calc(var(--spacing) * 2);
94
+ min-block-size: calc(var(--spacing) * 10);
95
+ padding-block: calc(var(--spacing) * 1.5);
96
+ padding-inline: calc(var(--spacing) * 3) calc(var(--spacing) * 1.5);
97
+ font-size: var(--text-body);
98
+ font-weight: var(--font-weight-medium);
99
+ }
100
+
101
+ :where([data-control-family="flow"][data-slot="node-body"]) {
102
+ gap: calc(var(--spacing) * 1.5);
103
+ padding-inline: calc(var(--spacing) * 3);
104
+ padding-block-end: calc(var(--spacing) * 3);
105
+ font-size: var(--text-caption);
106
+ }
107
+
108
+ :where([data-control-family="flow"][data-slot="handle"]) {
109
+ inline-size: var(--cui-flow-handle-size);
110
+ block-size: var(--cui-flow-handle-size);
111
+ border: var(--cui-flow-handle-border-width) solid var(--cui-flow-handle-border-color);
112
+ border-radius: 9999px;
113
+ background-color: var(--cui-flow-handle-background);
114
+ }
115
+
116
+ :where([data-control-family="flow"][data-slot="edge"]) {
117
+ stroke: var(--cui-flow-edge-stroke);
118
+ stroke-width: var(--cui-flow-edge-size);
119
+ transition: stroke var(--duration-fast) var(--ease-standard);
120
+ }
121
+
122
+ :where([data-control-family="flow"][data-slot="edge"][data-dashed]) {
123
+ stroke-dasharray: 5 5;
124
+ }
125
+
126
+ :where(.react-flow__edge.selected [data-control-family="flow"][data-slot="edge"]) {
127
+ stroke: var(--cui-flow-selected-edge-stroke);
128
+ }
129
+
130
+ :where([data-control-family="flow"][data-slot="edge-label"]) {
131
+ gap: calc(var(--spacing) * 1);
132
+ }
133
+
134
+ :where([data-control-family="flow"][data-slot="edge-label-chip"]) {
135
+ padding-block: calc(var(--spacing) * 0.5);
136
+ padding-inline: calc(var(--spacing) * 2);
137
+ border: var(--cui-flow-edge-label-border-width) solid var(--cui-flow-edge-label-border-color);
138
+ border-radius: 9999px;
139
+ background-color: var(--cui-flow-edge-label-background);
140
+ color: var(--cui-flow-edge-label-foreground);
141
+ font-size: var(--text-caption);
142
+ font-weight: var(--font-weight-medium);
143
+ }
144
+ }
145
+
146
+ @property --cui-flow-background {
147
+ syntax: "<color>";
148
+ inherits: true;
149
+ initial-value: transparent;
150
+ }
151
+
152
+ @property --cui-flow-grid-dot-color {
153
+ syntax: "<color>";
154
+ inherits: true;
155
+ initial-value: transparent;
156
+ }
157
+
158
+ @property --cui-flow-edge-stroke {
159
+ syntax: "<color>";
160
+ inherits: true;
161
+ initial-value: transparent;
162
+ }
163
+
164
+ @property --cui-flow-selected-edge-stroke {
165
+ syntax: "<color>";
166
+ inherits: true;
167
+ initial-value: transparent;
168
+ }
169
+
170
+ @property --cui-flow-edge-size {
171
+ syntax: "<length>";
172
+ inherits: true;
173
+ initial-value: 0px;
174
+ }
175
+
176
+ @property --cui-flow-node-background {
177
+ syntax: "<color>";
178
+ inherits: true;
179
+ initial-value: transparent;
180
+ }
181
+
182
+ @property --cui-flow-node-foreground {
183
+ syntax: "<color>";
184
+ inherits: true;
185
+ initial-value: transparent;
186
+ }
187
+
188
+ @property --cui-flow-node-border-color {
189
+ syntax: "<color>";
190
+ inherits: true;
191
+ initial-value: transparent;
192
+ }
193
+
194
+ @property --cui-flow-node-hover-border-color {
195
+ syntax: "<color>";
196
+ inherits: true;
197
+ initial-value: transparent;
198
+ }
199
+
200
+ @property --cui-flow-node-selected-border-color {
201
+ syntax: "<color>";
202
+ inherits: true;
203
+ initial-value: transparent;
204
+ }
205
+
206
+ @property --cui-flow-node-border-width {
207
+ syntax: "<length>";
208
+ inherits: true;
209
+ initial-value: 0px;
210
+ }
211
+
212
+ @property --cui-flow-node-radius {
213
+ syntax: "<length-percentage>";
214
+ inherits: true;
215
+ initial-value: 0px;
216
+ }
217
+
218
+ @property --cui-flow-node-shadow {
219
+ syntax: "*";
220
+ inherits: true;
221
+ }
222
+
223
+ @property --cui-flow-handle-size {
224
+ syntax: "<length>";
225
+ inherits: true;
226
+ initial-value: 0px;
227
+ }
228
+
229
+ @property --cui-flow-handle-background {
230
+ syntax: "<color>";
231
+ inherits: true;
232
+ initial-value: transparent;
233
+ }
234
+
235
+ @property --cui-flow-handle-border-color {
236
+ syntax: "<color>";
237
+ inherits: true;
238
+ initial-value: transparent;
239
+ }
240
+
241
+ @property --cui-flow-handle-border-width {
242
+ syntax: "<length>";
243
+ inherits: true;
244
+ initial-value: 0px;
245
+ }
246
+
247
+ @property --cui-flow-edge-label-background {
248
+ syntax: "<color>";
249
+ inherits: true;
250
+ initial-value: transparent;
251
+ }
252
+
253
+ @property --cui-flow-edge-label-foreground {
254
+ syntax: "<color>";
255
+ inherits: true;
256
+ initial-value: transparent;
257
+ }
258
+
259
+ @property --cui-flow-edge-label-border-color {
260
+ syntax: "<color>";
261
+ inherits: true;
262
+ initial-value: transparent;
263
+ }
264
+
265
+ @property --cui-flow-edge-label-border-width {
266
+ syntax: "<length>";
267
+ inherits: true;
268
+ initial-value: 0px;
269
+ }
270
+
271
+ @property --cui-flow-controls-item-radius {
272
+ syntax: "<length-percentage>";
273
+ inherits: true;
274
+ initial-value: 0px;
275
+ }
276
+
277
+ @property --cui-flow-controls-padding {
278
+ syntax: "<length-percentage>";
279
+ inherits: true;
280
+ initial-value: 0px;
281
+ }
282
+
283
+ @property --cui-flow-controls-radius {
284
+ syntax: "<length-percentage>";
285
+ inherits: true;
286
+ initial-value: 0px;
287
+ }
288
+
289
+ @property --cui-flow-controls-background {
290
+ syntax: "<color>";
291
+ inherits: true;
292
+ initial-value: transparent;
293
+ }
294
+
295
+ @property --cui-flow-controls-border-color {
296
+ syntax: "<color>";
297
+ inherits: true;
298
+ initial-value: transparent;
299
+ }
300
+
301
+ @property --cui-flow-controls-shadow {
302
+ syntax: "*";
303
+ inherits: true;
304
+ }