codex-lens 0.1.16 → 0.1.18
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/README.md +113 -2
- package/dist/aggregator.js +2 -1
- package/dist/public/assets/{main-ssN8akn5.js → main-Bno1eqbI.js} +30 -30
- package/dist/public/index.html +1 -1
- package/docs/README_zh.md +111 -0
- package/package.json +1 -1
- package/src/aggregator.js +2 -1
- package/src/components/App.jsx +6 -1
- package/src/components/CodeViewer.jsx +74 -2
- package/update.bat +0 -23
package/dist/public/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Codex Lens</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/main-
|
|
7
|
+
<script type="module" crossorigin src="./assets/main-Bno1eqbI.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="./assets/main-7-y-Utze.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Codex-Lens
|
|
2
|
+
|
|
3
|
+
一个可视化的 Codex 任务管理与代码审查工具。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
### 文件浏览器
|
|
8
|
+
- 实时监控项目文件变化,自动更新文件树
|
|
9
|
+
- 支持文件夹展开/折叠
|
|
10
|
+
- 右键菜单支持复制文件路径、在文件资源管理器中打开
|
|
11
|
+
|
|
12
|
+
### 代码查看器
|
|
13
|
+
- 支持 20+ 种编程语言的语法高亮(JavaScript、Python、Java、C/C++、Go、Rust 等)
|
|
14
|
+
- 行号显示
|
|
15
|
+
- 代码缩略图(Minimap),支持拖动滑块滚动、点击跳转
|
|
16
|
+
- 文件变更 Diff 显示,清晰展示新增/删除/修改的内容
|
|
17
|
+
|
|
18
|
+
### 终端集成
|
|
19
|
+
- 内置终端,直接与 Codex 交互
|
|
20
|
+
- 实时显示 Codex 输出
|
|
21
|
+
- 支持终端输入
|
|
22
|
+
|
|
23
|
+
### 标签页管理
|
|
24
|
+
- 多文件标签页浏览
|
|
25
|
+
- 支持快捷键:`Ctrl+W` 关闭标签、`Ctrl+Tab` 切换标签
|
|
26
|
+
- 右键菜单:关闭、关闭其他、关闭所有
|
|
27
|
+
|
|
28
|
+
### 其他特性
|
|
29
|
+
- WebSocket 实时通信,自动重连
|
|
30
|
+
- 版本检测与更新提示
|
|
31
|
+
- 现代化深色主题 UI
|
|
32
|
+
|
|
33
|
+
## 前置要求
|
|
34
|
+
|
|
35
|
+
在使用 Codex-Lens 之前,需要先安装 Codex:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -g @openai/codex
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 安装
|
|
42
|
+
|
|
43
|
+
### 通过 npm 全局安装
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install -g codex-lens
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 使用
|
|
50
|
+
|
|
51
|
+
在项目目录下运行:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
codexlens
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
工具会自动:
|
|
58
|
+
1. 检测当前项目根目录
|
|
59
|
+
2. 启动 Codex 进程
|
|
60
|
+
3. 打开浏览器界面(http://localhost:5174)
|
|
61
|
+
|
|
62
|
+
## 更新
|
|
63
|
+
|
|
64
|
+
### 检查更新
|
|
65
|
+
|
|
66
|
+
工具启动时会自动检查 npm 上的最新版本,如有更新会在界面右上角显示提示。
|
|
67
|
+
|
|
68
|
+
### 手动更新
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm update -g codex-lens
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 技术栈
|
|
75
|
+
|
|
76
|
+
- **前端**: React 18、Vite、CodeMirror 6
|
|
77
|
+
- **后端**: Node.js、Express、WebSocket
|
|
78
|
+
- **终端**: xterm.js、node-pty
|
|
79
|
+
- **文件监控**: chokidar
|
|
80
|
+
- **Diff 生成**: diff
|
|
81
|
+
|
|
82
|
+
## 开发
|
|
83
|
+
|
|
84
|
+
### 克隆项目
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git clone https://github.com/your-username/codex-lens.git
|
|
88
|
+
cd codex-lens
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 安装依赖
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm install
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 开发模式
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# 构建项目
|
|
101
|
+
npm run build
|
|
102
|
+
|
|
103
|
+
# 启动服务
|
|
104
|
+
npm start
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
开发流程:修改代码后,需要重新运行 `npm run build` 构建项目,然后运行 `npm start` 启动服务查看效果。
|
|
108
|
+
|
|
109
|
+
## 许可证
|
|
110
|
+
|
|
111
|
+
[MIT](LICENSE)
|
package/package.json
CHANGED
package/src/aggregator.js
CHANGED
|
@@ -76,7 +76,8 @@ class Aggregator {
|
|
|
76
76
|
codexRunning: !!this.ptyProcess,
|
|
77
77
|
version: current,
|
|
78
78
|
latestVersion: latestVersion,
|
|
79
|
-
hasUpdate: latestVersion && latestVersion !== current
|
|
79
|
+
hasUpdate: latestVersion && latestVersion !== current,
|
|
80
|
+
projectRoot: this.projectRoot
|
|
80
81
|
});
|
|
81
82
|
} else {
|
|
82
83
|
next();
|
package/src/components/App.jsx
CHANGED
|
@@ -11,6 +11,7 @@ export function App() {
|
|
|
11
11
|
const [version, setVersion] = useState(null);
|
|
12
12
|
const [latestVersion, setLatestVersion] = useState(null);
|
|
13
13
|
const [hasUpdate, setHasUpdate] = useState(false);
|
|
14
|
+
const [projectName, setProjectName] = useState('');
|
|
14
15
|
const wsRef = useRef(null);
|
|
15
16
|
|
|
16
17
|
useEffect(() => {
|
|
@@ -38,6 +39,10 @@ export function App() {
|
|
|
38
39
|
setVersion(data.version);
|
|
39
40
|
setLatestVersion(data.latestVersion);
|
|
40
41
|
setHasUpdate(data.hasUpdate);
|
|
42
|
+
if (data.projectRoot) {
|
|
43
|
+
const parts = data.projectRoot.split(/[/\\]/);
|
|
44
|
+
setProjectName(parts[parts.length - 1] || data.projectRoot);
|
|
45
|
+
}
|
|
41
46
|
}
|
|
42
47
|
} catch (error) {
|
|
43
48
|
console.error('Failed to fetch status:', error);
|
|
@@ -234,7 +239,7 @@ export function App() {
|
|
|
234
239
|
<div className="app-container">
|
|
235
240
|
<div className="top-bar">
|
|
236
241
|
<div className="top-bar-left">
|
|
237
|
-
<span className="top-bar-title"
|
|
242
|
+
<span className="top-bar-title">当前工作区: {projectName}</span>
|
|
238
243
|
</div>
|
|
239
244
|
<div className="top-bar-center">
|
|
240
245
|
<button className="task-btn task-btn-clear" onClick={clearAllDiff} title="清空所有 diff 显示">
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React, { useMemo, useRef } from 'react';
|
|
2
2
|
import CodeMirror from '@uiw/react-codemirror';
|
|
3
|
-
import { EditorView } from '@codemirror/view';
|
|
3
|
+
import { EditorView, Decoration, ViewPlugin, ViewUpdate } from '@codemirror/view';
|
|
4
4
|
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
5
5
|
import { tags as t } from '@lezer/highlight';
|
|
6
|
+
import { RangeSetBuilder } from '@codemirror/state';
|
|
6
7
|
import { javascript } from '@codemirror/lang-javascript';
|
|
7
8
|
import { python } from '@codemirror/lang-python';
|
|
8
9
|
import { java } from '@codemirror/lang-java';
|
|
@@ -56,6 +57,58 @@ function getLanguageExtension(filePath) {
|
|
|
56
57
|
return LANG_MAP[ext] ? [LANG_MAP[ext]()] : [];
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
const addedLineDecoration = Decoration.line({
|
|
61
|
+
class: 'cm-diff-added-line',
|
|
62
|
+
attributes: { 'data-diff-type': 'added' }
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const removedLineDecoration = Decoration.line({
|
|
66
|
+
class: 'cm-diff-removed-line',
|
|
67
|
+
attributes: { 'data-diff-type': 'removed' }
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
function createDiffHighlightPlugin(diffData) {
|
|
71
|
+
return ViewPlugin.fromClass(class {
|
|
72
|
+
decorations;
|
|
73
|
+
|
|
74
|
+
constructor(view) {
|
|
75
|
+
this.decorations = this.buildDecorations(view, diffData);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
update(update) {
|
|
79
|
+
if (update.docChanged || update.viewportChanged) {
|
|
80
|
+
this.decorations = this.buildDecorations(update.view, diffData);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
buildDecorations(view, diff) {
|
|
85
|
+
if (!diff || diff.length === 0) {
|
|
86
|
+
return Decoration.none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const builder = new RangeSetBuilder();
|
|
90
|
+
const doc = view.state.doc;
|
|
91
|
+
|
|
92
|
+
for (let i = 1; i <= doc.lines; i++) {
|
|
93
|
+
const line = doc.line(i);
|
|
94
|
+
const diffLine = diff[i - 1];
|
|
95
|
+
|
|
96
|
+
if (diffLine) {
|
|
97
|
+
if (diffLine.added) {
|
|
98
|
+
builder.add(line.from, line.from, addedLineDecoration);
|
|
99
|
+
} else if (diffLine.removed) {
|
|
100
|
+
builder.add(line.from, line.from, removedLineDecoration);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return builder.finish();
|
|
106
|
+
}
|
|
107
|
+
}, {
|
|
108
|
+
decorations: v => v.decorations
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
59
112
|
const darkTheme = EditorView.theme({
|
|
60
113
|
'&': {
|
|
61
114
|
backgroundColor: '#000000',
|
|
@@ -112,6 +165,20 @@ const darkTheme = EditorView.theme({
|
|
|
112
165
|
background: 'rgba(34, 197, 94, 0.4)',
|
|
113
166
|
cursor: 'grabbing',
|
|
114
167
|
},
|
|
168
|
+
'.cm-diff-added-line': {
|
|
169
|
+
backgroundColor: 'rgba(34, 197, 94, 0.15)',
|
|
170
|
+
borderLeft: '3px solid #22c55e',
|
|
171
|
+
},
|
|
172
|
+
'.cm-diff-added-line .cm-gutterElement': {
|
|
173
|
+
color: '#4ade80',
|
|
174
|
+
},
|
|
175
|
+
'.cm-diff-removed-line': {
|
|
176
|
+
backgroundColor: 'rgba(239, 68, 68, 0.15)',
|
|
177
|
+
borderLeft: '3px solid #ef4444',
|
|
178
|
+
},
|
|
179
|
+
'.cm-diff-removed-line .cm-gutterElement': {
|
|
180
|
+
color: '#f87171',
|
|
181
|
+
},
|
|
115
182
|
}, { dark: true });
|
|
116
183
|
|
|
117
184
|
const darkHighlightStyle = HighlightStyle.define([
|
|
@@ -151,8 +218,13 @@ export function CodeViewer({ content, diff, isDiff, filePath }) {
|
|
|
151
218
|
showOverlay: 'always',
|
|
152
219
|
})),
|
|
153
220
|
];
|
|
221
|
+
|
|
222
|
+
if (isDiff && diff && diff.length > 0) {
|
|
223
|
+
exts.push(createDiffHighlightPlugin(diff));
|
|
224
|
+
}
|
|
225
|
+
|
|
154
226
|
return exts;
|
|
155
|
-
}, [filePath]);
|
|
227
|
+
}, [filePath, isDiff, diff]);
|
|
156
228
|
|
|
157
229
|
const code = useMemo(() => {
|
|
158
230
|
if (isDiff && diff) {
|
package/update.bat
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
cd /d "%~dp0"
|
|
3
|
-
|
|
4
|
-
echo Publishing to npm...
|
|
5
|
-
call npm publish
|
|
6
|
-
if %errorlevel% neq 0 (
|
|
7
|
-
echo Failed to publish to npm
|
|
8
|
-
pause
|
|
9
|
-
exit /b 1
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
echo.
|
|
13
|
-
echo Installing codex-lens globally...
|
|
14
|
-
call npm install -g codex-lens
|
|
15
|
-
if %errorlevel% neq 0 (
|
|
16
|
-
echo Failed to install codex-lens globally
|
|
17
|
-
pause
|
|
18
|
-
exit /b 1
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
echo.
|
|
22
|
-
echo Done! codex-lens has been published and installed globally.
|
|
23
|
-
pause
|