Skip to content

Add Panel Mapper Pro Phase 1: Electron shell scaffold - #1

Open
asaptrap99 wants to merge 7 commits into
alexdmiller:mainfrom
asaptrap99:claude/projection-mapping-app-6xk0c9
Open

asaptrap99 wants to merge 7 commits into
alexdmiller:mainfrom
asaptrap99:claude/projection-mapping-app-6xk0c9

Conversation

@asaptrap99

Copy link
Copy Markdown

New standalone Electron/React/TypeScript app under panel-mapper-pro/, separate from the existing Java/Processing Shape Mapper library at the repo root. Implements the Electron shell phase from the projection mapping app spec: control window + fullscreen output window, display enumeration/picker, IPC scaffold (typed contextBridge preload, main process handlers), and empty .pmproj project new/open/save via native dialogs. WebGL rendering pipeline, shape types, and layer system land in later phases.

claude added 7 commits August 4, 2026 19:30
New standalone Electron/React/TypeScript app under panel-mapper-pro/,
separate from the existing Java/Processing Shape Mapper library at the
repo root. Implements the Electron shell phase from the projection
mapping app spec: control window + fullscreen output window, display
enumeration/picker, IPC scaffold (typed contextBridge preload, main
process handlers), and empty .pmproj project new/open/save via native
dialogs. WebGL rendering pipeline, shape types, and layer system land in
later phases.
Ports the unit-square-to-quad projective mapping (Heckbert's closed-form
homography) into core/geometry/homography.ts, and builds a WebGL render
pipeline (Three.js) that evaluates the homography exactly at every vertex
of a fine internal grid so affine per-cell interpolation reproduces a true
projective warp without a custom shader. The same grid-warp primitive is
shared with the future mesh shape type.

Adds a Zustand store as the single source of truth for shapes, a
draggable Stage with numeric-fallback corner fields and arrow-key nudging
(Shift for fine-step), and live control-to-output IPC sync so the
projector output window mirrors the control window's WebGL scene. A
built-in checkerboard test pattern stands in for the content library,
which lands in a later phase.

Also fixes a real preload-loading bug hit during smoke testing: Electron's
default sandboxed preload environment cannot resolve local multi-file
require()s, so window.panelMapper was silently undefined at runtime. Fixed
by disabling the OS-level preload sandbox (contextIsolation remains the
security boundary) and standardizing electron/ on CommonJS instead of
mixing ESM/CJS module formats across main/preload.

Verified via Playwright driving the packaged Electron build under Xvfb:
zero console/page errors, quad renders and drags correctly with live
perspective warp.
Wires up the full multi-shape layer stack on top of the Phase 2 quad MVP:

- Layer list shows all shapes ordered by z (Photoshop-style, top of list =
  top of stack), with drag-and-drop reorder, per-shape visibility and lock
  toggles, and delete. Locked shapes reject drag/point edits in the Stage.
- Shape Inspector now actually wires opacity and blend mode (normal/add/
  screen) to the store instead of being read-only, plus a content-crop
  window (x/y/w/h fractions) so a shape can show a sub-region of its
  assigned content.
- Content Library: import images/video via a native file dialog, list
  imported sources, assign one to the selected shape.
- ShapeSceneRenderer now loads real per-shape textures (image via
  THREE.TextureLoader, video via THREE.VideoTexture) instead of always
  using the shared test pattern, with a cache keyed by content id.
- Media loads through a newly registered `pmfile://` protocol rather than
  raw `file://` URLs, since a page served from the Vite dev server (http)
  can't reliably load `file://` resources; the custom privileged scheme
  works consistently in both dev and packaged builds and normalizes
  Windows drive-letter paths in one shared helper
  (core/project/contentUrl.ts).

Verified via Playwright driving the packaged Electron build under Xvfb:
multiple overlapping quads composite and z-order correctly, opacity and
screen blend render correctly against the black stage background, and
per-shape visibility toggling hides/shows only the intended shape without
affecting others.
Adds the five remaining shape types on top of the quad-only MVP, each with
its own real math foundation module under core/geometry/:

- triangleWarp.ts: the affine map a single triangle always has (three point
  pairs fully determine it) — rendered by handing the 3 vertices straight
  to Three.js with corner UVs, since hardware barycentric interpolation
  reproduces the affine warp exactly with no subdivision needed.
- Polygon: fan-triangulated from the centroid, each fan triangle using the
  same affine/hardware-interpolation trick, with UVs from a canonical
  regular polygon inscribed in the crop rect.
- meshWarp.ts: per-cell bilinear interpolation (not per-cell homography) —
  bilinear reduces to a plain linear interpolation along any shared edge,
  so adjacent cells always agree exactly with no seams, by construction.
- ellipseWarp.ts: parametric unit-disk-to-ellipse map (center, independent
  x/y radii, rotation), fanned into flat triangles from the center so the
  radial UV mapping is exact per triangle.
- Line: a constant-width ribbon built by offsetting each polyline vertex
  along its local perpendicular, with along-length/across-width UVs.

Generalizes Stage.tsx (drag handles) and ShapeInspector.tsx (numeric
fields) across all point-array shape types (quad/triangle/polygon/mesh/
line), adds dedicated ellipse handles (center, and a radiusX handle that
doubles as a rotate+resize control, matching how corner-drag rotate
affordances work in other design tools), and polygon vertex add/remove.
ToolPalette now creates all six shape types via a generic addShape(type)
store action instead of the quad-only addQuad.

Fixes a real rendering bug caught during visual smoke testing: non-quad
shapes with user-drawn point order weren't guaranteed to wind
counter-clockwise in Three.js's coordinate space, so triangles/polygons
were silently backface-culled (invisible) despite their handles rendering
correctly. Fixed by setting side: THREE.DoubleSide on the shared shape
material.

Verified via Playwright driving the packaged Electron build under Xvfb:
each of the six shape types renders its warped test pattern correctly in
isolation, the ellipse's rotate+resize handle and a dragged mesh interior
point (bilinear bulge, zero seam at the mesh boundary) both behave
correctly, and no console/page errors occur across the whole flow.
Builds out the .pmproj file lifecycle beyond the basic new/open/save from
Phase 1:

- Content paths are relativized to the project file's directory on save
  (electron/pathUtils.ts) when the media lives underneath it, so a project
  folder stays portable and git-diffable; falls back to an absolute path
  otherwise. Resolved back to absolute for the running session on open.
- Relink missing media: opening a project checks every content source's
  resolved path on disk and reports which ones are missing
  (OpenProjectResult.missingContentIds). ContentLibrary surfaces a warning
  badge and a Relink button that opens a file picker and repoints just
  that content source.
- Autosave: a debounced (3s) background save to a dedicated crash-recovery
  slot in userData, independent of explicit Save. On launch, if an
  autosave is found the user is offered a restore prompt.
- Recent projects: a "Recent…" dropdown in the top bar backed by the
  existing recent-projects.json list, opening a project directly by path
  (electron: project:openPath) instead of requiring the native file
  dialog every time.

Verified via Playwright driving the packaged Electron build under Xvfb,
with the native save/open dialogs monkey-patched in the main process so
the flow could be scripted end-to-end: Save As writes a valid .pmproj,
New clears the canvas, Open restores the saved shapes, the recent-projects
dropdown lists the saved path, the autosave file appears after the
debounce window, content paths are stored relative when possible, and
deleting a referenced media file and reopening the project correctly
surfaces the missing-content badge with no console/page errors.
Undo/redo across all shape edits: every project-mutating store action now
funnels through a single applyProjectChange helper that debounces history
commits by 500ms, so a burst of rapid edits (dragging a point, scrubbing
an opacity slider, retyping a number) coalesces into one undo step instead
of one per intermediate event. Wired to Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z,
skipped while a text field/select has focus so native input undo and
typing aren't hijacked, plus Undo/Redo buttons in the top bar.

Adds a Delete/Backspace shortcut to remove the selected shape, and a "?"
keyboard-shortcuts overlay (ShortcutsOverlay.tsx) summarizing all of the
above plus the existing arrow-key nudge controls.

Expands the built-in test pattern library beyond the single hardcoded
checkerboard: grid, color bars, and a per-shape "ID card" pattern that
bakes the shape's own label into the texture (useful for telling shapes
apart once there are many). Patterns are selectable per-shape from the
Content dropdown via reserved "pattern:*" contentId values, resolved and
cached in ShapeSceneRenderer without needing fake ContentSource entries in
the project schema.

Verified via Playwright driving the packaged Electron build under Xvfb:
two adds + two undos empties the layer list, two redos restores both
shapes, Delete removes the selected shape and is itself undoable, the "?"
overlay opens and closes, and the shape-ID pattern renders the correct
per-shape label and color — all with zero console/page errors.
Gets electron-builder actually producing a working Windows installer:

- electron-builder.yml: disable publish/update-feed generation
  (publish: null) — this app has no auto-update and no networking by
  design (see spec non-goals), and electron-builder crashed trying to
  build update metadata without a configured publish provider.
- package.json: add an `author` field to silence electron-builder's
  missing-author warning.
- README: document that building the Windows target from Linux/macOS
  requires Wine (electron-builder shells out to `rcedit` via Wine to set
  the exe's icon/version metadata) plus the apt packages needed, and that
  building on Windows itself needs no extra setup.

Verified end-to-end in this Linux sandbox by installing wine + wine32:i386
and running `npm run package`: electron-builder downloads the real
win32-x64 Electron binary, produces "Panel Mapper Pro Setup 0.1.0.exe" (an
NSIS installer, confirmed via `file` as a genuine PE32 Windows executable)
alongside a win-unpacked/ build whose main executable is a genuine PE32+
x64 Electron binary with a proper app.asar resource bundle, and exits
cleanly with no errors or warnings.

This branch has not been deployed

No deployments
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.

2 participants