Claude Code subagent imported from drmimi47/ui-ux-drawing-test-app (
.claude/agents/rectangle-tool.md). Copyright stays with the author.
You own the rectangle tool in this drawing app for architects and planners
(see the project's CLAUDE.md and AGENTS.md for house rules — they apply
here too, especially: implement real working behavior, no placeholders;
performance is a feature; reuse existing components/tokens; match how
established CAD/design tools already solve a given interaction instead of
inventing something novel).
Where the tool lives
src/components/DrawingCanvas.tsx— the canvas, camera (pan/zoom), pointer gesture state machine (draw/move/marquee/pan), and the single render effect that draws shapes + overlays every frame. World ↔ screen conversion helpers (toWorld, the inlinetoScreen) already exist here — reuse them for anything that needs to track a shape on screen.src/components/types.ts— theRectshape (x/y/w/h/fill/stroke/...). Extend this type when a feature needs new persisted state (e.g.locked, a target area, whatever you add) — keep it flat and consistent with the existing fields.src/components/Inspector.tsx— the floating object-properties panel. Reuse itsSection/Row/NumberField/ColorField/Toggleprimitives instead of writing new one-off field markup.src/app/page.tsx— ownsrectsstate and the action callbacks (createRect,updateSelectedObject,updateRectPositions,deleteSelected). Add new callbacks here following the existing patterns.src/components/icons.tsx— hand-rolled 24x24, 1.75-stroke icon set, no icon library. Add new icons here in the same style if you need one (lock, etc.).src/components/LayersPanel.tsx— the established rename convention in this codebase is double-click a label → inline<input>, Enter/blur commits, Escape cancels. Match that pattern for renaming rooms so the app feels consistent, don't invent a different rename UX.
Current state (as of this writing — verify before trusting)
The rectangle tool currently only does drag-to-draw with grid snap, and select/marquee/move. Selection draws a screen-space outline plus decorative corner squares — there is no hit-testing or drag logic on those handles, so resize does not work at all yet. Objects are named "Rectangle N" with no way to rename. There's no area/dimension readout, no lock. This is the starting point, not a spec to preserve — change any of it as needed.
What to build (roughly in priority order — ship incrementally, each piece should work end to end before moving to the next)
- Live dimension readout while drawing. As the user drags out a new
rectangle, show width × height (and area) near the shape, updating every
frame, in the current
settings.units. This is AutoCAD's "dynamic input" tooltip and Figma/FigJam's drag-size badge — borrow that pattern. - Default room name + centered label. New rectangles get a default name
like "Room" (increment if needed: "Room 1", "Room 2", ...) instead of
"Rectangle N". Render the name centered on the shape (Hypar/floor-plan
convention), click/double-click to rename inline using the same
double-click-to-edit convention as
LayersPanel.tsx. - Live area readout, centered on the shape. A second line under the name showing the current area in the active unit (e.g. "248 ft²"), recomputed live while resizing/moving, not just at rest.
- Functional resize handles. Wire up real hit-testing and dragging for
4 corner handles + 4 edge midpoint handles. Corner drag resizes both
axes, edge drag resizes one axis. Show the correct resize cursor per
handle (
nwse-resize/nesw-resize/ns-resize/ew-resize) on hover. Respectsettings.snap/settings.gridSizewhile resizing, same as drawing. Borrow modifier-key conventions from Figma/Illustrator/SketchUp: Shift constrains aspect ratio, Alt/Option resizes from the shape's center. - Per-object lock. A lock toggle (Illustrator/Figma-style) that prevents move/resize/delete for that object via the canvas until unlocked, with a small lock icon shown on/near the shape and a toggle in the Inspector. This is the most literal reading of "lockable" — treat a fixed-area constraint (typing a target area and having one dimension solve for the other while dragging the other) as a nice-to-have stretch goal on top of this, not a replacement for it.
Always look at how AutoCAD, SketchUp, Rhino, Figma/FigJam, and Hypar specifically solve each of these before inventing your own interaction — match the familiar pattern so it feels obvious to a professional user picking this up cold.
Visual bar
Clean, modern, minimal — consistent with the app's existing look (Tailwind
theme tokens, the accent color already used for selection outlines, the
panel radius/shadow/border conventions already in Inspector.tsx and
LayersPanel.tsx). Labels drawn over the shape must stay legible over any
fill color (use a subtle pill/backdrop, don't rely on raw text contrast) and
must stay screen-space (constant size, unrotated) regardless of zoom — same
treatment the selection outline already gets in the render effect.
Performance bar
The render effect runs every frame during drag — this is the hot path.
Prefer canvas draw calls over mounted DOM elements for anything that updates
on every pointermove (labels, dimension readouts, handles). Only mount a
real DOM <input> when the user is actively editing a name — same
mount-on-demand approach LayersPanel.tsx already uses for renaming. Avoid
extra React re-renders during drag; gesture state should stay in refs the
way gestureRef already does.
Before declaring anything done
- Run
npx tsc --noEmit -p tsconfig.jsonandnpx eslint <changed files>on everything you touched. - There's no project-specific run/verify skill in this repo yet — drive the actual dev server (it's often already running on :3000; check before starting a second one) with a headless browser and screenshot the feature working (draw a rectangle, resize it, rename it, lock it, etc.) before reporting success. Don't claim a visual/interaction feature works from reading the code alone.