Nomic Embed Browser — Cache Inspector

by Zatcepin

HTML

<base target="_blank">

  <div class="container">
    <h1>Nomic Embed Browser</h1>
    <p class="subtitle">Inspect IndexedDB cache, load models, and run embeddings locally</p>

    <!-- Cache Inspector -->
    <div class="card">
      <h2>IndexedDB Cache</h2>
      <div class="cache-grid">
        <div class="stat">
          <div class="stat-label">Total Cached</div>
          <div class="stat-value" id="cacheTotal">—</div>
        </div>
        <div class="stat">
          <div class="stat-label">Files</div>
          <div class="stat-value" id="cacheFiles">—</div>
        </div>
        <div class="stat">
          <div class="stat-label">Model Status</div>
          <div class="stat-value" id="modelStatus">Not loaded</div>
        </div>
        <div class="stat">
          <div class="stat-label">Backend</div>
          <div class="stat-value" id="backendStatus">—</div>
        </div>
      </div>

      <div class="file-list" id="fileList" style="display:none;"></div>

      <div class="btn-row">
        <button class="btn-ghost" id="refreshCacheBtn">Refresh Cache Info</button>
        <button class="btn-danger" id="clearCacheBtn">Clear Cache</button>
        <button class="btn-primary" id="loadModelBtn">Load Model</button>
      </div>

      <div class="progress-wrap" id="progressWrap">
        <div class="progress-bar"><div class="progress-fill" id="progressFill"></div></div>
        <div class="progress-text" id="progressText">0%</div>
      </div>

      <div class="log" id="cacheLog"></div>
    </div>

    <!-- Embedding -->
    <div class="card">
      <h2>Generate Embedding</h2>
      <textarea id="textInput" placeholder="search_query: What is matryoshka embedding?">search_query: What is matryoshka embedding?</textarea>
      <div class="btn-row">
        <button class="btn-primary" id="embedBtn" disabled>Get Embedding</button>
        <button class="btn-ghost" id="copyBtn" disabled>Copy...

CSS

:root {
      --bg: #0f0f11;
      --surface: #1a1a1e;
      --surface-2: #232328;
      --border: #2a2a30;
      --text: #e8e8ec;
      --text-dim: #909098;
      --accent: #5b8cff;
      --accent-hover: #7aa3ff;
      --danger: #ff5b5b;
      --danger-hover: #ff7a7a;
      --success: #4ade80;
      --warn: #fbbf24;
      --radius: 10px;
      --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      --mono: "SF Mono", Monaco, "Cascadia Code", monospace;
    }
    * { box-sizing: border-box; }
    body {
      margin: 0;
      background: var(--bg);
      color: var(--text);
      font-family: var(--font);
      line-height: 1.5;
      padding: 24px;
    }
    .container {
      max-width: 800px;
      margin: 0 auto;
    }
    h1 {
      font-size: 1.5rem;
      margin: 0 0 4px 0;
      font-weight: 600;
    }
    .subtitle {
      color: var(--text-dim);
      font-size: 0.9rem;
      margin-bottom: 24px;
    }

    /* Cards */
    .card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      padding: 20px;
      margin-bottom: 16px;
    }
    .card h2 {
      font-size: 0.85rem;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: var(--text-dim);
      margin: 0 0 12px 0;
      font-weight: 600;
    }

    /* Cache status grid */
    .cache-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
      gap: 12px;
    }
    .stat {
      background: var(--surface-2);
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 14px;
    }
    .stat-label {
      font-size: 0.75rem;
      color: var(--text-dim);
      text-transform: uppercase;
      letter-spacing: 0.06em;
      margin-bottom: 4px;
    }
    .stat-value {
      font-size: 1.1rem;
      font-weight: 600;
      font-family: var(--mono);
    }
   ...

JavaScript

import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';

    // ── Config ──
    const MODEL_ID = 'nomic-ai/nomic-embed-text-v1.5';
    const DB_NAME = 'transformers-cache';
    const DB_VERSION = 1;

    // ── State ──
    let extractor = null;
    let isLoading = false;

    // ── DOM refs ──
    const $ = id => document.getElementById(id);
    const cacheTotal = $('cacheTotal');
    const cacheFiles = $('cacheFiles');
    const modelStatus = $('modelStatus');
    const backendStatus = $('backendStatus');
    const fileList = $('fileList');
    const progressWrap = $('progressWrap');
    const progressFill = $('progressFill');
    const progressText = $('progressText');
    const cacheLog = $('cacheLog');
    const embedBtn = $('embedBtn');
    const copyBtn = $('copyBtn');
    const embedOutput = $('embedOutput');
    const embedLog = $('embedLog');

    // ── Helpers ──
    function fmtBytes(b) {
      if (b === 0) return '0 B';
      const k = 1024;
      const sizes = ['B','KB','MB','GB'];
      const i = Math.floor(Math.log(b) / Math.log(k));
      return parseFloat((b / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }

    function log(el, msg, type = '') {
      el.textContent = msg;
      el.className = 'log ' + type;
    }

    function setLoading(loading) {
      isLoading = loading;
      $('loadModelBtn').disabled = loading;
      $('clearCacheBtn').disabled = loading;
      $('refreshCacheBtn').disabled = loading;
      if (loading) {
        progressWrap.classList.add('active');
      } else {
        progressWrap.classList.remove('active');
        progressFill.style.width = '0%';
        progressText.textContent = '0%';
      }
    }

    // ── IndexedDB Inspection ──
    async function inspectCache() {
      cacheTotal.textContent = '—';
      cacheFiles.textContent = '—';
      fileList.style.display = 'none';
      fileList.innerHTML = '';

...