code-squad-cli 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,132 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, useEffect, useCallback } from 'react';
3
+ import { render, Box, Text, useInput, useApp } from 'ink';
4
+ import * as path from 'path';
5
+ import * as os from 'os';
6
+ import * as fs from 'fs';
7
+ import * as tty from 'tty';
8
+ import { GitAdapter } from '../adapters/GitAdapter.js';
9
+ import { loadConfig, getWorktreeCopyPatterns } from '../config.js';
10
+ import { copyFilesWithPatterns } from '../fileUtils.js';
11
+ const git = new GitAdapter();
12
+ function shorten(p) {
13
+ const home = os.homedir();
14
+ return p.startsWith(home) ? '~' + p.slice(home.length) : p;
15
+ }
16
+ function App({ initialWorktrees, root }) {
17
+ const { exit } = useApp();
18
+ const [view, setView] = useState('list');
19
+ const [worktrees, setWorktrees] = useState(initialWorktrees);
20
+ const [cursor, setCursor] = useState(0);
21
+ const [input, setInput] = useState('');
22
+ const [msg, setMsg] = useState(null);
23
+ const [busy, setBusy] = useState(false);
24
+ useEffect(() => {
25
+ if (!msg)
26
+ return;
27
+ const t = setTimeout(() => setMsg(null), 2000);
28
+ return () => clearTimeout(t);
29
+ }, [msg]);
30
+ const refresh = useCallback(async () => {
31
+ const wts = await git.listWorktrees(root);
32
+ setWorktrees(wts);
33
+ setCursor(c => Math.min(c, Math.max(0, wts.length - 1)));
34
+ }, [root]);
35
+ useInput((ch, key) => {
36
+ if (busy)
37
+ return;
38
+ if (view === 'list') {
39
+ if (key.upArrow) {
40
+ setCursor(c => Math.max(0, c - 1));
41
+ }
42
+ else if (key.downArrow) {
43
+ setCursor(c => Math.min(worktrees.length - 1, c + 1));
44
+ }
45
+ else if (key.return && worktrees.length > 0) {
46
+ process.stdout.write(worktrees[cursor].path + '\n');
47
+ exit();
48
+ }
49
+ else if (ch === 'n') {
50
+ setView('create');
51
+ setInput('');
52
+ }
53
+ else if (ch === 'd' && worktrees.length > 0) {
54
+ setView('delete');
55
+ }
56
+ else if (ch === 'q' || key.escape) {
57
+ exit();
58
+ }
59
+ return;
60
+ }
61
+ if (view === 'create') {
62
+ if (key.escape) {
63
+ setView('list');
64
+ return;
65
+ }
66
+ if (key.return && input.trim()) {
67
+ const name = input.trim();
68
+ setBusy(true);
69
+ const base = path.join(path.dirname(root), `${path.basename(root)}.worktree`);
70
+ const wtPath = path.join(base, name);
71
+ git.createWorktree(wtPath, name, root)
72
+ .then(async () => {
73
+ const config = await loadConfig(root);
74
+ const patterns = getWorktreeCopyPatterns(config);
75
+ if (patterns.length > 0) {
76
+ await copyFilesWithPatterns(root, wtPath, patterns);
77
+ }
78
+ process.stdout.write(wtPath + '\n');
79
+ exit();
80
+ })
81
+ .catch((e) => {
82
+ setMsg({ text: e.message, color: 'red' });
83
+ setView('list');
84
+ setBusy(false);
85
+ });
86
+ return;
87
+ }
88
+ if (key.backspace || key.delete) {
89
+ setInput(v => v.slice(0, -1));
90
+ return;
91
+ }
92
+ if (ch && !key.ctrl && !key.meta) {
93
+ setInput(v => v + ch);
94
+ }
95
+ return;
96
+ }
97
+ if (view === 'delete') {
98
+ if (ch === 'y' || key.return) {
99
+ const target = worktrees[cursor];
100
+ setBusy(true);
101
+ git.removeWorktree(target.path, root, true)
102
+ .then(() => git.deleteBranch(target.branch, root, true))
103
+ .then(async () => {
104
+ setMsg({ text: `Deleted ${target.branch}`, color: 'green' });
105
+ await refresh();
106
+ setBusy(false);
107
+ setView('list');
108
+ })
109
+ .catch((e) => {
110
+ setMsg({ text: e.message, color: 'red' });
111
+ setBusy(false);
112
+ setView('list');
113
+ });
114
+ }
115
+ else if (ch === 'n' || key.escape) {
116
+ setView('list');
117
+ }
118
+ }
119
+ });
120
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Code Squad" }), busy && _jsx(Text, { color: "yellow", children: " ..." })] }), view === 'list' && (worktrees.length === 0 ? (_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { dimColor: true, children: "No worktrees yet. Press " }), _jsx(Text, { color: "yellow", children: "n" }), _jsx(Text, { dimColor: true, children: " to create one." })] })) : (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: worktrees.map((wt, i) => (_jsxs(Box, { children: [_jsx(Text, { color: i === cursor ? 'cyan' : undefined, children: i === cursor ? '❯ ' : ' ' }), _jsx(Text, { bold: i === cursor, children: wt.branch.padEnd(20) }), _jsxs(Text, { dimColor: true, children: [" ", shorten(wt.path)] })] }, wt.path))) }))), view === 'create' && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, children: "New Worktree" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: '> ' }), _jsx(Text, { color: "cyan", children: input }), _jsx(Text, { dimColor: true, children: '█' })] })] })), view === 'delete' && worktrees[cursor] && (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsxs(Text, { children: ["Delete ", _jsx(Text, { bold: true, color: "yellow", children: worktrees[cursor].branch }), "?"] }) })), msg && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: msg.color, children: msg.text }) })), _jsxs(Box, { gap: 2, children: [view === 'list' && (_jsxs(_Fragment, { children: [worktrees.length > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: '↑↓' }), " ", _jsx(Text, { dimColor: true, children: "navigate" })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: '↵' }), " ", _jsx(Text, { dimColor: true, children: "switch" })] })] })), _jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "n" }), " ", _jsx(Text, { dimColor: true, children: "new" })] }), worktrees.length > 0 && (_jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "d" }), " ", _jsx(Text, { dimColor: true, children: "delete" })] })), _jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "q" }), " ", _jsx(Text, { dimColor: true, children: "quit" })] })] })), view === 'create' && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: '↵' }), " ", _jsx(Text, { dimColor: true, children: "create" })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "esc" }), " ", _jsx(Text, { dimColor: true, children: "cancel" })] })] })), view === 'delete' && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "y" }), " ", _jsx(Text, { dimColor: true, children: "confirm" })] }), _jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "n" }), " ", _jsx(Text, { dimColor: true, children: "cancel" })] })] }))] })] }));
121
+ }
122
+ export async function runTui(workspaceRoot) {
123
+ const worktrees = await git.listWorktrees(workspaceRoot);
124
+ // Render to /dev/tty directly — bypasses stdout/stderr capture
125
+ // so shell function's `2>&1` doesn't swallow TUI output
126
+ const fd = fs.openSync('/dev/tty', 'w');
127
+ const ttyStream = new tty.WriteStream(fd);
128
+ const { waitUntilExit } = render(_jsx(App, { initialWorktrees: worktrees, root: workspaceRoot }), { stdout: ttyStream });
129
+ await waitUntilExit();
130
+ ttyStream.destroy();
131
+ fs.closeSync(fd);
132
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-squad-cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "csq": "./dist/index.js"
@@ -10,12 +10,10 @@
10
10
  "dist"
11
11
  ],
12
12
  "scripts": {
13
- "build": "tsc && npm run bundle && npm run build:flip-ui",
14
- "bundle": "esbuild dist/index.js --bundle --platform=node --format=esm --outfile=dist/index.js --allow-overwrite --external:@inquirer/core --external:@inquirer/prompts --external:chalk --external:chokidar --external:express --external:cors --external:clipboardy --external:open --external:fast-glob",
15
- "build:flip-ui": "cd flip-ui && pnpm build && mkdir -p ../dist/flip-ui && cp -r dist ../dist/flip-ui/",
13
+ "build": "tsc && npm run bundle",
14
+ "bundle": "esbuild dist/index.js --bundle --platform=node --format=esm --outfile=dist/index.js --allow-overwrite --external:@inquirer/core --external:@inquirer/prompts --external:chalk --external:chokidar --external:express --external:cors --external:clipboardy --external:open --external:fast-glob --external:ink --external:react --external:react/jsx-runtime",
16
15
  "build:watch": "tsc --watch",
17
16
  "dev": "tsx src/index.ts",
18
- "dev:flip": "pnpm build:flip-ui && tsx src/index.ts flip",
19
17
  "type-check": "tsc --noEmit",
20
18
  "clean": "rimraf dist",
21
19
  "prepublishOnly": "npm run clean && npm run build"
@@ -29,13 +27,16 @@
29
27
  "cors": "^2.8.5",
30
28
  "express": "^4.18.2",
31
29
  "fast-glob": "^3.3.3",
32
- "open": "^10.0.3"
30
+ "ink": "^6.7.0",
31
+ "open": "^10.0.3",
32
+ "react": "^19.2.4"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@code-squad/core": "workspace:*",
36
36
  "@types/cors": "^2.8.17",
37
37
  "@types/express": "^4.17.21",
38
38
  "@types/node": "20.x",
39
+ "@types/react": "^19.2.14",
39
40
  "esbuild": "^0.27.2",
40
41
  "rimraf": "^6.1.2",
41
42
  "tsx": "^4.21.0",
@@ -1 +0,0 @@
1
- *{box-sizing:border-box;margin:0;padding:0}:root{--bg-primary: #0d1117;--bg-secondary: #161b22;--bg-tertiary: #21262d;--bg-hover: #30363d;--bg-selected: #1f6feb26;--text-primary: #e6edf3;--text-secondary: #8b949e;--text-muted: #6e7681;--border-color: #30363d;--border-subtle: #21262d;--accent-primary: #238636;--accent-primary-hover: #2ea043;--accent-secondary: #1f6feb;--git-modified: #d29922;--git-untracked: #3fb950;--git-deleted: #f85149;--git-added: #3fb950;--line-number-color: #6e7681;--selection-bg: #264f78;--diff-add-bg: rgba(46, 160, 67, .15);--diff-add-text: #7ee787;--diff-add-border: rgba(46, 160, 67, .4);--diff-delete-bg: rgba(248, 81, 73, .15);--diff-delete-text: #ffa198;--diff-delete-border: rgba(248, 81, 73, .4);--diff-hunk-bg: rgba(56, 139, 253, .1);--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;--font-mono: "JetBrains Mono", "Fira Code", "SF Mono", "Menlo", "Monaco", "Consolas", monospace;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 24px;--transition-fast: .15s ease;--transition-normal: .2s ease}body{font-family:var(--font-sans);font-size:14px;background-color:var(--bg-primary);color:var(--text-primary);height:100vh;overflow:hidden}#root{height:100%}.app{display:flex;flex-direction:column;height:100%}.main-content{display:flex;flex:1;overflow:hidden}.toolbar{display:flex;align-items:center;justify-content:space-between;height:44px;padding:0 var(--space-4);background-color:var(--bg-secondary);border-bottom:1px solid var(--border-subtle);box-shadow:0 1px 2px #0000001a}.toolbar-left,.toolbar-center,.toolbar-right{display:flex;align-items:center;gap:var(--space-2)}.toolbar-btn{display:flex;align-items:center;justify-content:center;gap:var(--space-1);height:28px;padding:0 var(--space-2);border:none;background:transparent;border-radius:6px;color:var(--text-secondary);cursor:pointer;transition:all var(--transition-fast)}.toolbar-btn:hover{background-color:var(--bg-hover);color:var(--text-primary)}.toolbar-btn.active{background-color:var(--bg-tertiary);color:var(--text-primary)}.toolbar-btn svg{flex-shrink:0}.diff-toggle{min-width:100px}.diff-mode-label{font-size:12px}.sidebar{width:280px;background-color:var(--bg-secondary);border-right:1px solid var(--border-subtle);overflow:hidden;display:flex;flex-direction:column;transition:width var(--transition-normal),opacity var(--transition-fast)}.sidebar.collapsed{width:0;opacity:0;border-right:none}.content{flex:1;overflow:hidden;display:flex;flex-direction:column;min-width:0}.staging-panel{width:320px;background-color:var(--bg-secondary);border-left:1px solid var(--border-subtle);overflow:hidden;display:flex;flex-direction:column;transition:width var(--transition-normal),opacity var(--transition-fast)}.staging-panel.collapsed{width:0;opacity:0;border-left:none}.footer{display:flex;align-items:center;justify-content:space-between;padding:var(--space-2) var(--space-4);background-color:var(--bg-secondary);border-top:1px solid var(--border-subtle);gap:var(--space-4)}.footer-hint{color:var(--text-muted);font-size:13px}.footer-actions{display:flex;gap:var(--space-2)}.file-tree{display:flex;flex-direction:column;height:100%}.file-tree-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-4) var(--space-4);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--text-secondary);border-bottom:1px solid var(--border-subtle)}.file-tree-header-actions{display:flex;gap:var(--space-1)}.filter-btn{display:flex;align-items:center;gap:var(--space-1);padding:2px 8px;border:none;background:transparent;border-radius:4px;color:var(--text-muted);font-size:10px;font-weight:600;cursor:pointer;transition:all var(--transition-fast)}.filter-btn:hover{background-color:var(--bg-hover);color:var(--text-secondary)}.filter-btn.active{background-color:var(--accent-secondary);color:#fff}.filter-btn .count{background-color:var(--bg-hover);padding:0 4px;border-radius:3px;font-size:9px}.filter-btn.active .count{background-color:#fff3}.file-tree-content{flex:1;overflow-y:auto;padding:var(--space-2) 0}.tree-item{display:flex;align-items:center;padding:var(--space-1) var(--space-2);min-height:28px;cursor:pointer;-webkit-user-select:none;user-select:none;gap:6px;border-radius:4px;margin:0 4px;transition:background-color var(--transition-fast)}.tree-item:hover{background-color:var(--bg-hover)}.tree-item-file.selected{background-color:var(--bg-selected);border-left:2px solid var(--accent-secondary)}.tree-icon{display:flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--text-secondary);flex-shrink:0}.tree-folder-icon{display:flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--git-modified);flex-shrink:0}.tree-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.git-badge{font-size:10px;font-weight:600;padding:1px 4px;border-radius:3px}.git-badge-modified{color:var(--git-modified);background-color:#d2992226}.git-badge-untracked{color:var(--git-untracked);background-color:#3fb95026}.git-badge-deleted{color:var(--git-deleted);background-color:#f8514926}.git-badge-added{color:var(--git-added);background-color:#3fb95026}.tree-item-filtered{opacity:.5;cursor:default}.tree-item-filtered:hover{background-color:inherit}.code-viewer{display:flex;flex-direction:column;height:100%;overflow:hidden}.code-viewer-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-secondary);gap:var(--space-3)}.code-viewer-empty .hint{display:flex;align-items:center;gap:var(--space-1);font-size:12px;color:var(--text-muted)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--space-5);gap:var(--space-3)}.empty-state-icon{color:var(--text-muted);opacity:.5}.code-viewer-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-3) var(--space-5);background-color:var(--bg-secondary);border-bottom:1px solid var(--border-subtle)}.code-viewer-header-right{display:flex;align-items:center;gap:var(--space-3)}.file-path{font-size:13px;font-family:var(--font-mono)}.file-language{font-size:11px;color:var(--text-secondary);text-transform:uppercase}.code-viewer-content{flex:1;overflow:auto;font-family:var(--font-mono);font-size:13px;line-height:1.6;animation:fadeIn .2s ease-out}.code-line{display:flex;min-height:22px;cursor:pointer}.code-line:hover{background-color:var(--bg-hover)}.code-line-selected{background-color:var(--selection-bg)!important}.code-line-added{background-color:var(--diff-add-bg)!important}.code-line-added:hover{background-color:#2ea04340!important}.code-line-deleted{background-color:var(--diff-delete-bg)!important}.code-line-deleted:hover{background-color:#f8514940!important}.line-gutter{position:relative;display:inline-block;width:16px;min-width:16px;background-color:var(--bg-tertiary)}.gutter-bar{position:absolute;width:3px;border-radius:1px}.gutter-bar-single{top:50%;height:6px;transform:translateY(-50%);border-radius:3px}.gutter-bar-start{top:50%;bottom:0}.gutter-bar-middle{top:0;bottom:0}.gutter-bar-end{top:0;bottom:50%}.gutter-bar.color-0{background:#58a6ff}.gutter-bar.color-1{background:#f78166}.gutter-bar.color-2{background:#7ee787}.gutter-bar.color-3{background:#d2a8ff}.gutter-bar.color-4{background:#ffa657}.gutter-bar.color-5{background:#ff7b72}.line-number{display:inline-block;min-width:55px;padding:0 var(--space-3);text-align:right;color:var(--text-muted);opacity:.7;font-size:12px;-webkit-user-select:none;user-select:none;background-color:var(--bg-tertiary);border-right:1px solid var(--border-subtle)}.code-line code{flex:1;padding:0 var(--space-3);white-space:pre;background:transparent}.code-line code.hljs{background:transparent;padding:0 var(--space-3)}.code-content{flex:1;padding:0 var(--space-3);white-space:pre}.staging-list{display:flex;flex-direction:column;height:100%}.staging-list-empty{padding:var(--space-4)}.staging-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-3) var(--space-4);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--text-secondary);border-bottom:1px solid var(--border-subtle)}.staging-hint{font-size:12px;color:var(--text-muted);line-height:1.5}.staging-items{flex:1;overflow-y:auto;padding:var(--space-2)}.staging-item{background-color:var(--bg-tertiary);border:1px solid var(--border-subtle);border-radius:6px;padding:var(--space-3);margin-bottom:var(--space-2);transition:border-color var(--transition-fast)}.staging-item:hover{border-color:var(--border-color)}.staging-item-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-2)}.staging-location{display:inline-flex;align-items:center;font-family:var(--font-mono);font-size:11px;color:var(--accent-secondary);padding:2px 6px;background-color:#1f6feb26;border-radius:4px}.staging-comment{font-size:13px;line-height:1.5;color:var(--text-primary)}.comment-input{display:flex;align-items:center;gap:var(--space-3);flex:1;padding:var(--space-2) var(--space-3);background-color:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:6px}.comment-selection{display:flex;align-items:center;gap:var(--space-2);font-family:var(--font-mono);font-size:12px;color:var(--accent-secondary);white-space:nowrap;padding:2px 8px;background-color:#1f6feb26;border-radius:4px}.comment-field{flex:1;padding:var(--space-2) var(--space-3);background-color:transparent;border:none;color:var(--text-primary);font-size:13px;transition:border-color var(--transition-fast)}.comment-field:focus{outline:none}.comment-field::placeholder{color:var(--text-muted)}.comment-hint{color:var(--text-muted);font-size:13px}.inline-comment-form{margin:8px 16px;background-color:var(--bg-primary);border:1px solid var(--border-color);border-radius:6px;overflow:hidden;box-shadow:0 4px 12px #0000004d}.inline-comment-form-header{padding:8px 12px;background-color:var(--bg-tertiary);border-bottom:1px solid var(--border-color);font-size:11px;font-family:var(--font-mono);color:var(--text-muted)}.inline-comment-textarea{width:100%;min-height:80px;padding:12px;background-color:var(--bg-secondary);color:var(--text-primary);border:none;resize:vertical;font-family:inherit;font-size:13px;box-sizing:border-box}.inline-comment-textarea:focus{outline:none}.inline-comment-textarea::placeholder{color:var(--text-muted)}.inline-comment-form-actions{display:flex;justify-content:flex-end;gap:8px;padding:8px 12px;background-color:var(--bg-tertiary);border-top:1px solid var(--border-color)}.inline-comment-form-actions .btn{width:auto;padding:6px 12px}.inline-comments-container{margin:8px 16px}.inline-staged-comment{background-color:var(--bg-tertiary);border:1px solid var(--border-color);border-left:3px solid var(--accent-secondary);border-radius:4px;margin-bottom:8px;overflow:hidden}.inline-staged-comment:last-child{margin-bottom:0}.inline-staged-comment-header{display:flex;justify-content:space-between;align-items:center;padding:6px 10px;background-color:var(--bg-overlay);border-bottom:1px solid var(--border-subtle)}.inline-staged-comment-location{font-size:11px;font-family:var(--font-mono);color:var(--text-muted);font-weight:600}.inline-staged-comment-remove{background:transparent;border:none;color:var(--text-muted);cursor:pointer;padding:2px 6px;border-radius:3px;font-size:12px;line-height:1}.inline-staged-comment-remove:hover{background-color:var(--bg-tertiary);color:var(--danger)}.inline-staged-comment-body{padding:10px 12px;font-size:12px;line-height:1.5;white-space:pre-wrap;word-wrap:break-word;color:var(--text-primary)}.fuzzy-finder-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background-color:#0009;display:flex;justify-content:center;padding-top:80px;z-index:100}.fuzzy-finder{background-color:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;box-shadow:0 16px 48px #00000080;width:600px;max-height:400px;display:flex;flex-direction:column;overflow:hidden}.fuzzy-finder-input{padding:var(--space-4);background-color:var(--bg-tertiary);border:none;border-bottom:1px solid var(--border-subtle);color:var(--text-primary);font-size:16px}.fuzzy-finder-input:focus{outline:none}.fuzzy-finder-results{flex:1;overflow-y:auto}.fuzzy-finder-item{padding:var(--space-3) var(--space-4);cursor:pointer;font-family:var(--font-mono);font-size:13px}.fuzzy-finder-item:hover,.fuzzy-finder-item-selected{background-color:var(--bg-selected)}.fuzzy-finder-empty{padding:var(--space-4);color:var(--text-muted);text-align:center}.fuzzy-finder-hint{padding:var(--space-2) var(--space-4);font-size:11px;color:var(--text-muted);background-color:var(--bg-tertiary);border-top:1px solid var(--border-subtle)}.btn{padding:var(--space-2) var(--space-4);border:none;border-radius:6px;font-size:13px;font-weight:500;cursor:pointer;transition:background-color var(--transition-fast)}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-primary{background-color:var(--accent-primary);color:#fff}.btn-primary:hover:not(:disabled){background-color:var(--accent-primary-hover)}.btn-secondary{background-color:var(--bg-tertiary);color:var(--text-primary)}.btn-secondary:hover:not(:disabled){background-color:var(--bg-hover)}.btn-small{padding:var(--space-1) var(--space-3)}.btn-link{background:none;border:none;color:var(--accent-secondary);cursor:pointer;font-size:11px}.btn-link:hover{text-decoration:underline}.btn-icon{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:2px 6px}.btn-icon:hover{color:var(--text-primary)}.diff-side-by-side{display:flex;height:100%;overflow:hidden}.diff-pane{flex:1;overflow:auto;font-family:var(--font-mono);font-size:13px;line-height:1.5}.diff-pane-left{border-right:1px solid var(--border-color)}.diff-pane-header{padding:var(--space-2) var(--space-3);background-color:var(--bg-tertiary);border-bottom:1px solid var(--border-subtle);font-size:11px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.5px}.code-viewer-loading{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--bg-hover);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#484f58}::-webkit-scrollbar-corner{background:transparent}.preview-toggle-btn{display:flex;align-items:center;gap:var(--space-1);height:24px;padding:0 var(--space-2);border:1px solid var(--border-color);background:transparent;border-radius:4px;color:var(--text-secondary);font-size:11px;font-weight:500;cursor:pointer;transition:all var(--transition-fast)}.preview-toggle-btn:hover{background-color:var(--bg-hover);color:var(--text-primary)}.preview-toggle-btn.active{background-color:var(--accent-secondary);color:#fff;border:none}.preview-toggle-btn:disabled{opacity:.4;cursor:not-allowed}.markdown-preview-container{overflow:auto;animation:fadeIn .2s ease-out}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.markdown-preview{padding:24px 32px;max-width:900px;margin:0 auto;font-family:var(--font-sans);font-size:15px;line-height:1.7;color:var(--text-primary)}.markdown-preview h1{font-size:2em;font-weight:600;border-bottom:1px solid var(--border-subtle);padding-bottom:8px;margin-top:24px;margin-bottom:16px}.markdown-preview h2{font-size:1.5em;font-weight:600;border-bottom:1px solid var(--border-subtle);padding-bottom:8px;margin-top:24px;margin-bottom:16px}.markdown-preview h3{font-size:1.25em;font-weight:600;margin-top:24px;margin-bottom:16px}.markdown-preview h4,.markdown-preview h5,.markdown-preview h6{font-size:1em;font-weight:600;margin-top:24px;margin-bottom:16px}.markdown-preview p{margin-bottom:16px}.markdown-preview strong{font-weight:600;color:var(--text-primary)}.markdown-preview em{font-style:italic}.markdown-preview a{color:var(--accent-secondary);text-decoration:none}.markdown-preview a:hover{text-decoration:underline}.markdown-code-block{background:var(--bg-primary);border-radius:6px;margin:16px 0;border:1px solid var(--border-color);overflow:hidden}.markdown-code-header{padding:6px 12px;background:var(--bg-tertiary);border-bottom:1px solid var(--border-subtle)}.markdown-code-language{font-size:11px;color:var(--text-muted);text-transform:lowercase}.markdown-code-content{padding:12px 16px;font-family:var(--font-mono);font-size:13px;line-height:1.5;overflow-x:auto;margin:0;background:transparent}.markdown-code-line{min-height:20px}.markdown-preview .inline-code{background:#6e768166;border-radius:4px;padding:2px 6px;font-family:var(--font-mono);font-size:.9em}.markdown-preview blockquote{border-left:4px solid var(--accent-secondary);padding-left:16px;margin:16px 0;color:var(--text-secondary)}.markdown-preview ul,.markdown-preview ol{padding-left:24px;margin-bottom:16px}.markdown-preview li{margin-bottom:4px}.markdown-preview table{width:100%;border-collapse:collapse;margin:16px 0}.markdown-preview th{background:var(--bg-tertiary);padding:8px 12px;border:1px solid var(--border-color);text-align:left;font-weight:600}.markdown-preview td{padding:8px 12px;border:1px solid var(--border-color)}.markdown-preview hr{border:none;border-top:1px solid var(--border-color);margin:24px 0}.markdown-image-placeholder{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-3);background:var(--bg-tertiary);border:1px solid var(--border-subtle);border-radius:6px;margin:16px 0}.markdown-image-icon{font-size:24px}.markdown-image-alt{font-weight:500;color:var(--text-primary)}.markdown-image-path{font-family:var(--font-mono);font-size:12px;color:var(--text-muted)}.preview-block{position:relative;padding:4px 8px;margin:0 -8px;border-radius:4px;cursor:pointer;transition:background-color var(--transition-fast)}.preview-block:hover{background-color:#6e76811a}.preview-block.preview-selected,.preview-block.preview-in-range{background-color:var(--selection-bg)}.preview-block.has-comment{padding-left:24px}.preview-gutter{position:absolute;left:4px;top:4px;bottom:4px;width:12px;display:flex;flex-direction:column;gap:2px}.preview-gutter-bar{width:3px;flex:1;border-radius:2px}.preview-gutter-bar.color-0{background:#58a6ff}.preview-gutter-bar.color-1{background:#f78166}.preview-gutter-bar.color-2{background:#7ee787}.preview-gutter-bar.color-3{background:#d2a8ff}.preview-gutter-bar.color-4{background:#ffa657}.preview-gutter-bar.color-5{background:#ff7b72}.preview-block-content{position:relative}.preview-inline-comments,.preview-comment-form{margin:8px 0}.preview-staged-comment.color-0{border-left-color:#58a6ff}.preview-staged-comment.color-1{border-left-color:#f78166}.preview-staged-comment.color-2{border-left-color:#7ee787}.preview-staged-comment.color-3{border-left-color:#d2a8ff}.preview-staged-comment.color-4{border-left-color:#ffa657}.preview-staged-comment.color-5{border-left-color:#ff7b72}.markdown-preview{-webkit-user-select:none;user-select:none}