@pixel-point/toolcraft 0.0.3 → 0.0.6
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/package.json +1 -1
- package/scripts/prepare-pack.mjs +5 -0
- package/src/generate.mjs +13 -0
- package/src/generate.test.mjs +6 -0
- package/templates/runtime/contracts/component-contracts.test.ts +86 -8
- package/templates/runtime/contracts/component-contracts.ts +36 -8
- package/templates/runtime/contracts/decision-contracts.ts +2 -2
- package/templates/runtime/export/export.test.ts +65 -0
- package/templates/runtime/export/export.ts +54 -1
- package/templates/runtime/react/canvas-shell.test.tsx +7 -7
- package/templates/runtime/react/controls-panel.test.tsx +323 -6
- package/templates/runtime/react/controls-panel.tsx +349 -24
- package/templates/runtime/react/settings-transfer.test.ts +6 -0
- package/templates/runtime/react/settings-transfer.ts +28 -2
- package/templates/runtime/react/timeline-panel.test.tsx +69 -0
- package/templates/runtime/react/timeline-panel.tsx +98 -10
- package/templates/runtime/react/toolbar-panel.test.tsx +6 -6
- package/templates/runtime/react/toolcraft-app.integration.test.tsx +2 -2
- package/templates/runtime/schema/canvas-aspect-ratio-presets.ts +50 -0
- package/templates/runtime/schema/define-toolcraft.test.ts +122 -2
- package/templates/runtime/schema/define-toolcraft.ts +197 -6
- package/templates/runtime/schema/keyframe-capability.test.ts +7 -0
- package/templates/runtime/schema/keyframe-capability.ts +2 -2
- package/templates/runtime/schema/runtime-targets.ts +6 -0
- package/templates/runtime/schema/types.ts +23 -1
- package/templates/runtime/state/canvas-zoom.ts +1 -1
- package/templates/runtime/state/create-template-state.test.ts +9 -3
- package/templates/runtime/state/reducer.test.ts +135 -2
- package/templates/runtime/state/reducer.ts +236 -12
- package/templates/runtime/state/types.ts +1 -0
- package/templates/starter/AGENTS.md +6 -4
- package/templates/starter/docs/toolcraft/README.md +1 -1
- package/templates/starter/docs/toolcraft/acceptance-testing.md +4 -2
- package/templates/starter/docs/toolcraft/assembly-workflow.md +13 -4
- package/templates/starter/docs/toolcraft/component-rules.md +24 -5
- package/templates/starter/docs/toolcraft/performance.md +5 -0
- package/templates/starter/docs/toolcraft/renderer-technique.md +1 -1
- package/templates/starter/docs/toolcraft/schema-reference.md +53 -9
- package/templates/starter/gitignore +36 -0
- package/templates/starter/src/app/starter-acceptance.test.ts +678 -21
- package/templates/starter/src/app/starter-acceptance.ts +357 -4
- package/templates/ui/components/control-layout/index.tsx +4 -4
- package/templates/ui/components/controls/file-drop/file-drop-control.tsx +101 -18
- package/templates/ui/components/controls/font-picker/font-picker-control.tsx +1 -1
- package/templates/ui/components/controls/range-slider/range-slider-value.ts +4 -1
- package/templates/ui/components/controls/slider/slider-value.ts +48 -5
- package/templates/ui/components/primitives/editable-slider-value-label.tsx +6 -1
|
@@ -226,7 +226,7 @@ describe("CanvasShell", () => {
|
|
|
226
226
|
await waitFor(() => {
|
|
227
227
|
expect(screen.getByTestId("canvas-offset").textContent).toBe("-8,12");
|
|
228
228
|
});
|
|
229
|
-
expect(screen.getByTestId("canvas-zoom").textContent).toBe("
|
|
229
|
+
expect(screen.getByTestId("canvas-zoom").textContent).toBe("100");
|
|
230
230
|
});
|
|
231
231
|
|
|
232
232
|
it("zooms the canvas world from a trackpad pinch around the pointer", async () => {
|
|
@@ -243,7 +243,7 @@ describe("CanvasShell", () => {
|
|
|
243
243
|
const world = canvas.querySelector("[data-toolcraft-canvas-world]") as HTMLElement;
|
|
244
244
|
mockCanvasRect(canvas);
|
|
245
245
|
|
|
246
|
-
expect(world.style.transform).toBe("translate(-50%, -50%) translate(0px, 0px) scale(
|
|
246
|
+
expect(world.style.transform).toBe("translate(-50%, -50%) translate(0px, 0px) scale(1)");
|
|
247
247
|
|
|
248
248
|
const wheelEvent = new WheelEvent("wheel", {
|
|
249
249
|
bubbles: true,
|
|
@@ -257,10 +257,10 @@ describe("CanvasShell", () => {
|
|
|
257
257
|
expect(canvas.dispatchEvent(wheelEvent)).toBe(false);
|
|
258
258
|
expect(wheelEvent.defaultPrevented).toBe(true);
|
|
259
259
|
await waitFor(() => {
|
|
260
|
-
expect(screen.getByTestId("canvas-zoom").textContent).toBe("
|
|
260
|
+
expect(screen.getByTestId("canvas-zoom").textContent).toBe("150");
|
|
261
261
|
});
|
|
262
|
-
expect(world.style.transform).toContain("translate(-
|
|
263
|
-
expect(world.style.transform).toContain("scale(1.
|
|
262
|
+
expect(world.style.transform).toContain("translate(-100px");
|
|
263
|
+
expect(world.style.transform).toContain("scale(1.5)");
|
|
264
264
|
});
|
|
265
265
|
|
|
266
266
|
it("prevents panel pinch gestures without moving the canvas viewport", async () => {
|
|
@@ -294,7 +294,7 @@ describe("CanvasShell", () => {
|
|
|
294
294
|
|
|
295
295
|
expect(overlay.dispatchEvent(wheelEvent)).toBe(false);
|
|
296
296
|
expect(wheelEvent.defaultPrevented).toBe(true);
|
|
297
|
-
expect(screen.getByTestId("canvas-zoom").textContent).toBe("
|
|
297
|
+
expect(screen.getByTestId("canvas-zoom").textContent).toBe("100");
|
|
298
298
|
expect(screen.getByTestId("canvas-offset").textContent).toBe("0,0");
|
|
299
299
|
expect(overlay.closest("[data-toolcraft-canvas-world]")).toBeNull();
|
|
300
300
|
|
|
@@ -306,7 +306,7 @@ describe("CanvasShell", () => {
|
|
|
306
306
|
|
|
307
307
|
expect(overlay.dispatchEvent(scrollEvent)).toBe(true);
|
|
308
308
|
expect(scrollEvent.defaultPrevented).toBe(false);
|
|
309
|
-
expect(screen.getByTestId("canvas-zoom").textContent).toBe("
|
|
309
|
+
expect(screen.getByTestId("canvas-zoom").textContent).toBe("100");
|
|
310
310
|
expect(screen.getByTestId("canvas-offset").textContent).toBe("0,0");
|
|
311
311
|
});
|
|
312
312
|
|
|
@@ -384,7 +384,7 @@ describe("ControlsPanel", () => {
|
|
|
384
384
|
expect(inlineGroup?.textContent).toContain("Snap Y");
|
|
385
385
|
});
|
|
386
386
|
|
|
387
|
-
it("renders
|
|
387
|
+
it("renders the managed background toggle plus unlabeled color row as equal-width inline columns", () => {
|
|
388
388
|
const schema = defineToolcraft({
|
|
389
389
|
canvas: { enabled: false },
|
|
390
390
|
panels: {
|
|
@@ -396,7 +396,7 @@ describe("ControlsPanel", () => {
|
|
|
396
396
|
defaultValue: true,
|
|
397
397
|
description:
|
|
398
398
|
"Controls PNG background transparency while preview and video keep the background.",
|
|
399
|
-
label:
|
|
399
|
+
label: "Include",
|
|
400
400
|
target: "export.includeBackground",
|
|
401
401
|
type: "switch",
|
|
402
402
|
},
|
|
@@ -428,12 +428,110 @@ describe("ControlsPanel", () => {
|
|
|
428
428
|
);
|
|
429
429
|
|
|
430
430
|
expect(screen.getAllByText("Background")).toHaveLength(1);
|
|
431
|
-
expect(screen.
|
|
431
|
+
expect(screen.getByText("Include")).toBeTruthy();
|
|
432
|
+
expect(screen.getByRole("switch")).toBeTruthy();
|
|
432
433
|
expect(toggleParameterGroup).toBeTruthy();
|
|
433
|
-
expect(toggleParameterGroup?.getAttribute("style")).toContain(
|
|
434
|
+
expect(toggleParameterGroup?.getAttribute("style")).toContain(
|
|
435
|
+
"repeat(2, minmax(0, 1fr))",
|
|
436
|
+
);
|
|
434
437
|
});
|
|
435
438
|
|
|
436
|
-
it("
|
|
439
|
+
it("suppresses obvious help icons in sequential color sections", () => {
|
|
440
|
+
const schema = defineToolcraft({
|
|
441
|
+
canvas: { enabled: false },
|
|
442
|
+
panels: {
|
|
443
|
+
controls: {
|
|
444
|
+
sections: [
|
|
445
|
+
{
|
|
446
|
+
controls: {
|
|
447
|
+
color1: {
|
|
448
|
+
defaultValue: { hex: "#DFFF1A" },
|
|
449
|
+
description: "Sets the first bead color.",
|
|
450
|
+
label: "Color 1",
|
|
451
|
+
target: "beads.color1",
|
|
452
|
+
type: "color",
|
|
453
|
+
},
|
|
454
|
+
color2: {
|
|
455
|
+
defaultValue: { hex: "#8CFF3A" },
|
|
456
|
+
description: "Sets the second bead color.",
|
|
457
|
+
label: "Color 2",
|
|
458
|
+
target: "beads.color2",
|
|
459
|
+
type: "color",
|
|
460
|
+
},
|
|
461
|
+
color3: {
|
|
462
|
+
defaultValue: { hex: "#F4FF5A" },
|
|
463
|
+
description: "Sets the third bead color.",
|
|
464
|
+
label: "Color 3",
|
|
465
|
+
target: "beads.color3",
|
|
466
|
+
type: "color",
|
|
467
|
+
},
|
|
468
|
+
color4: {
|
|
469
|
+
defaultValue: { hex: "#B8FF2E" },
|
|
470
|
+
description: "Sets the fourth bead color.",
|
|
471
|
+
label: "Color 4",
|
|
472
|
+
target: "beads.color4",
|
|
473
|
+
type: "color",
|
|
474
|
+
},
|
|
475
|
+
color5: {
|
|
476
|
+
defaultValue: { hex: "#ECFF68" },
|
|
477
|
+
description: "Sets the fifth bead color.",
|
|
478
|
+
label: "Color 5",
|
|
479
|
+
target: "beads.color5",
|
|
480
|
+
type: "color",
|
|
481
|
+
},
|
|
482
|
+
colorSpread: {
|
|
483
|
+
defaultValue: 34,
|
|
484
|
+
description:
|
|
485
|
+
"Controls how often beads use colors 2-5 instead of Color 1.",
|
|
486
|
+
label: "Spread",
|
|
487
|
+
max: 100,
|
|
488
|
+
min: 0,
|
|
489
|
+
target: "beads.colorSpread",
|
|
490
|
+
type: "slider",
|
|
491
|
+
unit: "%",
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
layoutGroups: [
|
|
495
|
+
{
|
|
496
|
+
columns: 2,
|
|
497
|
+
controls: ["color1", "color2"],
|
|
498
|
+
layout: "inline",
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
columns: 2,
|
|
502
|
+
controls: ["color3", "color4"],
|
|
503
|
+
layout: "inline",
|
|
504
|
+
},
|
|
505
|
+
],
|
|
506
|
+
title: "Bead Colors",
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
controls: {
|
|
510
|
+
includeBackground: {
|
|
511
|
+
defaultValue: true,
|
|
512
|
+
description:
|
|
513
|
+
"Controls PNG background transparency while preview and video keep the background.",
|
|
514
|
+
label: "Include",
|
|
515
|
+
target: "export.includeBackground",
|
|
516
|
+
type: "switch",
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
title: "Background",
|
|
520
|
+
},
|
|
521
|
+
],
|
|
522
|
+
title: "Controls",
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
renderControlsPanelWithSchema(schema);
|
|
528
|
+
|
|
529
|
+
expect(screen.queryByRole("button", { name: "Color 5 help" })).toBeNull();
|
|
530
|
+
expect(screen.queryByRole("button", { name: "Spread help" })).toBeNull();
|
|
531
|
+
expect(screen.getByRole("button", { name: "Include help" })).toBeTruthy();
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it("renders short visible toggle plus parameter rows as equal-width toggle-parameter rows", () => {
|
|
437
535
|
const schema = defineToolcraft({
|
|
438
536
|
canvas: { enabled: false },
|
|
439
537
|
panels: {
|
|
@@ -477,7 +575,9 @@ describe("ControlsPanel", () => {
|
|
|
477
575
|
expect(screen.getByText("Loop")).toBeTruthy();
|
|
478
576
|
expect(screen.getByText("Duration")).toBeTruthy();
|
|
479
577
|
expect(toggleParameterGroup).toBeTruthy();
|
|
480
|
-
expect(toggleParameterGroup?.getAttribute("style")).toContain(
|
|
578
|
+
expect(toggleParameterGroup?.getAttribute("style")).toContain(
|
|
579
|
+
"repeat(2, minmax(0, 1fr))",
|
|
580
|
+
);
|
|
481
581
|
});
|
|
482
582
|
|
|
483
583
|
it("renders compact select pairs inline with stacked full-width fields", () => {
|
|
@@ -1529,6 +1629,46 @@ describe("ControlsPanel", () => {
|
|
|
1529
1629
|
container.querySelector('[data-slot="slider"][data-variant="discrete"]'),
|
|
1530
1630
|
).toBeNull();
|
|
1531
1631
|
expect(container.querySelectorAll('[data-slot="slider-marker"]')).toHaveLength(0);
|
|
1632
|
+
expect(container.textContent).toContain("118 cols/s");
|
|
1633
|
+
});
|
|
1634
|
+
|
|
1635
|
+
it("keeps textual slider value labels non-editable", () => {
|
|
1636
|
+
const schema = defineToolcraft({
|
|
1637
|
+
canvas: { enabled: true },
|
|
1638
|
+
panels: {
|
|
1639
|
+
controls: {
|
|
1640
|
+
sections: [
|
|
1641
|
+
{
|
|
1642
|
+
controls: {
|
|
1643
|
+
letterSpacing: {
|
|
1644
|
+
defaultValue: 2,
|
|
1645
|
+
label: "Letter spacing",
|
|
1646
|
+
max: 4,
|
|
1647
|
+
min: 0,
|
|
1648
|
+
step: 1,
|
|
1649
|
+
target: "text.letterSpacing",
|
|
1650
|
+
type: "slider",
|
|
1651
|
+
valueLabel: "Normal",
|
|
1652
|
+
},
|
|
1653
|
+
},
|
|
1654
|
+
title: "Text",
|
|
1655
|
+
},
|
|
1656
|
+
],
|
|
1657
|
+
title: "Generation Controls",
|
|
1658
|
+
},
|
|
1659
|
+
},
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
renderControlsPanelWithSchema(schema);
|
|
1663
|
+
|
|
1664
|
+
const valueLabel = screen
|
|
1665
|
+
.getAllByText("Normal")
|
|
1666
|
+
.find((node) => node.getAttribute("aria-hidden") !== "true");
|
|
1667
|
+
|
|
1668
|
+
expect(screen.queryByRole("button", { name: "Edit Letter spacing value" })).toBeNull();
|
|
1669
|
+
expect(valueLabel).toBeTruthy();
|
|
1670
|
+
expect(valueLabel?.className).toContain("cursor-default");
|
|
1671
|
+
expect(valueLabel?.className).not.toContain("cursor-text");
|
|
1532
1672
|
});
|
|
1533
1673
|
|
|
1534
1674
|
it("keeps schema sliders stacked even when a layout group asks for an inline row", () => {
|
|
@@ -1584,6 +1724,8 @@ describe("ControlsPanel", () => {
|
|
|
1584
1724
|
expect(container.querySelector('[data-control-layout="inline"]')).toBeNull();
|
|
1585
1725
|
expect(fpsSlider).toBeTruthy();
|
|
1586
1726
|
expect(container.querySelectorAll('[data-slot="slider-marker"]').length).toBeGreaterThan(0);
|
|
1727
|
+
expect(container.textContent).toContain("17 fps");
|
|
1728
|
+
expect(container.textContent).toContain("3.7x");
|
|
1587
1729
|
});
|
|
1588
1730
|
|
|
1589
1731
|
it("passes schema disabled state into slider controls", () => {
|
|
@@ -2278,6 +2420,76 @@ describe("ControlsPanel", () => {
|
|
|
2278
2420
|
expect(screen.getByTestId("media-count").textContent).toBe("0");
|
|
2279
2421
|
});
|
|
2280
2422
|
|
|
2423
|
+
it("renders multi-image file uploads as an add-last thumbnail grid", () => {
|
|
2424
|
+
const schema = defineToolcraft({
|
|
2425
|
+
canvas: { enabled: true, upload: true },
|
|
2426
|
+
panels: {
|
|
2427
|
+
controls: {
|
|
2428
|
+
sections: [
|
|
2429
|
+
{
|
|
2430
|
+
controls: {
|
|
2431
|
+
source: {
|
|
2432
|
+
accept: "PNG, SVG",
|
|
2433
|
+
label: "Images",
|
|
2434
|
+
multiple: true,
|
|
2435
|
+
target: "input.sources",
|
|
2436
|
+
type: "fileDrop",
|
|
2437
|
+
},
|
|
2438
|
+
},
|
|
2439
|
+
layout: "standalone",
|
|
2440
|
+
title: "Input",
|
|
2441
|
+
},
|
|
2442
|
+
],
|
|
2443
|
+
title: "Generation Controls",
|
|
2444
|
+
},
|
|
2445
|
+
},
|
|
2446
|
+
});
|
|
2447
|
+
|
|
2448
|
+
const { container } = renderControlsPanelWithSchema(schema, undefined, {
|
|
2449
|
+
layers: [
|
|
2450
|
+
{ id: "layer-1", kind: "layer", name: "one", visible: true },
|
|
2451
|
+
{ id: "layer-2", kind: "layer", name: "two", visible: true },
|
|
2452
|
+
],
|
|
2453
|
+
mediaAssets: [
|
|
2454
|
+
{
|
|
2455
|
+
dataUrl:
|
|
2456
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E%3Crect width='96' height='96' fill='%23777'/%3E%3C/svg%3E",
|
|
2457
|
+
fileName: "one.svg",
|
|
2458
|
+
id: "media-1",
|
|
2459
|
+
layerId: "layer-1",
|
|
2460
|
+
mimeType: "image/svg+xml",
|
|
2461
|
+
position: { x: 0, y: 0 },
|
|
2462
|
+
size: { height: 96, unit: "px", width: 96 },
|
|
2463
|
+
},
|
|
2464
|
+
{
|
|
2465
|
+
dataUrl:
|
|
2466
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E%3Crect width='96' height='96' fill='%23999'/%3E%3C/svg%3E",
|
|
2467
|
+
fileName: "two.svg",
|
|
2468
|
+
id: "media-2",
|
|
2469
|
+
layerId: "layer-2",
|
|
2470
|
+
mimeType: "image/svg+xml",
|
|
2471
|
+
position: { x: 0, y: 0 },
|
|
2472
|
+
size: { height: 96, unit: "px", width: 96 },
|
|
2473
|
+
},
|
|
2474
|
+
],
|
|
2475
|
+
selectedLayerId: "layer-2",
|
|
2476
|
+
});
|
|
2477
|
+
|
|
2478
|
+
expect(screen.getByRole("button", { name: "Add image files" })).toBeTruthy();
|
|
2479
|
+
const previewGrid = container.querySelector('[data-slot="file-upload-preview-grid"]');
|
|
2480
|
+
expect(previewGrid?.className).toContain("grid-cols-4");
|
|
2481
|
+
expect(previewGrid?.lastElementChild?.getAttribute("data-slot")).toBe(
|
|
2482
|
+
"file-upload-add-preview",
|
|
2483
|
+
);
|
|
2484
|
+
expect(screen.getByRole("img", { name: "one.svg" })).toBeTruthy();
|
|
2485
|
+
expect(screen.getByRole("img", { name: "two.svg" })).toBeTruthy();
|
|
2486
|
+
|
|
2487
|
+
fireEvent.click(screen.getByRole("button", { name: "Remove two.svg" }));
|
|
2488
|
+
|
|
2489
|
+
expect(screen.getByTestId("media-count").textContent).toBe("1");
|
|
2490
|
+
expect(screen.queryByRole("img", { name: "two.svg" })).toBeNull();
|
|
2491
|
+
});
|
|
2492
|
+
|
|
2281
2493
|
it("keeps file upload deletion in the layers panel for multi-layer apps", () => {
|
|
2282
2494
|
const schema = defineToolcraft({
|
|
2283
2495
|
canvas: { enabled: true, upload: true },
|
|
@@ -3085,6 +3297,104 @@ describe("ControlsPanel", () => {
|
|
|
3085
3297
|
expect(screen.getByTestId("canvas-size").textContent).toBe("320,180");
|
|
3086
3298
|
});
|
|
3087
3299
|
|
|
3300
|
+
it("renders editable-output aspect ratio controls with the current canvas preset", () => {
|
|
3301
|
+
const schema = defineToolcraft({
|
|
3302
|
+
canvas: {
|
|
3303
|
+
enabled: true,
|
|
3304
|
+
size: { height: 1080, unit: "px", width: 1440 },
|
|
3305
|
+
sizing: { mode: "editable-output" },
|
|
3306
|
+
},
|
|
3307
|
+
panels: {
|
|
3308
|
+
controls: {
|
|
3309
|
+
sections: [],
|
|
3310
|
+
title: "Controls",
|
|
3311
|
+
},
|
|
3312
|
+
},
|
|
3313
|
+
});
|
|
3314
|
+
const { container } = renderControlsPanelWithSchema(schema);
|
|
3315
|
+
|
|
3316
|
+
expect(screen.getByTestId("canvas-size").textContent).toBe("1440,1080");
|
|
3317
|
+
|
|
3318
|
+
const aspectRatioControl = container.querySelector<HTMLElement>(
|
|
3319
|
+
'[data-slot="canvas-aspect-ratio-control"]',
|
|
3320
|
+
);
|
|
3321
|
+
|
|
3322
|
+
expect(screen.getByText("Aspect ratio")).toBeTruthy();
|
|
3323
|
+
expect(aspectRatioControl?.textContent).toContain("4:3");
|
|
3324
|
+
const values = JSON.parse(screen.getByTestId("values-json").textContent ?? "{}");
|
|
3325
|
+
|
|
3326
|
+
expect(values["canvas.aspectRatio"]).toEqual({
|
|
3327
|
+
height: 3,
|
|
3328
|
+
mode: "preset",
|
|
3329
|
+
value: "4:3",
|
|
3330
|
+
width: 4,
|
|
3331
|
+
});
|
|
3332
|
+
expect(values["canvas.size.width"]).toBe(1440);
|
|
3333
|
+
expect(values["canvas.size.height"]).toBe(1080);
|
|
3334
|
+
});
|
|
3335
|
+
|
|
3336
|
+
it("switches editable-output aspect ratio to custom after manual size edits", () => {
|
|
3337
|
+
const schema = defineToolcraft({
|
|
3338
|
+
canvas: {
|
|
3339
|
+
enabled: true,
|
|
3340
|
+
size: { height: 1080, unit: "px", width: 1920 },
|
|
3341
|
+
sizing: { mode: "editable-output" },
|
|
3342
|
+
},
|
|
3343
|
+
panels: {
|
|
3344
|
+
controls: {
|
|
3345
|
+
sections: [],
|
|
3346
|
+
title: "Controls",
|
|
3347
|
+
},
|
|
3348
|
+
},
|
|
3349
|
+
});
|
|
3350
|
+
|
|
3351
|
+
renderControlsPanelWithSchema(schema);
|
|
3352
|
+
|
|
3353
|
+
const widthInput = screen.getByDisplayValue("1920") as HTMLInputElement;
|
|
3354
|
+
|
|
3355
|
+
fireEvent.change(widthInput, { target: { value: "1600" } });
|
|
3356
|
+
fireEvent.blur(widthInput);
|
|
3357
|
+
|
|
3358
|
+
expect(screen.getByTestId("canvas-size").textContent).toBe("1600,1080");
|
|
3359
|
+
expect(screen.getAllByText("Custom...").length).toBeGreaterThan(0);
|
|
3360
|
+
expect(screen.getByDisplayValue("40")).toBeTruthy();
|
|
3361
|
+
expect(screen.getByDisplayValue("27")).toBeTruthy();
|
|
3362
|
+
expect(JSON.parse(screen.getByTestId("values-json").textContent ?? "{}")).toMatchObject({
|
|
3363
|
+
"canvas.aspectRatio": {
|
|
3364
|
+
height: 27,
|
|
3365
|
+
mode: "custom",
|
|
3366
|
+
value: "40:27",
|
|
3367
|
+
width: 40,
|
|
3368
|
+
},
|
|
3369
|
+
"canvas.size.height": 1080,
|
|
3370
|
+
"canvas.size.width": 1600,
|
|
3371
|
+
});
|
|
3372
|
+
});
|
|
3373
|
+
|
|
3374
|
+
it("renders raster render scale as the last technical setup control", () => {
|
|
3375
|
+
const schema = defineToolcraft({
|
|
3376
|
+
canvas: {
|
|
3377
|
+
enabled: true,
|
|
3378
|
+
renderScale: true,
|
|
3379
|
+
size: { height: 1080, unit: "px", width: 1920 },
|
|
3380
|
+
sizing: { mode: "editable-output" },
|
|
3381
|
+
},
|
|
3382
|
+
panels: {
|
|
3383
|
+
controls: {
|
|
3384
|
+
sections: [],
|
|
3385
|
+
title: "Controls",
|
|
3386
|
+
},
|
|
3387
|
+
},
|
|
3388
|
+
});
|
|
3389
|
+
renderControlsPanelWithSchema(schema);
|
|
3390
|
+
|
|
3391
|
+
const values = JSON.parse(screen.getByTestId("values-json").textContent ?? "{}");
|
|
3392
|
+
|
|
3393
|
+
expect(screen.getByText("Resolution scale")).toBeTruthy();
|
|
3394
|
+
expect(screen.getByText("2x")).toBeTruthy();
|
|
3395
|
+
expect(values["canvas.renderScale"]).toBe(2);
|
|
3396
|
+
});
|
|
3397
|
+
|
|
3088
3398
|
it("applies empty text input values while typing and reset restores defaults", () => {
|
|
3089
3399
|
renderControlsPanel();
|
|
3090
3400
|
|
|
@@ -3391,11 +3701,18 @@ describe("ControlsPanel", () => {
|
|
|
3391
3701
|
expect(firstSection?.querySelector("[data-control-list]")?.className).not.toContain("pb-5");
|
|
3392
3702
|
expect(firstInlineGroup?.textContent).toContain("Canvas width");
|
|
3393
3703
|
expect(firstInlineGroup?.textContent).toContain("Canvas height");
|
|
3704
|
+
expect(firstSection?.textContent).toContain("Aspect ratio");
|
|
3394
3705
|
expect(firstSection?.textContent).toContain("Canvas width");
|
|
3395
3706
|
expect(firstSection?.textContent).toContain("Canvas height");
|
|
3396
3707
|
expect(firstSection?.textContent).toContain("Export Settings");
|
|
3397
3708
|
expect(firstSection?.textContent).toContain("Import Settings");
|
|
3398
3709
|
expect((firstSection?.textContent ?? "").indexOf("Export Settings")).toBeLessThan(
|
|
3710
|
+
(firstSection?.textContent ?? "").indexOf("Import Settings"),
|
|
3711
|
+
);
|
|
3712
|
+
expect((firstSection?.textContent ?? "").indexOf("Import Settings")).toBeLessThan(
|
|
3713
|
+
(firstSection?.textContent ?? "").indexOf("Aspect ratio"),
|
|
3714
|
+
);
|
|
3715
|
+
expect((firstSection?.textContent ?? "").indexOf("Aspect ratio")).toBeLessThan(
|
|
3399
3716
|
(firstSection?.textContent ?? "").indexOf("Canvas width"),
|
|
3400
3717
|
);
|
|
3401
3718
|
expect(scrollContent?.textContent).toContain("Import Settings");
|