cc-viewer 1.0.8 → 1.0.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-viewer",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Claude Code Logger visualization management tool",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url';
4
4
  import { dirname, join, extname, basename } from 'node:path';
5
5
  import { homedir, userInfo, platform } from 'node:os';
6
6
  import { execSync } from 'node:child_process';
7
- import { LOG_FILE, _initPromise, _resumeState, resolveResumeChoice } from './interceptor.js';
7
+ import { LOG_FILE, _initPromise, _resumeState, resolveResumeChoice, _projectName } from './interceptor.js';
8
8
  import { t } from './i18n.js';
9
9
 
10
10
  const LOG_DIR = join(homedir(), '.claude', 'cc-viewer');
@@ -256,9 +256,8 @@ function handleRequest(req, res) {
256
256
  }
257
257
 
258
258
  const entries = readLogFile();
259
- entries.forEach(entry => {
260
- res.write(`data: ${JSON.stringify(entry)}\n\n`);
261
- });
259
+ // 初始化时发送 full_reload 事件(而非逐条发送),让前端可以批量处理时间戳
260
+ res.write(`event: full_reload\ndata: ${JSON.stringify(entries)}\n\n`);
262
261
 
263
262
  req.on('close', () => {
264
263
  clients = clients.filter(client => client !== res);
@@ -282,6 +281,13 @@ function handleRequest(req, res) {
282
281
  return;
283
282
  }
284
283
 
284
+ // 当前监控的项目名称
285
+ if (url === '/api/project-name' && method === 'GET') {
286
+ res.writeHead(200, { 'Content-Type': 'application/json' });
287
+ res.end(JSON.stringify({ projectName: _projectName || '' }));
288
+ return;
289
+ }
290
+
285
291
  // macOS 用户头像和显示名
286
292
  if (url === '/api/user-profile' && method === 'GET') {
287
293
  const profile = getUserProfile();
@@ -290,23 +296,30 @@ function handleRequest(req, res) {
290
296
  return;
291
297
  }
292
298
 
293
- // 列出本地日志文件(按项目分组)
299
+ // 列出本地日志文件(按项目分组,遍历项目子目录)
294
300
  if (url === '/api/local-logs' && method === 'GET') {
295
301
  try {
296
- const files = existsSync(LOG_DIR)
297
- ? readdirSync(LOG_DIR).filter(f => f.endsWith('.jsonl')).sort().reverse()
298
- : [];
299
- // 按项目名分组: {projectName: [{file, timestamp, size}]}
300
302
  const grouped = {};
301
- for (const f of files) {
302
- const match = f.match(/^(.+?)_(\d{8}_\d{6})\.jsonl$/);
303
- if (!match) continue;
304
- const project = match[1];
305
- const ts = match[2]; // 20260217_224218
306
- const filePath = join(LOG_DIR, f);
307
- const size = statSync(filePath).size;
308
- if (!grouped[project]) grouped[project] = [];
309
- grouped[project].push({ file: f, timestamp: ts, size });
303
+ if (existsSync(LOG_DIR)) {
304
+ const entries = readdirSync(LOG_DIR, { withFileTypes: true });
305
+ for (const entry of entries) {
306
+ if (!entry.isDirectory()) continue;
307
+ const project = entry.name;
308
+ const projectDir = join(LOG_DIR, project);
309
+ const files = readdirSync(projectDir)
310
+ .filter(f => f.endsWith('.jsonl'))
311
+ .sort()
312
+ .reverse();
313
+ for (const f of files) {
314
+ const match = f.match(/^(.+?)_(\d{8}_\d{6})\.jsonl$/);
315
+ if (!match) continue;
316
+ const ts = match[2];
317
+ const filePath = join(projectDir, f);
318
+ const size = statSync(filePath).size;
319
+ if (!grouped[project]) grouped[project] = [];
320
+ grouped[project].push({ file: `${project}/${f}`, timestamp: ts, size });
321
+ }
322
+ }
310
323
  }
311
324
  res.writeHead(200, { 'Content-Type': 'application/json' });
312
325
  res.end(JSON.stringify(grouped));
@@ -317,11 +330,11 @@ function handleRequest(req, res) {
317
330
  return;
318
331
  }
319
332
 
320
- // 读取指定本地日志文件
333
+ // 读取指定本地日志文件(支持 project/file 路径)
321
334
  if (url.startsWith('/api/local-log?') && method === 'GET') {
322
335
  const params = new URLSearchParams(url.split('?')[1]);
323
336
  const file = params.get('file');
324
- if (!file || file.includes('..') || file.includes('/')) {
337
+ if (!file || file.includes('..')) {
325
338
  res.writeHead(400, { 'Content-Type': 'application/json' });
326
339
  res.end(JSON.stringify({ error: 'Invalid file name' }));
327
340
  return;
@@ -1 +0,0 @@
1
- body{margin:0;background-color:#0d0d0d}*{scrollbar-width:thin;scrollbar-color:#3a3a3a #0d0d0d}*::-webkit-scrollbar{width:6px;height:6px}*::-webkit-scrollbar-track{background:#0d0d0d}*::-webkit-scrollbar-thumb{background:#3a3a3a;border-radius:3px}*::-webkit-scrollbar-thumb:hover{background:#555}.diff-view{background:#1a1a2e;border:1px solid #2a2a3e;border-radius:8px;padding:8px 12px}.diff-line-del{background:#ef444426;color:#fca5a5;padding:0 4px}.diff-line-add{background:#22c55e26;color:#86efac;padding:0 4px}.code-highlight{color:#e6edf3}.hl-keyword{color:#ff7b72}.hl-string{color:#a5d6ff}.hl-comment{color:#8b949e;font-style:italic}.hl-number{color:#79c0ff}.hl-linenum{color:#484f58;-webkit-user-select:none;user-select:none}.chat-md pre{background:#0d1117;border:1px solid #2a2a2a;border-radius:6px;padding:12px;overflow-x:auto;font-size:13px;line-height:1.5}.chat-md code{background:#1a1a2e;padding:2px 6px;border-radius:4px;font-size:13px;color:#e5e7eb}.chat-md pre code{background:none;padding:0}.chat-md p{margin:6px 0}.chat-md ul,.chat-md ol{padding-left:20px;margin:6px 0}.chat-md li{margin:2px 0}.chat-md h1,.chat-md h2,.chat-md h3{margin:12px 0 6px;color:#fff}.chat-md h1{font-size:1.3em}.chat-md h2{font-size:1.15em}.chat-md h3{font-size:1.05em}.chat-md blockquote{border-left:3px solid #3b82f6;margin:8px 0;padding:4px 12px;color:#9ca3af}.chat-md table{border-collapse:collapse;margin:8px 0;font-size:13px}.chat-md th,.chat-md td{border:1px solid #2a2a2a;padding:6px 10px}.chat-md th{background:#1a1a1a;color:#fff}.chat-md a{color:#60a5fa}.chat-md hr{border:none;border-top:1px solid #2a2a2a;margin:12px 0}.chat-md strong{color:#f1f5f9}.chat-md em{color:#cbd5e1}._headerBar_e7u4j_2{display:flex;align-items:center;justify-content:space-between;width:100%;height:100%}._titleText_e7u4j_11{color:#fff;font-size:18px;cursor:pointer}._titleArrow_e7u4j_17{font-size:12px;margin-left:4px}._tokenStatsTag_e7u4j_23{border-radius:12px;cursor:pointer;background:#2a2a2a;border:1px solid #3a3a3a;color:#ccc}._tokenStatsIcon_e7u4j_31{margin-right:4px}._liveTag_e7u4j_36{border-radius:12px}._liveTagText_e7u4j_40{margin-left:4px}._countdownStrong_e7u4j_45{font-variant-numeric:tabular-nums}._langSelector_e7u4j_50{color:#888;font-size:12px;cursor:pointer;-webkit-user-select:none;user-select:none;border:1px solid #555;border-radius:4px;padding:2px 8px}._tokenStatsEmpty_e7u4j_61{padding:8px 4px;color:#999;font-size:13px}._tokenStatsContainer_e7u4j_68{min-width:240px}._modelCard_e7u4j_73{border:1px solid #333;border-radius:6px;padding:8px 10px}._modelCardSpaced_e7u4j_79{margin-bottom:10px}._modelName_e7u4j_85{font-size:13px;font-weight:600;color:#e5e5e5;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid #333}._statsTable_e7u4j_95{width:100%;border-collapse:collapse}._th_e7u4j_100{padding:2px 12px;font-size:12px;font-family:monospace;white-space:nowrap;color:#888;font-weight:400;text-align:right}._td_e7u4j_110{padding:2px 12px;font-size:12px;font-family:monospace;white-space:nowrap;color:#e5e5e5;text-align:right}._label_e7u4j_119{padding:2px 12px;font-size:12px;font-family:monospace;white-space:nowrap;color:#aaa;font-weight:400;text-align:left}._rowBorder_e7u4j_129{border-bottom:1px solid #2a2a2a}._promptExportBar_e7u4j_134{margin-bottom:12px}._promptScrollArea_e7u4j_138{max-height:500px;overflow:auto}._promptEmpty_e7u4j_143{color:#999;padding:12px}._promptTimestamp_e7u4j_149{color:#666;font-size:12px;margin:12px 0 4px;padding-bottom:6px}._textPromptCard_e7u4j_157{margin:4px 0;background:#141414;border-radius:6px;border:1px solid #303030;padding:10px 14px}._preText_e7u4j_166{white-space:pre-wrap;word-break:break-word;font-size:13px;line-height:1.6;color:#d9d9d9;margin:4px 0}._systemCollapse_e7u4j_176{margin:4px 0;background:#1a1a1a;border:1px solid #303030;border-radius:6px}._systemLabel_e7u4j_183{color:#888;font-size:12px}._preSys_e7u4j_188{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5;color:#999;margin:0}._centerEmpty_1eo6z_1{display:flex;align-items:center;justify-content:center;height:100%}._scrollContainer_1eo6z_8{overflow:auto;height:100%}._listItem_1eo6z_13{cursor:pointer;padding:8px 12px;border-left:3px solid transparent;border-bottom:1px solid #1f1f1f;transition:background .15s}._listItem_1eo6z_13:hover{background:#151515}._listItemActive_1eo6z_25{background:#1a2332;border-left-color:#3b82f6}._listItemActive_1eo6z_25:hover{background:#1a2332}._itemContent_1eo6z_34{width:100%;min-width:0}._itemHeader_1eo6z_39{display:flex;align-items:center;gap:6px;margin-bottom:4px;font-size:12px}._tagNoMargin_1eo6z_47{margin:0;font-size:12px}._modelName_1eo6z_52{font-size:12px}._time_1eo6z_56{font-size:12px;color:#6b7280;margin-left:auto}._detailRow_1eo6z_62{display:flex;gap:8px;font-size:12px;align-items:center}._urlText_1eo6z_69{color:#555;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}._duration_1eo6z_78{color:#6b7280;flex-shrink:0}._statusOk_1eo6z_83{color:#10b981;flex-shrink:0}._statusErr_1eo6z_88{color:#ef4444;flex-shrink:0}._statusDefault_1eo6z_93{color:#9ca3af;flex-shrink:0}._usageBox_1eo6z_98{background:#111;border-radius:4px;padding:3px 6px;margin-top:4px;font-size:12px;color:#6b7280;line-height:1.6}._GzYRV{line-height:1.2;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}._3eOF8{margin-right:5px;font-weight:700}._3eOF8+._3eOF8{margin-left:-5px}._1MFti{cursor:pointer}._f10Tu{font-size:1.2em;margin-right:5px;-webkit-user-select:none;-moz-user-select:none;user-select:none}._1UmXx:after{content:"▸"}._1LId0:after{content:"▾"}._1pNG9{margin-right:5px}._1pNG9:after{content:"...";font-size:.8em}._2IvMF{background:#eee}._2bkNM{margin:0;padding:0 10px}._1BXBN{margin:0;padding:0}._1MGIk{font-weight:600;margin-right:5px;color:#000}._3uHL6{color:#000}._2T6PJ,._1Gho6{color:#df113a}._vGjyY{color:#2a3f3c}._1bQdo{color:#0b75f5}._3zQKs{color:#469038}._1xvuR{color:#43413d}._oLqym,._2AXVT,._2KJWg{color:#000}._11RoI{background:#002b36}._17H2C,._3QHg2,._3fDAz{color:#fdf6e3}._2bSDX{font-weight:bolder;margin-right:5px;color:#fdf6e3}._gsbQL{color:#fdf6e3}._LaAZe,._GTKgm{color:#81b5ac}._Chy1W{color:#cb4b16}._2bveF{color:#d33682}._2vRm-{color:#ae81ff}._1prJR{color:#268bd2}._container_1h2lr_1{background:#0d1117;border-radius:6px;border:1px solid #2a2a2a;padding:12px;font-size:13px;font-family:monospace;overflow:auto}._container_1d2jv_1{height:100%;overflow:auto;padding:0 16px}._emptyState_1d2jv_7{display:flex;align-items:center;justify-content:center;height:100%}._urlSection_1d2jv_14{padding:12px 0;border-bottom:1px solid #1f1f1f}._urlText_1d2jv_19{color:#d1d5db;font-size:13px;margin-bottom:8px;word-break:break-all}._metaText_1d2jv_26,._headersContainer_1d2jv_30{font-size:12px}._headerRow_1d2jv_34{display:flex;padding:4px 0;border-bottom:1px solid #1f1f1f}._headerKey_1d2jv_40{min-width:200px;flex-shrink:0}._headerValue_1d2jv_45{word-break:break-all;margin-left:8px}._streamingBox_1d2jv_50{padding:20px;background:#1a1a1a;border-radius:6px;border:1px solid #2a2a2a}._bodyToolbar_1d2jv_57{display:flex;gap:8px;margin-bottom:8px}._rawTextPre_1d2jv_63{background:#0d1117;border:1px solid #2a2a2a;border-radius:6px;padding:12px;font-size:12px;color:#e5e7eb;overflow:auto;max-height:600px;white-space:pre-wrap;word-break:break-all}._tabContent_1d2jv_76{padding:12px 0}._collapseSpacing_1d2jv_80{margin-bottom:16px}._bodyLabel_1d2jv_84{margin:0}._bodyHeader_1d2jv_88{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}._diffSection_1d2jv_95{margin-bottom:16px}._diffToggle_1d2jv_99{display:inline-block;margin-bottom:8px;cursor:pointer}._diffIcon_1d2jv_105{font-size:12px;margin-left:4px}._wrapper_1o255_1{margin:6px 0}._header_1o255_5{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}._filePath_1o255_12{color:#a78bfa;font-size:12px;font-weight:600}._toggle_1o255_18{color:#6b7280;font-size:11px;cursor:pointer;-webkit-user-select:none;user-select:none}._code_1o255_25{margin:0;font-size:12px;line-height:1.5;overflow:auto}._plainResult_19g53_1{background:#111;border-radius:6px;padding:8px 12px;font-size:12px}._plainTitle_19g53_8{font-size:11px;display:block;margin-bottom:4px}._plainPre_19g53_14{color:#d1d5db;margin:0;white-space:pre-wrap;word-break:break-all;font-size:12px}._codeResult_19g53_22{background:#0d1117;border-radius:6px;border:1px solid #1e2a3a;overflow:hidden}._codeHeader_19g53_29{display:flex;justify-content:space-between;align-items:center;padding:4px 10px;background:#161b22;border-bottom:1px solid #1e2a3a}._codeTitle_19g53_38{font-size:11px;color:#8b949e}._codeToggle_19g53_43{font-size:11px;color:#484f58;cursor:pointer;-webkit-user-select:none;user-select:none}._codePre_19g53_50{margin:0;padding:8px 12px;font-size:12px;line-height:1.5;overflow:auto;max-height:500px}._avatar_1uglv_3{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0}._avatarImg_1uglv_13{width:32px;height:32px;border-radius:50%;flex-shrink:0;object-fit:cover}._bubble_1uglv_21{border-radius:8px;padding:10px 14px;max-width:100%;font-size:14px;line-height:1.6;word-break:break-word}._messageRow_1uglv_32{display:flex;gap:10px;padding:8px 0}._messageRowEnd_1uglv_38{display:flex;gap:10px;padding:8px 0;justify-content:flex-end}._contentCol_1uglv_47{min-width:0;flex:1}._contentColLimited_1uglv_52{min-width:0;max-width:80%;width:fit-content}._labelRow_1uglv_60{display:flex;justify-content:space-between;align-items:center;margin-bottom:2px}._labelText_1uglv_67{font-size:11px}._timeText_1uglv_71{font-size:10px;color:#6b7280;flex-shrink:0;margin-left:8px}._timeTextNoMargin_1uglv_78{font-size:10px;color:#6b7280;flex-shrink:0}._labelTextRight_1uglv_84{font-size:11px;margin-left:auto}._bubbleUser_1uglv_91{background:#1668dc;color:#e5e7eb}._bubbleAssistant_1uglv_97{background:#141414;color:#e5e7eb}._bubblePlan_1uglv_103{background:#141414;border:1px solid #1668dc;color:#e5e7eb}._bubbleSubAgent_1uglv_110{background:#1a1a2e;color:#e5e7eb}._bubbleSelection_1uglv_116{background:#1a3a1a;color:#e5e7eb}._systemTagLabel_1uglv_124{font-size:12px}._systemTagPre_1uglv_128{font-size:12px;color:#9ca3af;white-space:pre-wrap;word-break:break-all;margin:0}._collapseMargin_1uglv_136{margin:4px 0}._collapseNoMargin_1uglv_140{margin:0}._thinkingLabel_1uglv_146{font-size:12px}._toolBox_1uglv_152{background:#1a1a2e;border:1px solid #2a2a3e;border-radius:8px;padding:8px 12px;margin:6px 0;font-size:12px}._toolLabel_1uglv_161{color:#a78bfa}._codePre_1uglv_167{font-size:12px;margin:4px 0 0;white-space:pre-wrap;word-break:break-all;background:#0d1117;border-radius:4px;padding:6px 8px}._pathTag_1uglv_179{color:#7dd3fc;font-size:11px}._descSpan_1uglv_186{color:#6b7280;font-weight:400}._secondarySpan_1uglv_191{color:#6b7280}._patternSpan_1uglv_195{color:#fbbf24}._kvContainer_1uglv_201{margin-top:4px;font-size:11px}._kvItem_1uglv_206{margin:2px 0}._kvKey_1uglv_210{color:#7dd3fc}._kvValue_1uglv_214{color:#9ca3af}._toolResult_1uglv_220{background:#111827;border:1px solid #1e293b;border-radius:6px;padding:6px 10px;margin:2px 0 6px;font-size:11px}._toolResultLabel_1uglv_229{font-size:11px}._compactLabel_1uglv_235{font-size:12px;color:#93c5fd}._compactPre_1uglv_240{font-size:12px;color:#d1d5db;white-space:pre-wrap;word-break:break-all;margin:0}._optionList_1uglv_250{padding-left:8px}._optionDesc_1uglv_254{color:#555;margin-left:6px;font-weight:400}._questionText_1uglv_262{font-size:13px;color:#ccc;margin-bottom:4px}._questionSpacing_1uglv_268{margin-bottom:10px}._option_1uglv_250{font-size:12px;padding:1px 0}._centerEmpty_1j3v3_1{display:flex;align-items:center;justify-content:center;height:100%}._container_1j3v3_8{height:100%;overflow:auto;padding:16px 24px;display:flex;flex-direction:column}._sessionDividerText_1j3v3_16{font-size:11px;color:#555}._lastResponseLabel_1j3v3_21{font-size:11px}._resizer_yamj2_1{width:6px;cursor:col-resize;background:#1f1f1f;flex-shrink:0;transition:background .2s}._resizer_yamj2_1:hover{background:#3b82f6}._layout_11p5u_1{height:100vh;overflow:hidden}._header_11p5u_6{background:#111;border-bottom:1px solid #1f1f1f;padding:0 24px;height:60px;line-height:60px}._content_11p5u_14{flex:1;overflow:hidden}._mainContainer_11p5u_19{display:flex;height:100%}._leftPanel_11p5u_24{flex-shrink:0;border-right:1px solid #1f1f1f;display:flex;flex-direction:column;background:#0a0a0a}._leftPanelHeader_11p5u_32{padding:10px 16px;border-bottom:1px solid #1f1f1f;font-size:13px;color:#9ca3af;font-weight:500;display:flex;justify-content:space-between;align-items:center}._leftPanelCount_11p5u_43{font-size:12px;color:#555;font-weight:400}._leftPanelBody_11p5u_49{flex:1;overflow:hidden}._rightPanel_11p5u_54{flex:1;overflow:hidden;background:#0d0d0d}._modalActions_11p5u_60{margin-bottom:12px}._spinCenter_11p5u_64{text-align:center;padding:40px}._emptyCenter_11p5u_69{text-align:center;color:#999;padding:40px}._logListItem_11p5u_75{cursor:pointer;padding:8px 12px}._logItemRow_11p5u_80{display:flex;align-items:center;width:100%;justify-content:space-between}._logFileIcon_11p5u_87{margin-right:8px;color:#3b82f6}._folderIcon_11p5u_92{margin-right:8px}._logTag_11p5u_96{margin-left:8px}