claude-code-marketplace 0.5.8 → 0.5.10

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.5.8",
3
+ "version": "0.5.10",
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
@@ -7,6 +7,7 @@ let componentCache = {};
7
7
  const detailHistory = [];
8
8
  let focusedRowId = null;
9
9
  let _focusedRowEl = null;
10
+ let treeContainer;
10
11
 
11
12
  function matchKey(e, ...keys) {
12
13
  if (e.ctrlKey || e.altKey || e.metaKey || e.shiftKey) return false;
@@ -91,11 +92,13 @@ function updateArrow(p) {
91
92
 
92
93
  // --- Init ---
93
94
  document.addEventListener('DOMContentLoaded', () => {
95
+ treeContainer = document.getElementById('treeContainer');
94
96
  document.getElementById('contentOpenEditor').innerHTML = ICONS.openEditor;
95
97
  document.getElementById('contentCopyPath').innerHTML = ICONS.copyPath;
96
98
  restoreAppState();
97
99
  loadProject();
98
100
  loadData();
101
+ initSidebarResize();
99
102
 
100
103
  let searchTimer;
101
104
  document.getElementById('searchInput').addEventListener('input', (e) => {
@@ -1168,13 +1171,13 @@ function shortenPath(p) {
1168
1171
  .replace(/^\/home\/[^/]+/i, home);
1169
1172
  }
1170
1173
 
1171
- let toastTimeout;
1172
1174
  function toast(msg, type = 'info') {
1173
- const el = document.getElementById('toast');
1175
+ const container = document.getElementById('toast');
1176
+ const el = document.createElement('div');
1177
+ el.className = `toast toast-${type}`;
1174
1178
  el.textContent = msg;
1175
- el.className = `toast ${type} show`;
1176
- clearTimeout(toastTimeout);
1177
- toastTimeout = setTimeout(() => el.classList.remove('show'), 3000);
1179
+ container.appendChild(el);
1180
+ setTimeout(() => el.remove(), 3000);
1178
1181
  }
1179
1182
 
1180
1183
  function closeModal(id) {
@@ -1218,7 +1221,19 @@ async function submitAddMarketplace() {
1218
1221
  // --- Keyboard Navigation ---
1219
1222
 
1220
1223
  function getVisibleRows() {
1221
- return [...document.querySelectorAll('#treeContainer .tree-row')].filter((r) => r.offsetParent !== null);
1224
+ return [...treeContainer.querySelectorAll('.tree-row')].filter((r) => r.offsetParent !== null);
1225
+ }
1226
+
1227
+ function scrollRowIntoView(row) {
1228
+ const rowTop = row.getBoundingClientRect().top - treeContainer.getBoundingClientRect().top + treeContainer.scrollTop;
1229
+ const rowBottom = rowTop + row.offsetHeight;
1230
+ const cTop = treeContainer.scrollTop;
1231
+ const cBottom = cTop + treeContainer.clientHeight;
1232
+ if (rowTop < cTop) {
1233
+ treeContainer.scrollTop = rowTop;
1234
+ } else if (rowBottom > cBottom) {
1235
+ treeContainer.scrollTop = rowBottom - treeContainer.clientHeight;
1236
+ }
1222
1237
  }
1223
1238
 
1224
1239
  function setFocusedRow(index, rows) {
@@ -1232,7 +1247,7 @@ function setFocusedRow(index, rows) {
1232
1247
  index = Math.max(0, Math.min(index, rows.length - 1));
1233
1248
  const row = rows[index];
1234
1249
  row.classList.add('focused');
1235
- row.scrollIntoView({ block: 'nearest' });
1250
+ scrollRowIntoView(row);
1236
1251
  focusedRowId = row.dataset.rowId;
1237
1252
  _focusedRowEl = row;
1238
1253
  }
@@ -1388,6 +1403,46 @@ if ('serviceWorker' in navigator) {
1388
1403
  navigator.serviceWorker.register('/sw.js');
1389
1404
  }
1390
1405
 
1406
+ function initSidebarResize() {
1407
+ const handle = document.getElementById('resizeHandle');
1408
+ const treePanel = document.getElementById('treePanel');
1409
+ const STORAGE_KEY = 'marketplace-sidebar-width';
1410
+
1411
+ const saved = parseInt(localStorage.getItem(STORAGE_KEY), 10);
1412
+ if (saved) document.documentElement.style.setProperty('--sidebar-w', `${saved}px`);
1413
+
1414
+ let dragging = false;
1415
+ let startX, startW, maxW, currentW;
1416
+
1417
+ handle.addEventListener('mousedown', (e) => {
1418
+ dragging = true;
1419
+ startX = e.clientX;
1420
+ startW = treePanel.getBoundingClientRect().width;
1421
+ currentW = startW;
1422
+ const detailMinW = parseInt(getComputedStyle(document.getElementById('detailPanel')).minWidth, 10);
1423
+ maxW = treePanel.parentElement.clientWidth - detailMinW - handle.offsetWidth;
1424
+ handle.classList.add('is-dragging');
1425
+ document.body.style.cursor = 'col-resize';
1426
+ document.body.style.userSelect = 'none';
1427
+ e.preventDefault();
1428
+ });
1429
+
1430
+ document.addEventListener('mousemove', (e) => {
1431
+ if (!dragging) return;
1432
+ currentW = Math.max(200, Math.min(maxW, startW + e.clientX - startX));
1433
+ document.documentElement.style.setProperty('--sidebar-w', `${currentW}px`);
1434
+ });
1435
+
1436
+ document.addEventListener('mouseup', () => {
1437
+ if (!dragging) return;
1438
+ dragging = false;
1439
+ handle.classList.remove('is-dragging');
1440
+ document.body.style.cursor = '';
1441
+ document.body.style.userSelect = '';
1442
+ localStorage.setItem(STORAGE_KEY, currentW);
1443
+ });
1444
+ }
1445
+
1391
1446
  // #region HUB_INTEGRATION
1392
1447
  (async function initHub() {
1393
1448
  const cfg = await fetch('/hub-config')
package/public/index.html CHANGED
@@ -46,24 +46,25 @@
46
46
  <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
47
  Refresh
48
48
  </button>
49
+ <button class="topbar-btn" id="themeBtn" title="Toggle theme">
50
+ <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
+ </button>
49
52
  <button class="topbar-btn" id="helpBtn" title="Keyboard shortcuts (?)">
50
53
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><circle cx="12" cy="17" r="0.5" fill="currentColor"/></svg>
51
54
  <kbd style="font-size:10px">?</kbd>
52
55
  </button>
53
- <button class="topbar-btn" id="themeBtn" title="Toggle theme">
54
- <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>
55
- </button>
56
56
  <a class="topbar-btn" href="https://github.com/NikiforovAll/claude-code-marketplace" target="_blank" rel="noopener" title="GitHub">
57
57
  <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>
58
58
  </a>
59
59
  </div>
60
60
 
61
61
  <div class="main-layout">
62
- <div class="tree-panel">
62
+ <div class="tree-panel" id="treePanel">
63
63
  <div class="tree-container" id="treeContainer">
64
64
  <div class="loading">Loading marketplaces...</div>
65
65
  </div>
66
66
  </div>
67
+ <div class="resize-handle" id="resizeHandle"></div>
67
68
  <div class="detail-panel" id="detailPanel">
68
69
  <div class="detail-empty">
69
70
  <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" opacity="0.3"><path d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"/></svg>
@@ -72,7 +73,7 @@
72
73
  </div>
73
74
  </div>
74
75
 
75
- <div class="toast" id="toast"></div>
76
+ <div class="toast-container" id="toast"></div>
76
77
 
77
78
  <!-- Add marketplace modal -->
78
79
  <div class="modal-overlay" id="addMarketplaceModal">
package/public/style.css CHANGED
@@ -279,11 +279,25 @@ body {
279
279
  /* === TREE PANEL === */
280
280
 
281
281
  .tree-panel {
282
- flex: 1;
282
+ flex: 0 0 var(--sidebar-w, 400px);
283
283
  display: flex;
284
284
  flex-direction: column;
285
285
  overflow: hidden;
286
- min-width: 400px;
286
+ min-width: 200px;
287
+ }
288
+
289
+ .resize-handle {
290
+ width: 4px;
291
+ flex-shrink: 0;
292
+ cursor: col-resize;
293
+ background: transparent;
294
+ transition: background 0.15s;
295
+ position: relative;
296
+ z-index: 1;
297
+ }
298
+ .resize-handle:hover,
299
+ .resize-handle.is-dragging {
300
+ background: var(--accent);
287
301
  }
288
302
 
289
303
  .tree-expand-toggle {
@@ -670,8 +684,8 @@ body.light .scope-toggle.local {
670
684
  /* === DETAIL PANEL === */
671
685
 
672
686
  .detail-panel {
673
- width: 500px;
674
- flex-shrink: 0;
687
+ flex: 1;
688
+ min-width: 300px;
675
689
  background: var(--bg-surface);
676
690
  border-left: 1px solid var(--border);
677
691
  display: flex;
@@ -995,37 +1009,47 @@ body.light .scope-toggle.local {
995
1009
 
996
1010
  /* === TOAST === */
997
1011
 
998
- .toast {
1012
+ .toast-container {
999
1013
  position: fixed;
1000
- bottom: 20px;
1001
- right: 20px;
1002
- padding: 10px 16px;
1014
+ bottom: 16px;
1015
+ right: 16px;
1016
+ z-index: 100;
1017
+ }
1018
+
1019
+ .toast {
1020
+ background: var(--bg-elevated);
1021
+ border: 1px solid var(--border);
1003
1022
  border-radius: 6px;
1004
- font-size: 12px;
1005
- font-weight: 500;
1006
- font-family: var(--mono);
1007
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
1008
- z-index: 1000;
1009
- opacity: 0;
1010
- transform: translateY(10px);
1011
- transition: all 300ms ease;
1012
- max-width: 400px;
1023
+ padding: 8px 14px;
1024
+ font-size: 11px;
1025
+ color: var(--text-secondary);
1026
+ animation: slideIn 0.2s ease;
1013
1027
  }
1014
- .toast.show {
1015
- opacity: 1;
1016
- transform: translateY(0);
1028
+
1029
+ .toast.toast-success {
1030
+ border-color: #2ea043;
1031
+ color: #2ea043;
1017
1032
  }
1018
- .toast.success {
1019
- background: var(--success);
1020
- color: #000;
1033
+
1034
+ .toast.toast-error {
1035
+ border-color: #f85149;
1036
+ color: #f85149;
1021
1037
  }
1022
- .toast.error {
1023
- background: var(--error);
1024
- color: #fff;
1038
+
1039
+ .toast.toast-info {
1040
+ border-color: var(--accent);
1041
+ color: var(--accent);
1025
1042
  }
1026
- .toast.info {
1027
- background: var(--accent);
1028
- color: #fff;
1043
+
1044
+ @keyframes slideIn {
1045
+ from {
1046
+ opacity: 0;
1047
+ transform: translateY(10px);
1048
+ }
1049
+ to {
1050
+ opacity: 1;
1051
+ transform: translateY(0);
1052
+ }
1029
1053
  }
1030
1054
 
1031
1055
  /* === MODAL === */
package/server.js CHANGED
@@ -258,7 +258,12 @@ function loadMarketplaces() {
258
258
  const components = {};
259
259
  for (const k of compKeys) {
260
260
  if (fsComps && Array.isArray(fsComps[k]) && fsComps[k].length > 0) {
261
- components[k] = fsComps[k];
261
+ if (Array.isArray(pd[k]) && pd[k].length > 0) {
262
+ const allowed = new Set(pd[k].map(p => path.basename(typeof p === 'string' ? p : (p.name || String(p)))));
263
+ components[k] = fsComps[k].filter(name => allowed.has(name));
264
+ } else {
265
+ components[k] = fsComps[k];
266
+ }
262
267
  } else if (Array.isArray(pd[k]) && pd[k].length > 0) {
263
268
  components[k] = pd[k].map(p => typeof p === 'string' ? path.basename(p) : (p.name || String(p)));
264
269
  } else if (pd[k]) {
@@ -617,9 +622,9 @@ app.get('/api/plugins/:pluginId/components', (req, res) => {
617
622
  }
618
623
  if (!plugin?._pluginDir) return res.status(404).json({ error: 'Plugin directory not found', pluginId });
619
624
 
620
- const comps = plugin._fsComps || countComponents(plugin._pluginDir, plugin.metadata);
621
- comps._pluginDir = plugin._pluginDir;
622
- res.json(comps);
625
+ const comps = plugin.components || plugin._fsComps || countComponents(plugin._pluginDir, plugin.metadata);
626
+ const result = { ...comps, _pluginDir: plugin._pluginDir };
627
+ res.json(result);
623
628
  });
624
629
 
625
630
  app.get('/api/plugins/:pluginId/preview/*', (req, res) => {