p2p-lockstep-kit-ui 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/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # p2p-lockstep-kit-ui
2
+
3
+ Web Components UI shell for `p2p-lockstep-kit-network` and `p2p-lockstep-kit-session`.
4
+
5
+ The UI package owns the generic app surface:
6
+
7
+ - lobby page
8
+ - signaling registration
9
+ - peer connection
10
+ - share link and QR code
11
+ - game page shell
12
+ - ready / start / undo / restart controls
13
+ - request dialogs and toast messages
14
+ - board host container
15
+
16
+ Game projects mount their actual board into the board host and talk to the session through the runtime exposed by the UI element.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npm install p2p-lockstep-kit-ui
22
+ ```
23
+
24
+ The UI package depends on:
25
+
26
+ ```text
27
+ p2p-lockstep-kit-network
28
+ p2p-lockstep-kit-session
29
+ ```
30
+
31
+ Game projects do not need to instantiate those packages directly for the common lobby/game flow.
32
+
33
+ ## Basic Usage
34
+
35
+ ```ts
36
+ import "p2p-lockstep-kit-ui";
37
+ import "p2p-lockstep-kit-ui/style.css";
38
+ ```
39
+
40
+ ```html
41
+ <p2p-lockstep-app
42
+ game-title="Gomoku"
43
+ session-id="gomoku"
44
+ ></p2p-lockstep-app>
45
+ ```
46
+
47
+ The default signaling server is `wss://signal.jiahengli.xyz`. Override it only when you
48
+ run your own signaling endpoint:
49
+
50
+ ```html
51
+ <p2p-lockstep-app
52
+ game-title="Gomoku"
53
+ session-id="gomoku"
54
+ signal-url="wss://signal.example.com"
55
+ ></p2p-lockstep-app>
56
+ ```
57
+
58
+ ## Mounting A Game Board
59
+
60
+ ```ts
61
+ import "p2p-lockstep-kit-ui";
62
+ import "p2p-lockstep-kit-ui/style.css";
63
+ import type { GameRuntime, P2PLockstepAppElement } from "p2p-lockstep-kit-ui";
64
+
65
+ const app = document.querySelector("p2p-lockstep-app") as P2PLockstepAppElement | null;
66
+
67
+ if (!app) {
68
+ throw new Error("Missing p2p-lockstep-app");
69
+ }
70
+
71
+ await customElements.whenDefined("p2p-lockstep-app");
72
+
73
+ const runtime = app.getRuntime() as GameRuntime | null;
74
+ const boardHost = app.getBoardHost();
75
+
76
+ if (!runtime || !boardHost) {
77
+ throw new Error("Lockstep UI is not ready");
78
+ }
79
+
80
+ const board = document.createElement("div");
81
+ board.className = "h-full";
82
+ boardHost.append(board);
83
+
84
+ runtime.observer.subscribe({
85
+ onStateChange(snapshot) {
86
+ // Render board state from snapshot.history, snapshot.turn, and local/remote states.
87
+ },
88
+ });
89
+
90
+ board.addEventListener("click", () => {
91
+ runtime.actions.move({
92
+ // Game-specific move payload.
93
+ });
94
+ });
95
+ ```
96
+
97
+ ## Runtime Boundary
98
+
99
+ The UI exposes the runtime from the app element:
100
+
101
+ ```ts
102
+ const runtime = app.getRuntime();
103
+ ```
104
+
105
+ Game runtime actions:
106
+
107
+ ```ts
108
+ runtime.actions.move(move);
109
+ ```
110
+
111
+ Ready, start, undo, restart, approve, and reject are owned by the UI shell
112
+ controls. Game packages should not drive those controls directly.
113
+
114
+ Runtime state:
115
+
116
+ ```ts
117
+ runtime.observer.subscribe({
118
+ onStateChange(snapshot) {},
119
+ onConnectionChange(connected) {},
120
+ onGameEvent(event) {},
121
+ onError(error) {},
122
+ });
123
+ ```
124
+
125
+ Network details are intentionally kept private to the UI shell for the standard flow.
126
+
127
+ ## Package Design
128
+
129
+ ```text
130
+ game project
131
+ -> imports p2p-lockstep-kit-ui
132
+ -> mounts board into p2p-lockstep-app.getBoardHost()
133
+ -> sends moves through p2p-lockstep-app.getRuntime().actions
134
+
135
+ p2p-lockstep-kit-ui
136
+ -> owns lobby/game pages and common controls
137
+ -> creates NetworkClient
138
+ -> creates session
139
+ -> maps session snapshots into UI state
140
+
141
+ p2p-lockstep-kit-network / p2p-lockstep-kit-session
142
+ -> provide transport and lockstep state
143
+ ```
144
+
145
+ ## Development
146
+
147
+ ```bash
148
+ pnpm install
149
+ pnpm dev
150
+ pnpm typecheck
151
+ pnpm build
152
+ ```
153
+
154
+ The local Vite config aliases `p2p-lockstep-kit-ui`, `p2p-lockstep-kit-network`, and `p2p-lockstep-kit-session` to local source folders for development. Published consumers resolve the package dependencies normally.
155
+
156
+ ## Cloudflare Pages Demo
157
+
158
+ `pnpm build` builds the npm library package and does not emit an `index.html`.
159
+ Use the Pages build when deploying the demo site:
160
+
161
+ ```bash
162
+ pnpm build:pages
163
+ ```
164
+
165
+ Cloudflare Pages settings:
166
+
167
+ ```text
168
+ Build command: pnpm build:pages
169
+ Build output directory: dist-pages
170
+ ```
package/dist/_headers ADDED
@@ -0,0 +1,8 @@
1
+ /
2
+ Cache-Control: no-store
3
+
4
+ /*.html
5
+ Cache-Control: no-store
6
+
7
+ /assets/*
8
+ Cache-Control: public, max-age=31536000, immutable
@@ -0,0 +1,11 @@
1
+ import type { GameRuntime } from "./types";
2
+ export declare class P2PLockstepAppElement extends HTMLElement {
3
+ #private;
4
+ static get observedAttributes(): string[];
5
+ connectedCallback(): void;
6
+ disconnectedCallback(): void;
7
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void;
8
+ getRuntime(): GameRuntime | null;
9
+ getBoardHost(): HTMLDivElement | null;
10
+ render(): void;
11
+ }
@@ -0,0 +1,9 @@
1
+ import type { ActionBarState } from "../types";
2
+ export declare class P2PLockstepActionBarElement extends HTMLElement {
3
+ #private;
4
+ connectedCallback(): void;
5
+ disconnectedCallback(): void;
6
+ set state(value: ActionBarState);
7
+ get state(): ActionBarState;
8
+ render(): void;
9
+ }
@@ -0,0 +1,9 @@
1
+ import type { DialogState } from "../types";
2
+ export declare class P2PLockstepConfirmDialogElement extends HTMLElement {
3
+ #private;
4
+ connectedCallback(): void;
5
+ disconnectedCallback(): void;
6
+ set state(value: DialogState);
7
+ get state(): DialogState;
8
+ render(): void;
9
+ }
@@ -0,0 +1,9 @@
1
+ import type { SharePanelState } from "../types";
2
+ export declare class P2PLockstepSharePanelElement extends HTMLElement {
3
+ #private;
4
+ connectedCallback(): void;
5
+ disconnectedCallback(): void;
6
+ set state(value: SharePanelState);
7
+ get state(): SharePanelState;
8
+ render(): Promise<void>;
9
+ }
@@ -0,0 +1,8 @@
1
+ import type { StatusPanelState } from "../types";
2
+ export declare class P2PLockstepStatusPanelElement extends HTMLElement {
3
+ #private;
4
+ connectedCallback(): void;
5
+ set state(value: StatusPanelState);
6
+ get state(): StatusPanelState;
7
+ render(): void;
8
+ }
@@ -0,0 +1,8 @@
1
+ import type { ToastState } from "../types";
2
+ export declare class P2PLockstepToastMessageElement extends HTMLElement {
3
+ #private;
4
+ connectedCallback(): void;
5
+ set state(value: ToastState);
6
+ get state(): ToastState;
7
+ render(): void;
8
+ }
@@ -0,0 +1 @@
1
+ export declare const DEFAULT_SIGNAL_URL = "wss://signal.jiahengli.xyz";
@@ -0,0 +1,7 @@
1
+ export declare class P2PLockstepBoardHostElement extends HTMLElement {
2
+ #private;
3
+ connectedCallback(): void;
4
+ disconnectedCallback(): void;
5
+ getMount(): HTMLDivElement | null;
6
+ render(): void;
7
+ }
@@ -0,0 +1,12 @@
1
+ export { DEFAULT_SIGNAL_URL } from "./config";
2
+ export declare const defineP2PLockstepUi: () => void;
3
+ export * from "./types";
4
+ export { P2PLockstepActionBarElement } from "./components/action-bar";
5
+ export { P2PLockstepConfirmDialogElement } from "./components/confirm-dialog";
6
+ export { P2PLockstepSharePanelElement } from "./components/share-panel";
7
+ export { P2PLockstepStatusPanelElement } from "./components/status-panel";
8
+ export { P2PLockstepToastMessageElement } from "./components/toast-message";
9
+ export { P2PLockstepBoardHostElement } from "./game/board-host";
10
+ export { P2PLockstepLobbyPageElement } from "./pages/lobby-page";
11
+ export { P2PLockstepGamePageElement } from "./pages/game-page";
12
+ export { P2PLockstepAppElement } from "./app-shell";