@effindomv2/fui-as 0.1.37 → 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 (118) 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 +23 -13
  7. package/scripts/build.sh +47 -46
  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 +65 -5
  16. package/src/FuiExports.ts +12 -1
  17. package/src/bindings/ui.ts +157 -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 +17 -8
  46. package/src/core/Bitmap.ts +192 -3
  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 +104 -5
  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 -865
  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 +17 -1
  75. package/src/drawing/DrawContext.ts +140 -32
  76. package/src/nodes/CustomDrawable.ts +8 -1
  77. package/src/nodes/FlexBox.ts +40 -11
  78. package/src/nodes/Grid.ts +5 -5
  79. package/src/nodes/Image.ts +20 -1
  80. package/src/nodes/RichText.ts +36 -25
  81. package/src/nodes/ScrollBar.ts +2 -1
  82. package/src/nodes/ScrollBox.ts +7 -2
  83. package/src/nodes/ScrollView.ts +17 -6
  84. package/src/nodes/Svg.ts +18 -1
  85. package/src/nodes/Text.ts +1 -1
  86. package/src/nodes/TextCore.ts +115 -20
  87. package/src/nodes/index.ts +1 -1
  88. package/src/text/DynamicTextLayout.ts +275 -0
  89. package/src/text/TextLayout.ts +308 -0
  90. package/src/worker/Worker.ts +3 -36
  91. package/src/worker/WorkerJob.ts +5 -9
  92. package/src/worker/ffi.ts +0 -6
  93. package/browser/src/common-harness/host-imports.ts +0 -447
  94. package/browser/src/common-harness/interop.ts +0 -39
  95. package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
  96. package/browser/src/common-harness/managed-harness-canvas-host.ts +0 -72
  97. package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
  98. package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
  99. package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
  100. package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
  101. package/browser/src/common-harness/managed-harness-session.ts +0 -15
  102. package/browser/src/common-harness/managed-harness.ts +0 -1472
  103. package/browser/src/common-harness/managed-history.ts +0 -168
  104. package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
  105. package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
  106. package/browser/src/common-harness/text-session-bridge.ts +0 -452
  107. package/browser/src/common-harness/types.ts +0 -206
  108. package/browser/src/common-harness/ui-chrome.ts +0 -206
  109. package/browser/src/common-harness/ui-imports.ts +0 -559
  110. package/browser/src/common-harness/wasm-module-cache.ts +0 -47
  111. package/browser/src/file-processing-worker.ts +0 -89
  112. package/browser/src/host-events.ts +0 -118
  113. package/browser/src/host-services.ts +0 -483
  114. package/browser/src/persisted-ui-state.ts +0 -206
  115. package/browser/src/routed-app-conventions.ts +0 -292
  116. package/browser/src/worker-bootstrap.ts +0 -916
  117. package/browser/src/worker-manager.ts +0 -234
  118. 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
- }