Files
moteva/task_plan.md
T
root d424b56076 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.
2026-07-17 11:28:29 +08:00

29 KiB

Task Plan: Server-Backed Brand Kit Canvas Context

Goal

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 35

Phases

Phase 1: Architecture Discovery

  • Map Brand Kit frontend state, project persistence, chat/thread creation, and generation context flow
  • Confirm API generation, storage-driver, schema, and test patterns
  • Status: complete

Phase 2: API and Domain Design

  • Update the .api contract before generated code
  • Define user-owned Brand Kit storage and project binding invariants
  • Define server-side prompt injection behavior for all project chat sessions
  • Status: complete

Phase 3: Backend Implementation

  • Generate handlers/types from the validated API specification
  • Implement memory and PostgreSQL Brand Kit persistence keyed by user ID
  • Persist and expose project brandKitId
  • Inject selected Brand Kit into Agent Chat and new-thread context
  • Add backend tests and API documentation
  • Status: complete

Phase 4: Frontend Integration

  • Replace local-only Brand Kit persistence with authenticated API persistence
  • Add canvas-title Brand Kit selector with None and user-owned kits
  • Add a Brand Kit selector to the Home prompt composer, including an empty create action
  • Keep Brand Kit editor, defaults, and project binding synchronized
  • Status: complete

Phase 5: Verification

  • Run API validation, generation checks, Go tests/build, frontend typecheck/build
  • Verify CRUD, user isolation, project binding, chat context, and selector behavior
  • Inspect desktop and mobile browser layouts
  • Status: in_progress

Phase 6: Delivery

  • Review scope, generated files, and prohibited product references
  • Provide preview URL and concise implementation notes
  • Status: pending

Phase 7: Device Management Discovery and Design

  • Trace the existing account dialog, authentication gateway, and device API behavior
  • Define production-grade loading, empty, error, current-device, and destructive-action states
  • Status: complete

Phase 8: Device Management Implementation

  • Implement the device management view and responsive styling in the existing account surface
  • Wire list/remove-all behavior to the authenticated device API without changing unrelated account flows
  • Make desktop/mobile session limits configuration-driven and return the effective policy from the device API
  • Enforce each configured device-type limit atomically and render last-active timestamps in the browser timezone
  • Add focused backend or frontend coverage where the repository supports it
  • Status: in_progress

Phase 9: Device Management Verification

  • Run focused tests, frontend typecheck/build, and Go verification if backend code changes
  • Inspect the authenticated desktop and mobile UI in a real browser
  • Status: pending

Phase 10: Device Management Delivery

  • Review scope and final diff
  • Provide the working preview URL and concise implementation notes
  • Status: pending

Phase 11: Visual Annotation Discovery and Design

  • Map image toolbar/action generation, canvas transforms, and reusable panel conventions
  • Translate the supplied annotation reference into the repository's interaction and visual language
  • Status: complete

Phase 12: Visual Annotation Implementation

  • Add an image-toolbar entry and visual annotation editing mode
  • Support brush, circle, rectangle, and cross marks on the selected image
  • Make each mark's AI edit instruction directly editable and removable
  • Submit the annotated source through the existing image action generation flow
  • Preserve the source node and write generation state/results only to a sibling copy
  • Status: in_progress

Phase 13: Visual Annotation Verification

  • Add focused interaction/serialization coverage where supported
  • Run frontend typecheck/build and inspect desktop/mobile behavior in a real browser
  • Status: pending

Phase 14: Visual Annotation Delivery

  • Review scope and final diff without altering unrelated user changes
  • Provide the working preview URL and concise implementation notes
  • Status: pending

Phase 15: CAD Import Discovery and Design

  • Trace the canvas asset import, upload, persistence, rendering, and export paths
  • Define the supported DWG/DXF conversion boundary and failure states
  • Status: complete

Phase 16: CAD Import Implementation

  • Confirm no backend API change is required for browser-side conversion
  • Add DWG/DXF selection, conversion, SVG/canvas rendering, and user feedback
  • Add focused tests and documentation
  • Status: complete

Phase 17: CAD Import Verification and Delivery

  • Run relevant frontend tests/builds
  • Inspect CAD import and canvas behavior in a browser with the exact R12 file
  • Review scope and deliver concise support/limitation notes
  • Status: complete

Phase 18: General CAD Compatibility Follow-up

  • Reproduce the block-heavy DWG timeout and isolate its dominant output path
  • Replace recursive INSERT/DIMENSION expansion with reusable SVG block definitions and full insert transforms
  • Cover nested blocks, repeated inserts, MINSERT arrays, malformed references, and recursion cycles
  • Status: complete

Phase 19: Vector Image Toolbar Follow-up

  • Recognize uploaded SVG and CAD-derived SVG nodes as vector images
  • Hide the raster image toolbar and show the compact vector/download toolbar for vector nodes
  • Preserve original SVG download behavior
  • Status: complete

Phase 20: Follow-up Verification and Delivery

  • Verify both user-provided CAD files plus representative DWG/DXF regression fixtures
  • Run focused tests, typecheck, and production builds
  • Verify vector toolbar, rendering, download, and persistence in a real browser
  • Status: complete

Phase 21: Vector Reference Message Rendering

  • Reproduce CAD-derived SVG references falling back to raw prompt directives
  • Parse SVG/CAD reference names with nested parentheses into the existing message capsule
  • Verify SVG directives enter the existing thumbnail/hover/copy component path; run tests and production build
  • Status: complete

Phase 22: Model-Compatible SVG References

  • Identify every canvas frontend submission path that can pass an SVG image reference to image generation
  • Rasterize SVG references to transparent WebP only at the model submission boundary
  • Cache and replace submitted reference URLs without mutating SVG canvas nodes or display names
  • Add focused regression coverage and verify prepared model content receives a WebP asset URL
  • Status: complete

Phase 23: CAD Semantics and Crop Discovery

  • Map CAD entity/layer/block/text data through conversion, canvas persistence, references, and Agent Chat generation
  • Map the existing crop overlay, sibling-node placement, vector toolbar, and upload/save paths
  • Define bounded semantic metadata and crop-output contracts for large drawings
  • Status: complete

Phase 24: CAD Semantics and Crop Implementation

  • Preserve bounded CAD semantics during DWG/DXF conversion and canvas persistence
  • Attach CAD semantic context to model-bound references without exposing it in user-visible chat text
  • Add vector crop action that preserves the source and creates a cropped sibling asset
  • Make cropped CAD regions attachable and reusable in Agent Chat
  • Status: complete

Phase 25: CAD Semantics and Crop Verification

  • Add conversion, persistence, semantic-payload, crop, and source-preservation regression coverage
  • Verify both supplied CAD files, focused Go/frontend tests, typecheck, and production builds
  • Attempt real-browser crop/reference inspection and record the unavailable browser backend
  • Status: complete

Phase 26: CAD Semantics and Crop Delivery

  • Review scope and generated files without altering unrelated changes
  • Document the semantic contract, crop behavior, and preview URL
  • Status: complete

Phase 27: Non-Passive Canvas Wheel Regression

  • Add a deterministic regression test for non-passive element-level wheel listener registration and cleanup
  • Replace the React synthetic canvas wheel handler with a native { passive: false } listener
  • Verify zoom behavior, DWG upload flow, focused/full tests, and production build
  • Status: complete

Phase 28: Workspace Toolbar Crop Action

  • Add a persistent Crop icon immediately after the Text tool
  • Enable it only for a selected, ready image/vector node and reuse the existing crop overlay
  • Verify icon order, disabled/active states, crop entry, tests, and production build
  • Status: complete

Phase 29: Enlarged Single-Image Crop

  • Add regression coverage for translating the visible viewport intersection into normalized crop coordinates
  • Let workspace Crop automatically target the only visible image when none is selected
  • Anchor the crop selection and controls inside the visible viewport for oversized images
  • Verify enlarged-image crop entry, multi-image ambiguity, tests, and production build
  • Status: complete

Phase 30: Native-Resolution Vector Crop Fidelity

  • Reproduce the CAD SVG crop stroke/text degradation with a deterministic regression seam
  • Crop SVGs by rewriting their viewBox and render once at the final WebP resolution
  • Preserve SVG structure, transparency, CAD semantics, source immutability, and raster crop behavior
  • Verify crop output dimensions/display sizing, focused tests, production builds, and browser behavior
  • Status: complete

Phase 31: Canvas Performance and LeaferJS Evaluation

  • Build a repeatable browser performance baseline for pan, zoom, selection, and large CAD nodes
  • Isolate React/DOM reconciliation, SVG rasterization, overlays, persistence, and event-path costs
  • Map LeaferJS capabilities and integration constraints against the existing canvas domain model
  • Recommend the smallest migration boundary with measurable acceptance criteria
  • Status: complete

Phase 32: Leafer Renderer and Vector Texture Cache

  • Extend the API/domain/storage contract with an optional vector render-preview URL
  • Generate and upload transparent WebP previews while preserving original SVG/CAD source and semantics
  • Add a LeaferJS visual renderer adapter for image and manual-frame nodes behind the existing canvas domain
  • Move wheel/pan and node drag hot paths to imperative engine/DOM updates with commit-on-end state synchronization
  • Preserve crop, export, selection, context menus, overlays, Agent Chat references, and persistence behavior
  • Add focused regression/performance coverage and run full frontend/backend/build/browser verification
  • Status: in_progress

Phase 33: Workspace Crop Drag Selection

  • Enter a crosshair preselection state after activating the workspace Crop tool
  • Select a connected color-bounded region on click, capped to the visible image area
  • Map a held left-button drag into normalized image coordinates across pan and zoom
  • Preserve the existing crop editor, vector semantics, and source-preservation behavior
  • Add focused regression coverage and run frontend verification
  • Status: complete

Phase 34: Reference-Anchored Generation Placement

  • Preserve ordered canvas-node identity from submitted image capsules for the active agent turn
  • Place newly generated image nodes beside the last canvas-backed image capsule
  • Keep backend/default placement when no submitted capsule maps to a canvas node
  • Add multiple-capsule and fallback regression coverage
  • Run frontend tests, typecheck, build, and preview verification
  • Status: complete

Phase 35: Pen Tool and Shape Path Editing

  • Trace the workspace shape tool, canvas-node representation, Leafer renderer, and persistence behavior
  • Install and integrate leafer-x-path-editor without breaking existing shape creation
  • Add a new Pen icon immediately after Shape while preserving the existing Brush tool unchanged
  • Enable double-click path editing for pen paths and shapes created by the Shape tool
  • Keep legacy/unmarked Brush strokes outside the vertex-editor path contract
  • Cover editor lifecycle, persistence, and interaction regressions
  • 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.
  • Selecting None clears the binding without deleting the Brand Kit.
  • Server-side Agent Chat context is derived from the persisted project binding, never trusted from a client-supplied prompt.
  • Home may request a Brand Kit ID only from the authenticated user's list; the server revalidates ownership before binding it.
  • Deleting a Brand Kit clears project bindings to it.
  • Device sessions are always scoped from the authenticated server-side user identity.
  • Bulk device removal preserves the current session unless the product contract explicitly requires otherwise.
  • New access and refresh tokens carry an opaque random session ID; the server validates that session before accepting the bearer token.
  • Legacy access tokens without a session ID are migrated once from a stable token digest after user validation, preserving existing signed-in users.
  • Revoked and expired sessions never authenticate or appear in the active-device list.
  • Device metadata is derived from request headers for display only and never used as an authorization signal.
  • Device timestamps cross the API boundary as RFC3339 UTC instants and are localized only in the browser's timezone.
  • Visual annotation actions never mutate the source image node; their copied result node is the only generation target.
  • Desktop and mobile web-session limits come from server configuration; the API returns the effective values and the UI never hard-codes them.
  • Imported CAD files are read without mutation; only a sanitized derived SVG is uploaded and rendered, with explicit size, timeout, empty-drawing, and conversion errors.
  • Repeated and nested CAD block references are represented once and instanced with SVG references; conversion cost must scale with unique block geometry, not insert count.
  • SVG-backed image nodes use vector-only actions and download their source SVG rather than exposing raster editing actions.
  • 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

Error Attempt Resolution
zsh rejected an unmatched HomePage/*.css glob 1 Replaced the glob with explicit files discovered via rg --files.
Assumed CanvasWorkspace/index.css, which does not exist 1 Locate the workspace stylesheet from its imports before reading it.
rg treated a pattern beginning with -- name: as a flag 1 Use rg -- <pattern> when searching SQLC query annotations.
Assumed AuthProvider.tsx instead of the component directory entry 1 Locate the file with rg --files before any further auth-provider reads.
PostgreSQL package test failed after adding project mapping because text conversion helpers were not yet defined 1 Add nullable pgtype.Text conversion helpers and rerun the package tests.
One patch to remove a temporary err = nil line used the wrong surrounding field name 1 Reapplied the patch against the exact current lines; no code change was lost.
Workspace import patch assumed a nonexistent BrandMark import 1 Located the actual import block and applied the selector change against current source.
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.
Desktop limit regression test observed two valid desktop sessions when the configured limit was 1 1 Confirmed the limit was presentation-only; enforce it atomically during session creation and reconcile pre-fix sessions during device listing.
Launching mcp-zero from server/ used a repository-root relative path 1 Relaunch with ../.tools/bin/mcp-zero while keeping generation output rooted in server/.
NPM lookup for a nonexistent @loaders.gl/dxf package returned 404 and stopped the chained lookup 1 Use independent package searches and validate the chosen converter package before installation.
Package inspection commands raced the tar extraction and looked under the wrong transient path 1 Inspect the extracted tree first, then run dependent file reads sequentially against the discovered path.
npm install inherited a parent workspace containing an unsupported workspace:* URL 1 Inspect local package-manager metadata and run installation with workspace discovery disabled or use the repository's actual package manager.
TypeScript rejected .length on the CAD library's entity collection 1 Use the collection's declared count/iteration API instead of assuming it is an array.
Next 16 Turbopack emitted the module worker as raw .ts instead of executable bundled JavaScript 1 Run Next dev/build through its supported webpack mode, which handles new Worker(new URL(...)); retain Vite's native worker bundling.
Used nonexistent Playwright CLI command network 1 Use the CLI's documented requests and response-body commands for network inspection.
First Vite DXF upload triggered dependency pre-bundling and a development-page reload 1 Retry after Vite finishes one-time dependency optimization; production worker output is already bundled.
Node's strip-only TypeScript test loader rejected a constructor parameter property in the Leafer adapter 1 Replace the parameter property with an explicit class field so production TypeScript and the repository's Node test runner share supported syntax.
The Leafer descriptor test could not resolve frontend @/ runtime aliases under Node's native loader 1 Keep the descriptor dependency-free at runtime with local source/adjustment checks; type-only aliases remain erased and production bundling is unchanged.
DOM window.setTimeout returned number but the viewport timer ref inferred Node's Timeout type 1 Declare the browser-owned timer ref as `number
Combined render-preview cleanup/thumbnail patch assumed mainCanvasPreviewImage lived in project_thumbnail.go 1 Locate the function in design_service.go and split the patch across exact current file contexts.
The first crop preselection integration patch used one stale keyboard-handler context and failed atomically 1 Split the integration into small patches against the exact current source; no production file received a partial edit.
A diagnostic rg command contained an unmatched quote around a template-literal search 1 Use separate fixed-string searches without shell-sensitive backticks.
Real upstream DXF fixture parsed to an empty model space in @node-projects/acad-ts 1 Validate the library's document assembly in Node, then replace the DXF path or reader entirely if real fixtures remain empty.
Directory-wide listing of the WeChat file folder stalled on container-managed storage 1 Use the exact user-provided path directly; metadata access confirms the file is readable.
The exact R12 DXF crashed while serializing the first inherited-color POINT 1 Resolve non-renderable ByLayer/ByBlock colors through the entity and layer before calling the third-party SVG writer; lock the case with a minimal R12 regression test.
Next production minification reduced valid DXF geometry to zero-valued entities 1 Restore acad-ts constructor names from stable export keys before its name-based metadata lookup; keep the normal minified build and cover the mangled-name path in the regression test.
Reusing one entity writer and slicing its cumulative string exhausted the Node heap on the exact R12 drawing 1 Give each entity a short-lived fragment writer so output assembly stays linear in total geometry size.
Node's strip-only TypeScript test runner rejected constructor parameter properties 1 Use explicit class fields and constructor assignments, which preserve the project test runner's supported syntax subset.
Vector reference regression suite failed on nested English and Chinese CAD filenames 1 Expected red phase confirmed: the directive regex stops before the final filename parenthesis; update it to bind the closing delimiter adjacent to the URL separator.
First post-fix parser assertion still expected no index property 1 The production parser correctly emits index: undefined; align the regression fixture with the stable result shape.
In-app browser discovery returned no available browser backend 1 Followed the browser skill troubleshooting flow and confirmed an empty backend list; retain automated tests/builds and report the interactive verification gap.
Combined final planning patch targeted progress lines as task-plan context 1 Split the update by file and apply each change against its exact current section.
Local mcp-zero stdio initialization returned no JSON-RPC response 1 API validation succeeded; use the documented goctl api go fallback with the existing go_zero style.
TypeScript did not infer that a crop existed from the derived crop-bounds check 1 Guard the scope line with both crop and cropBounds; Go targeted tests already pass.
One combined patch contained a malformed hunk boundary 1 Reissued the same two-file edit with valid patch boundaries; no source change was lost.
Node's strip-types test runner could not resolve the new extensionless runtime import 1 Keep the shared semantic types as a stripped type-only import and define the stable metadata element ID locally in the worker module.
Synthetic text-only CAD fixture did not expose a library bounding box 1 Add a simple visible line to establish drawing bounds while keeping the text-encoding assertion focused.
A second planning/error patch initially repeated the malformed hunk separator 1 Removed the empty hunk marker and applied the intended fixture/error update cleanly.
Isolated Next dev preview was rejected by an existing repository-wide dev lock 1 Keep the updated backend on 8889 and run the verified production Next build on 5175 with its API proxy pointed at 8889.
zsh rejected the optional frontend/.env* inspection glob because no env file exists 1 Search the concrete Next/Vite config and source paths without an unmatched optional glob.
Combined toolbar patch expected the wrong onChooseShapeKind callback name 1 Re-read the exact WorkspaceToolbar prop block and apply the component and call-site changes against current code.
First browser wheel probe stayed at 100% because the stage listener was never attached 1 The initial effect ran while the project-loading view had no .canvas-stage; re-run listener binding when project.id mounts the canvas.
Playwright run-code rejected a bare await page... snippet despite the abbreviated skill example 1 Read command help and pass an async (page) => { ... } function as required by the installed CLI.
The first mocked route handler used URL, which is unavailable in the Playwright callback sandbox 1 Derive the pathname from route.request().url() with bounded string operations; the corrected route exposed the real canvas.
Initial combined Phase 30 test/findings patch assumed a generic # Findings heading 1 Re-read the file header and apply the regression test and findings as separate patches against the exact heading.
Exact-DWG performance fixture served the raw converter intermediate and Chromium rejected anonymous </> tags 1 Use the real browser upload path so normalizeCadSvg repairs and sanitizes the final SVG before measuring paint/interaction cost.
Playwright file-input locator matched both canvas upload and Agent Chat upload controls 1 Target the first canvas file input explicitly for the real DWG upload probe.
Playwright request context bypassed the page's mocked asset route and could not resolve asset.local 1 Fetch the captured normalized SVG inside the page context so the route handler supplies the bytes.
Combined performance findings patch matched entries in a different order than the current file 1 Re-read the exact section and apply the package/result additions against their real positions.
Direct CDN import used LeaferJS's package ESM build, whose bare @leafer/core specifier is not browser-resolvable 1 Load the official prebundled browser distribution for the throwaway benchmark.
TypeScript widened the hidden reference-anchor content discriminator to string 1 Declare the helper's AgentContent[] return type explicitly and rerun the focused test and typecheck.
Detached nohup preview processes were reclaimed by the terminal runner 1 Start the frontend and backend in persistent tool sessions, then verify all direct and proxied endpoints.
pnpm dev -- --port 5175 passed --port as a Next project directory 1 Use the repository script's supported pnpm dev --port 5175 argument form.