@stacksjs/mobile 0.75.87 → 0.75.89

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.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Bundled TypeScript declarations
3
- * Generated from 6 source files
3
+ * Generated from 7 source files
4
4
  */
5
5
 
6
6
  // From: src/index.ts
@@ -234,6 +234,45 @@ declare interface RouteRecorder {
234
234
  readonly stats: RouteStats
235
235
  }
236
236
 
237
+ // From: src/indoor.ts
238
+ /**
239
+ * `elapsedS` is the session's own time, pauses left out. 'unsure' until it
240
+ * has run INDOOR_CHECK_AFTER_S; 'outside' as soon as the route has clearly
241
+ * gone somewhere.
242
+ *
243
+ * Measured robustly, because indoor GPS jumps: the middle is the median
244
+ * position and the spread is how far the nearest 80% of fixes lie from it,
245
+ * so one fix thrown 150 m across town neither hides a treadmill nor proves a
246
+ * run outside. A 400 m track (fixes within ~60 m of its middle, but sharp)
247
+ * is not mistaken for a treadmill: wandering only counts with a vague signal.
248
+ */
249
+ declare function indoorVerdict(fixes: IndoorFix[], elapsedS: number): IndoorVerdict;
250
+ /** A distance typed off a treadmill's display, as kilometres: "8.4", "8,4", "8400 m". */
251
+ declare function typedDistanceKm(input: string): number | null;
252
+ /** Seconds a session runs before the verdict is trusted. */
253
+ declare const INDOOR_CHECK_AFTER_S: 150;
254
+ /**
255
+ * Whether a run or ride being recorded is happening indoors: on a treadmill,
256
+ * or a bike on a trainer. Read the recording (`location.readRecording()`)
257
+ * every half minute or so while it runs and switch to a timed, GPS-free
258
+ * session when this says 'indoors'.
259
+ *
260
+ * Indoors the phone still reports positions, wandering around the room
261
+ * by tens of metres, and those wanderings added up to kilometres and a pace
262
+ * from nowhere ("2.82 km" and a 5:31 average over fifty minutes on a
263
+ * treadmill). Outside, even a walk leaves a 60-metre circle within a couple
264
+ * of minutes. So once a session has run long enough to tell, a route that
265
+ * never left that circle, a signal that never got good, or no positions at
266
+ * all, means the session is indoors and GPS is the wrong source for it.
267
+ */
268
+ declare interface IndoorFix {
269
+ latitude: number
270
+ longitude: number
271
+ accuracy?: number | null
272
+ timestamp: number
273
+ }
274
+ type IndoorVerdict = 'indoors' | 'outside' | 'unsure';
275
+
237
276
  // From: src/spotlight.ts
238
277
  /**
239
278
  * Read configured kinds, leaving out any that could not work.
@@ -787,7 +826,11 @@ type PermissionType = | 'camera'
787
826
  | 'motion'
788
827
  | 'health';
789
828
  type PermissionStatus = 'granted' | 'denied' | 'undetermined' | 'restricted';
790
- type BiometricType = 'faceId' | 'touchId' | 'fingerprint' | 'face' | 'iris';
829
+ /**
830
+ * The kind of biometric the device offers. Craft reports `'none'` as well
831
+ * since craft-native 0.0.119, so the type admits it.
832
+ */
833
+ type BiometricType = 'faceId' | 'touchId' | 'fingerprint' | 'face' | 'iris' | 'none';
791
834
  type AppState = 'active' | 'inactive' | 'background';
792
835
  type HealthDataType = | 'steps'
793
836
  | 'heartRate'
@@ -806,4 +849,4 @@ type HealthWorkoutActivity = | 'running' | 'cycling' | 'walking' | 'hiking' | 's
806
849
  | 'stairClimbing' | 'yoga' | 'pilates' | 'strength' | 'hiit' | 'crossTraining'
807
850
  | 'crossCountrySkiing' | 'skiing' | 'paddling' | 'climbing' | 'dance' | 'mobility' | 'other';
808
851
 
809
- export { normalizeDeepLinkURL, getNativeMobileBridge, isNativeMobile, hasNativeMobileHost, whenNativeMobile, whenBridgeReady, onMobileReady, withNativeFeedback, biometrics, camera, device, haptics, lifecycle, location, notifications, permissions, secureStorage, share, appReview, deepLinks, keepAwake, speech, network, pushNotifications, health, liveActivities, watchConnectivity, mobile, pageScrollTop, observePageScroll, pullDistance, observePullToRefresh, ScrollHost, PullToRefreshOptions, canGoBack, goBack, enterNavTrail, backLabelFor, navTrail, deepLinkPath, followDeepLinks, HistoryHost, NavTrailEntry, fixDistance, usableFixes, routeStats, paceLabel, speedLabel, createRouteRecorder, ROUTE_GAP_S, RouteFix, RouteStats, RouteStatsOptions, RouteRecorderOptions, RouteRecorder, readSpotlightKinds, spotlightKind, spotlightAction, spotlightSlot, spotlightActions, spotlightActivityTypes, spotlightTitle, placeSpotlightItem, removeSpotlightItem, spotlightRoute, readSpotlightEntries, spotlightBridge, createSpotlightIndex, onSpotlightTap, MAX_SPOTLIGHT_SLOTS_PER_KIND, SpotlightKind, SpotlightKindInput, SpotlightItem, SpotlightEntry, SpotlightKindsResult, SpotlightPlacement, SpotlightBridge, SpotlightIndexOptions, SpotlightIndex, CraftMobileCapabilities, CraftMobileBridge, CraftReadyEvent, MobileApi, DeviceInfo, DeviceCapabilities, CameraOptions, PhotoResult, Location, LocationOptions, LocationRecordingState, LocationRecordingResult, ShareOptions, NotificationOptions, DeviceApi, HapticsApi, PermissionsApi, CameraApi, BiometricsApi, SecureStorageApi, LocationApi, ShareApi, LifecycleApi, NotificationsApi, KeepAwakeApi, SpeechOptions, SpeechApi, DeepLinksApi, NetworkStatus, NetworkApi, AppReviewApi, PushNotificationsApi, HealthDataOptions, HealthDataResult, HealthWorkoutLocation, HealthWorkout, HealthWorkoutResult, HealthWorkoutQuery, HealthWorkoutSample, HealthDailyValue, HealthAuthorizationOptions, HealthApi, LiveActivityState, LiveActivityOptions, LiveActivitiesApi, WatchConnectivityApi, HapticStyle, HapticNotificationType, PermissionType, PermissionStatus, BiometricType, AppState, HealthDataType, HealthDailyType, HealthWorkoutType, HealthWorkoutActivity };
852
+ export { normalizeDeepLinkURL, getNativeMobileBridge, isNativeMobile, hasNativeMobileHost, whenNativeMobile, whenBridgeReady, onMobileReady, withNativeFeedback, biometrics, camera, device, haptics, lifecycle, location, notifications, permissions, secureStorage, share, appReview, deepLinks, keepAwake, speech, network, pushNotifications, health, liveActivities, watchConnectivity, mobile, pageScrollTop, observePageScroll, pullDistance, observePullToRefresh, ScrollHost, PullToRefreshOptions, canGoBack, goBack, enterNavTrail, backLabelFor, navTrail, deepLinkPath, followDeepLinks, HistoryHost, NavTrailEntry, fixDistance, usableFixes, routeStats, paceLabel, speedLabel, createRouteRecorder, ROUTE_GAP_S, RouteFix, RouteStats, RouteStatsOptions, RouteRecorderOptions, RouteRecorder, indoorVerdict, typedDistanceKm, INDOOR_CHECK_AFTER_S, IndoorFix, IndoorVerdict, readSpotlightKinds, spotlightKind, spotlightAction, spotlightSlot, spotlightActions, spotlightActivityTypes, spotlightTitle, placeSpotlightItem, removeSpotlightItem, spotlightRoute, readSpotlightEntries, spotlightBridge, createSpotlightIndex, onSpotlightTap, MAX_SPOTLIGHT_SLOTS_PER_KIND, SpotlightKind, SpotlightKindInput, SpotlightItem, SpotlightEntry, SpotlightKindsResult, SpotlightPlacement, SpotlightBridge, SpotlightIndexOptions, SpotlightIndex, CraftMobileCapabilities, CraftMobileBridge, CraftReadyEvent, MobileApi, DeviceInfo, DeviceCapabilities, CameraOptions, PhotoResult, Location, LocationOptions, LocationRecordingState, LocationRecordingResult, ShareOptions, NotificationOptions, DeviceApi, HapticsApi, PermissionsApi, CameraApi, BiometricsApi, SecureStorageApi, LocationApi, ShareApi, LifecycleApi, NotificationsApi, KeepAwakeApi, SpeechOptions, SpeechApi, DeepLinksApi, NetworkStatus, NetworkApi, AppReviewApi, PushNotificationsApi, HealthDataOptions, HealthDataResult, HealthWorkoutLocation, HealthWorkout, HealthWorkoutResult, HealthWorkoutQuery, HealthWorkoutSample, HealthDailyValue, HealthAuthorizationOptions, HealthApi, LiveActivityState, LiveActivityOptions, LiveActivitiesApi, WatchConnectivityApi, HapticStyle, HapticNotificationType, PermissionType, PermissionStatus, BiometricType, AppState, HealthDataType, HealthDailyType, HealthWorkoutType, HealthWorkoutActivity };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{appReview as mt,biometrics as gt,camera as ht,deepLinks as U,device as yt,haptics as St,health as vt,keepAwake as bt,lifecycle as xt,liveActivities as At,location as wt,network as Rt,notifications as Tt,permissions as Et,pushNotifications as kt,secureStorage as Lt,share as Pt,speech as Mt,watchConnectivity as It}from"craft-native/mobile";import{haptics as z}from"craft-native/mobile";function M(){if(typeof window>"u")return;return window}function k(t=M()){if(!t)return 0;if(typeof t.scrollY==="number")return t.scrollY;return t.document?.scrollingElement?.scrollTop??0}function te(t,n=M()){if(!n)return()=>{};let r=!1,e=typeof requestAnimationFrame==="function"?requestAnimationFrame:(i)=>setTimeout(i,16),o=()=>{if(r)return;r=!0,e(()=>{r=!1,t(k(n))})};return t(k(n)),n.addEventListener("scroll",o,{passive:!0}),()=>n.removeEventListener("scroll",o)}function J(t,n){let r=Math.max(0,-t),e=t<=0?Math.max(0,n)*0.5:0;return Math.round(Math.max(r,e))}function ee(t){let n=t.host??M();if(!n)return()=>{};let r=t.threshold??72,e=null,o=!1,i=!1,s=typeof requestAnimationFrame==="function"?requestAnimationFrame:(g)=>setTimeout(g,16),l=null,a=(g)=>{l=null,t.onPull?.(g,o)},u=(g)=>{let b=l!==null;if(l=g,b)return;s(()=>{if(l!==null)a(l)})},h=(g)=>{let b=g.touches?.[0];return b?b.clientY:null},d=(g)=>{if(i||k(n)>0)return;e=h(g),o=!1},p=(g)=>{if(e===null)return;let b=h(g);if(b===null)return;let A=J(k(n),b-e);if(!o&&A>=r)o=!0,Promise.resolve(z.impact("medium")).catch(()=>{});else if(o&&A<r*0.8)o=!1;u(A)},v=async()=>{if(e===null)return;if(e=null,!o){a(0);return}o=!1,i=!0,a(r);try{await t.onRefresh()}finally{i=!1,a(0)}},m=()=>{if(e===null)return;e=null,o=!1,a(0)};return n.addEventListener("touchstart",d,{passive:!0}),n.addEventListener("touchmove",p,{passive:!0}),n.addEventListener("touchend",v),n.addEventListener("touchcancel",m),()=>{l=null,n.removeEventListener("touchstart",d),n.removeEventListener("touchmove",p),n.removeEventListener("touchend",v),n.removeEventListener("touchcancel",m)}}function N(){if(typeof window>"u")return;return window}function V(t=N()){let n=t?.history?.state;return n!==null&&n!==void 0}function re(t=N()){if(!V(t))return!1;return t.history.back(),!0}function oe(t,n,r=50){let e=t[t.length-1];if(t.length>=2&&t[t.length-2].path===n)t.pop();else if(!e||e.path!==n)t.push({path:n,title:""});if(t.length>r)t.splice(0,t.length-r);return{current:t[t.length-1],previous:t[t.length-2]??null}}function ie(t,n,r=12){let e=t?.title.trim()??"";return e&&e.length<=r?e:n}function se(){if(typeof window>"u")return[];let t=window;return t.__stacksNavTrail??=[]}function Q(t,n=Z()){let r;try{r=new URL(t)}catch{return null}let e=`${r.search}${r.hash}`;if(r.protocol==="http:"||r.protocol==="https:"){if(!n||r.origin!==n)return null;return`${r.pathname||"/"}${e}`}return`${`/${[r.host,r.pathname.replace(/^\/+/,"")].filter(Boolean).join("/")}`}${e}`}function Z(){if(typeof window>"u")return;return window.location?.origin}var _="stacks-mobile-deep-link";function ae(t,n){let r=(e)=>{let o=Q(e);if(o)t(o)};return n.getInitialURL().then((e)=>{if(!e)return;try{if(sessionStorage.getItem(_)===e)return;sessionStorage.setItem(_,e)}catch{}r(e)}).catch(()=>{}),n.onLink((e,o)=>{if(o?.initial)return;r(e)})}var ce=30;function tt(t,n){let r=Math.PI/180,e=(n.latitude-t.latitude)*r,o=(n.longitude-t.longitude)*r,i=Math.sin(e/2)**2+Math.cos(t.latitude*r)*Math.cos(n.latitude*r)*Math.sin(o/2)**2;return 12742017.6*Math.asin(Math.min(1,Math.sqrt(i)))}function et(t,n=25){let r=new Set;return t.filter((e)=>Number.isFinite(e.latitude)&&Number.isFinite(e.longitude)&&Number.isFinite(e.timestamp)&&e.accuracy>=0&&e.accuracy<=n).sort((e,o)=>e.timestamp-o.timestamp).filter((e)=>{if(r.has(e.timestamp))return!1;return r.add(e.timestamp),!0})}function O(t,n={}){let r=et(t,n.maxAccuracyM??25),e=n.paceWindowS??30,o=[],i=0,s=0,l=null;for(let d of r){let p=o[o.length-1],v=l?(d.timestamp-l.timestamp)/1000:0;if(l=d,!p){o.push({fix:d,cumM:0});continue}if(v>30){o.push({fix:d,cumM:i});continue}let m=(d.timestamp-p.fix.timestamp)/1000,g=tt(p.fix,d),b=Math.min(10,Math.max(3,p.fix.accuracy,d.accuracy));if(g<b)continue;if(i+=g,m>0&&g/m>=0.5)s+=m;o.push({fix:d,cumM:i})}let a=o[o.length-1],u=null,h=null;if(a){let d=a.fix.timestamp-e*1000,p=o.find((g)=>g.fix.timestamp>=d)??a,v=a.cumM-p.cumM,m=(a.fix.timestamp-p.fix.timestamp)/1000;if(v>=20&&m>0)h=v/m,u=m/(v/1000)}return{distanceM:i,movingS:s,paceSPerKm:u,averagePaceSPerKm:i>=50&&s>0?s/(i/1000):null,speedMps:h,accuracyM:r.length?r[r.length-1].accuracy:t.length?t[t.length-1].accuracy??null:null,fixes:r.length}}function ue(t){if(!t||!Number.isFinite(t)||t>3600)return"–";let n=Math.round(t);return`${Math.floor(n/60)}:${String(n%60).padStart(2,"0")}`}function de(t){return t&&Number.isFinite(t)?(t*3.6).toFixed(1):"–"}var H={distanceM:0,movingS:0,paceSPerKm:null,averagePaceSPerKm:null,speedMps:null,accuracyM:null,fixes:0};function fe(t){let n=t.location,r=t.intervalMs??3000,e=H,o=null,i=()=>typeof document<"u"&&document.visibilityState==="hidden";async function s(){let u=await n.readRecording();return e=O(u,t),t.onUpdate?.(e),e}function l(){a(),o=setInterval(()=>{if(!i())s().catch(()=>{})},r)}function a(){if(o)clearInterval(o);o=null}return{get stats(){return e},async start(){return await n.startRecording({enableHighAccuracy:!0,...t.locationOptions}),e=H,t.onUpdate?.(e),l(),e},async attach(){let u=await n.getRecordingState().catch(()=>null);if(!u?.active)return null;if(await s().catch(()=>{}),!u.paused)l();return u},async pause(){a(),await n.pauseRecording(),await s().catch(()=>{})},async resume(){await n.resumeRecording(),l()},async stop(){a();let h=(await n.stopRecording())?.locations??[];return e=O(h,t),t.onUpdate?.(e),h},refresh:s}}var me=64;var nt=/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/,rt=/^(?:0|[1-9]\d*)$/;function F(t){let n=[],r=[],e=t&&typeof t==="object"&&!Array.isArray(t)?t:{};for(let[o,i]of Object.entries(e)){let s=i&&typeof i==="object"?i:null;if(!s){r.push(`spotlight kind "${o}" is not an object`);continue}let{slots:l,route:a}=s,u=typeof s.noun==="string"&&s.noun.trim()?s.noun.trim():"Item";if(!nt.test(o)||o.includes("-slot-")){r.push(`spotlight kind "${o}" is not a usable name: lowercase letters, digits and single dashes, and never "-slot-"`);continue}if(typeof l!=="number"||!Number.isInteger(l)||l<=0||l>64){r.push(`spotlight kind "${o}" needs slots between 1 and 64, not ${String(l)}`);continue}if(typeof a!=="string"||!a.startsWith("/")||!a.includes(":id")){r.push(`spotlight kind "${o}" needs a route starting with "/" and carrying ":id", not ${String(a)}`);continue}n.push({name:o,slots:l,route:a,noun:u})}return{kinds:n,problems:r}}function T(t,n){return t.find((r)=>r.name===n)??null}function R(t,n,r){let e=T(t,n);if(!e||!Number.isInteger(r)||r<0||r>=e.slots)return null;return`${n}-slot-${r}`}function ot(t,n){if(typeof n!=="string")return null;let r=n.lastIndexOf("-slot-");if(r<=0)return null;let e=n.slice(0,r),o=n.slice(r+6);if(!rt.test(o))return null;let i=Number(o);return R(t,e,i)===n?{kind:e,slot:i}:null}function C(t){return t.flatMap((n)=>Array.from({length:n.slots},(r,e)=>`${n.name}-slot-${e}`))}function ge(t,n){let r=t&&typeof t==="object"?t:{};if(r.enabled===!1)return[];let o=[...(Array.isArray(r.activityTypes)?r.activityTypes.filter((i)=>typeof i==="string"&&i.trim().length>0):[]).map((i)=>i.trim()),...C(F(r.kinds).kinds)];return[...new Set(o)].map((i)=>`${n}.${i}`)}function D(t,n,r){let e=typeof r.name==="string"?r.name.replace(/\s+/g," ").trim():"";if(!e)return`${T(t,n)?.noun??"Item"} #${r.id}`;return e.length>120?`${e.slice(0,119).trimEnd()}…`:e}function it(t,n,r,e,o=Date.now()){let i=T(t,r);if(!i||!Number.isInteger(e.id)||e.id<=0)return null;let s=n.filter((m)=>m.kind===r),l=D(t,r,e),a=s.find((m)=>m.itemId===e.id)??null,u=a?null:ct(s,i.slots),h=a||u!==null?null:ut(s),d=a?.slot??u??h?.slot;if(d===void 0||d===null)return null;let p={kind:r,slot:d,itemId:e.id,title:l,donatedAt:o},v=n.filter((m)=>m.kind!==r||m.slot!==d&&m.itemId!==e.id);return{entries:B(t,[...v,p]),entry:p,evicted:h,changed:!a||a.title!==l}}function st(t,n,r){let e=t.find((o)=>o.kind===n&&o.itemId===r)??null;return{entries:e?t.filter((o)=>o!==e):[...t],removed:e}}function at(t,n,r){let e=ot(t,r);if(!e)return null;let o=n.find((s)=>s.kind===e.kind&&s.slot===e.slot),i=T(t,e.kind)?.route;return o&&i?i.replace(":id",String(o.itemId)):null}function lt(t,n){if(!Array.isArray(n))return[];let r=new Map,e=new Set;for(let o of n){if(!o||typeof o!=="object")continue;let i=o,{kind:s,slot:l,itemId:a,title:u,donatedAt:h}=i;if(typeof s!=="string"||typeof l!=="number"||R(t,s,l)===null)continue;if(typeof a!=="number"||!Number.isInteger(a)||a<=0)continue;let d=`${s}-slot-${l}`,p=`${s}#${a}`;if(r.has(d)||e.has(p))continue;r.set(d,{kind:s,slot:l,itemId:a,title:typeof u==="string"&&u.trim()?u:D(t,s,{id:a}),donatedAt:typeof h==="number"&&Number.isFinite(h)&&h>0?h:0}),e.add(p)}return B(t,[...r.values()])}function B(t,n){let r=new Map(t.map((e,o)=>[e.name,o]));return[...n].sort((e,o)=>(r.get(e.kind)??0)-(r.get(o.kind)??0)||e.slot-o.slot)}function ct(t,n){let r=new Set(t.map((e)=>e.slot));for(let e=0;e<n;e++)if(!r.has(e))return e;return null}function ut(t){return t.reduce((n,r)=>!n||r.donatedAt<n.donatedAt?r:n,null)}function dt(){let t=globalThis.craft;return t&&typeof t==="object"?t.siri:void 0}var ft="stacks_spotlight_index",pt=3000;function he(t){let n=t.onProblem??((c)=>console.warn(`[spotlight] ${c}`)),r=Array.isArray(t.kinds)?{kinds:[...t.kinds],problems:[]}:F(t.kinds);for(let c of r.problems)n(c);let e=t.enabled===!1?[]:r.kinds,o=t.storageKey??ft,i=t.legacyStorageKeys??[],s=t.timeoutMs??pt,l=t.now??(()=>Date.now()),a=t.bridge??dt,u=null;function h(){try{return typeof localStorage>"u"?null:localStorage}catch{return null}}function d(){if(u)return u;u=[];let c=h();if(!c)return u;try{let f=[o,...i].map((y)=>c.getItem(y)).find((y)=>y);u=f?lt(e,JSON.parse(f)):[]}catch{u=[]}return u}function p(c){u=c;let f=h();if(!f)return;try{f.setItem(o,JSON.stringify(c));for(let y of i)f.removeItem(y)}catch{}}async function v(c){if(!c)return null;let f;try{return await Promise.race([c,new Promise((y)=>{f=setTimeout(()=>y(null),s)})])}catch{return null}finally{if(f!==void 0)clearTimeout(f)}}let m=Promise.resolve();function g(c){let f=m.then(c,c);return m=f.catch(()=>{return}),f}async function b(c,f){let y=a()?.register;if(!y||e.length===0)return!1;return g(async()=>{let S=it(e,d(),c,f,l()),x=S&&R(e,c,S.entry.slot);if(!S||!x)return!1;if(!S.changed)return p(S.entries),!0;let w=await v(y(S.entry.title,x))!==null;if(w)p(S.entries);return w})}async function A(c,f){let y=a()?.remove;if(!y)return!1;return g(async()=>{let{entries:S,removed:x}=st(d(),c,f);if(!x)return!1;let w=R(e,c,x.slot),X=w?await v(y(w))!==null:!1;return p(S),X})}async function Y(c,f){let y=T(e,c)?.slots;if(!y||!a()?.register)return 0;let S=0;for(let x of[...f].slice(0,y).reverse())if(await b(c,x))S++;return S}async function W(){let c=a()?.remove;if(!c)return p([]),0;return g(async()=>{let f=0;for(let y of d()){let S=R(e,y.kind,y.slot);if(S&&await v(c(S))!==null)f++}return p([]),f})}return{kinds:e,index:b,sync:Y,remove:A,clear:W,routeFor:(c)=>at(e,d(),c),entries:()=>d(),activityTypes:(c)=>C(e).map((f)=>`${c}.${f}`),reset:()=>{u=null}}}var K=["craftShortcut","craftSiriShortcut"];function ye(t,n){let r=(e)=>{let o=e.detail,i=typeof o==="string"?o:o&&typeof o==="object"?o.action??o.type:null,s=typeof i==="string"?t.routeFor(i):null;if(s)n(s)};if(typeof globalThis.addEventListener!=="function")return()=>{};for(let e of K)globalThis.addEventListener(e,r);return()=>{for(let e of K)globalThis.removeEventListener(e,r)}}var _t=gt,Nt=ht,Ot=yt,L=St,Ht=xt,Kt=wt,$t=Tt,Ft=Et,Ct=Lt,Dt=Pt,Bt=mt;function j(t){if(typeof t==="string")return t.trim()||null;if(!t||typeof t!=="object")return null;let n=t.url;return typeof n==="string"&&n.trim()?n:null}var Ut={async getInitialURL(){if(!await G())return null;return j(await U.getInitialURL?.())},onLink(t){let n=null,r=!1,e=()=>{if(r)return;n=U.onLink?.((o,i)=>{let s=j(o);if(s)t(s,{initial:i?.initial===!0})})??null};if(E()?.craft)e();else G().then(e);return()=>{r=!0,n?.()}}},jt=bt,Gt=Mt,qt=Rt,Yt=kt,Wt=vt,Xt=At,zt=It;function E(){if(typeof window>"u")return;return window}function P(){let t=E()?.craft;if(!t||typeof t!=="object")return null;if(!("platform"in t)||t.platform!=="ios"&&t.platform!=="android")return null;let n="capabilities"in t?t.capabilities:void 0,r=n&&typeof n==="object"&&!Array.isArray(n)?Object.fromEntries(Object.entries(n).filter(([,e])=>typeof e==="boolean")):{};return{platform:t.platform,capabilities:r}}function I(){return P()!==null}function q(){if(typeof window>"u")return!1;let t=window;if(t.CraftAndroid)return!0;let n=typeof navigator>"u"?"":navigator.userAgent;return Boolean(t.webkit?.messageHandlers?.craft)&&/iPhone|iPad|iPod/.test(n)}function Jt(t=2000){if(I()||q())return Promise.resolve(!0);let n=E(),r=n;if(!n||!(r?.craft||r?.webkit?.messageHandlers?.craft))return Promise.resolve(!1);return new Promise((e)=>{let o=()=>{clearTimeout(i),n.removeEventListener("craftReady",o),e(I())},i=setTimeout(o,t);n.addEventListener("craftReady",o,{once:!0})})}function G(t=15000){let n=E();if(n?.craft)return Promise.resolve(!0);let r=n;if(!n||!(q()||r?.webkit?.messageHandlers?.craft))return Promise.resolve(!1);return new Promise((e)=>{let o=()=>{clearTimeout(i),n.removeEventListener("craftReady",o),e(Boolean(n.craft))},i=setTimeout(o,t);n.addEventListener("craftReady",o)})}function Vt(t){let n=E();if(!n)return()=>{};if(P()){let e=!1;return queueMicrotask(()=>{if(!e)t(new Event("craftReady"))}),()=>{e=!0}}let r=(e)=>{if(!P())return;n.removeEventListener("craftReady",r),t(e)};return n.addEventListener("craftReady",r),()=>n.removeEventListener("craftReady",r)}async function Qt(t){await L.impact("light");try{let n=await t();return await L.notification("success"),n}catch(n){throw await L.notification("error"),n}}var be={get nativeBridge(){return P()},biometrics:_t,camera:Nt,device:Ot,haptics:L,lifecycle:Ht,location:Kt,notifications:$t,permissions:Ft,secureStorage:Ct,share:Dt,appReview:Bt,deepLinks:Ut,keepAwake:jt,speech:Gt,network:qt,pushNotifications:Yt,health:Wt,liveActivities:Xt,watchConnectivity:zt,isNativeMobile:I,whenNative:Jt,onReady:Vt,withFeedback:Qt};export{me as MAX_SPOTLIGHT_SLOTS_PER_KIND,ce as ROUTE_GAP_S,Bt as appReview,ie as backLabelFor,_t as biometrics,Nt as camera,V as canGoBack,fe as createRouteRecorder,he as createSpotlightIndex,Q as deepLinkPath,Ut as deepLinks,Ot as device,oe as enterNavTrail,tt as fixDistance,ae as followDeepLinks,P as getNativeMobileBridge,re as goBack,L as haptics,q as hasNativeMobileHost,Wt as health,I as isNativeMobile,jt as keepAwake,Ht as lifecycle,Xt as liveActivities,Kt as location,be as mobile,se as navTrail,qt as network,j as normalizeDeepLinkURL,$t as notifications,te as observePageScroll,ee as observePullToRefresh,Vt as onMobileReady,ye as onSpotlightTap,ue as paceLabel,k as pageScrollTop,Ft as permissions,it as placeSpotlightItem,J as pullDistance,Yt as pushNotifications,lt as readSpotlightEntries,F as readSpotlightKinds,st as removeSpotlightItem,O as routeStats,Ct as secureStorage,Dt as share,Gt as speech,de as speedLabel,R as spotlightAction,C as spotlightActions,ge as spotlightActivityTypes,dt as spotlightBridge,T as spotlightKind,at as spotlightRoute,ot as spotlightSlot,D as spotlightTitle,et as usableFixes,zt as watchConnectivity,G as whenBridgeReady,Jt as whenNativeMobile,Qt as withNativeFeedback};
1
+ import{appReview as St,biometrics as vt,camera as bt,deepLinks as j,device as At,haptics as xt,health as wt,keepAwake as Rt,lifecycle as Et,liveActivities as Tt,location as Lt,network as kt,notifications as Mt,permissions as Pt,pushNotifications as _t,secureStorage as It,share as Nt,speech as Ot,watchConnectivity as Ht}from"craft-native/mobile";import{haptics as J}from"craft-native/mobile";function _(){if(typeof window>"u")return;return window}function L(t=_()){if(!t)return 0;if(typeof t.scrollY==="number")return t.scrollY;return t.document?.scrollingElement?.scrollTop??0}function oe(t,n=_()){if(!n)return()=>{};let r=!1,e=typeof requestAnimationFrame==="function"?requestAnimationFrame:(i)=>setTimeout(i,16),o=()=>{if(r)return;r=!0,e(()=>{r=!1,t(L(n))})};return t(L(n)),n.addEventListener("scroll",o,{passive:!0}),()=>n.removeEventListener("scroll",o)}function Q(t,n){let r=Math.max(0,-t),e=t<=0?Math.max(0,n)*0.5:0;return Math.round(Math.max(r,e))}function ie(t){let n=t.host??_();if(!n)return()=>{};let r=t.threshold??72,e=null,o=!1,i=!1,s=typeof requestAnimationFrame==="function"?requestAnimationFrame:(g)=>setTimeout(g,16),u=null,a=(g)=>{u=null,t.onPull?.(g,o)},c=(g)=>{let b=u!==null;if(u=g,b)return;s(()=>{if(u!==null)a(u)})},h=(g)=>{let b=g.touches?.[0];return b?b.clientY:null},d=(g)=>{if(i||L(n)>0)return;e=h(g),o=!1},p=(g)=>{if(e===null)return;let b=h(g);if(b===null)return;let x=Q(L(n),b-e);if(!o&&x>=r)o=!0,Promise.resolve(J.impact("medium")).catch(()=>{});else if(o&&x<r*0.8)o=!1;c(x)},v=async()=>{if(e===null)return;if(e=null,!o){a(0);return}o=!1,i=!0,a(r);try{await t.onRefresh()}finally{i=!1,a(0)}},m=()=>{if(e===null)return;e=null,o=!1,a(0)};return n.addEventListener("touchstart",d,{passive:!0}),n.addEventListener("touchmove",p,{passive:!0}),n.addEventListener("touchend",v),n.addEventListener("touchcancel",m),()=>{u=null,n.removeEventListener("touchstart",d),n.removeEventListener("touchmove",p),n.removeEventListener("touchend",v),n.removeEventListener("touchcancel",m)}}function H(){if(typeof window>"u")return;return window}var N="__stxPushed",Z="__stxScroll";function tt(t=H()){let n=t?.history?.state;if(n===null||n===void 0)return!1;if(typeof n!=="object")return!0;let r=n;if(N in r)return r[N]===!0;return!(Z in r)}function ae(t=H()){if(!tt(t))return!1;return t.history.back(),!0}function ue(t,n,r=50){let e=t[t.length-1];if(t.length>=2&&t[t.length-2].path===n)t.pop();else if(!e||e.path!==n)t.push({path:n,title:""});if(t.length>r)t.splice(0,t.length-r);return{current:t[t.length-1],previous:t[t.length-2]??null}}function le(t,n,r=12){let e=t?.title.trim()??"";return e&&e.length<=r?e:n}function ce(){if(typeof window>"u")return[];let t=window;return t.__stacksNavTrail??=[]}function et(t,n=nt()){let r;try{r=new URL(t)}catch{return null}let e=`${r.search}${r.hash}`;if(r.protocol==="http:"||r.protocol==="https:"){if(!n||r.origin!==n)return null;return`${r.pathname||"/"}${e}`}return`${`/${[r.host,r.pathname.replace(/^\/+/,"")].filter(Boolean).join("/")}`}${e}`}function nt(){if(typeof window>"u")return;return window.location?.origin}var O="stacks-mobile-deep-link";function de(t,n){let r=(e)=>{let o=et(e);if(o)t(o)};return n.getInitialURL().then((e)=>{if(!e)return;try{if(sessionStorage.getItem(O)===e)return;sessionStorage.setItem(O,e)}catch{}r(e)}).catch(()=>{}),n.onLink((e,o)=>{if(o?.initial)return;r(e)})}var pe=30;function rt(t,n){let r=Math.PI/180,e=(n.latitude-t.latitude)*r,o=(n.longitude-t.longitude)*r,i=Math.sin(e/2)**2+Math.cos(t.latitude*r)*Math.cos(n.latitude*r)*Math.sin(o/2)**2;return 12742017.6*Math.asin(Math.min(1,Math.sqrt(i)))}function ot(t,n=25){let r=new Set;return t.filter((e)=>Number.isFinite(e.latitude)&&Number.isFinite(e.longitude)&&Number.isFinite(e.timestamp)&&e.accuracy>=0&&e.accuracy<=n).sort((e,o)=>e.timestamp-o.timestamp).filter((e)=>{if(r.has(e.timestamp))return!1;return r.add(e.timestamp),!0})}function K(t,n={}){let r=ot(t,n.maxAccuracyM??25),e=n.paceWindowS??30,o=[],i=0,s=0,u=null;for(let d of r){let p=o[o.length-1],v=u?(d.timestamp-u.timestamp)/1000:0;if(u=d,!p){o.push({fix:d,cumM:0});continue}if(v>30){o.push({fix:d,cumM:i});continue}let m=(d.timestamp-p.fix.timestamp)/1000,g=rt(p.fix,d),b=Math.min(10,Math.max(3,p.fix.accuracy,d.accuracy));if(g<b)continue;if(i+=g,m>0&&g/m>=0.5)s+=m;o.push({fix:d,cumM:i})}let a=o[o.length-1],c=null,h=null;if(a){let d=a.fix.timestamp-e*1000,p=o.find((g)=>g.fix.timestamp>=d)??a,v=a.cumM-p.cumM,m=(a.fix.timestamp-p.fix.timestamp)/1000;if(v>=20&&m>0)h=v/m,c=m/(v/1000)}return{distanceM:i,movingS:s,paceSPerKm:c,averagePaceSPerKm:i>=50&&s>0?s/(i/1000):null,speedMps:h,accuracyM:r.length?r[r.length-1].accuracy:t.length?t[t.length-1].accuracy??null:null,fixes:r.length}}function me(t){if(!t||!Number.isFinite(t)||t>3600)return"–";let n=Math.round(t);return`${Math.floor(n/60)}:${String(n%60).padStart(2,"0")}`}function ge(t){return t&&Number.isFinite(t)?(t*3.6).toFixed(1):"–"}var C={distanceM:0,movingS:0,paceSPerKm:null,averagePaceSPerKm:null,speedMps:null,accuracyM:null,fixes:0};function he(t){let n=t.location,r=t.intervalMs??3000,e=C,o=null,i=()=>typeof document<"u"&&document.visibilityState==="hidden";async function s(){let c=await n.readRecording();return e=K(c,t),t.onUpdate?.(e),e}function u(){a(),o=setInterval(()=>{if(!i())s().catch(()=>{})},r)}function a(){if(o)clearInterval(o);o=null}return{get stats(){return e},async start(){return await n.startRecording({enableHighAccuracy:!0,...t.locationOptions}),e=C,t.onUpdate?.(e),u(),e},async attach(){let c=await n.getRecordingState().catch(()=>null);if(!c?.active)return null;if(await s().catch(()=>{}),!c.paused)u();return c},async pause(){a(),await n.pauseRecording(),await s().catch(()=>{})},async resume(){await n.resumeRecording(),u()},async stop(){a();let h=(await n.stopRecording())?.locations??[];return e=K(h,t),t.onUpdate?.(e),h},refresh:s}}var Se=150;function it(t,n){let r=Math.PI/180,e=(n.latitude-t.latitude)*r,o=(n.longitude-t.longitude)*r,i=Math.sin(e/2)**2+Math.cos(t.latitude*r)*Math.cos(n.latitude*r)*Math.sin(o/2)**2;return 12742017.6*Math.asin(Math.min(1,Math.sqrt(i)))}function k(t,n){let r=[...t].sort((e,o)=>e-o);return r[Math.min(r.length-1,Math.floor(n*r.length))]}function ve(t,n){let r=(t||[]).filter((s)=>Number.isFinite(s?.latitude)&&Number.isFinite(s?.longitude)),e=r.map((s)=>Number(s.accuracy)).filter((s)=>Number.isFinite(s)&&s>=0),o=e.length?k(e,0.5):Number.POSITIVE_INFINITY,i=0;if(r.length){let s={latitude:k(r.map((u)=>u.latitude),0.5),longitude:k(r.map((u)=>u.longitude),0.5)};i=k(r.map((u)=>it(s,u)),0.8)}if(r.length>=10&&i>150&&o<=15)return"outside";if(n<150)return"unsure";if(r.length<3||o>30||i<25)return"indoors";if(i<50&&o>10)return"indoors";return"unsure"}function be(t){let r=String(t||"").trim().toLowerCase().replace(",",".").match(/^(\d+(?:\.\d+)?)\s*(km|k|m|mi)?$/);if(!r)return null;let e=Number(r[1]),o=r[2]||(e>100?"m":"km"),i=o==="m"?e/1000:o==="mi"?e*1.609344:e;return i>0&&i<1000?Math.round(i*1000)/1000:null}var xe=64;var st=/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/,at=/^(?:0|[1-9]\d*)$/;function D(t){let n=[],r=[],e=t&&typeof t==="object"&&!Array.isArray(t)?t:{};for(let[o,i]of Object.entries(e)){let s=i&&typeof i==="object"?i:null;if(!s){r.push(`spotlight kind "${o}" is not an object`);continue}let{slots:u,route:a}=s,c=typeof s.noun==="string"&&s.noun.trim()?s.noun.trim():"Item";if(!st.test(o)||o.includes("-slot-")){r.push(`spotlight kind "${o}" is not a usable name: lowercase letters, digits and single dashes, and never "-slot-"`);continue}if(typeof u!=="number"||!Number.isInteger(u)||u<=0||u>64){r.push(`spotlight kind "${o}" needs slots between 1 and 64, not ${String(u)}`);continue}if(typeof a!=="string"||!a.startsWith("/")||!a.includes(":id")){r.push(`spotlight kind "${o}" needs a route starting with "/" and carrying ":id", not ${String(a)}`);continue}n.push({name:o,slots:u,route:a,noun:c})}return{kinds:n,problems:r}}function E(t,n){return t.find((r)=>r.name===n)??null}function R(t,n,r){let e=E(t,n);if(!e||!Number.isInteger(r)||r<0||r>=e.slots)return null;return`${n}-slot-${r}`}function ut(t,n){if(typeof n!=="string")return null;let r=n.lastIndexOf("-slot-");if(r<=0)return null;let e=n.slice(0,r),o=n.slice(r+6);if(!at.test(o))return null;let i=Number(o);return R(t,e,i)===n?{kind:e,slot:i}:null}function U(t){return t.flatMap((n)=>Array.from({length:n.slots},(r,e)=>`${n.name}-slot-${e}`))}function we(t,n){let r=t&&typeof t==="object"?t:{};if(r.enabled===!1)return[];let o=[...(Array.isArray(r.activityTypes)?r.activityTypes.filter((i)=>typeof i==="string"&&i.trim().length>0):[]).map((i)=>i.trim()),...U(D(r.kinds).kinds)];return[...new Set(o)].map((i)=>`${n}.${i}`)}function B(t,n,r){let e=typeof r.name==="string"?r.name.replace(/\s+/g," ").trim():"";if(!e)return`${E(t,n)?.noun??"Item"} #${r.id}`;return e.length>120?`${e.slice(0,119).trimEnd()}…`:e}function lt(t,n,r,e,o=Date.now()){let i=E(t,r);if(!i||!Number.isInteger(e.id)||e.id<=0)return null;let s=n.filter((m)=>m.kind===r),u=B(t,r,e),a=s.find((m)=>m.itemId===e.id)??null,c=a?null:pt(s,i.slots),h=a||c!==null?null:mt(s),d=a?.slot??c??h?.slot;if(d===void 0||d===null)return null;let p={kind:r,slot:d,itemId:e.id,title:u,donatedAt:o},v=n.filter((m)=>m.kind!==r||m.slot!==d&&m.itemId!==e.id);return{entries:G(t,[...v,p]),entry:p,evicted:h,changed:!a||a.title!==u}}function ct(t,n,r){let e=t.find((o)=>o.kind===n&&o.itemId===r)??null;return{entries:e?t.filter((o)=>o!==e):[...t],removed:e}}function dt(t,n,r){let e=ut(t,r);if(!e)return null;let o=n.find((s)=>s.kind===e.kind&&s.slot===e.slot),i=E(t,e.kind)?.route;return o&&i?i.replace(":id",String(o.itemId)):null}function ft(t,n){if(!Array.isArray(n))return[];let r=new Map,e=new Set;for(let o of n){if(!o||typeof o!=="object")continue;let i=o,{kind:s,slot:u,itemId:a,title:c,donatedAt:h}=i;if(typeof s!=="string"||typeof u!=="number"||R(t,s,u)===null)continue;if(typeof a!=="number"||!Number.isInteger(a)||a<=0)continue;let d=`${s}-slot-${u}`,p=`${s}#${a}`;if(r.has(d)||e.has(p))continue;r.set(d,{kind:s,slot:u,itemId:a,title:typeof c==="string"&&c.trim()?c:B(t,s,{id:a}),donatedAt:typeof h==="number"&&Number.isFinite(h)&&h>0?h:0}),e.add(p)}return G(t,[...r.values()])}function G(t,n){let r=new Map(t.map((e,o)=>[e.name,o]));return[...n].sort((e,o)=>(r.get(e.kind)??0)-(r.get(o.kind)??0)||e.slot-o.slot)}function pt(t,n){let r=new Set(t.map((e)=>e.slot));for(let e=0;e<n;e++)if(!r.has(e))return e;return null}function mt(t){return t.reduce((n,r)=>!n||r.donatedAt<n.donatedAt?r:n,null)}function gt(){let t=globalThis.craft;return t&&typeof t==="object"?t.siri:void 0}var ht="stacks_spotlight_index",yt=3000;function Re(t){let n=t.onProblem??((l)=>console.warn(`[spotlight] ${l}`)),r=Array.isArray(t.kinds)?{kinds:[...t.kinds],problems:[]}:D(t.kinds);for(let l of r.problems)n(l);let e=t.enabled===!1?[]:r.kinds,o=t.storageKey??ht,i=t.legacyStorageKeys??[],s=t.timeoutMs??yt,u=t.now??(()=>Date.now()),a=t.bridge??gt,c=null;function h(){try{return typeof localStorage>"u"?null:localStorage}catch{return null}}function d(){if(c)return c;c=[];let l=h();if(!l)return c;try{let f=[o,...i].map((y)=>l.getItem(y)).find((y)=>y);c=f?ft(e,JSON.parse(f)):[]}catch{c=[]}return c}function p(l){c=l;let f=h();if(!f)return;try{f.setItem(o,JSON.stringify(l));for(let y of i)f.removeItem(y)}catch{}}async function v(l){if(!l)return null;let f;try{return await Promise.race([l,new Promise((y)=>{f=setTimeout(()=>y(null),s)})])}catch{return null}finally{if(f!==void 0)clearTimeout(f)}}let m=Promise.resolve();function g(l){let f=m.then(l,l);return m=f.catch(()=>{return}),f}async function b(l,f){let y=a()?.register;if(!y||e.length===0)return!1;return g(async()=>{let S=lt(e,d(),l,f,u()),A=S&&R(e,l,S.entry.slot);if(!S||!A)return!1;if(!S.changed)return p(S.entries),!0;let w=await v(y(S.entry.title,A))!==null;if(w)p(S.entries);return w})}async function x(l,f){let y=a()?.remove;if(!y)return!1;return g(async()=>{let{entries:S,removed:A}=ct(d(),l,f);if(!A)return!1;let w=R(e,l,A.slot),z=w?await v(y(w))!==null:!1;return p(S),z})}async function V(l,f){let y=E(e,l)?.slots;if(!y||!a()?.register)return 0;let S=0;for(let A of[...f].slice(0,y).reverse())if(await b(l,A))S++;return S}async function X(){let l=a()?.remove;if(!l)return p([]),0;return g(async()=>{let f=0;for(let y of d()){let S=R(e,y.kind,y.slot);if(S&&await v(l(S))!==null)f++}return p([]),f})}return{kinds:e,index:b,sync:V,remove:x,clear:X,routeFor:(l)=>dt(e,d(),l),entries:()=>d(),activityTypes:(l)=>U(e).map((f)=>`${l}.${f}`),reset:()=>{c=null}}}var F=["craftShortcut","craftSiriShortcut"];function Ee(t,n){let r=(e)=>{let o=e.detail,i=typeof o==="string"?o:o&&typeof o==="object"?o.action??o.type:null,s=typeof i==="string"?t.routeFor(i):null;if(s)n(s)};if(typeof globalThis.addEventListener!=="function")return()=>{};for(let e of F)globalThis.addEventListener(e,r);return()=>{for(let e of F)globalThis.removeEventListener(e,r)}}var Kt=vt,Ct=bt,$t=At,M=xt,Ft=Et,Dt=Lt,Ut=Mt,Bt=Pt,Gt=It,jt=Nt,Yt=St;function Y(t){if(typeof t==="string")return t.trim()||null;if(!t||typeof t!=="object")return null;let n=t.url;return typeof n==="string"&&n.trim()?n:null}var qt={async getInitialURL(){if(!await q())return null;return Y(await j.getInitialURL?.())},onLink(t){let n=null,r=!1,e=()=>{if(r)return;n=j.onLink?.((o,i)=>{let s=Y(o);if(s)t(s,{initial:i?.initial===!0})})??null};if(T()?.craft)e();else q().then(e);return()=>{r=!0,n?.()}}},Wt=Rt,Vt=Ot,Xt=kt,zt=_t,Jt=wt,Qt=Tt,Zt=Ht;function T(){if(typeof window>"u")return;return window}function P(){let t=T()?.craft;if(!t||typeof t!=="object")return null;if(!("platform"in t)||t.platform!=="ios"&&t.platform!=="android")return null;let n="capabilities"in t?t.capabilities:void 0,r=n&&typeof n==="object"&&!Array.isArray(n)?Object.fromEntries(Object.entries(n).filter(([,e])=>typeof e==="boolean")):{};return{platform:t.platform,capabilities:r}}function I(){return P()!==null}function W(){if(typeof window>"u")return!1;let t=window;if(t.CraftAndroid)return!0;let n=typeof navigator>"u"?"":navigator.userAgent;return Boolean(t.webkit?.messageHandlers?.craft)&&/iPhone|iPad|iPod/.test(n)}function te(t=2000){if(I()||W())return Promise.resolve(!0);let n=T(),r=n;if(!n||!(r?.craft||r?.webkit?.messageHandlers?.craft))return Promise.resolve(!1);return new Promise((e)=>{let o=()=>{clearTimeout(i),n.removeEventListener("craftReady",o),e(I())},i=setTimeout(o,t);n.addEventListener("craftReady",o,{once:!0})})}function q(t=15000){let n=T();if(n?.craft)return Promise.resolve(!0);let r=n;if(!n||!(W()||r?.webkit?.messageHandlers?.craft))return Promise.resolve(!1);return new Promise((e)=>{let o=()=>{clearTimeout(i),n.removeEventListener("craftReady",o),e(Boolean(n.craft))},i=setTimeout(o,t);n.addEventListener("craftReady",o)})}function ee(t){let n=T();if(!n)return()=>{};if(P()){let e=!1;return queueMicrotask(()=>{if(!e)t(new Event("craftReady"))}),()=>{e=!0}}let r=(e)=>{if(!P())return;n.removeEventListener("craftReady",r),t(e)};return n.addEventListener("craftReady",r),()=>n.removeEventListener("craftReady",r)}async function ne(t){await M.impact("light");try{let n=await t();return await M.notification("success"),n}catch(n){throw await M.notification("error"),n}}var ke={get nativeBridge(){return P()},biometrics:Kt,camera:Ct,device:$t,haptics:M,lifecycle:Ft,location:Dt,notifications:Ut,permissions:Bt,secureStorage:Gt,share:jt,appReview:Yt,deepLinks:qt,keepAwake:Wt,speech:Vt,network:Xt,pushNotifications:zt,health:Jt,liveActivities:Qt,watchConnectivity:Zt,isNativeMobile:I,whenNative:te,onReady:ee,withFeedback:ne};export{Se as INDOOR_CHECK_AFTER_S,xe as MAX_SPOTLIGHT_SLOTS_PER_KIND,pe as ROUTE_GAP_S,Yt as appReview,le as backLabelFor,Kt as biometrics,Ct as camera,tt as canGoBack,he as createRouteRecorder,Re as createSpotlightIndex,et as deepLinkPath,qt as deepLinks,$t as device,ue as enterNavTrail,rt as fixDistance,de as followDeepLinks,P as getNativeMobileBridge,ae as goBack,M as haptics,W as hasNativeMobileHost,Jt as health,ve as indoorVerdict,I as isNativeMobile,Wt as keepAwake,Ft as lifecycle,Qt as liveActivities,Dt as location,ke as mobile,ce as navTrail,Xt as network,Y as normalizeDeepLinkURL,Ut as notifications,oe as observePageScroll,ie as observePullToRefresh,ee as onMobileReady,Ee as onSpotlightTap,me as paceLabel,L as pageScrollTop,Bt as permissions,lt as placeSpotlightItem,Q as pullDistance,zt as pushNotifications,ft as readSpotlightEntries,D as readSpotlightKinds,ct as removeSpotlightItem,K as routeStats,Gt as secureStorage,jt as share,Vt as speech,ge as speedLabel,R as spotlightAction,U as spotlightActions,we as spotlightActivityTypes,gt as spotlightBridge,E as spotlightKind,dt as spotlightRoute,ut as spotlightSlot,B as spotlightTitle,be as typedDistanceKm,ot as usableFixes,Zt as watchConnectivity,q as whenBridgeReady,te as whenNativeMobile,ne as withNativeFeedback};
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `elapsedS` is the session's own time, pauses left out. 'unsure' until it
3
+ * has run INDOOR_CHECK_AFTER_S; 'outside' as soon as the route has clearly
4
+ * gone somewhere.
5
+ *
6
+ * Measured robustly, because indoor GPS jumps: the middle is the median
7
+ * position and the spread is how far the nearest 80% of fixes lie from it,
8
+ * so one fix thrown 150 m across town neither hides a treadmill nor proves a
9
+ * run outside. A 400 m track (fixes within ~60 m of its middle, but sharp)
10
+ * is not mistaken for a treadmill: wandering only counts with a vague signal.
11
+ */
12
+ export declare function indoorVerdict(fixes: IndoorFix[], elapsedS: number): IndoorVerdict;
13
+ /** A distance typed off a treadmill's display, as kilometres: "8.4", "8,4", "8400 m". */
14
+ export declare function typedDistanceKm(input: string): number | null;
15
+ /** Seconds a session runs before the verdict is trusted. */
16
+ export declare const INDOOR_CHECK_AFTER_S: 150;
17
+ /**
18
+ * Whether a run or ride being recorded is happening indoors: on a treadmill,
19
+ * or a bike on a trainer. Read the recording (`location.readRecording()`)
20
+ * every half minute or so while it runs and switch to a timed, GPS-free
21
+ * session when this says 'indoors'.
22
+ *
23
+ * Indoors the phone still reports positions, wandering around the room
24
+ * by tens of metres, and those wanderings added up to kilometres and a pace
25
+ * from nowhere ("2.82 km" and a 5:31 average over fifty minutes on a
26
+ * treadmill). Outside, even a walk leaves a 60-metre circle within a couple
27
+ * of minutes. So once a session has run long enough to tell, a route that
28
+ * never left that circle, a signal that never got good, or no positions at
29
+ * all, means the session is indoors and GPS is the wrong source for it.
30
+ */
31
+ export declare interface IndoorFix {
32
+ latitude: number
33
+ longitude: number
34
+ accuracy?: number | null
35
+ timestamp: number
36
+ }
37
+ export type IndoorVerdict = 'indoors' | 'outside' | 'unsure';
package/dist/types.d.ts CHANGED
@@ -352,7 +352,11 @@ export type PermissionType = | 'camera'
352
352
  | 'motion'
353
353
  | 'health';
354
354
  export type PermissionStatus = 'granted' | 'denied' | 'undetermined' | 'restricted';
355
- export type BiometricType = 'faceId' | 'touchId' | 'fingerprint' | 'face' | 'iris';
355
+ /**
356
+ * The kind of biometric the device offers. Craft reports `'none'` as well
357
+ * since craft-native 0.0.119, so the type admits it.
358
+ */
359
+ export type BiometricType = 'faceId' | 'touchId' | 'fingerprint' | 'face' | 'iris' | 'none';
356
360
  export type AppState = 'active' | 'inactive' | 'background';
357
361
  export type HealthDataType = | 'steps'
358
362
  | 'heartRate'
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/mobile",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.87",
5
+ "version": "0.75.89",
6
6
  "description": "Craft-powered native mobile runtime for Stacks and STX applications",
7
7
  "author": "Chris Breuer",
8
8
  "license": "MIT",
@@ -32,10 +32,10 @@
32
32
  "test": "bun test"
33
33
  },
34
34
  "dependencies": {
35
- "craft-native": ">=0.0.118"
35
+ "craft-native": ">=0.0.119"
36
36
  },
37
37
  "devDependencies": {
38
- "@stacksjs/build": "0.75.87",
38
+ "@stacksjs/build": "0.75.89",
39
39
  "better-dx": "^0.2.24"
40
40
  }
41
41
  }