feat: phase 1 MVP — sections, services, drag-drop, renderers

Backend: migration 0002 (sections/items/urls/settings), CRUD +
fractional reorder, icon upload, settings, export/import, cached
status pings. Handler tests cover reorder, URL scheme validation,
settings round-trip.

Frontend: shared board store over the generated client, dnd-kit
layer (same-section, cross-section, section reorder persisted),
URL chooser popover, add/edit service dialog, settings dialog,
Bento + Cards renderers behind the shared seam, dark/light theme.

Generated with [Devin](https://devin.ai)

Authored By: TDvorak <[email protected]>
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas Dvorak committed 2026-09-17 20:29:22 +02:00
1 parent b60821350a
commit 6004105038
37 files changed
+4187 -66

No files matched your search

@@ -0,0 +1,36 @@
-- +goose Up
-- Board tree: sections > items > urls. position REAL = fractional ordering;
-- a drag writes one row instead of renumbering the set.
CREATE TABLE sections (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
position REAL NOT NULL,
collapsed INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);
CREATE TABLE items (
id TEXT PRIMARY KEY,
section_id TEXT NOT NULL REFERENCES sections(id) ON DELETE CASCADE,
kind TEXT NOT NULL DEFAULT 'service' CHECK (kind IN ('service', 'widget')),
name TEXT NOT NULL,
icon TEXT NOT NULL DEFAULT '',
position REAL NOT NULL,
config JSON,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);
CREATE INDEX items_section_pos ON items(section_id, position);
CREATE TABLE urls (
id TEXT PRIMARY KEY,
item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
url TEXT NOT NULL,
label TEXT NOT NULL DEFAULT '',
position REAL NOT NULL
);
CREATE INDEX urls_item_pos ON urls(item_id, position);
-- +goose Down
DROP TABLE urls;
DROP TABLE items;
DROP TABLE sections;