Files
pad/web
xarmian 8eb4519fe7 feat(editor): a tab that dies with unconfirmed edits offers them on the next open (TASK-3606) (#2086)
* feat(editor): a tab that dies with unconfirmed edits offers them on the next open (TASK-3606)

The unload prompt and teardown flush never run when a tab dies outright
(a mobile process kill, a browser crash). This is the simple alternative
to IDEA-3494's Y.Doc persistence: nothing CRDT-shaped is stored.

- crashDraft.ts: while the open item holds edits the server may not
  have (editsAtRiskOnClose, or a dirty raw editor), its markdown is kept
  every 2s in the kept-draft store with the seq and body it was edited
  from. It is cleared only when the same item reports nothing at risk
  and the kept text is still its own. Switching items clears nothing.
  Bodies over 1M characters are not kept, and nothing is kept during a
  confirmed sign-out discard.
- refusedRawDraft: optional baseSeq/baseBody (back-compatible). A full
  store sweeps expired drafts, evicts the user's oldest other one, and
  retries once. Storage stays localStorage: on :7777 the largest body
  is 158K chars (about 316 KB UTF-16) against a ~5 MB quota (design note
  on TASK-3606).
- The next open offers it through the existing notice and expected_seq
  restore. The notice is producer-neutral now. When the item has moved
  past the draft's base seq, it warns that restoring replaces the newer
  version and makes Copy the primary button (lead ruling). baseBody is
  stored for the follow-up "apply my changes" restore.

Claude-Session: https://claude.ai/code/session_01FAVXA5mJ5BNhKmv95jxQkQ

* perf(editor): the crash-draft keeper serializes only when the document changed (TASK-3606 codex r1)

Every tick while edits were at risk serialized the whole document, even
an unchanged one. The provider now counts every doc update (docVersion,
local and remote), and the keeper skips serializing while that version,
or a raw editor's pending text, matches its last write.

Claude-Session: https://claude.ai/code/session_01FAVXA5mJ5BNhKmv95jxQkQ

* fix(editor): a crash draft is never filed under a user who did not type it (TASK-3606 codex r2)

The keeper keys drafts by the signed-in user. After an identity change,
and before the reload re-stamps identityEpochAtLoad, the editor still
held the previous user's text. The snapshot now returns nothing unless
the identity is the one the item was loaded under, the teardown flush's
own fence. The identity listener already keeps that text for its
author.

Claude-Session: https://claude.ai/code/session_01FAVXA5mJ5BNhKmv95jxQkQ

* test(items): the identity-epoch census names the crash-draft snapshot's fence (TASK-3606)

The page-load epoch is read at six sites now. The sixth is
crashDraftSnapshot's guard, which keeps a previous user's typing out of
the next user's draft key (codex round 2 on #2086).

Claude-Session: https://claude.ai/code/session_01FAVXA5mJ5BNhKmv95jxQkQ
2026-10-11 21:50:06 -04:00
..
2026-03-26 01:52:36 +00:00
2026-03-26 01:52:36 +00:00

Pad Web UI

SvelteKit 2 + Svelte 5 frontend for Pad, compiled to static files and embedded into the Go binary.

Development

npm install
npm run dev          # Dev server at localhost:5173 (proxies API to localhost:7777)
npm run build        # Production build to build/
npm run check        # Type checking with svelte-check

When developing, run the Go backend separately with make dev from the project root.

Building for Production

Do not build in isolation. Always use make build from the project root — this builds the web frontend, then compiles the Go binary with the build output embedded via //go:embed.

Stack

  • Svelte 5 with runes ($state, $derived, $effect)
  • SvelteKit 2 with adapter-static (SPA mode)
  • Tiptap block editor with markdown round-trip
  • svelte-dnd-action for drag-and-drop in board/list views
  • SSE for real-time updates
  • TypeScript throughout

Structure

src/
  routes/                    SvelteKit pages
    +layout.svelte           App shell (sidebar + main)
    +page.svelte             Landing/redirect
    [workspace]/
      +page.svelte           Dashboard (collections, phases, activity)
      +layout.svelte         SSE connection per workspace
      [collection]/
        +page.svelte         Collection view (board/list)
      [collection]/[item]/
        +page.svelte         Item detail + editor
      conventions/            Purpose-built conventions page
      playbooks/              Purpose-built playbooks page
      settings/               Workspace settings
  lib/
    api/client.ts            HTTP API client
    components/
      layout/                Sidebar, navigation
      editor/                Tiptap editor, raw markdown editor
      fields/                FieldEditor, relation picker
      items/                 ItemCard, ItemDetail
      collections/           BoardView, ListView
      common/                StatusBadge, badges, modals
      search/                CommandPalette
    stores/                  Svelte 5 reactive stores
      workspace.svelte.ts    Workspace state
      collections.svelte.ts  Collection + item state
      ui.svelte.ts           Sidebar, mobile state
    types/index.ts           TypeScript types and constants
  app.css                    Global styles and design tokens