apple / swift (push) Successful in 5m38s
windows-host / package (push) Successful in 19m0s
ci / web (push) Successful in 56s
ci / docs-site (push) Successful in 1m4s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m15s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m21s
ci / bench (push) Successful in 5m41s
ci / rust-arm64 (push) Successful in 9m32s
ci / rust (push) Failing after 12m19s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m46s
android / android (push) Successful in 17m49s
decky / build-publish (push) Successful in 1m11s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 16s
arch / build-publish (push) Successful in 19m46s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 39s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 12s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 22s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 5m22s
deb / build-publish (push) Successful in 9m9s
deb / build-publish-client-arm64 (push) Successful in 7m26s
deb / build-publish-host (push) Successful in 9m46s
flatpak / build-publish (push) Failing after 8m33s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m57s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 21m56s
docker / build-push-arm64cross (push) Successful in 9s
docker / deploy-docs (push) Successful in 11s
apple / screenshots (push) Successful in 25m51s
Emulation-and-beyond libraries need more than a title and a poster. Every library shape (GameEntry, CustomEntry, CustomInput, ProviderEntryInput) now carries a shared, flattened GameMeta: platform, description, developer, publisher, release_year, genres, tags, region, players. All fields are optional and flat on the wire, so existing library.json files, provider plugins, and clients keep working unchanged. - Installed-store scanners (Steam, Lutris, Heroic, Epic, GOG, Xbox) stamp platform=PC; custom/provider entries carry whatever was authored. - GET /library grows a ?platform= filter (case-insensitive) beside ?provider=. - Console: the add/edit form gets a Details section (round-tripping every field, since update replaces the entry), the poster tile a platform badge (non-PC only — the store badge already implies PC) and the year. - plugin-kit: ProviderEntry accepts the same fields (new GameMeta schema, spread flat); SDK + OpenAPI spec regenerated. - pf-client-core decodes platform for future client badges. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
82 lines
3.8 KiB
TypeScript
82 lines
3.8 KiB
TypeScript
// The library-provider wire schemas — a browser-safe module (no node imports) so plugin
|
|
// CONTRACTS can share these types with their UIs. Mirrors the host's `ProviderEntryInput`
|
|
// (crates/punktfunk-host mgmt/library.rs). Identity codecs: plain JSON shapes, so values
|
|
// pass through unencoded; the value is the shared type + authoring validation.
|
|
import { Schema } from "effect";
|
|
|
|
export const Artwork = Schema.Struct({
|
|
portrait: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
hero: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
logo: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
header: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
});
|
|
export type Artwork = typeof Artwork.Type;
|
|
|
|
export const LaunchSpec = Schema.Struct({
|
|
kind: Schema.Literal("command"),
|
|
value: Schema.String,
|
|
});
|
|
export type LaunchSpec = typeof LaunchSpec.Type;
|
|
|
|
export const PrepStep = Schema.Struct({
|
|
do: Schema.String,
|
|
undo: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
});
|
|
export type PrepStep = typeof PrepStep.Type;
|
|
|
|
/**
|
|
* How the host should recognize a title's process once it is running.
|
|
*
|
|
* Every field is optional, and omitting the whole thing is fine: the host tracks the process it
|
|
* spawns for the entry anyway. It matters when your launch command hands off and exits — a launcher
|
|
* client, a `flatpak run`, a front-end that starts an emulator — because then the host has nothing
|
|
* left to watch, and the two behaviors this feeds ("end the session when the game exits" and "end the
|
|
* game when the session ends") go quiet for that title.
|
|
*
|
|
* Send whatever you actually know. `install_dir` is the one worth sending if you send only one: any
|
|
* process running from under it counts as the game.
|
|
*/
|
|
export const DetectHint = Schema.Struct({
|
|
/** Where the title is installed (absolute path on the host). */
|
|
install_dir: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
/** The game's own executable (absolute path on the host). */
|
|
exe: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
/** The executable's file name (`Hades.exe`), when its location isn't fixed. Weakest signal. */
|
|
process_name: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
});
|
|
export type DetectHint = typeof DetectHint.Type;
|
|
|
|
/** Descriptive metadata, flat on the wire beside `title` (mirrors the host's flattened
|
|
* `GameMeta`). All fields optional; values are free-form display strings — the host does not
|
|
* normalize platform/genre vocabularies. */
|
|
export const GameMeta = Schema.Struct({
|
|
/** The system the title runs on — `"PS2"`, `"Xbox 360"`, `"SNES"`, … */
|
|
platform: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
/** Short blurb for a details pane. */
|
|
description: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
developer: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
publisher: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
/** Year of first release. */
|
|
release_year: Schema.optionalKey(Schema.NullOr(Schema.Number)),
|
|
/** Genre taxonomy from the metadata source (`"RPG"`, `"Platformer"`, …). */
|
|
genres: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
/** Free-form organizational labels (`"co-op"`, `"kids"`, …). */
|
|
tags: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
/** Release region — `"NTSC-U"`, `"PAL"`, `"NTSC-J"`. */
|
|
region: Schema.optionalKey(Schema.NullOr(Schema.String)),
|
|
/** Maximum simultaneous (local) players. */
|
|
players: Schema.optionalKey(Schema.NullOr(Schema.Number)),
|
|
});
|
|
export type GameMeta = typeof GameMeta.Type;
|
|
|
|
export const ProviderEntry = Schema.Struct({
|
|
external_id: Schema.String,
|
|
title: Schema.String,
|
|
art: Schema.optionalKey(Artwork),
|
|
launch: Schema.optionalKey(Schema.NullOr(LaunchSpec)),
|
|
prep: Schema.optionalKey(Schema.Array(PrepStep)),
|
|
detect: Schema.optionalKey(DetectHint),
|
|
...GameMeta.fields,
|
|
});
|
|
export type ProviderEntry = typeof ProviderEntry.Type;
|