docs: record CAD semantic context and vector crop planning notes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+88
@@ -1,5 +1,93 @@
|
|||||||
# Findings: Server-Backed Brand Kit Canvas Context
|
# Findings: Server-Backed Brand Kit Canvas Context
|
||||||
|
|
||||||
|
## 2026-07-16 Canvas Performance and LeaferJS Evaluation
|
||||||
|
|
||||||
|
- The current canvas is a React/DOM renderer: every visual node is a `CanvasNodeCard`, and the viewport is applied through a transformed DOM content layer.
|
||||||
|
- Wheel zoom and canvas pan update React `viewport` state at pointer/wheel frequency. Node dragging rebuilds the full `nodes` array and calls `setNodes` on every pointer event, so high-frequency interaction can reconcile the workspace and every un-memoized node even when only one transform changed.
|
||||||
|
- Large CAD drawings are displayed as one external SVG `<img>`, not thousands of React elements. Their 9 MB SVG decode/paint cost can amplify frame drops, but React state churn is a separate engine-wide bottleneck that also affects normal images and shapes.
|
||||||
|
- LeaferJS exposes a retained scene tree, Canvas rendering, hit testing, dragging, zooming, and an editor plugin. Those capabilities align with the current visual/interaction layer, while project persistence, Agent Chat, CAD conversion/semantics, crop, toolbars, and backend contracts should remain application-owned.
|
||||||
|
- The LeaferJS README's million-element benchmark is vendor-provided and has not been reproduced for this application's image-heavy/CAD workload. The migration decision must use this repo's pan/zoom/selection frame-time baseline.
|
||||||
|
- A full rewrite would unnecessarily couple chat, persistence, and action panels to the engine. The likely boundary is a canvas renderer adapter driven by the existing `CanvasNode[]` domain model, with React retained for toolbars, panels, overlays, and text editing.
|
||||||
|
- Viewport changes invalidate more than the `.canvas-world` transform: every `CanvasNodeView` receives `selectionScale` and `viewportScale`, fresh inline event callbacks, a fresh transform object, and a newly built context-menu handler object. A shallow `React.memo` alone would therefore not stop node rerenders.
|
||||||
|
- `workspaceCropTarget` scans all nodes and recomputes screen bounds on every viewport update. Selection overlays and annotations also consume React viewport state, adding more work to every wheel/pan event.
|
||||||
|
- Node drag calls `moveNodesByDelta` for the whole document, filters moved nodes, computes alignment guides against the full node list, replaces the `nodes` array, and updates React state on every pointer event. This is a stronger predicted bottleneck than SVG decoding once the image is loaded.
|
||||||
|
- The latest `leafer-ui` package observed is 2.2.2, MIT licensed, with about 4.3 MB unpacked package contents. The base engine can render images and vector paths, but editor/plugin bundle size and exact SVG/image behavior require a prototype before dependency adoption.
|
||||||
|
- The separate `@leafer-in/editor` 2.2.2 package is also MIT and about 1.23 MB unpacked. It provides selection/transform events, but React DOM overlays and the existing tool model still need an adapter rather than direct component replacement.
|
||||||
|
- `useCanvasPersistence` depends on both `viewport` and `nodes`; each interaction update reruns its effect, clears the existing 1.2-second timer, and creates a new timer. It does not save over the network each frame, but it adds avoidable effect/timer churn to the render path.
|
||||||
|
- Alignment-guide computation is linear in all document nodes for a single active node (with nine edge/center comparisons per candidate), then React still renders the resulting guides and the full node list. The measured browser baseline should vary node count to separate algorithmic scaling from a single large SVG's paint cost.
|
||||||
|
- Playwright production baseline on a 120Hz browser with lightweight frame nodes:
|
||||||
|
- 100 nodes: zoom p95 16.1ms, drag p95 9.1ms, one drag frame over 20ms.
|
||||||
|
- 500 nodes: zoom p95 40.9ms and drag p95 41.4ms; more than 90% of measured frames exceeded 20ms.
|
||||||
|
- 1000 nodes: zoom p95 83.4ms and drag p95 82.8ms; almost every frame exceeded 34ms and many 50ms+ long tasks were observed.
|
||||||
|
- The decisive differential probe kept the same 1000 DOM nodes but bypassed React. Direct `.canvas-world` transform updates measured p95 9.2ms with no frame over 20ms; direct single-node position updates also measured p95 9.2ms. This confirms React reconciliation/state/effect work, not raw DOM compositing capacity, as the primary bottleneck.
|
||||||
|
- Ranked causes after measurement: (1) confirmed React full-tree high-frequency updates; (2) drag-only full-array/alignment/persistence amplification; (3) unmeasured large SVG decode/paint cost; (4) disproved raw DOM node count as the primary cause.
|
||||||
|
- The first exact-DWG paint probe served the raw `convertCadBufferToSvg` output directly and Chromium rejected it. This is expected for the internal boundary: the third-party writer emits anonymous `</>` closing tags, while the normal browser import path repairs and sanitizes them in `normalizeCadSvg` before upload. The valid CAD paint baseline must capture the final UI-uploaded SVG, not the worker's raw intermediate string.
|
||||||
|
- The real browser upload path converted `/Users/liangxu/Desktop/花语江南6-204方案.dwg` into a valid 8,868,302-byte SVG with a 1200 x 839 preview viewport.
|
||||||
|
- With that single CAD node, React zoom measured roughly 192ms p95 and even direct `.canvas-world` transform measured roughly 158ms p95, with nearly every frame over 34ms. This isolates a second bottleneck: Chromium repeatedly rasterizes the complex SVG during transform/zoom.
|
||||||
|
- Replacing only the mocked display source with a one-time 1200 x 839 transparent WebP preview (119,676 bytes) reduced both React and direct zoom to 9.2ms p95 with no frame over 20ms. The persisted node/source remained conceptually SVG; only the display surface changed in the probe.
|
||||||
|
- The engine therefore needs two independent mechanisms: retained/incremental scene updates for general node count, and cached raster display textures for complex SVG/CAD sources. LeaferJS is relevant because its Canvas image nodes naturally draw cached bitmap textures while the application can keep the original SVG URL and CAD semantics in `CanvasNode`.
|
||||||
|
- LeaferJS's React guide explicitly warns against directly binding Leafer elements to reactive data because proxy/reactive wrapping significantly reduces performance. The adapter must own mutable engine objects outside React and emit domain updates only at transaction boundaries.
|
||||||
|
- `App.zoomLayer` exposes imperative `x`, `y`, and `scale`, matching the existing `CanvasViewport` without requiring React rerenders. `App` also separates ground/tree/sky engine layers, which maps cleanly to grid, document content, and editor handles.
|
||||||
|
- Image paint supports SVG URLs and maintains an asynchronous high-performance image-layer cache by default (`sync: false`). The global default image cache ceiling is documented as 2560 x 1600, which covers the current 1200 x 839 CAD preview texture.
|
||||||
|
- The recommended dependency boundary is `leafer-ui` plus narrowly selected plugins such as `@leafer-in/editor`, not the full `leafer-editor` bundle, which also includes viewport, scrollbars, arrows, HTML, export, text editing, and other features already owned by the application.
|
||||||
|
- Editor selection/move/scale events expose world-coordinate deltas and selected UI objects. These can update the Leafer scene immediately and commit the resulting `CanvasNode[]` once on interaction completion, keeping existing persistence and Agent Chat contracts unchanged.
|
||||||
|
- A throwaway LeaferJS 2.2.2 browser benchmark with 1000 draggable rectangles measured 9.2ms p95 for both viewport updates and one-node movement, with no frame over 20ms. This validates the retained renderer against the same node count where the current React path measured about 83ms p95.
|
||||||
|
- LeaferJS does not automatically solve complex CAD SVG paint: the exact 8.87 MB SVG used directly as an image fill measured 275.4ms p95 during viewport updates. The same Leafer scene using the one-time transparent WebP preview measured 9.1ms p95.
|
||||||
|
- Recommended architecture:
|
||||||
|
- Keep `CanvasNode[]`, original SVG URLs, CAD metadata/semantics, crop/export/model-reference logic, persistence, and Agent Chat application-owned.
|
||||||
|
- Add a renderer adapter whose Leafer scene objects are mutable and never wrapped in React state. Use `App.tree` for document nodes, `zoomLayer` for viewport interaction, and engine hit testing/editor events for selection and transforms.
|
||||||
|
- Keep React for workspace chrome, Agent Chat, action panels, menus, and temporary DOM text editing. React receives selection/screen-bounds snapshots and committed transforms, not per-frame movement.
|
||||||
|
- Persist a separate optional render-preview URL for complex vector nodes. Generate transparent WebP levels from the SVG once, use the current level throughout interaction, and upgrade the level asynchronously after zoom settles. The source `content` remains SVG and is still used by crop/download/semantic/model boundaries.
|
||||||
|
- Proposed acceptance gates before replacing the default renderer: 1000 mixed nodes and the exact CAD fixture both at <=16.7ms p95 for pan/zoom/drag after resources are ready; zero 50ms+ interaction long tasks; source SVG and semantic context byte/contracts unchanged; crop and Agent Chat reference tests unchanged; pixel comparison for CAD previews at each texture level.
|
||||||
|
- Production implementation discovery: canvas-node persistence uses explicit field lists across the go-zero `.api` type, Go domain/API mappers, PostgreSQL schema/queries/SQLC models, and frontend snapshot normalization. A durable preview URL therefore requires an explicit `renderContent` field through every layer.
|
||||||
|
- The canvas upload flow currently batches one file/public URL per optimistic node. Vector preview support must upload the source SVG and transparent WebP together, then commit both URLs to the node only when the source upload succeeds; failed preview upload should fail the vector node rather than silently reverting to the known-stalling raw SVG renderer.
|
||||||
|
|
||||||
|
## 2026-07-16 CAD SVG Crop Fidelity
|
||||||
|
- The crop overlay's `1934 × 1671` label is the selected region size in canvas coordinates, not the generated sibling node size. `createVectorCropSiblingNode` still caps the sibling's longest displayed edge at 640.
|
||||||
|
- `cropImageNodeFile` currently loads the complete SVG as an `HTMLImageElement`, uses intrinsic-image source coordinates in the nine-argument `drawImage`, and scales that crop to a 2048px long edge.
|
||||||
|
- This path does not change the SVG `viewBox` before decoding, so the browser can rasterize the full drawing first and then magnify the selected pixels. That explains thick CAD strokes and degraded text/detail in the crop.
|
||||||
|
- The fix seam is to map the normalized selection into the source SVG `viewBox`, set the cropped SVG's width/height to the final WebP dimensions, decode that rewritten SVG, and draw it once with no source-rectangle resampling.
|
||||||
|
- Raster image crop behavior remains on the current source-rectangle path.
|
||||||
|
- A real Chromium pixel probe imported the production `createCroppedSvgRasterSource` function and used an SVG line with `vector-effect="non-scaling-stroke"`. The old full-SVG source-rectangle upscale expanded the line from 4px to 82px; the cropped-viewBox render stayed 4px before and after WebP encoding.
|
||||||
|
- The generated sibling display cap is independent of the crop overlay's canvas-coordinate label. The crop asset remains a 2048px-long-edge WebP for model readability while the canvas sibling stays bounded to a 640-unit long edge.
|
||||||
|
- CAD crop semantics are carried separately from the raster bytes: `canvasNodeToReferenceImage` copies `semanticContext`, the composer emits it as hidden `cad-context`, and the backend includes that context in model prompts while excluding it from visible chat text.
|
||||||
|
|
||||||
|
## 2026-07-15 Canvas Wheel Passive-Listener Bug
|
||||||
|
- The reported stack points exactly to `CanvasWorkspace/index.tsx:1242`, where the React `onWheel` handler calls `event.preventDefault()`.
|
||||||
|
- React delegates `wheel` as a passive event in this runtime, so the browser rejects `preventDefault()` even though the handler is correct to suppress page scrolling while zooming the infinite canvas.
|
||||||
|
- There is no competing native wheel listener in the workspace. The correct boundary is one native listener on `.canvas-stage` registered with `{ passive: false }`, with explicit cleanup and the existing toolbar/popover bypass guard preserved.
|
||||||
|
- DWG parsing does not directly cause the warning; the long import makes canvas wheel interaction more likely while the React passive handler is mounted.
|
||||||
|
- A first native-listener implementation still missed the canvas because its effect ran during the loading view. Binding on `project.id` attaches after `.canvas-stage` mounts; browser verification then changed zoom from 100% to 91%, returned `defaultPrevented=true`, and emitted zero passive-listener warnings.
|
||||||
|
- The persistent workspace Crop action now follows Text directly. It remains visible/disabled without a selected image, enables for ready raster/vector/CAD images, and opens the same normalized crop overlay used by the floating image toolbars.
|
||||||
|
|
||||||
|
## 2026-07-16 Enlarged Single-Image Crop
|
||||||
|
- The screenshot reproduces a different contract from selected-node cropping: one image fills or exceeds the viewport, no node is selected, and the workspace Crop button is disabled.
|
||||||
|
- Enabling Crop for every unselected multi-image canvas would be ambiguous. The deterministic fallback should apply only when exactly one visible, unlocked, ready image exists; explicit selection remains authoritative otherwise.
|
||||||
|
- Initializing an oversized image to the full `{0,0,1,1}` crop leaves resize handles and the crop panel offscreen. The initial normalized rectangle must be the intersection of the image's screen bounds with a safe visible stage area.
|
||||||
|
- Crop controls should anchor to the active selection's screen bounds, not the full oversized image bounds.
|
||||||
|
- Browser verification used an unselected 2000 x 1600 image extending beyond a 680 x 672 stage. Workspace Crop enabled automatically, selected the image, and initialized a 632 x 576 crop at the safe visible inset; the panel remained within the 1280 x 720 browser viewport.
|
||||||
|
- With two visible images and no explicit selection, Workspace Crop remains disabled as intended.
|
||||||
|
|
||||||
|
## 2026-07-15 CAD Semantics and Crop Follow-up
|
||||||
|
- The user explicitly declined legacy-project semantic recovery and will re-upload all DWG/DXF assets. Semantic guarantees therefore apply to newly converted CAD nodes; no heuristic reconstruction from old SVG snapshots is included.
|
||||||
|
- The requested feature has two coupled outputs: a visually small crop for efficient inspection and a bounded semantic payload that preserves CAD meaning beyond raster pixels.
|
||||||
|
- The source CAD-derived SVG must remain unchanged. Crop must create a sibling node and asset, enabling repeated crops from the same large drawing and independent Agent Chat follow-ups.
|
||||||
|
- Useful model semantics should prioritize layer names, block names, visible text, entity-type counts, units/extents, and crop bounds while enforcing size/count limits so large files cannot inflate every prompt.
|
||||||
|
- Existing crop infrastructure already renders any image source, including SVG, into transparent WebP, but `applyCropImage` currently replaces and repositions the selected node. CAD can reuse the overlay/rasterizer while branching to a new sibling-node save path.
|
||||||
|
- Vector nodes currently expose only the compact vector/download toolbar. Adding a crop icon there is sufficient to enter the existing crop overlay without exposing unrelated raster AI actions.
|
||||||
|
- `AgentContent` already supports `textSource`; a `cad-context` text part can carry hidden model context without a new chat payload type. Frontend optimistic display and backend user-message serialization must explicitly exclude that source while agent prompt assembly retains it.
|
||||||
|
- `CanvasNode` has no semantic field and PostgreSQL persists node columns explicitly. Durable semantics therefore require a spec-first optional node field plus domain/mapping/schema/query updates.
|
||||||
|
- The CAD converter still has the parsed `CadDocument` in the worker and can embed a structured `<metadata>` payload into the derived SVG. This preserves richer layer/block/text/entity data in the vector asset while the node stores only a bounded prompt-ready summary.
|
||||||
|
- Selected contract: the SVG metadata keeps version, source/format/units, drawing bounds, layer/block/entity counts, bounded visible text, and a bounded top-level entity index with visual-coordinate bounds. The node persists a compact human-readable `semanticContext` derived from it.
|
||||||
|
- Crop semantics can intersect the normalized crop rectangle with the SVG metadata's visual-coordinate entity bounds, producing a crop-specific layer/block/entity/text summary even though the sibling visual is WebP.
|
||||||
|
- CAD imports will use a distinct `cad-vector` layer role; crop outputs use `cad-crop`. Generic SVGs may reuse the crop action but do not claim CAD semantics.
|
||||||
|
- Existing `TextSource` behavior is ideal for model delivery: `cad-context` remains in prompt/reference-memory builders, while optimistic UI and persisted user-message rendering need a narrow visibility filter.
|
||||||
|
- PostgreSQL `canvas_nodes` uses explicit columns and SQLC-generated queries. Adding `semantic_context TEXT NOT NULL DEFAULT ''` requires schema/query regeneration plus repository/domain/API mapping.
|
||||||
|
- Exact R12 semantic verification exposed legacy GBK bytes decoded as Latin-1 by the CAD library (`¿î·âÑùÖ½°å`). The converter now repairs this high-confidence mojibake before both SVG writing and semantic extraction so labels remain meaningful.
|
||||||
|
- Exact R12 verification now completes in about 0.37 seconds with 15 model entities, 11,198 reusable block entities, corrected Chinese garment labels, and a 1,050-character bounded model context.
|
||||||
|
- Exact `花语江南6-204方案.dwg` verification completes in about 11 seconds with 4,376 model entities, 3,397 reusable block entities, 4,422 crop-indexed entries, meaningful Chinese layer/block/text names, and a 1,912-character bounded model context.
|
||||||
|
- Semantic metadata increases the large DWG-derived SVG from about 8.49 MB to about 9.05 MB while retaining the reusable-block performance model; the increase is bounded and materially smaller than the former 50 MB recursive output.
|
||||||
|
|
||||||
## 2026-07-15 Vector Reference Follow-up
|
## 2026-07-15 Vector Reference Follow-up
|
||||||
- Raw CAD/SVG reference text is caused by the prompt-directive parser stopping at the first closing parenthesis in filenames such as `...2026-05-05(1) (DXF).svg`; the existing capsule and hover-preview UI is already shared with WebP references.
|
- Raw CAD/SVG reference text is caused by the prompt-directive parser stopping at the first closing parenthesis in filenames such as `...2026-05-05(1) (DXF).svg`; the existing capsule and hover-preview UI is already shared with WebP references.
|
||||||
- GPT Image rejects SVG input bytes even when the canvas and browser can render them. The SVG source must remain unchanged on canvas and be rasterized to alpha-preserving WebP only when building model-bound image contents.
|
- GPT Image rejects SVG input bytes even when the canvas and browser can render them. The SVG source must remain unchanged on canvas and be rasterized to alpha-preserving WebP only when building model-bound image contents.
|
||||||
|
|||||||
+89
@@ -1,5 +1,35 @@
|
|||||||
# Progress Log
|
# Progress Log
|
||||||
|
|
||||||
|
## Session: 2026-07-15 (CAD Semantics and Crop)
|
||||||
|
|
||||||
|
### Phase 23
|
||||||
|
- Loaded the repository workflow plus planning, frontend, and Go implementation guidance.
|
||||||
|
- Added the CAD semantic-context and non-destructive crop request as new phases without changing unrelated work.
|
||||||
|
- Fixed the product invariants: preserve the source, create a sibling crop, and keep model semantics out of visible chat copy.
|
||||||
|
- Completed the end-to-end design across worker metadata, durable node context, hidden Agent Chat content, vector crop UI, sibling placement, and PostgreSQL persistence.
|
||||||
|
- Added the optional `semanticContext` node field to the API specification before generated/server implementation.
|
||||||
|
|
||||||
|
### Phase 24
|
||||||
|
- Added versioned CAD metadata containing bounded source/format/unit, drawing bounds, layer/block/entity counts, visible text, and crop-intersection entries.
|
||||||
|
- Persisted compact node semantic context through the API/domain/PostgreSQL/SQLC/canvas snapshot paths.
|
||||||
|
- Added hidden `cad-context` Agent Chat contents: model prompt and memory retain them, while optimistic and persisted user copy exclude them.
|
||||||
|
- Added vector-toolbar crop, transparent WebP output, crop-specific semantic filtering, right-side sibling placement, source preservation, and automatic selection/reference of the new crop.
|
||||||
|
- Focused Go application/logic/PostgreSQL tests pass; frontend typecheck and all 16 frontend tests pass.
|
||||||
|
- Repaired legacy GBK CAD labels and locked the behavior with a focused converter regression.
|
||||||
|
- Re-ran both supplied files: exact DXF semantics complete in 0.37s and exact DWG semantics in 11s, with bounded contexts of 1,050 and 1,912 characters respectively.
|
||||||
|
- Increased vector crop raster output to a 2048px long edge so small selections from large drawings remain useful to the image model; sibling canvas display stays bounded to 220-640px.
|
||||||
|
|
||||||
|
### Phase 25
|
||||||
|
- All 20 frontend tests pass, including CAD conversion, GBK labels, semantic crop filtering, immutable sibling creation, readable crop sizing, hidden model context, WebP submission, and vector recognition.
|
||||||
|
- `go mod tidy`, full `go test ./...`, `go build ./...`, API validation, locale parsing, frontend production build, and `git diff --check` pass.
|
||||||
|
- Browser setup again reported no available backend, so live crop/hover/request inspection could not be repeated; the active Vite preview still responds at `http://127.0.0.1:5174`.
|
||||||
|
- Restored the repository's pre-existing `frontend/next-env.d.ts` development route import after production builds.
|
||||||
|
|
||||||
|
### Phase 26
|
||||||
|
- Documented CAD semantic metadata, hidden Agent Chat context, durable node persistence, and non-destructive crop behavior in the root README and API guide.
|
||||||
|
- Completed final scope review without reverting unrelated worktree changes.
|
||||||
|
- Started an isolated updated backend on `http://127.0.0.1:8889` and the verified production frontend on `http://127.0.0.1:5175`; the proxied health endpoint returns the updated service successfully.
|
||||||
|
|
||||||
## Session: 2026-07-15 (Vector Reference Follow-up)
|
## Session: 2026-07-15 (Vector Reference Follow-up)
|
||||||
|
|
||||||
### Phases 21-22
|
### Phases 21-22
|
||||||
@@ -105,6 +135,65 @@
|
|||||||
|
|
||||||
## Session: 2026-07-15
|
## Session: 2026-07-15
|
||||||
|
|
||||||
|
- Started Phase 27 after reproducing the warning directly from the source stack: `.canvas-stage` uses React `onWheel`, and its handler calls `preventDefault()` at the reported line under a passive delegated listener.
|
||||||
|
- Added the listener-registration regression first; its red run fails because the non-passive canvas wheel adapter does not yet exist.
|
||||||
|
- Added the element-level wheel adapter, switched the canvas to the latest viewport ref, removed React `onWheel`, and preserved the existing interactive-surface bypass behavior.
|
||||||
|
- The focused non-passive registration/cleanup test, TypeScript build, and diff check now pass.
|
||||||
|
- Added Phase 28 after confirming Crop existed only in the selected-image/vector floating toolbar, not in `workspace-toolbar` as requested.
|
||||||
|
- Browser instrumentation found that the first native-listener effect ran before the project canvas mounted. Bound the effect to `project.id` so the non-passive listener attaches when `.canvas-stage` appears.
|
||||||
|
- Added the Crop icon immediately after Text, with a visible disabled state, active crop state, and selection-tool handoff before opening the existing overlay.
|
||||||
|
- Full frontend verification passes with 21 tests and both Next/Vite production builds. The supplied DWG still converts in about 10.5 seconds to a 9.06 MB semantic SVG with 748 reusable block instances.
|
||||||
|
- Browser verification confirms `Text -> Crop -> Image` ordering, disabled/enabled Crop states, active state, one mounted crop overlay, functional 100% -> 91% wheel zoom, and no passive-listener warnings.
|
||||||
|
- Restarted the verified frontend preview on `http://127.0.0.1:5175` against the existing updated backend on port 8889.
|
||||||
|
- Started Phase 29 from the enlarged single-image screenshot: the workspace Crop target currently resolves only from `selectedImageNode`, so the button remains disabled even though there is one obvious image target.
|
||||||
|
- Added visible-stage crop geometry regressions first; the red run failed on the intentionally missing viewport geometry exports.
|
||||||
|
- Added shared crop viewport geometry, single-visible-image target resolution, deferred auto-selection crop entry, and selection-anchored crop controls.
|
||||||
|
- All 23 frontend tests and the Next/Vite production build pass.
|
||||||
|
- Browser verification confirms the exact enlarged-single-image case and the multi-image ambiguity guard; the updated preview is running at `http://127.0.0.1:5175`.
|
||||||
|
- Confirmed the final compatibility boundary: old project CAD nodes will not be migrated or heuristically enriched; the user will re-upload source DWG/DXF files so every retained CAD node uses the current semantic conversion contract.
|
||||||
|
- Re-ran the final verification after removing legacy recovery scope: all 20 frontend tests, full Go tests, API validation, locale parsing, Go build, Next build, Vite build, and diff checks pass.
|
||||||
|
- Restored Next's development route type import after production build and restarted the updated preview at `http://127.0.0.1:5175`; its backend proxy to `http://127.0.0.1:8889` returns HTTP 200.
|
||||||
|
|
||||||
|
## Session: 2026-07-16
|
||||||
|
|
||||||
|
### Phase 31: Canvas Performance and LeaferJS Evaluation
|
||||||
|
- **Status:** complete
|
||||||
|
- Started a diagnosis-only performance pass after the user reported canvas lag and pointed to `leaferjs/leafer-ui` as the reference engine.
|
||||||
|
- The evaluation will establish browser frame-time and long-task baselines before deciding whether LeaferJS should replace the full renderer or only the high-cost visual layer.
|
||||||
|
- Mapped the current renderer as React/DOM with viewport state updates and full-array node updates on high-frequency interaction.
|
||||||
|
- Reviewed LeaferJS's retained scene tree, Canvas renderer, hit testing, interaction, and editor capabilities; vendor benchmark claims remain unverified for the application's CAD/image workload.
|
||||||
|
- Identified viewport-dependent props and fresh per-node callbacks/objects that defeat a simple memoization fix, plus full-document alignment and node-array work on each drag event.
|
||||||
|
- Confirmed the current LeaferJS package baseline (2.2.2, MIT) and kept dependency installation out of the diagnosis phase.
|
||||||
|
- Found per-update persistence effect/timer churn and prepared a node-count-scaled Playwright baseline for zoom and drag interactions.
|
||||||
|
- Reproduced near-linear interaction degradation from 100 to 500 to 1000 nodes, reaching roughly 83ms p95 frame intervals at 1000 nodes.
|
||||||
|
- Ran the differential control on the same 1000-node DOM: direct world and node transforms held about 9.2ms p95, confirming React-path overhead as the primary bottleneck.
|
||||||
|
- The initial exact-DWG paint fixture used the raw worker intermediate and correctly failed SVG decoding; switched the probe to capture the normalized asset from the real browser upload path.
|
||||||
|
- Captured the final 8.87 MB SVG from the real UI upload and measured a single CAD node at 158-192ms p95 during zoom.
|
||||||
|
- Proved the cached-texture control: a one-time 1200 x 839 transparent WebP display preview reduced the same interaction to 9.2ms p95 while leaving the source contract separable.
|
||||||
|
- Confirmed that both the general retained renderer and complex-vector texture caching are required; neither optimization alone covers the reported workload.
|
||||||
|
- Mapped LeaferJS `App` ground/tree/sky layers, imperative `zoomLayer`, image caching, and editor events onto the existing canvas domain and React overlay responsibilities.
|
||||||
|
- Benchmarked LeaferJS itself rather than relying on vendor claims: 1000 rectangles held 9.2ms p95, compared with about 83ms in the current React path.
|
||||||
|
- Demonstrated that raw complex SVG remains slow in Leafer (275.4ms p95), while an application-owned transparent WebP texture restores 9.1ms p95.
|
||||||
|
- Completed the evaluation with an incremental renderer-adapter and vector texture-LOD recommendation; no production dependency or renderer code was changed during diagnosis.
|
||||||
|
|
||||||
|
### Phase 32: Leafer Renderer and Vector Texture Cache
|
||||||
|
- **Status:** in_progress
|
||||||
|
- Started the production implementation after user approval, using the existing `CanvasNode[]` and SVG/CAD semantic contracts as the source of truth.
|
||||||
|
- Chosen boundary: persist a separate render-preview URL, render supported visual nodes through LeaferJS, and keep React UI/overlays plus source-based crop/export/model behavior.
|
||||||
|
- Traced the explicit canvas-node field through the API/domain/PostgreSQL/snapshot layers and the paired source/preview upload changes needed for atomic vector imports.
|
||||||
|
|
||||||
|
### Phase 30: Native-Resolution Vector Crop Fidelity
|
||||||
|
- **Status:** in_progress
|
||||||
|
- Reproduced the user-visible symptom from the supplied comparison: the cropped transparent WebP has substantially thicker CAD strokes and degraded text/detail than the source SVG.
|
||||||
|
- Ranked the likely causes: low-resolution SVG rasterization followed by pixel upscaling, incorrect viewBox mapping, display-size mismatch, then WebP compression.
|
||||||
|
- Started a focused regression loop around SVG viewBox cropping and final-resolution rendering while preserving the existing raster crop path.
|
||||||
|
- Added the red regression for normalized crop mapping against a non-zero SVG viewBox, then implemented the mapping and final-size source preparation.
|
||||||
|
- Moved the browser SVG rewrite into the exported `createCroppedSvgRasterSource` production seam; `CanvasWorkspace` now draws the rewritten crop once at its final output dimensions while raster images retain the existing source-rectangle crop.
|
||||||
|
- Chromium verification measured a representative non-scaling CAD stroke at 82px through the old low-resolution upscale and 4px through the new native crop, unchanged after transparent WebP encoding.
|
||||||
|
- Reconfirmed that crop-specific CAD context travels with the sibling reference as hidden `cad-context` and remains available to the image-generation backend.
|
||||||
|
- Full verification passes: 24 frontend tests, `pnpm exec tsc -b`, `go test ./...`, `go build ./...`, Next/Vite production builds, and `git diff --check`.
|
||||||
|
- Restored Next's development route type import after the production build and restarted the updated preview at `http://127.0.0.1:5175`; the page and backend proxy return HTTP 200 with no browser console errors.
|
||||||
|
|
||||||
### Phase 15: CAD Import Discovery and Design
|
### Phase 15: CAD Import Discovery and Design
|
||||||
- **Status:** in_progress
|
- **Status:** in_progress
|
||||||
- Loaded the repository go-zero instructions plus the applicable Go, frontend, and file-planning skills.
|
- Loaded the repository go-zero instructions plus the applicable Go, frontend, and file-planning skills.
|
||||||
|
|||||||
+87
-1
@@ -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.
|
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
|
## Current Phase
|
||||||
Phases 21-22
|
Phase 32
|
||||||
|
|
||||||
## Phases
|
## Phases
|
||||||
|
|
||||||
@@ -139,6 +139,72 @@ Phases 21-22
|
|||||||
- [x] Add focused regression coverage and verify prepared model content receives a WebP asset URL
|
- [x] Add focused regression coverage and verify prepared model content receives a WebP asset URL
|
||||||
- **Status:** complete
|
- **Status:** complete
|
||||||
|
|
||||||
|
### Phase 23: CAD Semantics and Crop Discovery
|
||||||
|
- [x] Map CAD entity/layer/block/text data through conversion, canvas persistence, references, and Agent Chat generation
|
||||||
|
- [x] Map the existing crop overlay, sibling-node placement, vector toolbar, and upload/save paths
|
||||||
|
- [x] Define bounded semantic metadata and crop-output contracts for large drawings
|
||||||
|
- **Status:** complete
|
||||||
|
|
||||||
|
### Phase 24: CAD Semantics and Crop Implementation
|
||||||
|
- [x] Preserve bounded CAD semantics during DWG/DXF conversion and canvas persistence
|
||||||
|
- [x] Attach CAD semantic context to model-bound references without exposing it in user-visible chat text
|
||||||
|
- [x] Add vector crop action that preserves the source and creates a cropped sibling asset
|
||||||
|
- [x] Make cropped CAD regions attachable and reusable in Agent Chat
|
||||||
|
- **Status:** complete
|
||||||
|
|
||||||
|
### Phase 25: CAD Semantics and Crop Verification
|
||||||
|
- [x] Add conversion, persistence, semantic-payload, crop, and source-preservation regression coverage
|
||||||
|
- [x] Verify both supplied CAD files, focused Go/frontend tests, typecheck, and production builds
|
||||||
|
- [x] Attempt real-browser crop/reference inspection and record the unavailable browser backend
|
||||||
|
- **Status:** complete
|
||||||
|
|
||||||
|
### Phase 26: CAD Semantics and Crop Delivery
|
||||||
|
- [x] Review scope and generated files without altering unrelated changes
|
||||||
|
- [x] Document the semantic contract, crop behavior, and preview URL
|
||||||
|
- **Status:** complete
|
||||||
|
|
||||||
|
### Phase 27: Non-Passive Canvas Wheel Regression
|
||||||
|
- [x] Add a deterministic regression test for non-passive element-level wheel listener registration and cleanup
|
||||||
|
- [x] Replace the React synthetic canvas wheel handler with a native `{ passive: false }` listener
|
||||||
|
- [x] Verify zoom behavior, DWG upload flow, focused/full tests, and production build
|
||||||
|
- **Status:** complete
|
||||||
|
|
||||||
|
### Phase 28: Workspace Toolbar Crop Action
|
||||||
|
- [x] Add a persistent Crop icon immediately after the Text tool
|
||||||
|
- [x] Enable it only for a selected, ready image/vector node and reuse the existing crop overlay
|
||||||
|
- [x] Verify icon order, disabled/active states, crop entry, tests, and production build
|
||||||
|
- **Status:** complete
|
||||||
|
|
||||||
|
### Phase 29: Enlarged Single-Image Crop
|
||||||
|
- [x] Add regression coverage for translating the visible viewport intersection into normalized crop coordinates
|
||||||
|
- [x] Let workspace Crop automatically target the only visible image when none is selected
|
||||||
|
- [x] Anchor the crop selection and controls inside the visible viewport for oversized images
|
||||||
|
- [x] Verify enlarged-image crop entry, multi-image ambiguity, tests, and production build
|
||||||
|
- **Status:** complete
|
||||||
|
|
||||||
|
### Phase 30: Native-Resolution Vector Crop Fidelity
|
||||||
|
- [x] Reproduce the CAD SVG crop stroke/text degradation with a deterministic regression seam
|
||||||
|
- [x] Crop SVGs by rewriting their viewBox and render once at the final WebP resolution
|
||||||
|
- [x] Preserve SVG structure, transparency, CAD semantics, source immutability, and raster crop behavior
|
||||||
|
- [x] Verify crop output dimensions/display sizing, focused tests, production builds, and browser behavior
|
||||||
|
- **Status:** complete
|
||||||
|
|
||||||
|
### Phase 31: Canvas Performance and LeaferJS Evaluation
|
||||||
|
- [x] Build a repeatable browser performance baseline for pan, zoom, selection, and large CAD nodes
|
||||||
|
- [x] Isolate React/DOM reconciliation, SVG rasterization, overlays, persistence, and event-path costs
|
||||||
|
- [x] Map LeaferJS capabilities and integration constraints against the existing canvas domain model
|
||||||
|
- [x] 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
|
||||||
|
|
||||||
## Invariants
|
## Invariants
|
||||||
- Brand Kits are owned and queried only through the authenticated user ID from request context.
|
- 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.
|
- A project may reference zero or one Brand Kit owned by the same user.
|
||||||
@@ -160,6 +226,8 @@ Phases 21-22
|
|||||||
- SVG-backed image nodes use vector-only actions and download their source SVG rather than exposing raster editing actions.
|
- 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.
|
- 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.
|
- 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.
|
||||||
|
- CAD semantic context is bounded, persisted with the relevant source/crop, and sent as model context without becoming visible user-message copy.
|
||||||
|
|
||||||
## Errors Encountered
|
## Errors Encountered
|
||||||
| Error | Attempt | Resolution |
|
| Error | Attempt | Resolution |
|
||||||
@@ -197,3 +265,21 @@ Phases 21-22
|
|||||||
| 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
||||||
|
|||||||
Reference in New Issue
Block a user