Installable desktop app: PWA (manifest, service worker, install prompt, offline shell) #47
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
EduarD3V/AutoFirme#47
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Make the app installable on desktop (Chrome/Edge/Firefox Install-app) as a PWA — the boring, correct path for a self-hosted web app.
Tasks
Gates
pnpm test green, build clean, manifest+SW verified present in build output.
Ownership
Owns PWA plugin config, manifest, SW, header button. No signing-logic changes.
bullet: coder launched on branch
issue/47-installable-desktop-app-pwa-ma(modelnan/deepseek-v4-flash).Claimed — branch
issue/47-installable-desktop-app-pwa-ma. Doing: web manifest + SVG→PNG icon set, SvelteKit-native service worker (no vite-plugin-pwa), install/update header buttons, self-host note.Plan — #47 Installable desktop app (PWA: manifest, service worker, install prompt, offline shell)
Goal (restated)
Make AutoFirma Web installable from Chrome/Edge as a PWA and make the app shell work
offline: a web manifest with teal branding and a PNG icon set, a service worker that
precaches the built shell, header buttons for "Install app" (
beforeinstallprompt) and"Update version" (waiting service worker), plus a self-host note. "Done" =
pnpm buildemits
build/manifest.webmanifest,build/service-worker.jsand the icons; a Chromiumrun against the built app shows the manifest linked, the SW active with the shell
precached, and both header buttons behaving;
pnpm testhas no new failures.Key decision: no
vite-plugin-pwa— SvelteKit already ships thisThe issue says "prefer the plugin if it slots in without config sprawl". SvelteKit 3
(SvelteKit —
@sveltejs/[email protected], not Next.js) has service workers built in: asrc/service-worker.tsentry point, aserviceWorkerenvironment in the build, and an$app/manifestmodule (immutable/assets/prerendered— the exact precache listworkbox would build) plus
versionfrom$app/env. The client bundle even self-registersthe worker (
src/runtime/server/page/render.js→navigator.serviceWorker.register(...)).So the plugin is a dependency + config for something the framework does natively.
Rung: native platform feature beats installed dependency. Zero new dependencies, zero
new config keys (
vite.config.tsuntouched).Scope
Files I will change:
src/service-worker.ts(new) — precache shell, navigation fallback, update message.static/manifest.webmanifest(new) — name, icons, standalone, teal theme color.static/icon.svg(new, source art) +static/icon-192.png,static/icon-512.png,static/icon-maskable-512.png(generated withrsvg-convertfrom that SVG).src/lib/components/PwaControls.svelte(new) — install button + update button.src/lib/components/PwaControls.test.ts(new) — jsdom test for both buttons.src/lib/i18n.ts— two new copy keys (ES/EN):installApp,updateApp.src/lib/App.svelte— render<PwaControls copy={t} />in the header action row.src/app.html—<link rel="manifest">+<meta name="theme-color">.Caddyfile— oneheaderline: serve.webmanifestasapplication/manifest+json.README.md— self-host / PWA note (HTTPS requirement, icon regeneration command).Files I will not touch: everything under
src/lib/other than the two files above(no
cms.ts/xades.ts/pades.ts/verify//batch.ts/certstore.tschanges — nosigning logic),
vite.config.ts,src/app.css,src/lib/theme.ts,static/favicon.svg,docs/,Containerfile,package.json/pnpm-lock.yaml, any other test file.Ordered tasks (each with its verification command)
static/icon.svg(full-bleed#008d80square, white sealfrom
App.svelte'ssealsnippet, art radius ~157px of 512 → inside the maskable80 % safe circle), rasterize 192/512/maskable-512.
Verify:
file static/icon-*.png(PNG, 192/512/512) +rsvg-convertexit 0.static/manifest.webmanifest:name: AutoFirma Web,short_name,start_url: "/",scope: "/",display: standalone,theme_color: "#008d80",background_color: "#fbfaf7", 3 icons (any192/512,maskable512),lang: "es".Verify:
jq . static/manifest.webmanifest+pnpm build && jq . build/manifest.webmanifest.src/service-worker.ts:CACHE = autofirme-shell-${version};install→ open cache, addprerendered+assets+immutablepaths (per-entrytry/catch so one 404 cannot abort install; no
skipWaitinghere — it is what makesthe update prompt possible);
activate→ drop staleautofirme-shell-*caches +clients.claim();fetch→ GET + same-origin only, navigations network-first withcached-URL → cached-
'/'fallback, everything else cache-first and never written to acache (no
cache.putanywhere ⇒ a produced signature can never be stored);message→SKIP_WAITING→skipWaiting().Verify:
pnpm buildlogs "Building service worker" andbuild/service-worker.jsexists;grep -c cache.put build/service-worker.js→ 0.src/lib/i18n.ts(installApp,updateApp, ES+EN).Verify:
pnpm check.PwaControls.svelte:beforeinstallprompt→ showt.installApp,click →
prompt();registration.installing/waiting→ showt.updateApp, click →postMessage({type:'SKIP_WAITING'}),controllerchange→ one reload.Verify:
pnpm test src/lib/components/PwaControls.test.ts+ browser smoke (task 8).App.svelteheader (next toDownloadsDialog),app.htmlmanifest +theme-color links,
Caddyfilecontent-type.Verify:
grep -c manifest.webmanifest build/index.html→ ≥1 afterpnpm build.it already is; icon regeneration command; update flow.
Verify: read the rendered section.
pnpm build, servebuild/(pnpm preview),check: navigation manifest link resolves + parses,
navigator.serviceWorker.readyresolves and the cache holds the hashed shell, install/update UI present under the
stubbed-prompt conditions, and an offline navigation returns the cached shell.
Verify: browser output recorded in the report.
Open questions (assumptions I proceed on)
to any path serves the cached
'/', which renders the SPA (all crypto is client-side, sothe app really works offline). A separate
static/offline.htmlwould only ever be shownwhen the shell itself is missing, i.e. never on a working install, and would duplicate the
header/branding. Assumption: no separate offline page; cached shell is the fallback.
Flagged for the human in the report.
static/favicon.svgis blue (#1f6feb) while the app's primary is teal — stale, notpart of this issue. Assumption: leave it alone, note in the report.
apple-touch-icon: Safari is not in the issue's install targets. Assumption: skip.rsvg-convert; only the PNG outputs arecommitted, and the command is documented in the README.
Risk
$app/manifestdefines coming from the kit build plugin,src/mountexcluded from tsconfig): if the SW build fails, fallback = keep the SWhand-written but drop the generated-manifest import and precache a literal list
(
/,/favicon.svg, + a/_app/immutableglob — impossible offline-first) — i.e. lastresort:
vite-plugin-pwa. Expect not to need it; the defines are wired per build.pnpm test: on this clean checkoutpnpm testalready fails 5 testsin
xades.test.ts/App.test.ts(xmlsec1 printed no verdict (exit 1): Verification status: FAILED) before any edit of mine. Not caused by this issue, out of scope (nosigning changes allowed). Fallback: keep my test scoped to
PwaControls, report thepre-existing failure with evidence, do not touch those tests.
pnpm checknoise:svelte-checkcoverssrc, andsrc/service-workeris excluded bytsconfig.json(Kit's own recommended setup), so the worker'sself/cachesglobalsare not type-checked by the app config.
DONE — 4 commits on
4c9f6c31:1c4f581dmanifest + teal icon set,b5fc9c4aapp-shell service worker,be7309bdheader install/update controls,cf96607fself-host docs. 380 insertions, no new dependencies, no vite.config change (SvelteKit 3 native SW: src/service-worker.ts + $app/manifest — the plugin would only have added sprawl).Gates:
xmlsec1 printed no verdict (exit 1): Verification status: FAILED, xades.test.ts + App.test.ts): the helper matches^status:but this machine printsVerification status:. Signing area, out of ownership; not skipped, not weakened.Verified in headless Chromium on the real build: SW activated, 33 shell entries precached all HTTP 200; offline / renders the full sign shell and /sidebar-07 renders too, uncached path falls back to the shell; real beforeinstallprompt fired so Instalar app is visible and aligned in the header; rebuild -> waiting worker -> Actualizar versión -> SKIP_WAITING -> reload -> stale caches deleted.
Needs a human call: (1) no separate offline.html — the cached shell is the fallback and works offline; say so if you want a page anyway. (2) static/favicon.svg is still blue #1f6feb while manifest/icons/theme-color are teal #008d80 — left alone (out of ownership). (3) no apple-touch-icon (Safari not in scope). (4) PNGs committed, generated with rsvg-convert (commands in README). (5) the xmlsec1 test-helper mismatch deserves its own issue.
Note: BULLET_TOKEN (gotoi-agent) 404s on EduarD3V/AutoFirme, so these comments went through BULLET_BASIC.
bullet: coder session died — relaunched from committed state (branch
issue/47-installable-desktop-app-pwa-ma).bullet: implementation finished (DONE — 4 commits on
4c9f6c31:1c4f581dmanifest + teal icon set,b5fc9c4aapp-shell service worker,be7309bdheader install/update controls,cf96607fself-host docs. 380 insertions, no new dependencies, no vite.config change (SvelteKit 3 native SW: src/service-worker.ts + $app/manifest — the plugin would only have added sprawl).) — moving to review.bullet: reviewer launched (model
9router/reviewer, independent of coder).DONE (second run, gates green) — PWA work unchanged (
1c4f581d,b5fc9c4a,be7309bd,cf96607f); added0483ed14test: read the xmlsec1 1.3 verdict line — the 5 red cross-check tests werexmlsec1Verifymatching^status:while xmlsec1 1.3.12 printsVerification status: OK|FAILED; one regex in the test harness, no signing logic touched, ERROR still throws (no-verdict ≠ verdict). Gates: pnpm test 144/144 exit 0; pnpm build exit 0 ("Building service worker"); build/ has manifest.webmanifest, service-worker.js, 3 icons; build/service-worker.js cache.put count = 0; build/index.html has manifest link + SW registration; pnpm check 0 errors; git diff --check clean.bullet: reviewer launched (model
9router/reviewer, independent of coder).REVIEW: FIXED 1 — commit
0483ed14: PwaControls reloaded the page when the first worker clients.claim()s an uncontrolled page (controllerchange), wiping in-progress input seconds after a new visitors arrival — fixed in6a8cf97d(guard + mutation-verified regression test); UNFIXED: nonebullet: fixer round done — fresh reviewer re-checking.
REVIEW: CLEAN — commit
6a8cf97d: findings none (manifest+icons, shell precache with 0 cache.put, install/update header buttons, README self-host note, gates green: pnpm test 145/145, build clean, pnpm check 0 errors; disclosed deviations: test-oracle regex0483ed14, tsconfig exclude fix, shell-as-offline-fallback per plan); UNFIXED: nonebullet: review CLEAN — ready to publish.
bullet: branch
issue/47-installable-desktop-app-pwa-manot on origin — cannot integrate.EduarD3V referenced this issue2026-10-15 04:40:00 +02:00
bullet: PR #51 open →
bullet-develop(auto-merge follows).bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into
bullet-develop; PR auto-merges next tick.bullet: merge conflicts with
bullet-develop— conflict-solver agent owns the worktree until it pushes.