Files
CableGUI/CLAUDE.md
mAi c206a331ec rename: mCables → CableGUI (project + repo + image + paths)
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
2026-05-16 15:35:42 +02:00

9.3 KiB
Raw Permalink Blame History

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. v1v4 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.