passenger-mode 0.3.0 → 0.3.1

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/lib/cli.js CHANGED
@@ -120,7 +120,15 @@ async function pushCmd(rest) {
120
120
  console.error(`No manifest at ${file}. Run passenger-mode init (or crawl) first.`);
121
121
  process.exit(2);
122
122
  }
123
- const res = await fetch(`${api}/api/v1/manifest`, { method: "POST", headers: { authorization: `Bearer ${secret}`, "content-type": "application/json" }, body });
123
+ let res;
124
+ try {
125
+ res = await fetch(`${api}/api/v1/manifest`, { method: "POST", headers: { authorization: `Bearer ${secret}`, "content-type": "application/json" }, body });
126
+ }
127
+ catch (e) {
128
+ const why = e?.cause?.code ?? e?.cause?.message ?? e?.message ?? "network error";
129
+ console.error(`Couldn't reach ${api} (${why}). Check your connection. Behind a proxy? Run with NODE_USE_ENV_PROXY=1 (Node 22.21+) so Node uses HTTPS_PROXY.`);
130
+ process.exit(1);
131
+ }
124
132
  const out = await res.json().catch(() => ({}));
125
133
  if (!res.ok) {
126
134
  console.error(`Upload failed (${res.status}): ${out.error ?? "unknown error"}`);
@@ -5,6 +5,7 @@ import { RemotePlanner } from "./remote-planner.ts";
5
5
  import { OverlayUI } from "./ui.ts";
6
6
  export { Driver, RoutePlanner, RemotePlanner, OverlayUI };
7
7
  export type { Planner, TripLog };
8
+ export type { Manifest, Action, ActionKind, Destination, Route, Selector, Input, Framework } from "../types.ts";
8
9
  export declare const HOSTED = "https://passengermode.com";
9
10
  export interface MountOptions {
10
11
  /**
@@ -12,12 +12,37 @@ function framed(win) {
12
12
  return true;
13
13
  }
14
14
  }
15
+ const MOUNTED = "__passengerModeMounted";
15
16
  export async function mount(opts) {
16
17
  const win = opts.window ?? window;
17
18
  if (framed(win) && !opts.allowInFrame) {
18
19
  console.info("Passenger Mode is off inside frames.");
19
20
  return null;
20
21
  }
22
+ // One switch per page: a script tag plus the React component, or the tag pasted twice, must not
23
+ // put two drivers on the same page. Claimed before any await so concurrent mounts can't race.
24
+ const w = win;
25
+ if (w[MOUNTED]) {
26
+ console.info("Passenger Mode is already on this page; ignoring the second install.");
27
+ return null;
28
+ }
29
+ w[MOUNTED] = true;
30
+ try {
31
+ const handle = await mountOnce(opts, win);
32
+ if (!handle)
33
+ w[MOUNTED] = false;
34
+ else {
35
+ const destroy = handle.destroy;
36
+ handle.destroy = () => { destroy(); w[MOUNTED] = false; };
37
+ }
38
+ return handle;
39
+ }
40
+ catch (e) {
41
+ w[MOUNTED] = false;
42
+ throw e;
43
+ }
44
+ }
45
+ async function mountOnce(opts, win) {
21
46
  const api = (opts.apiBase ?? HOSTED).replace(/\/$/, "");
22
47
  const keyed = opts.key ? `key=${encodeURIComponent(opts.key)}` : "";
23
48
  // Hosted: the owner's kill switch and the free-tier cap are checked before the switch appears.
@@ -181,7 +181,10 @@ export class OverlayUI {
181
181
  const label = h(d, "label", { for: "pm-dest" }, "Where to?");
182
182
  const title = h(d, "h2");
183
183
  title.appendChild(label);
184
- const input = h(d, "input", { id: "pm-dest", name: "destination", autocomplete: "off", maxlength: "300", placeholder: "Say it your way: cancel my plan, invite Jo…" });
184
+ // The example comes from this site's own destinations, so it never suggests something the site can't do.
185
+ const example = (this.opts.suggestions ?? [])[0];
186
+ const placeholder = example ? `Say it your way, e.g. ${example.charAt(0).toLowerCase()}${example.slice(1)}` : "Say it your way";
187
+ const input = h(d, "input", { id: "pm-dest", name: "destination", autocomplete: "off", maxlength: "300", placeholder });
185
188
  const btn = h(d, "button", { class: "primary", type: "submit" }, "Plan trip");
186
189
  form.append(input, btn);
187
190
  form.addEventListener("submit", (e) => { e.preventDefault(); if (input.value.trim())
@@ -41,3 +41,5 @@ export declare function planStep(body: any, opts: PlannerHandlerOptions): Promis
41
41
  export declare function createPlannerHandler(opts: PlannerHandlerOptions): (req: Request) => Promise<Response>;
42
42
  /** The model's answer is only a suggestion: anything outside the visible manifest actions becomes "stuck". */
43
43
  export declare function checkStep(out: any, view: PageView): Step;
44
+ /** Visitors see plan steps: drop any "(/card/)" style path the model let slip through. */
45
+ export declare function plainStep(text: string): string;
@@ -32,7 +32,7 @@ const PLAN_TOOL = {
32
32
  properties: {
33
33
  possible: { type: "boolean" },
34
34
  summary: { type: "string", description: "One line, e.g. 'Cancel your Pro plan: 3 steps, no charge'." },
35
- steps: { type: "array", items: { type: "string" }, description: "2-6 plain steps. If the last step is irreversible, end it with 'You press it.'" },
35
+ steps: { type: "array", items: { type: "string" }, description: "2-6 plain steps in the visitor's words, naming buttons and pages by their labels. Never show URLs, paths or ids. If the last step is irreversible, end it with 'You press it.'" },
36
36
  },
37
37
  required: ["possible"],
38
38
  },
@@ -123,7 +123,7 @@ export async function planStep(body, opts) {
123
123
  if (body.op === "plan") {
124
124
  const out = await askModel(opts, PLAN_TOOL, describe(view, opts.manifest, true));
125
125
  const plan = out?.possible && out.summary
126
- ? { summary: String(out.summary).slice(0, 120), steps: (out.steps ?? []).slice(0, 8).map((s) => String(s).slice(0, 120)) }
126
+ ? { summary: String(out.summary).slice(0, 120), steps: (out.steps ?? []).slice(0, 8).map((s) => plainStep(String(s)).slice(0, 120)) }
127
127
  : null;
128
128
  return { status: 200, body: { plan } };
129
129
  }
@@ -157,3 +157,7 @@ export function checkStep(out, view) {
157
157
  }
158
158
  return { kind: "stuck", reason: out?.kind === "stuck" && out.reason ? String(out.reason).slice(0, 160) : "I don't know this road." };
159
159
  }
160
+ /** Visitors see plan steps: drop any "(/card/)" style path the model let slip through. */
161
+ export function plainStep(text) {
162
+ return text.replace(/\s*\((?:https?:\/\/[^)\s]+|\/[^)\s]*)\)/g, "").replace(/\s{2,}/g, " ").trim();
163
+ }
@@ -1,4 +1,4 @@
1
- "use strict";var PassengerMode=(()=>{var x=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var U=Object.prototype.hasOwnProperty;var V=(r,t,e)=>t in r?x(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var z=(r,t)=>{for(var e in t)x(r,e,{get:t[e],enumerable:!0})},j=(r,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of q(t))!U.call(r,i)&&i!==e&&x(r,i,{get:()=>t[i],enumerable:!(n=W(t,i))||n.enumerable});return r};var G=r=>j(x({},"__esModule",{value:!0}),r);var h=(r,t,e)=>V(r,typeof t!="symbol"?t+"":t,e);var ut={};z(ut,{Driver:()=>b,HOSTED:()=>N,OverlayUI:()=>y,RemotePlanner:()=>f,RoutePlanner:()=>v,mount:()=>H});var _=[[/\b(pay|payment|purchase|checkout|check ?out|buy|place (your )?order|order now|confirm (order|purchase)|donate|subscribe|upgrade)\b/,"payment"],[/\b(cancel|end|stop)\b.{0,30}\b(plan|subscription|membership|club|trial|order|booking|reservation)\b|\bunsubscribe\b|\bdowngrade\b/,"cancels a plan or order"],[/\b(delete|destroy|erase|wipe|purge)\b|\bremove (account|user|member|card|payment)\b|\b(close|deactivate) (my |your )?account\b/,"deletion"],[/\b(change|update|reset|set)\b.{0,20}\b(password|e-?mail|2fa|passkey|two[- ]factor)\b|\bnew password\b/,"credential change"],[/\b(sign ?out|log ?out)\b/,"ends the session"]],Y=[[/\b(send|invite|share with)\b/,"sends to other people"],[/\b(publish|go live|make public|unpublish)\b/,"publishes"]],A=/[$€£¥₹]\s?\d|\d(?:[.,]\d{2})?\s?(?:usd|eur|gbp|dollars?)\b|\bper (?:month|year|seat)\b|\/\s?(?:mo|month|yr|year)\b/i,K=/\b(send|track|capture|gtag|analytics|log|report|emit|posthog|plausible|umami)\s*\(/gi;function E(r){return r.replace(/([a-z])([A-Z])/g,"$1 $2").replace(/[_\-./]+/g," ").toLowerCase()}function P(r,t=[]){let e=r.filter(Boolean).map(i=>E(i)).join(" | "),n=[e,...t.filter(Boolean).map(i=>E(i.replace(K,"(")))].join(" | ");for(let[i,s]of _)if(i.test(n))return s;for(let[i,s]of Y)if(i.test(e))return s;return r.some(i=>i&&A.test(i))?"payment":null}function D(r,t){let e=t??"";return/(log-?out|sign-?out|logoff|\/delete|\/destroy|\/remove|unsubscribe)/i.test(e)?P([r,e])??"acts on its own":/\b(sign ?out|log ?out)\b/i.test(E(r))?"ends the session":null}var C=['input[autocomplete^="cc-"]','input[name*="card" i]','input[name*="cvc" i]','input[name*="cvv" i]','input[id*="card-number" i]',"[data-stripe]",'iframe[src*="js.stripe.com"]','iframe[name^="__privateStripeFrame"]','iframe[src*="paypal.com"]','iframe[src*="braintree"]','iframe[src*="squareup.com"]','iframe[src*="adyen"]','iframe[title*="payment" i]'].join(","),J=r=>(r.textContent??"").replace(/\s+/g," ").trim();function I(r){let t=r.tagName==="FORM"?r.querySelector('button:not([type="button"]):not([type="reset"]), input[type="submit"]')??r:r,e=[t.getAttribute("aria-label"),t===r&&r.tagName==="FORM"?"":J(t),t.getAttribute("value"),t.getAttribute("title")].filter(Boolean).join(" ").slice(0,300);if(r.tagName==="A")return D(e,r.getAttribute("href"));let i=P([e]);if(i)return i;if(A.test(e))return"payment";let o=(r.tagName==="FORM"?r:r.closest("form"))??null;if(o){if(o.querySelector(C))return"payment";if(o.querySelector('input[type="password"]'))return"uses your password"}else if(r.ownerDocument?.querySelector(C))return"payment";return null}var m=r=>'"'+r.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"',L=r=>(r??"").replace(/\s+/g," ").trim().toLowerCase();function X(r){if(!r.isConnected||r.closest("[hidden],[inert],[aria-hidden='true']"))return!1;let t=r.ownerDocument?.defaultView;for(let e=r;e;e=e.parentElement){let n=t?.getComputedStyle(e);if(n&&(n.display==="none"||n.visibility==="hidden"))return!1}return!r.disabled}var Z={navigate:"a[href]",click:"button, [role='button'], input[type='submit'], input[type='button']",submit:"form"};function $(r,t,e){let n=s=>!!s&&X(s)&&!(e&&e.contains(s)),i=s=>{for(let o of Array.from(t.querySelectorAll(s))){let l=r.kind==="submit"&&o.tagName!=="FORM"?o.closest("form")??o:o;if(n(l))return l}return null};for(let s of r.selectors){let o=null;switch(s.by){case"testid":o=i(`[data-testid=${m(s.value)}],[data-test=${m(s.value)}],[data-cy=${m(s.value)}]`);break;case"id":o=i(`[id=${m(s.value)}]`);break;case"name":o=i(`[name=${m(s.value)}]`);break;case"aria-label":o=i(`[aria-label=${m(s.value)}]`);break;case"href":o=i(`a[href=${m(s.value)}]`);break;case"text":{if(s.value.startsWith("{"))break;let l=L(s.value),a=r.kind==="submit"?Array.from(t.querySelectorAll("form button, form input[type='submit']")).map(c=>({el:c.closest("form"),text:L(c.textContent||c.getAttribute("value"))})):Array.from(t.querySelectorAll(Z[r.kind])).map(c=>({el:c,text:L(c.getAttribute("aria-label")||c.textContent||c.getAttribute("value"))}));o=(a.find(c=>c.text===l&&n(c.el))??a.find(c=>c.text.startsWith(l)&&n(c.el)))?.el??null;break}}if(o)return o}return null}function M(r){return(r.getAttribute("aria-label")||r.textContent||r.getAttribute("value")||"").replace(/\s+/g," ").trim().slice(0,80)}function Q(r){let t=r.split("/").filter(Boolean).map(e=>/^\[\[\.\.\..+\]\]$/.test(e)?"(?:/.*)?":/^\[\.\.\..+\]$/.test(e)?"/.+":/^\[.+\]$/.test(e)||e.startsWith(":")||e.includes(":param")?"/[^/]+":e==="*"?"(?:/.*)?":"/"+e.replace(/[.*+?^${}()|\\]/g,"\\$&"));return new RegExp("^"+(t.join("")||"/?")+"/?$")}function tt(r){return r.split("/").filter(Boolean).reduce((t,e)=>e.startsWith("[[...")||e==="*"?t:e.startsWith("[")||e.startsWith(":")?t+1:t+3,0)}var et=/^(utm_\w+|fbclid|gclid|msclkid|ref|ttclid|mc_\w+)$/i;function S(r){let[t,e=""]=r.split("#")[0].split("?"),n=[...new URLSearchParams(e)].filter(([i])=>!et.test(i)).sort(([i],[s])=>i.localeCompare(s));return(t||"/")+(n.length?"?"+new URLSearchParams(n).toString():"")}function w(r,t){let[e,n=""]=r.split("#")[0].split("?"),i=new URLSearchParams(n),s=null,o=-1;for(let l of t){let[a,d]=l.path.split("?");if(!Q(a).test(e||"/"))continue;let c=d?[...new URLSearchParams(d)]:[];if(!c.every(([g,T])=>i.get(g)===T))continue;let u=tt(a)+c.length*2;u>o&&(s=l.path,o=u)}return s}function R(r,t){if(!r.startsWith("/")&&!r.startsWith("#/"))return null;let e=r.startsWith("#")?r.slice(1):r;return w(e.replace(/:param/g,"x"),t)}var k="passenger-mode:trip",it=300,nt=/\b(all|every|each|everyone|everybody)\b.{0,40}\b(users?|members?|people|contacts?|customers?|items?|posts?|messages?|accounts?|orders?|friends?|followers?)\b|\b\d{2,}\s+times\b|\bmass\b|\bbulk\b/i,rt={payment:"pay buttons","cancels a plan or order":"cancel buttons",deletion:"delete buttons","sends to other people":"send buttons",publishes:"publish buttons","credential change":"password or email changes","ends the session":"sign-out buttons","uses your password":"buttons that use your password"};function st(){let r=globalThis.crypto;return r?.randomUUID?r.randomUUID():"t"+Date.now().toString(36)+Math.random().toString(36).slice(2,10)}var ot={render(){},async moveCursor(){},ring(){},owns:()=>!1},b=class{constructor(t){h(this,"state","off");h(this,"plan",null);h(this,"log",null);h(this,"o");h(this,"ui");h(this,"run",0);h(this,"gateEl",null);h(this,"gateAction",null);h(this,"stepCount",0);h(this,"detach",[]);if(this.o={routeFrom:"path",isTrusted:e=>e.isTrusted,maxSteps:15,timeLimitMs:3*6e4,settleMs:250,...t},this.ui=t.ui??ot,this.o.storage===void 0)try{this.o.storage=t.window.sessionStorage}catch{this.o.storage=null}this.listen(),this.restore()}get doc(){return this.o.window.document}path(){let t=this.o.window.location;return typeof this.o.routeFrom=="function"?this.o.routeFrom():this.o.routeFrom==="hash"?t.hash.replace(/^#/,"")||"/":t.pathname+t.search}set(t,e={}){this.state=t,this.ui.render(t,{plan:this.plan,destination:this.log?.destination,step:this.stepCount,...e})}open(t){return this.o.isTrusted(t)?(this.state==="off"&&this.set("open"),!0):!1}close(){this.finish("ended"),this.plan=null,this.set("off")}async planTrip(t,e){if(!this.o.isTrusted(e))return null;let n=t.trim().slice(0,it);if(!n)return null;if(this.log={id:st(),destination:n,startedAt:Date.now(),steps:[]},this.stepCount=0,nt.test(n))return this.set("stuck",{message:"Passenger Mode does one thing per trip. Try a single destination."}),null;this.set("planning");let i=null,s=this.view();try{i=await this.o.planner.plan(s,this.o.manifest)}catch{return this.set("stuck",{message:"I can't plan trips right now. Try again in a moment."}),null}return i?(this.plan=i,this.set("ready"),i):(this.set("stuck",{message:"I don't know this road. Try saying it another way."}),null)}go(t){return!this.o.isTrusted(t)||this.state!=="ready"?!1:(this.log.startedAt=Date.now(),this.drive(),!0)}notThis(){this.plan=null,this.log=null,this.set("open")}takeWheel(){this.state==="driving"&&(this.run++,this.ui.ring(null),this.set("paused",{message:"Nothing moves until you resume."}))}resume(t){return!this.o.isTrusted(t)||this.state!=="paused"&&this.state!=="handoff"?!1:(this.drive(),!0)}end(){this.finish(this.state==="arrived"?"arrived":this.state==="paused"?"taken-over":"ended"),this.plan=null,this.set("open")}rate(t){this.log&&(this.log.rating=t?"up":"down",this.o.onTrip?.(this.log))}view(){let t=w(this.path(),this.o.manifest.routes),e=[];for(let n of this.o.manifest.actions){if(n.route!==null&&n.route!==t||n.route===null&&n.seenOn&&t!==null&&!n.seenOn.includes(t))continue;let i=this.find(n);if(!i)continue;let s=n.label.startsWith("{");e.push({id:n.id,label:s?M(i):n.label,kind:n.kind,irreversible:n.irreversible,runtimeLabel:s})}return{destination:this.log?.destination??"",route:t,path:this.path(),visible:e,history:(this.log?.steps??[]).map(n=>({actionId:n.actionId,label:n.label,route:n.route})),...this.plan?{approvedPlan:this.plan.steps.slice(0,8)}:{}}}find(t){let e=$(t,this.doc);return e&&!this.ui.owns(e)?e:null}drive(){let t=++this.run;this.set("driving",{caption:"Starting the trip"}),this.loop(t)}live(t){return t===this.run&&this.state==="driving"}async loop(t){await this.settle();let e=!1;for(;this.live(t);){if(this.stepCount>=this.o.maxSteps)return this.stuck("That's more steps than one trip allows, so I stopped.");if(Date.now()-this.log.startedAt>this.o.timeLimitMs)return this.stuck("This trip is taking too long, so I stopped.");let n=this.view(),i;try{i=await this.o.planner.next(n,this.o.manifest)}catch{return this.stuck("I lost the road. Try again in a moment.")}if(!this.live(t))return;if(i.kind==="arrived")return this.arrive(i.summary);if(i.kind==="stuck"){if(!e){e=!0,await this.settle(1500);continue}let u=this.log.steps.at(-1);return n.route===null&&u?.by==="driver"?this.arrive(`This is where \u201C${u.label}\u201D leads. I don't know this page, so take it from here.`):this.stuck(i.reason)}e=!1;let s=this.log.steps.slice(-2);if(s.length===2&&s.every(u=>u.actionId===i.actionId))return this.stuck("I'm going in circles, so I stopped. Try saying it another way.");let o=this.o.manifest.actions.find(u=>u.id===i.actionId);if(!o||!n.visible.some(u=>u.id===o.id))return this.stuck("I don't know this road.");let l=this.find(o);if(!l)return this.stuck("I don't know this road.");if(o.kind==="navigate"&&this.linksHere(l))return this.arrive(i.caption??"You're already here.");let a=o.label.startsWith("{")?M(l):o.label,d=o.irreversible?o.irreversibleReason:I(l),c=d?`Pull over: ${a} is yours to press`:i.caption??`Pressing ${a}`;if(l.scrollIntoView?.({block:"center",behavior:"smooth"}),this.ui.ring(l,d?"gate":"drive"),await this.ui.moveCursor(l,c),!this.live(t))return;if(d)return this.pullOver(l,o,a,d);if(o.kind==="submit"){let u=l.tagName==="FORM"?l:l.closest("form"),g=u?Array.from(u.querySelectorAll("input[required],select[required],textarea[required]")).filter(T=>!T.value):[];if(g.length){this.ui.ring(u,"handoff"),g[0].scrollIntoView?.({block:"center"}),g[0].focus?.({preventScroll:!0}),this.set("handoff",{message:`Your turn: fill in ${g.length===1?"the highlighted field":`the ${g.length} highlighted fields`}, then press Resume.`});return}this.record(o,a,"driver"),this.markNav(),u?.requestSubmit?u.requestSubmit():u?.submit()}else this.record(o,a,"driver"),this.markNav(),l.click();await this.settle(),this.clearNav()}}pullOver(t,e,n,i){this.gateEl=t,this.gateAction={action:e,label:n};let s=t.tagName==="FORM"?t.querySelector('button:not([type="button"]):not([type="reset"]), input[type="submit"]')??t:t;this.ui.ring(s,"gate"),s!==t&&this.ui.moveCursor(s,`Pull over: ${n} is yours to press`),this.set("gate",{caption:`Pull over: ${n} is yours to press`,message:`This one's yours. Passenger Mode never presses ${rt[i]??"buttons like this"}. Press ${n} to finish, or end the trip.`})}onGateClick(t){if(this.state!=="gate"||!this.gateEl||!this.gateAction||!this.o.isTrusted(t))return;let e=t.target;if(!e||!this.gateEl.contains(e))return;let{action:n,label:i}=this.gateAction;this.gateAction=null,this.record(n,i,"passenger"),this.gateEl=null,this.ui.ring(null),this.markNav();let s=++this.run;this.state="driving",this.ui.render("driving",{plan:this.plan,caption:"Checking you arrived",destination:this.log?.destination,step:this.stepCount}),this.settle().then(()=>(this.clearNav(),this.loop(s)))}record(t,e,n){this.stepCount++,this.log?.steps.push({actionId:t.id,label:e,route:w(this.path(),this.o.manifest.routes),by:n,at:Date.now()})}arrive(t){this.ui.ring(null),this.finish("arrived"),this.set("arrived",{message:t})}stuck(t){this.ui.ring(null),this.finish("stuck",t),this.set("stuck",{message:t})}finish(t,e){this.run++,this.gateEl=null,this.clearNav(!0),this.log&&!this.log.outcome&&(this.log.outcome=t,this.log.reason=e,this.log.endedAt=Date.now(),this.o.onTrip?.(this.log))}settle(t){let e=this.o.window,n=t??this.o.settleMs;return new Promise(i=>{let s,o=()=>{d?.disconnect(),clearTimeout(c),i()},l=()=>{clearTimeout(s),s=setTimeout(o,n)},a=e.MutationObserver,d=a?new a(l):null;d?.observe(e.document.documentElement,{subtree:!0,childList:!0,attributes:!0});let c=setTimeout(o,Math.max(2500,n*4));l()})}linksHere(t){let e=t.getAttribute("href");if(!e||e.startsWith("#")||this.o.routeFrom==="hash"||typeof this.o.routeFrom=="function")return!1;try{let n=this.o.window.location,i=new URL(e,n.href);return i.origin===n.origin&&S(i.pathname+i.search)===S(n.pathname+n.search)}catch{return!1}}markNav(){try{this.o.storage?.setItem(k,JSON.stringify({log:this.log,plan:this.plan,stepCount:this.stepCount,at:Date.now(),origin:this.o.window.location.origin}))}catch{}}clearNav(t=!1){if(t||this.state==="driving")try{this.o.storage?.removeItem(k)}catch{}}restore(){let t=null;try{t=JSON.parse(this.o.storage?.getItem(k)??"null")}catch{t=null}if(t){try{this.o.storage?.removeItem(k)}catch{}Date.now()-t.at>15e3||t.origin!==this.o.window.location.origin||!t.log||(this.log=t.log,this.plan=t.plan,this.stepCount=t.stepCount??0,setTimeout(()=>this.drive(),400))}}listen(){let t=this.o.window,e=i=>{this.state!=="driving"||!this.o.isTrusted(i)||this.ui.owns(i.target)||this.takeWheel()};for(let i of["pointerdown","mousedown","keydown","wheel","touchstart"])t.addEventListener(i,e,{capture:!0,passive:!0}),this.detach.push(()=>t.removeEventListener(i,e,{capture:!0}));let n=i=>this.onGateClick(i);t.addEventListener("click",n,{capture:!0}),t.addEventListener("submit",n,{capture:!0}),this.detach.push(()=>{t.removeEventListener("click",n,{capture:!0}),t.removeEventListener("submit",n,{capture:!0})})}destroy(){this.run++,this.detach.forEach(t=>t()),this.detach=[]}};var at=new Set("a an the my me i to of for on in and or please want would like can you how do go get help with this that it our".split(" ")),O=r=>r.toLowerCase().replace(/[{}]/g," ").split(/[^a-z0-9]+/).filter(t=>t.length>1&&!at.has(t)).map(t=>t.replace(/(ing|es|s)$/,""));function F(r,t){let e=new Set(O(t));return O(r).filter(n=>e.has(n)).length}var v=class r{constructor(){h(this,"target",new Map)}pickTarget(t,e){let n=this.target.get(t);if(n)return n;let i=null,s=(l,a)=>{a>0&&(!i||a>i.score)&&(i={id:l,score:a})};for(let l of e.destinations){let a=F(t,l.goal);if(a!==0)for(let d of l.actionIds){let c=e.actions.find(u=>u.id===d);c&&s(d,a*2+F(t,c.label)+(c.kind!=="navigate"?.5:0))}}for(let l of e.actions){let a=F(t,l.label);a>0&&s(l.id,a+(l.kind!=="navigate"?.25:0))}let o=i;return o&&this.target.set(t,o.id),o?o.id:null}static on(t,e){return t.route!==null?t.route===e:!t.seenOn||e!==null&&t.seenOn.includes(e)}path(t,e,n){if(r.on(e,t))return[];let i=[{route:t,via:[]}],s=new Set([t]);for(;i.length;){let{route:o,via:l}=i.shift();for(let a of n.actions){if(a.kind!=="navigate"||!a.href||!r.on(a,o))continue;let d=R(a.href,n.routes);if(!d||s.has(d))continue;let c=[...l,a.id];if(r.on(e,d))return c;s.add(d),i.push({route:d,via:c})}}return null}async plan(t,e){let n=this.pickTarget(t.destination,e);if(!n)return null;let i=e.actions.find(a=>a.id===n),s=this.path(t.route,i,e);if(s===null)return null;let o=s.map(a=>`Open ${e.actions.find(c=>c.id===a).label}`);o.push(i.irreversible?`Stop at ${i.label}. You press it.`:`${i.kind==="submit"?"Submit":"Press"} ${i.label}`);let l=o.length;return{summary:`${i.label}: ${l} step${l===1?"":"s"}`,steps:o}}async next(t,e){let n=this.pickTarget(t.destination,e);if(!n)return{kind:"stuck",reason:"I don't know this road."};let i=e.actions.find(a=>a.id===n);if(t.history.some(a=>a.actionId===n))return{kind:"arrived",summary:`Done: ${i.label}.`};if(t.visible.some(a=>a.id===n))return{kind:"act",actionId:n,caption:i.irreversible?`Pull over: ${i.label} is yours to press`:`${i.kind==="submit"?"Submitting":"Pressing"} ${i.label}`};let o=this.path(t.route,i,e)?.[0];if(!o||!t.visible.some(a=>a.id===o))return{kind:"stuck",reason:"I don't know this road."};let l=e.actions.find(a=>a.id===o);return{kind:"act",actionId:o,caption:`Opening ${l.label}`}}};var f=class{constructor(t,e=(...i)=>fetch(...i),n={}){h(this,"endpoint",t);h(this,"fetchImpl",e);h(this,"headers",n)}async call(t,e){let n=await this.fetchImpl(this.endpoint,{method:"POST",headers:{"content-type":"application/json",...this.headers},body:JSON.stringify({op:t,view:e}),credentials:"same-origin"});if(!n.ok)throw new Error(`planner ${n.status}`);return await n.json()}async plan(t,e){return(await this.call("plan",t)).plan}async next(t,e){return(await this.call("next",t)).step}};var lt=`
1
+ "use strict";var PassengerMode=(()=>{var k=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var z=(r,t,e)=>t in r?k(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var j=(r,t)=>{for(var e in t)k(r,e,{get:t[e],enumerable:!0})},_=(r,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of q(t))!V.call(r,i)&&i!==e&&k(r,i,{get:()=>t[i],enumerable:!(n=U(t,i))||n.enumerable});return r};var G=r=>_(k({},"__esModule",{value:!0}),r);var h=(r,t,e)=>z(r,typeof t!="symbol"?t+"":t,e);var ht={};j(ht,{Driver:()=>v,HOSTED:()=>H,OverlayUI:()=>w,RemotePlanner:()=>m,RoutePlanner:()=>y,mount:()=>W});var K=[[/\b(pay|payment|purchase|checkout|check ?out|buy|place (your )?order|order now|confirm (order|purchase)|donate|subscribe|upgrade)\b/,"payment"],[/\b(cancel|end|stop)\b.{0,30}\b(plan|subscription|membership|club|trial|order|booking|reservation)\b|\bunsubscribe\b|\bdowngrade\b/,"cancels a plan or order"],[/\b(delete|destroy|erase|wipe|purge)\b|\bremove (account|user|member|card|payment)\b|\b(close|deactivate) (my |your )?account\b/,"deletion"],[/\b(change|update|reset|set)\b.{0,20}\b(password|e-?mail|2fa|passkey|two[- ]factor)\b|\bnew password\b/,"credential change"],[/\b(sign ?out|log ?out)\b/,"ends the session"]],Y=[[/\b(send|invite|share with)\b/,"sends to other people"],[/\b(publish|go live|make public|unpublish)\b/,"publishes"]],P=/[$€£¥₹]\s?\d|\d(?:[.,]\d{2})?\s?(?:usd|eur|gbp|dollars?)\b|\bper (?:month|year|seat)\b|\/\s?(?:mo|month|yr|year)\b/i,J=/\b(send|track|capture|gtag|analytics|log|report|emit|posthog|plausible|umami)\s*\(/gi;function A(r){return r.replace(/([a-z])([A-Z])/g,"$1 $2").replace(/[_\-./]+/g," ").toLowerCase()}function M(r,t=[]){let e=r.filter(Boolean).map(i=>A(i)).join(" | "),n=[e,...t.filter(Boolean).map(i=>A(i.replace(J,"(")))].join(" | ");for(let[i,s]of K)if(i.test(n))return s;for(let[i,s]of Y)if(i.test(e))return s;return r.some(i=>i&&P.test(i))?"payment":null}function C(r,t){let e=t??"";return/(log-?out|sign-?out|logoff|\/delete|\/destroy|\/remove|unsubscribe)/i.test(e)?M([r,e])??"acts on its own":/\b(sign ?out|log ?out)\b/i.test(A(r))?"ends the session":null}var I=['input[autocomplete^="cc-"]','input[name*="card" i]','input[name*="cvc" i]','input[name*="cvv" i]','input[id*="card-number" i]',"[data-stripe]",'iframe[src*="js.stripe.com"]','iframe[name^="__privateStripeFrame"]','iframe[src*="paypal.com"]','iframe[src*="braintree"]','iframe[src*="squareup.com"]','iframe[src*="adyen"]','iframe[title*="payment" i]'].join(","),X=r=>(r.textContent??"").replace(/\s+/g," ").trim();function $(r){let t=r.tagName==="FORM"?r.querySelector('button:not([type="button"]):not([type="reset"]), input[type="submit"]')??r:r,e=[t.getAttribute("aria-label"),t===r&&r.tagName==="FORM"?"":X(t),t.getAttribute("value"),t.getAttribute("title")].filter(Boolean).join(" ").slice(0,300);if(r.tagName==="A")return C(e,r.getAttribute("href"));let i=M([e]);if(i)return i;if(P.test(e))return"payment";let o=(r.tagName==="FORM"?r:r.closest("form"))??null;if(o){if(o.querySelector(I))return"payment";if(o.querySelector('input[type="password"]'))return"uses your password"}else if(r.ownerDocument?.querySelector(I))return"payment";return null}var f=r=>'"'+r.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"',L=r=>(r??"").replace(/\s+/g," ").trim().toLowerCase();function Z(r){if(!r.isConnected||r.closest("[hidden],[inert],[aria-hidden='true']"))return!1;let t=r.ownerDocument?.defaultView;for(let e=r;e;e=e.parentElement){let n=t?.getComputedStyle(e);if(n&&(n.display==="none"||n.visibility==="hidden"))return!1}return!r.disabled}var Q={navigate:"a[href]",click:"button, [role='button'], input[type='submit'], input[type='button']",submit:"form"};function R(r,t,e){let n=s=>!!s&&Z(s)&&!(e&&e.contains(s)),i=s=>{for(let o of Array.from(t.querySelectorAll(s))){let a=r.kind==="submit"&&o.tagName!=="FORM"?o.closest("form")??o:o;if(n(a))return a}return null};for(let s of r.selectors){let o=null;switch(s.by){case"testid":o=i(`[data-testid=${f(s.value)}],[data-test=${f(s.value)}],[data-cy=${f(s.value)}]`);break;case"id":o=i(`[id=${f(s.value)}]`);break;case"name":o=i(`[name=${f(s.value)}]`);break;case"aria-label":o=i(`[aria-label=${f(s.value)}]`);break;case"href":o=i(`a[href=${f(s.value)}]`);break;case"text":{if(s.value.startsWith("{"))break;let a=L(s.value),l=r.kind==="submit"?Array.from(t.querySelectorAll("form button, form input[type='submit']")).map(d=>({el:d.closest("form"),text:L(d.textContent||d.getAttribute("value"))})):Array.from(t.querySelectorAll(Q[r.kind])).map(d=>({el:d,text:L(d.getAttribute("aria-label")||d.textContent||d.getAttribute("value"))}));o=(l.find(d=>d.text===a&&n(d.el))??l.find(d=>d.text.startsWith(a)&&n(d.el)))?.el??null;break}}if(o)return o}return null}function S(r){return(r.getAttribute("aria-label")||r.textContent||r.getAttribute("value")||"").replace(/\s+/g," ").trim().slice(0,80)}function tt(r){let t=r.split("/").filter(Boolean).map(e=>/^\[\[\.\.\..+\]\]$/.test(e)?"(?:/.*)?":/^\[\.\.\..+\]$/.test(e)?"/.+":/^\[.+\]$/.test(e)||e.startsWith(":")||e.includes(":param")?"/[^/]+":e==="*"?"(?:/.*)?":"/"+e.replace(/[.*+?^${}()|\\]/g,"\\$&"));return new RegExp("^"+(t.join("")||"/?")+"/?$")}function et(r){return r.split("/").filter(Boolean).reduce((t,e)=>e.startsWith("[[...")||e==="*"?t:e.startsWith("[")||e.startsWith(":")?t+1:t+3,0)}var nt=/^(utm_\w+|fbclid|gclid|msclkid|ref|ttclid|mc_\w+)$/i;function F(r){let[t,e=""]=r.split("#")[0].split("?"),n=[...new URLSearchParams(e)].filter(([i])=>!nt.test(i)).sort(([i],[s])=>i.localeCompare(s));return(t||"/")+(n.length?"?"+new URLSearchParams(n).toString():"")}function T(r,t){let[e,n=""]=r.split("#")[0].split("?"),i=new URLSearchParams(n),s=null,o=-1;for(let a of t){let[l,c]=a.path.split("?");if(!tt(l).test(e||"/"))continue;let d=c?[...new URLSearchParams(c)]:[];if(!d.every(([g,b])=>i.get(g)===b))continue;let u=et(l)+d.length*2;u>o&&(s=a.path,o=u)}return s}function O(r,t){if(!r.startsWith("/")&&!r.startsWith("#/"))return null;let e=r.startsWith("#")?r.slice(1):r;return T(e.replace(/:param/g,"x"),t)}var E="passenger-mode:trip",it=300,rt=/\b(all|every|each|everyone|everybody)\b.{0,40}\b(users?|members?|people|contacts?|customers?|items?|posts?|messages?|accounts?|orders?|friends?|followers?)\b|\b\d{2,}\s+times\b|\bmass\b|\bbulk\b/i,st={payment:"pay buttons","cancels a plan or order":"cancel buttons",deletion:"delete buttons","sends to other people":"send buttons",publishes:"publish buttons","credential change":"password or email changes","ends the session":"sign-out buttons","uses your password":"buttons that use your password"};function ot(){let r=globalThis.crypto;return r?.randomUUID?r.randomUUID():"t"+Date.now().toString(36)+Math.random().toString(36).slice(2,10)}var at={render(){},async moveCursor(){},ring(){},owns:()=>!1},v=class{constructor(t){h(this,"state","off");h(this,"plan",null);h(this,"log",null);h(this,"o");h(this,"ui");h(this,"run",0);h(this,"gateEl",null);h(this,"gateAction",null);h(this,"stepCount",0);h(this,"detach",[]);if(this.o={routeFrom:"path",isTrusted:e=>e.isTrusted,maxSteps:15,timeLimitMs:3*6e4,settleMs:250,...t},this.ui=t.ui??at,this.o.storage===void 0)try{this.o.storage=t.window.sessionStorage}catch{this.o.storage=null}this.listen(),this.restore()}get doc(){return this.o.window.document}path(){let t=this.o.window.location;return typeof this.o.routeFrom=="function"?this.o.routeFrom():this.o.routeFrom==="hash"?t.hash.replace(/^#/,"")||"/":t.pathname+t.search}set(t,e={}){this.state=t,this.ui.render(t,{plan:this.plan,destination:this.log?.destination,step:this.stepCount,...e})}open(t){return this.o.isTrusted(t)?(this.state==="off"&&this.set("open"),!0):!1}close(){this.finish("ended"),this.plan=null,this.set("off")}async planTrip(t,e){if(!this.o.isTrusted(e))return null;let n=t.trim().slice(0,it);if(!n)return null;if(this.log={id:ot(),destination:n,startedAt:Date.now(),steps:[]},this.stepCount=0,rt.test(n))return this.set("stuck",{message:"Passenger Mode does one thing per trip. Try a single destination."}),null;this.set("planning");let i=null,s=this.view();try{i=await this.o.planner.plan(s,this.o.manifest)}catch{return this.set("stuck",{message:"I can't plan trips right now. Try again in a moment."}),null}return i?(this.plan=i,this.set("ready"),i):(this.set("stuck",{message:"I don't know this road. Try saying it another way."}),null)}go(t){return!this.o.isTrusted(t)||this.state!=="ready"?!1:(this.log.startedAt=Date.now(),this.drive(),!0)}notThis(){this.plan=null,this.log=null,this.set("open")}takeWheel(){this.state==="driving"&&(this.run++,this.ui.ring(null),this.set("paused",{message:"Nothing moves until you resume."}))}resume(t){return!this.o.isTrusted(t)||this.state!=="paused"&&this.state!=="handoff"?!1:(this.drive(),!0)}end(){this.finish(this.state==="arrived"?"arrived":this.state==="paused"?"taken-over":"ended"),this.plan=null,this.set("open")}rate(t){this.log&&(this.log.rating=t?"up":"down",this.o.onTrip?.(this.log))}view(){let t=T(this.path(),this.o.manifest.routes),e=[];for(let n of this.o.manifest.actions){if(n.route!==null&&n.route!==t||n.route===null&&n.seenOn&&t!==null&&!n.seenOn.includes(t))continue;let i=this.find(n);if(!i)continue;let s=n.label.startsWith("{");e.push({id:n.id,label:s?S(i):n.label,kind:n.kind,irreversible:n.irreversible,runtimeLabel:s})}return{destination:this.log?.destination??"",route:t,path:this.path(),visible:e,history:(this.log?.steps??[]).map(n=>({actionId:n.actionId,label:n.label,route:n.route})),...this.plan?{approvedPlan:this.plan.steps.slice(0,8)}:{}}}find(t){let e=R(t,this.doc);return e&&!this.ui.owns(e)?e:null}drive(){let t=++this.run;this.set("driving",{caption:"Starting the trip"}),this.loop(t)}live(t){return t===this.run&&this.state==="driving"}async loop(t){await this.settle();let e=!1;for(;this.live(t);){if(this.stepCount>=this.o.maxSteps)return this.stuck("That's more steps than one trip allows, so I stopped.");if(Date.now()-this.log.startedAt>this.o.timeLimitMs)return this.stuck("This trip is taking too long, so I stopped.");let n=this.view(),i;try{i=await this.o.planner.next(n,this.o.manifest)}catch{return this.stuck("I lost the road. Try again in a moment.")}if(!this.live(t))return;if(i.kind==="arrived")return this.arrive(i.summary);if(i.kind==="stuck"){if(!e){e=!0,await this.settle(1500);continue}let u=this.log.steps.at(-1);return n.route===null&&u?.by==="driver"?this.arrive(`This is where \u201C${u.label}\u201D leads. I don't know this page, so take it from here.`):this.stuck(i.reason)}e=!1;let s=this.log.steps.slice(-2);if(s.length===2&&s.every(u=>u.actionId===i.actionId))return this.stuck("I'm going in circles, so I stopped. Try saying it another way.");let o=this.o.manifest.actions.find(u=>u.id===i.actionId);if(!o||!n.visible.some(u=>u.id===o.id))return this.stuck("I don't know this road.");let a=this.find(o);if(!a)return this.stuck("I don't know this road.");if(o.kind==="navigate"&&this.linksHere(a))return this.arrive(i.caption??"You're already here.");let l=o.label.startsWith("{")?S(a):o.label,c=o.irreversible?o.irreversibleReason:$(a),d=c?`Pull over: ${l} is yours to press`:i.caption??`Pressing ${l}`;if(a.scrollIntoView?.({block:"center",behavior:"smooth"}),this.ui.ring(a,c?"gate":"drive"),await this.ui.moveCursor(a,d),!this.live(t))return;if(c)return this.pullOver(a,o,l,c);if(o.kind==="submit"){let u=a.tagName==="FORM"?a:a.closest("form"),g=u?Array.from(u.querySelectorAll("input[required],select[required],textarea[required]")).filter(b=>!b.value):[];if(g.length){this.ui.ring(u,"handoff"),g[0].scrollIntoView?.({block:"center"}),g[0].focus?.({preventScroll:!0}),this.set("handoff",{message:`Your turn: fill in ${g.length===1?"the highlighted field":`the ${g.length} highlighted fields`}, then press Resume.`});return}this.record(o,l,"driver"),this.markNav(),u?.requestSubmit?u.requestSubmit():u?.submit()}else this.record(o,l,"driver"),this.markNav(),a.click();await this.settle(),this.clearNav()}}pullOver(t,e,n,i){this.gateEl=t,this.gateAction={action:e,label:n};let s=t.tagName==="FORM"?t.querySelector('button:not([type="button"]):not([type="reset"]), input[type="submit"]')??t:t;this.ui.ring(s,"gate"),s!==t&&this.ui.moveCursor(s,`Pull over: ${n} is yours to press`),this.set("gate",{caption:`Pull over: ${n} is yours to press`,message:`This one's yours. Passenger Mode never presses ${st[i]??"buttons like this"}. Press ${n} to finish, or end the trip.`})}onGateClick(t){if(this.state!=="gate"||!this.gateEl||!this.gateAction||!this.o.isTrusted(t))return;let e=t.target;if(!e||!this.gateEl.contains(e))return;let{action:n,label:i}=this.gateAction;this.gateAction=null,this.record(n,i,"passenger"),this.gateEl=null,this.ui.ring(null),this.markNav();let s=++this.run;this.state="driving",this.ui.render("driving",{plan:this.plan,caption:"Checking you arrived",destination:this.log?.destination,step:this.stepCount}),this.settle().then(()=>(this.clearNav(),this.loop(s)))}record(t,e,n){this.stepCount++,this.log?.steps.push({actionId:t.id,label:e,route:T(this.path(),this.o.manifest.routes),by:n,at:Date.now()})}arrive(t){this.ui.ring(null),this.finish("arrived"),this.set("arrived",{message:t})}stuck(t){this.ui.ring(null),this.finish("stuck",t),this.set("stuck",{message:t})}finish(t,e){this.run++,this.gateEl=null,this.clearNav(!0),this.log&&!this.log.outcome&&(this.log.outcome=t,this.log.reason=e,this.log.endedAt=Date.now(),this.o.onTrip?.(this.log))}settle(t){let e=this.o.window,n=t??this.o.settleMs;return new Promise(i=>{let s,o=()=>{c?.disconnect(),clearTimeout(d),i()},a=()=>{clearTimeout(s),s=setTimeout(o,n)},l=e.MutationObserver,c=l?new l(a):null;c?.observe(e.document.documentElement,{subtree:!0,childList:!0,attributes:!0});let d=setTimeout(o,Math.max(2500,n*4));a()})}linksHere(t){let e=t.getAttribute("href");if(!e||e.startsWith("#")||this.o.routeFrom==="hash"||typeof this.o.routeFrom=="function")return!1;try{let n=this.o.window.location,i=new URL(e,n.href);return i.origin===n.origin&&F(i.pathname+i.search)===F(n.pathname+n.search)}catch{return!1}}markNav(){try{this.o.storage?.setItem(E,JSON.stringify({log:this.log,plan:this.plan,stepCount:this.stepCount,at:Date.now(),origin:this.o.window.location.origin}))}catch{}}clearNav(t=!1){if(t||this.state==="driving")try{this.o.storage?.removeItem(E)}catch{}}restore(){let t=null;try{t=JSON.parse(this.o.storage?.getItem(E)??"null")}catch{t=null}if(t){try{this.o.storage?.removeItem(E)}catch{}Date.now()-t.at>15e3||t.origin!==this.o.window.location.origin||!t.log||(this.log=t.log,this.plan=t.plan,this.stepCount=t.stepCount??0,setTimeout(()=>this.drive(),400))}}listen(){let t=this.o.window,e=i=>{this.state!=="driving"||!this.o.isTrusted(i)||this.ui.owns(i.target)||this.takeWheel()};for(let i of["pointerdown","mousedown","keydown","wheel","touchstart"])t.addEventListener(i,e,{capture:!0,passive:!0}),this.detach.push(()=>t.removeEventListener(i,e,{capture:!0}));let n=i=>this.onGateClick(i);t.addEventListener("click",n,{capture:!0}),t.addEventListener("submit",n,{capture:!0}),this.detach.push(()=>{t.removeEventListener("click",n,{capture:!0}),t.removeEventListener("submit",n,{capture:!0})})}destroy(){this.run++,this.detach.forEach(t=>t()),this.detach=[]}};var lt=new Set("a an the my me i to of for on in and or please want would like can you how do go get help with this that it our".split(" ")),B=r=>r.toLowerCase().replace(/[{}]/g," ").split(/[^a-z0-9]+/).filter(t=>t.length>1&&!lt.has(t)).map(t=>t.replace(/(ing|es|s)$/,""));function D(r,t){let e=new Set(B(t));return B(r).filter(n=>e.has(n)).length}var y=class r{constructor(){h(this,"target",new Map)}pickTarget(t,e){let n=this.target.get(t);if(n)return n;let i=null,s=(a,l)=>{l>0&&(!i||l>i.score)&&(i={id:a,score:l})};for(let a of e.destinations){let l=D(t,a.goal);if(l!==0)for(let c of a.actionIds){let d=e.actions.find(u=>u.id===c);d&&s(c,l*2+D(t,d.label)+(d.kind!=="navigate"?.5:0))}}for(let a of e.actions){let l=D(t,a.label);l>0&&s(a.id,l+(a.kind!=="navigate"?.25:0))}let o=i;return o&&this.target.set(t,o.id),o?o.id:null}static on(t,e){return t.route!==null?t.route===e:!t.seenOn||e!==null&&t.seenOn.includes(e)}path(t,e,n){if(r.on(e,t))return[];let i=[{route:t,via:[]}],s=new Set([t]);for(;i.length;){let{route:o,via:a}=i.shift();for(let l of n.actions){if(l.kind!=="navigate"||!l.href||!r.on(l,o))continue;let c=O(l.href,n.routes);if(!c||s.has(c))continue;let d=[...a,l.id];if(r.on(e,c))return d;s.add(c),i.push({route:c,via:d})}}return null}async plan(t,e){let n=this.pickTarget(t.destination,e);if(!n)return null;let i=e.actions.find(l=>l.id===n),s=this.path(t.route,i,e);if(s===null)return null;let o=s.map(l=>`Open ${e.actions.find(d=>d.id===l).label}`);o.push(i.irreversible?`Stop at ${i.label}. You press it.`:`${i.kind==="submit"?"Submit":"Press"} ${i.label}`);let a=o.length;return{summary:`${i.label}: ${a} step${a===1?"":"s"}`,steps:o}}async next(t,e){let n=this.pickTarget(t.destination,e);if(!n)return{kind:"stuck",reason:"I don't know this road."};let i=e.actions.find(l=>l.id===n);if(t.history.some(l=>l.actionId===n))return{kind:"arrived",summary:`Done: ${i.label}.`};if(t.visible.some(l=>l.id===n))return{kind:"act",actionId:n,caption:i.irreversible?`Pull over: ${i.label} is yours to press`:`${i.kind==="submit"?"Submitting":"Pressing"} ${i.label}`};let o=this.path(t.route,i,e)?.[0];if(!o||!t.visible.some(l=>l.id===o))return{kind:"stuck",reason:"I don't know this road."};let a=e.actions.find(l=>l.id===o);return{kind:"act",actionId:o,caption:`Opening ${a.label}`}}};var m=class{constructor(t,e=(...i)=>fetch(...i),n={}){h(this,"endpoint",t);h(this,"fetchImpl",e);h(this,"headers",n)}async call(t,e){let n=await this.fetchImpl(this.endpoint,{method:"POST",headers:{"content-type":"application/json",...this.headers},body:JSON.stringify({op:t,view:e}),credentials:"same-origin"});if(!n.ok)throw new Error(`planner ${n.status}`);return await n.json()}async plan(t,e){return(await this.call("plan",t)).plan}async next(t,e){return(await this.call("next",t)).step}};var pt=`
2
2
  :host{all:initial}
3
3
  *{box-sizing:border-box;font-family:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
4
4
  .layer{position:fixed;inset:0;pointer-events:none;z-index:2147483646}
@@ -49,4 +49,4 @@ ol li{display:flex;gap:10px;align-items:center}
49
49
  [hidden]{display:none!important}
50
50
  @media (prefers-reduced-motion:reduce){.cursor,.ring,.knob,.track{transition:none}.ring[data-tone="gate"]{animation:none}}
51
51
  @media (max-width:600px){.cursor svg{display:none}.panel{left:12px;right:12px;width:auto}.bar{left:12px;right:12px;transform:none;max-width:none}}
52
- `,pt='<svg width="30" height="34" viewBox="0 0 30 34" aria-hidden="true"><path d="M3 2 L26 19 L15 21 L10 32 Z" fill="#2D5BFF" fill-opacity=".6" stroke="#fff" stroke-width="2" stroke-linejoin="round"/></svg>',B='<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#111418" stroke-width="1.6" stroke-linejoin="round" aria-hidden="true"><path d="M6 9v8H3V9zM6 9l3.5-6.5c1.2 0 2 .9 1.8 2.1L10.8 8H16a1.5 1.5 0 0 1 1.5 1.8l-1.2 6A1.5 1.5 0 0 1 14.8 17H6"/></svg>';function p(r,t,e={},n){let i=r.createElement(t);for(let[s,o]of Object.entries(e))i.setAttribute(s,o);return n!==void 0&&(i.textContent=n),i}var y=class{constructor(t,e,n={}){h(this,"on",e);h(this,"opts",n);h(this,"host");h(this,"root");h(this,"layer");h(this,"switchBtn");h(this,"panel");h(this,"bar");h(this,"cursor");h(this,"cap");h(this,"ringEl");h(this,"ringTarget",null);h(this,"raf",0);h(this,"doc");h(this,"win");h(this,"destination","");this.win=t,this.doc=t.document;let i=this.doc;this.host=p(i,"div",{id:"passenger-mode","data-passenger-mode":""}),this.root=this.host.attachShadow({mode:"open"});let s=p(i,"style");s.textContent=lt,this.layer=p(i,"div",{class:"layer"}),n.offset?.right!=null&&this.layer.style.setProperty("--pm-right",`${n.offset.right}px`),n.offset?.bottom!=null&&this.layer.style.setProperty("--pm-bottom",`${n.offset.bottom}px`),this.ringEl=p(i,"div",{class:"ring",hidden:""}),this.cursor=p(i,"div",{class:"cursor",hidden:"","aria-hidden":"true"}),this.cursor.innerHTML=pt,this.cap=p(i,"span",{class:"cap"}),this.cursor.appendChild(this.cap),this.switchBtn=p(i,"button",{class:"switch auto",type:"button",role:"switch","aria-checked":"false"}),this.switchBtn.innerHTML=`<span class="t"><b>Passenger mode</b><span>Say where you're going. We'll do the clicking.</span></span><span class="track"><span class="knob"></span></span>`,this.switchBtn.addEventListener("click",o=>this.switchBtn.getAttribute("aria-checked")==="true"?this.on.close():this.on.open(o)),this.panel=p(i,"div",{class:"panel auto",role:"dialog","aria-label":"Passenger mode",hidden:""}),this.bar=p(i,"div",{class:"bar auto",role:"status","aria-live":"polite",hidden:""}),this.layer.append(this.ringEl,this.cursor,this.panel,this.bar,this.switchBtn),n.badge&&this.layer.appendChild(p(i,"span",{class:"badge"},"by Passenger Mode")),this.root.append(s,this.layer),i.body.appendChild(this.host)}owns(t){return t?t===this.host||this.host.contains(t)?!0:t.getRootNode?.()===this.root:!1}destroy(){this.win.cancelAnimationFrame?.(this.raf),this.host.remove()}render(t,e){let n=this.doc;e.destination&&(this.destination=e.destination),this.switchBtn.setAttribute("aria-checked",t==="off"?"false":"true");let i=t==="driving"||t==="paused"||t==="gate"||t==="handoff";if(this.cursor.hidden=!i||t==="handoff",i||this.ring(null),this.panel.replaceChildren(),this.panel.hidden=!(t==="open"||t==="planning"||t==="ready"||t==="stuck"||t==="arrived"),t==="open"&&this.renderOpen(),t==="planning"&&this.panel.append(p(n,"h2",{},"Planning the route\u2026"),p(n,"p",{class:"small"},this.destination)),t==="ready"&&e.plan&&this.renderPlan(e.plan),t==="stuck"){let s=p(n,"button",{class:"primary",type:"button"},"Try another destination");s.addEventListener("click",()=>this.on.notThis()),this.panel.append(p(n,"h2",{},"Pulled over"),p(n,"p",{},e.message??"I don't know this road."),s)}if(t==="arrived"&&this.renderArrived(e.message??""),this.bar.replaceChildren(),this.bar.hidden=!i,i){this.bar.dataset.tone=t==="driving"?"drive":t;let s=t==="driving"?"Passenger mode is driving":t==="paused"?"You have the wheel":t==="gate"?"Pulled over":"Your turn";this.bar.append(p(n,"span",{class:"dot"}),p(n,"b",{},s));let o=t==="driving"?e.caption:e.message;if(o&&this.bar.append(p(n,"span",{class:"msg"},o)),t==="driving"){let a=p(n,"button",{type:"button"},"Take the wheel");a.addEventListener("click",()=>this.on.takeWheel()),this.bar.append(a)}if(t==="paused"||t==="handoff"){let a=p(n,"button",{type:"button",class:"resume"},"Resume");a.addEventListener("click",d=>this.on.resume(d)),this.bar.append(a)}let l=p(n,"button",{type:"button"},"End trip");l.addEventListener("click",()=>this.on.end()),this.bar.append(l)}t==="driving"&&e.caption&&(this.cap.textContent=e.caption)}renderOpen(){let t=this.doc,e=p(t,"form",{class:"row"}),n=p(t,"label",{for:"pm-dest"},"Where to?"),i=p(t,"h2");i.appendChild(n);let s=p(t,"input",{id:"pm-dest",name:"destination",autocomplete:"off",maxlength:"300",placeholder:"Say it your way: cancel my plan, invite Jo\u2026"}),o=p(t,"button",{class:"primary",type:"submit"},"Plan trip");e.append(s,o),e.addEventListener("submit",a=>{a.preventDefault(),s.value.trim()&&this.on.plan(s.value,a)}),this.panel.append(i,e);let l=(this.opts.suggestions??[]).slice(0,4);if(l.length){let a=p(t,"div",{class:"row"});for(let d of l){let c=p(t,"button",{class:"chip",type:"button"},d);c.addEventListener("click",u=>this.on.plan(d,u)),a.appendChild(c)}this.panel.append(p(t,"p",{class:"small"},"Popular here"),a)}this.panel.append(p(t,"p",{class:"small"},"Nothing moves until you press Go. Any click takes the wheel.")),setTimeout(()=>s.focus(),0)}renderPlan(t){let e=this.doc;this.panel.append(p(e,"p",{class:"small"},`Where to: ${this.destination}`),p(e,"h2",{},t.summary));let n=p(e,"ol");t.steps.forEach((l,a)=>{let d=p(e,"li"),c=/you press it/i.test(l);d.append(p(e,"span",{class:c?"n gate":"n"},String(a+1)),e.createTextNode(l)),n.appendChild(d)});let i=p(e,"button",{class:"primary go",type:"button"},"Go");i.addEventListener("click",l=>this.on.go(l));let s=p(e,"button",{class:"ghost",type:"button"},"Not this");s.addEventListener("click",()=>this.on.notThis());let o=p(e,"div",{class:"row"});o.append(i,s),this.panel.append(n,o),setTimeout(()=>i.focus(),0)}renderArrived(t){let e=this.doc,n=p(e,"div",{class:"done"});n.innerHTML='<svg width="28" height="28" viewBox="0 0 28 28" aria-hidden="true"><circle cx="14" cy="14" r="13" fill="#2D5BFF"/><path d="M8.5 14.5l3.5 3.5 7.5-8" stroke="#fff" stroke-width="2.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>',n.appendChild(p(e,"h2",{},"You're here"));let i=p(e,"div",{class:"row"}),s=l=>{let a=p(e,"button",{class:"thumb",type:"button","aria-label":l?"Good trip":"Bad trip","aria-pressed":"false"});return a.innerHTML=l?B:B.replace("<svg ",'<svg style="transform:rotate(180deg)" '),a.addEventListener("click",()=>{this.on.rate(l),i.querySelectorAll(".thumb").forEach(d=>d.setAttribute("aria-pressed","false")),a.setAttribute("aria-pressed","true")}),a},o=p(e,"button",{class:"ghost",type:"button"},"Done");o.addEventListener("click",()=>this.on.end()),i.append(p(e,"span",{class:"small"},"How was the trip?"),s(!0),s(!1),o),this.panel.append(n,p(e,"p",{},t),i)}moveCursor(t,e){this.cap.textContent=e,this.cursor.hidden=!1;let n=t.getBoundingClientRect(),i=Math.max(4,Math.min(n.left+Math.min(n.width*.6,60),(this.win.innerWidth||1024)-40)),s=Math.max(4,n.top+Math.min(n.height*.6,24));this.cursor.style.transform=`translate(${i}px, ${s}px)`;let o=this.win.matchMedia?.("(prefers-reduced-motion: reduce)").matches;return new Promise(l=>setTimeout(l,o?300:900))}ring(t,e="drive"){if(this.ringTarget=t,this.ringEl.hidden=!t,!t){this.win.cancelAnimationFrame?.(this.raf);return}this.ringEl.dataset.tone=e;let n=()=>{if(!this.ringTarget)return;let i=this.ringTarget.getBoundingClientRect();Object.assign(this.ringEl.style,{left:`${i.left-4}px`,top:`${i.top-4}px`,width:`${i.width+8}px`,height:`${i.height+8}px`}),this.raf=this.win.requestAnimationFrame?.(n)??0};n()}};var N="https://passengermode.com";function dt(r){try{return r.top!==r.self}catch{return!0}}async function H(r){let t=r.window??window;if(dt(t)&&!r.allowInFrame)return console.info("Passenger Mode is off inside frames."),null;let e=(r.apiBase??N).replace(/\/$/,""),n=r.key?`key=${encodeURIComponent(r.key)}`:"";if(r.key)try{let u=await(await t.fetch(`${e}/api/v1/status?${n}`,{credentials:"omit"})).json();if(!u?.on)return console.info(`Passenger Mode is off for this site${u?.reason?`: ${u.reason}`:""}.`),null}catch{return null}let i=r.manifest??(r.key?`${e}/api/v1/manifest?${n}`:void 0);if(!i)throw new Error("Passenger Mode needs a manifest or a key.");let s=typeof i=="string"?await(await t.fetch(i,{credentials:r.key?"omit":"same-origin"})).json():i,o=r.planner??(r.key?new f(`${e}/api/v1/plan`,void 0,{"x-passenger-key":r.key}):r.endpoint?new f(r.endpoint):new v),l=r.key?u=>{try{t.fetch(`${e}/api/v1/trips`,{method:"POST",headers:{"content-type":"application/json","x-passenger-key":r.key},body:JSON.stringify(u),credentials:"omit",keepalive:!0}).catch(()=>{})}catch{}}:null,a=u=>{l?.(u),r.onTrip?.(u)},d,c=new y(t,{open:u=>d.open(u),close:()=>d.close(),plan:(u,g)=>{d.planTrip(u,g)},go:u=>d.go(u),notThis:()=>d.notThis(),takeWheel:()=>d.takeWheel(),resume:u=>d.resume(u),end:()=>d.end(),rate:u=>d.rate(u)},{suggestions:s.destinations.map(u=>u.goal),badge:r.badge??!0,offset:r.offset});return d=new b({manifest:s,planner:o,window:t,ui:c,routeFrom:r.routeFrom,onTrip:a}),{driver:d,ui:c,destroy(){d.destroy(),c.destroy()}}}if(typeof document<"u"){let r=document.currentScript;if(r?.dataset.manifest||r?.dataset.key){let t=n=>n!=null&&n!==""&&!Number.isNaN(Number(n))?Number(n):void 0,e=()=>{H({key:r.dataset.key,apiBase:r.dataset.api,manifest:r.dataset.manifest,endpoint:r.dataset.endpoint,routeFrom:r.dataset.route??"path",badge:r.dataset.badge!=="false",offset:{right:t(r.dataset.right),bottom:t(r.dataset.bottom)}})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()}}return G(ut);})();
52
+ `,ct='<svg width="30" height="34" viewBox="0 0 30 34" aria-hidden="true"><path d="M3 2 L26 19 L15 21 L10 32 Z" fill="#2D5BFF" fill-opacity=".6" stroke="#fff" stroke-width="2" stroke-linejoin="round"/></svg>',N='<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#111418" stroke-width="1.6" stroke-linejoin="round" aria-hidden="true"><path d="M6 9v8H3V9zM6 9l3.5-6.5c1.2 0 2 .9 1.8 2.1L10.8 8H16a1.5 1.5 0 0 1 1.5 1.8l-1.2 6A1.5 1.5 0 0 1 14.8 17H6"/></svg>';function p(r,t,e={},n){let i=r.createElement(t);for(let[s,o]of Object.entries(e))i.setAttribute(s,o);return n!==void 0&&(i.textContent=n),i}var w=class{constructor(t,e,n={}){h(this,"on",e);h(this,"opts",n);h(this,"host");h(this,"root");h(this,"layer");h(this,"switchBtn");h(this,"panel");h(this,"bar");h(this,"cursor");h(this,"cap");h(this,"ringEl");h(this,"ringTarget",null);h(this,"raf",0);h(this,"doc");h(this,"win");h(this,"destination","");this.win=t,this.doc=t.document;let i=this.doc;this.host=p(i,"div",{id:"passenger-mode","data-passenger-mode":""}),this.root=this.host.attachShadow({mode:"open"});let s=p(i,"style");s.textContent=pt,this.layer=p(i,"div",{class:"layer"}),n.offset?.right!=null&&this.layer.style.setProperty("--pm-right",`${n.offset.right}px`),n.offset?.bottom!=null&&this.layer.style.setProperty("--pm-bottom",`${n.offset.bottom}px`),this.ringEl=p(i,"div",{class:"ring",hidden:""}),this.cursor=p(i,"div",{class:"cursor",hidden:"","aria-hidden":"true"}),this.cursor.innerHTML=ct,this.cap=p(i,"span",{class:"cap"}),this.cursor.appendChild(this.cap),this.switchBtn=p(i,"button",{class:"switch auto",type:"button",role:"switch","aria-checked":"false"}),this.switchBtn.innerHTML=`<span class="t"><b>Passenger mode</b><span>Say where you're going. We'll do the clicking.</span></span><span class="track"><span class="knob"></span></span>`,this.switchBtn.addEventListener("click",o=>this.switchBtn.getAttribute("aria-checked")==="true"?this.on.close():this.on.open(o)),this.panel=p(i,"div",{class:"panel auto",role:"dialog","aria-label":"Passenger mode",hidden:""}),this.bar=p(i,"div",{class:"bar auto",role:"status","aria-live":"polite",hidden:""}),this.layer.append(this.ringEl,this.cursor,this.panel,this.bar,this.switchBtn),n.badge&&this.layer.appendChild(p(i,"span",{class:"badge"},"by Passenger Mode")),this.root.append(s,this.layer),i.body.appendChild(this.host)}owns(t){return t?t===this.host||this.host.contains(t)?!0:t.getRootNode?.()===this.root:!1}destroy(){this.win.cancelAnimationFrame?.(this.raf),this.host.remove()}render(t,e){let n=this.doc;e.destination&&(this.destination=e.destination),this.switchBtn.setAttribute("aria-checked",t==="off"?"false":"true");let i=t==="driving"||t==="paused"||t==="gate"||t==="handoff";if(this.cursor.hidden=!i||t==="handoff",i||this.ring(null),this.panel.replaceChildren(),this.panel.hidden=!(t==="open"||t==="planning"||t==="ready"||t==="stuck"||t==="arrived"),t==="open"&&this.renderOpen(),t==="planning"&&this.panel.append(p(n,"h2",{},"Planning the route\u2026"),p(n,"p",{class:"small"},this.destination)),t==="ready"&&e.plan&&this.renderPlan(e.plan),t==="stuck"){let s=p(n,"button",{class:"primary",type:"button"},"Try another destination");s.addEventListener("click",()=>this.on.notThis()),this.panel.append(p(n,"h2",{},"Pulled over"),p(n,"p",{},e.message??"I don't know this road."),s)}if(t==="arrived"&&this.renderArrived(e.message??""),this.bar.replaceChildren(),this.bar.hidden=!i,i){this.bar.dataset.tone=t==="driving"?"drive":t;let s=t==="driving"?"Passenger mode is driving":t==="paused"?"You have the wheel":t==="gate"?"Pulled over":"Your turn";this.bar.append(p(n,"span",{class:"dot"}),p(n,"b",{},s));let o=t==="driving"?e.caption:e.message;if(o&&this.bar.append(p(n,"span",{class:"msg"},o)),t==="driving"){let l=p(n,"button",{type:"button"},"Take the wheel");l.addEventListener("click",()=>this.on.takeWheel()),this.bar.append(l)}if(t==="paused"||t==="handoff"){let l=p(n,"button",{type:"button",class:"resume"},"Resume");l.addEventListener("click",c=>this.on.resume(c)),this.bar.append(l)}let a=p(n,"button",{type:"button"},"End trip");a.addEventListener("click",()=>this.on.end()),this.bar.append(a)}t==="driving"&&e.caption&&(this.cap.textContent=e.caption)}renderOpen(){let t=this.doc,e=p(t,"form",{class:"row"}),n=p(t,"label",{for:"pm-dest"},"Where to?"),i=p(t,"h2");i.appendChild(n);let s=(this.opts.suggestions??[])[0],o=s?`Say it your way, e.g. ${s.charAt(0).toLowerCase()}${s.slice(1)}`:"Say it your way",a=p(t,"input",{id:"pm-dest",name:"destination",autocomplete:"off",maxlength:"300",placeholder:o}),l=p(t,"button",{class:"primary",type:"submit"},"Plan trip");e.append(a,l),e.addEventListener("submit",d=>{d.preventDefault(),a.value.trim()&&this.on.plan(a.value,d)}),this.panel.append(i,e);let c=(this.opts.suggestions??[]).slice(0,4);if(c.length){let d=p(t,"div",{class:"row"});for(let u of c){let g=p(t,"button",{class:"chip",type:"button"},u);g.addEventListener("click",b=>this.on.plan(u,b)),d.appendChild(g)}this.panel.append(p(t,"p",{class:"small"},"Popular here"),d)}this.panel.append(p(t,"p",{class:"small"},"Nothing moves until you press Go. Any click takes the wheel.")),setTimeout(()=>a.focus(),0)}renderPlan(t){let e=this.doc;this.panel.append(p(e,"p",{class:"small"},`Where to: ${this.destination}`),p(e,"h2",{},t.summary));let n=p(e,"ol");t.steps.forEach((a,l)=>{let c=p(e,"li"),d=/you press it/i.test(a);c.append(p(e,"span",{class:d?"n gate":"n"},String(l+1)),e.createTextNode(a)),n.appendChild(c)});let i=p(e,"button",{class:"primary go",type:"button"},"Go");i.addEventListener("click",a=>this.on.go(a));let s=p(e,"button",{class:"ghost",type:"button"},"Not this");s.addEventListener("click",()=>this.on.notThis());let o=p(e,"div",{class:"row"});o.append(i,s),this.panel.append(n,o),setTimeout(()=>i.focus(),0)}renderArrived(t){let e=this.doc,n=p(e,"div",{class:"done"});n.innerHTML='<svg width="28" height="28" viewBox="0 0 28 28" aria-hidden="true"><circle cx="14" cy="14" r="13" fill="#2D5BFF"/><path d="M8.5 14.5l3.5 3.5 7.5-8" stroke="#fff" stroke-width="2.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>',n.appendChild(p(e,"h2",{},"You're here"));let i=p(e,"div",{class:"row"}),s=a=>{let l=p(e,"button",{class:"thumb",type:"button","aria-label":a?"Good trip":"Bad trip","aria-pressed":"false"});return l.innerHTML=a?N:N.replace("<svg ",'<svg style="transform:rotate(180deg)" '),l.addEventListener("click",()=>{this.on.rate(a),i.querySelectorAll(".thumb").forEach(c=>c.setAttribute("aria-pressed","false")),l.setAttribute("aria-pressed","true")}),l},o=p(e,"button",{class:"ghost",type:"button"},"Done");o.addEventListener("click",()=>this.on.end()),i.append(p(e,"span",{class:"small"},"How was the trip?"),s(!0),s(!1),o),this.panel.append(n,p(e,"p",{},t),i)}moveCursor(t,e){this.cap.textContent=e,this.cursor.hidden=!1;let n=t.getBoundingClientRect(),i=Math.max(4,Math.min(n.left+Math.min(n.width*.6,60),(this.win.innerWidth||1024)-40)),s=Math.max(4,n.top+Math.min(n.height*.6,24));this.cursor.style.transform=`translate(${i}px, ${s}px)`;let o=this.win.matchMedia?.("(prefers-reduced-motion: reduce)").matches;return new Promise(a=>setTimeout(a,o?300:900))}ring(t,e="drive"){if(this.ringTarget=t,this.ringEl.hidden=!t,!t){this.win.cancelAnimationFrame?.(this.raf);return}this.ringEl.dataset.tone=e;let n=()=>{if(!this.ringTarget)return;let i=this.ringTarget.getBoundingClientRect();Object.assign(this.ringEl.style,{left:`${i.left-4}px`,top:`${i.top-4}px`,width:`${i.width+8}px`,height:`${i.height+8}px`}),this.raf=this.win.requestAnimationFrame?.(n)??0};n()}};var H="https://passengermode.com";function ut(r){try{return r.top!==r.self}catch{return!0}}var x="__passengerModeMounted";async function W(r){let t=r.window??window;if(ut(t)&&!r.allowInFrame)return console.info("Passenger Mode is off inside frames."),null;let e=t;if(e[x])return console.info("Passenger Mode is already on this page; ignoring the second install."),null;e[x]=!0;try{let n=await dt(r,t);if(!n)e[x]=!1;else{let i=n.destroy;n.destroy=()=>{i(),e[x]=!1}}return n}catch(n){throw e[x]=!1,n}}async function dt(r,t){let e=(r.apiBase??H).replace(/\/$/,""),n=r.key?`key=${encodeURIComponent(r.key)}`:"";if(r.key)try{let u=await(await t.fetch(`${e}/api/v1/status?${n}`,{credentials:"omit"})).json();if(!u?.on)return console.info(`Passenger Mode is off for this site${u?.reason?`: ${u.reason}`:""}.`),null}catch{return null}let i=r.manifest??(r.key?`${e}/api/v1/manifest?${n}`:void 0);if(!i)throw new Error("Passenger Mode needs a manifest or a key.");let s=typeof i=="string"?await(await t.fetch(i,{credentials:r.key?"omit":"same-origin"})).json():i,o=r.planner??(r.key?new m(`${e}/api/v1/plan`,void 0,{"x-passenger-key":r.key}):r.endpoint?new m(r.endpoint):new y),a=r.key?u=>{try{t.fetch(`${e}/api/v1/trips`,{method:"POST",headers:{"content-type":"application/json","x-passenger-key":r.key},body:JSON.stringify(u),credentials:"omit",keepalive:!0}).catch(()=>{})}catch{}}:null,l=u=>{a?.(u),r.onTrip?.(u)},c,d=new w(t,{open:u=>c.open(u),close:()=>c.close(),plan:(u,g)=>{c.planTrip(u,g)},go:u=>c.go(u),notThis:()=>c.notThis(),takeWheel:()=>c.takeWheel(),resume:u=>c.resume(u),end:()=>c.end(),rate:u=>c.rate(u)},{suggestions:s.destinations.map(u=>u.goal),badge:r.badge??!0,offset:r.offset});return c=new v({manifest:s,planner:o,window:t,ui:d,routeFrom:r.routeFrom,onTrip:l}),{driver:c,ui:d,destroy(){c.destroy(),d.destroy()}}}if(typeof document<"u"){let r=document.currentScript;if(r?.dataset.manifest||r?.dataset.key){let t=n=>n!=null&&n!==""&&!Number.isNaN(Number(n))?Number(n):void 0,e=()=>{W({key:r.dataset.key,apiBase:r.dataset.api,manifest:r.dataset.manifest,endpoint:r.dataset.endpoint,routeFrom:r.dataset.route??"path",badge:r.dataset.badge!=="false",offset:{right:t(r.dataset.right),bottom:t(r.dataset.bottom)}})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()}}return G(ht);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "passenger-mode",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Say where you're going; the site drives itself there.",
5
5
  "keywords": [
6
6
  "navigation",
@@ -46,7 +46,8 @@
46
46
  "types": "./dist/lib/scan.d.ts",
47
47
  "default": "./dist/lib/scan.js"
48
48
  },
49
- "./script": "./dist/passenger-mode.js"
49
+ "./script": "./dist/passenger-mode.js",
50
+ "./package.json": "./package.json"
50
51
  },
51
52
  "files": [
52
53
  "dist",