0.4.0: clipart drawer, animation paths, bezier keyframes

- Clipart library: new left-edge drawer (5px idle, expands on hover,
  400px when open). User picks a local folder via the File System Access
  API in settings; directory handle is persisted in IndexedDB so the
  library survives reloads. Dragging a thumbnail onto the stage creates
  an image asset + layer at the drop point.
- Animation paths on the stage: selecting a keyframe draws the tween
  path from the previous keyframe to the selected one as a solid
  polyline with per-frame draggable dots. All other segments on the
  same position-track render as dashed ghost lines so the full curve
  progression is visible.
- Path dots grow on hover (2.5 → 6 px), show a grab cursor, and have a
  wider hit area so they're easier to grab.
- Bezier-curved position interpolation: keyframes gain optional
  ctrlX/ctrlY fields. When set, x/y tween along a quadratic bezier
  through the control point instead of a straight line. Dragging a
  path dot solves for the control so the curve passes through the
  drag point at its frame progress. Mirrored across all four render
  pipelines.
- Tests: new suites for polygon actions, freeform drawing lifecycle,
  polygon + stroke rendering (15 new tests). Fixed the mask-render
  tests that relied on a default-transparent canvas background.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.7 committed 2026-04-20 10:35:43 +02:00
1 parent 276d621d20
commit 869b67458a
16 files changed
+1379 -15

No files matched your search

+16 -3
View File
@@ -18,6 +18,7 @@ import {
import AboutDialog from './components/AboutDialog.vue'
import AssetSidebar from './components/AssetSidebar.vue'
import CanvasStage from './components/CanvasStage.vue'
import ClipartDrawer from './components/ClipartDrawer.vue'
import DocumentSettings from './components/DocumentSettings.vue'
import ExportDialog from './components/ExportDialog.vue'
import FullscreenPreview from './components/FullscreenPreview.vue'
@@ -290,7 +291,9 @@ onBeforeUnmount(() => {
</script>
<template>
<div class="app" :style="appStyle">
<div class="shell">
<ClipartDrawer />
<div class="app" :style="appStyle">
<header class="topbar">
<div class="brand-area">
<button class="brand" :title="t('about.openTooltip')" @click="aboutOpen = true">
@@ -404,10 +407,19 @@ onBeforeUnmount(() => {
<span>{{ loadError }}</span>
</Toast>
<Toast :visible="!!savedToast" variant="info" :message="savedToast ?? ''" />
</div>
</div>
</template>
<style scoped>
.shell {
display: flex;
align-items: stretch;
height: 100vh;
width: 100vw;
overflow: hidden;
}
.app {
display: grid;
grid-template-columns: 260px 1fr 300px;
@@ -416,8 +428,9 @@ onBeforeUnmount(() => {
'top top top'
'side stage props'
'time time time';
height: 100vh;
width: 100vw;
flex: 1 1 0;
min-width: 0;
height: 100%;
}
.topbar {