Live app: https://alicommit-malp.github.io/wherewasi/ · Source: https://github.com/alicommit-malp/wherewasi
Most task apps are built around what you have to do. wherewasi is built around a different moment: the one where you stop doing one thing and start doing another, and your entire mental context evaporates. It answers exactly one question, and it answers it fast — where was I?
Watch the same clip as MP4 — 40 seconds, no audio.
The problem it solves
You have five things in flight. You're deep in one of them when something interrupts you. Two days later you come back and spend fifteen minutes reconstructing what you'd already figured out: what you'd finished, what you were blocked on, what the next move was going to be.
That reconstruction is pure waste, and it's avoidable — the knowledge existed in your head at the moment you left. wherewasi is a place to dump it in five seconds, and to find it again instantly.
Three rules define the whole app:
- Exactly one task is active at a time. Not a philosophy about focus — just an honest record of where your attention actually is.
- Every task carries a stack of short notes. Newest first. "renewed the cert", "waiting on DNS", "next: draft the outline". Free text, no schema, no ceremony.
- Switching is instant. One click or one keystroke. No dialog, no prompt, no "are you sure" — because anything that adds friction to switching means you'll stop recording your switches, and then the app is worthless.
What it deliberately does not do
Design is mostly subtraction, and this app had a lot subtracted:
- No timers, no time tracking. The first draft measured how long each task was active. It got cut, because the value was in restoring context, not producing timesheets — and a running clock creates a low-grade obligation to "start" and "stop" things correctly.
- No tags or categories. An earlier version tagged each note as did / waiting / next. In practice the tag was one more decision at the exact moment you want zero decisions. Plain text carries the same meaning: just type "waiting on DNS".
- No forced note on switch. The original design popped a modal on every switch asking what happened. It was correct in theory and unbearable in practice.
- No accounts, no server, no network. Your data never leaves your browser.
Each of those removals came from actually using the thing and noticing the friction.
Using it
The board
One narrow column. Each row is a task: a dot, the title, and its three most recent notes underneath in muted text. The active task is highlighted and is the only row with a note input. That's the whole interface.
Clicking a task title switches to it instantly and moves it to the top of the list, so the board self-sorts by recency: what you're on is first, what you just left is right below it, and stale work sinks to the bottom on its own.
Naming the workspace
Click the wherewasi title in the header to give the board a name — wherewasi — work, wherewasi — side project, whatever. It also becomes the browser-tab title, so if you keep separate boards in separate browser profiles they stay easy to tell apart. Leave it blank and the header is just wherewasi.
Writing notes
The active task's input says What happened?. Type, press Enter, done. Notes stack newest-first, each with a small timestamp so you can see whether a thought is from ten minutes ago or last week.
Click any note to edit it inline (Enter saves, Escape cancels). If a task has more than three notes, a +N more link expands the full history — and that expansion collapses automatically the moment you switch tasks, so the board never grows cluttered behind your back.
Undo, because instant shouldn't mean irreversible
Archiving a task or deleting a note happens with no confirmation dialog — but neither is a dead end. Both drop a five-second Undo toast (click it, or press u), and undo restores the item exactly where it was: an archived task returns to its old board position rather than the bottom, and a deleted note slots back into its original place in the stack. It fits the whole design: act instantly, take it back if you didn't mean it. Let the five seconds lapse and it's committed.
Staying in sync across tabs
Open wherewasi in two tabs and they stay consistent — a change in one appears in the other within a moment, because every tab reads and writes the same local database and pings its siblings to refresh. No stale second tab quietly overwriting your work.
Keyboard
The app is fully keyboard-driven; press ? at any time for this list.
| Key | Action |
|---|---|
↓/j, ↑/k
|
Move the selection |
Enter |
Switch to selected — on the active task, jump into the note input |
a |
Write a note on the active task, from anywhere |
n |
New task |
e |
Rename selected task |
d |
Done — archive selected task |
u |
Undo the last archive or note deletion (while the prompt shows) |
Space / m
|
Expand / collapse selected task's notes |
v |
Toggle the archive |
/ |
Search (in the archive) |
Esc |
Leave input / close view / clear selection |
The fastest possible loop is j j Enter to land on a task, then Enter again to start typing what happened. Single-letter shortcuts are suppressed while you're typing, so notes never trigger commands.
On a phone
It's desktop-first, but it works on touch: the per-row actions (archive, rename, delete) are always visible instead of hiding behind hover, and it installs as a real PWA — proper maskable and Apple-touch icons, so the home-screen tile looks right on both Android and iOS, and it runs fully offline once installed.
Finishing and finding things
Pressing d (or clicking the ✓ that appears on the row) archives a task. It leaves the board but keeps its entire note stack, browsable under Archive and reopenable at any time. The archive has full-text search across both task titles and note contents — so months later, searching "headcount" surfaces the task where you wrote "travel numbers in, headcount still missing", even if the title never mentioned it.
How it's built
A deliberately small stack, chosen so the whole app stays readable in one sitting:
- Vite 8 + Svelte 5 (runes) + TypeScript — small bundle, minimal boilerplate
- Dexie 4 over IndexedDB — typed local persistence
- vite-plugin-pwa — generated service worker and manifest; installable and fully offline
-
GitHub Actions → GitHub Pages — every push to
mainbuilds and publishes
The data model
Three tables, all with UUIDs and timestamps:
Task { id, title, status: 'open' | 'archived', createdAt, archivedAt?, sortOrder }
Note { id, taskId, text, createdAt }
AppState { id: 'app', activeTaskId: string | null, title?: string } // exactly one row
The most important detail is the smallest one: "one active task" is a single pointer in AppState, not a boolean flag on each task. Two tasks being active simultaneously isn't a bug that has to be prevented — it's unrepresentable. Whole classes of state-sync bugs simply don't exist.
sortOrder is what makes switching bubble a task to the top: activating a task rewrites its order to just below the current minimum, and that reordering is persisted like everything else. That same persisted order is why undo can drop an un-archived task back into its exact former place.
State flow
A single store class (src/lib/store.svelte.ts) holds $state runes, hydrated from IndexedDB once at startup. Every mutation writes to Dexie first, then updates in-memory state, then broadcasts a one-line "changed" ping to any other open tabs. There is no memory-only state anywhere, so a refresh, a crash, or a closed laptop lid loses nothing. Empty titles and empty notes throw rather than silently no-op.
The UI is a handful of small components — the board, a task row, the note form, and a help overlay — none of which own any persistent state of their own.
Local-first, on purpose
Everything lives in your browser's IndexedDB. No account, no sync server, no telemetry, no network calls at all. On load the app calls navigator.storage.persist() to ask the browser not to evict the board under storage pressure. That local-first choice has real consequences worth being honest about:
- It's single-device. Tabs in the same browser stay in sync, but a different machine is a different board. The data model was built sync-ready (UUIDs and timestamps everywhere), so a sync layer can be added later without remodeling.
- Browser storage still isn't a vault. Persistent storage makes eviction far less likely, but clearing site data wipes it. A JSON export is the obvious next hardening step.
For a tool whose whole job is holding your working context, that trade is deliberate: zero setup, zero latency, and nothing to trust.
Where it goes next
Already shipped since the first cut: five-second undo for archiving and note deletion; multi-tab consistency via BroadcastChannel; persistent storage via navigator.storage.persist(); a nameable workspace; and clean PWA installation on iOS and Android with maskable and Apple-touch icons.
Still ahead, roughly in priority order:
- JSON export / import — the rest of durability: a backup you own and can move.
- Sync — a self-hosted backend (PocketBase or CouchDB/PouchDB) the static frontend talks to, giving multi-device use without handing task contents to a third party.
-
Tests — the store's invariants (one active task, archiving clears the pointer, switching reorders, undo restores in place) are exactly the logic worth pinning down with Vitest and
fake-indexeddb.
Try it
https://alicommit-malp.github.io/wherewasi/ — it installs as a PWA from the browser menu and works with the network off. Add a couple of the things you're juggling right now, switch between them, and leave yourself a note on the way out. The payoff arrives the next time you come back.

Top comments (0)