From 560398fefb88f5dcf759628c1982b6b8d7f39b7f Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 16 May 2026 20:45:42 +0100 Subject: [PATCH] more cleaning up --- assets/fonts/open-sans-v34-latin-regular.woff | Bin 20712 -> 0 bytes e2e/app.spec.ts | 190 ++----- index.html | 39 +- package-lock.json | 114 ---- package.json | 15 +- playwright.config.ts | 2 +- public/manifest.webmanifest | 11 +- public/robots.txt | 2 - src/analytics.ts | 8 - src/audio/garden-audio-config.ts | 16 +- src/audio/garden-audio-gesture-state.ts | 59 +-- src/audio/garden-audio-music.ts | 22 +- src/audio/garden-audio-types.ts | 9 - src/audio/garden-audio.test.ts | 26 +- src/audio/garden-audio.ts | 29 +- src/audio/generative-piano.test.ts | 99 +--- src/audio/generative-piano.ts | 498 +++++------------- src/audio/piano-sampler.test.ts | 34 +- src/audio/piano-sampler.ts | 2 +- src/audio/piano-samples.ts | 9 +- src/config.ts | 31 +- src/config/agent-budget.ts | 1 - src/config/color-interactions.ts | 16 +- src/config/default-settings.ts | 17 + src/config/runtime-controls.ts | 133 +++++ src/config/runtime-settings.ts | 191 ------- src/config/types.ts | 105 ++-- src/config/vibe-presets.ts | 22 +- src/game-loop/agent-population.test.ts | 42 +- src/game-loop/agent-population.ts | 59 +-- src/game-loop/export-4k-renderer.ts | 5 +- src/game-loop/export-4k.test.ts | 2 - src/game-loop/export-4k.ts | 16 +- src/game-loop/frame-performance.test.ts | 51 +- src/game-loop/frame-performance.ts | 73 +-- src/game-loop/game-loop-ping-pong.test.ts | 7 +- src/game-loop/game-loop-resources.ts | 27 +- src/game-loop/game-loop-settings.ts | 7 - src/game-loop/game-loop.ts | 97 +--- src/game-loop/intro-prompt.ts | 12 +- src/game-loop/pointer-input.test.ts | 6 - src/game-loop/pointer-input.ts | 11 +- src/game-loop/simulation-frame.ts | 9 +- src/game-loop/simulation-textures.ts | 27 +- .../toolbar-contrast-monitor.test.ts | 43 +- src/game-loop/toolbar-contrast-monitor.ts | 53 +- src/index.scss | 2 +- src/index.ts | 104 ++-- src/page/collapsible-panel-animator.ts | 27 +- src/page/config-pane.ts | 23 +- src/page/full-screen-handler.ts | 21 +- src/page/menu-hider.ts | 5 - .../agent-generation/agent-compaction.wgsl | 12 +- .../agent-generation/agent-counting.wgsl | 35 -- .../agent-first-generation.wgsl | 37 -- .../agent-generation-pipeline.test.ts | 40 +- .../agent-generation-pipeline.ts | 197 +------ .../agents/agent-generation/agent-resize.wgsl | 5 +- .../agent-generation/agent-schema.test.ts | 15 +- .../agents/agent-generation/agent-schema.wgsl | 6 +- .../agent-generation/generation-counts.ts | 4 - src/pipelines/agents/agent-pipeline.ts | 4 +- src/pipelines/agents/agent.wgsl | 5 +- src/pipelines/brush/brush-pipeline.ts | 28 +- src/pipelines/brush/brush-settings.ts | 3 - src/pipelines/brush/brush.wgsl | 15 - src/pipelines/common-state/common-state.ts | 16 +- src/pipelines/copy/copy-pipeline.ts | 5 +- src/pipelines/diffusion/diffuse.wgsl | 4 +- .../diffusion/diffusion-pipeline.test.ts | 6 + src/pipelines/diffusion/diffusion-pipeline.ts | 13 +- src/pipelines/diffusion/diffusion-settings.ts | 1 - src/pipelines/eraser/eraser-agent-pipeline.ts | 42 +- src/pipelines/eraser/eraser-agent.wgsl | 5 +- .../eraser/eraser-texture-pipeline.ts | 15 +- src/pipelines/eraser/eraser-texture.wgsl | 2 +- src/pipelines/render/render-pipeline.ts | 20 +- src/pipelines/render/render.wgsl | 9 +- src/pipelines/wgsl-uniform-layout.test.ts | 9 +- src/settings.ts | 28 +- src/style/_app-shell.scss | 21 - src/style/_control-dock.scss | 4 +- src/style/_garden-prompt.scss | 6 +- src/style/_loading.scss | 13 +- src/style/_motion.scss | 4 - src/style/_panels.scss | 90 ++-- src/style/_toolbar.scss | 26 +- src/style/common.scss | 12 +- src/style/fonts.scss | 6 +- src/style/vars.scss | 1 - src/utils/delta-time-calculator.ts | 25 +- src/utils/dom.ts | 10 +- src/utils/error-handler.ts | 2 - src/utils/exponential-decay.ts | 9 - .../graphics/get-workgroup-count.test.ts | 19 + src/utils/graphics/get-workgroup-count.ts | 17 + .../graphics/get-workgroup-counts.test.ts | 34 -- src/utils/graphics/get-workgroup-counts.ts | 39 -- src/utils/graphics/initialize-gpu.ts | 7 - src/utils/graphics/resizable-texture.ts | 4 +- src/utils/hsl.test.ts | 42 -- src/utils/hsl.ts | 35 -- src/utils/math.test.ts | 36 -- src/utils/mix.ts | 1 - src/utils/random.test.ts | 41 -- src/utils/random.ts | 23 - src/utils/rgb.ts | 3 - src/utils/sleep.ts | 3 - src/vibes.test.ts | 36 +- tsconfig.json | 1 - 110 files changed, 933 insertions(+), 2647 deletions(-) delete mode 100644 assets/fonts/open-sans-v34-latin-regular.woff delete mode 100644 public/robots.txt delete mode 100644 src/config/agent-budget.ts create mode 100644 src/config/default-settings.ts create mode 100644 src/config/runtime-controls.ts delete mode 100644 src/config/runtime-settings.ts delete mode 100644 src/game-loop/game-loop-settings.ts delete mode 100644 src/pipelines/agents/agent-generation/agent-counting.wgsl delete mode 100644 src/pipelines/agents/agent-generation/agent-first-generation.wgsl delete mode 100644 src/pipelines/agents/agent-generation/generation-counts.ts delete mode 100644 src/utils/exponential-decay.ts create mode 100644 src/utils/graphics/get-workgroup-count.test.ts create mode 100644 src/utils/graphics/get-workgroup-count.ts delete mode 100644 src/utils/graphics/get-workgroup-counts.test.ts delete mode 100644 src/utils/graphics/get-workgroup-counts.ts delete mode 100644 src/utils/hsl.test.ts delete mode 100644 src/utils/hsl.ts delete mode 100644 src/utils/mix.ts delete mode 100644 src/utils/random.test.ts delete mode 100644 src/utils/random.ts delete mode 100644 src/utils/rgb.ts delete mode 100644 src/utils/sleep.ts diff --git a/assets/fonts/open-sans-v34-latin-regular.woff b/assets/fonts/open-sans-v34-latin-regular.woff deleted file mode 100644 index b0836266265a09aaf1e3f4300fafcbdecf7680fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20712 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 diff --git a/e2e/app.spec.ts b/e2e/app.spec.ts index f0381bf..c641649 100644 --- a/e2e/app.spec.ts +++ b/e2e/app.spec.ts @@ -1,7 +1,5 @@ import { expect, test, type Page } from '@playwright/test'; -type WebGpuFailureMode = 'adapter-null' | 'adapter-rejects' | 'device-rejects'; - const disableWebGpu = async (page: Page) => { await page.addInitScript(() => { Object.defineProperty(navigator, 'gpu', { @@ -11,54 +9,6 @@ const disableWebGpu = async (page: Page) => { }); }; -const emulateWebGpuFailure = async (page: Page, mode: WebGpuFailureMode) => { - await page.addInitScript((failureMode) => { - const limits = { - maxBufferSize: 256 * 1024 * 1024, - maxComputeWorkgroupsPerDimension: 65_535, - maxStorageBufferBindingSize: 128 * 1024 * 1024, - }; - const adapter = { - features: new Set(), - info: { - architecture: 'test', - description: 'Playwright fake adapter', - device: 'test-device', - isFallbackAdapter: false, - subgroupMaxSize: 0, - subgroupMinSize: 0, - vendor: 'test-vendor', - }, - limits, - requestDevice: async () => { - if (failureMode === 'device-rejects') { - throw new Error('Playwright fake device failure'); - } - - return {}; - }, - }; - - Object.defineProperty(navigator, 'gpu', { - configurable: true, - value: { - getPreferredCanvasFormat: () => 'rgba8unorm', - requestAdapter: async () => { - if (failureMode === 'adapter-null') { - return null; - } - - if (failureMode === 'adapter-rejects') { - throw new Error('Playwright fake adapter failure'); - } - - return adapter; - }, - }, - }); - }, mode); -}; - const getFirstSwatchColor = (page: Page) => page .locator('.color-swatch') @@ -71,6 +21,18 @@ const getGardenBackground = (page: Page) => ); test('loads the app shell and WebGPU fallback in Chromium', async ({ page }) => { + const browserFailures: Array = []; + + page.on('requestfailed', (request) => { + const failure = request.failure(); + browserFailures.push(`${request.method()} ${request.url()} ${failure?.errorText}`); + }); + page.on('response', (response) => { + if (response.status() >= 400) { + browserFailures.push(`${response.status()} ${response.url()}`); + } + }); + await disableWebGpu(page); await page.goto('/'); @@ -85,6 +47,7 @@ test('loads the app shell and WebGPU fallback in Chromium', async ({ page }) => await page.getByRole('button', { name: 'About' }).click(); await expect(page.getByRole('heading', { name: 'Fleeting Garden' })).toBeVisible(); + expect(browserFailures).toEqual([]); }); test('keeps fallback controls interactive and accessible', async ({ page }) => { @@ -106,13 +69,21 @@ test('keeps fallback controls interactive and accessible', async ({ page }) => { await expect(aboutPanel).toHaveAttribute('aria-hidden', 'true'); await expect(aboutPanel).toHaveAttribute('inert', ''); - const settingsButton = page.getByRole('button', { name: 'Show config overlay' }); + const settingsButton = page.locator('button.settings'); + await expect(settingsButton).toHaveAttribute('aria-label', 'Show config overlay'); + await expect(settingsButton).toHaveAttribute('aria-expanded', 'false'); await settingsButton.click(); - await expect(page.getByRole('button', { name: 'Hide config overlay' })).toHaveAttribute( - 'aria-expanded', - 'true' - ); + await expect(settingsButton).toHaveAttribute('aria-expanded', 'true'); + await expect(settingsButton).toHaveAttribute('aria-label', 'Hide config overlay'); await expect(page.locator('.config-pane')).toBeVisible(); + await expect(page.locator('.config-pane')).toContainText('Runtime'); + await expect(page.locator('.color-reaction-matrix')).toBeVisible(); + + const colorReaction = page.getByLabel('Color 1 agents reacting to color 2'); + await colorReaction.selectOption('-1'); + await expect(colorReaction).toHaveValue('-1'); + await settingsButton.click(); + await expect(settingsButton).toHaveAttribute('aria-expanded', 'false'); const soundButton = page.locator('button.sound'); await expect(soundButton).toHaveAttribute('aria-pressed', 'false'); @@ -146,98 +117,35 @@ test('keeps fallback controls interactive and accessible', async ({ page }) => { await expect(page.locator('.mirror-segment-control')).toHaveClass(/active/); }); -( - [ - { - expectedCode: 'webgpu-adapter-unavailable', - expectedMessage: - 'WebGPU is available, but this browser could not provide a compatible GPU adapter.', - mode: 'adapter-null', - }, - { - expectedCode: 'webgpu-adapter-unavailable', - expectedMessage: 'Could not request a WebGPU adapter.', - mode: 'adapter-rejects', - }, - { - expectedCode: 'webgpu-device-unavailable', - expectedMessage: 'Could not create a WebGPU device for this adapter.', - mode: 'device-rejects', - }, - ] satisfies Array<{ - expectedCode: string; - expectedMessage: string; - mode: WebGpuFailureMode; - }> -).forEach(({ expectedCode, expectedMessage, mode }) => { - test(`reports ${mode} startup failures without leaving the shell loading`, async ({ - page, - }) => { - await emulateWebGpuFailure(page, mode); - - await page.goto('/'); - - await expect(page.locator('body')).not.toHaveClass(/is-loading/); - await expect(page.getByRole('toolbar', { name: 'Garden toolbar' })).toBeVisible(); - await expect(page.getByRole('alert')).toContainText(expectedMessage); - await expect(page.getByRole('alert')).toContainText(expectedCode); - }); -}); - -test('serves the production bundle without missing browser assets', async ({ page }) => { - const browserFailures: Array = []; - - page.on('requestfailed', (request) => { - const failure = request.failure(); - browserFailures.push(`${request.method()} ${request.url()} ${failure?.errorText}`); - }); - page.on('response', (response) => { - if (response.status() >= 400) { - browserFailures.push(`${response.status()} ${response.url()}`); - } - }); - +test('keeps the fallback shell usable on mobile', async ({ page }) => { + await page.setViewportSize({ height: 844, width: 390 }); await disableWebGpu(page); + await page.goto('/'); await expect(page.locator('body')).not.toHaveClass(/is-loading/); - expect(browserFailures).toEqual([]); -}); + const canvasBox = await page + .getByRole('img', { name: 'Interactive generative garden canvas' }) + .boundingBox(); + expect(canvasBox?.width).toBeGreaterThan(0); + expect(canvasBox?.height).toBeGreaterThan(0); + await expect(page.getByRole('toolbar', { name: 'Garden toolbar' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'About' })).toBeVisible(); + await expect(page.getByRole('alert')).toContainText('Fleeting Garden needs WebGPU'); -[ - { height: 720, name: 'desktop', width: 1280 }, - { height: 844, name: 'mobile', width: 390 }, -].forEach(({ height, name, width }) => { - test(`keeps the fallback shell usable on ${name}`, async ({ page }) => { - await page.setViewportSize({ height, width }); - await disableWebGpu(page); + const aboutButtonReceivesPointer = await page + .getByRole('button', { name: 'About' }) + .evaluate((button) => { + const rect = button.getBoundingClientRect(); + const target = document.elementFromPoint( + rect.left + rect.width / 2, + rect.top + rect.height / 2 + ); - await page.goto('/'); - await expect(page.locator('body')).not.toHaveClass(/is-loading/); + return button === target || button.contains(target); + }); - const canvasBox = await page - .getByRole('img', { name: 'Interactive generative garden canvas' }) - .boundingBox(); - expect(canvasBox?.width).toBeGreaterThan(0); - expect(canvasBox?.height).toBeGreaterThan(0); - await expect(page.getByRole('toolbar', { name: 'Garden toolbar' })).toBeVisible(); - await expect(page.getByRole('button', { name: 'About' })).toBeVisible(); - await expect(page.getByRole('alert')).toContainText('Fleeting Garden needs WebGPU'); - - const aboutButtonReceivesPointer = await page - .getByRole('button', { name: 'About' }) - .evaluate((button) => { - const rect = button.getBoundingClientRect(); - const target = document.elementFromPoint( - rect.left + rect.width / 2, - rect.top + rect.height / 2 - ); - - return button === target || button.contains(target); - }); - - expect(aboutButtonReceivesPointer).toBe(true); - }); + expect(aboutButtonReceivesPointer).toBe(true); }); test('hides the bottom dock after the cursor leaves fullscreen controls', async ({ @@ -305,5 +213,5 @@ test('keeps the bottom dock visible in mobile fullscreen', async ({ page }) => { await page.waitForTimeout(5200); await expect(page.locator('aside.control-dock')).not.toHaveClass(/menu-hidden/); - await expect(page.getByRole('button', { name: 'Show config overlay' })).toBeVisible(); + await expect(page.getByRole('button', { name: 'About' })).toBeVisible(); }); diff --git a/index.html b/index.html index 1957196..0a66ab3 100644 --- a/index.html +++ b/index.html @@ -22,10 +22,9 @@ - - - - + + + Fleeting Garden @@ -41,13 +40,13 @@

Fleeting Garden is a pointer-driven WebGPU drawing canvas. Drag or touch the scene - to paint coloured paths, then use the toolbar to change colours, erase, adjust the - config overlay, export, restart, or open more information. + to paint coloured paths, then use the toolbar to change colours, erase, export, + adjust the config overlay, restart, or open more information.

-
+
+ >
@@ -72,7 +69,15 @@