Today Canvas Kit
SDK packages

@todayai-labs/tck-host

Browser-side host runtime. Manifest registry, widget loader, layout grid, patch dispatcher, agent channel, two-tier mount surface.

The browser-side host runtime. A host app (today-platform-web, the workbench, the native WebView shell) wires up a single TodayHost and consumes it through React. tck-host owns the doc store, the patch dispatcher, the manifest registry, the widget loader, the layout canvas, the bus the agent talks on, and the <WidgetMount> mount primitive.

Widgets do not import this package — it's host-only, and the bundler enforces that absence at build time.

Deterministic product-policy helpers live separately in @todayai-labs/tck-host-utils. Keeping those helpers free of React, timers, and ambient browser reads lets web, native, and Preview Hosts share the same calculations while owning their own lifecycle.

The package is split into multiple subpaths to keep the import graph minimal per consumer-class — a Next.js server module evaluating getHostHeadNodes must not transitively reach createContext, and an import-map-served mountHost must not drag in the full React component graph. The split is structurally enforced by __tests__/export-safety.test.ts; see the design memo for the rationale.

Subpaths

@todayai-labs/tck-host (root)

The core runtime. Safe to import anywhere a client React runtime exists.

ExportWhat it does
TodayHostThe host root. Owns registry + loader + dispatcher + docStore + canvas state. See § TodayHost.
TodayHostOptionsConstructor options: storage, loaderOptions, onSizeMismatch.
WidgetInstanceImmutable per-mount metadata returned by host.mountWidget / host.restoreWidget.
SizeMismatchPolicy'warn' | 'throw' — see container contract.
TodayWidgetCanvasReact component: the grid surface. Drag-to-move, tile chrome.
TodayWidgetCanvasPropsProps + types for canvas tile rendering (WidgetTileRenderArgs, CanvasTileDragBindings).
TodayFeedStackReact component: the feed surface (linear column of full-width cards).
TodayFeedStackPropsProps + types for feed card rendering (FeedCardRenderArgs).
WidgetLoaderGiven a bundle URL → WidgetModule. Verifies named exports, ABI label, manifest schema.
WidgetLoaderOptions, WidgetModule, ModuleMap, DEFAULT_HOST_ABILoader plumbing.
WidgetLoadErrorLoader-side rejection (malformed bundle / missing exports / failed manifest validation).
WidgetAbiErrormanifest.engines.abi does not match the host's ABI label.
WidgetSizeErrorMount offered a size not in manifest.sizes. See container contract.
WidgetMountLow-level shadow-DOM mount primitive (also re-exported by /bootstrap).
WidgetMountProps, WidgetMountFallbackControls<WidgetMount> types.
PatchDispatcherSingle sequencer per doc. Applies RFC-6902 patches; rejects stale baseVersion; idempotent on patchId.
PatchDispatcherOptionsDispatcher tuning.
ManifestRegistryIn-memory {manifestId → RegisteredWidget} index.
RegisteredWidgetRegistry entry shape.
DocStore, ephemeralStoragePer-instance JSON-doc snapshots; pluggable storage backends.
DocSnapshot, DocStorageStorage backend contract.
migrateDoc, MigrationErrorForward-only state migrations on schemaVersion mismatch.
WidgetErrorBoundaryPer-widget React error boundary. Chrome-level "Reload widget" + "Reset to defaults".
AgentChannelImpl, makeWidgetAgentChannelHost-side agent bus implementation.
CanvasDescriptor, CanvasItemPage-level wire format for "what's on a canvas" — items + per-item bundle URL + pin bit. The shape a registry endpoint returns; the host hydrates from it on cold start.
Layout helpersDEFAULT_GRID, canPlaceAt, canvasWidth, effectiveFootprint, findFirstFreeSlot, gridCssVars, pointerToGridPosition, widgetPixelSize, TodayGridConfig.

@todayai-labs/tck-host/bootstrap

Tier-1 mount components — closed-contract single-widget surfaces. Client React only.

ExportWhat it does
TodayHostBoundaryReact provider exposing the host + docStore to descendant <TodayWidget> mounts.
TodayHostBoundaryPropsProps.
TodayWidgetClosed-contract widget mount; accepts {src, instanceId} OR {devModule, instanceId} plus optional size and reactive slotValues. No loadModule escape hatch — every module flows through WidgetLoader.parseModule. See Tier 1 mount surface.
TodayWidgetPropsDiscriminated-union prop shape.
TodayWidgetMountedInfoonMounted callback payload.
WidgetMountLow-level shadow-DOM primitive (re-exported from root).

@todayai-labs/tck-host/server

SSR-head slice. Safe to import in server modules — does not call createContext or touch the DOM.

ExportWhat it does
getHostHeadNodesReturns <link> / <script type="importmap"> / <html data-theme> React elements for the host's <head>.

@todayai-labs/tck-host/mount

Client-imperative slice — mountHost only. Safe to import anywhere; no React, no createContext.

ExportWhat it does
mountHost(document, options)Installs shared CSS/importmap concerns and root-scoped data-theme / data-tck-pointer state. Concurrent controls update their own roots. Returns a MountHostControl.
MountHostControl{ setTheme, setPointer, dispose } — runtime theme / pointer flips + cleanup.
MountHostOptions{ theme, pointer?, themeRoot?, pointerRoot?, hostCssUrl? }; theme and pointer accept explicit values or 'system'.
HostBootstrapErrorThrown when mountHost cannot verify the import-map's presence on the document.

@todayai-labs/tck-host/vite

Vite plugin: build-time importmap injection. For no-SSR Vite hosts (workbench, tck-preview) — the dev-server equivalent of getHostHeadNodes.

ExportWhat it does
hostImportMapPlugin()Vite plugin. In vite serve mode (dev), reads Vite's own dep-optimiser metadata at transformIndexHtml and injects <script type="importmap"> mapping each TCK_EXTERNAL_SPECIFIER to its /node_modules/.vite/deps/... URL (npm) or /@fs/.../packages/... URL (workspace). In vite build (production), the plugin currently emits { imports: {} } — production wiring that consumes a tck-shared-deps manifest.json via the manifest option is a documented deferred seam.

@todayai-labs/tck-host/messages

Cross-window message types for <TodayWidget>'s iframe-mode bridge. Internal-protocol; typed shapes only.

@todayai-labs/tck-host/style.css

Default host stylesheet (preflight + theme tokens + the [data-theme='dark'] variant binding). Import once at the host app's root, or copy alongside tck-shared-deps's output for serving over importmap-resolved URLs.

import '@todayai-labs/tck-host/style.css'

TodayHost

The canvas-tier API. Use this when building a Tier-2 host (workbench, drag-canvas, persisted layout); for embedding surfaces, prefer <TodayWidget> from /bootstrap.

import { TodayHost, ephemeralStorage } from '@todayai-labs/tck-host'

const host = new TodayHost({
  storage: ephemeralStorage, // or an idb-keyval-backed DocStorage
  loaderOptions: { loadModule: (url) => import(url) },
  onSizeMismatch: 'warn', // or 'throw' for debug surfaces
})

host.registerWidget({
  manifest: parsedManifest,
  bundleUrl: 'https://.../widget.mjs',
  cssUrl: 'https://.../widget.css',
})

const instance = await host.mountWidget({
  manifestId: 'com.example.counter',
  position: { col: 0, row: 0 },
  size: '2x1', // optional; defaults to manifest.defaultSize
  seedState: { count: 0 }, // optional; defaults to manifest.defaultState
})

Methods:

  • registerWidget / mountWidget / restoreWidget / resetWidget / removeWidget
  • moveWidget(instanceId, position) / resizeWidget(instanceId, size)
  • setTransient(instanceId, transient) — flip the suggestion / pinned bit
  • setAmbientWidgetCss(css, propertiesCss?) — for unbundled (workspace dev) widgets
  • list() / get(instanceId) / subscribeLayout(fn)
  • submitPatch({instanceId, baseVersion, patch}) — build envelope + dispatch + handle stale rebase
  • publishAgent(message) — broadcast onto the agent bus

Source

On this page