Files
punktfunk/web/src/stories/lib/fixtures.ts
T
enricobuehlerandClaude Fable 5 67ab64ed06 feat(web): the Host page names — and marks — the operating system
The Identity card gains an "Operating system" row: the pretty name as text,
the raw identity chain as its tooltip, and a leading OsIcon resolved by the
same most-specific-first walk as the clients. lucide deliberately ships no
brand marks, so os-icon.tsx vendors the ten as inline currentColor SVGs
(the brand-mark precedent, the plugin-registry pattern), falling back to a
generic monitor for a chain nothing recognizes. Row grows optional icon/title
props rather than a fork; stories cover Bazzite, Windows and the
unknown-distro degradation; labels land in both message files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 17:58:33 +02:00

300 lines
7.2 KiB
TypeScript

// Mock API payloads for the page stories — typed against the generated models so
// they stay honest if the OpenAPI schema changes.
import type { AvailableCompositor } from "@/api/gen/model/availableCompositor";
import type { Capture } from "@/api/gen/model/capture";
import type { CaptureMeta } from "@/api/gen/model/captureMeta";
import type { GameEntry } from "@/api/gen/model/gameEntry";
import type { HostInfo } from "@/api/gen/model/hostInfo";
import type { NativeClient } from "@/api/gen/model/nativeClient";
import type { NativePairStatus } from "@/api/gen/model/nativePairStatus";
import type { PairedClient } from "@/api/gen/model/pairedClient";
import type { PairingStatus } from "@/api/gen/model/pairingStatus";
import type { PendingDevice } from "@/api/gen/model/pendingDevice";
import type { RuntimeStatus } from "@/api/gen/model/runtimeStatus";
import type { StatsSample } from "@/api/gen/model/statsSample";
import type { StatsStatus } from "@/api/gen/model/statsStatus";
export const hostInfo: HostInfo = {
abi_version: 2,
app_version: "7.1.450.0",
codecs: ["h264", "hevc", "av1"],
gamestream: true,
gfe_version: "3.23.0.74",
hostname: "ENRICOS-DESKTOP",
local_ip: "192.168.1.173",
os: "linux/fedora/bazzite",
os_name: "Bazzite 42 (Kinoite)",
ports: {
audio: 48000,
control: 47999,
http: 47989,
https: 47984,
mgmt: 47990,
rtsp: 48010,
video: 47998,
},
uniqueid: "0f8a1c3e9b7d4a62",
version: "0.2.0",
};
export const compositors: AvailableCompositor[] = [
{ id: "kwin", label: "KWin (Plasma)", available: true, default: true },
{ id: "gamescope", label: "gamescope", available: true, default: false },
{ id: "mutter", label: "Mutter (GNOME)", available: false, default: false },
{ id: "wlroots", label: "Sway / wlroots", available: false, default: false },
];
export const statusActive: RuntimeStatus = {
video_streaming: true,
audio_streaming: true,
paired_clients: 3,
native_paired_clients: 2,
pin_pending: false,
active_sessions: 1,
session: { width: 5120, height: 1440, fps: 240 },
stream: {
codec: "hevc",
width: 5120,
height: 1440,
fps: 240,
bitrate_kbps: 150_000,
min_fec: 5,
packet_size: 1392,
},
games: [
{
session_id: 1,
client: "Living room TV",
app_id: "steam:1145360",
title: "Hades",
store: "steam",
plane: "native",
state: "running",
},
],
};
export const statusIdle: RuntimeStatus = {
video_streaming: false,
audio_streaming: false,
paired_clients: 1,
native_paired_clients: 0,
pin_pending: true,
active_sessions: 0,
session: null,
stream: null,
games: [],
};
/**
* Idle, but a game its client walked away from is still running — on a countdown to being closed.
* The state the running-game card exists for.
*/
export const statusGrace: RuntimeStatus = {
...statusIdle,
games: [
{
client: "Living room TV",
app_id: "steam:1145360",
title: "Hades",
store: "steam",
plane: "native",
state: "grace",
grace_remaining_s: 252,
},
],
};
export const pairedClients: PairedClient[] = [
{
fingerprint:
"a1b2c3d4e5f60718293a4b5c6d7e8f90112233445566778899aabbccddeeff00",
subject: "enricos-macbook",
not_before_unix: 1_718_000_000,
not_after_unix: 2_030_000_000,
},
{
fingerprint:
"ff00eeddccbbaa998877665544332211009f8e7d6c5b4a39281706f5e4d3c2b1",
subject: "living-room-tv",
not_before_unix: 1_718_500_000,
not_after_unix: 2_030_000_000,
},
{
fingerprint:
"0011223344556677889900aabbccddeeff112233445566778899aabbccddeeff",
subject: null,
},
];
const noArt = { header: null, hero: null, logo: null, portrait: null };
export const library: GameEntry[] = [
{
id: "steam:1245620",
store: "steam",
title: "Elden Ring",
art: noArt,
launch: null,
},
{
id: "steam:1086940",
store: "steam",
title: "Baldur's Gate 3",
art: noArt,
launch: null,
},
{
id: "steam:413150",
store: "steam",
title: "Stardew Valley",
art: noArt,
launch: null,
},
{
id: "custom:retroarch",
store: "custom",
title: "RetroArch",
art: noArt,
launch: null,
},
// An emulated title with the metadata fields filled — exercises the platform
// badge (non-PC) and the year in the caption.
{
id: "custom:sotc",
store: "custom",
title: "Shadow of the Colossus",
art: noArt,
launch: null,
platform: "PS2",
developer: "Team Ico",
publisher: "Sony Computer Entertainment",
release_year: 2005,
genres: ["Adventure"],
tags: ["favorite"],
region: "PAL",
players: 1,
},
];
// --- Performance (stats) page ------------------------------------------------
export const statsStatusIdle: StatsStatus = {
armed: false,
kind: "native",
sample_count: 0,
started_unix_ms: 0,
elapsed_ms: 0,
};
// A native-path pipeline: capture → submit → encode → send. Deterministic (no
// Math.random) so the screenshot is byte-stable across CI runs; a gentle sine
// gives the charts a realistic shape without a live capture.
const STAGE_BASE_US: Record<string, number> = {
capture: 320,
submit: 90,
encode: 760,
send: 140,
};
const STAGE_ORDER = ["capture", "submit", "encode", "send"];
function buildSamples(n: number): StatsSample[] {
const out: StatsSample[] = [];
for (let i = 0; i < n; i++) {
const wobble = Math.sin(i / 4);
out.push({
t_ms: i * 1000,
session_id: 1,
fps: 240,
repeat_fps: i % 3 === 0 ? 2 : 1,
mbps: 920 + wobble * 55,
bitrate_kbps: 150_000,
frames_dropped: i % 17 === 0 ? 1 : 0,
packets_dropped: i % 9 === 0 ? 2 : 0,
send_dropped: 0,
fec_recovered: i % 5 === 0 ? 3 : 1,
stages: STAGE_ORDER.map((name) => {
const base = STAGE_BASE_US[name] ?? 100;
const p50 = Math.round(base + wobble * base * 0.15);
return { name, p50_us: p50, p99_us: Math.round(p50 * 1.8) };
}),
});
}
return out;
}
export const captureMetas: CaptureMeta[] = [
{
id: "cap-20260628-2041",
client: "enricos-macbook",
kind: "native",
codec: "hevc",
width: 5120,
height: 1440,
fps: 240,
duration_ms: 92_000,
sample_count: 92,
started_unix_ms: 1_782_415_260_000,
encoder_backend: "nvenc",
gpu: "NVIDIA GeForce RTX 4090",
},
{
id: "cap-20260628-1903",
client: "living-room-tv",
kind: "gamestream",
codec: "av1",
width: 3840,
height: 2160,
fps: 120,
duration_ms: 240_000,
sample_count: 240,
started_unix_ms: 1_782_409_380_000,
},
];
export const captureDetail: Capture = {
meta: captureMetas[0] as CaptureMeta,
samples: buildSamples(60),
};
// --- Pairing page ------------------------------------------------------------
export const nativePairArmed: NativePairStatus = {
enabled: true,
armed: true,
pin: "4827",
expires_in_secs: 98,
paired_clients: 2,
};
export const pendingDevices: PendingDevice[] = [
{
id: 1,
name: "studio-deck",
fingerprint:
"9f8e7d6c5b4a39281706f5e4d3c2b1a0998877665544332211ffeeddccbbaa00",
age_secs: 8,
},
{
id: 2,
name: "Mac Mini",
fingerprint:
"ff00eeddccbbaa998877665544332211009f8e7d6c5b4a39281706f5e4d3c2b1",
age_secs: 30,
},
];
export const nativeClients: NativeClient[] = [
{
name: "enricos-macbook",
fingerprint:
"a1b2c3d4e5f60718293a4b5c6d7e8f90112233445566778899aabbccddeeff00",
},
{
name: "living-room-tv",
fingerprint:
"ff00eeddccbbaa998877665544332211009f8e7d6c5b4a39281706f5e4d3c2b1",
},
];
export const pairingIdle: PairingStatus = { pin_pending: false };