Commit Graph
8 Commits
Author SHA1 Message Date
kawaandClaude Opus 5 a6ec6c980a fix: audit src/js and add a frame-accurate export path
Two halves of one pass; TODO-FIXES.md lists every finding.

The audit fixed, among ~90 items: MP4 export dead-ending and locking the UI
behind a stuck "Downloading..." button; height keyframes storing the width;
shadow defaults and keyframes storing undefined because fabric's get() is not
a path getter; every letter animation writing to the last letter; keyframe
times drifting on each drag because data-time was layer-relative on expanded
rows; O(n^2 log n) playback, now indexed once per frame; and a save() that
rebuilt the record canvas on every edit, now debounced.

Exports also lost audio-layer sound outright: MediaRecorder records only the
first audio track, so every source now mixes through one AudioContext into one
destination.

The new half is src/js/render.js, an offline renderer. Real-time capture was
the root cause of dropped frames on heavy scenes and smeared video layers: a
bare currentTime assignment is async, so drawing straight after it captures the
previous frame. Each frame is now seeked, awaited on 'seeked', drawn, and
pushed through a VideoEncoder; audio is mixed in one OfflineAudioContext pass
and encoded to Opus. webm-writer2.js gained a second Opus track and lost three
bugs, including a MAX_CLUSTER_DURATION_MSEC of ~58 days that overflowed the
signed 16-bit block timecode past ~32s.

Real-time capture remains the fallback: the hand-rolled muxer's output is
decoded in a <video> element before being handed over, and any failure returns
null so record() falls back transparently.

test/webm-muxer.test.js parses the muxer output with an EBML reader in plain
Node. It caught the fixed-256-byte header overflow that inspection missed.

Not verified in a browser: node --check passes on every script and the muxer
test passes, but a running Chrome held the Playwright profile lock, so the
manual pass listed at the end of TODO-FIXES.md is still outstanding.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 12:58:51 +02:00
kawaandClaude Opus 5 337f38d58e feat: add systemd, nginx and Caddy deployment configs
Serve src/ directly from the web server with no Node process involved. The
Caddyfile is the shortest path to TLS, which every non-localhost deployment
needs: on a plain-HTTP LAN address Chromium drops WebCodecs and blocks
IndexedDB, so export slows to real-time capture and projects stop saving.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 12:58:07 +02:00
kawaandClaude Opus 5 7505ecdbe5 feat: add Electron desktop shell
electron/main.js starts the same static server the bare-metal target uses on
127.0.0.1 with a random port and points the window at it, rather than calling
loadFile(). Chromium exposes WebCodecs and IndexedDB only in a secure context:
over file:// the exporter would fall back to real-time MediaRecorder capture
and projects would stop saving, both silently.

The renderer runs sandboxed with no node integration, denies every permission
request, and hands http(s) navigations to the real browser.

PACKAGING.md documents all three distribution targets, including the NSIS
"Access denied" failure caused by an endpoint protection agent locking the
freshly written unsigned exe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 12:58:07 +02:00
kawaandClaude Opus 5 d501778ae5 build: add container image and ignore rules
Dockerfile builds a static-web-server image serving src/ with the third-party
assets vendored in a first stage, so the runtime layer carries no Node and no
shell tooling. WITH_FFMPEG=0 drops the 18.5 MB asm.js encoder for a smaller
image, at the cost of fetching it at export time.

.gitignore covers dist/, which the release scripts write on every run: without
it a fresh clone reports the build output as untracked and publish.ps1 warns
that the worktree is dirty on every build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 12:56:55 +02:00
kawaandClaude Opus 5 f2b9f28d14 build: add Gitea release scripts and npm entry points
scripts/build-release.ps1 packages the desktop installers; scripts/publish.ps1
pushes the container image to git.azuze.fr and attaches the installers to the
Gitea release for the same tag. The split keeps a local build free of any
credential, and lets a failed upload be retried with -BinariesOnly
-NoBinaryBuild without paying for the build again.

Two details the plain `npm run dist:*` path gets wrong for a release:

- package.json's global artifactName resolves NSIS and portable to the same
  file name, so one silently overwrites the other. The build script passes
  distinct -c.nsis.artifactName / -c.portable.artifactName.
- Artifact names now carry the tag, so publish.ps1 can glob exactly one tag's
  files and never ship a stale one. electron-builder's update metadata
  (.blockmap, latest*.yml, the NSIS .7z payload) is swept out of dist/ so it
  cannot match that glob and end up attached to the release.

The tag defaults to v<package.json version> rather than git describe, because
electron-builder stamps package.json into the app: a SHA-based tag would name
an installer whose About box disagrees with it.

Also brings scripts/server.cjs, vendor.mjs and make-icon.cjs under version
control, which the release scripts and the Docker build both call.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 12:56:11 +02:00
kawaandClaude Haiku 4.5 f8f863e7e2 Fix recording and playback issues
- Fix download filename handling in converter and export functions
- Make updateRecordCanvas return Promise to handle async loadFromJSON correctly
- Add setCoords() call in setObjectValue to fix object selection after updates
- Update recorder initialization and animation frame handling

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-11 16:20:25 +02:00
kawa 794995fbb5 Merge remote branch, keep local files 2026-08-11 15:59:30 +02:00
kawa b7790f89ff Initial commit 2026-08-11 15:58:47 +02:00