pi-central 0.3.0 → 0.4.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.
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ width="27.417528mm"
4
+ height="27.417528mm"
5
+ viewBox="0 0 27.417529 27.417529"
6
+ version="1.1"
7
+ id="svg1"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ xmlns:svg="http://www.w3.org/2000/svg">
10
+ <g
11
+ id="layer1"
12
+ transform="translate(-25.653968,-66.727691)">
13
+ <path
14
+ id="path52"
15
+ style="fill:#3fa383;stroke-width:3.02717;stroke-linejoin:round;paint-order:stroke fill markers"
16
+ d="M 53.071496,80.436455 A 13.708764,13.708764 0 0 1 39.362732,94.145219 13.708764,13.708764 0 0 1 25.653968,80.436455 13.708764,13.708764 0 0 1 39.362732,66.727691 13.708764,13.708764 0 0 1 53.071496,80.436455 Z" />
17
+ <path
18
+ style="font-weight:600;font-size:30.375px;line-height:2;font-family:Roboto;-inkscape-font-specification:'Roboto, Semi-Bold';letter-spacing:7.50712px;fill:#ffffff;stroke-width:4.48942;stroke-linejoin:round;paint-order:stroke fill markers"
19
+ d="m 48.046608,72.559311 v 2.951477 H 30.530556 v -2.951477 z m -10.975342,0 V 88.60704 H 33.140907 V 72.559311 Z m 4.523621,0 h 3.930359 l -0.01483,11.286804 q 0,0.726746 0.19281,1.112366 0.207642,0.370788 0.57843,0.519104 0.38562,0.133484 0.934388,0.133484 0.38562,0 0.711914,-0.04449 0.326294,-0.04449 0.548767,-0.08899 l 0.01483,2.936645 q -0.504272,0.148316 -1.112365,0.252137 -0.608094,0.118652 -1.379334,0.118652 -1.320007,0 -2.313721,-0.459778 -0.993713,-0.459778 -1.54248,-1.468323 -0.548767,-1.023376 -0.548767,-2.684509 z"
20
+ id="text52"
21
+ aria-label="π" />
22
+ </g>
23
+ </svg>
Binary file
Binary file
@@ -5,11 +5,41 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <meta
7
7
  http-equiv="Content-Security-Policy"
8
- content="default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"
8
+ content="default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"
9
9
  />
10
10
  <title>Pi Central</title>
11
- <script type="module" crossorigin src="/assets/index-CDs5_ZFh.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-Deb-EFhq.css">
11
+ <!--
12
+ An SVG icon first, because it is one file that stays sharp at every size, with a PNG fallback for
13
+ anything that cannot use it. `sizes="any"` tells the browser the SVG is scalable rather than
14
+ claiming a pixel size it does not have.
15
+
16
+ Both live in `src/assets/` so Vite fingerprints and emits them with the rest of the build — which
17
+ also means the orchestrator serves them with the right content type (`static-ui.ts` has `.svg` and
18
+ `.png`), rather than the app shipping a favicon the server would call `application/octet-stream`.
19
+ -->
20
+ <link rel="icon" type="image/svg+xml" href="/assets/logo-icon-BCSgj5RT.svg" sizes="any" />
21
+ <link rel="icon" type="image/png" href="/assets/logo-icon-CYTM3sb_.png" />
22
+ <!--
23
+ Safari's **Add to Dock** does not read the manifest's icons — it reads this link. So it has to be
24
+ here, at 180×180, at a STABLE path (issue #124).
25
+
26
+ Three faults in the previous version, and the symptom was exactly what they predict:
27
+ - it pointed at the 104×104 PNG, and Safari wants 180;
28
+ - it pointed into `src/assets/`, so the path was FINGERPRINTED — an icon URL is cached hard by
29
+ macOS and iOS, and a name that changes every build defeats that cache in the worst direction;
30
+ - and it was the only icon on a stable path that Safari could find, so when it rejected this one
31
+ there was nothing to fall back to but a screenshot of the page.
32
+ -->
33
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
34
+ <meta name="theme-color" content="#3fa383" />
35
+ <!--
36
+ Installable to the dock as a standalone window (issue #119). Deliberately NOT an offline app: every
37
+ screen is fed by the socket, so an installed Pi Central with the orchestrator down shows
38
+ `disconnected`. The manifest says `display: standalone`, not "works offline".
39
+ -->
40
+ <link rel="manifest" href="/manifest.webmanifest" />
41
+ <script type="module" crossorigin src="/assets/index-DO0lG4JX.js"></script>
42
+ <link rel="stylesheet" crossorigin href="/assets/index-CPKDQr93.css">
13
43
  </head>
14
44
  <body>
15
45
  <div id="root"></div>
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "Pi Central",
3
+ "short_name": "Pi Central",
4
+ "description": "A local dashboard for running, watching, and switching between multiple sandboxed Pi coding-agent sessions.",
5
+ "start_url": "/",
6
+ "scope": "/",
7
+ "display": "standalone",
8
+ "background_color": "#0e1116",
9
+ "theme_color": "#3fa383",
10
+ "icons": [
11
+ {
12
+ "src": "/apple-touch-icon.png",
13
+ "sizes": "180x180",
14
+ "type": "image/png",
15
+ "purpose": "any"
16
+ },
17
+ {
18
+ "src": "/icon-192.png",
19
+ "sizes": "192x192",
20
+ "type": "image/png",
21
+ "purpose": "any"
22
+ },
23
+ {
24
+ "src": "/icon-512.png",
25
+ "sizes": "512x512",
26
+ "type": "image/png",
27
+ "purpose": "any"
28
+ }
29
+ ]
30
+ }
package/dist/ui/sw.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The service worker (issue #119).
3
+ *
4
+ * ## What this is for, precisely
5
+ *
6
+ * A standalone window and a dock icon. **Not offline** — every screen is fed by the socket, so an
7
+ * installed Pi Central with the orchestrator down shows `disconnected` and nothing else. A worker that
8
+ * "worked offline" would install, open, and be useless while implying otherwise, so it deliberately does
9
+ * not try.
10
+ *
11
+ * ## The one rule that matters: `index.html` is NEVER cached
12
+ *
13
+ * `index.html` names the current fingerprint set, which is exactly what `static-ui.ts` says in its
14
+ * `Cache-Control`:
15
+ *
16
+ * // `index.html` names the current fingerprint set, so caching it is exactly the mistake that
17
+ * // serves a stale app after a rebuild.
18
+ *
19
+ * A worker that cached it would make a stale build **permanent and invisible** — no reload button, no URL
20
+ * to compare, and a worker the browser updates on its own schedule. So this file applies the SERVER's
21
+ * rule: hashed assets are immutable and cached forever, and the document is fetched from the network every
22
+ * time (issue #120 guards this).
23
+ *
24
+ * ## Why there is no precache list
25
+ *
26
+ * The build emits 266 chunks and 8.8 MB, dominated by Shiki grammars (`elisp` 772 kB, `cpp` 492 kB) that a
27
+ * session may never open. Precaching them would make the first visit pay for grammars it does not use. So
28
+ * everything is runtime-cached on FIRST REQUEST: the entry chunk and CSS on load, a grammar the first time
29
+ * that language appears. Nothing is fetched eagerly.
30
+ *
31
+ * ## `skipWaiting` is deliberately NOT used
32
+ *
33
+ * "Activate immediately" and "never interrupt a running session" pull in opposite directions, and this app
34
+ * holds live sessions. A worker that took over mid-stream would reload the UI under the reader. The new
35
+ * worker waits, and takes over on the next launch — which is the right trade for a tool that is open for
36
+ * hours at a time.
37
+ */
38
+ const CACHE = "pi-central-v1";
39
+
40
+ // A request for the app document, or for a directory that resolves to one.
41
+ function isDocument(request, url) {
42
+ return request.mode === "navigate" || url.pathname === "/" || url.pathname.endsWith("/index.html");
43
+ }
44
+
45
+ self.addEventListener("install", (event) => {
46
+ // Nothing to precache — see the note above. `skipWaiting` is absent on purpose.
47
+ event.waitUntil(Promise.resolve());
48
+ });
49
+
50
+ self.addEventListener("activate", (event) => {
51
+ // Drop caches from previous versions of this worker, so a schema change cannot leave stale bytes around
52
+ // forever. Scoped to this prefix so another app on the same origin is untouched.
53
+ event.waitUntil(
54
+ caches.keys().then((names) =>
55
+ Promise.all(names.filter((name) => name.startsWith("pi-central-") && name !== CACHE).map((name) => caches.delete(name))),
56
+ ),
57
+ );
58
+ });
59
+
60
+ self.addEventListener("fetch", (event) => {
61
+ const request = event.request;
62
+ if (request.method !== "GET") return;
63
+
64
+ const url = new URL(request.url);
65
+ // Same-origin only. The app has no business proxying anything else, and doing so would be a hole.
66
+ if (url.origin !== self.location.origin) return;
67
+
68
+ // The socket is not a fetch, but a dev-server proxy or an `http` fallback could arrive here. Leave it.
69
+ if (url.pathname.startsWith("/ws")) return;
70
+
71
+ if (isDocument(request, url)) {
72
+ // NETWORK, always. No `cache.put`, no fallback to a cached document — a cached `index.html` is how a
73
+ // rebuild stops being visible. If the orchestrator is down the request fails, which is the honest
74
+ // outcome for an app that cannot work without it.
75
+ return;
76
+ }
77
+
78
+ // Hashed assets: cache-first, because their name cannot change under new content.
79
+ event.respondWith(
80
+ caches.match(request).then((cached) => {
81
+ if (cached !== undefined) return cached;
82
+ return fetch(request).then((response) => {
83
+ // Only a real, complete same-origin response is worth keeping. `opaque` responses have no usable
84
+ // status, and a 404 or a 500 must not become permanent.
85
+ if (response.ok && response.type === "basic") {
86
+ const copy = response.clone();
87
+ caches.open(CACHE).then((cache) => cache.put(request, copy));
88
+ }
89
+ return response;
90
+ });
91
+ }),
92
+ );
93
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-central",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "description": "Local dashboard that orchestrates multiple sandboxed Pi coding-agent sessions across working directories.",
6
6
  "license": "MIT",