conductor-remote 1.73.1 → 1.73.2

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.html CHANGED
@@ -13,7 +13,7 @@
13
13
  <title>Conductor Remote</title>
14
14
  <!-- Runs before the module bundle so it can catch a stale shell that fails to boot. -->
15
15
  <script src="/self-heal.js"></script>
16
- <script type="module" crossorigin src="/assets/index-DPr3MT68.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-CPu66mE4.js"></script>
17
17
  <link rel="stylesheet" crossorigin href="/assets/index-CXd-zI4t.css">
18
18
  <link rel="manifest" href="/manifest.webmanifest"></head>
19
19
  <body>
package/dist/push-sw.js CHANGED
@@ -6,14 +6,22 @@
6
6
  // as a static asset, never bundled. Being part of the precache manifest is what makes a
7
7
  // change here produce a new `sw.js`, so edits actually ship.
8
8
  //
9
- // Two rules this obeys, both learned from iOS:
9
+ // Four rules this obeys, every one of them learned from iOS:
10
10
  // 1. **Every push shows a notification.** Safari treats a push that resolves without one
11
11
  // as abuse and can revoke the subscription, so there is no "suppress if the app is
12
12
  // open" branch — the relay only sends when something genuinely happened.
13
- // 2. **A tap focuses the existing window** rather than navigating it. The app is a
13
+ // 2. **A tap prefers focusing the existing window** over navigating it. The app is a
14
14
  // token-gated SPA; `openWindow` on a live client would remount the whole thing and
15
15
  // throw away in-progress composer text, so we focus and post a route instead.
16
- // 3. **The route is also parked in Cache Storage**, because on iOS neither of the two
16
+ // 3. **A focus that didn't land still owes the tap a window.** Nothing here happens by
17
+ // default: a notification click fires this handler and the app comes forward only
18
+ // because the handler asks it to. iOS returns a backgrounded home-screen web app as
19
+ // a live window client whose `focus()` can settle without foregrounding it, so
20
+ // treating "we found a client" as success ended the tap with the phone exactly where
21
+ // it was — a notification that ignores your finger. `focusClient` therefore reports
22
+ // whether the app really came up, and `openWindow` is the fallback rather than the
23
+ // branch for an app that isn't running.
24
+ // 4. **The route is also parked in Cache Storage**, because on iOS neither of the two
17
25
  // direct routes survives. A backgrounded home-screen web app is resumed on whatever
18
26
  // screen it was left on — `openWindow`'s path is ignored, and a `postMessage` to a
19
27
  // frozen page is dropped (WebKit, reported from iOS 17.1 through 18.x and still
@@ -33,6 +41,43 @@ async function parkRoute(url) {
33
41
  }
34
42
  }
35
43
 
44
+ /**
45
+ * Is this client one of ours? Parsed in a `try`, because an unparseable client URL
46
+ * throwing here would take the whole handler down and `openWindow` with it — the tap
47
+ * would then do nothing, which is the failure this file is trying to end.
48
+ */
49
+ function sameOrigin(clientUrl) {
50
+ try {
51
+ return new URL(clientUrl).origin === self.location.origin
52
+ } catch {
53
+ return false
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Bring an already-open app window to the front, and say whether it really came.
59
+ *
60
+ * That answer is the whole point. A notification click has no default action — the app
61
+ * comes forward only because this handler asks it to — so a `focus()` that quietly does
62
+ * nothing ends the tap with the phone exactly where it was, which reads as a
63
+ * notification that ignores your finger. iOS hands back a backgrounded home-screen web
64
+ * app as a live window client, and `focus()` on it can settle without foregrounding
65
+ * anything, so "we found a client" is not "the app is up".
66
+ *
67
+ * A refusal, a resolve with nothing, and a client that reports itself unfocused all
68
+ * count as "no". Answering "no" too readily costs at worst a second `openWindow` on a
69
+ * platform that had already handled the tap; answering "yes" too readily costs the tap.
70
+ */
71
+ async function focusClient(client) {
72
+ try {
73
+ const focused = await client.focus()
74
+ return !!focused && focused.focused !== false
75
+ } catch {
76
+ // Refused (some platforms want a user activation this event doesn't carry).
77
+ return false
78
+ }
79
+ }
80
+
36
81
  self.addEventListener('push', event => {
37
82
  const fallback = { title: 'Conductor Remote', body: 'Something changed in a workspace.', url: '/', tag: 'conductor' }
38
83
  let data = fallback
@@ -70,19 +115,18 @@ self.addEventListener('notificationclick', event => {
70
115
  await parkRoute(url)
71
116
  const clients = await self.clients.matchAll({ type: 'window', includeUncontrolled: true })
72
117
  for (const client of clients) {
73
- if (new URL(client.url).origin !== self.location.origin) continue
118
+ if (!sameOrigin(client.url)) continue
74
119
  // Handled in web/src/hooks.ts (usePushRouting) — an in-app route change, so the
75
120
  // token gate and React state survive the tap. Posted before the focus, since a
76
121
  // refused focus is no reason to skip a message the page may well receive.
77
122
  client.postMessage({ type: 'push-navigate', url })
78
- try {
79
- await client.focus()
80
- } catch {
81
- // focus() can be refused (no user activation on some platforms); on iOS the
82
- // system foregrounds the web app on the tap regardless.
83
- }
84
- return
123
+ if (await focusClient(client)) return
124
+ break
85
125
  }
126
+ // Nothing is open, or the focus above never landed. `openWindow` is the only lever
127
+ // left that can put the app on screen, and on an installed iOS web app it launches
128
+ // or resumes the one instance rather than adding a second — the path is what it
129
+ // drops, and the parked route above is what covers that.
86
130
  await self.clients.openWindow(url)
87
131
  })()
88
132
  )
package/dist/sw.js CHANGED
@@ -1 +1 @@
1
- if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const t=e=>i(e,o),c={module:{uri:o},exports:l,require:t};s[o]=Promise.all(n.map(e=>c[e]||t(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"aa32d6e097f4962393071bf82c472948"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-DPr3MT68.js",revision:null},{url:"assets/index-CXd-zI4t.css",revision:null},{url:"apple-touch-icon.png",revision:"1127bb396b4648add53dce3f22c92aee"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a9b0d962686287452216492cd2247499"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
1
+ if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const c=e=>i(e,o),t={module:{uri:o},exports:l,require:c};s[o]=Promise.all(n.map(e=>t[e]||c(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e7ef44deca46c0539e6ff7bba5eb815e"},{url:"index.html",revision:"0233482ea1581cc335272d86ff150fde"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-CXd-zI4t.css",revision:null},{url:"assets/index-CPu66mE4.js",revision:null},{url:"apple-touch-icon.png",revision:"1127bb396b4648add53dce3f22c92aee"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a9b0d962686287452216492cd2247499"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conductor-remote",
3
- "version": "1.73.1",
3
+ "version": "1.73.2",
4
4
  "type": "module",
5
5
  "packageManager": "yarn@4.15.0",
6
6
  "description": "Phone control panel for local Conductor agents. Reads ride SQLite + git; prompts ride Conductor's own dispatch path.",