@opennodes/registry 0.1.24 → 0.1.25
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 +1 -1
- package/src/web/index.html +46 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opennodes/registry",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"description": "OpenNodes (ONP) reference registry: node registration with proof-of-resource qualification (Stage A/B/C), transparent ranking, enforceable estimates, catalog importers (OpenRouter, Hugging Face, models.dev, Ollama), MCP surface, and the web app (catalog, chat, advisor, Node Builder)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
package/src/web/index.html
CHANGED
|
@@ -47,6 +47,12 @@
|
|
|
47
47
|
.mitem .t .nm { font-weight: 600; font-size: 0.86rem; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
48
48
|
.mitem .b { display: flex; gap: 0.8rem; margin-top: 0.25rem; font-size: 0.7rem; color: var(--muted); font-variant-numeric: tabular-nums; flex-wrap: wrap; }
|
|
49
49
|
.mdetail { min-height: 30rem; }
|
|
50
|
+
.pager { display: flex; align-items: center; justify-content: center; gap: 0.3rem; margin-top: 0.5rem; font-size: 0.78rem; color: var(--muted); flex-wrap: wrap; }
|
|
51
|
+
.pager:empty { display: none; }
|
|
52
|
+
.pgbtn { font: inherit; min-width: 2rem; padding: 0.25rem 0.5rem; border-radius: 6px; border: 1px solid var(--line); background: var(--panel); color: var(--accent); cursor: pointer; }
|
|
53
|
+
.pgbtn:disabled { color: var(--faint); cursor: default; }
|
|
54
|
+
.pgnum { padding: 0 0.3rem; }
|
|
55
|
+
.pgnum input { width: 4rem; font: inherit; padding: 0.2rem 0.3rem; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); color: var(--ink); text-align: center; }
|
|
50
56
|
|
|
51
57
|
/* chat */
|
|
52
58
|
.chatgrid { display: grid; grid-template-columns: minmax(0, 1fr) 19rem; gap: 1.2rem; align-items: start; }
|
|
@@ -244,7 +250,7 @@
|
|
|
244
250
|
<div id="adv-out"></div>
|
|
245
251
|
</details>
|
|
246
252
|
<div class="mgrid">
|
|
247
|
-
<div class="mlist" id="m-list"></div>
|
|
253
|
+
<div class="mcol"><div class="mlist" id="m-list"></div><div class="pager" id="m-pager"></div></div>
|
|
248
254
|
<div class="mdetail" id="m-detail"><p class="sub">Select a model from the list.</p></div>
|
|
249
255
|
</div>
|
|
250
256
|
</div>
|
|
@@ -607,34 +613,47 @@ function provLine(o) {
|
|
|
607
613
|
}
|
|
608
614
|
|
|
609
615
|
const PAGE = 60;
|
|
610
|
-
let listQuery = '', listTotal = 0;
|
|
611
|
-
|
|
612
|
-
// Count line and the
|
|
613
|
-
function
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
$('#m-counts').textContent = fmtInt(listTotal) + ' offerings' + (
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
616
|
+
let listQuery = '', listTotal = 0, listPage = 0;
|
|
617
|
+
|
|
618
|
+
// Count line and page controls under the list: first / previous / page N of M / next / last.
|
|
619
|
+
function updatePager() {
|
|
620
|
+
const pages = Math.max(1, Math.ceil(listTotal / PAGE));
|
|
621
|
+
const from = listTotal ? listPage * PAGE + 1 : 0, to = Math.min(listTotal, (listPage + 1) * PAGE);
|
|
622
|
+
$('#m-counts').textContent = fmtInt(listTotal) + ' offerings' + (pages > 1 ? ` · ${fmtInt(from)}–${fmtInt(to)}` : '');
|
|
623
|
+
const pager = $('#m-pager');
|
|
624
|
+
if (pages <= 1) { pager.innerHTML = ''; return; }
|
|
625
|
+
const btn = (label, page, title) => `<button class="pgbtn" data-page="${page}" title="${title}"${page < 0 || page >= pages || page === listPage ? ' disabled' : ''}>${label}</button>`;
|
|
626
|
+
pager.innerHTML = btn('«', 0, 'first page') + btn('‹', listPage - 1, 'previous page')
|
|
627
|
+
+ `<span class="pgnum">Page <input id="m-page" type="number" min="1" max="${pages}" value="${listPage + 1}" aria-label="page number"> of ${fmtInt(pages)}</span>`
|
|
628
|
+
+ btn('›', listPage + 1, 'next page') + btn('»', pages - 1, 'last page');
|
|
629
|
+
pager.querySelectorAll('.pgbtn').forEach((b) => b.addEventListener('click', () => goToPage(Number(b.dataset.page))));
|
|
630
|
+
$('#m-page').addEventListener('change', (e) => goToPage(Math.min(pages, Math.max(1, Math.round(Number(e.target.value) || 1))) - 1));
|
|
621
631
|
}
|
|
622
632
|
|
|
623
|
-
async function
|
|
624
|
-
const btn = $('#m-more');
|
|
625
|
-
btn.disabled = true;
|
|
633
|
+
async function goToPage(page) {
|
|
626
634
|
const query = listQuery;
|
|
627
635
|
const p = new URLSearchParams(query);
|
|
628
|
-
p.set('offset',
|
|
636
|
+
p.set('offset', page * PAGE);
|
|
637
|
+
$('#m-pager').querySelectorAll('button, input').forEach((el) => { el.disabled = true; });
|
|
629
638
|
try {
|
|
630
|
-
const { offerings } = await api('/v0/offerings?' + p);
|
|
639
|
+
const { offerings, total } = await api('/v0/offerings?' + p);
|
|
631
640
|
if (query !== listQuery) return; // the filters changed meanwhile
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
641
|
+
listPage = page;
|
|
642
|
+
listTotal = total ?? listTotal;
|
|
643
|
+
lastResults = offerings;
|
|
644
|
+
renderList(offerings);
|
|
645
|
+
$('#m-list').scrollTop = 0;
|
|
646
|
+
updatePager();
|
|
647
|
+
} catch (e) { updatePager(); $('#m-pager').insertAdjacentHTML('beforeend', '<span class="err">' + esc(e.message) + '</span>'); }
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Render one page of the list, keeping the highlighted model when it is on this page.
|
|
651
|
+
function renderList(offerings) {
|
|
652
|
+
$('#m-list').innerHTML = offerings.map(renderItem).join('')
|
|
653
|
+
|| '<p class="sub">No offerings match. A fresh registry fills up via catalog imports and node registrations.</p>';
|
|
654
|
+
document.querySelectorAll('.mitem').forEach((el) => el.addEventListener('click', () => selectModel(el)));
|
|
655
|
+
const current = $('#model-card')?.dataset.artifact;
|
|
656
|
+
document.querySelectorAll('.mitem').forEach((el) => el.classList.toggle('sel', el.dataset.artifact === current));
|
|
638
657
|
}
|
|
639
658
|
|
|
640
659
|
async function search() {
|
|
@@ -652,17 +671,16 @@ async function search() {
|
|
|
652
671
|
if (listQuery !== p.toString()) return; // a newer search is under way
|
|
653
672
|
lastResults = offerings;
|
|
654
673
|
listTotal = total ?? offerings.length;
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
document.querySelectorAll('.mitem').forEach((el) => el.addEventListener('click', () => selectModel(el)));
|
|
674
|
+
listPage = 0;
|
|
675
|
+
renderList(offerings);
|
|
676
|
+
updatePager();
|
|
659
677
|
// keep the current selection when still listed, else select the first result
|
|
660
678
|
const current = $('#model-card')?.dataset.artifact;
|
|
661
679
|
const keep = current && [...document.querySelectorAll('.mitem')].find((el) => el.dataset.artifact === current);
|
|
662
680
|
const target = keep ?? document.querySelector('.mitem');
|
|
663
681
|
if (target) selectModel(target);
|
|
664
682
|
else $('#m-detail').innerHTML = '<p class="sub">No offerings match.</p>';
|
|
665
|
-
} catch (e) { $('#m-list').innerHTML = '<p class="err">' + esc(e.message) + '</p>'; }
|
|
683
|
+
} catch (e) { $('#m-list').innerHTML = '<p class="err">' + esc(e.message) + '</p>'; $('#m-pager').innerHTML = ''; }
|
|
666
684
|
}
|
|
667
685
|
|
|
668
686
|
/* ---------- advisor (ONP-3 §5b) ---------- */
|