@ctrl-ui/react 0.8.18 → 0.8.19

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 (50) hide show
  1. package/dist/blocks/coding-agent.js +1 -1
  2. package/dist/blocks/design-canvas-inspector.js +1 -1
  3. package/dist/blocks/design-canvas.js +9 -5
  4. package/dist/blocks/discussion-markdown-input.d.ts +12 -0
  5. package/dist/blocks/discussion-markdown-input.js +34 -0
  6. package/dist/blocks/discussion.d.ts +7 -3
  7. package/dist/blocks/discussion.js +77 -16
  8. package/dist/blocks/file-explorer.js +4 -4
  9. package/dist/blocks/settings.js +5 -3
  10. package/dist/knob-contracts/label-knobs.d.ts +4 -0
  11. package/dist/knob-contracts/label-knobs.js +4 -0
  12. package/dist/knob-contracts/markdown-editor-knobs.d.ts +4 -0
  13. package/dist/knob-contracts/markdown-editor-knobs.js +16 -0
  14. package/dist/knob-contracts/switch-knobs.d.ts +1 -1
  15. package/dist/knob-contracts/switch-knobs.js +1 -0
  16. package/dist/markdown-editor/context.d.ts +22 -0
  17. package/dist/markdown-editor/context.js +10 -0
  18. package/dist/markdown-editor/extensions.d.ts +10 -0
  19. package/dist/markdown-editor/extensions.js +139 -0
  20. package/dist/markdown-editor/toolbar.d.ts +9 -0
  21. package/dist/markdown-editor/toolbar.js +336 -0
  22. package/dist/markdown-editor/uploads.d.ts +23 -0
  23. package/dist/markdown-editor/uploads.js +32 -0
  24. package/dist/markdown-editor/use-image-uploads.d.ts +12 -0
  25. package/dist/markdown-editor/use-image-uploads.js +120 -0
  26. package/dist/markdown-editor.d.ts +27 -0
  27. package/dist/markdown-editor.js +192 -0
  28. package/dist/styles/index.css +4 -2
  29. package/dist/styles/recipes/code.css +4 -0
  30. package/dist/styles/recipes/field.css +1 -1
  31. package/dist/styles/recipes/label.css +19 -0
  32. package/dist/styles/recipes/markdown-editor.css +196 -0
  33. package/dist/styles/recipes/switch.css +17 -5
  34. package/dist/ui/app-shell.js +1 -1
  35. package/dist/ui/autocomplete.js +1 -1
  36. package/dist/ui/code.js +1 -0
  37. package/dist/ui/combobox.js +1 -1
  38. package/dist/ui/command.js +1 -1
  39. package/dist/ui/dockable-panel.js +1 -1
  40. package/dist/ui/label.d.ts +8 -0
  41. package/dist/ui/label.js +14 -0
  42. package/dist/ui/markdown.d.ts +2 -1
  43. package/dist/ui/markdown.js +3 -3
  44. package/dist/ui/responsive-dialog.js +1 -1
  45. package/dist/ui/sidebar-menu.js +1 -1
  46. package/dist/ui/sidebar.js +1 -1
  47. package/dist/ui/typography.d.ts +44 -0
  48. package/dist/ui/typography.js +59 -0
  49. package/dist/ui/use-switch-drag.js +1 -1
  50. package/package.json +37 -1
@@ -0,0 +1,192 @@
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 { createMarkdownCodec, hasMarkdownContent, markdownExtensions, requiresMarkdownSource } from "./markdown-editor/extensions.js";
6
+ import { useImageUploads } from "./markdown-editor/use-image-uploads.js";
7
+ import { useEffect, useMemo, useRef, useState } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { Slice } from "@tiptap/pm/model";
10
+ import { EditorState } from "@tiptap/pm/state";
11
+ import { EditorContent, useEditor } from "@tiptap/react";
12
+ //#region src/markdown-editor.tsx
13
+ function MarkdownEditor({ value, defaultValue = "", onValueChange, disabled = false, onUploadImage, maxImageBytes = 10485760, onStatusChange, children, className, ...props }) {
14
+ const [internalValue, setInternalValue] = useState(defaultValue);
15
+ const markdown = value ?? internalValue;
16
+ const [sourceMode, setSourceMode] = useState(false);
17
+ const [contentAttributes, setContentAttributes] = useState({
18
+ role: "textbox",
19
+ "aria-multiline": "true"
20
+ });
21
+ const [codec] = useState(createMarkdownCodec);
22
+ const sourceRequired = useMemo(() => requiresMarkdownSource(markdown, codec), [markdown, codec]);
23
+ const source = sourceMode || sourceRequired;
24
+ const lastValue = useRef(markdown);
25
+ const [extensions] = useState(markdownExtensions);
26
+ const handlers = useRef(null);
27
+ function setMarkdown(next) {
28
+ lastValue.current = next;
29
+ setInternalValue(next);
30
+ onValueChange?.(next);
31
+ }
32
+ const editor = useEditor({
33
+ extensions,
34
+ immediatelyRender: false,
35
+ shouldRerenderOnTransaction: false,
36
+ content: sourceRequired ? "" : markdown,
37
+ contentType: "markdown",
38
+ editable: !disabled,
39
+ onUpdate: ({ editor: currentEditor }) => setMarkdown(currentEditor.getMarkdown()),
40
+ editorProps: {
41
+ attributes: contentAttributes,
42
+ handlePaste: (view, event) => {
43
+ if (disabled) return false;
44
+ const files = Array.from(event.clipboardData?.files ?? []);
45
+ if (onUploadImage && files.length > 0) {
46
+ event.preventDefault();
47
+ handlers.current?.upload(files);
48
+ return true;
49
+ }
50
+ const text = event.clipboardData?.getData("text/plain");
51
+ if (!text || event.clipboardData?.getData("text/html") || requiresMarkdownSource(text, codec)) return false;
52
+ const document = view.state.schema.nodeFromJSON(codec.parse(text));
53
+ event.preventDefault();
54
+ view.dispatch(view.state.tr.replaceSelection(Slice.maxOpen(document.content)).scrollIntoView());
55
+ return true;
56
+ },
57
+ handleDrop: (view, event, _slice, moved) => {
58
+ const files = Array.from(event.dataTransfer?.files ?? []);
59
+ if (moved || !onUploadImage || disabled || files.length === 0) return false;
60
+ event.preventDefault();
61
+ const position = view.posAtCoords({
62
+ left: event.clientX,
63
+ top: event.clientY
64
+ })?.pos;
65
+ handlers.current?.upload(files, position);
66
+ return true;
67
+ }
68
+ }
69
+ });
70
+ const imageUploads = useImageUploads(editor, onUploadImage, maxImageBytes);
71
+ useEffect(() => {
72
+ handlers.current = imageUploads;
73
+ });
74
+ useEffect(() => {
75
+ if (!editor) return;
76
+ editor.setEditable(!disabled, false);
77
+ }, [editor, disabled]);
78
+ useEffect(() => {
79
+ if (!editor) return;
80
+ const externalReset = lastValue.current !== markdown;
81
+ if (externalReset) handlers.current?.cancelAll();
82
+ if (source) {
83
+ lastValue.current = markdown;
84
+ return;
85
+ }
86
+ if (editor.getMarkdown() !== markdown) {
87
+ editor.commands.setContent(markdown, {
88
+ contentType: "markdown",
89
+ emitUpdate: false
90
+ });
91
+ if (externalReset) {
92
+ editor.view.updateState(EditorState.create({
93
+ schema: editor.schema,
94
+ doc: editor.state.doc,
95
+ plugins: editor.state.plugins
96
+ }));
97
+ editor.view.dispatch(editor.state.tr);
98
+ }
99
+ }
100
+ lastValue.current = markdown;
101
+ }, [
102
+ editor,
103
+ markdown,
104
+ source
105
+ ]);
106
+ const isEmpty = source ? markdown.trim() === "" : !hasMarkdownContent(codec.parse(markdown));
107
+ const hasPendingUploads = imageUploads.uploads.length > 0;
108
+ useEffect(() => {
109
+ onStatusChange?.({
110
+ isEmpty,
111
+ hasPendingUploads
112
+ });
113
+ }, [
114
+ onStatusChange,
115
+ isEmpty,
116
+ hasPendingUploads
117
+ ]);
118
+ return /* @__PURE__ */ jsx(MarkdownEditorContext, {
119
+ value: {
120
+ editor,
121
+ disabled,
122
+ source,
123
+ markdown,
124
+ sourceRequired,
125
+ setSource: setSourceMode,
126
+ setMarkdown,
127
+ setContentAttributes,
128
+ canUpload: Boolean(onUploadImage),
129
+ ...imageUploads
130
+ },
131
+ children: /* @__PURE__ */ jsx("div", {
132
+ "data-control-ui": "markdown-editor",
133
+ "data-control-family": "markdown-editor",
134
+ "data-slot": "root",
135
+ "data-disabled": disabled ? "" : void 0,
136
+ className: cn("min-w-0", className),
137
+ ...props,
138
+ children
139
+ })
140
+ });
141
+ }
142
+ function MarkdownEditorContent({ label, placeholder, autoFocus = false, className }) {
143
+ const { editor, disabled, source, sourceRequired, markdown, setMarkdown, setContentAttributes } = useMarkdownEditor();
144
+ useEffect(() => {
145
+ setContentAttributes({
146
+ role: "textbox",
147
+ "aria-multiline": "true",
148
+ "aria-label": label,
149
+ "aria-disabled": String(disabled),
150
+ "data-placeholder": placeholder ?? ""
151
+ });
152
+ }, [
153
+ setContentAttributes,
154
+ label,
155
+ disabled,
156
+ placeholder
157
+ ]);
158
+ useEffect(() => {
159
+ if (editor && autoFocus && !source && !disabled) editor.commands.focus("end");
160
+ }, [
161
+ editor,
162
+ autoFocus,
163
+ source,
164
+ disabled
165
+ ]);
166
+ if (source || !editor) return /* @__PURE__ */ jsxs("div", {
167
+ "data-control-ui": "markdown-editor",
168
+ "data-control-family": "markdown-editor",
169
+ "data-slot": "source",
170
+ className,
171
+ children: [sourceRequired ? /* @__PURE__ */ jsx("p", {
172
+ role: "status",
173
+ children: "This content uses Markdown that is only available in source mode. Its source is preserved."
174
+ }) : null, /* @__PURE__ */ jsx(Textarea, {
175
+ "aria-label": label,
176
+ placeholder,
177
+ value: markdown,
178
+ disabled: disabled || !editor,
179
+ onChange: (event) => setMarkdown(event.target.value),
180
+ rows: 5
181
+ })]
182
+ });
183
+ return /* @__PURE__ */ jsx(EditorContent, {
184
+ editor,
185
+ "data-control-ui": "markdown-editor",
186
+ "data-control-family": "markdown-editor",
187
+ "data-slot": "content",
188
+ className
189
+ });
190
+ }
191
+ //#endregion
192
+ export { MarkdownEditor, MarkdownEditorContent };
@@ -51,7 +51,11 @@
51
51
  @import "./recipes/resizable.css";
52
52
  @import "./recipes/separator.css";
53
53
  @import "./recipes/toolbar.css";
54
+ @import "./type-scale.css";
54
55
  @import "./recipes/kbd.css";
56
+ @import "./recipes/markdown.css";
57
+ @import "./recipes/label.css";
58
+ @import "./recipes/markdown-editor.css";
55
59
  @import "./recipes/dockable-panel.css";
56
60
  @import "./recipes/item.css";
57
61
  @import "./recipes/dropzone.css";
@@ -68,7 +72,6 @@
68
72
  @import "./recipes/inline-attachment.css";
69
73
  @import "./recipes/source-badge.css";
70
74
  @import "./recipes/inline-citation.css";
71
- @import "./recipes/markdown.css";
72
75
  @import "./recipes/markdown-block.css";
73
76
  @import "./recipes/range.css";
74
77
  @import "./recipes/morphing-panel.css";
@@ -86,6 +89,5 @@
86
89
  @import "./recipes/popup-toast.css";
87
90
  @import "./recipes/transcript-divider.css";
88
91
  @import "./recipes/tree.css";
89
- @import "./type-scale.css";
90
92
  @import "./recipes/user-ask.css";
91
93
  @import "./recipes/user-ask-details.css";
@@ -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,19 @@
1
+ @layer components {
2
+ :where([data-control-family="label"][data-slot="root"]) {
3
+ --cui-label-root-foreground: var(--foreground);
4
+ color: var(--cui-label-root-foreground);
5
+ font-size: var(--text-label);
6
+ font-weight: var(--font-weight-medium);
7
+ }
8
+
9
+ :where([data-control-family="label"][data-slot="root"]:has(:disabled, [data-disabled])) {
10
+ cursor: not-allowed;
11
+ opacity: var(--disabled-opacity);
12
+ }
13
+ }
14
+
15
+ @property --cui-label-root-foreground {
16
+ syntax: "<color>";
17
+ inherits: true;
18
+ initial-value: transparent;
19
+ }
@@ -0,0 +1,196 @@
1
+ @layer components {
2
+ :where([data-control-family="markdown-editor"][data-slot="root"]) {
3
+ --cui-markdown-editor-background: var(--background);
4
+ --cui-markdown-editor-foreground: var(--foreground);
5
+ --cui-markdown-editor-border-width: var(--control-rim-width);
6
+ --cui-markdown-editor-border-color: var(--border);
7
+ --cui-markdown-editor-radius: var(--radius-panel);
8
+ --cui-markdown-editor-padding: calc(var(--spacing) * 3);
9
+ --cui-markdown-editor-min-height: calc(var(--spacing) * 32);
10
+ --cui-markdown-editor-max-height: 60dvh;
11
+ --cui-markdown-editor-gap: calc(var(--spacing) * 3);
12
+ --cui-markdown-editor-muted-foreground: var(--muted-foreground);
13
+ --cui-markdown-editor-code-background: var(--muted);
14
+ color: var(--cui-markdown-editor-foreground);
15
+ background: var(--cui-markdown-editor-background);
16
+ border: var(--cui-markdown-editor-border-width) solid var(--cui-markdown-editor-border-color);
17
+ border-radius: var(--cui-markdown-editor-radius);
18
+ overflow: clip;
19
+ font-size: var(--text-body);
20
+ line-height: var(--leading-relaxed);
21
+ }
22
+ :where([data-control-family="markdown-editor"][data-slot="content"] .tiptap) {
23
+ min-block-size: var(--cui-markdown-editor-min-height);
24
+ max-block-size: var(--cui-markdown-editor-max-height);
25
+ overflow: auto;
26
+ padding: var(--cui-markdown-editor-padding);
27
+ overflow-wrap: anywhere;
28
+ white-space: pre-wrap;
29
+ }
30
+ :where([data-control-family="markdown-editor"] .tiptap:focus-visible) {
31
+ outline: 2px solid var(--ring);
32
+ outline-offset: -2px;
33
+ }
34
+ :where([data-control-family="markdown-editor"][data-slot="source"]),
35
+ :where([data-control-family="markdown-editor"][data-slot="uploads"]) {
36
+ padding: var(--cui-markdown-editor-padding);
37
+ }
38
+ :where([data-control-family="markdown-editor"] .tiptap > * + *) {
39
+ margin-block-start: var(--cui-markdown-editor-gap);
40
+ }
41
+ :where([data-control-family="markdown-editor"] .tiptap:has(> p:only-child > br:only-child))::before {
42
+ content: attr(data-placeholder);
43
+ color: var(--cui-markdown-editor-muted-foreground);
44
+ pointer-events: none;
45
+ float: inline-start;
46
+ block-size: 0;
47
+ }
48
+ :where([data-control-family="markdown-editor"] .tiptap h1) {
49
+ font-size: var(--text-heading-2);
50
+ font-weight: var(--font-weight-semibold);
51
+ }
52
+ :where([data-control-family="markdown-editor"] .tiptap h2) {
53
+ font-size: var(--text-heading-3);
54
+ font-weight: var(--font-weight-semibold);
55
+ }
56
+ :where([data-control-family="markdown-editor"] .tiptap :is(h3, h4, h5, h6)) {
57
+ font-size: var(--text-heading-4);
58
+ font-weight: var(--font-weight-semibold);
59
+ }
60
+ :where([data-control-family="markdown-editor"] .tiptap strong) {
61
+ font-weight: var(--font-weight-semibold);
62
+ }
63
+ :where([data-control-family="markdown-editor"] .tiptap em) {
64
+ font-style: italic;
65
+ }
66
+ :where([data-control-family="markdown-editor"] .tiptap s) {
67
+ text-decoration: line-through;
68
+ }
69
+ :where([data-control-family="markdown-editor"] .tiptap a) {
70
+ text-decoration: underline;
71
+ color: var(--primary);
72
+ }
73
+ :where([data-control-family="markdown-editor"] .tiptap ul) {
74
+ list-style: disc;
75
+ padding-inline-start: calc(var(--spacing) * 6);
76
+ }
77
+ :where([data-control-family="markdown-editor"] .tiptap ol) {
78
+ list-style: decimal;
79
+ padding-inline-start: calc(var(--spacing) * 6);
80
+ }
81
+ :where([data-control-family="markdown-editor"] .tiptap ul[data-type="taskList"]) {
82
+ list-style: none;
83
+ padding-inline-start: 0;
84
+ }
85
+ :where([data-control-family="markdown-editor"] .tiptap li[data-type="taskItem"]) {
86
+ display: flex;
87
+ align-items: baseline;
88
+ gap: var(--cui-markdown-editor-gap);
89
+ }
90
+ :where([data-control-family="markdown-editor"] .tiptap li[data-type="taskItem"] > div) {
91
+ flex: 1;
92
+ min-inline-size: 0;
93
+ }
94
+ :where([data-control-family="markdown-editor"] .tiptap blockquote) {
95
+ border-inline-start: var(--cui-markdown-editor-border-width) solid var(--cui-markdown-editor-border-color);
96
+ padding-inline-start: var(--cui-markdown-editor-padding);
97
+ color: var(--cui-markdown-editor-muted-foreground);
98
+ }
99
+ :where([data-control-family="markdown-editor"] .tiptap :is(code, pre)) {
100
+ font-family: var(--font-mono);
101
+ background: var(--cui-markdown-editor-code-background);
102
+ border-radius: var(--radius-sm);
103
+ }
104
+ :where([data-control-family="markdown-editor"] .tiptap pre) {
105
+ padding: var(--cui-markdown-editor-padding);
106
+ overflow: auto;
107
+ }
108
+ :where([data-control-family="markdown-editor"] .tiptap img[src]) {
109
+ display: block;
110
+ max-inline-size: 100%;
111
+ block-size: auto;
112
+ border-radius: var(--radius-sm);
113
+ }
114
+ :where([data-control-family="markdown-editor"] .tiptap .ProseMirror-selectednode) {
115
+ outline: 2px solid var(--ring);
116
+ }
117
+ :where([data-control-family="markdown-editor"] .tiptap [data-image-upload]) {
118
+ padding: var(--cui-markdown-editor-padding);
119
+ border: var(--cui-markdown-editor-border-width) dashed var(--cui-markdown-editor-border-color);
120
+ color: var(--cui-markdown-editor-muted-foreground);
121
+ }
122
+ :where([data-control-family="markdown-editor"] .tiptap table) {
123
+ border-collapse: collapse;
124
+ inline-size: 100%;
125
+ }
126
+ :where([data-control-family="markdown-editor"] .tiptap :is(td, th)) {
127
+ border: var(--cui-markdown-editor-border-width) solid var(--cui-markdown-editor-border-color);
128
+ padding: var(--cui-markdown-editor-padding);
129
+ }
130
+ }
131
+
132
+ @property --cui-markdown-editor-background {
133
+ syntax: "<color>";
134
+ inherits: true;
135
+ initial-value: transparent;
136
+ }
137
+
138
+ @property --cui-markdown-editor-foreground {
139
+ syntax: "<color>";
140
+ inherits: true;
141
+ initial-value: transparent;
142
+ }
143
+
144
+ @property --cui-markdown-editor-border-color {
145
+ syntax: "<color>";
146
+ inherits: true;
147
+ initial-value: transparent;
148
+ }
149
+
150
+ @property --cui-markdown-editor-radius {
151
+ syntax: "<length-percentage>";
152
+ inherits: true;
153
+ initial-value: 0px;
154
+ }
155
+
156
+ @property --cui-markdown-editor-padding {
157
+ syntax: "<length>";
158
+ inherits: true;
159
+ initial-value: 0px;
160
+ }
161
+
162
+ @property --cui-markdown-editor-min-height {
163
+ syntax: "<length>";
164
+ inherits: true;
165
+ initial-value: 0px;
166
+ }
167
+
168
+ @property --cui-markdown-editor-max-height {
169
+ syntax: "<length>";
170
+ inherits: true;
171
+ initial-value: 0px;
172
+ }
173
+
174
+ @property --cui-markdown-editor-gap {
175
+ syntax: "<length>";
176
+ inherits: true;
177
+ initial-value: 0px;
178
+ }
179
+
180
+ @property --cui-markdown-editor-muted-foreground {
181
+ syntax: "<color>";
182
+ inherits: true;
183
+ initial-value: transparent;
184
+ }
185
+
186
+ @property --cui-markdown-editor-code-background {
187
+ syntax: "<color>";
188
+ inherits: true;
189
+ initial-value: transparent;
190
+ }
191
+
192
+ @property --cui-markdown-editor-border-width {
193
+ syntax: "<length>";
194
+ inherits: true;
195
+ initial-value: 0px;
196
+ }
@@ -4,11 +4,13 @@
4
4
  --cui-switch-height: calc(var(--spacing) * 5);
5
5
  --cui-switch-padding: calc(var(--spacing) * 0.5);
6
6
  --cui-switch-thumb-inline-size: calc(var(--spacing) * 4);
7
- --cui-switch-thumb-press-scale: 1.25 1;
7
+ --cui-switch-thumb-press-inline-size: calc(var(--cui-switch-thumb-inline-size) * 1.25);
8
+ --cui-switch-thumb-press-scale: 1;
8
9
  --cui-switch-thumb-backdrop-filter: none;
9
10
  --_switch-direction: 1;
10
11
  --_switch-progress: 0;
11
- --_switch-travel: calc(var(--cui-switch-inline-size) - var(--cui-switch-padding) * 2 - var(--cui-switch-thumb-inline-size));
12
+ --_switch-thumb-inline-size: var(--cui-switch-thumb-inline-size);
13
+ --_switch-travel: calc(var(--cui-switch-inline-size) - var(--cui-switch-padding) * 2 - var(--_switch-thumb-inline-size));
12
14
  block-size: var(--cui-switch-height);
13
15
  inline-size: var(--cui-switch-inline-size);
14
16
  padding: var(--cui-switch-padding);
@@ -79,13 +81,13 @@
79
81
  border-radius: var(--cui-switch-thumb-radius);
80
82
  background: var(--cui-switch-thumb-background);
81
83
  box-shadow: var(--cui-switch-thumb-shadow);
82
- inline-size: var(--cui-switch-thumb-inline-size);
84
+ inline-size: var(--_switch-thumb-inline-size);
83
85
  block-size: calc(var(--cui-switch-height) - var(--cui-switch-padding) * 2);
84
86
  translate: calc(var(--_switch-direction) * var(--_switch-travel) * var(--_switch-progress)) 0;
85
87
  transform-origin: left center;
86
88
  -webkit-backdrop-filter: var(--cui-switch-thumb-backdrop-filter);
87
89
  backdrop-filter: var(--cui-switch-thumb-backdrop-filter);
88
- transition-property: translate, scale, background-color, box-shadow;
90
+ transition-property: translate, inline-size, scale, background-color, box-shadow;
89
91
  transition-duration: var(--duration-base);
90
92
  transition-timing-function: var(--ease-emphasized);
91
93
  }
@@ -136,6 +138,10 @@
136
138
  transform-origin: left center;
137
139
  }
138
140
 
141
+ :where([data-control-family="switch"][data-slot="root"]:is(:active, [data-dragging]):not([data-disabled], [data-readonly])) {
142
+ --_switch-thumb-inline-size: var(--cui-switch-thumb-press-inline-size);
143
+ }
144
+
139
145
  :where(
140
146
  [data-control-family="switch"][data-slot="root"]:is(:active, [data-dragging]):not([data-disabled], [data-readonly]) [data-slot="thumb"]
141
147
  ) {
@@ -157,7 +163,7 @@
157
163
  }
158
164
 
159
165
  :where([data-control-family="switch"][data-slot="root"][data-dragging] [data-slot="thumb"]) {
160
- transition-property: scale, background-color, box-shadow;
166
+ transition-property: inline-size, scale, background-color, box-shadow;
161
167
  }
162
168
  }
163
169
 
@@ -255,6 +261,12 @@
255
261
  initial-value: 16px;
256
262
  }
257
263
 
264
+ @property --cui-switch-thumb-press-inline-size {
265
+ syntax: "<length>";
266
+ inherits: true;
267
+ initial-value: 20px;
268
+ }
269
+
258
270
  @property --cui-switch-thumb-press-scale {
259
271
  syntax: "*";
260
272
  inherits: true;
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
+ import { PageScrollContext, usePageScroll } from "./page-layout.js";
3
4
  import { SidebarProvider } from "./sidebar-provider.js";
4
5
  import { SidebarInset } from "./sidebar.js";
5
- import { PageScrollContext, usePageScroll } from "./page-layout.js";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  //#region src/ui/app-shell.tsx
8
8
  function AppShell({ scroll = "auto", layout = "viewport", className, ...props }) {
@@ -85,7 +85,7 @@ function AutocompleteContent({ className, children, sideOffset = 6, ...props })
85
85
  function AutocompleteList({ className, children, ...props }) {
86
86
  return /* @__PURE__ */ jsx(ScrollArea, {
87
87
  className: "w-full",
88
- viewportClassName: "overscroll-contain",
88
+ viewportClassName: "overscroll-y-contain",
89
89
  maxHeight: "min(18rem, var(--available-height))",
90
90
  lockAxis: "x",
91
91
  children: /* @__PURE__ */ jsx(Autocomplete$1.List, {
package/dist/ui/code.js CHANGED
@@ -112,6 +112,7 @@ function CodeCopy({ value, copiedLabel, copiedAriaLabel = "Copied to clipboard",
112
112
  }
113
113
  function CodeFloatingCopy({ className, ...props }) {
114
114
  return /* @__PURE__ */ jsx(CodeCopy, {
115
+ variant: "surface",
115
116
  "data-code-floating": "true",
116
117
  className: cn("absolute z-10", className),
117
118
  ...props
@@ -115,7 +115,7 @@ function ComboboxContent({ className, children, sideOffset = 6, anchor, ...props
115
115
  function ComboboxList({ className, children, ...props }) {
116
116
  return /* @__PURE__ */ jsx(ScrollArea, {
117
117
  className: "w-full",
118
- viewportClassName: "overscroll-contain",
118
+ viewportClassName: "overscroll-y-contain",
119
119
  maxHeight: "min(18rem, var(--available-height))",
120
120
  lockAxis: "x",
121
121
  children: /* @__PURE__ */ jsx(Combobox$1.List, {
@@ -3,8 +3,8 @@ import { cn } from "../lib/cn.js";
3
3
  import { LiveStatus } from "./live-status.js";
4
4
  import { ScrollArea } from "./scroll-area.js";
5
5
  import { popupItemStructureClasses } from "../surface-variants.js";
6
- import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "./empty.js";
7
6
  import { Dialog, DialogContent, DialogDescription, DialogTitle } from "./dialog.js";
7
+ import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "./empty.js";
8
8
  import { useEffect, useRef } from "react";
9
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
10
  import { Command as Command$1, useCommandState } from "cmdk";
@@ -4,8 +4,8 @@ import { Button } from "./button.js";
4
4
  import { LiveStatus } from "./live-status.js";
5
5
  import { ScrollArea } from "./scroll-area.js";
6
6
  import { useIsMobile } from "../hooks/use-mobile.js";
7
- import { Drawer, DrawerBody, DrawerContent } from "./drawer.js";
8
7
  import { clampDockablePanelPosition, dockablePanelSideAt, dockablePanelSideForKey, oppositeDockablePanelSide } from "./dockable-panel-geometry.js";
8
+ import { Drawer, DrawerBody, DrawerContent } from "./drawer.js";
9
9
  import { createContext, use, useEffect, useEffectEvent, useId, useRef, useState } from "react";
10
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
11
  //#region src/ui/dockable-panel.tsx
@@ -0,0 +1,8 @@
1
+ import { LabelKnobStyle } from "../knob-contracts/label-knobs.js";
2
+ import { CSSProperties, ComponentProps } from "react";
3
+ //#region src/ui/label.d.ts
4
+ export type LabelProps = Omit<ComponentProps<"label">, "style"> & {
5
+ style?: CSSProperties & LabelKnobStyle;
6
+ };
7
+ export declare function Label({ htmlFor, children, ...props }: LabelProps): import("react").JSX.Element;
8
+ //#endregion
@@ -0,0 +1,14 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ //#region src/ui/label.tsx
3
+ function Label({ htmlFor, children, ...props }) {
4
+ return /* @__PURE__ */ jsx("label", {
5
+ ...props,
6
+ htmlFor,
7
+ "data-control-ui": "label",
8
+ "data-control-family": "label",
9
+ "data-slot": "root",
10
+ children
11
+ });
12
+ }
13
+ //#endregion
14
+ export { Label };
@@ -6,9 +6,10 @@ export type MarkdownRootProps = Omit<ComponentProps<"div">, "style"> & {
6
6
  };
7
7
  export type MarkdownProps = Omit<MarkdownRootProps, "children"> & {
8
8
  content: string;
9
+ mode?: "streaming" | "static";
9
10
  };
10
11
  export declare function MarkdownRoot({ className, ...props }: MarkdownRootProps): import("react").JSX.Element;
11
12
  export declare function MarkdownFlow(props: MarkdownRootProps): import("react").JSX.Element;
12
- export declare function Markdown({ content, className, ...props }: MarkdownProps): import("react").JSX.Element;
13
+ export declare function Markdown({ content, mode, className, ...props }: MarkdownProps): import("react").JSX.Element;
13
14
  export type MarkdownComponentProps = ComponentProps<typeof Markdown>;
14
15
  //#endregion
@@ -21,13 +21,13 @@ function MarkdownFlow(props) {
21
21
  ...props
22
22
  });
23
23
  }
24
- function Markdown({ content, className, ...props }) {
24
+ function Markdown({ content, mode = "streaming", className, ...props }) {
25
25
  return /* @__PURE__ */ jsx(MarkdownRoot, {
26
26
  className,
27
27
  ...props,
28
28
  children: /* @__PURE__ */ jsx(MarkdownFlow, { children: /* @__PURE__ */ jsx(Streamdown, {
29
- mode: "streaming",
30
- parseIncompleteMarkdown: true,
29
+ mode,
30
+ parseIncompleteMarkdown: mode === "streaming",
31
31
  controls: false,
32
32
  components: markdownComponents,
33
33
  className: "cui-markdown-streamdown [&>[data-control-family]]:[margin-block:revert-layer]",
@@ -2,8 +2,8 @@
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Button } from "./button.js";
4
4
  import { PopupCloseButton } from "../popup-parts.js";
5
- import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from "./drawer.js";
6
5
  import { Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "./dialog.js";
6
+ import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from "./drawer.js";
7
7
  import { createContext, useContext, useState, useSyncExternalStore } from "react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region src/ui/responsive-dialog.tsx
@@ -2,8 +2,8 @@
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { useSkin } from "../skin-provider.js";
4
4
  import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
5
- import { SidebarSurfaceContext, useSidebar } from "./sidebar-provider.js";
6
5
  import { TrackHighlight } from "../extensions/track-highlight.js";
6
+ import { SidebarSurfaceContext, useSidebar } from "./sidebar-provider.js";
7
7
  import { createContext, useContext, useRef, useState } from "react";
8
8
  import { useRender } from "@base-ui/react/use-render";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -257,7 +257,7 @@ function SidebarContent({ className, children, ...props }) {
257
257
  "data-slot": "content",
258
258
  className: cn("min-h-0 flex-1 group-data-[collapsible=icon]:overflow-hidden", className),
259
259
  lockAxis: "x",
260
- viewportClassName: "overscroll-contain",
260
+ viewportClassName: "overscroll-y-contain",
261
261
  ...props,
262
262
  children: /* @__PURE__ */ jsx("div", {
263
263
  "data-control-ui": "sidebar",