Skip to content

Plugin host for atomic-plugins drive apps: several destination tables, store.getMany, theme (consolidates #1748, #1750, #1768) - #2148

Closed
michielbdejong wants to merge 4 commits into
developfrom
claude/plugin-host-consolidated
Closed

michielbdejong wants to merge 4 commits into
developfrom
claude/plugin-host-consolidated

Conversation

@michielbdejong

@michielbdejong michielbdejong commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Consolidates the parts of the plugin-host stack (#1702–#1934) that atomic-plugins' drive apps need now and that don't depend on the open #1970 questions on #1726. Rebased onto current develop, nothing merged from the old stack base. Decided by Michiel (Decision Inbox Q-101), on @joepio's request on #1726 for one PR instead of the stack.

The inventory, the CLOSE list and the four #1970 questions are on #1726: #1726 (comment)

What's in it

Area Change Replaces Why atomic-plugins needs it
Importer destinations A manifest destination can declare keyed tables beside (or instead of) its single table. Set up creates each table with its own row class and default view, and stores tables: { <key>: { table, rowClass } } in the config. A drive app shown on one of them gets the same map from store.getData(). provisionDestination moves into @tomic/lib (plugin-destination.ts). The server now validates destination with the browser's rules, and both are held to new shared fixtures in testdata/plugin-manifest/. #1768 (own commit e739bf750) Money's bank-statement importer declares destination.tables.statements (integrations/money/plugin.ts), which feeds the Money app's Imports tab and closing balances.
App frames store.getMany(subjects): up to 100 reads in one round trip, each exactly like get, with an unreadable subject reported in its place. #1748 Called by the issue-tracker, money and notion drive apps.
App frames store.getTheme() / store.onThemeChange(handler) ({ colorScheme }), --t-color-success, and the host passing an explicit colorScheme to app frames and packaged views. #1750 Called by six drive apps: calendar, google-tasks, issue-tracker, money, notion, timesheets.

Commit by commit: one commit per PR, cherry-picked with -x. The hand edits made while resolving against develop are named in each commit message. Then one commit that settles the .po catalogs.

Notes on the port

What waits on #1726

The rest of the NEED set touches #1970's design, so it waits for Joep's answers to the questions on #1726:

This PR doesn't replace those PRs.

Flags

None needed. Everything here is additive:

  • A manifest without destination.tables gets the same config, the same table localId and the same release id as before. The single-table fixture's id is pinned, and so are the stored bytes of an app package carrying it.
  • The new frame calls do nothing until an app calls them.
  • colorScheme is an extra field on the style message, which older frames ignore.

Review guide (Joep, Polle, Michiel)

  • Signatures and crypto: none in this PR.
  • Grants and rights:
    • getMany reads each subject through the signed-in person's store, so it has the same rights as get.
    • It is capped at 100 subjects, and the subjects are validated before anything is read (hostStore.ts).
  • Manifest validation (server): validate_destination in server/src/plugins/manifest.rs now rejects destinations the browser already rejected. Before, the server only required a map.
    • Table keys must be identifiers.
    • Object.prototype names are refused as keys.
    • Each table needs its own row class.
    • Worth a look: whether any manifest published before this would now fail on the server. The browser rejected all of these already, so none should have been installable.
  • Theme: useFrameColorScheme in useCreateThemeVars.ts, and FrameBridge.setStyle.

Replaces

The rest of the CLOSE list is in the #1726 comment linked above. The branches stay.

Local checks

  • pnpm run -r build
  • pnpm run lint (the pre-commit hook on each commit)
  • data-browser tsc --noEmit and the per-package typecheck
  • vitest: @tomic/plugin, AppPage, PluginRuns, and the lib manifest, destination and app-package tests
  • cargo fmt --check
  • cargo clippy (CI's feature set)
  • cargo test -p atomic-server --lib
  • cargo test -p atomic_lib --features db-redb --lib: 698 of 699 passed. db::compaction::startup_compaction_shrinks_a_bloated_store_and_keeps_every_resource failed once on a size bound while the machine was busy, then passed when run alone. This PR doesn't touch lib/.

No UI change beyond FileImport's Set up line, which now names several tables when a manifest declares them. No screenshots taken.

CI

Green on 6126887: run 37828791674, with full e2e on Mancave.

The earlier runs on the same commit failed on tests this PR doesn't touch:

  • data-browser vitest recovery-assisted.test.ts timed out. It also failed on another branch's run that morning.
  • The flutter bridge test a_canvas_syncs_to_a_second_device_through_the_bridge failed. It passes locally 3/3.
  • One run was cancelled mid-e2e.
  • e2e search.spec.ts › scoped search failed. The same test failed in another branch's run at the same time.

🤖 Generated with Claude Code

Michiel de Jong and others added 4 commits October 8, 2026 11:29
A plugin's manifest `destination` can now declare keyed `tables` beside
(or instead of) its single `table`. Set up creates each one beneath the
plugin with its own row class and default view, and stores
`tables: { <key>: { table, rowClass } }` in the plugin's config next to
the unchanged `{ table, rowClass, properties }`. A drive app shown as a
view of any of those tables gets the same map from `store.getData()`.
This is what the money app needs for statements and closing balances
(atomic-plugins#148, M-7 and M-12).

- provisionDestination moves from FileImport into @tomic/lib
  (plugin-destination.ts), with destinationTablesFor for the host's
  `data` op. Set up resumes: a release that adds a key creates only that
  table.
- The server now checks `destination` with the browser's rules and
  messages instead of only requiring a map. Both parsers are held to new
  shared fixtures in testdata/plugin-manifest/.
- A single-table manifest keeps its config, its table localId and its
  release id; the single-table fixture's id is pinned, and so is the
  stored bytes of an app package carrying it.
- Docs: creating-plugins.md describes `accepts`, `destination.table`
  and `destination.tables`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ported from #1768 onto develop. Hand edits while
resolving: `manifest.rs` and `plugin-manifest.ts` keep develop's operation
URL check; the `atomic-proxy:` parsing (#1725) that sat next to this change
in the stack is left out. The `.po` catalogs are develop's, settled
separately.

(cherry picked from commit e739bf7)
Reads up to 100 resources in one round trip, each exactly as `get` reads
it, through the person's store; an unreadable subject is reported in its
place. Used by atomic-plugins' issue-tracker, money and notion drive apps.

Ported from #1748 (97eff29) onto develop's plugin
contract (#1970). Hand edits while resolving: kept develop's Store-shaped
`query`, `search` and `getContext` in view-client.js and added `getMany`
beside them, passing the title to `makeResource` like `getResource` does;
dropped the openExternal/openResource/proxy.disconnect pieces this commit
carried as context (types, docs, TESTING_COVERAGE, hostStore.test), which
wait on the questions on #1726.

(cherry picked from commit 97eff29)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apps had no success colour and guessed dark mode from the background colour.

- The theme gets `colors.success`: #237a42 in light mode, #4cc27a in dark.
  Frames get it as `--t-color-success`.
- The frame stylesheet sets `color-scheme: light | dark` on `:root`, and the
  theme message now carries `colorScheme`, both from the built theme's
  `darkMode` (the host's actual setting, including "auto"). FrameBridge
  keeps it across a ready message and a reload. AppFrame and packaged
  plugin views both send it.
- view-client: `store.getTheme()` returns `{ colorScheme }`. Before any
  message has arrived it reads the applied `color-scheme` back.
  `store.onThemeChange(handler)` fires once per real switch.
- `ColorScheme` and `ThemeMessage` in `@tomic/plugin`; documented in
  custom-views.md.

Closes #1738

Ported from #1750 (2923ce1, with the theme-test
rename from 62187a1) onto develop's plugin contract (#1970). Hand edits
while resolving: AppFrame keeps develop's host UI next to the frame's
colour scheme; pluginRPC keeps develop's HostUI import beside the
ColorScheme one; view-client.js keeps develop's ASKS_THE_PERSON and
`point` and adds the theme listener; the openExternal/openResource methods
and docs this commit carried as context are left out (they wait on #1726).

(cherry picked from commit 2923ce1)
(cherry picked from commit 62187a1)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pnpm clean-translations after the #1768 port: the new "Set up creates {0}
tables" message, the renamed placeholder of the single-table one, and a
msgstr develop's catalogs were missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

joepio commented Oct 9, 2026

Copy link
Copy Markdown
Member

Landed on develop through #2169 (batch with the message catalogs re-extracted), merged as b214af0 on a green run of the exact head. This head (6126887) is fully contained, so closing.


Generated by Claude Code

@joepio joepio closed this Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants