---
title: ContextDock (Native) — local-first library (in development)
---

# ContextDock (Native) — local-first library (in development)

## What it is

**Status: in-development — default-hidden.** Gated via the `contextdock-native`
unreleased-feature entry (`contextdockNativeEnabled` toggle in Settings → Lab, or
launch with `AMC_SHOW_CONTEXTDOCK_NATIVE=1`). Ships dark until flipped to `shipped`
in `src/shared/unreleased-features.ts`.

The **native ContextDock** is a local-first rebuild of the ContextDock library UI that
reads Omniscio's **own authoritative store** — documents, bundles, lists, tags, and
on-demand context assembly — rather than the separate ContextDock web app. It is
distinct from the vendored [ContextDock integration](contextdock.md) (`__contextdock__`,
which links bundles/lists snapshotted from the remote CLI); the native surface owns its
data end-to-end over the local `contextdock-store:*` IPC.

It adds a **"ContextDock (Native)"** sidebar virtual project (sentinel
`__contextdock_native__`, nested in the **Productivity** group). Its panel
(`ContextDockNativeView`) is NON-spawnable — it hosts no Claude session. Tab navigation
lives in a sub-sidebar (`ContextDockNativeSubSidebar`) with five panes:

- **Library** — browse, open, **add**, and **edit** documents: the "Add docs" button opens the import dialog (see [Adding documents](#adding-documents-the-add-docs-dialog)), an "Add files" button + drag-and-drop imports any local file type (text/office/pdf + image OCR + audio/video transcription), and the detail panel edits a selected doc (per-doc delete / retag / re-summarize — see [Editing a doc](#editing-a-doc)).
- **Bundles** — hand-curated collections of docs (create / rename / add-remove docs / **reorder docs** / pin / delete / duplicate, with a per-doc content-version override).
- **Lists** — tag-driven views over the docs (create / rename / manage driving tags + any-all match / add-remove-**reorder** extra docs / **pin** / delete / duplicate, with **persisted** per-doc content-version overrides).
- **Tags** — manage the tag roster.
- **Export** — assemble a selection into one context payload and copy / download it.

## Where to find it

It appears as its own ContextDock (Native) row in the sidebar, nested under the Productivity
group, and its panel fills the pane with a five-pane sub-sidebar for Library, Bundles, Lists, Tags
and Export. Because it is still in development it ships hidden: you have to switch it on from the
Labs area of Settings before the row shows up, and until then it is simply not there.

## How it behaves

### Adding documents (the "Add docs" dialog)

The Library's **Add docs** button opens `ContextDockNativeAddDocsDialog` — the ongoing
import surface (distinct from the one-time cloud migration below), matching the original
ContextDock site's import loop. One dialog, four modes via a SegmentedControl:

- **Paste URL(s)** — one Google Doc URL imports it (multi-tab docs auto-flatten
  server-side); multiple URLs bulk-import with an imported / updated / skipped / failed summary.
- **Google Drive folder** — paste a folder URL, **Browse** lists its Google Docs, pick a
  subset, import.
- **Manual** — a title + markdown → a local doc.
- **Upload** — a `.md` / `.txt` / `.pdf` / `.doc(x)` file → its extracted text (read to
  base64 in the renderer, size-guarded via `MAX_UPLOAD_BYTES`).

Optional tags apply across every mode; `Ctrl/Cmd+Enter` submits. Errors are humanized (a
not-connected Google account shows a "connect it in Settings" toast, never a raw error).
The dialog drives the Stage-3 import backend through `useContextDockStoreImport` over six
`contextdock-store:` import channels (`import-url`, `bulk-import-urls`, `list-folder`,
`import-folder`, `create-manual`, `upload-file`) — typed in the response-map,
native-flag-gated, and WS-blocked. After a successful import the handlers emit
`contextdock-store:changed {resource:'docs'}` (ONLY when a doc was actually written), so
the Library refetches on its own. The five import COMMAND channels are cli-parity
`deferred-no-route`; the reads (`list-folder` / `preview-url` / `discover-tabs`) stay out
of scope.

**Deferred (backend ready, not yet UI-wired):** selective per-tab import
(`discover-tabs` / `import-tabs`) and the dry-run `preview-url` — `import-url` already
auto-flattens multi-tab, the source product's default. Post-import doc editing
(delete / retag / private-toggle / manual re-condense) and background auto-sync also stay
deferred; the Library rows themselves are still read-only.

Files: `ContextDockNativeAddDocsDialog.tsx`, `useContextDockStoreImport.ts`,
`add-docs-helpers.ts`, wired from `LibraryPane.tsx`.

### Editing bundles and lists

Both panes are fully editable and reach parity with the original ContextDock's bundle/list
management, reusing the `upsert-bundle` / `upsert-list` channels (no dedicated edit channel):

- **Pin / unpin** — an inline pin toggle sits on each **list** row and its detail header (the
  same affordance bundles already have); pinned items sort to the top of their roster.
- **Reorder docs** — move-up / move-down controls order a **bundle's** member docs and a **list's
  extra** docs (a list's tag-matched docs are a live query, so only the explicit extras reorder).
  Order is stored in the `docIds` array and drives the assembled output order. Backed by the pure
  `moveInArray` helper (`reorder.ts`).
- **Per-doc version override** — pick Original / Key Points / Summary per doc. **Bundles and lists
  both PERSIST this** now (a `doc_versions_json` column on each table); the choice survives a reload
  and feeds the context assembler. (Previously a list's choice was renderer-local and lost on refresh.)

Every edit rides `saveList` / `saveBundle` (humanized errors, no optimistic mutation) and the
`contextdock-store:changed` push refetches the affected pane. New controls carry `data-ui-anchor`
entries in the co-located `*.ui-anchors.ts`. Files: `ListsPane.tsx`, `BundlesPane.tsx`, `reorder.ts`.

### Editing a doc

The Library's detail panel (`LibraryDocDetail`) makes a selected doc editable with three per-doc actions:

- **Delete** — a confirm dialog, then a soft-delete (`softDeleteContextDockStoreDoc`); the doc drops out of the
  Library (every read filters `is_deleted=0`, so it also disappears from any bundle/list automatically) and the
  selection clears.
- **Edit tags** — toggle chips over the workspace tags; toggling patches `updateContextDockStoreDoc {tags}` (doc
  tags are stored as NAMES). Tag filtering updates live.
- **Re-summarize** — `requestCondense([id], {force:true})` + a queue drain, re-running the AI condensing
  (keyPoints / summary / aiSummary) on the user's own Claude credential (bypasses smart-skip + the auto-condense
  toggle). If no API-key account is connected the sweep no-ops; the button shows a "re-summarizing" state, never a
  faked completion.

These ride three `contextdock-store:` channels — `delete-doc`, `update-doc`, `recondense-doc` — through
`useContextDockStoreDocActions` (humanized errors, no optimistic mutation). Each emits
`contextdock-store:changed {resource:'docs'}`; the condense write-back now emits it too (after its transaction), so
a finished re-summary shows live. The detail panel re-loads the doc on that push (`useIpcListener`). The three write
channels are native-flag-gated, WS-blocked, and cli-parity `deferred-no-route`. Files:
`useContextDockStoreDocActions.ts`, `LibraryDocDetail.tsx`.

### Importing your ContextDock cloud library (opt-in, Stage-5 cutover)

If you previously used the vendored (cloud) ContextDock, the Library tab shows an opt-in
**"Import your ContextDock cloud library"** card — but ONLY when a vendored ContextDock
API key still exists (`getApiKey()` in the vendored `auth.ts`). Clicking it confirms
first (the import spends your ContextDock cloud API quota), then reuses that key to pull
your cloud documents, bundles, lists and tags into the local store via the one-time
importer (`runContextDockStoreMigration`).

The importer is **idempotent, resumable, and paced** (400 ms/doc), so re-running is safe
and an interrupted run continues from where it left off — a very large library (>~1500
docs) that exceeds the IPC handler's abandon ceiling simply resumes on the next "Import".
A single migration runs at a time (a main-process single-flight guard refuses a concurrent
run). The vendored API key is read + used entirely in the main process; only its presence
(a boolean) ever crosses IPC. Once a run completes, the card collapses to a quiet
"imported" summary with a **Re-import** affordance. Wired in
`src/main/services/contextdock/store-migration-trigger.ts` +
`src/main/ipc/contextdock-store-migration-handlers.ts`; the card lives in
`src/renderer/src/features/contextdock-native/MigrationCard.tsx`.

### Adding files (any file type)

The Library's **"Add files"** button (`ContextDockUploadButton`) imports local files as context docs —
**any file type**:

- **Text, code, and data** (`.md`, `.py`, `.json`, `.yaml`, `.csv`, `.html`, notebooks, logs, config, or
  any file whose bytes are text) — decoded and imported immediately. The extension is only a hint; a
  byte-sniff decides, so an extension-less or mislabeled text file still imports and a true binary is
  rejected with a clear message.
- **Office + PDF** (`.docx`, `.pptx`, `.xlsx`, `.pdf`, …) — extracted to markdown.
- **Images** (`.png`, `.jpg`, `.gif`, `.webp`) — read with Claude vision (OCR + a short description).
  This costs about a cent per image on your Claude credit and sends the image to Claude; uploading a
  large batch confirms first. Audio, video, and text stay free and on-device.
- **Audio + video** (`.mp3`, `.mp4`, `.mov`, `.wav`, …) — transcribed on-device with Whisper (free,
  private). Needs system **ffmpeg** (install from Settings → Connected Tools if missing).

Images and audio/video process in the **background**: the doc appears immediately as "⏳ Processing…" and
fills in with the OCR text or transcript when ready (a `contextdock-store:changed` push refreshes the
row). You can also **drag files straight onto the Library**. Large media is streamed by file path (not
loaded into memory), and a transcription interrupted by an app restart is recovered on the next launch.
All of this reuses existing Omniscio building blocks — no new dependencies.

### Availability

**Desktop-only.** The `contextdock-store:*` reads and writes are blocked on the
mobile/web WS bridge (desktop-blocked until cutover), so the row + panel are dropped in
Web Access / mobile mode and the surface declares `mobile: desktop-only`.

## For agents

### Data + IPC

The store is backed locally and read/written over the `contextdock-store:*` channel
family: reads (`list-docs`, `get-doc`, `list-bundles`, `list-lists`, `list-tags`,
`assemble`) and read-modify-write mutations (`upsert-bundle` / `delete-bundle`,
`upsert-list` / `delete-list`, `upsert-tag` / `delete-tag`), each of which emits a
scoped `contextdock-store:changed` push so the pane hooks refetch only the affected
resource. Every handler is gated by the native-ContextDock feature flag.

The Stage-5 cutover adds two more: `get-migration-status` (a read — does a vendored
ContextDock API key exist, plus any saved import progress) and `run-migration` (the
command that performs the one-time cloud→local import). Both are native-flag-gated and
desktop-blocked on the WS bridge; `run-migration` is cli-parity `deferred-no-route`.

## Related

The older, cloud-backed version of this same surface — bundles and lists snapshotted from a remote
service rather than owned locally — is on the [ContextDock](contextdock.md) page, and the two are
separate sidebar rows you can tell apart by name. A different local-first library of your own
material, the knowledge vault, is described on the [KMS](kms.md) page.