@ccheever/exact-ibex-runtime 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/package.json +63 -0
- package/src/abort/AbortController.ts +23 -0
- package/src/abort/AbortSignal.ts +152 -0
- package/src/abort/index.ts +2 -0
- package/src/accessibility.ts +12 -0
- package/src/arraybuffer-detach.ts +109 -0
- package/src/base64/base64.ts +168 -0
- package/src/base64/index.ts +1 -0
- package/src/blob/Blob.ts +259 -0
- package/src/blob/File.ts +59 -0
- package/src/blob/FormData.ts +323 -0
- package/src/blob/index.ts +3 -0
- package/src/bootstrap.ts +1946 -0
- package/src/broadcast/BroadcastChannel.ts +280 -0
- package/src/broadcast/index.ts +5 -0
- package/src/cache/Cache.ts +349 -0
- package/src/cache/CacheStorage.ts +89 -0
- package/src/cache/index.ts +27 -0
- package/src/camera/index.ts +6202 -0
- package/src/camera/processor.worker.ts +194 -0
- package/src/camera/scene.ts +195 -0
- package/src/clipboard/Clipboard.ts +129 -0
- package/src/clipboard/ClipboardItem.ts +97 -0
- package/src/clipboard/index.ts +6 -0
- package/src/clone/index.ts +1 -0
- package/src/clone/structuredClone.ts +389 -0
- package/src/clone/transferableSymbols.ts +2 -0
- package/src/compression/CompressionStream.ts +146 -0
- package/src/compression/DecompressionStream.ts +342 -0
- package/src/compression/index.ts +4 -0
- package/src/console/Console.ts +341 -0
- package/src/console/index.ts +2 -0
- package/src/core/accessibility-state.ts +263 -0
- package/src/core/accessibility.ts +184 -0
- package/src/core/agent-state.ts +37 -0
- package/src/core/diagnostics-logs.ts +144 -0
- package/src/core/host-call-bridge.ts +16 -0
- package/src/core/i18n-helpers.ts +189 -0
- package/src/core/locale-state.ts +253 -0
- package/src/core/locale.ts +95 -0
- package/src/crypto/Crypto.ts +2743 -0
- package/src/crypto/index.ts +1 -0
- package/src/diagnostics/logs.ts +7 -0
- package/src/encoding/TextDecoder.ts +1181 -0
- package/src/encoding/TextDecoderStream.ts +58 -0
- package/src/encoding/TextEncoder.ts +180 -0
- package/src/encoding/TextEncoderStream.ts +39 -0
- package/src/encoding/index.ts +8 -0
- package/src/events/CloseEvent.ts +91 -0
- package/src/events/DOMException.ts +409 -0
- package/src/events/ErrorEvent.ts +39 -0
- package/src/events/Event.ts +151 -0
- package/src/events/EventTarget.ts +280 -0
- package/src/events/FocusEvent.ts +27 -0
- package/src/events/KeyboardEvent.ts +46 -0
- package/src/events/MessageEvent.ts +61 -0
- package/src/events/ProgressEvent.ts +33 -0
- package/src/events/PromiseRejectionEvent.ts +31 -0
- package/src/events/index.ts +52 -0
- package/src/eventsource/EventSource.ts +371 -0
- package/src/eventsource/index.ts +2 -0
- package/src/fetch/Headers.ts +642 -0
- package/src/fetch/Request.ts +760 -0
- package/src/fetch/Response.ts +543 -0
- package/src/fetch/body.ts +1256 -0
- package/src/fetch/cookie-jar.ts +566 -0
- package/src/fetch/demo.ts +207 -0
- package/src/fetch/errors.ts +101 -0
- package/src/fetch/fetch.ts +2610 -0
- package/src/fetch/index.ts +101 -0
- package/src/fetch/native-bridge.ts +65 -0
- package/src/fetch/types.ts +258 -0
- package/src/filereader/FileReader.ts +236 -0
- package/src/filereader/index.ts +1 -0
- package/src/fs/Dirent.ts +39 -0
- package/src/fs/ExactFile.ts +450 -0
- package/src/fs/Stats.ts +80 -0
- package/src/fs/index.ts +944 -0
- package/src/fs/promises.ts +386 -0
- package/src/fs/shared.ts +328 -0
- package/src/http-server/index.js +697 -0
- package/src/http-server/index.ts +27 -0
- package/src/identity.generated.ts +14 -0
- package/src/index.ts +283 -0
- package/src/indexeddb/IDBCursor.ts +188 -0
- package/src/indexeddb/IDBDatabase.ts +343 -0
- package/src/indexeddb/IDBFactory.ts +269 -0
- package/src/indexeddb/IDBIndex.ts +194 -0
- package/src/indexeddb/IDBKeyRange.ts +109 -0
- package/src/indexeddb/IDBObjectStore.ts +468 -0
- package/src/indexeddb/IDBRequest.ts +163 -0
- package/src/indexeddb/IDBTransaction.ts +207 -0
- package/src/indexeddb/index.ts +34 -0
- package/src/indexeddb/utils.ts +52 -0
- package/src/inspect/index.ts +1 -0
- package/src/inspect/inspect.ts +465 -0
- package/src/internal/detect.ts +104 -0
- package/src/locale.ts +10 -0
- package/src/location/index.ts +1059 -0
- package/src/locks/LockManager.ts +460 -0
- package/src/locks/index.ts +12 -0
- package/src/media/VideoFrame.ts +58 -0
- package/src/messaging/MessageChannel.ts +31 -0
- package/src/messaging/MessagePort.ts +180 -0
- package/src/messaging/index.ts +2 -0
- package/src/messaging.ts +247 -0
- package/src/native/NativeModules.ts +354 -0
- package/src/native/index.ts +1 -0
- package/src/navigator/Navigator.ts +351 -0
- package/src/navigator/index.ts +1 -0
- package/src/node/Buffer.ts +1786 -0
- package/src/node/index.ts +4 -0
- package/src/node/path.ts +495 -0
- package/src/node/process.ts +2528 -0
- package/src/performance/Performance.ts +532 -0
- package/src/performance/index.ts +21 -0
- package/src/polyfills/array.ts +236 -0
- package/src/polyfills/arraybuffer.ts +172 -0
- package/src/polyfills/groupby.ts +85 -0
- package/src/polyfills/index.ts +85 -0
- package/src/polyfills/intl.ts +1956 -0
- package/src/polyfills/iterator.ts +479 -0
- package/src/polyfills/promise.ts +37 -0
- package/src/polyfills/set.ts +245 -0
- package/src/polyfills/string.ts +85 -0
- package/src/polyfills/typedarray.ts +110 -0
- package/src/promise-rejection-tracking.ts +464 -0
- package/src/react-native/index.ts +388 -0
- package/src/runtime-entry.ts +55 -0
- package/src/scheduling/AnimationFrame.ts +105 -0
- package/src/scheduling/IdleCallback.ts +167 -0
- package/src/scheduling/index.ts +13 -0
- package/src/security/Capabilities.ts +1146 -0
- package/src/security/Permissions.ts +392 -0
- package/src/security/capability-bits.generated.ts +63 -0
- package/src/security/index.ts +16 -0
- package/src/sqlite/Database.ts +456 -0
- package/src/sqlite/Statement.ts +206 -0
- package/src/sqlite/constants.ts +79 -0
- package/src/sqlite/errors.ts +25 -0
- package/src/sqlite/index.ts +34 -0
- package/src/sqlite/module.js +438 -0
- package/src/storage/Storage.ts +291 -0
- package/src/storage/StorageManager.ts +91 -0
- package/src/storage/index.ts +3 -0
- package/src/stream-compat.ts +47 -0
- package/src/streams/ReadableStream.ts +4131 -0
- package/src/streams/TransformStream.ts +375 -0
- package/src/streams/WritableStream.ts +866 -0
- package/src/streams/index.ts +41 -0
- package/src/timers/Timers.ts +296 -0
- package/src/timers/index.ts +11 -0
- package/src/url/URL.ts +656 -0
- package/src/url/URLPattern.ts +850 -0
- package/src/url/URLSearchParams.ts +244 -0
- package/src/url/index.ts +9 -0
- package/src/websocket/WebSocket.ts +770 -0
- package/src/websocket/WebSocketError.ts +52 -0
- package/src/websocket/WebSocketStream.ts +628 -0
- package/src/websocket/index.ts +7 -0
- package/src/window/index.ts +872 -0
|
@@ -0,0 +1,872 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Window API Implementation for Ibex Runtime
|
|
4
|
+
*
|
|
5
|
+
* Provides browser-compatible window object for library compatibility.
|
|
6
|
+
* Many npm packages check for `typeof window !== 'undefined'` to detect
|
|
7
|
+
* browser environments.
|
|
8
|
+
*
|
|
9
|
+
* Note: This is NOT a DOM window - it's a compatibility shim that provides
|
|
10
|
+
* commonly-used window properties that make sense in a native app context.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { CustomEvent, Event, EventTarget } from "../events";
|
|
14
|
+
|
|
15
|
+
// Type declarations for native bridge
|
|
16
|
+
declare global {
|
|
17
|
+
var __exactGetScreenInfo: (() => {
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
scale: number;
|
|
21
|
+
fontScale: number;
|
|
22
|
+
}) | undefined;
|
|
23
|
+
var __exactAppearanceState:
|
|
24
|
+
| {
|
|
25
|
+
colorScheme?: 'light' | 'dark';
|
|
26
|
+
reducedMotion?: boolean;
|
|
27
|
+
}
|
|
28
|
+
| undefined;
|
|
29
|
+
var __exactWindowNotifyMediaChange:
|
|
30
|
+
| ((next: { colorScheme?: 'light' | 'dark'; reducedMotion?: boolean }) => void)
|
|
31
|
+
| undefined;
|
|
32
|
+
var __exactWindowNotifyResize: (() => void) | undefined;
|
|
33
|
+
var __exactAndroidDispatchPlatformEvent:
|
|
34
|
+
| ((event: string | AndroidPlatformEvent, state?: AndroidPlatformState | null) => void)
|
|
35
|
+
| undefined;
|
|
36
|
+
var __exactAndroidDrainPlatformEvents: (() => void) | undefined;
|
|
37
|
+
var __exactAndroidGetPlatformState: (() => AndroidPlatformState) | undefined;
|
|
38
|
+
var __exactAppState: AndroidAppState | undefined;
|
|
39
|
+
var __exactInitialURL: string | null | undefined;
|
|
40
|
+
var __exactLocaleChanged:
|
|
41
|
+
| ((snapshot?: { tag?: string; tags?: readonly string[]; uses24Hour?: boolean } | null) => void)
|
|
42
|
+
| undefined;
|
|
43
|
+
var __exactAccessibilityChanged:
|
|
44
|
+
| ((snapshot?: Record<string, unknown> | null) => void)
|
|
45
|
+
| undefined;
|
|
46
|
+
var __exactReactNativeNotifyDimensionsChange:
|
|
47
|
+
| ((next?: AndroidPlatformState['dimensions']) => void)
|
|
48
|
+
| undefined;
|
|
49
|
+
var __exactReactNativeNotifyAppState:
|
|
50
|
+
| ((state: AndroidAppState) => void)
|
|
51
|
+
| undefined;
|
|
52
|
+
var __exactReactNativeNotifyMemoryWarning: (() => void) | undefined;
|
|
53
|
+
var __exactReactNativeNotifyURL: ((url: string) => void) | undefined;
|
|
54
|
+
var __exactNativeDialog:
|
|
55
|
+
| ((type: 'alert' | 'confirm' | 'prompt', message: string, defaultValue?: string) => string | null)
|
|
56
|
+
| undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type AndroidAppState = 'active' | 'background' | 'inactive' | 'unknown';
|
|
60
|
+
|
|
61
|
+
interface AndroidPlatformEvent {
|
|
62
|
+
type?: string;
|
|
63
|
+
state?: AndroidAppState;
|
|
64
|
+
url?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface AndroidPlatformState {
|
|
68
|
+
appState?: AndroidAppState;
|
|
69
|
+
initialURL?: string | null;
|
|
70
|
+
locale?: { tag?: string; tags?: readonly string[]; uses24Hour?: boolean };
|
|
71
|
+
screen?: {
|
|
72
|
+
width?: number;
|
|
73
|
+
height?: number;
|
|
74
|
+
scale?: number;
|
|
75
|
+
fontScale?: number;
|
|
76
|
+
};
|
|
77
|
+
dimensions?: {
|
|
78
|
+
window?: {
|
|
79
|
+
width?: number;
|
|
80
|
+
height?: number;
|
|
81
|
+
scale?: number;
|
|
82
|
+
fontScale?: number;
|
|
83
|
+
};
|
|
84
|
+
screen?: {
|
|
85
|
+
width?: number;
|
|
86
|
+
height?: number;
|
|
87
|
+
scale?: number;
|
|
88
|
+
fontScale?: number;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
appearance?: {
|
|
92
|
+
colorScheme?: 'light' | 'dark';
|
|
93
|
+
reducedMotion?: boolean;
|
|
94
|
+
};
|
|
95
|
+
accessibility?: Record<string, unknown>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Screen information
|
|
100
|
+
*/
|
|
101
|
+
export interface ScreenInfo {
|
|
102
|
+
/** Screen width in points */
|
|
103
|
+
width: number;
|
|
104
|
+
/** Screen height in points */
|
|
105
|
+
height: number;
|
|
106
|
+
/** Available width (minus system UI) */
|
|
107
|
+
availWidth: number;
|
|
108
|
+
/** Available height (minus system UI) */
|
|
109
|
+
availHeight: number;
|
|
110
|
+
/** Color depth (always 24 for mobile) */
|
|
111
|
+
colorDepth: number;
|
|
112
|
+
/** Pixel depth (always 24 for mobile) */
|
|
113
|
+
pixelDepth: number;
|
|
114
|
+
/** Screen orientation */
|
|
115
|
+
orientation: {
|
|
116
|
+
type: 'portrait-primary' | 'portrait-secondary' | 'landscape-primary' | 'landscape-secondary';
|
|
117
|
+
angle: number;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Location stub for compatibility
|
|
123
|
+
* In a native app, this represents the current "virtual" location
|
|
124
|
+
*/
|
|
125
|
+
export interface LocationInfo {
|
|
126
|
+
href: string;
|
|
127
|
+
protocol: string;
|
|
128
|
+
host: string;
|
|
129
|
+
hostname: string;
|
|
130
|
+
port: string;
|
|
131
|
+
pathname: string;
|
|
132
|
+
search: string;
|
|
133
|
+
hash: string;
|
|
134
|
+
origin: string;
|
|
135
|
+
reload: () => void;
|
|
136
|
+
replace: (url: string) => void;
|
|
137
|
+
assign: (url: string) => void;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get screen dimensions from native or use defaults
|
|
142
|
+
*/
|
|
143
|
+
function getScreenDimensions(): { width: number; height: number; scale: number; fontScale: number } {
|
|
144
|
+
if (typeof __exactGetScreenInfo === 'function') {
|
|
145
|
+
const info = __exactGetScreenInfo();
|
|
146
|
+
return {
|
|
147
|
+
width: info.width,
|
|
148
|
+
height: info.height,
|
|
149
|
+
scale: info.scale,
|
|
150
|
+
fontScale: info.fontScale,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Default fallback (iPhone 14 Pro dimensions)
|
|
154
|
+
return { width: 393, height: 852, scale: 3, fontScale: 1 };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function getScreenOrientation(): ScreenInfo['orientation'] {
|
|
158
|
+
const dims = getScreenDimensions();
|
|
159
|
+
const landscape = dims.width > dims.height;
|
|
160
|
+
return {
|
|
161
|
+
type: landscape ? 'landscape-primary' : 'portrait-primary',
|
|
162
|
+
angle: landscape ? 90 : 0,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Create the screen object
|
|
168
|
+
*/
|
|
169
|
+
function createScreen(): ScreenInfo {
|
|
170
|
+
const dims = getScreenDimensions();
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
get width() {
|
|
174
|
+
return getScreenDimensions().width;
|
|
175
|
+
},
|
|
176
|
+
get height() {
|
|
177
|
+
return getScreenDimensions().height;
|
|
178
|
+
},
|
|
179
|
+
get availWidth() {
|
|
180
|
+
return getScreenDimensions().width;
|
|
181
|
+
},
|
|
182
|
+
get availHeight() {
|
|
183
|
+
// Subtract status bar height approximation
|
|
184
|
+
return getScreenDimensions().height - 47;
|
|
185
|
+
},
|
|
186
|
+
colorDepth: 24,
|
|
187
|
+
pixelDepth: 24,
|
|
188
|
+
orientation: {
|
|
189
|
+
get type() {
|
|
190
|
+
return getScreenOrientation().type;
|
|
191
|
+
},
|
|
192
|
+
get angle() {
|
|
193
|
+
return getScreenOrientation().angle;
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Create location stub
|
|
201
|
+
*/
|
|
202
|
+
function createLocation(): LocationInfo {
|
|
203
|
+
// In native apps, we use a virtual location
|
|
204
|
+
// Apps can override this if they implement deep linking
|
|
205
|
+
let currentHref = 'exact://app/';
|
|
206
|
+
|
|
207
|
+
const parseUrl = (href: string) => {
|
|
208
|
+
try {
|
|
209
|
+
const url = new URL(href);
|
|
210
|
+
return {
|
|
211
|
+
href: url.href,
|
|
212
|
+
protocol: url.protocol,
|
|
213
|
+
host: url.host,
|
|
214
|
+
hostname: url.hostname,
|
|
215
|
+
port: url.port,
|
|
216
|
+
pathname: url.pathname,
|
|
217
|
+
search: url.search,
|
|
218
|
+
hash: url.hash,
|
|
219
|
+
origin: url.origin,
|
|
220
|
+
};
|
|
221
|
+
} catch {
|
|
222
|
+
return {
|
|
223
|
+
href,
|
|
224
|
+
protocol: 'exact:',
|
|
225
|
+
host: 'app',
|
|
226
|
+
hostname: 'app',
|
|
227
|
+
port: '',
|
|
228
|
+
pathname: '/',
|
|
229
|
+
search: '',
|
|
230
|
+
hash: '',
|
|
231
|
+
origin: 'exact://app',
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const location: LocationInfo = {
|
|
237
|
+
get href() { return parseUrl(currentHref).href; },
|
|
238
|
+
get protocol() { return parseUrl(currentHref).protocol; },
|
|
239
|
+
get host() { return parseUrl(currentHref).host; },
|
|
240
|
+
get hostname() { return parseUrl(currentHref).hostname; },
|
|
241
|
+
get port() { return parseUrl(currentHref).port; },
|
|
242
|
+
get pathname() { return parseUrl(currentHref).pathname; },
|
|
243
|
+
get search() { return parseUrl(currentHref).search; },
|
|
244
|
+
get hash() { return parseUrl(currentHref).hash; },
|
|
245
|
+
get origin() { return parseUrl(currentHref).origin; },
|
|
246
|
+
toString() { return parseUrl(currentHref).href; },
|
|
247
|
+
reload: () => {
|
|
248
|
+
// No-op in native - could trigger app refresh
|
|
249
|
+
console.warn('location.reload() is not supported in Ibex runtime');
|
|
250
|
+
},
|
|
251
|
+
replace: (url: string) => {
|
|
252
|
+
currentHref = url;
|
|
253
|
+
},
|
|
254
|
+
assign: (url: string) => {
|
|
255
|
+
currentHref = url;
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
return location;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
interface AppearanceState {
|
|
263
|
+
colorScheme: 'light' | 'dark';
|
|
264
|
+
reducedMotion: boolean;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const mediaQueryLists = new Set<MediaQueryList>();
|
|
268
|
+
|
|
269
|
+
function normalizeAppearanceState(value: unknown): AppearanceState {
|
|
270
|
+
const candidate =
|
|
271
|
+
typeof value === 'object' && value !== null
|
|
272
|
+
? (value as { colorScheme?: unknown; reducedMotion?: unknown })
|
|
273
|
+
: null;
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
colorScheme: candidate?.colorScheme === 'dark' ? 'dark' : 'light',
|
|
277
|
+
reducedMotion: candidate?.reducedMotion === true,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let appearanceState = normalizeAppearanceState(globalThis.__exactAppearanceState);
|
|
282
|
+
|
|
283
|
+
function updateAppearanceState(next: unknown): void {
|
|
284
|
+
appearanceState = normalizeAppearanceState(next);
|
|
285
|
+
globalThis.__exactAppearanceState = { ...appearanceState };
|
|
286
|
+
for (const mediaQueryList of mediaQueryLists) {
|
|
287
|
+
mediaQueryList._syncFromAppearance();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function syncMediaQueries(): void {
|
|
292
|
+
for (const mediaQueryList of mediaQueryLists) {
|
|
293
|
+
mediaQueryList._syncFromAppearance();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function dispatchWindowEvent(event: Event): void {
|
|
298
|
+
try {
|
|
299
|
+
window.dispatchEvent(event);
|
|
300
|
+
} catch {
|
|
301
|
+
// Ignore app listener failures.
|
|
302
|
+
}
|
|
303
|
+
const globalDispatch = (globalThis as typeof globalThis & {
|
|
304
|
+
dispatchEvent?: (event: Event) => boolean;
|
|
305
|
+
}).dispatchEvent;
|
|
306
|
+
if (typeof globalDispatch === 'function' && globalThis !== window) {
|
|
307
|
+
try {
|
|
308
|
+
globalDispatch.call(globalThis, event);
|
|
309
|
+
} catch {
|
|
310
|
+
// Ignore app listener failures.
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function normalizeAndroidPlatformEvent(event: string | AndroidPlatformEvent): AndroidPlatformEvent {
|
|
316
|
+
if (typeof event === 'string') {
|
|
317
|
+
try {
|
|
318
|
+
const parsed = JSON.parse(event);
|
|
319
|
+
return parsed && typeof parsed === 'object' ? parsed : { type: event };
|
|
320
|
+
} catch {
|
|
321
|
+
return { type: event };
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return event && typeof event === 'object' ? event : {};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function applyAndroidPlatformState(
|
|
328
|
+
event: AndroidPlatformEvent,
|
|
329
|
+
state?: AndroidPlatformState | null,
|
|
330
|
+
): void {
|
|
331
|
+
if (state?.locale) {
|
|
332
|
+
globalThis.__exactLocaleSnapshot = state.locale;
|
|
333
|
+
globalThis.__exactLocaleChanged?.(state.locale);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (state?.accessibility) {
|
|
337
|
+
globalThis.__exactAccessibilitySnapshot = state.accessibility;
|
|
338
|
+
globalThis.__exactAccessibilityChanged?.(state.accessibility);
|
|
339
|
+
} else if (state?.appearance) {
|
|
340
|
+
updateAppearanceState(state.appearance);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (state?.initialURL !== undefined) {
|
|
344
|
+
globalThis.__exactInitialURL = state.initialURL ?? null;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const nextState = event.state ?? state?.appState;
|
|
348
|
+
if (nextState) {
|
|
349
|
+
const previous = globalThis.__exactAppState;
|
|
350
|
+
globalThis.__exactAppState = nextState;
|
|
351
|
+
globalThis.__exactReactNativeNotifyAppState?.(nextState);
|
|
352
|
+
if (previous !== nextState) {
|
|
353
|
+
dispatchWindowEvent(new CustomEvent('appstatechange', { detail: { state: nextState } }));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (state?.dimensions) {
|
|
358
|
+
globalThis.__exactReactNativeNotifyDimensionsChange?.(state.dimensions);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (event.type === 'configuration') {
|
|
362
|
+
syncMediaQueries();
|
|
363
|
+
dispatchWindowEvent(new Event('resize'));
|
|
364
|
+
dispatchWindowEvent(new Event('orientationchange'));
|
|
365
|
+
} else if (event.type === 'memoryWarning') {
|
|
366
|
+
globalThis.__exactReactNativeNotifyMemoryWarning?.();
|
|
367
|
+
dispatchWindowEvent(new Event('memorywarning'));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (event.type === 'url' && typeof event.url === 'string' && event.url.length > 0) {
|
|
371
|
+
globalThis.__exactInitialURL ??= event.url;
|
|
372
|
+
globalThis.__exactReactNativeNotifyURL?.(event.url);
|
|
373
|
+
dispatchWindowEvent(new CustomEvent('url', { detail: { url: event.url } }));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function callNativeDialog(
|
|
378
|
+
type: 'alert' | 'confirm' | 'prompt',
|
|
379
|
+
message: string,
|
|
380
|
+
defaultValue = '',
|
|
381
|
+
): string | null | undefined {
|
|
382
|
+
if (typeof globalThis.__exactNativeDialog !== 'function') {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
return globalThis.__exactNativeDialog(type, message, defaultValue);
|
|
387
|
+
} catch (error) {
|
|
388
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
389
|
+
console.warn(`${type}() native dialog failed: ${detail}`);
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* MediaQueryList for matchMedia support
|
|
396
|
+
*/
|
|
397
|
+
export class MediaQueryList extends EventTarget {
|
|
398
|
+
readonly media: string;
|
|
399
|
+
private _matches: boolean;
|
|
400
|
+
|
|
401
|
+
/** @deprecated Use addEventListener instead */
|
|
402
|
+
onchange: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null = null;
|
|
403
|
+
|
|
404
|
+
constructor(media: string) {
|
|
405
|
+
super();
|
|
406
|
+
this.media = media;
|
|
407
|
+
this._matches = this._evaluate(media);
|
|
408
|
+
mediaQueryLists.add(this);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
get matches(): boolean {
|
|
412
|
+
return this._matches;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private _evaluate(query: string): boolean {
|
|
416
|
+
const dims = getScreenDimensions();
|
|
417
|
+
|
|
418
|
+
// Simple media query parser
|
|
419
|
+
// Supports: (min-width: Xpx), (max-width: Xpx), (orientation: portrait/landscape)
|
|
420
|
+
// and (prefers-color-scheme: dark/light)
|
|
421
|
+
|
|
422
|
+
const minWidthMatch = query.match(/\(min-width:\s*(\d+)px\)/);
|
|
423
|
+
if (minWidthMatch) {
|
|
424
|
+
return dims.width >= parseInt(minWidthMatch[1], 10);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const maxWidthMatch = query.match(/\(max-width:\s*(\d+)px\)/);
|
|
428
|
+
if (maxWidthMatch) {
|
|
429
|
+
return dims.width <= parseInt(maxWidthMatch[1], 10);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const minHeightMatch = query.match(/\(min-height:\s*(\d+)px\)/);
|
|
433
|
+
if (minHeightMatch) {
|
|
434
|
+
return dims.height >= parseInt(minHeightMatch[1], 10);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const maxHeightMatch = query.match(/\(max-height:\s*(\d+)px\)/);
|
|
438
|
+
if (maxHeightMatch) {
|
|
439
|
+
return dims.height <= parseInt(maxHeightMatch[1], 10);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const orientationMatch = query.match(/\(orientation:\s*(portrait|landscape)\)/);
|
|
443
|
+
if (orientationMatch) {
|
|
444
|
+
const isPortrait = dims.height > dims.width;
|
|
445
|
+
return orientationMatch[1] === 'portrait' ? isPortrait : !isPortrait;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const colorSchemeMatch = query.match(/\(prefers-color-scheme:\s*(dark|light)\)/);
|
|
449
|
+
if (colorSchemeMatch) {
|
|
450
|
+
return appearanceState.colorScheme === colorSchemeMatch[1];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const reducedMotionMatch = query.match(/\(prefers-reduced-motion:\s*(reduce|no-preference)\)/);
|
|
454
|
+
if (reducedMotionMatch) {
|
|
455
|
+
return reducedMotionMatch[1] === 'reduce'
|
|
456
|
+
? appearanceState.reducedMotion
|
|
457
|
+
: !appearanceState.reducedMotion;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Unknown query - return false
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** @deprecated Use addEventListener('change', ...) instead */
|
|
465
|
+
addListener(callback: (this: MediaQueryList, ev: MediaQueryListEvent) => any): void {
|
|
466
|
+
this.addEventListener('change', callback as EventListener);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** @deprecated Use removeEventListener('change', ...) instead */
|
|
470
|
+
removeListener(callback: (this: MediaQueryList, ev: MediaQueryListEvent) => any): void {
|
|
471
|
+
this.removeEventListener('change', callback as EventListener);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
_syncFromAppearance(): void {
|
|
475
|
+
const nextMatches = this._evaluate(this.media);
|
|
476
|
+
if (nextMatches === this._matches) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
this._matches = nextMatches;
|
|
481
|
+
const event = new MediaQueryListEvent('change', {
|
|
482
|
+
matches: nextMatches,
|
|
483
|
+
media: this.media,
|
|
484
|
+
});
|
|
485
|
+
this.dispatchEvent(event);
|
|
486
|
+
this.onchange?.call(this, event);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* MediaQueryListEvent for matchMedia change events
|
|
492
|
+
*/
|
|
493
|
+
export class MediaQueryListEvent extends Event {
|
|
494
|
+
readonly matches: boolean;
|
|
495
|
+
readonly media: string;
|
|
496
|
+
|
|
497
|
+
constructor(type: string, init?: { matches?: boolean; media?: string }) {
|
|
498
|
+
super(type);
|
|
499
|
+
this.matches = init?.matches ?? false;
|
|
500
|
+
this.media = init?.media ?? '';
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Window object implementation
|
|
506
|
+
*/
|
|
507
|
+
class Window extends EventTarget {
|
|
508
|
+
// Self-references
|
|
509
|
+
get window(): Window { return this; }
|
|
510
|
+
get self(): Window { return this; }
|
|
511
|
+
get globalThis(): typeof globalThis { return globalThis; }
|
|
512
|
+
get frames(): Window { return this; }
|
|
513
|
+
get parent(): Window { return this; }
|
|
514
|
+
get top(): Window { return this; }
|
|
515
|
+
|
|
516
|
+
// Length (number of frames - always 0 in native)
|
|
517
|
+
readonly length: number = 0;
|
|
518
|
+
|
|
519
|
+
// Name (empty in native)
|
|
520
|
+
name: string = '';
|
|
521
|
+
|
|
522
|
+
// Opener (null in native - no popup windows)
|
|
523
|
+
readonly opener: null = null;
|
|
524
|
+
|
|
525
|
+
// Frame element (null in native)
|
|
526
|
+
readonly frameElement: null = null;
|
|
527
|
+
|
|
528
|
+
// Closed status
|
|
529
|
+
readonly closed: boolean = false;
|
|
530
|
+
|
|
531
|
+
// Screen info
|
|
532
|
+
readonly screen: ScreenInfo = createScreen();
|
|
533
|
+
|
|
534
|
+
// Location
|
|
535
|
+
readonly location: LocationInfo = createLocation();
|
|
536
|
+
|
|
537
|
+
// Device pixel ratio
|
|
538
|
+
get devicePixelRatio(): number {
|
|
539
|
+
return getScreenDimensions().scale;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Viewport dimensions (same as screen in native fullscreen apps)
|
|
543
|
+
get innerWidth(): number {
|
|
544
|
+
return getScreenDimensions().width;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
get innerHeight(): number {
|
|
548
|
+
return getScreenDimensions().height;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
get outerWidth(): number {
|
|
552
|
+
return getScreenDimensions().width;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
get outerHeight(): number {
|
|
556
|
+
return getScreenDimensions().height;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Scroll position (always 0 in native - no document scrolling)
|
|
560
|
+
get scrollX(): number { return 0; }
|
|
561
|
+
get scrollY(): number { return 0; }
|
|
562
|
+
get pageXOffset(): number { return 0; }
|
|
563
|
+
get pageYOffset(): number { return 0; }
|
|
564
|
+
|
|
565
|
+
// Screen position (always 0)
|
|
566
|
+
get screenX(): number { return 0; }
|
|
567
|
+
get screenY(): number { return 0; }
|
|
568
|
+
get screenLeft(): number { return 0; }
|
|
569
|
+
get screenTop(): number { return 0; }
|
|
570
|
+
|
|
571
|
+
// Visual viewport (same as dimensions)
|
|
572
|
+
get visualViewport(): { width: number; height: number; scale: number; offsetLeft: number; offsetTop: number } {
|
|
573
|
+
const dims = getScreenDimensions();
|
|
574
|
+
return {
|
|
575
|
+
width: dims.width,
|
|
576
|
+
height: dims.height,
|
|
577
|
+
scale: 1,
|
|
578
|
+
offsetLeft: 0,
|
|
579
|
+
offsetTop: 0,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Is secure context (always true in native)
|
|
584
|
+
get isSecureContext(): boolean {
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Origin
|
|
589
|
+
get origin(): string {
|
|
590
|
+
return 'exact://app';
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Methods
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Match media query
|
|
597
|
+
*/
|
|
598
|
+
matchMedia(query: string): MediaQueryList {
|
|
599
|
+
return new MediaQueryList(query);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Get computed style (stub - no DOM)
|
|
604
|
+
*/
|
|
605
|
+
getComputedStyle(_element: unknown, _pseudoElt?: string | null): Record<string, string> {
|
|
606
|
+
console.warn('getComputedStyle() is not supported in Ibex runtime (no DOM)');
|
|
607
|
+
return new Proxy({}, {
|
|
608
|
+
get: () => '',
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Get selection (stub - no DOM)
|
|
614
|
+
*/
|
|
615
|
+
getSelection(): null {
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Scroll methods (no-op in native)
|
|
621
|
+
*/
|
|
622
|
+
scroll(_x?: number | ScrollToOptions, _y?: number): void {
|
|
623
|
+
// No-op - no document to scroll
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
scrollTo(_x?: number | ScrollToOptions, _y?: number): void {
|
|
627
|
+
// No-op
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
scrollBy(_x?: number | ScrollToOptions, _y?: number): void {
|
|
631
|
+
// No-op
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Focus/blur (no-op in native)
|
|
636
|
+
*/
|
|
637
|
+
focus(): void {
|
|
638
|
+
// No-op
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
blur(): void {
|
|
642
|
+
// No-op
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Print (not supported)
|
|
647
|
+
*/
|
|
648
|
+
print(): void {
|
|
649
|
+
console.warn('print() is not supported in Ibex runtime');
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Stop (no-op)
|
|
654
|
+
*/
|
|
655
|
+
stop(): void {
|
|
656
|
+
// No-op
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Open (not supported - no popup windows)
|
|
661
|
+
*/
|
|
662
|
+
open(_url?: string, _target?: string, _features?: string): null {
|
|
663
|
+
console.warn('window.open() is not supported in Ibex runtime');
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Close (not supported)
|
|
669
|
+
*/
|
|
670
|
+
close(): void {
|
|
671
|
+
console.warn('window.close() is not supported in Ibex runtime');
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Alert dialog
|
|
676
|
+
*/
|
|
677
|
+
alert(message?: any): void {
|
|
678
|
+
const nativeResult = callNativeDialog('alert', String(message ?? ''));
|
|
679
|
+
if (nativeResult !== undefined) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
console.log('[Alert]', String(message ?? ''));
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Confirm dialog
|
|
687
|
+
*/
|
|
688
|
+
confirm(message?: string): boolean {
|
|
689
|
+
const nativeResult = callNativeDialog('confirm', String(message ?? ''));
|
|
690
|
+
if (nativeResult !== undefined) {
|
|
691
|
+
return nativeResult === 'true';
|
|
692
|
+
}
|
|
693
|
+
console.warn('confirm() is not fully supported in Ibex runtime');
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Prompt dialog
|
|
699
|
+
*/
|
|
700
|
+
prompt(message?: string, defaultValue?: string): string | null {
|
|
701
|
+
const nativeResult = callNativeDialog(
|
|
702
|
+
'prompt',
|
|
703
|
+
String(message ?? ''),
|
|
704
|
+
String(defaultValue ?? ''),
|
|
705
|
+
);
|
|
706
|
+
if (nativeResult !== undefined) {
|
|
707
|
+
return nativeResult;
|
|
708
|
+
}
|
|
709
|
+
console.warn('prompt() is not fully supported in Ibex runtime');
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Post message (for compatibility)
|
|
715
|
+
*/
|
|
716
|
+
postMessage(message: any, _targetOrigin?: string, _transfer?: Transferable[]): void {
|
|
717
|
+
// Dispatch message event to self
|
|
718
|
+
const event = new MessageEvent('message', {
|
|
719
|
+
data: message,
|
|
720
|
+
origin: this.origin,
|
|
721
|
+
source: this,
|
|
722
|
+
});
|
|
723
|
+
this.dispatchEvent(event);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Request animation frame - forward to global
|
|
728
|
+
*/
|
|
729
|
+
requestAnimationFrame(callback: FrameRequestCallback): number {
|
|
730
|
+
return (globalThis as any).requestAnimationFrame(callback);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Cancel animation frame - forward to global
|
|
735
|
+
*/
|
|
736
|
+
cancelAnimationFrame(handle: number): void {
|
|
737
|
+
return (globalThis as any).cancelAnimationFrame(handle);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Request idle callback - forward to global
|
|
742
|
+
*/
|
|
743
|
+
requestIdleCallback(callback: IdleRequestCallback, options?: IdleRequestOptions): number {
|
|
744
|
+
return (globalThis as any).requestIdleCallback(callback, options);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Cancel idle callback - forward to global
|
|
749
|
+
*/
|
|
750
|
+
cancelIdleCallback(handle: number): void {
|
|
751
|
+
return (globalThis as any).cancelIdleCallback(handle);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Set timeout - forward to global
|
|
756
|
+
*/
|
|
757
|
+
setTimeout(handler: TimerHandler, timeout?: number, ...args: any[]): number {
|
|
758
|
+
return (globalThis as any).setTimeout(handler, timeout, ...args);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Clear timeout - forward to global
|
|
763
|
+
*/
|
|
764
|
+
clearTimeout(id?: number): void {
|
|
765
|
+
return (globalThis as any).clearTimeout(id);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Set interval - forward to global
|
|
770
|
+
*/
|
|
771
|
+
setInterval(handler: TimerHandler, timeout?: number, ...args: any[]): number {
|
|
772
|
+
return (globalThis as any).setInterval(handler, timeout, ...args);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Clear interval - forward to global
|
|
777
|
+
*/
|
|
778
|
+
clearInterval(id?: number): void {
|
|
779
|
+
return (globalThis as any).clearInterval(id);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Queue microtask - forward to global
|
|
784
|
+
*/
|
|
785
|
+
queueMicrotask(callback: VoidFunction): void {
|
|
786
|
+
return (globalThis as any).queueMicrotask(callback);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Create image bitmap (not supported - no DOM)
|
|
791
|
+
*/
|
|
792
|
+
createImageBitmap(..._args: any[]): Promise<never> {
|
|
793
|
+
return Promise.reject(new Error('createImageBitmap() is not supported in Ibex runtime'));
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Fetch - forward to global
|
|
798
|
+
*/
|
|
799
|
+
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
|
800
|
+
return (globalThis as any).fetch(input, init);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* btoa - forward to global
|
|
805
|
+
*/
|
|
806
|
+
btoa(data: string): string {
|
|
807
|
+
return (globalThis as any).btoa(data);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* atob - forward to global
|
|
812
|
+
*/
|
|
813
|
+
atob(data: string): string {
|
|
814
|
+
return (globalThis as any).atob(data);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* structuredClone - forward to global
|
|
819
|
+
*/
|
|
820
|
+
structuredClone<T>(value: T, options?: StructuredSerializeOptions): T {
|
|
821
|
+
return (globalThis as any).structuredClone(value, options);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// String tag
|
|
825
|
+
get [Symbol.toStringTag](): string {
|
|
826
|
+
return 'Window';
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Type definitions for callbacks
|
|
831
|
+
type TimerHandler = string | Function;
|
|
832
|
+
type FrameRequestCallback = (time: DOMHighResTimeStamp) => void;
|
|
833
|
+
type IdleRequestCallback = (deadline: IdleDeadline) => void;
|
|
834
|
+
interface IdleRequestOptions {
|
|
835
|
+
timeout?: number;
|
|
836
|
+
}
|
|
837
|
+
interface IdleDeadline {
|
|
838
|
+
didTimeout: boolean;
|
|
839
|
+
timeRemaining(): DOMHighResTimeStamp;
|
|
840
|
+
}
|
|
841
|
+
type DOMHighResTimeStamp = number;
|
|
842
|
+
|
|
843
|
+
// Create window instance directly
|
|
844
|
+
// Note: This runs at module load time, but the circular dependency
|
|
845
|
+
// issue was fixed by having Navigator use inline detection functions
|
|
846
|
+
export const window = new Window();
|
|
847
|
+
|
|
848
|
+
globalThis.__exactWindowNotifyMediaChange = (next) => {
|
|
849
|
+
updateAppearanceState(next);
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
globalThis.__exactWindowNotifyResize = () => {
|
|
853
|
+
syncMediaQueries();
|
|
854
|
+
dispatchWindowEvent(new Event('resize'));
|
|
855
|
+
dispatchWindowEvent(new Event('orientationchange'));
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
globalThis.__exactAndroidDispatchPlatformEvent = (event, state) => {
|
|
859
|
+
applyAndroidPlatformState(normalizeAndroidPlatformEvent(event), state);
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
if (typeof globalThis.__exactAndroidGetPlatformState === 'function') {
|
|
863
|
+
applyAndroidPlatformState({ type: 'initial' }, globalThis.__exactAndroidGetPlatformState());
|
|
864
|
+
}
|
|
865
|
+
if (typeof globalThis.__exactAndroidDrainPlatformEvents === 'function') {
|
|
866
|
+
globalThis.__exactAndroidDrainPlatformEvents();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Also export the class for type checking
|
|
870
|
+
export { Window };
|
|
871
|
+
|
|
872
|
+
export default window;
|