# Embedding Drift Banner

Embedding Drift Banner [#embedding-drift-banner]

The **Embedding Drift Banner** is a warning panel shown when the active embedding model is incompatible with vectors already stored (the model changed or dimensions mismatch). It explains the stored-vs-current model ids, shows a reindex progress bar + phase label while re-embedding, and offers "Re-embed All" / Cancel. Bind to `useReindex` + your compatibility check.

Preview [#preview]

```tsx
'use client';

import { useEffect, useState } from 'react';

import { EmbeddingDriftBanner } from '@/components/embedding-drift-banner';

/**
 * Demo for EmbeddingDriftBanner. Simulates a re-embed run advancing through the
 * embedding phase. Wire onReindex/progress to useReindex in your app.
 */
export default function EmbeddingDriftBannerDemo() {
  const [running, setRunning] = useState(false);
  const [completed, setCompleted] = useState(0);
  const total = 240;

  useEffect(() => {
    if (!running) return;
    const id = setInterval(() => {
      setCompleted((c) => {
        if (c >= total) {
          setRunning(false);
          return 0;
        }
        return Math.min(total, c + 20);
      });
    }, 300);
    return () => clearInterval(id);
  }, [running]);

  return (
    <EmbeddingDriftBanner
      storedModelId="Xenova/bge-small-en-v1.5"
      currentModelId="Xenova/all-MiniLM-L6-v2"
      isReindexing={running}
      progress={running ? { completed, total, phase: 'embedding' } : null}
      onReindex={() => {
        setCompleted(0);
        setRunning(true);
      }}
      onCancel={() => setRunning(false)}
    />
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/local-first/embedding-drift-banner
```

Data source & dependencies [#data-source--dependencies]

**Data source:** renders the drift/reindex state you pass and emits Re-embed/Cancel — works with any backend. Recommended producer: `useReindex` from `@localmode/react` (on-device, optional).

* `lucide-react` — icons
* `clsx` + `tailwind-merge` — via the shared `cn()` util (installed automatically as a registry dependency)

Files installed [#files-installed]

* `embedding-drift-banner.tsx` — the `EmbeddingDriftBanner` component
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**EmbeddingDriftBanner**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `storedModelId` | `string` | — | **Required.** Model id used for the vectors already stored. |
| `currentModelId` | `string` | — | **Required.** The active embedding model id. |
| `isReindexing` | `boolean` | — | Whether a reindex is currently running. |
| `progress` | `object \| null` | — | Live reindex progress (null when not started). |
| `onReindex` | `function` | — | Fired when the user starts a re-embed. |
| `onCancel` | `function` | — | Fired when the user cancels a running reindex. |

**ReindexProgressLike**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `completed` | `number` | — | **Required.** Documents processed so far. |
| `total` | `number` | — | **Required.** Total documents to process. |
| `skipped` | `number` | — | Documents skipped (no text to re-embed). |
| `phase` | `"indexing" \| "embedding"` | — | **Required.** Current phase. |

Examples [#examples]

Bound to useReindex [#bound-to-usereindex]

```tsx
const { isReindexing, progress, reindex, cancel } = useReindex({ db, model });
<EmbeddingDriftBanner
  storedModelId={stored}
  currentModelId={current}
  isReindexing={isReindexing}
  progress={progress}
  onReindex={reindex}
  onCancel={cancel}
/>
```

Customization [#customization]

The banner uses the `amber` palette to read as a warning. The phase label maps `useReindex` phases (`embedding` / `indexing`) to human strings in `PHASE_LABEL`. Styled entirely with shadcn/ui CSS-variable utilities so it inherits your theme — because you own the copied file, every class and threshold is yours to change.