Merge pull request #19 from codex/issue-11-dimension-lab-seed

feat: add Dimension Lab seed model
This commit is contained in:
vince 2026-06-18 19:17:50 +02:00
commit 13e3ff867b
4 changed files with 674 additions and 87 deletions

View file

@ -49,6 +49,15 @@ Stored dashboard documents pass through a version migration boundary before
reads or writes; the MVP supports `dashboard.v1` and fails unsupported versions
with an explicit migration error.
## Seed Data
The initial Dimension Lab dashboard lives in
`src/lib/model/fixtures/dimensionlab.ts` as validated model data. It includes
the first-screen telemetry, service groups, status strip, weather module,
Iconify icon identifiers, links, and datasource references. Values that are not
live yet are labeled as fallback values in the data so later datasource adapters
can replace them without changing presentation components.
## Runtime Shape
The SvelteKit build uses the Node adapter, and the current persistence runtime

View file

@ -0,0 +1,146 @@
import { describe, expect, test } from "vitest";
import {
dimensionLabDashboardFixture,
} from "./dimensionlab";
import type {
DashboardDocument,
DatasourceReference,
ServiceEntry,
TelemetryCard,
} from "../schema";
describe("Dimension Lab dashboard seed", () => {
test("defines the primary first-screen sections as model data", () => {
expect(dimensionLabDashboardFixture.metadata.title).toBe("System Overview");
expect(dimensionLabDashboardFixture.layout.telemetry).toHaveLength(16);
expect(dimensionLabDashboardFixture.layout.serviceGroups).toEqual([
"essentials",
"monitoring",
"ai-automation",
"systems",
"runtime-health",
]);
expect(dimensionLabDashboardFixture.layout.statusStrips).toEqual([
"footer-status",
]);
expect(dimensionLabDashboardFixture.layout.modules).toEqual([
"weather-amsterdam",
"runtime-health-summary",
]);
});
test("keeps icon, link, and datasource references in seed data", () => {
const iconIds = [
...dimensionLabDashboardFixture.telemetry.map((card) => card.icon),
...allServices(dimensionLabDashboardFixture).map((service) => service.icon),
...(dimensionLabDashboardFixture.modules || []).map((module) => module.icon),
].filter((icon): icon is string => Boolean(icon));
expect(
dimensionLabDashboardFixture.telemetry.every(
(card) => card.icon && card.datasource,
),
).toBe(true);
expect(
dimensionLabDashboardFixture.telemetry.some(
(card) => card.datasource?.type === "external" &&
card.datasource.adapter === "prometheus",
),
).toBe(true);
expect(
allServices(dimensionLabDashboardFixture).some(
(service) => service.datasource?.type === "external" &&
service.datasource.adapter === "http-status",
),
).toBe(true);
for (const service of allServices(dimensionLabDashboardFixture)) {
expect(service.icon, `${service.id} must declare an Iconify id`).toBeTruthy();
expect(verifiedSeedIconIds.has(service.icon || ""), `${service.id} icon must be verified`).toBe(true);
expect(service.datasource, `${service.id} must declare health source`).toBeTruthy();
if (service.link) {
expect(service.link.label, `${service.id} link needs an accessible label`).toBeTruthy();
expect(service.link.external).toBe(true);
}
}
for (const icon of iconIds) {
expect(verifiedSeedIconIds.has(icon), `${icon} must be verified`).toBe(true);
}
});
test("labels fallback values and unresolved adapters explicitly", () => {
const placeholders = collectDatasources(dimensionLabDashboardFixture).filter(
(datasource) => datasource.type === "placeholder",
);
const fallbackText = [
...dimensionLabDashboardFixture.telemetry.map((card) => card.detail || ""),
...allServices(dimensionLabDashboardFixture).map((service) => service.detail || ""),
...dimensionLabDashboardFixture.statusStrips.flatMap((strip) =>
strip.items.map((item) => item.value),
),
...(dimensionLabDashboardFixture.modules || []).map((module) => module.detail || ""),
].join("\n").toLowerCase();
expect(fallbackText).toContain("fallback");
for (const service of allServices(dimensionLabDashboardFixture)) {
expect(service.detail?.toLowerCase(), `${service.id} detail must label fallback state`).toContain("fallback");
}
for (const statusItem of dimensionLabDashboardFixture.statusStrips.flatMap((strip) => strip.items)) {
expect(statusItem.value.toLowerCase(), `${statusItem.id} value must label fallback state`).toContain("fallback");
}
expect(placeholders.length).toBeGreaterThan(0);
for (const datasource of placeholders) {
expect(datasource.reason.toLowerCase()).toMatch(/fallback|pending|unresolved/);
}
});
});
function allServices(document: DashboardDocument): ServiceEntry[] {
return document.serviceGroups.flatMap((group) => group.services);
}
function collectDatasources(document: DashboardDocument): DatasourceReference[] {
const telemetry = document.telemetry
.map((card: TelemetryCard) => card.datasource)
.filter((datasource): datasource is DatasourceReference => Boolean(datasource));
const services = allServices(document)
.map((service) => service.datasource)
.filter((datasource): datasource is DatasourceReference => Boolean(datasource));
const modules = (document.modules || [])
.map((module) => module.datasource)
.filter((datasource): datasource is DatasourceReference => Boolean(datasource));
return [...telemetry, ...services, ...modules];
}
const verifiedSeedIconIds = new Set([
"mdi:account-hard-hat-outline",
"mdi:backup-restore",
"mdi:brain",
"mdi:cpu-64-bit",
"mdi:docker",
"mdi:expansion-card",
"mdi:fan",
"mdi:harddisk",
"mdi:image-edit-outline",
"mdi:memory",
"mdi:pulse",
"mdi:robot-outline",
"mdi:router-network",
"mdi:thermometer",
"mdi:web",
"mdi:weather-sunny",
"simple-icons:adminer",
"simple-icons:amazonwebservices",
"simple-icons:cockpit",
"simple-icons:forgejo",
"simple-icons:grafana",
"simple-icons:n8n",
"simple-icons:ollama",
"simple-icons:postgresql",
"simple-icons:prometheus",
"simple-icons:uptimekuma",
"simple-icons:vaultwarden",
"simple-icons:wikidotjs",
]);

View file

@ -1,14 +1,48 @@
import {
DASHBOARD_SCHEMA_VERSION,
type DashboardDocument,
type DatasourceReference,
type MetricValue,
type ServiceEntry,
type ServiceGroup,
type Severity,
type TelemetryCard,
} from "../schema";
type NumericValueKind = "percent" | "bytes" | "temperature" | "latency" | "number";
interface MetricSeed {
id: string;
label: string;
icon: string;
kind: NumericValueKind;
value: number;
detail: string;
severity: Severity;
datasource: DatasourceReference;
thresholds?: TelemetryCard["thresholds"];
sparkline?: number[];
precision?: number;
}
interface ServiceSeed {
id: string;
label: string;
description: string;
icon: string;
datasource: DatasourceReference;
href?: string;
severity?: Severity;
detail?: string;
}
export const dimensionLabDashboardFixture: DashboardDocument = {
schemaVersion: DASHBOARD_SCHEMA_VERSION,
metadata: {
title: "System Overview",
subtitle: "Capacity, noise & top consumers",
description: "Dimension Lab operational dashboard seed data.",
subtitle: "Dimension Lab home infra, GPU, and automation surface",
description:
"Initial Dimension Lab dashboard seed data. Runtime values are fallback values until datasource adapters are enabled.",
timezone: "Europe/Amsterdam",
refreshIntervalSeconds: 15,
},
@ -34,130 +68,528 @@ export const dimensionLabDashboardFixture: DashboardDocument = {
],
serviceGroups: ["essentials", "monitoring", "ai-automation", "systems", "runtime-health"],
statusStrips: ["footer-status"],
modules: ["weather"],
modules: ["weather-amsterdam", "runtime-health-summary"],
},
telemetry: [
metric("infra-ram", "Infra RAM", "percent", 19, "linux-infra", "ok"),
metric("gpu-host-ram", "GPU Host RAM", "percent", 17, "linux", "ok"),
metric("network-ram", "Network RAM", "percent", 5, "network-core", "ok"),
metric("user-disk-peak", "User Disk Peak", "percent", 29, "linux /home", "ok"),
metric("system-disk-peak", "System Disk Peak", "percent", 49, "linux /boot", "ok"),
metric("peak-cpu-busy", "Peak CPU Busy", "percent", 3, "linux-infra", "ok"),
metric("top-cpu-container", "Top CPU Container", "percent", 7.9, "container placeholder", "ok"),
metric("top-ram-container", "Top RAM Container", "bytes", 5.2 * 1024 ** 3, "container placeholder", "danger"),
metric("gpu-3060-load", "3060 GPU Load", "percent", 0, "RTX 3060", "ok"),
metric("gpu-3060-vram", "3060 VRAM", "percent", 0, "RTX 3060", "ok"),
metric("gpu-3060-temp", "3060 Temp", "temperature", 54, "RTX 3060", "ok"),
metric("gpu-3060-fan", "3060 Fan Spin", "percent", 0, "RTX 3060", "ok"),
metric("gpu-3090-load", "3090 GPU Load", "percent", 0, "RTX 3090", "ok"),
metric("gpu-3090-vram", "3090 VRAM", "percent", 74, "RTX 3090", "warning"),
metric("gpu-3090-temp", "3090 Temp", "temperature", 50, "RTX 3090", "ok"),
metric("gpu-3090-fan", "3090 Fan Spin", "percent", 0, "RTX 3090", "ok"),
metric({
id: "infra-ram",
label: "Infra RAM",
icon: "mdi:memory",
kind: "percent",
value: 19,
detail: "fallback - linux-infra memory used",
severity: "ok",
thresholds: percentThresholds(),
datasource: prometheus(
'100 * (1 - node_memory_MemAvailable_bytes{instance="linux-infra"} / node_memory_MemTotal_bytes{instance="linux-infra"})',
),
sparkline: [18, 18, 19, 19, 18, 19],
}),
metric({
id: "gpu-host-ram",
label: "GPU Host RAM",
icon: "mdi:memory",
kind: "percent",
value: 17,
detail: "fallback - GPU host memory used",
severity: "ok",
thresholds: percentThresholds(),
datasource: prometheus(
'100 * (1 - node_memory_MemAvailable_bytes{instance="linux-gpu"} / node_memory_MemTotal_bytes{instance="linux-gpu"})',
),
sparkline: [16, 16, 17, 17, 17, 17],
}),
metric({
id: "network-ram",
label: "Network RAM",
icon: "mdi:router-network",
kind: "percent",
value: 5,
detail: "fallback - network core memory used",
severity: "ok",
thresholds: percentThresholds(),
datasource: prometheus(
'100 * (1 - node_memory_MemAvailable_bytes{instance="network-core"} / node_memory_MemTotal_bytes{instance="network-core"})',
),
sparkline: [5, 5, 5, 6, 5, 5],
}),
metric({
id: "user-disk-peak",
label: "User Disk Peak",
icon: "mdi:harddisk",
kind: "percent",
value: 29,
detail: "fallback - /home peak usage",
severity: "ok",
thresholds: percentThresholds(80, 92),
datasource: prometheus(
'100 * (1 - node_filesystem_avail_bytes{mountpoint="/home"} / node_filesystem_size_bytes{mountpoint="/home"})',
),
sparkline: [27, 27, 28, 29, 29, 29],
}),
metric({
id: "system-disk-peak",
label: "System Disk Peak",
icon: "mdi:harddisk",
kind: "percent",
value: 49,
detail: "fallback - /boot peak usage",
severity: "ok",
thresholds: percentThresholds(80, 92),
datasource: prometheus(
'100 * (1 - node_filesystem_avail_bytes{mountpoint="/boot"} / node_filesystem_size_bytes{mountpoint="/boot"})',
),
sparkline: [48, 48, 49, 49, 49, 49],
}),
metric({
id: "peak-cpu-busy",
label: "Peak CPU Busy",
icon: "mdi:cpu-64-bit",
kind: "percent",
value: 3,
detail: "fallback - linux-infra 5m CPU busy",
severity: "ok",
thresholds: percentThresholds(75, 90),
datasource: prometheus(
'100 - (avg by(instance) (rate(node_cpu_seconds_total{instance="linux-infra",mode="idle"}[5m])) * 100)',
),
sparkline: [2, 3, 3, 4, 3, 3],
}),
metric({
id: "top-cpu-container",
label: "Top CPU Container",
icon: "mdi:docker",
kind: "percent",
value: 7.9,
precision: 1,
detail: "fallback - top container CPU pending label mapping",
severity: "ok",
thresholds: percentThresholds(70, 90),
datasource: placeholder(
"fallback top-container CPU query pending container label normalization",
),
sparkline: [6.1, 6.4, 7.0, 7.5, 7.2, 7.9],
}),
metric({
id: "top-ram-container",
label: "Top RAM Container",
icon: "mdi:docker",
kind: "bytes",
value: Math.round(5.2 * 1024 ** 3),
precision: 1,
detail: "fallback - top container RAM pending label mapping",
severity: "danger",
thresholds: { warning: 3 * 1024 ** 3, danger: 5 * 1024 ** 3 },
datasource: placeholder(
"fallback top-container memory query pending container label normalization",
),
sparkline: [
3.1 * 1024 ** 3,
3.5 * 1024 ** 3,
4.1 * 1024 ** 3,
4.8 * 1024 ** 3,
5.0 * 1024 ** 3,
5.2 * 1024 ** 3,
],
}),
metric({
id: "gpu-3060-load",
label: "3060 GPU Load",
icon: "mdi:expansion-card",
kind: "percent",
value: 0,
detail: "fallback - RTX 3060 GPU utilization",
severity: "ok",
thresholds: percentThresholds(85, 95),
datasource: prometheus('DCGM_FI_DEV_GPU_UTIL{gpu="rtx-3060"}'),
sparkline: [0, 0, 0, 0, 0, 0],
}),
metric({
id: "gpu-3060-vram",
label: "3060 VRAM",
icon: "mdi:memory",
kind: "percent",
value: 0,
detail: "fallback - RTX 3060 VRAM used",
severity: "ok",
thresholds: percentThresholds(80, 92),
datasource: prometheus(
'100 * DCGM_FI_DEV_FB_USED{gpu="rtx-3060"} / (DCGM_FI_DEV_FB_USED{gpu="rtx-3060"} + DCGM_FI_DEV_FB_FREE{gpu="rtx-3060"})',
),
sparkline: [0, 0, 0, 0, 0, 0],
}),
metric({
id: "gpu-3060-temp",
label: "3060 Temp",
icon: "mdi:thermometer",
kind: "temperature",
value: 54,
detail: "fallback - RTX 3060 temperature",
severity: "ok",
thresholds: { warning: 75, danger: 85 },
datasource: prometheus('DCGM_FI_DEV_GPU_TEMP{gpu="rtx-3060"}'),
sparkline: [52, 53, 54, 54, 53, 54],
}),
metric({
id: "gpu-3060-fan",
label: "3060 Fan Spin",
icon: "mdi:fan",
kind: "percent",
value: 0,
detail: "fallback - RTX 3060 fan duty",
severity: "ok",
thresholds: percentThresholds(80, 95),
datasource: prometheus('DCGM_FI_DEV_FAN_SPEED{gpu="rtx-3060"}'),
sparkline: [0, 0, 0, 0, 0, 0],
}),
metric({
id: "gpu-3090-load",
label: "3090 GPU Load",
icon: "mdi:expansion-card",
kind: "percent",
value: 0,
detail: "fallback - RTX 3090 GPU utilization",
severity: "ok",
thresholds: percentThresholds(85, 95),
datasource: prometheus('DCGM_FI_DEV_GPU_UTIL{gpu="rtx-3090"}'),
sparkline: [0, 0, 0, 0, 0, 0],
}),
metric({
id: "gpu-3090-vram",
label: "3090 VRAM",
icon: "mdi:memory",
kind: "percent",
value: 74,
detail: "fallback - RTX 3090 VRAM used",
severity: "warning",
thresholds: percentThresholds(70, 90),
datasource: prometheus(
'100 * DCGM_FI_DEV_FB_USED{gpu="rtx-3090"} / (DCGM_FI_DEV_FB_USED{gpu="rtx-3090"} + DCGM_FI_DEV_FB_FREE{gpu="rtx-3090"})',
),
sparkline: [68, 70, 72, 74, 73, 74],
}),
metric({
id: "gpu-3090-temp",
label: "3090 Temp",
icon: "mdi:thermometer",
kind: "temperature",
value: 50,
detail: "fallback - RTX 3090 temperature",
severity: "ok",
thresholds: { warning: 75, danger: 85 },
datasource: prometheus('DCGM_FI_DEV_GPU_TEMP{gpu="rtx-3090"}'),
sparkline: [49, 50, 50, 51, 50, 50],
}),
metric({
id: "gpu-3090-fan",
label: "3090 Fan Spin",
icon: "mdi:fan",
kind: "percent",
value: 0,
detail: "fallback - RTX 3090 fan duty",
severity: "ok",
thresholds: percentThresholds(80, 95),
datasource: prometheus('DCGM_FI_DEV_FAN_SPEED{gpu="rtx-3090"}'),
sparkline: [0, 0, 0, 0, 0, 0],
}),
],
serviceGroups: [
group("essentials", "Essentials", [
service("vaultwarden", "Vaultwarden", "Password manager", "simple-icons:vaultwarden", "https://vault.dimensionlab.net"),
service("forgejo", "Forgejo", "Git repositories", "simple-icons:forgejo", "https://git.dimensionlab.net"),
service("wiki", "Wiki", "Internal documentation", "simple-icons:wikidotjs", "https://wiki.dimensionlab.net"),
service("aws-start", "AWS Start", "AWS access portal", "simple-icons:amazonaws", "https://dimensionlab.awsapps.com/start"),
service({
id: "vaultwarden",
label: "Vaultwarden",
description: "Password manager",
icon: "simple-icons:vaultwarden",
href: "https://vault.dimensionlab.net",
datasource: httpStatus("https://vault.dimensionlab.net/alive"),
}),
service({
id: "forgejo",
label: "Forgejo",
description: "Git repositories",
icon: "simple-icons:forgejo",
href: "https://git.dimensionlab.net",
datasource: httpStatus("https://git.dimensionlab.net/api/healthz"),
}),
service({
id: "wiki",
label: "Wiki",
description: "Internal documentation",
icon: "simple-icons:wikidotjs",
href: "https://wiki.dimensionlab.net",
datasource: httpStatus("https://wiki.dimensionlab.net"),
}),
service({
id: "aws-start",
label: "AWS Start",
description: "AWS access portal",
icon: "simple-icons:amazonwebservices",
href: "https://dimensionlab.awsapps.com/start",
datasource: httpStatus("https://dimensionlab.awsapps.com/start"),
}),
]),
group("monitoring", "Monitoring", [
service("grafana", "Grafana", "Capacity and noise dashboard", "simple-icons:grafana", "https://grafana.dimensionlab.net"),
service("uptime-kuma", "Uptime Kuma", "Service uptime checks", "simple-icons:uptimekuma", "https://uptime.dimensionlab.net"),
service("prometheus", "Prometheus", "Metrics database", "simple-icons:prometheus", "https://prometheus.dimensionlab.net"),
service("backrest", "Backrest", "Restic backup manager", "mdi:backup-restore", "https://backups.dimensionlab.net"),
service({
id: "grafana",
label: "Grafana",
description: "Capacity and noise dashboard",
icon: "simple-icons:grafana",
href: "https://grafana.dimensionlab.net",
datasource: httpStatus("https://grafana.dimensionlab.net/api/health"),
}),
service({
id: "uptime-kuma",
label: "Uptime Kuma",
description: "Service uptime checks",
icon: "simple-icons:uptimekuma",
href: "https://uptime.dimensionlab.net",
datasource: httpStatus("https://uptime.dimensionlab.net/status/dimensionlab"),
}),
service({
id: "prometheus",
label: "Prometheus",
description: "Metrics database",
icon: "simple-icons:prometheus",
href: "https://prometheus.dimensionlab.net",
datasource: httpStatus("https://prometheus.dimensionlab.net/-/ready"),
}),
service({
id: "backrest",
label: "Backrest",
description: "Restic backup manager",
icon: "mdi:backup-restore",
href: "https://backups.dimensionlab.net",
datasource: httpStatus("https://backups.dimensionlab.net"),
}),
]),
group("ai-automation", "AI & Automation", [
service("n8n", "n8n", "Workflow automation", "simple-icons:n8n", "https://workflows.dimensionlab.net"),
service("open-webui", "Open WebUI", "Chat and model interface", "simple-icons:openwebui", "https://webui.dimensionlab.net"),
service("comfyui", "ComfyUI", "Image generation workflows", "mdi:image-edit-outline", "https://comfy.dimensionlab.net"),
service("models", "Models", "Local model management", "mdi:brain", "https://models.dimensionlab.net"),
service({
id: "n8n",
label: "n8n",
description: "Workflow automation",
icon: "simple-icons:n8n",
href: "https://workflows.dimensionlab.net",
datasource: httpStatus("https://workflows.dimensionlab.net/healthz"),
}),
service({
id: "open-webui",
label: "Open WebUI",
description: "Chat and model interface",
icon: "mdi:web",
href: "https://webui.dimensionlab.net",
datasource: httpStatus("https://webui.dimensionlab.net/health"),
}),
service({
id: "comfyui",
label: "ComfyUI",
description: "Image generation workflows",
icon: "mdi:image-edit-outline",
href: "https://comfy.dimensionlab.net",
datasource: httpStatus("https://comfy.dimensionlab.net"),
}),
service({
id: "models",
label: "Models",
description: "Local model management",
icon: "mdi:brain",
href: "https://models.dimensionlab.net",
datasource: httpStatus("https://models.dimensionlab.net/api/tags"),
}),
]),
group("systems", "Systems", [
service("adminer", "Adminer", "PostgreSQL database browser", "simple-icons:adminer", "https://db.dimensionlab.net"),
service("assistant", "Assistant", "Personal AI agent gateway", "mdi:robot-outline", "https://assistant.dimensionlab.net"),
service("suna", "Suna", "AI command center", "mdi:account-hard-hat-outline", "https://suna.dimensionlab.net"),
service("cockpit-infra", "Cockpit Infra", "linux-infra server console", "simple-icons:cockpit", "https://infra-cockpit.dimensionlab.net"),
service({
id: "adminer",
label: "Adminer",
description: "PostgreSQL database browser",
icon: "simple-icons:adminer",
href: "https://db.dimensionlab.net",
datasource: httpStatus("https://db.dimensionlab.net"),
}),
service({
id: "assistant",
label: "Assistant",
description: "Personal AI agent gateway",
icon: "mdi:robot-outline",
href: "https://assistant.dimensionlab.net",
datasource: httpStatus("https://assistant.dimensionlab.net/health"),
}),
service({
id: "suna",
label: "Suna",
description: "AI command center",
icon: "mdi:account-hard-hat-outline",
href: "https://suna.dimensionlab.net",
datasource: httpStatus("https://suna.dimensionlab.net"),
}),
service({
id: "cockpit-infra",
label: "Cockpit Infra",
description: "linux-infra server console",
icon: "simple-icons:cockpit",
href: "https://infra-cockpit.dimensionlab.net",
datasource: httpStatus("https://infra-cockpit.dimensionlab.net"),
}),
]),
group("runtime-health", "Runtime Health", [
service("forgejo-ssh-relay", "Forgejo SSH Relay", "Public Git SSH relay", "simple-icons:forgejo", "https://uptime.dimensionlab.net/status/dimensionlab"),
service("postgresql", "PostgreSQL", "Shared application database", "simple-icons:postgresql"),
service("ollama-api", "Ollama API", "Local model API", "simple-icons:ollama"),
service("node-exporter", "Node Exporter", "Host metrics exporter", "simple-icons:prometheus"),
service({
id: "forgejo-ssh-relay",
label: "Forgejo SSH Relay",
description: "Public Git SSH relay",
icon: "simple-icons:forgejo",
href: "https://uptime.dimensionlab.net/status/dimensionlab",
datasource: custom("tcp:git.dimensionlab.net:22"),
detail: "fallback - uptime monitor pending",
}),
service({
id: "postgresql",
label: "PostgreSQL",
description: "Shared application database",
icon: "simple-icons:postgresql",
datasource: prometheus('pg_up{cluster="dimensionlab"}'),
detail: "fallback - postgres exporter pending",
}),
service({
id: "ollama-api",
label: "Ollama API",
description: "Local model API",
icon: "simple-icons:ollama",
datasource: httpStatus("http://linux-gpu:11434/api/tags"),
detail: "fallback - internal health check pending",
}),
service({
id: "node-exporter",
label: "Node Exporter",
description: "Host metrics exporter",
icon: "simple-icons:prometheus",
datasource: prometheus('up{job="node-exporter"}'),
detail: "fallback - exporter health from Prometheus",
}),
]),
],
statusStrips: [
{
id: "footer-status",
items: [
{ id: "system-status", label: "System Status", value: "All systems operational", severity: "ok" },
{ id: "last-sync", label: "Last Sync", value: "2 minutes ago", severity: "stale" },
{ id: "uptime", label: "Uptime", value: "28d 14h 32m", severity: "ok" },
{ id: "load-avg", label: "Load Avg", value: "0.47 0.53 0.59", severity: "neutral" },
{ id: "system-status", label: "System Status", value: "Fallback operational", severity: "ok" },
{ id: "last-sync", label: "Last Sync", value: "fallback: 2 minutes ago", severity: "stale" },
{ id: "uptime", label: "Uptime", value: "fallback: 28d 14h 32m", severity: "ok" },
{ id: "load-avg", label: "Load Avg", value: "fallback: 0.47 0.53 0.59", severity: "neutral" },
],
},
],
modules: [
{
id: "weather",
id: "weather-amsterdam",
kind: "weather",
title: "Amsterdam",
value: "28.3 C",
detail: "Clear",
detail: "fallback - weather adapter pending",
icon: "mdi:weather-sunny",
severity: "ok",
datasource: { type: "placeholder", reason: "weather adapter pending" },
datasource: {
type: "external",
adapter: "weather",
reference: "open-meteo:latitude=52.3676&longitude=4.9041",
},
},
{
id: "runtime-health-summary",
kind: "summary",
title: "Runtime Health",
value: "fallback",
detail: "fallback - health adapters pending",
icon: "mdi:pulse",
severity: "stale",
datasource: placeholder("summary pending live health aggregation"),
},
],
};
type NumericValueKind = "percent" | "bytes" | "temperature" | "latency" | "number";
type Severity = "neutral" | "ok" | "warning" | "danger" | "stale" | "unavailable";
function metric(seed: MetricSeed): TelemetryCard {
return {
id: seed.id,
label: seed.label,
icon: seed.icon,
value: metricValue(seed.kind, seed.value, seed.precision),
detail: seed.detail,
severity: seed.severity,
thresholds: seed.thresholds,
datasource: seed.datasource,
sparkline: seed.sparkline,
};
}
function metric(
id: string,
label: string,
function metricValue(
kind: NumericValueKind,
value: number,
detail: string,
severity: Severity,
) {
precision?: number,
): MetricValue {
return precision === undefined ? { kind, value } : { kind, value, precision };
}
function service(seed: ServiceSeed): ServiceEntry {
const entry: ServiceEntry = {
id: seed.id,
label: seed.label,
description: seed.description,
icon: seed.icon,
severity: seed.severity || "ok",
detail: seed.detail || "fallback - health check pending",
datasource: seed.datasource,
};
if (!seed.href) return entry;
return {
id,
label,
value: { kind, value },
detail,
severity,
datasource: { type: "placeholder" as const, reason: "live datasource pending" },
sparkline: [10, 18, 15, 28, 24, 32],
...entry,
link: {
href: seed.href,
label: `Open ${seed.label}`,
external: true,
},
};
}
function service(
id: string,
label: string,
description: string,
icon: string,
href?: string,
) {
const entry = {
id,
label,
description,
icon,
severity: "ok" as const,
detail: "ready",
datasource: { type: "placeholder" as const, reason: "service health adapter pending" },
};
return href ? { ...entry, link: { href, external: true } } : entry;
}
function group(id: string, title: string, services: ReturnType<typeof service>[]) {
function group(id: string, title: string, services: ServiceEntry[]): ServiceGroup {
return {
id,
title,
layout: "list" as const,
layout: "list",
services,
};
}
function percentThresholds(warning = 80, danger = 92) {
return { warning, danger };
}
function prometheus(reference: string): DatasourceReference {
return {
type: "external",
adapter: "prometheus",
reference,
};
}
function httpStatus(url: string): DatasourceReference {
return {
type: "external",
adapter: "http-status",
reference: `GET ${url}`,
};
}
function custom(reference: string): DatasourceReference {
return {
type: "external",
adapter: "custom",
reference,
};
}
function placeholder(reason: string): DatasourceReference {
return {
type: "placeholder",
reason,
};
}

View file

@ -17,8 +17,8 @@ export interface PlaceholderDashboard {
export function loadPlaceholderDashboard(): PlaceholderDashboard {
return {
title: "System Overview",
subtitle: "Dashboard runtime scaffold",
title: "Dashboard Runtime",
subtitle: "Runtime scaffold",
status: "building",
message:
"SvelteKit is running. Later issues will replace this placeholder with the validated dashboard model.",
@ -30,8 +30,8 @@ export function loadDashboardRuntime(store?: DashboardStore): PlaceholderDashboa
try {
const seeded = dashboardStore.seedDashboardIfEmpty(dimensionLabDashboardFixture, {
actor: "dimensionlab-seed",
message: "load initial dashboard seed",
actor: "initial-seed",
message: "load initial dashboard document",
});
const active = dashboardStore.getActiveDashboard();
const document = active?.document || seeded.document;