feat: Unified Messaging System — kommunikation plugin, AI/Proactive/System participants, MessageSidebar, Rich Content Renderer

Phase 1: Backend plugin kommunikation (13 files, 10 tables, REST API, WebSocket, RBAC, DMS Bridge, Participant Registry, Mini-App Registry, Search Provider)
Phase 2: AI plugins as participants (ai_assistant + ai_proactive dock as participants, heartbeat job)
Phase 3: system_notif plugin (system events → chat messages, pinned System room)
Phase 4: Frontend MessageSidebar (replaces AISidebar, same design, comm API client, WebSocket hook, commStore)
Phase 5: Rich Content Block Renderer (11 components: Markdown, HTML, Image, Audio, Video, File, ActionCard, ContactCard, MiniApp, BlockRenderer)
BasePlugin: added services property + _container in on_activate
This commit is contained in:
Agent Zero
2026-07-22 01:22:15 +02:00
parent 5980d38c66
commit cc3ac9a43d
45 changed files with 8154 additions and 113 deletions
@@ -0,0 +1,75 @@
import React from 'react';
import type { MessageBlock } from '@/store/commStore';
import MarkdownBlock from './MarkdownBlock';
import HtmlBlock from './HtmlBlock';
import ImageBlock from './ImageBlock';
import AudioBlock from './AudioBlock';
import VideoBlock from './VideoBlock';
import FileBlock from './FileBlock';
import ActionCardBlock from './ActionCardBlock';
import ContactCardBlock from './ContactCardBlock';
import MiniAppBlock from './MiniAppBlock';
interface BlockRendererProps {
blocks: MessageBlock[];
}
const BlockRenderer: React.FC<BlockRendererProps> = ({ blocks }) => {
if (!blocks || blocks.length === 0) {
return null;
}
// Sort blocks by sort_order to ensure correct rendering sequence
const sortedBlocks = [...blocks].sort((a, b) => a.sort_order - b.sort_order);
return (
<div className="space-y-2">
{sortedBlocks.map((block) => {
const renderBlock = (): React.ReactNode => {
switch (block.block_type) {
case 'text':
// Text blocks: render as plain text in a styled div
return (
<div className="text-sm whitespace-pre-wrap break-words">
{block.block_data.text || block.block_data.content || ''}
</div>
);
case 'markdown':
return <MarkdownBlock block={block} />;
case 'html':
return <HtmlBlock block={block} />;
case 'image':
return <ImageBlock block={block} />;
case 'audio':
return <AudioBlock block={block} />;
case 'video':
return <VideoBlock block={block} />;
case 'file':
return <FileBlock block={block} />;
case 'action_card':
return <ActionCardBlock block={block} />;
case 'contact_card':
return <ContactCardBlock block={block} />;
case 'miniapp':
return <MiniAppBlock block={block} />;
default:
// Fallback for unknown block types
return (
<div className="text-xs text-secondary-400 italic p-2 rounded bg-secondary-50">
Unbekannter Block-Typ: {block.block_type}
</div>
);
}
};
return (
<div key={block.id} className="block-wrapper">
{renderBlock()}
</div>
);
})}
</div>
);
};
export default BlockRenderer;