Files
typst-leaf/PLAN.md
T
nav.sikand 008dab1cf9 feat: IDE shell, file management, UI rehaul, reliability, and git workflow
This is the next major chunk on top of the barebones MVP. It implements
the IDE shell, resizable editor/preview workspace, file CRUD, Git workflow
UI upgrade, SyncTeX/LSP reliability hardening, and documentation updates.

Highlights:

* New shared path utilities (typst-leaf-frontend/src/path-utils.ts):
  normalizeFsPath, encodeVirtualPathForFileUri, relativePathFromProject.

* SyncTeX reliability:
  * Jump targets now carry stable numeric IDs for deduplication.
  * tinymist/preview/scrollSource and window/showDocument payloads are
    validated and normalized through shared path utilities.
  * External preview paths are reported via a toast instead of silent drop.
  * Editor uses monaco.Uri.equals for URI comparison.

* IDE shell rehaul:
  * New components: TopBar, StatusBar, PanelSection, ResizableSplit,
    ToastHost, EmptyState.
  * App.tsx now uses a layered layout: top bar, sidebar tabs
    (Files/Git/Settings), resizable main workspace, status bar, toasts.
  * Sidebar tabs replace the old inline Git/settings panels.

* Resizable workspace:
  * Pointer-driven resizable split between editor and preview.
  * Split ratio persisted to localStorage (typst-leaf.split).
  * Double-click splitter resets to 55%.
  * onResize prop dispatches typst-leaf:layout so Monaco re-layouts.

* File management:
  * Backend CRUD: POST /file, DELETE /file, PATCH /file, POST /folder.
  * Frontend API wrappers and inline FileTree create/rename/delete.
  * Backend tree endpoint returns empty directories in a folders array.
  * Rename protects the .typ extension and rejects overwriting existing
    files atomically on POSIX.
  * Newly created files are opened automatically.
  * Dirty file badges in the tree.

* Git workflow upgrade:
  * GitPanel now shows changed files grouped by staged/unstaged, deduped
    by path, with per-file status icons.
  * Click a changed file to open it.
  * Commit disabled when clean or no message.
  * Push/pull with success/error toasts.
  * FileTree shows dirty badges from git status.

* Preview UX:
  * Preview toolbar with status chip, reload button, and SyncTeX hint.
  * Preview hidden/marked as "open a file" when no file is open.

* State/UX infrastructure (app-state.tsx):
  * activePanel, fileDirty, saving, notice (toast), vimOn, treeVersion,
    folders, and stable jumpTarget.id.
  * focusCommit switches to the Git panel.
  * selectProject resets more state to avoid stale data.

* Editor improvements:
  * Monaco model disposed on unmount so LSP sees didClose.
  * mountedRef survives React StrictMode remounts.
  * Global save/layout events bridge TopBar and ResizableSplit to the
    editor instance.
  * Vim mode state moved to global state/localStorage.
  * Removed redundant automaticLayout and inline Save button.

* Documentation:
  * Added DESIGN.md with the full UI aesthetic guide.
  * Updated PLAN.md, README.md, VISION.md, and the frontend README.

Verification:
  * pnpm -r typecheck passes.
  * pnpm --filter typst-leaf-frontend build passes.
2026-07-02 02:05:04 +05:30

8.1 KiB

Next Chunk: IDE Shell, File Management, UI Rehaul, Reliability, Git Workflow

This chunk is the next deliverable on top of the bfb4d8b commit. It picks options 1, 2, and 3: IDE shell + file management + UI rehaul, reliability and LSP/Preview hardening, and Git workflow upgrade. Direction and aesthetic decisions live in DESIGN.md — every UI decision below should read against that doc.

Guiding Constraints

  • Tailwind only, light theme only, no dark variant.
  • Browser-first; Tauri APIs only behind window.__TAURI__ guards.
  • No databases; filesystem + .git remain the source of truth.
  • No polling for file or preview changes.
  • Backend stays a thin proxy. No Typst or LSP reimplementation in TypeScript.
  • tinymist remains the LSP/preview source of truth.
  • Keep the raw JSON-RPC LSP client. Do not reintroduce monaco-languageclient.
  • Strict TypeScript with zod at API boundaries.

Aesthetic Direction (see DESIGN.md)

  • Document-editor tone: warm paper background, subtle emerald accents, no marketing gradients.
  • Dense-but-readable developer layout. System sans for chrome, monospace for paths/git.
  • Rounded corners and borders restrained to "panel" and "card"; default to rounded-md.
  • Status indicators must read clearly (label + color, never color alone).
  • Splitter, focus rings, etc. all designed in DESIGN.md; the implementation must match.

Phase 1 — Path Utilities and Jump Reliability

Move path/jump helpers into a single module so all callers use the same normalization rules.

New file

  • typst-leaf-frontend/src/path-utils.ts:
    • normalizeFsPath(input: string): string
    • encodeVirtualPathForFileUri(path: string): string
    • relativePathFromProject(projectUri: string, filepath: string): string | null

State changes (app-state.tsx)

  • jumpTarget: { id: number; file: string; start?: [number,number]; end?: [number,number] } | null
  • setJumpTarget action carries an id; reducer increments counter and stamps.
  • New pushNotice action for transient messages ("Preview source outside project").

LSP hardening (lsp.ts)

  • Validate tinymist/preview/scrollSource payload strictly: filepath string, optional numeric start/end.
  • Forward only well-formed paths to onJumpToSource.

Editor (Editor.tsx)

  • Use shared path utility.
  • Compare URIs via monaco.Uri.equals after parsing both sides.
  • Dedupe by jump.id, not object identity.
  • Keep the existing ResizeObserver and editor.layout() call before reveal.

Verification

  • External preview path ignored with non-blocking message.
  • Numeric start/end coercion.
  • Rapid clicks keep latest, no swallowed targets.

Phase 2 — UI Foundation (Rehaul)

Replace the current sparse shell with the layered "IDE" layout in DESIGN.md.

Files

  • typst-leaf-frontend/src/App.tsx (restructure)
  • typst-leaf-frontend/src/index.css (extend tailwindcss import; nothing else without need)
  • New components:
    • TopBar.tsx
    • StatusBar.tsx
    • Sidebar.tsx
    • PanelSection.tsx
    • ActivityBar.tsx
    • ResizableSplit.tsx
    • ToastHost.tsx
    • EmptyState.tsx

Layout

  • Top bar: project switcher, current file/dirty indicator, save, LSP/preview/git chips, settings.
  • Sidebar tabs: Files / Git / Settings.
  • Main: resizable editor / preview split.
  • Status bar: branch, clean/dirty, LSP, preview, vim state, cursor (optional).

Aesthetic tokens (mirror DESIGN.md)

  • bg-stone-50 for chrome, bg-white for editor surface, bg-emerald-600 for primary actions, border-zinc-200 for panel dividers, text-zinc-500 for muted metadata.
  • Tailwind reference snippets are kept in DESIGN.md.

QOL

  • Toasts for save/commit/push/pull, configured via pushNotice.
  • Clear empty states ("Select a project", "Open a file", "No changes to commit").
  • Keyboard shortcuts surfaced in StatusBar hints later if time.

Phase 3 — Resizable Workspace and Preview UX

  • Build ResizableSplit.tsx (pointer-driven, double-click reset, persisted ratio).
  • Persist typst-leaf.split in localStorage.
  • Add PreviewToolbar on top of iframe: status chip, reload button, "Click to jump" hint.
  • During drag and on drag-end, call editor.layout() so Monaco keeps accurate viewports.

Phase 4 — File Management

Make projects usable from the UI.

Backend

typst-leaf-backend/src/routes/files.ts:

  • POST /api/projects/:project/file
  • DELETE /api/projects/:project/file?path=…
  • PATCH /api/projects/:project/file
  • POST /api/projects/:project/folder

All paths through resolveFile/relPathSchema. zod validation stays.

Frontend

api.ts:

  • createFile, deleteFile, renameFile, createFolder.

types.ts:

  • Add request types.

FileTree.tsx:

  • Group tree view; folders from existing file paths.
  • Inline create/rename/delete actions; confirm destructive deletes.
  • After mutation: refresh tree, dispatch bumpGitVersion, open new file or close deleted open file (with model disposal).

app-state.tsx:

  • Action setTree already exists. Add bumpFileTree counter or reuse setProjects invalidation by re-fetching tree on a treeVersion.

Phase 5 — SyncTeX/LSP Reliability Hardening

(See Phase 1.) This phase covers the user-visible behavior of the jump path:

  • Stable IDs prevent dropped or duplicated jumps.
  • External path is announced via toast instead of silently dropping.
  • Vue of preview-after-resize: drag resize triggers editor.layout(). Preview iframe reload button re-syncs.
  • A small dev-only "Diagnostics" panel hidden behind ?debug=1 later is optional.

Phase 6 — Git Workflow Upgrade

Backend

  • GET /api/projects/:project/git/diff returning unified diffs (later; not blocking if skipped).
  • Optional POST /api/projects/:project/git/stage for partial staging (later).

Frontend

  • GitPanel.tsx becomes a workflow panel:
    • Branch + ahead/behind + clean state
    • Changed files list grouped by staged/unstaged/untracked (icons per status)
    • Commit composer
    • Push/Pull controls
    • Remote inline input
    • Recent commits list (keep existing)
  • FileTree.tsx shows dirty file badges.
  • Toasts on success/failure: "Committed abc1234", "Pushed to origin/main", etc.
  • Disable commit when clean; message should explain why.

types.ts:

  • Confirm FileStatusInfo carries A|M|D|? (already does).

Phase 7 — State and UX Infrastructure

Keep state in app-state.tsx (no Zustand yet). Add UI actions:

  • setActivePanel (files | git | settings)
  • setFileDirty
  • setSaving
  • pushNotice / dismissNotice
  • setSplitRatio (effect persists via localStorage directly; not strict state)

Toast component lives once at App.tsx, reads notices via context.

Phase 8 — Doc and Metadata Cleanup

  • README.md: port 1420, raw LSP client, preview-via-LSP-and-proxy model.
  • VISION.md: note raw LSP client; soften monaco-languageclient claim.
  • typst-leaf-frontend/README.md: replace Tauri-template text.
  • PLAN.md: replace with this plan at commit time. Keep DESIGN.md separate and referenced.

Implementation Order

  1. Shared path utilities + jump IDs.
  2. SyncTeX hardening (rely on shared utils, not yet aesthetic).
  3. UI shell rehaul (TopBar, Sidebar, ResizableSplit, StatusBar, ToastHost).
  4. File CRUD backend + frontend integration, FileTree rebuild.
  5. Git workflow UI rebuild + badges in tree.
  6. Preview toolbar and reload.
  7. Doc cleanup.
  8. Verification:
    • pnpm -r typecheck
    • pnpm --filter typst-leaf-frontend build
    • Manual checks: file CRUD, resize-during-jump, Git flow, external preview path ignored.

Risks

  • File URI/path encoding is still the highest-risk area; reuse existing helpers in Editor.tsx.
  • Rename/delete of currently open file must dispose Monaco model.
  • Git status can stale if a file mutation does not bump gitVersion.
  • Resizable panes must call layout() during and after drag.
  • Preview iframe behavior is owned by typst-preview; do not over-customize it.

Scope Boundary

In:

  • IDE shell rehaul, resizable split, file CRUD, Git workflow UI upgrade, SyncTeX reliability, toasts, status bar.

Out:

  • Per-file staging workflow.
  • Rich diff viewer.
  • Drag/drop reordering, multi-tab editor, collaborative cursors.
  • Tauri native file picker.