@ctrl-ui/react 0.8.19 → 0.8.21

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 (84) 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 +17 -11
  7. package/dist/blocks/discussion-markdown-input.d.ts +3 -1
  8. package/dist/blocks/discussion-markdown-input.js +3 -1
  9. package/dist/blocks/discussion.d.ts +3 -1
  10. package/dist/blocks/discussion.js +9 -4
  11. package/dist/blocks/file-explorer.js +47 -25
  12. package/dist/blocks/settings.js +8 -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/markdown-editor-knobs.d.ts +1 -1
  22. package/dist/knob-contracts/markdown-editor-knobs.js +1 -0
  23. package/dist/knob-contracts/morphing-panel-knobs.d.ts +1 -1
  24. package/dist/knob-contracts/morphing-panel-knobs.js +2 -1
  25. package/dist/knob-contracts/resize-handle-knobs.d.ts +4 -0
  26. package/dist/knob-contracts/resize-handle-knobs.js +12 -0
  27. package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
  28. package/dist/knob-contracts/sidebar-knobs.js +10 -0
  29. package/dist/lib/canvas-grid.d.ts +4 -0
  30. package/dist/lib/canvas-grid.js +5 -0
  31. package/dist/markdown-editor/drag-handle.d.ts +6 -0
  32. package/dist/markdown-editor/drag-handle.js +126 -0
  33. package/dist/markdown-editor/suggestions.d.ts +12 -0
  34. package/dist/markdown-editor/suggestions.js +235 -0
  35. package/dist/markdown-editor/toolbar.js +6 -3
  36. package/dist/markdown-editor.js +14 -4
  37. package/dist/styles/index.css +6 -3
  38. package/dist/styles/recipes/chat-message-edit.css +105 -0
  39. package/dist/styles/recipes/flow.css +306 -0
  40. package/dist/styles/recipes/infinite-canvas.css +1 -1
  41. package/dist/styles/recipes/item.css +39 -2
  42. package/dist/styles/recipes/markdown-editor.css +44 -6
  43. package/dist/styles/recipes/morphing-panel.css +53 -12
  44. package/dist/styles/recipes/page-layout.css +11 -1
  45. package/dist/styles/recipes/resize-handle.css +126 -0
  46. package/dist/styles/recipes/sidebar-mobile.css +171 -0
  47. package/dist/styles/recipes/toolbar.css +8 -1
  48. package/dist/styles/theme.css +1 -0
  49. package/dist/ui/app-shell.js +1 -1
  50. package/dist/ui/color-picker.js +1 -1
  51. package/dist/ui/combobox.d.ts +1 -1
  52. package/dist/ui/command.js +1 -1
  53. package/dist/ui/dockable-panel.js +1 -1
  54. package/dist/ui/flow.d.ts +51 -0
  55. package/dist/ui/flow.js +153 -0
  56. package/dist/ui/infinite-canvas.js +3 -3
  57. package/dist/ui/item.d.ts +5 -3
  58. package/dist/ui/item.js +2 -1
  59. package/dist/ui/morphing-panel.d.ts +21 -4
  60. package/dist/ui/morphing-panel.js +152 -36
  61. package/dist/ui/resize-handle.d.ts +11 -0
  62. package/dist/ui/resize-handle.js +26 -0
  63. package/dist/ui/responsive-dialog.js +1 -1
  64. package/dist/ui/sidebar-menu.js +1 -1
  65. package/dist/ui/sidebar-mobile.d.ts +17 -0
  66. package/dist/ui/sidebar-mobile.js +70 -0
  67. package/dist/ui/sidebar-provider.d.ts +1 -0
  68. package/dist/ui/sidebar-provider.js +3 -0
  69. package/dist/ui/sidebar.d.ts +3 -1
  70. package/dist/ui/sidebar.js +100 -48
  71. package/dist/ui/toolbar.d.ts +3 -1
  72. package/dist/ui/toolbar.js +2 -1
  73. package/dist/ui/typography.d.ts +5 -2
  74. package/dist/ui/typography.js +5 -3
  75. package/package.json +5 -1
  76. package/dist/environment-variables.d.ts +0 -125
  77. package/dist/environment-variables.js +0 -512
  78. package/dist/hooks/use-environment-variables.d.ts +0 -50
  79. package/dist/hooks/use-environment-variables.js +0 -188
  80. package/dist/knob-contracts/environment-variables-knobs.d.ts +0 -4
  81. package/dist/knob-contracts/environment-variables-knobs.js +0 -12
  82. package/dist/lib/env-file.d.ts +0 -27
  83. package/dist/lib/env-file.js +0 -218
  84. package/dist/styles/recipes/environment-variables.css +0 -170
@@ -0,0 +1,235 @@
1
+ "use client";
2
+ import { LiveStatus } from "../ui/live-status.js";
3
+ import { isComposingKey, useTriggerMenu } from "../hooks/use-trigger-menu.js";
4
+ import { useMarkdownEditor } from "./context.js";
5
+ import { isEditorUrl } from "./extensions.js";
6
+ import { detectTrigger } from "../lib/trigger-detect.js";
7
+ import { TriggerMenu, TriggerMenuEmpty, TriggerMenuIcon, TriggerMenuItem, TriggerMenuList } from "../ui/trigger-menu.js";
8
+ import { useEffect, useEffectEvent, useRef } from "react";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
+ import { Heading1Icon, Heading2Icon, Heading3Icon, ListChecksIcon, ListIcon, ListOrderedIcon, MinusIcon, QuoteIcon, SquareCodeIcon, TextIcon, UserIcon } from "lucide-react";
11
+ //#region src/markdown-editor/suggestions.tsx
12
+ const blockCommands = [
13
+ {
14
+ id: "paragraph",
15
+ label: "Text",
16
+ keywords: ["paragraph"],
17
+ icon: /* @__PURE__ */ jsx(TextIcon, {}),
18
+ command: (chain) => chain.clearNodes().setParagraph()
19
+ },
20
+ {
21
+ id: "heading-1",
22
+ label: "Heading 1",
23
+ keywords: ["h1", "title"],
24
+ icon: /* @__PURE__ */ jsx(Heading1Icon, {}),
25
+ command: (chain) => chain.clearNodes().setHeading({ level: 1 })
26
+ },
27
+ {
28
+ id: "heading-2",
29
+ label: "Heading 2",
30
+ keywords: ["h2", "subtitle"],
31
+ icon: /* @__PURE__ */ jsx(Heading2Icon, {}),
32
+ command: (chain) => chain.clearNodes().setHeading({ level: 2 })
33
+ },
34
+ {
35
+ id: "heading-3",
36
+ label: "Heading 3",
37
+ keywords: ["h3"],
38
+ icon: /* @__PURE__ */ jsx(Heading3Icon, {}),
39
+ command: (chain) => chain.clearNodes().setHeading({ level: 3 })
40
+ },
41
+ {
42
+ id: "bullet-list",
43
+ label: "Bullet list",
44
+ icon: /* @__PURE__ */ jsx(ListIcon, {}),
45
+ command: (chain) => chain.clearNodes().toggleBulletList()
46
+ },
47
+ {
48
+ id: "ordered-list",
49
+ label: "Numbered list",
50
+ keywords: ["ordered"],
51
+ icon: /* @__PURE__ */ jsx(ListOrderedIcon, {}),
52
+ command: (chain) => chain.clearNodes().toggleOrderedList()
53
+ },
54
+ {
55
+ id: "task-list",
56
+ label: "Task list",
57
+ keywords: ["todo", "checklist"],
58
+ icon: /* @__PURE__ */ jsx(ListChecksIcon, {}),
59
+ command: (chain) => chain.clearNodes().toggleTaskList()
60
+ },
61
+ {
62
+ id: "quote",
63
+ label: "Quote",
64
+ keywords: ["blockquote"],
65
+ icon: /* @__PURE__ */ jsx(QuoteIcon, {}),
66
+ command: (chain) => chain.clearNodes().toggleBlockquote()
67
+ },
68
+ {
69
+ id: "code-block",
70
+ label: "Code block",
71
+ icon: /* @__PURE__ */ jsx(SquareCodeIcon, {}),
72
+ command: (chain) => chain.clearNodes().setCodeBlock()
73
+ },
74
+ {
75
+ id: "divider",
76
+ label: "Divider",
77
+ keywords: ["rule", "separator"],
78
+ icon: /* @__PURE__ */ jsx(MinusIcon, {}),
79
+ command: (chain) => chain.setHorizontalRule()
80
+ }
81
+ ];
82
+ function readTrigger(editor) {
83
+ const { selection } = editor.state;
84
+ const { $from } = selection;
85
+ if (!editor.isFocused || !selection.empty || !$from.parent.isTextblock || $from.parent.type.spec.code || editor.isActive("code") || editor.isActive("link")) return null;
86
+ const text = $from.parent.textBetween(0, $from.parentOffset, void 0, "");
87
+ const match = detectTrigger(text, ["/", "@"]);
88
+ if (!match || match.char === "/" && match.start !== 0) return null;
89
+ return {
90
+ ...match,
91
+ start: $from.start() + match.start,
92
+ end: $from.pos
93
+ };
94
+ }
95
+ function MarkdownEditorSuggestions({ mentions = [], onMentionSelect }) {
96
+ const activeItem = useRef(null);
97
+ const { editor, disabled, source } = useMarkdownEditor();
98
+ const enabled = Boolean(editor) && !disabled && !source;
99
+ const controller = useTriggerMenu({
100
+ triggers: [{
101
+ char: "/",
102
+ items: blockCommands
103
+ }, {
104
+ char: "@",
105
+ items: mentions
106
+ }],
107
+ onCommit: (item) => {
108
+ if (!editor || !enabled || item.disabled) return;
109
+ const match = readTrigger(editor);
110
+ if (!match) return;
111
+ const range = {
112
+ from: match.start,
113
+ to: match.end
114
+ };
115
+ if (match.char === "/" && item.command) item.command(editor.chain().focus().deleteRange(range)).run();
116
+ if (match.char === "@" && item.href && isEditorUrl(item.href)) {
117
+ editor.chain().focus().insertContentAt(range, [{
118
+ type: "text",
119
+ text: `@${item.label}`,
120
+ marks: [{
121
+ type: "link",
122
+ attrs: { href: item.href }
123
+ }]
124
+ }, {
125
+ type: "text",
126
+ text: " "
127
+ }]).run();
128
+ onMentionSelect?.({
129
+ ...item,
130
+ href: item.href
131
+ });
132
+ }
133
+ }
134
+ });
135
+ const report = useEffectEvent(() => {
136
+ if (!editor) return;
137
+ const match = enabled ? readTrigger(editor) : null;
138
+ const coords = match ? editor.view.coordsAtPos(match.end) : null;
139
+ controller.report(match, coords ? new DOMRect(coords.left, coords.top, 1, coords.bottom - coords.top) : null);
140
+ });
141
+ const handleKeyDown = useEffectEvent((event) => {
142
+ if (!editor || !enabled || isComposingKey(event) || event.metaKey || event.ctrlKey || event.altKey || !readTrigger(editor)) return;
143
+ if (controller.handleKeyDown(event.key)) {
144
+ event.preventDefault();
145
+ event.stopImmediatePropagation();
146
+ }
147
+ });
148
+ useEffect(() => {
149
+ if (!editor || !enabled) return;
150
+ const dom = editor.view.dom;
151
+ editor.on("transaction", report).on("focus", report).on("blur", report);
152
+ dom.addEventListener("keydown", handleKeyDown, true);
153
+ return () => {
154
+ editor.off("transaction", report).off("focus", report).off("blur", report);
155
+ dom.removeEventListener("keydown", handleKeyDown, true);
156
+ };
157
+ }, [editor, enabled]);
158
+ const open = enabled && controller.open;
159
+ useEffect(() => {
160
+ if (!editor || !open) return;
161
+ const owner = editor.view.dom.ownerDocument;
162
+ owner.addEventListener("scroll", report, {
163
+ capture: true,
164
+ passive: true
165
+ });
166
+ owner.defaultView?.addEventListener("resize", report);
167
+ return () => {
168
+ owner.removeEventListener("scroll", report, true);
169
+ owner.defaultView?.removeEventListener("resize", report);
170
+ };
171
+ }, [editor, open]);
172
+ const { activeIndex, activeChar, query } = controller;
173
+ useEffect(() => {
174
+ if (open) activeItem.current?.scrollIntoView({ block: "nearest" });
175
+ }, [
176
+ open,
177
+ activeIndex,
178
+ activeChar,
179
+ query
180
+ ]);
181
+ const { "aria-controls": controls, "aria-activedescendant": activeDescendant } = controller.inputAria;
182
+ useEffect(() => {
183
+ if (!editor || !enabled) return;
184
+ const dom = editor.view.dom;
185
+ const attributes = {
186
+ "aria-autocomplete": "list",
187
+ "aria-haspopup": "listbox",
188
+ "aria-controls": controls,
189
+ "aria-activedescendant": activeDescendant
190
+ };
191
+ for (const [name, value] of Object.entries(attributes)) if (value) dom.setAttribute(name, value);
192
+ else dom.removeAttribute(name);
193
+ return () => {
194
+ for (const name of Object.keys(attributes)) dom.removeAttribute(name);
195
+ };
196
+ }, [
197
+ editor,
198
+ enabled,
199
+ controls,
200
+ activeDescendant
201
+ ]);
202
+ const label = controller.activeChar === "@" ? "Mention people" : "Insert block";
203
+ const emptyLabel = controller.activeChar === "@" ? "No people found" : "No commands found";
204
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(LiveStatus, { message: open ? `${controller.items.length} ${label.toLowerCase()} suggestions` : "" }), /* @__PURE__ */ jsxs(TriggerMenu, {
205
+ open,
206
+ onOpenChange: controller.setOpen,
207
+ anchorRect: controller.anchorRect,
208
+ side: "bottom",
209
+ children: [controller.items.length === 0 ? /* @__PURE__ */ jsx(TriggerMenuEmpty, { children: emptyLabel }) : null, /* @__PURE__ */ jsx(TriggerMenuList, {
210
+ id: controller.listId,
211
+ "aria-label": label,
212
+ children: controller.items.map((item, index) => /* @__PURE__ */ jsxs(TriggerMenuItem, {
213
+ id: controller.optionId(index),
214
+ active: index === controller.activeIndex,
215
+ disabled: item.disabled || item.href !== void 0 && !isEditorUrl(item.href),
216
+ ref: index === controller.activeIndex ? activeItem : void 0,
217
+ onPointerMove: () => controller.setActiveIndex(index),
218
+ onClick: () => controller.select(item),
219
+ children: [
220
+ /* @__PURE__ */ jsx(TriggerMenuIcon, { children: item.icon ?? /* @__PURE__ */ jsx(UserIcon, {}) }),
221
+ /* @__PURE__ */ jsx("span", {
222
+ className: "min-w-0 flex-1 truncate",
223
+ children: item.label
224
+ }),
225
+ item.description ? /* @__PURE__ */ jsx("span", {
226
+ className: "truncate text-muted-foreground",
227
+ children: item.description
228
+ }) : null
229
+ ]
230
+ }, item.id))
231
+ })]
232
+ })] });
233
+ }
234
+ //#endregion
235
+ export { MarkdownEditorSuggestions };
@@ -1,14 +1,15 @@
1
1
  "use client";
2
2
  import { Button } from "../ui/button.js";
3
+ import { Text } from "../ui/typography.js";
3
4
  import { ChatComposerAttachment, ChatComposerAttachments } from "../chat-composer-attachment.js";
4
5
  import { Popover, PopoverContent, PopoverTitle, PopoverTrigger } from "../ui/popover.js";
5
- import { Input } from "../ui/input.js";
6
6
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip.js";
7
7
  import { useMarkdownEditor } from "./context.js";
8
8
  import { isEditorUrl } from "./extensions.js";
9
9
  import { markdownImageTypes } from "./uploads.js";
10
- import { Label } from "../ui/label.js";
10
+ import { Input } from "../ui/input.js";
11
11
  import { Toolbar, ToolbarButton, ToolbarSeparator } from "../ui/toolbar.js";
12
+ import { Label } from "../ui/label.js";
12
13
  import { useId, useRef, useState } from "react";
13
14
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
14
15
  import { BoldIcon, CodeIcon, Heading2Icon, ImageIcon, ItalicIcon, LinkIcon, ListChecksIcon, ListIcon, ListOrderedIcon, QuoteIcon, RedoIcon, SquareCodeIcon, StrikethroughIcon, UndoIcon } from "lucide-react";
@@ -97,6 +98,7 @@ function MarkdownEditorToolbar({ children }) {
97
98
  ];
98
99
  return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Toolbar, {
99
100
  "aria-label": "Markdown formatting",
101
+ chrome: "embedded",
100
102
  className: "flex w-full flex-wrap",
101
103
  children: [
102
104
  actions.map(({ label, Icon, active, run }) => /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
@@ -299,7 +301,8 @@ function MarkdownEditorImage() {
299
301
  }
300
302
  function MarkdownEditorUploads() {
301
303
  const { uploads, uploadError, retryUpload, removeUpload, disabled } = useMarkdownEditor();
302
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [uploadError ? /* @__PURE__ */ jsx("p", {
304
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [uploadError ? /* @__PURE__ */ jsx(Text, {
305
+ as: "p",
303
306
  role: "alert",
304
307
  children: uploadError
305
308
  }) : null, uploads.length > 0 ? /* @__PURE__ */ jsxs("div", {
@@ -2,6 +2,7 @@
2
2
  import { cn } from "./lib/cn.js";
3
3
  import { Textarea } from "./ui/textarea.js";
4
4
  import { MarkdownEditorContext, useMarkdownEditor } from "./markdown-editor/context.js";
5
+ import { MarkdownEditorDragHandle } from "./markdown-editor/drag-handle.js";
5
6
  import { createMarkdownCodec, hasMarkdownContent, markdownExtensions, requiresMarkdownSource } from "./markdown-editor/extensions.js";
6
7
  import { useImageUploads } from "./markdown-editor/use-image-uploads.js";
7
8
  import { useEffect, useMemo, useRef, useState } from "react";
@@ -140,6 +141,7 @@ function MarkdownEditor({ value, defaultValue = "", onValueChange, disabled = fa
140
141
  });
141
142
  }
142
143
  function MarkdownEditorContent({ label, placeholder, autoFocus = false, className }) {
144
+ const container = useRef(null);
143
145
  const { editor, disabled, source, sourceRequired, markdown, setMarkdown, setContentAttributes } = useMarkdownEditor();
144
146
  useEffect(() => {
145
147
  setContentAttributes({
@@ -177,15 +179,23 @@ function MarkdownEditorContent({ label, placeholder, autoFocus = false, classNam
177
179
  value: markdown,
178
180
  disabled: disabled || !editor,
179
181
  onChange: (event) => setMarkdown(event.target.value),
180
- rows: 5
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
+ }
181
190
  })]
182
191
  });
183
- return /* @__PURE__ */ jsx(EditorContent, {
184
- editor,
192
+ return /* @__PURE__ */ jsxs("div", {
193
+ ref: container,
185
194
  "data-control-ui": "markdown-editor",
186
195
  "data-control-family": "markdown-editor",
187
196
  "data-slot": "content",
188
- className
197
+ className,
198
+ children: [/* @__PURE__ */ jsx(EditorContent, { editor }), /* @__PURE__ */ jsx(MarkdownEditorDragHandle, { container })]
189
199
  });
190
200
  }
191
201
  //#endregion
@@ -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,10 @@
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
- @import "./type-scale.css";
55
57
  @import "./recipes/kbd.css";
56
58
  @import "./recipes/markdown.css";
57
59
  @import "./recipes/label.css";
@@ -66,8 +68,8 @@
66
68
  @import "./recipes/icon-picker.css";
67
69
  @import "./recipes/tabs.css";
68
70
  @import "./recipes/tabs-motion.css";
69
- @import "./recipes/environment-variables.css";
70
71
  @import "./recipes/filter-bar.css";
72
+ @import "./recipes/flow.css";
71
73
  @import "./recipes/gradient-editor.css";
72
74
  @import "./recipes/inline-attachment.css";
73
75
  @import "./recipes/source-badge.css";
@@ -77,6 +79,7 @@
77
79
  @import "./recipes/morphing-panel.css";
78
80
  @import "./recipes/pagination.css";
79
81
  @import "./recipes/phone-input.css";
82
+ @import "./recipes/resize-handle.css";
80
83
  @import "./recipes/rich-tooltip.css";
81
84
  @import "./recipes/skeleton.css";
82
85
  @import "./recipes/stepper.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
+ }