Full project rename per m's call. Single atomic commit because the codebase rename is a coupled change — go module path, env vars, DB default, Docker artefact names, and on-disk mDock paths all flip together. - go.mod: module mgit.msbls.de/m/mcables → mgit.msbls.de/m/cablegui - cmd/mcables → cmd/cablegui (git mv) - All Go imports rewritten to the new module path - Env vars: MCABLES_ADDR/MCABLES_DB → CABLEGUI_ADDR/CABLEGUI_DB - DB default path: data/mcables.db → data/cablegui.db - Dockerfile + docker-compose.yml: image, container_name, env vars, bind-mount /home/m/stacks/mcables → /home/m/stacks/cablegui, secrets /home/m/secrets/mcables → /home/m/secrets/cablegui - Makefile: bin target + run/build commands point at cmd/cablegui - .gitignore + .dockerignore: /mcables → /cablegui - README, docs/design.md, CLAUDE.md: prose + paths + image name - web/static/index.html: <title> + brand - web/static/main.js + web/web.go: header comment - internal/exporter: Scene.Source "mcables" → "cablegui" - internal/server/export.go: error-detail secrets path - internal/db/migrations/*.sql: header comments (mCables vN → CableGUI vN) Memory group_id kept as "mcables" to preserve existing memory continuity. Documented as historical in CLAUDE.md. go build ./... clean; go test -race ./... green
189 lines
9.3 KiB
Markdown
189 lines
9.3 KiB
Markdown
# 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/<name>.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
|
||
`<name>.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=<name>` 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>/<slug>` = 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.
|