claude-code-marketplace 0.4.2 → 0.5.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.4.2",
3
+ "version": "0.5.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
@@ -123,6 +123,15 @@ document.addEventListener('DOMContentLoaded', () => {
123
123
  updateThemeColor(savedTheme !== 'dark');
124
124
 
125
125
  document.addEventListener('keydown', handleKeydown);
126
+
127
+ document.getElementById('treeContainer').addEventListener('click', (e) => {
128
+ const row = e.target.closest('.tree-row');
129
+ if (!row?.dataset.rowId) return;
130
+ const rowId = row.dataset.rowId;
131
+ const rows = getVisibleRows();
132
+ const idx = rows.findIndex((r) => r.dataset.rowId === rowId);
133
+ if (idx >= 0) setFocusedRow(idx, rows);
134
+ });
126
135
  });
127
136
 
128
137
  function syncHljsTheme() {
@@ -467,6 +476,7 @@ async function showDetail(pluginId) {
467
476
  <div class="detail-section">
468
477
  <p class="detail-desc">${esc(plugin.description || 'No description')}</p>
469
478
  ${metaRow}
479
+ ${renderPluginMetadata(plugin)}
470
480
  </div>
471
481
  ${scopeSection}
472
482
  <div class="detail-section">
@@ -515,6 +525,42 @@ function renderScopeMatrix(plugin) {
515
525
  .join('')}</div>`;
516
526
  }
517
527
 
528
+ function shortUrl(url) {
529
+ return url
530
+ .replace(/^https?:\/\/(www\.)?/, '')
531
+ .replace(/\.git$/, '')
532
+ .replace(/\/$/, '');
533
+ }
534
+
535
+ function renderPluginMetadata(plugin) {
536
+ const meta = plugin.metadata || {};
537
+ const chips = [];
538
+ if (meta.category) chips.push(`<span class="meta-tag">${esc(meta.category)}</span>`);
539
+ if (meta.author) {
540
+ const name = typeof meta.author === 'object' ? meta.author.name : meta.author;
541
+ if (name) chips.push(`<span class="meta-chip">${esc(name)}</span>`);
542
+ }
543
+ if (meta.tags?.length) {
544
+ for (const t of meta.tags) chips.push(`<span class="meta-tag">${esc(t)}</span>`);
545
+ }
546
+ const links = [];
547
+ if (meta.homepage) {
548
+ links.push(
549
+ `<a class="meta-link" href="${esc(meta.homepage)}" target="_blank" rel="noopener">${esc(shortUrl(meta.homepage))}</a>`,
550
+ );
551
+ }
552
+ if (plugin.source && typeof plugin.source === 'string' && plugin.source.startsWith('http')) {
553
+ const href = plugin.source.replace(/\.git$/, '');
554
+ if (!meta.homepage || !plugin.source.includes(meta.homepage)) {
555
+ links.push(
556
+ `<a class="meta-link" href="${esc(href)}" target="_blank" rel="noopener">${esc(shortUrl(plugin.source))}</a>`,
557
+ );
558
+ }
559
+ }
560
+ if (!chips.length && !links.length) return '';
561
+ return `<div class="plugin-meta-bar">${chips.join('')}${links.join('')}</div>`;
562
+ }
563
+
518
564
  function renderDetailComponents(pluginId, comps, hasDirAccess) {
519
565
  const configFiles = comps._configFiles || {};
520
566
  const entries = Object.entries(comps).filter(
@@ -1016,7 +1062,11 @@ function filterPlugins(plugins) {
1016
1062
  }
1017
1063
  if (searchFilter) {
1018
1064
  result = result.filter(
1019
- (p) => p.name.toLowerCase().includes(searchFilter) || (p.description || '').toLowerCase().includes(searchFilter),
1065
+ (p) =>
1066
+ p.name.toLowerCase().includes(searchFilter) ||
1067
+ (p.description || '').toLowerCase().includes(searchFilter) ||
1068
+ (p.metadata?.category || '').toLowerCase().includes(searchFilter) ||
1069
+ (p.metadata?.tags || []).some((t) => t.toLowerCase().includes(searchFilter)),
1020
1070
  );
1021
1071
  }
1022
1072
  return result;
@@ -1157,7 +1207,8 @@ function handleKeydown(e) {
1157
1207
  }
1158
1208
  return;
1159
1209
  }
1160
- if ((tag === 'BUTTON' || tag === 'A') && (e.key === 'Enter' || e.key === ' ')) return;
1210
+ if ((tag === 'BUTTON' || tag === 'A') && (e.key === 'Enter' || e.key === ' ') && !e.target.closest('#treeContainer'))
1211
+ return;
1161
1212
 
1162
1213
  const openModal = document.querySelector('.modal-overlay.open');
1163
1214
  if (openModal) {
package/public/style.css CHANGED
@@ -781,6 +781,39 @@ body.light .scope-toggle.local {
781
781
  letter-spacing: 0.03em;
782
782
  }
783
783
 
784
+ .plugin-meta-bar {
785
+ display: flex;
786
+ align-items: center;
787
+ gap: 6px;
788
+ flex-wrap: wrap;
789
+ margin-top: 8px;
790
+ }
791
+ .meta-tag {
792
+ font-size: 9px;
793
+ padding: 2px 8px;
794
+ border-radius: 10px;
795
+ border: 1px solid var(--border);
796
+ color: var(--text-muted);
797
+ letter-spacing: 0.02em;
798
+ }
799
+ .meta-chip {
800
+ font-size: 10px;
801
+ color: var(--text-muted);
802
+ }
803
+ .meta-link {
804
+ font-size: 10px;
805
+ color: var(--text-muted);
806
+ text-decoration: none;
807
+ overflow: hidden;
808
+ text-overflow: ellipsis;
809
+ white-space: nowrap;
810
+ max-width: 260px;
811
+ }
812
+ .meta-link:hover {
813
+ color: var(--accent);
814
+ text-decoration: underline;
815
+ }
816
+
784
817
  /* === SCOPE MATRIX === */
785
818
 
786
819
  .scope-matrix {
package/server.js CHANGED
@@ -218,11 +218,22 @@ function loadMarketplaces() {
218
218
  const srcDir = path.resolve(installLocation, rawSource);
219
219
  if (fs.existsSync(srcDir)) originDir = srcDir;
220
220
  }
221
+ if (!originDir && typeof rawSource === 'object' && rawSource?.path) {
222
+ const srcDir = path.resolve(installLocation, rawSource.path);
223
+ if (fs.existsSync(srcDir)) originDir = srcDir;
224
+ }
221
225
  if (!originDir) {
222
226
  const pluginSubdir = path.join(installLocation, 'plugins', pd.name);
223
227
  if (fs.existsSync(pluginSubdir)) originDir = pluginSubdir;
224
228
  else if ((mData.plugins || []).length === 1) originDir = installLocation;
225
229
  }
230
+ if (!originDir) {
231
+ const cacheDir = path.join(PLUGINS_DIR, 'cache', name, pd.name);
232
+ if (fs.existsSync(cacheDir)) {
233
+ const latest = findLatestVersionDir(cacheDir);
234
+ if (latest) originDir = latest;
235
+ }
236
+ }
226
237
  }
227
238
 
228
239
  // Resolve plugin dir for filesystem-based component counts
@@ -250,11 +261,13 @@ function loadMarketplaces() {
250
261
  .find(d => d?.version && d.version !== 'unknown')?.version || null;
251
262
 
252
263
  let availableVersion = pd.version || null;
253
- if (!availableVersion && installLocation) {
254
- const sourceDir = pd.source || `plugins/${pd.name}`;
255
- const pluginJson = path.join(installLocation, typeof sourceDir === 'string' ? sourceDir : pd.name, '.claude-plugin', 'plugin.json');
256
- const pjData = readJsonSafe(pluginJson);
257
- if (pjData?.version) availableVersion = pjData.version;
264
+ if (!availableVersion) {
265
+ const versionDir = pluginDir || originDir;
266
+ if (versionDir) {
267
+ const pluginJson = path.join(versionDir, '.claude-plugin', 'plugin.json');
268
+ const pjData = readJsonSafe(pluginJson);
269
+ if (pjData?.version) availableVersion = pjData.version;
270
+ }
258
271
  }
259
272
  const hasUpdate = isInstalled && semverNewer(availableVersion, installedVersion);
260
273