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
54 changes: 51 additions & 3 deletions .github/workflows/ci-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ permissions:
contents: read

jobs:
build-test:
unit-tests-node:
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -34,11 +34,59 @@ jobs:
- name: Install
run: npm ci

- name: Unit Tests
run: npm run test:unit
- name: Unit Tests (Node)
run: npm run test:unit:node

unit-tests-jsdom:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
cache: npm

- name: Install
run: npm ci

- name: Unit Tests (jsdom)
run: npm run test:unit:jsdom

storybook-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
cache: npm

- name: Install
run: npm ci

- name: Storybook Tests
run: npm run test:stories

build-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
cache: npm

- name: Install
run: npm ci

- name: Build
run: npm run build
325 changes: 325 additions & 0 deletions .plans/demo-pack-path-assets-plan-2026-06-25.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
# Demo Pack Path Assets Plan — 2026-06-25

## Summary
Make `Assets Dock -> + Add -> From demo pack` feel near-instant by importing Demo Pack items as stable local asset references instead of embedding every file as a base64 data URL during editor import.

This plan preserves three required constraints:

- Published games must still ship real asset bytes, not editor-only references.
- Image thumbnails in the Assets `Images` tab must keep working.
- [`mainwindow.png`](../res/images/mainwindow.png) must remain in `res/images` because it is used by the root [`README.md`](../README.md).

It also changes Demo Pack file placement:

- Keep `res/images/mainwindow.png` in place.
- Move Demo Pack content into a new project-root `assets/` folder.

## Problem
Current Demo Pack import is slow because the editor does all of the following for every asset:

- Loads the asset URL.
- Reads the response into a `Blob`.
- Converts the bytes into a base64 data URL.
- For images, reads dimensions before dispatching.
- Dispatches one asset action per item.
- Persists the whole project record after each asset import.

That turns “add Demo Pack” into repeated fetch/encode/save work instead of a cheap metadata operation.

## Goals

- Demo Pack import should feel close to instant.
- The existing user-facing workflow should stay the same:
- `Assets Dock -> + Add -> From demo pack`
- Imported items appear immediately in the same asset tabs.
- Thumbnails remain visible for images.
- Drag/drop, assignment, and scene usage continue to work without extra steps.
- Publish/cloud save must still materialize referenced local assets into real hosted assets.
- Demo Pack references must use stable project-relative paths, not transient hashed bundler URLs.

## Non-Goals

- Do not move `mainwindow.png`.
- Do not redesign the Assets Dock workflow.
- Do not add a second “Demo Pack import mode” in the UI.
- Do not make publish depend on the local `assets/` folder existing on the end-user website.

## Proposed Asset Layout

- `assets/demo-pack/images/*`
- `assets/demo-pack/audio/*`
- `assets/demo-pack/fonts/*`
- `res/images/mainwindow.png` stays unchanged.

## Proposed Data Model Strategy

Use `AssetFileSource.kind = 'path'` for Demo Pack assets imported into the editor.

Example stored source:

```ts
source: {
kind: 'path',
path: 'assets/demo-pack/images/player.png',
originalName: 'player.png',
mimeType: 'image/png',
}
```

Important rule:

- Store stable project-relative paths such as `assets/demo-pack/images/foo.png`.
- Do not store Vite `?url` output or hashed build asset URLs in project state.

## Why This Preserves UX

### 1) Import speed
Demo Pack import becomes metadata insertion instead of byte copying:

- no fetch per file
- no blob conversion
- no base64 encoding
- no image decode during import if dimensions come from a manifest

### 2) Thumbnails
The editor already supports inline preview for `path` sources. Demo Pack image rows can continue to render thumbnails using the resolved path URL.

### 3) Runtime/editor loading
The editor/runtime asset loaders already understand `path` sources. Demo Pack items can be resolved to URLs at load time instead of being pre-embedded in project state.

### 4) Publish
Publish must treat local `path` assets as uploadable material, not as final URLs. Before cloud save / publish output generation, the pipeline should read those local files and convert them into real hosted assets.

## Implementation Plan

### Phase 1 — Move Demo Pack files and add a manifest
Create a single source of truth for Demo Pack assets under `assets/demo-pack/`.

Tasks:

- Move Demo Pack files from `res/images`, `res/audio`, and `res/fonts` into:
- `assets/demo-pack/images`
- `assets/demo-pack/audio`
- `assets/demo-pack/fonts`
- Leave `res/images/mainwindow.png` untouched.
- Add a small manifest module for Demo Pack assets containing:
- relative path
- kind (`image` / `audio` / `font`)
- original filename
- mime type
- for images: width and height

Notes:

- The manifest avoids per-image metadata decoding during import.
- The manifest can be generated by script or maintained manually, but generated is safer long-term.

### Phase 2 — Replace embedded Demo Pack import with path-backed import
Change `From demo pack` to dispatch path-backed assets instead of embedded file assets.

Tasks:

- Replace the current `readUrlAsDataUrl` flow for Demo Pack import.
- Add reducer actions for ensuring path-backed image/audio/font assets if missing.
- Use the Demo Pack manifest to populate:
- `assetId`
- `source.kind = 'path'`
- `source.path`
- `originalName`
- `mimeType`
- image `width` / `height`

Expected result:

- Demo Pack import becomes a cheap loop over metadata entries.

### Phase 3 — Batch the import into one state change
The current per-asset dispatch pattern should be replaced with a bulk action.

Tasks:

- Add a bulk Demo Pack import action or a generic bulk asset upsert action.
- Ensure undo/redo treats the import as one logical step.
- Persist the project once after the batch rather than once per asset.

Expected result:

- Lower persistence cost.
- Cleaner history behavior.
- Better responsiveness for large Demo Packs.

### Phase 4 — Resolve local paths consistently in editor/runtime
Make sure project-relative asset paths resolve reliably in all local editor contexts.

Tasks:

- Add or reuse a helper that converts project-relative asset paths like `assets/demo-pack/images/foo.png` into usable browser URLs.
- Update Demo Pack thumbnail rendering, font loading, and scene/runtime loading to use the same resolution path.
- Keep path resolution stable whether the app runs from `/`, a subpath, or a preview server.

Important:

- This resolution layer is where bundler-specific URL mechanics should live.
- Project state should remain bundler-agnostic.

### Phase 5 — Materialize `path` assets during cloud save / publish
This is the key publish-preservation phase.

Tasks:

- Extend the cloud/publish preparation pipeline so `path` assets are uploaded just like embedded assets.
- Read the referenced local file bytes during prepare-for-cloud-save.
- Convert the resulting source to `kind: 'cloud'` for the saved/published project payload.
- Apply this to:
- images
- sprite sheets
- fonts
- audio

Important behavior:

- Editor project state may stay `kind: 'path'` locally.
- The transient project prepared for publish/cloud save becomes `kind: 'cloud'`.
- Published websites therefore receive real hosted asset bytes, not editor-local references.

### Phase 6 — Keep thumbnails fast and reliable
Thumbnails must remain visible in the Assets `Images` tab.

Tasks:

- Keep thumbnail generation path-based for Demo Pack images.
- Prefer manifest width/height metadata over runtime image decode where possible.
- Confirm thumbnail toggle behavior is unchanged.

## TDD / Verification Plan

### Unit tests

- Demo Pack manifest helper tests:
- classifies all entries correctly
- preserves stable relative paths
- includes width/height for images
- Reducer tests:
- ensure path-backed image assets
- ensure path-backed audio assets
- ensure path-backed font assets
- bulk import action is idempotent
- re-import does not duplicate existing Demo Pack assets
- Cloud/publish preparation tests:
- `embedded` assets still upload correctly
- `path` assets are read and uploaded correctly
- mixed `embedded` + `path` projects produce cloud-backed sources
- Path resolution tests:
- project-relative asset paths resolve correctly under configured base URL

### Editor/integration tests

- Assets Dock Demo Pack import adds expected rows without data URL embedding.
- Image thumbnails appear for path-backed Demo Pack images.
- Dragging imported Demo Pack assets onto the canvas still creates usable entities.
- Fonts and audio remain assignable after path-backed import.

### E2E
Because this is a GUI/editor workflow change, run Chromium smoke E2E locally before calling the code change complete.

Suggested coverage:

- Import Demo Pack.
- Verify images appear quickly in Assets Dock.
- Verify at least one image thumbnail renders.
- Drag one imported image to canvas and confirm entity creation.
- Assign one imported audio asset to scene music.
- Publish-path test coverage should be added at the lowest stable layer available; if true E2E publish is too heavy locally, cover materialization with integration tests and state that explicitly.

## Key Technical Decisions

### Decision 1 — Stable relative paths in state
Use `assets/demo-pack/...` in project state, not hashed build URLs.

Reason:

- stable across rebuilds
- portable within the project
- clearer publish-time materialization contract

### Decision 2 — Publish materialization, not runtime website references
Treat local `path` assets as an editor/storage optimization only.

Reason:

- satisfies the requirement that published games ship real assets
- avoids broken websites when local files are unavailable remotely

### Decision 3 — One bulk import action
Prefer a batch import over N individual dispatches.

Reason:

- lower save overhead
- better undo behavior
- simpler performance model

## Risks / Open Questions

### 1) How should local file bytes be read for `path` assets during publish?
Likely options:

- client-side fetch of the resolved local asset URL, then upload
- direct file-system read in an Electron-capable path if available

Preferred initial approach:

- fetch via resolved local URL inside the existing browser-side/cloud-save path, as long as it is stable and testable

### 2) Should all `path` assets be publish-materialized, or only Demo Pack ones?
Recommendation:

- materialize all local `path` assets during publish for consistency

Reason:

- avoids split behavior
- future-proofs “From device as reference” if added later

### 3) Should Demo Pack import remain reversible as one undo step?
Recommendation:

- yes, one undo step

### 4) Do any existing tests assume Demo Pack assets are embedded?
Likely yes in reducer/UI tests around asset source shape.

Plan:

- update tests to assert path-backed local storage behavior
- add separate publish-prep tests to verify real-byte materialization

## Acceptance Criteria

- Demo Pack assets live under `assets/demo-pack/**`.
- `res/images/mainwindow.png` remains in place.
- `From demo pack` import stores path-backed assets, not base64-embedded data URLs.
- Demo Pack import is one logical batch operation.
- Image thumbnails remain visible in the Assets `Images` tab.
- Editor/runtime usage of imported Demo Pack assets still works unchanged from the user’s perspective.
- Cloud save / publish converts local path-backed assets into real hosted/published assets.
- No published game depends on editor-local `assets/demo-pack/**` paths at runtime.

## Suggested File Touches

- `src/editor/AssetsDock.tsx`
- `src/editor/demoPackAssets.ts` or a new manifest module
- `src/editor/EditorStore.tsx`
- `src/cloud/projectCloudAssets.ts`
- `src/cloud/assetUrls.ts`
- `src/model/editorConfig.ts` or a new path resolver helper
- tests for reducer, manifest, and cloud materialization

## Recommended Implementation Order

1. Move Demo Pack assets to `assets/demo-pack/**` and add manifest coverage.
2. Add reducer support for path-backed ensured assets.
3. Convert Demo Pack import to bulk path-backed import.
4. Wire path resolution for thumbnails/editor/runtime.
5. Extend cloud/publish prep to materialize path-backed assets into hosted assets.
6. Run unit/integration coverage, then Chromium smoke E2E for the GUI change.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
Loading
Loading