@effindomv2/fui-as 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.
Files changed (137) hide show
  1. package/LICENSE.md +7 -0
  2. package/browser/src/common-harness/host-imports.ts +430 -0
  3. package/browser/src/common-harness/interop.ts +39 -0
  4. package/browser/src/common-harness/managed-harness-bitmap-host.ts +92 -0
  5. package/browser/src/common-harness/managed-harness-fetch-host.ts +201 -0
  6. package/browser/src/common-harness/managed-harness-file-host.ts +1101 -0
  7. package/browser/src/common-harness/managed-harness-file-payloads.ts +143 -0
  8. package/browser/src/common-harness/managed-harness-file-types.ts +106 -0
  9. package/browser/src/common-harness/managed-harness-session.ts +15 -0
  10. package/browser/src/common-harness/managed-harness.ts +1323 -0
  11. package/browser/src/common-harness/managed-history.ts +168 -0
  12. package/browser/src/common-harness/persisted-restore-policy.ts +50 -0
  13. package/browser/src/common-harness/persisted-ui-state-controller.ts +309 -0
  14. package/browser/src/common-harness/text-session-bridge.ts +452 -0
  15. package/browser/src/common-harness/types.ts +205 -0
  16. package/browser/src/common-harness/ui-chrome.ts +191 -0
  17. package/browser/src/common-harness/ui-imports.ts +529 -0
  18. package/browser/src/common-harness/wasm-module-cache.ts +47 -0
  19. package/browser/src/common-harness.ts +27 -0
  20. package/browser/src/file-processing-worker.ts +89 -0
  21. package/browser/src/host-events.ts +97 -0
  22. package/browser/src/host-services.ts +203 -0
  23. package/browser/src/index.ts +62 -0
  24. package/browser/src/persisted-ui-state.ts +206 -0
  25. package/browser/src/routed-harness.ts +198 -0
  26. package/browser/src/worker-bootstrap.ts +483 -0
  27. package/browser/src/worker-manager.ts +230 -0
  28. package/browser/src/worker-types.ts +50 -0
  29. package/package.json +89 -0
  30. package/scripts/build-demo-as.sh +91 -0
  31. package/scripts/build.sh +325 -0
  32. package/scripts/generate-host-events.ts +175 -0
  33. package/scripts/generate-host-services.ts +157 -0
  34. package/src/Fui.ts +205 -0
  35. package/src/FuiExports.ts +55 -0
  36. package/src/FuiPrimitives.ts +15 -0
  37. package/src/FuiWorker.ts +3 -0
  38. package/src/FuiWorkerExports.ts +6 -0
  39. package/src/bindings/ui.ts +531 -0
  40. package/src/color.ts +86 -0
  41. package/src/controls/AntiSelectionArea.ts +23 -0
  42. package/src/controls/Button.ts +750 -0
  43. package/src/controls/Checkbox.ts +181 -0
  44. package/src/controls/ContextMenu.ts +885 -0
  45. package/src/controls/ControlTemplateSet.ts +37 -0
  46. package/src/controls/Dialog.ts +355 -0
  47. package/src/controls/Dropdown.ts +856 -0
  48. package/src/controls/Form.ts +110 -0
  49. package/src/controls/NavLink.ts +211 -0
  50. package/src/controls/Popup.ts +129 -0
  51. package/src/controls/ProgressBar.ts +180 -0
  52. package/src/controls/RadioButton.ts +135 -0
  53. package/src/controls/RadioGroup.ts +244 -0
  54. package/src/controls/SelectionArea.ts +75 -0
  55. package/src/controls/Slider.ts +471 -0
  56. package/src/controls/Switch.ts +132 -0
  57. package/src/controls/TextArea.ts +20 -0
  58. package/src/controls/TextInput.ts +7 -0
  59. package/src/controls/index.ts +18 -0
  60. package/src/controls/internal/ButtonPresenter.ts +95 -0
  61. package/src/controls/internal/CheckboxIndicatorPresenter.ts +93 -0
  62. package/src/controls/internal/DropdownChevronPresenter.ts +67 -0
  63. package/src/controls/internal/DropdownFieldPresenter.ts +110 -0
  64. package/src/controls/internal/DropdownOptionRowPresenter.ts +82 -0
  65. package/src/controls/internal/PopupPresenter.ts +198 -0
  66. package/src/controls/internal/PressableIndicatorPresenter.ts +32 -0
  67. package/src/controls/internal/PressableLabeledControl.ts +221 -0
  68. package/src/controls/internal/RadioIndicatorPresenter.ts +73 -0
  69. package/src/controls/internal/SliderPresenter.ts +157 -0
  70. package/src/controls/internal/SwitchIndicatorPresenter.ts +72 -0
  71. package/src/controls/internal/TextInputCore.ts +695 -0
  72. package/src/controls/internal/TextInputPresenter.ts +72 -0
  73. package/src/controls/templating.ts +54 -0
  74. package/src/core/Action.ts +94 -0
  75. package/src/core/Actions.ts +37 -0
  76. package/src/core/Animation.ts +412 -0
  77. package/src/core/Application.ts +328 -0
  78. package/src/core/Assets.ts +264 -0
  79. package/src/core/AttachedProperties.ts +32 -0
  80. package/src/core/Bitmap.ts +70 -0
  81. package/src/core/BoundCallback.ts +104 -0
  82. package/src/core/Callbacks.ts +17 -0
  83. package/src/core/ContextMenuManager.ts +466 -0
  84. package/src/core/DebugApi.ts +30 -0
  85. package/src/core/Disposable.ts +10 -0
  86. package/src/core/DragDropManager.ts +179 -0
  87. package/src/core/DragGesture.ts +184 -0
  88. package/src/core/DynamicAssetIds.ts +24 -0
  89. package/src/core/Errors.ts +48 -0
  90. package/src/core/EventRouter.ts +408 -0
  91. package/src/core/ExternalDropManager.ts +122 -0
  92. package/src/core/Fetch.ts +264 -0
  93. package/src/core/FetchFfi.ts +15 -0
  94. package/src/core/File.ts +1002 -0
  95. package/src/core/FocusAdornerManager.ts +263 -0
  96. package/src/core/FocusVisibility.ts +36 -0
  97. package/src/core/FrameScheduler.ts +28 -0
  98. package/src/core/KeyboardScroll.ts +161 -0
  99. package/src/core/KeyboardScrollTracker.ts +386 -0
  100. package/src/core/Logger.ts +80 -0
  101. package/src/core/Navigation.ts +13 -0
  102. package/src/core/Node.ts +1708 -0
  103. package/src/core/PersistedState.ts +102 -0
  104. package/src/core/PersistedUiState.ts +142 -0
  105. package/src/core/Platform.ts +219 -0
  106. package/src/core/Signal.ts +89 -0
  107. package/src/core/Theme.ts +365 -0
  108. package/src/core/Timers.ts +129 -0
  109. package/src/core/ToolTip.ts +122 -0
  110. package/src/core/ToolTipManager.ts +459 -0
  111. package/src/core/Transitions.ts +34 -0
  112. package/src/core/Typography.ts +204 -0
  113. package/src/core/Worker.ts +196 -0
  114. package/src/core/bind.ts +37 -0
  115. package/src/core/event_exports.ts +596 -0
  116. package/src/core/ffi.ts +728 -0
  117. package/src/host-services/runtime.ts +25 -0
  118. package/src/nodes/FlexBox.ts +789 -0
  119. package/src/nodes/GradientStop.ts +9 -0
  120. package/src/nodes/Grid.ts +183 -0
  121. package/src/nodes/Image.ts +189 -0
  122. package/src/nodes/Portal.ts +14 -0
  123. package/src/nodes/RichText.ts +312 -0
  124. package/src/nodes/ScrollBar.ts +570 -0
  125. package/src/nodes/ScrollBox.ts +415 -0
  126. package/src/nodes/ScrollState.ts +10 -0
  127. package/src/nodes/ScrollView.ts +511 -0
  128. package/src/nodes/Svg.ts +142 -0
  129. package/src/nodes/Text.ts +145 -0
  130. package/src/nodes/TextCore.ts +558 -0
  131. package/src/nodes/VirtualList.ts +431 -0
  132. package/src/nodes/helpers.ts +25 -0
  133. package/src/nodes/index.ts +14 -0
  134. package/src/tsconfig.json +7 -0
  135. package/src/worker/Worker.ts +169 -0
  136. package/src/worker/WorkerJob.ts +65 -0
  137. package/src/worker/ffi.ts +23 -0
@@ -0,0 +1,596 @@
1
+ import { EventRouter } from "./EventRouter";
2
+ import { ContextMenuManager } from "./ContextMenuManager";
3
+ import { handleFetchComplete, handleFetchError } from "./Fetch";
4
+ import { handleKeyboardScrollFallback } from "./KeyboardScroll";
5
+ import { KeyEventType, PointerEventType } from "./ffi";
6
+ import {
7
+ BrowserFile,
8
+ BrowserFileWriter,
9
+ createBrowserFileWriter,
10
+ FileCapabilities,
11
+ FileReadChunk,
12
+ FileSaveMode,
13
+ FileSaveResult,
14
+ handleFileWorkerProcessChunk,
15
+ handleFileWorkerProcessComplete,
16
+ handleFileWorkerProcessError,
17
+ handleFileWorkerProcessProgress,
18
+ FileWriteProgress,
19
+ handleFilePickResult,
20
+ handleFileReadChunkResult,
21
+ handleFileSaveResult,
22
+ handleFileWriterCreated,
23
+ handleFileWriterFinished,
24
+ handleFileWriterProgress,
25
+ registerBrowserFile,
26
+ } from "./File";
27
+ import { handleRouteChanged } from "./Navigation";
28
+ import { handleSystemDarkModeChanged } from "./Theme";
29
+ import { handleTimer } from "./Timers";
30
+ import { handleWorkerComplete, handleWorkerError, handleWorkerProgress } from "./Worker";
31
+ import { tickAnimations } from "./Animation";
32
+ import { ExternalDragEventType } from "./ExternalDropManager";
33
+ import {
34
+ describeHandle,
35
+ describeKeyEventType,
36
+ describePointerEventType,
37
+ error,
38
+ log,
39
+ warn,
40
+ } from "./Logger";
41
+ import { Signal } from "./Signal";
42
+ import { DragDropEffects, ExternalDropItemInfo, ExternalDropItemKind } from "./Node";
43
+
44
+ const KEY_BUFFER = new Uint8Array(256);
45
+ const TEXT_BUFFER = new Uint8Array(1024 * 1024);
46
+ let focusedHandle: u64 = 0;
47
+ export const viewportWidthSignal = new Signal<f32>(0.0);
48
+ export const viewportHeightSignal = new Signal<f32>(0.0);
49
+ export const frameTimeSignal = new Signal<f64>(0.0);
50
+
51
+ export function __fui_key_buffer(): usize {
52
+ return KEY_BUFFER.dataStart;
53
+ }
54
+
55
+ export function __fui_text_buffer(): usize {
56
+ return TEXT_BUFFER.dataStart;
57
+ }
58
+
59
+ export function __fui_text_buffer_size(): u32 {
60
+ return <u32>TEXT_BUFFER.length;
61
+ }
62
+
63
+ export function __fui_on_pointer_event(eventType: u32, handle: u64, x: f32, y: f32, modifiers: u32 = 0): void {
64
+ log(
65
+ "Event",
66
+ "pointer type=" + describePointerEventType(<PointerEventType>eventType) +
67
+ " handle=" + describeHandle(handle) +
68
+ " x=" + x.toString() +
69
+ " y=" + y.toString() +
70
+ " modifiers=" + modifiers.toString(),
71
+ );
72
+ ContextMenuManager.trackPointerEvent(<PointerEventType>eventType, handle);
73
+ EventRouter.dispatchPointerEvent(handle, <PointerEventType>eventType, x, y, modifiers);
74
+ }
75
+
76
+ export function __fui_on_viewport_changed(w: f32, h: f32): void {
77
+ log("Event", "viewport width=" + w.toString() + " height=" + h.toString());
78
+ viewportWidthSignal.value = w;
79
+ viewportHeightSignal.value = h;
80
+ }
81
+
82
+ export function __fui_on_frame(timestampMs: f64): void {
83
+ frameTimeSignal.value = timestampMs;
84
+ tickAnimations(timestampMs);
85
+ }
86
+
87
+ export function __fui_on_timer(timerId: u32): void {
88
+ log("Event", "timer fired id=" + timerId.toString());
89
+ handleTimer(timerId);
90
+ }
91
+
92
+ export function __fui_on_worker_progress(workerId: u32, textPtr: usize, textLen: u32): void {
93
+ const progress = String.UTF8.decodeUnsafe(textPtr, <usize>textLen, false);
94
+ log("Event", "worker progress id=" + workerId.toString() + " value=" + progress);
95
+ handleWorkerProgress(workerId, progress);
96
+ }
97
+
98
+ export function __fui_on_worker_complete(workerId: u32, textPtr: usize, textLen: u32): void {
99
+ const result = String.UTF8.decodeUnsafe(textPtr, <usize>textLen, false);
100
+ log("Event", "worker complete id=" + workerId.toString() + " value=" + result);
101
+ handleWorkerComplete(workerId, result);
102
+ }
103
+
104
+ export function __fui_on_worker_error(workerId: u32, textPtr: usize, textLen: u32): void {
105
+ const message = String.UTF8.decodeUnsafe(textPtr, <usize>textLen, false);
106
+ log("Event", "worker error id=" + workerId.toString() + " value=" + message);
107
+ handleWorkerError(workerId, message);
108
+ }
109
+
110
+ export function __fui_on_fetch_complete(
111
+ requestId: u32,
112
+ ok: bool,
113
+ status: i32,
114
+ payloadPtr: usize,
115
+ payloadLen: u32,
116
+ ): void {
117
+ const payload = readTextPartsPayload(payloadPtr, payloadLen);
118
+ handleFetchComplete(
119
+ requestId,
120
+ ok,
121
+ status,
122
+ payload.length > 0 ? unchecked(payload[0]) : "",
123
+ payload.length > 1 ? unchecked(payload[1]) : "",
124
+ );
125
+ }
126
+
127
+ export function __fui_on_fetch_error(requestId: u32, payloadPtr: usize, payloadLen: u32): void {
128
+ handleFetchError(requestId, readHostMessage(payloadPtr, payloadLen));
129
+ }
130
+
131
+ function readHostMessage(payloadPtr: usize, payloadLen: u32): string | null {
132
+ if (payloadPtr == 0 || payloadLen == 0) {
133
+ return null;
134
+ }
135
+ return String.UTF8.decodeUnsafe(payloadPtr, <usize>payloadLen, false);
136
+ }
137
+
138
+ function readBrowserFiles(payloadPtr: usize, payloadLen: u32): Array<BrowserFile> {
139
+ const files = new Array<BrowserFile>();
140
+ if (payloadPtr == 0 || payloadLen == 0 || payloadLen < sizeof<u32>()) {
141
+ return files;
142
+ }
143
+ const end = payloadPtr + <usize>payloadLen;
144
+ let cursor = payloadPtr;
145
+ const count = load<u32>(cursor);
146
+ cursor += sizeof<u32>();
147
+ for (let index: u32 = 0; index < count; index += 1) {
148
+ if (cursor + sizeof<u32>() > end) {
149
+ warn("File", "Truncated file id length at index " + index.toString() + ".");
150
+ return files;
151
+ }
152
+ const idLen = load<u32>(cursor);
153
+ cursor += sizeof<u32>();
154
+ if (cursor + <usize>idLen > end) {
155
+ warn("File", "Truncated file id at index " + index.toString() + ".");
156
+ return files;
157
+ }
158
+ const id = idLen > 0 ? String.UTF8.decodeUnsafe(cursor, <usize>idLen, false) : "";
159
+ cursor += <usize>idLen;
160
+
161
+ if (cursor + sizeof<u64>() + sizeof<u64>() > end) {
162
+ warn("File", "Truncated file size metadata at index " + index.toString() + ".");
163
+ return files;
164
+ }
165
+ const sizeBytes = load<u64>(cursor);
166
+ cursor += sizeof<u64>();
167
+ const lastModifiedMs = load<u64>(cursor);
168
+ cursor += sizeof<u64>();
169
+
170
+ if (cursor + sizeof<u32>() > end) {
171
+ warn("File", "Truncated file name length at index " + index.toString() + ".");
172
+ return files;
173
+ }
174
+ const nameLen = load<u32>(cursor);
175
+ cursor += sizeof<u32>();
176
+ if (cursor + <usize>nameLen > end) {
177
+ warn("File", "Truncated file name at index " + index.toString() + ".");
178
+ return files;
179
+ }
180
+ const name = nameLen > 0 ? String.UTF8.decodeUnsafe(cursor, <usize>nameLen, false) : "";
181
+ cursor += <usize>nameLen;
182
+
183
+ if (cursor + sizeof<u32>() > end) {
184
+ warn("File", "Truncated file MIME length at index " + index.toString() + ".");
185
+ return files;
186
+ }
187
+ const mimeLen = load<u32>(cursor);
188
+ cursor += sizeof<u32>();
189
+ if (cursor + <usize>mimeLen > end) {
190
+ warn("File", "Truncated file MIME at index " + index.toString() + ".");
191
+ return files;
192
+ }
193
+ const mimeType = mimeLen > 0 ? String.UTF8.decodeUnsafe(cursor, <usize>mimeLen, false) : null;
194
+ cursor += <usize>mimeLen;
195
+ files.push(registerBrowserFile(id, name, mimeType, sizeBytes, lastModifiedMs));
196
+ }
197
+ return files;
198
+ }
199
+
200
+ function readWriterPayload(payloadPtr: usize, payloadLen: u32): Array<string> {
201
+ const values = new Array<string>();
202
+ if (payloadPtr == 0 || payloadLen < sizeof<u32>()) {
203
+ return values;
204
+ }
205
+ const end = payloadPtr + <usize>payloadLen;
206
+ let cursor = payloadPtr;
207
+ const mode = load<u32>(cursor);
208
+ if (mode != <u32>FileSaveMode.Download && mode != <u32>FileSaveMode.NativePicker) {
209
+ warn("File", "Unknown file save mode " + mode.toString() + ".");
210
+ }
211
+ values.push(mode.toString());
212
+ cursor += sizeof<u32>();
213
+ while (cursor < end) {
214
+ if (cursor + sizeof<u32>() > end) {
215
+ return values;
216
+ }
217
+ const partLen = load<u32>(cursor);
218
+ cursor += sizeof<u32>();
219
+ if (cursor + <usize>partLen > end) {
220
+ return values;
221
+ }
222
+ values.push(partLen > 0 ? String.UTF8.decodeUnsafe(cursor, <usize>partLen, false) : "");
223
+ cursor += <usize>partLen;
224
+ }
225
+ return values;
226
+ }
227
+
228
+ function readTextPartsPayload(payloadPtr: usize, payloadLen: u32): Array<string> {
229
+ const values = new Array<string>();
230
+ if (payloadPtr == 0 || payloadLen < sizeof<u32>()) {
231
+ return values;
232
+ }
233
+ const end = payloadPtr + <usize>payloadLen;
234
+ let cursor = payloadPtr;
235
+ const count = load<u32>(cursor);
236
+ cursor += sizeof<u32>();
237
+ for (let index: u32 = 0; index < count; index += 1) {
238
+ if (cursor + sizeof<u32>() > end) {
239
+ return values;
240
+ }
241
+ const partLen = load<u32>(cursor);
242
+ cursor += sizeof<u32>();
243
+ if (cursor + <usize>partLen > end) {
244
+ return values;
245
+ }
246
+ values.push(partLen > 0 ? String.UTF8.decodeUnsafe(cursor, <usize>partLen, false) : "");
247
+ cursor += <usize>partLen;
248
+ }
249
+ return values;
250
+ }
251
+
252
+ export function __fui_on_file_pick_result(requestId: u32, status: u32, payloadPtr: usize, payloadLen: u32): void {
253
+ handleFilePickResult(
254
+ requestId,
255
+ status,
256
+ status == 1 ? readBrowserFiles(payloadPtr, payloadLen) : new Array<BrowserFile>(),
257
+ status == 1 ? null : readHostMessage(payloadPtr, payloadLen),
258
+ );
259
+ }
260
+
261
+ export function __fui_on_file_read_result(
262
+ requestId: u32,
263
+ status: u32,
264
+ offsetBytes: u64,
265
+ fileSizeBytes: u64,
266
+ payloadPtr: usize,
267
+ payloadLen: u32,
268
+ ): void {
269
+ let chunk: FileReadChunk | null = null;
270
+ if (status == 1) {
271
+ const bytes = new Uint8Array(payloadLen);
272
+ if (payloadLen > 0) {
273
+ memory.copy(bytes.dataStart, payloadPtr, <usize>payloadLen);
274
+ }
275
+ chunk = new FileReadChunk(offsetBytes, fileSizeBytes, bytes);
276
+ }
277
+ handleFileReadChunkResult(requestId, status, chunk, status == 1 ? null : readHostMessage(payloadPtr, payloadLen));
278
+ }
279
+
280
+ export function __fui_on_file_save_result(
281
+ requestId: u32,
282
+ status: u32,
283
+ writtenBytes: u64,
284
+ payloadPtr: usize,
285
+ payloadLen: u32,
286
+ ): void {
287
+ let result: FileSaveResult | null = null;
288
+ if (status == 1) {
289
+ const payload = readWriterPayload(payloadPtr, payloadLen);
290
+ const mode = payload.length > 0 ? <FileSaveMode>I32.parseInt(unchecked(payload[0])) : FileSaveMode.Download;
291
+ const fileName = payload.length > 1 ? unchecked(payload[1]) : "";
292
+ result = new FileSaveResult(fileName, mode, writtenBytes);
293
+ }
294
+ handleFileSaveResult(requestId, status, result, status == 1 ? null : readHostMessage(payloadPtr, payloadLen));
295
+ }
296
+
297
+ export function __fui_on_file_writer_created(requestId: u32, status: u32, payloadPtr: usize, payloadLen: u32): void {
298
+ let writer = null as BrowserFileWriter | null;
299
+ if (status == 1) {
300
+ const payload = readWriterPayload(payloadPtr, payloadLen);
301
+ const mode = payload.length > 0 ? <FileSaveMode>I32.parseInt(unchecked(payload[0])) : FileSaveMode.NativePicker;
302
+ const writerId = payload.length > 1 ? unchecked(payload[1]) : "";
303
+ const fileName = payload.length > 2 ? unchecked(payload[2]) : "";
304
+ writer = createBrowserFileWriter(writerId, fileName, mode);
305
+ }
306
+ handleFileWriterCreated(requestId, status, writer, status == 1 ? null : readHostMessage(payloadPtr, payloadLen));
307
+ }
308
+
309
+ export function __fui_on_file_write_result(
310
+ requestId: u32,
311
+ status: u32,
312
+ writtenBytes: u64,
313
+ totalWrittenBytes: u64,
314
+ payloadPtr: usize,
315
+ payloadLen: u32,
316
+ ): void {
317
+ const progress = status == 1 ? new FileWriteProgress(writtenBytes, totalWrittenBytes) : null;
318
+ handleFileWriterProgress(requestId, status, progress, status == 1 ? null : readHostMessage(payloadPtr, payloadLen));
319
+ }
320
+
321
+ export function __fui_on_file_finish_result(
322
+ requestId: u32,
323
+ status: u32,
324
+ writtenBytes: u64,
325
+ payloadPtr: usize,
326
+ payloadLen: u32,
327
+ ): void {
328
+ let result: FileSaveResult | null = null;
329
+ if (status == 1) {
330
+ const payload = readWriterPayload(payloadPtr, payloadLen);
331
+ const mode = payload.length > 0 ? <FileSaveMode>I32.parseInt(unchecked(payload[0])) : FileSaveMode.NativePicker;
332
+ const fileName = payload.length > 1 ? unchecked(payload[1]) : "";
333
+ result = new FileSaveResult(fileName, mode, writtenBytes);
334
+ }
335
+ handleFileWriterFinished(requestId, status, result, status == 1 ? null : readHostMessage(payloadPtr, payloadLen));
336
+ }
337
+
338
+ export function __fui_on_file_worker_process_progress(
339
+ requestId: u32,
340
+ processedBytes: u64,
341
+ totalBytes: u64,
342
+ payloadPtr: usize,
343
+ payloadLen: u32,
344
+ ): void {
345
+ const outputFileName = readHostMessage(payloadPtr, payloadLen);
346
+ handleFileWorkerProcessProgress(
347
+ requestId,
348
+ processedBytes,
349
+ totalBytes,
350
+ outputFileName,
351
+ );
352
+ }
353
+
354
+ export function __fui_on_file_worker_process_chunk(
355
+ requestId: u32,
356
+ offsetBytes: u64,
357
+ fileSizeBytes: u64,
358
+ payloadPtr: usize,
359
+ payloadLen: u32,
360
+ ): void {
361
+ const bytes = new Uint8Array(payloadLen);
362
+ if (payloadLen > 0) {
363
+ memory.copy(bytes.dataStart, payloadPtr, <usize>payloadLen);
364
+ }
365
+ handleFileWorkerProcessChunk(requestId, new FileReadChunk(offsetBytes, fileSizeBytes, bytes));
366
+ }
367
+
368
+ export function __fui_on_file_worker_process_complete(
369
+ requestId: u32,
370
+ processedBytes: u64,
371
+ payloadPtr: usize,
372
+ payloadLen: u32,
373
+ ): void {
374
+ const outputFileName = readHostMessage(payloadPtr, payloadLen);
375
+ handleFileWorkerProcessComplete(
376
+ requestId,
377
+ processedBytes,
378
+ outputFileName,
379
+ );
380
+ }
381
+
382
+ export function __fui_on_file_worker_process_error(requestId: u32, status: u32, payloadPtr: usize, payloadLen: u32): void {
383
+ handleFileWorkerProcessError(requestId, status, readHostMessage(payloadPtr, payloadLen));
384
+ }
385
+
386
+ export function __fui_on_system_dark_mode_changed(isDark: bool): void {
387
+ log("Event", "system dark mode changed=" + (isDark ? "true" : "false"));
388
+ handleSystemDarkModeChanged(isDark);
389
+ }
390
+
391
+ export function __fui_on_route_changed(routePtr: usize, routeLen: u32): void {
392
+ const route = String.UTF8.decodeUnsafe(routePtr, <usize>routeLen, false);
393
+ log("Event", "route changed=" + route);
394
+ handleRouteChanged(route);
395
+ }
396
+
397
+ export function __fui_on_focus_changed(handle: u64, focused: bool): void {
398
+ log(
399
+ "Event",
400
+ "focus handle=" + describeHandle(handle) + " focused=" + (focused ? "true" : "false"),
401
+ );
402
+ if (focused) {
403
+ focusedHandle = handle;
404
+ } else if (focusedHandle == handle) {
405
+ focusedHandle = 0;
406
+ }
407
+ EventRouter.dispatchFocusChanged(handle, focused);
408
+ }
409
+
410
+ export function __fui_on_key_event(eventType: u32, keyPtr: usize, keyLen: u32, modifiers: u32): bool {
411
+ const key = String.UTF8.decodeUnsafe(keyPtr, <usize>keyLen, false);
412
+ log(
413
+ "Event",
414
+ "key type=" + describeKeyEventType(<KeyEventType>eventType) +
415
+ " key=" + key +
416
+ " modifiers=" + modifiers.toString(),
417
+ );
418
+ if (EventRouter.dispatchGlobalKeyEvent(<KeyEventType>eventType, key, modifiers)) {
419
+ return true;
420
+ }
421
+ if (focusedHandle == 0) {
422
+ if (<KeyEventType>eventType == KeyEventType.Down) {
423
+ return handleKeyboardScrollFallback(key, modifiers);
424
+ }
425
+ return false;
426
+ }
427
+ if (EventRouter.dispatchKeyEvent(focusedHandle, <KeyEventType>eventType, key, modifiers)) {
428
+ return true;
429
+ }
430
+ if (<KeyEventType>eventType == KeyEventType.Down) {
431
+ return handleKeyboardScrollFallback(key, modifiers);
432
+ }
433
+ return false;
434
+ }
435
+
436
+ export function __fui_on_context_menu(handle: u64, x: f32, y: f32): void {
437
+ ContextMenuManager.showForCurrentSelection(handle, x, y);
438
+ }
439
+
440
+ export function __fui_hide_active_context_menu(): void {
441
+ ContextMenuManager.hideActiveMenu();
442
+ }
443
+
444
+ export function __fui_on_scroll(
445
+ handle: u64,
446
+ offsetX: f32,
447
+ offsetY: f32,
448
+ contentWidth: f32,
449
+ contentHeight: f32,
450
+ viewportWidth: f32,
451
+ viewportHeight: f32,
452
+ ): void {
453
+ log(
454
+ "Event",
455
+ "scroll handle=" + describeHandle(handle) +
456
+ " offsetX=" + offsetX.toString() +
457
+ " offsetY=" + offsetY.toString(),
458
+ );
459
+ EventRouter.dispatchScroll(handle, offsetX, offsetY, contentWidth, contentHeight, viewportWidth, viewportHeight);
460
+ }
461
+
462
+ export function __fui_on_text_changed(handle: u64, textPtr: usize, textLen: u32): void {
463
+ const text = String.UTF8.decodeUnsafe(textPtr, <usize>textLen, false);
464
+ log("Event", "text-changed handle=" + describeHandle(handle) + " text=" + text);
465
+ EventRouter.dispatchTextChanged(handle, text);
466
+ }
467
+
468
+ export function __fui_on_text_replaced(handle: u64, start: u32, end: u32, textPtr: usize, textLen: u32): void {
469
+ const text = String.UTF8.decodeUnsafe(textPtr, <usize>textLen, false);
470
+ log(
471
+ "Event",
472
+ "text-replaced handle=" + describeHandle(handle) +
473
+ " start=" + start.toString() +
474
+ " end=" + end.toString() +
475
+ " text=" + text,
476
+ );
477
+ EventRouter.dispatchTextReplaced(handle, start, end, text);
478
+ }
479
+
480
+ export function __fui_on_selection_changed(handle: u64, start: u32, end: u32): void {
481
+ log(
482
+ "Event",
483
+ "selection-changed handle=" + describeHandle(handle) +
484
+ " start=" + start.toString() +
485
+ " end=" + end.toString(),
486
+ );
487
+ EventRouter.dispatchSelectionChanged(handle, start, end);
488
+ }
489
+
490
+ export function __fui_on_cross_selection_changed(handle: u64, textPtr: usize, textLen: u32): void {
491
+ const text = String.UTF8.decodeUnsafe(textPtr, <usize>textLen, false);
492
+ log("Event", "cross-selection handle=" + describeHandle(handle) + " text=" + text);
493
+ ContextMenuManager.handleSelectionChanged(text);
494
+ EventRouter.dispatchCrossSelectionChanged(handle, text);
495
+ }
496
+
497
+ function readExternalDropItems(payloadPtr: usize, payloadLen: u32): Array<ExternalDropItemInfo> {
498
+ const items = new Array<ExternalDropItemInfo>();
499
+ if (payloadPtr == 0 || payloadLen == 0) {
500
+ return items;
501
+ }
502
+ const end = payloadPtr + <usize>payloadLen;
503
+ if (end < payloadPtr || payloadLen < 4) {
504
+ warn("ExternalDrop", "Malformed external drop payload header.");
505
+ return items;
506
+ }
507
+ let cursor = payloadPtr;
508
+ const itemCount = load<u32>(cursor);
509
+ cursor += sizeof<u32>();
510
+ for (let index: u32 = 0; index < itemCount; index += 1) {
511
+ if (cursor + sizeof<u32>() + sizeof<f64>() > end) {
512
+ warn("ExternalDrop", "Truncated external drop item header at index " + index.toString() + ".");
513
+ return items;
514
+ }
515
+ const kind = <ExternalDropItemKind>load<u32>(cursor);
516
+ cursor += sizeof<u32>();
517
+ const sizeBytes = load<f64>(cursor);
518
+ cursor += sizeof<f64>();
519
+
520
+ if (cursor + sizeof<u32>() > end) {
521
+ warn("ExternalDrop", "Truncated external drop item id length at index " + index.toString() + ".");
522
+ return items;
523
+ }
524
+ const idLen = load<u32>(cursor);
525
+ cursor += sizeof<u32>();
526
+ if (cursor + <usize>idLen > end) {
527
+ warn("ExternalDrop", "Truncated external drop item id at index " + index.toString() + ".");
528
+ return items;
529
+ }
530
+ const id = idLen > 0 ? String.UTF8.decodeUnsafe(cursor, <usize>idLen, false) : "";
531
+ cursor += <usize>idLen;
532
+
533
+ if (cursor + sizeof<u32>() > end) {
534
+ warn("ExternalDrop", "Truncated external drop item name length at index " + index.toString() + ".");
535
+ return items;
536
+ }
537
+ const nameLen = load<u32>(cursor);
538
+ cursor += sizeof<u32>();
539
+ if (cursor + <usize>nameLen > end) {
540
+ warn("ExternalDrop", "Truncated external drop item name at index " + index.toString() + ".");
541
+ return items;
542
+ }
543
+ const name = nameLen > 0 ? String.UTF8.decodeUnsafe(cursor, <usize>nameLen, false) : "";
544
+ cursor += <usize>nameLen;
545
+
546
+ if (cursor + sizeof<u32>() > end) {
547
+ warn("ExternalDrop", "Truncated external drop item mime length at index " + index.toString() + ".");
548
+ return items;
549
+ }
550
+ const mimeLen = load<u32>(cursor);
551
+ cursor += sizeof<u32>();
552
+ if (cursor + <usize>mimeLen > end) {
553
+ warn("ExternalDrop", "Truncated external drop item mime at index " + index.toString() + ".");
554
+ return items;
555
+ }
556
+ const mimeType = mimeLen > 0 ? String.UTF8.decodeUnsafe(cursor, <usize>mimeLen, false) : null;
557
+ cursor += <usize>mimeLen;
558
+
559
+ const file = kind == ExternalDropItemKind.File && id.length > 0
560
+ ? registerBrowserFile(id, name, mimeType, <u64>sizeBytes, 0)
561
+ : null;
562
+ items.push(new ExternalDropItemInfo(id, kind, name, mimeType, sizeBytes, file));
563
+ }
564
+ return items;
565
+ }
566
+
567
+ export function __fui_on_external_drag_event(
568
+ eventType: u32,
569
+ handle: u64,
570
+ x: f32,
571
+ y: f32,
572
+ modifiers: u32,
573
+ payloadPtr: usize,
574
+ payloadLen: u32,
575
+ ): u32 {
576
+ const items = readExternalDropItems(payloadPtr, payloadLen);
577
+ const effect = EventRouter.dispatchExternalDropEvent(
578
+ handle,
579
+ <ExternalDragEventType>eventType,
580
+ x,
581
+ y,
582
+ modifiers,
583
+ items,
584
+ );
585
+ if (payloadPtr != 0 && payloadLen > 0 && items.length == 0) {
586
+ error("ExternalDrop", "Dropped malformed external payload for handle " + describeHandle(handle) + ".");
587
+ }
588
+ log(
589
+ "Event",
590
+ "external-drag type=" + eventType.toString() +
591
+ " handle=" + describeHandle(handle) +
592
+ " items=" + items.length.toString() +
593
+ " effect=" + (<u32>effect).toString(),
594
+ );
595
+ return <u32>effect;
596
+ }