- New .ruw file format: a STORE-only zip wrapping project.json +
an assets/ folder. packProject extracts every data-URL asset
(images, audio, background video) into its own file and
rewrites the in-JSON src fields to relative paths;
unpackProject rehydrates back to data URLs on read. Zero
dependencies — we ship a minimal in-house zip codec.
- ExportDialog surfaces the new format as a top-row
"RUWIO project (.ruw)" option that round-trips through the
welcome-dialog file picker. Legacy .json imports still work
via byte-sniffing the PK header in readAnyProjectFile. Dead
App.vue `exportProjectFile` helper removed.
- Frame-based exports (GIF, APNG, APNG preview) take an optional
targetFps override so a 60fps project doesn't balloon the file
— APNG / GIF size scales ~linearly with frame count. The
ExportDialog grows a second input next to target size;
defaults inherit the scene's fps and reset on each dialog
open.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Frame-based exports (GIF, APNG, APNG-preview) accept an optional
target output size. ExportDialog exposes it as a single width
input above the three image-export rows; height is derived live
from the scene's aspect ratio so the user can't squash the output.
The preview HTML mirrors the target dims so a downsized export
previews at its real pixel size.
renderFrames takes targetWidth / targetHeight, sizes its canvas
accordingly and scales the ctx once; drawSceneFrame's own
save/restore preserves the transform across frames, including the
top-level-mask offscreen compositing path.
Target width on dialog open is inherited from the current
scene.canvas — watched via modelValue so resizing the stage and
reopening the dialog picks up the new size (instead of the stale
value from whenever ExportDialog first instantiated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each clip instance now renders an invisible frame rect sized to the
union of its layers' footprints across every keyframe. The
transformer wraps that frame so its handles stay on the clip's own
outer constraints instead of chasing the currently-visible inner
shape, and the rect doubles as a hit region so empty-playhead
frames remain selectable. A plain click on a timeline clip-block
also selects the instance (drag still takes over past the 2px
dead-zone).
Clip tracks pick up the same editor-hide affordance as layer
tracks: new eye button on the track-name, setClipTrackEditorHidden
action, and filters in the Konva stackItems + render.ts so
hidden-track instances vanish in the editor. ExportDialog's
skip-hidden opt-in now also strips hidden clip tracks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New frame-by-frame export pipeline in src/lib/frameExport.ts: loads
scene images, walks rangeIn..rangeOut at scene.fps, drawSceneFrame
to an offscreen canvas, awaits a per-frame callback, yields every
4 frames to keep progress responsive, honours AbortSignal.
- Animated GIF via gifenc (per-frame quantize/palette, ~5 KB lib).
- Animated PNG via UPNG.js + pako (lossless RGBA, alpha-aware, ~30 KB).
- drawSceneFrame now clearRects first and skips the background fill
when scene.canvas.background === 'transparent' — prerequisite for
the alpha to reach the APNG output.
- DocumentDrawer: new checkerboard toggle next to the colour picker
flips the stage between its last-picked colour and transparent;
disabled swatch + 'Transparent' hex label make the state obvious.
- ExportDialog: three new option rows — Animated GIF, Animated PNG,
and an APNG preview page that bundles the rendered APNG into a
standalone HTML with a checkerboard backdrop + dark/light toggles
for verifying transparency at a glance.
- Tests: renderFrames frame count + abort, GIF header magic, APNG
acTL presence, transparent round-trip (decoded alpha == 0 on
un-painted pixels), preview HTML structure, and the two UI tests
covering ExportDialog's new rows and the progress-view transition.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tooltips
- New v-tooltip directive + TooltipHost floating panel replace the
native title attribute across 95+ sites (topbar, timeline, sidebar,
properties, stage overlays, fullscreen preview, export dialog,
clipart drawer, modals). Styled dark card with a configurable
label + optional help line for inline documentation; screen
readers still get an aria-label when the panel is disabled.
- Settings → Interface toggle (`ruwio:tooltipsEnabled`) hides the
visual panel for users who know the app. IconButton now routes
its title prop through v-tooltip so every existing call site is
tooltip-aware without churn.
Editor-hide + export filter
- New `Layer.editorHidden` flag, eye toggle per layer row, skipped
by the Konva stage and the mask-preview canvas path but honoured
by exports and fullscreen preview (runtime untouched).
- Export dialog gets a "Skip hidden tracks" switch that strips
editor-hidden layers from every format (scene JS, runtime,
example HTML, MP4, WebM) on opt-in. Disabled when the scene has
no hidden layers.
Button editor
- `+` buttons appear on empty state slots in button-timeline rows
and duplicate the nearest keyframe (prev by preference, next
otherwise) onto that slot. New `duplicateKeyframeAt` store
action carries props, easing, hold, and bezier handles across.
Stage / interaction
- Plain left-click on the empty content view pans the viewport,
but only when the pointer isn't over a Konva shape — transformer
handles, vertex dots, and selection boxes that extend past the
canvas edge keep their drag.
Settings reshuffle
- Timeline tab merged into Document (Duration + FPS sit under a
new "Timing" sub-heading). New Interface tab gathers three
global prefs: Show tooltips, Show locked tracks, Asset
thumbnails — driven by shared composables so the existing
toolbar toggles stay in sync. Interface-tab CSS defensively
resets the scoped label flex-column + uppercase rule that was
making the toggle row look like an accident.
Project identity + UX polish
- Favicon wired up (moved to public/favicon.ico, linked from
index.html); scripts/pack.sh packs the minimum Vue project
(src, index.html, configs, public) into a tarball with the
right version in the filename, and `npm run pack` drives it.
- About dialog now credits Torsten Hoeltge.
Test infra
- Progress bar turns red mid-run on the first failure: runAll
gets an onResult callback that TestOverlay uses to push each
result into the reactive array incrementally.
- mountComponent in the test harness registers v-tooltip on the
isolated app instance so IconButton's aria-label contract
actually fires under test.
Regression tests
- duplicateKeyframeAt: copy fidelity (props, easing, hold),
collision refusal, unknown-id guards, separated-track targeting.
- Shared composables: useTooltipPref and useAssetThumbsPref both
return the same ref across calls and persist to localStorage.
- Tooltip contract: IconButton ships `aria-label` (not title);
live-DOM smoke tests match on aria-label selectors now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Canvas stage: plain left-click in the empty content area (outside
the canvas rectangle) now starts a pan. Ctrl+left-click still pans
anywhere, middle-click unchanged. Only pure left-click qualifies
(any modifier leaves the old routing intact).
- Export rebrand Animatr → RUWIO for everything user-visible:
- Default filenames: projekt.animatr.json → projekt.ruwio.json;
animatr-runtime.js → ruwio-runtime.js; App's export fallback
base animatr → ruwio.
- Generated comment headers in runtime/scene/bundled scripts.
- Console warn prefix in the runtime and the scene store.
- Example HTML title, heading, and embed-usage snippet.
- parseProject error message.
- ExportDialog copy.
- RUNTIME_VERSION 14 → 15 so cached runtimes reload.
- On-disk magic stays `animatr-project` and window.AnimatrPlayer /
__animatrPending / localStorage & IndexedDB keys are untouched so
old project files and previously-exported HTML keep working.
Regression tests: legacy magic still parses; parseProject error
mentions RUWIO and not Animatr; runtime/scene/bundled scripts and
example HTML all carry the new brand; console prefix is [ruwio].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>