Flavors¶
A flavor bundles your customisations into a single switchable, exportable, importable profile. One flavor for cost estimating with the quantity-takeoff extension, your fire-rating lens, and CSV-with-semicolons defaults. Another for design review with markup tools and a different lens library. Switch between them with one click.
This guide covers managing flavors through the status-bar chip. For the technical model (three-way merge semantics, snapshot retention, the .iflv envelope) see the RFC.
What's in a flavor?¶
A flavor stores:
- Extension list — which
.iflxbundles are part of the flavor (with their granted capabilities) - Lenses — saved visualisation states (color by type, isolate by storey, etc.)
- Saved queries — named filter expressions
- Keybindings — keyboard shortcut overrides
- Layout state — panel sizes, dock arrangement
- Settings — viewer preferences (units, separators, default colors)
- Prompt overlay — durable notes the AI assistant sees in every chat (see Privacy)
Switching a flavor:
- Disables every installed extension that's not in the target's list.
- Enables + loads every extension that is in the target's list.
- Applies the target's lenses, settings, keybindings, layout.
- Moves the active-flavor pointer.
If any step fails the host rolls back: the previously-active flavor's state is restored, the pointer doesn't move, and the UI shows a structured error.
The status-bar chip¶
The status bar at the bottom of the viewer shows a small palette icon with the active flavor's name (or Default when none is active). Click it to open the Flavors dialog.
Visual reference
The chip appears in the status bar at the bottom-right of the viewer window, between the WebGPU indicator and the version label. A palette icon precedes the active flavor name.
The Flavors dialog¶
List view¶
Every flavor appears with:
- Name + active badge (when it's the current flavor)
- Stable id (used for
.iflvround-trips) - Description if set
- Counts: extensions / lenses / queries / last-updated date
- Per-row controls: Activate, Export, Delete (active flavor can't be deleted)
Header actions:
| Button | What it does |
|---|---|
| Import | Load a .iflv file → preview screen |
| Reset | Create / restore the baseline flavor with no extensions or overrides |
Activate¶
Click Activate on any non-active row to switch. The host:
- Locks the UI briefly while the switcher runs.
- Reports each disabled / enabled extension in the toast.
- On failure, shows which extensions could not load and rolls back to the prior flavor.
Already-correct extensions are not reloaded
If an extension is part of both the source and target flavor and already enabled, the switcher leaves it alone — no unnecessary deactivate / reactivate cycle.
Export¶
Click the download icon on a flavor row. The flavor serialises to a .iflv file (gzipped JSON envelope with a magic header + version) and downloads with the filename <flavor-id>.iflv.
What goes into the export:
- The flavor's full state (extensions list, lenses, queries, keybindings, layout, settings, overlay)
- No extension bundle bytes by default — the
.iflvreferences extensions by id + version. To produce a portable export that includes the bundles, use the future--include-bundlesflag (Phase 5). - No personal data — the action log and audit log do not travel with a flavor.
Import¶
Click Import in the header (or drop a .iflv on the dialog). The viewer:
- Decompresses the envelope, verifies the magic + version, runs the flavor through the validator.
- Shows a preview: name, id, description, content counts.
-
Offers three actions:
- Merge… — three-way merge against your active flavor (see Merging)
- Save as new — import with a fresh id (
<original>.imported-<ts>), no collisions - Replace existing — overwrite a flavor with the same id
The preview screen also surfaces any embedded summary the exporter wrote (a free-text note explaining what the flavor is for).
Reset¶
Reset restores the baseline flv.default flavor — empty extensions, no lenses, no overrides — and activates it. Your other flavors are preserved; reset only touches the baseline. Use it as a panic button when a flavor is broken or when you want to start from scratch.
Snapshots
Each time you save changes to a flavor (rename, edit overlay, change settings), the storage retains the prior version as a snapshot. The current implementation keeps the most recent 10 per flavor. The restore UI is a follow-up; the snapshots are queryable via the library API today.
Merging¶
The merge dialog implements a three-way merge between:
- base — the stored ancestor (whichever flavor in your library has the same id as the incoming one, if any; otherwise falls back to your active flavor for a two-way compare)
- theirs — the incoming
.iflvyou just imported - ours — your currently-active flavor
For each conflict you pick a winner: theirs, ours, or base. Conflict kinds:
| Kind | What conflicts | Default winner |
|---|---|---|
extension_version |
Same extension id, different version | Ours |
extension_capabilities |
Same extension, different granted caps | Ours |
lens |
Same lens id, different definition | Ours |
saved_query |
Same query id, different filter | Ours |
keybinding |
Same key, different command | Ours |
setting |
Same key, different value | Ours |
For each conflict, the dialog renders a 3-cell (or 2-cell when no base is available) chooser. Pick Theirs / Ours / Base per row, then click Save merged flavor.
The merged result is saved under a fresh id (<their-id>.merge-<ts>) so neither input is overwritten silently. You can activate it after the dialog closes.
Clean merges don't need conflict resolution
If the imported flavor and your active flavor have no overlap (different extensions, different lens ids), the dialog shows "Clean merge — no conflicts" and Save creates the merged result without any choices to make.
The .iflv format¶
.iflv is a gzipped JSON envelope:
{
"format": "iflv",
"version": 1,
"summary": "Optional free-text note explaining the flavor",
"flavor": { ... full Flavor object ... },
"extensionBundles": {
"com.example.my-ext": "<base64 of bundle bytes>"
}
}
- Magic + version are checked before any other parsing — a corrupt or mismatched envelope rejects with a structured error, never crashes the viewer.
- Extension bundles are optional. A "thin" export lists ids only; a "thick" export embeds the bundle bytes (Phase 5).
- Max uncompressed size is enforced during unpack to defend against decompression bombs.
The full schema lives in packages/extensions/src/flavor/types.ts; validateFlavor runs on every import.
Safe mode and flavors¶
Safe mode (?safe=1 in the URL) skips automatic flavor activation. The active-flavor pointer remains set in storage — when you reload without the flag, the original flavor reactivates. Use safe mode if a flavor switch leaves the viewer in a bad state.
Troubleshooting¶
I switched flavors and an extension didn't load
Open the Audit tab — failed activations are recorded with the reason (capability denied / bundle bytes missing / entry script not parseable). The switcher already rolled back to your prior flavor; the error is informational.
Import shows 'unsupported version'
.iflv files include a schema version. If your viewer is older than the version the file was produced with, you can't import it cleanly. Update the viewer, or ask the author to export against an older schema.
Merge said 'Clean merge' but I expected conflicts
The three-way merger compares by stable identifiers (extension id, lens id, query id, etc.). If the incoming flavor uses different ids for what you considered "the same lens", they'll be treated as additions, not conflicts. You can resolve this by renaming on either side before re-merging.
My exported .iflv won't import on someone else's viewer
Two common causes: schema version mismatch (see above), or the extensions in the flavor aren't installed on the target viewer. A "thin" export references extensions by id — the recipient needs to install the actual .iflx files separately.
Next steps¶
- Extensions — install, run, manage individual extensions
- Authoring extensions — build your own
- Privacy — manage the action log and prompt overlay