diff --git a/findings.md b/findings.md index 7fdae5d..f01e41c 100644 --- a/findings.md +++ b/findings.md @@ -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. diff --git a/progress.md b/progress.md index dfd8ba7..a9638cd 100644 --- a/progress.md +++ b/progress.md @@ -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 diff --git a/task_plan.md b/task_plan.md index 80681b6..16cf925 100644 --- a/task_plan.md +++ b/task_plan.md @@ -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. |