0.3.0: freeform drawing, stroke properties, polygon edit gestures

- Freeform drawing tool: new sidebar button enters a click-to-draw mode
  that appends points, shows a live ghost preview line, and exits via
  Enter or double-click (near the start point closes the path). Works
  end-to-end through all four render pipelines.
- Polygon authoring gestures: double-click on a path inserts a vertex on
  the nearest edge; double-click on an open polygon's endpoint resumes
  the drawing session from that end, reversing point order when needed.
- Stroke + closed-path properties: all shape types (rect, circle, text,
  polygon) expose stroke color, stroke width, and (for polygons) a
  closed toggle. Fill and stroke each support a "transparent" switch so
  shapes can render as outline-only or fill-only.
- Mask preview no longer inherits the user's stroke — the dashed ghost
  keeps its own outline regardless of stroke-width changes.
- Playhead no longer dims on inactive timeline tracks; only keyframes,
  segments and clip blocks fade.
- Properties panel point-count control for polygons, plus per-keyframe
  points animation that correctly interpolates once a baseline exists.
- Pivot drag hides the rotation handle until release.
- Transformer attach retries on a short timer to avoid races where the
  Konva node hasn't mounted yet when selection changes.

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 09:53:01 +02:00
1 parent a05396e734
commit 276d621d20
13 files changed
+944 -64

No files matched your search

+14
View File
@@ -65,6 +65,8 @@ export const en = {
circle: 'Circle',
text: 'Text',
polygon: 'Polygon',
freeform: 'Freeform (click to draw, Enter to finish, Esc to cancel)',
freeformActive: 'Drawing… (Enter to finish, Esc to cancel)',
},
newTextDialog: {
title: 'New text object',
@@ -73,6 +75,11 @@ export const en = {
confirm: 'Insert',
fallback: 'Text',
},
newPolygonDialog: {
title: 'New polygon',
message: 'Number of points (3–24):',
confirm: 'Create',
},
newClipDialog: {
title: 'New clip',
message: 'Clip name:',
@@ -97,13 +104,20 @@ export const en = {
deleteLayer: 'Delete layer',
noLayer: 'No layer selected.',
fill: 'Fill',
transparent: 'Transparent',
opacity: 'Opacity',
edgeBlur: 'Edge blur',
sections: {
transform: 'Transform',
text: 'Text',
polygon: 'Polygon',
appearance: 'Appearance',
},
polygonPoints: 'Points',
polygonPointsHint: 'Changing the point count rewrites all keyframes; new points are inserted at the midpoint of the last edge.',
polygonClosed: 'Closed path (fill)',
stroke: 'Stroke',
strokeWidth: 'Stroke width',
shadow: 'Shadow',
shadowBlur: 'Shadow blur',
shadowOpacity: 'Shadow opacity',