refactor(block-h): message block types resolve via plugin-contributable registry

This commit is contained in:
Agent Zero
2026-08-23 13:45:12 +02:00
parent 49949066d3
commit b7ad5294a5
3 changed files with 72 additions and 25 deletions
@@ -6,14 +6,8 @@ import ImageBlock from './ImageBlock';
import AudioBlock from './AudioBlock'; import AudioBlock from './AudioBlock';
import VideoBlock from './VideoBlock'; import VideoBlock from './VideoBlock';
import FileBlock from './FileBlock'; import FileBlock from './FileBlock';
import ActionCardBlock from './ActionCardBlock'; import { getBlockComponent } from './registry';
import ContactCardBlock from './ContactCardBlock'; import './registrations'; // plugin-contributed block registrations
import MiniAppBlock from './MiniAppBlock';
import AgentResultBlock from './AgentResultBlock';
import ApprovalRequestBlock from './ApprovalRequestBlock';
import TaskCardBlock from './TaskCardBlock';
import WorkflowCardBlock from './WorkflowCardBlock';
import KnowledgeCardBlock from './KnowledgeCardBlock';
interface BlockRendererProps { interface BlockRendererProps {
blocks: MessageBlock[]; blocks: MessageBlock[];
@@ -51,23 +45,13 @@ const BlockRenderer: React.FC<BlockRendererProps> = ({ blocks }) => {
return <VideoBlock block={block} />; return <VideoBlock block={block} />;
case 'file': case 'file':
return <FileBlock block={block} />; return <FileBlock block={block} />;
case 'action_card': default: {
return <ActionCardBlock block={block} />; // Plugin-contributed block types (Block H / HC-F)
case 'contact_card': const Contributed = getBlockComponent(block.block_type);
return <ContactCardBlock block={block} />; if (Contributed) {
case 'miniapp': const ContributedBlock = Contributed as React.FC<{ block: MessageBlock }>;
return <MiniAppBlock block={block} />; return <ContributedBlock block={block} />;
case 'agent_result': }
return <AgentResultBlock block={block} />;
case 'approval_request':
return <ApprovalRequestBlock block={block} />;
case 'task_card':
return <TaskCardBlock block={block} />;
case 'workflow_card':
return <WorkflowCardBlock block={block} />;
case 'knowledge_card':
return <KnowledgeCardBlock block={block} />;
default:
// Fallback for unknown block types // Fallback for unknown block types
return ( return (
<div className="text-xs text-secondary-400 italic p-2 rounded bg-secondary-50"> <div className="text-xs text-secondary-400 italic p-2 rounded bg-secondary-50">
@@ -75,6 +59,7 @@ const BlockRenderer: React.FC<BlockRendererProps> = ({ blocks }) => {
</div> </div>
); );
} }
}
}; };
return ( return (
@@ -0,0 +1,26 @@
/**
* Plugin block registrations.
*
* Currently a static import list; the dynamic plugin loader (ARCH-019 fix)
* will replace this file with manifest-driven registration. Plugins register
* their block renderers via ``registerBlockType`` with an owner tag so that
* ``unregisterBlockTypes(owner)`` can clean up on deactivation.
*/
import { registerBlockType } from './registry';
import ActionCardBlock from './ActionCardBlock';
import ContactCardBlock from './ContactCardBlock';
import MiniAppBlock from './MiniAppBlock';
import AgentResultBlock from './AgentResultBlock';
import ApprovalRequestBlock from './ApprovalRequestBlock';
import TaskCardBlock from './TaskCardBlock';
import WorkflowCardBlock from './WorkflowCardBlock';
import KnowledgeCardBlock from './KnowledgeCardBlock';
registerBlockType('action_card', ActionCardBlock, 'core-comm');
registerBlockType('contact_card', ContactCardBlock, 'contacts');
registerBlockType('miniapp', MiniAppBlock, 'kommunikation');
registerBlockType('agent_result', AgentResultBlock, 'automation');
registerBlockType('approval_request', ApprovalRequestBlock, 'core-approvals');
registerBlockType('task_card', TaskCardBlock, 'tasks');
registerBlockType('workflow_card', WorkflowCardBlock, 'workflows');
registerBlockType('knowledge_card', KnowledgeCardBlock, 'knowledge');
@@ -0,0 +1,36 @@
/**
* Message block type registry.
*
* Core owns the base block types; plugins can contribute additional block
* renderers via ``registerBlockType`` / ``unregisterBlockTypes`` during their
* frontend activation lifecycle (Block H / HC-F).
*/
import type { ComponentType } from 'react';
import type { MessageBlock } from '@/store/commStore';
export type BlockComponent = ComponentType<{ block: MessageBlock }>;
const _registry = new Map<string, BlockComponent>();
export function registerBlockType(blockType: string, component: BlockComponent, owner = ''): void {
if (_registry.has(blockType)) {
// eslint-disable-next-line no-console
console.warn(`[blockRegistry] block type '${blockType}' re-registered by '${owner || 'unknown'}' — overwriting`);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(component as any).__block_owner__ = owner;
_registry.set(blockType, component);
}
export function unregisterBlockTypes(owner: string): void {
for (const [key, component] of _registry.entries()) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((component as any).__block_owner__ === owner) {
_registry.delete(key);
}
}
}
export function getBlockComponent(blockType: string): BlockComponent | undefined {
return _registry.get(blockType);
}