claude-code-memory-explorer 1.3.2 → 1.3.4

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": "claude-code-memory-explorer",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "Memory explorer dashboard for Claude Code",
5
5
  "main": "server.js",
6
6
  "type": "commonjs",
@@ -22,6 +22,7 @@
22
22
  "open": "^11.0.0"
23
23
  },
24
24
  "devDependencies": {
25
+ "@biomejs/biome": "2.4.10",
25
26
  "husky": "^9.0.0"
26
27
  },
27
28
  "keywords": [
package/public/app.js CHANGED
@@ -672,7 +672,7 @@ function renderTree() {
672
672
  const fp = footprintHighlight && footprintHit(item) ? ' fp-hit' : '';
673
673
  let h = `<div class="tree-item${sel}${indent ? ' tree-child' : ''}${isConditional ? ' tree-conditional' : ''}${muted}${fp}" data-id="${esc(item.id)}" title="${esc(item.path)}" onclick="selectFile('${escAttrJs(item.id)}')"${pad}>`;
674
674
  h += chevron;
675
- h += `<span class="load-icon" title="${loadTitle}" style="color:var(--scope-${item.scope})">${loadIcon}</span>`;
675
+ h += `<span class="load-icon" title="${loadTitle}" style="color:color-mix(in srgb, var(--scope-${item.scope}) var(--glyph-mix), #000)">${loadIcon}</span>`;
676
676
  h += `<span class="file-name">${esc(item.name)}</span>`;
677
677
  const hs = treeHealth.get(item.id);
678
678
  if (hs) {
@@ -701,7 +701,7 @@ function renderTree() {
701
701
  const chevron = collapsible ? `<span class="group-chevron">${collapsed ? '\u25B8' : '\u25BE'}</span>` : '';
702
702
  const headerClass = collapsible ? 'tree-group-header tree-group-clickable' : 'tree-group-header';
703
703
  const onclick = collapsible ? ` onclick="_toggleGroup('${escAttrJs(scope)}')"` : '';
704
- html += `<div class="${headerClass}" data-scope="${esc(scope)}"${onclick}>${chevron}<span class="scope-dot" style="color:var(--scope-${scope})">\u25CF</span> ${esc(label)} <span style="opacity:0.5">${items.length}</span></div>`;
704
+ html += `<div class="${headerClass}" data-scope="${esc(scope)}"${onclick}>${chevron}<span class="scope-dot" style="color:color-mix(in srgb, var(--scope-${scope}) var(--glyph-mix), #000)">\u25CF</span> ${esc(label)} <span style="opacity:0.5">${items.length}</span></div>`;
705
705
  if (collapsed) continue;
706
706
  if (scope === 'agent-memory') {
707
707
  const sorted = [...items].sort((a, b) => {
@@ -1536,6 +1536,10 @@ function scopeBadge(f) {
1536
1536
  return `<span class="scope-tag t-${esc(f.scope)}" title="${esc(title)}">${esc(f.scope)}</span>`;
1537
1537
  }
1538
1538
 
1539
+ function sevLabel(f) {
1540
+ return `<span class="sev-label">${esc(f.severity)}</span>`;
1541
+ }
1542
+
1539
1543
  function copyText(text, okMsg) {
1540
1544
  navigator.clipboard
1541
1545
  .writeText(text)
@@ -1747,11 +1751,12 @@ function renderHealthDash(st, shown) {
1747
1751
  const dirty = new Set(findings.flatMap((f) => (f.files || []).flatMap((n) => findingFileIds(n, f.scope))));
1748
1752
  const clean = audited.filter((s) => !dirty.has(s.id)).length;
1749
1753
  const score = healthScore(findings);
1750
- const scoreColor = score >= 80 ? 'var(--success)' : score >= 50 ? 'var(--warning)' : 'var(--error)';
1754
+ const scoreTone = score >= 80 ? 'success' : score >= 50 ? 'warning' : 'error';
1755
+ const scoreColor = `var(--${scoreTone})`;
1751
1756
  const CIRC = 163.4; // 2πr for r=26
1752
1757
  let html = '<div class="hv-tiles">';
1753
1758
  html += '<div class="hv-tile hv-ring-tile">';
1754
- html += `<div class="hv-ring"><svg width="64" height="64" viewBox="0 0 64 64"><circle cx="32" cy="32" r="26" fill="none" stroke="var(--bg-hover)" stroke-width="6"/><circle cx="32" cy="32" r="26" fill="none" stroke="${esc(scoreColor)}" stroke-width="6" stroke-linecap="round" transform="rotate(-90 32 32)" stroke-dasharray="${CIRC}" stroke-dashoffset="${esc((CIRC * (100 - score)) / 100)}"/></svg><span class="hv-ring-val" style="color:${scoreColor}">${score}</span></div>`;
1759
+ html += `<div class="hv-ring"><svg width="64" height="64" viewBox="0 0 64 64"><circle cx="32" cy="32" r="26" fill="none" stroke="var(--bg-hover)" stroke-width="6"/><circle cx="32" cy="32" r="26" fill="none" stroke="${esc(scoreColor)}" stroke-width="6" stroke-linecap="round" transform="rotate(-90 32 32)" stroke-dasharray="${CIRC}" stroke-dashoffset="${esc((CIRC * (100 - score)) / 100)}"/></svg><span class="hv-ring-val" style="color:var(--${scoreTone}-ink)">${score}</span></div>`;
1755
1760
  html += `<div class="hv-tile-txt" title="Score = 100 − 10/high − 3/med − 1/low"><span class="hv-lbl">Health</span><span class="hv-sub">of 100</span></div></div>`;
1756
1761
  for (const [sev, label] of [
1757
1762
  ['high', 'High'],
@@ -1814,7 +1819,7 @@ function renderMemoryMap(shown) {
1814
1819
  const title = `${s.name} — ${s.lines}L${n ? ` · ${n} finding${n > 1 ? 's' : ''} (worst: ${sev})` : ' · clean'}`;
1815
1820
  html += `<div class="hv-cell hv-${esc(sev)}${analysisFileFilter?.id === s.id ? ' active' : ''}" onclick="_setFileFilter('${escAttrJs(s.id)}','${escAttrJs(s.name)}')" title="${esc(title)} — click to filter findings">`;
1816
1821
  if (n) html += `<span class="hv-cell-n hv-cn-${esc(sev)}">${n}</span>`;
1817
- html += `<span class="hv-cell-name">${esc(s.name)}</span><span class="hv-cell-meta">${s.lines}L</span></div>`;
1822
+ html += `<span class="hv-cell-name">${esc(s.name)}</span><span class="hv-cell-meta">${s.lines} lines</span></div>`;
1818
1823
  }
1819
1824
  html += '</div>';
1820
1825
  return html;
@@ -1832,6 +1837,7 @@ function renderFileFindings(source) {
1832
1837
  for (const [f, i] of hits) {
1833
1838
  html += `<div class="analysis-card pf-card ${esc(`sev-${f.severity}`)}">`;
1834
1839
  html += `<div class="analysis-card-head" onclick="this.parentElement.classList.toggle('expanded')">`;
1840
+ html += sevLabel(f);
1835
1841
  html += `<span class="kind-badge ${esc(`kind-${f.kind}`)}">${esc(f.kind)}</span>`;
1836
1842
  html += scopeBadge(f);
1837
1843
  html += `<span class="analysis-card-title">${esc(f.title)}</span>`;
@@ -1983,7 +1989,9 @@ function renderAnalysis(st) {
1983
1989
  return html;
1984
1990
  }
1985
1991
  const r = shown.result;
1986
- analysisFindings = r.findings || [];
1992
+ analysisFindings = [...(r.findings || [])].sort(
1993
+ (a, b) => (SEV_ORDER[a.severity] ?? 3) - (SEV_ORDER[b.severity] ?? 3),
1994
+ );
1987
1995
  const byName = Object.fromEntries(stackData.map((s) => [s.name, s]));
1988
1996
  html += renderHealthDash(st, shown);
1989
1997
  if (r.summary) html += `<div class="analysis-summary">${esc(r.summary)}</div>`;
@@ -2022,6 +2030,7 @@ function renderAnalysis(st) {
2022
2030
  html += `<div class="analysis-card ${esc(`sev-${f.severity}`)}">`;
2023
2031
  html += '<div class="analysis-card-head">';
2024
2032
  html += `<input type="checkbox" class="fix-check" ${fixSelected.has(findingKey(f)) ? 'checked ' : ''}onclick="_toggleFixSelect(${idx})" title="Queue for the fix plan">`;
2033
+ html += sevLabel(f);
2025
2034
  html += `<span class="kind-badge ${esc(`kind-${f.kind}`)}">${esc(f.kind)}</span>`;
2026
2035
  html += scopeBadge(f);
2027
2036
  html += `<span class="analysis-card-title">${esc(f.title)}</span>`;
package/public/style.css CHANGED
@@ -53,6 +53,21 @@ textarea {
53
53
  }
54
54
 
55
55
  body {
56
+ /* Text on a tint needs a darker shade of the meaning color in light mode; the
57
+ ink tokens are declared on body so they pick up body.light's --ink-mix. */
58
+ --ink-mix: 100%;
59
+ --glyph-mix: 100%;
60
+ --error-ink: color-mix(in srgb, var(--error) var(--ink-mix), #000);
61
+ --warning-ink: color-mix(in srgb, var(--warning) var(--ink-mix), #000);
62
+ --success-ink: color-mix(in srgb, var(--success) var(--ink-mix), #000);
63
+ --scope-user-ink: color-mix(in srgb, var(--scope-user) var(--ink-mix), #000);
64
+ --scope-project-ink: color-mix(in srgb, var(--scope-project) var(--ink-mix), #000);
65
+ --scope-local-ink: color-mix(in srgb, var(--scope-local) var(--ink-mix), #000);
66
+ --scope-rule-ink: color-mix(in srgb, var(--scope-rule) var(--ink-mix), #000);
67
+ --scope-skill-ink: color-mix(in srgb, var(--scope-skill) var(--ink-mix), #000);
68
+ --scope-memory-ink: color-mix(in srgb, var(--scope-memory) var(--ink-mix), #000);
69
+ --scope-agent-memory-ink: color-mix(in srgb, var(--scope-agent-memory) var(--ink-mix), #000);
70
+ --scope-policy-ink: color-mix(in srgb, var(--scope-policy) var(--ink-mix), #000);
56
71
  /* native widgets (select popups, form controls) follow color-scheme, not CSS vars */
57
72
  color-scheme: dark;
58
73
  background: var(--bg-deep);
@@ -273,9 +288,9 @@ body.fp-mode .tree-item.fp-hit {
273
288
  .budget-label {
274
289
  display: flex;
275
290
  justify-content: space-between;
276
- font-size: 9px;
277
- color: var(--text-muted);
278
- margin-bottom: 3px;
291
+ font-size: 10px;
292
+ color: var(--text-tertiary);
293
+ margin-bottom: 2px;
279
294
  text-transform: uppercase;
280
295
  letter-spacing: 0.3px;
281
296
  }
@@ -869,7 +884,7 @@ body.home-mode .summary-bar {
869
884
  padding: 1px 5px;
870
885
  border-radius: 3px;
871
886
  background: rgba(138, 168, 163, 0.12);
872
- color: var(--scope-agent-memory);
887
+ color: var(--scope-agent-memory-ink);
873
888
  opacity: 0.85;
874
889
  }
875
890
  .tree-group-clickable {
@@ -906,8 +921,8 @@ body.light .tree-agent-item .file-name {
906
921
  }
907
922
  .tree-item.selected {
908
923
  background: var(--accent-dim);
909
- color: var(--accent);
910
- box-shadow: inset 2px 0 0 var(--accent);
924
+ color: var(--text-primary);
925
+ box-shadow: inset 3px 0 0 var(--accent);
911
926
  }
912
927
  .tree-item .tree-chevron,
913
928
  .tree-item .tree-chevron-spacer {
@@ -929,7 +944,7 @@ body.light .tree-agent-item .file-name {
929
944
  background: var(--accent-dim);
930
945
  }
931
946
  .tree-item .load-icon {
932
- font-size: 10px;
947
+ font-size: 12px;
933
948
  width: 12px;
934
949
  text-align: center;
935
950
  flex-shrink: 0;
@@ -943,11 +958,10 @@ body.light .tree-agent-item .file-name {
943
958
  white-space: nowrap;
944
959
  }
945
960
  .tree-item .file-meta {
946
- font-size: 10px;
947
- color: var(--text-muted);
961
+ font-size: 11px;
962
+ color: var(--text-tertiary);
948
963
  font-family: var(--font-mono);
949
964
  font-variant-numeric: tabular-nums;
950
- opacity: 0.7;
951
965
  flex-shrink: 0;
952
966
  }
953
967
  .tree-child .file-name {
@@ -1076,7 +1090,7 @@ body.light .tree-conditional .file-name {
1076
1090
  opacity: 0.85;
1077
1091
  }
1078
1092
  .import-link {
1079
- color: var(--accent);
1093
+ color: var(--accent-text);
1080
1094
  cursor: pointer;
1081
1095
  text-decoration: underline;
1082
1096
  text-decoration-style: dotted;
@@ -1085,7 +1099,7 @@ body.light .tree-conditional .file-name {
1085
1099
  text-decoration-style: solid;
1086
1100
  }
1087
1101
  .inline-import {
1088
- color: var(--accent);
1102
+ color: var(--accent-text);
1089
1103
  text-decoration: underline;
1090
1104
  text-decoration-style: dotted;
1091
1105
  cursor: pointer;
@@ -1162,7 +1176,7 @@ body.light .tree-conditional .file-name {
1162
1176
  /* #region BADGES */
1163
1177
  .scope-badge {
1164
1178
  display: inline-block;
1165
- font-size: 9px;
1179
+ font-size: 10px;
1166
1180
  padding: 1px 5px;
1167
1181
  border-radius: 3px;
1168
1182
  font-weight: 600;
@@ -1171,43 +1185,45 @@ body.light .tree-conditional .file-name {
1171
1185
  }
1172
1186
  .scope-user {
1173
1187
  background: rgba(196, 149, 106, 0.18);
1174
- color: var(--scope-user);
1188
+ color: var(--scope-user-ink);
1175
1189
  }
1176
1190
  .scope-project {
1177
1191
  background: rgba(122, 181, 160, 0.18);
1178
- color: var(--scope-project);
1192
+ color: var(--scope-project-ink);
1179
1193
  }
1180
1194
  .scope-local {
1181
1195
  background: rgba(138, 155, 181, 0.18);
1182
- color: var(--scope-local);
1196
+ color: var(--scope-local-ink);
1183
1197
  }
1184
1198
  .scope-rule {
1185
1199
  background: rgba(168, 155, 196, 0.18);
1186
- color: var(--scope-rule);
1200
+ color: var(--scope-rule-ink);
1187
1201
  }
1188
1202
  .scope-skill {
1189
1203
  background: rgba(122, 155, 196, 0.18);
1190
- color: var(--scope-skill);
1204
+ color: var(--scope-skill-ink);
1191
1205
  }
1192
1206
  .scope-memory {
1193
1207
  background: rgba(196, 149, 106, 0.18);
1194
- color: var(--scope-memory);
1208
+ color: var(--scope-memory-ink);
1195
1209
  }
1196
1210
  .scope-agent-memory {
1197
1211
  background: rgba(138, 168, 163, 0.18);
1198
- color: var(--scope-agent-memory);
1212
+ color: var(--scope-agent-memory-ink);
1199
1213
  }
1200
1214
  .scope-policy {
1201
1215
  background: rgba(181, 138, 138, 0.18);
1202
- color: var(--scope-policy);
1216
+ color: var(--scope-policy-ink);
1203
1217
  }
1204
1218
  .load-badge {
1205
1219
  display: inline-block;
1206
- font-size: 9px;
1207
- padding: 1px 5px;
1220
+ font-size: 10px;
1221
+ padding: 1px 6px;
1208
1222
  border-radius: 3px;
1209
1223
  border: 1px solid var(--border);
1210
- color: var(--text-muted);
1224
+ background: var(--bg-elevated);
1225
+ color: var(--text-tertiary);
1226
+ font-family: var(--font-mono);
1211
1227
  }
1212
1228
  .tag-badge {
1213
1229
  display: inline-block;
@@ -1246,11 +1262,11 @@ body.light .tree-conditional .file-name {
1246
1262
  font-family: var(--font-mono);
1247
1263
  font-size: 11px;
1248
1264
  font-weight: 600;
1249
- color: var(--accent);
1265
+ color: var(--accent-text);
1250
1266
  }
1251
1267
  .sc-label {
1252
- font-size: 9px;
1253
- color: var(--text-muted);
1268
+ font-size: 11px;
1269
+ color: var(--text-tertiary);
1254
1270
  text-transform: uppercase;
1255
1271
  }
1256
1272
  /* #endregion SUMMARY_BAR */
@@ -1427,10 +1443,15 @@ body.light .tree-conditional .file-name {
1427
1443
  .action-btn.small {
1428
1444
  padding: 2px 6px;
1429
1445
  font-size: 10px;
1446
+ min-height: 24px;
1430
1447
  display: inline-flex;
1431
1448
  align-items: center;
1432
1449
  justify-content: center;
1433
1450
  }
1451
+ .preview-actions .action-btn.small {
1452
+ min-width: 28px;
1453
+ min-height: 28px;
1454
+ }
1434
1455
  .shortcut-section {
1435
1456
  font-size: 11px;
1436
1457
  color: var(--text-muted);
@@ -1727,6 +1748,8 @@ kbd {
1727
1748
  /* #region LIGHT_THEME */
1728
1749
  body.light {
1729
1750
  color-scheme: light;
1751
+ --ink-mix: 50%;
1752
+ --glyph-mix: 75%;
1730
1753
  --bg-deep: #e8e6e3;
1731
1754
  --bg-surface: #efede9;
1732
1755
  --bg-elevated: #fbfaf9;
@@ -1835,8 +1858,8 @@ body.light {
1835
1858
  animation: spin 0.9s linear infinite;
1836
1859
  }
1837
1860
  .analysis-card {
1838
- margin-top: 10px;
1839
- padding: 10px 12px;
1861
+ margin-top: 8px;
1862
+ padding: 8px 12px;
1840
1863
  border: 1px solid var(--border);
1841
1864
  border-radius: 6px;
1842
1865
  background: var(--bg-elevated);
@@ -1877,6 +1900,22 @@ body.light {
1877
1900
  .analysis-card.sev-med .analysis-card-head::before {
1878
1901
  background: var(--warning);
1879
1902
  }
1903
+ .sev-label {
1904
+ font-family: var(--font-mono);
1905
+ font-size: 10px;
1906
+ font-weight: 600;
1907
+ text-transform: uppercase;
1908
+ letter-spacing: 0.5px;
1909
+ min-width: 4ch;
1910
+ color: var(--text-tertiary);
1911
+ flex-shrink: 0;
1912
+ }
1913
+ .analysis-card.sev-high .sev-label {
1914
+ color: var(--error-ink);
1915
+ }
1916
+ .analysis-card.sev-med .sev-label {
1917
+ color: var(--warning-ink);
1918
+ }
1880
1919
  .analysis-card-head {
1881
1920
  display: flex;
1882
1921
  align-items: center;
@@ -1916,22 +1955,22 @@ body.light {
1916
1955
  padding: 1px 6px;
1917
1956
  border-radius: 4px;
1918
1957
  background: var(--bg-hover);
1919
- color: var(--text-tertiary);
1958
+ color: var(--text-secondary);
1920
1959
  flex-shrink: 0;
1921
1960
  }
1922
1961
  .kind-badge.kind-contradiction,
1923
1962
  .kind-badge.kind-invalidate {
1924
1963
  background: var(--error-dim);
1925
- color: var(--error);
1964
+ color: var(--error-ink);
1926
1965
  }
1927
1966
  .kind-badge.kind-duplicate,
1928
1967
  .kind-badge.kind-merge {
1929
1968
  background: var(--warning-dim);
1930
- color: var(--warning);
1969
+ color: var(--warning-ink);
1931
1970
  }
1932
1971
  .kind-badge.kind-promote {
1933
1972
  background: var(--success-dim);
1934
- color: var(--success);
1973
+ color: var(--success-ink);
1935
1974
  }
1936
1975
  .analysis-card-title {
1937
1976
  flex: 1;
@@ -1946,27 +1985,22 @@ body.light {
1946
1985
  cursor: default;
1947
1986
  }
1948
1987
 
1949
- /* per-card actions — appear on hover */
1950
1988
  .card-actions {
1951
1989
  display: flex;
1952
1990
  gap: 6px;
1953
- opacity: 0;
1954
- transition: opacity 0.15s;
1955
1991
  flex-shrink: 0;
1956
1992
  }
1957
- .analysis-card:hover .card-actions {
1958
- opacity: 1;
1959
- }
1960
1993
  .card-btn {
1961
1994
  display: inline-flex;
1962
1995
  align-items: center;
1963
1996
  gap: 4px;
1997
+ min-height: 24px;
1964
1998
  background: var(--bg-surface);
1965
1999
  border: 1px solid var(--border);
1966
2000
  border-radius: 6px;
1967
- color: var(--text-tertiary);
2001
+ color: var(--text-secondary);
1968
2002
  font-family: var(--font-mono);
1969
- font-size: 10px;
2003
+ font-size: 11px;
1970
2004
  padding: 2px 8px;
1971
2005
  cursor: pointer;
1972
2006
  white-space: nowrap;
@@ -2003,28 +2037,27 @@ a.file-chip:hover {
2003
2037
  border-color: var(--accent);
2004
2038
  }
2005
2039
  .file-chip .fscope {
2006
- font-size: 8px;
2040
+ font-size: 9px;
2007
2041
  text-transform: uppercase;
2008
2042
  letter-spacing: 0.5px;
2009
- opacity: 0.85;
2010
2043
  }
2011
2044
  .file-chip .fscope-memory {
2012
- color: var(--scope-memory);
2045
+ color: var(--scope-memory-ink);
2013
2046
  }
2014
2047
  .file-chip .fscope-skill {
2015
- color: var(--scope-skill);
2048
+ color: var(--scope-skill-ink);
2016
2049
  }
2017
2050
  .file-chip .fscope-agent {
2018
- color: var(--scope-agent-memory);
2051
+ color: var(--scope-agent-memory-ink);
2019
2052
  }
2020
2053
  .file-chip .fscope-user {
2021
- color: var(--scope-user);
2054
+ color: var(--scope-user-ink);
2022
2055
  }
2023
2056
  .file-chip .fscope-project {
2024
- color: var(--scope-project);
2057
+ color: var(--scope-project-ink);
2025
2058
  }
2026
2059
  .file-chip .fscope-local {
2027
- color: var(--scope-local);
2060
+ color: var(--scope-local-ink);
2028
2061
  }
2029
2062
  .no-files {
2030
2063
  font-size: 10px;
@@ -2090,7 +2123,7 @@ a.file-chip:hover {
2090
2123
  }
2091
2124
  .run-chip-user.active {
2092
2125
  border-color: var(--scope-user);
2093
- color: var(--scope-user);
2126
+ color: var(--scope-user-ink);
2094
2127
  background: rgba(192, 128, 80, 0.12);
2095
2128
  }
2096
2129
  .sev-strip {
@@ -2103,6 +2136,7 @@ a.file-chip:hover {
2103
2136
  .sev-pill {
2104
2137
  font-family: var(--font-mono);
2105
2138
  font-size: 11px;
2139
+ min-height: 24px;
2106
2140
  padding: 2px 10px;
2107
2141
  border-radius: 20px;
2108
2142
  border: 1px solid var(--border);
@@ -2189,7 +2223,7 @@ a.file-chip:hover {
2189
2223
  pointer-events: none;
2190
2224
  }
2191
2225
  .scope-tag {
2192
- font-size: 9px;
2226
+ font-size: 10px;
2193
2227
  text-transform: uppercase;
2194
2228
  letter-spacing: 0.5px;
2195
2229
  padding: 0 6px;
@@ -2198,27 +2232,27 @@ a.file-chip:hover {
2198
2232
  }
2199
2233
  .scope-tag.t-memory {
2200
2234
  background: rgba(196, 160, 106, 0.18);
2201
- color: var(--scope-memory);
2235
+ color: var(--scope-memory-ink);
2202
2236
  }
2203
2237
  .scope-tag.t-skill {
2204
2238
  background: rgba(122, 155, 196, 0.18);
2205
- color: var(--scope-skill);
2239
+ color: var(--scope-skill-ink);
2206
2240
  }
2207
2241
  .scope-tag.t-agent {
2208
2242
  background: rgba(138, 168, 163, 0.18);
2209
- color: var(--scope-agent-memory);
2243
+ color: var(--scope-agent-memory-ink);
2210
2244
  }
2211
2245
  .scope-tag.t-claudemd {
2212
2246
  background: rgba(122, 181, 160, 0.18);
2213
- color: var(--scope-project);
2247
+ color: var(--scope-project-ink);
2214
2248
  }
2215
2249
  .scope-tag.t-user {
2216
2250
  background: rgba(192, 128, 80, 0.18);
2217
- color: var(--scope-user);
2251
+ color: var(--scope-user-ink);
2218
2252
  }
2219
2253
  .scope-tag.t-cross {
2220
2254
  background: rgba(155, 130, 190, 0.18);
2221
- color: var(--scope-rule);
2255
+ color: var(--scope-rule-ink);
2222
2256
  }
2223
2257
  .scope-meta {
2224
2258
  color: var(--text-muted);
@@ -2314,15 +2348,15 @@ a.file-chip:hover {
2314
2348
  }
2315
2349
  .tree-health.th-high {
2316
2350
  background: var(--error-dim);
2317
- color: var(--error);
2351
+ color: var(--error-ink);
2318
2352
  }
2319
2353
  .tree-health.th-med {
2320
2354
  background: var(--warning-dim);
2321
- color: var(--warning);
2355
+ color: var(--warning-ink);
2322
2356
  }
2323
2357
  .tree-health.th-low {
2324
2358
  background: var(--bg-hover);
2325
- color: var(--text-tertiary);
2359
+ color: var(--text-secondary);
2326
2360
  }
2327
2361
  .tree-health.th-clean {
2328
2362
  min-width: 0;
@@ -2427,10 +2461,10 @@ a.file-chip:hover {
2427
2461
  color: var(--text-tertiary);
2428
2462
  }
2429
2463
  .hv-num-high {
2430
- color: var(--error);
2464
+ color: var(--error-ink);
2431
2465
  }
2432
2466
  .hv-num-med {
2433
- color: var(--warning);
2467
+ color: var(--warning-ink);
2434
2468
  }
2435
2469
  .hv-num-low {
2436
2470
  color: var(--text-tertiary);
@@ -2534,6 +2568,7 @@ a.file-chip:hover {
2534
2568
  }
2535
2569
  .hv-cell.hv-low {
2536
2570
  background: var(--bg-hover);
2571
+ border-color: var(--text-muted);
2537
2572
  }
2538
2573
  .hv-cell.hv-med {
2539
2574
  background: var(--warning-dim);
@@ -2569,10 +2604,10 @@ a.file-chip:hover {
2569
2604
  background: var(--bg-deep);
2570
2605
  }
2571
2606
  .hv-cn-high {
2572
- color: var(--error);
2607
+ color: var(--error-ink);
2573
2608
  }
2574
2609
  .hv-cn-med {
2575
- color: var(--warning);
2610
+ color: var(--warning-ink);
2576
2611
  }
2577
2612
  .hv-cn-low {
2578
2613
  color: var(--text-tertiary);
@@ -2586,7 +2621,7 @@ a.file-chip:hover {
2586
2621
  }
2587
2622
  .pf-card {
2588
2623
  margin-top: 6px;
2589
- padding: 6px 10px;
2624
+ padding: 4px 10px;
2590
2625
  }
2591
2626
  .pf-card .analysis-card-head {
2592
2627
  cursor: pointer;
package/public/themes.css CHANGED
@@ -52,7 +52,7 @@ body:not([data-color-theme]):not(.light) {
52
52
  --text-primary: #f0f1f3;
53
53
  --text-secondary: #c2c4c9;
54
54
  --text-tertiary: #9a9da5;
55
- --text-muted: #7d808a;
55
+ --text-muted: #858891;
56
56
  }
57
57
  body.light:not([data-color-theme]) {
58
58
  --accent: #e86f33;
@@ -67,7 +67,7 @@ body.light:not([data-color-theme]) {
67
67
  --text-primary: #0a0a0a;
68
68
  --text-secondary: #2c2c2c;
69
69
  --text-tertiary: #565656;
70
- --text-muted: #767676;
70
+ --text-muted: #666666;
71
71
  }
72
72
 
73
73
  /* Gruvbox */
@@ -84,7 +84,7 @@ body[data-color-theme="gruvbox"]:not(.light) {
84
84
  --text-primary: #ebdbb2;
85
85
  --text-secondary: #d5c4a1;
86
86
  --text-tertiary: #bdae93;
87
- --text-muted: #928374;
87
+ --text-muted: #a69782;
88
88
  }
89
89
  body.light[data-color-theme="gruvbox"] {
90
90
  --accent: #d65d0e;
@@ -98,8 +98,8 @@ body.light[data-color-theme="gruvbox"] {
98
98
  --border: #c9bc9d;
99
99
  --text-primary: #3c3836;
100
100
  --text-secondary: #504945;
101
- --text-tertiary: #665c54;
102
- --text-muted: #7c6f64;
101
+ --text-tertiary: #5f564f;
102
+ --text-muted: #70645b;
103
103
  }
104
104
 
105
105
  /* Catppuccin */
@@ -116,7 +116,7 @@ body[data-color-theme="catppuccin"]:not(.light) {
116
116
  --text-primary: #cdd6f4;
117
117
  --text-secondary: #bac2de;
118
118
  --text-tertiary: #a6adc8;
119
- --text-muted: #7f849c;
119
+ --text-muted: #82879f;
120
120
  }
121
121
  body.light[data-color-theme="catppuccin"] {
122
122
  --accent: #8839ef;
@@ -130,8 +130,8 @@ body.light[data-color-theme="catppuccin"] {
130
130
  --border: #bcc0cc;
131
131
  --text-primary: #4c4f69;
132
132
  --text-secondary: #5c5f77;
133
- --text-tertiary: #6c6f85;
134
- --text-muted: #8c8fa1;
133
+ --text-tertiary: #545770;
134
+ --text-muted: #63667d;
135
135
  }
136
136
 
137
137
  /* Tokyo Night */
@@ -147,8 +147,8 @@ body[data-color-theme="tokyo-night"]:not(.light) {
147
147
  --border: #3b4261;
148
148
  --text-primary: #c0caf5;
149
149
  --text-secondary: #a9b1d6;
150
- --text-tertiary: #787c99;
151
- --text-muted: #565f89;
150
+ --text-tertiary: #959cbe;
151
+ --text-muted: #838aaf;
152
152
  }
153
153
  body.light[data-color-theme="tokyo-night"] {
154
154
  --accent: #2e7de9;
@@ -162,8 +162,8 @@ body.light[data-color-theme="tokyo-night"] {
162
162
  --border: #a8aecb;
163
163
  --text-primary: #343b58;
164
164
  --text-secondary: #565a6e;
165
- --text-tertiary: #6c6e75;
166
- --text-muted: #848cb5;
165
+ --text-tertiary: #505466;
166
+ --text-muted: #5d627a;
167
167
  }
168
168
 
169
169
  /* Solarized */
@@ -179,8 +179,8 @@ body[data-color-theme="solarized"]:not(.light) {
179
179
  --border: #586e75;
180
180
  --text-primary: #e2dfd0;
181
181
  --text-secondary: #b7c0bf;
182
- --text-tertiary: #93a1a1;
183
- --text-muted: #839496;
182
+ --text-tertiary: #b7beb7;
183
+ --text-muted: #9faaa7;
184
184
  }
185
185
  body.light[data-color-theme="solarized"] {
186
186
  --accent: #2280c2;
@@ -194,8 +194,8 @@ body.light[data-color-theme="solarized"] {
194
194
  --border: #cdc4ab;
195
195
  --text-primary: #073642;
196
196
  --text-secondary: #4f6369;
197
- --text-tertiary: #586e75;
198
- --text-muted: #728589;
197
+ --text-tertiary: #385861;
198
+ --text-muted: #4a666e;
199
199
  --sidebar-bg: #e6dfc7;
200
200
  --sidebar-item-bg: #f3ecd6;
201
201
  }
@@ -213,8 +213,8 @@ body[data-color-theme="dracula"]:not(.light) {
213
213
  --border: #565a75;
214
214
  --text-primary: #f8f8f2;
215
215
  --text-secondary: #d8d8d2;
216
- --text-tertiary: #b0b3c5;
217
- --text-muted: #6272a4;
216
+ --text-tertiary: #b2b5c6;
217
+ --text-muted: #9aa1bc;
218
218
  }
219
219
  body.light[data-color-theme="dracula"] {
220
220
  --accent: #7c3aed;
@@ -228,8 +228,8 @@ body.light[data-color-theme="dracula"] {
228
228
  --border: #c9c9d6;
229
229
  --text-primary: #16161e;
230
230
  --text-secondary: #34343f;
231
- --text-tertiary: #5a5a68;
232
- --text-muted: #787885;
231
+ --text-tertiary: #575765;
232
+ --text-muted: #666674;
233
233
  }
234
234
 
235
235
  /* Nord */
@@ -245,8 +245,8 @@ body[data-color-theme="nord"]:not(.light) {
245
245
  --border: #4c566a;
246
246
  --text-primary: #eceff4;
247
247
  --text-secondary: #d8dee9;
248
- --text-tertiary: #aeb6c5;
249
- --text-muted: #7b88a1;
248
+ --text-tertiary: #bec4d1;
249
+ --text-muted: #a7b0c1;
250
250
  }
251
251
  body.light[data-color-theme="nord"] {
252
252
  --accent: #5e81ac;
@@ -261,7 +261,7 @@ body.light[data-color-theme="nord"] {
261
261
  --text-primary: #2e3440;
262
262
  --text-secondary: #3b4252;
263
263
  --text-tertiary: #4c566a;
264
- --text-muted: #6b7589;
264
+ --text-muted: #5e687c;
265
265
  }
266
266
 
267
267
  /* Rosé Pine */
@@ -277,8 +277,8 @@ body[data-color-theme="rose-pine"]:not(.light) {
277
277
  --border: #403d52;
278
278
  --text-primary: #e0def4;
279
279
  --text-secondary: #c5c2dd;
280
- --text-tertiary: #908caa;
281
- --text-muted: #6e6a86;
280
+ --text-tertiary: #a09db9;
281
+ --text-muted: #8e8ba7;
282
282
  }
283
283
  body.light[data-color-theme="rose-pine"] {
284
284
  --accent: #907aa9;
@@ -292,8 +292,8 @@ body.light[data-color-theme="rose-pine"] {
292
292
  --border: #cecacd;
293
293
  --text-primary: #575279;
294
294
  --text-secondary: #635e87;
295
- --text-tertiary: #6e6a88;
296
- --text-muted: #857f96;
295
+ --text-tertiary: #5a557b;
296
+ --text-muted: #6a6485;
297
297
  }
298
298
 
299
299
  /* Everforest */
@@ -309,8 +309,8 @@ body[data-color-theme="everforest"]:not(.light) {
309
309
  --border: #475258;
310
310
  --text-primary: #d3c6aa;
311
311
  --text-secondary: #bfb6a3;
312
- --text-tertiary: #9da9a0;
313
- --text-muted: #859289;
312
+ --text-tertiary: #c3bea7;
313
+ --text-muted: #a8ab9a;
314
314
  }
315
315
  body.light[data-color-theme="everforest"] {
316
316
  --accent: #6f8352;
@@ -324,8 +324,8 @@ body.light[data-color-theme="everforest"] {
324
324
  --border: #d3d2bf;
325
325
  --text-primary: #3d484e;
326
326
  --text-secondary: #55636b;
327
- --text-tertiary: #66705f;
328
- --text-muted: #7a8577;
327
+ --text-tertiary: #515b56;
328
+ --text-muted: #606a62;
329
329
  }
330
330
 
331
331
  /* Kanagawa */
@@ -342,7 +342,7 @@ body[data-color-theme="kanagawa"]:not(.light) {
342
342
  --text-primary: #dcd7ba;
343
343
  --text-secondary: #c8c093;
344
344
  --text-tertiary: #a6a69c;
345
- --text-muted: #727169;
345
+ --text-muted: #94948a;
346
346
  }
347
347
  body.light[data-color-theme="kanagawa"] {
348
348
  --accent: #4d699b;
@@ -356,8 +356,8 @@ body.light[data-color-theme="kanagawa"] {
356
356
  --border: #d2ccb6;
357
357
  --text-primary: #545464;
358
358
  --text-secondary: #66667a;
359
- --text-tertiary: #716e61;
360
- --text-muted: #8a8775;
359
+ --text-tertiary: #565564;
360
+ --text-muted: #666469;
361
361
  }
362
362
 
363
363
  /* One Dark */
@@ -373,8 +373,8 @@ body[data-color-theme="one-dark"]:not(.light) {
373
373
  --border: #4b5263;
374
374
  --text-primary: #d7dae0;
375
375
  --text-secondary: #abb2bf;
376
- --text-tertiary: #8b919e;
377
- --text-muted: #5c6370;
376
+ --text-tertiary: #acb0ba;
377
+ --text-muted: #999ea8;
378
378
  }
379
379
  body.light[data-color-theme="one-dark"] {
380
380
  --accent: #4078f2;
@@ -388,8 +388,8 @@ body.light[data-color-theme="one-dark"] {
388
388
  --border: #d4d4d6;
389
389
  --text-primary: #383a42;
390
390
  --text-secondary: #50525a;
391
- --text-tertiary: #696c77;
392
- --text-muted: #a0a1a7;
391
+ --text-tertiary: #575963;
392
+ --text-muted: #666871;
393
393
  }
394
394
 
395
395
  /* Night Owl */
@@ -405,8 +405,8 @@ body[data-color-theme="night-owl"]:not(.light) {
405
405
  --border: #5f7e97;
406
406
  --text-primary: #d6deeb;
407
407
  --text-secondary: #b8c5d6;
408
- --text-tertiary: #8ba3b8;
409
- --text-muted: #637777;
408
+ --text-tertiary: #9bb0c3;
409
+ --text-muted: #899eab;
410
410
  }
411
411
  body.light[data-color-theme="night-owl"] {
412
412
  --accent: #0c969b;
@@ -420,8 +420,8 @@ body.light[data-color-theme="night-owl"] {
420
420
  --border: #d0d0d0;
421
421
  --text-primary: #403f53;
422
422
  --text-secondary: #545167;
423
- --text-tertiary: #6f6e85;
424
- --text-muted: #989fb1;
423
+ --text-tertiary: #5a596f;
424
+ --text-muted: #68687e;
425
425
  }
426
426
 
427
427
  /* Monokai Pro */
@@ -437,8 +437,8 @@ body[data-color-theme="monokai"]:not(.light) {
437
437
  --border: #5b595c;
438
438
  --text-primary: #fcfcfa;
439
439
  --text-secondary: #d9d8d6;
440
- --text-tertiary: #939293;
441
- --text-muted: #727072;
440
+ --text-tertiary: #bfbfbe;
441
+ --text-muted: #ababab;
442
442
  }
443
443
  body.light[data-color-theme="monokai"] {
444
444
  --accent: #c08a00;
@@ -452,8 +452,8 @@ body.light[data-color-theme="monokai"] {
452
452
  --border: #cdc8c5;
453
453
  --text-primary: #2c292d;
454
454
  --text-secondary: #46434a;
455
- --text-tertiary: #6b686d;
456
- --text-muted: #8d8a8d;
455
+ --text-tertiary: #59565b;
456
+ --text-muted: #68656a;
457
457
  }
458
458
 
459
459
  /* GitHub */
@@ -469,8 +469,8 @@ body[data-color-theme="github"]:not(.light) {
469
469
  --border: #444d56;
470
470
  --text-primary: #e1e4e8;
471
471
  --text-secondary: #d1d5da;
472
- --text-tertiary: #959da5;
473
- --text-muted: #6a737d;
472
+ --text-tertiary: #a6adb4;
473
+ --text-muted: #929aa2;
474
474
  }
475
475
  body.light[data-color-theme="github"] {
476
476
  --accent: #0366d6;
@@ -484,8 +484,8 @@ body.light[data-color-theme="github"] {
484
484
  --border: #d1d5da;
485
485
  --text-primary: #24292e;
486
486
  --text-secondary: #444d56;
487
- --text-tertiary: #586069;
488
- --text-muted: #6a737d;
487
+ --text-tertiary: #575f68;
488
+ --text-muted: #666e78;
489
489
  }
490
490
 
491
491
  /* Ayu */
@@ -501,8 +501,8 @@ body[data-color-theme="ayu"]:not(.light) {
501
501
  --border: #2d3343;
502
502
  --text-primary: #bfbdb6;
503
503
  --text-secondary: #a8a6a0;
504
- --text-tertiary: #8a9199;
505
- --text-muted: #6c7380;
504
+ --text-tertiary: #8d949b;
505
+ --text-muted: #7c838d;
506
506
  }
507
507
  body.light[data-color-theme="ayu"] {
508
508
  --accent: #c97612;
@@ -516,8 +516,8 @@ body.light[data-color-theme="ayu"] {
516
516
  --border: #d8dde2;
517
517
  --text-primary: #3d4149;
518
518
  --text-secondary: #5c6166;
519
- --text-tertiary: #787b80;
520
- --text-muted: #8a9199;
519
+ --text-tertiary: #5a5e64;
520
+ --text-muted: #686d74;
521
521
  }
522
522
 
523
523
  /* Vitesse */
@@ -533,8 +533,8 @@ body[data-color-theme="vitesse"]:not(.light) {
533
533
  --border: #333333;
534
534
  --text-primary: #dbd7ca;
535
535
  --text-secondary: #bfbaaa;
536
- --text-tertiary: #8f8f85;
537
- --text-muted: #758575;
536
+ --text-tertiary: #99998e;
537
+ --text-muted: #7e8a7b;
538
538
  }
539
539
  body.light[data-color-theme="vitesse"] {
540
540
  --accent: #1c6b48;
@@ -548,8 +548,8 @@ body.light[data-color-theme="vitesse"] {
548
548
  --border: #d6d6d6;
549
549
  --text-primary: #393a34;
550
550
  --text-secondary: #4e4f47;
551
- --text-tertiary: #6b6d63;
552
- --text-muted: #a0ada0;
551
+ --text-tertiary: #5d5e56;
552
+ --text-muted: #6a6d64;
553
553
  }
554
554
 
555
555
  /* Synthwave '84 */
@@ -566,7 +566,7 @@ body[data-color-theme="synthwave"]:not(.light) {
566
566
  --text-primary: #ffffff;
567
567
  --text-secondary: #d8d6e8;
568
568
  --text-tertiary: #b6b1d8;
569
- --text-muted: #848bbd;
569
+ --text-muted: #8f94c3;
570
570
  }
571
571
  body.light[data-color-theme="synthwave"] {
572
572
  --accent: #c936a6;
@@ -580,6 +580,6 @@ body.light[data-color-theme="synthwave"] {
580
580
  --border: #c5bcdd;
581
581
  --text-primary: #241b2f;
582
582
  --text-secondary: #3d3455;
583
- --text-tertiary: #5c5380;
584
- --text-muted: #7d76a3;
583
+ --text-tertiary: #59507c;
584
+ --text-muted: #675f8b;
585
585
  }
@@ -1,4 +1,4 @@
1
- // claude-hub-sdk 1.5.0 (sha256 1505d3d5f8ba). Copied by npm run sdk:sync in claude-code-hub. Do not edit.
1
+ // claude-hub-sdk 1.8.0 (sha256 9a67dd2aa534). Copied by npm run sdk:sync in claude-code-hub. Do not edit.
2
2
  // Claude Code Hub SDK: the one rule that names a key press, shared by the hub page, client.js and
3
3
  // stub.js. The hub serves it to its page as /sdk/keys.js; mount() and sdk:sync put it in front of
4
4
  // client.js and stub.js, so an app still loads one file.
@@ -33,7 +33,21 @@ var ClaudeHubKeys = (() => {
33
33
  return combo.split('+').map((k) => mods[k] ?? named[k] ?? (k.length === 1 ? k.toUpperCase() : k));
34
34
  }
35
35
 
36
- return { comboOf, keyParts };
36
+ // A host claim (protocol §5.2) names a press by its physical code, or by its character when the layout
37
+ // moves the key: {ctrlKey?, altKey?, shiftKey?, metaKey?, code, key: 'Unidentified'} or {…, key, code: ''}.
38
+ // A press matches a claim when one of its two signatures is the claim's first. AltGr types a
39
+ // character, so its press matches by character only. cck's frames use the same format.
40
+ function keySigs(e) {
41
+ const mods = [e.ctrlKey && 'ctrl', e.altKey && 'alt', e.shiftKey && 'shift', e.metaKey && 'meta']
42
+ .filter(Boolean)
43
+ .join('+');
44
+ return [
45
+ e.code && !e.getModifierState?.('AltGraph') && `${mods}|c:${e.code}`,
46
+ typeof e.key === 'string' && e.key !== 'Unidentified' && `${mods}|k:${e.key.toLowerCase()}`,
47
+ ].filter(Boolean);
48
+ }
49
+
50
+ return { comboOf, keyParts, keySigs };
37
51
  })();
38
52
 
39
53
  if (typeof module === 'object' && module.exports) module.exports = ClaudeHubKeys;
@@ -60,6 +74,8 @@ if (typeof module === 'object' && module.exports) module.exports = ClaudeHubKeys
60
74
  status: 'standalone',
61
75
  themes: [],
62
76
  inHub: false,
77
+ settings: null,
78
+ onSettings: () => off,
63
79
  onStatus: () => off,
64
80
  onActive: () => off,
65
81
  onThemes: () => off,
@@ -67,9 +83,10 @@ if (typeof module === 'object' && module.exports) module.exports = ClaudeHubKeys
67
83
  bindTheme: () => off,
68
84
  handle() {},
69
85
  publish() {},
70
- invoke(action, params = {}) {
86
+ invoke(action, params = {}, opts = {}) {
71
87
  const target = standaloneFn(action)?.(params);
72
88
  if (!target) return Promise.resolve({ ok: false, reason: 'unhandled' });
89
+ if (opts.open === 'pane') return Promise.resolve({ ok: true, handledBy: 'standalone', url: target });
73
90
  win.open(target, '_blank', 'noopener');
74
91
  return Promise.resolve({ ok: true, handledBy: 'standalone' });
75
92
  },