DeveloperComponent Blocks Reference

Component Blocks Reference

Reusable UI widgets for the Layout Designer

Overview

Blocks vs Widgets: Component blocks are self-contained layout sections (Asset Browser, Timeline, AI Chat). For individual field inputs (color pickers, sliders, entity selectors), see Field Widgets Reference.

Component blocks are self-contained UI widgets that can be placed in layout sections alongside form fields. They are managed through the Component Block Registry (src/lib/component-block-registry.ts) and rendered by ComponentBlockRenderer.

Built-in Blocks

entity-assets

PropertyValue
Categorymedia
Modesedit, view
Sourcebuiltin

Full asset browser with upload, primary image selection, and asset role management.

Config Options:

  • title (string, default: “Entity Assets”) — Section heading
  • showUpload (boolean, default: true) — Show upload controls

entity-timeline

PropertyValue
Categorydisplay
Modesedit, view
Sourcebuiltin

Timeline event panel showing entity history with auto-populate capability.

Config Options:

  • compact (boolean, default: true) — Use compact display mode
  • showAutoPopulate (boolean, default: true) — Show auto-populate button

production-status

PropertyValue
Categoryworkflow
Modesedit, view
Sourcebuiltin

Production pipeline status editor showing general/game/TV status with inline save.

Config Options: None (uses entity’s production_status field directly)


markdown-content

PropertyValue
Categorydisplay
Modesedit, view
Sourcebuiltin

Rendered markdown body content using MarkdownRenderer component.

Config Options:

  • field (string, default: “markdown_body”) — Which entity field contains the markdown

primary-image

PropertyValue
Categorymedia
Modesedit, view
Sourcebuiltin

Hero/primary image display with placeholder fallback when no image is set.

Config Options:

  • field (string, default: “primary_image”) — Entity field containing the image path
  • height (string, default: “300px”) — Display height

entity-chat

PropertyValue
Categoryinteraction
Modesedit
Sourcebuiltin

AI-powered entity chat panel for conversational editing of entity fields.

Config Options:

  • visibility (select: team/private, default: “team”) — Chat session visibility

entity-relationships

PropertyValue
Categorydisplay
Modesedit, view
Sourcebuiltin

Automatically scans common relationship fields and renders them as grouped link cards. Scanned fields include: relationships, allies, enemies, members, key_personnel, associated_brands, subsidiaries, parent_company, primary_npcs, controlled_territories.

Config Options: None (auto-discovers fields)


entity-workflow-trigger

PropertyValue
Categoryworkflow
Modesedit
Sourcebuiltin

Workflow trigger buttons for entity-level automations.

Config Options: None

Creating a Custom Block

Step 1: Create the Component

// src/components/blocks/MyCustomBlock.tsx
interface MyCustomBlockProps {
  config?: Record<string, any>;
  entityType: string;
  entityId: string;
  data: any;
  mode: 'edit' | 'view';
}
 
export default function MyCustomBlock({ config, data, mode }: MyCustomBlockProps) {
  return <div>{/* Block UI */}</div>;
}

Step 2: Register in ComponentBlockRenderer

// src/components/ComponentBlockRenderer.tsx
case 'my-custom-block':
  return <MyCustomBlock config={config} entityType={entityType} entityId={entityId} data={data} mode={mode} />;

Step 3: Add to Registry

// src/lib/component-block-registry.ts — add to BUILTIN_BLOCKS array
{
  id: 'my-custom-block',
  label: 'My Custom Block',
  description: 'Does something useful',
  icon: 'Sparkles',
  category: 'display',
  source: 'builtin',
  default_config: {},
  config_schema: {
    title: { type: 'string', label: 'Title', default: 'Default Title' },
    showExtra: { type: 'boolean', label: 'Show Extra', default: false },
  },
  modes: ['edit', 'view'],
}

The block will automatically appear in the Layout Designer palette under its category.

Config Schema

Blocks can declare a config_schema for designer-editable settings. The BlockConfigPanel renders this dynamically.

Supported Field Types

TypeRenders AsExample
stringText inputtype: 'string', label: 'Title', default: 'Assets'
numberNumber inputtype: 'number', label: 'Max Items', default: 10
booleanToggle checkboxtype: 'boolean', label: 'Show Upload', default: true
selectDropdowntype: 'select', label: 'Visibility', default: 'team'

Rendering

ComponentBlockRenderer receives these props:

interface ComponentBlockRendererProps {
  componentId: string;        // Block ID or "plugin:{id}:{panel}"
  config?: Record<string, any>;
  entityType: string;
  entityId: string;
  data: any;
  mode?: 'edit' | 'view';
}

It uses React.lazy for code-splitting large components (EntityAssets, EntityChatPanel) and dispatches to the correct block based on componentId. Unknown blocks show a warning fallback. Plugin blocks (matching plugin:*:*) are routed to PluginBlockIframe.


Manifest Components

These components work with the project manifest (S2.1/S5.1) for entity asset browsing.

EntityAssetIndicator

Location: none — this component was removed from the source tree during the studiobrain-core open-source cleanup and no longer exists. The section below is retained as historical reference.

Displays asset count and total size for an entity using manifest data.

Shows asset indicators like “2 images, 1 video (14 MB)” or “6 assets (14 MB)” for an entity. Used in entity list views and detail pages.

Props:

interface EntityAssetIndicatorProps {
  summary: EntityAssetSummary | null;  // From useManifest().getEntityAssetSummary()
  compact?: boolean;                   // Use compact mode (default: false)
}

Display modes:

  • Full mode: "2 images, 1 video, 3 audio (14 MB)" — Shows breakdown by type
  • Compact mode: "6 assets (14 MB)" — Shows total count only
  • Empty state: "No assets" — When entity has no assets

Example usage:

import { useManifest } from '@/hooks/useManifest';
import { EntityAssetIndicator } from '@/components/assets/EntityAssetIndicator';
 
function EntityListView({ entityType, entityId }) {
  const { getEntityAssetSummary } = useManifest();
  const summary = getEntityAssetSummary(entityType, entityId);
 
  return (
    <div className="entity-row">
      <h3>Entity Name</h3>
      <EntityAssetIndicator summary={summary} />
    </div>
  );
}

Related:

useManifest Hook

Location: studiobrain-core/shared/src/hooks/useManifest.ts

React hook that fetches and caches the project manifest.

Fetches the manifest from /api/manifest on mount and provides computed data for entity browsing.

Returns:

interface UseManifestResult {
  manifest: ProjectManifest | null;
  isLoading: boolean;
  error: string | null;
  entries: ManifestEntry[];
  getEntityEntries: (entityType: string, entityId: string) => ManifestEntry[];
  getEntityAssetSummary: (entityType: string, entityId: string) => EntityAssetSummary | null;
  entityIndex: Record<string, string[]>;  // type -> [id, id, ...]
  refresh: () => Promise<void>;
  lastRefreshed: Date | null;
  manifestAge: number | null;  // seconds since generation
}

Features:

  • Auto-refresh on WebSocket events — Listens for manifest_changed events
  • Periodic polling — 60-second polling in cloud mode (skipped in desktop mode)
  • Manual refresh — Call refresh() to trigger immediate server-side refresh
  • Computed indices — Entity index and asset summaries are memoized

Example usage:

import { useManifest } from '@studiobrain/shared';
 
function EntityBrowser() {
  const { manifest, entityIndex, getEntityAssetSummary, refresh, isLoading } = useManifest();
 
  if (isLoading) return <div>Loading manifest...</div>;
 
  return (
    <div>
      <button onClick={refresh}>Refresh Manifest</button>
      <p>Manifest generated: {new Date(manifest?.generated_at || '').toLocaleString()}</p>
 
      {Object.entries(entityIndex).map(([type, ids]) => (
        <details key={type}>
          <summary>{type} ({ids.length})</summary>
          {ids.map(id => {
            const summary = getEntityAssetSummary(type, id);
            return (
              <div key={id}>
                <a href={`/entities/${type}/${id}`}>
                  {id}
                </a>
                {summary && <EntityAssetIndicator summary={summary} />}
              </div>
            );
          })}
        </details>
      ))}
    </div>
  );
}

Schema types:

TypeDescription
ProjectManifestFull manifest (project_id, entries, provider_type, etc.)
ManifestEntrySingle entry (relative_path, entity_type, size_bytes, etc.)
EntityAssetSummaryComputed summary (image_count, video_count, total_size_bytes, etc.)