From 1d84c34c155d705461e1424d83576729b2c293f5 Mon Sep 17 00:00:00 2001 From: th Date: Tue, 21 Apr 2026 23:57:35 +0200 Subject: [PATCH] 0.8.3: custom tooltips, editor-hide, pack script, settings reshuffle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .gitignore | 1 + index.html | 1 + package.json | 5 +- public/favicon.ico | Bin 0 -> 34642 bytes scripts/pack.sh | 40 ++++++ src/App.vue | 30 +++-- src/components/AboutDialog.vue | 9 ++ src/components/ActionEditor.vue | 6 +- src/components/AssetSidebar.vue | 50 ++++---- src/components/CanvasStage.vue | 54 +++++--- src/components/ClipartDrawer.vue | 6 +- src/components/DocumentSettings.vue | 151 ++++++++++++++++++++-- src/components/ExportDialog.vue | 136 +++++++++++++++++++- src/components/FullscreenPreview.vue | 6 +- src/components/PropertiesPanel.vue | 14 +-- src/components/Timeline.vue | 167 +++++++++++++++++++++---- src/components/TooltipHost.vue | 132 ++++++++++++++++++++ src/components/WelcomeDialog.vue | 2 +- src/components/ui/IconButton.vue | 14 ++- src/components/ui/Modal.vue | 2 +- src/composables/useAssetThumbsPref.ts | 28 +++++ src/composables/useTooltipPref.ts | 25 ++++ src/devtools/TestOverlay.vue | 14 ++- src/devtools/testRunner.ts | 16 ++- src/devtools/tests.ts | 122 +++++++++++++++++- src/directives/tooltip.ts | 173 ++++++++++++++++++++++++++ src/i18n/messages.ts | 27 +++- src/lib/render.ts | 6 + src/main.ts | 2 + src/stores/scene.ts | 37 ++++++ src/types/scene.ts | 9 ++ src/version.ts | 2 +- todo | 26 ++-- 33 files changed, 1185 insertions(+), 128 deletions(-) create mode 100644 public/favicon.ico create mode 100755 scripts/pack.sh create mode 100644 src/components/TooltipHost.vue create mode 100644 src/composables/useAssetThumbsPref.ts create mode 100644 src/composables/useTooltipPref.ts create mode 100644 src/directives/tooltip.ts diff --git a/.gitignore b/.gitignore index acd4d89..0593bc3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules dist dist-ssr +dist-archive *.local .vite diff --git a/index.html b/index.html index 686d998..66c89ee 100644 --- a/index.html +++ b/index.html @@ -2,6 +2,7 @@ + RUWIO diff --git a/package.json b/package.json index 5d32ba0..b09b768 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "name": "animatr", "private": true, - "version": "0.8.2", + "version": "0.8.3", "type": "module", "scripts": { "dev": "vite", "build": "vue-tsc --noEmit && vite build", "preview": "vite preview", - "type-check": "vue-tsc --noEmit" + "type-check": "vue-tsc --noEmit", + "pack": "bash scripts/pack.sh" }, "dependencies": { "@phosphor-icons/vue": "^2.2.1", diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..97d4cbffb2136cbcbdd37a13ca42708e0a045b5d GIT binary patch literal 34642 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 p{C`.tar.gz +# cd ruwio- +# npm install +# npm run dev # or: npm run build +# +# Deliberately excluded: node_modules, dist, the render server, +# docker-compose, logo working files, personal notes, screenshots, +# .claude/, .git/. +set -euo pipefail + +cd "$(dirname "$0")/.." + +VERSION=$(node -p "require('./package.json').version") +NAME="ruwio-${VERSION}" +OUT_DIR="dist-archive" +ARCHIVE="${OUT_DIR}/${NAME}.tar.gz" + +mkdir -p "$OUT_DIR" +rm -f "$ARCHIVE" + +# Explicit allowlist — everything else stays out. If you add a new +# top-level file the Vue app depends on (e.g. a separate env file or +# a PostCSS config), extend this list. +tar --transform "s,^,${NAME}/," -czf "$ARCHIVE" \ + index.html \ + package.json \ + package-lock.json \ + tsconfig.json \ + vite.config.ts \ + .gitignore \ + public \ + src + +echo "Created $ARCHIVE" +ls -lh "$ARCHIVE" diff --git a/src/App.vue b/src/App.vue index ca7509b..e594168 100644 --- a/src/App.vue +++ b/src/App.vue @@ -26,6 +26,7 @@ import ExportDialog from './components/ExportDialog.vue' import FullscreenPreview from './components/FullscreenPreview.vue' import PropertiesPanel from './components/PropertiesPanel.vue' import Timeline from './components/Timeline.vue' +import TooltipHost from './components/TooltipHost.vue' import WelcomeDialog from './components/WelcomeDialog.vue' import TestOverlay from './devtools/TestOverlay.vue' import ruwioLogo from './assets/logo.png' @@ -418,13 +419,16 @@ onBeforeUnmount(() => {
-
- - - @@ -504,7 +515,7 @@ onBeforeUnmount(() => { {{ t('app.mainTimeline') }} - + {{ store.activeClip?.name ?? '' }} @@ -514,7 +525,7 @@ onBeforeUnmount(() => { {{ t('app.mainTimeline') }} - + {{ store.activeButtonAsset?.name ?? '' }} @@ -526,7 +537,7 @@ onBeforeUnmount(() => {
{
+
diff --git a/src/components/AboutDialog.vue b/src/components/AboutDialog.vue index e94452f..7d5ae2e 100644 --- a/src/components/AboutDialog.vue +++ b/src/components/AboutDialog.vue @@ -46,6 +46,8 @@ const shortcuts: Shortcut[] = [ + +

{{ t('about.credit') }}

@@ -128,4 +130,11 @@ kbd { color: var(--text-dim-strong); } +.credit { + margin: 6px 0 0; + font-size: 11px; + text-align: center; + color: var(--text-dim); +} + diff --git a/src/components/ActionEditor.vue b/src/components/ActionEditor.vue index 8c71b4e..4542115 100644 --- a/src/components/ActionEditor.vue +++ b/src/components/ActionEditor.vue @@ -106,13 +106,13 @@ function onTypeChange(index: number, newType: Action['type']) { {{ actionTypeLabel(type) }} - - - diff --git a/src/components/AssetSidebar.vue b/src/components/AssetSidebar.vue index 65f74f0..eaa5f36 100644 --- a/src/components/AssetSidebar.vue +++ b/src/components/AssetSidebar.vue @@ -1,5 +1,5 @@ + + + + diff --git a/src/components/WelcomeDialog.vue b/src/components/WelcomeDialog.vue index 4560a5f..8f3fdbf 100644 --- a/src/components/WelcomeDialog.vue +++ b/src/components/WelcomeDialog.vue @@ -161,7 +161,7 @@ watch(