codex-lens 0.1.23 → 0.1.25

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.
@@ -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-D-AWzk2Q.js"></script>
7
+ <script type="module" crossorigin src="./assets/main-DJ9sK-1n.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="./assets/main-CYNmzqDG.css">
9
9
  </head>
10
10
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-lens",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "A visualization tool for Codex that monitors API requests and file system changes",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useRef, useCallback } from 'react';
1
+ import React, { useState, useEffect, useRef } from 'react';
2
2
  import { TerminalPanel } from './TerminalPanel';
3
3
  import { CodeViewer } from './CodeViewer';
4
4
 
@@ -15,6 +15,7 @@ export function App() {
15
15
  const wsRef = useRef(null);
16
16
  const activeTabIdRef = useRef(null);
17
17
  const tabsRef = useRef([]);
18
+ const saveCurrentFileRef = useRef(null);
18
19
 
19
20
  useEffect(() => {
20
21
  activeTabIdRef.current = activeTabId;
@@ -24,79 +25,7 @@ export function App() {
24
25
  tabsRef.current = tabs;
25
26
  }, [tabs]);
26
27
 
27
- useEffect(() => {
28
- fetchStatus();
29
- connectWebSocket();
30
- document.addEventListener('click', handleDocumentClick);
31
- document.addEventListener('keydown', handleKeyDown);
32
-
33
- const preventBrowserSave = (e) => {
34
- if (e.ctrlKey && e.key === 's') {
35
- e.preventDefault();
36
- e.stopPropagation();
37
- }
38
- };
39
- window.addEventListener('keydown', preventBrowserSave, true);
40
-
41
- return () => {
42
- document.removeEventListener('click', handleDocumentClick);
43
- document.removeEventListener('keydown', handleKeyDown);
44
- window.removeEventListener('keydown', preventBrowserSave, true);
45
- if (wsRef.current) {
46
- wsRef.current.close();
47
- }
48
- };
49
- }, []);
50
-
51
- async function fetchStatus() {
52
- try {
53
- const port = window.location.port === '5173' ? '5174' : window.location.port;
54
- const protocol = window.location.protocol === 'https:' ? 'https:' : 'http:';
55
- const response = await fetch(`${protocol}//${window.location.hostname}:${port}/api/status`);
56
- if (response.ok) {
57
- const data = await response.json();
58
- setVersion(data.version);
59
- setLatestVersion(data.latestVersion);
60
- setHasUpdate(data.hasUpdate);
61
- if (data.projectRoot) {
62
- const parts = data.projectRoot.split(/[/\\]/);
63
- setProjectName(parts[parts.length - 1] || data.projectRoot);
64
- }
65
- }
66
- } catch (error) {
67
- console.error('Failed to fetch status:', error);
68
- }
69
- }
70
-
71
- function handleDocumentClick() {
72
- setContextMenu(null);
73
- }
74
-
75
- function handleKeyDown(e) {
76
- if (e.ctrlKey && e.key === 's') {
77
- e.preventDefault();
78
- if (activeTabId) {
79
- saveCurrentFile();
80
- }
81
- return;
82
- }
83
-
84
- if (!activeTabId) return;
85
-
86
- if (e.ctrlKey && e.key === 'w') {
87
- e.preventDefault();
88
- closeTab(activeTabId);
89
- } else if (e.ctrlKey && e.key === 'Tab') {
90
- e.preventDefault();
91
- if (e.shiftKey) {
92
- switchToPrevTab();
93
- } else {
94
- switchToNextTab();
95
- }
96
- }
97
- }
98
-
99
- const saveCurrentFile = useCallback(() => {
28
+ function saveCurrentFile() {
100
29
  const currentTabId = activeTabIdRef.current;
101
30
  const currentTabs = tabsRef.current;
102
31
  const activeTab = currentTabs.find(t => t.id === currentTabId);
@@ -131,6 +60,84 @@ export function App() {
131
60
  .catch(error => {
132
61
  console.error('Failed to save file:', error);
133
62
  });
63
+ }
64
+
65
+ useEffect(() => {
66
+ saveCurrentFileRef.current = saveCurrentFile;
67
+ });
68
+
69
+ async function fetchStatus() {
70
+ try {
71
+ const port = window.location.port === '5173' ? '5174' : window.location.port;
72
+ const protocol = window.location.protocol === 'https:' ? 'https:' : 'http:';
73
+ const response = await fetch(`${protocol}//${window.location.hostname}:${port}/api/status`);
74
+ if (response.ok) {
75
+ const data = await response.json();
76
+ setVersion(data.version);
77
+ setLatestVersion(data.latestVersion);
78
+ setHasUpdate(data.hasUpdate);
79
+ if (data.projectRoot) {
80
+ const parts = data.projectRoot.split(/[/\\]/);
81
+ setProjectName(parts[parts.length - 1] || data.projectRoot);
82
+ }
83
+ }
84
+ } catch (error) {
85
+ console.error('Failed to fetch status:', error);
86
+ }
87
+ }
88
+
89
+ function handleDocumentClick() {
90
+ setContextMenu(null);
91
+ }
92
+
93
+ useEffect(() => {
94
+ fetchStatus();
95
+ connectWebSocket();
96
+
97
+ function handleKeyDown(e) {
98
+ if (e.ctrlKey && e.key === 's') {
99
+ e.preventDefault();
100
+ console.log('Ctrl+S pressed, activeTabId:', activeTabIdRef.current);
101
+ if (saveCurrentFileRef.current) {
102
+ saveCurrentFileRef.current();
103
+ }
104
+ return;
105
+ }
106
+
107
+ if (!activeTabIdRef.current) return;
108
+
109
+ if (e.ctrlKey && e.key === 'w') {
110
+ e.preventDefault();
111
+ closeTab(activeTabIdRef.current);
112
+ } else if (e.ctrlKey && e.key === 'Tab') {
113
+ e.preventDefault();
114
+ if (e.shiftKey) {
115
+ switchToPrevTab();
116
+ } else {
117
+ switchToNextTab();
118
+ }
119
+ }
120
+ }
121
+
122
+ document.addEventListener('click', handleDocumentClick);
123
+ document.addEventListener('keydown', handleKeyDown);
124
+
125
+ const preventBrowserSave = (e) => {
126
+ if (e.ctrlKey && e.key === 's') {
127
+ e.preventDefault();
128
+ e.stopPropagation();
129
+ }
130
+ };
131
+ window.addEventListener('keydown', preventBrowserSave, true);
132
+
133
+ return () => {
134
+ document.removeEventListener('click', handleDocumentClick);
135
+ document.removeEventListener('keydown', handleKeyDown);
136
+ window.removeEventListener('keydown', preventBrowserSave, true);
137
+ if (wsRef.current) {
138
+ wsRef.current.close();
139
+ }
140
+ };
134
141
  }, []);
135
142
 
136
143
  function handleContentChange(newContent) {
@@ -261,7 +268,8 @@ export function App() {
261
268
  function closeTab(tabId) {
262
269
  setTabs(prev => {
263
270
  const newTabs = prev.filter(t => t.id !== tabId);
264
- if (activeTabId === tabId) {
271
+ const currentActiveId = activeTabIdRef.current;
272
+ if (currentActiveId === tabId) {
265
273
  const newActiveId = newTabs.length > 0 ? newTabs[newTabs.length - 1].id : null;
266
274
  setActiveTabId(newActiveId);
267
275
  }
@@ -284,20 +292,24 @@ export function App() {
284
292
  }
285
293
 
286
294
  function switchToNextTab() {
287
- const currentIndex = tabs.findIndex(t => t.id === activeTabId);
288
- if (currentIndex < tabs.length - 1) {
289
- setActiveTabId(tabs[currentIndex + 1].id);
290
- } else if (tabs.length > 0) {
291
- setActiveTabId(tabs[0].id);
295
+ const currentTabs = tabsRef.current;
296
+ const currentActiveId = activeTabIdRef.current;
297
+ const currentIndex = currentTabs.findIndex(t => t.id === currentActiveId);
298
+ if (currentIndex < currentTabs.length - 1) {
299
+ setActiveTabId(currentTabs[currentIndex + 1].id);
300
+ } else if (currentTabs.length > 0) {
301
+ setActiveTabId(currentTabs[0].id);
292
302
  }
293
303
  }
294
304
 
295
305
  function switchToPrevTab() {
296
- const currentIndex = tabs.findIndex(t => t.id === activeTabId);
306
+ const currentTabs = tabsRef.current;
307
+ const currentActiveId = activeTabIdRef.current;
308
+ const currentIndex = currentTabs.findIndex(t => t.id === currentActiveId);
297
309
  if (currentIndex > 0) {
298
- setActiveTabId(tabs[currentIndex - 1].id);
299
- } else if (tabs.length > 0) {
300
- setActiveTabId(tabs[tabs.length - 1].id);
310
+ setActiveTabId(currentTabs[currentIndex - 1].id);
311
+ } else if (currentTabs.length > 0) {
312
+ setActiveTabId(currentTabs[currentTabs.length - 1].id);
301
313
  }
302
314
  }
303
315
 
@@ -365,7 +377,12 @@ export function App() {
365
377
  <ContextMenu
366
378
  x={contextMenu.x}
367
379
  y={contextMenu.y}
380
+ tabId={contextMenu.tabId}
368
381
  onClose={() => setContextMenu(null)}
382
+ onSave={() => {
383
+ saveCurrentFile();
384
+ setContextMenu(null);
385
+ }}
369
386
  onCloseTab={() => {
370
387
  closeTab(contextMenu.tabId);
371
388
  setContextMenu(null);
@@ -419,9 +436,14 @@ function TabBar({ tabs, activeTabId, onTabClick, onTabClose, onContextMenu }) {
419
436
  );
420
437
  }
421
438
 
422
- function ContextMenu({ x, y, onClose, onCloseTab, onCloseOtherTabs, onCloseAllTabs }) {
439
+ function ContextMenu({ x, y, tabId, onClose, onSave, onCloseTab, onCloseOtherTabs, onCloseAllTabs }) {
440
+ const tabs = tabsRef.current;
441
+ const tab = tabs.find(t => t.id === tabId);
442
+ const canSave = tab && !tab.isDiff;
443
+
423
444
  return (
424
445
  <div className="context-menu" style={{ left: x, top: y }} onClick={(e) => e.stopPropagation()}>
446
+ {canSave && <div className="context-menu-item" onClick={onSave}>保存</div>}
425
447
  <div className="context-menu-item" onClick={onCloseTab}>关闭</div>
426
448
  <div className="context-menu-item" onClick={onCloseOtherTabs}>关闭其他</div>
427
449
  <div className="context-menu-item" onClick={onCloseAllTabs}>关闭所有</div>