Decks (AI presentation builder) (part 3)
Decks is a Marketplace plugin: installing it from Settings → Plugins also switches it on. It runs in a sandboxed panel that reaches the host only through its bridge calls, within fixed file-size and budget limits. Deleting a deck removes it for good, with its slides, their earlier versions and its build records.
What it is
This is part 3 of the Decks (AI presentation builder) page. It covers how Decks is installed and switched on, the sandboxed plugin panel it runs in (called the island), what that panel reaches on the host and what it may read of your work, and what happens when you delete a deck. How a deck is made, edited and published is in the first part of this page, and export and the data model are in part 2.
Where to find it
Decks is a Marketplace plugin: installing it from the plugin Marketplace in Settings → Plugins also switches it on, and it then appears as its own row in the left sidebar. You can switch it off and on again in Settings → Plugins.
How it behaves
How it is installed and switched on
Decks is a first-party plugin (src/plugins/decks/manifest.json, plugin.id: 'decks') delivered through the Marketplace. Omniscio never registers the copy in its own source tree as a built-in plugin (decks is listed in BUILTIN_EXCLUDED_PLUGIN_IDS), so installing it from the Marketplace is what adds decks to enabledPlugins and switches it on. Until then it is off (enabledPlugins starts empty); it is not gated by an unreleased-feature flag.
Switching it on creates the __plugin_decks__ sidebar row (via the standard ensurePluginSidebarRow), and switching it off removes the row. The dashboard shows Decks the way it shows any plugin with a user interface: it loads the plugin's own ui/index.html, the island, in the shared plugin webview host. The main-process CLI read routes mirror the same state: they answer 403 unless settings.enabledPlugins includes 'decks'.
People who used Omniscio's old built-in Decks are moved over by a one-time startup migration (migrateDecksBuiltin). Before the Marketplace copy is installed, it copies their decks from Omniscio's old Decks tables into the plugin and checks the copy by content (the deck count, then each deck's card and block counts), so nobody loses their decks.
The old Decks webview switch in Settings → Lab now does nothing. It once chose between a native Decks panel and the plugin panel (the decks-webview feature, setting decksWebviewEnabled). The dashboard no longer has a native Decks panel, so the plugin panel is the only one; the switch and its setting are still in the code and default to on, but nothing uses them.
How the panel fits and looks
On a narrow panel the island shows one pane at a time (2026-09-28). Below 768px of the
island's own width the frame is a list/detail switch: with nothing open the deck library fills the
panel, and while a screen is open (a deck in the editor, the New deck flow or Deck settings) that
screen fills it and the library steps aside. Each such screen carries its own way back: the
editor's Decks button slides the same library back as a drawer, every New deck stage has a
Back, and Deck settings has a Back. Every narrow rule keys on one data-decks-main mark
(settings, create, editor or empty) that DecksView writes on its row, because a rule that
walked the frame's nesting broke silently when the settings screen added a wrapper. The rules and
the checks that pin them are recorded in src/plugins/decks/CLAUDE.md.
Decks follows the app's Text Size and its button colours (2026-09-28). The island sizes its
whole chrome from the app's Text Size (the host's --app-font-size, mapped onto the page root), so
headings, body text, spacing and the library width grow together; slides size in container units
and do not move. Its primary buttons paint from the label and fill the app itself uses, so they stay
readable on pale accents and on a white accent (dark Vercel Clean used to leave them as blank white
pills), and its native checkboxes follow the app's light/dark scheme and accent. How the values
reach a plugin: plugin-webview-theme-contract.md.
Every coloured word in Decks is readable on every app theme (2026-09-29). Outline buttons
(Present, Share, Download, New deck, the slide toolbar), status chips (Ready, Error, Outline), the
delete question and the load-error strip used to paint their words in raw accent or status
colours, which fell under 4.5:1 on half the app's themes. Text now takes a readable ink derived
from each colour, measured against all 28 theme modes. A button that is working keeps keyboard
focus instead of dropping it, each New deck stage and each planning question catches focus as it
appears, and buttons in one row are one height. The recipe, the per-hue values and the checks
are recorded in src/plugins/decks/CLAUDE.md.
What the plugin reaches on the host
The island is sandboxed, so anything it cannot do itself is a named bridge call into Omniscio. Decks has nine: decks.measureArtifact (the overflow gate, below), decks.renderHtml (the host's deck renderer), decks.exportPptx (PowerPoint export), decks.assembleFile (read and distil a document), decks.researchTopic (the web-search grounding pass), decks.assembleContext (a shared session or project), decks.assembleBoardContext (a shared board), decks.aiBudget (whether today's pooled deck budget is spent) and decks.discardDesignerFiles (clean up designer-mode files). measureArtifact, renderHtml, exportPptx, assembleFile, researchTopic and assembleBoardContext each answer a discriminated { ok: true, … } | { ok: false, reason }, so a failure can never be misread as a clean result; see plugin-bridge-hardening-contract.md measureartifact-reports-whether-it-measured / renderhtml-reports-whether-it-rendered. The island builds its own PDF, web-page and Share documents, so PowerPoint is the one export that still goes through the host's renderer.
exportPptx is the one that SAVES rather than returning data: a .pptx is a binary zip and the
generic export.saveFile writes utf-8 only, so the host pops the Save dialog and writes the file,
exactly as export.savePdf does. It carries a THIRD arm the others do not need: a user who
cancels the dialog gets { ok: true, saved: false }, because a cancel genuinely ran and genuinely
did not fail. On a real save it reports rendered / total, and the plugin tells you when the
saved deck came back SHORT of real slides: the per-slide render is fail-open by design, so a deck
carrying placeholders is a real outcome, not an exotic edge.
assembleFile and researchTopic are the CREATE-side pair. Office extraction is main-process code
and a PDF rides to Claude as a native document block, neither of which the text-only AI door can
express; and research is a tool_choice: auto call carrying the web_search SERVER tool, which the
forced-tool AI door structurally cannot issue. Both are bridged for that reason and no other.
The island now reads PDF, Office and text files itself and distils them on a one-shot AI session; assembleFile is the fallback, used once, when the island cannot read the whole file or that session cannot answer.
The island starts a deck three ways: Describe it (a typed prompt), From a file, and From your work (a past session, project or board you have shared with it). Each runs the same guided planning steps (the separate "Interview me" choice was folded into them in plugin 0.21.0), each can look facts up on the web first, and a finished deck exports to PDF, web page or PowerPoint.
What it may read of your work
"From your work" only reaches what you share with it, and that is the privacy model rather than a limitation. Decks can only list what you have explicitly shared with it, so its picker starts empty, with a "Choose what Decks can read" button that opens Omniscio's own picker. You choose there; Decks never browses your work to offer it to you. A few consequences worth knowing:
- Sharing a project shares its sessions, but sharing one session does NOT share its project. The asymmetry is deliberate.
- Sharing a board shares only that board. Boards sit inside a workspace, and sharing one does NOT share the others: a workspace is far wider than the thing you actually pointed at.
- Decks never sees your messages, or your board's items. It hands Omniscio an id and gets back only a distilled summary, so the underlying content never enters the plugin. That is structural: there is no path for it, not merely a rule being followed.
- Boards asked separately, and on purpose. Decks requests a distinct "read your boards" permission rather than reusing the session-history one, because a board is project-management data and not a conversation. Agreeing to share past sessions should never quietly hand over something else.
- You can withdraw any of it later in Settings → Plugins, where every grant is listed together: sessions, projects and boards in one place, each labelled for what it is. A grant whose target you have since deleted still appears there, so you can always withdraw it.
Boards needed a SEPARATE method rather than a third value on assembleContext's sourceKind,
and the reason is worth knowing because it looks like extra work for nothing: the host gates
permissions per method, not per argument. A board accepted by assembleContext would have
been read under the session-history permission, so a plugin holding only that permission could
have read your boards. One method, one permission, one honest sentence.
Limits and safety nets
The plugin cannot show a model a picture through its bridge. Its ai bridge takes text only (there is no image parameter). In designer mode, which is on by default, the deck's own AI session renders every slide in a browser and looks at the screenshots itself before handing the slides back.
Two limits on the create path. First, a picked file must be under about 24 MB: a file the host reads crosses the plugin bridge as base64 inside a payload capped at 32 MiB, so the plugin refuses a larger file up front rather than failing after the read. Second, web research, file reading on the host and the plugin's ai calls all bill one pooled daily deck budget (decksDailyCostCapUsd, $5 a day by default), while the outline and slides are normally written by the deck's own AI session. If research is skipped, the planning screen says why for the rest of the run, with its own sentence for each reason.
The overflow gate guarantees a MEASURED slide, not a FITTING one (2026-07-28). Its repair is
bounded on purpose (6% per pass, two passes), so it rescues a slide that is slightly too full and
deliberately leaves a badly overflowing one alone. Treat it as a safety net for near-misses, not
a guarantee every slide fits. This was proven on a real render, and proving it exposed a defect
worth knowing about: because the bridge used to infer its violation count from "did the card
change?", an unfixable slide came back unchanged and reported violations: 0 ("measured and
clean", for the worst slides in the deck), while the gate logged nothing at all in that case. The
host now REPORTS { status, violations, repaired } and nothing downstream infers it, and the unfixable case logs. The plugin records the count, but since plugin 1.0.0 its finished-deck screen says only whether the deck is ready or a draft. The gate runs on the standard build and on a slide redraw; a designer-mode deck (the default) skips it and gets the plugin's own read-only render checks instead. The rule is overflow-gate-measures-not-fits in decks-plugin-marketplace-publish-preconditions-detach-build-deck-contract.md. The end-to-end proof is npm run decks:measure-proof; it is a script and not a vitest test because the gate needs a real BrowserWindow, which the unit lane cannot construct.
Where its code and bundle live
The plugin's user interface is build output and is not committed. The island's source is src/plugins/decks/src/. Inside src/plugins/decks/, npm run build type-checks it and builds ui/, and npm run package packs the plugin for the Marketplace, which is where users get it. The committed bundle, and the content stamp that kept it in step with its source, were retired on 2026-09-16.
Deleting a deck is for good
Deleting a deck removes it for good: the deck, its slides, their earlier versions and its build records all go, and no later Omniscio start, update or re-enable brings it back. A deck that was carried in from Omniscio's old built-in Decks panel leaves only an empty, invisible placeholder behind, because that placeholder is what stops Omniscio copying the deck back in. The rules are in decks-deck-delete-contract.md.
Related
The first part of this page covers what Decks is and how a deck is made, edited and published, and part 2 covers per-card generation, export and the data model. The rules the plugin keeps are in decks-plugin-contract.md, and plugins in general are managed on the plugin settings page.
Last verified 2026-09-30