claude-code-marketplace 0.7.0 → 0.8.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/README.md CHANGED
@@ -38,7 +38,7 @@ npx claude-code-marketplace --port 8080
38
38
  ### Options
39
39
 
40
40
  ```
41
- --port <number> Custom port (default: 3457)
41
+ --port <number> Custom port (default: 3542)
42
42
  --project <path> Project directory for project-scoped plugins
43
43
  --open Open browser on start
44
44
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-marketplace",
3
- "version": "0.7.0",
3
+ "version": "0.8.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');
@@ -1523,21 +1593,35 @@ function hubNavigate(app, url) {
1523
1593
 
1524
1594
  (function initHubTheme() {
1525
1595
  const getTheme = () => (document.body.classList.contains('light') ? 'light' : 'dark');
1596
+ const getColorTheme = () => document.body.dataset.colorTheme || 'ember';
1526
1597
  const hubOrigin = () => (window.__HUB__?.url ? new URL(window.__HUB__.url).origin : null);
1598
+ // lastTheme/lastColorTheme are updated synchronously when applying a hub
1599
+ // message, so the (async) observer sees no diff and doesn't echo it back.
1527
1600
  let lastTheme = getTheme();
1601
+ let lastColorTheme = getColorTheme();
1528
1602
  window.addEventListener('message', (e) => {
1529
1603
  if (e.source !== window.parent || e.origin !== hubOrigin()) return;
1530
1604
  if (e.data?.type !== 'hub:theme') return;
1531
- if (getTheme() === e.data.theme) return;
1532
- window.toggleTheme();
1533
- lastTheme = getTheme();
1605
+ if (typeof e.data.colorTheme === 'string' && e.data.colorTheme !== getColorTheme()) {
1606
+ setColorTheme(e.data.colorTheme);
1607
+ lastColorTheme = getColorTheme();
1608
+ }
1609
+ if (getTheme() !== e.data.theme) {
1610
+ window.toggleTheme();
1611
+ lastTheme = getTheme();
1612
+ }
1534
1613
  });
1535
1614
  new MutationObserver(() => {
1536
1615
  const t = getTheme();
1537
- if (t === lastTheme) return;
1616
+ const ct = getColorTheme();
1617
+ if (t === lastTheme && ct === lastColorTheme) return;
1538
1618
  lastTheme = t;
1619
+ lastColorTheme = ct;
1539
1620
  const origin = hubOrigin();
1540
- if (origin) window.parent.postMessage({ type: 'hub:theme', theme: t }, origin);
1541
- }).observe(document.body, { attributes: true, attributeFilter: ['class'] });
1621
+ if (origin) window.parent.postMessage({ type: 'hub:theme', theme: t, colorTheme: ct }, origin);
1622
+ }).observe(document.body, {
1623
+ attributes: true,
1624
+ attributeFilter: ['class', 'data-color-theme'],
1625
+ });
1542
1626
  })();
1543
1627
  // #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>
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);
@@ -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
+ }
package/server.js CHANGED
@@ -34,7 +34,7 @@ function getArg(name) {
34
34
 
35
35
  let projectPath = getArg('project') || process.cwd();
36
36
  if (projectPath.startsWith('~')) projectPath = projectPath.replace('~', os.homedir());
37
- const PORT = parseInt(getArg('port') || process.env.PORT || '3457', 10);
37
+ const PORT = parseInt(getArg('port') || process.env.PORT || '3542', 10);
38
38
 
39
39
  function toUnixPath(p) {
40
40
  return p ? p.replace(/\\/g, '/') : p;