2026-05-22 10:04:24 -04:00
|
|
|
// screens-home.jsx
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
//
|
|
|
|
|
// Two routes share this file:
|
|
|
|
|
//
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
// • Home - the launcher. Big-button entry into each section of the MAM.
|
2026-05-26 23:10:23 -04:00
|
|
|
// Untouched in this rewrite.
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
//
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
// • Dashboard - the operations view. Rebuilt as a control-room status
|
2026-05-26 23:10:23 -04:00
|
|
|
// board, not a SaaS analytics page. Sections render top-down by
|
|
|
|
|
// operator priority:
|
|
|
|
|
//
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
// 1. ON AIR - live recorder tiles, full-width
|
|
|
|
|
// 2. UP NEXT - single-row strip of next scheduled recordings
|
|
|
|
|
// 3. ATTENTION - conditional; only when something failed
|
|
|
|
|
// 4. WORK + CLUSTER - two-column dense panels
|
|
|
|
|
// 5. STATUS BAR - single mono-text line, bottom
|
2026-05-26 23:10:23 -04:00
|
|
|
//
|
|
|
|
|
// Anything that would just say "all clear" is hidden, not rendered.
|
2026-05-22 08:15:35 -04:00
|
|
|
|
|
|
|
|
function Home({ navigate }) {
|
2026-05-30 11:59:27 -04:00
|
|
|
const [showDownloads, setShowDownloads] = React.useState(false);
|
2026-05-28 20:01:19 -04:00
|
|
|
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
// Pull live counts so the tile subtitles ("34 assets", "0 live", "3 running")
|
|
|
|
|
// reflect what's actually in the DB right now, not a stale boot-time cache.
|
|
|
|
|
const [cards, setCards] = React.useState({});
|
2026-05-30 10:59:59 -04:00
|
|
|
// Playout has no /metrics/home card yet (and the playout schema may not be
|
|
|
|
|
// migrated on every install); fetch /playout/channels separately and degrade
|
|
|
|
|
// silently — the tile just shows "No channels" if the endpoint isn't there.
|
|
|
|
|
const [playoutChannels, setPlayoutChannels] = React.useState(null);
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
React.useEffect(() => {
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
const load = () => {
|
|
|
|
|
window.ZAMPP_API.fetch('/metrics/home?hours=1')
|
|
|
|
|
.then(d => { if (!cancelled) setCards(d?.cards || {}); })
|
|
|
|
|
.catch(() => {});
|
2026-05-30 10:59:59 -04:00
|
|
|
window.ZAMPP_API.fetch('/playout/channels')
|
|
|
|
|
.then(d => { if (!cancelled) setPlayoutChannels(Array.isArray(d) ? d : []); })
|
|
|
|
|
.catch(() => { if (!cancelled) setPlayoutChannels([]); });
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
};
|
|
|
|
|
load();
|
|
|
|
|
const t = setInterval(load, 30_000);
|
|
|
|
|
return () => { cancelled = true; clearInterval(t); };
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const { ASSETS = [], RECORDERS = [], JOBS = [], NODES = [] } = window.ZAMPP_DATA || {};
|
|
|
|
|
const assetsTotal = cards.assets?.total ?? ASSETS.length;
|
|
|
|
|
const liveCount = cards.recorders?.live ?? RECORDERS.filter(r => r.status === 'recording').length;
|
|
|
|
|
const totalRecs = cards.recorders?.total ?? RECORDERS.length;
|
|
|
|
|
const runningJobs = cards.jobs?.running ?? JOBS.filter(j => j.status === 'running' || j.status === 'queued').length;
|
|
|
|
|
const failedJobs = cards.jobs?.failed_total ?? JOBS.filter(j => j.status === 'failed').length;
|
|
|
|
|
const nodesOnline = cards.cluster?.online ?? NODES.filter(n => n.status === 'online' || n.online === true).length;
|
|
|
|
|
const nodesTotal = cards.cluster?.total ?? NODES.length;
|
|
|
|
|
|
|
|
|
|
const tiles = [
|
|
|
|
|
{
|
|
|
|
|
id: 'library',
|
|
|
|
|
label: 'Library',
|
|
|
|
|
icon: 'library',
|
|
|
|
|
tone: 'accent',
|
|
|
|
|
sub: assetsTotal > 0 ? assetsTotal.toLocaleString() + ' assets' : 'No assets yet',
|
|
|
|
|
desc: 'Browse projects, bins, and assets. Hover-scrub previews.',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'recorders',
|
|
|
|
|
label: 'Recorders',
|
|
|
|
|
icon: 'record',
|
|
|
|
|
tone: 'live',
|
|
|
|
|
sub: liveCount > 0
|
|
|
|
|
? liveCount + ' live · ' + totalRecs + ' configured'
|
|
|
|
|
: totalRecs + ' configured',
|
|
|
|
|
desc: 'SDI · SRT · RTMP ingest. Start, stop, schedule.',
|
|
|
|
|
},
|
2026-05-30 10:59:59 -04:00
|
|
|
{
|
|
|
|
|
id: 'playout',
|
|
|
|
|
label: 'Playout',
|
|
|
|
|
icon: 'monitor',
|
|
|
|
|
tone: 'accent',
|
|
|
|
|
sub: (() => {
|
|
|
|
|
if (playoutChannels === null) return '·';
|
|
|
|
|
const total = playoutChannels.length;
|
|
|
|
|
const onAir = playoutChannels.filter(c => c.status === 'running').length;
|
|
|
|
|
if (total === 0) return 'No channels';
|
|
|
|
|
if (onAir > 0) return onAir + ' on air · ' + total + ' channel' + (total === 1 ? '' : 's');
|
|
|
|
|
return total + ' channel' + (total === 1 ? '' : 's');
|
|
|
|
|
})(),
|
|
|
|
|
desc: 'Master Control. SDI · NDI · SRT · RTMP playout, playlists, as-run.',
|
|
|
|
|
},
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
{
|
2026-05-30 11:59:27 -04:00
|
|
|
id: '__downloads',
|
|
|
|
|
label: 'Downloads',
|
|
|
|
|
icon: 'download',
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
tone: 'purple',
|
2026-05-30 11:59:27 -04:00
|
|
|
sub: 'Plugin · Teams ISO',
|
|
|
|
|
desc: 'Download the Premiere Pro UXP plugin and the Teams ISO installer.',
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'jobs',
|
|
|
|
|
label: 'Jobs',
|
|
|
|
|
icon: 'jobs',
|
|
|
|
|
tone: failedJobs > 0 ? 'warn' : 'success',
|
|
|
|
|
sub: runningJobs > 0
|
|
|
|
|
? runningJobs + ' running' + (failedJobs > 0 ? ' · ' + failedJobs + ' failed' : '')
|
|
|
|
|
: (failedJobs > 0 ? failedJobs + ' failed' : 'All clear'),
|
|
|
|
|
desc: 'Proxy + thumbnail queue. Retry failed jobs.',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'settings',
|
|
|
|
|
label: 'Settings',
|
|
|
|
|
icon: 'settings',
|
|
|
|
|
tone: 'neutral',
|
|
|
|
|
sub: 'S3 · Encoder · Growing files',
|
|
|
|
|
desc: 'Storage, proxy encoder, capture SDK, growing-file mode.',
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const clusterHealthy = !nodesTotal || nodesOnline >= nodesTotal;
|
|
|
|
|
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
// Activity strip (#153): live recorders + last-24h assets + alerts.
|
|
|
|
|
const liveRecorders = RECORDERS.filter(r => r.status === 'recording').slice(0, 4);
|
|
|
|
|
const recentAssets = (() => {
|
|
|
|
|
const dayAgo = Date.now() - 86400000;
|
|
|
|
|
return ASSETS
|
|
|
|
|
.filter(a => a.created_at && new Date(a.created_at).getTime() > dayAgo)
|
|
|
|
|
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at))
|
|
|
|
|
.slice(0, 6);
|
|
|
|
|
})();
|
|
|
|
|
const failedCount = JOBS.filter(j => j.status === 'failed').length;
|
|
|
|
|
const errCount = RECORDERS.filter(r => r.status === 'error').length;
|
|
|
|
|
const hasActivity = liveRecorders.length || recentAssets.length || failedCount || errCount;
|
|
|
|
|
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
return (
|
|
|
|
|
<div className="launcher">
|
|
|
|
|
<div className="launcher-inner">
|
|
|
|
|
<div className="launcher-hero">
|
|
|
|
|
<img
|
|
|
|
|
className="launcher-logo"
|
|
|
|
|
src="img/dragon-logo.png"
|
|
|
|
|
alt="Dragonflight"
|
|
|
|
|
draggable="false"
|
|
|
|
|
/>
|
|
|
|
|
<h1 className="launcher-wordmark">DRAGONFLIGHT</h1>
|
|
|
|
|
<p className="launcher-tagline">
|
|
|
|
|
Self-hosted broadcast media-asset management
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="launcher-grid">
|
|
|
|
|
{tiles.map(t => (
|
|
|
|
|
<button
|
|
|
|
|
key={t.id}
|
|
|
|
|
className={'launcher-tile tone-' + t.tone}
|
2026-05-30 11:59:27 -04:00
|
|
|
onClick={() => t.id === '__downloads' ? setShowDownloads(true) : navigate(t.id)}
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
>
|
|
|
|
|
<span className="launcher-tile-icon">
|
|
|
|
|
<Icon name={t.icon} size={26} />
|
|
|
|
|
</span>
|
|
|
|
|
<span className="launcher-tile-label">{t.label}</span>
|
|
|
|
|
<span className="launcher-tile-sub">{t.sub}</span>
|
|
|
|
|
<span className="launcher-tile-desc">{t.desc}</span>
|
|
|
|
|
<span className="launcher-tile-arrow">
|
|
|
|
|
<Icon name="arrowRight" size={14} />
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
className="launcher-tile tone-ghost launcher-tile-secondary"
|
|
|
|
|
onClick={() => navigate('dashboard')}
|
|
|
|
|
>
|
|
|
|
|
<span className="launcher-tile-icon">
|
|
|
|
|
<Icon name="home" size={22} />
|
|
|
|
|
</span>
|
|
|
|
|
<span className="launcher-tile-label">Dashboard</span>
|
|
|
|
|
<span className="launcher-tile-sub">Operations view</span>
|
|
|
|
|
<span className="launcher-tile-desc">
|
|
|
|
|
Recent activity, job queue, cluster health.
|
|
|
|
|
</span>
|
|
|
|
|
<span className="launcher-tile-arrow">
|
|
|
|
|
<Icon name="arrowRight" size={14} />
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
{hasActivity && (
|
|
|
|
|
<div className="launcher-activity">
|
|
|
|
|
{(failedCount > 0 || errCount > 0) && (
|
|
|
|
|
<div className="launcher-activity-strip alert">
|
|
|
|
|
<Icon name="alert" size={14} />
|
|
|
|
|
<span>
|
|
|
|
|
{errCount > 0 && <strong>{errCount} recorder{errCount === 1 ? '' : 's'} in error.</strong>}
|
|
|
|
|
{errCount > 0 && failedCount > 0 && ' '}
|
|
|
|
|
{failedCount > 0 && <strong>{failedCount} failed job{failedCount === 1 ? '' : 's'}.</strong>}
|
|
|
|
|
</span>
|
|
|
|
|
<button className="btn ghost sm" onClick={() => navigate('dashboard')}>Open Dashboard</button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{liveRecorders.length > 0 && (
|
|
|
|
|
<div className="launcher-activity-section">
|
|
|
|
|
<div className="launcher-activity-head">
|
|
|
|
|
<span className="rec-dot" />
|
|
|
|
|
Recording now
|
|
|
|
|
<span className="muted">{liveRecorders.length} live</span>
|
|
|
|
|
<div style={{ flex: 1 }} />
|
|
|
|
|
<button className="btn ghost sm" onClick={() => navigate('monitors')}>Monitors</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="launcher-activity-grid">
|
|
|
|
|
{liveRecorders.map(r => (
|
|
|
|
|
<button key={r.id} className="launcher-activity-item" onClick={() => navigate('recorders')}>
|
|
|
|
|
<span className="badge live">REC</span>
|
|
|
|
|
<span className="launcher-activity-item-name">{r.name}</span>
|
|
|
|
|
<span className="launcher-activity-item-meta mono">{r.source_type || 'sdi'}</span>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{recentAssets.length > 0 && (
|
|
|
|
|
<div className="launcher-activity-section">
|
|
|
|
|
<div className="launcher-activity-head">
|
|
|
|
|
<Icon name="library" size={12} />
|
|
|
|
|
Last 24 hours
|
|
|
|
|
<span className="muted">{recentAssets.length} new asset{recentAssets.length === 1 ? '' : 's'}</span>
|
|
|
|
|
<div style={{ flex: 1 }} />
|
|
|
|
|
<button className="btn ghost sm" onClick={() => navigate('library')}>Library</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="launcher-activity-grid">
|
|
|
|
|
{recentAssets.map(a => (
|
|
|
|
|
<button key={a.id} className="launcher-activity-item" onClick={() => navigate('library')}>
|
|
|
|
|
<Icon name={a.media_type === 'audio' ? 'audio' : 'video'} size={13} />
|
|
|
|
|
<span className="launcher-activity-item-name">{a.display_name || a.filename || 'untitled'}</span>
|
|
|
|
|
<span className="launcher-activity-item-meta mono">
|
|
|
|
|
{(() => {
|
|
|
|
|
const mins = Math.round((Date.now() - new Date(a.created_at)) / 60000);
|
|
|
|
|
if (mins < 60) return mins + 'm';
|
|
|
|
|
const h = Math.round(mins / 60);
|
|
|
|
|
return h + 'h';
|
|
|
|
|
})()}
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
<div className="launcher-status">
|
|
|
|
|
<span className="launcher-status-pip">
|
|
|
|
|
<span
|
|
|
|
|
className="dot"
|
|
|
|
|
style={{ background: clusterHealthy ? 'var(--success)' : 'var(--warning)' }}
|
|
|
|
|
/>
|
|
|
|
|
{clusterHealthy ? 'cluster healthy' : 'cluster degraded'}
|
|
|
|
|
<span className="muted">· {nodesOnline}/{nodesTotal || 0} node{nodesTotal === 1 ? '' : 's'}</span>
|
|
|
|
|
</span>
|
|
|
|
|
{liveCount > 0 && (
|
|
|
|
|
<span className="launcher-status-pip live">
|
|
|
|
|
<span className="dot" style={{ background: 'var(--live)' }} />
|
|
|
|
|
{liveCount} recorder{liveCount === 1 ? '' : 's'} live
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-30 11:59:27 -04:00
|
|
|
{showDownloads && <DownloadsModal onClose={() => setShowDownloads(false)} />}
|
2026-05-28 20:01:19 -04:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-30 11:59:27 -04:00
|
|
|
// Modal listing all downloads: the Premiere Pro UXP plugin (.ccx, one per
|
|
|
|
|
// released version, sourced from window.PREMIERE_RELEASES written by the
|
|
|
|
|
// Settings → SDKs section in screens-admin.jsx) plus the Teams ISO installer
|
|
|
|
|
// (window.TEAMS_ISO; the .exe slot is wired but the file may still be pending).
|
|
|
|
|
function DownloadsModal({ onClose }) {
|
|
|
|
|
const teamsIso = window.TEAMS_ISO || {};
|
2026-05-28 20:01:19 -04:00
|
|
|
const releases = (window.PREMIERE_RELEASES || []).slice().sort((a, b) => {
|
|
|
|
|
// Newest first; fall back to lexicographic compare on version string.
|
|
|
|
|
const av = String(a.version || ''), bv = String(b.version || '');
|
|
|
|
|
return bv.localeCompare(av, undefined, { numeric: true });
|
|
|
|
|
});
|
|
|
|
|
const latest = window.PREMIERE_LATEST || releases[0] || null;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="modal-backdrop" onClick={onClose}>
|
|
|
|
|
<div className="modal" onClick={(e) => e.stopPropagation()} style={{ maxWidth: 560 }}>
|
|
|
|
|
<div className="modal-head">
|
|
|
|
|
<div>
|
2026-05-30 11:59:27 -04:00
|
|
|
<div style={{ fontSize: 15, fontWeight: 600 }}>Downloads</div>
|
2026-05-28 20:01:19 -04:00
|
|
|
<div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 2 }}>
|
2026-05-30 11:59:27 -04:00
|
|
|
The Premiere Pro (UXP) plugin and the Teams ISO installer. Install the .ccx per workstation via the Adobe UXP Developer Tool, or double-click it with Creative Cloud installed.
|
2026-05-28 20:01:19 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<button className="icon-btn" aria-label="Close" onClick={onClose}><Icon name="x" /></button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="modal-body">
|
2026-05-30 11:59:27 -04:00
|
|
|
<div className="premiere-release">
|
|
|
|
|
<div className="premiere-release-head">
|
|
|
|
|
<span className="premiere-release-version mono">Teams ISO</span>
|
|
|
|
|
{teamsIso.version && (
|
|
|
|
|
<span className="premiere-release-date mono">v{teamsIso.version}</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="premiere-release-notes">
|
|
|
|
|
Windows installer for the Teams ISO workstation build.
|
|
|
|
|
</div>
|
|
|
|
|
<div className="premiere-release-actions">
|
|
|
|
|
{teamsIso.available && teamsIso.url ? (
|
|
|
|
|
<a href={teamsIso.url} download className="btn primary sm">
|
|
|
|
|
<Icon name="download" />Teams ISO (.exe)
|
|
|
|
|
</a>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<span className="btn primary sm" aria-disabled="true" style={{ opacity: 0.5, pointerEvents: 'none' }}>
|
|
|
|
|
<Icon name="download" />Teams ISO (.exe)
|
|
|
|
|
</span>
|
|
|
|
|
<span style={{ fontSize: 11.5, color: 'var(--text-3)' }}>coming soon — file pending</span>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-28 20:01:19 -04:00
|
|
|
{releases.length === 0 && (
|
|
|
|
|
<div style={{ padding: '24px 0', textAlign: 'center', color: 'var(--text-3)', fontSize: 12 }}>
|
|
|
|
|
No releases registered yet. Upload one from Settings → Capture SDKs.
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{releases.map((rel, i) => (
|
|
|
|
|
<div key={rel.version || i} className="premiere-release">
|
|
|
|
|
<div className="premiere-release-head">
|
|
|
|
|
<span className="premiere-release-version mono">v{rel.version}</span>
|
|
|
|
|
{latest && latest.version === rel.version && (
|
|
|
|
|
<span className="badge accent">LATEST</span>
|
|
|
|
|
)}
|
|
|
|
|
{rel.released_at && (
|
|
|
|
|
<span className="premiere-release-date mono">
|
|
|
|
|
{new Date(rel.released_at).toLocaleDateString()}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
{rel.notes && <div className="premiere-release-notes">{rel.notes}</div>}
|
|
|
|
|
<div className="premiere-release-actions">
|
2026-05-29 14:20:46 -04:00
|
|
|
{rel.ccx && (
|
|
|
|
|
<a href={rel.ccx} download className="btn primary sm">
|
|
|
|
|
<Icon name="download" />UXP plugin (.ccx)
|
2026-05-28 20:01:19 -04:00
|
|
|
</a>
|
|
|
|
|
)}
|
|
|
|
|
{rel.installer && (
|
|
|
|
|
<a href={rel.installer} download className="btn ghost sm">
|
|
|
|
|
<Icon name="download" />Windows installer
|
|
|
|
|
</a>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="modal-foot">
|
|
|
|
|
<div style={{ flex: 1, fontSize: 11.5, color: 'var(--text-3)' }}>
|
|
|
|
|
Need help installing? Use the Adobe Extension Manager or UPIA.
|
|
|
|
|
</div>
|
|
|
|
|
<button className="btn ghost" onClick={onClose}>Close</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
// Dashboard - broadcast-ops control board
|
2026-05-26 23:10:23 -04:00
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
function Dashboard({ navigate }) {
|
2026-05-26 23:10:23 -04:00
|
|
|
const { RECORDERS, JOBS, NODES } = window.ZAMPP_DATA;
|
2026-05-22 10:04:24 -04:00
|
|
|
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
// Live state - recompute every second so elapsed timers keep ticking.
|
2026-05-26 23:10:23 -04:00
|
|
|
const [tick, setTick] = React.useState(0);
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
const i = setInterval(() => setTick(t => t + 1), 1000);
|
|
|
|
|
return () => clearInterval(i);
|
|
|
|
|
}, []);
|
2026-05-22 08:15:35 -04:00
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
// Upcoming schedule for UP NEXT strip.
|
|
|
|
|
const [upcoming, setUpcoming] = React.useState([]);
|
2026-05-22 23:30:10 -04:00
|
|
|
React.useEffect(() => {
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
const load = () => {
|
2026-05-26 23:10:23 -04:00
|
|
|
window.ZAMPP_API.fetch('/schedules?status=upcoming')
|
|
|
|
|
.then(d => { if (!cancelled) setUpcoming(d?.schedules || []); })
|
2026-05-22 23:30:10 -04:00
|
|
|
.catch(() => {});
|
|
|
|
|
};
|
|
|
|
|
load();
|
|
|
|
|
const t = setInterval(load, 30_000);
|
|
|
|
|
return () => { cancelled = true; clearInterval(t); };
|
|
|
|
|
}, []);
|
2026-05-22 10:55:18 -04:00
|
|
|
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
// Refresh jobs frequently - this screen is the failed-job alert surface.
|
2026-05-26 23:10:23 -04:00
|
|
|
const [jobs, setJobs] = React.useState(JOBS);
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
const load = () => {
|
|
|
|
|
window.ZAMPP_API.fetch('/jobs')
|
|
|
|
|
.then(raw => {
|
|
|
|
|
if (cancelled || !Array.isArray(raw)) return;
|
|
|
|
|
const statusMap = { waiting: 'queued', active: 'running', completed: 'done', failed: 'failed' };
|
|
|
|
|
const kindMap = { proxy: 'Proxy', thumbnail: 'Thumbnail', conform: 'Conform', transcode: 'Transcode', import: 'YouTube' };
|
|
|
|
|
const norm = raw.map(j => {
|
|
|
|
|
const meta = j.metadata || {};
|
|
|
|
|
return {
|
|
|
|
|
...j,
|
|
|
|
|
status: statusMap[j.status] || j.status,
|
|
|
|
|
kind: kindMap[j.type] || j.type || 'Job',
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
asset: j.asset_name || meta.filename || '·',
|
|
|
|
|
node: meta.node || '·',
|
2026-05-26 23:10:23 -04:00
|
|
|
error: j.error || null,
|
|
|
|
|
progress: j.progress || 0,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
window.ZAMPP_DATA.JOBS = norm;
|
|
|
|
|
setJobs(norm);
|
|
|
|
|
})
|
|
|
|
|
.catch(() => {});
|
|
|
|
|
};
|
|
|
|
|
load();
|
|
|
|
|
const t = setInterval(load, 5_000);
|
|
|
|
|
return () => { cancelled = true; clearInterval(t); };
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const liveRecorders = RECORDERS.filter(r => r.status === 'recording');
|
|
|
|
|
const erroredRecorders = RECORDERS.filter(r => r.status === 'error');
|
|
|
|
|
const runningJobs = jobs.filter(j => j.status === 'running');
|
|
|
|
|
const queuedJobs = jobs.filter(j => j.status === 'queued');
|
|
|
|
|
const failedJobs = jobs.filter(j => j.status === 'failed');
|
|
|
|
|
const doneJobs = jobs.filter(j => j.status === 'done');
|
|
|
|
|
const offlineNodes = NODES.filter(n => !(n.status === 'online' || n.online === true));
|
|
|
|
|
const onlineNodes = NODES.length - offlineNodes.length;
|
2026-05-22 23:30:10 -04:00
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
// Next 3 upcoming, sorted, future only
|
|
|
|
|
const nowMs = Date.now();
|
|
|
|
|
const nextUp = upcoming
|
|
|
|
|
.filter(s => new Date(s.start_at).getTime() > nowMs && s.status === 'pending')
|
|
|
|
|
.slice(0, 3);
|
2026-05-22 23:30:10 -04:00
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
const hasAttention = failedJobs.length > 0 || offlineNodes.length > 0 || erroredRecorders.length > 0;
|
2026-05-22 08:15:35 -04:00
|
|
|
|
|
|
|
|
return (
|
2026-05-26 23:10:23 -04:00
|
|
|
<div className="page dash">
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
<div className="page-header">
|
|
|
|
|
<h1>Dashboard</h1>
|
|
|
|
|
<span className="subtitle">Live operations: on-air recorders, jobs, cluster health</span>
|
|
|
|
|
<div className="spacer" />
|
|
|
|
|
{hasAttention && (
|
|
|
|
|
<span className="badge danger" title="Items need attention">
|
|
|
|
|
<Icon name="alert" size={10} />
|
|
|
|
|
{failedJobs.length + offlineNodes.length + erroredRecorders.length} alert{failedJobs.length + offlineNodes.length + erroredRecorders.length === 1 ? '' : 's'}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
<span className="status-pip">
|
|
|
|
|
<span className="dot" style={{ background: offlineNodes.length === 0 ? 'var(--success)' : 'var(--warning)' }} />
|
|
|
|
|
<span>{onlineNodes}/{NODES.length || 0} nodes online</span>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
{/* ────────── ON AIR ────────── */}
|
|
|
|
|
<section className="dash-section">
|
|
|
|
|
<DashSectionHead
|
|
|
|
|
title="On air"
|
|
|
|
|
accent="live"
|
|
|
|
|
count={liveRecorders.length}
|
|
|
|
|
countLabel={liveRecorders.length === 1 ? 'recorder live' : 'recorders live'}
|
|
|
|
|
onMore={() => navigate('recorders')}
|
|
|
|
|
moreLabel="All recorders"
|
|
|
|
|
/>
|
|
|
|
|
{liveRecorders.length === 0 ? (
|
|
|
|
|
<DashInlineEmpty
|
|
|
|
|
icon="record"
|
|
|
|
|
text="No recorders on air."
|
|
|
|
|
cta="Start a recorder"
|
|
|
|
|
onCta={() => navigate('recorders')}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="dash-onair-grid" data-count={Math.min(liveRecorders.length, 4)}>
|
|
|
|
|
{liveRecorders.slice(0, 4).map(r => (
|
|
|
|
|
<OnAirTile key={r.id} recorder={r} onClick={() => navigate('recorders')} />
|
|
|
|
|
))}
|
2026-05-26 22:54:45 -04:00
|
|
|
</div>
|
2026-05-26 23:10:23 -04:00
|
|
|
)}
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
{/* ────────── UP NEXT ────────── */}
|
|
|
|
|
{nextUp.length > 0 && (
|
|
|
|
|
<section className="dash-section">
|
|
|
|
|
<DashSectionHead
|
|
|
|
|
title="Up next"
|
|
|
|
|
accent="accent"
|
|
|
|
|
count={nextUp.length}
|
|
|
|
|
countLabel="scheduled"
|
|
|
|
|
onMore={() => navigate('schedule')}
|
|
|
|
|
moreLabel="Schedule"
|
|
|
|
|
/>
|
|
|
|
|
<div className="dash-next-row">
|
|
|
|
|
{nextUp.map(s => (
|
|
|
|
|
<UpNextCard key={s.id} schedule={s} />
|
|
|
|
|
))}
|
2026-05-26 22:54:45 -04:00
|
|
|
</div>
|
2026-05-26 23:10:23 -04:00
|
|
|
</section>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* ────────── ATTENTION ────────── */}
|
|
|
|
|
{hasAttention && (
|
|
|
|
|
<section className="dash-section dash-attention">
|
|
|
|
|
<DashSectionHead
|
|
|
|
|
title="Needs attention"
|
|
|
|
|
accent="danger"
|
|
|
|
|
count={failedJobs.length + offlineNodes.length + erroredRecorders.length}
|
|
|
|
|
countLabel="items"
|
|
|
|
|
/>
|
|
|
|
|
<div className="panel dash-attention-panel">
|
|
|
|
|
{erroredRecorders.map(r => (
|
|
|
|
|
<AttentionRow
|
|
|
|
|
key={'r-' + r.id}
|
|
|
|
|
level="danger"
|
|
|
|
|
icon="record"
|
|
|
|
|
title={r.name}
|
|
|
|
|
detail={'recorder error' + (r.error_message ? ' · ' + r.error_message : '')}
|
|
|
|
|
onClick={() => navigate('recorders')}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
{offlineNodes.map(n => (
|
|
|
|
|
<AttentionRow
|
|
|
|
|
key={'n-' + (n.id || n.hostname)}
|
|
|
|
|
level="warning"
|
|
|
|
|
icon="cluster"
|
|
|
|
|
title={n.hostname || n.id}
|
|
|
|
|
detail="node offline · no heartbeat for >2 min"
|
|
|
|
|
onClick={() => navigate('cluster')}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
{failedJobs.slice(0, 5).map(j => (
|
|
|
|
|
<AttentionRow
|
|
|
|
|
key={'j-' + j.id}
|
|
|
|
|
level="danger"
|
|
|
|
|
icon="alert"
|
|
|
|
|
title={j.kind + ' failed'}
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
detail={(j.asset || '·') + (j.error ? ' · ' + j.error.slice(0, 100) : '')}
|
2026-05-26 23:10:23 -04:00
|
|
|
onClick={() => navigate('jobs')}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
{failedJobs.length > 5 && (
|
|
|
|
|
<div className="dash-attention-more" onClick={() => navigate('jobs')}>
|
|
|
|
|
+{failedJobs.length - 5} more failed jobs
|
|
|
|
|
<Icon name="arrowRight" size={12} />
|
|
|
|
|
</div>
|
2026-05-22 23:30:10 -04:00
|
|
|
)}
|
|
|
|
|
</div>
|
2026-05-26 23:10:23 -04:00
|
|
|
</section>
|
|
|
|
|
)}
|
2026-05-22 08:15:35 -04:00
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
{/* ────────── WORK + CLUSTER ────────── */}
|
|
|
|
|
<section className="dash-grid">
|
|
|
|
|
{/* JOB QUEUE */}
|
|
|
|
|
<div className="dash-col">
|
|
|
|
|
<DashSectionHead
|
|
|
|
|
title="Job queue"
|
|
|
|
|
count={runningJobs.length + queuedJobs.length}
|
|
|
|
|
countLabel="active"
|
|
|
|
|
onMore={() => navigate('jobs')}
|
|
|
|
|
moreLabel="All jobs"
|
|
|
|
|
/>
|
|
|
|
|
<div className="panel dash-jobs-panel">
|
|
|
|
|
{jobs.length === 0 ? (
|
|
|
|
|
<div className="dash-panel-empty">
|
|
|
|
|
<Icon name="check" size={14} />
|
|
|
|
|
Queue clear · {doneJobs.length} done
|
2026-05-22 08:15:35 -04:00
|
|
|
</div>
|
2026-05-26 23:10:23 -04:00
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<div className="dash-jobs-head">
|
|
|
|
|
<span></span>
|
|
|
|
|
<span>Job</span>
|
|
|
|
|
<span>Asset</span>
|
|
|
|
|
<span>Progress</span>
|
2026-05-22 08:15:35 -04:00
|
|
|
</div>
|
2026-05-26 23:10:23 -04:00
|
|
|
{/* Running jobs first (with bars), then queued, then a few recent done */}
|
|
|
|
|
{[...runningJobs, ...queuedJobs, ...doneJobs.slice(0, Math.max(0, 6 - runningJobs.length - queuedJobs.length))]
|
|
|
|
|
.slice(0, 8)
|
|
|
|
|
.map(j => <DashJobRow key={j.id} job={j} />)}
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2026-05-22 08:15:35 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
{/* CLUSTER */}
|
|
|
|
|
<div className="dash-col">
|
|
|
|
|
<DashSectionHead
|
|
|
|
|
title="Cluster"
|
|
|
|
|
count={onlineNodes}
|
|
|
|
|
countLabel={NODES.length ? 'of ' + NODES.length + ' online' : 'no nodes'}
|
|
|
|
|
onMore={() => navigate('cluster')}
|
|
|
|
|
moreLabel="All nodes"
|
|
|
|
|
/>
|
|
|
|
|
<div className="panel dash-cluster-panel">
|
|
|
|
|
{NODES.length === 0 ? (
|
|
|
|
|
<div className="dash-panel-empty">
|
|
|
|
|
<Icon name="alert" size={14} />
|
|
|
|
|
No nodes registered
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<div className="dash-cluster-head">
|
|
|
|
|
<span></span>
|
|
|
|
|
<span>Host</span>
|
|
|
|
|
<span>CPU</span>
|
|
|
|
|
<span>Mem</span>
|
|
|
|
|
</div>
|
|
|
|
|
{NODES.slice(0, 6).map(n => <DashClusterRow key={n.id || n.hostname || n.name} node={n} />)}
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2026-05-22 08:15:35 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-26 23:10:23 -04:00
|
|
|
</section>
|
|
|
|
|
|
2026-05-28 21:04:24 -04:00
|
|
|
{/* ────────── RESOURCES ────────── */}
|
|
|
|
|
<section className="dash-section">
|
|
|
|
|
<DashSectionHead title="Resources" />
|
|
|
|
|
{window.ClusterResources && <window.ClusterResources />}
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
{/* ────────── RESOURCES ────────── */}
|
|
|
|
|
<section className="dash-section">
|
|
|
|
|
<DashSectionHead title="Resources" />
|
|
|
|
|
{window.ClusterResources && React.createElement(window.ClusterResources)}
|
|
|
|
|
</section>
|
|
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
{/* ────────── STATUS BAR (bottom) ────────── */}
|
|
|
|
|
<footer className="dash-statusbar">
|
|
|
|
|
<span className="dash-stat-pip" data-tone={liveRecorders.length > 0 ? 'live' : 'idle'}>
|
|
|
|
|
<span className="dash-pip-dot" />
|
|
|
|
|
<span className="dash-pip-num">{liveRecorders.length}</span>
|
|
|
|
|
<span className="dash-pip-label">live</span>
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-statusbar-sep">·</span>
|
|
|
|
|
<span className="dash-stat-pip" data-tone={runningJobs.length > 0 ? 'accent' : 'idle'}>
|
|
|
|
|
<span className="dash-pip-num">{runningJobs.length}</span>
|
|
|
|
|
<span className="dash-pip-label">running</span>
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-statusbar-sep">·</span>
|
|
|
|
|
<span className="dash-stat-pip">
|
|
|
|
|
<span className="dash-pip-num">{queuedJobs.length}</span>
|
|
|
|
|
<span className="dash-pip-label">queued</span>
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-statusbar-sep">·</span>
|
|
|
|
|
<span className="dash-stat-pip" data-tone={failedJobs.length > 0 ? 'warning' : 'idle'}>
|
|
|
|
|
<span className="dash-pip-num">{failedJobs.length}</span>
|
|
|
|
|
<span className="dash-pip-label">failed</span>
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-statusbar-spacer" />
|
|
|
|
|
<span className="dash-stat-pip" data-tone={offlineNodes.length === 0 ? 'success' : 'warning'}>
|
|
|
|
|
<span className="dash-pip-num">{onlineNodes}/{NODES.length}</span>
|
|
|
|
|
<span className="dash-pip-label">nodes online</span>
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-statusbar-sep">·</span>
|
|
|
|
|
<span className="dash-statusbar-clock">{new Date(nowMs).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' })}</span>
|
|
|
|
|
</footer>
|
2026-05-22 08:15:35 -04:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
|
// Subcomponents
|
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
function DashSectionHead({ title, accent, count, countLabel, onMore, moreLabel }) {
|
2026-05-26 22:54:45 -04:00
|
|
|
return (
|
2026-05-26 23:10:23 -04:00
|
|
|
<div className="dash-sectionhead" data-accent={accent || 'neutral'}>
|
|
|
|
|
<span className="dash-sectionhead-title">{title}</span>
|
|
|
|
|
{typeof count === 'number' && (
|
|
|
|
|
<span className="dash-sectionhead-count">
|
|
|
|
|
<span className="dash-sectionhead-num">{count}</span>
|
|
|
|
|
{countLabel && <span className="dash-sectionhead-label">{countLabel}</span>}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{onMore && (
|
|
|
|
|
<button className="dash-sectionhead-more" onClick={onMore}>
|
|
|
|
|
{moreLabel || 'View all'}
|
|
|
|
|
<Icon name="arrowRight" size={11} />
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
2026-05-26 22:54:45 -04:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
function DashInlineEmpty({ icon, text, cta, onCta }) {
|
2026-05-22 08:15:35 -04:00
|
|
|
return (
|
2026-05-26 23:10:23 -04:00
|
|
|
<div className="dash-inline-empty">
|
|
|
|
|
<Icon name={icon} size={13} />
|
|
|
|
|
<span>{text}</span>
|
|
|
|
|
{cta && onCta && (
|
|
|
|
|
<button className="dash-inline-empty-cta" onClick={onCta}>
|
|
|
|
|
{cta} <Icon name="arrowRight" size={11} />
|
2026-05-22 08:15:35 -04:00
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
function OnAirTile({ recorder, onClick }) {
|
2026-05-22 08:15:35 -04:00
|
|
|
return (
|
2026-05-26 23:10:23 -04:00
|
|
|
<div className="dash-onair-tile" onClick={onClick}>
|
|
|
|
|
<div className="dash-onair-video">
|
|
|
|
|
{recorder.live_asset_id
|
2026-05-28 23:20:02 -04:00
|
|
|
? <HlsPreview assetId={recorder.live_asset_id} recorderId={recorder.id} />
|
2026-05-26 23:10:23 -04:00
|
|
|
: <FauxFrame />}
|
|
|
|
|
<span className="dash-onair-rec-pip">
|
|
|
|
|
<span className="dash-onair-rec-dot" />
|
|
|
|
|
REC
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-onair-time">{recorder.elapsed || '00:00:00'}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="dash-onair-meta">
|
|
|
|
|
<div className="dash-onair-name">{recorder.name}</div>
|
|
|
|
|
<div className="dash-onair-sub">
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
<span className="dash-onair-source">{recorder.source || '·'}</span>
|
|
|
|
|
{recorder.res && recorder.res !== '·' && (
|
2026-05-26 23:10:23 -04:00
|
|
|
<>
|
|
|
|
|
<span className="dash-onair-dot">·</span>
|
|
|
|
|
<span className="dash-onair-res">{recorder.res}</span>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
{recorder.codec && recorder.codec !== '·' && (
|
2026-05-26 23:10:23 -04:00
|
|
|
<>
|
|
|
|
|
<span className="dash-onair-dot">·</span>
|
|
|
|
|
<span className="dash-onair-codec">{recorder.codec}</span>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2026-05-22 08:15:35 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-26 23:10:23 -04:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function UpNextCard({ schedule }) {
|
|
|
|
|
const start = new Date(schedule.start_at);
|
|
|
|
|
const end = new Date(schedule.end_at);
|
|
|
|
|
const nowMs = Date.now();
|
|
|
|
|
const inMs = start.getTime() - nowMs;
|
|
|
|
|
const inMin = Math.round(inMs / 60_000);
|
|
|
|
|
const durMin = Math.round((end - start) / 60_000);
|
|
|
|
|
|
|
|
|
|
let relative;
|
|
|
|
|
if (inMin < 1) relative = 'starting now';
|
|
|
|
|
else if (inMin < 60) relative = 'in ' + inMin + 'm';
|
|
|
|
|
else if (inMin < 1440) relative = 'in ' + Math.round(inMin / 60) + 'h';
|
|
|
|
|
else relative = 'in ' + Math.round(inMin / 1440) + 'd';
|
|
|
|
|
|
|
|
|
|
const imminent = inMin <= 5;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className={'dash-next-card' + (imminent ? ' imminent' : '')}>
|
|
|
|
|
<div className="dash-next-time">
|
|
|
|
|
<span className="dash-next-clock">
|
|
|
|
|
{start.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-next-rel">{relative}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="dash-next-body">
|
|
|
|
|
<div className="dash-next-name">{schedule.name}</div>
|
|
|
|
|
<div className="dash-next-sub">
|
|
|
|
|
<Icon name="record" size={11} />
|
|
|
|
|
<span>{schedule.recorder_name || 'unbound'}</span>
|
|
|
|
|
<span className="dash-onair-dot">·</span>
|
|
|
|
|
<span>{durMin}m</span>
|
|
|
|
|
</div>
|
2026-05-22 08:15:35 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 23:10:23 -04:00
|
|
|
function AttentionRow({ level, icon, title, detail, onClick }) {
|
|
|
|
|
return (
|
|
|
|
|
<div className={'dash-attention-row level-' + level} onClick={onClick}>
|
|
|
|
|
<span className="dash-attention-icon"><Icon name={icon} size={13} /></span>
|
|
|
|
|
<span className="dash-attention-title">{title}</span>
|
|
|
|
|
<span className="dash-attention-detail">{detail}</span>
|
|
|
|
|
<Icon name="arrowRight" size={12} className="dash-attention-arrow" />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function DashJobRow({ job }) {
|
|
|
|
|
const iconMap = { Proxy: 'proxy', Transcode: 'film', Thumbnail: 'image', Conform: 'layers', YouTube: 'download' };
|
|
|
|
|
return (
|
|
|
|
|
<div className="dash-jobs-row" data-status={job.status}>
|
|
|
|
|
<span className="dash-jobs-status">
|
|
|
|
|
<StatusDot status={job.status} />
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-jobs-kind">
|
|
|
|
|
<Icon name={iconMap[job.kind] || 'jobs'} size={12} />
|
|
|
|
|
<span>{job.kind}</span>
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-jobs-asset">{job.asset}</span>
|
|
|
|
|
<span className="dash-jobs-progress">
|
|
|
|
|
{job.status === 'running' && (
|
|
|
|
|
<>
|
|
|
|
|
<span className="dash-jobs-bar">
|
|
|
|
|
<span className="dash-jobs-bar-fill" style={{ width: Math.round(job.progress || 0) + '%' }} />
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-jobs-pct">{Math.round(job.progress || 0)}%</span>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{job.status === 'queued' && <span className="dash-jobs-state">queued</span>}
|
|
|
|
|
{job.status === 'done' && <span className="dash-jobs-state done">done</span>}
|
|
|
|
|
{job.status === 'failed' && <span className="dash-jobs-state failed">failed</span>}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function DashClusterRow({ node }) {
|
|
|
|
|
const nodeId = node.hostname || node.id || node.name || 'node';
|
|
|
|
|
const isOnline = node.status === 'online' || node.online === true;
|
|
|
|
|
const cpuPct = node.cpu_percent ?? node.cpu ?? node.cpu_usage ?? null;
|
|
|
|
|
const memUsed = node.memory_used_gb ?? node.mem ?? (node.mem_used_mb != null ? node.mem_used_mb / 1024 : null);
|
|
|
|
|
const memTotal = node.memory_total_gb ?? node.mem_total_gb ?? null;
|
|
|
|
|
const memPct = memUsed != null && memTotal != null
|
|
|
|
|
? Math.round((memUsed / memTotal) * 100)
|
|
|
|
|
: (memUsed != null ? Math.min(100, Math.round((memUsed / 32) * 100)) : null);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="dash-cluster-row" data-online={isOnline}>
|
|
|
|
|
<span className="dash-cluster-status">
|
|
|
|
|
<StatusDot status={isOnline ? 'online' : 'offline'} />
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-cluster-name">
|
|
|
|
|
<span className="dash-cluster-host">{nodeId}</span>
|
|
|
|
|
{node.role && <span className="dash-cluster-role">{node.role}</span>}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-cluster-metric">
|
|
|
|
|
{cpuPct != null ? (
|
|
|
|
|
<>
|
|
|
|
|
<span className="dash-cluster-bar">
|
|
|
|
|
<span
|
|
|
|
|
className="dash-cluster-bar-fill"
|
|
|
|
|
style={{
|
|
|
|
|
width: Math.round(cpuPct) + '%',
|
|
|
|
|
background: cpuPct > 85 ? 'var(--warning)' : cpuPct > 60 ? 'var(--accent)' : 'var(--success)',
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-cluster-val">{Math.round(cpuPct)}%</span>
|
|
|
|
|
</>
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
) : <span className="dash-cluster-val muted">·</span>}
|
2026-05-26 23:10:23 -04:00
|
|
|
</span>
|
|
|
|
|
<span className="dash-cluster-metric">
|
|
|
|
|
{memPct != null ? (
|
|
|
|
|
<>
|
|
|
|
|
<span className="dash-cluster-bar">
|
|
|
|
|
<span
|
|
|
|
|
className="dash-cluster-bar-fill"
|
|
|
|
|
style={{
|
|
|
|
|
width: memPct + '%',
|
|
|
|
|
background: memPct > 85 ? 'var(--warning)' : 'var(--text-2)',
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</span>
|
|
|
|
|
<span className="dash-cluster-val">{memUsed < 1 ? Math.round(memUsed * 1024) + 'M' : memUsed.toFixed(1) + 'G'}</span>
|
|
|
|
|
</>
|
ui: full audit pass (fixes #146, #147, #148, #149, #151, #152, #153, #154, #155)
Sweep of 9 web-ui audit findings from tracker #156. Issue #150 (modal
codec stubs) deferred per user request.
## #146 sweep em-dashes (186 to 0)
- Replace placeholder '—' with '·' across all jsx
- Convert ' — ' to ': ' or '. ' in copy where context permits
- Comment-only em-dashes converted to ASCII dash
- Sweep css files too (16 comments)
## #147 remove glassmorphism + accent gradients
- Strip 8 backdrop-filter declarations from styles-screens.css and
styles-asset.css. Only legit modal scrim in styles-modal.css remains.
- Replace .job-progress-fill gradient with solid var(--accent)
- Replace .monitor-tile.audio gradient with flat var(--bg-1)
## #148 extract Jobs inline styles to CSS
- Cut 19 inline style={{...}} blocks in screens-jobs.jsx to 1 (dynamic
width on progress bar). Live DOM was 487 inline-styled elements due
to per-row repetition; now ~0.
- Added job-row-kind, job-row-asset, job-row-node, job-row-time,
job-row-actions, job-row-status-* utility classes in styles-screens.css
## #149 sidebar IA reorganized
- Replace flat NAV_TREE + ADMIN_TREE with NAV_SECTIONS:
Workspace / Ingest / Operations / Admin
- Move Capture out of Ingest into Operations (it's a live-signal monitor,
not an ingest action)
- Drop the 0/N capture badge from nav (belongs in topbar)
- Add BETA badge to Editor
## #151 redesign Editor 'Coming Soon' bumper
- Replace fullscreen glassmorphism + gradient + glow overlay with a flat
beta banner across the top of the editor area
- New .editor-beta-banner CSS class (flat, accent-soft tint, no blur)
## #152 hide Tokens parody, restore real API token mgmt
- New top-level Tokens admin page wraps existing ApiTokensSection
- Old parody renamed to TokensParody, accessible at /tokens-parody route
- Add window-level df:nav event for cross-component routing
## #153 make Home actually useful
- New activity strip below the launcher grid: 'Recording now' tiles for
live recorders, 'Last 24 hours' tiles for newly created assets, plus
an attention strip when there are failed jobs or errored recorders
- Each item is clickable and routes to the relevant screen
## #154 aria-labels on icon-only buttons
- Projects + Library grid/list view toggles now have aria-label + title
## #155 page-header pattern
- Dashboard now renders a proper .page-header h1 with subtitle + alert
badge + cluster status pip
- Library toolbar-title promoted to h1 for screen-reader hierarchy
- Document Home/Library/Editor full-bleed exceptions in DESIGN.md
- Editor's chrome is the beta banner (covered by #151)
2026-05-28 19:50:07 -04:00
|
|
|
) : <span className="dash-cluster-val muted">·</span>}
|
2026-05-26 23:10:23 -04:00
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 08:15:35 -04:00
|
|
|
window.Home = Home;
|
feat(home): restore launcher home page; move current home to Dashboard
The original first-version home page (big-button launcher with the
Dragonflight wordmark) is back at /. The Frame.io-style metrics +
recent-activity layout we've been treating as "home" is now the
Dashboard, reachable from the sidebar and from the launcher's
"Open dashboard" button.
- Renames existing Home → Dashboard (all the cards, sparklines, live
feed, job-queue, cluster mini-list are unchanged).
- New Home component: hero with the dragon-coiled-D logo (existing
img/dragon-logo.png), wordmark "DRAGONFLIGHT", a tag line, and 5
big tiles (Library, Recorders, Editor, Jobs, Settings) plus a
smaller Dashboard tile. Live cluster + recorder status pip at the
bottom mirrors what's in the topbar.
- The launcher pulls /metrics/home so the tile counts ("34 assets",
"0 live", "0 running") reflect reality.
2026-05-23 10:48:06 -04:00
|
|
|
window.Dashboard = Dashboard;
|