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

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.
@@ -636,6 +636,254 @@ var http = createAbsoluteHttpClient();
636
636
  // src/mobile/shellHttp.ts
637
637
  var installAbsoluteMobileShellHttp = (origin, fetch2 = globalThis.fetch) => installAbsoluteHttpTransport(createAbsoluteHttpTransport({ fetch: fetch2, origin, runtime: "native" }));
638
638
 
639
+ // src/mobile/adaptiveShell.ts
640
+ import {
641
+ keyboard,
642
+ network,
643
+ platform,
644
+ systemBars
645
+ } from "@absolutejs/devices";
646
+ var STYLE_ID = "absolute-mobile-adaptive-shell";
647
+ var ANNOUNCER_ID = "absolute-mobile-announcer";
648
+ var HOST_METRICS_EVENT = "absolute:native-host-metrics";
649
+ var STATUS_STYLE = `
650
+ [data-absolute-mobile-status] {
651
+ display: grid;
652
+ min-height: var(--absolute-available-height, 100dvh);
653
+ place-items: center;
654
+ padding: max(1.5rem, var(--absolute-safe-area-inset-top, 0px)) max(1.5rem, var(--absolute-safe-area-inset-right, 0px)) max(1.5rem, var(--absolute-safe-area-inset-bottom, 0px)) max(1.5rem, var(--absolute-safe-area-inset-left, 0px));
655
+ box-sizing: border-box;
656
+ color: CanvasText;
657
+ background: Canvas;
658
+ font: 500 1rem/1.5 system-ui, sans-serif;
659
+ text-align: center;
660
+ }
661
+ [data-absolute-mobile-announcer] {
662
+ position: fixed;
663
+ width: 1px;
664
+ height: 1px;
665
+ padding: 0;
666
+ margin: -1px;
667
+ overflow: hidden;
668
+ clip: rect(0, 0, 0, 0);
669
+ white-space: nowrap;
670
+ border: 0;
671
+ }
672
+ `;
673
+ var DEFAULT_KEYBOARD = {
674
+ heightPx: 0,
675
+ visible: false
676
+ };
677
+ var DEFAULT_NETWORK = {
678
+ connected: true,
679
+ connectionType: "unknown"
680
+ };
681
+ var DEFAULT_PLATFORM = {
682
+ formFactor: "unknown",
683
+ isNative: true,
684
+ os: "unknown",
685
+ runtime: "capacitor"
686
+ };
687
+ var finitePixels = (value) => Number.isFinite(value) ? Math.max(0, Math.round(value ?? 0)) : 0;
688
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
689
+ var viewportSize = () => ({
690
+ height: finitePixels(globalThis.visualViewport?.height ?? innerHeight),
691
+ width: finitePixels(globalThis.visualViewport?.width ?? innerWidth)
692
+ });
693
+ var setPixels = (name, value) => document.documentElement.style.setProperty(name, `${finitePixels(value)}px`);
694
+ var ensureStyle = () => {
695
+ let style = document.getElementById(STYLE_ID);
696
+ if (!(style instanceof HTMLStyleElement)) {
697
+ style = document.createElement("style");
698
+ style.id = STYLE_ID;
699
+ style.textContent = STATUS_STYLE;
700
+ document.head.append(style);
701
+ }
702
+ };
703
+ var ensureViewport = () => {
704
+ let viewport = document.head.querySelector('meta[name="viewport"]');
705
+ if (!viewport) {
706
+ viewport = document.createElement("meta");
707
+ viewport.name = "viewport";
708
+ document.head.prepend(viewport);
709
+ }
710
+ const values = new Set(viewport.content.split(",").map((value) => value.trim()).filter(Boolean));
711
+ values.add("width=device-width");
712
+ values.add("initial-scale=1");
713
+ values.add("viewport-fit=cover");
714
+ viewport.content = [...values].join(",");
715
+ };
716
+ var ensureAnnouncer = () => {
717
+ let announcer = document.getElementById(ANNOUNCER_ID);
718
+ if (!(announcer instanceof HTMLElement)) {
719
+ announcer = document.createElement("div");
720
+ announcer.id = ANNOUNCER_ID;
721
+ announcer.dataset.absoluteMobileAnnouncer = "";
722
+ announcer.setAttribute("aria-atomic", "true");
723
+ announcer.setAttribute("aria-live", "polite");
724
+ announcer.setAttribute("role", "status");
725
+ document.body.append(announcer);
726
+ }
727
+ return announcer;
728
+ };
729
+ var dispatchState = (state) => dispatchEvent(new CustomEvent("absolute:adaptive-shell-change", { detail: state }));
730
+ var defaultDevices = {
731
+ keyboard,
732
+ network,
733
+ platform,
734
+ systemBars
735
+ };
736
+ var hostSafeAreaInsets = (value) => {
737
+ if (typeof value !== "object" || value === null)
738
+ return;
739
+ const candidate = Reflect.get(value, "safeAreaInsets");
740
+ if (typeof candidate !== "object" || candidate === null)
741
+ return;
742
+ const insets = {
743
+ bottom: Reflect.get(candidate, "bottom"),
744
+ left: Reflect.get(candidate, "left"),
745
+ right: Reflect.get(candidate, "right"),
746
+ top: Reflect.get(candidate, "top")
747
+ };
748
+ if (!isFiniteNumber(insets.bottom) || !isFiniteNumber(insets.left) || !isFiniteNumber(insets.right) || !isFiniteNumber(insets.top))
749
+ return;
750
+ return {
751
+ bottom: finitePixels(insets.bottom),
752
+ left: finitePixels(insets.left),
753
+ right: finitePixels(insets.right),
754
+ top: finitePixels(insets.top)
755
+ };
756
+ };
757
+ var installAbsoluteMobileAdaptiveShell = async (devices = defaultDevices) => {
758
+ let disposed = false;
759
+ let keyboardState = { ...DEFAULT_KEYBOARD };
760
+ let networkState = { ...DEFAULT_NETWORK };
761
+ let platformInfo = { ...DEFAULT_PLATFORM };
762
+ let viewport = viewportSize();
763
+ const subscriptions = [];
764
+ const state = () => ({
765
+ availableHeight: globalThis.visualViewport ? viewport.height : Math.max(0, viewport.height - keyboardState.heightPx),
766
+ keyboard: keyboardState,
767
+ network: networkState,
768
+ platform: platformInfo,
769
+ viewportHeight: viewport.height,
770
+ viewportWidth: viewport.width
771
+ });
772
+ const refreshDocument = () => {
773
+ if (disposed || !document.head || !document.body)
774
+ return;
775
+ ensureViewport();
776
+ ensureStyle();
777
+ const root = document.documentElement;
778
+ const current = state();
779
+ root.dataset.absoluteMobile = "";
780
+ root.dataset.absoluteRuntime = current.platform.runtime;
781
+ root.dataset.absolutePlatform = current.platform.os;
782
+ root.dataset.absoluteFormFactor = current.platform.formFactor;
783
+ root.dataset.absoluteKeyboard = current.keyboard.visible ? "visible" : "hidden";
784
+ root.dataset.absoluteNetwork = current.network.connected ? "online" : "offline";
785
+ root.dataset.absoluteConnection = current.network.connectionType;
786
+ root.dataset.absoluteReducedMotion = current.platform.prefersReducedMotion ? "reduce" : "no-preference";
787
+ const insets = current.platform.safeAreaInsets ?? {
788
+ bottom: 0,
789
+ left: 0,
790
+ right: 0,
791
+ top: 0
792
+ };
793
+ setPixels("--absolute-safe-area-inset-top", insets.top);
794
+ setPixels("--absolute-safe-area-inset-right", insets.right);
795
+ setPixels("--absolute-safe-area-inset-bottom", insets.bottom);
796
+ setPixels("--absolute-safe-area-inset-left", insets.left);
797
+ setPixels("--absolute-keyboard-height", current.keyboard.heightPx);
798
+ setPixels("--absolute-viewport-height", current.viewportHeight);
799
+ setPixels("--absolute-viewport-width", current.viewportWidth);
800
+ setPixels("--absolute-available-height", current.availableHeight);
801
+ ensureAnnouncer();
802
+ };
803
+ const publish = () => {
804
+ refreshDocument();
805
+ dispatchState(state());
806
+ };
807
+ const updateViewport = () => {
808
+ viewport = viewportSize();
809
+ devices.platform.getInfo().then((info) => {
810
+ platformInfo = info;
811
+ publish();
812
+ return;
813
+ }).catch(() => publish());
814
+ };
815
+ const updateKeyboard = (next) => {
816
+ keyboardState = {
817
+ heightPx: finitePixels(next.heightPx),
818
+ visible: next.visible
819
+ };
820
+ publish();
821
+ };
822
+ const updateNetwork = (next) => {
823
+ const changed = networkState.connected !== next.connected;
824
+ networkState = next;
825
+ publish();
826
+ if (changed || !next.connected)
827
+ ensureAnnouncer().textContent = next.connected ? "Connection restored." : "You are offline.";
828
+ };
829
+ const updateHostMetrics = (event) => {
830
+ const insets = hostSafeAreaInsets(Reflect.get(event, "detail"));
831
+ if (!insets)
832
+ return;
833
+ platformInfo = { ...platformInfo, safeAreaInsets: insets };
834
+ publish();
835
+ };
836
+ const installKeyboard = async () => {
837
+ const capability = await devices.keyboard.capability();
838
+ if (!capability.available)
839
+ return;
840
+ keyboardState = await devices.keyboard.getState();
841
+ subscriptions.push(await devices.keyboard.onChange(updateKeyboard));
842
+ };
843
+ try {
844
+ platformInfo = await devices.platform.getInfo();
845
+ } catch {}
846
+ const initialHostInsets = hostSafeAreaInsets(Reflect.get(globalThis, "__absoluteNativeHostMetrics"));
847
+ if (initialHostInsets)
848
+ platformInfo = {
849
+ ...platformInfo,
850
+ safeAreaInsets: initialHostInsets
851
+ };
852
+ try {
853
+ networkState = await devices.network.getStatus();
854
+ subscriptions.push(await devices.network.onChange(updateNetwork));
855
+ } catch {}
856
+ try {
857
+ await installKeyboard();
858
+ } catch {}
859
+ try {
860
+ const capability = await devices.systemBars.capability("appearance");
861
+ if (capability.available)
862
+ await devices.systemBars.setAppearance("automatic");
863
+ } catch {}
864
+ addEventListener("resize", updateViewport);
865
+ addEventListener(HOST_METRICS_EVENT, updateHostMetrics);
866
+ globalThis.visualViewport?.addEventListener("resize", updateViewport);
867
+ globalThis.visualViewport?.addEventListener("scroll", updateViewport);
868
+ publish();
869
+ if (!networkState.connected)
870
+ ensureAnnouncer().textContent = "You are offline.";
871
+ return {
872
+ refreshDocument,
873
+ state,
874
+ dispose: async () => {
875
+ if (disposed)
876
+ return;
877
+ disposed = true;
878
+ removeEventListener("resize", updateViewport);
879
+ removeEventListener(HOST_METRICS_EVENT, updateHostMetrics);
880
+ globalThis.visualViewport?.removeEventListener("resize", updateViewport);
881
+ globalThis.visualViewport?.removeEventListener("scroll", updateViewport);
882
+ await Promise.all(subscriptions.map((unsubscribe) => unsubscribe()));
883
+ }
884
+ };
885
+ };
886
+
639
887
  // src/mobile/shellBootstrap.ts
640
888
  var MANIFEST_PATH = "./absolute-mobile-manifest.json";
641
889
  var STATUS_ID = "absolute-mobile-status";
@@ -672,6 +920,7 @@ var notifyExpoNavigationPath = (path) => {
672
920
  }
673
921
  };
674
922
  var navigationGeneration = 0;
923
+ var adaptiveShell;
675
924
  var notifyCapacitorSystemBarsDomReady = () => {
676
925
  const provider = Reflect.get(globalThis, "CapacitorSystemBarsAndroidInterface");
677
926
  if (typeof provider !== "object" || provider === null)
@@ -691,18 +940,20 @@ var readManifest = async () => {
691
940
  const manifest = await response.json();
692
941
  return manifest;
693
942
  };
694
- var renderStatus = (message) => {
943
+ var renderStatus = (message, kind = "loading") => {
695
944
  document.title = "AbsoluteJS";
696
945
  document.head.innerHTML = '<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">';
697
- document.body.innerHTML = `<main id="${STATUS_ID}" role="status"></main>`;
946
+ document.body.innerHTML = `<main id="${STATUS_ID}" data-absolute-mobile-status="${kind}" role="status" aria-atomic="true" aria-live="polite"></main>`;
698
947
  const status = document.getElementById(STATUS_ID);
699
948
  if (status)
700
949
  status.textContent = message;
950
+ adaptiveShell?.refreshDocument();
701
951
  };
702
952
  var renderPageTarget = () => {
703
953
  document.title = "AbsoluteJS";
704
954
  document.head.innerHTML = '<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">';
705
955
  document.body.innerHTML = '<div id="root"></div>';
956
+ adaptiveShell?.refreshDocument();
706
957
  };
707
958
  var localBundleUrl = (manifest, pageId, contract) => {
708
959
  const page = manifest.pages.find((candidate) => candidate.pageId === pageId && candidate.contract === contract);
@@ -744,9 +995,10 @@ var navigate = async (manifest, path, pushHistory, fetchImpl) => {
744
995
  }
745
996
  });
746
997
  if (activation.kind === "upgrade-required") {
747
- renderStatus("This app version must be updated to continue.");
998
+ renderStatus("This app version must be updated to continue.", "update");
748
999
  return;
749
1000
  }
1001
+ adaptiveShell?.refreshDocument();
750
1002
  if (pushHistory)
751
1003
  pushNavigationHistory(path);
752
1004
  };
@@ -794,13 +1046,15 @@ var navigateWithFailureState = async (manifest, path, pushHistory, reinstallBrow
794
1046
  await navigate(manifest, path, pushHistory, fetchImpl);
795
1047
  } catch (error) {
796
1048
  console.error("[Absolute Mobile] Navigation failed:", error);
797
- renderStatus("Unable to load this page. Check your connection and retry.");
1049
+ 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");
798
1050
  } finally {
799
1051
  reinstallBrowserNavigation();
800
1052
  }
801
1053
  };
802
1054
  var startAbsoluteMobileShell = async (options = {}) => {
803
1055
  notifyCapacitorSystemBarsDomReady();
1056
+ await adaptiveShell?.dispose();
1057
+ adaptiveShell = await installAbsoluteMobileAdaptiveShell();
804
1058
  const manifest = await readManifest();
805
1059
  const auth = manifest.auth && options.createAuth ? await options.createAuth(manifest.auth, {
806
1060
  beforeSignOut: options.beforeSignOut
@@ -0,0 +1,27 @@
1
+ import { keyboard, network, platform, systemBars, type DeviceKeyboardState, type DeviceNetworkStatus, type DevicePlatformInfo } from '@absolutejs/devices';
2
+ export type AbsoluteMobileAdaptiveShellState = {
3
+ availableHeight: number;
4
+ keyboard: DeviceKeyboardState;
5
+ network: DeviceNetworkStatus;
6
+ platform: DevicePlatformInfo;
7
+ viewportHeight: number;
8
+ viewportWidth: number;
9
+ };
10
+ declare global {
11
+ interface WindowEventMap {
12
+ 'absolute:adaptive-shell-change': CustomEvent<AbsoluteMobileAdaptiveShellState>;
13
+ }
14
+ }
15
+ export type AbsoluteMobileAdaptiveShellDevices = {
16
+ keyboard: Pick<typeof keyboard, 'capability' | 'getState' | 'onChange'>;
17
+ network: Pick<typeof network, 'getStatus' | 'onChange'>;
18
+ platform: Pick<typeof platform, 'getInfo'>;
19
+ systemBars: Pick<typeof systemBars, 'capability' | 'setAppearance'>;
20
+ };
21
+ export type AbsoluteMobileAdaptiveShell = {
22
+ dispose(): Promise<void>;
23
+ refreshDocument(): void;
24
+ state(): AbsoluteMobileAdaptiveShellState;
25
+ };
26
+ /** Installs the framework-neutral environment contract owned by the native shell. */
27
+ export declare const installAbsoluteMobileAdaptiveShell: (devices?: AbsoluteMobileAdaptiveShellDevices) => Promise<AbsoluteMobileAdaptiveShell>;
@@ -22,6 +22,7 @@ export type AbsoluteDeviceCapabilityPlan = {
22
22
  providers: Record<string, AbsoluteDeviceCapabilityProvider>;
23
23
  requiredPackages: string[];
24
24
  };
25
+ export declare const ABSOLUTE_MOBILE_SHELL_DEVICE_CAPABILITIES: readonly ["keyboard", "systemBars"];
25
26
  export type AbsoluteDeviceProvider = 'capacitor' | 'expo';
26
27
  export declare const absoluteDeviceNativeRequirements: (plan: AbsoluteDeviceCapabilityPlan) => {
27
28
  androidPermissions: string[];
@@ -1,4 +1,5 @@
1
1
  export * from './artifactStore';
2
+ export * from './adaptiveShell';
2
3
  export * from './androidRelease';
3
4
  export * from './androidUpgradeConformance';
4
5
  export * from './iosRelease';
package/package.json CHANGED
@@ -289,12 +289,12 @@
289
289
  "main": "./dist/index.js",
290
290
  "name": "@absolutejs/absolute",
291
291
  "optionalDependencies": {
292
- "@absolutejs/native-darwin-arm64": "0.20.0-beta.55",
293
- "@absolutejs/native-darwin-x64": "0.20.0-beta.55",
294
- "@absolutejs/native-linux-arm64": "0.20.0-beta.55",
295
- "@absolutejs/native-linux-x64": "0.20.0-beta.55",
296
- "@absolutejs/native-windows-arm64": "0.20.0-beta.55",
297
- "@absolutejs/native-windows-x64": "0.20.0-beta.55"
292
+ "@absolutejs/native-darwin-arm64": "0.20.0-beta.56",
293
+ "@absolutejs/native-darwin-x64": "0.20.0-beta.56",
294
+ "@absolutejs/native-linux-arm64": "0.20.0-beta.56",
295
+ "@absolutejs/native-linux-x64": "0.20.0-beta.56",
296
+ "@absolutejs/native-windows-arm64": "0.20.0-beta.56",
297
+ "@absolutejs/native-windows-x64": "0.20.0-beta.56"
298
298
  },
299
299
  "overrides": {
300
300
  "@sinclair/typebox": "0.34.52",
@@ -508,7 +508,7 @@
508
508
  ]
509
509
  }
510
510
  },
511
- "version": "0.20.0-beta.55",
511
+ "version": "0.20.0-beta.56",
512
512
  "workspaces": [
513
513
  "tests/fixtures/*",
514
514
  "tests/fixtures/_packages/*"