Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
689a8bd
Add interactive TUI mode with file tree side panel
armanarutiunov Feb 11, 2026
4d11f97
Add per-file addition/deletion line counts
armanarutiunov Feb 11, 2026
c434705
Add dir, border-focused, additions, deletions theme colors
armanarutiunov Feb 11, 2026
0575e48
Rewrite file tree as nested expandable tree with file icons and +N -M…
armanarutiunov Feb 11, 2026
92ae324
Add focus border indicator, ctrl+d/u half-page scroll, update sync fo…
armanarutiunov Feb 11, 2026
53d1685
Match folder color to file name header, darken tree background
armanarutiunov Feb 11, 2026
e5aed4e
Add git staging status detection with green/orange file coloring
armanarutiunov Feb 11, 2026
2cc4e2a
Add staged/partial-staged/file-selected theme colors, fix span ordering
armanarutiunov Feb 11, 2026
4096f5d
Use background-only selection for files, keep inverse for dirs
armanarutiunov Feb 11, 2026
c06368d
Fix dead ternary in FileTreePanel, use execFileSync in gitStatus
armanarutiunov Feb 12, 2026
9a533ca
Deduplicate side-by-side diff parser into single iterSideBySideDiffEv…
armanarutiunov Feb 12, 2026
6eb9fa7
Add Windows fallback for interactive mode, deduplicate TREE_WIDTH/BOR…
armanarutiunov Feb 12, 2026
df4fa57
Fix stale rendering artifacts with ERASE_TO_EOL and screen clear
armanarutiunov Feb 12, 2026
4086e02
Add 'e' key tree toggle and dynamic diff re-render on terminal resize
armanarutiunov Feb 12, 2026
a608033
Fix ttyFd leak, remove dead params, restore comments, export helpers
armanarutiunov Feb 12, 2026
211ffe4
Add unit tests for TUI helpers
armanarutiunov Feb 12, 2026
0be5f85
Make tree width configurable via split-diffs.tree-width
armanarutiunov Feb 12, 2026
6f29eee
Add 'f' key to toggle flat/folder file tree mode
armanarutiunov Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Make tree width configurable via split-diffs.tree-width
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
  • Loading branch information
armanarutiunov and claude committed Feb 12, 2026
commit 0be5f8543d54f78ad1bb7a9dfbf0c6d61c5f7488
2 changes: 2 additions & 0 deletions src/getConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ export type Config = Theme & {
WRAP_LINES: boolean;
HIGHLIGHT_LINE_CHANGES: boolean;
INTERACTIVE: boolean;
TREE_WIDTH: number;
};

export const CONFIG_DEFAULTS: Omit<Config, keyof Theme> = {
MIN_LINE_WIDTH: 80,
WRAP_LINES: true,
HIGHLIGHT_LINE_CHANGES: true,
INTERACTIVE: false,
TREE_WIDTH: 30,
};

export function getConfig(gitConfig: GitConfig): Config {
Expand Down
4 changes: 4 additions & 0 deletions src/getGitConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
DEFAULT_MIN_LINE_WIDTH,
DEFAULT_THEME_DIRECTORY,
DEFAULT_THEME_NAME,
DEFAULT_TREE_WIDTH,
GitConfig,
getGitConfig,
} from './getGitConfig';
Expand All @@ -13,6 +14,7 @@ const DEFAULT_CONFIG: GitConfig = {
THEME_NAME: DEFAULT_THEME_NAME,
THEME_DIRECTORY: DEFAULT_THEME_DIRECTORY,
INTERACTIVE: false,
TREE_WIDTH: DEFAULT_TREE_WIDTH,
};

describe('getGitConfig', () => {
Expand All @@ -29,6 +31,7 @@ split-diffs.min-line-width=40
split-diffs.theme-name=arctic
split-diffs.theme-directory=/tmp
split-diffs.syntax-highlighting-theme=dark-plus
split-diffs.tree-width=40
`)
).toEqual({
WRAP_LINES: false,
Expand All @@ -38,6 +41,7 @@ split-diffs.syntax-highlighting-theme=dark-plus
THEME_DIRECTORY: '/tmp',
SYNTAX_HIGHLIGHTING_THEME: 'dark-plus',
INTERACTIVE: false,
TREE_WIDTH: 40,
});
});

Expand Down
13 changes: 13 additions & 0 deletions src/getGitConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ export type GitConfig = {
THEME_NAME: string;
SYNTAX_HIGHLIGHTING_THEME?: string;
INTERACTIVE: boolean;
TREE_WIDTH: number;
};

export const DEFAULT_MIN_LINE_WIDTH = 80;
export const DEFAULT_TREE_WIDTH = 30;
export const DEFAULT_THEME_DIRECTORY = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'..',
Expand Down Expand Up @@ -50,6 +52,16 @@ export function getGitConfig(configString: string): GitConfig {
// Ignore invalid values
}

let treeWidth = DEFAULT_TREE_WIDTH;
try {
const parsedTreeWidth = parseInt(rawConfig['tree-width'], 10);
if (!isNaN(parsedTreeWidth)) {
treeWidth = parsedTreeWidth;
}
} catch {
// Ignore invalid values
}

return {
MIN_LINE_WIDTH: minLineWidth,
WRAP_LINES: rawConfig['wrap-lines'] !== 'false',
Expand All @@ -59,5 +71,6 @@ export function getGitConfig(configString: string): GitConfig {
THEME_NAME: rawConfig['theme-name'] ?? DEFAULT_THEME_NAME,
SYNTAX_HIGHLIGHTING_THEME: rawConfig['syntax-highlighting-theme'],
INTERACTIVE: rawConfig['interactive'] === 'true',
TREE_WIDTH: treeWidth,
};
}
1 change: 1 addition & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const TEST_CONFIG: Config = {
WRAP_LINES: false,
HIGHLIGHT_LINE_CHANGES: false,
INTERACTIVE: false,
TREE_WIDTH: 30,
...TEST_THEME,
};

Expand Down
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getContextForConfig } from './context';
import { getGitConfig } from './getGitConfig';
import { transformContentsStreaming } from './transformContentsStreaming';
import { getConfig } from './getConfig';
import { TuiApp, TREE_WIDTH, BORDER_WIDTH } from './tui/TuiApp';
import { TuiApp, BORDER_WIDTH } from './tui/TuiApp';
const execAsync = util.promisify(exec);

async function main() {
Expand All @@ -29,14 +29,14 @@ async function main() {

const termCols = terminalSize().columns;
const screenWidth = isInteractive
? termCols - TREE_WIDTH - BORDER_WIDTH
? Math.max(1, termCols - config.TREE_WIDTH - BORDER_WIDTH)
: termCols;

const context = await getContextForConfig(config, chalk, screenWidth);

if (isInteractive) {
const app = new TuiApp();
await app.run(context, process.stdin);
await app.run(context, process.stdin, config.TREE_WIDTH);
} else {
await transformContentsStreaming(
context,
Expand Down
1 change: 1 addition & 0 deletions src/previewTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const CONFIG = {
WRAP_LINES: true,
HIGHLIGHT_LINE_CHANGES: true,
INTERACTIVE: false,
TREE_WIDTH: 30,
};

async function previewTheme(
Expand Down
19 changes: 10 additions & 9 deletions src/tui/TuiApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { syncTreeToDiff, syncDiffToTree } from './sync';
import { getGitStagingStatus } from './gitStatus';
import { RESET } from './ansi';

export const TREE_WIDTH = 30;
export const BORDER_WIDTH = 1;

type FocusPanel = 'tree' | 'diff';
Expand All @@ -26,10 +25,12 @@ export class TuiApp {
private focus: FocusPanel = 'tree';
private treeVisible: boolean = true;
private lastDiffWidth: number = 0;
private treeWidth: number = 30;
private resolve!: () => void;

async run(context: Context, stdin: Readable): Promise<void> {
async run(context: Context, stdin: Readable, treeWidth: number = 30): Promise<void> {
this.context = context;
this.treeWidth = treeWidth;

// Consume all diff data from stdin first
this.data = await collectDiffData(context, stdin);
Expand All @@ -54,12 +55,12 @@ export class TuiApp {
this.screen.enter();

const viewHeight = rows;
const diffWidth = cols - TREE_WIDTH - BORDER_WIDTH;
const diffWidth = Math.max(1, cols - this.treeWidth - BORDER_WIDTH);
this.lastDiffWidth = diffWidth;

this.tree = new FileTreePanel(
this.data.files,
TREE_WIDTH,
this.treeWidth,
viewHeight,
context
);
Expand Down Expand Up @@ -94,9 +95,9 @@ export class TuiApp {
this.screen.cols = cols;

const viewHeight = rows;
const treeWidth = this.treeVisible ? TREE_WIDTH : 0;
const treeWidth = this.treeVisible ? this.treeWidth : 0;
const borderWidth = this.treeVisible ? BORDER_WIDTH : 0;
const diffWidth = cols - treeWidth - borderWidth;
const diffWidth = Math.max(1, cols - treeWidth - borderWidth);

this.tree.resize(treeWidth, viewHeight);
this.diff.resize(diffWidth, viewHeight);
Expand Down Expand Up @@ -364,10 +365,10 @@ export class TuiApp {
const viewHeight = this.screen.rows;

if (this.treeVisible) {
const borderCol = TREE_WIDTH;
const diffCol = TREE_WIDTH + BORDER_WIDTH;
const borderCol = this.treeWidth;
const diffCol = this.treeWidth + BORDER_WIDTH;

this.tree.render(this.screen, 0, 0, this.focus === 'tree');
this.tree.render(this.screen, 0, 0);

const borderColor = this.focus === 'tree'
? this.context.FILE_TREE_BORDER_FOCUSED_COLOR
Expand Down