Skip to content

Add OpenSpec proposal path-box: per-panel path boxes replace the command line - #59

Open
kkaminsk wants to merge 18 commits into
mainfrom
Spec
Open

kkaminsk wants to merge 18 commits into
mainfrom
Spec

Conversation

@kkaminsk

@kkaminsk kkaminsk commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Summary

OpenSpec proposal path-box (openspec/changes/path-box/), tracked by Linear issue BIG-900. Spec only — no implementation.

  • Replaces the command line above the F-key bar with two path boxes, one under each panel, showing that panel's directory.
  • Ctrl+G (key.focus_path) or a click focuses a box; Tab / Shift+Tab complete folder and file names anywhere on disk (case-insensitive prefix, directories first, off-thread directory reads); Enter lands the panel cursor on the named entry, navigating to its parent if needed; Esc cancels.
  • Retires the command-line capability: cd / del / rmdir builtins, command history (Up/Down, Alt+F8, Commands → History), Ctrl+Enter / Ctrl+] paste, Ctrl+Y clear, and the key.paste_name / key.paste_path / key.clear_line config keys are removed. Ctrl+O and the suspended shell run (Run entry, F2 user menu, configurable shell) move to application-shell unchanged.
  • Panel keys stop depending on hidden text state: Tab, Backspace, Up/Down and +/-/* always keep their panel meanings.
  • Delta specs for the 13 other capabilities that mention the command line; MODIFIED blocks restate the current requirement text verbatim with only the wording changed (scenario titles are kept, per the validator — see design D11).

Verification

  • openspec validate path-box --strict passes.
  • Claims in proposal/design were fact-checked against the code; corrections applied (notably: the panel comparator interleaves files and folders, so the directories-first completion order needs its own comparator).

After merge

Implementation goes on build/path-box off main. The auto-refresh change's probe eligibility should also exclude a focused path box, whichever of the two is built second (design D10).

🤖 Generated with Claude Code

kkaminsk and others added 18 commits October 7, 2026 19:38
Decorative ASCII animations for the startup splash (frame trace, identity-line reveal, title sweep inside the 800 ms hold, then a panel wipe), the F9 menu bar and every pop-up menu (sweep, top-down unfold, highlight settle), and every modal dialog (100 ms iris). All motion is render-only interpolation of the reducer's Tick clock against open-time stamps, so core::update stays pure, input is never blocked, and insta snapshots stay deterministic. Disabled via `animations = false` or `--noanimations` (flag wins); no new theme roles, so user theme files keep validating. Supersedes design doc §4.10's no-animation rule for decorative chrome only; loading feedback stays static. Adds the new ui-animations capability and deltas for startup-splash and pulldown-menus.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
After a confirmed delete (and every other same-directory re-read: copy/move/mkdir completion, Ctrl+R, stale-tab refresh) the panel jumps back to the top because Panel::begin_new_listing resets cursor and scroll offset unconditionally. This proposal adds a re-read anchor so the cursor returns to its entry or the nearest survivor (next after a delete, Norton Commander style) and the scroll offset is restored and re-clamped, applied while the listing streams so there is no transient jump. Modifies panel-navigation and file-operations.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Resolve one per-user profile directory (--config-dir flag, portable marker, or %APPDATA%\FileCommand) for config.toml, history.json, and usermenu.toml instead of the launch directory; create it on first run, fail soft with a startup warning, and show a legacy notice for old files in the working directory without migrating them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Render Full-mode Size, Date, and Time values untruncated (9/8/5 cells, sizes right-aligned) by spending the row's slack cells; reserved widths and the column-drop ladder are unchanged so the 80x24 four-column guarantee holds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Introduce a shared LineEdit model (new text-field-editing capability) so every single-line field and the command line get a movable caret, Home/End, word keys, Delete, Ctrl+Backspace, and clipboard paste, with scrolling field rendering; panel key meanings are preserved.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Bind Ctrl+Y (rebindable via key.clear_line) to the existing CommandLineClear command over the panels, ending any history recall; Esc keeps requesting quit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Carry the hidden/system attribute on listing entries and render them with a new panel.hidden theme role in Full and Brief modes, with drag > cursor > selected > hidden > file/directory precedence; no show/hide toggle.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Wire the greyed pull-down items that already have backing features (View, Edit, Filter, New/Close tab), add the small missing ones (Alt+F8 history dialog, Ctrl+U swap panels, Menu file edit with reload), and remove Attributes, Compare directories, Configuration, Editor selection, and Save setup.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add an Open entry to the file-action menu (first and highlighted for documents, after Run for executables, Explorer for directories) that launches the target through the platform file association without suspending the TUI and reports failures on the panel status line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
help-flags: the flag is accepted anywhere, not only first. delete-key-triggers-delete: the guard is an exact no-modifier check, not is_plain. delete-builtins-reject-dot-targets: the function is dispatch_delete_builtin. reread-preserves-cursor: exclude the Tree-mode preview caller, restore TabData, handle quick-filter and Brief-mode offsets. ui-animations: --noanimations precedence is applied in run, stamp About and the startup warning, and correct the config and usage wording.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…reject-dot-targets

All three are fully implemented, so their spec deltas are synced into application-shell, panel-tabs, and command-line and the changes move to the archive. delete-key-triggers-delete stays open until its manual check (task 3.3) is done.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The manual Delete-key check (task 3.3) is confirmed done and validation is clean. The spec delta was already present in operation-dialogs, so the archive only moves the change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Resolves the duplicate archive of delete-key-triggers-delete in favour of
main's 2026-10-14 copy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Checks each visible panel's directory every refresh_interval seconds
(default 60, configurable, 0 = off) on a worker thread by comparing a
content digest of its entries with the listing on screen, and re-reads the
panel in place — cursor, scroll, selection, and sort kept, no history
visit — only when the directory actually changed; a no-change check draws
nothing. Adds F12 as a second binding for Ctrl+R's re-read and an eleventh
`12Refresh` slot on the F-key bar (77/56/23-column forms). Deltas touch
responsive-layout, sort-modes, mouse-input, application-shell,
panel-navigation, and configuration-files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replaces the command line above the F-key bar with two per-panel path boxes that show each panel's directory, Tab-complete folder and file names anywhere on disk, and on Enter land the panel cursor on the named entry. Retires the command-line capability (cd/del/rmdir builtins, command history, paste-name/path, clear-line), re-homes Ctrl+O and the suspended shell run under application-shell, and adds delta specs for the twelve other capabilities that mention the command line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-03T20:20:05.758564Z 4cc3dfc PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4cc3dfcbf8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


- **The command-line row becomes two path boxes.** The row above the F-key bar splits at the panel divider. Each box shows its panel's current folder followed by a path separator (`C:\Github\FileCommand\`) and follows the panel through navigation, tab switches and panel swaps. Unfocused boxes are drawn in the existing `commandline` theme role; a long folder left-truncates with a leading `…`.
- **A box must be focused to type into it.** Ctrl+G (rebindable via `key.focus_path`) focuses the active panel's box; a mouse click on either box focuses that box and makes its panel active. While a box has focus it owns the keyboard and the terminal cursor and is drawn in the dialog text-field style. Esc, or a click outside the box, returns focus to the panel and resets the box to the panel's folder. While no box is focused the panels keep every key meaning they have today — Tab still switches panels, Backspace still goes to the parent, `+`/`-`/`*` always select — and plain printable keys over the panels are ignored.
- **Tab completes a path segment.** The text before the last separator names a folder; the text after it is a case-insensitive prefix. Tab replaces that last segment with the next child of the folder whose name starts with the prefix (folders first, then files, each sorted by name); Shift+Tab goes back; both wrap. A completed folder gets a trailing separator so the next Tab descends into it. The panel's mini-status shows `3/12 matches`, `no matches`, or `Reading…` while the folder is being read. Candidates come from a panel's own listing when the folder is that panel's current folder, otherwise from a directory read on a worker thread; a reply that no longer matches the box text is discarded.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Define one Tab behavior after completing a directory

This says the separator appended to a directory makes the next Tab descend into it, but specs/path-box/spec.md requires that same next Tab to advance to the next sibling candidate and wrap. For any cycle whose first candidate is a directory, both behaviors cannot be implemented, so align the proposal, design, and cycling scenario before implementation.

Useful? React with 👍 / 👎.


### Requirement: Completion reads run off the event loop

When no complete panel listing covers the folder, the reducer SHALL emit a completion-read request tagged with a request id, run on a worker thread through the `\\?\` long-path abstraction, with the mini-status showing `Reading…` until the reply arrives; Tab meanwhile SHALL be ignored. A reply SHALL be applied only when its id is outstanding and its folder is the one the text names. Names SHALL be reused while the folder part of the text is unchanged.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve a new prefix while completion is pending

When a user starts a read for D:\Proj\re and edits the text to D:\Proj\x before it returns, the reply still passes this id-and-folder guard because the folder is unchanged. The design freezes the old prefix and the reply automatically applies its first candidate, so it can overwrite the user's newer text; either guard the complete input/version or recompute candidates from the current prefix before applying the reply.

Useful? React with 👍 / 👎.

- [ ] 2.6 `ListingComplete`: exact-then-case-insensitive match for `pending_cursor_target` (design D5); find-file tests still pass.
- [ ] 2.7 Close the box whenever a layer above the panels opens (design D1): file-operation dialogs, overlays, the startup warning.
- [ ] 2.8 Reducer tests for every `path-box` scenario, run with no filesystem (application-shell "The reducer performs no filesystem I/O": all four path-box scenarios).
- [ ] 2.9 If `auto-refresh` is already merged: add `path_box.is_none()` to its probe eligibility with a test (design D10).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the auto-refresh path-box guard unconditional

The parent already contains the active auto-refresh proposal, whose implementation tasks and eligibility tests do not mention path boxes. If path-box is implemented first, this conditional task is skipped and the later auto-refresh implementation can re-read a panel while its path box is focused, invalidating the listing from which a completion cycle was derived; update the auto-refresh proposal now or otherwise assign the guard unconditionally to whichever change is implemented second.

Useful? React with 👍 / 👎.


**D3 — Completion state is derived from the text and cached per folder.** On Tab the reducer splits the text at the last separator into `(folder_text, prefix)`, resolves `folder_text` with `resolve_cd_target` against the box's panel `cwd` (so `..\`, `D:x\`, `\\srv\share\` and relative text work) and compares it with `completion.dir`. If equal and `names` is loaded it advances the cycle; otherwise it starts a new completion: if the folder equals either panel's `cwd` and that listing is `Complete`, the names come from that listing at once; else it emits `Effect::ListCompletions { request, dir }` and the mini-status shows `Reading…` until `Command::CompletionsListed { request, dir, names: Option<Vec<(OsString, bool)>> }` arrives. `names` holds every readable child, no `..`; the worker reads through `FsReader::read_dir` in a new `listing::list_children_for_completion`. Candidates are the names whose `sort_key()`-style lowercase form starts with the lowercase prefix, ordered by `(is_file, lowercase name)` — directories first, then files. This is a small comparator of its own: the panel's `cmp_entries` / `cmp_by_name` deliberately interleave directories and files (classic NC order) and are not reused here. The prefix used for matching is frozen when the cycle starts so repeated Tabs cycle instead of narrowing. Applying a candidate replaces the last segment with the on-disk name and appends a separator for a directory. Any text mutation other than Tab/Shift+Tab sets `index = None` but keeps `names` while `dir` is unchanged, so backspacing and retyping do not re-read. A reply whose `request` is not outstanding, or whose `dir` differs from the folder the current text names, is discarded; Tab during an outstanding read is ignored; `names: None` (unreadable) yields `no matches`.

**D4 — Enter reuses the cd probe and the find-file cursor landing.** `PathBoxAccept` resolves the whole text (ignoring one trailing separator) with `resolve_cd_target`. If it equals the panel `cwd` → close the box. If `parent_path` is `None` (drive root, UNC share root, `/`) → `begin_listing(side, path)`, close. Otherwise `(parent, name)`: when `parent` equals the box's panel `cwd` and its complete listing holds an entry matching `name` case-insensitively → immediate; when it equals the other panel's complete listing → likewise; else `Effect::ProbePath` with `ProbeIntent::Select { parent, name }` and `pending_note = Checking <path>…`. The reply is applied only if `path_box` is still focused on that side with the same text and the probe id is current; otherwise it is dropped silently. On success: if `parent != cwd`, `begin_listing(side, parent)` then `pending_cursor_target = Some(name)`; when already there, move the cursor directly with the same lookup `jump_to_prefix` uses, clearing a quick filter that hides the target; close the box. On failure: `last_error` names the path (filesystem-error-handling "Error messages name the affected path"); the box keeps focus and text. `ProbeIntent::{Cd, Delete}` are deleted; `PendingProbe { request, side, cwd, path, intent }` keeps its shape.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify root paths before closing the box

For a nonexistent drive root such as Z:\ or a misspelled UNC share root, parent_path is None, so this branch immediately changes the panel directory and closes the box without probing. The eventual listing failure therefore loses the editable path, contrary to the stated failure behavior that keeps focus and text; roots also need an off-thread existence check before navigation and closure.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant