glad-web 1.0.44 → 1.0.45

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/lib/web/git.js CHANGED
@@ -51,23 +51,23 @@
51
51
  let addCount = 0;
52
52
  let subCount = 0;
53
53
  for (const line of block.lines) {
54
- let color = '#ccc', bg = 'transparent', borderLeft = '2px solid transparent';
55
- if (line.startsWith('+') && !line.startsWith('+++')) { color = '#4ade80'; bg = 'rgba(74, 222, 128, 0.1)'; borderLeft = '2px solid #4ade80'; addCount++; }
56
- else if (line.startsWith('-') && !line.startsWith('---')) { color = '#f87171'; bg = 'rgba(248, 113, 113, 0.1)'; borderLeft = '2px solid #f87171'; subCount++; }
57
- else if (line.startsWith('@@')) { color = '#60a5fa'; bg = 'rgba(96, 165, 250, 0.1)'; }
58
- else if (line.startsWith('diff') || line.startsWith('index') || line.startsWith('commit') || line.startsWith('Author') || line.startsWith('Date')) color = '#fff';
54
+ let color = 'var(--git-diff-text)', bg = 'transparent', borderLeft = '2px solid transparent';
55
+ if (line.startsWith('+') && !line.startsWith('+++')) { color = 'var(--git-add-text)'; bg = 'var(--git-add-bg)'; borderLeft = '2px solid var(--git-add-text)'; addCount++; }
56
+ else if (line.startsWith('-') && !line.startsWith('---')) { color = 'var(--git-del-text)'; bg = 'var(--git-del-bg)'; borderLeft = '2px solid var(--git-del-text)'; subCount++; }
57
+ else if (line.startsWith('@@')) { color = 'var(--git-hunk-text)'; bg = 'var(--git-hunk-bg)'; }
58
+ else if (line.startsWith('diff') || line.startsWith('index') || line.startsWith('commit') || line.startsWith('Author') || line.startsWith('Date')) color = 'var(--text)';
59
59
 
60
60
  blockContent += `<div style="color:${color}; background:${bg}; border-left:${borderLeft}; padding:2px 8px; white-space:pre-wrap; word-break:break-all;">${line.replace(/</g, '&lt;').replace(/>/g, '&gt;') || ' '}</div>`;
61
61
  }
62
62
 
63
63
  const isOpen = block.name === 'Commit Details';
64
- const statHTML = block.name !== 'Commit Details' ? `<span style="margin-left: 12px; font-family: monospace; font-size: 12px;"><span style="color:#4ade80;">+${addCount}</span> <span style="color:#f87171; margin-left:6px;">-${subCount}</span></span>` : '';
64
+ const statHTML = block.name !== 'Commit Details' ? `<span style="margin-left: 12px; font-family: monospace; font-size: 12px;"><span style="color:var(--git-add-text);">+${addCount}</span> <span style="color:var(--git-del-text); margin-left:6px;">-${subCount}</span></span>` : '';
65
65
  diffHTML += `
66
- <details ${isOpen ? 'open' : ''} style="margin-bottom: 8px; border: 1px solid #333; border-radius: 4px; overflow: hidden;">
67
- <summary style="background: #1e1e1e; padding: 6px 10px; cursor: pointer; color: #fff; font-weight: 500; font-size: 13px; outline: none; user-select: none;">
66
+ <details ${isOpen ? 'open' : ''} class="git-diff-panel">
67
+ <summary class="git-diff-summary">
68
68
  ${block.name === 'Commit Details' ? '📝 ' : '📄 '}${block.name.replace(/</g, '&lt;').replace(/>/g, '&gt;')}${statHTML}
69
69
  </summary>
70
- <div style="background: #0d0d0d; overflow-x: auto; font-family: monospace; font-size: 12px; line-height: 1.5; padding: 4px 0;">
70
+ <div class="git-diff-body">
71
71
  ${blockContent}
72
72
  </div>
73
73
  </details>
@@ -86,7 +86,7 @@
86
86
  function renderCommitDiffFullView() {
87
87
  const content = document.getElementById('git-content');
88
88
  let html = `
89
- <div style="display:flex; align-items:center; background: var(--card-bg); padding: 12px 14px; border-bottom: 1px solid rgba(255,255,255,0.05); position: sticky; top: 0; z-index: 10;">
89
+ <div style="display:flex; align-items:center; background: var(--card-bg); padding: 12px 14px; border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 10;">
90
90
  <button class="icon-btn" onclick="switchGitTab('graph')" style="color: var(--primary); margin-right: 12px; font-weight:600; font-size:14px; display:flex; align-items:center;">
91
91
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg> Back
92
92
  </button>
@@ -150,11 +150,11 @@
150
150
  let colorStyle = '';
151
151
  let badgeHtml = '';
152
152
  if (f.gitStatus) {
153
- let color = '#fff';
153
+ let color = 'var(--text)';
154
154
  let label = f.gitStatus.trim();
155
- if (label.includes('U') || label.includes('?')) { colorStyle = 'color: #4ade80;'; color = '#4ade80'; if(label === '??') label = 'U'; }
156
- else if (label.includes('M')) { colorStyle = 'color: #f59e0b;'; color = '#f59e0b'; }
157
- else if (label.includes('D')) { colorStyle = 'color: #f87171;'; color = '#f87171'; }
155
+ if (label.includes('U') || label.includes('?')) { colorStyle = 'color: var(--git-add-text);'; color = 'var(--git-add-text)'; if(label === '??') label = 'U'; }
156
+ else if (label.includes('M')) { colorStyle = 'color: var(--git-modified-text);'; color = 'var(--git-modified-text)'; }
157
+ else if (label.includes('D')) { colorStyle = 'color: var(--git-del-text);'; color = 'var(--git-del-text)'; }
158
158
  badgeHtml = `<span style="color:${color}; font-weight:700; font-size:10px; border:1px solid ${color}; padding:1px 4px; border-radius:3px; opacity:0.7; margin-left: auto;">${label}</span>`;
159
159
  }
160
160
 
@@ -232,11 +232,10 @@
232
232
  files.forEach((f, idx) => {
233
233
  const encodedPath = encodePathValue(f.path);
234
234
  const escapedPath = escapeHtml(f.path);
235
- let color = '#fff';
235
+ let statusClass = 'modified';
236
236
  let label = f.status;
237
- if (label.includes('M')) { color = '#f59e0b'; }
238
- else if (label.includes('A') || label === '??') { color = '#4ade80'; if(label === '??') label = 'U'; }
239
- else if (label.includes('D')) { color = '#f87171'; }
237
+ if (label.includes('A') || label === '??') { statusClass = 'added'; if(label === '??') label = 'U'; }
238
+ else if (label.includes('D')) { statusClass = 'deleted'; }
240
239
  const isUntracked = f.status === '??';
241
240
  const hasStaged = !isUntracked && f.status[0] && f.status[0] !== ' ';
242
241
  const hasUnstaged = !isUntracked && f.status[1] && f.status[1] !== ' ';
@@ -245,14 +244,14 @@
245
244
  if (statsMap[f.path]) {
246
245
  const { added, removed } = statsMap[f.path];
247
246
  if (added > 0 || removed > 0) {
248
- statHTML = `<span style="margin-left: 12px; font-family: monospace; font-size: 12px; white-space: nowrap;"><span style="color:#4ade80;">+${added}</span> <span style="color:#f87171; margin-left:6px;">-${removed}</span></span>`;
247
+ statHTML = `<span style="margin-left: 12px; font-family: monospace; font-size: 12px; white-space: nowrap;"><span style="color:var(--git-add-text);">+${added}</span> <span style="color:var(--git-del-text); margin-left:6px;">-${removed}</span></span>`;
249
248
  }
250
249
  }
251
250
 
252
- html += `<div style="margin-bottom: 8px; border: 1px solid #333; border-radius: 4px; background: #1e1e1e; overflow: hidden;">
253
- <div onclick="toggleInlineDiff('${encodedPath}', 'inline-diff-${idx}', ${!!hasStaged}, ${!!hasUnstaged}, ${isUntracked})" style="padding: 6px 10px; cursor: pointer; color: #fff; font-weight: 500; font-size: 13px; outline: none; user-select: none; display: flex; align-items: center;">
254
- <div style="flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">📄 ${escapedPath}${statHTML}</div>
255
- <span style="color:${color}; font-weight:700; font-size:10px; border:1px solid ${color}; padding:1px 4px; border-radius:3px; opacity:0.8; flex-shrink: 0; margin-left: 8px;">${label}</span>
251
+ html += `<div class="git-change-card">
252
+ <div class="git-change-header" onclick="toggleInlineDiff('${encodedPath}', 'inline-diff-${idx}', ${!!hasStaged}, ${!!hasUnstaged}, ${isUntracked})">
253
+ <div class="git-change-path">📄 ${escapedPath}${statHTML}</div>
254
+ <span class="git-status-badge ${statusClass}">${label}</span>
256
255
  </div>
257
256
  <div id="inline-diff-${idx}" style="display: none;" data-loaded="false"></div>
258
257
  </div>`;
@@ -335,26 +334,26 @@
335
334
  });
336
335
 
337
336
  if (!currentFileDiff && !currentFileContent) {
338
- container.innerHTML = `<div style="padding: 10px; color: #f87171; font-size: 12px; text-align:center;">No diff available</div>`;
337
+ container.innerHTML = `<div style="padding: 10px; color: var(--git-del-text); font-size: 12px; text-align:center;">No diff available</div>`;
339
338
  return;
340
339
  }
341
340
 
342
341
  let blockContent = '';
343
342
  currentFileDiff.split('\n').forEach(line => {
344
- let color = '#ccc', bg = 'transparent', borderLeft = '2px solid transparent';
345
- if (line.startsWith('+') && !line.startsWith('+++')) { color = '#4ade80'; bg = 'rgba(74, 222, 128, 0.1)'; borderLeft = '2px solid #4ade80'; }
346
- else if (line.startsWith('-') && !line.startsWith('---')) { color = '#f87171'; bg = 'rgba(248, 113, 113, 0.1)'; borderLeft = '2px solid #f87171'; }
347
- else if (line.startsWith('@@')) { color = '#60a5fa'; bg = 'rgba(96, 165, 250, 0.1)'; }
343
+ let color = 'var(--git-diff-text)', bg = 'transparent', borderLeft = '2px solid transparent';
344
+ if (line.startsWith('+') && !line.startsWith('+++')) { color = 'var(--git-add-text)'; bg = 'var(--git-add-bg)'; borderLeft = '2px solid var(--git-add-text)'; }
345
+ else if (line.startsWith('-') && !line.startsWith('---')) { color = 'var(--git-del-text)'; bg = 'var(--git-del-bg)'; borderLeft = '2px solid var(--git-del-text)'; }
346
+ else if (line.startsWith('@@')) { color = 'var(--git-hunk-text)'; bg = 'var(--git-hunk-bg)'; }
348
347
 
349
348
  blockContent += `<div style="color:${color}; background:${bg}; border-left:${borderLeft}; padding:2px 8px; white-space:pre-wrap; word-break:break-all;">${escapeHtml(line) || ' '}</div>`;
350
349
  });
351
350
 
352
351
  container.dataset.loaded = 'true';
353
352
  container.innerHTML = `
354
- <div style="background: #0d0d0d; overflow-x: auto; font-family: monospace; font-size: 12px; line-height: 1.5; padding: 4px 0; border-top: 1px solid #333; max-height: 400px;">
353
+ <div class="git-inline-diff git-inline-diff-body">
355
354
  ${blockContent}
356
355
  </div>
357
- <div style="padding: 8px; background: #1a1a1a; border-top: 1px solid #333; text-align: center;">
356
+ <div class="git-inline-diff-footer">
358
357
  <button onclick="showFileDetails(decodePathValue('${encodedPath}'), true, ${!!hasStaged}, ${!!hasUnstaged}, ${!!isUntracked})" style="background: var(--primary); border: none; color: #fff; padding: 6px 12px; border-radius: 4px; font-size: 12px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px;">
359
358
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>
360
359
  View Full File
@@ -362,7 +361,7 @@
362
361
  </div>
363
362
  `;
364
363
  } catch (e) {
365
- container.innerHTML = `<div style="padding: 10px; color: #f87171; font-size: 12px; text-align:center;">Error: ${e.message}</div>`;
364
+ container.innerHTML = `<div style="padding: 10px; color: var(--git-del-text); font-size: 12px; text-align:center;">Error: ${e.message}</div>`;
366
365
  }
367
366
  };
368
367
 
@@ -404,7 +403,7 @@
404
403
  function renderFileDetails() {
405
404
  const content = document.getElementById('git-content');
406
405
  let html = `
407
- <div style="display:flex; align-items:center; background: var(--card-bg); padding: 12px 14px; border-bottom: 1px solid rgba(255,255,255,0.05); position: sticky; top: 0; z-index: 10;">
406
+ <div style="display:flex; align-items:center; background: var(--card-bg); padding: 12px 14px; border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 10;">
408
407
  <button class="icon-btn" onclick="switchGitTab(currentGitTab)" style="color: var(--primary); margin-right: 12px; font-weight:600; font-size:14px; display:flex; align-items:center; flex-shrink: 0;">
409
408
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg> Back
410
409
  </button>
@@ -415,27 +414,27 @@
415
414
  if (currentFileDiff && currentFileContent) {
416
415
  html += `
417
416
  <div style="display: flex; gap: 6px; flex-shrink: 0; align-items: center;">
418
- <div style="display: flex; gap: 2px; margin-right: 8px; background: rgba(0,0,0,0.3); padding: 2px; border-radius: 4px;">
419
- <button onclick="changeFontSize(-1)" style="background: transparent; border: none; color: #aaa; padding: 4px 8px; font-size: 12px; cursor: pointer; outline: none; border-radius: 3px;" title="Zoom Out" onmouseover="this.style.background='rgba(255,255,255,0.1)';this.style.color='#fff'" onmouseout="this.style.background='transparent';this.style.color='#aaa'">A-</button>
420
- <button onclick="changeFontSize(0)" style="background: transparent; border: none; color: #aaa; padding: 4px 8px; font-size: 12px; cursor: pointer; outline: none; border-radius: 3px;" title="Reset Size" onmouseover="this.style.background='rgba(255,255,255,0.1)';this.style.color='#fff'" onmouseout="this.style.background='transparent';this.style.color='#aaa'">${currentFontSize}</button>
421
- <button onclick="changeFontSize(1)" style="background: transparent; border: none; color: #aaa; padding: 4px 8px; font-size: 12px; cursor: pointer; outline: none; border-radius: 3px;" title="Zoom In" onmouseover="this.style.background='rgba(255,255,255,0.1)';this.style.color='#fff'" onmouseout="this.style.background='transparent';this.style.color='#aaa'">A+</button>
417
+ <div class="git-file-toolbar-group">
418
+ <button class="git-file-toolbar-button" onclick="changeFontSize(-1)" title="Zoom Out">A-</button>
419
+ <button class="git-file-toolbar-button" onclick="changeFontSize(0)" title="Reset Size">${currentFontSize}</button>
420
+ <button class="git-file-toolbar-button" onclick="changeFontSize(1)" title="Zoom In">A+</button>
422
421
  </div>
423
- <button onclick="toggleFileMode()" style="background: ${currentFileMode === 'diff' ? 'var(--primary)' : 'rgba(255,255,255,0.1)'}; border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 4px 8px; border-radius: 4px; font-size: 12px; cursor: pointer; outline: none;">
422
+ <button class="git-file-mode-button${currentFileMode === 'diff' ? ' active' : ''}" onclick="toggleFileMode()">
424
423
  Diff
425
424
  </button>
426
- <button onclick="toggleFileWrap()" style="background: ${currentFileWrap ? 'var(--primary)' : 'rgba(255,255,255,0.1)'}; border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 4px 8px; border-radius: 4px; font-size: 12px; cursor: pointer; outline: none;">
425
+ <button class="git-file-mode-button${currentFileWrap ? ' active' : ''}" onclick="toggleFileWrap()">
427
426
  Wrap
428
427
  </button>
429
428
  </div>`;
430
429
  } else {
431
430
  html += `
432
431
  <div style="display: flex; gap: 6px; flex-shrink: 0; align-items: center;">
433
- <div style="display: flex; gap: 2px; margin-right: 8px; background: rgba(0,0,0,0.3); padding: 2px; border-radius: 4px;">
434
- <button onclick="changeFontSize(-1)" style="background: transparent; border: none; color: #aaa; padding: 4px 8px; font-size: 12px; cursor: pointer; outline: none; border-radius: 3px;" title="Zoom Out" onmouseover="this.style.background='rgba(255,255,255,0.1)';this.style.color='#fff'" onmouseout="this.style.background='transparent';this.style.color='#aaa'">A-</button>
435
- <button onclick="changeFontSize(0)" style="background: transparent; border: none; color: #aaa; padding: 4px 8px; font-size: 12px; cursor: pointer; outline: none; border-radius: 3px;" title="Reset Size" onmouseover="this.style.background='rgba(255,255,255,0.1)';this.style.color='#fff'" onmouseout="this.style.background='transparent';this.style.color='#aaa'">${currentFontSize}</button>
436
- <button onclick="changeFontSize(1)" style="background: transparent; border: none; color: #aaa; padding: 4px 8px; font-size: 12px; cursor: pointer; outline: none; border-radius: 3px;" title="Zoom In" onmouseover="this.style.background='rgba(255,255,255,0.1)';this.style.color='#fff'" onmouseout="this.style.background='transparent';this.style.color='#aaa'">A+</button>
432
+ <div class="git-file-toolbar-group">
433
+ <button class="git-file-toolbar-button" onclick="changeFontSize(-1)" title="Zoom Out">A-</button>
434
+ <button class="git-file-toolbar-button" onclick="changeFontSize(0)" title="Reset Size">${currentFontSize}</button>
435
+ <button class="git-file-toolbar-button" onclick="changeFontSize(1)" title="Zoom In">A+</button>
437
436
  </div>
438
- <button onclick="toggleFileWrap()" style="background: ${currentFileWrap ? 'var(--primary)' : 'rgba(255,255,255,0.1)'}; border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 4px 8px; border-radius: 4px; font-size: 12px; cursor: pointer; outline: none;">
437
+ <button class="git-file-mode-button${currentFileWrap ? ' active' : ''}" onclick="toggleFileWrap()">
439
438
  Wrap
440
439
  </button>
441
440
  </div>`;
@@ -446,7 +445,7 @@
446
445
  const wrapStyle = currentFileWrap ? 'white-space: pre-wrap; word-break: break-all;' : 'white-space: pre;';
447
446
  const innerWrapStyle = currentFileWrap ? '' : 'min-width: max-content;';
448
447
  html += `<div style="padding: 10px;">`;
449
- html += `<div style="background:#0d0d0d; border-radius:8px; overflow-x:auto; font-family:monospace; font-size:${currentFontSize}px; line-height:1.5; padding: 12px 0; margin: 0; -webkit-text-size-adjust: 100%; text-size-adjust: 100%;">`;
448
+ html += `<div class="git-file-code" style="font-size:${currentFontSize}px; line-height:1.5; -webkit-text-size-adjust:100%; text-size-adjust:100%;">`;
450
449
  html += `<div style="${innerWrapStyle}">`;
451
450
 
452
451
  let mergedLines = [];
@@ -505,20 +504,20 @@
505
504
  }
506
505
 
507
506
  mergedLines.forEach(item => {
508
- let color = '#ccc', bg = 'transparent', borderLeft = '2px solid transparent';
507
+ let color = 'var(--git-diff-text)', bg = 'transparent', borderLeft = '2px solid transparent';
509
508
  let prefix = ' ';
510
- if (item.type === 'added') { color = '#4ade80'; bg = 'rgba(74, 222, 128, 0.1)'; borderLeft = '2px solid #4ade80'; prefix = '+'; }
511
- else if (item.type === 'deleted') { color = '#f87171'; bg = 'rgba(248, 113, 113, 0.1)'; borderLeft = '2px solid #f87171'; prefix = '-'; }
509
+ if (item.type === 'added') { color = 'var(--git-add-text)'; bg = 'var(--git-add-bg)'; borderLeft = '2px solid var(--git-add-text)'; prefix = '+'; }
510
+ else if (item.type === 'deleted') { color = 'var(--git-del-text)'; bg = 'var(--git-del-bg)'; borderLeft = '2px solid var(--git-del-text)'; prefix = '-'; }
512
511
  else if (item.type === 'raw') {
513
- if (item.text.startsWith('+') && !item.text.startsWith('+++')) { color = '#4ade80'; bg = 'rgba(74, 222, 128, 0.1)'; borderLeft = '2px solid #4ade80'; }
514
- else if (item.text.startsWith('-') && !item.text.startsWith('---')) { color = '#f87171'; bg = 'rgba(248, 113, 113, 0.1)'; borderLeft = '2px solid #f87171'; }
515
- else if (item.text.startsWith('@@')) { color = '#60a5fa'; bg = 'rgba(96, 165, 250, 0.1)'; }
512
+ if (item.text.startsWith('+') && !item.text.startsWith('+++')) { color = 'var(--git-add-text)'; bg = 'var(--git-add-bg)'; borderLeft = '2px solid var(--git-add-text)'; }
513
+ else if (item.text.startsWith('-') && !item.text.startsWith('---')) { color = 'var(--git-del-text)'; bg = 'var(--git-del-bg)'; borderLeft = '2px solid var(--git-del-text)'; }
514
+ else if (item.text.startsWith('@@')) { color = 'var(--git-hunk-text)'; bg = 'var(--git-hunk-bg)'; }
516
515
  prefix = '';
517
516
  }
518
517
 
519
518
  const renderedText = item.type === 'raw' ? item.text : `${prefix} ${item.text}`;
520
519
  html += `<div style="color:${color}; background:${bg}; border-left:${borderLeft}; padding: 0 12px 0 8px; display:flex;">`;
521
- html += `<div style="color:#555; margin-right:16px; user-select:none; flex-shrink:0; width: 36px; text-align:right;">${item.lineNum || ''}</div>`;
520
+ html += `<div class="git-line-number">${item.lineNum || ''}</div>`;
522
521
  html += `<div style="flex:1; min-width:0; ${wrapStyle}">${escapeHtml(renderedText) || ' '}</div>`;
523
522
  html += `</div>`;
524
523
  });
@@ -531,3 +530,6 @@
531
530
  refreshSessionsNow();
532
531
  document.addEventListener('visibilitychange', refreshSessionsNow);
533
532
  });
533
+ window.addEventListener('glad-theme-change', () => {
534
+ if (document.getElementById('git-view').classList.contains('active')) switchGitTab(currentGitTab);
535
+ });
@@ -74,10 +74,10 @@ class GitGraphRenderer {
74
74
  rowDiv.style.display = 'flex';
75
75
  rowDiv.style.alignItems = 'center';
76
76
  rowDiv.style.height = this.rowHeight + 'px';
77
- rowDiv.style.borderBottom = '1px solid rgba(255,255,255,0.05)';
77
+ rowDiv.style.borderBottom = '1px solid var(--line)';
78
78
  rowDiv.style.position = 'relative';
79
79
  rowDiv.style.cursor = 'pointer';
80
- rowDiv.onmouseover = () => rowDiv.style.backgroundColor = 'rgba(255,255,255,0.05)';
80
+ rowDiv.onmouseover = () => rowDiv.style.backgroundColor = 'var(--surface-soft)';
81
81
  rowDiv.onmouseout = () => rowDiv.style.backgroundColor = 'transparent';
82
82
 
83
83
  const canvas = document.createElement('canvas');
@@ -102,7 +102,7 @@ class GitGraphRenderer {
102
102
  contentDiv.style.overflow = 'hidden';
103
103
 
104
104
  const msgSpan = document.createElement('span');
105
- msgSpan.style.color = '#fff';
105
+ msgSpan.style.color = 'var(--text)';
106
106
  msgSpan.style.fontSize = '13px';
107
107
  msgSpan.style.fontWeight = '500';
108
108
  msgSpan.style.overflow = 'hidden';
@@ -160,18 +160,18 @@ class GitGraphRenderer {
160
160
  el.remove();
161
161
  });
162
162
 
163
- rowDiv.style.backgroundColor = 'rgba(255,255,255,0.05)';
163
+ rowDiv.style.backgroundColor = 'var(--surface-soft)';
164
164
 
165
165
  const detailsDiv = document.createElement('div');
166
166
  detailsDiv.className = 'gitgraph-details-view';
167
- detailsDiv.style.backgroundColor = '#121212';
167
+ detailsDiv.style.backgroundColor = 'var(--surface)';
168
168
  detailsDiv.style.borderLeft = '2px solid ' + this.getColor(commit.col);
169
169
  detailsDiv.style.padding = '16px';
170
170
  detailsDiv.style.margin = '4px 0 4px ' + (canvasWidth) + 'px';
171
171
  detailsDiv.style.borderRadius = '0 6px 6px 0';
172
172
  detailsDiv.style.fontSize = '13px';
173
173
  detailsDiv.style.color = 'var(--text)';
174
- detailsDiv.style.boxShadow = 'inset 0 0 10px rgba(0,0,0,0.5)';
174
+ detailsDiv.style.boxShadow = 'inset 0 0 10px rgba(0,0,0,0.12)';
175
175
 
176
176
  let detailsHTML = `
177
177
  <div style="display:flex; justify-content:space-between; margin-bottom:12px;">
@@ -189,7 +189,7 @@ class GitGraphRenderer {
189
189
 
190
190
  // Add diff placeholder
191
191
  detailsHTML += `
192
- <div style="border-top:1px solid #333; padding-top:12px; margin-top:12px;">
192
+ <div style="border-top:1px solid var(--line); padding-top:12px; margin-top:12px;">
193
193
  <button onclick="window.loadCommitDiff('${commit.hash}')" style="background:var(--primary); border:none; color:#fff; padding:6px 12px; border-radius:4px; font-size:12px; cursor:pointer;">View Full Diff</button>
194
194
  </div>
195
195
  `;
@@ -269,7 +269,7 @@ class GitGraphRenderer {
269
269
  ctx.fillStyle = this.getColor(commit.col);
270
270
  ctx.fill();
271
271
  ctx.lineWidth = 2;
272
- ctx.strokeStyle = '#121212'; // dark background match
272
+ ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--surface').trim() || '#121212';
273
273
  ctx.stroke();
274
274
  }
275
275
 
@@ -39,7 +39,9 @@
39
39
  <symbol id="icon-lock" viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></symbol>
40
40
  <symbol id="icon-skills" viewBox="0 0 24 24"><path d="M9 3H4a1 1 0 0 0-1 1v5h2.5a2.5 2.5 0 1 1 0 5H3v5a1 1 0 0 0 1 1h5v-2.5a2.5 2.5 0 1 1 5 0V20h5a1 1 0 0 0 1-1v-5h-2.5a2.5 2.5 0 1 1 0-5H20V4a1 1 0 0 0-1-1h-5v2.5a2.5 2.5 0 1 1-5 0V3Z"/></symbol>
41
41
  <symbol id="icon-image" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="9" cy="10" r="2"/><path d="m4 17 4.5-4 3.5 3 2.5-2 5.5 4"/></symbol>
42
+ <symbol id="icon-file" viewBox="0 0 24 24"><path d="M9 8.5 14.5 3a4 4 0 0 1 5.7 5.7L10.4 18.5a5.5 5.5 0 0 1-7.8-7.8l9.2-9.2"/><path d="m7 11 7-7a2 2 0 1 1 2.8 2.8l-9.1 9.1a2.5 2.5 0 0 1-3.5-3.5L12 4.6"/></symbol>
42
43
  <symbol id="icon-schedule" viewBox="0 0 24 24"><circle cx="10.5" cy="13" r="7.5"/><path d="M10.5 9v4l2.5 1.5M18 3v6M15 6h6"/></symbol>
44
+ <symbol id="icon-calendar" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M8 3v4M16 3v4M3 10h18"/></symbol>
43
45
  </svg>
44
46
 
45
47
  <div id="app-shell">
@@ -59,9 +61,7 @@
59
61
  </button>
60
62
  <button id="schedule-create-button" class="header-action-btn icon-only" type="button"
61
63
  onclick="showScheduleModal()" title="New scheduled task" aria-label="New scheduled task">
62
- <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
63
- <circle cx="12" cy="13" r="8"></circle><path d="M12 9v4l2.5 1.5"></path><path d="M9 2h6"></path><path d="M12 2v3"></path>
64
- </svg>
64
+ <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="#icon-calendar"></use></svg>
65
65
  </button>
66
66
  <button id="app-settings-button" class="header-action-btn icon-only" type="button"
67
67
  onclick="openSettings()" title="Settings" aria-label="Settings">
@@ -164,7 +164,6 @@
164
164
  </button>
165
165
  <div id="session-title">Terminal</div>
166
166
  <div style="display: flex; align-items: center; gap: 8px;">
167
- <button id="codex-terminal-switch" class="icon-btn" onclick="toggleCodexPresentation()" title="Switch Codex interface" style="display:none; color: var(--primary); font-size: 12px; font-weight: 700; background: rgba(0, 122, 255, 0.1); padding: 5px 9px; border-radius: 15px; gap: 4px;">TERM</button>
168
167
  <button class="icon-btn" onclick="showHistory()" title="History" style="color: var(--primary); font-size: 12px; font-weight: 700; background: rgba(0, 122, 255, 0.1); padding: 5px 9px; border-radius: 15px; gap: 4px;">
169
168
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7"></path><polyline points="3 3 3 9 9 9"></polyline><path d="M12 7v5l3 2"></path></svg>
170
169
  HIS
@@ -175,18 +174,23 @@
175
174
  </button>
176
175
  </div>
177
176
  </div>
177
+ <div id="session-status-strip" aria-live="polite">
178
+ <div id="session-attention-rail"></div>
179
+ </div>
178
180
  <div id="terminal-controls">
179
181
  <div id="timed-tag-rail"></div>
182
+ <div id="composer-skill-prefix"></div>
180
183
  <div id="input-row">
181
184
  <textarea id="cmd-input" rows="1" placeholder="Type a message..."></textarea>
182
- <button id="attach-image-btn" class="composer-action-btn" type="button" title="Add image" aria-label="Add image"><svg class="action-icon" aria-hidden="true"><use href="#icon-image"></use></svg></button>
183
- <button id="schedule-send-btn" class="composer-action-btn" type="button" title="Schedule send" aria-label="Schedule send"><svg class="action-icon" aria-hidden="true"><use href="#icon-schedule"></use></svg></button>
185
+ <button id="attachment-btn" class="composer-action-btn" type="button" title="Attachment" aria-label="Attachment"><svg class="action-icon" aria-hidden="true"><use href="#icon-file"></use></svg></button>
186
+ <button id="schedule-send-btn" class="composer-action-btn" type="button" title="Schedule" aria-label="Schedule"><svg class="action-icon" aria-hidden="true"><use href="#icon-schedule"></use></svg></button>
184
187
  <button id="send-btn" type="button" title="Send" aria-label="Send">
185
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
188
+ <svg class="action-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
186
189
  </button>
187
190
  </div>
188
191
  <div id="attachment-strip" aria-live="polite"></div>
189
- <input id="image-file-input" type="file" accept="image/png,image/jpeg,image/gif,image/webp" multiple hidden>
192
+ <input id="attachment-file-input" type="file" multiple hidden>
193
+ <div id="composer-action-tooltip" role="tooltip"></div>
190
194
  <div id="claude-control-panel">
191
195
  <div class="claude-control-rail">
192
196
  <div class="claude-control-page primary">
@@ -304,13 +308,13 @@
304
308
 
305
309
  <!-- Plain Text History View -->
306
310
  <div id="history-view" class="view">
307
- <div style="flex-shrink: 0; background: #121212; border-bottom: 1px solid #222; padding-top: env(safe-area-inset-top);">
308
- <div style="display: flex; align-items: center; padding: 8px 14px;">
311
+ <div class="subview-nav">
312
+ <div class="subview-nav-row">
309
313
  <button class="icon-btn" onclick="showTerminal()" style="color: var(--primary); font-size: 16px; font-weight: 500;">
310
314
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
311
315
  Terminal
312
316
  </button>
313
- <div style="flex: 1; text-align: center; font-weight: 600; font-size: 16px;">History</div>
317
+ <div class="subview-nav-title">History</div>
314
318
  <button class="icon-btn" onclick="loadHistory()" title="Refresh" style="color: var(--primary); margin-right: 8px;">
315
319
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"></path><polyline points="21 3 21 9 15 9"></polyline></svg>
316
320
  </button>
@@ -328,13 +332,13 @@
328
332
 
329
333
  <!-- Git View -->
330
334
  <div id="git-view" class="view">
331
- <div style="flex-shrink: 0; background: #121212; border-bottom: 1px solid #222; padding-top: env(safe-area-inset-top);">
332
- <div style="display: flex; align-items: center; padding: 8px 14px;">
335
+ <div class="subview-nav">
336
+ <div class="subview-nav-row">
333
337
  <button class="icon-btn" onclick="showTerminal()" style="color: var(--primary); font-size: 16px; font-weight: 500;">
334
338
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
335
339
  Terminal
336
340
  </button>
337
- <div style="flex: 1; text-align: center; font-weight: 600; font-size: 16px;">Git Status</div>
341
+ <div class="subview-nav-title">Git Status</div>
338
342
  <button class="icon-btn" onclick="loadGitStatus()" style="color: var(--primary);">
339
343
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"></path><polyline points="21 3 21 9 15 9"></polyline></svg>
340
344
  </button>
@@ -355,7 +359,7 @@
355
359
  <!-- Tool Modal -->
356
360
  <div id="modal-overlay" onclick="closeToolModal(event)">
357
361
  <div id="tool-modal" onclick="event.stopPropagation()">
358
- <h2 style="margin-top:0">Create Session</h2>
362
+ <div class="tool-modal-header"><h2>Create Session</h2><button class="icon-btn" type="button" onclick="closeToolModal()" aria-label="Close">×</button></div>
359
363
  <div style="margin-bottom: 15px;">
360
364
  <label style="display:block; margin-bottom: 8px; font-size: 14px; color: var(--text-dim);">Working Directory (Optional):</label>
361
365
  <div style="margin-bottom: 8px; color: var(--text-dim); font-size: 12px; line-height: 1.4;">
@@ -1,6 +1,6 @@
1
1
  function joinSession(id, sessionName, toolKey = null) {
2
- if (typeof clearImageAttachments === 'function' && selectedImageAttachments.length) {
3
- void clearImageAttachments();
2
+ if (typeof clearComposerAttachments === 'function' && (selectedImageAttachments.length || selectedFileAttachments.length)) {
3
+ void clearComposerAttachments();
4
4
  }
5
5
  stopTimedInputTimers();
6
6
  activeSessionId = id;
@@ -25,7 +25,7 @@
25
25
  initClaudeSession(sessionId);
26
26
  return;
27
27
  }
28
- if (isCodexSession() && codexState.presentation === 'structured') {
28
+ if (isCodexSession()) {
29
29
  initCodexSession(sessionId);
30
30
  return;
31
31
  }
@@ -62,14 +62,6 @@
62
62
  const msg = JSON.parse(e.data);
63
63
  if (msg.type === 'output') term.write(msg.data);
64
64
  if (msg.type === 'reset') term.reset();
65
- if (msg.type === 'codex-event' && isCodexSession()) {
66
- applyCodexEvent(msg.event);
67
- if (msg.event?.type === 'presentation' && msg.event.presentation === 'structured') {
68
- currentSocket.close();
69
- currentSocket = null;
70
- initCodexSession(sessionId);
71
- }
72
- }
73
65
  if (msg.type === 'exit') showLobby();
74
66
  };
75
67
  }
@@ -170,14 +162,7 @@
170
162
  applyCodexState(msg.snapshot.state || {});
171
163
  }
172
164
  if (msg.type === 'codex-detail-response' && msg.detail) applyCodexDetailResponse(msg);
173
- if (msg.type === 'codex-event') {
174
- applyCodexEvent(msg.event);
175
- if (msg.event?.type === 'presentation' && msg.event.presentation === 'terminal') {
176
- currentSocket.close();
177
- currentSocket = null;
178
- initTerminal(sessionId);
179
- }
180
- }
165
+ if (msg.type === 'codex-event') applyCodexEvent(msg.event);
181
166
  if (msg.type === 'exit') showLobby();
182
167
  };
183
168
  }