# CableGUI — Project Instructions ## Project Overview Cable-management **framework + solver** for m's setup. m declares his **devices** and the **connection requirements** between them ("NAS must connect to Switch via RJ45"). CableGUI runs a solver that emits the cable plan + bundle recommendations. CableGUI is a **schematic**, not a physical-routing tool — cables are straight lines between endpoints; the "maximum bundling" objective is satisfied by the endpoint-pair rule (when two or more cables share the same A↔B endpoint pair, group them into one bundle). The visual editor is still there for tweaking the plan, but the solver is the headline. Each cable-managed environment (LOFT, OFFICE, …) is a separate **CableGUI project**, and each project is backed by exactly one Excalidraw drawing. The framework provides a visual web interface backed by a Go HTTP API and SQLite, plus an export pipeline that writes `.excalidraw` files via mExDraw. **Memory group_id:** `mcables` (kept historical — all prior memories live under this id; renaming would orphan them) **No CLI.** Frontend-first — every interaction is through the visual interface. The backend serves the UI and the API; there is no `cablegui` shell binary intended for humans. ## Goal - A reusable framework for tracking devices, ports, cables, cable types, bundles, frames — **scoped per project** (LOFT and OFFICE are separate projects, each a separate drawing). - A **solver** that, given the project's devices + connection requirements, emits the cable plan + bundle recommendations. Objective: maximum bundling via the endpoint-pair rule (schematic only — no path/trunk/cable-tray modelling). - A **hybrid device-type catalog**: 14 built-in types (NAS, PC, Mac, Notebook, TV, Soundbar, Switch, fritz, ChromeCast, SteamLink, IOx-3/6/8, Screen, Keyboard, Mouse) with default port profiles, extensible per project. Picking a type on device-create seeds the device's ports automatically; m overrides per instance. - **Setup templates** for bootstrapping a project from blank to solver-ready: built-ins 'Living Room', 'Home Office', 'Server Rack' stamp their device-types + connection requirements in one transaction. - A visual editor for switching projects, adding frames/devices, declaring requirements, running the solver, and tweaking the resulting plan. Unmet requirements get a one-click quick-fix ("+ Add <type> port to <device> and re-solve"). - A one-way export from the DB to the corresponding `.excalidraw` drawing on `mxdrw.msbls.de` whenever m clicks Export — DB is authoritative, Excalidraw is the projection. ## Architecture | Layer | Tech | Notes | |---|---|---| | DB | SQLite | `./data/cablegui.db` (project-local, gitignored). Driver: `modernc.org/sqlite` (cgo-free). | | Backend | Go | `net/http` HTTP API + static frontend via `embed.FS`. Standard library + minimal deps. Single binary. | | Frontend | Vanilla JS modules + SVG, no build step | TypeScript types via JSDoc, optional `tsc --noEmit` in CI. Preact-via-CDN-ESM is the documented fallback if vanilla state gets painful — no build step either way. | | Diagram I/O | mExDraw HTTP API | `PUT https://mxdrw.msbls.de/api/drawings/.excalidraw` with `Authorization: Bearer $MEXDRAW_TOKEN`. (The `mcp__mexdraw__*` MCP tools are not currently configured for this project — workers use the raw HTTP API.) | ## Hierarchy - **Project** (`projects` table) is the top-level concept. LOFT, OFFICE, HOMELAB, … are separate projects. One project ↔ one `.excalidraw` drawing in mExDraw. `projects.drawing_name` defaults to `.excalidraw` server-side when omitted on create; editable later. - **Frames** sub-divide a project (LOFT has `desk`, `rack`, `media`; OFFICE has `desk`, `server`). Frames are not projects — they're zones within one drawing. - Every device, port, cable, IO marker, bundle, and **connection requirement** is **project-scoped** (`project_id` denormalised onto every row, with `ON DELETE CASCADE` from `projects`). `UNIQUE (project_id, devices.name)` — no two devices in one project share a name. - **Cable types are global.** A single shared `cable_types` table — no `project_id`. The five defaults (Power/USB/HDMI/DP/RJ45) are seeded by migration 001 once, not per project. Renaming or recolouring a type affects every project's legend immediately. - **Device types are hybrid.** `device_types` is one global table with `project_id` NULL for the 11 built-in catalog rows (seeded by migration 002) and `project_id = current` for project-custom types. Each `device_type` carries a `device_type_ports` profile that seeds `ports` rows when a device of that type is created. m can extend the catalog per project; built-ins are read-only from the API. - **Connection requirements** (`connection_requirements` table) are the solver's input. m declares "from_device ↔ to_device, preferred cable type, must_connect"; the solver assigns ports and emits cables. - **Project deletion guardrail.** `DELETE /api/projects/:pid` requires `?confirm=` matching the project's current name. 400 otherwise. - **Solver-owned vs. user-owned cables.** `cables.auto = 1` = created by the solver and replaceable on re-solve. `auto = 0` = hand-drawn by m, left alone by the solver. PATCHing endpoint or type of an auto cable promotes it to manual (explicit "Promote to manual" button in the inspector, per design v4 §5b.3). ## Branch Strategy - `main` = production-deployable. - `mai//` = worker branches via the mai workflow. - No `dev` branch — too small a project for staging. - Merge with `--no-ff` to main, delete branches after merge. ## Tech Stack - **Go** for the backend (matches m's other tools: `m`, `mai`, youpcms, mExDraw). - **SQLite** via `modernc.org/sqlite` (cgo-free → clean `scratch`/distroless container, no toolchain pain). - **mExDraw** via HTTP for diagram export. Never edit raw `.excalidraw` files directly outside the mExDraw API. - **Vanilla JS + SVG** for the frontend — no build step. JSDoc-typed. ## Deployment — mDock, raw docker (NOT Dokploy) CableGUI runs on **mDock** (`192.168.178.131` on the LAN, Tailscale `mdock`) as a **plain docker-compose service**. Dokploy is for public mlake/mRiver stuff; mDock uses raw `docker compose` per the conventions of the existing mDock services (mgreen, mgeo, msports-garmin, paperless, …). - Repo layout on mDock: `/home/m/stacks/cablegui/` with `docker-compose.yml`, `data/` bind-mount, secrets in `/home/m/secrets/cablegui/.env`. - Image: `mgit.msbls.de/m/cablegui:latest` (built and pushed by a Gitea Actions workflow on push to `main`, runs on the self-hosted runner on mDock with label `self-hosted:host`). - Port mapping: `7777:7777`, exposed on the LAN — no reverse proxy. - Restart policy: `unless-stopped`. - LAN URL: `http://mdock:7777`. - No auth — LAN-trusted. Local dev (no Docker): `go run ./cmd/cablegui` against `./data/cablegui.db`. ## Seed drawing — visual grammar reference, **not** a runtime importer `mxdrw.msbls.de/draw/Cable-Management.excalidraw` is **reference material only**. CableGUI does **not** auto-ingest it. m will rebuild LOFT and OFFICE from scratch inside the tool — the seed exists so the **exporter** mimics its visual grammar: - **Devices** = rectangles with a text label. - **Ports** = small ellipses (~12×9) positioned on a device edge. Positional, *not* containerId-bound. Stroke colour = cable type. - **Cables** = arrows with `startBinding` / `endBinding` to ports or devices or IO diamonds. - **Cable types** = colour, with a legend at the top-left of the project's first frame listing the project's cable_types. - **IO markers** = small diamonds. Semantically **wall outlets / power entry points** — a cable terminating at an IO marker means "this end is plugged into a wall socket outside the diagram". They are *not* inter-frame bridges and they do *not* pair up. - **Frames** = sub-zones inside a project (`desk`, `rack`, `media`, …). - **Lines** = decorative only (legend separators in the seed). Ignored on export. Legend colours (global, seeded once by migration 001): | Type | Hex | |---|---| | Power | `#e03131` | | USB | `#2f9e44` | | HDMI | `#1971c2` | | DP | `#9c36b5` | | RJ45 | `#ffd500` | ## Out of scope (v0) - Multi-user. CableGUI is m-only. - Auth / sharing — LAN-trusted on mDock. - Mobile / responsive — desktop browser only. - Cable inventory beyond visual structure (no length, no purchase history, no SKU). Strictly visual structure for v0. - Import from `.excalidraw` at runtime. If a one-shot migration is ever needed, a separate `cablegui-migrate` CLI tool is the right shape, not a hot API endpoint. ## Worker Preferences - **Inventor shifts** (design passes): conventions, schema, API, export pipeline, mDock deploy plan, UI flows, slices. Output: `docs/design.md` + open questions for m. v1–v4 are versioned in the doc's header callout. - **Coder shifts** (after m's go on a design version): build to the current design.md. Current state: slice 1 (project CRUD + global cable_types) and slice 2 (frames + devices + drag) are merged; design v4 reshapes slices 3+ (IO + cable-type editing → device-type catalog → device-type manage → connection-requirements UI → solver → manual port/cable draw → export). See `docs/design.md` §8 for the current sequence. - Use **Sonnet** for both — structure matters more than depth.