feat: add CanvasPathEditor and editable path functionality

- Implemented CanvasPathEditor component for editing paths on the canvas.
- Created editablePath module to handle path data manipulation and conversion to SVG.
- Introduced pathPen module for managing path drawing with a pen tool, including anchor management and path data generation.
- Added tests for editablePath and pathPen functionalities to ensure correctness of path editing and drawing behavior.
This commit is contained in:
2026-07-17 11:28:29 +08:00
parent fe4b08f548
commit d424b56076
30 changed files with 3791 additions and 44 deletions
+23 -1
View File
@@ -4,7 +4,7 @@
Persist Brand Kits by authenticated user, bind one optional Brand Kit to each project/canvas, expose a canvas-header selector, and make every Agent Chat turn and newly created thread use the selected kit as authoritative creative context.
## Current Phase
Phase 32
Phase 35
## Phases
@@ -221,6 +221,16 @@ Phase 32
- [x] Run frontend tests, typecheck, build, and preview verification
- **Status:** complete
### Phase 35: Pen Tool and Shape Path Editing
- [x] Trace the workspace shape tool, canvas-node representation, Leafer renderer, and persistence behavior
- [x] Install and integrate `leafer-x-path-editor` without breaking existing shape creation
- [x] Add a new Pen icon immediately after Shape while preserving the existing Brush tool unchanged
- [x] Enable double-click path editing for pen paths and shapes created by the Shape tool
- [x] Keep legacy/unmarked Brush strokes outside the vertex-editor path contract
- [x] Cover editor lifecycle, persistence, and interaction regressions
- [x] Run frontend tests, typecheck/build, and browser verification
- **Status:** complete
## Invariants
- Brand Kits are owned and queried only through the authenticated user ID from request context.
- A project may reference zero or one Brand Kit owned by the same user.
@@ -243,6 +253,7 @@ Phase 32
- User-message image directives render through one attachment capsule/preview path regardless of raster or SVG source format.
- SVG remains the persisted canvas source; transparent WebP derivatives exist only for model-compatible image inputs.
- CAD source nodes are immutable during crop; every crop is a sibling asset positioned beside the source.
- Pen and Brush remain distinct workspace tools: Pen creates vertex-editable paths, while Brush keeps the existing continuous freehand behavior.
- CAD semantic context is bounded, persisted with the relevant source/crop, and sent as model context without becoming visible user-message copy.
## Errors Encountered
@@ -258,7 +269,18 @@ Phase 32
| First i18n patch used an inexact Chinese source string | 1 | Re-read the locale file and patched the exact key/value context. |
| Existing account-device test called removal without an authenticated session context | 1 | Update the test to resolve the issued bearer identity and assert the new current-session contract. |
| Preview backend rejected `/dev/stdin` because go-zero infers config format from the filename extension | 1 | Use a `.yaml` symlink to stdin so the transformed preview config retains a recognized extension without creating a repository config file. |
| Path editor typecheck treated the asynchronously initialized Leafer App as nullable | 1 | Use a non-null local `nextApp` after construction while retaining a nullable cleanup reference. |
| Node's TypeScript test loader could not resolve the extensionless `shapeNode` import from `editablePath.ts` | 1 | Use the explicit `.ts` extension, which is supported by the no-emit bundler configuration and Node test runner. |
| TypeScript rejected the explicit `.ts` import until the no-emit project opted in | 1 | Enable `allowImportingTsExtensions`, matching the Node test loader and bundler-only output model. |
| Pen-path test expected unchanged local coordinates after a control point expanded the node bounds | 1 | Keep the world-coordinate-preserving implementation and use an in-bounds control point for the write-back assertion. |
| Initially repurposed the existing freehand Brush button as Pen | 1 | Restore Brush unchanged and add Pen as a separate tool immediately after Shape. |
| A shell health-check used zsh's read-only `status` variable | 1 | Rename the variable to `http_status`; subsequent endpoint checks returned 200. |
| Next standalone preview was first launched with `next start`, then without copied static assets | 1 | Use the already-running project dev server for the final preview and Vite for isolated mocked browser verification. |
| A second Next development server refused to start because the existing repo-wide dev lock is active | 1 | Use the already-built Next production server on the alternate preview port. |
| Final frontend build found another active `next build` process | 1 | Inspect the active process and wait for it to finish before retrying once; do not remove build locks blindly. |
| Path-tool editor branch was first patched into the duplicate background handler | 1 | TypeScript caught undefined `node`; restore the background branch and scope the node regression to `handleNodePointerDown`. |
| Anchor-Pen follow-up again matched the background Path branch | 1 | Scoped regression and TypeScript caught it; patch using the node handler's unique text-node condition. |
| Browser kept an obsolete Pen module after concurrent exports changed | 1 | Vite reported Fast Refresh invalidation; perform a full page reload after typecheck/build settles before re-verifying. |
| BSD `sed` did not support the GNU `0,/pattern/` address used for the preview port substitution | 1 | Replace it with a portable anchored substitution; the backend now listens on 8889. |
| The in-app browser runtime reported no available browser backends | 1 | Keep the verified local preview running and report the browser-only visual inspection gap; do not substitute an unrelated browser surface against the browser skill rules. |
| Escaping the `psql \d` meta-command through Docker produced an invalid command | 1 | Query `information_schema.columns` and `pg_indexes` with regular SQL instead. |