codex-lens 0.1.14 → 0.1.16
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/aggregator.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/pty-manager.js +22 -3
- package/dist/public/assets/main-7-y-Utze.css +32 -0
- package/dist/public/assets/main-ssN8akn5.js +88 -0
- package/dist/public/index.html +3 -3
- package/package.json +22 -5
- package/src/aggregator.js +1 -1
- package/src/cli.js +1 -1
- package/src/components/App.jsx +59 -61
- package/src/components/CodeViewer.jsx +185 -0
- package/src/global.css +367 -374
- package/src/index.html +1 -1
- package/src/pty-manager.js +28 -3
- package/dist/public/assets/main-0MulWSMb.css +0 -32
- package/dist/public/assets/main-gWFaVAuJ.js +0 -50
package/dist/public/index.html
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Codex
|
|
7
|
-
<script type="module" crossorigin src="./assets/main-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="./assets/main-
|
|
6
|
+
<title>Codex Lens</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/main-ssN8akn5.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="./assets/main-7-y-Utze.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-lens",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "A visualization tool for Codex that monitors API requests and file system changes
|
|
3
|
+
"version": "0.1.16",
|
|
4
|
+
"description": "A visualization tool for Codex that monitors API requests and file system changes",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/aggregator.js",
|
|
@@ -17,13 +17,30 @@
|
|
|
17
17
|
"keywords": [
|
|
18
18
|
"codex",
|
|
19
19
|
"openai",
|
|
20
|
-
"codex-
|
|
20
|
+
"codex-lens",
|
|
21
21
|
"ai-coding",
|
|
22
22
|
"file-watcher",
|
|
23
|
-
"
|
|
24
|
-
"rollback"
|
|
23
|
+
"terminal"
|
|
25
24
|
],
|
|
26
25
|
"dependencies": {
|
|
26
|
+
"@codemirror/lang-cpp": "^6.0.3",
|
|
27
|
+
"@codemirror/lang-css": "^6.3.1",
|
|
28
|
+
"@codemirror/lang-go": "^6.0.1",
|
|
29
|
+
"@codemirror/lang-html": "^6.4.11",
|
|
30
|
+
"@codemirror/lang-java": "^6.0.2",
|
|
31
|
+
"@codemirror/lang-javascript": "^6.2.5",
|
|
32
|
+
"@codemirror/lang-json": "^6.0.2",
|
|
33
|
+
"@codemirror/lang-markdown": "^6.5.0",
|
|
34
|
+
"@codemirror/lang-python": "^6.2.1",
|
|
35
|
+
"@codemirror/lang-rust": "^6.0.2",
|
|
36
|
+
"@codemirror/lang-sql": "^6.10.0",
|
|
37
|
+
"@codemirror/lang-yaml": "^6.1.2",
|
|
38
|
+
"@codemirror/language": "^6.12.2",
|
|
39
|
+
"@codemirror/state": "^6.6.0",
|
|
40
|
+
"@codemirror/view": "^6.40.0",
|
|
41
|
+
"@lezer/highlight": "^1.2.3",
|
|
42
|
+
"@replit/codemirror-minimap": "^0.5.2",
|
|
43
|
+
"@uiw/react-codemirror": "^4.25.8",
|
|
27
44
|
"@xterm/addon-fit": "^0.10.0",
|
|
28
45
|
"@xterm/addon-web-links": "^0.11.0",
|
|
29
46
|
"@xterm/xterm": "^5.5.0",
|
package/src/aggregator.js
CHANGED
|
@@ -265,7 +265,7 @@ class Aggregator {
|
|
|
265
265
|
<head>
|
|
266
266
|
<meta charset="UTF-8">
|
|
267
267
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
268
|
-
<title>Codex
|
|
268
|
+
<title>Codex Lens</title>
|
|
269
269
|
<link rel="stylesheet" href="/lib/xterm/xterm.css">
|
|
270
270
|
<style>
|
|
271
271
|
:root {
|
package/src/cli.js
CHANGED
|
@@ -58,7 +58,7 @@ function findProjectRoot() {
|
|
|
58
58
|
|
|
59
59
|
async function main() {
|
|
60
60
|
logger.info('========================================');
|
|
61
|
-
logger.info(' Codex
|
|
61
|
+
logger.info(' Codex Lens Starting');
|
|
62
62
|
logger.info('========================================');
|
|
63
63
|
|
|
64
64
|
const projectRoot = findProjectRoot();
|
package/src/components/App.jsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef } from 'react';
|
|
2
2
|
import { TerminalPanel } from './TerminalPanel';
|
|
3
|
+
import { CodeViewer } from './CodeViewer';
|
|
3
4
|
|
|
4
5
|
export function App() {
|
|
5
6
|
const [files, setFiles] = useState([]);
|
|
@@ -231,65 +232,17 @@ export function App() {
|
|
|
231
232
|
|
|
232
233
|
return (
|
|
233
234
|
<div className="app-container">
|
|
234
|
-
<
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
<div className="panel middle-panel">
|
|
240
|
-
<div className="task-bar">
|
|
235
|
+
<div className="top-bar">
|
|
236
|
+
<div className="top-bar-left">
|
|
237
|
+
<span className="top-bar-title">文件浏览器</span>
|
|
238
|
+
</div>
|
|
239
|
+
<div className="top-bar-center">
|
|
241
240
|
<button className="task-btn task-btn-clear" onClick={clearAllDiff} title="清空所有 diff 显示">
|
|
242
241
|
清空 diff
|
|
243
242
|
</button>
|
|
244
243
|
</div>
|
|
245
|
-
<
|
|
246
|
-
|
|
247
|
-
activeTabId={activeTabId}
|
|
248
|
-
onTabClick={setActiveTabId}
|
|
249
|
-
onTabClose={closeTab}
|
|
250
|
-
onContextMenu={handleContextMenu}
|
|
251
|
-
/>
|
|
252
|
-
<div className="panel-content code-panel">
|
|
253
|
-
{!activeTab ? (
|
|
254
|
-
<div className="empty-state">双击左侧文件查看内容...</div>
|
|
255
|
-
) : activeTab.isDiff && activeTab.diff ? (
|
|
256
|
-
<div className="diff-container">
|
|
257
|
-
{activeTab.diff.map((line, i) => (
|
|
258
|
-
<div
|
|
259
|
-
key={i}
|
|
260
|
-
className={`diff-line ${line.added ? 'added' : line.removed ? 'removed' : ''}`}
|
|
261
|
-
>
|
|
262
|
-
{line.content}
|
|
263
|
-
</div>
|
|
264
|
-
))}
|
|
265
|
-
</div>
|
|
266
|
-
) : (
|
|
267
|
-
<pre className="code-content">{activeTab.content}</pre>
|
|
268
|
-
)}
|
|
269
|
-
</div>
|
|
270
|
-
</div>
|
|
271
|
-
{contextMenu && (
|
|
272
|
-
<ContextMenu
|
|
273
|
-
x={contextMenu.x}
|
|
274
|
-
y={contextMenu.y}
|
|
275
|
-
onClose={() => setContextMenu(null)}
|
|
276
|
-
onCloseTab={() => {
|
|
277
|
-
closeTab(contextMenu.tabId);
|
|
278
|
-
setContextMenu(null);
|
|
279
|
-
}}
|
|
280
|
-
onCloseOtherTabs={() => {
|
|
281
|
-
closeOtherTabs(contextMenu.tabId);
|
|
282
|
-
setContextMenu(null);
|
|
283
|
-
}}
|
|
284
|
-
onCloseAllTabs={() => {
|
|
285
|
-
closeAllTabs();
|
|
286
|
-
setContextMenu(null);
|
|
287
|
-
}}
|
|
288
|
-
/>
|
|
289
|
-
)}
|
|
290
|
-
<div className="panel right-panel">
|
|
291
|
-
<div className="panel-header">
|
|
292
|
-
<span>Codex 终端</span>
|
|
244
|
+
<div className="top-bar-right">
|
|
245
|
+
<span className="top-bar-title">Codex 终端</span>
|
|
293
246
|
<span className={`ws-status ${wsStatus}`}></span>
|
|
294
247
|
<span className="version-info">
|
|
295
248
|
{version && <span className="version-number">v{version}</span>}
|
|
@@ -300,8 +253,57 @@ export function App() {
|
|
|
300
253
|
)}
|
|
301
254
|
</span>
|
|
302
255
|
</div>
|
|
303
|
-
|
|
304
|
-
|
|
256
|
+
</div>
|
|
257
|
+
<div className="main-content">
|
|
258
|
+
<LeftPanel
|
|
259
|
+
files={files}
|
|
260
|
+
activeFile={activeTab?.path || null}
|
|
261
|
+
onFileClick={handleFileClick}
|
|
262
|
+
/>
|
|
263
|
+
<div className="panel middle-panel">
|
|
264
|
+
<TabBar
|
|
265
|
+
tabs={tabs}
|
|
266
|
+
activeTabId={activeTabId}
|
|
267
|
+
onTabClick={setActiveTabId}
|
|
268
|
+
onTabClose={closeTab}
|
|
269
|
+
onContextMenu={handleContextMenu}
|
|
270
|
+
/>
|
|
271
|
+
<div className="panel-content code-panel">
|
|
272
|
+
{!activeTab ? (
|
|
273
|
+
<div className="empty-state">双击左侧文件查看内容...</div>
|
|
274
|
+
) : (
|
|
275
|
+
<CodeViewer
|
|
276
|
+
content={activeTab.content}
|
|
277
|
+
diff={activeTab.diff}
|
|
278
|
+
isDiff={activeTab.isDiff}
|
|
279
|
+
filePath={activeTab.path}
|
|
280
|
+
/>
|
|
281
|
+
)}
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
{contextMenu && (
|
|
285
|
+
<ContextMenu
|
|
286
|
+
x={contextMenu.x}
|
|
287
|
+
y={contextMenu.y}
|
|
288
|
+
onClose={() => setContextMenu(null)}
|
|
289
|
+
onCloseTab={() => {
|
|
290
|
+
closeTab(contextMenu.tabId);
|
|
291
|
+
setContextMenu(null);
|
|
292
|
+
}}
|
|
293
|
+
onCloseOtherTabs={() => {
|
|
294
|
+
closeOtherTabs(contextMenu.tabId);
|
|
295
|
+
setContextMenu(null);
|
|
296
|
+
}}
|
|
297
|
+
onCloseAllTabs={() => {
|
|
298
|
+
closeAllTabs();
|
|
299
|
+
setContextMenu(null);
|
|
300
|
+
}}
|
|
301
|
+
/>
|
|
302
|
+
)}
|
|
303
|
+
<div className="panel right-panel">
|
|
304
|
+
<div className="terminal-wrapper">
|
|
305
|
+
<TerminalPanel />
|
|
306
|
+
</div>
|
|
305
307
|
</div>
|
|
306
308
|
</div>
|
|
307
309
|
</div>
|
|
@@ -417,12 +419,8 @@ function LeftPanel({ files, activeFile, onFileClick }) {
|
|
|
417
419
|
|
|
418
420
|
return (
|
|
419
421
|
<div className="panel left-panel" onClick={() => setContextMenu(null)}>
|
|
420
|
-
<div className="panel-header">
|
|
421
|
-
文件浏览器
|
|
422
|
-
</div>
|
|
423
422
|
<div className="panel-content">
|
|
424
423
|
<div className="section">
|
|
425
|
-
<div className="section-title">项目文件</div>
|
|
426
424
|
{files.length === 0 ? (
|
|
427
425
|
<div className="empty-state">等待文件变化...</div>
|
|
428
426
|
) : (
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import React, { useMemo, useRef } from 'react';
|
|
2
|
+
import CodeMirror from '@uiw/react-codemirror';
|
|
3
|
+
import { EditorView } from '@codemirror/view';
|
|
4
|
+
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
5
|
+
import { tags as t } from '@lezer/highlight';
|
|
6
|
+
import { javascript } from '@codemirror/lang-javascript';
|
|
7
|
+
import { python } from '@codemirror/lang-python';
|
|
8
|
+
import { java } from '@codemirror/lang-java';
|
|
9
|
+
import { cpp } from '@codemirror/lang-cpp';
|
|
10
|
+
import { rust } from '@codemirror/lang-rust';
|
|
11
|
+
import { go } from '@codemirror/lang-go';
|
|
12
|
+
import { json } from '@codemirror/lang-json';
|
|
13
|
+
import { yaml } from '@codemirror/lang-yaml';
|
|
14
|
+
import { markdown } from '@codemirror/lang-markdown';
|
|
15
|
+
import { css } from '@codemirror/lang-css';
|
|
16
|
+
import { sql } from '@codemirror/lang-sql';
|
|
17
|
+
import { html } from '@codemirror/lang-html';
|
|
18
|
+
import { showMinimap } from '@replit/codemirror-minimap';
|
|
19
|
+
|
|
20
|
+
const LANG_MAP = {
|
|
21
|
+
js: javascript,
|
|
22
|
+
jsx: javascript,
|
|
23
|
+
ts: javascript,
|
|
24
|
+
tsx: javascript,
|
|
25
|
+
mjs: javascript,
|
|
26
|
+
cjs: javascript,
|
|
27
|
+
py: python,
|
|
28
|
+
pyw: python,
|
|
29
|
+
java: java,
|
|
30
|
+
c: cpp,
|
|
31
|
+
cpp: cpp,
|
|
32
|
+
cc: cpp,
|
|
33
|
+
cxx: cpp,
|
|
34
|
+
h: cpp,
|
|
35
|
+
hpp: cpp,
|
|
36
|
+
go: go,
|
|
37
|
+
rs: rust,
|
|
38
|
+
json: json,
|
|
39
|
+
yml: yaml,
|
|
40
|
+
yaml: yaml,
|
|
41
|
+
md: markdown,
|
|
42
|
+
markdown: markdown,
|
|
43
|
+
css: css,
|
|
44
|
+
scss: css,
|
|
45
|
+
sass: css,
|
|
46
|
+
less: css,
|
|
47
|
+
sql: sql,
|
|
48
|
+
html: html,
|
|
49
|
+
htm: html,
|
|
50
|
+
svg: html,
|
|
51
|
+
xml: html,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function getLanguageExtension(filePath) {
|
|
55
|
+
const ext = filePath?.split('.').pop()?.toLowerCase();
|
|
56
|
+
return LANG_MAP[ext] ? [LANG_MAP[ext]()] : [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const darkTheme = EditorView.theme({
|
|
60
|
+
'&': {
|
|
61
|
+
backgroundColor: '#000000',
|
|
62
|
+
color: '#e0e0e0',
|
|
63
|
+
height: '100%',
|
|
64
|
+
},
|
|
65
|
+
'.cm-gutters': {
|
|
66
|
+
backgroundColor: '#0a0a0a',
|
|
67
|
+
color: '#6b7280',
|
|
68
|
+
border: 'none',
|
|
69
|
+
borderRight: '1px solid #1f1f1f',
|
|
70
|
+
},
|
|
71
|
+
'.cm-activeLineGutter': {
|
|
72
|
+
backgroundColor: '#1a1a1a',
|
|
73
|
+
},
|
|
74
|
+
'.cm-activeLine': {
|
|
75
|
+
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
76
|
+
},
|
|
77
|
+
'.cm-cursor': {
|
|
78
|
+
borderLeftColor: '#22c55e',
|
|
79
|
+
},
|
|
80
|
+
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground': {
|
|
81
|
+
backgroundColor: 'rgba(34, 197, 94, 0.2)',
|
|
82
|
+
},
|
|
83
|
+
'.cm-scroller': {
|
|
84
|
+
fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', Consolas, monospace",
|
|
85
|
+
fontSize: '13px',
|
|
86
|
+
lineHeight: '1.6',
|
|
87
|
+
overflow: 'auto',
|
|
88
|
+
},
|
|
89
|
+
'.cm-content': {
|
|
90
|
+
minHeight: '100%',
|
|
91
|
+
},
|
|
92
|
+
'.cm-minimap-gutter': {
|
|
93
|
+
background: '#0a0a0a',
|
|
94
|
+
borderLeft: '1px solid #1f1f1f',
|
|
95
|
+
},
|
|
96
|
+
'.cm-minimap-overlay-container': {
|
|
97
|
+
cursor: 'pointer',
|
|
98
|
+
},
|
|
99
|
+
'.cm-minimap-overlay': {
|
|
100
|
+
border: '1px solid rgba(34, 197, 94, 0.6)',
|
|
101
|
+
background: 'rgba(34, 197, 94, 0.2)',
|
|
102
|
+
borderRadius: '2px',
|
|
103
|
+
cursor: 'grab',
|
|
104
|
+
transition: 'background 0.15s ease',
|
|
105
|
+
},
|
|
106
|
+
'.cm-minimap-overlay:hover': {
|
|
107
|
+
border: '1px solid rgba(34, 197, 94, 0.9)',
|
|
108
|
+
background: 'rgba(34, 197, 94, 0.35)',
|
|
109
|
+
},
|
|
110
|
+
'.cm-minimap-overlay-container.cm-minimap-overlay-active .cm-minimap-overlay': {
|
|
111
|
+
border: '1px solid rgba(34, 197, 94, 1)',
|
|
112
|
+
background: 'rgba(34, 197, 94, 0.4)',
|
|
113
|
+
cursor: 'grabbing',
|
|
114
|
+
},
|
|
115
|
+
}, { dark: true });
|
|
116
|
+
|
|
117
|
+
const darkHighlightStyle = HighlightStyle.define([
|
|
118
|
+
{ tag: t.keyword, color: '#ff7b72' },
|
|
119
|
+
{ tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: '#ffa657' },
|
|
120
|
+
{ tag: [t.function(t.variableName), t.labelName], color: '#d2a8ff' },
|
|
121
|
+
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: '#79c0ff' },
|
|
122
|
+
{ tag: [t.definition(t.name), t.separator], color: '#e0e0e0' },
|
|
123
|
+
{ tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: '#ffa657' },
|
|
124
|
+
{ tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)], color: '#79c0ff' },
|
|
125
|
+
{ tag: [t.meta, t.comment], color: '#6b7280', fontStyle: 'italic' },
|
|
126
|
+
{ tag: t.strong, fontWeight: 'bold' },
|
|
127
|
+
{ tag: t.emphasis, fontStyle: 'italic' },
|
|
128
|
+
{ tag: t.link, color: '#79c0ff', textDecoration: 'underline' },
|
|
129
|
+
{ tag: t.heading, fontWeight: 'bold', color: '#ffa657' },
|
|
130
|
+
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: '#79c0ff' },
|
|
131
|
+
{ tag: [t.processingInstruction, t.string, t.inserted], color: '#a5d6ff' },
|
|
132
|
+
{ tag: t.invalid, color: '#f85149' },
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
const syntaxTheme = syntaxHighlighting(darkHighlightStyle);
|
|
136
|
+
|
|
137
|
+
export function CodeViewer({ content, diff, isDiff, filePath }) {
|
|
138
|
+
const editorRef = useRef(null);
|
|
139
|
+
|
|
140
|
+
const extensions = useMemo(() => {
|
|
141
|
+
const exts = [
|
|
142
|
+
...getLanguageExtension(filePath),
|
|
143
|
+
syntaxTheme,
|
|
144
|
+
EditorView.lineWrapping,
|
|
145
|
+
showMinimap.compute(['doc'], (state) => ({
|
|
146
|
+
create: (view) => {
|
|
147
|
+
const dom = document.createElement('div');
|
|
148
|
+
return { dom };
|
|
149
|
+
},
|
|
150
|
+
displayText: 'characters',
|
|
151
|
+
showOverlay: 'always',
|
|
152
|
+
})),
|
|
153
|
+
];
|
|
154
|
+
return exts;
|
|
155
|
+
}, [filePath]);
|
|
156
|
+
|
|
157
|
+
const code = useMemo(() => {
|
|
158
|
+
if (isDiff && diff) {
|
|
159
|
+
return diff.map(line => line.content).join('\n');
|
|
160
|
+
}
|
|
161
|
+
return content || '';
|
|
162
|
+
}, [content, diff, isDiff]);
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<div className="code-viewer-codemirror">
|
|
166
|
+
<CodeMirror
|
|
167
|
+
value={code}
|
|
168
|
+
height="100%"
|
|
169
|
+
theme={darkTheme}
|
|
170
|
+
extensions={extensions}
|
|
171
|
+
editable={false}
|
|
172
|
+
basicSetup={{
|
|
173
|
+
lineNumbers: true,
|
|
174
|
+
foldGutter: false,
|
|
175
|
+
highlightActiveLine: true,
|
|
176
|
+
highlightSelectionMatches: false,
|
|
177
|
+
bracketMatching: true,
|
|
178
|
+
}}
|
|
179
|
+
onCreateEditor={(view) => {
|
|
180
|
+
editorRef.current = view;
|
|
181
|
+
}}
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
}
|