# 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.