@deck-shelves/host 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jonathan Santos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # @deck-shelves/host
2
+
3
+ The **host contract** for [Deck Shelves](https://github.com/santojon/Deck-Shelves) —
4
+ the `HostApi` types that both host implementations and the bundle build against.
5
+
6
+ It is the boundary between a *host* and the Deck Shelves bundle:
7
+
8
+ - the plugin's **Decky** adapter (`runtime/host/decky.ts`), and
9
+ - the standalone adapter (`runtime/host/standalone.ts`),
10
+ which wraps the loader-injected `window.__SHELVES_HOST__` runtime.
11
+
12
+ Both fulfil the same contract, so the bundle's call sites never depend on a
13
+ specific host.
14
+
15
+ > **Types only.** The standalone host *runtime* — the injected
16
+ > `window.__SHELVES_HOST__` that locates Steam's UI components and adds the
17
+ > Quick Access Menu tab — lives in the **Shelves Loader**, not here. This
18
+ > package is just the interface both sides agree on.
19
+
20
+ It is **not** `@deck-shelves/api` — that package is the public *extension* API
21
+ consumed by third-party plugins (`window.deckShelves`). This one is the
22
+ host↔bundle contract, a different audience.
23
+
24
+ ## What's inside
25
+
26
+ | Path | What it is |
27
+ |---|---|
28
+ | `src/contract/` | **`HostApi` types** (`HOST_API_VERSION`, `lifecycle`, `rpc`, `ui`, `routes`, `notifications`, `platform`, optional `qam`). The single source of truth both hosts and the bundle build against. |
29
+
30
+ ## Usage
31
+
32
+ ```ts
33
+ import { HOST_API_VERSION, type HostApi } from "@deck-shelves/host";
34
+ ```
35
+
36
+ ## Build
37
+
38
+ ```
39
+ pnpm install
40
+ pnpm build # tsup → dist/index.{js,cjs,d.ts}
41
+ pnpm check # typecheck + lint + test
42
+ ```
43
+
44
+ ## License
45
+
46
+ [MIT](LICENSE) © [santojon](https://github.com/santojon)
package/dist/index.cjs ADDED
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ // src/contract/index.ts
4
+ var HOST_API_VERSION = "1.1.0";
5
+
6
+ exports.HOST_API_VERSION = HOST_API_VERSION;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * @deck-shelves/host — HostApi contract (v1.1.0).
3
+ *
4
+ * The single source of truth for the boundary between a *host* (the plugin's
5
+ * host adapter OR the standalone Shelves Loader) and the Deck Shelves bundle.
6
+ * Both host adapters fulfil this shape, so the bundle's call sites depend only
7
+ * on it and never on a specific host's UI library directly.
8
+ *
9
+ * Additive-only after 1.0.0. `qam` was added in 1.1.0 as an optional, additive
10
+ * namespace (only the standalone host implements it today).
11
+ *
12
+ * Dependency-free by design (mirrors `@deck-shelves/api`): supporting data
13
+ * types are inlined. Members annotated "reconciled in Batch 5" are placeholders
14
+ * that get tightened against the plugin's `src/types.ts` / `runtime/platform.ts`
15
+ * when the contract formally supersedes the two divergent in-repo copies.
16
+ */
17
+ declare const HOST_API_VERSION: "1.1.0";
18
+ interface PluginDescriptor {
19
+ name: string;
20
+ version: string;
21
+ }
22
+ interface Disposable {
23
+ dispose(): void;
24
+ }
25
+ interface HostLifecycle {
26
+ /** Call once at bundle mount. Returns a disposer that unregisters. */
27
+ register(plugin: PluginDescriptor): Disposable;
28
+ onMount(cb: () => void): void;
29
+ onUnmount(cb: () => void): void;
30
+ }
31
+ /** Generic RPC channel into the host backend. On the plugin host this routes
32
+ * through the host's backend bridge; on standalone through the loader's HTTP
33
+ * RPC server, which proxies to the plugin's Python backend. */
34
+ interface HostRpc {
35
+ call<Req = unknown, Res = unknown>(method: string, args?: Req): Promise<Res>;
36
+ }
37
+ interface HostRoutes {
38
+ /** Register a full-page route; returns a disposer that removes it. */
39
+ register(path: string, component: () => unknown): Disposable;
40
+ }
41
+ interface ToastOptions {
42
+ title?: string;
43
+ body: string;
44
+ durationMs?: number;
45
+ }
46
+ interface HostNotifications {
47
+ toast(opts: ToastOptions): void;
48
+ }
49
+ /**
50
+ * Steam UI primitives the bundle renders with. On the plugin host these come
51
+ * from its UI library; on the standalone host the injected runtime locates the
52
+ * SAME Steam webpack components (via the Shelves Loader's injected runtime) — we do not
53
+ * reimplement the widgets, we find Steam's own and provide fallbacks.
54
+ *
55
+ * Typed as `unknown` to stay framework-agnostic and dependency-free; the bundle
56
+ * casts each to its React component / handler type at the call site.
57
+ */
58
+ interface HostUi {
59
+ ConfirmModal: unknown;
60
+ DialogBody: unknown;
61
+ DialogButton: unknown;
62
+ DialogControlsSection: unknown;
63
+ Dropdown: unknown;
64
+ DropdownItem: unknown;
65
+ Field: unknown;
66
+ Focusable: unknown;
67
+ GamepadButton: unknown;
68
+ Menu: unknown;
69
+ MenuItem: unknown;
70
+ Navigation: unknown;
71
+ SliderField: unknown;
72
+ Spinner: unknown;
73
+ Tabs: unknown;
74
+ TextField: unknown;
75
+ ToggleField: unknown;
76
+ showContextMenu: (menu: unknown) => void;
77
+ showModal: (modal: unknown) => void;
78
+ }
79
+ /** Opaque until reconciled in Batch 5 with the plugin's `ShelfSource` union. */
80
+ type ShelfSource = unknown;
81
+ interface PlatformCollection {
82
+ id: string;
83
+ name: string;
84
+ }
85
+ interface PlatformTab {
86
+ id: string;
87
+ name: string;
88
+ source?: ShelfSource;
89
+ }
90
+ interface PlatformAppMeta {
91
+ appid: number;
92
+ name: string;
93
+ heroUrl?: string;
94
+ portraitUrl?: string;
95
+ logoUrl?: string;
96
+ iconUrl?: string;
97
+ installed?: boolean;
98
+ isSteam?: boolean;
99
+ deckCompatCategory?: number;
100
+ playtimeMinutes?: number;
101
+ addedTimestamp?: number;
102
+ updatePending?: boolean;
103
+ description?: string;
104
+ fullDescription?: string;
105
+ releaseTimestamp?: number;
106
+ metacriticScore?: number;
107
+ diskUsageBytes?: number;
108
+ }
109
+ interface PlatformApi {
110
+ listCollections(): Promise<PlatformCollection[]>;
111
+ listLibraryTabs(): Promise<PlatformTab[]>;
112
+ resolveShelfAppIds(source: ShelfSource, limit: number, sort?: string | string[], shelfId?: string, sortReverse?: boolean | boolean[], options?: {
113
+ hiddenAppIds?: number[];
114
+ dedupeByName?: boolean;
115
+ }): Promise<number[]>;
116
+ getAppName(appid: number): Promise<string>;
117
+ getAppMeta(appid: number): Promise<PlatformAppMeta>;
118
+ getAppMetaBatch?(appids: number[]): Promise<Map<number, PlatformAppMeta>>;
119
+ navigateToApp(appid: number): void;
120
+ navigateToShelfSource?(source: ShelfSource, title?: string): void;
121
+ /** Host/OS info folded in from the standalone loader's original contract. */
122
+ getOSVersion?(): string;
123
+ checkCompatibility?(): boolean;
124
+ }
125
+ /** A dedicated panel + icon shown in the Steam Quick Access Menu. */
126
+ interface QamPanel {
127
+ /** Stable id — re-registering with the same id replaces the panel. */
128
+ id: string;
129
+ /** Label / accessible name. */
130
+ title: string;
131
+ /** Inline SVG markup (or a `data:` URI) used as the icon. */
132
+ icon: string;
133
+ /** Render into the host-owned container; return an optional cleanup fn. */
134
+ render(container: HTMLElement): void | (() => void);
135
+ }
136
+ interface HostQam {
137
+ /** Register a QAM panel; returns an unregister function. */
138
+ registerPanel(panel: QamPanel): () => void;
139
+ }
140
+ /**
141
+ * What the host process provides to the Deck Shelves bundle. The bundle receives
142
+ * this at startup as `window.__SHELVES_HOST__` and uses it to register itself,
143
+ * invoke host methods, add routes, render Steam-native UI, and (on the
144
+ * standalone host) add Quick Access Menu panels.
145
+ */
146
+ interface HostApi {
147
+ readonly version: typeof HOST_API_VERSION;
148
+ readonly lifecycle: HostLifecycle;
149
+ readonly rpc: HostRpc;
150
+ readonly ui: HostUi;
151
+ readonly routes: HostRoutes;
152
+ readonly notifications?: HostNotifications;
153
+ readonly platform: PlatformApi;
154
+ /** Optional so hosts without a QAM surface still satisfy the shape. */
155
+ readonly qam?: HostQam;
156
+ }
157
+ /** Shape of the runtime global the host installs in the renderer. */
158
+ type ShelvesHostGlobal = HostApi;
159
+ declare global {
160
+ interface Window {
161
+ __SHELVES_HOST__?: ShelvesHostGlobal;
162
+ }
163
+ }
164
+
165
+ export { type Disposable, HOST_API_VERSION, type HostApi, type HostLifecycle, type HostNotifications, type HostQam, type HostRoutes, type HostRpc, type HostUi, type PlatformApi, type PlatformAppMeta, type PlatformCollection, type PlatformTab, type PluginDescriptor, type QamPanel, type ShelfSource, type ShelvesHostGlobal, type ToastOptions };
@@ -0,0 +1,165 @@
1
+ /**
2
+ * @deck-shelves/host — HostApi contract (v1.1.0).
3
+ *
4
+ * The single source of truth for the boundary between a *host* (the plugin's
5
+ * host adapter OR the standalone Shelves Loader) and the Deck Shelves bundle.
6
+ * Both host adapters fulfil this shape, so the bundle's call sites depend only
7
+ * on it and never on a specific host's UI library directly.
8
+ *
9
+ * Additive-only after 1.0.0. `qam` was added in 1.1.0 as an optional, additive
10
+ * namespace (only the standalone host implements it today).
11
+ *
12
+ * Dependency-free by design (mirrors `@deck-shelves/api`): supporting data
13
+ * types are inlined. Members annotated "reconciled in Batch 5" are placeholders
14
+ * that get tightened against the plugin's `src/types.ts` / `runtime/platform.ts`
15
+ * when the contract formally supersedes the two divergent in-repo copies.
16
+ */
17
+ declare const HOST_API_VERSION: "1.1.0";
18
+ interface PluginDescriptor {
19
+ name: string;
20
+ version: string;
21
+ }
22
+ interface Disposable {
23
+ dispose(): void;
24
+ }
25
+ interface HostLifecycle {
26
+ /** Call once at bundle mount. Returns a disposer that unregisters. */
27
+ register(plugin: PluginDescriptor): Disposable;
28
+ onMount(cb: () => void): void;
29
+ onUnmount(cb: () => void): void;
30
+ }
31
+ /** Generic RPC channel into the host backend. On the plugin host this routes
32
+ * through the host's backend bridge; on standalone through the loader's HTTP
33
+ * RPC server, which proxies to the plugin's Python backend. */
34
+ interface HostRpc {
35
+ call<Req = unknown, Res = unknown>(method: string, args?: Req): Promise<Res>;
36
+ }
37
+ interface HostRoutes {
38
+ /** Register a full-page route; returns a disposer that removes it. */
39
+ register(path: string, component: () => unknown): Disposable;
40
+ }
41
+ interface ToastOptions {
42
+ title?: string;
43
+ body: string;
44
+ durationMs?: number;
45
+ }
46
+ interface HostNotifications {
47
+ toast(opts: ToastOptions): void;
48
+ }
49
+ /**
50
+ * Steam UI primitives the bundle renders with. On the plugin host these come
51
+ * from its UI library; on the standalone host the injected runtime locates the
52
+ * SAME Steam webpack components (via the Shelves Loader's injected runtime) — we do not
53
+ * reimplement the widgets, we find Steam's own and provide fallbacks.
54
+ *
55
+ * Typed as `unknown` to stay framework-agnostic and dependency-free; the bundle
56
+ * casts each to its React component / handler type at the call site.
57
+ */
58
+ interface HostUi {
59
+ ConfirmModal: unknown;
60
+ DialogBody: unknown;
61
+ DialogButton: unknown;
62
+ DialogControlsSection: unknown;
63
+ Dropdown: unknown;
64
+ DropdownItem: unknown;
65
+ Field: unknown;
66
+ Focusable: unknown;
67
+ GamepadButton: unknown;
68
+ Menu: unknown;
69
+ MenuItem: unknown;
70
+ Navigation: unknown;
71
+ SliderField: unknown;
72
+ Spinner: unknown;
73
+ Tabs: unknown;
74
+ TextField: unknown;
75
+ ToggleField: unknown;
76
+ showContextMenu: (menu: unknown) => void;
77
+ showModal: (modal: unknown) => void;
78
+ }
79
+ /** Opaque until reconciled in Batch 5 with the plugin's `ShelfSource` union. */
80
+ type ShelfSource = unknown;
81
+ interface PlatformCollection {
82
+ id: string;
83
+ name: string;
84
+ }
85
+ interface PlatformTab {
86
+ id: string;
87
+ name: string;
88
+ source?: ShelfSource;
89
+ }
90
+ interface PlatformAppMeta {
91
+ appid: number;
92
+ name: string;
93
+ heroUrl?: string;
94
+ portraitUrl?: string;
95
+ logoUrl?: string;
96
+ iconUrl?: string;
97
+ installed?: boolean;
98
+ isSteam?: boolean;
99
+ deckCompatCategory?: number;
100
+ playtimeMinutes?: number;
101
+ addedTimestamp?: number;
102
+ updatePending?: boolean;
103
+ description?: string;
104
+ fullDescription?: string;
105
+ releaseTimestamp?: number;
106
+ metacriticScore?: number;
107
+ diskUsageBytes?: number;
108
+ }
109
+ interface PlatformApi {
110
+ listCollections(): Promise<PlatformCollection[]>;
111
+ listLibraryTabs(): Promise<PlatformTab[]>;
112
+ resolveShelfAppIds(source: ShelfSource, limit: number, sort?: string | string[], shelfId?: string, sortReverse?: boolean | boolean[], options?: {
113
+ hiddenAppIds?: number[];
114
+ dedupeByName?: boolean;
115
+ }): Promise<number[]>;
116
+ getAppName(appid: number): Promise<string>;
117
+ getAppMeta(appid: number): Promise<PlatformAppMeta>;
118
+ getAppMetaBatch?(appids: number[]): Promise<Map<number, PlatformAppMeta>>;
119
+ navigateToApp(appid: number): void;
120
+ navigateToShelfSource?(source: ShelfSource, title?: string): void;
121
+ /** Host/OS info folded in from the standalone loader's original contract. */
122
+ getOSVersion?(): string;
123
+ checkCompatibility?(): boolean;
124
+ }
125
+ /** A dedicated panel + icon shown in the Steam Quick Access Menu. */
126
+ interface QamPanel {
127
+ /** Stable id — re-registering with the same id replaces the panel. */
128
+ id: string;
129
+ /** Label / accessible name. */
130
+ title: string;
131
+ /** Inline SVG markup (or a `data:` URI) used as the icon. */
132
+ icon: string;
133
+ /** Render into the host-owned container; return an optional cleanup fn. */
134
+ render(container: HTMLElement): void | (() => void);
135
+ }
136
+ interface HostQam {
137
+ /** Register a QAM panel; returns an unregister function. */
138
+ registerPanel(panel: QamPanel): () => void;
139
+ }
140
+ /**
141
+ * What the host process provides to the Deck Shelves bundle. The bundle receives
142
+ * this at startup as `window.__SHELVES_HOST__` and uses it to register itself,
143
+ * invoke host methods, add routes, render Steam-native UI, and (on the
144
+ * standalone host) add Quick Access Menu panels.
145
+ */
146
+ interface HostApi {
147
+ readonly version: typeof HOST_API_VERSION;
148
+ readonly lifecycle: HostLifecycle;
149
+ readonly rpc: HostRpc;
150
+ readonly ui: HostUi;
151
+ readonly routes: HostRoutes;
152
+ readonly notifications?: HostNotifications;
153
+ readonly platform: PlatformApi;
154
+ /** Optional so hosts without a QAM surface still satisfy the shape. */
155
+ readonly qam?: HostQam;
156
+ }
157
+ /** Shape of the runtime global the host installs in the renderer. */
158
+ type ShelvesHostGlobal = HostApi;
159
+ declare global {
160
+ interface Window {
161
+ __SHELVES_HOST__?: ShelvesHostGlobal;
162
+ }
163
+ }
164
+
165
+ export { type Disposable, HOST_API_VERSION, type HostApi, type HostLifecycle, type HostNotifications, type HostQam, type HostRoutes, type HostRpc, type HostUi, type PlatformApi, type PlatformAppMeta, type PlatformCollection, type PlatformTab, type PluginDescriptor, type QamPanel, type ShelfSource, type ShelvesHostGlobal, type ToastOptions };
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // src/contract/index.ts
2
+ var HOST_API_VERSION = "1.1.0";
3
+
4
+ export { HOST_API_VERSION };
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@deck-shelves/host",
3
+ "version": "0.1.0",
4
+ "description": "Deck Shelves host contract — the HostApi types all host adapters and the bundle build against.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "sideEffects": false,
22
+ "engines": {
23
+ "node": ">=20"
24
+ },
25
+ "scripts": {
26
+ "upgrade": "node scripts/upgrade-pnpm.cjs && pnpm --version",
27
+ "update": "pnpm update --latest && pnpm install",
28
+ "update:check": "pnpm outdated || true",
29
+ "build": "tsup",
30
+ "typecheck": "tsc --noEmit",
31
+ "lint": "eslint \"src/**/*.ts\"",
32
+ "lint:fix": "eslint --fix \"src/**/*.ts\"",
33
+ "test": "vitest run",
34
+ "test:watch": "vitest",
35
+ "check": "pnpm run typecheck && pnpm run lint && pnpm run test",
36
+ "dev:check": "pnpm run check",
37
+ "clean": "node scripts/clean.mjs",
38
+ "release:dry": "pnpm run clean && pnpm run check && pnpm run build && pnpm pack --dry-run",
39
+ "release:local": "pnpm run clean && pnpm run check && pnpm run build && pnpm pack",
40
+ "prepublishOnly": "pnpm run clean && pnpm run check && pnpm run build"
41
+ },
42
+ "keywords": [
43
+ "deck-shelves",
44
+ "steam-deck",
45
+ "steam",
46
+ "host"
47
+ ],
48
+ "author": "Jonathan Santos",
49
+ "license": "MIT",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/santojon/Deck-Shelves-HOST.git"
53
+ },
54
+ "bugs": {
55
+ "url": "https://github.com/santojon/Deck-Shelves-HOST/issues"
56
+ },
57
+ "homepage": "https://github.com/santojon/Deck-Shelves-HOST#readme",
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "pnpm": {
62
+ "onlyBuiltDependencies": [
63
+ "esbuild"
64
+ ],
65
+ "overrides": {
66
+ "esbuild": "^0.25.0",
67
+ "vite": "^6.4.2",
68
+ "form-data": "^4.0.6"
69
+ }
70
+ },
71
+ "devDependencies": {
72
+ "@typescript-eslint/parser": "^8.61.1",
73
+ "eslint": "^9.39.4",
74
+ "jsdom": "^25.0.1",
75
+ "tsup": "^8.5.1",
76
+ "typescript": "^5.9.3",
77
+ "vitest": "^3.2.6"
78
+ },
79
+ "packageManager": "pnpm@10.33.0"
80
+ }