Skip to content

Closes 411 and 412 which adss the ability to import image into a scene editor - #493

Open
mayokunl wants to merge 7 commits into
mainfrom
mayors-branch
Open

mayokunl wants to merge 7 commits into
mainfrom
mayors-branch

Conversation

@mayokunl

Copy link
Copy Markdown
Collaborator

Pull Request Summary

Closes #411 and #412

Adds the ability to import an image into a Scene Editor scene and have it persist across reloads.

What: Users can now click "Import image…" (toolbar button or the one shown in the empty-scene placeholder) to add a PNG/JPEG/GIF/WEBP/SVG file to the current scene. The image renders on the canvas immediately, can be clicked to select/deselect (dashed outline), and is now saved to the scene so it's still there after a page reload.

Why: #411 covers letting a user get an image onto the canvas at all; #412 covers making that import actually durable — before this, an imported image only lived in browser memory (URL.createObjectURL) and vanished on reload, which failed the issue's own persistence requirement.

How:

  • Import UI added to boneset.html (toolbar button + empty-state button + hidden file input + inline error message area), styled in style.css.
  • templates/js/scenes.js reads the selected file(s) as a base64 data URL (FileReader.readAsDataURL), scales it to fit a max display dimension, and adds it to the in-memory scene for an immediate render.
  • The image (with a client-generated crypto.randomUUID() id) is then persisted via a new PATCH /api/scenes/:sceneId payload shape, { image: {...} }, independent of the existing { name } rename support. The server (boneset-api/scenes.js) validates and appends it, upserting by id so a retried/duplicate save can never create a duplicate image record.
  • Images are stored as base64 data:image/... strings directly in the scene JSON document (no new file/blob storage infrastructure needed) — validated server-side to be well-formed, correctly typed, and under a 2MB (pre-encoding) size cap, enforced both client-side (immediate feedback) and server-side (defense in depth). express.json()'s body limit was raised from the 100kb default to 6mb to accommodate this.
  • sceneCanvas.js's renderer already trusted data:image/ sources; its safe-source allowlist was extended to also accept blob: for the local-preview path.
  • Unsupported file types and oversized files are rejected with a clear inline message instead of failing silently; if the persistence PATCH itself fails, the image stays visible with a retry affordance rather than disappearing.

Testing: Added/extended unit and integration tests in boneset-api/scenes.test.js, boneset-api/server.test.js, templates/tests/scenes.test.js, and templates/tests/sceneCanvas.test.js covering: import success, unsupported-type/oversized-file rejection, selection toggling, idempotent persistence (no duplicate on repeated save), scene reload showing the persisted image, and save-failure/retry behavior. All suites pass (npm test). Also manually verified end-to-end against the running dev server (import → reload → re-open scene → image still present) and via direct API calls confirming the idempotency guarantee.

Screenshots

image

PR Checklist

image
  • Project builds and runs
  • Tests and linters pass
  • Any related documentation has been updated, including JSDoc comments or docstrings

Detailed Description

Design choices worth noting for reviewers:

  • *reuses the existing scene JSON storage abstraction (local file store in dev, Redis in production) with zero special-casing.
  • Append-one-image PATCH shape, not replace-the-whole-array: keeps request payload size proportional to one new image rather than growing with every image already on the scene.
  • Client-generated UUID + server-side upsert-by-id: makes save retries safe, directly satisfying the "no unintended duplicate image records" acceptance criterion.

@mayokunl mayokunl changed the title Mayors branch Closes 411 and 412 which adss the ability to import image into a scene edito Sep 28, 2026
@mayokunl mayokunl changed the title Closes 411 and 412 which adss the ability to import image into a scene edito Closes 411 and 412 which adss the ability to import image into a scene editor Sep 28, 2026

@Remex9 Remex9 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on the import flow: validation on both sides, retry-safe IDs, and good test coverage. Requesting changes for a few issues:

Major issues

  1. Large scenes break on Vercel. Vercel caps requests and responses at 4.5 MB. A scene with two 2 MB images (about 5.6 MB as base64) can no longer be opened, and the second import saves but reports "could not be saved". The 6mb JSON limit doesn't help on Vercel. Store image bytes separately (e.g., Vercel Blob) or cap each scene's total image size well under 4.5 MB.
  2. Renaming while an image is saving deletes the image. Each PATCH writes the whole scene back, so the last write wins. I reproduced it: a rename sent alongside an image save lost the image 20 out of 20 times. Save images atomically or under separate keys, or disable Rename while saves are pending.

Should fix

  • The scene list still says "0 images" after an import until the page reloads.
  • If the user switches scenes during an import, the image shows up in the wrong scene locally. Only push to state.active when its ID still matches sceneId.
  • Use "Closes #411, closes #412" so GitHub auto-closes both issues.

@mudabs

mudabs commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the thorough review. I agree with the requested changes.

The import flow meets the basic requirements for issues #411 and #412, but the scene-size limitation and concurrent-save issue could cause failed saves or data loss in production. Please address those two issues before I can merge.

Please also fix the stale image count and ensure images are only added to the scene that was active when the import started. The unrelated navigation and description-formatting changes should either be moved into separate PRs or clearly justified here.

Once these updates are pushed, I can retest the import, persistence, retry, and scene-switching flows.

@mayokunl

mayokunl commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

I've updated the fix !

@mudabs mudabs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Jest and lint checks are now passing, and the earlier image-size, concurrent-save, scene-count, and scene-switching concerns appear to have been addressed.

However, the overall CodeQL check is still failing with one high-severity alert:

Uncontrolled data used in path expression at boneset-api/scenes.js:126.

Although sceneId is validated as a UUID, CodeQL still detects user-controlled data flowing into the filesystem path used by the local scene store. Please update the path construction so the input is explicitly sanitized or otherwise proven to remain inside the scene storage directory. Do not suppress the alert unless the safety of the path handling is clearly demonstrated.

After the CodeQL check passes, please rerun the full test suite and request another review.

@mayokunl

mayokunl commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator Author

Hello Munashe, all tests are passing now, im requesting to merge this again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Small Rock] Import an Image into a Scene

3 participants