@effindomv2/fui-as 0.1.36 → 0.1.39

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 (119) hide show
  1. package/browser/routed-app-conventions.ts +1 -1
  2. package/browser/src/common-harness.ts +1 -27
  3. package/browser/src/index.ts +19 -26
  4. package/browser/src/routed-harness.ts +1 -194
  5. package/browser/src/shared-browser.ts +6 -0
  6. package/package.json +26 -16
  7. package/scripts/build.sh +51 -47
  8. package/scripts/framework-host-services.ts +1 -1
  9. package/scripts/generate-host-events.ts +15 -241
  10. package/scripts/generate-host-services.ts +13 -261
  11. package/scripts/hostgen/assemblyscript-host-events.ts +165 -0
  12. package/scripts/hostgen/assemblyscript-host-services.ts +207 -0
  13. package/scripts/hostgen/common.ts +73 -0
  14. package/scripts/hostgen/registry.ts +159 -0
  15. package/src/Fui.ts +70 -4
  16. package/src/FuiExports.ts +13 -1
  17. package/src/bindings/ui.ts +165 -8
  18. package/src/controls/Button.ts +98 -71
  19. package/src/controls/Checkbox.ts +23 -21
  20. package/src/controls/ComboBox.ts +1201 -0
  21. package/src/controls/ContextMenu.ts +279 -165
  22. package/src/controls/Dialog.ts +91 -17
  23. package/src/controls/Dropdown.ts +101 -333
  24. package/src/controls/NavLink.ts +29 -6
  25. package/src/controls/Popup.ts +10 -5
  26. package/src/controls/ProgressBar.ts +1 -2
  27. package/src/controls/RadioButton.ts +34 -13
  28. package/src/controls/RadioGroup.ts +32 -19
  29. package/src/controls/Slider.ts +21 -23
  30. package/src/controls/Switch.ts +34 -17
  31. package/src/controls/index.ts +2 -1
  32. package/src/controls/internal/ButtonPresenter.ts +5 -2
  33. package/src/controls/internal/CheckboxIndicatorPresenter.ts +10 -10
  34. package/src/controls/internal/DropdownChevronPresenter.ts +3 -3
  35. package/src/controls/internal/DropdownFieldPresenter.ts +6 -6
  36. package/src/controls/internal/DropdownOptionRowPresenter.ts +5 -4
  37. package/src/controls/internal/PressableLabeledControl.ts +4 -7
  38. package/src/controls/internal/RadioIndicatorPresenter.ts +7 -7
  39. package/src/controls/internal/SelectablePopupList.ts +463 -0
  40. package/src/controls/internal/SliderPresenter.ts +5 -5
  41. package/src/controls/internal/SwitchIndicatorPresenter.ts +63 -17
  42. package/src/controls/internal/TextInputCore.ts +307 -41
  43. package/src/controls/internal/TextInputPresenter.ts +2 -2
  44. package/src/controls/templating.ts +10 -0
  45. package/src/core/Application.ts +19 -9
  46. package/src/core/Bitmap.ts +219 -1
  47. package/src/core/ContextMenuManager.ts +128 -44
  48. package/src/core/DebugApi.ts +17 -2
  49. package/src/core/DragGesture.ts +8 -7
  50. package/src/core/EventRouter.ts +332 -12
  51. package/src/core/Fetch.ts +15 -7
  52. package/src/core/FetchFfi.ts +1 -15
  53. package/src/core/File.ts +75 -34
  54. package/src/core/FocusAdornerManager.ts +3 -3
  55. package/src/core/FocusVisibility.ts +7 -0
  56. package/src/core/FrameScheduler.ts +126 -0
  57. package/src/core/ImageSampling.ts +43 -0
  58. package/src/core/Logger.ts +2 -0
  59. package/src/core/MobileTextSelectionToolbarManager.ts +901 -0
  60. package/src/core/Node.ts +1039 -97
  61. package/src/core/Platform.ts +16 -16
  62. package/src/core/SelectionHandleAdornerManager.ts +697 -0
  63. package/src/core/Theme.ts +34 -20
  64. package/src/core/ToolTipManager.ts +3 -3
  65. package/src/core/Typography.ts +411 -19
  66. package/src/core/Worker.ts +69 -33
  67. package/src/core/event_exports.ts +123 -5
  68. package/src/core/ffi.ts +5 -746
  69. package/src/core/generated/FetchHostAbi.ts +10 -0
  70. package/src/core/generated/FrameworkHostServices.ts +1 -1
  71. package/src/core/generated/HostAbi.ts +220 -0
  72. package/src/core/generated/UiAbi.ts +356 -0
  73. package/src/core/generated/UiEnums.ts +186 -0
  74. package/src/drawing/DrawCallback.ts +34 -0
  75. package/src/drawing/DrawContext.ts +189 -0
  76. package/src/drawing/Paint.ts +34 -0
  77. package/src/drawing/Path.ts +40 -0
  78. package/src/nodes/CustomDrawable.ts +61 -0
  79. package/src/nodes/FlexBox.ts +57 -11
  80. package/src/nodes/Grid.ts +5 -5
  81. package/src/nodes/Image.ts +20 -1
  82. package/src/nodes/RichText.ts +36 -25
  83. package/src/nodes/ScrollBar.ts +2 -1
  84. package/src/nodes/ScrollBox.ts +7 -2
  85. package/src/nodes/ScrollView.ts +17 -6
  86. package/src/nodes/Svg.ts +18 -1
  87. package/src/nodes/Text.ts +1 -1
  88. package/src/nodes/TextCore.ts +115 -20
  89. package/src/nodes/index.ts +3 -1
  90. package/src/text/DynamicTextLayout.ts +275 -0
  91. package/src/text/TextLayout.ts +308 -0
  92. package/src/worker/Worker.ts +3 -36
  93. package/src/worker/WorkerJob.ts +5 -9
  94. package/src/worker/ffi.ts +0 -6
  95. package/browser/src/common-harness/host-imports.ts +0 -447
  96. package/browser/src/common-harness/interop.ts +0 -39
  97. package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
  98. package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
  99. package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
  100. package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
  101. package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
  102. package/browser/src/common-harness/managed-harness-session.ts +0 -15
  103. package/browser/src/common-harness/managed-harness.ts +0 -1451
  104. package/browser/src/common-harness/managed-history.ts +0 -168
  105. package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
  106. package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
  107. package/browser/src/common-harness/text-session-bridge.ts +0 -452
  108. package/browser/src/common-harness/types.ts +0 -205
  109. package/browser/src/common-harness/ui-chrome.ts +0 -206
  110. package/browser/src/common-harness/ui-imports.ts +0 -553
  111. package/browser/src/common-harness/wasm-module-cache.ts +0 -47
  112. package/browser/src/file-processing-worker.ts +0 -89
  113. package/browser/src/host-events.ts +0 -118
  114. package/browser/src/host-services.ts +0 -483
  115. package/browser/src/persisted-ui-state.ts +0 -206
  116. package/browser/src/routed-app-conventions.ts +0 -292
  117. package/browser/src/worker-bootstrap.ts +0 -916
  118. package/browser/src/worker-manager.ts +0 -234
  119. package/browser/src/worker-types.ts +0 -68
@@ -1,206 +0,0 @@
1
- export const PERSISTED_UI_STATE_DB_NAME = 'effindom-ui-state';
2
- export const PERSISTED_UI_STATE_DB_VERSION = 1;
3
- export const PERSISTED_UI_STATE_SNAPSHOT_SCHEMA_VERSION = 1;
4
- export const PERSISTED_UI_STATE_SNAPSHOTS_STORE = 'snapshots';
5
- export const PERSISTED_UI_STATE_ROUTE_HEADS_STORE = 'route-heads';
6
- export const PERSISTED_SCROLL_ENTRY_KIND = 'scroll-position';
7
- export const PERSISTED_SCROLL_ENTRY_VERSION = 1;
8
-
9
- const SNAPSHOT_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;
10
-
11
- export interface PersistedSnapshotEntry {
12
- readonly nodeId: string;
13
- readonly kind: string;
14
- readonly version: number;
15
- readonly payload: unknown;
16
- }
17
-
18
- export interface PersistedScrollPayload {
19
- readonly x: number;
20
- readonly y: number;
21
- }
22
-
23
- export interface PersistedSnapshotRecord {
24
- readonly snapshotId: string;
25
- readonly appKey: string;
26
- readonly routeHref: string;
27
- readonly createdAt: number;
28
- readonly lastAccessedAt: number;
29
- readonly schemaVersion: number;
30
- readonly entries: readonly PersistedSnapshotEntry[];
31
- }
32
-
33
- export interface PersistedRouteHeadRecord {
34
- readonly routeKey: string;
35
- readonly appKey: string;
36
- readonly routeHref: string;
37
- readonly snapshotId: string;
38
- readonly updatedAt: number;
39
- }
40
-
41
- export interface PersistedUiStateStore {
42
- saveSnapshot(record: PersistedSnapshotRecord): Promise<void>;
43
- loadSnapshot(snapshotId: string): Promise<PersistedSnapshotRecord | null>;
44
- loadRouteHead(appKey: string, routeHref: string): Promise<PersistedRouteHeadRecord | null>;
45
- deleteSnapshot(snapshotId: string): Promise<void>;
46
- collectGarbage(now: number): Promise<void>;
47
- }
48
-
49
- export function buildPersistedUiRouteKey(appKey: string, routeHref: string): string {
50
- return `${appKey}\n${routeHref}`;
51
- }
52
-
53
- function requestToPromise<T>(request: IDBRequest<T>): Promise<T> {
54
- return new Promise<T>((resolve, reject) => {
55
- request.onsuccess = () => resolve(request.result);
56
- request.onerror = () => reject(request.error ?? new Error('IndexedDB request failed.'));
57
- });
58
- }
59
-
60
- function transactionToPromise(transaction: IDBTransaction): Promise<void> {
61
- return new Promise<void>((resolve, reject) => {
62
- transaction.oncomplete = () => resolve();
63
- transaction.onabort = () => reject(transaction.error ?? new Error('IndexedDB transaction aborted.'));
64
- transaction.onerror = () => reject(transaction.error ?? new Error('IndexedDB transaction failed.'));
65
- });
66
- }
67
-
68
- function openDatabase(factory: IDBFactory): Promise<IDBDatabase> {
69
- return new Promise<IDBDatabase>((resolve, reject) => {
70
- const request = factory.open(PERSISTED_UI_STATE_DB_NAME, PERSISTED_UI_STATE_DB_VERSION);
71
- request.onupgradeneeded = () => {
72
- const database = request.result;
73
- let snapshots = database.objectStoreNames.contains(PERSISTED_UI_STATE_SNAPSHOTS_STORE)
74
- ? request.transaction?.objectStore(PERSISTED_UI_STATE_SNAPSHOTS_STORE) ?? null
75
- : null;
76
- if (snapshots === null) {
77
- snapshots = database.createObjectStore(PERSISTED_UI_STATE_SNAPSHOTS_STORE, {
78
- keyPath: 'snapshotId',
79
- });
80
- }
81
- if (!snapshots.indexNames.contains('byLastAccessedAt')) {
82
- snapshots.createIndex('byLastAccessedAt', 'lastAccessedAt');
83
- }
84
-
85
- let routeHeads = database.objectStoreNames.contains(PERSISTED_UI_STATE_ROUTE_HEADS_STORE)
86
- ? request.transaction?.objectStore(PERSISTED_UI_STATE_ROUTE_HEADS_STORE) ?? null
87
- : null;
88
- if (routeHeads === null) {
89
- routeHeads = database.createObjectStore(PERSISTED_UI_STATE_ROUTE_HEADS_STORE, {
90
- keyPath: 'routeKey',
91
- });
92
- }
93
- if (!routeHeads.indexNames.contains('byUpdatedAt')) {
94
- routeHeads.createIndex('byUpdatedAt', 'updatedAt');
95
- }
96
- if (!routeHeads.indexNames.contains('bySnapshotId')) {
97
- routeHeads.createIndex('bySnapshotId', 'snapshotId');
98
- }
99
- };
100
- request.onsuccess = () => resolve(request.result);
101
- request.onerror = () => reject(request.error ?? new Error('Failed to open IndexedDB database.'));
102
- });
103
- }
104
-
105
- class IndexedDbPersistedUiStateStore implements PersistedUiStateStore {
106
- private readonly databasePromise: Promise<IDBDatabase>;
107
-
108
- constructor(factory: IDBFactory) {
109
- this.databasePromise = openDatabase(factory);
110
- }
111
-
112
- async saveSnapshot(record: PersistedSnapshotRecord): Promise<void> {
113
- const database = await this.databasePromise;
114
- const transaction = database.transaction(
115
- [PERSISTED_UI_STATE_SNAPSHOTS_STORE, PERSISTED_UI_STATE_ROUTE_HEADS_STORE],
116
- 'readwrite',
117
- );
118
- const snapshots = transaction.objectStore(PERSISTED_UI_STATE_SNAPSHOTS_STORE);
119
- const routeHeads = transaction.objectStore(PERSISTED_UI_STATE_ROUTE_HEADS_STORE);
120
- snapshots.put(record);
121
- const updatedAt = Math.max(record.createdAt, record.lastAccessedAt);
122
- routeHeads.put({
123
- routeKey: buildPersistedUiRouteKey(record.appKey, record.routeHref),
124
- appKey: record.appKey,
125
- routeHref: record.routeHref,
126
- snapshotId: record.snapshotId,
127
- updatedAt,
128
- } satisfies PersistedRouteHeadRecord);
129
- await transactionToPromise(transaction);
130
- }
131
-
132
- async loadSnapshot(snapshotId: string): Promise<PersistedSnapshotRecord | null> {
133
- const database = await this.databasePromise;
134
- const loadTransaction = database.transaction(PERSISTED_UI_STATE_SNAPSHOTS_STORE, 'readonly');
135
- const snapshots = loadTransaction.objectStore(PERSISTED_UI_STATE_SNAPSHOTS_STORE);
136
- const existing = await requestToPromise<PersistedSnapshotRecord | undefined>(snapshots.get(snapshotId));
137
- await transactionToPromise(loadTransaction);
138
- if (existing === undefined) {
139
- return null;
140
- }
141
-
142
- const updatedRecord: PersistedSnapshotRecord = {
143
- ...existing,
144
- lastAccessedAt: Date.now(),
145
- };
146
- const saveTransaction = database.transaction(PERSISTED_UI_STATE_SNAPSHOTS_STORE, 'readwrite');
147
- saveTransaction.objectStore(PERSISTED_UI_STATE_SNAPSHOTS_STORE).put(updatedRecord);
148
- await transactionToPromise(saveTransaction);
149
- return updatedRecord;
150
- }
151
-
152
- async loadRouteHead(appKey: string, routeHref: string): Promise<PersistedRouteHeadRecord | null> {
153
- const database = await this.databasePromise;
154
- const transaction = database.transaction(PERSISTED_UI_STATE_ROUTE_HEADS_STORE, 'readonly');
155
- const routeHeads = transaction.objectStore(PERSISTED_UI_STATE_ROUTE_HEADS_STORE);
156
- const routeHead = await requestToPromise<PersistedRouteHeadRecord | undefined>(
157
- routeHeads.get(buildPersistedUiRouteKey(appKey, routeHref)),
158
- );
159
- await transactionToPromise(transaction);
160
- return routeHead ?? null;
161
- }
162
-
163
- async deleteSnapshot(snapshotId: string): Promise<void> {
164
- const database = await this.databasePromise;
165
- const transaction = database.transaction(PERSISTED_UI_STATE_SNAPSHOTS_STORE, 'readwrite');
166
- transaction.objectStore(PERSISTED_UI_STATE_SNAPSHOTS_STORE).delete(snapshotId);
167
- await transactionToPromise(transaction);
168
- }
169
-
170
- async collectGarbage(now: number): Promise<void> {
171
- const database = await this.databasePromise;
172
- const transaction = database.transaction(
173
- [PERSISTED_UI_STATE_SNAPSHOTS_STORE, PERSISTED_UI_STATE_ROUTE_HEADS_STORE],
174
- 'readwrite',
175
- );
176
- const snapshots = transaction.objectStore(PERSISTED_UI_STATE_SNAPSHOTS_STORE);
177
- const routeHeads = transaction.objectStore(PERSISTED_UI_STATE_ROUTE_HEADS_STORE);
178
- const snapshotRecords = await requestToPromise<Array<PersistedSnapshotRecord>>(snapshots.getAll());
179
- const routeHeadRecords = await requestToPromise<Array<PersistedRouteHeadRecord>>(routeHeads.getAll());
180
- const retainedSnapshotIds = new Set(routeHeadRecords.map((record) => record.snapshotId));
181
- const knownSnapshotIds = new Set(snapshotRecords.map((record) => record.snapshotId));
182
- const pruneBefore = now - SNAPSHOT_MAX_AGE_MS;
183
-
184
- for (const routeHead of routeHeadRecords) {
185
- if (!knownSnapshotIds.has(routeHead.snapshotId)) {
186
- routeHeads.delete(routeHead.routeKey);
187
- }
188
- }
189
-
190
- for (const snapshot of snapshotRecords) {
191
- if (snapshot.lastAccessedAt >= pruneBefore || retainedSnapshotIds.has(snapshot.snapshotId)) {
192
- continue;
193
- }
194
- snapshots.delete(snapshot.snapshotId);
195
- }
196
-
197
- await transactionToPromise(transaction);
198
- }
199
- }
200
-
201
- export function createPersistedUiStateStore(): PersistedUiStateStore | null {
202
- if (typeof globalThis.indexedDB === 'undefined') {
203
- return null;
204
- }
205
- return new IndexedDbPersistedUiStateStore(globalThis.indexedDB);
206
- }
@@ -1,292 +0,0 @@
1
- export class RoutedAppHeadTag {
2
- kind: string;
3
- name: string;
4
- content: string;
5
-
6
- constructor(kind: string = "", name: string = "", content: string = "") {
7
- this.kind = kind;
8
- this.name = name;
9
- this.content = content;
10
- }
11
- }
12
-
13
- export class RoutedAppRoute {
14
- key: string;
15
- title: string;
16
- headTags: Array<RoutedAppHeadTag>;
17
- entrypoint: string;
18
- wasmFile: string;
19
- shellDir: string;
20
- sourceRoutePath: string;
21
- publishedRoutePath: string;
22
-
23
- constructor(
24
- key: string = "",
25
- title: string = "",
26
- headTags: Array<RoutedAppHeadTag> = new Array<RoutedAppHeadTag>(),
27
- entrypoint: string = "",
28
- wasmFile: string = "",
29
- shellDir: string = "",
30
- sourceRoutePath: string = "",
31
- publishedRoutePath: string = "",
32
- ) {
33
- this.key = key;
34
- this.title = title;
35
- this.headTags = headTags;
36
- this.entrypoint = entrypoint;
37
- this.wasmFile = wasmFile;
38
- this.shellDir = shellDir;
39
- this.sourceRoutePath = sourceRoutePath;
40
- this.publishedRoutePath = publishedRoutePath;
41
- }
42
- }
43
-
44
- export class RoutedAppRouteManifest {
45
- sourceRouteBase: string;
46
- routes: Array<RoutedAppRoute>;
47
-
48
- constructor(sourceRouteBase: string = "", routes: Array<RoutedAppRoute> = new Array<RoutedAppRoute>()) {
49
- this.sourceRouteBase = sourceRouteBase;
50
- this.routes = routes;
51
- }
52
- }
53
-
54
- export class ResolvedRoutedAppRoute {
55
- key: string;
56
- title: string;
57
- headTags: Array<RoutedAppHeadTag>;
58
- shellDir: string;
59
- wasmFile: string;
60
- entrypoint: string;
61
- sourceRoutePath: string;
62
- publishedRoutePath: string;
63
-
64
- constructor(
65
- key: string = "",
66
- title: string = "",
67
- headTags: Array<RoutedAppHeadTag> = new Array<RoutedAppHeadTag>(),
68
- shellDir: string = "",
69
- wasmFile: string = "",
70
- entrypoint: string = "",
71
- sourceRoutePath: string = "",
72
- publishedRoutePath: string = "",
73
- ) {
74
- this.key = key;
75
- this.title = title;
76
- this.headTags = headTags;
77
- this.shellDir = shellDir;
78
- this.wasmFile = wasmFile;
79
- this.entrypoint = entrypoint;
80
- this.sourceRoutePath = sourceRoutePath;
81
- this.publishedRoutePath = publishedRoutePath;
82
- }
83
- }
84
-
85
- export class ResolvedRoutedAppRouteManifest {
86
- sourceRouteBase: string;
87
- routes: Array<ResolvedRoutedAppRoute>;
88
-
89
- constructor(sourceRouteBase: string = "", routes: Array<ResolvedRoutedAppRoute> = new Array<ResolvedRoutedAppRoute>()) {
90
- this.sourceRouteBase = sourceRouteBase;
91
- this.routes = routes;
92
- }
93
- }
94
-
95
- export class RoutedHarnessRouteSpec {
96
- routePath: string;
97
- wasmPath: string;
98
- title: string;
99
-
100
- constructor(routePath: string = "", wasmPath: string = "", title: string = "") {
101
- this.routePath = routePath;
102
- this.wasmPath = wasmPath;
103
- this.title = title;
104
- }
105
- }
106
-
107
- export class RoutedAppRouteDefinition {
108
- key: string;
109
- title: string;
110
- entrypoint: string;
111
- wasmFile: string;
112
- shellDir: string;
113
- sourceRoutePath: string;
114
- publishedRoutePath: string;
115
-
116
- constructor(
117
- key: string = "",
118
- title: string = "",
119
- entrypoint: string = "",
120
- wasmFile: string = "",
121
- shellDir: string = "",
122
- sourceRoutePath: string = "",
123
- publishedRoutePath: string = "",
124
- ) {
125
- this.key = key;
126
- this.title = title;
127
- this.entrypoint = entrypoint;
128
- this.wasmFile = wasmFile;
129
- this.shellDir = shellDir;
130
- this.sourceRoutePath = sourceRoutePath;
131
- this.publishedRoutePath = publishedRoutePath;
132
- }
133
- }
134
-
135
- function trimSlashes(path: string): string {
136
- let normalized = path;
137
- while (normalized.startsWith("/")) {
138
- normalized = normalized.slice(1);
139
- }
140
- while (normalized.endsWith("/")) {
141
- normalized = normalized.slice(0, -1);
142
- }
143
- return normalized;
144
- }
145
-
146
- function normalizeRouteBase(path: string): string {
147
- const trimmed = trimSlashes(path);
148
- return trimmed.length === 0 ? "" : `/${trimmed}`;
149
- }
150
-
151
- function toPascalCase(value: string): string {
152
- let result = "";
153
- let capitalizeNext = true;
154
- const normalized = trimSlashes(value);
155
- for (let index = 0; index < normalized.length; index += 1) {
156
- const char = normalized.charAt(index);
157
- const code = char.charCodeAt(0);
158
- const isAlphaNum =
159
- (code >= 48 && code <= 57) ||
160
- (code >= 65 && code <= 90) ||
161
- (code >= 97 && code <= 122);
162
- if (!isAlphaNum) {
163
- capitalizeNext = true;
164
- continue;
165
- }
166
- result += capitalizeNext ? char.toUpperCase() : char;
167
- capitalizeNext = false;
168
- }
169
- return result;
170
- }
171
-
172
- export function routeDef(
173
- key: string,
174
- title: string,
175
- headTags: Array<RoutedAppHeadTag> = new Array<RoutedAppHeadTag>(),
176
- entrypoint: string = "",
177
- wasmFile: string = "",
178
- shellDir: string = "",
179
- sourceRoutePath: string = "",
180
- publishedRoutePath: string = "",
181
- ): RoutedAppRoute {
182
- return new RoutedAppRoute(key, title, headTags, entrypoint, wasmFile, shellDir, sourceRoutePath, publishedRoutePath);
183
- }
184
-
185
- export function defineRoutedAppManifest(
186
- sourceRouteBase: string,
187
- routes: Array<RoutedAppRoute>,
188
- ): RoutedAppRouteManifest {
189
- const normalizedBase = normalizeRouteBase(sourceRouteBase);
190
- const normalizedRoutes = new Array<RoutedAppRoute>();
191
- for (let index = 0; index < routes.length; index += 1) {
192
- const route = routes[index];
193
- const routeKey = trimSlashes(route.key);
194
- const entrypoint = route.entrypoint == null || route.entrypoint.length == 0 ? `src/routes/${toPascalCase(routeKey)}App.ts` : route.entrypoint;
195
- const wasmFile = route.wasmFile == null || route.wasmFile.length == 0 ? `${routeKey}.wasm` : route.wasmFile;
196
- const shellDir = route.shellDir == null || route.shellDir.length == 0 ? routeKey : route.shellDir;
197
- const sourceRoutePath = route.sourceRoutePath == null || route.sourceRoutePath.length == 0 ? `${normalizedBase}/${routeKey}/` : route.sourceRoutePath;
198
- const publishedRoutePath = route.publishedRoutePath == null || route.publishedRoutePath.length == 0 ? `/${routeKey}/` : route.publishedRoutePath;
199
- normalizedRoutes.push(new RoutedAppRoute(routeKey, route.title, route.headTags, entrypoint, wasmFile, shellDir, sourceRoutePath, publishedRoutePath));
200
- }
201
- return new RoutedAppRouteManifest(normalizedBase, normalizedRoutes);
202
- }
203
-
204
- export function resolveRouteManifest(manifest: RoutedAppRouteManifest): ResolvedRoutedAppRouteManifest {
205
- const routes = new Array<ResolvedRoutedAppRoute>();
206
- for (let index = 0; index < manifest.routes.length; index += 1) {
207
- const route = manifest.routes[index];
208
- const routeKey = trimSlashes(route.key);
209
- routes.push(
210
- new ResolvedRoutedAppRoute(
211
- routeKey,
212
- route.title,
213
- route.headTags,
214
- routeKey.length == 0 ? "" : routeKey,
215
- `${routeKey}.wasm`,
216
- `src/routes/${toPascalCase(routeKey)}App.ts`,
217
- `${manifest.sourceRouteBase}/${routeKey}/`,
218
- `/${routeKey}/`,
219
- ),
220
- );
221
- }
222
- return new ResolvedRoutedAppRouteManifest(manifest.sourceRouteBase, routes);
223
- }
224
-
225
- function escapeHtml(value: string): string {
226
- return value
227
- .replace(/&/g, "&amp;")
228
- .replace(/</g, "&lt;")
229
- .replace(/>/g, "&gt;")
230
- .replace(/"/g, "&quot;");
231
- }
232
-
233
- function pushHeadTag(tags: Array<string>, headTag: RoutedAppHeadTag): void {
234
- if (headTag.content.length == 0 || headTag.name.length == 0) {
235
- return;
236
- }
237
- const attribute = headTag.kind == "property" ? "property" : "name";
238
- tags.push(` <meta ${attribute}="${escapeHtml(headTag.name)}" content="${escapeHtml(headTag.content)}" />`);
239
- }
240
-
241
- export function routeHead(...entries: Array<string>): Array<RoutedAppHeadTag> {
242
- const headTags = new Array<RoutedAppHeadTag>();
243
- for (let index = 0; index + 1 < entries.length; index += 2) {
244
- const name = entries[index];
245
- const content = entries[index + 1];
246
- const kind = name.startsWith("og:") || name.startsWith("fb:") ? "property" : "name";
247
- headTags.push(new RoutedAppHeadTag(kind, name, content));
248
- }
249
- return headTags;
250
- }
251
-
252
- export function renderRoutedPageHead(title: string, headTags: Array<RoutedAppHeadTag> = new Array<RoutedAppHeadTag>()): string {
253
- const tags = new Array<string>();
254
- const effectiveTitle = title.length == 0 ? "FUI-AS" : title;
255
- tags.push(` <title>${escapeHtml(effectiveTitle)}</title>`);
256
- for (let index = 0; index < headTags.length; index += 1) {
257
- pushHeadTag(tags, headTags[index]);
258
- }
259
- return tags.join("\n");
260
- }
261
-
262
- export function buildRoutedHarnessRoutes(
263
- manifest: RoutedAppRouteManifest,
264
- pathname: string,
265
- ): Array<RoutedHarnessRouteSpec> {
266
- const resolvedManifest = resolveRouteManifest(manifest);
267
- const routePrefix = pathname.startsWith(`${resolvedManifest.sourceRouteBase}/`) ? resolvedManifest.sourceRouteBase : "";
268
- const routes = new Array<RoutedHarnessRouteSpec>();
269
- for (let index = 0; index < resolvedManifest.routes.length; index += 1) {
270
- const route = resolvedManifest.routes[index];
271
- routes.push(
272
- new RoutedHarnessRouteSpec(
273
- `${routePrefix}${route.publishedRoutePath}`,
274
- `${routePrefix}/${route.wasmFile}`.replace("//", "/"),
275
- route.title,
276
- ),
277
- );
278
- }
279
- return routes;
280
- }
281
-
282
- export function resolveRoutePath(manifest: RoutedAppRouteManifest, routeKey: string, currentRoutePath: string): string {
283
- const resolvedManifest = resolveRouteManifest(manifest);
284
- const isSourceRoute = currentRoutePath.length == 0 || currentRoutePath.startsWith(`${resolvedManifest.sourceRouteBase}/`);
285
- for (let index = 0; index < resolvedManifest.routes.length; index += 1) {
286
- const route = resolvedManifest.routes[index];
287
- if (route.key == routeKey) {
288
- return isSourceRoute ? route.sourceRoutePath : route.publishedRoutePath;
289
- }
290
- }
291
- return "";
292
- }