Skip to content

Latest commit

 

History

History
240 lines (191 loc) · 13 KB

File metadata and controls

240 lines (191 loc) · 13 KB

VSCommander — Development Guide

Project Overview

Dual-panel file manager VS Code extension overlaid on a real terminal via pseudoterminal + node-pty.

It is designed to be as close replica as possible to the Far Manager. The Far Manager source code is located inside the "../FarManager" directory. Also there is a copy of Midnight Commander source code inside the "../mc" directory - both next to the project in the same parent directory.

IMPORTANT when implementing a new feature - carefully read the corresponding Far Manager code to get a complete understanding of:

  • the feature logic
  • edge cases and oddities
  • visual representation
  • hotkeys, key bindings, and mouse interactions and replicate the feature EXACTLY.
  • when implementing a file system file-related feature (copy, move, deletion, etc) - think of all possible edge cases (low disk space, different filesystems, different drives for the source and target locations, symlinks, hardlinks, recursive soft- and hardlinks, file system errors, bad blocks, missing access permissions, incorrect file state and opned descriptiors, and much more). Read the Far Manager source code and Midnight commander for reference before thinking on how to implement these features. Ask all the necessary clarifying questions before implementation.

Your context window will be automatically compacted as it approaches its limit, allowing you to continue working indefinitely from where you left off. Therefore, do not stop tasks early due to token budget concerns. As you approach your token budget limit, save your current progress and state to memory before the context window refreshes. Always be as persistent and autonomous as possible and complete tasks fully, even if the end of your budget is approaching. Never stop and report unfinished tasks: complete everything before stopping work.

Every feature must go through the Feature Implementation Workflow (see Conventions below) — driven by the far-feature-spec skill.

Build & Run

npm install
npm run compile        # tsc -p ./ (for debugging with F5)
npm run watch          # tsc -watch -p ./ (development)
npm run bundle         # esbuild production bundle (single minified file)
npm run bundle:watch   # esbuild watch mode
npm run typecheck      # tsc --noEmit (type checking only)
npm run package        # bundle + vsce package (produces .vsix)
npm test               # integration test suite (see Testing below)

Testing

Integration tests live in src/test/ and run the real extension inside a headless VS Code via @vscode/test-electron + Mocha.

  • Each test is a directory src/test/suite/nnn - description/ containing test.ts, an optional filesystem/ fixture (panel1/, panel2/, settings.json — all optional), and screenshots/ reference files.
  • Harness (src/test/suite/harness.ts) drives a live panel: it copies the fixture to a temp sandbox, opens the panel at a pinned 100x30 size, sends keyboard/mouse input, and captures the panel's render as a text grid.
  • expectScreenshot(name) compares the panel against a reference file where ? matches any character (volatile cells — paths, clock, dates, the shell command line — are auto-masked). Regenerate references with UPDATE_SCREENSHOTS=1 npm test.
  • Tests validate both the UI (screenshots) and filesystem side effects.
  • Every new feature should get a nnn - description/ test directory.
  • TEST_ONLY="<substring>" npm test runs only the test directories whose name matches — used to run a feature's own tests before the full suite.

Press F5 in VS Code to launch the Extension Development Host. Run "VSCommander: Open Terminal" from the command palette, then Ctrl+O to toggle the panel.

Architecture

src/
  extension.ts      Orchestrator — Pseudoterminal, command registration, VS Code API. Delegates to components below.
  shell.ts          node-pty shell proxy — spawn, resize, data forwarding, kill
  panel.ts          Panel coordinator — layout, popup routing, pane navigation, render dispatch
  draw.ts           Low-level ANSI escape sequence primitives (cursor, box, color, alt screen)
  timerManager.ts   Generic BlinkTimer + PollTimer — replaces all timer patterns
  copyMoveController.ts  Copy/move orchestration — scan, progress, error dialog, navigate — no vscode
  fileOps.ts        File system operations (mkdir, copy, move, recursive copy) — no vscode
  shellRouter.ts    Shell input tracking, cd suppression, output buffer — no vscode
  quickView.ts      Quick view state machine + QuickViewHost interface — no vscode
  commandLine.ts    Command line row: render, cursor blink, spinner, shell input
  fkeyBar.ts        Function key bar: render, mouse hit-test
  terminalArea.ts   Terminal buffer rendering in hidden-pane area
  cellQuery.ts      Cell-at-coordinate query for popup shadows — pure function
  pane.ts           Single file-list pane: entries, cursor, selection, rendering
  components/       Generic, window-agnostic UI components (see "Component
                    Architecture" below): formView, popup, composedPopup,
                    popupTable, inputControl, checkboxControl, dropdownControl,
                    buttonGroup, buttonGrid, comboBox, checkboxGrid,
                    maskedInput, optionList, textBlock, progressBar,
                    scrollIndicator, colorGrid
  windows/<name>/   One directory per dialog window; index.ts defines the
                    window, specialized component files nest alongside it

Data flow: VS Code terminal ↔ Pseudoterminal (extension.ts) ↔ shell.ts (normal mode) OR panel.ts (panel mode). Toggle switches between alt screen buffer (panel) and main buffer (shell).

Dependency tree:

extension.ts (orchestrator)
  ├── timerManager.ts
  ├── copyMoveController.ts
  │     └── fileOps.ts
  ├── shellRouter.ts
  ├── quickView.ts
  ├── directoryInfo.ts
  ├── shell.ts
  └── panel.ts (coordinator)
        ├── commandLine.ts
        ├── fkeyBar.ts
        ├── terminalArea.ts
        ├── cellQuery.ts
        ├── pane.ts
        └── windows/<name>/ (each built from components/)

No new file imports vscode (except extension.ts and directoryInfo.ts which already do).

Conventions

Cross-Platform (MANDATORY)

This extension must work on Linux, FreeBSD, macOS, and Windows. Every change must respect:

  • Paths: Always use path.join() / path.resolve() / path.dirname() — never concatenate with / or \
  • Home directory: Use os.homedir() — never process.env.HOME (unset on Windows)
  • Shell detection: os.platform() === 'win32' → PowerShell; otherwise $SHELL or /bin/bash
  • Terminal characters: ASCII and standard Unicode box-drawing only (U+2500 block). No emoji — they have inconsistent width across terminal emulators and platforms
  • Line endings: All source files use LF. The terminal handles its own line endings
  • ANSI sequences: Stick to xterm-256color — supported by Windows Terminal, conhost (Win10+), and all Unix terminals

TypeScript

  • Strict mode enabled ("strict": true in tsconfig)
  • Target ES2020, CommonJS modules
  • Output to out/ directory
  • Imports: import * as X from 'X' style (esModuleInterop enabled)

Code Style

  • No comments unless the logic is non-obvious
  • No emoji in code or output strings
  • Functions that produce ANSI output return string — the caller writes to the emitter
  • Panel state is mutated in place, render functions read from it
  • Keep draw.ts as pure ANSI primitives with no business logic
  • Keep panel.ts and all files it depends on with no knowledge of VS Code APIs
  • Keep shell.ts with no knowledge of VS Code APIs
  • Only extension.ts and directoryInfo.ts may import vscode

Settings Persistence (MANDATORY)

All panel settings changes (theme, colors, display options) are IN MEMORY by default. Settings only persist to VS Code configuration when the user explicitly uses F9 > Options > Save settings. Never auto-persist settings changes.

Terminal Rendering

  • All drawing uses absolute cursor positioning (moveTo(row, col))
  • Panel uses alternate screen buffer (\x1b[?1049h / \x1b[?1049l) to preserve shell history
  • Icon/text column widths must be tracked as display width (terminal cells), not JS string length
  • Box borders use Unicode box-drawing: BOX.topLeft (), BOX.horizontal (), etc.

Commands & Keybindings

  • vscommander.open — opens a new VSCommander terminal
  • vscommander.toggle — toggles panel overlay (bound to ctrl+o when terminalFocus)

User Documentation (MANDATORY)

USER.md at the project root is the user-facing guide. docs/ at the project root is the directory containing grouped by functionality articles in depth with examples. This is the source for the built-in help menu. If the documentation is updated here it should be updated in the F1 (help) menu too. Every feature change must update USER.md and create a new document or update an existing one in in docs/ — new keybindings, new commands, new panel behaviors, changed defaults. If you add it to the code, update the documentation.

Readme file (MANDATORY)

README.md at the project root is the community-facing project description. Every major feature must be reflected in USER.md. The file language is concise, highlighting important and competitive features.

Workflow

  • After each iteration of code changes, run npm run compile to check for TypeScript errors before moving on

Component Architecture (MANDATORY)

Every window is assembled only from components — no in-place UI code, no ad-hoc one-off components.

  • Strict hierarchy. A specialized component must be a derivative of a generic component. Example: a UserDropdown specialized component derives from the generic DropdownInput component, specializing it by filling its default options from /etc/passwd. Every component traces, by derivation, down to a generic base component.
  • No in-place or ad-hoc components. Every component is a named, reusable class in its own file — never defined inline inside a window.
  • Generic components live in src/components/. They know nothing about any specific window.
  • Windows live in src/windows/<window-name>/: an index.ts defines the window; its window-specific (specialized) component files nest in the same directory.
  • Before implementing a feature, search src/components/ first — reuse an existing generic component, or derive a specialized one from it.
  • If a new feature needs new components, discuss the component hierarchy with the user before building it.

Feature Implementation Workflow (MANDATORY)

Every feature that replicates or extends Far Manager follows this workflow:

  1. Spec it. Invoke the far-feature-spec skill (.claude/skills/far-feature-spec/). It drives the real Far Manager, captures its behaviour, mines the Far source, and writes specs/<feature>.md with reference captures.
  2. Write the tests first. Implement the spec's test plan (its section 14) as a src/test/suite/nnn - <feature>/ test directory — one reference screenshot per distinct visual state, plus filesystem assertions. See the Testing section.
  3. Implement the feature per the spec and the architecture rules in "Adding New Features" below.
  4. Run the feature tests only: TEST_ONLY="<feature>" npm test — iterate until they pass.
  5. Run the entire suite: npm test — confirm there are no regressions.

Rules:

  • Far is the default source of truth — replicate it exactly unless an intentional improvement has been decided.
  • Intentional improvements: VSCommander sometimes deliberately improves on Far Manager. When it does, the spec MUST describe the improvement and the reason, clearly marked as a deliberate deviation — never a silent divergence.
  • If the existing implementation differs from the spec: do NOT silently pick one. Ask which behaviour to implement — replicate Far, keep the current VSCommander behaviour, or adopt a new improvement — before proceeding.

Adding New Features

  1. Drawing primitives go in draw.ts
  2. Panel visual components go in their own files (commandLine.ts, fkeyBar.ts, terminalArea.ts, cellQuery.ts)
  3. Panel coordination (navigation, popup routing) goes in panel.ts
  4. File system operations go in fileOps.ts
  5. Shell/PTY concerns go in shell.ts or shellRouter.ts
  6. Timer patterns use BlinkTimer/PollTimer from timerManager.ts
  7. VS Code API integration goes in extension.ts
  8. Generic UI components go in src/components/; a dialog window is a src/windows/<name>/index.ts assembled from them — see "Component Architecture". A FormComponent (e.g. comboBox.ts, checkboxGrid.ts, maskedInput.ts) plugs into a FormView via addComponent()
  9. New files must not import vscode — only extension.ts and directoryInfo.ts may
  10. Test on multiple platforms or at minimum verify no platform-specific APIs are used without guards
  11. Update USER.md with any user-visible changes