Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion web/src/composition-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ export function buildCompositionPanel(
const seedHint = document.createElement("p");
seedHint.className = "section-hint seed-hint";
seedHint.textContent =
"Same seed → same shape from Randomize. To share an exact shape, copy its ID or URL instead.";
"Randomize rolls a fresh seed each time. Type a seed and press Enter to re-run it. " +
"To share an exact shape, copy its ID or URL instead.";
panel.appendChild(seedHint);

const primitives = document.createElement("div");
Expand Down Expand Up @@ -159,7 +160,18 @@ export function buildCompositionPanel(
);
});

// Randomize always rolls a fresh seed, so repeated clicks keep producing new
// shapes. To re-run a specific seed, type it and press Enter in the field.
randomizeButton.addEventListener("click", () => {
seedInput.value = randomSeed();
opts.onRandomize();
});

seedInput.addEventListener("keydown", (event) => {
if (event.key !== "Enter") {
return;
}
event.preventDefault();
if (seedInput.value.trim().length === 0) {
seedInput.value = randomSeed();
}
Expand Down
37 changes: 30 additions & 7 deletions web/test/composition-panel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,47 @@ describe("buildCompositionPanel — primitive toggles", () => {
});

describe("buildCompositionPanel — Randomize", () => {
it("auto-fills a blank seed and calls onRandomize", () => {
it("rolls a fresh seed and calls onRandomize on every click", () => {
const onRandomize = vi.fn();
const { container, handle } = build(onRandomize);
container.querySelector<HTMLButtonElement>(".randomize-button")?.click();
expect(handle.seedValue().length).toBeGreaterThan(0);
const button = container.querySelector<HTMLButtonElement>(".randomize-button");

button?.click();
const first = handle.seedValue();
expect(first.length).toBeGreaterThan(0);

button?.click();
const second = handle.seedValue();

expect(second).not.toBe(first);
expect(onRandomize).toHaveBeenCalledTimes(2);
});

it("re-runs a typed seed on Enter without overwriting it", () => {
const onRandomize = vi.fn();
const { container } = build(onRandomize);
const seed = container.querySelector<HTMLInputElement>('input[name="seed"]');
if (seed) {
seed.value = "pinecone";
seed.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
}
expect(seed?.value).toBe("pinecone");
expect(onRandomize).toHaveBeenCalledOnce();
});

it("feeds a decodable, deterministic id when wired to generateFilteredShapeId", () => {
it("feeds a decodable, deterministic id for a fixed seed (Enter re-run)", () => {
const ids: string[] = [];
const { container, handle } = build(() => {
ids.push(
generateFilteredShapeId(handle.seedValue(), handle.gridSize(), handle.allowedTypes()),
);
});
(container.querySelector('input[name="seed"]') as HTMLInputElement).value = "wallpaper";
container.querySelector<HTMLButtonElement>(".randomize-button")?.click();
container.querySelector<HTMLButtonElement>(".randomize-button")?.click();
const seed = container.querySelector<HTMLInputElement>('input[name="seed"]');
if (seed) {
seed.value = "wallpaper";
seed.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
seed.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
}
expect(ids[0]).toBe(ids[1]);
expect(() => decodeShapeId(ids[0] as string)).not.toThrow();
});
Expand Down
Loading