@toriistudio/v0-playground 0.5.3 → 0.6.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 +1 -20
- package/dist/index.d.mts +103 -30
- package/dist/index.d.ts +103 -30
- package/dist/index.js +1132 -342
- package/dist/index.mjs +1132 -338
- package/dist/preset.d.mts +5 -0
- package/dist/preset.d.ts +5 -0
- package/package.json +4 -33
package/dist/index.js
CHANGED
|
@@ -31,18 +31,26 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
Button: () => Button,
|
|
34
|
-
CameraLogger: () => CameraLogger,
|
|
35
|
-
Canvas: () => Canvas_default,
|
|
36
34
|
ControlsProvider: () => ControlsProvider,
|
|
35
|
+
DEFAULT_ADVANCED_PALETTE: () => DEFAULT_ADVANCED_PALETTE,
|
|
36
|
+
DEFAULT_HEX_PALETTE: () => DEFAULT_HEX_PALETTE,
|
|
37
37
|
Playground: () => Playground,
|
|
38
|
-
|
|
38
|
+
advancedPaletteToHexColors: () => advancedPaletteToHexColors,
|
|
39
|
+
clonePalette: () => clonePalette,
|
|
40
|
+
computePaletteGradient: () => computePaletteGradient,
|
|
41
|
+
createAdvancedPalette: () => createAdvancedPalette,
|
|
42
|
+
createPaletteSignature: () => createPaletteSignature,
|
|
43
|
+
hexToPaletteValue: () => hexToPaletteValue,
|
|
44
|
+
paletteValueToHex: () => paletteValueToHex,
|
|
45
|
+
useAdvancedPaletteControls: () => useAdvancedPaletteControls,
|
|
39
46
|
useControls: () => useControls,
|
|
47
|
+
useDefaultAdvancedPaletteControls: () => useDefaultAdvancedPaletteControls,
|
|
40
48
|
useUrlSyncedControls: () => useUrlSyncedControls
|
|
41
49
|
});
|
|
42
50
|
module.exports = __toCommonJS(src_exports);
|
|
43
51
|
|
|
44
52
|
// src/components/Playground/Playground.tsx
|
|
45
|
-
var
|
|
53
|
+
var import_react7 = require("react");
|
|
46
54
|
var import_lucide_react4 = require("lucide-react");
|
|
47
55
|
|
|
48
56
|
// src/context/ResizableLayout.tsx
|
|
@@ -160,6 +168,274 @@ var getUrlParams = () => {
|
|
|
160
168
|
return entries;
|
|
161
169
|
};
|
|
162
170
|
|
|
171
|
+
// src/constants/urlParams.ts
|
|
172
|
+
var NO_CONTROLS_PARAM = "nocontrols";
|
|
173
|
+
var PRESENTATION_PARAM = "presentation";
|
|
174
|
+
var CONTROLS_ONLY_PARAM = "controlsonly";
|
|
175
|
+
|
|
176
|
+
// src/utils/getControlsChannelName.ts
|
|
177
|
+
var EXCLUDED_KEYS = /* @__PURE__ */ new Set([
|
|
178
|
+
NO_CONTROLS_PARAM,
|
|
179
|
+
PRESENTATION_PARAM,
|
|
180
|
+
CONTROLS_ONLY_PARAM
|
|
181
|
+
]);
|
|
182
|
+
var getControlsChannelName = () => {
|
|
183
|
+
if (typeof window === "undefined") return null;
|
|
184
|
+
const params = new URLSearchParams(window.location.search);
|
|
185
|
+
for (const key of EXCLUDED_KEYS) {
|
|
186
|
+
params.delete(key);
|
|
187
|
+
}
|
|
188
|
+
const query = params.toString();
|
|
189
|
+
const base = window.location.pathname || "/";
|
|
190
|
+
return `v0-controls:${base}${query ? `?${query}` : ""}`;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/lib/advancedPalette.ts
|
|
194
|
+
var CHANNEL_KEYS = ["r", "g", "b"];
|
|
195
|
+
var DEFAULT_CHANNEL_LABELS = {
|
|
196
|
+
r: "Red",
|
|
197
|
+
g: "Green",
|
|
198
|
+
b: "Blue"
|
|
199
|
+
};
|
|
200
|
+
var DEFAULT_SECTIONS = [
|
|
201
|
+
{ key: "A", label: "Vector A", helper: "Base offset" },
|
|
202
|
+
{ key: "B", label: "Vector B", helper: "Amplitude" },
|
|
203
|
+
{ key: "C", label: "Vector C", helper: "Frequency" },
|
|
204
|
+
{ key: "D", label: "Vector D", helper: "Phase shift" }
|
|
205
|
+
];
|
|
206
|
+
var DEFAULT_RANGES = {
|
|
207
|
+
A: { min: 0, max: 1, step: 0.01 },
|
|
208
|
+
B: { min: -1, max: 1, step: 0.01 },
|
|
209
|
+
C: { min: 0, max: 2, step: 0.01 },
|
|
210
|
+
D: { min: 0, max: 1, step: 0.01 }
|
|
211
|
+
};
|
|
212
|
+
var DEFAULT_HIDDEN_KEY_PREFIX = "palette";
|
|
213
|
+
var DEFAULT_GRADIENT_STEPS = 12;
|
|
214
|
+
var DEFAULT_HEX_PALETTE = {
|
|
215
|
+
A: { r: 0.5, g: 0.5, b: 0.5 },
|
|
216
|
+
B: { r: 0.5, g: 0.5, b: 0.5 },
|
|
217
|
+
C: { r: 1, g: 1, b: 1 },
|
|
218
|
+
D: { r: 0, g: 0.1, b: 0.2 }
|
|
219
|
+
};
|
|
220
|
+
var createPaletteControlKey = (prefix, section, channel) => `${prefix}${section}${channel}`;
|
|
221
|
+
var clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
222
|
+
var clamp01 = (value) => clamp(value, 0, 1);
|
|
223
|
+
var toNumberOr = (value, fallback) => {
|
|
224
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
225
|
+
if (typeof value === "string") {
|
|
226
|
+
const parsed = parseFloat(value);
|
|
227
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
228
|
+
}
|
|
229
|
+
return fallback;
|
|
230
|
+
};
|
|
231
|
+
var paletteColorAt = (palette, t) => {
|
|
232
|
+
const twoPi = Math.PI * 2;
|
|
233
|
+
const computeChannel = (a, b, c, d) => {
|
|
234
|
+
const value = a + b * Math.cos(twoPi * (c * t + d));
|
|
235
|
+
return clamp(value, 0, 1);
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
r: computeChannel(
|
|
239
|
+
palette.A?.r ?? 0,
|
|
240
|
+
palette.B?.r ?? 0,
|
|
241
|
+
palette.C?.r ?? 0,
|
|
242
|
+
palette.D?.r ?? 0
|
|
243
|
+
),
|
|
244
|
+
g: computeChannel(
|
|
245
|
+
palette.A?.g ?? 0,
|
|
246
|
+
palette.B?.g ?? 0,
|
|
247
|
+
palette.C?.g ?? 0,
|
|
248
|
+
palette.D?.g ?? 0
|
|
249
|
+
),
|
|
250
|
+
b: computeChannel(
|
|
251
|
+
palette.A?.b ?? 0,
|
|
252
|
+
palette.B?.b ?? 0,
|
|
253
|
+
palette.C?.b ?? 0,
|
|
254
|
+
palette.D?.b ?? 0
|
|
255
|
+
)
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
var toRgba = ({ r, g, b }, alpha = 0.5) => `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
|
|
259
|
+
b * 255
|
|
260
|
+
)}, ${alpha})`;
|
|
261
|
+
var computePaletteGradient = (palette, steps = DEFAULT_GRADIENT_STEPS) => {
|
|
262
|
+
const stops = Array.from({ length: steps }, (_, index) => {
|
|
263
|
+
const t = index / (steps - 1);
|
|
264
|
+
const color = paletteColorAt(palette, t);
|
|
265
|
+
const stop = (t * 100).toFixed(1);
|
|
266
|
+
return `${toRgba(color)} ${stop}%`;
|
|
267
|
+
});
|
|
268
|
+
return `linear-gradient(to right, ${stops.join(", ")})`;
|
|
269
|
+
};
|
|
270
|
+
var createPaletteSignature = (palette) => Object.entries(palette).sort(([aKey], [bKey]) => aKey.localeCompare(bKey)).flatMap(
|
|
271
|
+
([, channels]) => CHANNEL_KEYS.map((channel) => (channels?.[channel] ?? 0).toFixed(3))
|
|
272
|
+
).join("-");
|
|
273
|
+
var isAdvancedPaletteValue = (value) => Boolean(
|
|
274
|
+
value && typeof value === "object" && CHANNEL_KEYS.every((channel) => {
|
|
275
|
+
const channelValue = value[channel];
|
|
276
|
+
return typeof channelValue === "number" && Number.isFinite(channelValue);
|
|
277
|
+
})
|
|
278
|
+
);
|
|
279
|
+
var isAdvancedPalette = (value) => Boolean(
|
|
280
|
+
value && typeof value === "object" && Object.values(value).every(
|
|
281
|
+
(entry) => isAdvancedPaletteValue(entry) || typeof entry === "object"
|
|
282
|
+
)
|
|
283
|
+
);
|
|
284
|
+
var normalizePaletteValue = (source) => {
|
|
285
|
+
if (typeof source === "string") {
|
|
286
|
+
return hexToPaletteValue(source);
|
|
287
|
+
}
|
|
288
|
+
const channelSource = source ?? {};
|
|
289
|
+
const toChannel = (channel) => clamp01(
|
|
290
|
+
toNumberOr(
|
|
291
|
+
channelSource[channel],
|
|
292
|
+
0
|
|
293
|
+
)
|
|
294
|
+
);
|
|
295
|
+
return {
|
|
296
|
+
r: toChannel("r"),
|
|
297
|
+
g: toChannel("g"),
|
|
298
|
+
b: toChannel("b")
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
var createPaletteFromRecord = (record) => Object.entries(record).reduce((acc, [key, value]) => {
|
|
302
|
+
acc[key] = normalizePaletteValue(value);
|
|
303
|
+
return acc;
|
|
304
|
+
}, {});
|
|
305
|
+
var clonePalette = (palette) => Object.fromEntries(
|
|
306
|
+
Object.entries(palette).map(([sectionKey, channels]) => [
|
|
307
|
+
sectionKey,
|
|
308
|
+
{ ...channels }
|
|
309
|
+
])
|
|
310
|
+
);
|
|
311
|
+
var hexComponentToNormalized = (component) => clamp01(parseInt(component, 16) / 255 || 0);
|
|
312
|
+
var normalizedChannelToHex = (value) => Math.round(clamp01(value) * 255).toString(16).padStart(2, "0");
|
|
313
|
+
var sanitizeHex = (hex) => {
|
|
314
|
+
let sanitized = hex.trim();
|
|
315
|
+
if (sanitized.startsWith("#")) {
|
|
316
|
+
sanitized = sanitized.slice(1);
|
|
317
|
+
}
|
|
318
|
+
if (sanitized.length === 3) {
|
|
319
|
+
sanitized = sanitized.split("").map((char) => char + char).join("");
|
|
320
|
+
}
|
|
321
|
+
return sanitized.length === 6 ? sanitized : null;
|
|
322
|
+
};
|
|
323
|
+
var hexToPaletteValue = (hex) => {
|
|
324
|
+
const sanitized = sanitizeHex(hex);
|
|
325
|
+
if (!sanitized) {
|
|
326
|
+
return { r: 0, g: 0, b: 0 };
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
r: hexComponentToNormalized(sanitized.slice(0, 2)),
|
|
330
|
+
g: hexComponentToNormalized(sanitized.slice(2, 4)),
|
|
331
|
+
b: hexComponentToNormalized(sanitized.slice(4, 6))
|
|
332
|
+
};
|
|
333
|
+
};
|
|
334
|
+
var paletteValueToHex = (value) => `#${normalizedChannelToHex(value.r)}${normalizedChannelToHex(
|
|
335
|
+
value.g
|
|
336
|
+
)}${normalizedChannelToHex(value.b)}`;
|
|
337
|
+
var createAdvancedPalette = (source = DEFAULT_HEX_PALETTE, options) => {
|
|
338
|
+
if (Array.isArray(source)) {
|
|
339
|
+
const order = options?.sectionOrder ?? DEFAULT_SECTIONS.map((section) => section.key);
|
|
340
|
+
const record = {};
|
|
341
|
+
source.forEach((value, index) => {
|
|
342
|
+
const preferredKey = order[index];
|
|
343
|
+
const fallbackKey = `Color${index + 1}`;
|
|
344
|
+
const key = preferredKey && !(preferredKey in record) ? preferredKey : fallbackKey;
|
|
345
|
+
record[key] = value;
|
|
346
|
+
});
|
|
347
|
+
return createPaletteFromRecord(record);
|
|
348
|
+
}
|
|
349
|
+
if (isAdvancedPalette(source)) {
|
|
350
|
+
return clonePalette(
|
|
351
|
+
Object.entries(source ?? {}).reduce(
|
|
352
|
+
(acc, [key, value]) => {
|
|
353
|
+
acc[key] = normalizePaletteValue(value);
|
|
354
|
+
return acc;
|
|
355
|
+
},
|
|
356
|
+
{}
|
|
357
|
+
)
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
if (source && typeof source === "object") {
|
|
361
|
+
return createPaletteFromRecord(source);
|
|
362
|
+
}
|
|
363
|
+
return createPaletteFromRecord(DEFAULT_HEX_PALETTE);
|
|
364
|
+
};
|
|
365
|
+
var DEFAULT_ADVANCED_PALETTE = createAdvancedPalette(
|
|
366
|
+
DEFAULT_HEX_PALETTE
|
|
367
|
+
);
|
|
368
|
+
var advancedPaletteToHexColors = (palette, options) => {
|
|
369
|
+
const fallbackPalette = options?.fallbackPalette ?? DEFAULT_ADVANCED_PALETTE;
|
|
370
|
+
const orderedKeys = options?.sectionOrder ?? (Object.keys(palette).length > 0 ? Object.keys(palette) : Object.keys(fallbackPalette));
|
|
371
|
+
const uniqueKeys = Array.from(new Set(orderedKeys));
|
|
372
|
+
if (uniqueKeys.length === 0) {
|
|
373
|
+
uniqueKeys.push(...Object.keys(DEFAULT_ADVANCED_PALETTE));
|
|
374
|
+
}
|
|
375
|
+
const defaultColor = options?.defaultColor ?? "#000000";
|
|
376
|
+
return uniqueKeys.map((key) => {
|
|
377
|
+
const paletteValue = palette[key] ?? fallbackPalette[key];
|
|
378
|
+
if (!paletteValue) return defaultColor;
|
|
379
|
+
return paletteValueToHex(paletteValue);
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
var createDefaultSectionsFromPalette = (palette) => {
|
|
383
|
+
const sectionKeys = Object.keys(palette);
|
|
384
|
+
if (sectionKeys.length === 0) return DEFAULT_SECTIONS;
|
|
385
|
+
return sectionKeys.map((key, index) => ({
|
|
386
|
+
key,
|
|
387
|
+
label: `Vector ${key}`,
|
|
388
|
+
helper: DEFAULT_SECTIONS[index]?.helper ?? "Palette parameter"
|
|
389
|
+
}));
|
|
390
|
+
};
|
|
391
|
+
var resolveAdvancedPaletteConfig = (config) => {
|
|
392
|
+
const defaultPalette = createAdvancedPalette(config.defaultPalette);
|
|
393
|
+
const sections = config.sections ?? createDefaultSectionsFromPalette(defaultPalette);
|
|
394
|
+
const ranges = {};
|
|
395
|
+
sections.forEach((section) => {
|
|
396
|
+
ranges[section.key] = config.ranges?.[section.key] ?? DEFAULT_RANGES[section.key] ?? {
|
|
397
|
+
min: 0,
|
|
398
|
+
max: 1,
|
|
399
|
+
step: 0.01
|
|
400
|
+
};
|
|
401
|
+
});
|
|
402
|
+
const channelLabels = {
|
|
403
|
+
...DEFAULT_CHANNEL_LABELS,
|
|
404
|
+
...config.channelLabels ?? {}
|
|
405
|
+
};
|
|
406
|
+
return {
|
|
407
|
+
...config,
|
|
408
|
+
defaultPalette,
|
|
409
|
+
sections,
|
|
410
|
+
ranges,
|
|
411
|
+
channelLabels,
|
|
412
|
+
hiddenKeyPrefix: config.hiddenKeyPrefix ?? DEFAULT_HIDDEN_KEY_PREFIX,
|
|
413
|
+
controlKey: config.controlKey ?? "advancedPaletteControl",
|
|
414
|
+
gradientSteps: config.gradientSteps ?? DEFAULT_GRADIENT_STEPS
|
|
415
|
+
};
|
|
416
|
+
};
|
|
417
|
+
var createAdvancedPaletteSchemaEntries = (schema, resolvedConfig) => {
|
|
418
|
+
const { sections, hiddenKeyPrefix, defaultPalette } = resolvedConfig;
|
|
419
|
+
const updatedSchema = { ...schema };
|
|
420
|
+
sections.forEach((section) => {
|
|
421
|
+
CHANNEL_KEYS.forEach((channel) => {
|
|
422
|
+
const key = createPaletteControlKey(
|
|
423
|
+
hiddenKeyPrefix,
|
|
424
|
+
section.key,
|
|
425
|
+
channel
|
|
426
|
+
);
|
|
427
|
+
if (!(key in updatedSchema)) {
|
|
428
|
+
updatedSchema[key] = {
|
|
429
|
+
type: "number",
|
|
430
|
+
value: defaultPalette?.[section.key]?.[channel] ?? DEFAULT_RANGES[section.key]?.min ?? 0,
|
|
431
|
+
hidden: true
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
return updatedSchema;
|
|
437
|
+
};
|
|
438
|
+
|
|
163
439
|
// src/context/ControlsContext.tsx
|
|
164
440
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
165
441
|
var ControlsContext = (0, import_react2.createContext)(null);
|
|
@@ -175,6 +451,18 @@ var ControlsProvider = ({ children }) => {
|
|
|
175
451
|
showCopyButton: true
|
|
176
452
|
});
|
|
177
453
|
const [componentName, setComponentName] = (0, import_react2.useState)();
|
|
454
|
+
const [channelName, setChannelName] = (0, import_react2.useState)(null);
|
|
455
|
+
const channelRef = (0, import_react2.useRef)(null);
|
|
456
|
+
const instanceIdRef = (0, import_react2.useRef)(null);
|
|
457
|
+
const skipBroadcastRef = (0, import_react2.useRef)(false);
|
|
458
|
+
const latestValuesRef = (0, import_react2.useRef)(values);
|
|
459
|
+
(0, import_react2.useEffect)(() => {
|
|
460
|
+
latestValuesRef.current = values;
|
|
461
|
+
}, [values]);
|
|
462
|
+
(0, import_react2.useEffect)(() => {
|
|
463
|
+
if (typeof window === "undefined") return;
|
|
464
|
+
setChannelName(getControlsChannelName());
|
|
465
|
+
}, []);
|
|
178
466
|
const setValue = (key, value) => {
|
|
179
467
|
setValues((prev) => ({ ...prev, [key]: value }));
|
|
180
468
|
};
|
|
@@ -183,9 +471,16 @@ var ControlsProvider = ({ children }) => {
|
|
|
183
471
|
setComponentName(opts.componentName);
|
|
184
472
|
}
|
|
185
473
|
if (opts?.config) {
|
|
474
|
+
const { addAdvancedPaletteControl, ...otherConfig } = opts.config;
|
|
186
475
|
setConfig((prev) => ({
|
|
187
476
|
...prev,
|
|
188
|
-
...
|
|
477
|
+
...otherConfig,
|
|
478
|
+
...Object.prototype.hasOwnProperty.call(
|
|
479
|
+
opts.config,
|
|
480
|
+
"addAdvancedPaletteControl"
|
|
481
|
+
) ? {
|
|
482
|
+
addAdvancedPaletteControl: addAdvancedPaletteControl ? resolveAdvancedPaletteConfig(addAdvancedPaletteControl) : void 0
|
|
483
|
+
} : {}
|
|
189
484
|
}));
|
|
190
485
|
}
|
|
191
486
|
setSchema((prevSchema) => ({ ...prevSchema, ...newSchema }));
|
|
@@ -202,6 +497,66 @@ var ControlsProvider = ({ children }) => {
|
|
|
202
497
|
return updated;
|
|
203
498
|
});
|
|
204
499
|
};
|
|
500
|
+
(0, import_react2.useEffect)(() => {
|
|
501
|
+
if (!channelName) return;
|
|
502
|
+
if (typeof window === "undefined") return;
|
|
503
|
+
if (typeof window.BroadcastChannel === "undefined") return;
|
|
504
|
+
const instanceId = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
505
|
+
instanceIdRef.current = instanceId;
|
|
506
|
+
const channel = new BroadcastChannel(channelName);
|
|
507
|
+
channelRef.current = channel;
|
|
508
|
+
const sendValues = () => {
|
|
509
|
+
if (!instanceIdRef.current) return;
|
|
510
|
+
channel.postMessage({
|
|
511
|
+
type: "controls-sync-values",
|
|
512
|
+
source: instanceIdRef.current,
|
|
513
|
+
values: latestValuesRef.current
|
|
514
|
+
});
|
|
515
|
+
};
|
|
516
|
+
const handleMessage = (event) => {
|
|
517
|
+
const data = event.data;
|
|
518
|
+
if (!data || data.source === instanceIdRef.current) return;
|
|
519
|
+
if (data.type === "controls-sync-request") {
|
|
520
|
+
sendValues();
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (data.type === "controls-sync-values" && data.values) {
|
|
524
|
+
const incoming = data.values;
|
|
525
|
+
setValues((prev) => {
|
|
526
|
+
const prevKeys = Object.keys(prev);
|
|
527
|
+
const incomingKeys = Object.keys(incoming);
|
|
528
|
+
const sameLength = prevKeys.length === incomingKeys.length;
|
|
529
|
+
const sameValues = sameLength && incomingKeys.every((key) => prev[key] === incoming[key]);
|
|
530
|
+
if (sameValues) return prev;
|
|
531
|
+
skipBroadcastRef.current = true;
|
|
532
|
+
return { ...incoming };
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
channel.addEventListener("message", handleMessage);
|
|
537
|
+
channel.postMessage({
|
|
538
|
+
type: "controls-sync-request",
|
|
539
|
+
source: instanceId
|
|
540
|
+
});
|
|
541
|
+
return () => {
|
|
542
|
+
channel.removeEventListener("message", handleMessage);
|
|
543
|
+
channel.close();
|
|
544
|
+
channelRef.current = null;
|
|
545
|
+
instanceIdRef.current = null;
|
|
546
|
+
};
|
|
547
|
+
}, [channelName]);
|
|
548
|
+
(0, import_react2.useEffect)(() => {
|
|
549
|
+
if (!channelRef.current || !instanceIdRef.current) return;
|
|
550
|
+
if (skipBroadcastRef.current) {
|
|
551
|
+
skipBroadcastRef.current = false;
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
channelRef.current.postMessage({
|
|
555
|
+
type: "controls-sync-values",
|
|
556
|
+
source: instanceIdRef.current,
|
|
557
|
+
values
|
|
558
|
+
});
|
|
559
|
+
}, [values]);
|
|
205
560
|
const contextValue = (0, import_react2.useMemo)(
|
|
206
561
|
() => ({
|
|
207
562
|
schema,
|
|
@@ -218,28 +573,41 @@ var ControlsProvider = ({ children }) => {
|
|
|
218
573
|
var useControls = (schema, options) => {
|
|
219
574
|
const ctx = (0, import_react2.useContext)(ControlsContext);
|
|
220
575
|
if (!ctx) throw new Error("useControls must be used within ControlsProvider");
|
|
576
|
+
const lastAdvancedPaletteSignature = (0, import_react2.useRef)(null);
|
|
221
577
|
const urlParams = getUrlParams();
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
key
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
578
|
+
const resolvedAdvancedConfig = options?.config?.addAdvancedPaletteControl ? resolveAdvancedPaletteConfig(options.config.addAdvancedPaletteControl) : void 0;
|
|
579
|
+
const schemaWithAdvanced = (0, import_react2.useMemo)(() => {
|
|
580
|
+
const baseSchema = { ...schema };
|
|
581
|
+
if (!resolvedAdvancedConfig) return baseSchema;
|
|
582
|
+
return createAdvancedPaletteSchemaEntries(
|
|
583
|
+
baseSchema,
|
|
584
|
+
resolvedAdvancedConfig
|
|
585
|
+
);
|
|
586
|
+
}, [schema, resolvedAdvancedConfig]);
|
|
587
|
+
const urlParamsKey = (0, import_react2.useMemo)(() => JSON.stringify(urlParams), [urlParams]);
|
|
588
|
+
const mergedSchema = (0, import_react2.useMemo)(() => {
|
|
589
|
+
return Object.fromEntries(
|
|
590
|
+
Object.entries(schemaWithAdvanced).map(([key, control]) => {
|
|
591
|
+
const urlValue = urlParams[key];
|
|
592
|
+
if (!urlValue || !("value" in control)) return [key, control];
|
|
593
|
+
const defaultValue = control.value;
|
|
594
|
+
let parsed = urlValue;
|
|
595
|
+
if (typeof defaultValue === "number") {
|
|
596
|
+
parsed = parseFloat(urlValue);
|
|
597
|
+
if (isNaN(parsed)) parsed = defaultValue;
|
|
598
|
+
} else if (typeof defaultValue === "boolean") {
|
|
599
|
+
parsed = urlValue === "true";
|
|
239
600
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
601
|
+
return [
|
|
602
|
+
key,
|
|
603
|
+
{
|
|
604
|
+
...control,
|
|
605
|
+
value: parsed
|
|
606
|
+
}
|
|
607
|
+
];
|
|
608
|
+
})
|
|
609
|
+
);
|
|
610
|
+
}, [schemaWithAdvanced, urlParams, urlParamsKey]);
|
|
243
611
|
(0, import_react2.useEffect)(() => {
|
|
244
612
|
ctx.registerSchema(mergedSchema, options);
|
|
245
613
|
}, [JSON.stringify(mergedSchema), JSON.stringify(options)]);
|
|
@@ -250,8 +618,35 @@ var useControls = (schema, options) => {
|
|
|
250
618
|
}
|
|
251
619
|
}
|
|
252
620
|
}, [JSON.stringify(mergedSchema), JSON.stringify(ctx.values)]);
|
|
621
|
+
(0, import_react2.useEffect)(() => {
|
|
622
|
+
if (!resolvedAdvancedConfig?.onPaletteChange) return;
|
|
623
|
+
const palette = resolvedAdvancedConfig.sections.reduce(
|
|
624
|
+
(acc, section) => {
|
|
625
|
+
const channels = CHANNEL_KEYS.reduce(
|
|
626
|
+
(channelAcc, channel) => {
|
|
627
|
+
const key = createPaletteControlKey(
|
|
628
|
+
resolvedAdvancedConfig.hiddenKeyPrefix,
|
|
629
|
+
section.key,
|
|
630
|
+
channel
|
|
631
|
+
);
|
|
632
|
+
const fallback = resolvedAdvancedConfig.defaultPalette?.[section.key]?.[channel] ?? 0;
|
|
633
|
+
channelAcc[channel] = toNumberOr(ctx.values[key], fallback);
|
|
634
|
+
return channelAcc;
|
|
635
|
+
},
|
|
636
|
+
{}
|
|
637
|
+
);
|
|
638
|
+
acc[section.key] = channels;
|
|
639
|
+
return acc;
|
|
640
|
+
},
|
|
641
|
+
{}
|
|
642
|
+
);
|
|
643
|
+
const signature = createPaletteSignature(palette);
|
|
644
|
+
if (lastAdvancedPaletteSignature.current === signature) return;
|
|
645
|
+
lastAdvancedPaletteSignature.current = signature;
|
|
646
|
+
resolvedAdvancedConfig.onPaletteChange(clonePalette(palette));
|
|
647
|
+
}, [ctx.values, resolvedAdvancedConfig]);
|
|
253
648
|
const typedValues = ctx.values;
|
|
254
|
-
const
|
|
649
|
+
const jsx14 = (0, import_react2.useCallback)(() => {
|
|
255
650
|
if (!options?.componentName) return "";
|
|
256
651
|
const props = Object.entries(typedValues).map(([key, val]) => {
|
|
257
652
|
if (typeof val === "string") return `${key}="${val}"`;
|
|
@@ -265,13 +660,13 @@ var useControls = (schema, options) => {
|
|
|
265
660
|
controls: ctx.values,
|
|
266
661
|
schema: ctx.schema,
|
|
267
662
|
setValue: ctx.setValue,
|
|
268
|
-
jsx:
|
|
663
|
+
jsx: jsx14
|
|
269
664
|
};
|
|
270
665
|
};
|
|
271
666
|
var useUrlSyncedControls = useControls;
|
|
272
667
|
|
|
273
668
|
// src/components/ControlPanel/ControlPanel.tsx
|
|
274
|
-
var
|
|
669
|
+
var import_react5 = require("react");
|
|
275
670
|
var import_lucide_react3 = require("lucide-react");
|
|
276
671
|
|
|
277
672
|
// src/hooks/usePreviewUrl.ts
|
|
@@ -281,7 +676,7 @@ var usePreviewUrl = (values, basePath = "") => {
|
|
|
281
676
|
(0, import_react3.useEffect)(() => {
|
|
282
677
|
if (typeof window === "undefined") return;
|
|
283
678
|
const params = new URLSearchParams();
|
|
284
|
-
params.set(
|
|
679
|
+
params.set(NO_CONTROLS_PARAM, "true");
|
|
285
680
|
for (const [key, value] of Object.entries(values)) {
|
|
286
681
|
if (value !== void 0 && value !== null) {
|
|
287
682
|
params.set(key, value.toString());
|
|
@@ -544,20 +939,218 @@ var Button = React8.forwardRef(
|
|
|
544
939
|
);
|
|
545
940
|
Button.displayName = "Button";
|
|
546
941
|
|
|
547
|
-
// src/
|
|
942
|
+
// src/constants/layout.ts
|
|
943
|
+
var MOBILE_CONTROL_PANEL_PEEK = 112;
|
|
944
|
+
|
|
945
|
+
// src/components/AdvancedPaletteControl/AdvancedPaletteControl.tsx
|
|
946
|
+
var import_react4 = require("react");
|
|
548
947
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
948
|
+
var AdvancedPaletteControl = ({
|
|
949
|
+
config
|
|
950
|
+
}) => {
|
|
951
|
+
const { values, setValue } = useControlsContext();
|
|
952
|
+
const palette = (0, import_react4.useMemo)(() => {
|
|
953
|
+
const result = {};
|
|
954
|
+
config.sections.forEach((section) => {
|
|
955
|
+
result[section.key] = CHANNEL_KEYS.reduce((acc, channel) => {
|
|
956
|
+
const key = createPaletteControlKey(
|
|
957
|
+
config.hiddenKeyPrefix,
|
|
958
|
+
section.key,
|
|
959
|
+
channel
|
|
960
|
+
);
|
|
961
|
+
const defaultValue = config.defaultPalette?.[section.key]?.[channel] ?? DEFAULT_RANGES[section.key]?.min ?? 0;
|
|
962
|
+
acc[channel] = toNumberOr(values?.[key], defaultValue);
|
|
963
|
+
return acc;
|
|
964
|
+
}, {});
|
|
965
|
+
});
|
|
966
|
+
return result;
|
|
967
|
+
}, [config.defaultPalette, config.hiddenKeyPrefix, config.sections, values]);
|
|
968
|
+
const paletteGradient = (0, import_react4.useMemo)(
|
|
969
|
+
() => computePaletteGradient(palette, config.gradientSteps),
|
|
970
|
+
[palette, config.gradientSteps]
|
|
971
|
+
);
|
|
972
|
+
const paletteSignature = (0, import_react4.useMemo)(
|
|
973
|
+
() => createPaletteSignature(palette),
|
|
974
|
+
[palette]
|
|
975
|
+
);
|
|
976
|
+
const lastSignatureRef = (0, import_react4.useRef)(null);
|
|
977
|
+
(0, import_react4.useEffect)(() => {
|
|
978
|
+
if (!config.onPaletteChange) return;
|
|
979
|
+
if (lastSignatureRef.current === paletteSignature) return;
|
|
980
|
+
lastSignatureRef.current = paletteSignature;
|
|
981
|
+
config.onPaletteChange(palette);
|
|
982
|
+
}, [config, palette, paletteSignature]);
|
|
983
|
+
const updatePaletteValue = (0, import_react4.useCallback)(
|
|
984
|
+
(sectionKey, channel, nextValue) => {
|
|
985
|
+
const range = config.ranges[sectionKey] ?? DEFAULT_RANGES[sectionKey] ?? {
|
|
986
|
+
min: 0,
|
|
987
|
+
max: 1,
|
|
988
|
+
step: 0.01
|
|
989
|
+
};
|
|
990
|
+
const clamped = Math.min(Math.max(nextValue, range.min), range.max);
|
|
991
|
+
config.onInteraction?.();
|
|
992
|
+
const controlKey = createPaletteControlKey(
|
|
993
|
+
config.hiddenKeyPrefix,
|
|
994
|
+
sectionKey,
|
|
995
|
+
channel
|
|
996
|
+
);
|
|
997
|
+
setValue(controlKey, clamped);
|
|
998
|
+
},
|
|
999
|
+
[config, setValue]
|
|
1000
|
+
);
|
|
1001
|
+
const handleResetPalette = (0, import_react4.useCallback)(() => {
|
|
1002
|
+
config.onInteraction?.();
|
|
1003
|
+
config.sections.forEach((section) => {
|
|
1004
|
+
CHANNEL_KEYS.forEach((channel) => {
|
|
1005
|
+
const controlKey = createPaletteControlKey(
|
|
1006
|
+
config.hiddenKeyPrefix,
|
|
1007
|
+
section.key,
|
|
1008
|
+
channel
|
|
1009
|
+
);
|
|
1010
|
+
const defaultValue = config.defaultPalette?.[section.key]?.[channel] ?? DEFAULT_RANGES[section.key]?.min ?? 0;
|
|
1011
|
+
setValue(controlKey, defaultValue);
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
}, [config, setValue]);
|
|
1015
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex w-full flex-col gap-6", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex w-full flex-col gap-4", children: [
|
|
1016
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1017
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs font-semibold uppercase tracking-wide text-stone-200", children: "Palette" }),
|
|
1018
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1019
|
+
"button",
|
|
1020
|
+
{
|
|
1021
|
+
type: "button",
|
|
1022
|
+
onClick: handleResetPalette,
|
|
1023
|
+
className: "rounded border border-stone-700 px-3 py-1 text-[10px] font-semibold uppercase tracking-widest text-stone-200 transition hover:border-stone-500",
|
|
1024
|
+
children: "Reset Palette"
|
|
1025
|
+
}
|
|
1026
|
+
)
|
|
1027
|
+
] }),
|
|
1028
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1029
|
+
"div",
|
|
1030
|
+
{
|
|
1031
|
+
className: "h-4 w-full rounded border border-stone-700",
|
|
1032
|
+
style: { background: paletteGradient }
|
|
1033
|
+
}
|
|
1034
|
+
),
|
|
1035
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-col gap-4", children: config.sections.map((section) => {
|
|
1036
|
+
const range = config.ranges[section.key];
|
|
1037
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3", children: [
|
|
1038
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between text-[11px] uppercase tracking-widest text-stone-300", children: [
|
|
1039
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: section.label }),
|
|
1040
|
+
section.helper && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-stone-500", children: section.helper })
|
|
1041
|
+
] }),
|
|
1042
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "grid grid-cols-3 gap-3", children: CHANNEL_KEYS.map((channel) => {
|
|
1043
|
+
const value = palette[section.key][channel];
|
|
1044
|
+
const channelLabel = config.channelLabels?.[channel] ?? DEFAULT_CHANNEL_LABELS[channel];
|
|
1045
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
|
|
1046
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between text-[10px] uppercase tracking-widest text-stone-400", children: [
|
|
1047
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: channelLabel }),
|
|
1048
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: value.toFixed(2) })
|
|
1049
|
+
] }),
|
|
1050
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1051
|
+
"input",
|
|
1052
|
+
{
|
|
1053
|
+
type: "range",
|
|
1054
|
+
min: range.min,
|
|
1055
|
+
max: range.max,
|
|
1056
|
+
step: range.step,
|
|
1057
|
+
value,
|
|
1058
|
+
onPointerDown: config.onInteraction,
|
|
1059
|
+
onChange: (event) => updatePaletteValue(
|
|
1060
|
+
section.key,
|
|
1061
|
+
channel,
|
|
1062
|
+
parseFloat(event.target.value)
|
|
1063
|
+
),
|
|
1064
|
+
className: "w-full cursor-pointer accent-stone-300"
|
|
1065
|
+
}
|
|
1066
|
+
),
|
|
1067
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1068
|
+
"input",
|
|
1069
|
+
{
|
|
1070
|
+
type: "number",
|
|
1071
|
+
min: range.min,
|
|
1072
|
+
max: range.max,
|
|
1073
|
+
step: range.step,
|
|
1074
|
+
value: value.toFixed(3),
|
|
1075
|
+
onPointerDown: config.onInteraction,
|
|
1076
|
+
onFocus: config.onInteraction,
|
|
1077
|
+
onChange: (event) => {
|
|
1078
|
+
const parsed = parseFloat(event.target.value);
|
|
1079
|
+
if (Number.isNaN(parsed)) return;
|
|
1080
|
+
updatePaletteValue(section.key, channel, parsed);
|
|
1081
|
+
},
|
|
1082
|
+
className: "w-full rounded border border-stone-700 bg-stone-900 px-2 py-1 text-xs text-stone-200 focus:border-stone-500 focus:outline-none"
|
|
1083
|
+
}
|
|
1084
|
+
)
|
|
1085
|
+
] }, channel);
|
|
1086
|
+
}) })
|
|
1087
|
+
] }, section.key);
|
|
1088
|
+
}) })
|
|
1089
|
+
] }) });
|
|
1090
|
+
};
|
|
1091
|
+
var AdvancedPaletteControl_default = AdvancedPaletteControl;
|
|
1092
|
+
|
|
1093
|
+
// src/components/ControlPanel/ControlPanel.tsx
|
|
1094
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
549
1095
|
var ControlPanel = () => {
|
|
550
|
-
const [copied, setCopied] = (0,
|
|
1096
|
+
const [copied, setCopied] = (0, import_react5.useState)(false);
|
|
1097
|
+
const [folderStates, setFolderStates] = (0, import_react5.useState)({});
|
|
551
1098
|
const { leftPanelWidth, isDesktop, isHydrated } = useResizableLayout();
|
|
552
1099
|
const { schema, setValue, values, componentName, config } = useControlsContext();
|
|
553
1100
|
const previewUrl = usePreviewUrl(values);
|
|
554
|
-
const
|
|
555
|
-
(
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
1101
|
+
const buildUrl = (0, import_react5.useCallback)(
|
|
1102
|
+
(modifier) => {
|
|
1103
|
+
if (!previewUrl) return "";
|
|
1104
|
+
const [path, search = ""] = previewUrl.split("?");
|
|
1105
|
+
const params = new URLSearchParams(search);
|
|
1106
|
+
modifier(params);
|
|
1107
|
+
const query = params.toString();
|
|
1108
|
+
return query ? `${path}?${query}` : path;
|
|
1109
|
+
},
|
|
1110
|
+
[previewUrl]
|
|
559
1111
|
);
|
|
560
|
-
const
|
|
1112
|
+
const presentationUrl = (0, import_react5.useMemo)(() => {
|
|
1113
|
+
if (!previewUrl) return "";
|
|
1114
|
+
return buildUrl((params) => {
|
|
1115
|
+
params.set(PRESENTATION_PARAM, "true");
|
|
1116
|
+
});
|
|
1117
|
+
}, [buildUrl, previewUrl]);
|
|
1118
|
+
const controlsOnlyUrl = (0, import_react5.useMemo)(() => {
|
|
1119
|
+
if (!previewUrl) return "";
|
|
1120
|
+
return buildUrl((params) => {
|
|
1121
|
+
params.delete(NO_CONTROLS_PARAM);
|
|
1122
|
+
params.delete(PRESENTATION_PARAM);
|
|
1123
|
+
params.set(CONTROLS_ONLY_PARAM, "true");
|
|
1124
|
+
});
|
|
1125
|
+
}, [buildUrl, previewUrl]);
|
|
1126
|
+
const handlePresentationClick = (0, import_react5.useCallback)(() => {
|
|
1127
|
+
if (typeof window === "undefined" || !presentationUrl) return;
|
|
1128
|
+
window.open(presentationUrl, "_blank", "noopener,noreferrer");
|
|
1129
|
+
if (controlsOnlyUrl) {
|
|
1130
|
+
const viewportWidth = window.innerWidth || 1200;
|
|
1131
|
+
const viewportHeight = window.innerHeight || 900;
|
|
1132
|
+
const controlsWidth = Math.max(
|
|
1133
|
+
320,
|
|
1134
|
+
Math.min(
|
|
1135
|
+
600,
|
|
1136
|
+
Math.round(viewportWidth * leftPanelWidth / 100)
|
|
1137
|
+
)
|
|
1138
|
+
);
|
|
1139
|
+
const controlsHeight = Math.max(600, viewportHeight);
|
|
1140
|
+
const controlsFeatures = [
|
|
1141
|
+
"noopener",
|
|
1142
|
+
"noreferrer",
|
|
1143
|
+
"toolbar=0",
|
|
1144
|
+
"menubar=0",
|
|
1145
|
+
"resizable=yes",
|
|
1146
|
+
"scrollbars=yes",
|
|
1147
|
+
`width=${controlsWidth}`,
|
|
1148
|
+
`height=${controlsHeight}`
|
|
1149
|
+
].join(",");
|
|
1150
|
+
window.open(controlsOnlyUrl, "v0-controls", controlsFeatures);
|
|
1151
|
+
}
|
|
1152
|
+
}, [controlsOnlyUrl, leftPanelWidth, presentationUrl]);
|
|
1153
|
+
const jsx14 = (0, import_react5.useMemo)(() => {
|
|
561
1154
|
if (!componentName) return "";
|
|
562
1155
|
const props = Object.entries(values).map(([key, val]) => {
|
|
563
1156
|
if (typeof val === "string") return `${key}="${val}"`;
|
|
@@ -566,182 +1159,367 @@ var ControlPanel = () => {
|
|
|
566
1159
|
}).join(" ");
|
|
567
1160
|
return `<${componentName} ${props} />`;
|
|
568
1161
|
}, [componentName, values]);
|
|
569
|
-
|
|
1162
|
+
const visibleEntries = Object.entries(schema).filter(
|
|
1163
|
+
([, control]) => !control.hidden
|
|
1164
|
+
);
|
|
1165
|
+
const rootControls = [];
|
|
1166
|
+
const folderOrder = [];
|
|
1167
|
+
const folderControls = /* @__PURE__ */ new Map();
|
|
1168
|
+
const folderExtras = /* @__PURE__ */ new Map();
|
|
1169
|
+
const folderPlacement = /* @__PURE__ */ new Map();
|
|
1170
|
+
const seenFolders = /* @__PURE__ */ new Set();
|
|
1171
|
+
const ensureFolder = (folder) => {
|
|
1172
|
+
if (!seenFolders.has(folder)) {
|
|
1173
|
+
seenFolders.add(folder);
|
|
1174
|
+
folderOrder.push(folder);
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
visibleEntries.forEach((entry) => {
|
|
1178
|
+
const [key, control] = entry;
|
|
1179
|
+
const folder = control.folder?.trim();
|
|
1180
|
+
if (folder) {
|
|
1181
|
+
const placement = control.folderPlacement ?? "bottom";
|
|
1182
|
+
ensureFolder(folder);
|
|
1183
|
+
if (!folderControls.has(folder)) {
|
|
1184
|
+
folderControls.set(folder, []);
|
|
1185
|
+
}
|
|
1186
|
+
folderControls.get(folder).push(entry);
|
|
1187
|
+
const existingPlacement = folderPlacement.get(folder);
|
|
1188
|
+
if (!existingPlacement || placement === "top") {
|
|
1189
|
+
folderPlacement.set(folder, placement);
|
|
1190
|
+
}
|
|
1191
|
+
} else {
|
|
1192
|
+
rootControls.push(entry);
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
const advancedConfig = config?.addAdvancedPaletteControl;
|
|
1196
|
+
let advancedPaletteControlNode = null;
|
|
1197
|
+
if (advancedConfig) {
|
|
1198
|
+
const advancedNode = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1199
|
+
AdvancedPaletteControl_default,
|
|
1200
|
+
{
|
|
1201
|
+
config: advancedConfig
|
|
1202
|
+
},
|
|
1203
|
+
"advancedPaletteControl"
|
|
1204
|
+
);
|
|
1205
|
+
const advancedFolder = advancedConfig.folder?.trim();
|
|
1206
|
+
if (advancedFolder) {
|
|
1207
|
+
const placement = advancedConfig.folderPlacement ?? "bottom";
|
|
1208
|
+
ensureFolder(advancedFolder);
|
|
1209
|
+
if (!folderControls.has(advancedFolder)) {
|
|
1210
|
+
folderControls.set(advancedFolder, []);
|
|
1211
|
+
}
|
|
1212
|
+
const existingPlacement = folderPlacement.get(advancedFolder);
|
|
1213
|
+
if (!existingPlacement || placement === "top") {
|
|
1214
|
+
folderPlacement.set(advancedFolder, placement);
|
|
1215
|
+
}
|
|
1216
|
+
if (!folderExtras.has(advancedFolder)) {
|
|
1217
|
+
folderExtras.set(advancedFolder, []);
|
|
1218
|
+
}
|
|
1219
|
+
folderExtras.get(advancedFolder).push(advancedNode);
|
|
1220
|
+
} else {
|
|
1221
|
+
advancedPaletteControlNode = advancedNode;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
const rootButtonControls = rootControls.filter(
|
|
1225
|
+
([, control]) => control.type === "button"
|
|
1226
|
+
);
|
|
1227
|
+
const rootNormalControls = rootControls.filter(
|
|
1228
|
+
([, control]) => control.type !== "button"
|
|
1229
|
+
);
|
|
1230
|
+
const folderGroups = folderOrder.map((folder) => ({
|
|
1231
|
+
folder,
|
|
1232
|
+
entries: folderControls.get(folder) ?? [],
|
|
1233
|
+
extras: folderExtras.get(folder) ?? [],
|
|
1234
|
+
placement: folderPlacement.get(folder) ?? "bottom"
|
|
1235
|
+
})).filter((group) => group.entries.length > 0 || group.extras.length > 0);
|
|
1236
|
+
const hasRootButtonControls = rootButtonControls.length > 0;
|
|
1237
|
+
const hasAnyFolders = folderGroups.length > 0;
|
|
1238
|
+
const jsonToComponentString = (0, import_react5.useCallback)(
|
|
1239
|
+
({
|
|
1240
|
+
componentName: componentNameOverride,
|
|
1241
|
+
props
|
|
1242
|
+
}) => {
|
|
1243
|
+
const resolvedComponentName = componentNameOverride ?? componentName;
|
|
1244
|
+
if (!resolvedComponentName) return "";
|
|
1245
|
+
const formatProp = (key, value) => {
|
|
1246
|
+
if (value === void 0) return null;
|
|
1247
|
+
if (value === null) return `${key}={null}`;
|
|
1248
|
+
if (typeof value === "string") return `${key}="${value}"`;
|
|
1249
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
1250
|
+
return `${key}={${value}}`;
|
|
1251
|
+
}
|
|
1252
|
+
if (typeof value === "bigint") {
|
|
1253
|
+
return `${key}={${value.toString()}n}`;
|
|
1254
|
+
}
|
|
1255
|
+
return `${key}={${JSON.stringify(value)}}`;
|
|
1256
|
+
};
|
|
1257
|
+
const formattedProps = Object.entries(props ?? {}).map(([key, value]) => formatProp(key, value)).filter((prop) => Boolean(prop)).join(" ");
|
|
1258
|
+
if (!formattedProps) {
|
|
1259
|
+
return `<${resolvedComponentName} />`;
|
|
1260
|
+
}
|
|
1261
|
+
return `<${resolvedComponentName} ${formattedProps} />`;
|
|
1262
|
+
},
|
|
1263
|
+
[componentName]
|
|
1264
|
+
);
|
|
1265
|
+
const copyText = config?.showCopyButtonFn?.({
|
|
1266
|
+
componentName,
|
|
1267
|
+
values,
|
|
1268
|
+
schema,
|
|
1269
|
+
jsx: jsx14,
|
|
1270
|
+
jsonToComponentString
|
|
1271
|
+
}) ?? jsx14;
|
|
1272
|
+
const shouldShowCopyButton = config?.showCopyButton !== false && Boolean(copyText);
|
|
1273
|
+
const labelize = (key) => key.replace(/([A-Z])/g, " $1").replace(/[\-_]/g, " ").replace(/\s+/g, " ").trim().replace(/(^|\s)\S/g, (s) => s.toUpperCase());
|
|
1274
|
+
const renderButtonControl = (key, control, variant) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
570
1275
|
"div",
|
|
571
1276
|
{
|
|
572
|
-
className:
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex items-center justify-between pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: key }) }),
|
|
649
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
650
|
-
"input",
|
|
651
|
-
{
|
|
652
|
-
type: "color",
|
|
653
|
-
id: key,
|
|
654
|
-
value,
|
|
655
|
-
onChange: (e) => setValue(key, e.target.value),
|
|
656
|
-
className: "w-full h-10 rounded border border-stone-600 bg-transparent"
|
|
657
|
-
}
|
|
658
|
-
)
|
|
659
|
-
] }, key);
|
|
660
|
-
case "select":
|
|
661
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
662
|
-
"div",
|
|
663
|
-
{
|
|
664
|
-
className: "space-y-2 border-t border-stone-700 pt-4",
|
|
665
|
-
children: [
|
|
666
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: key }),
|
|
667
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
668
|
-
Select,
|
|
669
|
-
{
|
|
670
|
-
value,
|
|
671
|
-
onValueChange: (val) => setValue(key, val),
|
|
672
|
-
children: [
|
|
673
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectValue, { placeholder: "Select option" }) }),
|
|
674
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectContent, { children: Object.entries(control.options).map(
|
|
675
|
-
([label, _val]) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectItem, { value: label, children: label }, label)
|
|
676
|
-
) })
|
|
677
|
-
]
|
|
678
|
-
}
|
|
679
|
-
)
|
|
680
|
-
]
|
|
681
|
-
},
|
|
682
|
-
key
|
|
683
|
-
);
|
|
684
|
-
default:
|
|
685
|
-
return null;
|
|
1277
|
+
className: variant === "root" ? "flex-1 [&_[data-slot=button]]:w-full" : "[&_[data-slot=button]]:w-full",
|
|
1278
|
+
children: control.render ? control.render() : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1279
|
+
"button",
|
|
1280
|
+
{
|
|
1281
|
+
onClick: control.onClick,
|
|
1282
|
+
className: "w-full px-4 py-2 text-sm bg-stone-800 hover:bg-stone-700 text-white rounded-md shadow",
|
|
1283
|
+
children: control.label ?? key
|
|
1284
|
+
}
|
|
1285
|
+
)
|
|
1286
|
+
},
|
|
1287
|
+
`control-panel-custom-${key}`
|
|
1288
|
+
);
|
|
1289
|
+
const renderControl = (key, control, variant) => {
|
|
1290
|
+
if (control.type === "button") {
|
|
1291
|
+
return renderButtonControl(key, control, variant);
|
|
1292
|
+
}
|
|
1293
|
+
const value = values[key];
|
|
1294
|
+
switch (control.type) {
|
|
1295
|
+
case "boolean":
|
|
1296
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1297
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { htmlFor: key, className: "cursor-pointer", children: labelize(key) }),
|
|
1298
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1299
|
+
Switch,
|
|
1300
|
+
{
|
|
1301
|
+
id: key,
|
|
1302
|
+
checked: value,
|
|
1303
|
+
onCheckedChange: (v) => setValue(key, v),
|
|
1304
|
+
className: "cursor-pointer scale-90"
|
|
1305
|
+
}
|
|
1306
|
+
)
|
|
1307
|
+
] }, key);
|
|
1308
|
+
case "number":
|
|
1309
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-3 w-full", children: [
|
|
1310
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1311
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: labelize(key) }),
|
|
1312
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1313
|
+
Input,
|
|
1314
|
+
{
|
|
1315
|
+
type: "number",
|
|
1316
|
+
value,
|
|
1317
|
+
min: control.min ?? 0,
|
|
1318
|
+
max: control.max ?? 100,
|
|
1319
|
+
step: control.step ?? 1,
|
|
1320
|
+
onChange: (e) => {
|
|
1321
|
+
const v = parseFloat(e.target.value);
|
|
1322
|
+
if (Number.isNaN(v)) return;
|
|
1323
|
+
setValue(key, v);
|
|
1324
|
+
},
|
|
1325
|
+
className: "w-20 text-center cursor-text"
|
|
1326
|
+
}
|
|
1327
|
+
)
|
|
1328
|
+
] }),
|
|
1329
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1330
|
+
Slider,
|
|
1331
|
+
{
|
|
1332
|
+
id: key,
|
|
1333
|
+
min: control.min ?? 0,
|
|
1334
|
+
max: control.max ?? 100,
|
|
1335
|
+
step: control.step ?? 1,
|
|
1336
|
+
value: [value],
|
|
1337
|
+
onValueChange: ([v]) => setValue(key, v),
|
|
1338
|
+
className: "w-full cursor-pointer"
|
|
1339
|
+
}
|
|
1340
|
+
)
|
|
1341
|
+
] }, key);
|
|
1342
|
+
case "string":
|
|
1343
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-2 w-full", children: [
|
|
1344
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: labelize(key) }),
|
|
1345
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1346
|
+
Input,
|
|
1347
|
+
{
|
|
1348
|
+
id: key,
|
|
1349
|
+
value,
|
|
1350
|
+
placeholder: key,
|
|
1351
|
+
onChange: (e) => setValue(key, e.target.value),
|
|
1352
|
+
className: "bg-stone-900"
|
|
686
1353
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
1354
|
+
)
|
|
1355
|
+
] }, key);
|
|
1356
|
+
case "color":
|
|
1357
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-2 w-full", children: [
|
|
1358
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: labelize(key) }),
|
|
1359
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1360
|
+
"input",
|
|
1361
|
+
{
|
|
1362
|
+
type: "color",
|
|
1363
|
+
id: key,
|
|
1364
|
+
value,
|
|
1365
|
+
onChange: (e) => setValue(key, e.target.value),
|
|
1366
|
+
className: "w-full h-10 rounded border border-stone-600 bg-transparent"
|
|
1367
|
+
}
|
|
1368
|
+
)
|
|
1369
|
+
] }, key);
|
|
1370
|
+
case "select":
|
|
1371
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "space-y-2", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
1372
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "min-w-fit", htmlFor: key, children: labelize(key) }),
|
|
1373
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Select, { value, onValueChange: (val) => setValue(key, val), children: [
|
|
1374
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectTrigger, { className: "flex-1 cursor-pointer", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectValue, { placeholder: "Select option" }) }),
|
|
1375
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectContent, { className: "cursor-pointer z-[9999]", children: Object.entries(control.options).map(([label]) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1376
|
+
SelectItem,
|
|
1377
|
+
{
|
|
1378
|
+
value: label,
|
|
1379
|
+
className: "cursor-pointer",
|
|
1380
|
+
children: label
|
|
1381
|
+
},
|
|
1382
|
+
label
|
|
1383
|
+
)) })
|
|
1384
|
+
] })
|
|
1385
|
+
] }) }, key);
|
|
1386
|
+
default:
|
|
1387
|
+
return null;
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
const renderFolder = (folder, entries, extras = []) => {
|
|
1391
|
+
const isOpen = folderStates[folder] ?? true;
|
|
1392
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1393
|
+
"div",
|
|
1394
|
+
{
|
|
1395
|
+
className: "border border-stone-700/60 rounded-lg bg-stone-900/70",
|
|
1396
|
+
children: [
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1398
|
+
"button",
|
|
690
1399
|
{
|
|
691
|
-
|
|
1400
|
+
type: "button",
|
|
1401
|
+
onClick: () => setFolderStates((prev) => ({
|
|
1402
|
+
...prev,
|
|
1403
|
+
[folder]: !isOpen
|
|
1404
|
+
})),
|
|
1405
|
+
className: "w-full flex items-center justify-between px-4 py-3 text-left font-semibold text-stone-200 tracking-wide",
|
|
692
1406
|
children: [
|
|
693
|
-
|
|
694
|
-
|
|
1407
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: folder }),
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1409
|
+
import_lucide_react3.ChevronDown,
|
|
695
1410
|
{
|
|
696
|
-
|
|
697
|
-
navigator.clipboard.writeText(jsx16);
|
|
698
|
-
setCopied(true);
|
|
699
|
-
setTimeout(() => setCopied(false), 5e3);
|
|
700
|
-
},
|
|
701
|
-
className: "w-full px-4 py-2 text-sm bg-stone-700 hover:bg-stone-600 text-white rounded flex items-center justify-center gap-2",
|
|
702
|
-
children: copied ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
703
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.Check, { className: "w-4 h-4" }),
|
|
704
|
-
"Copied"
|
|
705
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
706
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.Copy, { className: "w-4 h-4" }),
|
|
707
|
-
"Copy to Clipboard"
|
|
708
|
-
] })
|
|
1411
|
+
className: `w-4 h-4 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
|
|
709
1412
|
}
|
|
710
|
-
)
|
|
711
|
-
buttonControls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-wrap gap-2 pt-4", children: buttonControls.map(
|
|
712
|
-
([key, control]) => control.type === "button" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
713
|
-
"div",
|
|
714
|
-
{
|
|
715
|
-
className: "flex-1",
|
|
716
|
-
children: control.render ? control.render() : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
717
|
-
"button",
|
|
718
|
-
{
|
|
719
|
-
onClick: control.onClick,
|
|
720
|
-
className: "w-full px-4 py-2 text-sm bg-stone-700 hover:bg-stone-600 text-white rounded",
|
|
721
|
-
children: control.label ?? key
|
|
722
|
-
}
|
|
723
|
-
)
|
|
724
|
-
},
|
|
725
|
-
`control-panel-custom-${key}`
|
|
726
|
-
) : null
|
|
727
|
-
) })
|
|
1413
|
+
)
|
|
728
1414
|
]
|
|
729
1415
|
}
|
|
730
|
-
)
|
|
1416
|
+
),
|
|
1417
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "px-4 pb-4 pt-0 space-y-5", children: [
|
|
1418
|
+
entries.map(
|
|
1419
|
+
([key, control]) => renderControl(key, control, "folder")
|
|
1420
|
+
),
|
|
1421
|
+
extras.map((extra) => extra)
|
|
1422
|
+
] })
|
|
1423
|
+
]
|
|
1424
|
+
},
|
|
1425
|
+
folder
|
|
1426
|
+
);
|
|
1427
|
+
};
|
|
1428
|
+
const topFolderSections = hasAnyFolders ? folderGroups.filter(({ placement }) => placement === "top").map(
|
|
1429
|
+
({ folder, entries, extras }) => renderFolder(folder, entries, extras)
|
|
1430
|
+
) : null;
|
|
1431
|
+
const bottomFolderSections = hasAnyFolders ? folderGroups.filter(({ placement }) => placement === "bottom").map(
|
|
1432
|
+
({ folder, entries, extras }) => renderFolder(folder, entries, extras)
|
|
1433
|
+
) : null;
|
|
1434
|
+
const panelStyle = {
|
|
1435
|
+
width: "100%",
|
|
1436
|
+
height: "auto",
|
|
1437
|
+
flex: "0 0 auto"
|
|
1438
|
+
};
|
|
1439
|
+
if (isHydrated) {
|
|
1440
|
+
if (isDesktop) {
|
|
1441
|
+
Object.assign(panelStyle, {
|
|
1442
|
+
position: "absolute",
|
|
1443
|
+
left: 0,
|
|
1444
|
+
top: 0,
|
|
1445
|
+
bottom: 0,
|
|
1446
|
+
width: `${leftPanelWidth}%`,
|
|
1447
|
+
overflowY: "auto"
|
|
1448
|
+
});
|
|
1449
|
+
} else {
|
|
1450
|
+
Object.assign(panelStyle, {
|
|
1451
|
+
marginTop: `calc(-1 * (${MOBILE_CONTROL_PANEL_PEEK}px + env(safe-area-inset-bottom, 0px)))`,
|
|
1452
|
+
paddingBottom: `calc(${MOBILE_CONTROL_PANEL_PEEK}px + env(safe-area-inset-bottom, 0px))`
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1457
|
+
"div",
|
|
1458
|
+
{
|
|
1459
|
+
className: `order-2 md:order-1 w-full md:h-auto p-2 md:p-4 bg-stone-900 font-mono text-stone-300 transition-opacity duration-300 z-max ${!isHydrated ? "opacity-0" : "opacity-100"}`,
|
|
1460
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
1461
|
+
onTouchStart: (e) => e.stopPropagation(),
|
|
1462
|
+
style: panelStyle,
|
|
1463
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "dark mb-10 space-y-6 p-4 md:p-6 bg-stone-900/95 backdrop-blur-md border-2 border-stone-700 rounded-xl shadow-lg", children: [
|
|
1464
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "space-y-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-lg text-stone-100 font-semibold", children: config?.mainLabel ?? "Controls" }) }),
|
|
1465
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-6", children: [
|
|
1466
|
+
topFolderSections,
|
|
1467
|
+
hasRootButtonControls && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-wrap gap-2", children: rootButtonControls.map(
|
|
1468
|
+
([key, control]) => renderButtonControl(key, control, "root")
|
|
1469
|
+
) }),
|
|
1470
|
+
advancedPaletteControlNode,
|
|
1471
|
+
rootNormalControls.map(
|
|
1472
|
+
([key, control]) => renderControl(key, control, "root")
|
|
1473
|
+
),
|
|
1474
|
+
bottomFolderSections,
|
|
1475
|
+
shouldShowCopyButton && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1476
|
+
"button",
|
|
1477
|
+
{
|
|
1478
|
+
onClick: () => {
|
|
1479
|
+
if (!copyText) return;
|
|
1480
|
+
navigator.clipboard.writeText(copyText);
|
|
1481
|
+
setCopied(true);
|
|
1482
|
+
setTimeout(() => setCopied(false), 5e3);
|
|
1483
|
+
},
|
|
1484
|
+
className: "w-full px-4 py-2 text-sm bg-stone-800 hover:bg-stone-700 text-white rounded-md flex items-center justify-center gap-2 shadow",
|
|
1485
|
+
children: copied ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
1486
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.Check, { className: "w-4 h-4" }),
|
|
1487
|
+
"Copied"
|
|
1488
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
1489
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.Copy, { className: "w-4 h-4" }),
|
|
1490
|
+
"Copy to Clipboard"
|
|
1491
|
+
] })
|
|
1492
|
+
}
|
|
1493
|
+
) }, "control-panel-jsx")
|
|
731
1494
|
] }),
|
|
732
|
-
previewUrl && /* @__PURE__ */ (0,
|
|
733
|
-
"
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1495
|
+
previewUrl && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1496
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { asChild: true, className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1497
|
+
"a",
|
|
1498
|
+
{
|
|
1499
|
+
href: previewUrl,
|
|
1500
|
+
target: "_blank",
|
|
1501
|
+
rel: "noopener noreferrer",
|
|
1502
|
+
className: "w-full px-4 py-2 text-sm text-center bg-stone-900 hover:bg-stone-800 text-white rounded-md border border-stone-700",
|
|
1503
|
+
children: [
|
|
1504
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.SquareArrowOutUpRight, {}),
|
|
1505
|
+
" Open in a New Tab"
|
|
1506
|
+
]
|
|
1507
|
+
}
|
|
1508
|
+
) }),
|
|
1509
|
+
config?.showPresentationButton && presentationUrl && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1510
|
+
Button,
|
|
1511
|
+
{
|
|
1512
|
+
type: "button",
|
|
1513
|
+
onClick: handlePresentationClick,
|
|
1514
|
+
variant: "secondary",
|
|
1515
|
+
className: "w-full bg-stone-800 text-white hover:bg-stone-700 border border-stone-700",
|
|
1516
|
+
children: [
|
|
1517
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.Presentation, {}),
|
|
1518
|
+
" Presentation Mode"
|
|
1519
|
+
]
|
|
1520
|
+
}
|
|
1521
|
+
)
|
|
1522
|
+
] })
|
|
745
1523
|
] })
|
|
746
1524
|
}
|
|
747
1525
|
);
|
|
@@ -749,12 +1527,12 @@ var ControlPanel = () => {
|
|
|
749
1527
|
var ControlPanel_default = ControlPanel;
|
|
750
1528
|
|
|
751
1529
|
// src/components/PreviewContainer/PreviewContainer.tsx
|
|
752
|
-
var
|
|
1530
|
+
var import_react6 = require("react");
|
|
753
1531
|
|
|
754
1532
|
// src/components/Grid/Grid.tsx
|
|
755
|
-
var
|
|
1533
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
756
1534
|
function Grid() {
|
|
757
|
-
return /* @__PURE__ */ (0,
|
|
1535
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
758
1536
|
"div",
|
|
759
1537
|
{
|
|
760
1538
|
className: "absolute inset-0 w-screen h-screen z-[0] blur-[1px]",
|
|
@@ -772,12 +1550,12 @@ function Grid() {
|
|
|
772
1550
|
var Grid_default = Grid;
|
|
773
1551
|
|
|
774
1552
|
// src/components/PreviewContainer/PreviewContainer.tsx
|
|
775
|
-
var
|
|
1553
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
776
1554
|
var PreviewContainer = ({ children, hideControls }) => {
|
|
777
1555
|
const { config } = useControlsContext();
|
|
778
1556
|
const { leftPanelWidth, isDesktop, isHydrated, containerRef } = useResizableLayout();
|
|
779
|
-
const previewRef = (0,
|
|
780
|
-
return /* @__PURE__ */ (0,
|
|
1557
|
+
const previewRef = (0, import_react6.useRef)(null);
|
|
1558
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
781
1559
|
"div",
|
|
782
1560
|
{
|
|
783
1561
|
ref: previewRef,
|
|
@@ -786,9 +1564,9 @@ var PreviewContainer = ({ children, hideControls }) => {
|
|
|
786
1564
|
width: `${100 - leftPanelWidth}%`,
|
|
787
1565
|
marginLeft: `${leftPanelWidth}%`
|
|
788
1566
|
} : {},
|
|
789
|
-
children: /* @__PURE__ */ (0,
|
|
790
|
-
config?.showGrid && /* @__PURE__ */ (0,
|
|
791
|
-
/* @__PURE__ */ (0,
|
|
1567
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "w-screen h-screen", children: [
|
|
1568
|
+
config?.showGrid && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Grid_default, {}),
|
|
1569
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "w-screen h-screen flex items-center justify-center relative", children })
|
|
792
1570
|
] })
|
|
793
1571
|
}
|
|
794
1572
|
);
|
|
@@ -796,167 +1574,179 @@ var PreviewContainer = ({ children, hideControls }) => {
|
|
|
796
1574
|
var PreviewContainer_default = PreviewContainer;
|
|
797
1575
|
|
|
798
1576
|
// src/components/Playground/Playground.tsx
|
|
799
|
-
var
|
|
800
|
-
var
|
|
1577
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1578
|
+
var HiddenPreview = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { "aria-hidden": "true", className: "hidden", children });
|
|
801
1579
|
function Playground({ children }) {
|
|
802
|
-
const [isHydrated, setIsHydrated] = (0,
|
|
803
|
-
const [copied, setCopied] = (0,
|
|
804
|
-
(0,
|
|
1580
|
+
const [isHydrated, setIsHydrated] = (0, import_react7.useState)(false);
|
|
1581
|
+
const [copied, setCopied] = (0, import_react7.useState)(false);
|
|
1582
|
+
(0, import_react7.useEffect)(() => {
|
|
805
1583
|
setIsHydrated(true);
|
|
806
1584
|
}, []);
|
|
807
|
-
const
|
|
808
|
-
if (typeof window === "undefined")
|
|
809
|
-
|
|
1585
|
+
const { showControls, isPresentationMode, isControlsOnly } = (0, import_react7.useMemo)(() => {
|
|
1586
|
+
if (typeof window === "undefined") {
|
|
1587
|
+
return {
|
|
1588
|
+
showControls: true,
|
|
1589
|
+
isPresentationMode: false,
|
|
1590
|
+
isControlsOnly: false
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
const params = new URLSearchParams(window.location.search);
|
|
1594
|
+
const presentation = params.get(PRESENTATION_PARAM) === "true";
|
|
1595
|
+
const controlsOnly = params.get(CONTROLS_ONLY_PARAM) === "true";
|
|
1596
|
+
const noControlsParam = params.get(NO_CONTROLS_PARAM) === "true";
|
|
1597
|
+
const showControlsValue = controlsOnly || !presentation && !noControlsParam;
|
|
1598
|
+
return {
|
|
1599
|
+
showControls: showControlsValue,
|
|
1600
|
+
isPresentationMode: presentation,
|
|
1601
|
+
isControlsOnly: controlsOnly
|
|
1602
|
+
};
|
|
810
1603
|
}, []);
|
|
1604
|
+
const shouldShowShareButton = !showControls && !isPresentationMode;
|
|
1605
|
+
const layoutHideControls = !showControls || isControlsOnly;
|
|
811
1606
|
const handleCopy = () => {
|
|
812
1607
|
navigator.clipboard.writeText(window.location.href);
|
|
813
1608
|
setCopied(true);
|
|
814
1609
|
setTimeout(() => setCopied(false), 2e3);
|
|
815
1610
|
};
|
|
816
1611
|
if (!isHydrated) return null;
|
|
817
|
-
return /* @__PURE__ */ (0,
|
|
818
|
-
|
|
1612
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ResizableLayout, { hideControls: layoutHideControls, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ControlsProvider, { children: [
|
|
1613
|
+
shouldShowShareButton && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
819
1614
|
"button",
|
|
820
1615
|
{
|
|
821
1616
|
onClick: handleCopy,
|
|
822
1617
|
className: "absolute top-4 right-4 z-50 flex items-center gap-1 rounded bg-black/70 px-3 py-1 text-white hover:bg-black",
|
|
823
1618
|
children: [
|
|
824
|
-
copied ? /* @__PURE__ */ (0,
|
|
1619
|
+
copied ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Check, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Copy, { size: 16 }),
|
|
825
1620
|
copied ? "Copied!" : "Share"
|
|
826
1621
|
]
|
|
827
1622
|
}
|
|
828
1623
|
),
|
|
829
|
-
/* @__PURE__ */ (0,
|
|
830
|
-
|
|
1624
|
+
isControlsOnly ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HiddenPreview, { children }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PreviewContainer_default, { hideControls: layoutHideControls, children }),
|
|
1625
|
+
showControls && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ControlPanel_default, {})
|
|
831
1626
|
] }) });
|
|
832
1627
|
}
|
|
833
1628
|
|
|
834
|
-
// src/
|
|
835
|
-
var import_react8 =
|
|
836
|
-
var
|
|
837
|
-
var
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
(0,
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
}, 200);
|
|
853
|
-
}, [camera]);
|
|
854
|
-
(0, import_react7.useEffect)(() => {
|
|
855
|
-
const controls = controlsRef.current;
|
|
856
|
-
const handler = logRef.current;
|
|
857
|
-
if (!controls || !handler) return;
|
|
858
|
-
controls.addEventListener("change", handler);
|
|
859
|
-
return () => controls.removeEventListener("change", handler);
|
|
860
|
-
}, []);
|
|
861
|
-
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_drei.OrbitControls, { ref: controlsRef });
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// src/components/Canvas/Canvas.tsx
|
|
865
|
-
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
866
|
-
var ResponsiveCamera = ({
|
|
867
|
-
height,
|
|
868
|
-
width
|
|
869
|
-
}) => {
|
|
870
|
-
const { camera } = (0, import_fiber2.useThree)();
|
|
871
|
-
(0, import_react8.useEffect)(() => {
|
|
872
|
-
const isMobile = width < 768;
|
|
873
|
-
const zoomFactor = isMobile ? 70 : 100;
|
|
874
|
-
camera.position.z = height / zoomFactor;
|
|
875
|
-
camera.updateProjectionMatrix();
|
|
876
|
-
}, [height, camera, width]);
|
|
877
|
-
return null;
|
|
878
|
-
};
|
|
879
|
-
var Canvas = ({
|
|
880
|
-
mediaProps,
|
|
881
|
-
children,
|
|
882
|
-
...otherProps
|
|
883
|
-
}) => {
|
|
884
|
-
const canvasRef = (0, import_react8.useRef)(null);
|
|
885
|
-
const [parentSize, setParentSize] = (0, import_react8.useState)(null);
|
|
1629
|
+
// src/hooks/useAdvancedPaletteControls.ts
|
|
1630
|
+
var import_react8 = require("react");
|
|
1631
|
+
var cloneForCallbacks = (palette) => clonePalette(palette);
|
|
1632
|
+
var useAdvancedPaletteControls = (options = {}) => {
|
|
1633
|
+
const resolvedDefaultPalette = (0, import_react8.useMemo)(
|
|
1634
|
+
() => createAdvancedPalette(options.defaultPalette),
|
|
1635
|
+
[options.defaultPalette]
|
|
1636
|
+
);
|
|
1637
|
+
const resolvedFallbackPalette = (0, import_react8.useMemo)(
|
|
1638
|
+
() => options.fallbackPalette ? createAdvancedPalette(options.fallbackPalette) : resolvedDefaultPalette,
|
|
1639
|
+
[options.fallbackPalette, resolvedDefaultPalette]
|
|
1640
|
+
);
|
|
1641
|
+
const [palette, setPaletteState] = (0, import_react8.useState)(
|
|
1642
|
+
() => clonePalette(resolvedDefaultPalette)
|
|
1643
|
+
);
|
|
1644
|
+
const defaultSignatureRef = (0, import_react8.useRef)(
|
|
1645
|
+
createPaletteSignature(resolvedDefaultPalette)
|
|
1646
|
+
);
|
|
886
1647
|
(0, import_react8.useEffect)(() => {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1648
|
+
const nextSignature = createPaletteSignature(resolvedDefaultPalette);
|
|
1649
|
+
if (defaultSignatureRef.current === nextSignature) return;
|
|
1650
|
+
defaultSignatureRef.current = nextSignature;
|
|
1651
|
+
setPaletteState(clonePalette(resolvedDefaultPalette));
|
|
1652
|
+
}, [resolvedDefaultPalette]);
|
|
1653
|
+
const notifyChange = (0, import_react8.useCallback)(
|
|
1654
|
+
(nextPalette) => {
|
|
1655
|
+
options.onChange?.(cloneForCallbacks(nextPalette));
|
|
1656
|
+
},
|
|
1657
|
+
[options.onChange]
|
|
1658
|
+
);
|
|
1659
|
+
const setPalette = (0, import_react8.useCallback)(
|
|
1660
|
+
(source) => {
|
|
1661
|
+
const nextPalette = createAdvancedPalette(
|
|
1662
|
+
source ?? resolvedDefaultPalette
|
|
1663
|
+
);
|
|
1664
|
+
setPaletteState(clonePalette(nextPalette));
|
|
1665
|
+
notifyChange(nextPalette);
|
|
1666
|
+
},
|
|
1667
|
+
[notifyChange, resolvedDefaultPalette]
|
|
1668
|
+
);
|
|
1669
|
+
const updatePalette = (0, import_react8.useCallback)(
|
|
1670
|
+
(updater) => {
|
|
1671
|
+
setPaletteState((current) => {
|
|
1672
|
+
const nextSource = updater(clonePalette(current));
|
|
1673
|
+
const nextPalette = createAdvancedPalette(
|
|
1674
|
+
nextSource ?? current ?? resolvedDefaultPalette
|
|
1675
|
+
);
|
|
1676
|
+
notifyChange(nextPalette);
|
|
1677
|
+
return clonePalette(nextPalette);
|
|
898
1678
|
});
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
tryObserve();
|
|
902
|
-
return () => {
|
|
903
|
-
if (observer) observer.disconnect();
|
|
904
|
-
};
|
|
905
|
-
}, []);
|
|
906
|
-
const mergedMediaProps = {
|
|
907
|
-
...mediaProps || {},
|
|
908
|
-
size: mediaProps?.size || { width: 400, height: 400 }
|
|
909
|
-
};
|
|
910
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
911
|
-
"div",
|
|
912
|
-
{
|
|
913
|
-
ref: canvasRef,
|
|
914
|
-
className: "w-full h-full pointer-events-none relative touch-none",
|
|
915
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
916
|
-
import_fiber2.Canvas,
|
|
917
|
-
{
|
|
918
|
-
resize: { polyfill: ResizeObserver },
|
|
919
|
-
style: { width: parentSize?.width, height: parentSize?.height },
|
|
920
|
-
gl: { preserveDrawingBuffer: true },
|
|
921
|
-
...otherProps,
|
|
922
|
-
children: [
|
|
923
|
-
parentSize?.height && parentSize?.width && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
924
|
-
ResponsiveCamera,
|
|
925
|
-
{
|
|
926
|
-
height: parentSize.height,
|
|
927
|
-
width: parentSize.width
|
|
928
|
-
}
|
|
929
|
-
),
|
|
930
|
-
mediaProps?.debugOrbit && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CameraLogger, {}),
|
|
931
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ambientLight", { intensity: 1 }),
|
|
932
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("pointLight", { position: [10, 10, 10] }),
|
|
933
|
-
import_react8.default.cloneElement(children, mergedMediaProps)
|
|
934
|
-
]
|
|
935
|
-
}
|
|
936
|
-
)
|
|
937
|
-
}
|
|
1679
|
+
},
|
|
1680
|
+
[notifyChange, resolvedDefaultPalette]
|
|
938
1681
|
);
|
|
1682
|
+
const resetPalette = (0, import_react8.useCallback)(() => {
|
|
1683
|
+
setPaletteState(clonePalette(resolvedDefaultPalette));
|
|
1684
|
+
notifyChange(resolvedDefaultPalette);
|
|
1685
|
+
}, [notifyChange, resolvedDefaultPalette]);
|
|
1686
|
+
const handleControlPaletteChange = (0, import_react8.useCallback)(
|
|
1687
|
+
(nextPalette) => {
|
|
1688
|
+
setPaletteState(clonePalette(nextPalette));
|
|
1689
|
+
notifyChange(nextPalette);
|
|
1690
|
+
},
|
|
1691
|
+
[notifyChange]
|
|
1692
|
+
);
|
|
1693
|
+
const controlConfig = (0, import_react8.useMemo)(
|
|
1694
|
+
() => ({
|
|
1695
|
+
...options.control ?? {},
|
|
1696
|
+
defaultPalette: resolvedDefaultPalette,
|
|
1697
|
+
onPaletteChange: handleControlPaletteChange
|
|
1698
|
+
}),
|
|
1699
|
+
[handleControlPaletteChange, options.control, resolvedDefaultPalette]
|
|
1700
|
+
);
|
|
1701
|
+
const hexColors = (0, import_react8.useMemo)(
|
|
1702
|
+
() => advancedPaletteToHexColors(palette, {
|
|
1703
|
+
sectionOrder: options.sectionOrder,
|
|
1704
|
+
fallbackPalette: resolvedFallbackPalette,
|
|
1705
|
+
defaultColor: options.defaultColor
|
|
1706
|
+
}),
|
|
1707
|
+
[
|
|
1708
|
+
options.defaultColor,
|
|
1709
|
+
options.sectionOrder,
|
|
1710
|
+
palette,
|
|
1711
|
+
resolvedFallbackPalette
|
|
1712
|
+
]
|
|
1713
|
+
);
|
|
1714
|
+
const paletteSignature = (0, import_react8.useMemo)(
|
|
1715
|
+
() => createPaletteSignature(palette),
|
|
1716
|
+
[palette]
|
|
1717
|
+
);
|
|
1718
|
+
const paletteGradient = (0, import_react8.useMemo)(
|
|
1719
|
+
() => computePaletteGradient(palette, options.gradientSteps),
|
|
1720
|
+
[options.gradientSteps, palette]
|
|
1721
|
+
);
|
|
1722
|
+
return {
|
|
1723
|
+
palette,
|
|
1724
|
+
hexColors,
|
|
1725
|
+
controlConfig,
|
|
1726
|
+
paletteGradient,
|
|
1727
|
+
setPalette,
|
|
1728
|
+
updatePalette,
|
|
1729
|
+
resetPalette,
|
|
1730
|
+
paletteSignature
|
|
1731
|
+
};
|
|
939
1732
|
};
|
|
940
|
-
var
|
|
941
|
-
|
|
942
|
-
// src/components/PlaygroundCanvas/PlaygroundCanvas.tsx
|
|
943
|
-
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
944
|
-
var PlaygroundCanvas = ({
|
|
945
|
-
children,
|
|
946
|
-
mediaProps,
|
|
947
|
-
...otherProps
|
|
948
|
-
}) => {
|
|
949
|
-
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Playground, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Canvas_default, { mediaProps, ...otherProps, children }) });
|
|
950
|
-
};
|
|
951
|
-
var PlaygroundCanvas_default = PlaygroundCanvas;
|
|
1733
|
+
var useDefaultAdvancedPaletteControls = () => useAdvancedPaletteControls({ defaultPalette: DEFAULT_ADVANCED_PALETTE });
|
|
952
1734
|
// Annotate the CommonJS export names for ESM import in node:
|
|
953
1735
|
0 && (module.exports = {
|
|
954
1736
|
Button,
|
|
955
|
-
CameraLogger,
|
|
956
|
-
Canvas,
|
|
957
1737
|
ControlsProvider,
|
|
1738
|
+
DEFAULT_ADVANCED_PALETTE,
|
|
1739
|
+
DEFAULT_HEX_PALETTE,
|
|
958
1740
|
Playground,
|
|
959
|
-
|
|
1741
|
+
advancedPaletteToHexColors,
|
|
1742
|
+
clonePalette,
|
|
1743
|
+
computePaletteGradient,
|
|
1744
|
+
createAdvancedPalette,
|
|
1745
|
+
createPaletteSignature,
|
|
1746
|
+
hexToPaletteValue,
|
|
1747
|
+
paletteValueToHex,
|
|
1748
|
+
useAdvancedPaletteControls,
|
|
960
1749
|
useControls,
|
|
1750
|
+
useDefaultAdvancedPaletteControls,
|
|
961
1751
|
useUrlSyncedControls
|
|
962
1752
|
});
|