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>
- Unsaved-changes indicator: a small accent dot appears on the save
button whenever the scene has changed since the last save/load.
Save/load/new all mark the project clean on next tick.
- Cmd/Ctrl+S saves the project (reuses the same flow as the button;
prompts for a name on first save). Tooltip now mentions the
shortcut.
- Fullscreen preview plays to the OUT marker (or scene.duration when
no OUT is set) instead of looping at the last keyframe. Dropped
the runtime's effectiveEnd helper; RUNTIME_VERSION 13 → 14.
- "Still frame" keyframes no longer disable easing. A held kf's
easing still drives the segment INTO it from the previous kf, so
you can ease into a pause. Only the first kf disables easing
(nothing precedes it). Dropped `timeline.holdDisabledHint`.
- Settings dialog: hint text 11px → 13px (and dropped italic) so
the inline help is readable; section headings 10px → 13px.
- Canvas stage shadow: `shadowCfg` always emits explicit values plus
a `shadowEnabled` flag instead of returning `{}`. Konva's default
shadowOpacity is 1, so bailing with `{}` could leave stale shadow
attrs on the shape. Preview and editor now gate on the same
condition deterministically.
- Sidebar empty-state copy: "No clips" → "No clips yet" to match
"No buttons yet".
Regression tests: runtime rangeOut fallback is scene.duration (not
last kf); sidebar empty-states both contain "yet"; the
holdDisabledHint key is gone; saveProject tooltips mention the
shortcut.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Headline:
- Group colour cascades to every non-overridden child (layer or clip
track). New `colorOverridden` flag on Layer / ClipTrack / ScriptTrack
/ TrackGroup; setTrackColor on a non-group pins the track, clearing
re-inherits from the parent group. moveTrackIntoGroup also inherits.
normalizeScene migrates legacy scenes (existing colour ⇒ overridden).
- Keyframes inside a button-asset timeline snap to the 4 state slots
(normal/hover/pressed/disabled). upsertKeyframe / setKeyframeTime /
duplicateKeyframe all enforce it; colliding drags refuse the move,
duplicates walk to the nearest free slot.
- Backspace / Delete on a selected layer removes it (respecting the
layer's own lock and any enclosing group lock).
- "Still frame" allowed on the last keyframe: the layer stays on stage
past its final kf. Timeline draws a solid dim tail line from the
last kf to the end, and single-keyframe tracks now show a full-span
line so on-stage/off-stage reads at a glance. Height stays 3px.
- Project name moved next to the logo, turns into a rename button on
hover (pencil fades in). Rename syncs the stored record when the
project is already persisted.
- Shadow + blur keyframes animate in the preview and exported runtime:
the runtime's lerpProps used to drop them, so every preview rendered
shadow=0 between keyframes. RUNTIME_VERSION 11 → 13 (covers the
button-mask + shadow fixes). Held-last-kf also taught to the runtime.
- Button masks render across every path: edit-mode mask-preview,
nested buttons on the main stage, sidebar thumbnails, fullscreen
preview + exports. drawButtonFrame new in render.ts.
Smaller fixes:
- Dragging layer A on the stage with a stale selection on layer B used
to move B and snap A back. Now the shift-selected branch only runs
when the dragged layer is itself in the selection.
- Clip-track row was missing its icon — PhFilmStrip was referenced but
never imported. Added, plus dropped the bespoke `gap` on clip / script
name rows so their icon+label spacing matches layer rows.
- Clip-art drawer pre-lays-out its 400px grid behind `overflow: hidden`
so slide-in reveals a finished grid instead of reflowing auto-fill
columns over 220ms.
- Timeline clip-track dropzone hides until the library has a clip; its
"drop a clip here" hint stays centred in the visible ruler area.
- Keyframe context menu: check-mark and icons share a single 12px column
so the ✓ on an easing option lines up with PhPauseCircle / PhCopy /
PhTrash on the rows below. "Still frame"'s active indicator moved to
the trailing-check slot.
- Button editor's mask-preview now shows the button's own stage instead
of leaking through to the main scene. drawButtonFrame drives it.
Regression tests cover all of the above: group-color cascade, pin,
clear-re-inherit, migration, button-kf snap and collision rules,
backspace-deletes-selected-layer (incl. lock guards + editable-target
guard), still-frame-past-end visibility, clipart drawer rigid width,
mask rendering in drawButtonFrame / drawSceneFrame, shadow + blur
interpolation in both canvas and runtime paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Buttons used to ignore `isMask` on every render path — the mask
shape drew as normal content instead of clipping the button's
other layers. Every entry point now routes button children through
`drawMaskedGroup`:
- `drawLayers` nested-button branch
- new `drawButtonFrame` (used by the mask-preview overlay in
button-edit mode so the preview reflects the button's own
stage instead of the main scene)
- `renderButtonThumbnail` so sidebar previews match the stage
- exported-runtime button branch mirrors the split; bumped
`RUNTIME_VERSION` to '11' so cached players reload
Also:
- Timeline clip-track dropzone hides until the library has at
least one clip, and the hint stays centred in the visible
ruler area instead of drifting with horizontal scroll.
- Regression tests pin positive/negative button masks in
`drawButtonFrame` and verify `drawSceneFrame` applies
button-internal masks when the button sits on the main stage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Button assets
- Re-introduce button-asset type with 4 content states (normal/hover/
pressed/disabled) stored as keyframes at BUTTON_STATE_TIMES, plus a
separate hit-area editor mode.
- useButtonRuntime composable for per-instance state/disabled flags on
the main stage.
- Sidebar library lists button assets with live 80×80 thumbnails;
renderButtonThumbnail now accepts an images map so image-kind layers
inside a button render instead of producing a fully-transparent PNG.
- Compact (no-thumbnail) mode aligns button rows to the library-row
height.
Scope footer
- Button-edit mode now uses the same sidebar scope-footer pattern as
clip-edit (back button to "Main timeline" + active scope chip);
removed the inline exit button from the timeline scrub-bar.
Timeline
- Track-name column default bumped to 260px to match the assets sidebar
above it (storage key bumped to :v2 so stale values reset).
- scrub-bar z-index raised above sticky track-name columns so rows
scroll under it instead of over.
- scrub-handle flips z-index based on horizontal scroll: above
name-spacer when the playhead is in view (no half-clip at t=0),
below when the playhead has scrolled off the left edge (so the
toggle row cleanly covers it).
- name-spacer extends ±3/1px to fully cover the obscured handle.
- scrub-handle head pokes 3px above the ruler with rounded top corners
via clip-path path().
Track context menu
- Added missing i18n keys (renameScriptTrack / deleteScriptTrack /
renameScriptTrackDialog.{title,confirm}).
- Rename items show a visible pencil icon; empty .check slot removed
so items line up flush-left; toggle items place their active ✓ on
the right (kf-menu-trailing-check).
Dev HUD
- Test progress bar flips to --danger as soon as the first failure is
recorded, instead of only at the end of the run.
Regression tests
- renderButtonThumbnail: null on empty button, renders shape layers,
accepts custom images map.
- i18n: renameScriptTrack / deleteScriptTrack / dialog keys resolve.
- scrub-handle-obscured logic (visible at t=0, hidden when playhead is
scrolled off-screen, still visible when playhead is far to the
right of the scroll offset).
- Updated button state-scrubber expectations to match
BUTTON_STATE_TIMES (normal=1, hover=2, pressed=3, disabled=4) and
that hit-mode parks at normal=1 rather than 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>