@toriistudio/v0-playground 0.5.2 → 0.5.5
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 +0 -19
- package/dist/index.d.mts +102 -30
- package/dist/index.d.ts +102 -30
- package/dist/index.js +941 -323
- package/dist/index.mjs +935 -319
- package/dist/preset.d.mts +5 -0
- package/dist/preset.d.ts +5 -0
- package/package.json +25 -16
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,252 @@ var getUrlParams = () => {
|
|
|
160
168
|
return entries;
|
|
161
169
|
};
|
|
162
170
|
|
|
171
|
+
// src/lib/advancedPalette.ts
|
|
172
|
+
var CHANNEL_KEYS = ["r", "g", "b"];
|
|
173
|
+
var DEFAULT_CHANNEL_LABELS = {
|
|
174
|
+
r: "Red",
|
|
175
|
+
g: "Green",
|
|
176
|
+
b: "Blue"
|
|
177
|
+
};
|
|
178
|
+
var DEFAULT_SECTIONS = [
|
|
179
|
+
{ key: "A", label: "Vector A", helper: "Base offset" },
|
|
180
|
+
{ key: "B", label: "Vector B", helper: "Amplitude" },
|
|
181
|
+
{ key: "C", label: "Vector C", helper: "Frequency" },
|
|
182
|
+
{ key: "D", label: "Vector D", helper: "Phase shift" }
|
|
183
|
+
];
|
|
184
|
+
var DEFAULT_RANGES = {
|
|
185
|
+
A: { min: 0, max: 1, step: 0.01 },
|
|
186
|
+
B: { min: -1, max: 1, step: 0.01 },
|
|
187
|
+
C: { min: 0, max: 2, step: 0.01 },
|
|
188
|
+
D: { min: 0, max: 1, step: 0.01 }
|
|
189
|
+
};
|
|
190
|
+
var DEFAULT_HIDDEN_KEY_PREFIX = "palette";
|
|
191
|
+
var DEFAULT_GRADIENT_STEPS = 12;
|
|
192
|
+
var DEFAULT_HEX_PALETTE = {
|
|
193
|
+
A: { r: 0.5, g: 0.5, b: 0.5 },
|
|
194
|
+
B: { r: 0.5, g: 0.5, b: 0.5 },
|
|
195
|
+
C: { r: 1, g: 1, b: 1 },
|
|
196
|
+
D: { r: 0, g: 0.1, b: 0.2 }
|
|
197
|
+
};
|
|
198
|
+
var createPaletteControlKey = (prefix, section, channel) => `${prefix}${section}${channel}`;
|
|
199
|
+
var clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
200
|
+
var clamp01 = (value) => clamp(value, 0, 1);
|
|
201
|
+
var toNumberOr = (value, fallback) => {
|
|
202
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
203
|
+
if (typeof value === "string") {
|
|
204
|
+
const parsed = parseFloat(value);
|
|
205
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
206
|
+
}
|
|
207
|
+
return fallback;
|
|
208
|
+
};
|
|
209
|
+
var paletteColorAt = (palette, t) => {
|
|
210
|
+
const twoPi = Math.PI * 2;
|
|
211
|
+
const computeChannel = (a, b, c, d) => {
|
|
212
|
+
const value = a + b * Math.cos(twoPi * (c * t + d));
|
|
213
|
+
return clamp(value, 0, 1);
|
|
214
|
+
};
|
|
215
|
+
return {
|
|
216
|
+
r: computeChannel(
|
|
217
|
+
palette.A?.r ?? 0,
|
|
218
|
+
palette.B?.r ?? 0,
|
|
219
|
+
palette.C?.r ?? 0,
|
|
220
|
+
palette.D?.r ?? 0
|
|
221
|
+
),
|
|
222
|
+
g: computeChannel(
|
|
223
|
+
palette.A?.g ?? 0,
|
|
224
|
+
palette.B?.g ?? 0,
|
|
225
|
+
palette.C?.g ?? 0,
|
|
226
|
+
palette.D?.g ?? 0
|
|
227
|
+
),
|
|
228
|
+
b: computeChannel(
|
|
229
|
+
palette.A?.b ?? 0,
|
|
230
|
+
palette.B?.b ?? 0,
|
|
231
|
+
palette.C?.b ?? 0,
|
|
232
|
+
palette.D?.b ?? 0
|
|
233
|
+
)
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
var toRgba = ({ r, g, b }, alpha = 0.5) => `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(
|
|
237
|
+
b * 255
|
|
238
|
+
)}, ${alpha})`;
|
|
239
|
+
var computePaletteGradient = (palette, steps = DEFAULT_GRADIENT_STEPS) => {
|
|
240
|
+
const stops = Array.from({ length: steps }, (_, index) => {
|
|
241
|
+
const t = index / (steps - 1);
|
|
242
|
+
const color = paletteColorAt(palette, t);
|
|
243
|
+
const stop = (t * 100).toFixed(1);
|
|
244
|
+
return `${toRgba(color)} ${stop}%`;
|
|
245
|
+
});
|
|
246
|
+
return `linear-gradient(to right, ${stops.join(", ")})`;
|
|
247
|
+
};
|
|
248
|
+
var createPaletteSignature = (palette) => Object.entries(palette).sort(([aKey], [bKey]) => aKey.localeCompare(bKey)).flatMap(
|
|
249
|
+
([, channels]) => CHANNEL_KEYS.map((channel) => (channels?.[channel] ?? 0).toFixed(3))
|
|
250
|
+
).join("-");
|
|
251
|
+
var isAdvancedPaletteValue = (value) => Boolean(
|
|
252
|
+
value && typeof value === "object" && CHANNEL_KEYS.every((channel) => {
|
|
253
|
+
const channelValue = value[channel];
|
|
254
|
+
return typeof channelValue === "number" && Number.isFinite(channelValue);
|
|
255
|
+
})
|
|
256
|
+
);
|
|
257
|
+
var isAdvancedPalette = (value) => Boolean(
|
|
258
|
+
value && typeof value === "object" && Object.values(value).every(
|
|
259
|
+
(entry) => isAdvancedPaletteValue(entry) || typeof entry === "object"
|
|
260
|
+
)
|
|
261
|
+
);
|
|
262
|
+
var normalizePaletteValue = (source) => {
|
|
263
|
+
if (typeof source === "string") {
|
|
264
|
+
return hexToPaletteValue(source);
|
|
265
|
+
}
|
|
266
|
+
const channelSource = source ?? {};
|
|
267
|
+
const toChannel = (channel) => clamp01(
|
|
268
|
+
toNumberOr(
|
|
269
|
+
channelSource[channel],
|
|
270
|
+
0
|
|
271
|
+
)
|
|
272
|
+
);
|
|
273
|
+
return {
|
|
274
|
+
r: toChannel("r"),
|
|
275
|
+
g: toChannel("g"),
|
|
276
|
+
b: toChannel("b")
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
var createPaletteFromRecord = (record) => Object.entries(record).reduce((acc, [key, value]) => {
|
|
280
|
+
acc[key] = normalizePaletteValue(value);
|
|
281
|
+
return acc;
|
|
282
|
+
}, {});
|
|
283
|
+
var clonePalette = (palette) => Object.fromEntries(
|
|
284
|
+
Object.entries(palette).map(([sectionKey, channels]) => [
|
|
285
|
+
sectionKey,
|
|
286
|
+
{ ...channels }
|
|
287
|
+
])
|
|
288
|
+
);
|
|
289
|
+
var hexComponentToNormalized = (component) => clamp01(parseInt(component, 16) / 255 || 0);
|
|
290
|
+
var normalizedChannelToHex = (value) => Math.round(clamp01(value) * 255).toString(16).padStart(2, "0");
|
|
291
|
+
var sanitizeHex = (hex) => {
|
|
292
|
+
let sanitized = hex.trim();
|
|
293
|
+
if (sanitized.startsWith("#")) {
|
|
294
|
+
sanitized = sanitized.slice(1);
|
|
295
|
+
}
|
|
296
|
+
if (sanitized.length === 3) {
|
|
297
|
+
sanitized = sanitized.split("").map((char) => char + char).join("");
|
|
298
|
+
}
|
|
299
|
+
return sanitized.length === 6 ? sanitized : null;
|
|
300
|
+
};
|
|
301
|
+
var hexToPaletteValue = (hex) => {
|
|
302
|
+
const sanitized = sanitizeHex(hex);
|
|
303
|
+
if (!sanitized) {
|
|
304
|
+
return { r: 0, g: 0, b: 0 };
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
r: hexComponentToNormalized(sanitized.slice(0, 2)),
|
|
308
|
+
g: hexComponentToNormalized(sanitized.slice(2, 4)),
|
|
309
|
+
b: hexComponentToNormalized(sanitized.slice(4, 6))
|
|
310
|
+
};
|
|
311
|
+
};
|
|
312
|
+
var paletteValueToHex = (value) => `#${normalizedChannelToHex(value.r)}${normalizedChannelToHex(
|
|
313
|
+
value.g
|
|
314
|
+
)}${normalizedChannelToHex(value.b)}`;
|
|
315
|
+
var createAdvancedPalette = (source = DEFAULT_HEX_PALETTE, options) => {
|
|
316
|
+
if (Array.isArray(source)) {
|
|
317
|
+
const order = options?.sectionOrder ?? DEFAULT_SECTIONS.map((section) => section.key);
|
|
318
|
+
const record = {};
|
|
319
|
+
source.forEach((value, index) => {
|
|
320
|
+
const preferredKey = order[index];
|
|
321
|
+
const fallbackKey = `Color${index + 1}`;
|
|
322
|
+
const key = preferredKey && !(preferredKey in record) ? preferredKey : fallbackKey;
|
|
323
|
+
record[key] = value;
|
|
324
|
+
});
|
|
325
|
+
return createPaletteFromRecord(record);
|
|
326
|
+
}
|
|
327
|
+
if (isAdvancedPalette(source)) {
|
|
328
|
+
return clonePalette(
|
|
329
|
+
Object.entries(source ?? {}).reduce(
|
|
330
|
+
(acc, [key, value]) => {
|
|
331
|
+
acc[key] = normalizePaletteValue(value);
|
|
332
|
+
return acc;
|
|
333
|
+
},
|
|
334
|
+
{}
|
|
335
|
+
)
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
if (source && typeof source === "object") {
|
|
339
|
+
return createPaletteFromRecord(source);
|
|
340
|
+
}
|
|
341
|
+
return createPaletteFromRecord(DEFAULT_HEX_PALETTE);
|
|
342
|
+
};
|
|
343
|
+
var DEFAULT_ADVANCED_PALETTE = createAdvancedPalette(
|
|
344
|
+
DEFAULT_HEX_PALETTE
|
|
345
|
+
);
|
|
346
|
+
var advancedPaletteToHexColors = (palette, options) => {
|
|
347
|
+
const fallbackPalette = options?.fallbackPalette ?? DEFAULT_ADVANCED_PALETTE;
|
|
348
|
+
const orderedKeys = options?.sectionOrder ?? (Object.keys(palette).length > 0 ? Object.keys(palette) : Object.keys(fallbackPalette));
|
|
349
|
+
const uniqueKeys = Array.from(new Set(orderedKeys));
|
|
350
|
+
if (uniqueKeys.length === 0) {
|
|
351
|
+
uniqueKeys.push(...Object.keys(DEFAULT_ADVANCED_PALETTE));
|
|
352
|
+
}
|
|
353
|
+
const defaultColor = options?.defaultColor ?? "#000000";
|
|
354
|
+
return uniqueKeys.map((key) => {
|
|
355
|
+
const paletteValue = palette[key] ?? fallbackPalette[key];
|
|
356
|
+
if (!paletteValue) return defaultColor;
|
|
357
|
+
return paletteValueToHex(paletteValue);
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
var createDefaultSectionsFromPalette = (palette) => {
|
|
361
|
+
const sectionKeys = Object.keys(palette);
|
|
362
|
+
if (sectionKeys.length === 0) return DEFAULT_SECTIONS;
|
|
363
|
+
return sectionKeys.map((key, index) => ({
|
|
364
|
+
key,
|
|
365
|
+
label: `Vector ${key}`,
|
|
366
|
+
helper: DEFAULT_SECTIONS[index]?.helper ?? "Palette parameter"
|
|
367
|
+
}));
|
|
368
|
+
};
|
|
369
|
+
var resolveAdvancedPaletteConfig = (config) => {
|
|
370
|
+
const defaultPalette = createAdvancedPalette(config.defaultPalette);
|
|
371
|
+
const sections = config.sections ?? createDefaultSectionsFromPalette(defaultPalette);
|
|
372
|
+
const ranges = {};
|
|
373
|
+
sections.forEach((section) => {
|
|
374
|
+
ranges[section.key] = config.ranges?.[section.key] ?? DEFAULT_RANGES[section.key] ?? {
|
|
375
|
+
min: 0,
|
|
376
|
+
max: 1,
|
|
377
|
+
step: 0.01
|
|
378
|
+
};
|
|
379
|
+
});
|
|
380
|
+
const channelLabels = {
|
|
381
|
+
...DEFAULT_CHANNEL_LABELS,
|
|
382
|
+
...config.channelLabels ?? {}
|
|
383
|
+
};
|
|
384
|
+
return {
|
|
385
|
+
...config,
|
|
386
|
+
defaultPalette,
|
|
387
|
+
sections,
|
|
388
|
+
ranges,
|
|
389
|
+
channelLabels,
|
|
390
|
+
hiddenKeyPrefix: config.hiddenKeyPrefix ?? DEFAULT_HIDDEN_KEY_PREFIX,
|
|
391
|
+
controlKey: config.controlKey ?? "advancedPaletteControl",
|
|
392
|
+
gradientSteps: config.gradientSteps ?? DEFAULT_GRADIENT_STEPS
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
var createAdvancedPaletteSchemaEntries = (schema, resolvedConfig) => {
|
|
396
|
+
const { sections, hiddenKeyPrefix, defaultPalette } = resolvedConfig;
|
|
397
|
+
const updatedSchema = { ...schema };
|
|
398
|
+
sections.forEach((section) => {
|
|
399
|
+
CHANNEL_KEYS.forEach((channel) => {
|
|
400
|
+
const key = createPaletteControlKey(
|
|
401
|
+
hiddenKeyPrefix,
|
|
402
|
+
section.key,
|
|
403
|
+
channel
|
|
404
|
+
);
|
|
405
|
+
if (!(key in updatedSchema)) {
|
|
406
|
+
updatedSchema[key] = {
|
|
407
|
+
type: "number",
|
|
408
|
+
value: defaultPalette?.[section.key]?.[channel] ?? DEFAULT_RANGES[section.key]?.min ?? 0,
|
|
409
|
+
hidden: true
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
return updatedSchema;
|
|
415
|
+
};
|
|
416
|
+
|
|
163
417
|
// src/context/ControlsContext.tsx
|
|
164
418
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
165
419
|
var ControlsContext = (0, import_react2.createContext)(null);
|
|
@@ -183,9 +437,16 @@ var ControlsProvider = ({ children }) => {
|
|
|
183
437
|
setComponentName(opts.componentName);
|
|
184
438
|
}
|
|
185
439
|
if (opts?.config) {
|
|
440
|
+
const { addAdvancedPaletteControl, ...otherConfig } = opts.config;
|
|
186
441
|
setConfig((prev) => ({
|
|
187
442
|
...prev,
|
|
188
|
-
...
|
|
443
|
+
...otherConfig,
|
|
444
|
+
...Object.prototype.hasOwnProperty.call(
|
|
445
|
+
opts.config,
|
|
446
|
+
"addAdvancedPaletteControl"
|
|
447
|
+
) ? {
|
|
448
|
+
addAdvancedPaletteControl: addAdvancedPaletteControl ? resolveAdvancedPaletteConfig(addAdvancedPaletteControl) : void 0
|
|
449
|
+
} : {}
|
|
189
450
|
}));
|
|
190
451
|
}
|
|
191
452
|
setSchema((prevSchema) => ({ ...prevSchema, ...newSchema }));
|
|
@@ -218,28 +479,41 @@ var ControlsProvider = ({ children }) => {
|
|
|
218
479
|
var useControls = (schema, options) => {
|
|
219
480
|
const ctx = (0, import_react2.useContext)(ControlsContext);
|
|
220
481
|
if (!ctx) throw new Error("useControls must be used within ControlsProvider");
|
|
482
|
+
const lastAdvancedPaletteSignature = (0, import_react2.useRef)(null);
|
|
221
483
|
const urlParams = getUrlParams();
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
key
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
484
|
+
const resolvedAdvancedConfig = options?.config?.addAdvancedPaletteControl ? resolveAdvancedPaletteConfig(options.config.addAdvancedPaletteControl) : void 0;
|
|
485
|
+
const schemaWithAdvanced = (0, import_react2.useMemo)(() => {
|
|
486
|
+
const baseSchema = { ...schema };
|
|
487
|
+
if (!resolvedAdvancedConfig) return baseSchema;
|
|
488
|
+
return createAdvancedPaletteSchemaEntries(
|
|
489
|
+
baseSchema,
|
|
490
|
+
resolvedAdvancedConfig
|
|
491
|
+
);
|
|
492
|
+
}, [schema, resolvedAdvancedConfig]);
|
|
493
|
+
const urlParamsKey = (0, import_react2.useMemo)(() => JSON.stringify(urlParams), [urlParams]);
|
|
494
|
+
const mergedSchema = (0, import_react2.useMemo)(() => {
|
|
495
|
+
return Object.fromEntries(
|
|
496
|
+
Object.entries(schemaWithAdvanced).map(([key, control]) => {
|
|
497
|
+
const urlValue = urlParams[key];
|
|
498
|
+
if (!urlValue || !("value" in control)) return [key, control];
|
|
499
|
+
const defaultValue = control.value;
|
|
500
|
+
let parsed = urlValue;
|
|
501
|
+
if (typeof defaultValue === "number") {
|
|
502
|
+
parsed = parseFloat(urlValue);
|
|
503
|
+
if (isNaN(parsed)) parsed = defaultValue;
|
|
504
|
+
} else if (typeof defaultValue === "boolean") {
|
|
505
|
+
parsed = urlValue === "true";
|
|
239
506
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
507
|
+
return [
|
|
508
|
+
key,
|
|
509
|
+
{
|
|
510
|
+
...control,
|
|
511
|
+
value: parsed
|
|
512
|
+
}
|
|
513
|
+
];
|
|
514
|
+
})
|
|
515
|
+
);
|
|
516
|
+
}, [schemaWithAdvanced, urlParams, urlParamsKey]);
|
|
243
517
|
(0, import_react2.useEffect)(() => {
|
|
244
518
|
ctx.registerSchema(mergedSchema, options);
|
|
245
519
|
}, [JSON.stringify(mergedSchema), JSON.stringify(options)]);
|
|
@@ -250,8 +524,35 @@ var useControls = (schema, options) => {
|
|
|
250
524
|
}
|
|
251
525
|
}
|
|
252
526
|
}, [JSON.stringify(mergedSchema), JSON.stringify(ctx.values)]);
|
|
527
|
+
(0, import_react2.useEffect)(() => {
|
|
528
|
+
if (!resolvedAdvancedConfig?.onPaletteChange) return;
|
|
529
|
+
const palette = resolvedAdvancedConfig.sections.reduce(
|
|
530
|
+
(acc, section) => {
|
|
531
|
+
const channels = CHANNEL_KEYS.reduce(
|
|
532
|
+
(channelAcc, channel) => {
|
|
533
|
+
const key = createPaletteControlKey(
|
|
534
|
+
resolvedAdvancedConfig.hiddenKeyPrefix,
|
|
535
|
+
section.key,
|
|
536
|
+
channel
|
|
537
|
+
);
|
|
538
|
+
const fallback = resolvedAdvancedConfig.defaultPalette?.[section.key]?.[channel] ?? 0;
|
|
539
|
+
channelAcc[channel] = toNumberOr(ctx.values[key], fallback);
|
|
540
|
+
return channelAcc;
|
|
541
|
+
},
|
|
542
|
+
{}
|
|
543
|
+
);
|
|
544
|
+
acc[section.key] = channels;
|
|
545
|
+
return acc;
|
|
546
|
+
},
|
|
547
|
+
{}
|
|
548
|
+
);
|
|
549
|
+
const signature = createPaletteSignature(palette);
|
|
550
|
+
if (lastAdvancedPaletteSignature.current === signature) return;
|
|
551
|
+
lastAdvancedPaletteSignature.current = signature;
|
|
552
|
+
resolvedAdvancedConfig.onPaletteChange(clonePalette(palette));
|
|
553
|
+
}, [ctx.values, resolvedAdvancedConfig]);
|
|
253
554
|
const typedValues = ctx.values;
|
|
254
|
-
const
|
|
555
|
+
const jsx14 = (0, import_react2.useCallback)(() => {
|
|
255
556
|
if (!options?.componentName) return "";
|
|
256
557
|
const props = Object.entries(typedValues).map(([key, val]) => {
|
|
257
558
|
if (typeof val === "string") return `${key}="${val}"`;
|
|
@@ -265,13 +566,13 @@ var useControls = (schema, options) => {
|
|
|
265
566
|
controls: ctx.values,
|
|
266
567
|
schema: ctx.schema,
|
|
267
568
|
setValue: ctx.setValue,
|
|
268
|
-
jsx:
|
|
569
|
+
jsx: jsx14
|
|
269
570
|
};
|
|
270
571
|
};
|
|
271
572
|
var useUrlSyncedControls = useControls;
|
|
272
573
|
|
|
273
574
|
// src/components/ControlPanel/ControlPanel.tsx
|
|
274
|
-
var
|
|
575
|
+
var import_react5 = require("react");
|
|
275
576
|
var import_lucide_react3 = require("lucide-react");
|
|
276
577
|
|
|
277
578
|
// src/hooks/usePreviewUrl.ts
|
|
@@ -544,20 +845,166 @@ var Button = React8.forwardRef(
|
|
|
544
845
|
);
|
|
545
846
|
Button.displayName = "Button";
|
|
546
847
|
|
|
547
|
-
// src/
|
|
848
|
+
// src/constants/layout.ts
|
|
849
|
+
var MOBILE_CONTROL_PANEL_PEEK = 112;
|
|
850
|
+
|
|
851
|
+
// src/components/AdvancedPaletteControl/AdvancedPaletteControl.tsx
|
|
852
|
+
var import_react4 = require("react");
|
|
548
853
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
854
|
+
var AdvancedPaletteControl = ({
|
|
855
|
+
config
|
|
856
|
+
}) => {
|
|
857
|
+
const { values, setValue } = useControlsContext();
|
|
858
|
+
const palette = (0, import_react4.useMemo)(() => {
|
|
859
|
+
const result = {};
|
|
860
|
+
config.sections.forEach((section) => {
|
|
861
|
+
result[section.key] = CHANNEL_KEYS.reduce((acc, channel) => {
|
|
862
|
+
const key = createPaletteControlKey(
|
|
863
|
+
config.hiddenKeyPrefix,
|
|
864
|
+
section.key,
|
|
865
|
+
channel
|
|
866
|
+
);
|
|
867
|
+
const defaultValue = config.defaultPalette?.[section.key]?.[channel] ?? DEFAULT_RANGES[section.key]?.min ?? 0;
|
|
868
|
+
acc[channel] = toNumberOr(values?.[key], defaultValue);
|
|
869
|
+
return acc;
|
|
870
|
+
}, {});
|
|
871
|
+
});
|
|
872
|
+
return result;
|
|
873
|
+
}, [config.defaultPalette, config.hiddenKeyPrefix, config.sections, values]);
|
|
874
|
+
const paletteGradient = (0, import_react4.useMemo)(
|
|
875
|
+
() => computePaletteGradient(palette, config.gradientSteps),
|
|
876
|
+
[palette, config.gradientSteps]
|
|
877
|
+
);
|
|
878
|
+
const paletteSignature = (0, import_react4.useMemo)(
|
|
879
|
+
() => createPaletteSignature(palette),
|
|
880
|
+
[palette]
|
|
881
|
+
);
|
|
882
|
+
const lastSignatureRef = (0, import_react4.useRef)(null);
|
|
883
|
+
(0, import_react4.useEffect)(() => {
|
|
884
|
+
if (!config.onPaletteChange) return;
|
|
885
|
+
if (lastSignatureRef.current === paletteSignature) return;
|
|
886
|
+
lastSignatureRef.current = paletteSignature;
|
|
887
|
+
config.onPaletteChange(palette);
|
|
888
|
+
}, [config, palette, paletteSignature]);
|
|
889
|
+
const updatePaletteValue = (0, import_react4.useCallback)(
|
|
890
|
+
(sectionKey, channel, nextValue) => {
|
|
891
|
+
const range = config.ranges[sectionKey] ?? DEFAULT_RANGES[sectionKey] ?? {
|
|
892
|
+
min: 0,
|
|
893
|
+
max: 1,
|
|
894
|
+
step: 0.01
|
|
895
|
+
};
|
|
896
|
+
const clamped = Math.min(Math.max(nextValue, range.min), range.max);
|
|
897
|
+
config.onInteraction?.();
|
|
898
|
+
const controlKey = createPaletteControlKey(
|
|
899
|
+
config.hiddenKeyPrefix,
|
|
900
|
+
sectionKey,
|
|
901
|
+
channel
|
|
902
|
+
);
|
|
903
|
+
setValue(controlKey, clamped);
|
|
904
|
+
},
|
|
905
|
+
[config, setValue]
|
|
906
|
+
);
|
|
907
|
+
const handleResetPalette = (0, import_react4.useCallback)(() => {
|
|
908
|
+
config.onInteraction?.();
|
|
909
|
+
config.sections.forEach((section) => {
|
|
910
|
+
CHANNEL_KEYS.forEach((channel) => {
|
|
911
|
+
const controlKey = createPaletteControlKey(
|
|
912
|
+
config.hiddenKeyPrefix,
|
|
913
|
+
section.key,
|
|
914
|
+
channel
|
|
915
|
+
);
|
|
916
|
+
const defaultValue = config.defaultPalette?.[section.key]?.[channel] ?? DEFAULT_RANGES[section.key]?.min ?? 0;
|
|
917
|
+
setValue(controlKey, defaultValue);
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
}, [config, setValue]);
|
|
921
|
+
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: [
|
|
922
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
923
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs font-semibold uppercase tracking-wide text-stone-200", children: "Palette" }),
|
|
924
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
925
|
+
"button",
|
|
926
|
+
{
|
|
927
|
+
type: "button",
|
|
928
|
+
onClick: handleResetPalette,
|
|
929
|
+
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",
|
|
930
|
+
children: "Reset Palette"
|
|
931
|
+
}
|
|
932
|
+
)
|
|
933
|
+
] }),
|
|
934
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
935
|
+
"div",
|
|
936
|
+
{
|
|
937
|
+
className: "h-4 w-full rounded border border-stone-700",
|
|
938
|
+
style: { background: paletteGradient }
|
|
939
|
+
}
|
|
940
|
+
),
|
|
941
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-col gap-4", children: config.sections.map((section) => {
|
|
942
|
+
const range = config.ranges[section.key];
|
|
943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-3", children: [
|
|
944
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between text-[11px] uppercase tracking-widest text-stone-300", children: [
|
|
945
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: section.label }),
|
|
946
|
+
section.helper && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-stone-500", children: section.helper })
|
|
947
|
+
] }),
|
|
948
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "grid grid-cols-3 gap-3", children: CHANNEL_KEYS.map((channel) => {
|
|
949
|
+
const value = palette[section.key][channel];
|
|
950
|
+
const channelLabel = config.channelLabels?.[channel] ?? DEFAULT_CHANNEL_LABELS[channel];
|
|
951
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
|
|
952
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between text-[10px] uppercase tracking-widest text-stone-400", children: [
|
|
953
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: channelLabel }),
|
|
954
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: value.toFixed(2) })
|
|
955
|
+
] }),
|
|
956
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
957
|
+
"input",
|
|
958
|
+
{
|
|
959
|
+
type: "range",
|
|
960
|
+
min: range.min,
|
|
961
|
+
max: range.max,
|
|
962
|
+
step: range.step,
|
|
963
|
+
value,
|
|
964
|
+
onPointerDown: config.onInteraction,
|
|
965
|
+
onChange: (event) => updatePaletteValue(
|
|
966
|
+
section.key,
|
|
967
|
+
channel,
|
|
968
|
+
parseFloat(event.target.value)
|
|
969
|
+
),
|
|
970
|
+
className: "w-full cursor-pointer accent-stone-300"
|
|
971
|
+
}
|
|
972
|
+
),
|
|
973
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
974
|
+
"input",
|
|
975
|
+
{
|
|
976
|
+
type: "number",
|
|
977
|
+
min: range.min,
|
|
978
|
+
max: range.max,
|
|
979
|
+
step: range.step,
|
|
980
|
+
value: value.toFixed(3),
|
|
981
|
+
onPointerDown: config.onInteraction,
|
|
982
|
+
onFocus: config.onInteraction,
|
|
983
|
+
onChange: (event) => {
|
|
984
|
+
const parsed = parseFloat(event.target.value);
|
|
985
|
+
if (Number.isNaN(parsed)) return;
|
|
986
|
+
updatePaletteValue(section.key, channel, parsed);
|
|
987
|
+
},
|
|
988
|
+
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"
|
|
989
|
+
}
|
|
990
|
+
)
|
|
991
|
+
] }, channel);
|
|
992
|
+
}) })
|
|
993
|
+
] }, section.key);
|
|
994
|
+
}) })
|
|
995
|
+
] }) });
|
|
996
|
+
};
|
|
997
|
+
var AdvancedPaletteControl_default = AdvancedPaletteControl;
|
|
998
|
+
|
|
999
|
+
// src/components/ControlPanel/ControlPanel.tsx
|
|
1000
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
549
1001
|
var ControlPanel = () => {
|
|
550
|
-
const [copied, setCopied] = (0,
|
|
1002
|
+
const [copied, setCopied] = (0, import_react5.useState)(false);
|
|
1003
|
+
const [folderStates, setFolderStates] = (0, import_react5.useState)({});
|
|
551
1004
|
const { leftPanelWidth, isDesktop, isHydrated } = useResizableLayout();
|
|
552
1005
|
const { schema, setValue, values, componentName, config } = useControlsContext();
|
|
553
1006
|
const previewUrl = usePreviewUrl(values);
|
|
554
|
-
const
|
|
555
|
-
([, control]) => control.type !== "button" && !control.hidden
|
|
556
|
-
);
|
|
557
|
-
const buttonControls = Object.entries(schema).filter(
|
|
558
|
-
([, control]) => control.type === "button" && !control.hidden
|
|
559
|
-
);
|
|
560
|
-
const jsx16 = (0, import_react4.useMemo)(() => {
|
|
1007
|
+
const jsx14 = (0, import_react5.useMemo)(() => {
|
|
561
1008
|
if (!componentName) return "";
|
|
562
1009
|
const props = Object.entries(values).map(([key, val]) => {
|
|
563
1010
|
if (typeof val === "string") return `${key}="${val}"`;
|
|
@@ -566,178 +1013,348 @@ var ControlPanel = () => {
|
|
|
566
1013
|
}).join(" ");
|
|
567
1014
|
return `<${componentName} ${props} />`;
|
|
568
1015
|
}, [componentName, values]);
|
|
569
|
-
|
|
1016
|
+
const visibleEntries = Object.entries(schema).filter(
|
|
1017
|
+
([, control]) => !control.hidden
|
|
1018
|
+
);
|
|
1019
|
+
const rootControls = [];
|
|
1020
|
+
const folderOrder = [];
|
|
1021
|
+
const folderControls = /* @__PURE__ */ new Map();
|
|
1022
|
+
const folderExtras = /* @__PURE__ */ new Map();
|
|
1023
|
+
const folderPlacement = /* @__PURE__ */ new Map();
|
|
1024
|
+
const seenFolders = /* @__PURE__ */ new Set();
|
|
1025
|
+
const ensureFolder = (folder) => {
|
|
1026
|
+
if (!seenFolders.has(folder)) {
|
|
1027
|
+
seenFolders.add(folder);
|
|
1028
|
+
folderOrder.push(folder);
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
visibleEntries.forEach((entry) => {
|
|
1032
|
+
const [key, control] = entry;
|
|
1033
|
+
const folder = control.folder?.trim();
|
|
1034
|
+
if (folder) {
|
|
1035
|
+
const placement = control.folderPlacement ?? "bottom";
|
|
1036
|
+
ensureFolder(folder);
|
|
1037
|
+
if (!folderControls.has(folder)) {
|
|
1038
|
+
folderControls.set(folder, []);
|
|
1039
|
+
}
|
|
1040
|
+
folderControls.get(folder).push(entry);
|
|
1041
|
+
const existingPlacement = folderPlacement.get(folder);
|
|
1042
|
+
if (!existingPlacement || placement === "top") {
|
|
1043
|
+
folderPlacement.set(folder, placement);
|
|
1044
|
+
}
|
|
1045
|
+
} else {
|
|
1046
|
+
rootControls.push(entry);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
const advancedConfig = config?.addAdvancedPaletteControl;
|
|
1050
|
+
let advancedPaletteControlNode = null;
|
|
1051
|
+
if (advancedConfig) {
|
|
1052
|
+
const advancedNode = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1053
|
+
AdvancedPaletteControl_default,
|
|
1054
|
+
{
|
|
1055
|
+
config: advancedConfig
|
|
1056
|
+
},
|
|
1057
|
+
"advancedPaletteControl"
|
|
1058
|
+
);
|
|
1059
|
+
const advancedFolder = advancedConfig.folder?.trim();
|
|
1060
|
+
if (advancedFolder) {
|
|
1061
|
+
const placement = advancedConfig.folderPlacement ?? "bottom";
|
|
1062
|
+
ensureFolder(advancedFolder);
|
|
1063
|
+
if (!folderControls.has(advancedFolder)) {
|
|
1064
|
+
folderControls.set(advancedFolder, []);
|
|
1065
|
+
}
|
|
1066
|
+
const existingPlacement = folderPlacement.get(advancedFolder);
|
|
1067
|
+
if (!existingPlacement || placement === "top") {
|
|
1068
|
+
folderPlacement.set(advancedFolder, placement);
|
|
1069
|
+
}
|
|
1070
|
+
if (!folderExtras.has(advancedFolder)) {
|
|
1071
|
+
folderExtras.set(advancedFolder, []);
|
|
1072
|
+
}
|
|
1073
|
+
folderExtras.get(advancedFolder).push(advancedNode);
|
|
1074
|
+
} else {
|
|
1075
|
+
advancedPaletteControlNode = advancedNode;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
const rootButtonControls = rootControls.filter(
|
|
1079
|
+
([, control]) => control.type === "button"
|
|
1080
|
+
);
|
|
1081
|
+
const rootNormalControls = rootControls.filter(
|
|
1082
|
+
([, control]) => control.type !== "button"
|
|
1083
|
+
);
|
|
1084
|
+
const folderGroups = folderOrder.map((folder) => ({
|
|
1085
|
+
folder,
|
|
1086
|
+
entries: folderControls.get(folder) ?? [],
|
|
1087
|
+
extras: folderExtras.get(folder) ?? [],
|
|
1088
|
+
placement: folderPlacement.get(folder) ?? "bottom"
|
|
1089
|
+
})).filter((group) => group.entries.length > 0 || group.extras.length > 0);
|
|
1090
|
+
const hasRootButtonControls = rootButtonControls.length > 0;
|
|
1091
|
+
const hasAnyFolders = folderGroups.length > 0;
|
|
1092
|
+
const jsonToComponentString = (0, import_react5.useCallback)(
|
|
1093
|
+
({
|
|
1094
|
+
componentName: componentNameOverride,
|
|
1095
|
+
props
|
|
1096
|
+
}) => {
|
|
1097
|
+
const resolvedComponentName = componentNameOverride ?? componentName;
|
|
1098
|
+
if (!resolvedComponentName) return "";
|
|
1099
|
+
const formatProp = (key, value) => {
|
|
1100
|
+
if (value === void 0) return null;
|
|
1101
|
+
if (value === null) return `${key}={null}`;
|
|
1102
|
+
if (typeof value === "string") return `${key}="${value}"`;
|
|
1103
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
1104
|
+
return `${key}={${value}}`;
|
|
1105
|
+
}
|
|
1106
|
+
if (typeof value === "bigint") {
|
|
1107
|
+
return `${key}={${value.toString()}n}`;
|
|
1108
|
+
}
|
|
1109
|
+
return `${key}={${JSON.stringify(value)}}`;
|
|
1110
|
+
};
|
|
1111
|
+
const formattedProps = Object.entries(props ?? {}).map(([key, value]) => formatProp(key, value)).filter((prop) => Boolean(prop)).join(" ");
|
|
1112
|
+
if (!formattedProps) {
|
|
1113
|
+
return `<${resolvedComponentName} />`;
|
|
1114
|
+
}
|
|
1115
|
+
return `<${resolvedComponentName} ${formattedProps} />`;
|
|
1116
|
+
},
|
|
1117
|
+
[componentName]
|
|
1118
|
+
);
|
|
1119
|
+
const copyText = config?.showCopyButtonFn?.({
|
|
1120
|
+
componentName,
|
|
1121
|
+
values,
|
|
1122
|
+
schema,
|
|
1123
|
+
jsx: jsx14,
|
|
1124
|
+
jsonToComponentString
|
|
1125
|
+
}) ?? jsx14;
|
|
1126
|
+
const shouldShowCopyButton = config?.showCopyButton !== false && Boolean(copyText);
|
|
1127
|
+
const labelize = (key) => key.replace(/([A-Z])/g, " $1").replace(/[\-_]/g, " ").replace(/\s+/g, " ").trim().replace(/(^|\s)\S/g, (s) => s.toUpperCase());
|
|
1128
|
+
const renderButtonControl = (key, control, variant) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
570
1129
|
"div",
|
|
571
1130
|
{
|
|
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
|
-
case "string":
|
|
635
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
636
|
-
Input,
|
|
637
|
-
{
|
|
638
|
-
id: key,
|
|
639
|
-
value,
|
|
640
|
-
className: "bg-stone-900",
|
|
641
|
-
placeholder: key,
|
|
642
|
-
onChange: (e) => setValue(key, e.target.value)
|
|
643
|
-
},
|
|
644
|
-
key
|
|
645
|
-
);
|
|
646
|
-
case "color":
|
|
647
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2 w-full", children: [
|
|
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;
|
|
1131
|
+
className: variant === "root" ? "flex-1 [&_[data-slot=button]]:w-full" : "[&_[data-slot=button]]:w-full",
|
|
1132
|
+
children: control.render ? control.render() : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1133
|
+
"button",
|
|
1134
|
+
{
|
|
1135
|
+
onClick: control.onClick,
|
|
1136
|
+
className: "w-full px-4 py-2 text-sm bg-stone-800 hover:bg-stone-700 text-white rounded-md shadow",
|
|
1137
|
+
children: control.label ?? key
|
|
1138
|
+
}
|
|
1139
|
+
)
|
|
1140
|
+
},
|
|
1141
|
+
`control-panel-custom-${key}`
|
|
1142
|
+
);
|
|
1143
|
+
const renderControl = (key, control, variant) => {
|
|
1144
|
+
if (control.type === "button") {
|
|
1145
|
+
return renderButtonControl(key, control, variant);
|
|
1146
|
+
}
|
|
1147
|
+
const value = values[key];
|
|
1148
|
+
switch (control.type) {
|
|
1149
|
+
case "boolean":
|
|
1150
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1151
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { htmlFor: key, className: "cursor-pointer", children: labelize(key) }),
|
|
1152
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1153
|
+
Switch,
|
|
1154
|
+
{
|
|
1155
|
+
id: key,
|
|
1156
|
+
checked: value,
|
|
1157
|
+
onCheckedChange: (v) => setValue(key, v),
|
|
1158
|
+
className: "cursor-pointer scale-90"
|
|
1159
|
+
}
|
|
1160
|
+
)
|
|
1161
|
+
] }, key);
|
|
1162
|
+
case "number":
|
|
1163
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-3 w-full", children: [
|
|
1164
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1165
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: labelize(key) }),
|
|
1166
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1167
|
+
Input,
|
|
1168
|
+
{
|
|
1169
|
+
type: "number",
|
|
1170
|
+
value,
|
|
1171
|
+
min: control.min ?? 0,
|
|
1172
|
+
max: control.max ?? 100,
|
|
1173
|
+
step: control.step ?? 1,
|
|
1174
|
+
onChange: (e) => {
|
|
1175
|
+
const v = parseFloat(e.target.value);
|
|
1176
|
+
if (Number.isNaN(v)) return;
|
|
1177
|
+
setValue(key, v);
|
|
1178
|
+
},
|
|
1179
|
+
className: "w-20 text-center cursor-text"
|
|
1180
|
+
}
|
|
1181
|
+
)
|
|
1182
|
+
] }),
|
|
1183
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1184
|
+
Slider,
|
|
1185
|
+
{
|
|
1186
|
+
id: key,
|
|
1187
|
+
min: control.min ?? 0,
|
|
1188
|
+
max: control.max ?? 100,
|
|
1189
|
+
step: control.step ?? 1,
|
|
1190
|
+
value: [value],
|
|
1191
|
+
onValueChange: ([v]) => setValue(key, v),
|
|
1192
|
+
className: "w-full cursor-pointer"
|
|
686
1193
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
1194
|
+
)
|
|
1195
|
+
] }, key);
|
|
1196
|
+
case "string":
|
|
1197
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-2 w-full", children: [
|
|
1198
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: labelize(key) }),
|
|
1199
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1200
|
+
Input,
|
|
1201
|
+
{
|
|
1202
|
+
id: key,
|
|
1203
|
+
value,
|
|
1204
|
+
placeholder: key,
|
|
1205
|
+
onChange: (e) => setValue(key, e.target.value),
|
|
1206
|
+
className: "bg-stone-900"
|
|
1207
|
+
}
|
|
1208
|
+
)
|
|
1209
|
+
] }, key);
|
|
1210
|
+
case "color":
|
|
1211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-2 w-full", children: [
|
|
1212
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: labelize(key) }),
|
|
1213
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1214
|
+
"input",
|
|
1215
|
+
{
|
|
1216
|
+
type: "color",
|
|
1217
|
+
id: key,
|
|
1218
|
+
value,
|
|
1219
|
+
onChange: (e) => setValue(key, e.target.value),
|
|
1220
|
+
className: "w-full h-10 rounded border border-stone-600 bg-transparent"
|
|
1221
|
+
}
|
|
1222
|
+
)
|
|
1223
|
+
] }, key);
|
|
1224
|
+
case "select":
|
|
1225
|
+
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: [
|
|
1226
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "min-w-fit", htmlFor: key, children: labelize(key) }),
|
|
1227
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Select, { value, onValueChange: (val) => setValue(key, val), children: [
|
|
1228
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectTrigger, { className: "flex-1 cursor-pointer", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectValue, { placeholder: "Select option" }) }),
|
|
1229
|
+
/* @__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)(
|
|
1230
|
+
SelectItem,
|
|
1231
|
+
{
|
|
1232
|
+
value: label,
|
|
1233
|
+
className: "cursor-pointer",
|
|
1234
|
+
children: label
|
|
1235
|
+
},
|
|
1236
|
+
label
|
|
1237
|
+
)) })
|
|
1238
|
+
] })
|
|
1239
|
+
] }) }, key);
|
|
1240
|
+
default:
|
|
1241
|
+
return null;
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
const renderFolder = (folder, entries, extras = []) => {
|
|
1245
|
+
const isOpen = folderStates[folder] ?? true;
|
|
1246
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1247
|
+
"div",
|
|
1248
|
+
{
|
|
1249
|
+
className: "border border-stone-700/60 rounded-lg bg-stone-900/70",
|
|
1250
|
+
children: [
|
|
1251
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1252
|
+
"button",
|
|
690
1253
|
{
|
|
691
|
-
|
|
1254
|
+
type: "button",
|
|
1255
|
+
onClick: () => setFolderStates((prev) => ({
|
|
1256
|
+
...prev,
|
|
1257
|
+
[folder]: !isOpen
|
|
1258
|
+
})),
|
|
1259
|
+
className: "w-full flex items-center justify-between px-4 py-3 text-left font-semibold text-stone-200 tracking-wide",
|
|
692
1260
|
children: [
|
|
693
|
-
|
|
694
|
-
|
|
1261
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: folder }),
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1263
|
+
import_lucide_react3.ChevronDown,
|
|
695
1264
|
{
|
|
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
|
-
] })
|
|
1265
|
+
className: `w-4 h-4 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
|
|
709
1266
|
}
|
|
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
|
-
) })
|
|
1267
|
+
)
|
|
728
1268
|
]
|
|
729
1269
|
}
|
|
730
|
-
)
|
|
1270
|
+
),
|
|
1271
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "px-4 pb-4 pt-0 space-y-5", children: [
|
|
1272
|
+
entries.map(
|
|
1273
|
+
([key, control]) => renderControl(key, control, "folder")
|
|
1274
|
+
),
|
|
1275
|
+
extras.map((extra) => extra)
|
|
1276
|
+
] })
|
|
1277
|
+
]
|
|
1278
|
+
},
|
|
1279
|
+
folder
|
|
1280
|
+
);
|
|
1281
|
+
};
|
|
1282
|
+
const topFolderSections = hasAnyFolders ? folderGroups.filter(({ placement }) => placement === "top").map(
|
|
1283
|
+
({ folder, entries, extras }) => renderFolder(folder, entries, extras)
|
|
1284
|
+
) : null;
|
|
1285
|
+
const bottomFolderSections = hasAnyFolders ? folderGroups.filter(({ placement }) => placement === "bottom").map(
|
|
1286
|
+
({ folder, entries, extras }) => renderFolder(folder, entries, extras)
|
|
1287
|
+
) : null;
|
|
1288
|
+
const panelStyle = {
|
|
1289
|
+
width: "100%",
|
|
1290
|
+
height: "auto",
|
|
1291
|
+
flex: "0 0 auto"
|
|
1292
|
+
};
|
|
1293
|
+
if (isHydrated) {
|
|
1294
|
+
if (isDesktop) {
|
|
1295
|
+
Object.assign(panelStyle, {
|
|
1296
|
+
position: "absolute",
|
|
1297
|
+
left: 0,
|
|
1298
|
+
top: 0,
|
|
1299
|
+
bottom: 0,
|
|
1300
|
+
width: `${leftPanelWidth}%`,
|
|
1301
|
+
overflowY: "auto"
|
|
1302
|
+
});
|
|
1303
|
+
} else {
|
|
1304
|
+
Object.assign(panelStyle, {
|
|
1305
|
+
marginTop: `calc(-1 * (${MOBILE_CONTROL_PANEL_PEEK}px + env(safe-area-inset-bottom, 0px)))`,
|
|
1306
|
+
paddingBottom: `calc(${MOBILE_CONTROL_PANEL_PEEK}px + env(safe-area-inset-bottom, 0px))`
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1311
|
+
"div",
|
|
1312
|
+
{
|
|
1313
|
+
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"}`,
|
|
1314
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
1315
|
+
onTouchStart: (e) => e.stopPropagation(),
|
|
1316
|
+
style: panelStyle,
|
|
1317
|
+
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: [
|
|
1318
|
+
/* @__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" }) }),
|
|
1319
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-6", children: [
|
|
1320
|
+
topFolderSections,
|
|
1321
|
+
hasRootButtonControls && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-wrap gap-2", children: rootButtonControls.map(
|
|
1322
|
+
([key, control]) => renderButtonControl(key, control, "root")
|
|
1323
|
+
) }),
|
|
1324
|
+
advancedPaletteControlNode,
|
|
1325
|
+
rootNormalControls.map(
|
|
1326
|
+
([key, control]) => renderControl(key, control, "root")
|
|
1327
|
+
),
|
|
1328
|
+
bottomFolderSections,
|
|
1329
|
+
shouldShowCopyButton && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1330
|
+
"button",
|
|
1331
|
+
{
|
|
1332
|
+
onClick: () => {
|
|
1333
|
+
if (!copyText) return;
|
|
1334
|
+
navigator.clipboard.writeText(copyText);
|
|
1335
|
+
setCopied(true);
|
|
1336
|
+
setTimeout(() => setCopied(false), 5e3);
|
|
1337
|
+
},
|
|
1338
|
+
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",
|
|
1339
|
+
children: copied ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
1340
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.Check, { className: "w-4 h-4" }),
|
|
1341
|
+
"Copied"
|
|
1342
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
1343
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.Copy, { className: "w-4 h-4" }),
|
|
1344
|
+
"Copy to Clipboard"
|
|
1345
|
+
] })
|
|
1346
|
+
}
|
|
1347
|
+
) }, "control-panel-jsx")
|
|
731
1348
|
] }),
|
|
732
|
-
previewUrl && /* @__PURE__ */ (0,
|
|
1349
|
+
previewUrl && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
733
1350
|
"a",
|
|
734
1351
|
{
|
|
735
1352
|
href: previewUrl,
|
|
736
1353
|
target: "_blank",
|
|
737
1354
|
rel: "noopener noreferrer",
|
|
738
|
-
className: "w-full px-4 py-2 text-sm text-center bg-stone-
|
|
1355
|
+
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",
|
|
739
1356
|
children: [
|
|
740
|
-
/* @__PURE__ */ (0,
|
|
1357
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.SquareArrowOutUpRight, {}),
|
|
741
1358
|
" Open in a New Tab"
|
|
742
1359
|
]
|
|
743
1360
|
}
|
|
@@ -749,12 +1366,12 @@ var ControlPanel = () => {
|
|
|
749
1366
|
var ControlPanel_default = ControlPanel;
|
|
750
1367
|
|
|
751
1368
|
// src/components/PreviewContainer/PreviewContainer.tsx
|
|
752
|
-
var
|
|
1369
|
+
var import_react6 = require("react");
|
|
753
1370
|
|
|
754
1371
|
// src/components/Grid/Grid.tsx
|
|
755
|
-
var
|
|
1372
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
756
1373
|
function Grid() {
|
|
757
|
-
return /* @__PURE__ */ (0,
|
|
1374
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
758
1375
|
"div",
|
|
759
1376
|
{
|
|
760
1377
|
className: "absolute inset-0 w-screen h-screen z-[0] blur-[1px]",
|
|
@@ -772,12 +1389,12 @@ function Grid() {
|
|
|
772
1389
|
var Grid_default = Grid;
|
|
773
1390
|
|
|
774
1391
|
// src/components/PreviewContainer/PreviewContainer.tsx
|
|
775
|
-
var
|
|
1392
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
776
1393
|
var PreviewContainer = ({ children, hideControls }) => {
|
|
777
1394
|
const { config } = useControlsContext();
|
|
778
1395
|
const { leftPanelWidth, isDesktop, isHydrated, containerRef } = useResizableLayout();
|
|
779
|
-
const previewRef = (0,
|
|
780
|
-
return /* @__PURE__ */ (0,
|
|
1396
|
+
const previewRef = (0, import_react6.useRef)(null);
|
|
1397
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
781
1398
|
"div",
|
|
782
1399
|
{
|
|
783
1400
|
ref: previewRef,
|
|
@@ -786,9 +1403,9 @@ var PreviewContainer = ({ children, hideControls }) => {
|
|
|
786
1403
|
width: `${100 - leftPanelWidth}%`,
|
|
787
1404
|
marginLeft: `${leftPanelWidth}%`
|
|
788
1405
|
} : {},
|
|
789
|
-
children: /* @__PURE__ */ (0,
|
|
790
|
-
config?.showGrid && /* @__PURE__ */ (0,
|
|
791
|
-
/* @__PURE__ */ (0,
|
|
1406
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "w-screen h-screen", children: [
|
|
1407
|
+
config?.showGrid && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Grid_default, {}),
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "w-screen h-screen flex items-center justify-center relative", children })
|
|
792
1409
|
] })
|
|
793
1410
|
}
|
|
794
1411
|
);
|
|
@@ -796,15 +1413,15 @@ var PreviewContainer = ({ children, hideControls }) => {
|
|
|
796
1413
|
var PreviewContainer_default = PreviewContainer;
|
|
797
1414
|
|
|
798
1415
|
// src/components/Playground/Playground.tsx
|
|
799
|
-
var
|
|
1416
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
800
1417
|
var NO_CONTROLS_PARAM = "nocontrols";
|
|
801
1418
|
function Playground({ children }) {
|
|
802
|
-
const [isHydrated, setIsHydrated] = (0,
|
|
803
|
-
const [copied, setCopied] = (0,
|
|
804
|
-
(0,
|
|
1419
|
+
const [isHydrated, setIsHydrated] = (0, import_react7.useState)(false);
|
|
1420
|
+
const [copied, setCopied] = (0, import_react7.useState)(false);
|
|
1421
|
+
(0, import_react7.useEffect)(() => {
|
|
805
1422
|
setIsHydrated(true);
|
|
806
1423
|
}, []);
|
|
807
|
-
const hideControls = (0,
|
|
1424
|
+
const hideControls = (0, import_react7.useMemo)(() => {
|
|
808
1425
|
if (typeof window === "undefined") return false;
|
|
809
1426
|
return new URLSearchParams(window.location.search).get(NO_CONTROLS_PARAM) === "true";
|
|
810
1427
|
}, []);
|
|
@@ -814,143 +1431,144 @@ function Playground({ children }) {
|
|
|
814
1431
|
setTimeout(() => setCopied(false), 2e3);
|
|
815
1432
|
};
|
|
816
1433
|
if (!isHydrated) return null;
|
|
817
|
-
return /* @__PURE__ */ (0,
|
|
818
|
-
hideControls && /* @__PURE__ */ (0,
|
|
1434
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ResizableLayout, { hideControls, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ControlsProvider, { children: [
|
|
1435
|
+
hideControls && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
819
1436
|
"button",
|
|
820
1437
|
{
|
|
821
1438
|
onClick: handleCopy,
|
|
822
1439
|
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
1440
|
children: [
|
|
824
|
-
copied ? /* @__PURE__ */ (0,
|
|
1441
|
+
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
1442
|
copied ? "Copied!" : "Share"
|
|
826
1443
|
]
|
|
827
1444
|
}
|
|
828
1445
|
),
|
|
829
|
-
/* @__PURE__ */ (0,
|
|
830
|
-
!hideControls && /* @__PURE__ */ (0,
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PreviewContainer_default, { hideControls, children }),
|
|
1447
|
+
!hideControls && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ControlPanel_default, {})
|
|
831
1448
|
] }) });
|
|
832
1449
|
}
|
|
833
1450
|
|
|
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 = ({ mediaProps, children }) => {
|
|
880
|
-
const canvasRef = (0, import_react8.useRef)(null);
|
|
881
|
-
const [parentSize, setParentSize] = (0, import_react8.useState)(null);
|
|
1451
|
+
// src/hooks/useAdvancedPaletteControls.ts
|
|
1452
|
+
var import_react8 = require("react");
|
|
1453
|
+
var cloneForCallbacks = (palette) => clonePalette(palette);
|
|
1454
|
+
var useAdvancedPaletteControls = (options = {}) => {
|
|
1455
|
+
const resolvedDefaultPalette = (0, import_react8.useMemo)(
|
|
1456
|
+
() => createAdvancedPalette(options.defaultPalette),
|
|
1457
|
+
[options.defaultPalette]
|
|
1458
|
+
);
|
|
1459
|
+
const resolvedFallbackPalette = (0, import_react8.useMemo)(
|
|
1460
|
+
() => options.fallbackPalette ? createAdvancedPalette(options.fallbackPalette) : resolvedDefaultPalette,
|
|
1461
|
+
[options.fallbackPalette, resolvedDefaultPalette]
|
|
1462
|
+
);
|
|
1463
|
+
const [palette, setPaletteState] = (0, import_react8.useState)(
|
|
1464
|
+
() => clonePalette(resolvedDefaultPalette)
|
|
1465
|
+
);
|
|
1466
|
+
const defaultSignatureRef = (0, import_react8.useRef)(
|
|
1467
|
+
createPaletteSignature(resolvedDefaultPalette)
|
|
1468
|
+
);
|
|
882
1469
|
(0, import_react8.useEffect)(() => {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1470
|
+
const nextSignature = createPaletteSignature(resolvedDefaultPalette);
|
|
1471
|
+
if (defaultSignatureRef.current === nextSignature) return;
|
|
1472
|
+
defaultSignatureRef.current = nextSignature;
|
|
1473
|
+
setPaletteState(clonePalette(resolvedDefaultPalette));
|
|
1474
|
+
}, [resolvedDefaultPalette]);
|
|
1475
|
+
const notifyChange = (0, import_react8.useCallback)(
|
|
1476
|
+
(nextPalette) => {
|
|
1477
|
+
options.onChange?.(cloneForCallbacks(nextPalette));
|
|
1478
|
+
},
|
|
1479
|
+
[options.onChange]
|
|
1480
|
+
);
|
|
1481
|
+
const setPalette = (0, import_react8.useCallback)(
|
|
1482
|
+
(source) => {
|
|
1483
|
+
const nextPalette = createAdvancedPalette(
|
|
1484
|
+
source ?? resolvedDefaultPalette
|
|
1485
|
+
);
|
|
1486
|
+
setPaletteState(clonePalette(nextPalette));
|
|
1487
|
+
notifyChange(nextPalette);
|
|
1488
|
+
},
|
|
1489
|
+
[notifyChange, resolvedDefaultPalette]
|
|
1490
|
+
);
|
|
1491
|
+
const updatePalette = (0, import_react8.useCallback)(
|
|
1492
|
+
(updater) => {
|
|
1493
|
+
setPaletteState((current) => {
|
|
1494
|
+
const nextSource = updater(clonePalette(current));
|
|
1495
|
+
const nextPalette = createAdvancedPalette(
|
|
1496
|
+
nextSource ?? current ?? resolvedDefaultPalette
|
|
1497
|
+
);
|
|
1498
|
+
notifyChange(nextPalette);
|
|
1499
|
+
return clonePalette(nextPalette);
|
|
894
1500
|
});
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
tryObserve();
|
|
898
|
-
return () => {
|
|
899
|
-
if (observer) observer.disconnect();
|
|
900
|
-
};
|
|
901
|
-
}, []);
|
|
902
|
-
const mergedMediaProps = {
|
|
903
|
-
...mediaProps || {},
|
|
904
|
-
size: mediaProps?.size || { width: 400, height: 400 }
|
|
905
|
-
};
|
|
906
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
907
|
-
"div",
|
|
908
|
-
{
|
|
909
|
-
ref: canvasRef,
|
|
910
|
-
className: "w-full h-full pointer-events-none relative touch-none",
|
|
911
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
912
|
-
import_fiber2.Canvas,
|
|
913
|
-
{
|
|
914
|
-
resize: { polyfill: ResizeObserver },
|
|
915
|
-
style: { width: parentSize?.width, height: parentSize?.height },
|
|
916
|
-
gl: { preserveDrawingBuffer: true },
|
|
917
|
-
children: [
|
|
918
|
-
parentSize?.height && parentSize?.width && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
919
|
-
ResponsiveCamera,
|
|
920
|
-
{
|
|
921
|
-
height: parentSize.height,
|
|
922
|
-
width: parentSize.width
|
|
923
|
-
}
|
|
924
|
-
),
|
|
925
|
-
mediaProps?.debugOrbit && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CameraLogger, {}),
|
|
926
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ambientLight", { intensity: 1 }),
|
|
927
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("pointLight", { position: [10, 10, 10] }),
|
|
928
|
-
import_react8.default.cloneElement(children, mergedMediaProps)
|
|
929
|
-
]
|
|
930
|
-
}
|
|
931
|
-
)
|
|
932
|
-
}
|
|
1501
|
+
},
|
|
1502
|
+
[notifyChange, resolvedDefaultPalette]
|
|
933
1503
|
);
|
|
1504
|
+
const resetPalette = (0, import_react8.useCallback)(() => {
|
|
1505
|
+
setPaletteState(clonePalette(resolvedDefaultPalette));
|
|
1506
|
+
notifyChange(resolvedDefaultPalette);
|
|
1507
|
+
}, [notifyChange, resolvedDefaultPalette]);
|
|
1508
|
+
const handleControlPaletteChange = (0, import_react8.useCallback)(
|
|
1509
|
+
(nextPalette) => {
|
|
1510
|
+
setPaletteState(clonePalette(nextPalette));
|
|
1511
|
+
notifyChange(nextPalette);
|
|
1512
|
+
},
|
|
1513
|
+
[notifyChange]
|
|
1514
|
+
);
|
|
1515
|
+
const controlConfig = (0, import_react8.useMemo)(
|
|
1516
|
+
() => ({
|
|
1517
|
+
...options.control ?? {},
|
|
1518
|
+
defaultPalette: resolvedDefaultPalette,
|
|
1519
|
+
onPaletteChange: handleControlPaletteChange
|
|
1520
|
+
}),
|
|
1521
|
+
[handleControlPaletteChange, options.control, resolvedDefaultPalette]
|
|
1522
|
+
);
|
|
1523
|
+
const hexColors = (0, import_react8.useMemo)(
|
|
1524
|
+
() => advancedPaletteToHexColors(palette, {
|
|
1525
|
+
sectionOrder: options.sectionOrder,
|
|
1526
|
+
fallbackPalette: resolvedFallbackPalette,
|
|
1527
|
+
defaultColor: options.defaultColor
|
|
1528
|
+
}),
|
|
1529
|
+
[
|
|
1530
|
+
options.defaultColor,
|
|
1531
|
+
options.sectionOrder,
|
|
1532
|
+
palette,
|
|
1533
|
+
resolvedFallbackPalette
|
|
1534
|
+
]
|
|
1535
|
+
);
|
|
1536
|
+
const paletteSignature = (0, import_react8.useMemo)(
|
|
1537
|
+
() => createPaletteSignature(palette),
|
|
1538
|
+
[palette]
|
|
1539
|
+
);
|
|
1540
|
+
const paletteGradient = (0, import_react8.useMemo)(
|
|
1541
|
+
() => computePaletteGradient(palette, options.gradientSteps),
|
|
1542
|
+
[options.gradientSteps, palette]
|
|
1543
|
+
);
|
|
1544
|
+
return {
|
|
1545
|
+
palette,
|
|
1546
|
+
hexColors,
|
|
1547
|
+
controlConfig,
|
|
1548
|
+
paletteGradient,
|
|
1549
|
+
setPalette,
|
|
1550
|
+
updatePalette,
|
|
1551
|
+
resetPalette,
|
|
1552
|
+
paletteSignature
|
|
1553
|
+
};
|
|
934
1554
|
};
|
|
935
|
-
var
|
|
936
|
-
|
|
937
|
-
// src/components/PlaygroundCanvas/PlaygroundCanvas.tsx
|
|
938
|
-
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
939
|
-
var PlaygroundCanvas = ({
|
|
940
|
-
children,
|
|
941
|
-
mediaProps
|
|
942
|
-
}) => {
|
|
943
|
-
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Playground, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Canvas_default, { mediaProps, children }) });
|
|
944
|
-
};
|
|
945
|
-
var PlaygroundCanvas_default = PlaygroundCanvas;
|
|
1555
|
+
var useDefaultAdvancedPaletteControls = () => useAdvancedPaletteControls({ defaultPalette: DEFAULT_ADVANCED_PALETTE });
|
|
946
1556
|
// Annotate the CommonJS export names for ESM import in node:
|
|
947
1557
|
0 && (module.exports = {
|
|
948
1558
|
Button,
|
|
949
|
-
CameraLogger,
|
|
950
|
-
Canvas,
|
|
951
1559
|
ControlsProvider,
|
|
1560
|
+
DEFAULT_ADVANCED_PALETTE,
|
|
1561
|
+
DEFAULT_HEX_PALETTE,
|
|
952
1562
|
Playground,
|
|
953
|
-
|
|
1563
|
+
advancedPaletteToHexColors,
|
|
1564
|
+
clonePalette,
|
|
1565
|
+
computePaletteGradient,
|
|
1566
|
+
createAdvancedPalette,
|
|
1567
|
+
createPaletteSignature,
|
|
1568
|
+
hexToPaletteValue,
|
|
1569
|
+
paletteValueToHex,
|
|
1570
|
+
useAdvancedPaletteControls,
|
|
954
1571
|
useControls,
|
|
1572
|
+
useDefaultAdvancedPaletteControls,
|
|
955
1573
|
useUrlSyncedControls
|
|
956
1574
|
});
|