75b3c94f60
ci / rust (push) Failing after 45s
ci / web (push) Successful in 52s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
ci / docs-site (push) Successful in 1m6s
decky / build-publish (push) Successful in 33s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 10s
ci / bench (push) Successful in 6m1s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8m33s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9m40s
docker / deploy-docs (push) Successful in 26s
windows-host / package (push) Successful in 15m34s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 13m24s
arch / build-publish (push) Successful in 20m32s
android / android (push) Successful in 20m50s
deb / build-publish (push) Successful in 20m33s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 21m41s
apple / swift (push) Has been cancelled
apple / screenshots (push) Has been cancelled
Pairing: - Refresh the paired-devices list after a native PIN pairing (the happy path never invalidated it, so a newly paired device stayed hidden until remount). - Moonlight PIN: a 204 means "PIN delivered to the waiting handshake", NOT paired, so it now reads "PIN sent" instead of a false "Paired successfully". - Hide the Moonlight pairing card on native-only hosts (HostInfo.gamestream) — it could never receive a PIN there. - Per-row pending on unpair/approve/deny; PIN input maxLength 16 (was 8). Displays / Library: - "Arrange displays" save refreshes the settings card (it rewrites the policy), without clobbering unsaved Custom edits (re-seed only when the draft still matches the server). - Live-display list wrapped in QueryState so errors don't read as "no displays". - "Forever" keep-alive option in the custom editor; edit-game form round-trips the logo artwork (was dropped on save); per-card delete pending. Stats: - Distinct colour for the native "queue" latency stage (it collided with "capture"). - "Not measured on this path" note on the GameStream health chart; configured-bitrate target line on throughput; host-authoritative elapsed timer; LiveCard surfaces non-404 errors. Shell / auth / i18n: - SSR-stable locale: first client render matches the base-locale SSR (no hydration mismatch), then adopts the persisted/browser locale post-hydration. - BFF proxy maps an upstream (mgmt-token) 401 to 502 so a logged-in user isn't bounced into a post-login redirect loop. - Logout checks the POST result before navigating; logs dedup by seq (StrictMode); login "next" keeps query/hash; Dashboard shows the active-session count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
246 lines
6.0 KiB
TypeScript
246 lines
6.0 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",
|
|
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,
|
|
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,
|
|
},
|
|
};
|
|
|
|
export const statusIdle: RuntimeStatus = {
|
|
video_streaming: false,
|
|
audio_streaming: false,
|
|
paired_clients: 1,
|
|
pin_pending: true,
|
|
active_sessions: 0,
|
|
session: null,
|
|
stream: null,
|
|
};
|
|
|
|
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,
|
|
},
|
|
];
|
|
|
|
// --- 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,
|
|
},
|
|
{
|
|
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 };
|