dreamboard 0.1.21 → 0.1.22

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.
Files changed (186) hide show
  1. package/dist/{chunk-EYYWGWTO.js → chunk-2RCUHMGL.js} +343 -1911
  2. package/dist/chunk-2RCUHMGL.js.map +1 -0
  3. package/dist/{chunk-MOVHYB6E.js → chunk-AQ5UUNJS.js} +1018 -692
  4. package/dist/chunk-AQ5UUNJS.js.map +1 -0
  5. package/dist/{chunk-BMYC6772.js → chunk-H3O43F5P.js} +9765 -3068
  6. package/dist/chunk-H3O43F5P.js.map +1 -0
  7. package/dist/dev-host/dev-api-proxy-plugin.ts +330 -0
  8. package/dist/dev-host/dev-diagnostics.ts +62 -0
  9. package/dist/dev-host/dev-fallback-stylesheet.ts +50 -0
  10. package/dist/dev-host/dev-host-controller.ts +686 -0
  11. package/dist/dev-host/dev-host-player-query.ts +17 -0
  12. package/dist/dev-host/dev-host-session-transport.ts +52 -0
  13. package/dist/dev-host/dev-host-storage.ts +56 -0
  14. package/dist/dev-host/dev-log-relay-plugin.ts +469 -0
  15. package/dist/dev-host/dev-runtime-config.ts +14 -0
  16. package/dist/dev-host/dev-runtime-platform.ts +419 -0
  17. package/dist/dev-host/dev-virtual-modules-plugin.ts +63 -0
  18. package/dist/dev-host/host-main.css +182 -0
  19. package/dist/dev-host/host-main.tsx +754 -0
  20. package/dist/dev-host/index.html +56 -0
  21. package/dist/dev-host/plugin-main.ts +55 -0
  22. package/dist/dev-host/plugin.html +24 -0
  23. package/dist/dev-host/start-dev-server.ts +138 -0
  24. package/dist/dev-host/virtual-modules.d.ts +27 -0
  25. package/dist/dist-FEPN3BDN.js +16543 -0
  26. package/dist/dist-FEPN3BDN.js.map +1 -0
  27. package/dist/{dist-WJRJNFLI.js → dist-FRURQI7Q.js} +4 -2
  28. package/dist/index.js +3 -3
  29. package/dist/internal.js +159 -124
  30. package/dist/internal.js.map +1 -1
  31. package/dist/runtime-packages/tailwind-config/shared-styles.css +146 -0
  32. package/dist/runtime-packages/ui/src/components/.gitkeep +0 -0
  33. package/dist/runtime-packages/ui/src/components/accordion.tsx +66 -0
  34. package/dist/runtime-packages/ui/src/components/ai-elements/actions.tsx +65 -0
  35. package/dist/runtime-packages/ui/src/components/ai-elements/artifact.tsx +147 -0
  36. package/dist/runtime-packages/ui/src/components/ai-elements/branch.tsx +215 -0
  37. package/dist/runtime-packages/ui/src/components/ai-elements/canvas.tsx +22 -0
  38. package/dist/runtime-packages/ui/src/components/ai-elements/chain-of-thought.tsx +228 -0
  39. package/dist/runtime-packages/ui/src/components/ai-elements/code-block.tsx +179 -0
  40. package/dist/runtime-packages/ui/src/components/ai-elements/confirmation.tsx +158 -0
  41. package/dist/runtime-packages/ui/src/components/ai-elements/connection.tsx +28 -0
  42. package/dist/runtime-packages/ui/src/components/ai-elements/context.tsx +408 -0
  43. package/dist/runtime-packages/ui/src/components/ai-elements/controls.tsx +18 -0
  44. package/dist/runtime-packages/ui/src/components/ai-elements/conversation.tsx +97 -0
  45. package/dist/runtime-packages/ui/src/components/ai-elements/edge.tsx +140 -0
  46. package/dist/runtime-packages/ui/src/components/ai-elements/image.tsx +24 -0
  47. package/dist/runtime-packages/ui/src/components/ai-elements/inline-citation.tsx +287 -0
  48. package/dist/runtime-packages/ui/src/components/ai-elements/loader.tsx +96 -0
  49. package/dist/runtime-packages/ui/src/components/ai-elements/message.tsx +463 -0
  50. package/dist/runtime-packages/ui/src/components/ai-elements/node.tsx +71 -0
  51. package/dist/runtime-packages/ui/src/components/ai-elements/open-in-chat.tsx +365 -0
  52. package/dist/runtime-packages/ui/src/components/ai-elements/panel.tsx +15 -0
  53. package/dist/runtime-packages/ui/src/components/ai-elements/plan.tsx +142 -0
  54. package/dist/runtime-packages/ui/src/components/ai-elements/prompt-input.tsx +1380 -0
  55. package/dist/runtime-packages/ui/src/components/ai-elements/queue.tsx +274 -0
  56. package/dist/runtime-packages/ui/src/components/ai-elements/reasoning.tsx +182 -0
  57. package/dist/runtime-packages/ui/src/components/ai-elements/response.tsx +22 -0
  58. package/dist/runtime-packages/ui/src/components/ai-elements/shimmer.tsx +64 -0
  59. package/dist/runtime-packages/ui/src/components/ai-elements/sources.tsx +77 -0
  60. package/dist/runtime-packages/ui/src/components/ai-elements/suggestion.tsx +53 -0
  61. package/dist/runtime-packages/ui/src/components/ai-elements/task.tsx +87 -0
  62. package/dist/runtime-packages/ui/src/components/ai-elements/tool.tsx +165 -0
  63. package/dist/runtime-packages/ui/src/components/ai-elements/toolbar.tsx +16 -0
  64. package/dist/runtime-packages/ui/src/components/ai-elements/web-preview.tsx +263 -0
  65. package/dist/runtime-packages/ui/src/components/alert-dialog.tsx +157 -0
  66. package/dist/runtime-packages/ui/src/components/alert.tsx +66 -0
  67. package/dist/runtime-packages/ui/src/components/avatar.tsx +53 -0
  68. package/dist/runtime-packages/ui/src/components/badge.tsx +46 -0
  69. package/dist/runtime-packages/ui/src/components/button-group.tsx +83 -0
  70. package/dist/runtime-packages/ui/src/components/button.tsx +65 -0
  71. package/dist/runtime-packages/ui/src/components/card.tsx +92 -0
  72. package/dist/runtime-packages/ui/src/components/carousel.tsx +241 -0
  73. package/dist/runtime-packages/ui/src/components/collapsible.tsx +33 -0
  74. package/dist/runtime-packages/ui/src/components/command.tsx +184 -0
  75. package/dist/runtime-packages/ui/src/components/context-menu.tsx +252 -0
  76. package/dist/runtime-packages/ui/src/components/counter.tsx +11 -0
  77. package/dist/runtime-packages/ui/src/components/dialog.tsx +146 -0
  78. package/dist/runtime-packages/ui/src/components/drawer.tsx +132 -0
  79. package/dist/runtime-packages/ui/src/components/dropdown-menu.tsx +257 -0
  80. package/dist/runtime-packages/ui/src/components/form.tsx +168 -0
  81. package/dist/runtime-packages/ui/src/components/header.tsx +13 -0
  82. package/dist/runtime-packages/ui/src/components/hover-card.tsx +44 -0
  83. package/dist/runtime-packages/ui/src/components/input-group.tsx +178 -0
  84. package/dist/runtime-packages/ui/src/components/input.tsx +21 -0
  85. package/dist/runtime-packages/ui/src/components/item.tsx +193 -0
  86. package/dist/runtime-packages/ui/src/components/label.tsx +24 -0
  87. package/dist/runtime-packages/ui/src/components/navigation-menu.tsx +168 -0
  88. package/dist/runtime-packages/ui/src/components/popover.tsx +55 -0
  89. package/dist/runtime-packages/ui/src/components/progress.tsx +31 -0
  90. package/dist/runtime-packages/ui/src/components/radio-group.tsx +45 -0
  91. package/dist/runtime-packages/ui/src/components/resizable.tsx +56 -0
  92. package/dist/runtime-packages/ui/src/components/scroll-area.tsx +58 -0
  93. package/dist/runtime-packages/ui/src/components/select.tsx +187 -0
  94. package/dist/runtime-packages/ui/src/components/separator.tsx +28 -0
  95. package/dist/runtime-packages/ui/src/components/skeleton.tsx +13 -0
  96. package/dist/runtime-packages/ui/src/components/sonner.tsx +25 -0
  97. package/dist/runtime-packages/ui/src/components/spinner.tsx +16 -0
  98. package/dist/runtime-packages/ui/src/components/switch.tsx +31 -0
  99. package/dist/runtime-packages/ui/src/components/table.tsx +137 -0
  100. package/dist/runtime-packages/ui/src/components/tabs.tsx +74 -0
  101. package/dist/runtime-packages/ui/src/components/textarea.tsx +23 -0
  102. package/dist/runtime-packages/ui/src/components/theme-provider.tsx +73 -0
  103. package/dist/runtime-packages/ui/src/components/tooltip.tsx +61 -0
  104. package/dist/runtime-packages/ui/src/components/ui/file-upload.tsx +195 -0
  105. package/dist/runtime-packages/ui/src/hooks/.gitkeep +0 -0
  106. package/dist/runtime-packages/ui/src/index.ts +68 -0
  107. package/dist/runtime-packages/ui/src/lib/utils.ts +6 -0
  108. package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +68 -0
  109. package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +359 -0
  110. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +274 -0
  111. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +214 -0
  112. package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +135 -0
  113. package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +5 -0
  114. package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +194 -0
  115. package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +1 -0
  116. package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +1 -0
  117. package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +162 -0
  118. package/dist/runtime-packages/ui-host-runtime/src/index.ts +2 -0
  119. package/dist/runtime-packages/ui-host-runtime/src/logger.ts +11 -0
  120. package/dist/runtime-packages/ui-host-runtime/src/perf.ts +253 -0
  121. package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +195 -0
  122. package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +138 -0
  123. package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +159 -0
  124. package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +524 -0
  125. package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +12 -0
  126. package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +122 -0
  127. package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +26 -0
  128. package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +477 -0
  129. package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +209 -0
  130. package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +112 -0
  131. package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +318 -0
  132. package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +211 -0
  133. package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +814 -0
  134. package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +334 -0
  135. package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +180 -0
  136. package/dist/scaffold/assets/static/app/tsconfig.framework.json +3 -1
  137. package/dist/scaffold/assets/static/ui/tsconfig.framework.json +7 -19
  138. package/package.json +52 -17
  139. package/skills/dreamboard/SKILL.md +41 -75
  140. package/LICENSE +0 -89
  141. package/NOTICE +0 -1
  142. package/dist/chunk-BMYC6772.js.map +0 -1
  143. package/dist/chunk-EYYWGWTO.js.map +0 -1
  144. package/dist/chunk-MOVHYB6E.js.map +0 -1
  145. package/dist/scaffold/assets/static/ui/App.tsx +0 -22
  146. package/dist/src-CUL7EGGG.js +0 -634
  147. package/dist/src-CUL7EGGG.js.map +0 -1
  148. package/skills/dreamboard/references/authoring-lifecycle.md +0 -102
  149. package/skills/dreamboard/references/board-surfaces.md +0 -36
  150. package/skills/dreamboard/references/board-topology.md +0 -443
  151. package/skills/dreamboard/references/boards-and-topology.md +0 -100
  152. package/skills/dreamboard/references/card-actions.md +0 -107
  153. package/skills/dreamboard/references/cli.md +0 -120
  154. package/skills/dreamboard/references/core-concepts.md +0 -514
  155. package/skills/dreamboard/references/custom-renderers.md +0 -26
  156. package/skills/dreamboard/references/derived-values.md +0 -55
  157. package/skills/dreamboard/references/effects.md +0 -111
  158. package/skills/dreamboard/references/game-contract.md +0 -89
  159. package/skills/dreamboard/references/game-definition.md +0 -89
  160. package/skills/dreamboard/references/game-shell.md +0 -80
  161. package/skills/dreamboard/references/hand-surfaces.md +0 -33
  162. package/skills/dreamboard/references/index.md +0 -112
  163. package/skills/dreamboard/references/inputs-and-targets.md +0 -160
  164. package/skills/dreamboard/references/interactions.md +0 -158
  165. package/skills/dreamboard/references/manifest-fields.md +0 -137
  166. package/skills/dreamboard/references/manifest.md +0 -81
  167. package/skills/dreamboard/references/package-surfaces.md +0 -69
  168. package/skills/dreamboard/references/phases.md +0 -145
  169. package/skills/dreamboard/references/prompts-and-choices.md +0 -55
  170. package/skills/dreamboard/references/quickstart.md +0 -71
  171. package/skills/dreamboard/references/rule-authoring.md +0 -144
  172. package/skills/dreamboard/references/setup-bootstrap.md +0 -78
  173. package/skills/dreamboard/references/stages-and-zones.md +0 -59
  174. package/skills/dreamboard/references/static-views.md +0 -67
  175. package/skills/dreamboard/references/table-queries-and-ops.md +0 -103
  176. package/skills/dreamboard/references/testing-bases.md +0 -147
  177. package/skills/dreamboard/references/testing-generated-contracts.md +0 -142
  178. package/skills/dreamboard/references/testing-runtime-assertions.md +0 -124
  179. package/skills/dreamboard/references/testing-scenarios.md +0 -148
  180. package/skills/dreamboard/references/testing-ui-tests.md +0 -174
  181. package/skills/dreamboard/references/testing.md +0 -161
  182. package/skills/dreamboard/references/ui-architecture.md +0 -137
  183. package/skills/dreamboard/references/ui-components.md +0 -34
  184. package/skills/dreamboard/references/views.md +0 -72
  185. package/skills/dreamboard/references/workspace-layout.md +0 -136
  186. /package/dist/{dist-WJRJNFLI.js.map → dist-FRURQI7Q.js.map} +0 -0
@@ -0,0 +1,112 @@
1
+ import type { HostSessionWireEvent } from "./session-ingress.js";
2
+ import type { SSEManager } from "./sse-manager.js";
3
+
4
+ export interface SSEManagerLike {
5
+ connect: (
6
+ sessionId: string,
7
+ options: {
8
+ source?: string;
9
+ playerId?: string;
10
+ },
11
+ ) => Promise<void> | void;
12
+ disconnect: () => void;
13
+ on: SSEManager["on"];
14
+ onAnyMessage: SSEManager["onAnyMessage"];
15
+ }
16
+
17
+ export interface SessionLiveRuntime {
18
+ connectSession: (
19
+ sessionId: string,
20
+ options: { source?: string; playerId?: string },
21
+ ) => void;
22
+ reconnectGameplay: (
23
+ sessionId: string,
24
+ playerId: string,
25
+ source: string,
26
+ ) => void;
27
+ requestGameplayResync: (
28
+ sessionId: string,
29
+ playerId: string,
30
+ source: string,
31
+ ) => void;
32
+ closeStreams: () => void;
33
+ getConnectedSessionId: () => string | null;
34
+ }
35
+
36
+ export function createSessionLiveRuntime(options: {
37
+ createSseManager: () => SSEManagerLike;
38
+ onConnectionChange: (
39
+ channel: "lobby" | "gameplay",
40
+ connected: boolean,
41
+ ) => void;
42
+ onConnectionError: (error: unknown) => void;
43
+ onLiveMessage: (message: HostSessionWireEvent) => void;
44
+ }): SessionLiveRuntime {
45
+ let connectedSessionId: string | null = null;
46
+ let sessionManager: SSEManagerLike | null = null;
47
+ let unsubscribeSession: (() => void) | null = null;
48
+
49
+ const attachConnectionHandlers = (
50
+ manager: SSEManagerLike,
51
+ channel: "lobby" | "gameplay",
52
+ ) => {
53
+ manager.on("connected", () => options.onConnectionChange(channel, true));
54
+ manager.on("disconnected", () =>
55
+ options.onConnectionChange(channel, false),
56
+ );
57
+ manager.on("error", (error) => {
58
+ options.onConnectionChange(channel, false);
59
+ options.onConnectionError(error);
60
+ });
61
+ };
62
+
63
+ const closeStreams = () => {
64
+ unsubscribeSession?.();
65
+ unsubscribeSession = null;
66
+ sessionManager?.disconnect();
67
+ sessionManager = null;
68
+ connectedSessionId = null;
69
+ options.onConnectionChange("lobby", false);
70
+ options.onConnectionChange("gameplay", false);
71
+ };
72
+
73
+ const openSession = (
74
+ sessionId: string,
75
+ playerId: string | undefined,
76
+ source: string,
77
+ ) => {
78
+ closeStreams();
79
+ connectedSessionId = sessionId;
80
+ sessionManager = options.createSseManager();
81
+ attachConnectionHandlers(sessionManager, playerId ? "gameplay" : "lobby");
82
+ unsubscribeSession = sessionManager.onAnyMessage(options.onLiveMessage);
83
+ void sessionManager.connect(sessionId, {
84
+ source,
85
+ playerId,
86
+ });
87
+ };
88
+
89
+ return {
90
+ connectSession: (sessionId, connectOptions) => {
91
+ openSession(
92
+ sessionId,
93
+ connectOptions.playerId,
94
+ connectOptions.source ?? "connect-session",
95
+ );
96
+ },
97
+ reconnectGameplay: (sessionId, playerId, source) => {
98
+ openSession(sessionId, playerId, source);
99
+ },
100
+ requestGameplayResync: (sessionId, playerId, source) => {
101
+ if (!sessionManager) {
102
+ return;
103
+ }
104
+ void sessionManager.connect(sessionId, {
105
+ source,
106
+ playerId,
107
+ });
108
+ },
109
+ closeStreams,
110
+ getConnectedSessionId: () => connectedSessionId,
111
+ };
112
+ }
@@ -0,0 +1,318 @@
1
+ import type {
2
+ HistoryEntrySummary,
3
+ InteractionDescriptor,
4
+ SeatAssignment,
5
+ SessionActor,
6
+ SessionGameSource,
7
+ ZoneHandles,
8
+ } from "@dreamboard/api-client";
9
+ import type { PluginStateSnapshot } from "@dreamboard/ui-sdk/reducer";
10
+ import {
11
+ seatControlledByPrincipal,
12
+ gameIdFromGameSource,
13
+ } from "./actor-principal.js";
14
+
15
+ type LayoutConfig = unknown;
16
+ type CardDisplayConfig = unknown;
17
+
18
+ export interface SessionIdentity {
19
+ sessionId: string;
20
+ shortCode: string;
21
+ gameId: string;
22
+ }
23
+
24
+ export interface HistoryState {
25
+ entries: HistoryEntrySummary[];
26
+ currentIndex: number;
27
+ canGoBack: boolean;
28
+ canGoForward: boolean;
29
+ }
30
+
31
+ export interface SessionContextIngress {
32
+ sessionId: string;
33
+ shortCode: string;
34
+ gameSource: SessionGameSource;
35
+ hostActor: SessionActor;
36
+ switchablePlayerIds: string[];
37
+ history?: HistoryState | null;
38
+ }
39
+
40
+ export interface LobbyIngress {
41
+ seats: SeatAssignment[];
42
+ canStart: boolean;
43
+ }
44
+
45
+ export interface GameplayViewportIngress {
46
+ version: number;
47
+ actionSetVersion: string;
48
+ playerId: string;
49
+ activePlayers: string[];
50
+ currentPhase: string | null;
51
+ currentStage?: string | null;
52
+ stageSeats?: string[];
53
+ simultaneousPhase?: SimultaneousPhaseSnapshot | null;
54
+ view?: string | null;
55
+ availableInteractions?: InteractionDescriptor[];
56
+ zones?: Record<string, ZoneHandles>;
57
+ boardStatic?: string | null;
58
+ boardStaticHash?: string | null;
59
+ }
60
+
61
+ export interface SessionContext {
62
+ identity: SessionIdentity;
63
+ userId: string | null;
64
+ seats: SeatAssignment[];
65
+ canStart: boolean;
66
+ hostActor: SessionActor;
67
+ switchablePlayerIds: string[];
68
+ history: HistoryState | null;
69
+ }
70
+
71
+ export interface GameplayPerspective {
72
+ playerId: string;
73
+ }
74
+
75
+ export interface GameplayViewport {
76
+ version: number;
77
+ actionSetVersion: string;
78
+ activePlayers: string[];
79
+ currentPhase: string | null;
80
+ currentStage: string | null;
81
+ stageSeats: string[];
82
+ simultaneousPhase: SimultaneousPhaseSnapshot | null;
83
+ view: PluginStateSnapshot["view"];
84
+ availableInteractions: InteractionDescriptor[];
85
+ zones: Record<string, ZoneHandles>;
86
+ boardStatic: Record<string, unknown> | null;
87
+ boardStaticHash: string | null;
88
+ layout?: LayoutConfig | null;
89
+ cardDisplayConfigs?: CardDisplayConfig[] | null;
90
+ }
91
+
92
+ export interface SimultaneousPhaseSnapshot {
93
+ phaseName: string;
94
+ interactionId: string;
95
+ actorIds: string[];
96
+ sealedPlayerIds: string[];
97
+ pendingPlayerIds: string[];
98
+ }
99
+
100
+ export type UnifiedSessionModel =
101
+ | { type: "idle" }
102
+ | { type: "loading"; target?: { sessionId?: string; shortCode?: string } }
103
+ | { type: "lobby"; context: SessionContext; preferredPlayerId: string | null }
104
+ | {
105
+ type: "gameplayLoading";
106
+ context: SessionContext;
107
+ requestedPlayerId: string;
108
+ }
109
+ | {
110
+ type: "gameplay";
111
+ context: SessionContext;
112
+ perspective: GameplayPerspective;
113
+ gameplay: GameplayViewport;
114
+ }
115
+ | { type: "ended"; context: SessionContext }
116
+ | { type: "error"; message: string; context?: SessionContext };
117
+
118
+ export type SessionPhase = UnifiedSessionModel["type"];
119
+
120
+ export function toHistoryState(
121
+ history: SessionContextIngress["history"] | undefined,
122
+ ): HistoryState | null {
123
+ return history
124
+ ? {
125
+ entries: history.entries,
126
+ currentIndex: history.currentIndex,
127
+ canGoBack: history.canGoBack,
128
+ canGoForward: history.canGoForward,
129
+ }
130
+ : null;
131
+ }
132
+
133
+ export function identityFromHostContext(
134
+ context: SessionContextIngress,
135
+ ): SessionIdentity {
136
+ return {
137
+ sessionId: context.sessionId,
138
+ shortCode: context.shortCode,
139
+ gameId: gameIdFromGameSource(context.gameSource),
140
+ };
141
+ }
142
+
143
+ export function deriveControlledPlayerIdsFromSeats(
144
+ seats: SeatAssignment[],
145
+ userId: string | null,
146
+ fallbackToAllSeatsWhenUserIdMissing = false,
147
+ ): string[] {
148
+ if (!userId) {
149
+ return fallbackToAllSeatsWhenUserIdMissing
150
+ ? seats.map((seat) => seat.playerId)
151
+ : [];
152
+ }
153
+ return seats
154
+ .filter((seat) => seatControlledByPrincipal(seat, userId))
155
+ .map((seat) => seat.playerId);
156
+ }
157
+
158
+ export function resolveControllablePlayerIds(
159
+ preferredPlayerIds: string[] | null | undefined,
160
+ seats: SeatAssignment[],
161
+ userId: string | null,
162
+ fallbackToAllSeatsWhenUserIdMissing = false,
163
+ ): string[] {
164
+ return preferredPlayerIds && preferredPlayerIds.length > 0
165
+ ? preferredPlayerIds
166
+ : deriveControlledPlayerIdsFromSeats(
167
+ seats,
168
+ userId,
169
+ fallbackToAllSeatsWhenUserIdMissing,
170
+ );
171
+ }
172
+
173
+ export function contextFromHostParts(
174
+ wireContext: SessionContextIngress,
175
+ lobby: LobbyIngress,
176
+ userId: string | null,
177
+ ): SessionContext {
178
+ return {
179
+ identity: identityFromHostContext(wireContext),
180
+ userId,
181
+ seats: lobby.seats,
182
+ canStart: lobby.canStart,
183
+ hostActor: wireContext.hostActor,
184
+ switchablePlayerIds: wireContext.switchablePlayerIds,
185
+ history: toHistoryState(wireContext.history),
186
+ };
187
+ }
188
+
189
+ export function contextFromHostSnapshot(
190
+ snapshot: { context: SessionContextIngress; lobby: LobbyIngress },
191
+ userId: string | null,
192
+ ): SessionContext {
193
+ return contextFromHostParts(snapshot.context, snapshot.lobby, userId);
194
+ }
195
+
196
+ export function contextFromHostEventContext(
197
+ context: SessionContext,
198
+ wireContext: SessionContextIngress,
199
+ lobby?: LobbyIngress,
200
+ ): SessionContext {
201
+ return {
202
+ ...context,
203
+ identity: identityFromHostContext(wireContext),
204
+ seats: lobby?.seats ?? context.seats,
205
+ canStart: lobby?.canStart ?? context.canStart,
206
+ hostActor: wireContext.hostActor,
207
+ switchablePlayerIds: wireContext.switchablePlayerIds,
208
+ history:
209
+ wireContext.history !== undefined
210
+ ? toHistoryState(wireContext.history)
211
+ : context.history,
212
+ };
213
+ }
214
+
215
+ export function contextWithHistory(
216
+ context: SessionContext,
217
+ history: SessionContextIngress["history"],
218
+ ): SessionContext {
219
+ return { ...context, history: toHistoryState(history) };
220
+ }
221
+
222
+ export function safeJsonParseValue(jsonString: string): unknown {
223
+ try {
224
+ return JSON.parse(jsonString);
225
+ } catch {
226
+ return null;
227
+ }
228
+ }
229
+
230
+ export function transformView(
231
+ serializedView: string | null | undefined,
232
+ ): PluginStateSnapshot["view"] {
233
+ return serializedView
234
+ ? (safeJsonParseValue(serializedView) as PluginStateSnapshot["view"])
235
+ : null;
236
+ }
237
+
238
+ export function gameplayViewportFromSnapshot(
239
+ snapshot: GameplayViewportIngress,
240
+ previous?: GameplayViewport | null,
241
+ ): GameplayViewport {
242
+ const incomingBoardStatic = snapshot.boardStatic
243
+ ? (safeJsonParseValue(snapshot.boardStatic) as Record<
244
+ string,
245
+ unknown
246
+ > | null)
247
+ : null;
248
+ const incomingBoardStaticHash = snapshot.boardStaticHash ?? null;
249
+ const hashDisagrees =
250
+ previous?.boardStaticHash !== null &&
251
+ previous?.boardStaticHash !== undefined &&
252
+ incomingBoardStaticHash !== null &&
253
+ incomingBoardStaticHash !== previous.boardStaticHash;
254
+
255
+ return {
256
+ version: snapshot.version,
257
+ actionSetVersion: snapshot.actionSetVersion,
258
+ activePlayers: snapshot.activePlayers,
259
+ currentPhase: snapshot.currentPhase,
260
+ currentStage: snapshot.currentStage ?? previous?.currentStage ?? null,
261
+ stageSeats: snapshot.stageSeats ?? previous?.stageSeats ?? [],
262
+ simultaneousPhase: snapshot.simultaneousPhase ?? null,
263
+ view: transformView(snapshot.view),
264
+ availableInteractions: snapshot.availableInteractions ?? [],
265
+ zones: snapshot.zones ?? previous?.zones ?? {},
266
+ boardStatic: hashDisagrees
267
+ ? (previous?.boardStatic ?? null)
268
+ : (incomingBoardStatic ?? previous?.boardStatic ?? null),
269
+ boardStaticHash:
270
+ incomingBoardStaticHash ?? previous?.boardStaticHash ?? null,
271
+ layout: previous?.layout ?? null,
272
+ cardDisplayConfigs: previous?.cardDisplayConfigs ?? null,
273
+ };
274
+ }
275
+
276
+ export function getSessionContext(
277
+ session: UnifiedSessionModel,
278
+ ): SessionContext | null {
279
+ switch (session.type) {
280
+ case "lobby":
281
+ case "gameplayLoading":
282
+ case "gameplay":
283
+ case "ended":
284
+ return session.context;
285
+ case "error":
286
+ return session.context ?? null;
287
+ case "idle":
288
+ case "loading":
289
+ return null;
290
+ }
291
+ }
292
+
293
+ export function getGameplayViewport(
294
+ session: UnifiedSessionModel,
295
+ ): GameplayViewport | null {
296
+ return session.type === "gameplay" ? session.gameplay : null;
297
+ }
298
+
299
+ export function withContext(
300
+ session: UnifiedSessionModel,
301
+ context: SessionContext,
302
+ ): UnifiedSessionModel {
303
+ switch (session.type) {
304
+ case "lobby":
305
+ return { ...session, context };
306
+ case "gameplayLoading":
307
+ return { ...session, context };
308
+ case "gameplay":
309
+ return { ...session, context };
310
+ case "ended":
311
+ return { type: "ended", context };
312
+ case "error":
313
+ return { type: "error", message: session.message, context };
314
+ case "idle":
315
+ case "loading":
316
+ return { type: "lobby", context, preferredPlayerId: null };
317
+ }
318
+ }
@@ -0,0 +1,211 @@
1
+ import type {
2
+ InteractionDescriptor,
3
+ ZoneHandles,
4
+ } from "@dreamboard/api-client";
5
+ import type { PluginStateSnapshot } from "@dreamboard/ui-sdk/reducer";
6
+ import {
7
+ principalKey,
8
+ principalMatchesActor,
9
+ seatsForPluginSnapshot,
10
+ } from "./actor-principal.js";
11
+ import type {
12
+ GameplayViewport,
13
+ HistoryState,
14
+ SessionContext,
15
+ SessionIdentity,
16
+ UnifiedSessionModel,
17
+ } from "./session-model.js";
18
+ import {
19
+ getGameplayViewport,
20
+ getSessionContext,
21
+ resolveControllablePlayerIds,
22
+ } from "./session-model.js";
23
+
24
+ export interface NotificationProjectionState {
25
+ notifications: PluginStateSnapshot["notifications"];
26
+ syncId: number;
27
+ }
28
+
29
+ export interface ProjectionState {
30
+ session: UnifiedSessionModel;
31
+ activity: NotificationProjectionState;
32
+ }
33
+
34
+ export interface LobbyViewModel {
35
+ identity: SessionIdentity;
36
+ seats: SessionContext["seats"];
37
+ canStart: boolean;
38
+ hostActor: SessionContext["hostActor"];
39
+ shortCode: string;
40
+ isSessionHost: boolean;
41
+ }
42
+
43
+ export interface GameplayViewModel {
44
+ context: SessionContext;
45
+ perspective: { playerId: string };
46
+ gameplay: GameplayViewport;
47
+ }
48
+
49
+ export type BootstrapStatus = "loading" | "lobby" | "renderable" | "error";
50
+
51
+ const lobbyViewModelCache = new WeakMap<SessionContext, LobbyViewModel>();
52
+ const gameplayViewModelCache = new WeakMap<
53
+ GameplayViewport,
54
+ { context: SessionContext; value: GameplayViewModel }
55
+ >();
56
+
57
+ export function selectSessionContext(
58
+ state: Pick<ProjectionState, "session">,
59
+ ): SessionContext | null {
60
+ return getSessionContext(state.session);
61
+ }
62
+
63
+ export function selectGameplayViewport(
64
+ state: Pick<ProjectionState, "session">,
65
+ ): GameplayViewport | null {
66
+ return getGameplayViewport(state.session);
67
+ }
68
+
69
+ export function selectSessionType(
70
+ state: Pick<ProjectionState, "session">,
71
+ ): UnifiedSessionModel["type"] {
72
+ return state.session.type;
73
+ }
74
+
75
+ export function selectBootstrapStatus(
76
+ state: Pick<ProjectionState, "session">,
77
+ ): BootstrapStatus {
78
+ switch (state.session.type) {
79
+ case "idle":
80
+ case "loading":
81
+ case "gameplayLoading":
82
+ return "loading";
83
+ case "lobby":
84
+ case "ended":
85
+ return "lobby";
86
+ case "gameplay":
87
+ return "renderable";
88
+ case "error":
89
+ return "error";
90
+ }
91
+ }
92
+
93
+ export function selectSessionError(
94
+ state: Pick<ProjectionState, "session">,
95
+ ): string | null {
96
+ return state.session.type === "error" ? state.session.message : null;
97
+ }
98
+
99
+ export function selectLobbyViewModel(
100
+ state: Pick<ProjectionState, "session">,
101
+ ): LobbyViewModel | null {
102
+ const context = getSessionContext(state.session);
103
+ if (!context) return null;
104
+ const cached = lobbyViewModelCache.get(context);
105
+ if (cached) return cached;
106
+ const viewModel = {
107
+ identity: context.identity,
108
+ seats: context.seats,
109
+ canStart: context.canStart,
110
+ hostActor: context.hostActor,
111
+ shortCode: context.identity.shortCode,
112
+ isSessionHost:
113
+ Boolean(context.userId) &&
114
+ principalMatchesActor(context.userId, context.hostActor),
115
+ };
116
+ lobbyViewModelCache.set(context, viewModel);
117
+ return viewModel;
118
+ }
119
+
120
+ export function selectGameplayViewModel(
121
+ state: Pick<ProjectionState, "session">,
122
+ ): GameplayViewModel | null {
123
+ if (state.session.type !== "gameplay") return null;
124
+ const cached = gameplayViewModelCache.get(state.session.gameplay);
125
+ if (cached?.context === state.session.context) return cached.value;
126
+ const value = {
127
+ context: state.session.context,
128
+ perspective: state.session.perspective,
129
+ gameplay: state.session.gameplay,
130
+ };
131
+ gameplayViewModelCache.set(state.session.gameplay, {
132
+ context: state.session.context,
133
+ value,
134
+ });
135
+ return value;
136
+ }
137
+
138
+ export function selectPluginSnapshot(
139
+ state: ProjectionState,
140
+ fallbackToAllSeatsWhenUserIdMissing = false,
141
+ ): PluginStateSnapshot {
142
+ const context = getSessionContext(state.session);
143
+ const gameplay = getGameplayViewport(state.session);
144
+ const perspectivePlayerId =
145
+ state.session.type === "gameplay"
146
+ ? state.session.perspective.playerId
147
+ : null;
148
+ const controllablePlayerIds = context
149
+ ? resolveControllablePlayerIds(
150
+ context.switchablePlayerIds,
151
+ context.seats,
152
+ context.userId,
153
+ fallbackToAllSeatsWhenUserIdMissing,
154
+ )
155
+ : [];
156
+ const controllingPlayerId = perspectivePlayerId;
157
+ const gameplayIsRenderable =
158
+ gameplay !== null && perspectivePlayerId !== null;
159
+ const visibleInteractions: InteractionDescriptor[] = gameplayIsRenderable
160
+ ? gameplay.availableInteractions
161
+ : [];
162
+ const seatView = gameplayIsRenderable ? gameplay.view : null;
163
+ const boardStatic = gameplay?.boardStatic ?? null;
164
+ const view: PluginStateSnapshot["view"] =
165
+ boardStatic && seatView && typeof seatView === "object"
166
+ ? ({
167
+ ...(seatView as Record<string, unknown>),
168
+ board: {
169
+ ...((seatView as { board?: Record<string, unknown> }).board ?? {}),
170
+ ...boardStatic,
171
+ },
172
+ } as PluginStateSnapshot["view"])
173
+ : boardStatic && !seatView
174
+ ? ({ board: boardStatic } as unknown as PluginStateSnapshot["view"])
175
+ : seatView;
176
+ const visibleZones: Record<string, ZoneHandles> = gameplayIsRenderable
177
+ ? gameplay.zones
178
+ : {};
179
+
180
+ return {
181
+ view,
182
+ gameplay: {
183
+ currentPhase: gameplay?.currentPhase ?? null,
184
+ currentStage: gameplay?.currentStage ?? null,
185
+ activePlayers: gameplay?.activePlayers ?? [],
186
+ simultaneousPhase: gameplay?.simultaneousPhase ?? null,
187
+ availableInteractions: visibleInteractions,
188
+ zones: visibleZones,
189
+ },
190
+ lobby: {
191
+ seats: seatsForPluginSnapshot(context?.seats ?? []),
192
+ canStart: context?.canStart ?? false,
193
+ hostUserId: context ? principalKey(context.hostActor) : "",
194
+ },
195
+ notifications: state.activity.notifications,
196
+ session: {
197
+ sessionId: context?.identity.sessionId ?? null,
198
+ controllablePlayerIds,
199
+ controllingPlayerId,
200
+ userId: context?.userId ?? null,
201
+ },
202
+ history: context?.history ?? null,
203
+ syncId: state.activity.syncId,
204
+ };
205
+ }
206
+
207
+ export function selectHistory(
208
+ state: Pick<ProjectionState, "session">,
209
+ ): HistoryState | null {
210
+ return getSessionContext(state.session)?.history ?? null;
211
+ }