@absolutejs/absolute 0.20.0-beta.56 → 0.20.0-beta.58

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 (58) hide show
  1. package/dist/angular/browser.js +1 -45
  2. package/dist/angular/browser.js.map +2 -2
  3. package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
  4. package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
  5. package/dist/angular/index.js +1 -17
  6. package/dist/angular/index.js.map +2 -2
  7. package/dist/build.js +8 -2
  8. package/dist/build.js.map +3 -3
  9. package/dist/cli/index.js +21 -3
  10. package/dist/client/chunks/angularPatch-arh4sr68.js +157 -0
  11. package/dist/client/chunks/angularPatch-arh4sr68.js.map +10 -0
  12. package/dist/client/chunks/index-5py90ctf.js +87 -0
  13. package/dist/client/chunks/index-5py90ctf.js.map +10 -0
  14. package/dist/client/chunks/index-m35nbkdp.js +374 -0
  15. package/dist/client/chunks/index-m35nbkdp.js.map +12 -0
  16. package/dist/client/chunks/index-qxjc7kc5.js +12 -0
  17. package/dist/client/chunks/index-qxjc7kc5.js.map +9 -0
  18. package/dist/client/chunks/islands-xj2n8nwf.js +281 -0
  19. package/dist/client/chunks/islands-xj2n8nwf.js.map +14 -0
  20. package/dist/client/chunks/react-pdg4325x.js +65 -0
  21. package/dist/client/chunks/react-pdg4325x.js.map +10 -0
  22. package/dist/client/chunks/svelte-s0c964kb.js +19 -0
  23. package/dist/client/chunks/svelte-s0c964kb.js.map +10 -0
  24. package/dist/client/chunks/vue-rdskrnyg.js +24 -0
  25. package/dist/client/chunks/vue-rdskrnyg.js.map +10 -0
  26. package/dist/client/index.js +56 -947
  27. package/dist/client/index.js.map +7 -22
  28. package/dist/index.js +8 -2
  29. package/dist/index.js.map +3 -3
  30. package/dist/islands/browser.js +1 -17
  31. package/dist/islands/browser.js.map +2 -2
  32. package/dist/islands/index.js +1 -17
  33. package/dist/islands/index.js.map +2 -2
  34. package/dist/mobile/browser.js +264 -2
  35. package/dist/mobile/browser.js.map +5 -4
  36. package/dist/mobile/index.js +276 -4
  37. package/dist/mobile/index.js.map +7 -6
  38. package/dist/mobile/remoteMacAgentEntry.js +13 -3
  39. package/dist/mobile/shellBootstrap.js +368 -29
  40. package/dist/mobile/uiPrimitives.js +264 -0
  41. package/dist/react/browser.js +1 -45
  42. package/dist/react/browser.js.map +2 -2
  43. package/dist/react/hooks/index.js +1 -17
  44. package/dist/react/hooks/index.js.map +2 -2
  45. package/dist/react/index.js +1 -17
  46. package/dist/react/index.js.map +2 -2
  47. package/dist/src/mobile/browser.d.ts +1 -0
  48. package/dist/src/mobile/index.d.ts +1 -0
  49. package/dist/src/mobile/uiPrimitives.d.ts +37 -0
  50. package/dist/svelte/browser.js +1 -17
  51. package/dist/svelte/browser.js.map +2 -2
  52. package/dist/svelte/index.js +1 -17
  53. package/dist/svelte/index.js.map +2 -2
  54. package/dist/vue/browser.js +84 -121
  55. package/dist/vue/browser.js.map +4 -4
  56. package/dist/vue/index.js +84 -121
  57. package/dist/vue/index.js.map +4 -4
  58. package/package.json +13 -7
@@ -826,6 +826,11 @@ const bridgeBootstrap = (path: string, safeAreaInsets: { bottom: number; left: n
826
826
  send({ format: 3, kind: 'event', event: 'navigation', path });
827
827
  }
828
828
  };
829
+ globalThis.__absoluteRequestBack = () => {
830
+ const event = new CustomEvent('absolute:back-request', { cancelable: true });
831
+ if (!dispatchEvent(event)) return;
832
+ send({ format: 3, kind: 'event', event: 'back-unhandled', path: currentPath });
833
+ };
829
834
  if (\${DEV_ORIGIN ? 'true' : 'false'}) {
830
835
  const publishPath = () => {
831
836
  const path = location.pathname + location.search + location.hash;
@@ -931,12 +936,12 @@ export function AbsoluteWebHost() {
931
936
  }, [safeAreaInsets.bottom, safeAreaInsets.left, safeAreaInsets.right, safeAreaInsets.top]);
932
937
  useEffect(() => {
933
938
  const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
934
- if (!canGoBack) return false;
935
- webView.current?.goBack();
939
+ if (!indexUri) return false;
940
+ webView.current?.injectJavaScript('globalThis.__absoluteRequestBack?.(); true;');
936
941
  return true;
937
942
  });
938
943
  return () => subscription.remove();
939
- }, [canGoBack]);
944
+ }, [indexUri]);
940
945
 
941
946
  const respond = (message: Record<string, unknown>) => {
942
947
  const source = JSON.stringify(message).replaceAll('\\u2028', '\\\\u2028').replaceAll('\\u2029', '\\\\u2029');
@@ -976,6 +981,11 @@ export function AbsoluteWebHost() {
976
981
  else activeWebPath.current = message.path;
977
982
  return;
978
983
  }
984
+ if (message.kind === 'event' && message.event === 'back-unhandled') {
985
+ if (canGoBack) webView.current?.goBack();
986
+ else BackHandler.exitApp();
987
+ return;
988
+ }
979
989
  if (message.kind !== 'request' || typeof message.id !== 'string' || message.path !== activeWebPath.current) return;
980
990
  try {
981
991
  if (typeof message.params !== 'object' || message.params === null || Array.isArray(message.params)) throw new Error('Expo bridge method params are invalid.');
@@ -1,5 +1,264 @@
1
+ // src/mobile/uiPrimitives.ts
2
+ var STYLE_ID = "absolute-mobile-ui-primitives";
3
+ var NAVIGATION_EVENT = "absolute:navigation-change";
4
+ var SHEET_EVENT = "absolute:sheet-change";
5
+ var BACK_EVENT = "absolute:back-request";
6
+ var ACTIVE_SHEET_SELECTOR = "dialog[data-absolute-sheet][open]";
7
+ var TAB_LINK_SELECTOR = "[data-absolute-tab-bar] a[href]";
8
+ var UI_STYLE = `
9
+ [data-absolute-app-shell] {
10
+ display: grid;
11
+ grid-template-rows: auto minmax(0, 1fr) auto;
12
+ box-sizing: border-box;
13
+ min-height: var(--absolute-available-height, 100dvh);
14
+ padding-inline: var(--absolute-safe-area-inset-left, 0px) var(--absolute-safe-area-inset-right, 0px);
15
+ }
16
+ [data-absolute-app-header] {
17
+ padding-top: var(--absolute-safe-area-inset-top, 0px);
18
+ }
19
+ [data-absolute-app-main], [data-absolute-navigation-stack] {
20
+ min-width: 0;
21
+ min-height: 0;
22
+ }
23
+ [data-absolute-app-main] {
24
+ overflow: auto;
25
+ overscroll-behavior-y: contain;
26
+ }
27
+ [data-absolute-navigation-stack] {
28
+ view-transition-name: absolute-mobile-stack;
29
+ }
30
+ [data-absolute-tab-bar] {
31
+ display: flex;
32
+ align-items: stretch;
33
+ justify-content: space-around;
34
+ box-sizing: border-box;
35
+ padding-bottom: var(--absolute-safe-area-inset-bottom, 0px);
36
+ background: Canvas;
37
+ color: CanvasText;
38
+ }
39
+ [data-absolute-tab-bar] > a {
40
+ display: grid;
41
+ flex: 1 1 0;
42
+ min-width: 0;
43
+ min-height: 2.75rem;
44
+ place-items: center;
45
+ padding: 0.375rem 0.5rem;
46
+ color: inherit;
47
+ text-align: center;
48
+ text-decoration: none;
49
+ touch-action: manipulation;
50
+ }
51
+ [data-absolute-tab-bar] > a[aria-current="page"] {
52
+ font-weight: 700;
53
+ }
54
+ dialog[data-absolute-sheet] {
55
+ box-sizing: border-box;
56
+ width: min(100%, var(--absolute-sheet-max-width, 42rem));
57
+ max-height: min(90dvh, var(--absolute-available-height, 90dvh));
58
+ margin: auto auto 0;
59
+ padding: 1rem max(1rem, var(--absolute-safe-area-inset-right, 0px)) max(1rem, var(--absolute-safe-area-inset-bottom, 0px)) max(1rem, var(--absolute-safe-area-inset-left, 0px));
60
+ overflow: auto;
61
+ border: 1px solid color-mix(in srgb, CanvasText 18%, transparent);
62
+ border-radius: 1rem 1rem 0 0;
63
+ background: Canvas;
64
+ color: CanvasText;
65
+ box-shadow: 0 -0.5rem 2rem color-mix(in srgb, CanvasText 18%, transparent);
66
+ }
67
+ dialog[data-absolute-sheet]::backdrop {
68
+ background: color-mix(in srgb, CanvasText 38%, transparent);
69
+ }
70
+ @keyframes absolute-mobile-forward-old { to { opacity: 0; transform: translateX(-12%); } }
71
+ @keyframes absolute-mobile-forward-new { from { opacity: 0; transform: translateX(12%); } }
72
+ @keyframes absolute-mobile-back-old { to { opacity: 0; transform: translateX(12%); } }
73
+ @keyframes absolute-mobile-back-new { from { opacity: 0; transform: translateX(-12%); } }
74
+ html[data-absolute-navigation-direction="forward"]::view-transition-old(absolute-mobile-stack) { animation: 180ms ease both absolute-mobile-forward-old; }
75
+ html[data-absolute-navigation-direction="forward"]::view-transition-new(absolute-mobile-stack) { animation: 180ms ease both absolute-mobile-forward-new; }
76
+ html[data-absolute-navigation-direction="back"]::view-transition-old(absolute-mobile-stack) { animation: 180ms ease both absolute-mobile-back-old; }
77
+ html[data-absolute-navigation-direction="back"]::view-transition-new(absolute-mobile-stack) { animation: 180ms ease both absolute-mobile-back-new; }
78
+ html[data-absolute-reduced-motion="reduce"]::view-transition-old(absolute-mobile-stack),
79
+ html[data-absolute-reduced-motion="reduce"]::view-transition-new(absolute-mobile-stack) { animation: none; }
80
+ `;
81
+ var currentPath = () => `${location.pathname}${location.search}${location.hash}`;
82
+ var ensureStyle = () => {
83
+ let style = document.getElementById(STYLE_ID);
84
+ if (!(style instanceof HTMLStyleElement)) {
85
+ style = document.createElement("style");
86
+ style.id = STYLE_ID;
87
+ style.textContent = UI_STYLE;
88
+ document.head.append(style);
89
+ }
90
+ };
91
+ var sheetById = (id) => {
92
+ const target = document.getElementById(id);
93
+ return target instanceof HTMLDialogElement && target.dataset.absoluteSheet !== undefined ? target : undefined;
94
+ };
95
+ var dispatchSheet = (sheet, open) => dispatchEvent(new CustomEvent(SHEET_EVENT, {
96
+ detail: { id: sheet.id, open }
97
+ }));
98
+ var focusSheet = (sheet) => {
99
+ const target = sheet.querySelector('[autofocus], button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
100
+ (target ?? sheet).focus();
101
+ };
102
+ var sheetOpeners = new WeakMap;
103
+ var closeAbsoluteMobileSheet = (target) => {
104
+ const sheet = typeof target === "string" ? sheetById(target) : target;
105
+ if (!sheet || !sheet.open)
106
+ return false;
107
+ if (typeof sheet.close === "function")
108
+ sheet.close();
109
+ else
110
+ sheet.removeAttribute("open");
111
+ sheet.removeAttribute("aria-modal");
112
+ sheetOpeners.get(sheet)?.focus();
113
+ sheetOpeners.delete(sheet);
114
+ dispatchSheet(sheet, false);
115
+ return true;
116
+ };
117
+ var openAbsoluteMobileSheet = (target, opener) => {
118
+ const sheet = typeof target === "string" ? sheetById(target) : target;
119
+ if (!sheet || sheet.dataset.absoluteSheet === undefined)
120
+ return false;
121
+ const active = document.querySelector(ACTIVE_SHEET_SELECTOR);
122
+ if (active && active !== sheet)
123
+ closeAbsoluteMobileSheet(active);
124
+ if (opener)
125
+ sheetOpeners.set(sheet, opener);
126
+ if (!sheet.open) {
127
+ if (typeof sheet.showModal === "function")
128
+ sheet.showModal();
129
+ else
130
+ sheet.setAttribute("open", "");
131
+ }
132
+ sheet.setAttribute("aria-modal", "true");
133
+ focusSheet(sheet);
134
+ dispatchSheet(sheet, true);
135
+ return true;
136
+ };
137
+ var readAbsoluteMobileLinkIntent = (anchor) => {
138
+ const mode = anchor.dataset.absoluteLink;
139
+ if (mode === "back")
140
+ return { kind: "back" };
141
+ if (mode === "external")
142
+ return { kind: "external" };
143
+ return { kind: "navigate", replace: mode === "replace" };
144
+ };
145
+ var requestAbsoluteMobileBack = () => {
146
+ const event = new CustomEvent(BACK_EVENT, { cancelable: true });
147
+ return !dispatchEvent(event);
148
+ };
149
+ var normalizePathname = (value) => {
150
+ try {
151
+ return new URL(value, location.href).pathname.replace(/\/$/u, "") || "/";
152
+ } catch {
153
+ return;
154
+ }
155
+ };
156
+ var syncTabLinks = (path) => {
157
+ const activePath = normalizePathname(path);
158
+ if (!activePath)
159
+ return;
160
+ document.querySelectorAll(TAB_LINK_SELECTOR).forEach((anchor) => {
161
+ const candidate = normalizePathname(anchor.href);
162
+ const prefix = anchor.dataset.absoluteTabMatch === "prefix";
163
+ const active = candidate !== undefined && (prefix ? activePath === candidate || candidate !== "/" && activePath.startsWith(`${candidate}/`) : activePath === candidate);
164
+ if (active)
165
+ anchor.setAttribute("aria-current", "page");
166
+ else
167
+ anchor.removeAttribute("aria-current");
168
+ });
169
+ };
170
+ var installAbsoluteMobileUiPrimitives = () => {
171
+ let disposed = false;
172
+ let scheduled = false;
173
+ let path = currentPath();
174
+ const refreshDocument = (nextPath = path) => {
175
+ if (disposed || !document.head || !document.body)
176
+ return;
177
+ path = nextPath;
178
+ ensureStyle();
179
+ syncTabLinks(path);
180
+ };
181
+ const scheduleRefresh = () => {
182
+ if (scheduled || disposed)
183
+ return;
184
+ scheduled = true;
185
+ queueMicrotask(() => {
186
+ scheduled = false;
187
+ refreshDocument();
188
+ });
189
+ };
190
+ const handleClick = (event) => {
191
+ if (!(event.target instanceof Element))
192
+ return;
193
+ const opener = event.target.closest("[data-absolute-sheet-open]");
194
+ if (opener?.dataset.absoluteSheetOpen) {
195
+ if (openAbsoluteMobileSheet(opener.dataset.absoluteSheetOpen, opener))
196
+ event.preventDefault();
197
+ return;
198
+ }
199
+ const closer = event.target.closest("[data-absolute-sheet-close]");
200
+ const sheet = closer?.closest("dialog[data-absolute-sheet]");
201
+ if (sheet && closeAbsoluteMobileSheet(sheet)) {
202
+ event.preventDefault();
203
+ return;
204
+ }
205
+ if (event.target instanceof HTMLDialogElement && event.target.dataset.absoluteSheet !== undefined) {
206
+ const rect = event.target.getBoundingClientRect();
207
+ const outside = event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom;
208
+ if (outside && closeAbsoluteMobileSheet(event.target))
209
+ event.preventDefault();
210
+ }
211
+ };
212
+ const handleCancel = (event) => {
213
+ if (!(event.target instanceof HTMLDialogElement))
214
+ return;
215
+ if (event.target.dataset.absoluteSheet === undefined)
216
+ return;
217
+ event.preventDefault();
218
+ closeAbsoluteMobileSheet(event.target);
219
+ };
220
+ const handleBack = (event) => {
221
+ const active = document.querySelector(ACTIVE_SHEET_SELECTOR);
222
+ if (!active)
223
+ return;
224
+ event.preventDefault();
225
+ closeAbsoluteMobileSheet(active);
226
+ };
227
+ const observer = new MutationObserver(scheduleRefresh);
228
+ observer.observe(document.documentElement, {
229
+ childList: true,
230
+ subtree: true
231
+ });
232
+ addEventListener("click", handleClick);
233
+ addEventListener("cancel", handleCancel, true);
234
+ addEventListener(BACK_EVENT, handleBack);
235
+ refreshDocument();
236
+ return {
237
+ refreshDocument,
238
+ requestBack: requestAbsoluteMobileBack,
239
+ dispose: () => {
240
+ if (disposed)
241
+ return;
242
+ disposed = true;
243
+ observer.disconnect();
244
+ removeEventListener("click", handleClick);
245
+ removeEventListener("cancel", handleCancel, true);
246
+ removeEventListener(BACK_EVENT, handleBack);
247
+ },
248
+ navigate: (detail) => {
249
+ path = detail.to;
250
+ document.documentElement.dataset.absoluteNavigationDirection = detail.direction;
251
+ refreshDocument(path);
252
+ dispatchEvent(new CustomEvent(NAVIGATION_EVENT, {
253
+ detail
254
+ }));
255
+ }
256
+ };
257
+ };
258
+
1
259
  // src/mobile/shellBootstrap.ts
2
260
  import { App } from "@capacitor/app";
261
+ import { Browser } from "@capacitor/browser";
3
262
 
4
263
  // src/mobile/producerContextState.ts
5
264
  var ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY = Symbol.for("absolutejs.mobileProducerAsyncLocalStorage");
@@ -643,7 +902,7 @@ import {
643
902
  platform,
644
903
  systemBars
645
904
  } from "@absolutejs/devices";
646
- var STYLE_ID = "absolute-mobile-adaptive-shell";
905
+ var STYLE_ID2 = "absolute-mobile-adaptive-shell";
647
906
  var ANNOUNCER_ID = "absolute-mobile-announcer";
648
907
  var HOST_METRICS_EVENT = "absolute:native-host-metrics";
649
908
  var STATUS_STYLE = `
@@ -691,11 +950,11 @@ var viewportSize = () => ({
691
950
  width: finitePixels(globalThis.visualViewport?.width ?? innerWidth)
692
951
  });
693
952
  var setPixels = (name, value) => document.documentElement.style.setProperty(name, `${finitePixels(value)}px`);
694
- var ensureStyle = () => {
695
- let style = document.getElementById(STYLE_ID);
953
+ var ensureStyle2 = () => {
954
+ let style = document.getElementById(STYLE_ID2);
696
955
  if (!(style instanceof HTMLStyleElement)) {
697
956
  style = document.createElement("style");
698
- style.id = STYLE_ID;
957
+ style.id = STYLE_ID2;
699
958
  style.textContent = STATUS_STYLE;
700
959
  document.head.append(style);
701
960
  }
@@ -773,7 +1032,7 @@ var installAbsoluteMobileAdaptiveShell = async (devices = defaultDevices) => {
773
1032
  if (disposed || !document.head || !document.body)
774
1033
  return;
775
1034
  ensureViewport();
776
- ensureStyle();
1035
+ ensureStyle2();
777
1036
  const root = document.documentElement;
778
1037
  const current = state();
779
1038
  root.dataset.absoluteMobile = "";
@@ -913,6 +1172,19 @@ var pushNavigationHistory = (path) => {
913
1172
  Reflect.get(bridge, "setPath").call(bridge, path);
914
1173
  }
915
1174
  };
1175
+ var replaceNavigationHistory = (path) => {
1176
+ const state = { absoluteMobile: true };
1177
+ if (!Reflect.get(globalThis, "__absoluteExpoBridge")) {
1178
+ history.replaceState(state, "", path);
1179
+ return;
1180
+ }
1181
+ const url = new URL(location.href);
1182
+ url.search = "";
1183
+ url.hash = "";
1184
+ url.searchParams.set("absolutePath", path);
1185
+ history.replaceState(state, "", url.href);
1186
+ notifyExpoNavigationPath(path);
1187
+ };
916
1188
  var notifyExpoNavigationPath = (path) => {
917
1189
  const bridge = Reflect.get(globalThis, "__absoluteExpoBridge");
918
1190
  if (typeof bridge === "object" && bridge !== null && typeof Reflect.get(bridge, "setPath") === "function") {
@@ -921,6 +1193,7 @@ var notifyExpoNavigationPath = (path) => {
921
1193
  };
922
1194
  var navigationGeneration = 0;
923
1195
  var adaptiveShell;
1196
+ var uiPrimitives;
924
1197
  var notifyCapacitorSystemBarsDomReady = () => {
925
1198
  const provider = Reflect.get(globalThis, "CapacitorSystemBarsAndroidInterface");
926
1199
  if (typeof provider !== "object" || provider === null)
@@ -974,33 +1247,63 @@ var installLocalPageStyle = (manifest, pageId, contract) => {
974
1247
  link.dataset.absoluteMobilePageStyle = page.bundleHash;
975
1248
  document.head.appendChild(link);
976
1249
  };
977
- var navigate = async (manifest, path, pushHistory, fetchImpl) => {
978
- await disposeAbsoluteMobilePage();
979
- renderStatus("Loading…");
1250
+ var navigate = async (manifest, path, historyMode, fetchImpl) => {
1251
+ const hadActivePage = document.body.dataset.absoluteMobilePageActive !== undefined;
1252
+ if (!hadActivePage)
1253
+ renderStatus("Loading…");
980
1254
  const envelope = await fetchAbsoluteMobilePage(manifest, path, {
981
1255
  ...fetchImpl ? { fetch: fetchImpl } : {}
982
1256
  });
983
- const activation = await activateAbsoluteMobilePage(envelope, {
984
- loadPage: ({ contract, pageId }) => {
985
- const page = manifest.pages.find((candidate) => candidate.pageId === pageId && candidate.contract === contract);
986
- if (!page) {
987
- throw new TypeError(`The embedded app does not contain ${pageId} contract ${contract}.`);
988
- }
989
- if (page.framework === "html" || page.framework === "htmx") {
990
- return installAbsoluteMobileStaticDocument(manifest, page, localBundleUrl(manifest, pageId, contract));
1257
+ let activation;
1258
+ const commit = async () => {
1259
+ await disposeAbsoluteMobilePage();
1260
+ activation = await activateAbsoluteMobilePage(envelope, {
1261
+ loadPage: ({ contract, pageId }) => {
1262
+ const page = manifest.pages.find((candidate) => candidate.pageId === pageId && candidate.contract === contract);
1263
+ if (!page) {
1264
+ throw new TypeError(`The embedded app does not contain ${pageId} contract ${contract}.`);
1265
+ }
1266
+ if (page.framework === "html" || page.framework === "htmx") {
1267
+ return installAbsoluteMobileStaticDocument(manifest, page, localBundleUrl(manifest, pageId, contract));
1268
+ }
1269
+ renderPageTarget();
1270
+ installLocalPageStyle(manifest, pageId, contract);
1271
+ return import(localBundleUrl(manifest, pageId, contract));
991
1272
  }
992
- renderPageTarget();
993
- installLocalPageStyle(manifest, pageId, contract);
994
- return import(localBundleUrl(manifest, pageId, contract));
995
- }
996
- });
1273
+ });
1274
+ };
1275
+ const transitionDocument = document;
1276
+ if (hadActivePage && document.documentElement.dataset.absoluteReducedMotion !== "reduce" && transitionDocument.startViewTransition) {
1277
+ const transition = transitionDocument.startViewTransition(commit);
1278
+ await transition.updateCallbackDone;
1279
+ await transition.finished.catch(() => {
1280
+ return;
1281
+ });
1282
+ } else
1283
+ await commit();
1284
+ if (!activation)
1285
+ throw new TypeError("The mobile page did not produce an activation.");
997
1286
  if (activation.kind === "upgrade-required") {
998
1287
  renderStatus("This app version must be updated to continue.", "update");
999
1288
  return;
1000
1289
  }
1290
+ document.body.dataset.absoluteMobilePageActive = "";
1001
1291
  adaptiveShell?.refreshDocument();
1002
- if (pushHistory)
1292
+ if (historyMode === "push")
1003
1293
  pushNavigationHistory(path);
1294
+ else if (historyMode === "replace")
1295
+ replaceNavigationHistory(path);
1296
+ };
1297
+ var openExternalLink = (anchor, event) => {
1298
+ if (Reflect.get(globalThis, "__absoluteExpoBridge"))
1299
+ return;
1300
+ try {
1301
+ const url = new URL(anchor.href);
1302
+ if (url.username || url.password || url.protocol !== "http:" && url.protocol !== "https:")
1303
+ return;
1304
+ event.preventDefault();
1305
+ Browser.open({ url: url.href }).catch(() => location.assign(url.href));
1306
+ } catch {}
1004
1307
  };
1005
1308
  var installAnchorNavigation = (manifest, onNavigate) => {
1006
1309
  const handleClick = (event) => {
@@ -1009,12 +1312,23 @@ var installAnchorNavigation = (manifest, onNavigate) => {
1009
1312
  const anchor = event.target.closest("a[href]");
1010
1313
  if (!(anchor instanceof HTMLAnchorElement) || anchor.target)
1011
1314
  return;
1315
+ const intent = readAbsoluteMobileLinkIntent(anchor);
1316
+ if (intent.kind === "back") {
1317
+ event.preventDefault();
1318
+ if (!uiPrimitives?.requestBack())
1319
+ history.back();
1320
+ return;
1321
+ }
1322
+ if (intent.kind === "external") {
1323
+ openExternalLink(anchor, event);
1324
+ return;
1325
+ }
1012
1326
  try {
1013
1327
  const path = resolveAbsoluteMobileNavigation(manifest, anchor.href, location.origin);
1014
1328
  if (!path)
1015
1329
  return;
1016
1330
  event.preventDefault();
1017
- onNavigate(path);
1331
+ onNavigate(path, intent.kind === "navigate" && intent.replace ? "replace" : "forward", intent.kind === "navigate" && intent.replace);
1018
1332
  } catch {}
1019
1333
  };
1020
1334
  addEventListener("click", handleClick);
@@ -1041,21 +1355,29 @@ var installDeepLinks = async (manifest, onNavigate, authRedirectUri) => {
1041
1355
  } catch {}
1042
1356
  return listener;
1043
1357
  };
1044
- var navigateWithFailureState = async (manifest, path, pushHistory, reinstallBrowserNavigation, fetchImpl) => {
1358
+ var navigateWithFailureState = async (manifest, path, historyMode, direction, from, reinstallBrowserNavigation, fetchImpl) => {
1359
+ let completed = false;
1045
1360
  try {
1046
- await navigate(manifest, path, pushHistory, fetchImpl);
1361
+ document.documentElement.dataset.absoluteNavigationDirection = direction;
1362
+ await navigate(manifest, path, historyMode, fetchImpl);
1363
+ completed = true;
1047
1364
  } catch (error) {
1048
1365
  console.error("[Absolute Mobile] Navigation failed:", error);
1049
1366
  renderStatus(document.documentElement.dataset.absoluteNetwork === "offline" ? "You are offline. Reconnect to load this page." : "Unable to load this page. Check your connection and retry.", "error");
1050
1367
  } finally {
1051
1368
  reinstallBrowserNavigation();
1052
1369
  }
1370
+ if (completed)
1371
+ uiPrimitives?.navigate({ direction, from, to: path });
1053
1372
  };
1054
1373
  var startAbsoluteMobileShell = async (options = {}) => {
1055
1374
  notifyCapacitorSystemBarsDomReady();
1056
1375
  await adaptiveShell?.dispose();
1376
+ uiPrimitives?.dispose();
1057
1377
  adaptiveShell = await installAbsoluteMobileAdaptiveShell();
1378
+ uiPrimitives = installAbsoluteMobileUiPrimitives();
1058
1379
  const manifest = await readManifest();
1380
+ let activePath = initialNavigationPath(manifest.entry);
1059
1381
  const auth = manifest.auth && options.createAuth ? await options.createAuth(manifest.auth, {
1060
1382
  beforeSignOut: options.beforeSignOut
1061
1383
  }) : undefined;
@@ -1070,20 +1392,37 @@ var startAbsoluteMobileShell = async (options = {}) => {
1070
1392
  };
1071
1393
  const handlePopState = () => {
1072
1394
  const path = currentNavigationPath();
1395
+ const from = activePath;
1396
+ activePath = path;
1073
1397
  notifyExpoNavigationPath(path);
1074
- navigateWithFailureState(manifest, path, false, reinstallBrowserNavigation, applicationFetch);
1398
+ navigateWithFailureState(manifest, path, "none", "back", from, reinstallBrowserNavigation, applicationFetch);
1075
1399
  };
1076
1400
  const reinstallBrowserNavigation = () => {
1077
1401
  removeAnchorNavigation();
1078
1402
  removeEventListener("popstate", handlePopState);
1403
+ uiPrimitives?.dispose();
1404
+ uiPrimitives = installAbsoluteMobileUiPrimitives();
1405
+ uiPrimitives.refreshDocument(activePath);
1079
1406
  removeAnchorNavigation = installAnchorNavigation(manifest, onNavigate);
1080
1407
  addEventListener("popstate", handlePopState);
1081
1408
  };
1082
- const onNavigate = (path) => {
1083
- navigateWithFailureState(manifest, path, true, reinstallBrowserNavigation, applicationFetch);
1409
+ const onNavigate = (path, direction = "forward", replace = false) => {
1410
+ const from = activePath;
1411
+ activePath = path;
1412
+ navigateWithFailureState(manifest, path, replace ? "replace" : "push", direction, from, reinstallBrowserNavigation, applicationFetch);
1084
1413
  };
1085
- await navigateWithFailureState(manifest, initialNavigationPath(manifest.entry), false, reinstallBrowserNavigation, applicationFetch);
1414
+ await navigateWithFailureState(manifest, activePath, "none", "replace", activePath, reinstallBrowserNavigation, applicationFetch);
1086
1415
  await installDeepLinks(manifest, onNavigate, auth?.redirectUri);
1416
+ try {
1417
+ await App.addListener("backButton", ({ canGoBack }) => {
1418
+ if (uiPrimitives?.requestBack())
1419
+ return;
1420
+ if (canGoBack)
1421
+ history.back();
1422
+ else
1423
+ App.exitApp();
1424
+ });
1425
+ } catch {}
1087
1426
  };
1088
1427
  export {
1089
1428
  startAbsoluteMobileShell