Files
pad/web
xarmian 1eecf710c8 fix(web): angle-bracketed prose survives the editor: <Button>, Promise<T>, comments and unknown tags are text, not dropped markup (BUG-3557) (#2035)
The editor parsed markdown with html enabled, so `<Name>` in prose was a
tag the schema could not hold: dropped on the next flush (one keystroke,
or an agent's write applied by an open tab, which answered 200 while the
text vanished). `<Table>` became an empty table that was then removed.

htmlProse.ts derives the allowlist from the schema's own parse rules, so
the HTML the editor itself writes (tables with spans, HTML-only marks)
still reads back:
- an inline tag passes only if written lower case, matching an inline
  node's or a mark's rule, and closing one that passed; anything else
  is literal text;
- an HTML block passes only if every tag in it matches a rule; otherwise
  it is kept whole as an HtmlBlock (stored as a ```html fence);
- comments are visible text.
PadText escapes `<` only where the parse would read a tag, so
`Promise<T>` round-trips byte for byte. Both the editor and the headless
materializer take the same extensions, right after StarterKit.

materializerParity now skips and counts cases where the two live tabs
converge to different documents (BUG-3568, pre-existing: 2 of 300 seeds
on main), at most 2 of 50, and still requires the materializer to match
one of them; such cases stay out of the Go corpus (lead ruling).

Claude-Session: https://claude.ai/code/session_01FAVXA5mJ5BNhKmv95jxQkQ
2026-10-10 20:12:18 -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