claude-code-marketplace 0.7.1 → 0.9.0

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-marketplace",
3
- "version": "0.7.1",
3
+ "version": "0.9.0",
4
4
  "description": "Web UI for browsing and managing Claude Code marketplace plugins",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/app.js CHANGED
@@ -126,6 +126,8 @@ document.addEventListener('DOMContentLoaded', () => {
126
126
 
127
127
  document.getElementById('refreshBtn').addEventListener('click', refresh);
128
128
  document.getElementById('themeBtn').addEventListener('click', toggleTheme);
129
+ document.getElementById('themePickerBtn').addEventListener('click', toggleThemeMenu);
130
+ buildThemeMenu();
129
131
 
130
132
  document.getElementById('projectBtn').addEventListener('click', changeProject);
131
133
  document.getElementById('addMarketplaceBtn').addEventListener('click', openAddMarketplace);
@@ -152,6 +154,9 @@ document.addEventListener('DOMContentLoaded', () => {
152
154
  } else {
153
155
  document.body.classList.add('light');
154
156
  }
157
+ const savedColorTheme = localStorage.getItem('color-theme');
158
+ if (savedColorTheme) document.body.dataset.colorTheme = savedColorTheme;
159
+ syncColorThemeMenu(savedColorTheme || 'ember');
155
160
  syncHljsTheme();
156
161
  updateThemeColor(savedTheme !== 'dark');
157
162
 
@@ -195,6 +200,71 @@ function toggleTheme() {
195
200
  updateThemeColor(!isLight);
196
201
  }
197
202
 
203
+ const COLOR_THEMES = [
204
+ ['ember', 'Ember'],
205
+ ['gruvbox', 'Gruvbox'],
206
+ ['catppuccin', 'Catppuccin'],
207
+ ['tokyo-night', 'Tokyo Night'],
208
+ ['solarized', 'Solarized'],
209
+ ['dracula', 'Dracula'],
210
+ ['nord', 'Nord'],
211
+ ['rose-pine', 'Rosé Pine'],
212
+ ['everforest', 'Everforest'],
213
+ ['kanagawa', 'Kanagawa'],
214
+ ['one-dark', 'One Dark'],
215
+ ['night-owl', 'Night Owl'],
216
+ ['monokai', 'Monokai Pro'],
217
+ ['github', 'GitHub'],
218
+ ['ayu', 'Ayu'],
219
+ ['vitesse', 'Vitesse'],
220
+ ['synthwave', "Synthwave '84"],
221
+ ];
222
+
223
+ // 'ember' (the :root default) has no override block — selecting it clears the attribute.
224
+ function setColorTheme(id) {
225
+ if (!id || id === 'ember') {
226
+ delete document.body.dataset.colorTheme;
227
+ localStorage.removeItem('color-theme');
228
+ } else {
229
+ document.body.dataset.colorTheme = id;
230
+ localStorage.setItem('color-theme', id);
231
+ }
232
+ syncColorThemeMenu(id);
233
+ }
234
+
235
+ function buildThemeMenu() {
236
+ const menu = document.getElementById('themeMenu');
237
+ menu.innerHTML = COLOR_THEMES.map(
238
+ ([id, label]) =>
239
+ `<button type="button" class="theme-menu-item" data-theme-id="${id}">
240
+ <span class="theme-swatch theme-swatch-${id}"><i class="sw-bg"></i><i class="sw-accent"></i><i class="sw-ink"></i></span>${label}
241
+ </button>`,
242
+ ).join('');
243
+ // stopPropagation keeps the click from bubbling to the picker button, which would re-toggle the menu
244
+ menu.addEventListener('click', (e) => {
245
+ const item = e.target.closest('.theme-menu-item');
246
+ if (!item) return;
247
+ e.stopPropagation();
248
+ setColorTheme(item.dataset.themeId);
249
+ menu.classList.remove('open');
250
+ });
251
+ }
252
+
253
+ function toggleThemeMenu(e) {
254
+ e?.stopPropagation();
255
+ const menu = document.getElementById('themeMenu');
256
+ const open = menu.classList.toggle('open');
257
+ if (open) {
258
+ document.addEventListener('click', () => menu.classList.remove('open'), { once: true });
259
+ }
260
+ }
261
+
262
+ function syncColorThemeMenu(id) {
263
+ document.querySelectorAll('.theme-menu-item').forEach((el) => {
264
+ el.classList.toggle('on', el.dataset.themeId === (id || 'ember'));
265
+ });
266
+ }
267
+
198
268
  async function loadProject() {
199
269
  try {
200
270
  const res = await fetch('/api/project');
@@ -1286,6 +1356,11 @@ function setFocusedRow(index, rows) {
1286
1356
  _focusedRowEl = row;
1287
1357
  }
1288
1358
 
1359
+ function getFocusedPluginId() {
1360
+ if (_focusedRowEl?.dataset.rowType === 'plugin') return _focusedRowEl.dataset.rowId;
1361
+ return null;
1362
+ }
1363
+
1289
1364
  function getFocusedIndex(rows) {
1290
1365
  if (!focusedRowId) return -1;
1291
1366
  return rows.findIndex((r) => r.dataset.rowId === focusedRowId);
@@ -1338,7 +1413,13 @@ function handleKeydown(e) {
1338
1413
 
1339
1414
  if (matchKey(e, 'e')) {
1340
1415
  e.preventDefault();
1341
- toggleExpandAll();
1416
+ const id = selectedPluginId || getFocusedPluginId();
1417
+ if (id) {
1418
+ openFolderInEditor({ pluginId: id });
1419
+ toast('Opening in editor…', 'info');
1420
+ } else {
1421
+ toast('Select a plugin first', 'info');
1422
+ }
1342
1423
  return;
1343
1424
  }
1344
1425
 
@@ -1523,21 +1604,35 @@ function hubNavigate(app, url) {
1523
1604
 
1524
1605
  (function initHubTheme() {
1525
1606
  const getTheme = () => (document.body.classList.contains('light') ? 'light' : 'dark');
1607
+ const getColorTheme = () => document.body.dataset.colorTheme || 'ember';
1526
1608
  const hubOrigin = () => (window.__HUB__?.url ? new URL(window.__HUB__.url).origin : null);
1609
+ // lastTheme/lastColorTheme are updated synchronously when applying a hub
1610
+ // message, so the (async) observer sees no diff and doesn't echo it back.
1527
1611
  let lastTheme = getTheme();
1612
+ let lastColorTheme = getColorTheme();
1528
1613
  window.addEventListener('message', (e) => {
1529
1614
  if (e.source !== window.parent || e.origin !== hubOrigin()) return;
1530
1615
  if (e.data?.type !== 'hub:theme') return;
1531
- if (getTheme() === e.data.theme) return;
1532
- window.toggleTheme();
1533
- lastTheme = getTheme();
1616
+ if (typeof e.data.colorTheme === 'string' && e.data.colorTheme !== getColorTheme()) {
1617
+ setColorTheme(e.data.colorTheme);
1618
+ lastColorTheme = getColorTheme();
1619
+ }
1620
+ if (getTheme() !== e.data.theme) {
1621
+ window.toggleTheme();
1622
+ lastTheme = getTheme();
1623
+ }
1534
1624
  });
1535
1625
  new MutationObserver(() => {
1536
1626
  const t = getTheme();
1537
- if (t === lastTheme) return;
1627
+ const ct = getColorTheme();
1628
+ if (t === lastTheme && ct === lastColorTheme) return;
1538
1629
  lastTheme = t;
1630
+ lastColorTheme = ct;
1539
1631
  const origin = hubOrigin();
1540
- if (origin) window.parent.postMessage({ type: 'hub:theme', theme: t }, origin);
1541
- }).observe(document.body, { attributes: true, attributeFilter: ['class'] });
1632
+ if (origin) window.parent.postMessage({ type: 'hub:theme', theme: t, colorTheme: ct }, origin);
1633
+ }).observe(document.body, {
1634
+ attributes: true,
1635
+ attributeFilter: ['class', 'data-color-theme'],
1636
+ });
1542
1637
  })();
1543
1638
  // #endregion HUB_INTEGRATION
package/public/index.html CHANGED
@@ -5,6 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Claude Code Marketplace</title>
7
7
  <link rel="stylesheet" href="style.css">
8
+ <link rel="stylesheet" href="themes.css">
8
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css" id="hljsDark">
9
10
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github.min.css" id="hljsLight" disabled>
10
11
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
@@ -46,6 +47,10 @@
46
47
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>
47
48
  Refresh
48
49
  </button>
50
+ <span class="topbar-btn theme-picker" id="themePickerBtn" title="Color theme">
51
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="13.5" cy="6.5" r=".5" fill="currentColor"/><circle cx="17.5" cy="10.5" r=".5" fill="currentColor"/><circle cx="8.5" cy="7.5" r=".5" fill="currentColor"/><circle cx="6.5" cy="12.5" r=".5" fill="currentColor"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z"/></svg>
52
+ <div class="theme-menu" id="themeMenu"></div>
53
+ </span>
49
54
  <button class="topbar-btn" id="themeBtn" title="Toggle theme">
50
55
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
51
56
  </button>
@@ -133,7 +138,7 @@
133
138
  <tr><td><kbd>?</kbd></td><td>Show keyboard shortcuts</td></tr>
134
139
  <tr><td><kbd>/</kbd></td><td>Focus search</td></tr>
135
140
  <tr><td><kbd>S</kbd></td><td>Focus scope filter</td></tr>
136
- <tr><td><kbd>E</kbd></td><td>Expand / Collapse all</td></tr>
141
+ <tr><td><kbd>E</kbd></td><td>Open plugin in editor</td></tr>
137
142
  <tr><td><kbd>R</kbd></td><td>Refresh data</td></tr>
138
143
  <tr><td><kbd>T</kbd></td><td>Toggle theme</td></tr>
139
144
  <tr><td><kbd>U</kbd></td><td>Open user CLAUDE.md</td></tr>
package/public/style.css CHANGED
@@ -259,6 +259,75 @@ body {
259
259
  .topbar-btn.loading svg {
260
260
  animation: spin 1s linear infinite;
261
261
  }
262
+ /* theme picker: palette icon button + custom popover with per-theme swatches
263
+ (swatch colors are generated into themes.css) */
264
+ .theme-picker {
265
+ position: relative;
266
+ }
267
+ .theme-menu {
268
+ display: none;
269
+ position: absolute;
270
+ top: calc(100% + 6px);
271
+ right: 0;
272
+ z-index: 100;
273
+ min-width: 170px;
274
+ max-height: 70vh;
275
+ overflow-y: auto;
276
+ padding: 4px;
277
+ background: var(--bg-elevated);
278
+ border: 1px solid var(--border);
279
+ border-radius: 8px;
280
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
281
+ }
282
+ .theme-menu.open {
283
+ display: block;
284
+ }
285
+ .theme-menu-item {
286
+ display: flex;
287
+ align-items: center;
288
+ gap: 8px;
289
+ width: 100%;
290
+ padding: 6px 8px;
291
+ border: 0;
292
+ border-radius: 6px;
293
+ background: none;
294
+ color: var(--text-secondary);
295
+ font-family: var(--mono);
296
+ font-size: 11px;
297
+ text-transform: uppercase;
298
+ letter-spacing: 0.03em;
299
+ text-align: left;
300
+ cursor: pointer;
301
+ }
302
+ .theme-menu-item:hover {
303
+ background: var(--bg-hover);
304
+ color: var(--text-primary);
305
+ }
306
+ .theme-menu-item.on {
307
+ color: var(--accent-text);
308
+ }
309
+ /* 3-dot palette preview: surface, accent, ink (colors from themes.css vars) */
310
+ .theme-swatch {
311
+ display: inline-flex;
312
+ align-items: center;
313
+ gap: 3px;
314
+ flex: 0 0 auto;
315
+ }
316
+ .theme-swatch i {
317
+ width: 9px;
318
+ height: 9px;
319
+ border-radius: 50%;
320
+ border: 1px solid var(--sw-border);
321
+ }
322
+ .theme-swatch .sw-bg {
323
+ background: var(--sw-bg);
324
+ }
325
+ .theme-swatch .sw-accent {
326
+ background: var(--sw-accent);
327
+ }
328
+ .theme-swatch .sw-ink {
329
+ background: var(--sw-ink);
330
+ }
262
331
  @keyframes spin {
263
332
  from {
264
333
  transform: rotate(0deg);
package/public/sw.js CHANGED
@@ -1,4 +1,4 @@
1
- const CACHE_NAME = 'cc-marketplace-v1';
1
+ const CACHE_NAME = 'cc-marketplace-v2';
2
2
  const PRECACHE = ['/', '/style.css', '/app.js'];
3
3
 
4
4
  self.addEventListener('install', (e) => {
@@ -0,0 +1,551 @@
1
+ /* GENERATED by scripts/generate-themes.mjs — do not edit by hand.
2
+ Color themes override only the shared design-token variables; semantic
3
+ colors (success/warning/error/scope/...) stay constant across themes. */
4
+
5
+ /* picker swatches */
6
+ .theme-swatch-ember { --sw-bg: #16181c; --sw-accent: #e86f33; --sw-ink: #f0f1f3; --sw-border: #363840; }
7
+ body.light .theme-swatch-ember { --sw-bg: #efede9; --sw-accent: #e86f33; --sw-ink: #0a0a0a; --sw-border: #cfcbc4; }
8
+ .theme-swatch-gruvbox { --sw-bg: #282828; --sw-accent: #fe8019; --sw-ink: #ebdbb2; --sw-border: #504945; }
9
+ body.light .theme-swatch-gruvbox { --sw-bg: #f3eee0; --sw-accent: #d65d0e; --sw-ink: #3c3836; --sw-border: #c9bc9d; }
10
+ .theme-swatch-catppuccin { --sw-bg: #181825; --sw-accent: #cba6f7; --sw-ink: #cdd6f4; --sw-border: #45475a; }
11
+ body.light .theme-swatch-catppuccin { --sw-bg: #eff1f5; --sw-accent: #8839ef; --sw-ink: #4c4f69; --sw-border: #bcc0cc; }
12
+ .theme-swatch-tokyo-night { --sw-bg: #1a1b26; --sw-accent: #7aa2f7; --sw-ink: #c0caf5; --sw-border: #3b4261; }
13
+ body.light .theme-swatch-tokyo-night { --sw-bg: #e9eaf0; --sw-accent: #2e7de9; --sw-ink: #343b58; --sw-border: #a8aecb; }
14
+ .theme-swatch-solarized { --sw-bg: #073642; --sw-accent: #cb4b16; --sw-ink: #93a1a1; --sw-border: #586e75; }
15
+ body.light .theme-swatch-solarized { --sw-bg: #f5efdc; --sw-accent: #cb4b16; --sw-ink: #073642; --sw-border: #d3cbb7; }
16
+ .theme-swatch-dracula { --sw-bg: #282a36; --sw-accent: #bd93f9; --sw-ink: #f8f8f2; --sw-border: #565a75; }
17
+ body.light .theme-swatch-dracula { --sw-bg: #f0f0f5; --sw-accent: #7c3aed; --sw-ink: #16161e; --sw-border: #c9c9d6; }
18
+ .theme-swatch-nord { --sw-bg: #2e3440; --sw-accent: #88c0d0; --sw-ink: #eceff4; --sw-border: #4c566a; }
19
+ body.light .theme-swatch-nord { --sw-bg: #eceff4; --sw-accent: #5e81ac; --sw-ink: #2e3440; --sw-border: #c2c9d6; }
20
+ .theme-swatch-rose-pine { --sw-bg: #1f1d2e; --sw-accent: #c4a7e7; --sw-ink: #e0def4; --sw-border: #403d52; }
21
+ body.light .theme-swatch-rose-pine { --sw-bg: #faf4ed; --sw-accent: #907aa9; --sw-ink: #575279; --sw-border: #dfdad9; }
22
+ .theme-swatch-everforest { --sw-bg: #2d353b; --sw-accent: #a7c080; --sw-ink: #d3c6aa; --sw-border: #475258; }
23
+ body.light .theme-swatch-everforest { --sw-bg: #fdf6e3; --sw-accent: #6f8352; --sw-ink: #4d5960; --sw-border: #d8d3ba; }
24
+ .theme-swatch-kanagawa { --sw-bg: #1f1f28; --sw-accent: #7e9cd8; --sw-ink: #dcd7ba; --sw-border: #54546d; }
25
+ body.light .theme-swatch-kanagawa { --sw-bg: #f2ecbc; --sw-accent: #4d699b; --sw-ink: #545464; --sw-border: #c7bf94; }
26
+ .theme-swatch-one-dark { --sw-bg: #282c34; --sw-accent: #61afef; --sw-ink: #d7dae0; --sw-border: #4b5263; }
27
+ body.light .theme-swatch-one-dark { --sw-bg: #fafafa; --sw-accent: #4078f2; --sw-ink: #383a42; --sw-border: #d4d4d6; }
28
+ .theme-swatch-night-owl { --sw-bg: #0b2942; --sw-accent: #82aaff; --sw-ink: #d6deeb; --sw-border: #5f7e97; }
29
+ body.light .theme-swatch-night-owl { --sw-bg: #f6f6f6; --sw-accent: #0c969b; --sw-ink: #403f53; --sw-border: #d0d0d0; }
30
+ .theme-swatch-monokai { --sw-bg: #2d2a2e; --sw-accent: #ffd866; --sw-ink: #fcfcfa; --sw-border: #5b595c; }
31
+ body.light .theme-swatch-monokai { --sw-bg: #f1efed; --sw-accent: #c08a00; --sw-ink: #2c292d; --sw-border: #cdc8c5; }
32
+ .theme-swatch-github { --sw-bg: #24292e; --sw-accent: #58a6ff; --sw-ink: #e1e4e8; --sw-border: #444d56; }
33
+ body.light .theme-swatch-github { --sw-bg: #f6f8fa; --sw-accent: #0366d6; --sw-ink: #24292e; --sw-border: #d1d5da; }
34
+ .theme-swatch-ayu { --sw-bg: #10141c; --sw-accent: #e6b450; --sw-ink: #bfbdb6; --sw-border: #2d3343; }
35
+ body.light .theme-swatch-ayu { --sw-bg: #f8f9fa; --sw-accent: #f2ae49; --sw-ink: #3d4149; --sw-border: #d8dde2; }
36
+ .theme-swatch-vitesse { --sw-bg: #181818; --sw-accent: #4d9375; --sw-ink: #dbd7ca; --sw-border: #333333; }
37
+ body.light .theme-swatch-vitesse { --sw-bg: #f7f7f7; --sw-accent: #1c6b48; --sw-ink: #393a34; --sw-border: #d6d6d6; }
38
+ .theme-swatch-synthwave { --sw-bg: #262335; --sw-accent: #ff7edb; --sw-ink: #ffffff; --sw-border: #495495; }
39
+ body.light .theme-swatch-synthwave { --sw-bg: #efecf6; --sw-accent: #c936a6; --sw-ink: #241b2f; --sw-border: #c5bcdd; }
40
+
41
+ /* Gruvbox */
42
+ body[data-color-theme="gruvbox"]:not(.light) {
43
+ --accent: #fe8019;
44
+ --accent-text: #f9b27c;
45
+ --accent-dim: rgba(254,128,25,0.25);
46
+ --accent-glow: rgba(254, 128, 25, 0.55);
47
+ --bg-deep: #1d2021;
48
+ --bg-surface: #282828;
49
+ --bg-elevated: #32302f;
50
+ --bg-hover: #3c3836;
51
+ --border: #504945;
52
+ --text-primary: #ebdbb2;
53
+ --text-secondary: #d5c4a1;
54
+ --text-tertiary: #bdae93;
55
+ --text-muted: #928374;
56
+ }
57
+ body.light[data-color-theme="gruvbox"] {
58
+ --accent: #d65d0e;
59
+ --accent-text: #af3a03;
60
+ --accent-dim: rgba(214,93,14,0.18);
61
+ --accent-glow: rgba(214, 93, 14, 0.5);
62
+ --bg-deep: #ede7d5;
63
+ --bg-surface: #f3eee0;
64
+ --bg-elevated: #faf7ec;
65
+ --bg-hover: #e4dcc6;
66
+ --border: #c9bc9d;
67
+ --text-primary: #3c3836;
68
+ --text-secondary: #504945;
69
+ --text-tertiary: #665c54;
70
+ --text-muted: #7c6f64;
71
+ }
72
+
73
+ /* Catppuccin */
74
+ body[data-color-theme="catppuccin"]:not(.light) {
75
+ --accent: #cba6f7;
76
+ --accent-text: #d8c2fa;
77
+ --accent-dim: rgba(203,166,247,0.25);
78
+ --accent-glow: rgba(203, 166, 247, 0.55);
79
+ --bg-deep: #11111b;
80
+ --bg-surface: #181825;
81
+ --bg-elevated: #1e1e2e;
82
+ --bg-hover: #313244;
83
+ --border: #45475a;
84
+ --text-primary: #cdd6f4;
85
+ --text-secondary: #bac2de;
86
+ --text-tertiary: #a6adc8;
87
+ --text-muted: #7f849c;
88
+ }
89
+ body.light[data-color-theme="catppuccin"] {
90
+ --accent: #8839ef;
91
+ --accent-text: #6f2dbd;
92
+ --accent-dim: rgba(136,57,239,0.18);
93
+ --accent-glow: rgba(136, 57, 239, 0.5);
94
+ --bg-deep: #e6e9ef;
95
+ --bg-surface: #eff1f5;
96
+ --bg-elevated: #ffffff;
97
+ --bg-hover: #ccd0da;
98
+ --border: #bcc0cc;
99
+ --text-primary: #4c4f69;
100
+ --text-secondary: #5c5f77;
101
+ --text-tertiary: #6c6f85;
102
+ --text-muted: #8c8fa1;
103
+ }
104
+
105
+ /* Tokyo Night */
106
+ body[data-color-theme="tokyo-night"]:not(.light) {
107
+ --accent: #7aa2f7;
108
+ --accent-text: #9ab8ff;
109
+ --accent-dim: rgba(122,162,247,0.25);
110
+ --accent-glow: rgba(122, 162, 247, 0.55);
111
+ --bg-deep: #16161e;
112
+ --bg-surface: #1a1b26;
113
+ --bg-elevated: #1f2335;
114
+ --bg-hover: #292e42;
115
+ --border: #3b4261;
116
+ --text-primary: #c0caf5;
117
+ --text-secondary: #a9b1d6;
118
+ --text-tertiary: #787c99;
119
+ --text-muted: #565f89;
120
+ }
121
+ body.light[data-color-theme="tokyo-night"] {
122
+ --accent: #2e7de9;
123
+ --accent-text: #1659c7;
124
+ --accent-dim: rgba(46,125,233,0.18);
125
+ --accent-glow: rgba(46, 125, 233, 0.5);
126
+ --bg-deep: #e1e2e7;
127
+ --bg-surface: #e9eaf0;
128
+ --bg-elevated: #f7f8fc;
129
+ --bg-hover: #d0d5e3;
130
+ --border: #a8aecb;
131
+ --text-primary: #343b58;
132
+ --text-secondary: #565a6e;
133
+ --text-tertiary: #6c6e75;
134
+ --text-muted: #848cb5;
135
+ }
136
+
137
+ /* Solarized */
138
+ body[data-color-theme="solarized"]:not(.light) {
139
+ --accent: #cb4b16;
140
+ --accent-text: #e9663a;
141
+ --accent-dim: rgba(203,75,22,0.25);
142
+ --accent-glow: rgba(203, 75, 22, 0.55);
143
+ --bg-deep: #002b36;
144
+ --bg-surface: #073642;
145
+ --bg-elevated: #0a4250;
146
+ --bg-hover: #11505f;
147
+ --border: #586e75;
148
+ --text-primary: #93a1a1;
149
+ --text-secondary: #839496;
150
+ --text-tertiary: #657b83;
151
+ --text-muted: #586e75;
152
+ }
153
+ body.light[data-color-theme="solarized"] {
154
+ --accent: #cb4b16;
155
+ --accent-text: #b34a12;
156
+ --accent-dim: rgba(203,75,22,0.18);
157
+ --accent-glow: rgba(203, 75, 22, 0.5);
158
+ --bg-deep: #eee8d5;
159
+ --bg-surface: #f5efdc;
160
+ --bg-elevated: #fdf6e3;
161
+ --bg-hover: #e4ddc8;
162
+ --border: #d3cbb7;
163
+ --text-primary: #073642;
164
+ --text-secondary: #586e75;
165
+ --text-tertiary: #657b83;
166
+ --text-muted: #839496;
167
+ }
168
+
169
+ /* Dracula */
170
+ body[data-color-theme="dracula"]:not(.light) {
171
+ --accent: #bd93f9;
172
+ --accent-text: #d6b5ff;
173
+ --accent-dim: rgba(189,147,249,0.25);
174
+ --accent-glow: rgba(189, 147, 249, 0.55);
175
+ --bg-deep: #21222c;
176
+ --bg-surface: #282a36;
177
+ --bg-elevated: #343746;
178
+ --bg-hover: #44475a;
179
+ --border: #565a75;
180
+ --text-primary: #f8f8f2;
181
+ --text-secondary: #d8d8d2;
182
+ --text-tertiary: #b0b3c5;
183
+ --text-muted: #6272a4;
184
+ }
185
+ body.light[data-color-theme="dracula"] {
186
+ --accent: #7c3aed;
187
+ --accent-text: #5b21b6;
188
+ --accent-dim: rgba(124,58,237,0.18);
189
+ --accent-glow: rgba(124, 58, 237, 0.5);
190
+ --bg-deep: #e8e8ee;
191
+ --bg-surface: #f0f0f5;
192
+ --bg-elevated: #fbfbfd;
193
+ --bg-hover: #dfdfe8;
194
+ --border: #c9c9d6;
195
+ --text-primary: #16161e;
196
+ --text-secondary: #34343f;
197
+ --text-tertiary: #5a5a68;
198
+ --text-muted: #787885;
199
+ }
200
+
201
+ /* Nord */
202
+ body[data-color-theme="nord"]:not(.light) {
203
+ --accent: #88c0d0;
204
+ --accent-text: #a8d8e8;
205
+ --accent-dim: rgba(136,192,208,0.25);
206
+ --accent-glow: rgba(136, 192, 208, 0.55);
207
+ --bg-deep: #272c36;
208
+ --bg-surface: #2e3440;
209
+ --bg-elevated: #3b4252;
210
+ --bg-hover: #434c5e;
211
+ --border: #4c566a;
212
+ --text-primary: #eceff4;
213
+ --text-secondary: #d8dee9;
214
+ --text-tertiary: #aeb6c5;
215
+ --text-muted: #7b88a1;
216
+ }
217
+ body.light[data-color-theme="nord"] {
218
+ --accent: #5e81ac;
219
+ --accent-text: #44688f;
220
+ --accent-dim: rgba(94,129,172,0.18);
221
+ --accent-glow: rgba(94, 129, 172, 0.5);
222
+ --bg-deep: #e5e9f0;
223
+ --bg-surface: #eceff4;
224
+ --bg-elevated: #f8f9fb;
225
+ --bg-hover: #d8dee9;
226
+ --border: #c2c9d6;
227
+ --text-primary: #2e3440;
228
+ --text-secondary: #3b4252;
229
+ --text-tertiary: #4c566a;
230
+ --text-muted: #6b7589;
231
+ }
232
+
233
+ /* Rosé Pine */
234
+ body[data-color-theme="rose-pine"]:not(.light) {
235
+ --accent: #c4a7e7;
236
+ --accent-text: #d9c4f2;
237
+ --accent-dim: rgba(196,167,231,0.25);
238
+ --accent-glow: rgba(196, 167, 231, 0.55);
239
+ --bg-deep: #191724;
240
+ --bg-surface: #1f1d2e;
241
+ --bg-elevated: #26233a;
242
+ --bg-hover: #34304e;
243
+ --border: #403d52;
244
+ --text-primary: #e0def4;
245
+ --text-secondary: #c5c2dd;
246
+ --text-tertiary: #908caa;
247
+ --text-muted: #6e6a86;
248
+ }
249
+ body.light[data-color-theme="rose-pine"] {
250
+ --accent: #907aa9;
251
+ --accent-text: #6f598c;
252
+ --accent-dim: rgba(144,122,169,0.18);
253
+ --accent-glow: rgba(144, 122, 169, 0.5);
254
+ --bg-deep: #f2e9e1;
255
+ --bg-surface: #faf4ed;
256
+ --bg-elevated: #fffaf3;
257
+ --bg-hover: #ebdfd4;
258
+ --border: #dfdad9;
259
+ --text-primary: #575279;
260
+ --text-secondary: #635e87;
261
+ --text-tertiary: #797593;
262
+ --text-muted: #9893a5;
263
+ }
264
+
265
+ /* Everforest */
266
+ body[data-color-theme="everforest"]:not(.light) {
267
+ --accent: #a7c080;
268
+ --accent-text: #c3d6a2;
269
+ --accent-dim: rgba(167,192,128,0.25);
270
+ --accent-glow: rgba(167, 192, 128, 0.55);
271
+ --bg-deep: #232a2e;
272
+ --bg-surface: #2d353b;
273
+ --bg-elevated: #343f44;
274
+ --bg-hover: #3d484d;
275
+ --border: #475258;
276
+ --text-primary: #d3c6aa;
277
+ --text-secondary: #bfb6a3;
278
+ --text-tertiary: #9da9a0;
279
+ --text-muted: #859289;
280
+ }
281
+ body.light[data-color-theme="everforest"] {
282
+ --accent: #6f8352;
283
+ --accent-text: #56683f;
284
+ --accent-dim: rgba(111,131,82,0.18);
285
+ --accent-glow: rgba(111, 131, 82, 0.5);
286
+ --bg-deep: #f4f0d9;
287
+ --bg-surface: #fdf6e3;
288
+ --bg-elevated: #fffbef;
289
+ --bg-hover: #efebd4;
290
+ --border: #d8d3ba;
291
+ --text-primary: #4d5960;
292
+ --text-secondary: #5c6a72;
293
+ --text-tertiary: #7a8478;
294
+ --text-muted: #939f91;
295
+ }
296
+
297
+ /* Kanagawa */
298
+ body[data-color-theme="kanagawa"]:not(.light) {
299
+ --accent: #7e9cd8;
300
+ --accent-text: #9fb9ec;
301
+ --accent-dim: rgba(126,156,216,0.25);
302
+ --accent-glow: rgba(126, 156, 216, 0.55);
303
+ --bg-deep: #16161d;
304
+ --bg-surface: #1f1f28;
305
+ --bg-elevated: #2a2a37;
306
+ --bg-hover: #363646;
307
+ --border: #54546d;
308
+ --text-primary: #dcd7ba;
309
+ --text-secondary: #c8c093;
310
+ --text-tertiary: #a6a69c;
311
+ --text-muted: #727169;
312
+ }
313
+ body.light[data-color-theme="kanagawa"] {
314
+ --accent: #4d699b;
315
+ --accent-text: #38537f;
316
+ --accent-dim: rgba(77,105,155,0.18);
317
+ --accent-glow: rgba(77, 105, 155, 0.5);
318
+ --bg-deep: #e5ddb0;
319
+ --bg-surface: #f2ecbc;
320
+ --bg-elevated: #faf5d2;
321
+ --bg-hover: #dcd5ac;
322
+ --border: #c7bf94;
323
+ --text-primary: #545464;
324
+ --text-secondary: #66667a;
325
+ --text-tertiary: #716e61;
326
+ --text-muted: #8a8775;
327
+ }
328
+
329
+ /* One Dark */
330
+ body[data-color-theme="one-dark"]:not(.light) {
331
+ --accent: #61afef;
332
+ --accent-text: #8cc7ff;
333
+ --accent-dim: rgba(97,175,239,0.25);
334
+ --accent-glow: rgba(97, 175, 239, 0.55);
335
+ --bg-deep: #21252b;
336
+ --bg-surface: #282c34;
337
+ --bg-elevated: #2f343e;
338
+ --bg-hover: #3e4452;
339
+ --border: #4b5263;
340
+ --text-primary: #d7dae0;
341
+ --text-secondary: #abb2bf;
342
+ --text-tertiary: #8b919e;
343
+ --text-muted: #5c6370;
344
+ }
345
+ body.light[data-color-theme="one-dark"] {
346
+ --accent: #4078f2;
347
+ --accent-text: #2c5dd4;
348
+ --accent-dim: rgba(64,120,242,0.18);
349
+ --accent-glow: rgba(64, 120, 242, 0.5);
350
+ --bg-deep: #eaeaeb;
351
+ --bg-surface: #fafafa;
352
+ --bg-elevated: #ffffff;
353
+ --bg-hover: #e0e0e2;
354
+ --border: #d4d4d6;
355
+ --text-primary: #383a42;
356
+ --text-secondary: #50525a;
357
+ --text-tertiary: #696c77;
358
+ --text-muted: #a0a1a7;
359
+ }
360
+
361
+ /* Night Owl */
362
+ body[data-color-theme="night-owl"]:not(.light) {
363
+ --accent: #82aaff;
364
+ --accent-text: #a8c4ff;
365
+ --accent-dim: rgba(130,170,255,0.25);
366
+ --accent-glow: rgba(130, 170, 255, 0.55);
367
+ --bg-deep: #011627;
368
+ --bg-surface: #0b2942;
369
+ --bg-elevated: #13344f;
370
+ --bg-hover: #1d3b53;
371
+ --border: #5f7e97;
372
+ --text-primary: #d6deeb;
373
+ --text-secondary: #b8c5d6;
374
+ --text-tertiary: #8ba3b8;
375
+ --text-muted: #637777;
376
+ }
377
+ body.light[data-color-theme="night-owl"] {
378
+ --accent: #0c969b;
379
+ --accent-text: #0a7479;
380
+ --accent-dim: rgba(12,150,155,0.18);
381
+ --accent-glow: rgba(12, 150, 155, 0.5);
382
+ --bg-deep: #ededed;
383
+ --bg-surface: #f6f6f6;
384
+ --bg-elevated: #fbfbfb;
385
+ --bg-hover: #e2e2e2;
386
+ --border: #d0d0d0;
387
+ --text-primary: #403f53;
388
+ --text-secondary: #545167;
389
+ --text-tertiary: #6f6e85;
390
+ --text-muted: #989fb1;
391
+ }
392
+
393
+ /* Monokai Pro */
394
+ body[data-color-theme="monokai"]:not(.light) {
395
+ --accent: #ffd866;
396
+ --accent-text: #ffe6a0;
397
+ --accent-dim: rgba(255,216,102,0.25);
398
+ --accent-glow: rgba(255, 216, 102, 0.55);
399
+ --bg-deep: #221f22;
400
+ --bg-surface: #2d2a2e;
401
+ --bg-elevated: #403e41;
402
+ --bg-hover: #4a484b;
403
+ --border: #5b595c;
404
+ --text-primary: #fcfcfa;
405
+ --text-secondary: #d9d8d6;
406
+ --text-tertiary: #939293;
407
+ --text-muted: #727072;
408
+ }
409
+ body.light[data-color-theme="monokai"] {
410
+ --accent: #c08a00;
411
+ --accent-text: #9a6e00;
412
+ --accent-dim: rgba(192,138,0,0.18);
413
+ --accent-glow: rgba(192, 138, 0, 0.5);
414
+ --bg-deep: #e9e6e4;
415
+ --bg-surface: #f1efed;
416
+ --bg-elevated: #fcfbfa;
417
+ --bg-hover: #e0dcda;
418
+ --border: #cdc8c5;
419
+ --text-primary: #2c292d;
420
+ --text-secondary: #46434a;
421
+ --text-tertiary: #6b686d;
422
+ --text-muted: #8d8a8d;
423
+ }
424
+
425
+ /* GitHub */
426
+ body[data-color-theme="github"]:not(.light) {
427
+ --accent: #58a6ff;
428
+ --accent-text: #85bdff;
429
+ --accent-dim: rgba(88,166,255,0.25);
430
+ --accent-glow: rgba(88, 166, 255, 0.55);
431
+ --bg-deep: #1f2428;
432
+ --bg-surface: #24292e;
433
+ --bg-elevated: #2b3138;
434
+ --bg-hover: #2f363d;
435
+ --border: #444d56;
436
+ --text-primary: #e1e4e8;
437
+ --text-secondary: #d1d5da;
438
+ --text-tertiary: #959da5;
439
+ --text-muted: #6a737d;
440
+ }
441
+ body.light[data-color-theme="github"] {
442
+ --accent: #0366d6;
443
+ --accent-text: #044289;
444
+ --accent-dim: rgba(3,102,214,0.18);
445
+ --accent-glow: rgba(3, 102, 214, 0.5);
446
+ --bg-deep: #f0f2f4;
447
+ --bg-surface: #f6f8fa;
448
+ --bg-elevated: #ffffff;
449
+ --bg-hover: #e8eaed;
450
+ --border: #d1d5da;
451
+ --text-primary: #24292e;
452
+ --text-secondary: #444d56;
453
+ --text-tertiary: #586069;
454
+ --text-muted: #6a737d;
455
+ }
456
+
457
+ /* Ayu */
458
+ body[data-color-theme="ayu"]:not(.light) {
459
+ --accent: #e6b450;
460
+ --accent-text: #f0cd85;
461
+ --accent-dim: rgba(230,180,80,0.25);
462
+ --accent-glow: rgba(230, 180, 80, 0.55);
463
+ --bg-deep: #0d1017;
464
+ --bg-surface: #10141c;
465
+ --bg-elevated: #141821;
466
+ --bg-hover: #1d2330;
467
+ --border: #2d3343;
468
+ --text-primary: #bfbdb6;
469
+ --text-secondary: #a8a6a0;
470
+ --text-tertiary: #8a9199;
471
+ --text-muted: #6c7380;
472
+ }
473
+ body.light[data-color-theme="ayu"] {
474
+ --accent: #f2ae49;
475
+ --accent-text: #b87514;
476
+ --accent-dim: rgba(242,174,73,0.18);
477
+ --accent-glow: rgba(242, 174, 73, 0.5);
478
+ --bg-deep: #eff1f3;
479
+ --bg-surface: #f8f9fa;
480
+ --bg-elevated: #fcfcfc;
481
+ --bg-hover: #e7eaed;
482
+ --border: #d8dde2;
483
+ --text-primary: #3d4149;
484
+ --text-secondary: #5c6166;
485
+ --text-tertiary: #787b80;
486
+ --text-muted: #8a9199;
487
+ }
488
+
489
+ /* Vitesse */
490
+ body[data-color-theme="vitesse"]:not(.light) {
491
+ --accent: #4d9375;
492
+ --accent-text: #6fb392;
493
+ --accent-dim: rgba(77,147,117,0.25);
494
+ --accent-glow: rgba(77, 147, 117, 0.55);
495
+ --bg-deep: #121212;
496
+ --bg-surface: #181818;
497
+ --bg-elevated: #1e1e1e;
498
+ --bg-hover: #262626;
499
+ --border: #333333;
500
+ --text-primary: #dbd7ca;
501
+ --text-secondary: #bfbaaa;
502
+ --text-tertiary: #8f8f85;
503
+ --text-muted: #758575;
504
+ }
505
+ body.light[data-color-theme="vitesse"] {
506
+ --accent: #1c6b48;
507
+ --accent-text: #145236;
508
+ --accent-dim: rgba(28,107,72,0.18);
509
+ --accent-glow: rgba(28, 107, 72, 0.5);
510
+ --bg-deep: #f0f0f0;
511
+ --bg-surface: #f7f7f7;
512
+ --bg-elevated: #ffffff;
513
+ --bg-hover: #e7e7e7;
514
+ --border: #d6d6d6;
515
+ --text-primary: #393a34;
516
+ --text-secondary: #4e4f47;
517
+ --text-tertiary: #6b6d63;
518
+ --text-muted: #a0ada0;
519
+ }
520
+
521
+ /* Synthwave '84 */
522
+ body[data-color-theme="synthwave"]:not(.light) {
523
+ --accent: #ff7edb;
524
+ --accent-text: #ffa9e7;
525
+ --accent-dim: rgba(255,126,219,0.25);
526
+ --accent-glow: rgba(255, 126, 219, 0.55);
527
+ --bg-deep: #241b2f;
528
+ --bg-surface: #262335;
529
+ --bg-elevated: #322a47;
530
+ --bg-hover: #3d3460;
531
+ --border: #495495;
532
+ --text-primary: #ffffff;
533
+ --text-secondary: #d8d6e8;
534
+ --text-tertiary: #b6b1d8;
535
+ --text-muted: #848bbd;
536
+ }
537
+ body.light[data-color-theme="synthwave"] {
538
+ --accent: #c936a6;
539
+ --accent-text: #9c2381;
540
+ --accent-dim: rgba(201,54,166,0.18);
541
+ --accent-glow: rgba(201, 54, 166, 0.5);
542
+ --bg-deep: #e6e2f0;
543
+ --bg-surface: #efecf6;
544
+ --bg-elevated: #fbfafd;
545
+ --bg-hover: #ddd7ec;
546
+ --border: #c5bcdd;
547
+ --text-primary: #241b2f;
548
+ --text-secondary: #3d3455;
549
+ --text-tertiary: #5c5380;
550
+ --text-muted: #7d76a3;
551
+ }