feat(ui): static shell + router + api wrapper

Three-column grid (sidebar / main / right rail) with Cradle aesthetic:
blackflame accent on Cinzel display headings + Cormorant Garamond body
in cards, system UI for chrome. Hash-based router covers all entity
routes plus search, inbox, sacred-valley. api.js stores OWNER_TOKEN in
localStorage and prompts via a modal on 401. dom.js provides safe el()
+ mount() builders so no component ever assigns innerHTML from API data
(the only exception is an explicit, scary-named html: opt-in for
sanitizer output, used later by the markdown editor).

state.js is a tiny event bus for shared chrome state (pending count).
Components and views are loaded as ES modules — sidebar / topbar /
rightrail + 9 view stubs that the later Phase E tasks fill in.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-01 02:12:18 +10:00
parent 69e26ada98
commit 59ad86425d
21 changed files with 575 additions and 0 deletions

15
public/views/home.js Normal file
View File

@@ -0,0 +1,15 @@
// T17 stub — recent activity lands in T19.
import { el, mount } from '../dom.js';
export async function render(main) {
mount(main,
el('h1', { class: 'view-h1' }, 'The Void'),
el('p', { class: 'view-sub' }, 'Step into the abyss. Everything else loads as you navigate.'),
el('div', { class: 'card' },
el('h3', {}, 'Welcome'),
el('p', { class: 'muted' },
'Pick a space from the sidebar to start. The API is alive; the views populate as Phase E lands.'
)
)
);
}

9
public/views/inbox.js Normal file
View File

@@ -0,0 +1,9 @@
// T17 stub — full implementation lands in T21.
import { el, mount } from '../dom.js';
export async function render(main) {
mount(main,
el('h1', { class: 'view-h1' }, 'Inbox'),
el('p', { class: 'view-sub muted' }, 'Pending agent suggestions.'),
el('div', { class: 'card muted' }, 'Inbox view ships in T21.')
);
}

9
public/views/page.js Normal file
View File

@@ -0,0 +1,9 @@
// T17 stub — full implementation lands in T20.
import { el, mount } from '../dom.js';
export async function render(main, ctx) {
mount(main,
el('h1', { class: 'view-h1' }, 'Page'),
el('p', { class: 'view-sub muted' }, 'id: ' + (ctx.params.id || '—')),
el('div', { class: 'card muted' }, 'Page editor ships in T20.')
);
}

9
public/views/project.js Normal file
View File

@@ -0,0 +1,9 @@
// T17 stub — full implementation lands in T19.
import { el, mount } from '../dom.js';
export async function render(main, ctx) {
mount(main,
el('h1', { class: 'view-h1' }, 'Project'),
el('p', { class: 'view-sub muted' }, 'id: ' + (ctx.params.id || '—')),
el('div', { class: 'card muted' }, 'Project view ships in T19.')
);
}

View File

@@ -0,0 +1,9 @@
// T17 stub — full implementation lands in T20.
import { el, mount } from '../dom.js';
export async function render(main, ctx) {
mount(main,
el('h1', { class: 'view-h1' }, 'Reference'),
el('p', { class: 'view-sub muted' }, 'id: ' + (ctx.params.id || '—')),
el('div', { class: 'card muted' }, 'Reference detail ships in T20.')
);
}

9
public/views/resource.js Normal file
View File

@@ -0,0 +1,9 @@
// T17 stub — full implementation lands in T21.
import { el, mount } from '../dom.js';
export async function render(main, ctx) {
mount(main,
el('h1', { class: 'view-h1' }, 'Resource'),
el('p', { class: 'view-sub muted' }, 'id: ' + (ctx.params.id || '—')),
el('div', { class: 'card muted' }, 'Resource detail ships in T21.')
);
}

View File

@@ -0,0 +1,15 @@
// T17 stub — placeholder card per plan (full widgets in Plan 6).
import { el, mount } from '../dom.js';
export async function render(main) {
mount(main,
el('h1', { class: 'view-h1' }, 'Sacred Valley'),
el('p', { class: 'view-sub' }, 'The homelab dashboard. Widgets port over in Plan 6.'),
el('div', { class: 'card' },
el('h3', {}, 'Coming home'),
el('p', { class: 'muted' },
'Weather, speedtest, host-perf, media cards — all ride in from Void 1.x in Plan 6. ' +
'For now this card holds the route open so the sidebar link works.'
)
)
);
}

9
public/views/search.js Normal file
View File

@@ -0,0 +1,9 @@
// T17 stub — full implementation lands in T22.
import { el, mount } from '../dom.js';
export async function render(main, ctx) {
mount(main,
el('h1', { class: 'view-h1' }, 'Search'),
el('p', { class: 'view-sub muted' }, 'q: ' + (ctx.query.q || '—')),
el('div', { class: 'card muted' }, 'Search view ships in T22.')
);
}

9
public/views/space.js Normal file
View File

@@ -0,0 +1,9 @@
// T17 stub — full implementation lands in T19.
import { el, mount } from '../dom.js';
export async function render(main, ctx) {
mount(main,
el('h1', { class: 'view-h1' }, 'Space'),
el('p', { class: 'view-sub muted' }, 'id: ' + (ctx.params.id || '—')),
el('div', { class: 'card muted' }, 'Space view ships in T19.')
);
}