fuzzyworld

tower / commit

feat(dev): serve on the network, and print a QR of the address

b55cf01bac479382a5a1ccd586ed2516ca684f84 · Pavel Safronov · 2026-09-08 16:45:51 -0700 · parents 6da638104c15

Claude Code usage: this commit ~13.0K out / ~1.3M cached (first commit of its session: everything since it began) · cost unknown (no per-model trailer) · session 1 tally 66 in / 13.0K out / 1.3M cached · usage by commit and session

feat(dev): serve on the network, and print a QR of the address

`npm run dev` now binds to every interface, so a phone on the same
Wi-Fi can open the app; vite-plugin-qrcode prints a scannable code of
that LAN URL beside the usual localhost line. Touch behaviour — the
drag axes above, chiefly — is hard to judge with a mouse, and typing
192.168.x.y:5173 by hand every time is its own small tax.

The server is reachable by anyone on the network while it runs, which
is fine at home and worth knowing anywhere else. README says so.

Usage: 66 in / 13.0K out / 1.3M cached (session tally)
rtk:   3.3M saved, 39.1% over 3164 cmds (global/lifetime)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
 README.md         |  7 ++++++-
 package-lock.json | 21 +++++++++++++++++++++
 package.json      |  3 ++-
 vite.config.ts    |  4 ++++
 4 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 8f4d9a2..3bb4f26 100644
--- a/README.md
+++ b/README.md
@@ -112,13 +112,18 @@ regressions, not things that were already broken.
 
 ```
 npm install
-npm run dev      # http://localhost:5173
+npm run dev      # http://localhost:5173, plus a LAN URL and a QR code for it
 npm test         # 94 tests; canon figures are pinned to design.md
 npm run build
 npm run terrain   # regenerate the maps from content/geography.json
 npm run screenshots  # drive the app and rephotograph docs/screenshots/
 ```
 
+`npm run dev` binds to every interface (`vite --host`) and prints a scannable QR
+code of the network URL, so the touch interactions can be tried on a phone on the
+same Wi-Fi without typing an address. The server is reachable by anyone on that
+network while it runs; drop the `--host` if that is not wanted.
+
 Vite + TypeScript, deployed to GitLab Pages by `.gitlab-ci.yml` on every push to the
 default branch. Three.js is loaded only when a 3D view is opened, so a reader who
 stays on the map never downloads it.
diff --git a/package-lock.json b/package-lock.json
index c46d17d..8e4bd26 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
         "playwright": "^1.63.0",
         "typescript": "^7.0.2",
         "vite": "^8.2.2",
+        "vite-plugin-qrcode": "^0.4.1",
         "vitest": "^5.0.0"
       }
     },
@@ -1419,6 +1420,13 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/uqr": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.3.tgz",
+      "integrity": "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/vite": {
       "version": "8.2.2",
       "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz",
@@ -1497,6 +1505,19 @@
         }
       }
     },
+    "node_modules/vite-plugin-qrcode": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/vite-plugin-qrcode/-/vite-plugin-qrcode-0.4.1.tgz",
+      "integrity": "sha512-t3gdCbn/a2HHLK3M8bUrQ+4c+I0YQGJ/STxAL9eI6TBnfdeM/CJr3HctvOb7UYhEtWxHs5xZWpz7kYnRd5w+rA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "uqr": "^0.1.2"
+      },
+      "peerDependencies": {
+        "vite": ">=3"
+      }
+    },
     "node_modules/vitest": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.0.tgz",
diff --git a/package.json b/package.json
index 9711d67..24076b4 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "version": "0.1.0",
   "type": "module",
   "scripts": {
-    "dev": "vite",
+    "dev": "vite --host",
     "build": "tsc --noEmit && vite build",
     "preview": "vite preview",
     "test": "vitest run",
@@ -18,6 +18,7 @@
     "playwright": "^1.63.0",
     "typescript": "^7.0.2",
     "vite": "^8.2.2",
+    "vite-plugin-qrcode": "^0.4.1",
     "vitest": "^5.0.0"
   },
   "description": "Companion map to a novel set inside Tower, a spun cylinder habitat in Earth orbit.",
diff --git a/vite.config.ts b/vite.config.ts
index 10d1b0e..678d2d8 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,4 +1,5 @@
 import { defineConfig } from 'vite'
+import { qrcode } from 'vite-plugin-qrcode'
 
 /**
  * Relative asset paths by default, so the same build works wherever Pages puts it.
@@ -16,6 +17,9 @@ const base = process.env.BASE || './'
 
 export default defineConfig({
   base,
+  // `npm run dev` binds to every interface (--host) so a phone on the same Wi-Fi
+  // can open the app; this prints a scannable QR of that network URL alongside it.
+  plugins: [qrcode()],
   // Vite's static directory is `static/`, not the default `public/`, because
   // GitLab Pages requires its artifact to be a directory named `public` — and
   // `mv dist public` does not rename when `public/` already exists, it moves dist