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.
@@ -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 Viewer</title>
7
- <script type="module" crossorigin src="./assets/main-gWFaVAuJ.js"></script>
8
- <link rel="stylesheet" crossorigin href="./assets/main-0MulWSMb.css">
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.14",
4
- "description": "A visualization tool for Codex that monitors API requests and file system changes with task snapshot rollback",
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-viewer",
20
+ "codex-lens",
21
21
  "ai-coding",
22
22
  "file-watcher",
23
- "snapshot",
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 Viewer</title>
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 Viewer Starting');
61
+ logger.info(' Codex Lens Starting');
62
62
  logger.info('========================================');
63
63
 
64
64
  const projectRoot = findProjectRoot();
@@ -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
- <LeftPanel
235
- files={files}
236
- activeFile={activeTab?.path || null}
237
- onFileClick={handleFileClick}
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
- <TabBar
246
- tabs={tabs}
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
- <div className="terminal-wrapper">
304
- <TerminalPanel />
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
+ }