@joycostudio/studio 0.0.0-stage → 0.1.0
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.
- package/README.md +271 -2
- package/dist/chunk-QKEAOM2U.js +13 -0
- package/dist/chunk-QKEAOM2U.js.map +1 -0
- package/dist/chunk-VYNEG5Z7.js +75 -0
- package/dist/chunk-VYNEG5Z7.js.map +1 -0
- package/dist/cli.js +687 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +63 -0
- package/dist/client.js +273 -0
- package/dist/client.js.map +1 -0
- package/dist/command-menu.css +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol-BrHDtNkL.d.ts +140 -0
- package/dist/react.d.ts +184 -0
- package/dist/react.js +1527 -0
- package/dist/react.js.map +1 -0
- package/dist/runtime.d.ts +41 -0
- package/dist/runtime.js +546 -0
- package/dist/runtime.js.map +1 -0
- package/dist/styles.css +2 -0
- package/package.json +98 -3
package/dist/react.js
ADDED
|
@@ -0,0 +1,1527 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// src/react/studio.tsx
|
|
4
|
+
import {
|
|
5
|
+
ChevronLeft,
|
|
6
|
+
ChevronRight,
|
|
7
|
+
LoaderCircle,
|
|
8
|
+
PanelLeftClose,
|
|
9
|
+
PanelLeftOpen,
|
|
10
|
+
Pin as Pin2,
|
|
11
|
+
PlugZap,
|
|
12
|
+
Unplug
|
|
13
|
+
} from "lucide-react";
|
|
14
|
+
import { useEffect as useEffect4, useRef as useRef3, useState as useState7 } from "react";
|
|
15
|
+
|
|
16
|
+
// package.json
|
|
17
|
+
var version = "0.1.0";
|
|
18
|
+
|
|
19
|
+
// src/client/studio.ts
|
|
20
|
+
import { button } from "@joycostudio/xyz/debug";
|
|
21
|
+
var COMPARE_MESSAGE = "joyco-studio:compare";
|
|
22
|
+
function postCompare(target, tool, showSaved) {
|
|
23
|
+
const message = { type: COMPARE_MESSAGE, tool, showSaved };
|
|
24
|
+
target?.postMessage(message, location.origin);
|
|
25
|
+
}
|
|
26
|
+
var instances = globalThis.__joycoStudio ??= /* @__PURE__ */ new Map();
|
|
27
|
+
|
|
28
|
+
// src/react/hooks.ts
|
|
29
|
+
import { useSyncExternalStore } from "react";
|
|
30
|
+
function useStudio(studio) {
|
|
31
|
+
useSyncExternalStore(
|
|
32
|
+
studio.subscribe,
|
|
33
|
+
() => studio.version,
|
|
34
|
+
() => 0
|
|
35
|
+
);
|
|
36
|
+
return studio;
|
|
37
|
+
}
|
|
38
|
+
function useToolConfig(handle) {
|
|
39
|
+
useSyncExternalStore(
|
|
40
|
+
handle.subscribe,
|
|
41
|
+
() => handle.version,
|
|
42
|
+
() => 0
|
|
43
|
+
);
|
|
44
|
+
return handle.config;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/react/inspector.tsx
|
|
48
|
+
import { bindDebugSchema } from "@joycostudio/xyz/debug";
|
|
49
|
+
import { SquareSplitHorizontal } from "lucide-react";
|
|
50
|
+
import { useEffect, useRef, useState } from "react";
|
|
51
|
+
|
|
52
|
+
// src/core/diff.ts
|
|
53
|
+
function changedPaths(before, after, path = "") {
|
|
54
|
+
const nested = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
55
|
+
if (nested(before) && nested(after)) {
|
|
56
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
57
|
+
return [...keys].flatMap((key) => changedPaths(before[key], after[key], path ? `${path}.${key}` : key));
|
|
58
|
+
}
|
|
59
|
+
return JSON.stringify(before) === JSON.stringify(after) ? [] : [path];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/react/theme.ts
|
|
63
|
+
var tweakpaneTheme = {
|
|
64
|
+
"--tp-base-background-color": "transparent",
|
|
65
|
+
"--tp-base-shadow-color": "transparent",
|
|
66
|
+
"--tp-base-border-radius": "0px",
|
|
67
|
+
"--tp-base-font-family": "var(--studio-font)",
|
|
68
|
+
"--tp-blade-border-radius": "var(--radius)",
|
|
69
|
+
"--tp-blade-horizontal-padding": "4px",
|
|
70
|
+
"--tp-blade-value-width": "168px",
|
|
71
|
+
"--tp-container-horizontal-padding": "8px",
|
|
72
|
+
"--tp-container-vertical-padding": "6px",
|
|
73
|
+
"--tp-container-unit-size": "22px",
|
|
74
|
+
"--tp-container-unit-spacing": "6px",
|
|
75
|
+
"--tp-container-background-color": "var(--card)",
|
|
76
|
+
"--tp-container-background-color-hover": "var(--field)",
|
|
77
|
+
"--tp-container-background-color-focus": "var(--field-hover)",
|
|
78
|
+
"--tp-container-background-color-active": "var(--field-active)",
|
|
79
|
+
"--tp-container-foreground-color": "var(--muted-foreground)",
|
|
80
|
+
"--tp-input-background-color": "var(--field)",
|
|
81
|
+
"--tp-input-background-color-hover": "var(--field-hover)",
|
|
82
|
+
"--tp-input-background-color-focus": "var(--field-active)",
|
|
83
|
+
"--tp-input-background-color-active": "var(--field-active)",
|
|
84
|
+
"--tp-input-foreground-color": "var(--foreground)",
|
|
85
|
+
// Buttons and slider knobs: lighter than fields so the knob reads against the track.
|
|
86
|
+
"--tp-button-background-color": "var(--knob)",
|
|
87
|
+
"--tp-button-background-color-hover": "var(--knob-hover)",
|
|
88
|
+
"--tp-button-background-color-focus": "var(--knob-hover)",
|
|
89
|
+
"--tp-button-background-color-active": "var(--muted-foreground)",
|
|
90
|
+
"--tp-button-foreground-color": "var(--foreground)",
|
|
91
|
+
"--tp-groove-foreground-color": "var(--border)",
|
|
92
|
+
"--tp-label-foreground-color": "var(--muted-foreground)",
|
|
93
|
+
"--tp-monitor-background-color": "var(--card)",
|
|
94
|
+
"--tp-monitor-foreground-color": "var(--muted-foreground)"
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// src/react/tool-icon.tsx
|
|
98
|
+
import { SlidersHorizontal } from "lucide";
|
|
99
|
+
import { jsx } from "react/jsx-runtime";
|
|
100
|
+
var camel = (name) => name.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
101
|
+
function ToolIcon({ icon = SlidersHorizontal, ...props }) {
|
|
102
|
+
return /* @__PURE__ */ jsx(
|
|
103
|
+
"svg",
|
|
104
|
+
{
|
|
105
|
+
viewBox: "0 0 24 24",
|
|
106
|
+
fill: "none",
|
|
107
|
+
stroke: "currentColor",
|
|
108
|
+
strokeWidth: 2,
|
|
109
|
+
strokeLinecap: "round",
|
|
110
|
+
strokeLinejoin: "round",
|
|
111
|
+
"aria-hidden": true,
|
|
112
|
+
...props,
|
|
113
|
+
children: icon.map(([Tag, attrs], index) => {
|
|
114
|
+
const Element2 = Tag;
|
|
115
|
+
return /* @__PURE__ */ jsx(
|
|
116
|
+
Element2,
|
|
117
|
+
{
|
|
118
|
+
...Object.fromEntries(Object.entries(attrs).map(([name, value]) => [camel(name), value]))
|
|
119
|
+
},
|
|
120
|
+
index
|
|
121
|
+
);
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/react/ui/button.tsx
|
|
128
|
+
import { cva } from "class-variance-authority";
|
|
129
|
+
|
|
130
|
+
// src/react/ui/utils.ts
|
|
131
|
+
import { clsx } from "clsx";
|
|
132
|
+
import { twMerge } from "tailwind-merge";
|
|
133
|
+
function cn(...inputs) {
|
|
134
|
+
return twMerge(clsx(inputs));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/react/ui/button.tsx
|
|
138
|
+
import { Slot } from "radix-ui";
|
|
139
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
140
|
+
var buttonVariants = cva(
|
|
141
|
+
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
142
|
+
{
|
|
143
|
+
variants: {
|
|
144
|
+
variant: {
|
|
145
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
146
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
147
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
148
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
149
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
150
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
151
|
+
},
|
|
152
|
+
size: {
|
|
153
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
154
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
155
|
+
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
156
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
157
|
+
icon: "size-9",
|
|
158
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
159
|
+
"icon-sm": "size-8",
|
|
160
|
+
"icon-lg": "size-10"
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
defaultVariants: {
|
|
164
|
+
variant: "default",
|
|
165
|
+
size: "default"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
function Button({
|
|
170
|
+
className,
|
|
171
|
+
variant = "default",
|
|
172
|
+
size = "default",
|
|
173
|
+
asChild = false,
|
|
174
|
+
...props
|
|
175
|
+
}) {
|
|
176
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
177
|
+
return /* @__PURE__ */ jsx2(
|
|
178
|
+
Comp,
|
|
179
|
+
{
|
|
180
|
+
"data-slot": "button",
|
|
181
|
+
"data-variant": variant,
|
|
182
|
+
"data-size": size,
|
|
183
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
184
|
+
...props
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/react/ui/kbd.tsx
|
|
190
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
191
|
+
function Kbd({ className, ...props }) {
|
|
192
|
+
return /* @__PURE__ */ jsx3(
|
|
193
|
+
"kbd",
|
|
194
|
+
{
|
|
195
|
+
"data-slot": "kbd",
|
|
196
|
+
className: cn(
|
|
197
|
+
"bg-muted text-muted-foreground border-border pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm border border-b-2 px-1 font-mono text-xs font-normal select-none",
|
|
198
|
+
"[&_svg:not([class*='size-'])]:size-3",
|
|
199
|
+
"[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10",
|
|
200
|
+
className
|
|
201
|
+
),
|
|
202
|
+
...props
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
function KbdGroup({ className, ...props }) {
|
|
207
|
+
return /* @__PURE__ */ jsx3("span", { "data-slot": "kbd-group", className: cn("inline-flex items-center gap-1", className), ...props });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/react/ui/tooltip.tsx
|
|
211
|
+
import { Tooltip as TooltipPrimitive } from "radix-ui";
|
|
212
|
+
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
213
|
+
function TooltipProvider({ delayDuration = 0, ...props }) {
|
|
214
|
+
return /* @__PURE__ */ jsx4(TooltipPrimitive.Provider, { "data-slot": "tooltip-provider", delayDuration, ...props });
|
|
215
|
+
}
|
|
216
|
+
function Tooltip({ ...props }) {
|
|
217
|
+
return /* @__PURE__ */ jsx4(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props });
|
|
218
|
+
}
|
|
219
|
+
function TooltipTrigger({ ...props }) {
|
|
220
|
+
return /* @__PURE__ */ jsx4(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
221
|
+
}
|
|
222
|
+
function TooltipContent({
|
|
223
|
+
className,
|
|
224
|
+
sideOffset = 0,
|
|
225
|
+
children,
|
|
226
|
+
...props
|
|
227
|
+
}) {
|
|
228
|
+
return /* @__PURE__ */ jsx4(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
229
|
+
TooltipPrimitive.Content,
|
|
230
|
+
{
|
|
231
|
+
"data-slot": "tooltip-content",
|
|
232
|
+
sideOffset,
|
|
233
|
+
className: cn(
|
|
234
|
+
"z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
235
|
+
className
|
|
236
|
+
),
|
|
237
|
+
...props,
|
|
238
|
+
children: [
|
|
239
|
+
children,
|
|
240
|
+
/* @__PURE__ */ jsx4(TooltipPrimitive.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" })
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
) });
|
|
244
|
+
}
|
|
245
|
+
function Hint({
|
|
246
|
+
children,
|
|
247
|
+
content,
|
|
248
|
+
...props
|
|
249
|
+
}) {
|
|
250
|
+
return /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
251
|
+
/* @__PURE__ */ jsx4(TooltipTrigger, { asChild: true, children }),
|
|
252
|
+
/* @__PURE__ */ jsx4(TooltipContent, { ...props, children: content })
|
|
253
|
+
] });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/react/ui/panel.tsx
|
|
257
|
+
import { Slot as Slot3 } from "radix-ui";
|
|
258
|
+
import { forwardRef as forwardRef2 } from "react";
|
|
259
|
+
|
|
260
|
+
// src/react/surface.tsx
|
|
261
|
+
import { Slot as Slot2 } from "radix-ui";
|
|
262
|
+
import { forwardRef } from "react";
|
|
263
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
264
|
+
var StudioSurface = forwardRef(
|
|
265
|
+
function StudioSurface2({ asChild = false, className, ...props }, ref) {
|
|
266
|
+
const Comp = asChild ? Slot2.Root : "div";
|
|
267
|
+
return /* @__PURE__ */ jsx5(Comp, { ref, "data-slot": "studio-surface", className: cn("studio-surface", className), ...props });
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
var StudioToolbar = forwardRef(function StudioToolbar2({ className, ...props }, ref) {
|
|
271
|
+
return /* @__PURE__ */ jsx5(StudioSurface, { ref, role: "group", className: cn("studio-toolbar", className), ...props });
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// src/react/ui/panel.tsx
|
|
275
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
276
|
+
var Panel = forwardRef2(function Panel2({ className, asChild = false, ...props }, ref) {
|
|
277
|
+
const Comp = asChild ? Slot3.Root : "div";
|
|
278
|
+
return /* @__PURE__ */ jsx6(
|
|
279
|
+
Comp,
|
|
280
|
+
{
|
|
281
|
+
ref,
|
|
282
|
+
"data-slot": "panel",
|
|
283
|
+
className: cn(
|
|
284
|
+
"rounded-lg border border-sidebar-border bg-sidebar/85 shadow-xl shadow-black/40 backdrop-blur-xl",
|
|
285
|
+
className
|
|
286
|
+
),
|
|
287
|
+
...props
|
|
288
|
+
}
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
function PanelHeader({ className, ...props }) {
|
|
292
|
+
return /* @__PURE__ */ jsx6(StudioSurface, { asChild: true, className: cn("px-3 pt-2.5 pb-2", className), children: /* @__PURE__ */ jsx6("header", { "data-slot": "panel-header", ...props }) });
|
|
293
|
+
}
|
|
294
|
+
var PanelContent = forwardRef2(function PanelContent2({ className, ...props }, ref) {
|
|
295
|
+
return /* @__PURE__ */ jsx6(
|
|
296
|
+
"div",
|
|
297
|
+
{
|
|
298
|
+
ref,
|
|
299
|
+
"data-slot": "panel-content",
|
|
300
|
+
className: cn("min-h-0 flex-1 overflow-auto px-1 py-1.5", className),
|
|
301
|
+
...props
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
function PanelFooter({ className, ...props }) {
|
|
306
|
+
return /* @__PURE__ */ jsx6(StudioSurface, { asChild: true, className: cn("grid gap-2 p-2", className), children: /* @__PURE__ */ jsx6("footer", { "data-slot": "panel-footer", ...props }) });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/react/inspector.tsx
|
|
310
|
+
import { Fragment, jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
311
|
+
function describe(studio, handle) {
|
|
312
|
+
const { state } = handle;
|
|
313
|
+
if (studio.connection.phase !== "connected") return { tone: "text-faint", text: "Not persisting" };
|
|
314
|
+
if (!state) return { tone: "text-destructive", text: "Restart studio dev" };
|
|
315
|
+
if (handle.comparing) return { tone: "text-sky-400", text: "Showing saved" };
|
|
316
|
+
if (state.conflict) return { tone: "text-destructive", text: "Changed on disk" };
|
|
317
|
+
if (state.dirty) return { tone: "text-warning", text: "Unsaved" };
|
|
318
|
+
return { tone: "text-success", text: state.saved.hash ? "Saved" : "Not on disk yet" };
|
|
319
|
+
}
|
|
320
|
+
function Inspector({
|
|
321
|
+
studio,
|
|
322
|
+
handle,
|
|
323
|
+
onCompare,
|
|
324
|
+
fileHref
|
|
325
|
+
}) {
|
|
326
|
+
useStudio(studio);
|
|
327
|
+
const container = useRef(null);
|
|
328
|
+
const pane = useRef(null);
|
|
329
|
+
const [failure, setFailure] = useState(null);
|
|
330
|
+
const run = (action) => void action.then((result) => setFailure(result.ok ? null : result.message ?? result.error));
|
|
331
|
+
const save = (force = false) => run(handle.save({ force }));
|
|
332
|
+
useEffect(() => {
|
|
333
|
+
let disposed = false;
|
|
334
|
+
let teardown = () => {
|
|
335
|
+
};
|
|
336
|
+
void import("tweakpane").then(({ Pane }) => {
|
|
337
|
+
if (disposed || !container.current) return;
|
|
338
|
+
const instance = new Pane({ container: container.current });
|
|
339
|
+
pane.current = instance;
|
|
340
|
+
const bound = bindDebugSchema(instance, { schema: () => handle.controls() });
|
|
341
|
+
const unsubscribe = handle.subscribe(() => bound.refresh());
|
|
342
|
+
teardown = () => {
|
|
343
|
+
unsubscribe();
|
|
344
|
+
bound.dispose();
|
|
345
|
+
instance.dispose();
|
|
346
|
+
pane.current = null;
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
return () => {
|
|
350
|
+
disposed = true;
|
|
351
|
+
teardown();
|
|
352
|
+
};
|
|
353
|
+
}, [handle]);
|
|
354
|
+
useEffect(() => {
|
|
355
|
+
const onKeyDown = (event) => {
|
|
356
|
+
if (event.key !== "s" || !(event.metaKey || event.ctrlKey)) return;
|
|
357
|
+
event.preventDefault();
|
|
358
|
+
if (handle.state?.dirty) save();
|
|
359
|
+
};
|
|
360
|
+
addEventListener("keydown", onKeyDown);
|
|
361
|
+
return () => removeEventListener("keydown", onKeyDown);
|
|
362
|
+
});
|
|
363
|
+
useEffect(() => {
|
|
364
|
+
if (pane.current) pane.current.disabled = handle.comparing;
|
|
365
|
+
});
|
|
366
|
+
const { definition, state } = handle;
|
|
367
|
+
const ConfigFile = fileHref ? "a" : "span";
|
|
368
|
+
const status = describe(studio, handle);
|
|
369
|
+
const editable2 = studio.connection.phase === "connected" && !!state?.dirty && !handle.comparing;
|
|
370
|
+
const changes = (state?.dirty ? changedPaths(state.saved.value, state.draft.value) : []).map(
|
|
371
|
+
(path) => path.split(".").map((key) => key.charAt(0).toUpperCase() + key.slice(1)).join(" \u203A ")
|
|
372
|
+
);
|
|
373
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
374
|
+
/* @__PURE__ */ jsxs2(PanelHeader, { children: [
|
|
375
|
+
/* @__PURE__ */ jsxs2("strong", { className: "flex min-w-0 items-center gap-2 text-foreground", children: [
|
|
376
|
+
/* @__PURE__ */ jsx7(ToolIcon, { icon: definition.icon, className: "size-3.5 shrink-0" }),
|
|
377
|
+
/* @__PURE__ */ jsx7("span", { className: "truncate", title: definition.title ?? definition.id, children: definition.title ?? definition.id })
|
|
378
|
+
] }),
|
|
379
|
+
/* @__PURE__ */ jsxs2("div", { className: "mt-1 flex items-center gap-3 pl-5.5 text-[11px]", children: [
|
|
380
|
+
/* @__PURE__ */ jsx7(
|
|
381
|
+
ConfigFile,
|
|
382
|
+
{
|
|
383
|
+
"data-slot": "studio-config-file",
|
|
384
|
+
className: "min-w-0 flex-1 truncate text-muted-foreground",
|
|
385
|
+
href: fileHref,
|
|
386
|
+
title: fileHref ? `Open ${definition.file}` : definition.file,
|
|
387
|
+
children: definition.file
|
|
388
|
+
}
|
|
389
|
+
),
|
|
390
|
+
/* @__PURE__ */ jsxs2(
|
|
391
|
+
"span",
|
|
392
|
+
{
|
|
393
|
+
className: `flex shrink-0 items-center gap-1.5 ${status.tone}`,
|
|
394
|
+
title: state ? `Draft revision ${state.draft.revision}` : void 0,
|
|
395
|
+
children: [
|
|
396
|
+
/* @__PURE__ */ jsx7("span", { "aria-hidden": "true", className: "size-1.5 rounded-full bg-current" }),
|
|
397
|
+
status.text
|
|
398
|
+
]
|
|
399
|
+
}
|
|
400
|
+
)
|
|
401
|
+
] })
|
|
402
|
+
] }),
|
|
403
|
+
/* @__PURE__ */ jsx7(PanelContent, { ref: container, "data-slot": "studio-controls", style: tweakpaneTheme }),
|
|
404
|
+
/* @__PURE__ */ jsxs2(PanelFooter, { children: [
|
|
405
|
+
failure && /* @__PURE__ */ jsx7("div", { className: "text-destructive", children: failure }),
|
|
406
|
+
changes.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "truncate text-muted-foreground", title: changes.join(", "), children: [
|
|
407
|
+
changes.length,
|
|
408
|
+
" changed: ",
|
|
409
|
+
/* @__PURE__ */ jsx7("span", { className: "text-foreground", children: changes.join(", ") })
|
|
410
|
+
] }),
|
|
411
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
|
|
412
|
+
/* @__PURE__ */ jsx7(
|
|
413
|
+
Hint,
|
|
414
|
+
{
|
|
415
|
+
className: "flex items-center gap-2",
|
|
416
|
+
content: /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
417
|
+
"Hold to compare with saved ",
|
|
418
|
+
/* @__PURE__ */ jsx7(Kbd, { children: "\\" })
|
|
419
|
+
] }),
|
|
420
|
+
children: /* @__PURE__ */ jsx7(
|
|
421
|
+
Button,
|
|
422
|
+
{
|
|
423
|
+
variant: "secondary",
|
|
424
|
+
size: "icon-sm",
|
|
425
|
+
"aria-label": "Hold to compare with saved",
|
|
426
|
+
"aria-pressed": handle.comparing,
|
|
427
|
+
disabled: !state?.dirty,
|
|
428
|
+
className: "shrink-0 aria-pressed:bg-primary aria-pressed:text-primary-foreground",
|
|
429
|
+
onPointerDown: (event) => {
|
|
430
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
431
|
+
onCompare(true);
|
|
432
|
+
},
|
|
433
|
+
onPointerUp: () => onCompare(false),
|
|
434
|
+
onPointerCancel: () => onCompare(false),
|
|
435
|
+
children: /* @__PURE__ */ jsx7(SquareSplitHorizontal, {})
|
|
436
|
+
}
|
|
437
|
+
)
|
|
438
|
+
}
|
|
439
|
+
),
|
|
440
|
+
/* @__PURE__ */ jsx7(
|
|
441
|
+
Button,
|
|
442
|
+
{
|
|
443
|
+
variant: "secondary",
|
|
444
|
+
size: "sm",
|
|
445
|
+
className: "flex-1",
|
|
446
|
+
disabled: !editable2,
|
|
447
|
+
onClick: () => run(handle.discard()),
|
|
448
|
+
children: "Discard"
|
|
449
|
+
}
|
|
450
|
+
),
|
|
451
|
+
state?.conflict ? /* @__PURE__ */ jsx7(Button, { variant: "destructive", size: "sm", className: "flex-1", onClick: () => save(true), children: "Overwrite file" }) : /* @__PURE__ */ jsxs2(Button, { size: "sm", className: "flex-1", disabled: !editable2, onClick: () => save(), children: [
|
|
452
|
+
"Save",
|
|
453
|
+
/* @__PURE__ */ jsxs2(KbdGroup, { children: [
|
|
454
|
+
/* @__PURE__ */ jsx7(Kbd, { children: "\u2318" }),
|
|
455
|
+
/* @__PURE__ */ jsx7(Kbd, { children: "S" })
|
|
456
|
+
] })
|
|
457
|
+
] })
|
|
458
|
+
] })
|
|
459
|
+
] })
|
|
460
|
+
] });
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// src/react/logo.tsx
|
|
464
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
465
|
+
function Logo(props) {
|
|
466
|
+
return /* @__PURE__ */ jsx8("svg", { viewBox: "0 0 352 144", fill: "currentColor", "aria-label": "JOYCO", role: "img", ...props, children: /* @__PURE__ */ jsx8("path", { d: "M328 96L280 144H0V40H176V112H192V24H0V0H352V24H328V96Z" }) });
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/react/pinned-tools.ts
|
|
470
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
471
|
+
var STORAGE_KEY = "joyco-studio:pinned-tools";
|
|
472
|
+
var CHANGE_EVENT = "joyco-studio:pins-changed";
|
|
473
|
+
function readPins() {
|
|
474
|
+
try {
|
|
475
|
+
const value = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "[]");
|
|
476
|
+
return Array.isArray(value) ? value.filter((id) => typeof id === "string") : [];
|
|
477
|
+
} catch {
|
|
478
|
+
return [];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function usePinnedTools(studio) {
|
|
482
|
+
const [pins, setPins] = useState2([]);
|
|
483
|
+
useEffect2(() => {
|
|
484
|
+
setPins(readPins());
|
|
485
|
+
const onChange = (event) => setPins(event.detail);
|
|
486
|
+
const onStorage = (event) => {
|
|
487
|
+
if (event.key === STORAGE_KEY || event.key === null) setPins(readPins());
|
|
488
|
+
};
|
|
489
|
+
window.addEventListener(CHANGE_EVENT, onChange);
|
|
490
|
+
window.addEventListener("storage", onStorage);
|
|
491
|
+
return () => {
|
|
492
|
+
window.removeEventListener(CHANGE_EVENT, onChange);
|
|
493
|
+
window.removeEventListener("storage", onStorage);
|
|
494
|
+
};
|
|
495
|
+
}, []);
|
|
496
|
+
const pinned = new Set(pins);
|
|
497
|
+
const tools = [...studio.tools].sort(
|
|
498
|
+
(a, b) => Number(pinned.has(b.definition.id)) - Number(pinned.has(a.definition.id))
|
|
499
|
+
);
|
|
500
|
+
const togglePin = (id) => {
|
|
501
|
+
const next = pinned.has(id) ? pins.filter((pin) => pin !== id) : [...pins, id];
|
|
502
|
+
try {
|
|
503
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
|
504
|
+
} catch {
|
|
505
|
+
}
|
|
506
|
+
window.dispatchEvent(new CustomEvent(CHANGE_EVENT, { detail: next }));
|
|
507
|
+
};
|
|
508
|
+
return { tools, pinned, togglePin };
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/react/preview-canvas.tsx
|
|
512
|
+
import { RotateCw } from "lucide-react";
|
|
513
|
+
import { forwardRef as forwardRef3, useImperativeHandle, useRef as useRef2, useState as useState4 } from "react";
|
|
514
|
+
|
|
515
|
+
// src/react/canvas-toolbar.tsx
|
|
516
|
+
import { Maximize, Minimize, Pin, PinOff } from "lucide-react";
|
|
517
|
+
import { useState as useState3 } from "react";
|
|
518
|
+
import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
519
|
+
function CanvasToolbar({ label, expanded = false, onToggle, children }) {
|
|
520
|
+
const [ghost, setGhost] = useState3(false);
|
|
521
|
+
const ExpandIcon = expanded ? Minimize : Maximize;
|
|
522
|
+
const PinIcon = ghost ? PinOff : Pin;
|
|
523
|
+
return /* @__PURE__ */ jsx9(
|
|
524
|
+
"div",
|
|
525
|
+
{
|
|
526
|
+
className: "studio-canvas-toolbar",
|
|
527
|
+
"data-slot": "canvas-toolbar",
|
|
528
|
+
"data-expanded": expanded || void 0,
|
|
529
|
+
"data-ghost": !expanded && ghost || void 0,
|
|
530
|
+
role: "group",
|
|
531
|
+
"aria-label": label,
|
|
532
|
+
children: /* @__PURE__ */ jsxs3(StudioToolbar, { className: "studio-canvas-actions", children: [
|
|
533
|
+
children,
|
|
534
|
+
onToggle && /* @__PURE__ */ jsx9("div", { className: "studio-canvas-toggle", children: /* @__PURE__ */ jsxs3("button", { "data-slot": "toolbar-action", type: "button", "aria-pressed": expanded, onClick: onToggle, children: [
|
|
535
|
+
/* @__PURE__ */ jsx9(ExpandIcon, { size: 14, strokeWidth: 1.5, "aria-hidden": true }),
|
|
536
|
+
expanded ? "Contain canvas" : "Expand"
|
|
537
|
+
] }) }),
|
|
538
|
+
!expanded && /* @__PURE__ */ jsx9(
|
|
539
|
+
"button",
|
|
540
|
+
{
|
|
541
|
+
type: "button",
|
|
542
|
+
"aria-label": `Auto-hide ${label.toLowerCase()} header`,
|
|
543
|
+
"aria-pressed": ghost,
|
|
544
|
+
title: ghost ? "Pin header" : "Auto-hide header",
|
|
545
|
+
onClick: () => setGhost(!ghost),
|
|
546
|
+
children: /* @__PURE__ */ jsx9(PinIcon, { size: 14, strokeWidth: 1.5, "aria-hidden": true })
|
|
547
|
+
}
|
|
548
|
+
)
|
|
549
|
+
] })
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// src/react/preview-canvas.tsx
|
|
555
|
+
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
556
|
+
var PreviewCanvas = forwardRef3(function PreviewCanvas2({ title = "Preview", expanded: controlled, onExpandedChange, onReload, toolbar, ...props }, ref) {
|
|
557
|
+
const [localExpanded, setExpanded] = useState4(false);
|
|
558
|
+
const expanded = controlled ?? localExpanded;
|
|
559
|
+
const frame = useRef2(null);
|
|
560
|
+
useImperativeHandle(ref, () => frame.current, []);
|
|
561
|
+
return /* @__PURE__ */ jsxs4(
|
|
562
|
+
"section",
|
|
563
|
+
{
|
|
564
|
+
className: "studio-preview-canvas",
|
|
565
|
+
"data-slot": "preview-canvas",
|
|
566
|
+
"data-expanded": expanded || void 0,
|
|
567
|
+
"aria-label": `${title} canvas`,
|
|
568
|
+
children: [
|
|
569
|
+
/* @__PURE__ */ jsxs4(
|
|
570
|
+
CanvasToolbar,
|
|
571
|
+
{
|
|
572
|
+
label: "Preview canvas",
|
|
573
|
+
expanded,
|
|
574
|
+
onToggle: () => {
|
|
575
|
+
setExpanded(!expanded);
|
|
576
|
+
onExpandedChange?.(!expanded);
|
|
577
|
+
},
|
|
578
|
+
children: [
|
|
579
|
+
/* @__PURE__ */ jsx10(
|
|
580
|
+
"button",
|
|
581
|
+
{
|
|
582
|
+
type: "button",
|
|
583
|
+
"aria-label": "Reload preview",
|
|
584
|
+
title: "Reload preview",
|
|
585
|
+
onClick: () => {
|
|
586
|
+
onReload?.();
|
|
587
|
+
if (frame.current) frame.current.src = frame.current.src;
|
|
588
|
+
},
|
|
589
|
+
children: /* @__PURE__ */ jsx10(RotateCw, { size: 14, strokeWidth: 1.5, "aria-hidden": true })
|
|
590
|
+
}
|
|
591
|
+
),
|
|
592
|
+
/* @__PURE__ */ jsx10("div", { className: "studio-preview-source", children: toolbar ?? /* @__PURE__ */ jsx10("span", { className: "studio-preview-title", children: title }) })
|
|
593
|
+
]
|
|
594
|
+
}
|
|
595
|
+
),
|
|
596
|
+
/* @__PURE__ */ jsx10("iframe", { ...props, ref: frame, title })
|
|
597
|
+
]
|
|
598
|
+
}
|
|
599
|
+
);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// src/react/ui/sidebar.tsx
|
|
603
|
+
import * as React2 from "react";
|
|
604
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
605
|
+
import { Slot as Slot4 } from "radix-ui";
|
|
606
|
+
|
|
607
|
+
// src/react/ui/hooks/use-mobile.ts
|
|
608
|
+
import * as React from "react";
|
|
609
|
+
var MOBILE_BREAKPOINT = 768;
|
|
610
|
+
function useIsMobile() {
|
|
611
|
+
const [isMobile, setIsMobile] = React.useState(void 0);
|
|
612
|
+
React.useEffect(() => {
|
|
613
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
614
|
+
const onChange = () => {
|
|
615
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
616
|
+
};
|
|
617
|
+
mql.addEventListener("change", onChange);
|
|
618
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
619
|
+
return () => mql.removeEventListener("change", onChange);
|
|
620
|
+
}, []);
|
|
621
|
+
return !!isMobile;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// src/react/ui/sheet.tsx
|
|
625
|
+
import { XIcon } from "lucide-react";
|
|
626
|
+
import { Dialog as SheetPrimitive } from "radix-ui";
|
|
627
|
+
import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
628
|
+
function Sheet({ ...props }) {
|
|
629
|
+
return /* @__PURE__ */ jsx11(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
|
|
630
|
+
}
|
|
631
|
+
function SheetPortal({ ...props }) {
|
|
632
|
+
return /* @__PURE__ */ jsx11(SheetPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
|
|
633
|
+
}
|
|
634
|
+
function SheetOverlay({ className, ...props }) {
|
|
635
|
+
return /* @__PURE__ */ jsx11(
|
|
636
|
+
SheetPrimitive.Overlay,
|
|
637
|
+
{
|
|
638
|
+
"data-slot": "sheet-overlay",
|
|
639
|
+
className: cn(
|
|
640
|
+
"fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
641
|
+
className
|
|
642
|
+
),
|
|
643
|
+
...props
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
function SheetContent({
|
|
648
|
+
className,
|
|
649
|
+
children,
|
|
650
|
+
side = "right",
|
|
651
|
+
showCloseButton = true,
|
|
652
|
+
...props
|
|
653
|
+
}) {
|
|
654
|
+
return /* @__PURE__ */ jsxs5(SheetPortal, { children: [
|
|
655
|
+
/* @__PURE__ */ jsx11(SheetOverlay, {}),
|
|
656
|
+
/* @__PURE__ */ jsxs5(
|
|
657
|
+
SheetPrimitive.Content,
|
|
658
|
+
{
|
|
659
|
+
"data-slot": "sheet-content",
|
|
660
|
+
className: cn(
|
|
661
|
+
"fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500",
|
|
662
|
+
side === "right" && "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
|
663
|
+
side === "left" && "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
|
664
|
+
side === "top" && "inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
|
665
|
+
side === "bottom" && "inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
|
666
|
+
className
|
|
667
|
+
),
|
|
668
|
+
...props,
|
|
669
|
+
children: [
|
|
670
|
+
children,
|
|
671
|
+
showCloseButton && /* @__PURE__ */ jsxs5(SheetPrimitive.Close, { className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary", children: [
|
|
672
|
+
/* @__PURE__ */ jsx11(XIcon, { className: "size-4" }),
|
|
673
|
+
/* @__PURE__ */ jsx11("span", { className: "sr-only", children: "Close" })
|
|
674
|
+
] })
|
|
675
|
+
]
|
|
676
|
+
}
|
|
677
|
+
)
|
|
678
|
+
] });
|
|
679
|
+
}
|
|
680
|
+
function SheetHeader({ className, ...props }) {
|
|
681
|
+
return /* @__PURE__ */ jsx11("div", { "data-slot": "sheet-header", className: cn("flex flex-col gap-1.5 p-4", className), ...props });
|
|
682
|
+
}
|
|
683
|
+
function SheetTitle({ className, ...props }) {
|
|
684
|
+
return /* @__PURE__ */ jsx11(
|
|
685
|
+
SheetPrimitive.Title,
|
|
686
|
+
{
|
|
687
|
+
"data-slot": "sheet-title",
|
|
688
|
+
className: cn("font-semibold text-foreground", className),
|
|
689
|
+
...props
|
|
690
|
+
}
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
function SheetDescription({ className, ...props }) {
|
|
694
|
+
return /* @__PURE__ */ jsx11(
|
|
695
|
+
SheetPrimitive.Description,
|
|
696
|
+
{
|
|
697
|
+
"data-slot": "sheet-description",
|
|
698
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
699
|
+
...props
|
|
700
|
+
}
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// src/react/ui/sidebar.tsx
|
|
705
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
706
|
+
var SIDEBAR_COOKIE_NAME = "sidebar_state";
|
|
707
|
+
var SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|
708
|
+
var SIDEBAR_WIDTH = "16rem";
|
|
709
|
+
var SIDEBAR_WIDTH_MOBILE = "18rem";
|
|
710
|
+
var SIDEBAR_WIDTH_ICON = "3rem";
|
|
711
|
+
var SidebarContext = React2.createContext(null);
|
|
712
|
+
function useSidebar() {
|
|
713
|
+
const context = React2.useContext(SidebarContext);
|
|
714
|
+
if (!context) {
|
|
715
|
+
throw new Error("useSidebar must be used within a SidebarProvider.");
|
|
716
|
+
}
|
|
717
|
+
return context;
|
|
718
|
+
}
|
|
719
|
+
function SidebarProvider({
|
|
720
|
+
defaultOpen = true,
|
|
721
|
+
open: openProp,
|
|
722
|
+
onOpenChange: setOpenProp,
|
|
723
|
+
className,
|
|
724
|
+
style,
|
|
725
|
+
children,
|
|
726
|
+
...props
|
|
727
|
+
}) {
|
|
728
|
+
const isMobile = useIsMobile();
|
|
729
|
+
const [openMobile, setOpenMobile] = React2.useState(false);
|
|
730
|
+
const [_open, _setOpen] = React2.useState(defaultOpen);
|
|
731
|
+
const open = openProp ?? _open;
|
|
732
|
+
const setOpen = React2.useCallback(
|
|
733
|
+
(value) => {
|
|
734
|
+
const openState = typeof value === "function" ? value(open) : value;
|
|
735
|
+
if (setOpenProp) {
|
|
736
|
+
setOpenProp(openState);
|
|
737
|
+
} else {
|
|
738
|
+
_setOpen(openState);
|
|
739
|
+
}
|
|
740
|
+
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
|
741
|
+
},
|
|
742
|
+
[setOpenProp, open]
|
|
743
|
+
);
|
|
744
|
+
const toggleSidebar = React2.useCallback(() => {
|
|
745
|
+
return isMobile ? setOpenMobile((open2) => !open2) : setOpen((open2) => !open2);
|
|
746
|
+
}, [isMobile, setOpen, setOpenMobile]);
|
|
747
|
+
const state = open ? "expanded" : "collapsed";
|
|
748
|
+
const contextValue = React2.useMemo(
|
|
749
|
+
() => ({
|
|
750
|
+
state,
|
|
751
|
+
open,
|
|
752
|
+
setOpen,
|
|
753
|
+
isMobile,
|
|
754
|
+
openMobile,
|
|
755
|
+
setOpenMobile,
|
|
756
|
+
toggleSidebar
|
|
757
|
+
}),
|
|
758
|
+
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
|
|
759
|
+
);
|
|
760
|
+
return /* @__PURE__ */ jsx12(SidebarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx12(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx12(
|
|
761
|
+
"div",
|
|
762
|
+
{
|
|
763
|
+
"data-slot": "sidebar-wrapper",
|
|
764
|
+
style: {
|
|
765
|
+
"--sidebar-width": SIDEBAR_WIDTH,
|
|
766
|
+
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
|
|
767
|
+
...style
|
|
768
|
+
},
|
|
769
|
+
className: cn("group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar", className),
|
|
770
|
+
...props,
|
|
771
|
+
children
|
|
772
|
+
}
|
|
773
|
+
) }) });
|
|
774
|
+
}
|
|
775
|
+
function Sidebar({
|
|
776
|
+
side = "left",
|
|
777
|
+
variant = "sidebar",
|
|
778
|
+
collapsible = "offcanvas",
|
|
779
|
+
className,
|
|
780
|
+
children,
|
|
781
|
+
...props
|
|
782
|
+
}) {
|
|
783
|
+
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
784
|
+
const Surface = variant === "floating" ? Panel : "div";
|
|
785
|
+
if (collapsible === "none") {
|
|
786
|
+
return /* @__PURE__ */ jsx12(
|
|
787
|
+
"div",
|
|
788
|
+
{
|
|
789
|
+
"data-slot": "sidebar",
|
|
790
|
+
className: cn("flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground", className),
|
|
791
|
+
...props,
|
|
792
|
+
children
|
|
793
|
+
}
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
if (isMobile) {
|
|
797
|
+
return /* @__PURE__ */ jsx12(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: /* @__PURE__ */ jsxs6(
|
|
798
|
+
SheetContent,
|
|
799
|
+
{
|
|
800
|
+
"data-sidebar": "sidebar",
|
|
801
|
+
"data-slot": "sidebar",
|
|
802
|
+
"data-mobile": "true",
|
|
803
|
+
className: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden",
|
|
804
|
+
style: {
|
|
805
|
+
"--sidebar-width": SIDEBAR_WIDTH_MOBILE
|
|
806
|
+
},
|
|
807
|
+
side,
|
|
808
|
+
children: [
|
|
809
|
+
/* @__PURE__ */ jsxs6(SheetHeader, { className: "sr-only", children: [
|
|
810
|
+
/* @__PURE__ */ jsx12(SheetTitle, { children: "Sidebar" }),
|
|
811
|
+
/* @__PURE__ */ jsx12(SheetDescription, { children: "Displays the mobile sidebar." })
|
|
812
|
+
] }),
|
|
813
|
+
/* @__PURE__ */ jsx12("div", { className: "flex h-full w-full flex-col", children })
|
|
814
|
+
]
|
|
815
|
+
}
|
|
816
|
+
) });
|
|
817
|
+
}
|
|
818
|
+
return /* @__PURE__ */ jsxs6(
|
|
819
|
+
"div",
|
|
820
|
+
{
|
|
821
|
+
className: "group peer hidden text-sidebar-foreground md:block",
|
|
822
|
+
"data-state": state,
|
|
823
|
+
"data-collapsible": state === "collapsed" ? collapsible : "",
|
|
824
|
+
"data-variant": variant,
|
|
825
|
+
"data-side": side,
|
|
826
|
+
"data-slot": "sidebar",
|
|
827
|
+
children: [
|
|
828
|
+
/* @__PURE__ */ jsx12(
|
|
829
|
+
"div",
|
|
830
|
+
{
|
|
831
|
+
"data-slot": "sidebar-gap",
|
|
832
|
+
className: cn(
|
|
833
|
+
"relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
|
|
834
|
+
"group-data-[collapsible=offcanvas]:w-0",
|
|
835
|
+
"group-data-[side=right]:rotate-180",
|
|
836
|
+
variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
|
|
837
|
+
)
|
|
838
|
+
}
|
|
839
|
+
),
|
|
840
|
+
/* @__PURE__ */ jsx12(
|
|
841
|
+
"div",
|
|
842
|
+
{
|
|
843
|
+
"data-slot": "sidebar-container",
|
|
844
|
+
className: cn(
|
|
845
|
+
"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
|
|
846
|
+
side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
|
847
|
+
// Adjust the padding for floating and inset variants.
|
|
848
|
+
variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
|
849
|
+
className
|
|
850
|
+
),
|
|
851
|
+
...props,
|
|
852
|
+
children: /* @__PURE__ */ jsx12(
|
|
853
|
+
Surface,
|
|
854
|
+
{
|
|
855
|
+
"data-sidebar": "sidebar",
|
|
856
|
+
"data-slot": "sidebar-inner",
|
|
857
|
+
className: cn("flex h-full w-full flex-col", variant !== "floating" && "bg-sidebar"),
|
|
858
|
+
children
|
|
859
|
+
}
|
|
860
|
+
)
|
|
861
|
+
}
|
|
862
|
+
)
|
|
863
|
+
]
|
|
864
|
+
}
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
function SidebarHeader({ className, ...props }) {
|
|
868
|
+
return /* @__PURE__ */ jsx12(
|
|
869
|
+
"div",
|
|
870
|
+
{
|
|
871
|
+
"data-slot": "sidebar-header",
|
|
872
|
+
"data-sidebar": "header",
|
|
873
|
+
className: cn("flex flex-col gap-2 p-2", className),
|
|
874
|
+
...props
|
|
875
|
+
}
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
function SidebarFooter({ className, ...props }) {
|
|
879
|
+
return /* @__PURE__ */ jsx12(
|
|
880
|
+
"div",
|
|
881
|
+
{
|
|
882
|
+
"data-slot": "sidebar-footer",
|
|
883
|
+
"data-sidebar": "footer",
|
|
884
|
+
className: cn("flex flex-col gap-2 p-2", className),
|
|
885
|
+
...props
|
|
886
|
+
}
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
function SidebarContent({ className, ...props }) {
|
|
890
|
+
return /* @__PURE__ */ jsx12(
|
|
891
|
+
"div",
|
|
892
|
+
{
|
|
893
|
+
"data-slot": "sidebar-content",
|
|
894
|
+
"data-sidebar": "content",
|
|
895
|
+
className: cn(
|
|
896
|
+
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
|
|
897
|
+
className
|
|
898
|
+
),
|
|
899
|
+
...props
|
|
900
|
+
}
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
function SidebarGroup({ className, ...props }) {
|
|
904
|
+
return /* @__PURE__ */ jsx12(
|
|
905
|
+
"div",
|
|
906
|
+
{
|
|
907
|
+
"data-slot": "sidebar-group",
|
|
908
|
+
"data-sidebar": "group",
|
|
909
|
+
className: cn("relative flex w-full min-w-0 flex-col p-2", className),
|
|
910
|
+
...props
|
|
911
|
+
}
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
function SidebarMenu({ className, ...props }) {
|
|
915
|
+
return /* @__PURE__ */ jsx12(
|
|
916
|
+
"ul",
|
|
917
|
+
{
|
|
918
|
+
"data-slot": "sidebar-menu",
|
|
919
|
+
"data-sidebar": "menu",
|
|
920
|
+
className: cn("flex w-full min-w-0 flex-col gap-1", className),
|
|
921
|
+
...props
|
|
922
|
+
}
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
function SidebarMenuItem({ className, ...props }) {
|
|
926
|
+
return /* @__PURE__ */ jsx12(
|
|
927
|
+
"li",
|
|
928
|
+
{
|
|
929
|
+
"data-slot": "sidebar-menu-item",
|
|
930
|
+
"data-sidebar": "menu-item",
|
|
931
|
+
className: cn("group/menu-item relative", className),
|
|
932
|
+
...props
|
|
933
|
+
}
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
var sidebarMenuButtonVariants = cva2(
|
|
937
|
+
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
|
938
|
+
{
|
|
939
|
+
variants: {
|
|
940
|
+
variant: {
|
|
941
|
+
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
942
|
+
outline: "bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]"
|
|
943
|
+
},
|
|
944
|
+
size: {
|
|
945
|
+
default: "h-8 text-sm",
|
|
946
|
+
sm: "h-7 text-xs",
|
|
947
|
+
lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!"
|
|
948
|
+
}
|
|
949
|
+
},
|
|
950
|
+
defaultVariants: {
|
|
951
|
+
variant: "default",
|
|
952
|
+
size: "default"
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
);
|
|
956
|
+
function SidebarMenuButton({
|
|
957
|
+
asChild = false,
|
|
958
|
+
isActive = false,
|
|
959
|
+
variant = "default",
|
|
960
|
+
size = "default",
|
|
961
|
+
tooltip,
|
|
962
|
+
className,
|
|
963
|
+
...props
|
|
964
|
+
}) {
|
|
965
|
+
const Comp = asChild ? Slot4.Root : "button";
|
|
966
|
+
const { isMobile, state } = useSidebar();
|
|
967
|
+
const button2 = /* @__PURE__ */ jsx12(
|
|
968
|
+
Comp,
|
|
969
|
+
{
|
|
970
|
+
"data-slot": "sidebar-menu-button",
|
|
971
|
+
"data-sidebar": "menu-button",
|
|
972
|
+
"data-size": size,
|
|
973
|
+
"data-active": isActive,
|
|
974
|
+
className: cn(sidebarMenuButtonVariants({ variant, size }), className),
|
|
975
|
+
...props
|
|
976
|
+
}
|
|
977
|
+
);
|
|
978
|
+
if (!tooltip) return button2;
|
|
979
|
+
const { children, ...hint } = typeof tooltip === "string" ? { children: tooltip } : tooltip;
|
|
980
|
+
return /* @__PURE__ */ jsx12(Hint, { side: "right", align: "center", hidden: state !== "collapsed" || isMobile, ...hint, content: children, children: button2 });
|
|
981
|
+
}
|
|
982
|
+
function SidebarMenuAction({
|
|
983
|
+
className,
|
|
984
|
+
asChild = false,
|
|
985
|
+
showOnHover = false,
|
|
986
|
+
...props
|
|
987
|
+
}) {
|
|
988
|
+
const Comp = asChild ? Slot4.Root : "button";
|
|
989
|
+
return /* @__PURE__ */ jsx12(
|
|
990
|
+
Comp,
|
|
991
|
+
{
|
|
992
|
+
"data-slot": "sidebar-menu-action",
|
|
993
|
+
"data-sidebar": "menu-action",
|
|
994
|
+
className: cn(
|
|
995
|
+
"absolute top-1/2 right-1 flex aspect-square w-5 -translate-y-1/2 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform peer-hover/menu-button:text-sidebar-accent-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
996
|
+
// Increases the hit area of the button on mobile.
|
|
997
|
+
"after:absolute after:-inset-2 md:after:hidden",
|
|
998
|
+
"group-data-[collapsible=icon]:hidden",
|
|
999
|
+
showOnHover && "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground data-[state=open]:opacity-100 md:opacity-0",
|
|
1000
|
+
className
|
|
1001
|
+
),
|
|
1002
|
+
...props
|
|
1003
|
+
}
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/react/studio.tsx
|
|
1008
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1009
|
+
var CONNECTION = {
|
|
1010
|
+
connected: { icon: PlugZap, tone: "text-success", label: "Runtime connected" },
|
|
1011
|
+
connecting: { icon: LoaderCircle, tone: "text-warning animate-spin", label: "Connecting to runtime" },
|
|
1012
|
+
offline: { icon: Unplug, tone: "text-destructive", label: "Runtime offline" },
|
|
1013
|
+
local: { icon: Unplug, tone: "text-faint", label: "No runtime" }
|
|
1014
|
+
};
|
|
1015
|
+
function StudioFooter({ connection }) {
|
|
1016
|
+
const { state, toggleSidebar } = useSidebar();
|
|
1017
|
+
const { icon: Status, tone, label } = CONNECTION[connection.phase];
|
|
1018
|
+
const Toggle = state === "expanded" ? PanelLeftClose : PanelLeftOpen;
|
|
1019
|
+
return /* @__PURE__ */ jsx13(SidebarFooter, { children: /* @__PURE__ */ jsxs7(SidebarMenu, { children: [
|
|
1020
|
+
/* @__PURE__ */ jsx13(SidebarMenuItem, { children: /* @__PURE__ */ jsxs7(SidebarMenuButton, { tooltip: label, className: "cursor-default hover:bg-transparent active:bg-transparent", children: [
|
|
1021
|
+
/* @__PURE__ */ jsx13(Status, { className: tone }),
|
|
1022
|
+
/* @__PURE__ */ jsx13("span", { className: "text-clip! group-data-[collapsible=icon]:hidden", children: label })
|
|
1023
|
+
] }) }),
|
|
1024
|
+
/* @__PURE__ */ jsx13(SidebarMenuItem, { children: /* @__PURE__ */ jsxs7(
|
|
1025
|
+
SidebarMenuButton,
|
|
1026
|
+
{
|
|
1027
|
+
"aria-label": `${state === "expanded" ? "Collapse" : "Expand"} sidebar`,
|
|
1028
|
+
tooltip: `${state === "expanded" ? "Collapse" : "Expand"} sidebar`,
|
|
1029
|
+
onClick: toggleSidebar,
|
|
1030
|
+
children: [
|
|
1031
|
+
/* @__PURE__ */ jsx13(Toggle, {}),
|
|
1032
|
+
/* @__PURE__ */ jsx13("span", { className: "text-clip! group-data-[collapsible=icon]:hidden", children: "Collapse" })
|
|
1033
|
+
]
|
|
1034
|
+
}
|
|
1035
|
+
) })
|
|
1036
|
+
] }) });
|
|
1037
|
+
}
|
|
1038
|
+
var SHORTCUTS = /* @__PURE__ */ new Set(["b", "s", "k"]);
|
|
1039
|
+
var COMPARE_KEY = "\\";
|
|
1040
|
+
var editable = (target) => target instanceof Element && target.matches("input, textarea, select, [contenteditable]");
|
|
1041
|
+
var isShortcut = (event) => (event.metaKey || event.ctrlKey) && SHORTCUTS.has(event.key.toLowerCase()) || event.key === COMPARE_KEY && !editable(event.target);
|
|
1042
|
+
function forwardShortcuts(frame) {
|
|
1043
|
+
const forward = (event) => {
|
|
1044
|
+
if (event.defaultPrevented || !isShortcut(event)) return;
|
|
1045
|
+
event.preventDefault();
|
|
1046
|
+
window.dispatchEvent(new KeyboardEvent(event.type, event));
|
|
1047
|
+
};
|
|
1048
|
+
try {
|
|
1049
|
+
frame.contentWindow?.addEventListener("keydown", forward);
|
|
1050
|
+
frame.contentWindow?.addEventListener("keyup", forward);
|
|
1051
|
+
} catch {
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
function Studio({ studio, previewComponent, getConfigFileHref }) {
|
|
1055
|
+
useStudio(studio);
|
|
1056
|
+
const { tools, pinned, togglePin } = usePinnedTools(studio);
|
|
1057
|
+
const [mounted, setMounted] = useState7(false);
|
|
1058
|
+
const [panelsHidden, setPanelsHidden] = useState7(false);
|
|
1059
|
+
const [selected, setSelected] = useState7(studio.tools[0]?.definition.id);
|
|
1060
|
+
const preview = useRef3(null);
|
|
1061
|
+
const handle = studio.tools.find((tool) => tool.definition.id === selected);
|
|
1062
|
+
const Preview = previewComponent ?? (handle?.definition.previewLayout === "custom" ? "iframe" : PreviewCanvas);
|
|
1063
|
+
useEffect4(() => {
|
|
1064
|
+
const onKey = (event) => {
|
|
1065
|
+
if (event.defaultPrevented || event.isComposing || event.altKey || !(event.metaKey || event.ctrlKey)) return;
|
|
1066
|
+
if (event.key.toLowerCase() !== "b") return;
|
|
1067
|
+
event.preventDefault();
|
|
1068
|
+
if (event.repeat) return;
|
|
1069
|
+
const active = document.activeElement;
|
|
1070
|
+
if (active instanceof HTMLElement && active.closest('[data-slot="sidebar-container"], [data-slot="studio-inspector"]')) {
|
|
1071
|
+
active.blur();
|
|
1072
|
+
preview.current?.focus();
|
|
1073
|
+
}
|
|
1074
|
+
setPanelsHidden((hidden) => !hidden);
|
|
1075
|
+
};
|
|
1076
|
+
window.addEventListener("keydown", onKey);
|
|
1077
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1078
|
+
}, []);
|
|
1079
|
+
const compare = (showSaved) => {
|
|
1080
|
+
if (!handle) return;
|
|
1081
|
+
handle.compare(showSaved);
|
|
1082
|
+
postCompare(preview.current?.contentWindow, handle.definition.id, showSaved);
|
|
1083
|
+
};
|
|
1084
|
+
useEffect4(() => () => handle?.compare(false), [handle]);
|
|
1085
|
+
const current = useRef3({ handle, compare });
|
|
1086
|
+
current.current = { handle, compare };
|
|
1087
|
+
useEffect4(() => {
|
|
1088
|
+
const onKey = (event) => {
|
|
1089
|
+
if (event.key !== COMPARE_KEY || editable(event.target) || event.repeat) return;
|
|
1090
|
+
event.preventDefault();
|
|
1091
|
+
const { handle: handle2, compare: compare2 } = current.current;
|
|
1092
|
+
compare2(event.type === "keydown" && !!handle2?.state?.dirty);
|
|
1093
|
+
};
|
|
1094
|
+
const release = () => current.current.compare(false);
|
|
1095
|
+
addEventListener("keydown", onKey);
|
|
1096
|
+
addEventListener("keyup", onKey);
|
|
1097
|
+
addEventListener("blur", release);
|
|
1098
|
+
return () => {
|
|
1099
|
+
release();
|
|
1100
|
+
removeEventListener("keydown", onKey);
|
|
1101
|
+
removeEventListener("keyup", onKey);
|
|
1102
|
+
removeEventListener("blur", release);
|
|
1103
|
+
};
|
|
1104
|
+
}, []);
|
|
1105
|
+
useEffect4(() => {
|
|
1106
|
+
const fromHash = () => {
|
|
1107
|
+
const id = decodeURIComponent(location.hash.slice(1));
|
|
1108
|
+
if (studio.tools.some((handle2) => handle2.definition.id === id)) setSelected(id);
|
|
1109
|
+
};
|
|
1110
|
+
setMounted(true);
|
|
1111
|
+
fromHash();
|
|
1112
|
+
addEventListener("hashchange", fromHash);
|
|
1113
|
+
return () => removeEventListener("hashchange", fromHash);
|
|
1114
|
+
}, [studio]);
|
|
1115
|
+
if (!mounted) return null;
|
|
1116
|
+
const sidebarOpen = !document.cookie.split("; ").includes("sidebar_state=false");
|
|
1117
|
+
return /* @__PURE__ */ jsxs7(
|
|
1118
|
+
SidebarProvider,
|
|
1119
|
+
{
|
|
1120
|
+
defaultOpen: sidebarOpen,
|
|
1121
|
+
"data-preview-layout": previewComponent ? "canvas" : handle?.definition.previewLayout ?? "canvas",
|
|
1122
|
+
className: "studio-workspace relative h-svh min-h-0 overflow-hidden text-xs",
|
|
1123
|
+
children: [
|
|
1124
|
+
handle?.definition.preview ? /* @__PURE__ */ jsx13(
|
|
1125
|
+
Preview,
|
|
1126
|
+
{
|
|
1127
|
+
"data-slot": "studio-preview",
|
|
1128
|
+
ref: preview,
|
|
1129
|
+
src: handle.definition.preview,
|
|
1130
|
+
title: handle.definition.title ?? handle.definition.id,
|
|
1131
|
+
onLoad: (event) => forwardShortcuts(event.currentTarget)
|
|
1132
|
+
},
|
|
1133
|
+
handle.definition.id
|
|
1134
|
+
) : /* @__PURE__ */ jsx13("div", { className: "absolute inset-0 grid place-items-center text-faint", children: handle ? "This tool has no preview" : "No tools registered" }),
|
|
1135
|
+
panelsHidden && ["left", "right"].map((side) => {
|
|
1136
|
+
const Chevron = side === "left" ? ChevronRight : ChevronLeft;
|
|
1137
|
+
return /* @__PURE__ */ jsx13(
|
|
1138
|
+
"div",
|
|
1139
|
+
{
|
|
1140
|
+
"data-slot": "studio-panel-edge",
|
|
1141
|
+
"data-side": side,
|
|
1142
|
+
className: cn("group/edge absolute inset-y-0 z-20 w-8", side === "left" ? "left-0" : "right-0"),
|
|
1143
|
+
children: /* @__PURE__ */ jsx13(
|
|
1144
|
+
Hint,
|
|
1145
|
+
{
|
|
1146
|
+
side: side === "left" ? "right" : "left",
|
|
1147
|
+
sideOffset: 8,
|
|
1148
|
+
className: "flex items-center gap-2",
|
|
1149
|
+
content: /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
1150
|
+
"Show Studio panels",
|
|
1151
|
+
" ",
|
|
1152
|
+
/* @__PURE__ */ jsxs7(KbdGroup, { children: [
|
|
1153
|
+
/* @__PURE__ */ jsx13(Kbd, { children: "\u2318/Ctrl" }),
|
|
1154
|
+
/* @__PURE__ */ jsx13(Kbd, { children: "B" })
|
|
1155
|
+
] })
|
|
1156
|
+
] }),
|
|
1157
|
+
children: /* @__PURE__ */ jsx13(Panel, { asChild: true, children: /* @__PURE__ */ jsx13(
|
|
1158
|
+
Button,
|
|
1159
|
+
{
|
|
1160
|
+
"data-slot": "studio-panel-reveal",
|
|
1161
|
+
variant: "ghost",
|
|
1162
|
+
size: "icon",
|
|
1163
|
+
"aria-label": "Show Studio panels",
|
|
1164
|
+
onClick: () => setPanelsHidden(false),
|
|
1165
|
+
className: cn(
|
|
1166
|
+
"absolute top-1/2 h-16 w-6 -translate-y-1/2 cursor-pointer px-0 text-sidebar-foreground opacity-0 transition-[translate,opacity] duration-200 ease-out hover:bg-sidebar/95 hover:text-foreground focus:translate-x-0 focus:opacity-100 group-hover/edge:translate-x-0 group-hover/edge:opacity-100 motion-reduce:transition-none [@media(hover:none)]:translate-x-0 [@media(hover:none)]:opacity-100",
|
|
1167
|
+
side === "left" ? "left-1 -translate-x-[calc(100%+0.25rem)]" : "right-1 translate-x-[calc(100%+0.25rem)]"
|
|
1168
|
+
),
|
|
1169
|
+
children: /* @__PURE__ */ jsx13(Chevron, { "aria-hidden": true })
|
|
1170
|
+
}
|
|
1171
|
+
) })
|
|
1172
|
+
}
|
|
1173
|
+
)
|
|
1174
|
+
},
|
|
1175
|
+
side
|
|
1176
|
+
);
|
|
1177
|
+
}),
|
|
1178
|
+
/* @__PURE__ */ jsxs7(
|
|
1179
|
+
Sidebar,
|
|
1180
|
+
{
|
|
1181
|
+
collapsible: "icon",
|
|
1182
|
+
variant: "floating",
|
|
1183
|
+
inert: panelsHidden,
|
|
1184
|
+
"aria-hidden": panelsHidden,
|
|
1185
|
+
className: cn(
|
|
1186
|
+
"transition-[left,right,width,translate] duration-200 ease-out motion-reduce:transition-none",
|
|
1187
|
+
panelsHidden && "-translate-x-[calc(100%+1rem)]"
|
|
1188
|
+
),
|
|
1189
|
+
children: [
|
|
1190
|
+
/* @__PURE__ */ jsx13(SidebarHeader, { className: "h-10 flex-row items-center overflow-hidden py-0 pr-0 pl-4 transition-[padding] duration-200 ease-out group-data-[collapsible=icon]:pl-[calc(1.5rem-0.875rem*352/144/2)] motion-reduce:transition-none", children: /* @__PURE__ */ jsxs7("span", { className: "flex items-center gap-2.5 text-foreground", children: [
|
|
1191
|
+
/* @__PURE__ */ jsx13(Logo, { className: "h-3.5 w-auto shrink-0" }),
|
|
1192
|
+
/* @__PURE__ */ jsx13("strong", { className: "tracking-[0.08em] whitespace-nowrap", children: "STUDIO" }),
|
|
1193
|
+
/* @__PURE__ */ jsxs7("span", { "data-slot": "studio-version", className: "text-[10px] text-faint group-data-[collapsible=icon]:hidden", children: [
|
|
1194
|
+
"v",
|
|
1195
|
+
version
|
|
1196
|
+
] })
|
|
1197
|
+
] }) }),
|
|
1198
|
+
/* @__PURE__ */ jsx13(SidebarContent, { children: /* @__PURE__ */ jsx13(SidebarGroup, { children: /* @__PURE__ */ jsx13(SidebarMenu, { children: tools.map(({ definition, state }) => {
|
|
1199
|
+
const title = definition.title ?? definition.id;
|
|
1200
|
+
const isPinned = pinned.has(definition.id);
|
|
1201
|
+
const pinLabel = `${isPinned ? "Unpin" : "Pin"} ${title}`;
|
|
1202
|
+
return /* @__PURE__ */ jsxs7(SidebarMenuItem, { children: [
|
|
1203
|
+
/* @__PURE__ */ jsxs7(
|
|
1204
|
+
SidebarMenuButton,
|
|
1205
|
+
{
|
|
1206
|
+
isActive: definition.id === selected,
|
|
1207
|
+
tooltip: title,
|
|
1208
|
+
onClick: () => location.hash = definition.id,
|
|
1209
|
+
children: [
|
|
1210
|
+
/* @__PURE__ */ jsxs7("span", { className: "relative", children: [
|
|
1211
|
+
/* @__PURE__ */ jsx13(ToolIcon, { icon: definition.icon, className: "size-4" }),
|
|
1212
|
+
state?.dirty && /* @__PURE__ */ jsx13(
|
|
1213
|
+
"span",
|
|
1214
|
+
{
|
|
1215
|
+
"aria-label": state.conflict ? "Changed on disk" : "Unsaved",
|
|
1216
|
+
className: cn(
|
|
1217
|
+
"absolute -top-0.5 -right-0.5 size-1.5 rounded-full",
|
|
1218
|
+
state.conflict ? "bg-destructive" : "bg-warning"
|
|
1219
|
+
)
|
|
1220
|
+
}
|
|
1221
|
+
)
|
|
1222
|
+
] }),
|
|
1223
|
+
/* @__PURE__ */ jsx13("span", { className: "text-clip! group-data-[collapsible=icon]:hidden", children: title })
|
|
1224
|
+
]
|
|
1225
|
+
}
|
|
1226
|
+
),
|
|
1227
|
+
/* @__PURE__ */ jsx13(Hint, { content: pinLabel, side: "right", children: /* @__PURE__ */ jsx13(
|
|
1228
|
+
SidebarMenuAction,
|
|
1229
|
+
{
|
|
1230
|
+
type: "button",
|
|
1231
|
+
"aria-label": pinLabel,
|
|
1232
|
+
"aria-pressed": isPinned,
|
|
1233
|
+
showOnHover: !isPinned,
|
|
1234
|
+
onClick: () => togglePin(definition.id),
|
|
1235
|
+
className: cn(
|
|
1236
|
+
"cursor-pointer [&>svg]:size-3 [@media(hover:none)]:opacity-100",
|
|
1237
|
+
isPinned && "text-sidebar-primary"
|
|
1238
|
+
),
|
|
1239
|
+
children: /* @__PURE__ */ jsx13(Pin2, { "aria-hidden": true, fill: isPinned ? "currentColor" : "none" })
|
|
1240
|
+
}
|
|
1241
|
+
) })
|
|
1242
|
+
] }, definition.id);
|
|
1243
|
+
}) }) }) }),
|
|
1244
|
+
/* @__PURE__ */ jsx13(StudioFooter, { connection: studio.connection })
|
|
1245
|
+
]
|
|
1246
|
+
}
|
|
1247
|
+
),
|
|
1248
|
+
handle && /* @__PURE__ */ jsx13(Panel, { asChild: true, children: /* @__PURE__ */ jsx13(
|
|
1249
|
+
"aside",
|
|
1250
|
+
{
|
|
1251
|
+
"data-slot": "studio-inspector",
|
|
1252
|
+
inert: panelsHidden,
|
|
1253
|
+
"aria-hidden": panelsHidden,
|
|
1254
|
+
className: cn(
|
|
1255
|
+
"absolute inset-y-2 right-2 z-10 flex w-(--studio-inspector-width) flex-col gap-1 p-1 transition-transform duration-200 ease-out motion-reduce:transition-none",
|
|
1256
|
+
panelsHidden && "translate-x-[calc(100%+1rem)]"
|
|
1257
|
+
),
|
|
1258
|
+
children: /* @__PURE__ */ jsx13(
|
|
1259
|
+
Inspector,
|
|
1260
|
+
{
|
|
1261
|
+
studio,
|
|
1262
|
+
handle,
|
|
1263
|
+
onCompare: compare,
|
|
1264
|
+
fileHref: getConfigFileHref?.(handle.definition.file)
|
|
1265
|
+
},
|
|
1266
|
+
handle.definition.id
|
|
1267
|
+
)
|
|
1268
|
+
}
|
|
1269
|
+
) })
|
|
1270
|
+
]
|
|
1271
|
+
}
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// src/react/command-menu.tsx
|
|
1276
|
+
import { Pin as Pin3 } from "lucide-react";
|
|
1277
|
+
import { Dialog } from "radix-ui";
|
|
1278
|
+
import { useEffect as useEffect5, useRef as useRef4, useState as useState8 } from "react";
|
|
1279
|
+
|
|
1280
|
+
// src/react/ui/command.tsx
|
|
1281
|
+
import * as React3 from "react";
|
|
1282
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
1283
|
+
import { SearchIcon } from "lucide-react";
|
|
1284
|
+
import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1285
|
+
function Command({ className, ...props }) {
|
|
1286
|
+
return /* @__PURE__ */ jsx14(
|
|
1287
|
+
CommandPrimitive,
|
|
1288
|
+
{
|
|
1289
|
+
"data-slot": "command",
|
|
1290
|
+
className: cn(
|
|
1291
|
+
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
|
1292
|
+
className
|
|
1293
|
+
),
|
|
1294
|
+
...props
|
|
1295
|
+
}
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
var CommandInput = React3.forwardRef(function CommandInput2({ className, ...props }, ref) {
|
|
1299
|
+
return /* @__PURE__ */ jsxs8("div", { "data-slot": "command-input-wrapper", className: "flex h-9 items-center gap-2 border-b px-3", children: [
|
|
1300
|
+
/* @__PURE__ */ jsx14(SearchIcon, { "aria-hidden": true, className: "size-4 shrink-0 opacity-50" }),
|
|
1301
|
+
/* @__PURE__ */ jsx14(
|
|
1302
|
+
CommandPrimitive.Input,
|
|
1303
|
+
{
|
|
1304
|
+
ref,
|
|
1305
|
+
"data-slot": "command-input",
|
|
1306
|
+
className: cn(
|
|
1307
|
+
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
|
1308
|
+
className
|
|
1309
|
+
),
|
|
1310
|
+
...props
|
|
1311
|
+
}
|
|
1312
|
+
)
|
|
1313
|
+
] });
|
|
1314
|
+
});
|
|
1315
|
+
function CommandList({ className, ...props }) {
|
|
1316
|
+
return /* @__PURE__ */ jsx14(
|
|
1317
|
+
CommandPrimitive.List,
|
|
1318
|
+
{
|
|
1319
|
+
"data-slot": "command-list",
|
|
1320
|
+
className: cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className),
|
|
1321
|
+
...props
|
|
1322
|
+
}
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
function CommandEmpty({ ...props }) {
|
|
1326
|
+
return /* @__PURE__ */ jsx14(CommandPrimitive.Empty, { "data-slot": "command-empty", className: "py-6 text-center text-sm", ...props });
|
|
1327
|
+
}
|
|
1328
|
+
function CommandItem({ className, ...props }) {
|
|
1329
|
+
return /* @__PURE__ */ jsx14(
|
|
1330
|
+
CommandPrimitive.Item,
|
|
1331
|
+
{
|
|
1332
|
+
"data-slot": "command-item",
|
|
1333
|
+
className: cn(
|
|
1334
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
1335
|
+
className
|
|
1336
|
+
),
|
|
1337
|
+
...props
|
|
1338
|
+
}
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// src/react/command-menu.tsx
|
|
1343
|
+
import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1344
|
+
function StudioCommandMenu({ studio, studioPath = "/studio", navigate }) {
|
|
1345
|
+
const { tools, pinned } = usePinnedTools(studio);
|
|
1346
|
+
const [open, setOpen] = useState8(false);
|
|
1347
|
+
const opener = useRef4(null);
|
|
1348
|
+
const input = useRef4(null);
|
|
1349
|
+
useEffect5(() => {
|
|
1350
|
+
if (window.frameElement?.getAttribute("data-slot") === "studio-preview") return;
|
|
1351
|
+
const onKey = (event) => {
|
|
1352
|
+
if (event.defaultPrevented || event.isComposing || event.altKey || !(event.metaKey || event.ctrlKey)) return;
|
|
1353
|
+
if (event.key.toLowerCase() !== "k") return;
|
|
1354
|
+
event.preventDefault();
|
|
1355
|
+
if (event.repeat) return;
|
|
1356
|
+
setOpen((current) => !current);
|
|
1357
|
+
};
|
|
1358
|
+
window.addEventListener("keydown", onKey);
|
|
1359
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1360
|
+
}, []);
|
|
1361
|
+
const select = (id) => {
|
|
1362
|
+
setOpen(false);
|
|
1363
|
+
const target = new URL(studioPath, window.location.href);
|
|
1364
|
+
target.hash = id;
|
|
1365
|
+
if (target.pathname === window.location.pathname) {
|
|
1366
|
+
window.location.hash = target.hash;
|
|
1367
|
+
} else if (navigate) {
|
|
1368
|
+
navigate(`${target.pathname}${target.search}${target.hash}`);
|
|
1369
|
+
} else {
|
|
1370
|
+
window.location.assign(target.href);
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
return /* @__PURE__ */ jsx15(Dialog.Root, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs9(Dialog.Portal, { children: [
|
|
1374
|
+
/* @__PURE__ */ jsx15(Dialog.Overlay, { "data-slot": "studio-command-overlay" }),
|
|
1375
|
+
/* @__PURE__ */ jsxs9(
|
|
1376
|
+
Dialog.Content,
|
|
1377
|
+
{
|
|
1378
|
+
"data-slot": "studio-command-menu",
|
|
1379
|
+
onOpenAutoFocus: (event) => {
|
|
1380
|
+
event.preventDefault();
|
|
1381
|
+
opener.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
1382
|
+
input.current?.focus();
|
|
1383
|
+
},
|
|
1384
|
+
onCloseAutoFocus: (event) => {
|
|
1385
|
+
event.preventDefault();
|
|
1386
|
+
opener.current?.focus();
|
|
1387
|
+
},
|
|
1388
|
+
children: [
|
|
1389
|
+
/* @__PURE__ */ jsxs9("div", { "data-slot": "studio-command-heading", children: [
|
|
1390
|
+
/* @__PURE__ */ jsxs9(Dialog.Title, { "aria-label": "Studio tools", children: [
|
|
1391
|
+
/* @__PURE__ */ jsx15(Logo, { "aria-hidden": true }),
|
|
1392
|
+
/* @__PURE__ */ jsx15("strong", { children: "STUDIO" })
|
|
1393
|
+
] }),
|
|
1394
|
+
/* @__PURE__ */ jsx15(Dialog.Close, { "aria-label": "Close tool menu", children: /* @__PURE__ */ jsx15(Kbd, { children: "esc" }) })
|
|
1395
|
+
] }),
|
|
1396
|
+
/* @__PURE__ */ jsx15(Dialog.Description, { "data-slot": "studio-command-description", children: "Open a tool in Studio." }),
|
|
1397
|
+
/* @__PURE__ */ jsxs9(Command, { label: "Studio tools", loop: true, vimBindings: false, children: [
|
|
1398
|
+
/* @__PURE__ */ jsx15(CommandInput, { ref: input, placeholder: "Search tools\u2026" }),
|
|
1399
|
+
/* @__PURE__ */ jsxs9(CommandList, { children: [
|
|
1400
|
+
/* @__PURE__ */ jsx15(CommandEmpty, { children: studio.tools.length ? "No tools found." : "No tools registered yet." }),
|
|
1401
|
+
tools.map(({ definition: tool }) => /* @__PURE__ */ jsxs9(
|
|
1402
|
+
CommandItem,
|
|
1403
|
+
{
|
|
1404
|
+
value: tool.id,
|
|
1405
|
+
keywords: [tool.title ?? tool.id],
|
|
1406
|
+
onSelect: () => select(tool.id),
|
|
1407
|
+
children: [
|
|
1408
|
+
/* @__PURE__ */ jsx15(ToolIcon, { icon: tool.icon }),
|
|
1409
|
+
/* @__PURE__ */ jsx15("span", { "data-slot": "studio-command-label", children: tool.title ?? tool.id }),
|
|
1410
|
+
pinned.has(tool.id) && /* @__PURE__ */ jsx15(Pin3, { "aria-hidden": true })
|
|
1411
|
+
]
|
|
1412
|
+
},
|
|
1413
|
+
tool.id
|
|
1414
|
+
))
|
|
1415
|
+
] })
|
|
1416
|
+
] }),
|
|
1417
|
+
/* @__PURE__ */ jsxs9("div", { "data-slot": "studio-command-footer", "aria-hidden": true, children: [
|
|
1418
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
1419
|
+
/* @__PURE__ */ jsxs9(KbdGroup, { children: [
|
|
1420
|
+
/* @__PURE__ */ jsx15(Kbd, { children: "\u2191" }),
|
|
1421
|
+
/* @__PURE__ */ jsx15(Kbd, { children: "\u2193" })
|
|
1422
|
+
] }),
|
|
1423
|
+
"navigate"
|
|
1424
|
+
] }),
|
|
1425
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
1426
|
+
/* @__PURE__ */ jsx15(Kbd, { children: "\u21B5" }),
|
|
1427
|
+
" open tool"
|
|
1428
|
+
] })
|
|
1429
|
+
] })
|
|
1430
|
+
]
|
|
1431
|
+
}
|
|
1432
|
+
)
|
|
1433
|
+
] }) });
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// src/react/select.tsx
|
|
1437
|
+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
1438
|
+
import { Select } from "radix-ui";
|
|
1439
|
+
import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1440
|
+
function StudioSelect({ label, title, className, children, ...props }) {
|
|
1441
|
+
return /* @__PURE__ */ jsxs10(Select.Root, { ...props, children: [
|
|
1442
|
+
/* @__PURE__ */ jsxs10(
|
|
1443
|
+
Select.Trigger,
|
|
1444
|
+
{
|
|
1445
|
+
"data-slot": "select-trigger",
|
|
1446
|
+
"aria-label": label,
|
|
1447
|
+
title,
|
|
1448
|
+
className: cn(
|
|
1449
|
+
"border-input text-foreground focus-visible:border-ring focus-visible:ring-ring/50 flex h-7 max-w-full shrink-0 items-center justify-between gap-2 rounded-md border bg-(--field) px-2 text-xs whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-(--field-hover) focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span:first-child]:truncate",
|
|
1450
|
+
className
|
|
1451
|
+
),
|
|
1452
|
+
children: [
|
|
1453
|
+
/* @__PURE__ */ jsx16(Select.Value, {}),
|
|
1454
|
+
/* @__PURE__ */ jsx16(Select.Icon, { children: /* @__PURE__ */ jsx16(ChevronDown, { className: "shrink-0 text-muted-foreground", size: 14, strokeWidth: 1.5, "aria-hidden": true }) })
|
|
1455
|
+
]
|
|
1456
|
+
}
|
|
1457
|
+
),
|
|
1458
|
+
/* @__PURE__ */ jsx16(Select.Portal, { children: /* @__PURE__ */ jsxs10(
|
|
1459
|
+
Select.Content,
|
|
1460
|
+
{
|
|
1461
|
+
className: "relative z-50 max-h-(--radix-select-content-available-height) min-w-(--radix-select-trigger-width) overflow-hidden rounded-md border border-border bg-popover font-(family-name:--studio-font) text-xs font-normal text-popover-foreground shadow-md",
|
|
1462
|
+
"data-slot": "select-content",
|
|
1463
|
+
position: "popper",
|
|
1464
|
+
align: "end",
|
|
1465
|
+
sideOffset: 6,
|
|
1466
|
+
children: [
|
|
1467
|
+
/* @__PURE__ */ jsx16(Select.ScrollUpButton, { className: "flex justify-center py-1", children: /* @__PURE__ */ jsx16(ChevronUp, { size: 14, "aria-hidden": true }) }),
|
|
1468
|
+
/* @__PURE__ */ jsx16(Select.Viewport, { className: "p-1", children }),
|
|
1469
|
+
/* @__PURE__ */ jsx16(Select.ScrollDownButton, { className: "flex justify-center py-1", children: /* @__PURE__ */ jsx16(ChevronDown, { size: 14, "aria-hidden": true }) })
|
|
1470
|
+
]
|
|
1471
|
+
}
|
|
1472
|
+
) })
|
|
1473
|
+
] });
|
|
1474
|
+
}
|
|
1475
|
+
function StudioSelectItem({ className, children, ...props }) {
|
|
1476
|
+
return /* @__PURE__ */ jsxs10(
|
|
1477
|
+
Select.Item,
|
|
1478
|
+
{
|
|
1479
|
+
"data-slot": "select-item",
|
|
1480
|
+
className: cn(
|
|
1481
|
+
"relative flex min-h-7 cursor-default items-center rounded-sm py-1.5 pr-8 pl-2 outline-none select-none data-[highlighted]:bg-(--field-hover) data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
1482
|
+
className
|
|
1483
|
+
),
|
|
1484
|
+
...props,
|
|
1485
|
+
children: [
|
|
1486
|
+
/* @__PURE__ */ jsx16(Select.ItemText, { children }),
|
|
1487
|
+
/* @__PURE__ */ jsx16(Select.ItemIndicator, { className: "absolute right-2", children: /* @__PURE__ */ jsx16(Check, { size: 14, strokeWidth: 1.5, "aria-hidden": true }) })
|
|
1488
|
+
]
|
|
1489
|
+
}
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// src/react/ui/input.tsx
|
|
1494
|
+
import { forwardRef as forwardRef5 } from "react";
|
|
1495
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
1496
|
+
var Input = forwardRef5(function Input2({ className, ...props }, ref) {
|
|
1497
|
+
return /* @__PURE__ */ jsx17(
|
|
1498
|
+
"input",
|
|
1499
|
+
{
|
|
1500
|
+
ref,
|
|
1501
|
+
"data-slot": "input",
|
|
1502
|
+
className: cn(
|
|
1503
|
+
"border-input text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 h-8 w-full min-w-0 rounded-md border bg-transparent py-1 ps-3 pe-3 text-sm transition-[color,box-shadow] outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1504
|
+
className
|
|
1505
|
+
),
|
|
1506
|
+
...props
|
|
1507
|
+
}
|
|
1508
|
+
);
|
|
1509
|
+
});
|
|
1510
|
+
export {
|
|
1511
|
+
CanvasToolbar,
|
|
1512
|
+
PreviewCanvas,
|
|
1513
|
+
Studio,
|
|
1514
|
+
StudioCommandMenu,
|
|
1515
|
+
Input as StudioInput,
|
|
1516
|
+
Panel as StudioPanel,
|
|
1517
|
+
PanelContent as StudioPanelContent,
|
|
1518
|
+
PanelFooter as StudioPanelFooter,
|
|
1519
|
+
PanelHeader as StudioPanelHeader,
|
|
1520
|
+
StudioSelect,
|
|
1521
|
+
StudioSelectItem,
|
|
1522
|
+
StudioSurface,
|
|
1523
|
+
StudioToolbar,
|
|
1524
|
+
useStudio,
|
|
1525
|
+
useToolConfig
|
|
1526
|
+
};
|
|
1527
|
+
//# sourceMappingURL=react.js.map
|