feat: shared file manage TUI with inline editor#344
Merged
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract a reusable TUI shell from the collect picker into shared_picker/, defining a PickerMode trait that separates generic navigation/rendering from mode-specific business logic. New module structure: - shared_picker/mod.rs — PickerMode trait, LoopAction, EntryDecoration - shared_picker/state.rs — generic PickerState (navigation only) - shared_picker/input.rs — routes navigation in shell, delegates to mode - shared_picker/render.rs — uses mode for decorations/footer/warnings - shared_picker/collect_mode.rs — CollectMode implementing PickerMode - shared_picker/dialog.rs — reusable confirmation dialog - shared_picker/shell.rs — terminal restore helper - shared_picker/highlight.rs — unchanged syntect highlighting The collect picker works exactly as before — same keybindings, same rendering, same behavior. All 943 unit tests pass, all integration tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement the core of `daft shared manage` — an interactive TUI for inspecting and modifying shared file status (linked/materialized/ missing/conflict/broken) across all worktrees with immediate actions. - Add WorktreeStatus enum and detect_shared_statuses() to core/shared - Create ManageMode implementing the PickerMode trait - Add tab_idx parameter to entry_decoration for status lookup - Wire manage subcommand and run_manage_picker entry point - Add 7 unit tests for status detection (linked, missing, not-collected, materialized, conflict, broken symlink, mixed multi-worktree) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Press `d` on a worktree entry to set it as the diff pivot, then navigate to other worktrees to see a colored line-level diff in the preview panel. Press `d` again or `Esc` to exit diff mode. Shows contextual messages for identical files, missing files, and when on the pivot itself. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a confirmation modal triggered by r/Del/Backspace that lets users remove a shared file with two options: materialize into selected worktrees (with expandable per-worktree checklist) or delete everywhere (with secondary confirmation). After removal, updates daft.yml, materialized.json, and shared storage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add an interactive file tree browser modal triggered by pressing 'a' in the manage TUI. Supports directory expand/collapse, search filtering, selecting existing files to share, and declaring new shared file paths when no matches are found. After adding, tabs refresh automatically. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove early return when no shared files declared — the manage TUI now launches with an empty tab list so users can press 'a' to add their first shared file. - Add 'manage' to bash, zsh, and fish shell completion scripts for the 'daft shared' subcommand list. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The manage TUI now always shows a + tab at the end of the tab bar. Navigating to it and pressing Enter, Space, or a opens the add file modal. Supports empty state where no shared files exist yet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Start with TabBar focus when no real tabs exist (only the virtual + tab), so Enter/Space/a immediately triggers the add flow - Down from TabBar on virtual tab goes to footer (was a no-op) - Up from footer on virtual tab returns to TabBar (was panicking on current_tab() with no real tabs) - Tab key in footer guarded against virtual tab state Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… UI fixes Rewrite the add-file modal as a full-screen fuzzy finder: - Instant tree view on open (synchronous top-level scan) - Background-indexed fuzzy search via nucleo-matcher with match position highlighting (underline) - Gitignore-aware styling via ignore crate: orange for ignored dirs, white for ignored files (good sharing candidates), muted for tracked - Expandable folders with arrow key navigation in browse mode Align add behavior with sync/collect parity: - Extract compute_materialization_defaults() in core/shared.rs so both sync and manage use the same deep-compare logic - Identical copies across worktrees get linked, different copies get materialized, missing worktrees get symlinked Fix selection legibility across all TUI components: - Cursor row always uses bright white text on dark bg - Tags inherit cursor bg for consistent highlight - Remove modal: expanded worktree list uses normal color instead of dim, selection highlight only covers checkbox+name (not leading whitespace), uses white-on-orange instead of black-on-orange Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refresh_all now sets focus to WorktreeList after rebuilding tabs, preventing the cursor from being stuck on TabBar (where Down skips to Footer instead of navigating the worktree list). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use pattern.score() (cheap) for all entries during search, and compute pattern.indices() (expensive) only for the ~20 visible items during render. Fixes search lag caused by computing match positions for every entry on every keystroke. Add effective_score() that blends nucleo's raw score with match density (matched chars / path length) and depth penalty, so short dense matches like ".env" for query "env" rank above longer paths like "src/environment.rs". Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Embed an edtui-based Vim editor in the preview pane. Press Enter while focused on the preview to start editing, Esc (in Normal mode) to save and exit. A color-coded header shows whether the shared copy (green) or a materialized copy (yellow) is being edited. - Add edtui dependency with syntax-highlighting feature - Create editor.rs with EditSession (buffer, save, render, key routing) - Wire Enter in preview to start_edit via handle_list_key delegation - Intercept all keys in manage event loop during editing - Add render_editor to PickerMode trait, implement for ManageMode - Change render chain to &mut dyn PickerMode for editor rendering - Syntax highlighting uses base16-ocean.dark theme (matching preview) - .env files mapped to shell syntax (matching existing highlighter) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix theme name: base16-ocean.dark -> base16-ocean-dark (edtui bundles its own theme set with hyphenated names) - Add line numbers (LineNumbers::Absolute) - Preserve bordered frame around editor (matching preview pane) - Remove misleading :w hint from header (save is automatic on Esc) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Switch from Vim to emacs mode (EditorEventHandler::emacs_mode) for a simple editing experience: just type, arrow keys to move, Esc to exit - Esc exits directly (no double-Esc needed) - Dim line numbers (color 239) to distinguish from file content - Simplify header: show "Editing shared copy" (green) or "Editing materialized copy (name)" (yellow), no mode indicator - Simplify block title to just "Edit" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prepend right-aligned line numbers in dim gray (Color::Indexed(239)) to the syntax-highlighted preview, matching the editor's line number style. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Inset the editor area by 1 column to create visual padding before line numbers, matching the preview pane's line number style. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fill the 1-column leading spacer with the same Rgb(30,30,30) background as edtui's gutter, so it blends seamlessly with the line number column. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mbers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ignore Release and Repeat key events which caused double-processing and unresponsive behavior in emacs mode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EditorState defaults to Normal mode, but emacs mode has no keybinding to enter Insert mode. Set mode to Insert on creation, matching the official edtui emacs example. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d saves - Remove separate header row; merge info into block title: "Editing shared copy" (green) or "Editing <name> (materialized)" (yellow) - Frame border color matches the title color (green/yellow) - "Esc: save & exit" hint in the bottom-right of the frame border - Only write to disk if content actually changed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pad between worktree name and status tag (linked/materialized/etc.) so tags form a right-aligned column flush with the panel edge. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e len Unicode characters like ▸ and → are multi-byte but single-column width. Using .len() (byte count) over-estimated the left content width, causing the right-aligned tag to jump inward on cursor rows. Switch to .chars().count() for correct display width calculation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The spacer line between tabs and body is now always 1 row. Info/warning messages fill this row instead of inserting a new one, preventing the body content from shifting down when toggling materialize/link. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prefix all info messages from toggle_materialize and link_entry with the worktree name (e.g., "feat/foo: materialized (local copy created)") so the action context is clear when navigating back to a tab. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pointer (▸) and white text stay visible on the current worktree entry even when focus is on the preview or editor pane, making it clear which worktree's file is being viewed/edited. The background highlight only appears when the worktree list itself is focused. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The padding spaces and right-aligned status tag now also get SELECTED_BG when the current entry is highlighted (both focused and unfocused), so the entire row is uniformly highlighted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the inline editor is active on a shared (linked) file, all other linked worktrees are highlighted in green in the worktree list, making it clear which worktrees will be affected by the edit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… exit - Add SELECTED_BG background to co-edited linked worktrees (green text alone was not visible enough on dark terminals) - Only exit editor on plain Esc with no modifiers, preventing Shift+Tab escape sequences from triggering an unwanted exit Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The padding and status tag spans now also get SELECTED_BG for co-edited linked worktrees, matching the full-row highlight. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…itor Some terminals send Shift+Tab as an Esc-prefixed escape sequence that crossterm splits into a bare Esc followed by additional characters. After receiving Esc, peek for a follow-up event within 20ms — if one arrives, it was part of a sequence, not a standalone Esc press. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When toggling a materialized file to linked, deep-compare the local copy against the shared file. If they differ, show a confirmation dialog (defaulting to No) warning that local changes will be lost. Identical copies are linked without prompting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add vertical and horizontal padding inside the dialog border, indent body text by 2 chars, and indent button row to match. Wider dialog (54 cols) for better readability. Consistent with other modal overlays. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove Wrap which caused padding to break on line wraps - Auto-size dialog width to fit longest body line (no more truncation) - Shorter body lines to avoid needing wrapping at all - Keep dialog as overlay (Clear only clears dialog area, rest of screen retains previous frame content via ratatui diffing) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The dialog now accepts an optional background render callback (FnMut). The link-overwrite confirmation in manage mode passes the full UI renderer, so the dialog overlays on top of the manage interface instead of appearing on a blank screen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Modifier::DIM to all buffer cells before rendering the dialog overlay, creating a modal backdrop effect that draws attention to the dialog while keeping the underlying UI visible but subdued. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
daft shared manage) with tabbed interface showing all shared files across worktreesKnown issues
Test plan
mise run clippypasses with zero warningsmise run test:unitpasses (960+ tests)mise run test:manual -- --ci managepassesdaft shared manage, navigate tabs/worktreesm, verify file operationsd, compare files across worktrees+tab ora, verify fuzzy search and gitignore stylingr, verify materialization optionsFixes #339
🤖 Generated with Claude Code