code-squad-cli 2.0.0 → 2.1.0
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.
- package/dist/index.js +372 -213
- package/dist/tui/App.d.ts +1 -0
- package/dist/tui/App.js +124 -0
- package/package.json +7 -6
- package/dist/flip-ui/dist/assets/index-DYY1gRRa.css +0 -1
- package/dist/flip-ui/dist/assets/index-KAtdqB2p.js +0 -217
- package/dist/flip-ui/dist/index.html +0 -13
package/dist/tui/App.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
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 { GitAdapter } from '../adapters/GitAdapter.js';
|
|
7
|
+
import { loadConfig, getWorktreeCopyPatterns } from '../config.js';
|
|
8
|
+
import { copyFilesWithPatterns } from '../fileUtils.js';
|
|
9
|
+
const git = new GitAdapter();
|
|
10
|
+
function shorten(p) {
|
|
11
|
+
const home = os.homedir();
|
|
12
|
+
return p.startsWith(home) ? '~' + p.slice(home.length) : p;
|
|
13
|
+
}
|
|
14
|
+
function App({ initialWorktrees, root }) {
|
|
15
|
+
const { exit } = useApp();
|
|
16
|
+
const [view, setView] = useState('list');
|
|
17
|
+
const [worktrees, setWorktrees] = useState(initialWorktrees);
|
|
18
|
+
const [cursor, setCursor] = useState(0);
|
|
19
|
+
const [input, setInput] = useState('');
|
|
20
|
+
const [msg, setMsg] = useState(null);
|
|
21
|
+
const [busy, setBusy] = useState(false);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!msg)
|
|
24
|
+
return;
|
|
25
|
+
const t = setTimeout(() => setMsg(null), 2000);
|
|
26
|
+
return () => clearTimeout(t);
|
|
27
|
+
}, [msg]);
|
|
28
|
+
const refresh = useCallback(async () => {
|
|
29
|
+
const wts = await git.listWorktrees(root);
|
|
30
|
+
setWorktrees(wts);
|
|
31
|
+
setCursor(c => Math.min(c, Math.max(0, wts.length - 1)));
|
|
32
|
+
}, [root]);
|
|
33
|
+
useInput((ch, key) => {
|
|
34
|
+
if (busy)
|
|
35
|
+
return;
|
|
36
|
+
if (view === 'list') {
|
|
37
|
+
if (key.upArrow) {
|
|
38
|
+
setCursor(c => Math.max(0, c - 1));
|
|
39
|
+
}
|
|
40
|
+
else if (key.downArrow) {
|
|
41
|
+
setCursor(c => Math.min(worktrees.length - 1, c + 1));
|
|
42
|
+
}
|
|
43
|
+
else if (key.return && worktrees.length > 0) {
|
|
44
|
+
process.stdout.write(worktrees[cursor].path + '\n');
|
|
45
|
+
exit();
|
|
46
|
+
}
|
|
47
|
+
else if (ch === 'n') {
|
|
48
|
+
setView('create');
|
|
49
|
+
setInput('');
|
|
50
|
+
}
|
|
51
|
+
else if (ch === 'd' && worktrees.length > 0) {
|
|
52
|
+
setView('delete');
|
|
53
|
+
}
|
|
54
|
+
else if (ch === 'q' || key.escape) {
|
|
55
|
+
exit();
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (view === 'create') {
|
|
60
|
+
if (key.escape) {
|
|
61
|
+
setView('list');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (key.return && input.trim()) {
|
|
65
|
+
const name = input.trim();
|
|
66
|
+
setBusy(true);
|
|
67
|
+
const base = path.join(path.dirname(root), `${path.basename(root)}.worktree`);
|
|
68
|
+
const wtPath = path.join(base, name);
|
|
69
|
+
git.createWorktree(wtPath, name, root)
|
|
70
|
+
.then(async () => {
|
|
71
|
+
const config = await loadConfig(root);
|
|
72
|
+
const patterns = getWorktreeCopyPatterns(config);
|
|
73
|
+
if (patterns.length > 0) {
|
|
74
|
+
await copyFilesWithPatterns(root, wtPath, patterns);
|
|
75
|
+
}
|
|
76
|
+
process.stdout.write(wtPath + '\n');
|
|
77
|
+
exit();
|
|
78
|
+
})
|
|
79
|
+
.catch((e) => {
|
|
80
|
+
setMsg({ text: e.message, color: 'red' });
|
|
81
|
+
setView('list');
|
|
82
|
+
setBusy(false);
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (key.backspace || key.delete) {
|
|
87
|
+
setInput(v => v.slice(0, -1));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (ch && !key.ctrl && !key.meta) {
|
|
91
|
+
setInput(v => v + ch);
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (view === 'delete') {
|
|
96
|
+
if (ch === 'y' || key.return) {
|
|
97
|
+
const target = worktrees[cursor];
|
|
98
|
+
setBusy(true);
|
|
99
|
+
git.removeWorktree(target.path, root, true)
|
|
100
|
+
.then(() => git.deleteBranch(target.branch, root, true))
|
|
101
|
+
.then(async () => {
|
|
102
|
+
setMsg({ text: `Deleted ${target.branch}`, color: 'green' });
|
|
103
|
+
await refresh();
|
|
104
|
+
setBusy(false);
|
|
105
|
+
setView('list');
|
|
106
|
+
})
|
|
107
|
+
.catch((e) => {
|
|
108
|
+
setMsg({ text: e.message, color: 'red' });
|
|
109
|
+
setBusy(false);
|
|
110
|
+
setView('list');
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else if (ch === 'n' || key.escape) {
|
|
114
|
+
setView('list');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
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" })] })] }))] })] }));
|
|
119
|
+
}
|
|
120
|
+
export async function runTui(workspaceRoot) {
|
|
121
|
+
const worktrees = await git.listWorktrees(workspaceRoot);
|
|
122
|
+
const { waitUntilExit } = render(_jsx(App, { initialWorktrees: worktrees, root: workspaceRoot }), { stdout: process.stderr });
|
|
123
|
+
await waitUntilExit();
|
|
124
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-squad-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
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
|
|
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
|
-
"
|
|
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}
|