aarbitandClaude Sonnet 5 71e4c2c22d
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
Pin wrangler and supabase CLI as devDependencies
The last pipeline run failed on \`npx wrangler deploy\` with npm unable to
find wrangler@4.144.0 — a transient registry propagation gap right after
that version's release, not an actual config problem (it installs fine
moments later, confirmed locally). Both wrangler and the supabase CLI were
being fetched fresh via bare npx on every single pipeline run instead of
coming from the lockfile like every other dependency, which is exactly
what left CI exposed to whatever npm's registry happens to be doing at
that moment. Pinning them means npm ci resolves the exact locked version
every time, and npx picks up the local install instead of hitting the
registry at all.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017DUU6CnxECCDeqDNYJgr5x
2026-09-29 16:35:36 -05:00
2026-09-06 23:28:25 -05:00

Diagrav

Plan out AV/network installs: define devices and their ports, wire them together on a canvas, and get a bill of materials (devices + cables, with lengths) for what you'll need to buy.

Stack

  • React 19 + TypeScript + Vite
  • @xyflow/react (React Flow) for the device/wiring canvas
  • Zustand for app state
  • Tailwind CSS v4 for styling

Architecture

src/
  domain/       Framework-agnostic types + logic (compatibility rules, BOM math,
                the built-in connector/cable/device library). No React, no
                React Flow — safe to unit test or reuse from a future backend.
  data/         Storage abstraction. `DiagramRepository` is the interface the
                rest of the app codes against; `SupabaseDiagramRepository` is
                the active implementation, with `LocalStorageDiagramRepository`
                kept as a reference/fallback. Swapping storage means adding
                one new implementation of that interface, not touching
                state/UI code.
  state/        Zustand store (`diagramStore`) — the single source of truth for
                the current diagram. Auto-saves to the repository (debounced)
                on every change.
  components/
    canvas/     React Flow wiring surface: custom device node, custom cable
                edge, connection validation.
    palette/    Device library sidebar + the "new custom device" editor.
    inspector/  Right-panel editors for the selected device or cable.
    bom/        Bill-of-materials view.
    layout/     App shell, top bar, panels.

Data model

  • PortType — a connector kind (HDMI, XLR, Cat5e/6, ...). Two ports can be wired together only if their PortTypes share a compatibility family.
  • DeviceTemplate — a reusable device shape (its ports), shown in the palette. Built-ins live in domain/library.ts and seed the public catalog; custom ones are your own private catalog entries (or submitted for public review).
  • Device — an instance placed on the canvas, with its own copy of ports (editing an instance never mutates its template).
  • CableType — a physical cable spec, scoped to a PortType family.
  • Connection — a wire between two ports, referencing a CableType and an optional user-entered distance (there's no floor plan / scale model, so length is a manual estimate per connection).
  • Diagram — devices + connections. This is the one object persisted (auto-saved to Supabase, or explicitly exported/imported as JSON); the catalog (PortType/CableType/DeviceTemplate/category) is shared and global, not part of any one diagram.

Development

npm install
npm run dev      # start the dev server
npm run build    # typecheck + production build
npm run lint      # oxlint
S
Description
Visual AV planner
Readme
694 KiB
Languages
TypeScript 79.7%
PLpgSQL 16.3%
HTML 1.6%
JavaScript 1.3%
CSS 1.1%