Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ export default defineConfig({
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
use: {
...devices['Desktop Chrome'],
...(process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
? { launchOptions: { executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH } }
: {}),
},
},
],
webServer: {
Expand Down
3 changes: 3 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AgentOrchestrator } from './agent/AgentOrchestrator';
import { useFrameStore } from './store/store';
import { CommandPalette, useCommandPalette } from './components/dashboard/command-dispatch/CommandPalette';
import type { AppPage } from './store/types';
import { LocalDashboardPage } from './pages/LocalDashboardPage';

// OMN-12943 — ported event-dash views, lazily loaded so they add zero weight to
// the default dashboard bundle. Existing static page imports above are untouched.
Expand All @@ -30,6 +31,8 @@ function PageContent({ page }: { page: AppPage }) {
case 'experiments': return <Suspense fallback={null}><ExperimentsPage /></Suspense>;
case 'sea-control': return <Suspense fallback={null}><SeaControlPage /></Suspense>;
case 'lab': return <Suspense fallback={null}><LabPage /></Suspense>;
case 'local-overview': return <LocalDashboardPage pageName="overview" />;
case 'local-runs': return <LocalDashboardPage pageName="runs" />;
default: return <DashboardView />;
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/components/frame/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export function Sidebar() {
{!collapsed && (
<div className="sidebar-bottom-title" style={{ padding: '4px 12px 2px' }}>
<Text size="xs" color="tertiary" transform="uppercase" weight="semibold">
Event-driven runtime
Developer workspace
</Text>
</div>
)}
Expand All @@ -298,6 +298,8 @@ export function Sidebar() {
{ page: 'experiments' as AppPage, label: 'Experimentation', icon: <FlaskConical size={13} /> },
{ page: 'sea-control' as AppPage, label: 'Agent Workbench', icon: <Sparkles size={13} /> },
{ page: 'lab' as AppPage, label: 'Lab', icon: <Activity size={13} /> },
{ page: 'local-overview' as AppPage, label: 'Overview', icon: <Activity size={13} /> },
{ page: 'local-runs' as AppPage, label: 'Runs', icon: <GitBranch size={13} /> },
] as const
).map(({ page, label, icon }) => (
<div
Expand Down
137 changes: 137 additions & 0 deletions src/layout/local-page-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import type { ProjectionSnapshot, ProtocolSnapshotSource } from '../data-source/protocol-snapshot-source';
import type { ModelComponentContract } from '../shared/types/generated/onex-models';

export type LocalPageName = 'overview' | 'runs';
export type LocalPageEmptyState = 'NO_RUNS_YET' | 'BASELINE_UNRESOLVED';

/** Narrow dashboard fields consumed by this renderer; the YAML itself is a core config. */
export interface LocalDashboardView {
dashboard_id: string;
name: string;
description?: string | null;
layout: { columns: number; row_height: number; gap: number; responsive: boolean };
widgets: Array<Record<string, unknown>>;
refresh_interval_seconds?: number | null;
theme?: string;
}

export interface LocalPageDocument {
dashboard: LocalDashboardView;
components: ModelComponentContract[];
empty_state_reasons: LocalPageEmptyState[];
}

export interface LocalPageLoadOptions {
mode: 'http' | 'file';
availableTopics: ReadonlySet<string>;
}

export interface BoundProjectionSnapshot extends ProjectionSnapshot {
topic: string;
}

const pageFiles = import.meta.glob('../pages/local/*.page.yaml', {
eager: true,
query: '?raw',
import: 'default',
}) as Record<string, string>;
const contractFiles = import.meta.glob('../pages/local/*.contracts.yaml', {
eager: true,
query: '?raw',
import: 'default',
}) as Record<string, string>;

function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}

function isDashboardView(value: unknown): value is LocalDashboardView {
return isRecord(value)
&& typeof value.dashboard_id === 'string'
&& typeof value.name === 'string'
&& isRecord(value.layout)
&& Array.isArray(value.widgets);
}

function isComponentContract(value: unknown): value is ModelComponentContract {
if (!isRecord(value) || typeof value.component_id !== 'string' || typeof value.title !== 'string') return false;
if (!['chart', 'table', 'metric_card', 'status_grid', 'event_feed'].includes(String(value.component_kind))) return false;
if (!isRecord(value.contract_version) || !Array.isArray(value.data_bindings)) return false;
return value.data_bindings.every((binding) => isRecord(binding)
&& typeof binding.binding_id === 'string'
&& typeof binding.projection_topic === 'string'
&& typeof binding.ordering_authority_field === 'string');
}

/** Load a dashboard and its component/binding contracts from JSON-compatible YAML. */
export function loadLocalPageConfig(page: LocalPageName): LocalPageDocument {
const configPath = `../pages/local/${page}.page.yaml`;
const contractsPath = `../pages/local/${page}.contracts.yaml`;
const configSource = pageFiles[configPath];
const contractsSource = contractFiles[contractsPath];
if (typeof configSource !== 'string' || typeof contractsSource !== 'string') {
throw new Error(`Local dashboard page or component contracts are missing: ${page}`);
}
const dashboard: unknown = JSON.parse(configSource);
const contracts: unknown = JSON.parse(contractsSource);
if (!isDashboardView(dashboard) || !isRecord(contracts) || !Array.isArray(contracts.components)) {
throw new Error(`Local dashboard page is invalid: ${page}`);
}
const emptyStates = contracts.page_empty_state_reasons;
if (!Array.isArray(emptyStates) || !emptyStates.every((reason) => reason === 'NO_RUNS_YET' || reason === 'BASELINE_UNRESOLVED')) {
throw new Error(`Local dashboard page empty-state contract is invalid: ${page}`);
}
if (!contracts.components.every(isComponentContract)) {
throw new Error(`Local dashboard component contract is invalid: ${page}`);
}
return {
dashboard,
components: contracts.components,
empty_state_reasons: emptyStates,
};
}

/** Read the declared exposures through the HTTP projection source only. */
export async function loadLocalPageSnapshots(
page: LocalPageDocument,
source: ProtocolSnapshotSource,
options: LocalPageLoadOptions,
): Promise<BoundProjectionSnapshot[]> {
if (options.mode !== 'http') {
throw new Error('Local dashboard pages require HTTP mode for runtime exposures');
}
if (!source.readSnapshot) throw new Error('HTTP snapshot source does not support snapshots');
const topics = new Set(
page.components.flatMap((component) =>
(component.data_bindings ?? []).map((binding) => binding.projection_topic),
),
);
for (const topic of topics) {
if (!options.availableTopics.has(topic)) {
throw new Error(`Projection exposure is not served: ${topic}`);
}
}
return Promise.all([...topics].map(async (topic) => ({
topic,
...await source.readSnapshot!(topic),
})));
}

/** Keep the two domain-specific empty states explicit instead of substituting zero. */
export function resolveLocalPageEmptyState(
page: LocalPageDocument,
snapshots: readonly BoundProjectionSnapshot[],
): LocalPageEmptyState | null {
const rows = snapshots.flatMap((snapshot) => snapshot.rows);
if (rows.some((value) => {
if (typeof value !== 'object' || value === null) return false;
const row = value as Record<string, unknown>;
return row.baseline_state === 'BASELINE_UNRESOLVED' || row.savings_usd === null;
}) && page.empty_state_reasons.includes('BASELINE_UNRESOLVED')) {
return 'BASELINE_UNRESOLVED';
}
if (rows.length === 0 && page.empty_state_reasons.includes('NO_RUNS_YET')) {
return 'NO_RUNS_YET';
}
return null;
}
121 changes: 121 additions & 0 deletions src/pages/LocalDashboardPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useEffect, useState } from 'react';
import { createSnapshotSource } from '@/data-source';
import { fetchExposureCensus } from '@/data-source/exposure-census';
import { resolveEffectiveDataSource } from '@/data-source/data-source-override';
import '@/styles/local-dashboard.css';
import {
loadLocalPageConfig,
loadLocalPageSnapshots,
resolveLocalPageEmptyState,
type BoundProjectionSnapshot,
type LocalPageDocument,
type LocalPageName,
} from '@/layout/local-page-loader';

interface LocalDashboardPageProps {
pageName: LocalPageName;
}

function valueOrUnmeasured(value: unknown): string {
if (value === null || value === undefined || value === '') return 'Not measured';
if (typeof value === 'number' && !Number.isFinite(value)) return 'Not measured';
return String(value);
}

function rowsFor(component: LocalPageDocument['components'][number], snapshots: BoundProjectionSnapshot[]) {
const topic = component.data_bindings?.[0]?.projection_topic;
return topic === undefined ? [] : snapshots.find((snapshot) => snapshot.topic === topic)?.rows ?? [];
}

export function LocalDashboardPage({ pageName }: LocalDashboardPageProps) {
const [page, setPage] = useState<LocalPageDocument | null>(null);
const [snapshots, setSnapshots] = useState<BoundProjectionSnapshot[]>([]);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
let active = true;
async function load() {
try {
const document = loadLocalPageConfig(pageName);
const { mode } = resolveEffectiveDataSource();
if (mode !== 'http') {
throw new Error('Local pages require HTTP mode to read runtime exposures.');
}
const census = await fetchExposureCensus();
const availableTopics = new Set(
census.rows.filter((row) => row.reachability === 'reachable').map((row) => row.topic),
);
const data = await loadLocalPageSnapshots(document, createSnapshotSource(), {
mode,
availableTopics,
});
if (active) {
setPage(document);
setSnapshots(data);
setError(null);
}
} catch (cause) {
if (active) setError(cause instanceof Error ? cause.message : String(cause));
} finally {
if (active) setLoading(false);
}
}
void load();
return () => { active = false; };
}, [pageName]);

const emptyState = page ? resolveLocalPageEmptyState(page, snapshots) : null;
return (
<main aria-label={`${page?.dashboard.name ?? 'Local dashboard'} page`} className="local-dashboard-page">
<header className="local-dashboard-header">
<div>
<h1>{page?.dashboard.name ?? (pageName === 'overview' ? 'Overview' : 'Runs')}</h1>
<p>{page?.dashboard.description}</p>
</div>
<span className="local-dashboard-mode">Local runtime · HTTP</span>
</header>
{loading && <p role="status">Loading runtime exposures…</p>}
{error && <p className="local-dashboard-error" role="alert">{error}</p>}
{!loading && !error && page && (
<section className="local-dashboard-grid" aria-label="Dashboard components">
{page.components.map((component) => {
const rows = rowsFor(component, snapshots);
const first = (rows[0] ?? {}) as Record<string, unknown>;
return (
<article className="local-dashboard-panel" key={component.component_id}>
<h2>{component.title}</h2>
{emptyState ? (
<p className="local-dashboard-empty" role="status">
{emptyState === 'BASELINE_UNRESOLVED' ? 'Baseline unresolved' : 'No runs yet'}
</p>
) : component.component_kind === 'table' ? (
rows.length === 0 ? <p className="local-dashboard-empty">No runs yet</p> : (
<div className="local-dashboard-table-wrap">
<table>
<thead><tr><th>Run</th><th>Status</th><th>Started</th><th>Savings</th></tr></thead>
<tbody>{rows.map((row, index) => {
const record = row as Record<string, unknown>;
return <tr key={String(record.run_id ?? index)}>
<td>{valueOrUnmeasured(record.run_id)}</td>
<td>{valueOrUnmeasured(record.status)}</td>
<td>{valueOrUnmeasured(record.started_at)}</td>
<td>{valueOrUnmeasured(record.savings_usd)}</td>
</tr>;
})}</tbody>
</table>
</div>
)
) : (
<p className="local-dashboard-metric">
{valueOrUnmeasured(first.roi_percent)}{first.roi_percent == null ? '' : '%'}
</p>
)}
</article>
);
})}
</section>
)}
</main>
);
}
Loading
Loading