Commit Graph
3 Commits
Author SHA1 Message Date
steveandClaude Opus 4.8 ae3d52db31 Render the assistant's replies as Markdown
Build image / build-and-push (push) Successful in 12s
Gadfly review (reusable) / review (pull_request) Successful in 12m49s
Adversarial Review (Gadfly) / review (pull_request) Successful in 12m49s
The assistant answers in Markdown — tables, lists, bold, links — but the
chat bubble showed the raw source (whitespace-pre-wrap), so a table came
through as a wall of pipes.

- New MarkdownMessage: react-markdown + remark-gfm (for GFM tables). It does
  NOT enable raw HTML (no rehype-raw), so a model reply can't inject markup;
  every element the assistant actually uses is Tailwind-styled for a chat
  bubble, and wide content (tables, code) scrolls in its own box so the
  bubble can't blow out.
- Only ASSISTANT bubbles render as Markdown; the user's own text stays
  literal (their `*` shouldn't become a bullet) and the assistant bubble goes
  full-width so a table has room.
- Lazy-loaded: the renderer + its ecosystem (~150 KB) is its own async chunk,
  loaded only when an assistant message renders — not for everyone who opens
  the editor. To keep it out of the eager first paint, manualChunks now
  vendors only the app-wide core (react/react-dom/scheduler kept together —
  splitting react-dom breaks React 19 at load) and lets everything else ride
  with its importer.

Tested via renderToStaticMarkup (node, no DOM): a GFM table renders with
styled cells + horizontal scroll; **bold**/lists render; raw <script>/<b> are
escaped not emitted; links get rel="noopener noreferrer". App re-verified to
mount cleanly with the re-chunk (no React-19 init-order break). tsc + vitest
(99) + build green, no >500 KB warning.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
2026-07-22 12:17:34 -04:00
steveandClaude Opus 4.8 30b36b7033 Add canvas foundation: SVG viewport, pan/zoom/pinch, geometry lib (#9)
Build image / build-and-push (push) Successful in 16s
Gadfly review (reusable) / review (pull_request) Successful in 9m31s
Adversarial Review (Gadfly) / review (pull_request) Successful in 9m31s
The editor's technically-riskiest slice, built against mock data so #11
only adds interactions on top.

- lib/geometry.ts (+ vitest): world<->screen and object-local<->world
  transforms, clampScale, zoomViewportAt (the wheel/pinch "keep the point
  under the cursor stationary" math), zoomToFitRect. 11 geometry tests
  (round-trips, rotation, cursor-anchored zoom, fit) + 6 units tests.
- editor/store.ts: Zustand store for ephemeral editor state — viewport,
  selection, focusedObjectId (selection interactions grow in #11).
- editor/useViewport.ts: @use-gesture wiring — wheel + pinch zoom toward
  the pointer, drag-pan, scale clamped to [0.05, 20] px/cm, and an animated
  fitToRect (eased rAF tween). touch-action:none so the browser doesn't
  fight gestures.
- editor/ObjectShape.tsx: rect/circle centered + rotated, kind default
  colors, name label; non-scaling strokes so outlines stay 1px at any zoom.
- editor/GardenCanvas.tsx: full-size svg, single world <g>, fading 1m grid,
  garden boundary, z-ordered objects; ResizeObserver-driven auto-fit, a
  zoom readout, and a Fit control. Deps: zustand, @use-gesture/react,
  vitest (+ test scripts).
- GardenEditorPage renders the canvas with a mock scene (#11 swaps in
  /full).

Verified in a real browser: wheel zoom keeps the world point under the
cursor stationary (sub-cm drift on a 12m garden), a real drag pans, and
Fit animates to frame the garden. tsc + vitest green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
2026-07-18 19:59:40 -04:00
steveandClaude Opus 4.8 91da9ff945 Phase 0: backend + frontend scaffold, single-binary build
Gadfly review (reusable) / review (pull_request) Successful in 10m8s
Adversarial Review (Gadfly) / review (pull_request) Successful in 10m9s
Implements the Pansy v1 scaffold (epic #20, phase 0).

#1 Backend: Go module (pure-Go, builds with CGO_ENABLED=0), env config,
   gin server with slog + recovery + /api/v1/healthz, modernc SQLite store
   with WAL/busy_timeout/foreign_keys pragmas, embedded numbered-migration
   runner, full initial schema (users, sessions, gardens, garden_shares,
   garden_objects, plants, plantings), domain structs + sentinel errors,
   graceful shutdown.

#2 Frontend: Vite + React 19 + TS (strict) + Tailwind 4 + TanStack
   Router/Query, typed /api/v1 fetch wrapper (ApiError carries status +
   body so later issues can read 409 conflict rows), dev proxy /api -> :8080,
   responsive app shell with stub pages for all five routes.

#3 Single binary: //go:embed of the web build with a committed placeholder,
   SPA fallback (deep links, immutable asset caching, JSON 404 for unmatched
   /api and missing assets), Makefile (web/build/dev/test), README quickstart
   + env var table.

Verified: go build/vet/test clean (CGO off); binary migrates idempotently and
serves healthz; web tsc + build clean; integrated binary serves the SPA, deep
links, and correct cache headers; app mounts and navigates all five routes at
desktop and 375px widths.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
2026-07-18 14:52:05 -04:00