@ctrl-ui/react 0.8.19 → 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 (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 +304 -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,126 @@
1
+ @layer components {
2
+ :where([data-control-family="resize-handle"][data-slot="root"]) {
3
+ --cui-resize-handle-size: 10px;
4
+ --cui-resize-handle-hit-size: var(--target-min);
5
+ --cui-resize-handle-background: var(--background);
6
+ --cui-resize-handle-hover-background: oklch(from var(--primary) l c h / 0.1);
7
+ --cui-resize-handle-border-color: var(--primary);
8
+ --cui-resize-handle-border-width: var(--control-rim-width);
9
+ --cui-resize-handle-radius: 0px;
10
+ inline-size: var(--cui-resize-handle-hit-size);
11
+ block-size: var(--cui-resize-handle-hit-size);
12
+ translate: -50% -50%;
13
+ padding: 0;
14
+ border: 0;
15
+ background: transparent;
16
+ }
17
+
18
+ :where([data-control-family="resize-handle"][data-slot="root"])::before {
19
+ content: "";
20
+ position: absolute;
21
+ inset: 50% auto auto 50%;
22
+ inline-size: var(--cui-resize-handle-size);
23
+ block-size: var(--cui-resize-handle-size);
24
+ translate: -50% -50%;
25
+ border: var(--cui-resize-handle-border-width) solid var(--cui-resize-handle-border-color);
26
+ border-radius: var(--cui-resize-handle-radius);
27
+ background: var(--cui-resize-handle-background);
28
+ transition: background-color var(--duration-fast) var(--ease-standard);
29
+ }
30
+
31
+ @media (hover: hover) {
32
+ :where([data-control-family="resize-handle"][data-slot="root"]:hover)::before {
33
+ background: var(--cui-resize-handle-hover-background);
34
+ }
35
+ }
36
+
37
+ :where([data-control-family="resize-handle"][data-slot="root"][data-direction="n"]) {
38
+ top: 0;
39
+ left: 50%;
40
+ cursor: ns-resize;
41
+ }
42
+
43
+ :where([data-control-family="resize-handle"][data-slot="root"][data-direction="ne"]) {
44
+ top: 0;
45
+ left: 100%;
46
+ cursor: nesw-resize;
47
+ }
48
+
49
+ :where([data-control-family="resize-handle"][data-slot="root"][data-direction="e"]) {
50
+ top: 50%;
51
+ left: 100%;
52
+ cursor: ew-resize;
53
+ }
54
+
55
+ :where([data-control-family="resize-handle"][data-slot="root"][data-direction="se"]) {
56
+ top: 100%;
57
+ left: 100%;
58
+ cursor: nwse-resize;
59
+ }
60
+
61
+ :where([data-control-family="resize-handle"][data-slot="root"][data-direction="s"]) {
62
+ top: 100%;
63
+ left: 50%;
64
+ cursor: ns-resize;
65
+ }
66
+
67
+ :where([data-control-family="resize-handle"][data-slot="root"][data-direction="sw"]) {
68
+ top: 100%;
69
+ left: 0;
70
+ cursor: nesw-resize;
71
+ }
72
+
73
+ :where([data-control-family="resize-handle"][data-slot="root"][data-direction="w"]) {
74
+ top: 50%;
75
+ left: 0;
76
+ cursor: ew-resize;
77
+ }
78
+
79
+ :where([data-control-family="resize-handle"][data-slot="root"][data-direction="nw"]) {
80
+ top: 0;
81
+ left: 0;
82
+ cursor: nwse-resize;
83
+ }
84
+ }
85
+
86
+ @property --cui-resize-handle-size {
87
+ syntax: "<length>";
88
+ inherits: true;
89
+ initial-value: 0px;
90
+ }
91
+
92
+ @property --cui-resize-handle-hit-size {
93
+ syntax: "<length>";
94
+ inherits: true;
95
+ initial-value: 0px;
96
+ }
97
+
98
+ @property --cui-resize-handle-background {
99
+ syntax: "<color>";
100
+ inherits: true;
101
+ initial-value: transparent;
102
+ }
103
+
104
+ @property --cui-resize-handle-hover-background {
105
+ syntax: "<color>";
106
+ inherits: true;
107
+ initial-value: transparent;
108
+ }
109
+
110
+ @property --cui-resize-handle-border-color {
111
+ syntax: "<color>";
112
+ inherits: true;
113
+ initial-value: transparent;
114
+ }
115
+
116
+ @property --cui-resize-handle-border-width {
117
+ syntax: "<length>";
118
+ inherits: true;
119
+ initial-value: 0px;
120
+ }
121
+
122
+ @property --cui-resize-handle-radius {
123
+ syntax: "<length-percentage>";
124
+ inherits: true;
125
+ initial-value: 0px;
126
+ }
@@ -0,0 +1,171 @@
1
+ @layer components {
2
+ :where([data-control-family="sidebar"][data-slot="wrapper"]) {
3
+ --cui-sidebar-nav-height: 4.5rem;
4
+ --cui-sidebar-nav-background: var(--sidebar);
5
+ --cui-sidebar-nav-foreground: var(--sidebar-foreground);
6
+ --cui-sidebar-nav-border-color: var(--sidebar-border);
7
+ --cui-sidebar-nav-border-width: var(--control-rim-width);
8
+ --cui-sidebar-nav-shadow: var(--shadow-md);
9
+ --cui-sidebar-nav-item-radius: var(--radius-control);
10
+ --cui-sidebar-nav-item-active-background: var(--active-fill);
11
+ --cui-sidebar-nav-item-active-foreground: var(--foreground);
12
+ --cui-sidebar-nav-gap: calc(var(--spacing) * 3);
13
+ }
14
+
15
+ :where([data-control-family="sidebar"][data-slot="mobile-nav-container"]) {
16
+ --_sidebar-nav-space: calc(var(--cui-sidebar-nav-height) + env(safe-area-inset-bottom, 0px));
17
+ block-size: var(--_sidebar-nav-space);
18
+ }
19
+
20
+ :where([data-control-family="sidebar"][data-slot="mobile-nav"]) {
21
+ position: fixed;
22
+ inset-block-end: 0;
23
+ inset-inline: 0;
24
+ z-index: 30;
25
+ min-block-size: var(--_sidebar-nav-space);
26
+ gap: calc(var(--spacing) * 1);
27
+ padding: calc(var(--spacing) * 1) max(calc(var(--spacing) * 2), env(safe-area-inset-right, 0px))
28
+ calc(var(--spacing) * 1 + env(safe-area-inset-bottom, 0px)) max(calc(var(--spacing) * 2), env(safe-area-inset-left, 0px));
29
+ background-color: var(--cui-sidebar-nav-background);
30
+ color: var(--cui-sidebar-nav-foreground);
31
+ }
32
+
33
+ :where([data-control-family="sidebar"][data-slot="mobile-nav"][data-variant="bar"]) {
34
+ border-block-start: var(--cui-sidebar-nav-border-width) solid var(--cui-sidebar-nav-border-color);
35
+ }
36
+
37
+ :where([data-control-family="sidebar"][data-slot="mobile-nav-container"][data-layout="contained"] > [data-slot="mobile-nav"]) {
38
+ position: absolute;
39
+ }
40
+
41
+ :where([data-control-family="sidebar"][data-slot="mobile-nav"] [data-mobile-nav-item]) {
42
+ display: flex;
43
+ flex: 1 1 0;
44
+ flex-direction: column;
45
+ align-items: center;
46
+ justify-content: center;
47
+ gap: calc(var(--spacing) * 1);
48
+ min-inline-size: 48px;
49
+ min-block-size: 48px;
50
+ block-size: auto;
51
+ padding: calc(var(--spacing) * 1);
52
+ border-radius: var(--cui-sidebar-nav-item-radius);
53
+ background-color: transparent;
54
+ color: var(--cui-sidebar-nav-foreground);
55
+ font-size: var(--text-caption);
56
+ font-weight: var(--font-weight-medium);
57
+ text-decoration: none;
58
+ text-align: center;
59
+ }
60
+
61
+ :where([data-control-family="sidebar"][data-slot="mobile-nav"] [data-mobile-nav-item] > svg) {
62
+ inline-size: calc(var(--spacing) * 5);
63
+ block-size: calc(var(--spacing) * 5);
64
+ flex-shrink: 0;
65
+ }
66
+
67
+ :where([data-control-family="sidebar"][data-slot="mobile-nav"] [data-mobile-nav-item] > span) {
68
+ max-inline-size: 100%;
69
+ }
70
+
71
+ :where([data-control-family="sidebar"][data-slot="mobile-nav"] [data-mobile-nav-item][data-active]) {
72
+ background-color: var(--cui-sidebar-nav-item-active-background);
73
+ color: var(--cui-sidebar-nav-item-active-foreground);
74
+ }
75
+
76
+ :where([data-control-family="sidebar"][data-slot="mobile-nav"] [data-mobile-nav-item]:is(:disabled, [aria-disabled="true"])) {
77
+ pointer-events: none;
78
+ opacity: 0.5;
79
+ }
80
+
81
+ :where([data-control-family="sidebar"][data-slot="mobile-nav-container"][data-variant="floating"]) {
82
+ block-size: calc(var(--_sidebar-nav-space) + var(--cui-sidebar-nav-gap));
83
+ }
84
+
85
+ :where([data-control-family="sidebar"][data-slot="mobile-nav"][data-variant="floating"]) {
86
+ inset-inline-start: auto;
87
+ inset-inline-end: max(var(--cui-sidebar-nav-gap), env(safe-area-inset-right, 0px));
88
+ inset-block-end: calc(var(--cui-sidebar-nav-gap) + env(safe-area-inset-bottom, 0px));
89
+ min-block-size: 0;
90
+ padding: calc(var(--spacing) * 1);
91
+ border: var(--cui-sidebar-nav-border-width) solid var(--cui-sidebar-nav-border-color);
92
+ border-radius: var(--cui-sidebar-nav-item-radius);
93
+ box-shadow: var(--cui-sidebar-nav-shadow);
94
+ }
95
+
96
+ @media (width < 64rem) {
97
+ :where([data-control-family="sidebar"][data-slot="wrapper"]:has(> [data-slot="mobile-nav-container"])) {
98
+ flex-direction: column;
99
+ }
100
+
101
+ :where([data-control-family="sidebar"][data-slot="wrapper"]:has(> [data-slot="mobile-nav-container"]) > [data-slot="inset"]) {
102
+ scroll-padding-block-end: calc(var(--cui-sidebar-nav-height) + env(safe-area-inset-bottom, 0px));
103
+ }
104
+ }
105
+
106
+ @media (hover: hover) {
107
+ :where([data-control-family="sidebar"][data-slot="mobile-nav"] [data-mobile-nav-item]:hover) {
108
+ background-color: var(--cui-sidebar-nav-item-active-background);
109
+ color: var(--cui-sidebar-nav-item-active-foreground);
110
+ }
111
+ }
112
+ }
113
+
114
+ @property --cui-sidebar-nav-height {
115
+ syntax: "<length>";
116
+ inherits: true;
117
+ initial-value: 72px;
118
+ }
119
+
120
+ @property --cui-sidebar-nav-background {
121
+ syntax: "<color>";
122
+ inherits: true;
123
+ initial-value: transparent;
124
+ }
125
+
126
+ @property --cui-sidebar-nav-foreground {
127
+ syntax: "<color>";
128
+ inherits: true;
129
+ initial-value: transparent;
130
+ }
131
+
132
+ @property --cui-sidebar-nav-border-color {
133
+ syntax: "<color>";
134
+ inherits: true;
135
+ initial-value: transparent;
136
+ }
137
+
138
+ @property --cui-sidebar-nav-border-width {
139
+ syntax: "<length>";
140
+ inherits: true;
141
+ initial-value: 0px;
142
+ }
143
+
144
+ @property --cui-sidebar-nav-shadow {
145
+ syntax: "*";
146
+ inherits: true;
147
+ }
148
+
149
+ @property --cui-sidebar-nav-item-radius {
150
+ syntax: "<length-percentage>";
151
+ inherits: true;
152
+ initial-value: 0px;
153
+ }
154
+
155
+ @property --cui-sidebar-nav-item-active-background {
156
+ syntax: "<color>";
157
+ inherits: true;
158
+ initial-value: transparent;
159
+ }
160
+
161
+ @property --cui-sidebar-nav-item-active-foreground {
162
+ syntax: "<color>";
163
+ inherits: true;
164
+ initial-value: transparent;
165
+ }
166
+
167
+ @property --cui-sidebar-nav-gap {
168
+ syntax: "<length>";
169
+ inherits: true;
170
+ initial-value: 12px;
171
+ }
@@ -32,6 +32,13 @@
32
32
  gap: calc(var(--spacing) * 1);
33
33
  }
34
34
 
35
+ :where([data-control-family="toolbar"][data-slot="root"][data-chrome="embedded"]) {
36
+ --cui-toolbar-background: transparent;
37
+ --cui-toolbar-border-width: 0px;
38
+ --cui-toolbar-radius: 0px;
39
+ --cui-toolbar-shadow: none;
40
+ }
41
+
35
42
  :where([data-control-family="toolbar"][data-slot="root"][data-variant="floating"]) {
36
43
  --cui-toolbar-background: transparent;
37
44
  --cui-toolbar-border-color: transparent;
@@ -112,7 +119,7 @@
112
119
  }
113
120
  }
114
121
 
115
- :where([data-control-family="toolbar"][data-slot="button"]:is([data-pressed], [data-active])),
122
+ :where([data-control-family="toolbar"][data-slot="button"]:is([data-pressed], [data-active], [aria-pressed="true"])),
116
123
  :where([data-control-family="toolbar"][data-slot="link"]:is([data-pressed], [data-active])) {
117
124
  background-color: var(--cui-toolbar-item-active-background);
118
125
  color: var(--cui-toolbar-item-active-foreground);
@@ -168,6 +168,7 @@
168
168
  --popover: var(--scale-neutral-1);
169
169
  --popover-foreground: var(--scale-neutral-12);
170
170
  --canvas: var(--scale-neutral-1);
171
+ --canvas-grid-dot: oklch(from var(--foreground) l c h / 0.16);
171
172
 
172
173
  --radius: 0px;
173
174
  --shadow-size: 0;
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
- import { PageScrollContext, usePageScroll } from "./page-layout.js";
4
3
  import { SidebarProvider } from "./sidebar-provider.js";
5
4
  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 }) {
@@ -4,10 +4,10 @@ import { controlEffectsAttribute } from "../skin.js";
4
4
  import { useSkin } from "../skin-provider.js";
5
5
  import { Button } from "./button.js";
6
6
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
7
- import { Input } from "./input.js";
8
7
  import { useColorArea } from "../hooks/use-color-area.js";
9
8
  import { formatColor, getChannels, hsvaToRgba, parseColor, pointToHueSat, pointToSaturationValue, setChannel } from "../lib/color.js";
10
9
  import { TARGET_RATIO, contrastOf, fixColorForContrast, formatContrastRatio, nextFixLevel, wcagLevels } from "../lib/contrast.js";
10
+ import { Input } from "./input.js";
11
11
  import { NumberField, NumberFieldGroup, NumberFieldInput } from "./number-field.js";
12
12
  import { createContext, useContext, useEffect, useId, useRef, useState, useSyncExternalStore } from "react";
13
13
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -4,7 +4,7 @@ import { PopupKnobStyle } from "../knob-contracts/popup-knobs.js";
4
4
  import { CSSProperties, ComponentProps, ReactNode } from "react";
5
5
  import { Combobox as Combobox$1 } from "@base-ui/react/combobox";
6
6
  //#region src/ui/combobox.d.ts
7
- export type ComboboxProps<Value = string, Multiple extends boolean | undefined = false> = Pick<Combobox$1.Root.Props<Value, Multiple>, "children" | "multiple" | "value" | "defaultValue" | "onValueChange" | "inputValue" | "defaultInputValue" | "onInputValueChange" | "open" | "defaultOpen" | "onOpenChange" | "disabled" | "readOnly" | "required" | "name" | "autoHighlight" | "itemToStringLabel" | "isItemEqualToValue" | "filter" | "modal"> & {
7
+ export type ComboboxProps<Value = string, Multiple extends boolean | undefined = false> = Pick<Combobox$1.Root.Props<Value, Multiple>, "children" | "multiple" | "value" | "defaultValue" | "onValueChange" | "inputValue" | "defaultInputValue" | "onInputValueChange" | "open" | "defaultOpen" | "onOpenChange" | "disabled" | "readOnly" | "required" | "name" | "autoHighlight" | "itemToStringLabel" | "isItemEqualToValue" | "filter" | "limit" | "modal"> & {
8
8
  items?: readonly Value[];
9
9
  };
10
10
  export type ComboboxInputProps = Omit<Omit<ComponentProps<"input">, "size">, "style"> & {
@@ -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 { Dialog, DialogContent, DialogDescription, DialogTitle } from "./dialog.js";
7
6
  import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "./empty.js";
7
+ import { Dialog, DialogContent, DialogDescription, DialogTitle } from "./dialog.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";
@@ -3,9 +3,9 @@ import { cn } from "../lib/cn.js";
3
3
  import { Button } from "./button.js";
4
4
  import { LiveStatus } from "./live-status.js";
5
5
  import { ScrollArea } from "./scroll-area.js";
6
+ import { Drawer, DrawerBody, DrawerContent } from "./drawer.js";
6
7
  import { useIsMobile } from "../hooks/use-mobile.js";
7
8
  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,51 @@
1
+ import { FlowKnobStyle } from "../knob-contracts/flow-knobs.js";
2
+ import { CSSProperties, ComponentProps } from "react";
3
+ import { BaseEdgeProps, Edge, FitViewOptions, Handle, Node, Panel, ReactFlowProps } from "@xyflow/react";
4
+ //#region src/ui/flow.d.ts
5
+ export type FlowProps<NodeType extends Node = Node, EdgeType extends Edge = Edge> = Omit<ReactFlowProps<NodeType, EdgeType>, "colorMode" | "style"> & {
6
+ style?: CSSProperties & FlowKnobStyle;
7
+ };
8
+ export type FlowBackgroundProps = {
9
+ id?: string;
10
+ className?: string;
11
+ };
12
+ export type FlowPanelProps = Omit<ComponentProps<typeof Panel>, "style"> & {
13
+ style?: CSSProperties & FlowKnobStyle;
14
+ };
15
+ export type FlowControlsProps = Omit<FlowPanelProps, "children"> & {
16
+ fitViewOptions?: FitViewOptions;
17
+ };
18
+ export type FlowNodeProps = Omit<ComponentProps<"div">, "style"> & {
19
+ selected?: boolean;
20
+ style?: CSSProperties & FlowKnobStyle;
21
+ };
22
+ export type FlowNodePartProps = Omit<ComponentProps<"div">, "style"> & {
23
+ style?: CSSProperties & FlowKnobStyle;
24
+ };
25
+ export type FlowHandleProps = Omit<ComponentProps<typeof Handle>, "style"> & {
26
+ style?: CSSProperties & FlowKnobStyle;
27
+ };
28
+ export type FlowEdgeProps = Omit<BaseEdgeProps, "style"> & {
29
+ dashed?: boolean;
30
+ style?: CSSProperties & FlowKnobStyle;
31
+ };
32
+ export type FlowEdgeLabelProps = Omit<ComponentProps<"div">, "style"> & {
33
+ x: number;
34
+ y: number;
35
+ style?: CSSProperties & FlowKnobStyle;
36
+ };
37
+ export type FlowEdgeLabelChipProps = Omit<ComponentProps<"span">, "style"> & {
38
+ style?: CSSProperties & FlowKnobStyle;
39
+ };
40
+ export declare function Flow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(props: FlowProps<NodeType, EdgeType>): import("react").JSX.Element;
41
+ export declare function FlowBackground(props: FlowBackgroundProps): import("react").JSX.Element;
42
+ export declare function FlowPanel(props: FlowPanelProps): import("react").JSX.Element;
43
+ export declare function FlowControls({ position, fitViewOptions, className, ...props }: FlowControlsProps): import("react").JSX.Element;
44
+ export declare function FlowNode({ selected, className, ...props }: FlowNodeProps): import("react").JSX.Element;
45
+ export declare function FlowNodeHeader({ className, ...props }: FlowNodePartProps): import("react").JSX.Element;
46
+ export declare function FlowNodeBody({ className, ...props }: FlowNodePartProps): import("react").JSX.Element;
47
+ export declare function FlowHandle(props: FlowHandleProps): import("react").JSX.Element;
48
+ export declare function FlowEdge({ dashed, ...props }: FlowEdgeProps): import("react").JSX.Element;
49
+ export declare function FlowEdgeLabel({ x, y, className, style, ...props }: FlowEdgeLabelProps): import("react").JSX.Element;
50
+ export declare function FlowEdgeLabelChip({ className, ...props }: FlowEdgeLabelChipProps): import("react").JSX.Element;
51
+ //#endregion
@@ -0,0 +1,153 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { Button } from "./button.js";
4
+ import { prefersReducedMotion, readDurationMs } from "../lib/motion.js";
5
+ import "../lib/canvas-grid.js";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { MaximizeIcon, MinusIcon, PlusIcon } from "lucide-react";
8
+ import { Background, BackgroundVariant, BaseEdge, EdgeLabelRenderer, Handle, Panel, ReactFlow, useReactFlow, useStore } from "@xyflow/react";
9
+ //#region src/ui/flow.tsx
10
+ function viewportTransitionMs(element) {
11
+ return prefersReducedMotion(element) ? 0 : readDurationMs(element, "--duration-base");
12
+ }
13
+ function Flow(props) {
14
+ return /* @__PURE__ */ jsx(ReactFlow, {
15
+ defaultMarkerColor: null,
16
+ ...props,
17
+ "data-control-ui": "flow",
18
+ "data-control-family": "flow",
19
+ "data-slot": "root"
20
+ });
21
+ }
22
+ function FlowBackground(props) {
23
+ return /* @__PURE__ */ jsx(Background, {
24
+ ...props,
25
+ variant: BackgroundVariant.Dots,
26
+ gap: 24,
27
+ size: 2
28
+ });
29
+ }
30
+ function FlowPanel(props) {
31
+ return /* @__PURE__ */ jsx(Panel, {
32
+ ...props,
33
+ "data-control-ui": "flow",
34
+ "data-control-family": "flow",
35
+ "data-slot": "panel"
36
+ });
37
+ }
38
+ function FlowControls({ position = "bottom-left", fitViewOptions, className, ...props }) {
39
+ const { zoomIn, zoomOut, fitView } = useReactFlow();
40
+ const atMinZoom = useStore((state) => state.transform[2] <= state.minZoom);
41
+ const atMaxZoom = useStore((state) => state.transform[2] >= state.maxZoom);
42
+ return /* @__PURE__ */ jsxs(Panel, {
43
+ ...props,
44
+ position,
45
+ "data-control-ui": "flow",
46
+ "data-control-family": "flow",
47
+ "data-slot": "controls",
48
+ className: cn("flex items-center", className),
49
+ children: [
50
+ /* @__PURE__ */ jsx(Button, {
51
+ type: "button",
52
+ size: "sm",
53
+ variant: "ghost",
54
+ iconOnly: true,
55
+ disabled: atMinZoom,
56
+ "aria-label": "Zoom out",
57
+ onClick: (event) => zoomOut({ duration: viewportTransitionMs(event.currentTarget) }),
58
+ children: /* @__PURE__ */ jsx(MinusIcon, {})
59
+ }),
60
+ /* @__PURE__ */ jsx(Button, {
61
+ type: "button",
62
+ size: "sm",
63
+ variant: "ghost",
64
+ iconOnly: true,
65
+ disabled: atMaxZoom,
66
+ "aria-label": "Zoom in",
67
+ onClick: (event) => zoomIn({ duration: viewportTransitionMs(event.currentTarget) }),
68
+ children: /* @__PURE__ */ jsx(PlusIcon, {})
69
+ }),
70
+ /* @__PURE__ */ jsx(Button, {
71
+ type: "button",
72
+ size: "sm",
73
+ variant: "ghost",
74
+ iconOnly: true,
75
+ "aria-label": "Fit view",
76
+ onClick: (event) => fitView({
77
+ ...fitViewOptions,
78
+ duration: viewportTransitionMs(event.currentTarget)
79
+ }),
80
+ children: /* @__PURE__ */ jsx(MaximizeIcon, {})
81
+ })
82
+ ]
83
+ });
84
+ }
85
+ function FlowNode({ selected, className, ...props }) {
86
+ return /* @__PURE__ */ jsx("div", {
87
+ ...props,
88
+ "data-control-ui": "flow",
89
+ "data-control-family": "flow",
90
+ "data-slot": "node",
91
+ "data-selected": selected || void 0,
92
+ className: cn("flex flex-col", className)
93
+ });
94
+ }
95
+ function FlowNodeHeader({ className, ...props }) {
96
+ return /* @__PURE__ */ jsx("div", {
97
+ ...props,
98
+ "data-control-ui": "flow",
99
+ "data-control-family": "flow",
100
+ "data-slot": "node-header",
101
+ className: cn("flex items-center", className)
102
+ });
103
+ }
104
+ function FlowNodeBody({ className, ...props }) {
105
+ return /* @__PURE__ */ jsx("div", {
106
+ ...props,
107
+ "data-control-ui": "flow",
108
+ "data-control-family": "flow",
109
+ "data-slot": "node-body",
110
+ className: cn("flex flex-col", className)
111
+ });
112
+ }
113
+ function FlowHandle(props) {
114
+ return /* @__PURE__ */ jsx(Handle, {
115
+ ...props,
116
+ "data-control-ui": "flow",
117
+ "data-control-family": "flow",
118
+ "data-slot": "handle"
119
+ });
120
+ }
121
+ function FlowEdge({ dashed, ...props }) {
122
+ return /* @__PURE__ */ jsx(BaseEdge, {
123
+ ...props,
124
+ "data-control-ui": "flow",
125
+ "data-control-family": "flow",
126
+ "data-slot": "edge",
127
+ "data-dashed": dashed || void 0
128
+ });
129
+ }
130
+ function FlowEdgeLabel({ x, y, className, style, ...props }) {
131
+ return /* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsx("div", {
132
+ ...props,
133
+ "data-control-ui": "flow",
134
+ "data-control-family": "flow",
135
+ "data-slot": "edge-label",
136
+ className: cn("nodrag nopan pointer-events-auto absolute flex items-center", className),
137
+ style: {
138
+ ...style,
139
+ transform: `translate(-50%, -50%) translate(${x}px, ${y}px)`
140
+ }
141
+ }) });
142
+ }
143
+ function FlowEdgeLabelChip({ className, ...props }) {
144
+ return /* @__PURE__ */ jsx("span", {
145
+ ...props,
146
+ "data-control-ui": "flow",
147
+ "data-control-family": "flow",
148
+ "data-slot": "edge-label-chip",
149
+ className: cn("inline-flex max-w-40 items-center truncate", className)
150
+ });
151
+ }
152
+ //#endregion
153
+ export { Flow, FlowBackground, FlowControls, FlowEdge, FlowEdgeLabel, FlowEdgeLabelChip, FlowHandle, FlowNode, FlowNodeBody, FlowNodeHeader, FlowPanel };
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { Button } from "./button.js";
4
+ import "../lib/canvas-grid.js";
4
5
  import { createContext, use, useEffect, useEffectEvent, useRef, useState } from "react";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  import { MinusIcon, PlusIcon } from "lucide-react";
@@ -13,7 +14,6 @@ const DEFAULT_TRANSFORM = {
13
14
  const DEFAULT_MIN_SCALE = .35;
14
15
  const DEFAULT_MAX_SCALE = 2.5;
15
16
  const KEYBOARD_PAN_STEP = 32;
16
- const GRID_SIZE = 24;
17
17
  const WHEEL_ZOOM_SPEED = .004;
18
18
  const MAX_WHEEL_ZOOM_DELTA = 50;
19
19
  const CANVAS_KEY_SHORTCUTS = "ArrowLeft ArrowRight ArrowUp ArrowDown + - 0";
@@ -78,7 +78,7 @@ function useInfiniteCanvas() {
78
78
  return context;
79
79
  }
80
80
  function InfiniteCanvas({ transform: controlledTransform, defaultTransform = DEFAULT_TRANSFORM, onTransformChange, minScale: minScaleProp = DEFAULT_MIN_SCALE, maxScale: maxScaleProp = DEFAULT_MAX_SCALE, className, children, style, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onWheel, onKeyDown, "aria-label": ariaLabel = "Infinite canvas", "aria-keyshortcuts": ariaKeyShortcuts, ...props }) {
81
- const minScale = Math.max(.1, minScaleProp);
81
+ const minScale = minScaleProp > 0 ? minScaleProp : DEFAULT_MIN_SCALE;
82
82
  const maxScale = Math.max(minScale, maxScaleProp);
83
83
  const [uncontrolledTransform, setUncontrolledTransform] = useState(defaultTransform);
84
84
  const [panning, setPanning] = useState(false);
@@ -222,7 +222,7 @@ function InfiniteCanvas({ transform: controlledTransform, defaultTransform = DEF
222
222
  const canvasStyle = {
223
223
  ...style,
224
224
  backgroundPosition: `${transform.x}px ${transform.y}px`,
225
- backgroundSize: `${GRID_SIZE * transform.scale}px ${GRID_SIZE * transform.scale}px`
225
+ backgroundSize: `${24 * transform.scale}px ${24 * transform.scale}px`
226
226
  };
227
227
  return /* @__PURE__ */ jsx(InfiniteCanvasContext.Provider, {
228
228
  value: context,
package/dist/ui/item.d.ts CHANGED
@@ -27,6 +27,10 @@ export type ItemGroupProps = ComponentProps<"div"> & {
27
27
  style?: CSSProperties & ItemKnobStyle;
28
28
  };
29
29
  export type ItemSeparatorProps = ComponentProps<"div">;
30
+ export type ItemActionsProps = Omit<ComponentProps<"div">, "style"> & {
31
+ showOnHover?: boolean;
32
+ style?: CSSProperties & ItemKnobStyle;
33
+ };
30
34
  export declare function ItemGroup({ className, ...props }: ItemGroupProps): import("react").JSX.Element;
31
35
  export declare function ItemSeparator({ className, ...props }: ItemSeparatorProps): import("react").JSX.Element;
32
36
  export declare function Item({ variant, render, className, children, ...props }: ItemProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
@@ -36,9 +40,7 @@ export declare function ItemContent({ className, ...props }: ComponentProps<"div
36
40
  }): import("react").JSX.Element;
37
41
  export declare function ItemTitle({ render, className, ...props }: ItemTitleProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
38
42
  export declare function ItemDescription({ className, ...props }: ItemDescriptionProps): import("react").JSX.Element;
39
- export declare function ItemActions({ className, ...props }: ComponentProps<"div"> & {
40
- style?: CSSProperties & ItemKnobStyle;
41
- }): import("react").JSX.Element;
43
+ export declare function ItemActions({ showOnHover, className, ...props }: ItemActionsProps): import("react").JSX.Element;
42
44
  export declare function ItemHeader({ className, ...props }: ComponentProps<"div"> & {
43
45
  style?: CSSProperties & ItemKnobStyle;
44
46
  }): import("react").JSX.Element;
package/dist/ui/item.js CHANGED
@@ -83,11 +83,12 @@ function ItemDescription({ className, ...props }) {
83
83
  ...props
84
84
  });
85
85
  }
86
- function ItemActions({ className, ...props }) {
86
+ function ItemActions({ showOnHover, className, ...props }) {
87
87
  return /* @__PURE__ */ jsx("div", {
88
88
  "data-control-ui": "item",
89
89
  "data-control-family": "item",
90
90
  "data-slot": "actions",
91
+ "data-show-on-hover": showOnHover || void 0,
91
92
  className: cn("flex shrink-0 items-center", className),
92
93
  ...props
93
94
  });