@bsb/registry 1.0.1

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.
Files changed (54) hide show
  1. package/README.md +133 -0
  2. package/bsb-plugin.json +47 -0
  3. package/lib/.bsb/clients/service-bsb-registry.d.ts +1118 -0
  4. package/lib/.bsb/clients/service-bsb-registry.d.ts.map +1 -0
  5. package/lib/.bsb/clients/service-bsb-registry.js +393 -0
  6. package/lib/.bsb/clients/service-bsb-registry.js.map +1 -0
  7. package/lib/plugins/service-bsb-registry/auth.d.ts +87 -0
  8. package/lib/plugins/service-bsb-registry/auth.d.ts.map +1 -0
  9. package/lib/plugins/service-bsb-registry/auth.js +197 -0
  10. package/lib/plugins/service-bsb-registry/auth.js.map +1 -0
  11. package/lib/plugins/service-bsb-registry/db/file.d.ts +73 -0
  12. package/lib/plugins/service-bsb-registry/db/file.d.ts.map +1 -0
  13. package/lib/plugins/service-bsb-registry/db/file.js +588 -0
  14. package/lib/plugins/service-bsb-registry/db/file.js.map +1 -0
  15. package/lib/plugins/service-bsb-registry/db/index.d.ts +75 -0
  16. package/lib/plugins/service-bsb-registry/db/index.d.ts.map +1 -0
  17. package/lib/plugins/service-bsb-registry/db/index.js +24 -0
  18. package/lib/plugins/service-bsb-registry/db/index.js.map +1 -0
  19. package/lib/plugins/service-bsb-registry/index.d.ts +1228 -0
  20. package/lib/plugins/service-bsb-registry/index.d.ts.map +1 -0
  21. package/lib/plugins/service-bsb-registry/index.js +661 -0
  22. package/lib/plugins/service-bsb-registry/index.js.map +1 -0
  23. package/lib/plugins/service-bsb-registry/types.d.ts +559 -0
  24. package/lib/plugins/service-bsb-registry/types.d.ts.map +1 -0
  25. package/lib/plugins/service-bsb-registry/types.js +235 -0
  26. package/lib/plugins/service-bsb-registry/types.js.map +1 -0
  27. package/lib/plugins/service-bsb-registry-ui/http-server.d.ts +138 -0
  28. package/lib/plugins/service-bsb-registry-ui/http-server.d.ts.map +1 -0
  29. package/lib/plugins/service-bsb-registry-ui/http-server.js +1660 -0
  30. package/lib/plugins/service-bsb-registry-ui/http-server.js.map +1 -0
  31. package/lib/plugins/service-bsb-registry-ui/index.d.ts +62 -0
  32. package/lib/plugins/service-bsb-registry-ui/index.d.ts.map +1 -0
  33. package/lib/plugins/service-bsb-registry-ui/index.js +101 -0
  34. package/lib/plugins/service-bsb-registry-ui/index.js.map +1 -0
  35. package/lib/plugins/service-bsb-registry-ui/static/assets/images/apple-touch-icon.png +0 -0
  36. package/lib/plugins/service-bsb-registry-ui/static/assets/images/favicon-16x16.png +0 -0
  37. package/lib/plugins/service-bsb-registry-ui/static/assets/images/favicon-32x32.png +0 -0
  38. package/lib/plugins/service-bsb-registry-ui/static/assets/images/favicon.ico +0 -0
  39. package/lib/plugins/service-bsb-registry-ui/static/css/style.css +1849 -0
  40. package/lib/plugins/service-bsb-registry-ui/static/js/app.js +336 -0
  41. package/lib/plugins/service-bsb-registry-ui/templates/layouts/main.hbs +39 -0
  42. package/lib/plugins/service-bsb-registry-ui/templates/pages/error.hbs +13 -0
  43. package/lib/plugins/service-bsb-registry-ui/templates/pages/home.hbs +62 -0
  44. package/lib/plugins/service-bsb-registry-ui/templates/pages/not-found.hbs +13 -0
  45. package/lib/plugins/service-bsb-registry-ui/templates/pages/plugin-detail.hbs +537 -0
  46. package/lib/plugins/service-bsb-registry-ui/templates/pages/plugins.hbs +40 -0
  47. package/lib/plugins/service-bsb-registry-ui/templates/partials/pagination.hbs +41 -0
  48. package/lib/plugins/service-bsb-registry-ui/templates/partials/plugin-card.hbs +40 -0
  49. package/lib/plugins/service-bsb-registry-ui/templates/partials/search-form.hbs +31 -0
  50. package/lib/schemas/service-bsb-registry-ui.json +57 -0
  51. package/lib/schemas/service-bsb-registry-ui.plugin.json +73 -0
  52. package/lib/schemas/service-bsb-registry.json +1883 -0
  53. package/lib/schemas/service-bsb-registry.plugin.json +68 -0
  54. package/package.json +60 -0
@@ -0,0 +1,537 @@
1
+ <section class="plugins">
2
+ <div class="container">
3
+ <div class="breadcrumb">
4
+ <a href="/plugins">&larr; Back to Browse</a>
5
+ </div>
6
+
7
+ <div class="detail-card plugin-hero-card">
8
+ <div class="plugin-tags detail-top-tags">
9
+ <span class="plugin-badge badge-language">{{plugin.language}}</span>
10
+ <span class="plugin-badge badge-category">{{plugin.category}}</span>
11
+ {{#if plugin.badges}}
12
+ {{#each plugin.badges}}
13
+ <span class="plugin-badge badge-{{type}}">{{label}}</span>
14
+ {{/each}}
15
+ {{/if}}
16
+ <span class="plugin-badge badge-version">v{{plugin.version}}</span>
17
+ </div>
18
+
19
+ <h1 class="detail-title">{{plugin.displayName}}</h1>
20
+ <div class="detail-id"><code>{{pluginDisplayId plugin.id}}</code></div>
21
+ <p class="detail-description">{{plugin.description}}</p>
22
+
23
+ {{#if plugin.tags}}
24
+ <div class="plugin-tags">
25
+ {{#each plugin.tags}}
26
+ <span class="tag">{{this}}</span>
27
+ {{/each}}
28
+ </div>
29
+ {{/if}}
30
+ </div>
31
+
32
+ <div class="detail-card detail-tabs-shell">
33
+ <div class="detail-tab-nav" role="tablist" aria-label="Plugin sections">
34
+ <button class="detail-tab-btn active" type="button" data-tab-target="documentation">Documentation</button>
35
+ <button class="detail-tab-btn" type="button" data-tab-target="installation">Installation</button>
36
+ {{#if plugin.showConfigCard}}
37
+ <button class="detail-tab-btn" type="button" data-tab-target="configuration">Configuration</button>
38
+ {{/if}}
39
+ {{#if plugin.showEventsCard}}
40
+ <button class="detail-tab-btn" type="button" data-tab-target="events">Events</button>
41
+ {{/if}}
42
+ {{#if plugin.showSupportedFeaturesCard}}
43
+ <button class="detail-tab-btn" type="button" data-tab-target="features">Supported Features</button>
44
+ {{/if}}
45
+ {{#if versions}}
46
+ <button class="detail-tab-btn" type="button" data-tab-target="versions">Versions</button>
47
+ {{/if}}
48
+ {{#if plugin.showDependenciesCard}}
49
+ <button class="detail-tab-btn" type="button" data-tab-target="dependencies">Dependencies</button>
50
+ {{/if}}
51
+ <button class="detail-tab-btn" type="button" data-tab-target="metadata">Metadata</button>
52
+ </div>
53
+
54
+ <div class="detail-tab-panels">
55
+ <div class="detail-tab-panel active" data-tab-panel="documentation">
56
+ {{#if plugin.docTabs}}
57
+ {{#if (gt plugin.docTabs.length 1)}}
58
+ <div class="doc-tabs">
59
+ <div class="doc-tab-bar">
60
+ {{#each plugin.docTabs}}
61
+ <button class="doc-tab-btn{{#if active}} active{{/if}}" data-tab="{{id}}">{{title}}</button>
62
+ {{/each}}
63
+ </div>
64
+ {{#each plugin.docTabs}}
65
+ <div class="doc-tab-panel{{#if active}} active{{/if}}" id="{{id}}">
66
+ <div class="readme-content markdown-body">
67
+ {{{html}}}
68
+ </div>
69
+ </div>
70
+ {{/each}}
71
+ </div>
72
+ <script>
73
+ document.querySelector('.doc-tab-bar').addEventListener('click', function(e) {
74
+ var btn = e.target.closest('.doc-tab-btn');
75
+ if (!btn) return;
76
+ var tabId = btn.getAttribute('data-tab');
77
+ var tabs = this.parentElement;
78
+ tabs.querySelectorAll('.doc-tab-btn').forEach(function(b) { b.classList.remove('active'); });
79
+ tabs.querySelectorAll('.doc-tab-panel').forEach(function(p) { p.classList.remove('active'); });
80
+ btn.classList.add('active');
81
+ document.getElementById(tabId).classList.add('active');
82
+ });
83
+ </script>
84
+ {{else}}
85
+ <div class="readme-content markdown-body">
86
+ {{{plugin.docTabs.0.html}}}
87
+ </div>
88
+ {{/if}}
89
+ {{else}}
90
+ <p class="detail-description dependency-none">No documentation available</p>
91
+ {{/if}}
92
+ </div>
93
+
94
+ <div class="detail-tab-panel" data-tab-panel="installation">
95
+ <h2 class="detail-section-title">Installation</h2>
96
+
97
+ {{#if (eq plugin.category 'service')}}
98
+ <div class="install-block">
99
+ <span class="install-label">Use as a dependency (client reference)</span>
100
+ <pre><code>bsb client install {{pluginDisplayId plugin.id}}</code></pre>
101
+ </div>
102
+ <div class="install-block">
103
+ <span class="install-label">Deploy as a service</span>
104
+ <pre><code>bsb plugin install {{pluginDisplayId plugin.id}}</code></pre>
105
+ </div>
106
+ {{else}}
107
+ <div class="install-block">
108
+ <span class="install-label">Install plugin (Node.js)</span>
109
+ {{#if plugin.package.nodejs}}
110
+ <pre><code>npm i {{plugin.package.nodejs}}</code></pre>
111
+ {{else}}
112
+ <pre><code>npm i {{pluginDisplayId plugin.id}}</code></pre>
113
+ {{/if}}
114
+ </div>
115
+ {{/if}}
116
+
117
+ {{#if (eq plugin.category 'service')}}
118
+ {{#if plugin.package}}
119
+ {{#if plugin.package.nodejs}}
120
+ <div class="install-block">
121
+ <span class="install-label">Node.js (npm)</span>
122
+ <pre><code>npm install {{plugin.package.nodejs}}</code></pre>
123
+ </div>
124
+ {{/if}}
125
+ {{#if plugin.package.csharp}}
126
+ <div class="install-block">
127
+ <span class="install-label">C# (NuGet)</span>
128
+ <pre><code>dotnet add package {{plugin.package.csharp}}</code></pre>
129
+ </div>
130
+ {{/if}}
131
+ {{#if plugin.package.go}}
132
+ <div class="install-block">
133
+ <span class="install-label">Go</span>
134
+ <pre><code>go get {{plugin.package.go}}</code></pre>
135
+ </div>
136
+ {{/if}}
137
+ {{#if plugin.package.java}}
138
+ <div class="install-block">
139
+ <span class="install-label">Java (Maven)</span>
140
+ <pre><code>&lt;dependency&gt;
141
+ &lt;groupId&gt;{{plugin.package.java}}&lt;/groupId&gt;
142
+ &lt;/dependency&gt;</code></pre>
143
+ </div>
144
+ {{/if}}
145
+ {{/if}}
146
+ {{/if}}
147
+ </div>
148
+
149
+ {{#if plugin.showConfigCard}}
150
+ <div class="detail-tab-panel" data-tab-panel="configuration">
151
+ <h2 class="detail-section-title">Configuration</h2>
152
+ <p class="detail-description">{{plugin.configDescription}}</p>
153
+ {{#if plugin.hasNestedConfigProps}}
154
+ <p class="event-section-desc">Tree view shows nested config fields and object branches.</p>
155
+ {{/if}}
156
+
157
+ {{#if plugin.configProps}}
158
+ <div class="config-tree">
159
+ {{#each plugin.configProps}}
160
+ <div class="config-tree-node {{#if isObject}}is-object{{else}}is-field{{/if}}" style="--config-level: {{level}};">
161
+ <div class="config-tree-head">
162
+ <code class="config-key">{{name}}</code>
163
+ <span class="config-row-path">{{fullPath}}</span>
164
+ {{#if isObject}}
165
+ <span class="config-pill config-pill-branch">object</span>
166
+ {{else}}
167
+ <span class="config-type">{{type}}</span>
168
+ {{/if}}
169
+ {{#unless required}}<span class="config-pill">optional</span>{{/unless}}
170
+ {{#if defaultValue}}<span class="config-pill config-pill-default">default: {{defaultValue}}</span>{{/if}}
171
+ </div>
172
+ {{#if description}}<div class="config-tree-desc">{{description}}</div>{{/if}}
173
+ </div>
174
+ {{/each}}
175
+ </div>
176
+ {{else}}
177
+ <p class="detail-description dependency-none">No configuration required</p>
178
+ {{/if}}
179
+ </div>
180
+ {{/if}}
181
+
182
+ {{#if plugin.showEventsCard}}
183
+ <div class="detail-tab-panel" data-tab-panel="events">
184
+ <h2 class="detail-section-title">Events</h2>
185
+ <p class="detail-description">Events available to clients of this plugin:</p>
186
+
187
+ {{#if plugin.eventSchema.emitReturnableEvents}}
188
+ <h3 class="event-section-title event-returnable">Emit and Return</h3>
189
+ <p class="event-section-desc">Call these events and receive a response.</p>
190
+ {{#each plugin.eventSchema.emitReturnableEvents}}
191
+ <details class="event-item">
192
+ <summary class="event-item-summary">
193
+ <div class="event-item-header">
194
+ <code class="event-returnable">{{name}}</code>
195
+ <span class="event-type-badge returnable">returnable</span>
196
+ </div>
197
+ {{#if description}}<p class="event-description">{{description}}</p>{{/if}}
198
+ </summary>
199
+ <div class="event-item-body">
200
+ {{#if inputProps.length}}
201
+ <div class="event-schema-block">
202
+ <span class="event-schema-label">Input</span>
203
+ <div class="event-props">
204
+ {{#each inputProps}}
205
+ <div class="event-prop">
206
+ <code class="event-prop-name">{{name}}</code>
207
+ <span class="event-prop-type">{{type}}</span>
208
+ {{#unless required}}<span class="event-prop-optional">optional</span>{{/unless}}
209
+ {{#if description}}<span class="event-prop-desc">{{description}}</span>{{/if}}
210
+ </div>
211
+ {{/each}}
212
+ </div>
213
+ </div>
214
+ {{/if}}
215
+ {{#if outputProps.length}}
216
+ <div class="event-schema-block">
217
+ <span class="event-schema-label">Returns</span>
218
+ <div class="event-props">
219
+ {{#each outputProps}}
220
+ <div class="event-prop">
221
+ <code class="event-prop-name">{{name}}</code>
222
+ <span class="event-prop-type">{{type}}</span>
223
+ {{#unless required}}<span class="event-prop-optional">optional</span>{{/unless}}
224
+ {{#if description}}<span class="event-prop-desc">{{description}}</span>{{/if}}
225
+ </div>
226
+ {{/each}}
227
+ </div>
228
+ </div>
229
+ {{/if}}
230
+ </div>
231
+ </details>
232
+ {{/each}}
233
+ {{/if}}
234
+
235
+ {{#if plugin.eventSchema.emitEvents}}
236
+ <h3 class="event-section-title event-emit">Fire and Forget</h3>
237
+ <p class="event-section-desc">Emit these events with no response expected.</p>
238
+ {{#each plugin.eventSchema.emitEvents}}
239
+ <details class="event-item">
240
+ <summary class="event-item-summary">
241
+ <div class="event-item-header">
242
+ <code class="event-emit">{{name}}</code>
243
+ <span class="event-type-badge emit">fire-and-forget</span>
244
+ </div>
245
+ {{#if description}}<p class="event-description">{{description}}</p>{{/if}}
246
+ </summary>
247
+ <div class="event-item-body">
248
+ {{#if inputProps.length}}
249
+ <div class="event-schema-block">
250
+ <span class="event-schema-label">Input</span>
251
+ <div class="event-props">
252
+ {{#each inputProps}}
253
+ <div class="event-prop">
254
+ <code class="event-prop-name">{{name}}</code>
255
+ <span class="event-prop-type">{{type}}</span>
256
+ {{#unless required}}<span class="event-prop-optional">optional</span>{{/unless}}
257
+ {{#if description}}<span class="event-prop-desc">{{description}}</span>{{/if}}
258
+ </div>
259
+ {{/each}}
260
+ </div>
261
+ </div>
262
+ {{/if}}
263
+ </div>
264
+ </details>
265
+ {{/each}}
266
+ {{/if}}
267
+
268
+ {{#if plugin.eventSchema.onReturnableEvents}}
269
+ <h3 class="event-section-title event-returnable">Listen and Respond</h3>
270
+ <p class="event-section-desc">Events this plugin listens for and responds to.</p>
271
+ {{#each plugin.eventSchema.onReturnableEvents}}
272
+ <details class="event-item">
273
+ <summary class="event-item-summary">
274
+ <div class="event-item-header">
275
+ <code class="event-returnable">{{name}}</code>
276
+ <span class="event-type-badge returnable">returnable</span>
277
+ </div>
278
+ {{#if description}}<p class="event-description">{{description}}</p>{{/if}}
279
+ </summary>
280
+ <div class="event-item-body">
281
+ {{#if inputProps.length}}
282
+ <div class="event-schema-block">
283
+ <span class="event-schema-label">Receives</span>
284
+ <div class="event-props">
285
+ {{#each inputProps}}
286
+ <div class="event-prop">
287
+ <code class="event-prop-name">{{name}}</code>
288
+ <span class="event-prop-type">{{type}}</span>
289
+ {{#unless required}}<span class="event-prop-optional">optional</span>{{/unless}}
290
+ {{#if description}}<span class="event-prop-desc">{{description}}</span>{{/if}}
291
+ </div>
292
+ {{/each}}
293
+ </div>
294
+ </div>
295
+ {{/if}}
296
+ {{#if outputProps.length}}
297
+ <div class="event-schema-block">
298
+ <span class="event-schema-label">Returns</span>
299
+ <div class="event-props">
300
+ {{#each outputProps}}
301
+ <div class="event-prop">
302
+ <code class="event-prop-name">{{name}}</code>
303
+ <span class="event-prop-type">{{type}}</span>
304
+ {{#unless required}}<span class="event-prop-optional">optional</span>{{/unless}}
305
+ {{#if description}}<span class="event-prop-desc">{{description}}</span>{{/if}}
306
+ </div>
307
+ {{/each}}
308
+ </div>
309
+ </div>
310
+ {{/if}}
311
+ </div>
312
+ </details>
313
+ {{/each}}
314
+ {{/if}}
315
+
316
+ {{#if plugin.eventSchema.onEvents}}
317
+ <h3 class="event-section-title event-on">Listen (Fire and Forget)</h3>
318
+ <p class="event-section-desc">Events this plugin handles silently.</p>
319
+ {{#each plugin.eventSchema.onEvents}}
320
+ <details class="event-item">
321
+ <summary class="event-item-summary">
322
+ <div class="event-item-header">
323
+ <code class="event-on">{{name}}</code>
324
+ <span class="event-type-badge on">fire-and-forget</span>
325
+ </div>
326
+ {{#if description}}<p class="event-description">{{description}}</p>{{/if}}
327
+ </summary>
328
+ <div class="event-item-body">
329
+ {{#if inputProps.length}}
330
+ <div class="event-schema-block">
331
+ <span class="event-schema-label">Receives</span>
332
+ <div class="event-props">
333
+ {{#each inputProps}}
334
+ <div class="event-prop">
335
+ <code class="event-prop-name">{{name}}</code>
336
+ <span class="event-prop-type">{{type}}</span>
337
+ {{#unless required}}<span class="event-prop-optional">optional</span>{{/unless}}
338
+ {{#if description}}<span class="event-prop-desc">{{description}}</span>{{/if}}
339
+ </div>
340
+ {{/each}}
341
+ </div>
342
+ </div>
343
+ {{/if}}
344
+ </div>
345
+ </details>
346
+ {{/each}}
347
+ {{/if}}
348
+
349
+ {{#if plugin.eventSchema.emitBroadcast}}
350
+ <h3 class="event-section-title event-broadcast">Broadcast</h3>
351
+ <p class="event-section-desc">Broadcast events to all listeners.</p>
352
+ {{#each plugin.eventSchema.emitBroadcast}}
353
+ <details class="event-item">
354
+ <summary class="event-item-summary">
355
+ <div class="event-item-header">
356
+ <code class="event-broadcast">{{name}}</code>
357
+ <span class="event-type-badge broadcast">broadcast</span>
358
+ </div>
359
+ {{#if description}}<p class="event-description">{{description}}</p>{{/if}}
360
+ </summary>
361
+ <div class="event-item-body">
362
+ {{#if inputProps.length}}
363
+ <div class="event-schema-block">
364
+ <span class="event-schema-label">Payload</span>
365
+ <div class="event-props">
366
+ {{#each inputProps}}
367
+ <div class="event-prop">
368
+ <code class="event-prop-name">{{name}}</code>
369
+ <span class="event-prop-type">{{type}}</span>
370
+ {{#unless required}}<span class="event-prop-optional">optional</span>{{/unless}}
371
+ {{#if description}}<span class="event-prop-desc">{{description}}</span>{{/if}}
372
+ </div>
373
+ {{/each}}
374
+ </div>
375
+ </div>
376
+ {{/if}}
377
+ </div>
378
+ </details>
379
+ {{/each}}
380
+ {{/if}}
381
+
382
+ {{#if plugin.eventSchema.onBroadcast}}
383
+ <h3 class="event-section-title event-broadcast">Listen to Broadcasts</h3>
384
+ <p class="event-section-desc">Broadcast events this plugin subscribes to.</p>
385
+ {{#each plugin.eventSchema.onBroadcast}}
386
+ <details class="event-item">
387
+ <summary class="event-item-summary">
388
+ <div class="event-item-header">
389
+ <code class="event-broadcast">{{name}}</code>
390
+ <span class="event-type-badge broadcast">broadcast</span>
391
+ </div>
392
+ {{#if description}}<p class="event-description">{{description}}</p>{{/if}}
393
+ </summary>
394
+ <div class="event-item-body">
395
+ {{#if inputProps.length}}
396
+ <div class="event-schema-block">
397
+ <span class="event-schema-label">Payload</span>
398
+ <div class="event-props">
399
+ {{#each inputProps}}
400
+ <div class="event-prop">
401
+ <code class="event-prop-name">{{name}}</code>
402
+ <span class="event-prop-type">{{type}}</span>
403
+ {{#unless required}}<span class="event-prop-optional">optional</span>{{/unless}}
404
+ {{#if description}}<span class="event-prop-desc">{{description}}</span>{{/if}}
405
+ </div>
406
+ {{/each}}
407
+ </div>
408
+ </div>
409
+ {{/if}}
410
+ </div>
411
+ </details>
412
+ {{/each}}
413
+ {{/if}}
414
+ </div>
415
+ {{/if}}
416
+
417
+ {{#if plugin.showSupportedFeaturesCard}}
418
+ <div class="detail-tab-panel" data-tab-panel="features">
419
+ <h2 class="detail-section-title">Supported Features</h2>
420
+ <p class="detail-description">Observable capabilities implemented by this plugin:</p>
421
+ {{#if (hasItems plugin.observableFeatureGroups)}}
422
+ {{#each plugin.observableFeatureGroups}}
423
+ <div class="feature-group">
424
+ <h3 class="feature-group-title">{{title}}</h3>
425
+ <div class="feature-chip-list">
426
+ {{#each items}}
427
+ <div class="feature-chip {{#if supported}}is-supported{{else}}is-unsupported{{/if}}">
428
+ <code class="feature-chip-name">{{name}}</code>
429
+ <span class="feature-chip-state">{{#if supported}}available{{else}}unavailable{{/if}}</span>
430
+ </div>
431
+ {{/each}}
432
+ </div>
433
+ </div>
434
+ {{/each}}
435
+ {{else}}
436
+ <p class="detail-description dependency-none">No capability metadata published</p>
437
+ {{/if}}
438
+ </div>
439
+ {{/if}}
440
+
441
+ {{#if versions}}
442
+ <div class="detail-tab-panel" data-tab-panel="versions">
443
+ <h2 class="detail-section-title">Available Versions</h2>
444
+ <div class="version-list">
445
+ {{#each versions}}
446
+ <span class="tag{{#if (eq version ../plugin.version)}} category{{/if}}">v{{version}}</span>
447
+ {{/each}}
448
+ </div>
449
+ </div>
450
+ {{/if}}
451
+
452
+ {{#if plugin.showDependenciesCard}}
453
+ <div class="detail-tab-panel" data-tab-panel="dependencies">
454
+ <h2 class="detail-section-title">Dependencies</h2>
455
+ {{#if (hasItems plugin.dependencies)}}
456
+ <div class="dependency-list">
457
+ {{#each plugin.dependencies}}
458
+ <div class="dependency-item">
459
+ <a href="{{dependencyHref id}}" class="dependency-link">
460
+ <code class="dependency-id">{{pluginDisplayId id}}</code>
461
+ <span class="dependency-version">{{version}}</span>
462
+ </a>
463
+ </div>
464
+ {{/each}}
465
+ </div>
466
+ {{else}}
467
+ <p class="detail-description dependency-none">No dependencies</p>
468
+ {{/if}}
469
+ </div>
470
+ {{/if}}
471
+
472
+ <div class="detail-tab-panel" data-tab-panel="metadata">
473
+ <h2 class="detail-section-title">Metadata</h2>
474
+ <div class="metadata-grid">
475
+ {{#if plugin.author}}
476
+ <span class="metadata-label">Author</span>
477
+ <span class="metadata-value">{{formatAuthor plugin.author}}</span>
478
+ {{/if}}
479
+ {{#if plugin.license}}
480
+ <span class="metadata-label">License</span>
481
+ <span class="metadata-value">{{plugin.license}}</span>
482
+ {{/if}}
483
+ {{#if plugin.homepage}}
484
+ <span class="metadata-label">Homepage</span>
485
+ <span class="metadata-value"><a href="{{plugin.homepage}}" target="_blank">{{plugin.homepage}}</a></span>
486
+ {{/if}}
487
+ {{#if plugin.repository}}
488
+ <span class="metadata-label">Repository</span>
489
+ <span class="metadata-value"><a href="{{plugin.repository}}" target="_blank">{{plugin.repository}}</a></span>
490
+ {{/if}}
491
+ <span class="metadata-label">Published</span>
492
+ <span class="metadata-value">{{plugin.publishedAt}}</span>
493
+ <span class="metadata-label">Updated</span>
494
+ <span class="metadata-value">{{plugin.updatedAt}}</span>
495
+ {{#if plugin.visibility}}
496
+ <span class="metadata-label">Visibility</span>
497
+ <span class="metadata-value">{{plugin.visibility}}</span>
498
+ {{/if}}
499
+ </div>
500
+ </div>
501
+ </div>
502
+ </div>
503
+
504
+ <script>
505
+ (function () {
506
+ var nav = document.querySelector('.detail-tab-nav');
507
+ if (!nav) return;
508
+ var buttons = nav.querySelectorAll('.detail-tab-btn');
509
+ var panels = document.querySelectorAll('.detail-tab-panel[data-tab-panel]');
510
+
511
+ nav.addEventListener('click', function (e) {
512
+ var btn = e.target.closest('.detail-tab-btn');
513
+ if (!btn) return;
514
+ var target = btn.getAttribute('data-tab-target');
515
+ if (!target) return;
516
+
517
+ buttons.forEach(function (b) { b.classList.remove('active'); });
518
+ panels.forEach(function (p) {
519
+ p.classList.remove('active');
520
+ p.setAttribute('hidden', 'hidden');
521
+ });
522
+
523
+ btn.classList.add('active');
524
+ var panel = document.querySelector('.detail-tab-panel[data-tab-panel="' + target + '"]');
525
+ if (panel) {
526
+ panel.classList.add('active');
527
+ panel.removeAttribute('hidden');
528
+ }
529
+ });
530
+
531
+ panels.forEach(function (p, index) {
532
+ if (index !== 0) p.setAttribute('hidden', 'hidden');
533
+ });
534
+ })();
535
+ </script>
536
+ </div>
537
+ </section>
@@ -0,0 +1,40 @@
1
+ <section class="plugins">
2
+ <div class="container">
3
+ <div class="section-header">
4
+ {{#if searchQuery}}
5
+ <h1 class="section-title">Results for "{{searchQuery}}"</h1>
6
+ {{else}}
7
+ <h1 class="section-title">Browse Plugins</h1>
8
+ {{/if}}
9
+ {{#if pagination}}
10
+ <span class="result-count">{{pagination.total}} plugin{{#if (gt pagination.total 1)}}s{{/if}}</span>
11
+ {{/if}}
12
+ </div>
13
+
14
+ {{> search-form}}
15
+
16
+ {{#if plugins}}
17
+ <div class="plugin-grid">
18
+ {{#each plugins}}
19
+ {{> plugin-card}}
20
+ {{/each}}
21
+ </div>
22
+
23
+ {{#if pagination}}
24
+ {{> pagination}}
25
+ {{/if}}
26
+ {{else}}
27
+ <div class="empty-state">
28
+ {{#if searchQuery}}
29
+ <h2 class="empty-state-title">No plugins found</h2>
30
+ <p class="empty-state-text">No plugins match "{{searchQuery}}". Try a different search or adjust your filters.</p>
31
+ <a href="/plugins" class="btn">Clear Search</a>
32
+ {{else}}
33
+ <h2 class="empty-state-title">No plugins yet</h2>
34
+ <p class="empty-state-text">No plugins match your current filters.</p>
35
+ <a href="/plugins" class="btn">Clear Filters</a>
36
+ {{/if}}
37
+ </div>
38
+ {{/if}}
39
+ </div>
40
+ </section>
@@ -0,0 +1,41 @@
1
+ {{#if (gt pagination.totalPages 1)}}
2
+ <div class="pagination">
3
+ {{#if (gt pagination.currentPage 1)}}
4
+ <a href="/plugins?page={{subtract pagination.currentPage 1}}{{#if searchQuery}}&query={{searchQuery}}{{/if}}{{#if filters.category}}&category={{filters.category}}{{/if}}{{#if filters.language}}&language={{filters.language}}{{/if}}" class="page-btn">Prev</a>
5
+ {{else}}
6
+ <span class="page-btn disabled">Prev</span>
7
+ {{/if}}
8
+
9
+ {{#if (gt pagination.currentPage 3)}}
10
+ <a href="/plugins?page=1{{#if searchQuery}}&query={{searchQuery}}{{/if}}{{#if filters.category}}&category={{filters.category}}{{/if}}{{#if filters.language}}&language={{filters.language}}{{/if}}" class="page-btn">1</a>
11
+ {{#if (gt pagination.currentPage 4)}}
12
+ <span class="page-btn disabled">...</span>
13
+ {{/if}}
14
+ {{/if}}
15
+
16
+ {{#each (range (subtract pagination.currentPage 2) (add pagination.currentPage 2) pagination.totalPages)}}
17
+ {{#if (eq this ../pagination.currentPage)}}
18
+ <span class="page-btn active">{{this}}</span>
19
+ {{else}}
20
+ <a href="/plugins?page={{this}}{{#if ../searchQuery}}&query={{../searchQuery}}{{/if}}{{#if ../filters.category}}&category={{../filters.category}}{{/if}}{{#if ../filters.language}}&language={{../filters.language}}{{/if}}" class="page-btn">{{this}}</a>
21
+ {{/if}}
22
+ {{/each}}
23
+
24
+ {{#if (lt pagination.currentPage (subtract pagination.totalPages 2))}}
25
+ {{#if (lt pagination.currentPage (subtract pagination.totalPages 3))}}
26
+ <span class="page-btn disabled">...</span>
27
+ {{/if}}
28
+ <a href="/plugins?page={{pagination.totalPages}}{{#if searchQuery}}&query={{searchQuery}}{{/if}}{{#if filters.category}}&category={{filters.category}}{{/if}}{{#if filters.language}}&language={{filters.language}}{{/if}}" class="page-btn">{{pagination.totalPages}}</a>
29
+ {{/if}}
30
+
31
+ {{#if (lt pagination.currentPage pagination.totalPages)}}
32
+ <a href="/plugins?page={{add pagination.currentPage 1}}{{#if searchQuery}}&query={{searchQuery}}{{/if}}{{#if filters.category}}&category={{filters.category}}{{/if}}{{#if filters.language}}&language={{filters.language}}{{/if}}" class="page-btn">Next</a>
33
+ {{else}}
34
+ <span class="page-btn disabled">Next</span>
35
+ {{/if}}
36
+ </div>
37
+
38
+ <div class="pagination-summary">
39
+ Page {{pagination.currentPage}} of {{pagination.totalPages}} ({{pagination.total}} total)
40
+ </div>
41
+ {{/if}}
@@ -0,0 +1,40 @@
1
+ <a href="/plugins/{{org}}/{{name}}" class="plugin-card">
2
+ <div class="plugin-image{{#unless imageUrl}} placeholder{{/unless}}">
3
+ {{#if imageUrl}}
4
+ <img src="{{imageUrl}}" alt="{{displayName}}" loading="lazy">
5
+ {{else}}
6
+ <div class="plugin-image-fallback">{{category}}</div>
7
+ {{/if}}
8
+
9
+ </div>
10
+
11
+ <div class="plugin-content">
12
+ <div class="plugin-header">
13
+ <div class="plugin-name">{{displayName}}</div>
14
+ <div class="plugin-id">{{pluginDisplayId id}}</div>
15
+ </div>
16
+
17
+ <div class="plugin-meta-right">
18
+ <div class="plugin-badges">
19
+ <span class="plugin-badge badge-language">{{language}}</span>
20
+ <span class="plugin-badge badge-category">{{category}}</span>
21
+ {{#if badges}}
22
+ {{#each badges}}
23
+ <span class="plugin-badge badge-{{type}}">{{label}}</span>
24
+ {{/each}}
25
+ {{/if}}
26
+ <span class="plugin-badge badge-version">v{{version}}</span>
27
+ </div>
28
+ </div>
29
+
30
+ <p class="plugin-description">{{description}}</p>
31
+
32
+ {{#if tags}}
33
+ <div class="plugin-tags">
34
+ {{#each tags}}
35
+ <span class="tag">{{this}}</span>
36
+ {{/each}}
37
+ </div>
38
+ {{/if}}
39
+ </div>
40
+ </a>