diff --git "a/index.html" "b/index.html" --- "a/index.html" +++ "b/index.html" @@ -1,1266 +1,1264 @@ - + - Browser RAG Visualizer + TokenLens — LLM Tokenizer Playground + + + - - -
-
-
-

Browser RAG Visualizer

-

All retrieval, reranking, and generation steps run locally in the browser. Models are cached after the first load.

-
-
- Models: idle - Vector DB: local - Cache: on - Backend: checking -
-
- -
-
-
-

Chat

- Qwen3.5-0.8B + retrieved context -
- -
- -
-
Selected context snippets
-
-
- -
- -
- - -
-
-
- -
-
-
-

Ingest + Vector Table

- Insert, embed, and visualize rows -
-
-
-
- - -
-
- - -
-
- - -
-
- - -
- -
- -
- Pipeline idle - Top K - Rerank K - Chunk size -
- -
- - - - - - - - - - - - - -
idtextvectormetadatadatesimrerank
-
-
-
- -
-
-
- Top-k and reranked picks - Animated rows move here -
-
-
-
-
-
-
- Run log - Retrieval → rerank → answer -
-
-
-
-
-
- -
-
-

Node Flow Editor

- Drag nodes, tweak settings per stage -
-
-
- -
-
Tip: this is a single-page static build. In HF Spaces, just serve this HTML file.
-
-
-
-
-
- - + }); +} + +// ── Dropdown toggle ──────────────────────────────────────── +[0, 1].forEach(idx => { + const $btn = document.getElementById(`dropdown-btn-${idx}`); + const $menu = document.getElementById(`dropdown-menu-${idx}`); + $btn.addEventListener('click', (e) => { + e.stopPropagation(); + const otherIdx = 1 - idx; + document.getElementById(`dropdown-menu-${otherIdx}`).classList.remove('open'); + $menu.classList.toggle('open'); + }); +}); + +document.addEventListener('click', () => { + document.querySelectorAll('.dropdown-menu').forEach(m => m.classList.remove('open')); +}); +document.querySelectorAll('.dropdown-menu').forEach(m => { + m.addEventListener('click', e => e.stopPropagation()); +}); + +// ── Load buttons ─────────────────────────────────────────── +[0, 1].forEach(idx => { + const $btn = document.getElementById(`load-btn-${idx}`); + const $input = document.getElementById(`search-input-${idx}`); + function doLoad() { + const id = $input.value.trim(); + if (!id) { showToast('Please enter a model ID'); return; } + loadModel(idx, id); + } + $btn.addEventListener('click', doLoad); + $input.addEventListener('keydown', e => { if (e.key === 'Enter') doLoad(); }); +}); + +// ── View Toggles ─────────────────────────────────────────── +document.querySelectorAll('.toggle-btn').forEach(btn => { + btn.addEventListener('click', () => { + const panelIdx = parseInt(btn.dataset.panel); + const group = document.getElementById(`toggle-group-${panelIdx}`); + group.querySelectorAll('.toggle-btn').forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + panels[panelIdx].view = btn.dataset.view; + runTokenize(); + }); +}); + +// ── Input Handling ───────────────────────────────────────── +async function runTokenize() { + const text = $input.value; + $charCount.textContent = text.length; + await Promise.all([ + tokenizeForPanel(0, text), + panel1Visible ? tokenizeForPanel(1, text) : Promise.resolve() + ]); +} + + $input.addEventListener('input', () => { + $charCount.textContent = $input.value.length; + clearTimeout(debounceTimer); + debounceTimer = setTimeout(runTokenize, 280); +}); + +// ── Sample Buttons ───────────────────────────────────────── +document.querySelectorAll('.sample-btn').forEach(btn => { + btn.addEventListener('click', () => { + $input.value = SAMPLES[btn.dataset.sample] ?? ''; + $input.focus(); + runTokenize(); + }); +}); + +// ── Panel Toggle ─────────────────────────────────────────── + $panelToggle.addEventListener('click', () => { + panel1Visible = !panel1Visible; + $panelToggle.classList.toggle('active', panel1Visible); + if (!isMobile()) { + $panel1.style.display = panel1Visible ? '' : 'none'; + } + const $searchGroup1 = document.getElementById('search-group-1'); + $searchGroup1.style.display = panel1Visible ? '' : 'none'; + if (panel1Visible) { + $mainGrid.classList.remove('single-panel'); + } else { + $mainGrid.classList.add('single-panel'); + } + updateMobileTabBState(); + runTokenize(); +}); + +// ── Theme Toggle ─────────────────────────────────────────── +const $iconSun = document.getElementById('theme-icon-sun'); +const $iconMoon = document.getElementById('theme-icon-moon'); + +function setTheme(theme) { + document.documentElement.dataset.theme = theme; + if (theme === 'light') { + $iconSun.style.display = 'none'; + $iconMoon.style.display = 'block'; + } else { + $iconSun.style.display = 'block'; + $iconMoon.style.display = 'none'; + } + runTokenize(); +} + + $themeToggle.addEventListener('click', () => { + const current = document.documentElement.dataset.theme; + setTheme(current === 'dark' ? 'light' : 'dark'); +}); + +// ── Init ─────────────────────────────────────────────────── +buildDropdowns(); + $overlay.classList.add('hidden'); + $input.value = ''; + +setTheme('dark'); + +// Set initial mobile state +handleResize(); + +document.getElementById('search-input-0').value = MODELS[0].id; +loadModel(0, MODELS[0].id); + \ No newline at end of file