claude-code-marketplace 0.5.7 → 0.5.9
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 +3 -1
- package/package.json +1 -1
- package/public/app.js +68 -7
- package/public/index.html +7 -5
- package/public/style.css +53 -29
- package/server.js +11 -3
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
A web-based dashboard for browsing, installing, and managing [Claude Code](https://docs.anthropic.com/en/docs/claude-code) plugins across multiple marketplaces.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/claude-code-marketplace)
|
|
5
|
+
[](https://www.npmjs.com/package/claude-code-marketplace)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.npmjs.com/package/claude-code-marketplace)
|
|
6
8
|
|
|
7
9
|
<p align="center">
|
|
8
10
|
<img src="assets/main-dark.png" alt="Marketplace — dark theme" width="100%">
|
package/package.json
CHANGED
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
|
|
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
|
|
1176
|
-
|
|
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 [...
|
|
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
|
|
1250
|
+
scrollRowIntoView(row);
|
|
1236
1251
|
focusedRowId = row.dataset.rowId;
|
|
1237
1252
|
_focusedRowEl = row;
|
|
1238
1253
|
}
|
|
@@ -1299,6 +1314,12 @@ function handleKeydown(e) {
|
|
|
1299
1314
|
return;
|
|
1300
1315
|
}
|
|
1301
1316
|
|
|
1317
|
+
if (matchKey(e, 't')) {
|
|
1318
|
+
e.preventDefault();
|
|
1319
|
+
toggleTheme();
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1302
1323
|
const rows = getVisibleRows();
|
|
1303
1324
|
const idx = getFocusedIndex(rows);
|
|
1304
1325
|
|
|
@@ -1382,6 +1403,46 @@ if ('serviceWorker' in navigator) {
|
|
|
1382
1403
|
navigator.serviceWorker.register('/sw.js');
|
|
1383
1404
|
}
|
|
1384
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
|
+
|
|
1385
1446
|
// #region HUB_INTEGRATION
|
|
1386
1447
|
(async function initHub() {
|
|
1387
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">
|
|
@@ -134,6 +135,7 @@
|
|
|
134
135
|
<tr><td><kbd>S</kbd></td><td>Focus scope filter</td></tr>
|
|
135
136
|
<tr><td><kbd>E</kbd></td><td>Expand / Collapse all</td></tr>
|
|
136
137
|
<tr><td><kbd>R</kbd></td><td>Refresh data</td></tr>
|
|
138
|
+
<tr><td><kbd>T</kbd></td><td>Toggle theme</td></tr>
|
|
137
139
|
<tr><td><kbd>Esc</kbd></td><td>Close panel / blur input</td></tr>
|
|
138
140
|
</table>
|
|
139
141
|
</div>
|
package/public/style.css
CHANGED
|
@@ -279,11 +279,25 @@ body {
|
|
|
279
279
|
/* === TREE PANEL === */
|
|
280
280
|
|
|
281
281
|
.tree-panel {
|
|
282
|
-
flex:
|
|
282
|
+
flex: 0 0 var(--sidebar-w, 400px);
|
|
283
283
|
display: flex;
|
|
284
284
|
flex-direction: column;
|
|
285
285
|
overflow: hidden;
|
|
286
|
-
min-width:
|
|
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
|
-
|
|
674
|
-
|
|
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:
|
|
1001
|
-
right:
|
|
1002
|
-
|
|
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
|
-
|
|
1005
|
-
font-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1028
|
+
|
|
1029
|
+
.toast.toast-success {
|
|
1030
|
+
border-color: #2ea043;
|
|
1031
|
+
color: #2ea043;
|
|
1017
1032
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
color: #
|
|
1033
|
+
|
|
1034
|
+
.toast.toast-error {
|
|
1035
|
+
border-color: #f85149;
|
|
1036
|
+
color: #f85149;
|
|
1021
1037
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
color:
|
|
1038
|
+
|
|
1039
|
+
.toast.toast-info {
|
|
1040
|
+
border-color: var(--accent);
|
|
1041
|
+
color: var(--accent);
|
|
1025
1042
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
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
|
@@ -427,9 +427,8 @@ const VIRTUAL_PREFIX = '_custom/';
|
|
|
427
427
|
const SCOPE_LABELS = { user: 'User Customizations', project: 'Project Customizations' };
|
|
428
428
|
const EMPTY_SCOPE = { installed: false, enabled: false, version: null, installPath: null };
|
|
429
429
|
|
|
430
|
-
function
|
|
430
|
+
function rescanVirtualComponents(basePath, scope) {
|
|
431
431
|
const components = countComponents(basePath);
|
|
432
|
-
|
|
433
432
|
// Strip .md extensions from command/agent names for cleaner display
|
|
434
433
|
components.commands = components.commands.map(n => n.replace(/\.md$/, ''));
|
|
435
434
|
components.agents = components.agents.map(n => n.replace(/\.md$/, ''));
|
|
@@ -461,6 +460,12 @@ function scanCustomizations(basePath, scope) {
|
|
|
461
460
|
}
|
|
462
461
|
if (claudeMdFiles.length) components.claudeMd = claudeMdFiles;
|
|
463
462
|
|
|
463
|
+
return components;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function scanCustomizations(basePath, scope) {
|
|
467
|
+
const components = rescanVirtualComponents(basePath, scope);
|
|
468
|
+
|
|
464
469
|
const hasAny = Object.values(components).some(v => Array.isArray(v) && v.length > 0);
|
|
465
470
|
if (!hasAny) return null;
|
|
466
471
|
|
|
@@ -605,7 +610,10 @@ app.get('/api/plugins/:pluginId/components', (req, res) => {
|
|
|
605
610
|
const plugin = findPlugin(pluginId, mktData);
|
|
606
611
|
|
|
607
612
|
if (plugin?.isVirtual) {
|
|
608
|
-
|
|
613
|
+
const scope = plugin.fullId.replace(VIRTUAL_PREFIX, '');
|
|
614
|
+
const comps = rescanVirtualComponents(plugin._pluginDir, scope);
|
|
615
|
+
comps._pluginDir = plugin._pluginDir;
|
|
616
|
+
return res.json(comps);
|
|
609
617
|
}
|
|
610
618
|
if (!plugin?._pluginDir) return res.status(404).json({ error: 'Plugin directory not found', pluginId });
|
|
611
619
|
|