feat(ui): PWA polish, smart service worker caching, and DuckDB splash screen#48
feat(ui): PWA polish, smart service worker caching, and DuckDB splash screen#48
Conversation
- Add SVG icon variants (favicon, 192, 512, 1024, maskable) - Update manifest.json to reference new icons - Extract component styles into CSS modules (Header, Icon, Layout, ResultsTable, Sidebar, SidebarTables) - Clean up theme.css and add base/codemirror/utilities style files - Refactor vite.config.ts and update index.html Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace aggressive stale-while-revalidate-everything approach with two explicit strategies: - cache-first: DuckDB WASM + worker, Vite content-hashed assets (/assets/*.js, *.css) - network-first: everything else (index.html, manifest, icons) This ensures app updates are seen immediately on next load while keeping DuckDB's large binaries fully cached for offline/repeat use. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Pre-fetch duckdb-eh.wasm with ReadableStream to track download bytes (service worker caches it so duckdb's subsequent fetch is instant) - Expose loadProgress (0-100) and loadStatus signals from SchemaContext - New SplashScreen component: full-screen overlay with logo, animated progress bar, status text, and fade-out on ready - Progress phases: download (5-75%), init (80%), connect (92%), restore files (97%), ready (100%) - On repeat visits WASM is cached so splash is sub-100ms Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d.ts Triple-slash directives must appear before any statements. With 'import "solid-js"' on line 1, TypeScript was silently ignoring the '/// <reference types="vite/client" />' directive, causing all *.module.css imports to report TS2307 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR improves the UI’s first-load/install experience by polishing PWA assets/styles, updating the service worker caching strategy, and adding a DuckDB loading splash screen with progress reporting.
Changes:
- Refactors global styles into
base.css/codemirror.css/utilities.cssand multiple component CSS modules. - Reworks the service worker to use cache-first for DuckDB + hashed assets and network-first for everything else, with an update-triggered reload flow.
- Adds DuckDB initialization progress tracking and a full-screen splash screen.
Reviewed changes
Copilot reviewed 24 out of 30 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ui/vite.config.ts | Adds a build hook to inject a version token into the service worker output. |
| packages/ui/src/vite-env.d.ts | Fixes triple-slash directive ordering; adds typings for *.module.css. |
| packages/ui/src/styles/utilities.css | Introduces reusable layout utility classes. |
| packages/ui/src/styles/theme.css | Reduces this file to theme variables only. |
| packages/ui/src/styles/codemirror.css | Extracts CodeMirror styling into a dedicated stylesheet. |
| packages/ui/src/styles/base.css | Extracts base element/layout styles (reset, typography, buttons). |
| packages/ui/src/contexts/SchemaContext.tsx | Adds DuckDB load progress/status signals and WASM prefetch progress tracking. |
| packages/ui/src/components/SplashScreen.tsx | New splash screen UI driven by schema load progress/status. |
| packages/ui/src/components/SidebarTables.module.css | Adds table-list CSS module (currently not wired into the component). |
| packages/ui/src/components/Sidebar.tsx | Migrates sidebar styling to CSS modules (but currently mismatched with the module content). |
| packages/ui/src/components/Sidebar.module.css | Adds sidebar CSS module (missing classes referenced by Sidebar.tsx). |
| packages/ui/src/components/ResultsTable.tsx | Migrates results table styling from inline styles to a CSS module. |
| packages/ui/src/components/ResultsTable.module.css | New CSS module backing ResultsTable styling. |
| packages/ui/src/components/Layout.tsx | Migrates layout styling to a CSS module. |
| packages/ui/src/components/Layout.module.css | New CSS module backing Layout styling. |
| packages/ui/src/components/Icon.tsx | Applies CSS-module styling for icons. |
| packages/ui/src/components/Icon.module.css | New CSS module for base icon styling. |
| packages/ui/src/components/Header.tsx | Migrates header styling to a CSS module. |
| packages/ui/src/components/Header.module.css | New CSS module backing Header styling. |
| packages/ui/src/App.tsx | Imports the new CSS layers; mounts the splash screen. |
| packages/ui/src/App.module.css | New CSS module for editor/results pane styling. |
| packages/ui/public/sw.js | Implements explicit cache-first/network-first strategies plus SKIP_WAITING handling. |
| packages/ui/public/manifest.json | Updates theme color to match the new branding. |
| packages/ui/public/icon-maskable.svg | Adds a maskable PWA icon. |
| packages/ui/public/icon-512.svg | Adds a 512px SVG icon. |
| packages/ui/public/icon-192.svg | Adds a 192px SVG icon. |
| packages/ui/public/icon-1024.svg | Adds a 1024px SVG icon. |
| packages/ui/public/favicon.svg | Adds an SVG favicon. |
| packages/ui/index.html | Updates theme-color meta and adds SW update/reload wiring. |
| .serena/project.yml | Adds/updates Serena project configuration metadata. |
You can also share your feedback on Copilot code review. Take the survey.
| .tablesList { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 0.25rem; | ||
| margin-top: 0.5rem; | ||
| } |
There was a problem hiding this comment.
SidebarTables.module.css does not appear to be imported/used anywhere in the UI (no references found), so these styles will never be applied and the file becomes dead code. Either import it from Sidebar.tsx (and wire up its collapsed selectors) or remove it and move the needed class definitions into the module that is actually used.
| const cacheNames = await caches.keys(); | ||
| await Promise.all( | ||
| cacheNames.map((name) => { | ||
| if (name !== CACHE_NAME) { | ||
| console.log(`[SW] Deleting old cache: ${name}`); | ||
| return caches.delete(name); | ||
| } | ||
| return Promise.resolve(); |
There was a problem hiding this comment.
On activation, the SW deletes every cache except the current CACHE_NAME. Because CACHE_NAME is versioned per build, this guarantees that large immutable assets (DuckDB WASM/worker and Vite hashed assets) will be purged and re-downloaded on every deployment, negating the “cache forever” intent and potentially hurting repeat-load performance. Consider using separate caches (e.g., a stable static cache for DuckDB + hashed assets, and a versioned runtime cache for network-first resources), and only clear the versioned/runtime cache on update.
| createEffect(() => { | ||
| if (!loading()) { | ||
| // Brief pause so user sees 100% before fade | ||
| setTimeout(() => setGone(true), 700); | ||
| } |
There was a problem hiding this comment.
createEffect schedules a setTimeout when loading() becomes false, but the timeout isn’t cleared on cleanup. During HMR/unmounts this can trigger setGone(true) after the component is gone. Store the timeout id and clear it via onCleanup, and consider guarding against multiple timers if the effect re-runs.
| class="sidebar" | ||
| classList={{ collapsed: props.collapsed }} | ||
| class={styles.sidebar} | ||
| classList={{ [styles.collapsed]: props.collapsed }} |
There was a problem hiding this comment.
classList={{ [styles.collapsed]: props.collapsed }} relies on styles.collapsed, but Sidebar.module.css only defines the compound selector .sidebar.collapsed and does not define/export a standalone .collapsed class. This makes styles.collapsed undefined at runtime, so the collapsed class won’t be applied. Define a .collapsed class in the module (and update selectors accordingly) or adjust the CSS/module usage so the exported class name exists.
| classList={{ [styles.collapsed]: props.collapsed }} |
| <div class={styles.tablesList}> | ||
| <Show when={tables().length === 0}> | ||
| <div class="empty-state"> | ||
| <div class={styles.emptyState}> | ||
| <p>No data loaded</p> | ||
| <small>Click "Add Data" to load a CSV file</small> |
There was a problem hiding this comment.
This file references table-related CSS module keys like styles.tablesList / styles.emptyState / styles.tableItem etc., but Sidebar.module.css does not define these classes (they currently exist in SidebarTables.module.css, which isn’t imported). As-is, those class={...} bindings will evaluate to undefined and the sidebar table list will lose styling. Import/use the correct CSS module or consolidate the styles into Sidebar.module.css.
Summary
Bundles three related UI improvements for a better first-load and install experience.
1. PWA icons & CSS modules refactor
manifest.jsonHeader,Icon,Layout,ResultsTable,Sidebar)theme.css, addsbase.css/codemirror.css/utilities.css2. Service worker: targeted caching strategy
Replaces the old stale-while-revalidate-everything approach with two explicit strategies:
/assets/*.js,*.css)index.html,manifest.json, icons)Users now see app updates immediately on next load instead of one visit later.
3. DuckDB loading splash screen
First load of DuckDB WASM can take several seconds. This adds a full-screen splash that looks like a desktop app launcher:
duckdb-eh.wasmviaReadableStreamfor real byte-level progress trackinginstantiatecall hits the cache4. TypeScript fix:
vite-env.d.tsTriple-slash directives must appear before any statements.
import "solid-js"was on line 1, silently discarding the/// <reference types="vite/client" />directive and causing all*.module.cssimports to emit TS2307 errors. Fixed by reordering.This PR was created by an AI agent on behalf of @sushruth.