@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
package/README.md CHANGED
@@ -73,8 +73,15 @@ Install a peer only when you import the component that needs it.
73
73
  | `ui/calendar` | `react-day-picker` |
74
74
  | `ui/code`, `ui/code-diff` | `@tanstack/react-virtual`, `diff`, `shiki` |
75
75
  | `ui/command` | `cmdk` |
76
+ | `ui/flow` | `@xyflow/react` |
76
77
  | `ui/markdown` | `streamdown` |
77
78
  | `ui/phone-input` | `react-phone-number-input`, `libphonenumber-js`, `zod` |
78
79
  | `ui/resizable` | `react-resizable-panels` |
79
80
  | `chat-composer` | `prosemirror-*` |
80
81
  | `email/email`, `email/templates` | `react-email` |
82
+
83
+ `ui/flow` also needs the React Flow structural stylesheet, loaded under the recipes:
84
+
85
+ ```css
86
+ @import "@xyflow/react/dist/base.css" layer(base);
87
+ ```
@@ -92,14 +92,14 @@ function ActionBarEdit({ value, children = "Edit", disabled, onClick, ...props }
92
92
  if (event.defaultPrevented || !onEdit) return;
93
93
  try {
94
94
  const nextValue = await resolveCopyValue(editValue);
95
- if (!nextValue) return;
95
+ if (nextValue === void 0) return;
96
96
  onEdit(nextValue);
97
97
  } catch (error) {
98
98
  onEditError?.(error);
99
99
  }
100
100
  }
101
101
  return /* @__PURE__ */ jsx(ActionBarItem, {
102
- disabled: disabled ?? (!editValue || !onEdit),
102
+ disabled: disabled ?? (editValue === void 0 || !onEdit),
103
103
  onClick: handleClick,
104
104
  ...props,
105
105
  children
@@ -2,6 +2,7 @@
2
2
  import { cn } from "./lib/cn.js";
3
3
  import { Button } from "./ui/button.js";
4
4
  import { Select, SelectContent, SelectItem, SelectTrigger } from "./ui/select.js";
5
+ import { Text } from "./ui/typography.js";
5
6
  import { createContext, use, useState } from "react";
6
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
8
  import { MicIcon } from "lucide-react";
@@ -118,8 +119,10 @@ function AudioDeviceSelectContent({ children, ...props }) {
118
119
  children: [/* @__PURE__ */ jsx("span", {
119
120
  className: "min-w-0 flex-1 truncate",
120
121
  children: deviceLabel(device, index)
121
- }), device.isDefault || device.deviceId === "default" ? /* @__PURE__ */ jsx("span", {
122
- className: "ms-3 text-caption text-muted-foreground",
122
+ }), device.isDefault || device.deviceId === "default" ? /* @__PURE__ */ jsx(Text, {
123
+ size: "caption",
124
+ tone: "muted",
125
+ className: "ms-3",
123
126
  children: "Default"
124
127
  }) : null]
125
128
  }, device.deviceId)),
@@ -2,6 +2,7 @@
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Button } from "../ui/button.js";
4
4
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible.js";
5
+ import { Heading, Text } from "../ui/typography.js";
5
6
  import { ChatLayout, ChatThread } from "../chat-layout.js";
6
7
  import { SidebarProvider, useSidebar } from "../ui/sidebar-provider.js";
7
8
  import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "../ui/sidebar-menu.js";
@@ -10,7 +11,6 @@ import { jsx, jsxs } from "react/jsx-runtime";
10
11
  import { BotIcon, ChevronRightIcon, CircleIcon, FolderIcon, PlusIcon, SearchIcon } from "lucide-react";
11
12
  //#region src/blocks/coding-agent.tsx
12
13
  function CodingAgentBlock({ appName = "Agent", brand, sidebarTop, navigation = [], projects, activeTaskId, activeTaskTitle = "New task", onNavigationSelect, onTaskSelect, onNewTask, onSearch, headerActions, sidebarFooter, children, layout = "viewport", className, style, ...props }) {
13
- const Heading = layout === "contained" ? "h2" : "h1";
14
14
  const providerStyle = {
15
15
  "--sidebar-width": "17.5rem",
16
16
  ...style
@@ -46,7 +46,10 @@ function CodingAgentBlock({ appName = "Agent", brand, sidebarTop, navigation = [
46
46
  "aria-hidden": "true"
47
47
  }),
48
48
  /* @__PURE__ */ jsx(Heading, {
49
- className: "min-w-0 flex-1 truncate text-label font-medium",
49
+ level: layout === "contained" ? 2 : 1,
50
+ size: "label",
51
+ weight: "medium",
52
+ className: "min-w-0 flex-1 truncate",
50
53
  title: activeTaskTitle,
51
54
  children: activeTaskTitle
52
55
  }),
@@ -88,8 +91,10 @@ function CodingAgentSidebar({ appName, brand, sidebarTop, navigation, projects,
88
91
  className: "size-3.5",
89
92
  "aria-hidden": "true"
90
93
  })
91
- }), /* @__PURE__ */ jsx("span", {
92
- className: "truncate text-label font-semibold",
94
+ }), /* @__PURE__ */ jsx(Text, {
95
+ size: "label",
96
+ weight: "semibold",
97
+ className: "truncate",
93
98
  title: appName,
94
99
  children: appName
95
100
  })]
@@ -214,12 +219,14 @@ function CodingAgentEmptyState({ title = "What should we build?", description, i
214
219
  "aria-hidden": "true"
215
220
  })
216
221
  }),
217
- /* @__PURE__ */ jsx("h2", {
218
- className: "text-heading-3 sm:text-heading-2",
222
+ /* @__PURE__ */ jsx(Heading, {
223
+ level: 2,
219
224
  children: title
220
225
  }),
221
- description ? /* @__PURE__ */ jsx("p", {
222
- className: "mt-2 max-w-xl text-pretty text-body text-muted-foreground",
226
+ description ? /* @__PURE__ */ jsx(Text, {
227
+ as: "p",
228
+ tone: "muted",
229
+ className: "mt-2 max-w-xl text-pretty",
223
230
  children: description
224
231
  }) : null,
225
232
  suggestions.length > 0 ? /* @__PURE__ */ jsx("div", {
@@ -236,12 +243,15 @@ function CodingAgentEmptyState({ title = "What should we build?", description, i
236
243
  className: "text-primary-text",
237
244
  children: suggestion.icon
238
245
  }),
239
- /* @__PURE__ */ jsx("span", {
240
- className: "text-body font-medium text-foreground",
246
+ /* @__PURE__ */ jsx(Text, {
247
+ weight: "medium",
248
+ tone: "foreground",
241
249
  children: suggestion.title
242
250
  }),
243
- suggestion.description ? /* @__PURE__ */ jsx("span", {
244
- className: "text-caption leading-5 text-muted-foreground",
251
+ suggestion.description ? /* @__PURE__ */ jsx(Text, {
252
+ size: "caption",
253
+ tone: "muted",
254
+ className: "leading-5",
245
255
  children: suggestion.description
246
256
  }) : null
247
257
  ]
@@ -1,12 +1,13 @@
1
1
  "use client";
2
2
  import { Button } from "../ui/button.js";
3
+ import { Heading } from "../ui/typography.js";
4
+ import { resizeDesignCanvasLayer } from "./design-canvas-data.js";
3
5
  import { InputGroup, InputGroupAddon } from "../ui/input-group.js";
4
6
  import { NumberField, NumberFieldGroup, NumberFieldInput, NumberFieldScrubArea } from "../ui/number-field.js";
5
7
  import { ColorPicker, ColorPickerArea, ColorPickerContent, ColorPickerEyeDropper, ColorPickerHue, ColorPickerInput, ColorPickerTrigger } from "../ui/color-picker.js";
6
8
  import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from "../ui/empty.js";
7
9
  import { Separator } from "../ui/separator.js";
8
10
  import { Toggle } from "../ui/toggle.js";
9
- import { resizeDesignCanvasLayer } from "./design-canvas-data.js";
10
11
  import { useState } from "react";
11
12
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
13
  import { BlendIcon, EyeIcon, EyeOffIcon, Link2Icon, MinusIcon, PlusIcon, RotateCwIcon, SquareRoundCornerIcon, Unlink2Icon } from "lucide-react";
@@ -136,8 +137,10 @@ function InspectorSection({ title, action, children }) {
136
137
  className: "flex flex-col gap-2 border-b px-3 py-3",
137
138
  children: [/* @__PURE__ */ jsxs("header", {
138
139
  className: "flex h-6 items-center justify-between",
139
- children: [/* @__PURE__ */ jsx("h3", {
140
- className: "text-caption font-medium",
140
+ children: [/* @__PURE__ */ jsx(Heading, {
141
+ level: 3,
142
+ size: "caption",
143
+ weight: "medium",
141
144
  children: title
142
145
  }), action]
143
146
  }), /* @__PURE__ */ jsx("div", {
@@ -2,14 +2,14 @@
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Button } from "../ui/button.js";
4
4
  import { ScrollArea } from "../ui/scroll-area.js";
5
+ import { Heading, Text } from "../ui/typography.js";
5
6
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip.js";
6
- import { Toolbar, ToolbarButton, ToolbarGroup, ToolbarSeparator } from "../ui/toolbar.js";
7
7
  import { Drawer, DrawerContent, DrawerTitle } from "../ui/drawer.js";
8
- import { InfiniteCanvas, InfiniteCanvasContent, InfiniteCanvasControls, InfiniteCanvasItem } from "../ui/infinite-canvas.js";
9
- import { ResizableFloatingPanel } from "../ui/resizable.js";
10
- import { Text } from "../ui/typography.js";
11
8
  import { createDesignCanvasLayer, designCanvasFillColor, drawnDesignCanvasBounds } from "./design-canvas-data.js";
12
9
  import { DesignCanvasInspector } from "./design-canvas-inspector.js";
10
+ import { InfiniteCanvas, InfiniteCanvasContent, InfiniteCanvasControls, InfiniteCanvasItem } from "../ui/infinite-canvas.js";
11
+ import { ResizableFloatingPanel } from "../ui/resizable.js";
12
+ import { Toolbar, ToolbarButton, ToolbarGroup, ToolbarSeparator } from "../ui/toolbar.js";
13
13
  import { useRef, useState } from "react";
14
14
  import { jsx, jsxs } from "react/jsx-runtime";
15
15
  import { CircleIcon, FrameIcon, LayersIcon, MousePointer2Icon, SlidersHorizontalIcon, SquareIcon, TypeIcon } from "lucide-react";
@@ -345,7 +345,7 @@ function DesignCanvasBlock({ layers, defaultLayers = [], onLayersChange, selecte
345
345
  });
346
346
  }
347
347
  function DesignCanvasPanel({ label, title, inDrawer = false, children }) {
348
- const titleClassName = "flex h-11 shrink-0 items-center border-b px-3 text-caption font-medium";
348
+ const titleClassName = "flex h-11 shrink-0 items-center border-b px-3";
349
349
  const titleText = /* @__PURE__ */ jsx("span", {
350
350
  className: "truncate",
351
351
  title,
@@ -355,9 +355,12 @@ function DesignCanvasPanel({ label, title, inDrawer = false, children }) {
355
355
  "aria-label": label,
356
356
  className: "flex min-h-0 flex-1 flex-col",
357
357
  children: [inDrawer ? /* @__PURE__ */ jsx(DrawerTitle, {
358
- className: titleClassName,
358
+ className: `${titleClassName} text-caption font-medium`,
359
359
  children: titleText
360
- }) : /* @__PURE__ */ jsx("h2", {
360
+ }) : /* @__PURE__ */ jsx(Heading, {
361
+ level: 2,
362
+ size: "caption",
363
+ weight: "medium",
361
364
  className: titleClassName,
362
365
  children: titleText
363
366
  }), /* @__PURE__ */ jsx(ScrollArea, {
@@ -411,8 +414,10 @@ function DesignCanvasLayerNode({ layer, selected, onSelect, onKeyDown, onLayerCh
411
414
  y: Math.round(position.y)
412
415
  })),
413
416
  children: [
414
- layer.kind === "frame" ? /* @__PURE__ */ jsx("span", {
415
- className: "absolute start-0 bottom-full mb-1 whitespace-nowrap text-caption text-muted-foreground",
417
+ layer.kind === "frame" ? /* @__PURE__ */ jsx(Text, {
418
+ size: "caption",
419
+ tone: "muted",
420
+ className: "absolute start-0 bottom-full mb-1 whitespace-nowrap",
416
421
  children: layer.name
417
422
  }) : null,
418
423
  layer.kind === "text" ? /* @__PURE__ */ jsx(Text, {
@@ -429,8 +434,9 @@ function DesignCanvasLayerNode({ layer, selected, onSelect, onKeyDown, onLayerCh
429
434
  borderRadius: layer.kind === "ellipse" ? "50%" : layer.cornerRadius
430
435
  }
431
436
  }),
432
- selected ? /* @__PURE__ */ jsxs("span", {
433
- className: "absolute inset-x-0 top-full mx-auto mt-1.5 w-max rounded-sm bg-primary px-1 text-micro whitespace-nowrap text-primary-foreground tabular-nums",
437
+ selected ? /* @__PURE__ */ jsxs(Text, {
438
+ size: "micro",
439
+ className: "absolute inset-x-0 top-full mx-auto mt-1.5 w-max rounded-sm bg-primary px-1 whitespace-nowrap text-primary-foreground tabular-nums",
434
440
  children: [
435
441
  layer.width,
436
442
  " × ",
@@ -1,11 +1,13 @@
1
1
  import { MarkdownImageUploader } from "../markdown-editor/uploads.js";
2
2
  import { MarkdownEditorStatus } from "../markdown-editor.js";
3
+ import { MarkdownEditorMention } from "../markdown-editor/suggestions.js";
3
4
  //#region src/blocks/discussion-markdown-input.d.ts
4
- export default function DiscussionMarkdownInput({ label, placeholder, autoFocus, onUploadImage, onStatusChange, blocked }: {
5
+ export default function DiscussionMarkdownInput({ label, placeholder, autoFocus, onUploadImage, mentions, onStatusChange, blocked }: {
5
6
  label: string;
6
7
  placeholder?: string;
7
8
  autoFocus?: boolean;
8
9
  onUploadImage?: MarkdownImageUploader;
10
+ mentions?: readonly MarkdownEditorMention[];
9
11
  onStatusChange: (status: MarkdownEditorStatus) => void;
10
12
  blocked: boolean;
11
13
  }): import("react").JSX.Element;
@@ -1,10 +1,11 @@
1
1
  "use client";
2
2
  import { useChatComposerContext } from "../chat-composer.js";
3
3
  import { MarkdownEditor, MarkdownEditorContent } from "../markdown-editor.js";
4
+ import { MarkdownEditorSuggestions } from "../markdown-editor/suggestions.js";
4
5
  import { MarkdownEditorToolbar, MarkdownEditorUploads } from "../markdown-editor/toolbar.js";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  //#region src/blocks/discussion-markdown-input.tsx
7
- function DiscussionMarkdownInput({ label, placeholder, autoFocus, onUploadImage, onStatusChange, blocked }) {
8
+ function DiscussionMarkdownInput({ label, placeholder, autoFocus, onUploadImage, mentions, onStatusChange, blocked }) {
8
9
  const composer = useChatComposerContext();
9
10
  return /* @__PURE__ */ jsxs(MarkdownEditor, {
10
11
  value: composer.value,
@@ -26,6 +27,7 @@ function DiscussionMarkdownInput({ label, placeholder, autoFocus, onUploadImage,
26
27
  placeholder,
27
28
  autoFocus
28
29
  }),
30
+ /* @__PURE__ */ jsx(MarkdownEditorSuggestions, { mentions }),
29
31
  /* @__PURE__ */ jsx(MarkdownEditorUploads, {})
30
32
  ]
31
33
  });
@@ -1,4 +1,5 @@
1
1
  import { MarkdownImageUploader } from "../markdown-editor/uploads.js";
2
+ import { MarkdownEditorMention } from "../markdown-editor/suggestions.js";
2
3
  import { ChatComposer } from "../chat-composer.js";
3
4
  import { ChatMessage } from "../chat-message.js";
4
5
  import { ComponentProps, ReactNode } from "react";
@@ -12,8 +13,9 @@ export type DiscussionComposerProps = Omit<ComponentProps<typeof ChatComposer>,
12
13
  autoFocus?: boolean;
13
14
  format?: "plain" | "markdown";
14
15
  onUploadImage?: MarkdownImageUploader;
16
+ mentions?: readonly MarkdownEditorMention[];
15
17
  };
16
- export declare function DiscussionComposer({ label, placeholder, submitLabel, secondaryAction, hint, autoFocus, format, onUploadImage, onSubmit, ...props }: DiscussionComposerProps): import("react").JSX.Element;
18
+ export declare function DiscussionComposer({ label, placeholder, submitLabel, secondaryAction, hint, autoFocus, format, onUploadImage, mentions, onSubmit, ...props }: DiscussionComposerProps): import("react").JSX.Element;
17
19
  export type DiscussionCommentProps = Omit<ComponentProps<typeof ChatMessage>, "from" | "layout" | "authorLabel" | "children"> & {
18
20
  author: string;
19
21
  avatar?: ReactNode;
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { Button } from "../ui/button.js";
3
+ import { Text } from "../ui/typography.js";
3
4
  import { ChatComposer, ChatComposerShell, ChatComposerSubmit, ChatComposerTextarea, ChatComposerToolbar } from "../chat-composer.js";
4
5
  import { ChatMessageReplies } from "../chat-message/social.js";
5
6
  import { ChatMessage, ChatMessageActions, ChatMessageAuthor, ChatMessageAvatar, ChatMessageBody, ChatMessageContent, ChatMessageFooter, ChatMessageHeader, ChatMessageRow, ChatMessageTime } from "../chat-message.js";
@@ -10,7 +11,7 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
11
  import { CornerDownRightIcon } from "lucide-react";
11
12
  //#region src/blocks/discussion.tsx
12
13
  const DiscussionMarkdownInput = lazy(() => import("./discussion-markdown-input.js"));
13
- function DiscussionComposer({ label, placeholder, submitLabel = "Post", secondaryAction, hint = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(KbdGroup, { children: [/* @__PURE__ */ jsx(Kbd, { children: "⌘" }), /* @__PURE__ */ jsx(Kbd, { children: "Enter" })] }), /* @__PURE__ */ jsx("span", { children: "to post" })] }), autoFocus, format = "plain", onUploadImage, onSubmit, ...props }) {
14
+ function DiscussionComposer({ label, placeholder, submitLabel = "Post", secondaryAction, hint = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(KbdGroup, { children: [/* @__PURE__ */ jsx(Kbd, { children: "⌘" }), /* @__PURE__ */ jsx(Kbd, { children: "Enter" })] }), /* @__PURE__ */ jsx("span", { children: "to post" })] }), autoFocus, format = "plain", onUploadImage, mentions, onSubmit, ...props }) {
14
15
  const [draft, setDraft] = useState(props.defaultValue ?? "");
15
16
  const value = props.value ?? draft;
16
17
  const [status, setStatus] = useState({
@@ -56,6 +57,7 @@ function DiscussionComposer({ label, placeholder, submitLabel = "Post", secondar
56
57
  placeholder,
57
58
  autoFocus,
58
59
  onUploadImage,
60
+ mentions,
59
61
  onStatusChange: setStatus,
60
62
  blocked
61
63
  })
@@ -65,13 +67,16 @@ function DiscussionComposer({ label, placeholder, submitLabel = "Post", secondar
65
67
  autoFocus,
66
68
  "aria-keyshortcuts": "Meta+Enter Control+Enter"
67
69
  }),
68
- error ? /* @__PURE__ */ jsx("p", {
70
+ error ? /* @__PURE__ */ jsx(Text, {
71
+ as: "p",
69
72
  role: "alert",
70
73
  className: "px-3",
71
74
  children: error
72
75
  }) : null,
73
- /* @__PURE__ */ jsxs(ChatComposerToolbar, { children: [/* @__PURE__ */ jsx("span", {
74
- className: "flex min-w-0 items-center gap-1.5 text-caption text-muted-foreground",
76
+ /* @__PURE__ */ jsxs(ChatComposerToolbar, { children: [/* @__PURE__ */ jsx(Text, {
77
+ size: "caption",
78
+ tone: "muted",
79
+ className: "flex min-w-0 items-center gap-1.5",
75
80
  children: hint
76
81
  }), /* @__PURE__ */ jsxs("span", {
77
82
  className: "flex items-center gap-2",
@@ -3,17 +3,18 @@ import { cn } from "../lib/cn.js";
3
3
  import { Button } from "../ui/button.js";
4
4
  import { LiveStatus } from "../ui/live-status.js";
5
5
  import { ScrollArea } from "../ui/scroll-area.js";
6
- import { InputGroup, InputGroupAddon } from "../ui/input-group.js";
7
- import { Input } from "../ui/input.js";
6
+ import { Heading, Text } from "../ui/typography.js";
7
+ import { Drawer, DrawerBody, DrawerContent, DrawerTitle, DrawerTrigger } from "../ui/drawer.js";
8
8
  import { SidebarProvider, useSidebar } from "../ui/sidebar-provider.js";
9
9
  import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "../ui/sidebar-menu.js";
10
10
  import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarInset, SidebarRail, SidebarTrigger } from "../ui/sidebar.js";
11
- import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "../ui/breadcrumb.js";
11
+ import { InputGroup, InputGroupAddon } from "../ui/input-group.js";
12
+ import { Input } from "../ui/input.js";
12
13
  import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "../ui/empty.js";
13
- import { Drawer, DrawerBody, DrawerContent, DrawerTitle, DrawerTrigger } from "../ui/drawer.js";
14
- import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
15
14
  import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "../ui/resizable.js";
16
15
  import { groupFileExplorerItems, orderFileExplorerEntries, resolveFileExplorer, searchFileExplorer } from "./file-explorer-data.js";
16
+ import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "../ui/breadcrumb.js";
17
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
17
18
  import { Fragment, useEffect, useId, useRef, useState } from "react";
18
19
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
19
20
  import { ChevronLeftIcon, ChevronRightIcon, FileIcon, FolderIcon, MoreHorizontalIcon, SearchIcon, XIcon } from "lucide-react";
@@ -81,7 +82,7 @@ function FileExplorerBlock({ locations, activeLocationId, defaultActiveLocationI
81
82
  children: [
82
83
  /* @__PURE__ */ jsx(FileExplorerHeader, {
83
84
  location: activeLocation,
84
- headingAs: contained ? "h2" : "h1",
85
+ headingLevel: contained ? 2 : 1,
85
86
  breadcrumbs: resolution.breadcrumbs,
86
87
  query,
87
88
  searchPlaceholder,
@@ -110,7 +111,7 @@ function FileExplorerBlock({ locations, activeLocationId, defaultActiveLocationI
110
111
  columns: resolution.columns,
111
112
  selectedEntry: resolution.selectedEntry,
112
113
  selectedPath: resolution.validPath,
113
- headingAs: contained ? "h3" : "h2",
114
+ headingLevel: contained ? 3 : 2,
114
115
  backLabel,
115
116
  detailsLabel,
116
117
  onNavigate: changePath,
@@ -164,7 +165,7 @@ function FileExplorerSidebar({ locations, activeLocationId, sidebarTop, sidebarF
164
165
  ]
165
166
  });
166
167
  }
167
- function FileExplorerHeader({ location, headingAs: Heading, breadcrumbs, query, searchPlaceholder, searchLabel, clearSearchLabel, backLabel, moreActionsLabel, canGoBack, headerActions, headerMenuItems, onBack, onBreadcrumbSelect, onQueryChange }) {
168
+ function FileExplorerHeader({ location, headingLevel, breadcrumbs, query, searchPlaceholder, searchLabel, clearSearchLabel, backLabel, moreActionsLabel, canGoBack, headerActions, headerMenuItems, onBack, onBreadcrumbSelect, onQueryChange }) {
168
169
  return /* @__PURE__ */ jsxs("header", {
169
170
  className: "shrink-0 border-b border-border/70 bg-card/35",
170
171
  children: [/* @__PURE__ */ jsxs("div", {
@@ -184,7 +185,10 @@ function FileExplorerHeader({ location, headingAs: Heading, breadcrumbs, query,
184
185
  })
185
186
  }),
186
187
  /* @__PURE__ */ jsx(Heading, {
187
- className: "min-w-0 flex-1 truncate text-label font-semibold",
188
+ level: headingLevel,
189
+ size: "label",
190
+ weight: "semibold",
191
+ className: "min-w-0 flex-1 truncate",
188
192
  title: location.label,
189
193
  children: location.label
190
194
  }),
@@ -245,7 +249,7 @@ function FileExplorerHeader({ location, headingAs: Heading, breadcrumbs, query,
245
249
  })]
246
250
  });
247
251
  }
248
- function FileExplorerColumns({ columns, selectedEntry, selectedPath, headingAs, backLabel, detailsLabel, onNavigate, onOpen }) {
252
+ function FileExplorerColumns({ columns, selectedEntry, selectedPath, headingLevel, backLabel, detailsLabel, onNavigate, onOpen }) {
249
253
  const rootRef = useRef(null);
250
254
  const optionElements = useRef(/* @__PURE__ */ new Map());
251
255
  const pendingFocus = useRef(null);
@@ -381,8 +385,10 @@ function FileExplorerColumns({ columns, selectedEntry, selectedPath, headingAs,
381
385
  className: "truncate",
382
386
  children: parentColumn.label
383
387
  })]
384
- }) : /* @__PURE__ */ jsx("span", {
385
- className: "min-w-0 truncate px-1 text-label font-medium",
388
+ }) : /* @__PURE__ */ jsx(Text, {
389
+ size: "label",
390
+ weight: "medium",
391
+ className: "min-w-0 truncate px-1",
386
392
  children: activeColumn?.label
387
393
  }), /* @__PURE__ */ jsxs(Drawer, { children: [/* @__PURE__ */ jsx(DrawerTrigger, {
388
394
  render: /* @__PURE__ */ jsx(Button, {
@@ -418,7 +424,7 @@ function FileExplorerColumns({ columns, selectedEntry, selectedPath, headingAs,
418
424
  minSize: "20%",
419
425
  children: /* @__PURE__ */ jsx(FileExplorerPreview, {
420
426
  entry: selectedEntry,
421
- headingAs
427
+ headingLevel
422
428
  })
423
429
  })]
424
430
  }) : null
@@ -502,10 +508,14 @@ function FileExplorerColumnView({ column, optionRef, onSelect, onOpen, onOptionK
502
508
  role: "group",
503
509
  "aria-labelledby": labelId,
504
510
  className: "grid min-w-0 gap-0.5",
505
- children: [/* @__PURE__ */ jsx("div", {
511
+ children: [/* @__PURE__ */ jsx(Text, {
506
512
  role: "presentation",
507
513
  id: labelId,
508
- className: "mb-1 px-2 text-caption font-semibold text-muted-foreground",
514
+ as: "div",
515
+ size: "caption",
516
+ weight: "semibold",
517
+ tone: "muted",
518
+ className: "mb-1 px-2",
509
519
  children: group.label
510
520
  }), options]
511
521
  }, group.label) : /* @__PURE__ */ jsx("div", {
@@ -517,7 +527,7 @@ function FileExplorerColumnView({ column, optionRef, onSelect, onOpen, onOptionK
517
527
  })
518
528
  });
519
529
  }
520
- function FileExplorerPreview({ entry, headingAs: Heading }) {
530
+ function FileExplorerPreview({ entry, headingLevel }) {
521
531
  if (!entry) return /* @__PURE__ */ jsx(Empty, {
522
532
  className: "h-full rounded-none bg-background/45",
523
533
  children: /* @__PURE__ */ jsxs(EmptyHeader, { children: [
@@ -533,7 +543,9 @@ function FileExplorerPreview({ entry, headingAs: Heading }) {
533
543
  entry,
534
544
  className: "px-6 py-10",
535
545
  title: /* @__PURE__ */ jsx(Heading, {
536
- className: "mt-4 max-w-full truncate text-heading-4",
546
+ level: headingLevel,
547
+ size: "heading-4",
548
+ className: "mt-4 max-w-full truncate",
537
549
  title: entry.name,
538
550
  children: entry.name
539
551
  })
@@ -549,8 +561,11 @@ function FileExplorerPreviewContent({ entry, title, className }) {
549
561
  children: /* @__PURE__ */ jsx(FileExplorerEntryIcon, { entry })
550
562
  }),
551
563
  title,
552
- entry.description ? /* @__PURE__ */ jsx("div", {
553
- className: "mt-1 text-caption text-muted-foreground",
564
+ entry.description ? /* @__PURE__ */ jsx(Text, {
565
+ as: "div",
566
+ size: "caption",
567
+ tone: "muted",
568
+ className: "mt-1",
554
569
  children: entry.description
555
570
  }) : null,
556
571
  entry.details && entry.details.length > 0 ? /* @__PURE__ */ jsx("dl", {
@@ -613,8 +628,11 @@ function FileExplorerSearchResults({ label, status, clearSearchLabel, results, o
613
628
  lockAxis: "x",
614
629
  children: /* @__PURE__ */ jsxs("div", {
615
630
  className: "mx-auto grid w-full max-w-3xl gap-1 p-4",
616
- children: [/* @__PURE__ */ jsx("div", {
617
- className: "mb-2 px-2 text-caption text-muted-foreground tabular-nums",
631
+ children: [/* @__PURE__ */ jsx(Text, {
632
+ as: "div",
633
+ size: "caption",
634
+ tone: "muted",
635
+ className: "mb-2 px-2 tabular-nums",
618
636
  children: status
619
637
  }), /* @__PURE__ */ jsx("div", {
620
638
  role: "listbox",
@@ -639,11 +657,15 @@ function FileExplorerSearchResults({ label, status, clearSearchLabel, results, o
639
657
  children: /* @__PURE__ */ jsx(FileExplorerEntryIcon, { entry: result.entry })
640
658
  }), /* @__PURE__ */ jsxs("span", {
641
659
  className: "min-w-0 flex-1",
642
- children: [/* @__PURE__ */ jsx("span", {
643
- className: "block truncate text-label text-foreground",
660
+ children: [/* @__PURE__ */ jsx(Text, {
661
+ size: "label",
662
+ tone: "foreground",
663
+ className: "block truncate",
644
664
  children: result.entry.name
645
- }), /* @__PURE__ */ jsx("span", {
646
- className: "block truncate text-caption text-muted-foreground",
665
+ }), /* @__PURE__ */ jsx(Text, {
666
+ size: "caption",
667
+ tone: "muted",
668
+ className: "block truncate",
647
669
  children: result.parents.join(" / ") || "Top level"
648
670
  })]
649
671
  })]
@@ -3,16 +3,16 @@ import { cn } from "../lib/cn.js";
3
3
  import { Button } from "../ui/button.js";
4
4
  import { LiveStatus } from "../ui/live-status.js";
5
5
  import { ScrollArea } from "../ui/scroll-area.js";
6
+ import { Heading, Text } from "../ui/typography.js";
6
7
  import { prefersReducedMotion } from "../lib/motion.js";
7
- import { InputGroup, InputGroupAddon } from "../ui/input-group.js";
8
- import { Input } from "../ui/input.js";
9
8
  import { SidebarProvider, useSidebar } from "../ui/sidebar-provider.js";
10
9
  import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "../ui/sidebar-menu.js";
11
10
  import { Sidebar, SidebarContent, SidebarHeader, SidebarInset, SidebarTrigger } from "../ui/sidebar.js";
11
+ import { InputGroup, InputGroupAddon } from "../ui/input-group.js";
12
+ import { Input } from "../ui/input.js";
12
13
  import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "../ui/empty.js";
13
- import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet } from "../ui/field.js";
14
- import { Heading } from "../ui/typography.js";
15
14
  import { searchSettings } from "./settings-data.js";
15
+ import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet } from "../ui/field.js";
16
16
  import { useEffect, useId, useState } from "react";
17
17
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
18
18
  import { ArrowLeftIcon, SearchIcon, XIcon } from "lucide-react";
@@ -119,8 +119,10 @@ function SettingsShell({ pages, currentPage, query, results, backAction, searchP
119
119
  className: "h-full min-h-0 min-w-0",
120
120
  children: [/* @__PURE__ */ jsxs("header", {
121
121
  className: "flex h-12 shrink-0 items-center gap-2 border-b px-3 lg:hidden",
122
- children: [/* @__PURE__ */ jsx(SidebarTrigger, {}), /* @__PURE__ */ jsx("span", {
123
- className: "truncate text-label font-medium",
122
+ children: [/* @__PURE__ */ jsx(SidebarTrigger, {}), /* @__PURE__ */ jsx(Text, {
123
+ size: "label",
124
+ weight: "medium",
125
+ className: "truncate",
124
126
  title: currentPage?.title,
125
127
  children: currentPage?.title ?? "Settings"
126
128
  })]
@@ -1,5 +1,5 @@
1
- import { ChatComposerEditorExtension } from "../types.js";
2
1
  import { TriggerConfig, TriggerMenuItemData } from "../../hooks/use-trigger-menu.js";
2
+ import { ChatComposerEditorExtension } from "../types.js";
3
3
  import { Command } from "prosemirror-state";
4
4
  //#region src/chat-composer-editor/extensions/mention.d.ts
5
5
  export declare const deleteMentionBackward: Command;
@@ -23,6 +23,19 @@ export type ChatMessageTimeProps = ChatMessagePartProps<"time"> & {
23
23
  export declare function ChatMessageTime({ className, ...props }: ChatMessageTimeProps): JSX.Element;
24
24
  export type ChatMessageContentProps = ChatMessagePartProps<"div">;
25
25
  export declare function ChatMessageContent({ className, ...props }: ChatMessageContentProps): JSX.Element;
26
+ export type ChatMessageEditableProps = Omit<ChatMessagePartProps<"div">, "onError"> & {
27
+ value: string;
28
+ /** Persist the text in the host. Reject to keep the draft open for retry. */
29
+ onSave: (value: string) => void | Promise<void>;
30
+ onSaveError?: (error: unknown) => void;
31
+ editorLabel?: string;
32
+ saveLabel?: string;
33
+ cancelLabel?: string;
34
+ savingLabel?: string;
35
+ saveErrorLabel?: string;
36
+ };
37
+ /** Plain-text editing in the message surface. Children replace the default Copy/Edit actions. */
38
+ export declare function ChatMessageEditable({ value, onSave, onSaveError, editorLabel, saveLabel, cancelLabel, savingLabel, saveErrorLabel, children, className, ...props }: ChatMessageEditableProps): JSX.Element;
26
39
  export type ChatMessagePendingProps = Omit<ChatMessagePartProps<"div">, "children">;
27
40
  /** Visual only: the pending transition is announced by `ChatMessage` through the thread's status region. */
28
41
  export declare function ChatMessagePending({ className, ...props }: ChatMessagePendingProps): JSX.Element | null;