Skip to content

Commit 7da0701

Browse files
authored
Merge pull request #111 from DenisValeev/codex/verify-dataset-rgb-projection-rendering
2 parents d34657b + 4d828cc commit 7da0701

3 files changed

Lines changed: 136 additions & 40 deletions

File tree

apps/embedding-explorer/app.js

Lines changed: 91 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
text: secondaryText,
7878
caption: secondaryCaption,
7979
};
80+
const MAX_NEIGHBOR_DISPLAY = 10;
8081

8182
function safeText(value) {
8283
return typeof value === 'string' ? value.trim() : '';
@@ -524,12 +525,85 @@
524525
function updateActiveNeighborButton() {
525526
const buttons = neighborList.querySelectorAll('.neighbor-button');
526527
buttons.forEach((button) => {
528+
if (button.dataset.self === 'true') {
529+
button.dataset.active = 'true';
530+
button.setAttribute('aria-pressed', 'true');
531+
return;
532+
}
527533
const isActive = button.dataset.neighborId === activeNeighborId;
528534
button.dataset.active = isActive ? 'true' : 'false';
529535
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
530536
});
531537
}
532538

539+
function createNeighborListItem(record, datasetData, options = {}) {
540+
if (!record || !datasetData) {
541+
return null;
542+
}
543+
544+
const item = document.createElement('li');
545+
const button = document.createElement('button');
546+
button.type = 'button';
547+
button.className = 'neighbor-button';
548+
549+
const isSelf = options.isSelf === true;
550+
const similarity = Number.isFinite(options.similarity) ? options.similarity : 0;
551+
552+
if (isSelf) {
553+
button.disabled = true;
554+
button.dataset.self = 'true';
555+
button.dataset.active = 'true';
556+
button.setAttribute('aria-disabled', 'true');
557+
button.setAttribute('aria-pressed', 'true');
558+
} else {
559+
button.dataset.neighborId = record.id;
560+
const isActiveNeighbor = record.id === activeNeighborId;
561+
button.dataset.active = isActiveNeighbor ? 'true' : 'false';
562+
button.setAttribute('aria-pressed', isActiveNeighbor ? 'true' : 'false');
563+
if (activeRecordId) {
564+
button.setAttribute(
565+
'aria-label',
566+
`Compare ${record.id} with ${activeRecordId} (similarity ${formatSimilarity(similarity)})`
567+
);
568+
}
569+
}
570+
571+
const header = document.createElement('div');
572+
header.className = 'neighbor-button__header';
573+
574+
const idSpan = document.createElement('span');
575+
idSpan.className = 'neighbor-button__id';
576+
idSpan.textContent = record.id;
577+
578+
const scoreSpan = document.createElement('span');
579+
scoreSpan.className = 'neighbor-button__score';
580+
scoreSpan.textContent = formatSimilarity(isSelf ? 1 : similarity);
581+
582+
header.append(idSpan, scoreSpan);
583+
button.appendChild(header);
584+
585+
if (isSelf) {
586+
const badge = document.createElement('span');
587+
badge.className = 'neighbor-button__self';
588+
badge.textContent = typeof options.label === 'string' && options.label ? options.label : 'Selected vector';
589+
button.appendChild(badge);
590+
}
591+
592+
const snippetSpan = document.createElement('span');
593+
snippetSpan.className = 'neighbor-button__snippet';
594+
const contentEntry = getContentEntry(datasetData.contentKey, record.id);
595+
const fullPreview = getContentPreview(datasetData.contentKey, contentEntry);
596+
const preview = truncateText(fullPreview, 120);
597+
snippetSpan.textContent = preview || 'No source text available.';
598+
if (fullPreview && preview !== fullPreview) {
599+
snippetSpan.setAttribute('title', fullPreview);
600+
}
601+
602+
button.appendChild(snippetSpan);
603+
item.appendChild(button);
604+
return item;
605+
}
606+
533607
function renderNeighborList(datasetData) {
534608
neighborList.innerHTML = '';
535609

@@ -551,49 +625,26 @@
551625
return;
552626
}
553627

554-
neighborStatus.textContent = `Top ${neighborRecords.length} matches for ${activeRecordId}.`;
555-
556628
const fragment = document.createDocumentFragment();
629+
const primaryRecord = datasetData.recordMap.get(activeRecordId) || null;
630+
neighborStatus.textContent = `Selected vector and top ${neighborRecords.length} matches for ${activeRecordId}.`;
557631

558-
neighborRecords.forEach((entry) => {
559-
const item = document.createElement('li');
560-
const button = document.createElement('button');
561-
button.type = 'button';
562-
button.className = 'neighbor-button';
563-
button.dataset.neighborId = entry.id;
564-
const isActive = entry.id === activeNeighborId;
565-
button.dataset.active = isActive ? 'true' : 'false';
566-
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
567-
if (activeRecordId) {
568-
button.setAttribute('aria-label', `Compare ${entry.record.id} with ${activeRecordId} (similarity ${formatSimilarity(entry.similarity)})`);
569-
}
570-
571-
const header = document.createElement('div');
572-
header.className = 'neighbor-button__header';
632+
const selfItem = createNeighborListItem(primaryRecord, datasetData, {
633+
isSelf: true,
634+
similarity: 1,
635+
label: 'Selected vector',
636+
});
637+
if (selfItem) {
638+
fragment.appendChild(selfItem);
639+
}
573640

574-
const idSpan = document.createElement('span');
575-
idSpan.className = 'neighbor-button__id';
576-
idSpan.textContent = entry.record.id;
577-
578-
const scoreSpan = document.createElement('span');
579-
scoreSpan.className = 'neighbor-button__score';
580-
scoreSpan.textContent = formatSimilarity(entry.similarity);
581-
582-
header.append(idSpan, scoreSpan);
583-
584-
const snippetSpan = document.createElement('span');
585-
snippetSpan.className = 'neighbor-button__snippet';
586-
const contentEntry = getContentEntry(datasetData.contentKey, entry.record.id);
587-
const fullPreview = getContentPreview(datasetData.contentKey, contentEntry);
588-
const preview = truncateText(fullPreview, 120);
589-
snippetSpan.textContent = preview || 'No source text available.';
590-
if (fullPreview && preview !== fullPreview) {
591-
snippetSpan.setAttribute('title', fullPreview);
641+
neighborRecords.forEach((entry) => {
642+
const item = createNeighborListItem(entry.record, datasetData, {
643+
similarity: entry.similarity,
644+
});
645+
if (item) {
646+
fragment.appendChild(item);
592647
}
593-
594-
button.append(header, snippetSpan);
595-
item.appendChild(button);
596-
fragment.appendChild(item);
597648
});
598649

599650
neighborList.appendChild(fragment);
@@ -644,7 +695,7 @@
644695
});
645696

646697
neighbors.sort((a, b) => b.similarity - a.similarity);
647-
return neighbors.slice(0, 10);
698+
return neighbors.slice(0, MAX_NEIGHBOR_DISPLAY);
648699
}
649700

650701
function renderPane(pane, record, datasetMeta, source, options = {}) {

apps/embedding-explorer/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,11 @@
334334
transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
335335
}
336336

337+
.neighbor-button[disabled] {
338+
cursor: default;
339+
opacity: 1;
340+
}
341+
337342
.record-button:hover,
338343
.neighbor-button:hover {
339344
transform: translateY(-1px);
@@ -355,6 +360,14 @@
355360
box-shadow: 0 14px 24px color-mix(in srgb, var(--accent) 35%, transparent);
356361
}
357362

363+
.neighbor-button__self {
364+
font-size: 0.7rem;
365+
font-weight: 600;
366+
letter-spacing: 0.08em;
367+
text-transform: uppercase;
368+
color: color-mix(in srgb, var(--accent-contrast) 75%, transparent);
369+
}
370+
358371
.record-button[data-active="true"] .record-button__meta,
359372
.neighbor-button[data-active="true"] .neighbor-button__snippet {
360373
color: color-mix(in srgb, var(--accent-contrast) 75%, transparent);

tests/embedding-explorer.spec.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,36 @@ test.describe('Embedding Explorer app', () => {
4747
await expect(firstRow.locator('td').nth(1).locator('span')).toHaveText('1.000');
4848
await expect(firstRow.locator('td').nth(2).locator('span')).toHaveText('0.333');
4949
});
50+
51+
test('lists the top ten neighbors for a dataset vector', async ({ page }) => {
52+
await page.goto('/apps/embedding-explorer/');
53+
54+
const datasetSelect = page.getByLabel('Embedding collection');
55+
const recordFilter = page.getByLabel('Filter by id or hash');
56+
await expect(recordFilter).toBeEnabled();
57+
58+
const jokesOptions = await datasetSelect.locator('option').evaluateAll((options) =>
59+
options.map((option) => ({ value: option.value, label: option.label }))
60+
);
61+
62+
for (const option of jokesOptions.filter((entry) => entry.value.startsWith('jokes-'))) {
63+
await datasetSelect.selectOption(option.value);
64+
await expect(recordFilter).toBeEnabled();
65+
66+
await recordFilter.fill('j-0907');
67+
const recordButton = page
68+
.locator('[data-record-list] .record-button')
69+
.filter({ hasText: /^j-0907/ })
70+
.first();
71+
await expect(recordButton).toBeVisible();
72+
await recordButton.click();
73+
74+
const neighborButtons = page.locator('[data-neighbor-list] .neighbor-button');
75+
await expect(neighborButtons).toHaveCount(11);
76+
await expect(neighborButtons.first()).toContainText('Selected vector');
77+
await expect(page.locator('[data-neighbor-status]')).toContainText('Selected vector and top 10 matches for j-0907.');
78+
79+
await recordFilter.fill('');
80+
}
81+
});
5082
});

0 commit comments

Comments
 (0)