docs: record renderContent, leafer renderer, and crop tool planning notes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-16 20:46:37 +08:00
parent b363dcf888
commit 7a6f4a2727
3 changed files with 47 additions and 0 deletions
+17
View File
@@ -236,3 +236,20 @@
- The existing `VectorizedImageToolbar` already matches the requested compact `矢量` plus download UI. Its caller only identifies `layerRole === "vectorized-image"`, so normal uploaded SVGs and CAD-derived SVGs incorrectly receive the raster `image-node-toolbar`.
- SVG export already preserves SVG-backed source content through `downloadSvgSource`; vector detection can route the toolbar without changing the download implementation.
- Browser inspection of the second DWG reproduced the tiny-content symptom after the performance fix. Its extreme bounds come from one `MTEXT` entity around 550,000 drawing units away from the actual plans; the source marks that entity `isInvisible`, but the third-party SVG writer ignores CAD visibility. Excluding invisible/off/frozen-layer entities from both SVG output and bounds is the generic display fix.
## Leafer Renderer Implementation (2026-07-16)
- The resumed worktree contains only the Phase 32 API-spec field and its regenerated Go type; `leafer-ui` is not yet installed and no persistence or frontend render-preview plumbing exists.
- `CanvasNode` crosses both a compressed frontend snapshot and explicit server API/domain/PostgreSQL mappings. PostgreSQL uses ordered SQLC column/parameter lists, so `render_content` must be added consistently to schema, select, upsert, generated SQLC parameters, repository save/map, and mapper paths.
- Canvas covers currently derive from `node.content`; this should remain source-based for durable semantics/export unless a dedicated thumbnail consumer needs the preview texture.
- Canvas import currently creates one optimistic node per prepared file and uploads only its source. The extension point is `PreparedCanvasImportAsset`: add an optional WebP file, keep the local SVG object URL as optimistic content, and mark the node successful only when both source and preview uploads resolve.
- Existing SVG-to-transparent-WebP conversion already validates SVG, derives dimensions from `viewBox`, clears the canvas, and encodes WebP at up to 2048 px. It can be reused for render textures with a render-specific filename while leaving model-reference behavior unchanged.
- `ArtboardPreview` is a source-only DOM renderer today. Preview consumers can prefer `node.renderContent`, but crop/export/model paths must keep reading `node.content`.
- Leafer's `around: "center"` interprets `x/y` as the transform center. Passing the domain's top-left coordinates shifts the rendered image up and left by half its width/height while the React selection overlay stays at the correct domain bounds; retained visuals must pass center coordinates and convert imperative drag positions the same way.
- The apparent rectangular canvas boundary was a real initialization race: the renderer's async import completed after the stage resize state update, then applied the stale `1100 x 760` fallback captured by the project effect. The stage was actually `1685 x 1670`, so Leafer clipped everything outside the smaller internal canvas. Initial sizing must read the mounted layer's live `clientWidth/clientHeight` when the renderer becomes ready.
## Workspace Crop Drag Selection (2026-07-16)
- The clarified interaction is a two-stage tool: the workspace Crop button arms a crosshair without opening `CropOverlay`; the next click or drag chooses the initial region, then the existing editor opens.
- A click analyzes the displayed raster/WebP texture with bounded connected-color region growth. If no enclosing color boundary exists, the result becomes the maximum currently visible image region.
- A drag bypasses color analysis and maps the pointer rectangle directly into normalized source coordinates, clamped to the same visible-region maximum. This remains correct under canvas pan and zoom because mapping uses current screen bounds.
- Color analysis is capped at a 640-pixel edge and measured about 22ms for a fully connected 640 x 480 image in the local Node benchmark.
- CAD/SVG source and semantic metadata are untouched: the preview texture is used only for click analysis, while the established source-based crop path creates the final result and crop-specific CAD context.
+16
View File
@@ -181,6 +181,22 @@
- 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.
- Resumed Phase 32 with a clean two-file partial diff: the API spec and generated Go type already expose optional `renderContent`; persistence, preview generation, and renderer work remain outstanding.
- Completed `renderContent` plumbing through the frontend domain/snapshot normalizer, Go domain/API mappers, PostgreSQL schema/queries/repository, SQLC generation, mapper regression coverage, and API documentation.
- Added one-time transparent WebP generation for imported SVG/CAD sources, paired source/texture uploads with partial-upload cleanup, optimistic local texture display, and preview-preference helpers for the canvas and thumbnail surfaces.
- Installed `leafer-ui@2.2.2` and added a retained Leafer renderer for successful image nodes and manual frames. Vector nodes enter Leafer only when their transparent WebP texture is present; React remains the hit-target, selection, toolbar, crop, and text-edit overlay.
- Moved wheel zoom, canvas pan, and ordinary node drag frames onto imperative Leafer/DOM transforms. React viewport/node state now commits after interaction settles or ends, and drag alignment guides render imperatively.
- Focused/full verification passes after the renderer migration: 29 frontend tests, strict TypeScript compilation, and the complete Go test suite.
### Phase 33: Workspace Crop Drag Selection
- **Status:** complete
- Implemented the clarified two-stage workspace Crop tool: activation now enters a crosshair preselection state instead of immediately opening a full crop selection.
- A click selects the connected color-bounded area under the pointer from the displayed raster/WebP texture; the selection can never exceed the currently visible part of the image.
- Holding the left button and dragging draws a live rule-of-thirds selection, clamps it to the visible target bounds, and opens the existing crop editor on release.
- The existing source-preserving CAD/SVG crop, crop-specific semantic context, raster replacement behavior, presets, resizing, and confirm/cancel flow remain unchanged.
- Added synthetic color-boundary and visible-range drag regressions. All 35 frontend tests, strict TypeScript compilation, `git diff --check`, and the Next/Vite production build pass.
- Benchmarked the maximum fully connected 640 x 480 color analysis at about 22ms and confirmed the updated single preview server responds at `http://127.0.0.1:5175`.
- The configured in-app browser backend remained unavailable, so final interactive visual inspection is left to the running local preview; source-level interaction coverage and HTTP verification pass.
### Phase 30: Native-Resolution Vector Crop Fidelity
- **Status:** in_progress
+14
View File
@@ -205,6 +205,14 @@ Phase 32
- [ ] Add focused regression/performance coverage and run full frontend/backend/build/browser verification
- **Status:** in_progress
### Phase 33: Workspace Crop Drag Selection
- [x] Enter a crosshair preselection state after activating the workspace Crop tool
- [x] Select a connected color-bounded region on click, capped to the visible image area
- [x] Map a held left-button drag into normalized image coordinates across pan and zoom
- [x] Preserve the existing crop editor, vector semantics, and source-preservation behavior
- [x] Add focused regression coverage and run frontend 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.
@@ -255,6 +263,12 @@ Phase 32
| 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 | null`, matching the existing browser timeout usage. |
| 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. |