@synap-core/capabilities
v0.1.3
Published
React-free capability layer (families, interchange, manifest, registration meta) over the renderables catalog in @synap-core/types/renderables, which it re-exports. Safe to import from backend, AI agents and build scripts.
Downloads
541
Maintainers
Readme
@synap-core/capabilities
Static manifest of all Synap view types and widget types. Zero React dependencies — safe to import from frontend components, backend routes, AI agents, and build scripts.
import { CAPABILITIES_MANIFEST, WIDGET_BY_KEY, VIEW_BY_KEY } from "@synap-core/capabilities";
import type { WidgetTypeKey, ViewTypeKey, BentoBlockDef } from "@synap-core/capabilities";What It Contains
This package is the single source of truth for what widgets and views exist, what config they accept, how big they are, and which display modes they support. It does not contain React components — those live in cell-runtime, bento, and feature packages.
VIEW_DEFINITIONS
An array of ViewCapabilityDef objects describing every view type. Each entry includes:
| Field | Purpose |
|-------|---------|
| key | View type string stored on the DB view record (e.g. "table", "kanban") |
| name | Human-readable label |
| description | One-liner for pickers and AI prompts |
| icon | Lucide icon name |
| category | "structured" (entity query), "canvas" (freeform), or "special" |
| implemented | Whether StructuredViewRenderer can render it today |
| configSchema | Flat array of ViewConfigField for AI generation and settings UI |
| requiredConfig | Config keys that must be set for the view to function |
Currently 17 view types defined (13 implemented, 4 not yet: timeline, graph, gantt, mindmap).
Convenience exports:
VIEW_BY_KEY—Record<string, ViewCapabilityDef>fast lookupIMPLEMENTED_VIEW_TYPES— string array of renderable typesSTRUCTURED_VIEW_TYPES— string array of entity-query-based types
WIDGET_DEFINITIONS
An array of WidgetCapabilityDef objects describing every bento widget type. Each entry includes:
| Field | Purpose |
|-------|---------|
| key | Widget type key used in BentoWidgetBlock.widgetType |
| name | Human-readable label |
| description | One-liner for pickers and AI prompts |
| icon | Lucide icon name |
| category | See categories table below |
| displayModes | Subset of "compact" \| "medium" \| "full" |
| defaultSize | { w, h } in 12-column bento grid units |
| minSize | Optional minimum size |
| configSchema | Flat array of WidgetConfigField for AI generation and settings UI |
| requiresWorkspace | Whether this widget needs a pod connection |
| aliasOf | If set, this key is an alias for another widget |
| presets | Pre-built config presets for quick creation (see below) |
Convenience export: WIDGET_BY_KEY — Record<string, WidgetCapabilityDef> fast lookup.
Preset System
Widgets can declare presets — named config bundles that pre-fill common use cases. Each preset has:
interface Preset {
label: string; // "Task Progress", "Revenue"
icon?: string; // Lucide icon name
config: Record<string, unknown>; // Pre-filled config values
}Example on stat-card:
presets: [
{ label: "Task Progress", icon: "CheckCircle2", config: { aggregation: "completion", profileSlug: "task", chartType: "progress" } },
{ label: "Entity Count", icon: "Hash", config: { aggregation: "count", label: "Total" } },
{ label: "Revenue", icon: "DollarSign", config: { aggregation: "sum", field: "value", profileSlug: "deal", prefix: "$" } },
]The BentoBlockPicker reads presets from WIDGET_BY_KEY and shows them as one-click options. AI agents use them when composing dashboards via get_bento_schema.
How Widget Registration Works
Registration spans three layers:
- capabilities — declares the schema (this package). Defines
key,configSchema,defaultSize,displayModes. No React. - cell-runtime — registers the React component via
cellRegistry.register(). Happens inregisterBentoCells(),registerCoreCells(),registerViewRunnerCells(), and feature packages. - bento — patches settings forms onto cells registered by other packages via
cellRegistry.patch(). For example,view-tableis registered byview-renderer, but its settings popover is patched inregisterBentoCells.ts.
At render time, BentoBlock resolves the widget via cellRegistry.resolve(widgetType). The capabilities manifest is only used for picker UI, AI prompts, and validation — not for rendering.
WidgetTypeKey Union
A union of all ~50 string literals matching registered widget keys:
type WidgetTypeKey =
| "section-header" | "workspace-info" | "home-tabs" | "calendar"
| "entity-list" | "entity-card" | "entity-header" | "entity-properties"
| "entity-content" | "entity-spotlight" | "entity-gallery"
| "stat-card" | "quick-capture" | "random-highlight" | "quote-card"
| "reading-progress" | "quick-access" | "feed" | "inbox"
| "link-grid" | "iframe-embed"
| "view-table" | "view-list" | "view-kanban" | "view-calendar" | "view-grid" | "view-map"
| "map-widget"
| "channel" | "channel-feed" | "channel-navigator" | "channel-view"
| "ai-chat" | "search" | "notifications"
| "entity-detail" | "document-editor" | "entity-relationships"
| "property-value" | "property-group"
| "proposals-list" | "proposal-detail" | "proposal-timeline"
| "workflow-list" | "automation-detail" | "automation-flow" | "automation-status"
| "command-detail" | "trigger-button" | "run-history"
| "welcome" | "welcome-header";WidgetConfigByType Discriminated Union
Maps each WidgetTypeKey to its typed config interface:
interface WidgetConfigByType {
"stat-card": StatCardConfig;
"entity-list": EntityListConfig;
"property-value": PropertyValueConfig;
"property-group": PropertyGroupConfig;
// ... etc
}Used for type-safe template authoring via BentoBlockDef:
const block: BentoBlockDef = {
widgetType: "stat-card",
x: 0, y: 0, w: 3, h: 3,
config: { profileSlug: "task", aggregation: "count" }, // TypeScript-checked
};Widget Categories
| Category | Purpose | Example Widgets |
|----------|---------|-----------------|
| core | Workspace infrastructure | section-header, workspace-info, home-tabs, quick-capture, link-grid, quote-card, notifications |
| data | Entity data visualization | entity-list, entity-card, entity-spotlight, entity-gallery, stat-card, calendar, feed, random-highlight, reading-progress, map-widget |
| entity | Single-entity context (entity bento) | entity-header, entity-properties, entity-content, entity-detail, entity-relationships |
| content | Rich content editing | document-editor |
| communication | Channels and messaging | channel, channel-feed, channel-navigator, channel-view |
| ai | AI and automation | ai-chat, workflow-list, automation-detail, automation-flow, automation-status, command-detail, trigger-button, run-history |
| governance | Proposal review | proposals-list, proposal-detail, proposal-timeline |
| productivity | Personal productivity | quick-access, inbox, search |
| app-specific | External/custom embeds | iframe-embed, custom-widget |
View embeds (view-table, view-list, etc.) are categorized as data.
Manifest
import { CAPABILITIES_MANIFEST } from "@synap-core/capabilities";
CAPABILITIES_MANIFEST.widgets; // WidgetCapabilityDef[]
CAPABILITIES_MANIFEST.views; // ViewCapabilityDef[]The pod does not read a generated manifest: it imports
@synap-core/types/renderables in-process (listRenderables, arrange
validation, synap_list_widgets).
Key Files
The catalog DATA moved to @synap-core/types/renderables (synap-backend
packages/types/src/renderables/) so the backend, IS and relay read the same
catalog. This package re-exports all of it.
synap-backend/packages/types/src/renderables/types.ts— key tuples + all catalog types.../renderables/widgets.ts— WIDGET_DEFINITIONS (with placements, dataBinding, aiHint, requiredConfig, package).../renderables/views.ts— VIEW_DEFINITIONS.../renderables/catalog.ts— placementsFor, requiredConfigFor, isAiPlaceable, fallbackFor, exampleEmbedProps (props only; the directive is written byserializeEmbed), configFieldsToJsonSchemasrc/manifest.ts— CAPABILITIES_MANIFEST
