Skip to content

Browser Extension

Chrome MV3 · Search + tagging · Claude.ai + ChatGPT

The loci browser extension is the collector: it reads your AI chat conversations as you have them, indexes them locally, and makes them available for search.

What it does not do: make any network calls. Everything stays in your browser.


What it does

The extension attaches a MutationObserver to conversation containers on supported platforms. As you chat, it:

  1. Detects new conversation turns (user + assistant messages) via DOM mutation events
  2. Extracts message content and metadata (timestamp, role, conversation ID)
  3. Sanitises content (strips scripts, normalises whitespace)
  4. Writes raw conversation data to IndexedDB (origin-isolated storage)
  5. Updates the MiniSearch 7.x index incrementally
  6. Serialises the updated index to chrome.storage.local

The extension provides two search surfaces: a quick-access overlay (triggered by keyboard shortcut) and a persistent side panel for deeper navigation.


Supported platforms

PlatformURL patternsSelector strategyStatus
Claude.aiclaude.ai/*[data-testid="conversation-turn-list"] container, [data-testid="user-message"] / [data-testid="assistant-message"] for turnsTargeted
ChatGPTchatgpt.com/*, chat.openai.com/*article[data-turn] (primary), [data-message-author-role] (fallback)Targeted
Geminigemini.google.com/*:v0.2 planned

Selectors are maintained in src/platforms/platforms.config.ts. This is the first file to update when a site redesigns.


Build from source

bash
git clone https://github.com/huximaxi/Loci
cd Loci/packages/extension
npm install
npm run build

Load the built extension:

  1. Navigate to chrome://extensions
  2. Enable "Developer mode" (toggle in top-right)
  3. Click "Load unpacked"
  4. Select the packages/extension/dist/ directory

The extension icon appears in your toolbar. Pin it for quick access to the side panel.


The command-K overlay

Press Cmd+K (Mac) or Ctrl+K (Windows/Linux) on any supported AI chat page to open the search overlay.

ShortcutAction
Cmd/Ctrl+KOpen overlay
EscapeClose overlay
EnterOpen selected conversation
Up/DownNavigate results
Cmd/Ctrl+EnterOpen in loci desktop app

The overlay renders in Shadow DOM, isolated from page styles. Results display: conversation title, platform icon, date, and matched excerpt with highlighted terms.

Click "Open in loci" to jump directly to the conversation in the desktop app (if installed) or open it in a new tab.


The side panel

Open via the toolbar icon or Cmd+Shift+L (Mac) / Ctrl+Shift+L (Windows).

The side panel persists across page navigation. Features:

  • Full conversation list: browse all indexed conversations, sorted by recency
  • Search: same MiniSearch-powered search as the overlay
  • Tag management: add, remove, and filter by tags
  • Room assignment (Wizard tier): drag conversations into rooms, or right-click to assign
  • Crystallise action: convert a conversation into a permanent locus

Permissions explained

PermissionWhy
storageRead/write chrome.storage.local for the serialised search index
unlimitedStorageRemove the default 10 MB cap on chrome.storage.local: required for heavy users
scriptingInject content scripts dynamically (MV3 requirement)
activeTabAccess the current tab's DOM when the user invokes the extension
sidePanelRegister and display the persistent side panel
Host permissions (*://*.claude.ai/*, etc.)Run content scripts on supported platforms

No permissions beyond what is required. No webRequest, no tabs (beyond activeTab), no history, no cookies.


Storage architecture

StoreTechnologyContentsApprox. size (2000 conversations)
Raw contentIndexedDBFull conversation text + metadata~50 MB
Search indexchrome.storage.localSerialised MiniSearch blob~10 MB
Tagschrome.storage.local{ conversationId: string[] }~500 KB

IndexedDB is origin-isolated to chrome-extension://{extension-id}/. Other extensions and websites cannot access it.


MV3 service worker lifecycle

Chrome MV3 terminates service workers after 30 seconds of inactivity. This would destroy any in-memory search index.

loci handles this cleanly:

  1. After every index update, call index.toJSON() and write to chrome.storage.local
  2. On service worker wake (triggered by any extension event), check if index is loaded
  3. If not, call MiniSearch.loadJSON(stored) to restore the index

Restoration cost: ~50ms for 2000 conversations. Imperceptible.


Known limitations

v0.1 limitations

  • No retroactive import: the extension only indexes conversations that occur after installation. Existing chat history is not imported.
  • No offline export: JSON/Markdown export is planned for v0.2.
  • Chrome only: Firefox and Safari ports are planned for v1.0.

Roadmap

VersionFeatures
v0.1Chrome · Claude.ai + ChatGPT · Search + tagging · Overlay + side panel
v0.2Full-text search · Gemini support · Export to JSON/Markdown
v0.3Semantic search (local embedding model) · Room assignment UI polish
v1.0Chrome Web Store submission · Firefox port · Safari Web Extension

Architecture

packages/extension/
├── manifest.json
├── src/
│   ├── background/
│   │   └── service-worker.ts     # MiniSearch instance, IndexedDB read/write
│   ├── content/
│   │   ├── claude.ts             # MutationObserver for claude.ai
│   │   ├── chatgpt.ts            # MutationObserver for chatgpt.com
│   │   └── overlay.ts            # Cmd+K search overlay (Shadow DOM)
│   ├── sidepanel/
│   │   └── index.html            # Persistent side panel UI
│   ├── platforms/
│   │   └── platforms.config.ts   # DOM selector map (single source of truth)
│   └── shared/
│       ├── index.ts              # MiniSearch wrapper + IndexedDB helpers
│       └── types.ts              # Conversation, Turn, Tag types
└── vite.config.ts                # vite-plugin-web-extension config

Known risks

RiskMitigation
DOM selector instabilityAll selectors centralised in platforms.config.ts. Quarterly maintenance releases planned.
Site CSP blocking injected UIShadow DOM isolation. Extension content scripts run in an isolated world, not subject to page CSP.
IndexedDB read by other extensionsOrigin-isolated to chrome-extension://{id}/. Inaccessible to other extensions and websites.
CWS rejectionStandard MV3 permissions, no remote code execution, privacy policy stating no data leaves device.

See Security for the full security model.

Built by Hux × Vesper · Apache 2.0