@seriousgaming/sdk 0.0.0-stage → 1.0.0-alpha.1

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 (170) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +1 -0
  3. package/README.md +29 -2
  4. package/lib/build/callbacks.d.ts +998 -0
  5. package/lib/build/callbacks.js +32 -0
  6. package/lib/build/context-merge.d.ts +59 -0
  7. package/lib/build/context-merge.js +118 -0
  8. package/lib/build/document.d.ts +36 -0
  9. package/lib/build/document.js +8 -0
  10. package/lib/build/encoding.d.ts +26 -0
  11. package/lib/build/encoding.js +78 -0
  12. package/lib/build/endpoints.d.ts +421 -0
  13. package/lib/build/endpoints.js +46 -0
  14. package/lib/build/hooks.d.ts +74 -0
  15. package/lib/build/hooks.js +1 -0
  16. package/lib/build/index.d.ts +18 -0
  17. package/lib/build/index.js +16 -0
  18. package/lib/build/llm.d.ts +166 -0
  19. package/lib/build/llm.js +17 -0
  20. package/lib/build/resource-schema.d.ts +1048 -0
  21. package/lib/build/resource-schema.js +7 -0
  22. package/lib/build/scheduling.d.ts +112 -0
  23. package/lib/build/scheduling.js +217 -0
  24. package/lib/build/seed.d.ts +51 -0
  25. package/lib/build/seed.js +89 -0
  26. package/lib/build/signatures.d.ts +372 -0
  27. package/lib/build/signatures.js +1 -0
  28. package/lib/build/speech.d.ts +102 -0
  29. package/lib/build/speech.js +11 -0
  30. package/lib/build/tick-validation.d.ts +23 -0
  31. package/lib/build/tick-validation.js +96 -0
  32. package/lib/build/timer.d.ts +74 -0
  33. package/lib/build/timer.js +1 -0
  34. package/lib/build/validators.d.ts +116 -0
  35. package/lib/build/validators.js +1 -0
  36. package/lib/commands.d.ts +357 -0
  37. package/lib/commands.js +14 -0
  38. package/lib/e2e.d.ts +238 -0
  39. package/lib/e2e.js +248 -0
  40. package/lib/projection.d.ts +266 -0
  41. package/lib/projection.js +23 -0
  42. package/lib/testing/index.d.ts +13 -0
  43. package/lib/testing/index.js +13 -0
  44. package/lib/testing/logic-args.d.ts +177 -0
  45. package/lib/testing/logic-args.js +229 -0
  46. package/lib/testing/stub-services.d.ts +41 -0
  47. package/lib/testing/stub-services.js +176 -0
  48. package/lib/ui/CrossTeamDataContext.d.ts +36 -0
  49. package/lib/ui/CrossTeamDataContext.js +32 -0
  50. package/lib/ui/PresentationContext.d.ts +72 -0
  51. package/lib/ui/PresentationContext.js +13 -0
  52. package/lib/ui/PresentationWrapper.d.ts +12 -0
  53. package/lib/ui/PresentationWrapper.js +78 -0
  54. package/lib/ui/SessionUserInterfaceCallbacksContext.d.ts +3 -0
  55. package/lib/ui/SessionUserInterfaceCallbacksContext.js +7 -0
  56. package/lib/ui/SessionUserInterfaceDataContext.d.ts +103 -0
  57. package/lib/ui/SessionUserInterfaceDataContext.js +17 -0
  58. package/lib/ui/SessionUserInterfaceHookFailuresContext.d.ts +44 -0
  59. package/lib/ui/SessionUserInterfaceHookFailuresContext.js +15 -0
  60. package/lib/ui/SessionUserInterfaceHostOverlaysContext.d.ts +23 -0
  61. package/lib/ui/SessionUserInterfaceHostOverlaysContext.js +23 -0
  62. package/lib/ui/SessionUserInterfaceLocaleContext.d.ts +11 -0
  63. package/lib/ui/SessionUserInterfaceLocaleContext.js +13 -0
  64. package/lib/ui/SessionUserInterfaceMetaContext.d.ts +39 -0
  65. package/lib/ui/SessionUserInterfaceMetaContext.js +13 -0
  66. package/lib/ui/SessionUserInterfaceParticipantsContext.d.ts +12 -0
  67. package/lib/ui/SessionUserInterfaceParticipantsContext.js +16 -0
  68. package/lib/ui/SessionUserInterfaceTeamConfigurationContext.d.ts +53 -0
  69. package/lib/ui/SessionUserInterfaceTeamConfigurationContext.js +23 -0
  70. package/lib/ui/SessionUserInterfaceTeamLinksContext.d.ts +56 -0
  71. package/lib/ui/SessionUserInterfaceTeamLinksContext.js +12 -0
  72. package/lib/ui/SessionUserInterfaceTeamsContext.d.ts +13 -0
  73. package/lib/ui/SessionUserInterfaceTeamsContext.js +13 -0
  74. package/lib/ui/SessionUserInterfaceThemeContext.d.ts +9 -0
  75. package/lib/ui/SessionUserInterfaceThemeContext.js +7 -0
  76. package/lib/ui/SessionUserInterfaceThemePreferenceContext.d.ts +3 -0
  77. package/lib/ui/SessionUserInterfaceThemePreferenceContext.js +7 -0
  78. package/lib/ui/SessionUserInterfaceWalkthroughLinkContext.d.ts +42 -0
  79. package/lib/ui/SessionUserInterfaceWalkthroughLinkContext.js +26 -0
  80. package/lib/ui/SessionUserInterfaceWrapper.d.ts +8 -0
  81. package/lib/ui/SessionUserInterfaceWrapper.js +321 -0
  82. package/lib/ui/SettingsEditorCallbacksContext.d.ts +3 -0
  83. package/lib/ui/SettingsEditorCallbacksContext.js +7 -0
  84. package/lib/ui/SettingsEditorContext.d.ts +33 -0
  85. package/lib/ui/SettingsEditorContext.js +16 -0
  86. package/lib/ui/SettingsEditorLocaleContext.d.ts +13 -0
  87. package/lib/ui/SettingsEditorLocaleContext.js +15 -0
  88. package/lib/ui/SettingsEditorThemeContext.d.ts +9 -0
  89. package/lib/ui/SettingsEditorThemeContext.js +7 -0
  90. package/lib/ui/SettingsEditorThemePreferenceContext.d.ts +3 -0
  91. package/lib/ui/SettingsEditorThemePreferenceContext.js +7 -0
  92. package/lib/ui/SettingsEditorWrapper.d.ts +13 -0
  93. package/lib/ui/SettingsEditorWrapper.js +74 -0
  94. package/lib/ui/UserInterfaceCallbacksContext.d.ts +3 -0
  95. package/lib/ui/UserInterfaceCallbacksContext.js +7 -0
  96. package/lib/ui/UserInterfaceDataContext.d.ts +78 -0
  97. package/lib/ui/UserInterfaceDataContext.js +11 -0
  98. package/lib/ui/UserInterfaceLocaleContext.d.ts +13 -0
  99. package/lib/ui/UserInterfaceLocaleContext.js +15 -0
  100. package/lib/ui/UserInterfaceOwnTeamContext.d.ts +25 -0
  101. package/lib/ui/UserInterfaceOwnTeamContext.js +26 -0
  102. package/lib/ui/UserInterfaceOwnTeamJoinLinkContext.d.ts +52 -0
  103. package/lib/ui/UserInterfaceOwnTeamJoinLinkContext.js +13 -0
  104. package/lib/ui/UserInterfaceParticipantContext.d.ts +16 -0
  105. package/lib/ui/UserInterfaceParticipantContext.js +24 -0
  106. package/lib/ui/UserInterfaceParticipantsContext.d.ts +12 -0
  107. package/lib/ui/UserInterfaceParticipantsContext.js +13 -0
  108. package/lib/ui/UserInterfaceRunKindContext.d.ts +31 -0
  109. package/lib/ui/UserInterfaceRunKindContext.js +31 -0
  110. package/lib/ui/UserInterfaceTeamsContext.d.ts +12 -0
  111. package/lib/ui/UserInterfaceTeamsContext.js +13 -0
  112. package/lib/ui/UserInterfaceThemeContext.d.ts +9 -0
  113. package/lib/ui/UserInterfaceThemeContext.js +7 -0
  114. package/lib/ui/UserInterfaceThemePreferenceContext.d.ts +3 -0
  115. package/lib/ui/UserInterfaceThemePreferenceContext.js +7 -0
  116. package/lib/ui/UserInterfaceTimerContext.d.ts +10 -0
  117. package/lib/ui/UserInterfaceTimerContext.js +12 -0
  118. package/lib/ui/UserInterfaceValidationContext.d.ts +25 -0
  119. package/lib/ui/UserInterfaceValidationContext.js +14 -0
  120. package/lib/ui/UserInterfaceViewerRoleContext.d.ts +14 -0
  121. package/lib/ui/UserInterfaceViewerRoleContext.js +17 -0
  122. package/lib/ui/UserInterfaceWrapper.d.ts +8 -0
  123. package/lib/ui/UserInterfaceWrapper.js +219 -0
  124. package/lib/ui/expectedParentOrigin.d.ts +10 -0
  125. package/lib/ui/expectedParentOrigin.js +32 -0
  126. package/lib/ui/index.d.ts +50 -0
  127. package/lib/ui/index.js +52 -0
  128. package/lib/ui/internal/SurfaceGate.d.ts +19 -0
  129. package/lib/ui/internal/SurfaceGate.js +17 -0
  130. package/lib/ui/internal/SurfaceScope.d.ts +17 -0
  131. package/lib/ui/internal/SurfaceScope.js +16 -0
  132. package/lib/ui/internal/createContexts.d.ts +50 -0
  133. package/lib/ui/internal/createContexts.js +32 -0
  134. package/lib/ui/internal/document-attributes.d.ts +9 -0
  135. package/lib/ui/internal/document-attributes.js +87 -0
  136. package/lib/ui/internal/surface-timer.d.ts +3 -0
  137. package/lib/ui/internal/surface-timer.js +11 -0
  138. package/lib/ui/internal/useLatestRef.d.ts +7 -0
  139. package/lib/ui/internal/useLatestRef.js +11 -0
  140. package/lib/ui/internal/useOptimisticMerge.d.ts +19 -0
  141. package/lib/ui/internal/useOptimisticMerge.js +28 -0
  142. package/lib/ui/internal/useSurfaceControls.d.ts +52 -0
  143. package/lib/ui/internal/useSurfaceControls.js +19 -0
  144. package/lib/ui/internal/useTypedPastState.d.ts +17 -0
  145. package/lib/ui/internal/useTypedPastState.js +17 -0
  146. package/lib/ui/mutation-commit.d.ts +32 -0
  147. package/lib/ui/mutation-commit.js +1 -0
  148. package/lib/ui/mutation-outcome.d.ts +66 -0
  149. package/lib/ui/mutation-outcome.js +49 -0
  150. package/lib/ui/request-id.d.ts +18 -0
  151. package/lib/ui/request-id.js +44 -0
  152. package/lib/ui/testing.d.ts +365 -0
  153. package/lib/ui/testing.js +572 -0
  154. package/lib/ui/tick-snapshot.d.ts +28 -0
  155. package/lib/ui/tick-snapshot.js +34 -0
  156. package/lib/ui/ui-protocol.d.ts +799 -0
  157. package/lib/ui/ui-protocol.js +50 -0
  158. package/lib/ui/useAsset.d.ts +80 -0
  159. package/lib/ui/useAsset.js +139 -0
  160. package/lib/ui/useEndpointCallbacks.d.ts +31 -0
  161. package/lib/ui/useEndpointCallbacks.js +68 -0
  162. package/lib/ui/useHostChannel.d.ts +90 -0
  163. package/lib/ui/useHostChannel.js +338 -0
  164. package/lib/ui/useMutationReporter.d.ts +100 -0
  165. package/lib/ui/useMutationReporter.js +97 -0
  166. package/lib/ui/usePastState.d.ts +39 -0
  167. package/lib/ui/usePastState.js +63 -0
  168. package/lib/ui/useViewerStorage.d.ts +54 -0
  169. package/lib/ui/useViewerStorage.js +141 -0
  170. package/package.json +83 -3
@@ -0,0 +1,219 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useMemo, useRef, useState } from 'react';
3
+ import { CrossTeamDataContext } from './CrossTeamDataContext.js';
4
+ import { useDocumentAttributes } from './internal/document-attributes.js';
5
+ import { DEFAULT_SURFACE_TIMER } from './internal/surface-timer.js';
6
+ import { SurfaceGate } from './internal/SurfaceGate.js';
7
+ import { SurfaceScope } from './internal/SurfaceScope.js';
8
+ import { useLatestRef } from './internal/useLatestRef.js';
9
+ import { useOptimisticMerge } from './internal/useOptimisticMerge.js';
10
+ import { useTypedPastState } from './internal/useTypedPastState.js';
11
+ import { useHostChannel } from './useHostChannel.js';
12
+ import { UserInterfaceCallbacksContext } from './UserInterfaceCallbacksContext.js';
13
+ import { UserInterfaceDataContext } from './UserInterfaceDataContext.js';
14
+ import { UserInterfaceLocaleContext } from './UserInterfaceLocaleContext.js';
15
+ import { UserInterfaceOwnTeamContext } from './UserInterfaceOwnTeamContext.js';
16
+ import { UserInterfaceOwnTeamJoinLinkContext, } from './UserInterfaceOwnTeamJoinLinkContext.js';
17
+ import { UserInterfaceParticipantContext } from './UserInterfaceParticipantContext.js';
18
+ import { UserInterfaceParticipantsContext } from './UserInterfaceParticipantsContext.js';
19
+ import { UserInterfaceRunKindContext } from './UserInterfaceRunKindContext.js';
20
+ import { UserInterfaceTeamsContext } from './UserInterfaceTeamsContext.js';
21
+ import { UserInterfaceThemeContext } from './UserInterfaceThemeContext.js';
22
+ import { UserInterfaceThemePreferenceContext } from './UserInterfaceThemePreferenceContext.js';
23
+ import { UserInterfaceTimerContext } from './UserInterfaceTimerContext.js';
24
+ import { UserInterfaceValidationContext } from './UserInterfaceValidationContext.js';
25
+ import { UserInterfaceViewerRoleContext, } from './UserInterfaceViewerRoleContext.js';
26
+ const REQUIRED_MESSAGES = [
27
+ 'stateChanged',
28
+ 'setSettings',
29
+ 'setTheme',
30
+ 'setLocale',
31
+ 'setParticipant',
32
+ 'setAssetUrls',
33
+ ];
34
+ function UserInterfaceWrapper({ children, fallback = _jsx("div", { children: "Loading..." }), targetWindow = window.parent, }) {
35
+ const [data, setDataState] = useState({});
36
+ const [completedTicks, setCompletedTicks] = useState(0);
37
+ // Snapshot of the most recent host-confirmed data. Used to roll back local
38
+ // state when a UI-submitted mutation is rejected by validators — without
39
+ // this, the iframe would keep its optimistic value while the host's
40
+ // persisted state diverges.
41
+ const lastHostDataRef = useRef({});
42
+ const [settings, setSettingsState] = useState(undefined);
43
+ const [crossTeamData, setCrossTeamDataState] = useState([]);
44
+ const [theme, setThemeState] = useState(undefined);
45
+ const [locale, setLocaleState] = useState(undefined);
46
+ // `null` is a real value here, not a placeholder: an observer has no caller
47
+ // identity. A participant render replaces it via setParticipant; an observer
48
+ // render leaves it null and satisfies the participant gate via setViewerRole.
49
+ const [participant, setParticipantState] = useState(null);
50
+ const [viewerRole, setViewerRoleState] = useState('participant');
51
+ // Fixed for the life of the run. 'session' is what a host offering no
52
+ // walkthroughs means, so it never gates first paint.
53
+ const [runKind, setRunKindState] = useState('session');
54
+ const [participants, setParticipantsState] = useState([]);
55
+ const [teams, setTeamsState] = useState([]);
56
+ // The team this surface renders, as the host reported it. Populated for
57
+ // observers too, which is the whole point: an observer has no participant to
58
+ // derive a team from (#332).
59
+ const [hostOwnTeam, setHostOwnTeamState] = useState(null);
60
+ // How to join this surface's own team. Never gates first paint: a join
61
+ // panel renders a placeholder rather than holding up the canvas.
62
+ const [ownTeamJoinLink, setOwnTeamJoinLinkState] = useState(null);
63
+ const [timer, setTimerState] = useState(DEFAULT_SURFACE_TIMER);
64
+ // Not processing is the honest default and the overwhelmingly common state,
65
+ // so this does not gate first paint: a surface that never hears about it
66
+ // renders as it always did.
67
+ const [isRoundProcessing, setRoundProcessingState] = useState(false);
68
+ const [validationRejections, setValidationRejections] = useState([]);
69
+ const channel = useHostChannel({
70
+ targetWindow,
71
+ label: 'UserInterfaceWrapper',
72
+ requiredMessages: REQUIRED_MESSAGES,
73
+ onMessage: (msg, { markReceived }) => {
74
+ if (msg.type === 'stateChanged') {
75
+ const payload = msg.payload ?? {};
76
+ setDataState(payload);
77
+ lastHostDataRef.current = payload;
78
+ if (typeof msg.completedTicks === 'number') {
79
+ setCompletedTicks(msg.completedTicks);
80
+ }
81
+ markReceived('stateChanged');
82
+ }
83
+ else if (msg.type === 'setCrossTeamData') {
84
+ setCrossTeamDataState(msg.payload ?? []);
85
+ }
86
+ else if (msg.type === 'setSettings') {
87
+ setSettingsState(msg.payload);
88
+ markReceived('setSettings');
89
+ }
90
+ else if (msg.type === 'setTheme') {
91
+ setThemeState(msg.payload);
92
+ markReceived('setTheme');
93
+ }
94
+ else if (msg.type === 'setLocale') {
95
+ setLocaleState(msg.payload);
96
+ markReceived('setLocale');
97
+ }
98
+ else if (msg.type === 'setParticipant') {
99
+ setParticipantState(msg.payload);
100
+ markReceived('setParticipant');
101
+ }
102
+ else if (msg.type === 'setViewerRole') {
103
+ setViewerRoleState(msg.role);
104
+ if (msg.role === 'observer') {
105
+ // An observer has no caller identity and never receives
106
+ // setParticipant, so satisfy the required-message gate here
107
+ // and keep the participant null.
108
+ setParticipantState(null);
109
+ markReceived('setParticipant');
110
+ }
111
+ }
112
+ else if (msg.type === 'setRunKind') {
113
+ setRunKindState(msg.runKind);
114
+ }
115
+ else if (msg.type === 'setParticipantList') {
116
+ setParticipantsState(msg.participants);
117
+ }
118
+ else if (msg.type === 'setTeamList') {
119
+ setTeamsState(msg.payload);
120
+ }
121
+ else if (msg.type === 'setOwnTeam') {
122
+ setHostOwnTeamState(msg.payload);
123
+ }
124
+ else if (msg.type === 'setOwnTeamJoinLink') {
125
+ setOwnTeamJoinLinkState(msg.payload);
126
+ }
127
+ else if (msg.type === 'setTimer') {
128
+ setTimerState(msg.payload);
129
+ }
130
+ else if (msg.type === 'setRoundProcessing') {
131
+ setRoundProcessingState(msg.processing);
132
+ }
133
+ else if (msg.type === 'validationRejected') {
134
+ setValidationRejections(msg.rejections);
135
+ // Roll local state back to the host's last confirmed value so the
136
+ // optimistic write done by setData is discarded.
137
+ setDataState(lastHostDataRef.current);
138
+ }
139
+ },
140
+ });
141
+ const { isInitialized, expectedParentOrigin, sendMutation, sendMutationReportingCommit, uploadBlob, readAsset, getPastState: getPastStateRaw, getAllTeamsParticipants, getAssetUrl, assetUrls, setThemePreference, endpoints, themePreference, sessionKey, } = channel;
142
+ useDocumentAttributes(locale?.locale, theme?.mode);
143
+ const getPastState = useTypedPastState(getPastStateRaw);
144
+ // The tick the participant is looking at is held here and compared against
145
+ // the tick the host reports committing into, so the pair never leaves the
146
+ // surface. Read from a ref rather than closed over, or every tick would
147
+ // rebuild `setData` and re-render every control that depends on its
148
+ // identity.
149
+ const completedTicksRef = useLatestRef(completedTicks);
150
+ const setData = useOptimisticMerge(data, setDataState, useCallback((update, deletes) => {
151
+ const composedAtTick = completedTicksRef.current;
152
+ return sendMutationReportingCommit({
153
+ type: 'updateData',
154
+ payload: update,
155
+ ...(deletes?.length ? { deletes } : {}),
156
+ }).then(({ committedAtTick }) => ({
157
+ committedAtTick,
158
+ composedAtTick,
159
+ // Both numbers are only ever in one place at once, so the
160
+ // comparison lives here rather than in every caller.
161
+ late: typeof committedAtTick === 'number' && committedAtTick > composedAtTick,
162
+ }));
163
+ }, [sendMutationReportingCommit, completedTicksRef]));
164
+ const requestTick = useCallback(() => sendMutation({ type: 'requestTick' }), [sendMutation]);
165
+ // No teamId: the host resolves the caller's team, so a surface can only
166
+ // rename its own. The result arrives back through setOwnTeam/setTeamList,
167
+ // which is what to render rather than the name that was sent.
168
+ const renameOwnTeam = useCallback((name) => sendMutation({ type: 'renameOwnTeam', name }), [sendMutation]);
169
+ const clearValidationRejections = useCallback(() => {
170
+ setValidationRejections([]);
171
+ }, []);
172
+ // Memoize each context value so consumers only re-render when their slice
173
+ // actually changes — otherwise every state update would invalidate every
174
+ // context's value object identity and cascade through the whole tree.
175
+ const dataContextValue = useMemo(() => ({ data, settings, completedTicks, isRoundProcessing, setData }), [data, settings, completedTicks, isRoundProcessing, setData]);
176
+ const validationContextValue = useMemo(() => ({
177
+ rejections: validationRejections,
178
+ clearRejections: clearValidationRejections,
179
+ }), [validationRejections, clearValidationRejections]);
180
+ const callbacksContextValue = useMemo(() => ({
181
+ requestTick,
182
+ renameOwnTeam,
183
+ uploadBlob,
184
+ getAssetUrl,
185
+ readAsset,
186
+ getPastState,
187
+ getAllTeamsParticipants,
188
+ setThemePreference,
189
+ endpoints,
190
+ }), [
191
+ requestTick,
192
+ renameOwnTeam,
193
+ uploadBlob,
194
+ getAssetUrl,
195
+ readAsset,
196
+ getPastState,
197
+ getAllTeamsParticipants,
198
+ setThemePreference,
199
+ endpoints,
200
+ ]);
201
+ // Prefer what the host reported. The participant fallback keeps a host that
202
+ // predates `setOwnTeam` working for participants; only an observer on such a
203
+ // host is left without a team, which is the case that was already broken.
204
+ const ownTeam = useMemo(() => {
205
+ if (hostOwnTeam)
206
+ return hostOwnTeam;
207
+ if (!participant)
208
+ return null;
209
+ return teams.find((team) => team.teamId === participant.teamId) ?? null;
210
+ }, [hostOwnTeam, participant, teams]);
211
+ const participantId = participant?.id;
212
+ const storageScope = useMemo(() => ({
213
+ sessionKey,
214
+ surface: 'player',
215
+ viewer: participantId ?? viewerRole,
216
+ }), [sessionKey, participantId, viewerRole]);
217
+ return (_jsx(SurfaceGate, { expectedParentOrigin: expectedParentOrigin, ready: isInitialized && theme !== undefined && locale !== undefined, fallback: fallback, children: _jsx(SurfaceScope, { storage: storageScope, assetUrls: assetUrls, getAssetUrl: getAssetUrl, locale: locale?.locale, getPastState: getPastState, children: _jsx(UserInterfaceDataContext.Provider, { value: dataContextValue, children: _jsx(CrossTeamDataContext.Provider, { value: crossTeamData, children: _jsx(UserInterfaceThemeContext.Provider, { value: theme, children: _jsx(UserInterfaceThemePreferenceContext.Provider, { value: themePreference, children: _jsx(UserInterfaceLocaleContext.Provider, { value: locale, children: _jsx(UserInterfaceRunKindContext.Provider, { value: runKind, children: _jsx(UserInterfaceViewerRoleContext.Provider, { value: viewerRole, children: _jsx(UserInterfaceParticipantContext.Provider, { value: participant, children: _jsx(UserInterfaceParticipantsContext.Provider, { value: participants, children: _jsx(UserInterfaceTeamsContext.Provider, { value: teams, children: _jsx(UserInterfaceOwnTeamContext.Provider, { value: ownTeam, children: _jsx(UserInterfaceOwnTeamJoinLinkContext.Provider, { value: ownTeamJoinLink, children: _jsx(UserInterfaceTimerContext.Provider, { value: timer, children: _jsx(UserInterfaceCallbacksContext.Provider, { value: callbacksContextValue, children: _jsx(UserInterfaceValidationContext.Provider, { value: validationContextValue, children: children }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }));
218
+ }
219
+ export default UserInterfaceWrapper;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Reads the `expectedParentOrigin` query parameter from `window.location` and
3
+ * validates it as a bare origin string (`scheme://host[:port]`, no path/query/
4
+ * fragment). Returns the canonicalised origin if valid, `null` otherwise.
5
+ *
6
+ * The wrapper uses this both as the `targetOrigin` for its outbound `ready`
7
+ * postMessage and as the allowed `event.origin` for inbound `init`. Without it
8
+ * the wrapper refuses to operate (see each wrapper for the user-facing error).
9
+ */
10
+ export declare function readExpectedParentOrigin(): string | null;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Reads the `expectedParentOrigin` query parameter from `window.location` and
3
+ * validates it as a bare origin string (`scheme://host[:port]`, no path/query/
4
+ * fragment). Returns the canonicalised origin if valid, `null` otherwise.
5
+ *
6
+ * The wrapper uses this both as the `targetOrigin` for its outbound `ready`
7
+ * postMessage and as the allowed `event.origin` for inbound `init`. Without it
8
+ * the wrapper refuses to operate (see each wrapper for the user-facing error).
9
+ */
10
+ export function readExpectedParentOrigin() {
11
+ const raw = new URLSearchParams(window.location.search).get('expectedParentOrigin');
12
+ if (!raw)
13
+ return null;
14
+ let url;
15
+ try {
16
+ url = new URL(raw);
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ if (url.protocol !== 'http:' && url.protocol !== 'https:')
22
+ return null;
23
+ if (url.pathname !== '/' && url.pathname !== '')
24
+ return null;
25
+ if (url.search)
26
+ return null;
27
+ if (url.hash)
28
+ return null;
29
+ if (url.username || url.password)
30
+ return null;
31
+ return url.origin;
32
+ }
@@ -0,0 +1,50 @@
1
+ export { default as UserInterfaceWrapper } from './UserInterfaceWrapper.js';
2
+ export { useUserInterfaceData, UserInterfaceDataContext } from './UserInterfaceDataContext.js';
3
+ export { useUserInterfaceTheme, type UserInterfaceTheme, type UserInterfaceModeColors, UserInterfaceThemeContext, } from './UserInterfaceThemeContext.js';
4
+ export { useUserInterfaceThemePreference, UserInterfaceThemePreferenceContext, } from './UserInterfaceThemePreferenceContext.js';
5
+ export { useUserInterfaceLocale, type UserInterfaceLocale, UserInterfaceLocaleContext, } from './UserInterfaceLocaleContext.js';
6
+ export { useUserInterfaceParticipant, UserInterfaceParticipantContext, } from './UserInterfaceParticipantContext.js';
7
+ export { useUserInterfaceViewerRole, type ViewerRole, UserInterfaceViewerRoleContext, } from './UserInterfaceViewerRoleContext.js';
8
+ export { useRunKind, type RunKind, UserInterfaceRunKindContext, } from './UserInterfaceRunKindContext.js';
9
+ export { useUserInterfaceTimer, UserInterfaceTimerContext } from './UserInterfaceTimerContext.js';
10
+ export { useViewerStorage, ViewerStorageContext, type ViewerStorageScope, type ViewerStorageSurface, } from './useViewerStorage.js';
11
+ export { useAssetUrl, useAsset, localisedReferences, AssetContext, type AssetFormat, type AssetScope, type AssetState, type UseAssetOptions, } from './useAsset.js';
12
+ export { usePastState, PastStateContext, type PastStateReader, type PastStateResult, } from './usePastState.js';
13
+ export type { ThemeColorRole, ThemeContrastText } from './internal/createContexts.js';
14
+ export type { TimerState } from '../build/timer.js';
15
+ export { useUserInterfaceParticipants, UserInterfaceParticipantsContext, } from './UserInterfaceParticipantsContext.js';
16
+ export { useUserInterfaceTeams, UserInterfaceTeamsContext } from './UserInterfaceTeamsContext.js';
17
+ export { useUserInterfaceOwnTeam, UserInterfaceOwnTeamContext, } from './UserInterfaceOwnTeamContext.js';
18
+ export { useUserInterfaceOwnTeamJoinLink, type OwnTeamJoinLink, UserInterfaceOwnTeamJoinLinkContext, } from './UserInterfaceOwnTeamJoinLinkContext.js';
19
+ export { useUserInterfaceCrossTeamData, CrossTeamDataContext, type CrossTeamData, } from './CrossTeamDataContext.js';
20
+ export { useUserInterfaceCallbacks, UserInterfaceCallbacksContext, } from './UserInterfaceCallbacksContext.js';
21
+ export { useUserInterfaceValidationRejections, type UserInterfaceValidationContextValue, UserInterfaceValidationContext, } from './UserInterfaceValidationContext.js';
22
+ export { default as SettingsEditorWrapper } from './SettingsEditorWrapper.js';
23
+ export { useSettingsEditor, type SettingsEditorValue, SettingsEditorContext, } from './SettingsEditorContext.js';
24
+ export { useSettingsEditorTheme, type SettingsEditorTheme, type SettingsEditorModeColors, SettingsEditorThemeContext, } from './SettingsEditorThemeContext.js';
25
+ export { useSettingsEditorThemePreference, SettingsEditorThemePreferenceContext, } from './SettingsEditorThemePreferenceContext.js';
26
+ export { useSettingsEditorLocale, type SettingsEditorLocale, SettingsEditorLocaleContext, } from './SettingsEditorLocaleContext.js';
27
+ export { useSettingsEditorCallbacks, SettingsEditorCallbacksContext, } from './SettingsEditorCallbacksContext.js';
28
+ export { default as SessionUserInterfaceWrapper } from './SessionUserInterfaceWrapper.js';
29
+ export { useSessionUserInterfaceData, SessionUserInterfaceDataContext, type SessionData, type SharedState, type TeamState, } from './SessionUserInterfaceDataContext.js';
30
+ export { useSessionUserInterfaceTheme, type SessionUserInterfaceTheme, type SessionUserInterfaceModeColors, SessionUserInterfaceThemeContext, } from './SessionUserInterfaceThemeContext.js';
31
+ export { useSessionUserInterfaceThemePreference, SessionUserInterfaceThemePreferenceContext, } from './SessionUserInterfaceThemePreferenceContext.js';
32
+ export { useSessionUserInterfaceLocale, type SessionUserInterfaceLocale, SessionUserInterfaceLocaleContext, } from './SessionUserInterfaceLocaleContext.js';
33
+ export { useSessionUserInterfaceCallbacks, SessionUserInterfaceCallbacksContext, } from './SessionUserInterfaceCallbacksContext.js';
34
+ export { useSessionUserInterfaceParticipants, SessionUserInterfaceParticipantsContext, } from './SessionUserInterfaceParticipantsContext.js';
35
+ export { useSessionUserInterfaceHookFailures, type SessionHookFailure, type SessionUserInterfaceHookFailuresValue, SessionUserInterfaceHookFailuresContext, } from './SessionUserInterfaceHookFailuresContext.js';
36
+ export { useSessionUserInterfaceHostOverlays, type HostOverlay, SessionUserInterfaceHostOverlaysContext, } from './SessionUserInterfaceHostOverlaysContext.js';
37
+ export { useSessionUserInterfaceMeta, type SessionMeta, SessionUserInterfaceMetaContext, } from './SessionUserInterfaceMetaContext.js';
38
+ export { useSessionUserInterfaceTeamLinks, type TeamLinks, SessionUserInterfaceTeamLinksContext, } from './SessionUserInterfaceTeamLinksContext.js';
39
+ export { useSessionUserInterfaceWalkthroughLink, type WalkthroughLink, SessionUserInterfaceWalkthroughLinkContext, } from './SessionUserInterfaceWalkthroughLinkContext.js';
40
+ export { useSessionUserInterfaceTeamConfiguration, type TeamConfiguration, SessionUserInterfaceTeamConfigurationContext, } from './SessionUserInterfaceTeamConfigurationContext.js';
41
+ export { useSessionUserInterfaceTeams, SessionUserInterfaceTeamsContext, } from './SessionUserInterfaceTeamsContext.js';
42
+ export { PresentationWrapper } from './PresentationWrapper.js';
43
+ export { usePresentation, PresentationContext, type PresentationGame, type PresentationSession, type PresentationValue, } from './PresentationContext.js';
44
+ export { MutationError, type MutationOutcome } from './mutation-outcome.js';
45
+ export { EndpointError, type EffectStep, type EndpointFailure, type EndpointFailureOutcome, } from '../build/endpoints.js';
46
+ export { useMutationReporter, type MutationFailure, type MutationReporter, } from './useMutationReporter.js';
47
+ export { TickSnapshotError } from './tick-snapshot.js';
48
+ export type { Participant, PastStateEntry, PastStateQuery, TeamInfo, TeamStateEntry, } from '../build/callbacks.js';
49
+ export type { ContextDeletePath, ContextUpdate } from '../build/context-merge.js';
50
+ export type { SetDataOptions } from './UserInterfaceDataContext.js';
@@ -0,0 +1,52 @@
1
+ export { default as UserInterfaceWrapper } from './UserInterfaceWrapper.js';
2
+ export { useUserInterfaceData, UserInterfaceDataContext } from './UserInterfaceDataContext.js';
3
+ export { useUserInterfaceTheme, UserInterfaceThemeContext, } from './UserInterfaceThemeContext.js';
4
+ export { useUserInterfaceThemePreference, UserInterfaceThemePreferenceContext, } from './UserInterfaceThemePreferenceContext.js';
5
+ export { useUserInterfaceLocale, UserInterfaceLocaleContext, } from './UserInterfaceLocaleContext.js';
6
+ export { useUserInterfaceParticipant, UserInterfaceParticipantContext, } from './UserInterfaceParticipantContext.js';
7
+ export { useUserInterfaceViewerRole, UserInterfaceViewerRoleContext, } from './UserInterfaceViewerRoleContext.js';
8
+ export { useRunKind, UserInterfaceRunKindContext, } from './UserInterfaceRunKindContext.js';
9
+ export { useUserInterfaceTimer, UserInterfaceTimerContext } from './UserInterfaceTimerContext.js';
10
+ export { useViewerStorage, ViewerStorageContext, } from './useViewerStorage.js';
11
+ export { useAssetUrl, useAsset, localisedReferences, AssetContext, } from './useAsset.js';
12
+ export { usePastState, PastStateContext, } from './usePastState.js';
13
+ export { useUserInterfaceParticipants, UserInterfaceParticipantsContext, } from './UserInterfaceParticipantsContext.js';
14
+ export { useUserInterfaceTeams, UserInterfaceTeamsContext } from './UserInterfaceTeamsContext.js';
15
+ export { useUserInterfaceOwnTeam, UserInterfaceOwnTeamContext, } from './UserInterfaceOwnTeamContext.js';
16
+ export { useUserInterfaceOwnTeamJoinLink, UserInterfaceOwnTeamJoinLinkContext, } from './UserInterfaceOwnTeamJoinLinkContext.js';
17
+ export { useUserInterfaceCrossTeamData, CrossTeamDataContext, } from './CrossTeamDataContext.js';
18
+ export { useUserInterfaceCallbacks, UserInterfaceCallbacksContext, } from './UserInterfaceCallbacksContext.js';
19
+ export { useUserInterfaceValidationRejections, UserInterfaceValidationContext, } from './UserInterfaceValidationContext.js';
20
+ export { default as SettingsEditorWrapper } from './SettingsEditorWrapper.js';
21
+ export { useSettingsEditor, SettingsEditorContext, } from './SettingsEditorContext.js';
22
+ export { useSettingsEditorTheme, SettingsEditorThemeContext, } from './SettingsEditorThemeContext.js';
23
+ export { useSettingsEditorThemePreference, SettingsEditorThemePreferenceContext, } from './SettingsEditorThemePreferenceContext.js';
24
+ export { useSettingsEditorLocale, SettingsEditorLocaleContext, } from './SettingsEditorLocaleContext.js';
25
+ export { useSettingsEditorCallbacks, SettingsEditorCallbacksContext, } from './SettingsEditorCallbacksContext.js';
26
+ export { default as SessionUserInterfaceWrapper } from './SessionUserInterfaceWrapper.js';
27
+ export { useSessionUserInterfaceData, SessionUserInterfaceDataContext, } from './SessionUserInterfaceDataContext.js';
28
+ export { useSessionUserInterfaceTheme, SessionUserInterfaceThemeContext, } from './SessionUserInterfaceThemeContext.js';
29
+ export { useSessionUserInterfaceThemePreference, SessionUserInterfaceThemePreferenceContext, } from './SessionUserInterfaceThemePreferenceContext.js';
30
+ export { useSessionUserInterfaceLocale, SessionUserInterfaceLocaleContext, } from './SessionUserInterfaceLocaleContext.js';
31
+ export { useSessionUserInterfaceCallbacks, SessionUserInterfaceCallbacksContext, } from './SessionUserInterfaceCallbacksContext.js';
32
+ export { useSessionUserInterfaceParticipants, SessionUserInterfaceParticipantsContext, } from './SessionUserInterfaceParticipantsContext.js';
33
+ export { useSessionUserInterfaceHookFailures, SessionUserInterfaceHookFailuresContext, } from './SessionUserInterfaceHookFailuresContext.js';
34
+ export { useSessionUserInterfaceHostOverlays, SessionUserInterfaceHostOverlaysContext, } from './SessionUserInterfaceHostOverlaysContext.js';
35
+ export { useSessionUserInterfaceMeta, SessionUserInterfaceMetaContext, } from './SessionUserInterfaceMetaContext.js';
36
+ export { useSessionUserInterfaceTeamLinks, SessionUserInterfaceTeamLinksContext, } from './SessionUserInterfaceTeamLinksContext.js';
37
+ export { useSessionUserInterfaceWalkthroughLink, SessionUserInterfaceWalkthroughLinkContext, } from './SessionUserInterfaceWalkthroughLinkContext.js';
38
+ export { useSessionUserInterfaceTeamConfiguration, SessionUserInterfaceTeamConfigurationContext, } from './SessionUserInterfaceTeamConfigurationContext.js';
39
+ export { useSessionUserInterfaceTeams, SessionUserInterfaceTeamsContext, } from './SessionUserInterfaceTeamsContext.js';
40
+ // The wire protocol the wrappers speak (./ui-protocol.ts) is deliberately not
41
+ // exported: game code never handles a message. A host implementation takes the
42
+ // host's definition from @seriousgaming/sdk-tools/host.
43
+ export { PresentationWrapper } from './PresentationWrapper.js';
44
+ export { usePresentation, PresentationContext, } from './PresentationContext.js';
45
+ export { MutationError } from './mutation-outcome.js';
46
+ export { EndpointError, } from '../build/endpoints.js';
47
+ export { useMutationReporter, } from './useMutationReporter.js';
48
+ export { TickSnapshotError } from './tick-snapshot.js';
49
+ // The render/mock testing helpers are published from the dedicated
50
+ // `@seriousgaming/sdk/ui/testing` subpath (see ./testing.ts)
51
+ // so the main `/ui` entry does not pull in the optional @testing-library/react
52
+ // peer at runtime.
@@ -0,0 +1,19 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+ interface SurfaceGateProps {
3
+ /** The pinned parent origin; a missing/malformed value is a config error. */
4
+ expectedParentOrigin: string | null;
5
+ /** Whether the wrapper has received everything it needs to render children. */
6
+ ready: boolean;
7
+ /** Shown while `ready` is false. */
8
+ fallback: ReactNode;
9
+ children: ReactNode;
10
+ }
11
+ /**
12
+ * The shared render gate every surface wrapper puts in front of its provider
13
+ * tree: an inline error when the asset-host origin is unconfigured, the
14
+ * fallback until the wrapper is `ready`, otherwise the children. Callers
15
+ * compute `ready` so surfaces with extra conditions (e.g. configuration's
16
+ * `surface`) can AND them in without a bespoke prop.
17
+ */
18
+ export declare function SurfaceGate({ expectedParentOrigin, ready, fallback, children, }: SurfaceGateProps): ReactElement;
19
+ export {};
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * The shared render gate every surface wrapper puts in front of its provider
4
+ * tree: an inline error when the asset-host origin is unconfigured, the
5
+ * fallback until the wrapper is `ready`, otherwise the children. Callers
6
+ * compute `ready` so surfaces with extra conditions (e.g. configuration's
7
+ * `surface`) can AND them in without a bespoke prop.
8
+ */
9
+ export function SurfaceGate({ expectedParentOrigin, ready, fallback, children, }) {
10
+ if (!expectedParentOrigin) {
11
+ return (_jsx("div", { role: "alert", children: "Asset host configuration error: expectedParentOrigin missing or malformed." }));
12
+ }
13
+ if (!ready) {
14
+ return _jsx(_Fragment, { children: fallback });
15
+ }
16
+ return _jsx(_Fragment, { children: children });
17
+ }
@@ -0,0 +1,17 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { AssetUrls } from '../ui-protocol.js';
3
+ import { type PastStateReader } from '../usePastState.js';
4
+ import { type ViewerStorageScope } from '../useViewerStorage.js';
5
+ /**
6
+ * The contexts behind the hooks every surface shares (`useViewerStorage`,
7
+ * `useAssetUrl`, `useAsset` and, where the surface can read history,
8
+ * `usePastState`), provided once per wrapper.
9
+ */
10
+ export declare function SurfaceScope({ storage, assetUrls, getAssetUrl, locale, getPastState, children, }: {
11
+ storage: ViewerStorageScope;
12
+ assetUrls: AssetUrls | undefined;
13
+ getAssetUrl: (reference: string) => string;
14
+ locale: string | undefined;
15
+ getPastState?: PastStateReader;
16
+ children: ReactNode;
17
+ }): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { AssetContext } from '../useAsset.js';
4
+ import { PastStateContext } from '../usePastState.js';
5
+ import { ViewerStorageContext } from '../useViewerStorage.js';
6
+ /**
7
+ * The contexts behind the hooks every surface shares (`useViewerStorage`,
8
+ * `useAssetUrl`, `useAsset` and, where the surface can read history,
9
+ * `usePastState`), provided once per wrapper.
10
+ */
11
+ export function SurfaceScope({ storage, assetUrls, getAssetUrl, locale, getPastState, children, }) {
12
+ const ready = assetUrls !== undefined;
13
+ const assets = useMemo(() => ({ getAssetUrl: ready ? getAssetUrl : undefined, locale }), [ready, getAssetUrl, locale]);
14
+ const scoped = (_jsx(ViewerStorageContext.Provider, { value: storage, children: _jsx(AssetContext.Provider, { value: assets, children: children }) }));
15
+ return getPastState ? (_jsx(PastStateContext.Provider, { value: getPastState, children: scoped })) : (scoped);
16
+ }
@@ -0,0 +1,50 @@
1
+ import { type Context } from 'react';
2
+ /**
3
+ * Colour roles shared by every surface's theme. Internal only — each surface
4
+ * re-declares its public `*Theme` type locally (referencing this) so the
5
+ * published type names are unchanged.
6
+ */
7
+ export type SurfaceModeColors = {
8
+ primary: string;
9
+ secondary: string;
10
+ success: string;
11
+ error: string;
12
+ warning: string;
13
+ info: string;
14
+ /**
15
+ * The text colour to draw on each of the colours above, as an opaque
16
+ * `#rrggbb`, meeting WCAG AA (4.5:1) against it (§12.3).
17
+ */
18
+ contrastText: ThemeContrastText;
19
+ };
20
+ /** The palette roles a theme sends, each a colour a surface may paint with. */
21
+ export type ThemeColorRole = 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info';
22
+ /** One text colour per palette role, for text drawn on that role's colour. */
23
+ export type ThemeContrastText = Record<ThemeColorRole, string>;
24
+ export interface RequiredContext<T> {
25
+ Context: Context<T | undefined>;
26
+ /** Read the value; throws with a surface-specific message if unprovided. */
27
+ useValue: () => T;
28
+ }
29
+ /**
30
+ * Build a context whose consumer hook throws when read outside its wrapper.
31
+ * Backs the boilerplate "throwing" context roles (theme, theme-preference,
32
+ * callbacks, locale, surface, ui-list, validation, participant(s)); each
33
+ * surface file wraps this and re-exports the concrete `*Context` / `use*`
34
+ * names so the published API is unchanged.
35
+ */
36
+ export declare function createRequiredContext<T>(options: {
37
+ hookName: string;
38
+ wrapperName: string;
39
+ }): RequiredContext<T>;
40
+ export interface DefaultedContext<T> {
41
+ Context: Context<T>;
42
+ /** Read the value; always defined (falls back to the default). */
43
+ useValue: () => T;
44
+ }
45
+ /**
46
+ * Build a context that always returns a value, defaulting to `defaultValue`
47
+ * when unprovided. Backs the non-throwing roles (timer, basic team list,
48
+ * player participant list).
49
+ */
50
+ export declare function createDefaultedContext<T>(defaultValue: T): DefaultedContext<T>;
@@ -0,0 +1,32 @@
1
+ import { createContext, useContext } from 'react';
2
+ /**
3
+ * Build a context whose consumer hook throws when read outside its wrapper.
4
+ * Backs the boilerplate "throwing" context roles (theme, theme-preference,
5
+ * callbacks, locale, surface, ui-list, validation, participant(s)); each
6
+ * surface file wraps this and re-exports the concrete `*Context` / `use*`
7
+ * names so the published API is unchanged.
8
+ */
9
+ export function createRequiredContext(options) {
10
+ const { hookName, wrapperName } = options;
11
+ const ContextObject = createContext(undefined);
12
+ function useValue() {
13
+ const value = useContext(ContextObject);
14
+ if (value === undefined) {
15
+ throw new Error(`${hookName} must be used inside a ${wrapperName}`);
16
+ }
17
+ return value;
18
+ }
19
+ return { Context: ContextObject, useValue };
20
+ }
21
+ /**
22
+ * Build a context that always returns a value, defaulting to `defaultValue`
23
+ * when unprovided. Backs the non-throwing roles (timer, basic team list,
24
+ * player participant list).
25
+ */
26
+ export function createDefaultedContext(defaultValue) {
27
+ const ContextObject = createContext(defaultValue);
28
+ function useValue() {
29
+ return useContext(ContextObject);
30
+ }
31
+ return { Context: ContextObject, useValue };
32
+ }
@@ -0,0 +1,9 @@
1
+ /** The direction a locale's text runs in. */
2
+ export declare function textDirection(locale: string): 'ltr' | 'rtl';
3
+ /**
4
+ * Keep `lang`, `dir` and `color-scheme` on the document root in step with the
5
+ * surface's locale and theme, so a surface gets right-to-left layout, the
6
+ * correct hyphenation and voice, and dark scrollbars and form controls without
7
+ * setting any of them itself. Each is restored when the wrapper unmounts.
8
+ */
9
+ export declare function useDocumentAttributes(locale: string | undefined, mode: 'light' | 'dark' | undefined): void;
@@ -0,0 +1,87 @@
1
+ import { useEffect } from 'react';
2
+ /**
3
+ * Languages written right to left, for a runtime whose `Intl.Locale` cannot
4
+ * say. The script decides first where the tag names one, so `ku-Arab` is
5
+ * right to left and `ku-Latn` is not.
6
+ */
7
+ const RTL_LANGUAGES = new Set([
8
+ 'ar',
9
+ 'arc',
10
+ 'ckb',
11
+ 'dv',
12
+ 'fa',
13
+ 'he',
14
+ 'iw',
15
+ 'ks',
16
+ 'ps',
17
+ 'sd',
18
+ 'ug',
19
+ 'ur',
20
+ 'yi',
21
+ ]);
22
+ const RTL_SCRIPTS = new Set(['Adlm', 'Arab', 'Hebr', 'Nkoo', 'Rohg', 'Syrc', 'Thaa']);
23
+ function fromIntl(locale) {
24
+ try {
25
+ const parsed = new Intl.Locale(locale);
26
+ const info = typeof parsed.getTextInfo === 'function' ? parsed.getTextInfo() : parsed.textInfo;
27
+ if (info?.direction === 'rtl' || info?.direction === 'ltr')
28
+ return info.direction;
29
+ }
30
+ catch {
31
+ // Not a well-formed tag: the list below still has a view.
32
+ }
33
+ return undefined;
34
+ }
35
+ function fromList(locale) {
36
+ const parts = locale.split(/[-_]/);
37
+ const script = parts.find((part, index) => index > 0 && /^[A-Za-z]{4}$/.test(part));
38
+ if (script) {
39
+ const normalised = script[0].toUpperCase() + script.slice(1).toLowerCase();
40
+ return RTL_SCRIPTS.has(normalised) ? 'rtl' : 'ltr';
41
+ }
42
+ return RTL_LANGUAGES.has(parts[0].toLowerCase()) ? 'rtl' : 'ltr';
43
+ }
44
+ /** The direction a locale's text runs in. */
45
+ export function textDirection(locale) {
46
+ return fromIntl(locale) ?? fromList(locale);
47
+ }
48
+ /**
49
+ * Keep `lang`, `dir` and `color-scheme` on the document root in step with the
50
+ * surface's locale and theme, so a surface gets right-to-left layout, the
51
+ * correct hyphenation and voice, and dark scrollbars and form controls without
52
+ * setting any of them itself. Each is restored when the wrapper unmounts.
53
+ */
54
+ export function useDocumentAttributes(locale, mode) {
55
+ useEffect(() => {
56
+ if (locale === undefined || typeof document === 'undefined')
57
+ return;
58
+ const root = document.documentElement;
59
+ const previousLang = root.getAttribute('lang');
60
+ const previousDir = root.getAttribute('dir');
61
+ root.setAttribute('lang', locale);
62
+ root.setAttribute('dir', textDirection(locale));
63
+ return () => {
64
+ restore(root, 'lang', previousLang);
65
+ restore(root, 'dir', previousDir);
66
+ };
67
+ }, [locale]);
68
+ useEffect(() => {
69
+ if (mode === undefined || typeof document === 'undefined')
70
+ return;
71
+ const style = document.documentElement.style;
72
+ const previous = style.getPropertyValue('color-scheme');
73
+ style.setProperty('color-scheme', mode);
74
+ return () => {
75
+ if (previous)
76
+ style.setProperty('color-scheme', previous);
77
+ else
78
+ style.removeProperty('color-scheme');
79
+ };
80
+ }, [mode]);
81
+ }
82
+ function restore(root, name, previous) {
83
+ if (previous === null)
84
+ root.removeAttribute(name);
85
+ else
86
+ root.setAttribute(name, previous);
87
+ }
@@ -0,0 +1,3 @@
1
+ import type { TimerState } from '../../build/timer.js';
2
+ /** The timer a surface holds before the host sends one: nothing scheduled. */
3
+ export declare const DEFAULT_SURFACE_TIMER: TimerState;
@@ -0,0 +1,11 @@
1
+ /** The timer a surface holds before the host sends one: nothing scheduled. */
2
+ export const DEFAULT_SURFACE_TIMER = {
3
+ enabled: false,
4
+ paused: false,
5
+ nextTickAt: null,
6
+ intervalMs: null,
7
+ pausedAt: null,
8
+ pausedMs: 0,
9
+ extendedMs: 0,
10
+ scheduleStartedAt: null,
11
+ };