8 Commits

Author SHA1 Message Date
enricobuehler e8acf922de feat(ui): three-page console SPA on @unom/ui + the kit's react helpers
CI / build (push) Waiting to run
CI / exporter (push) Waiting to run
CI / publish (push) Blocked by required conditions
Overview (exporter health, Playnite version, counts, sync, live SSE activity,
and a first-run panel that names the exact ingest path the .pext writes to),
Library (covers + per-game include toggles + why-not-synced), Settings
(filters, art delivery, sync cadence, paths).

Data layer is AtomHttpApi atoms derived from the shared contract; the whole
thing runs with zero host against an in-browser mock transport with four
scenarios, and Storybook renders the real pages on the same fixtures. CI now
asserts the production bundle carries no fixture strings.

Also: CI rebuilt for the workspace layout (one root install, per-package
typecheck, publish from plugin/ on a v* tag) with the exporter job and
`publish: needs [build, exporter]` unchanged; README rewritten around the three
workspaces, with the ingest inbox explained as the load-bearing mechanism it is.
2026-07-20 21:05:04 +02:00
enricobuehler b81e03685a refactor: rewrite on @punktfunk/plugin-kit — contract + plugin workspaces
The framework half of this plugin (engine lifecycle, config/state, host
reconcile, UI server, CLI) was ~80% identical to rom-manager's. That code is
now @punktfunk/plugin-kit; this replaces the local copy with it and adopts the
rom-manager blueprint layout.

  contract/  Effect Schemas: the cross-process export contract (the C#
             exporter's other half, with the schema-version guard as a decode
             check), the config schema (one schema, Encoded = authored file
             shape, defaults only via withDecodingDefaultKey), domain DTOs, the
             HttpApi contract, API errors.
  plugin/    src/domain = the pure core (locate/read, art, reconcile), ported
             tests green before any Effect wiring; src/services = the kit's
             ConfigService/CacheStore/SyncEngine/ProviderClient; definePluginKit
             entry, kit CLI, auth-free dev API.

Preserved, because they are what makes playnite work at all: the ingest inbox
is still read FIRST (the de-privileged LocalService runner cannot reach the
interactive user's %APPDATA%), the host/dataurl/off art modes, and the
playnite:// launch hand-back. The C# exporter is untouched.

New: per-game include/exclude overrides, and an allow-listed /api/art proxy so
the SPA can render local Playnite covers — it serves only paths the currently
ingested export references.

Dropped: the standalone password UI server (console surface + CLI only, as in
rom-manager 0.3.0) and the devEntry flag.

Trap found and fixed here: the HttpApi encoder serialises `undefined` as
`null`, so a `Schema.optional` field the server omits cannot be decoded by the
client — silently in the SSE feed, loudly in the typed client. Every optional
EngineStatus field is `Schema.NullOr` with an explicit value on the wire.
2026-07-20 20:56:12 +02:00
enricobuehler e832201669 fix: read/write the library via the host ingest inbox (de-privileged runner)
CI / publish (push) Successful in 17s
CI / exporter (push) Successful in 11s
CI / build (push) Successful in 20s
The host plugin runner is de-privileged now (LocalService) and can't read the
interactive user's %APPDATA%\Playnite\ExtensionsData — so it never saw the
export. Bridge it through the host's user-writable ingest inbox:

- exporter (C#): also drop punktfunk-library.json into
  %ProgramData%\punktfunk\ingest\playnite (best-effort, only when a punktfunk
  host is present), alongside its ExtensionsData home.
- plugin (TS): locateExport() reads <config_dir>/ingest/playnite first, then
  falls back to the ExtensionsData scan (same-user/SYSTEM runner, Linux). The
  watcher also watches the inbox so a drop reconciles within seconds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.1.1
2026-07-20 00:40:28 +02:00
enricobuehler 1eb0f650ab fix: persist state under plugin-state/ (runner de-privilege)
CI / exporter (push) Successful in 12s
CI / build (push) Successful in 23s
CI / publish (push) Successful in 19s
The managed runner is de-privileged on Windows now (runs as LocalService),
and %ProgramData%\punktfunk is locked read-only to it — so writing config/
cache straight under the config dir fails EPERM. Move the plugin's state to
<config_dir>/plugin-state/playnite, which `punktfunk-host plugins enable`
grants the runner write on. On Linux the runner owns the config dir, so the
path is writable there too. First release, so no migration needed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.1.0
2026-07-20 00:15:14 +02:00
enricobuehler 9988729aab feat(art): default to host-served cover art (scales to large libraries)
CI / publish (push) Has been skipped
CI / exporter (push) Successful in 12s
CI / build (push) Successful in 20s
Adds art.mode "host" (now the default): emit each cover's local file PATH
and let the host serve the bytes via its art proxy, instead of inlining a
data URL. The reconcile payload carries no image data, so a large Playnite
library no longer exceeds the host's request-body limit. "dataurl" (inline,
small libraries) and "off" remain. Requires a host with the provider-art
proxy (punktfunk main: feat/library-local-art).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 00:56:15 +02:00
enricobuehler f77790db05 ci: fix default-export sanity check (default export IS the plugin)
CI / exporter (push) Successful in 11s
CI / build (push) Successful in 22s
CI / publish (push) Has been skipped
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 23:37:48 +02:00
enricobuehler aef88842d2 ci: drop unzip verification from .pext packaging (unzip absent in the dotnet SDK image)
CI / exporter (push) Successful in 11s
CI / build (push) Failing after 19s
CI / publish (push) Has been skipped
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 23:36:45 +02:00
enricobuehler 6694be9848 feat: Playnite library sync plugin + exporter
CI / exporter (push) Failing after 24s
CI / build (push) Failing after 25s
CI / publish (push) Has been skipped
A two-part Punktfunk plugin that syncs the Playnite library into the host
game library as the `playnite` provider:

- exporter/ — a minimal C# Playnite GenericPlugin ("Punktfunk Sync") that
  writes punktfunk-library.json on every library change. Reading Playnite's
  live-locked LiteDB from outside isn't robust, so this adapter runs inside
  Playnite. Builds net462 via reference assemblies; packaged as a .pext.
- src/ — the TS plugin (on @punktfunk/host, rom-manager shape): watches the
  export, embeds covers as data URLs, reconciles via
  PUT /library/provider/playnite, with a console-hosted web UI + standalone
  fallback + CLI. Launch = playnite://playnite/start/<id>, run by the host
  in the interactive session so Playnite performs the real launch.

Verified locally: backend tsc + 23 tests + biome clean, SPA build, C#
exporter build + .pext pack.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 23:35:35 +02:00