- New src/lib/fonts.ts: curated set (Inter, Roboto, Lora, Playfair
Display, Bebas Neue, Source Code Pro, Permanent Marker) shipped as
local .woff2 in public/fonts/. loadFonts() registers them via
FontFace at app start; fontsReady ref re-triggers Konva text configs
once fonts resolve so the first paint isn't stuck on the fallback.
- TextAsset gets optional fontFamily (id; undefined = system sans).
PropertiesPanel adds a font dropdown next to font size.
- All five render sites read the family: CanvasStage textConfig +
clipTextConfig (Konva), drawLayer + drawMaskShape (canvas2d),
exportPlayer engine (uses an embedded FONT_FAMILIES map and fontFam
helper; runtime version 27 → 28).
- Example HTML export now lists used bundled fonts and prints the
@font-face CSS the user has to paste alongside the .woff2 files —
fonts themselves are NOT bundled into the export to keep it small.
- Default text fill changed from yellow #f5d76e to black #000000 in
both the main sidebar and the SVG-scope add-text path.
- 8 new tests under "text font picker — model + render + export".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Document drawer slides out from under the topbar (no tab, no hover
peek); opens via project-name button, closes on outside-click or ESC.
Layout: inputs row + divider + preset row, presets user-managed via
popup and persisted in localStorage.
- PWA manifest + minimal service worker so Chrome installs RUWIO as a
standalone app — suppresses the native fullscreen exit overlay.
- Clips and audio clips get right-click context menus (Duplicate /
Delete) via new `duplicateClipInstance` / `duplicateAudioClip` store
actions.
- Clip moves cascade non-overlap in both directions; `setAudioClipPosition`
now pushes backward as well as forward. New `setClipInstancePosition`
gives clip instances the same behaviour.
- Library drags carry timeline-shaped ghost previews (audio pill, clip
pill, button thumb) instead of the library row; clip lane renders a
dashed drop-target ghost and rejects window-overlap drops.
- Scene / Clipart drawer tabs disappear in fullscreen; the 24 px they
occupied is reclaimed for the stage. Play button sits on the exact
horizontal center via a 3-column grid in the transport cluster.
- Tests: context menu + push-cascade + duplicate action coverage for
both clip kinds; DocumentDrawer suite rewritten for the slide-out
model.
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>