@webmcp-auto-ui/ui
Le package @webmcp-auto-ui/ui fournit une bibliotheque complete de composants Svelte 5 pour construire des applications agent. Il couvre quatre domaines : les widgets (plus de 25 types pour afficher des donnees), les layouts (grille, flottant, empile, flex), les composants agent (selecteur LLM, progression, chat, parametres), et l’infrastructure (theme, bus de messages, primitives UI).
Tous les composants utilisent le design system du projet (tokens CSS, Tailwind preset) et supportent les themes light/dark.
Architecture interne
Section intitulée « Architecture interne »graph TD subgraph "@webmcp-auto-ui/ui" THEME[ThemeProvider] --> TOKENS[Tokens CSS] WR[WidgetRenderer] --> SIMPLE[9 Widgets simples] WR --> RICH[16 Widgets riches] WR --> CUSTOM[Custom widgets<br/>via WebMCP server] BR[BlockRenderer] --> WR
LAYOUTS[Layouts] --> TILING[TilingLayout] LAYOUTS --> FLOATING[FloatingLayout] LAYOUTS --> STACK[StackLayout] LAYOUTS --> FLEX[FlexLayout]
AGENT_UI[Composants Agent] --> LLM[LLMSelector] AGENT_UI --> GEMMA[ModelLoader] AGENT_UI --> MCP_S[McpStatus] AGENT_UI --> PROG[AgentProgress] AGENT_UI --> CHAT[ChatPanel] AGENT_UI --> CONSOLE[AgentConsole] AGENT_UI --> SETTINGS[SettingsPanel]
BUS[bus FONC] --> LINKS[LinkOverlay] BUS --> LI[LinkIndicators] ADAPTER[layoutAdapter] --> LAYOUTS end
SDK["@webmcp-auto-ui/sdk"] -->|canvas store| AGENT_UI AGENT["@webmcp-auto-ui/agent"] -->|autoui server| WRInstallation
Section intitulée « Installation »import { WidgetRenderer, LLMSelector, ThemeProvider, bus } from '@webmcp-auto-ui/ui';Dans un package.json d’app :
{ "devDependencies": { "@webmcp-auto-ui/ui": "file:../../packages/ui", "@webmcp-auto-ui/sdk": "file:../../packages/sdk" }}Peer dependencies : svelte ^5.0.0, d3 ^7.9.0 (pour D3Widget), leaflet >=1.9.0 (pour MapView).
ThemeProvider
Section intitulée « ThemeProvider »Enveloppe racine qui fournit le contexte de theme a tous les composants enfants. Gere le basculement dark/light et les overrides CSS.
<script lang="ts"> import { ThemeProvider } from '@webmcp-auto-ui/ui';</script>
<ThemeProvider defaultMode="dark" overrides={{ '--color-primary': '#4F46E5' }}> <!-- Tous les composants enfants heritent du theme --> <slot /></ThemeProvider>Props :
interface Props { defaultMode?: ThemeMode; // 'light' | 'dark' (defaut: 'light') overrides?: Record<string, string>; // CSS custom properties theme?: ThemeJSON; // Theme JSON externe children: Snippet;}getTheme
Section intitulée « getTheme »Fonction contexte Svelte pour acceder a l’API de theme depuis n’importe quel composant enfant de ThemeProvider.
<script lang="ts"> import { getTheme } from '@webmcp-auto-ui/ui';
const theme = getTheme();</script>
<span>Mode : {theme.mode}</span><button onclick={theme.toggle}>Basculer</button>API retournee :
interface ThemeAPI { readonly mode: ThemeMode; // 'light' | 'dark' toggle: () => void; // Bascule dark ↔ light setMode: (m: ThemeMode) => void;}Tokens et constantes
Section intitulée « Tokens et constantes »import { DARK_TOKENS, LIGHT_TOKENS, THEME_MAP } from '@webmcp-auto-ui/ui';import type { ThemeMode, ThemeOverrides, ThemeTokens } from '@webmcp-auto-ui/ui';
// DARK_TOKENS / LIGHT_TOKENS : Record<string, string> avec les CSS variables par mode// THEME_MAP : { light: ThemeTokens, dark: ThemeTokens }Rendu de widgets
Section intitulée « Rendu de widgets »WidgetRenderer
Section intitulée « WidgetRenderer »Composant principal qui resout et affiche un widget. Il cherche le widget dans les serveurs WebMCP fournis (custom), puis dans le registre natif. Si le widget est inconnu, un fallback est affiche.
<script lang="ts"> import { WidgetRenderer } from '@webmcp-auto-ui/ui'; import { autoui } from '@webmcp-auto-ui/agent';</script>
<WidgetRenderer id="block_1" type="stat" data={{ label: 'Revenue', value: '$42k', trend: 'up' }} servers={[autoui]} oninteract={(type, action, payload) => console.log(action, payload)}/>Props :
interface Props { id?: string; // ID du widget (auto-genere si absent) type: string; // Type de widget ('stat', 'chart', etc.) data: Record<string, unknown>; // Donnees du widget servers?: WebMcpServer[]; // Serveurs WebMCP (pour les widgets custom) oninteract?: (type: string, action: string, payload: unknown) => void;}Actions d’interaction supportees :
| Action | Widget | Description |
|---|---|---|
itemclick | list | Clic sur un item de liste |
rowclick | data-table | Clic sur une ligne du tableau |
cardclick | cards | Clic sur une carte |
imageclick | gallery | Clic sur une image |
slidechange | carousel | Changement de slide |
cellclick | grid-data | Clic sur une cellule |
Auto-enregistrement WebMCP : chaque widget s’enregistre automatiquement sur navigator.modelContext avec 3 outils :
widget_{id}_get()— obtenir les donnees actuelleswidget_{id}_update(...)— mettre a jour les donneeswidget_{id}_remove()— supprimer le widget
BlockRenderer
Section intitulée « BlockRenderer »Alias herite de WidgetRenderer. Conserve pour retrocompatibilite.
<BlockRenderer type="stat" data={{ label: 'Sales', value: '100' }} />Widgets simples (9)
Section intitulée « Widgets simples (9) »Widgets legers pour l’affichage de donnees basiques.
StatBlock
Section intitulée « StatBlock »Statistique cle avec label, valeur, et tendance optionnelle. Affiche une carte compacte avec une fleche de tendance.
<StatBlock data={{ label: 'Revenue', value: '$42k', trend: 'up' // 'up' | 'down' | 'stable'}} />Paires cle-valeur en grille.
<KVBlock data={{ items: [ { key: 'Status', value: 'Active' }, { key: 'Region', value: 'Europe' }, { key: 'Users', value: '1,234' } ]}} />ListBlock
Section intitulée « ListBlock »Liste d’items avec puces.
<ListBlock data={{ items: ['Premier item', 'Deuxieme item', 'Troisieme item'], title: 'Taches'}} />ChartBlock
Section intitulée « ChartBlock »Graphique a barres simple (sans librairie externe).
<ChartBlock data={{ bars: [['Jan', 10], ['Fev', 20], ['Mar', 15], ['Avr', 25]]}} />AlertBlock
Section intitulée « AlertBlock »Alerte coloree avec icone et message. Niveaux : info, warning, error, success.
<AlertBlock data={{ level: 'warning', title: 'Attention', message: 'Les donnees sont obsoletes depuis 24h.'}} />CodeBlock
Section intitulée « CodeBlock »Bloc de code avec coloration syntaxique et bouton de copie.
<CodeBlock data={{ code: 'const x = 42;\nconsole.log(x);', language: 'javascript'}} />TextBlock
Section intitulée « TextBlock »Texte Markdown rendu en HTML.
<TextBlock data={{ text: '# Titre\n\nParagraphe avec **gras** et *italique*.'}} />ActionsBlock
Section intitulée « ActionsBlock »Boutons d’action interactifs. L’agent peut proposer des choix a l’utilisateur.
<ActionsBlock data={{ actions: [ { label: 'Approuver', value: 'approve', variant: 'primary' }, { label: 'Rejeter', value: 'reject', variant: 'danger' } ]}} />TagsBlock
Section intitulée « TagsBlock »Badges/tags colores.
<TagsBlock data={{ tags: [ { label: 'Frontend', color: '#3B82F6' }, { label: 'Production', color: '#10B981' }, { label: 'Urgent', color: '#EF4444' } ]}} />Widgets riches (16)
Section intitulée « Widgets riches (16) »Widgets avances pour des visualisations complexes.
DataTable
Section intitulée « DataTable »Tableau de donnees avec tri par colonne cliquable.
<DataTable spec={{ columns: [ { key: 'name', label: 'Nom' }, { key: 'age', label: 'Age' }, { key: 'role', label: 'Role' } ], rows: [ { name: 'Alice', age: 30, role: 'Dev' }, { name: 'Bob', age: 25, role: 'Design' } ] }} onrowclick={(row) => console.log('Clic:', row)}/>StatCard
Section intitulée « StatCard »Carte statistique enrichie avec icone, tendance, et description.
<StatCard spec={{ title: 'Revenus', value: '$42,000', change: '+12%', trend: 'up', description: 'Par rapport au trimestre precedent'}} />Timeline
Section intitulée « Timeline »Chronologie d’evenements avec statuts visuels.
<Timeline spec={{ events: [ { title: 'Lancement', date: '2024-01', status: 'done' }, { title: 'Beta publique', date: '2024-03', status: 'active' }, { title: 'v1.0', date: '2024-06', status: 'pending' } ]}} />ProfileCard
Section intitulée « ProfileCard »Fiche profil avec avatar, champs personnalises et statistiques.
<ProfileCard spec={{ name: 'Alice Martin', subtitle: 'Lead Developer', avatar: 'https://example.com/avatar.jpg', fields: [ { label: 'Equipe', value: 'Backend' }, { label: 'Localisation', value: 'Paris' } ], stats: [ { label: 'Commits', value: '1,234' }, { label: 'PRs', value: '89' } ]}} />Trombinoscope
Section intitulée « Trombinoscope »Grille de profils (equipe, classe, organisation).
<Trombinoscope spec={{ people: [ { name: 'Alice', role: 'Dev', avatar: '...' }, { name: 'Bob', role: 'Design', avatar: '...' } ]}} />JsonViewer
Section intitulée « JsonViewer »Arbre JSON interactif et explorable avec ouverture/fermeture des noeuds.
<JsonViewer spec={{ data: { users: [{ name: 'Alice' }, { name: 'Bob' }], count: 2 }, expanded: true}} />Hemicycle
Section intitulée « Hemicycle »Representation d’un hemicycle parlementaire avec sieges colores par groupe.
<Hemicycle spec={{ groups: [ { name: 'Majorite', seats: 289, color: '#3B82F6' }, { name: 'Opposition', seats: 248, color: '#EF4444' } ]}} />Chart (Rich)
Section intitulée « Chart (Rich) »Graphique multi-series. Supporte : bar, line, area, pie, donut.
<Chart spec={{ type: 'bar', labels: ['Q1', 'Q2', 'Q3', 'Q4'], data: [ { label: 'Ventes', values: [10, 20, 15, 25] }, { label: 'Couts', values: [8, 12, 10, 15] } ]}} />Grille de cartes avec titre, description, et tags.
<Cards spec={{ cards: [ { title: 'Projet A', description: 'En cours', tags: ['frontend'] }, { title: 'Projet B', description: 'Termine', tags: ['backend'] } ] }} oncardclick={(card) => console.log(card)}/>GridData
Section intitulée « GridData »Grille de donnees avec cellules cliquables (similaire a un tableur).
<GridData spec={{ headers: ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven'], rows: [ { label: 'Alice', cells: [8, 7, 9, 8, 6] }, { label: 'Bob', cells: [6, 8, 7, 9, 7] } ]}} />Diagramme Sankey pour visualiser des flux entre categories.
<Sankey spec={{ nodes: ['Source A', 'Source B', 'Target X', 'Target Y'], links: [ { source: 0, target: 2, value: 10 }, { source: 0, target: 3, value: 5 }, { source: 1, target: 3, value: 8 } ]}} />Carte Leaflet interactive avec marqueurs et popups.
<MapView spec={{ center: { lat: 48.8566, lng: 2.3522 }, zoom: 12, height: '400px', markers: [ { lat: 48.8566, lng: 2.3522, label: 'Paris', popup: 'Capitale' }, { lat: 48.8606, lng: 2.3376, label: 'Louvre' } ]}} />D3Widget
Section intitulée « D3Widget »Visualisations D3.js avec presets : hex-heatmap, radial, treemap, force graph.
<D3Widget spec={{ preset: 'treemap', data: { name: 'root', children: [ { name: 'A', value: 100 }, { name: 'B', value: 200 }, { name: 'C', children: [ { name: 'C1', value: 50 }, { name: 'C2', value: 150 } ]} ] }}} />JsSandbox
Section intitulée « JsSandbox »Sandbox JavaScript isolee pour des visualisations custom. Execute du code dans une iframe securisee.
<JsSandbox spec={{ code: "document.getElementById('root').textContent = 'Hello from sandbox!';", html: '<div id="root"></div>', css: 'body { font-family: sans-serif; padding: 16px; }', height: '300px'}} />LogViewer
Section intitulée « LogViewer »Visionneuse de logs avec filtrage par niveau et coloration.
<LogViewer spec={{ title: 'Application Logs', entries: [ { timestamp: '2024-01-15T10:30:00Z', level: 'info', message: 'Server started', source: 'main' }, { timestamp: '2024-01-15T10:31:00Z', level: 'error', message: 'Connection failed', source: 'db' }, { timestamp: '2024-01-15T10:32:00Z', level: 'warn', message: 'Slow query detected', source: 'db' }, ], maxHeight: '400px',}} />Galerie d’images responsive en grille.
<Gallery spec={{ title: 'Collection', images: [ { src: 'https://example.com/a.jpg', alt: 'Image A', caption: 'Premiere' }, { src: 'https://example.com/b.jpg', alt: 'Image B', caption: 'Deuxieme' }, ], columns: 3, gap: '8px', }} onimageclick={(img, index) => console.log(img, index)}/>Carousel
Section intitulée « Carousel »Carousel d’images/contenus avec navigation et autoplay.
<Carousel spec={{ title: 'Presentation', slides: [ { src: 'https://example.com/slide1.jpg', title: 'Slide 1' }, { content: '<h2>Texte</h2><p>Contenu HTML</p>', title: 'Slide 2' }, ], autoPlay: true, interval: 5000, }} onslidechange={(slide, index) => console.log(slide, index)}/>Composants agent
Section intitulée « Composants agent »LLMSelector
Section intitulée « LLMSelector »Selecteur de modele LLM avec support des modeles distants (e.g. Claude, Gemini, ChatGPT) et locaux (Gemma WASM).
<script lang="ts"> import { LLMSelector } from '@webmcp-auto-ui/ui'; import { canvas } from '@webmcp-auto-ui/sdk/canvas';</script>
<LLMSelector value={canvas.llm} onchange={(model) => canvas.setLlm(model)} />Props :
interface Props { value?: string; // Modele selectionne disabled?: boolean; onchange?: (model: string) => void;}Modeles affiches : haiku, sonnet, opus (LLM distant, e.g. Claude), gemma-e2b, gemma-e4b (Google Gemma WASM).
ModelLoader
Section intitulée « ModelLoader »Indicateur de chargement pour le modele Gemma WASM. Affiche une barre de progression, la taille telechargee, et le temps ecoule.
<ModelLoader status="loading" progress={45} elapsed={12} loadedMB={120} totalMB={267} modelName="Gemma E2B" onunload={() => { /* decharger le modele */ }}/>Props :
interface Props { status: 'idle' | 'loading' | 'ready' | 'error'; progress?: number; // 0-100 elapsed?: number; // Secondes ecoulees loadedMB?: number; // Mo telecharges totalMB?: number; // Mo total modelName?: string; onunload?: () => void; // Callback pour decharger le modele}McpStatus
Section intitulée « McpStatus »Indicateur de connexion MCP (pastille verte/rouge + nombre d’outils disponibles).
<McpStatus connected={canvas.mcpConnected} connecting={canvas.mcpConnecting} name={canvas.mcpName} toolCount={canvas.mcpTools.length} servers={[{ url: '...', name: 'recipes', toolCount: 5 }]}/>Props :
interface Props { connected: boolean; connecting: boolean; name?: string; toolCount?: number; servers?: { url: string; name: string; toolCount: number }[]; onconnect?: () => void;}AgentProgress
Section intitulée « AgentProgress »Barre de progression animee de l’agent avec metriques temps reel.
<AgentProgress active={canvas.generating} elapsed={12} toolCalls={3} lastTool="search_recipes"/>Props :
interface Props { active?: boolean; // Agent en cours de generation elapsed?: number; // Secondes ecoulees toolCalls?: number; // Nombre de tool calls lastTool?: string; // Nom du dernier outil appele}McpConnector
Section intitulée « McpConnector »Interface de connexion MCP avec champ URL et bouton.
<McpConnector url={canvas.mcpUrl} connecting={canvas.mcpConnecting} onconnect={(url) => { /* logique de connexion */ }} oninput={(url) => canvas.setMcpUrl(url)}/>Props :
interface Props { url?: string; connecting?: boolean; oninput?: (url: string) => void; onconnect?: (url: string) => void;}ChatPanel
Section intitulée « ChatPanel »Panneau de chat complet avec feed de messages, indicateur de generation, et champ de saisie.
<script lang="ts"> import { ChatPanel } from '@webmcp-auto-ui/ui'; import type { ChatFeedItem } from '@webmcp-auto-ui/ui';
let feed = $state<ChatFeedItem[]>([]); let input = $state('');</script>
<ChatPanel {feed} bind:input generating={false} timer={0} toolCount={0} lastTool="" onsend={(msg) => { /* envoyer le message a l'agent */ }}/>Props :
interface Props { feed?: ChatFeedItem[]; // Messages du chat input?: string; // Valeur du champ (bindable) generating?: boolean; // Agent en cours timer?: number; // Temps ecoule toolCount?: number; lastTool?: string; placeholder?: string; showSrc?: boolean; // Afficher la source des messages onsend?: (msg: string) => void; class?: string;}Types du feed :
interface ChatBubble { role: 'user' | 'assistant'; html: string;}interface ChatBlock { type: string; data: Record<string, unknown>;}type ChatFeedItem = ChatBubble | ChatBlock;AgentConsole
Section intitulée « AgentConsole »Console de logs de l’agent avec filtrage par type et bouton de vidage.
<AgentConsole logs={[ { ts: Date.now(), type: 'tool', detail: 'search_recipes({query: "pasta"})' }, { ts: Date.now(), type: 'rag', detail: 'Ingested 5 chunks (1.2k chars)' }, ]} onclear={() => { /* vider les logs */ }}/>Props :
interface Props { logs: { ts: number; type: string; detail: string }[]; onclear?: () => void; class?: string;}SettingsPanel
Section intitulée « SettingsPanel »Panneau de reglages agent avec tous les parametres bindables.
<SettingsPanel bind:systemPrompt bind:maxTokens bind:maxContextTokens bind:maxTools bind:cacheEnabled bind:temperature bind:topK/>Props :
interface Props { systemPrompt?: string; // Bindable effectivePrompt?: string; // Prompt final genere (lecture seule) maxTokens?: number; // Defaut: 4096 maxContextTokens?: number; // Defaut: 150000 maxTools?: number; // Defaut: 8 cacheEnabled?: boolean; // Defaut: true temperature?: number; // Defaut: 0.7 topK?: number; // Defaut: 10 modelType?: 'remote' | 'wasm'; modelId?: string; class?: string;}EphemeralBubble
Section intitulée « EphemeralBubble »Bulles de messages ephemeres affichees pendant la generation. Disparaissent automatiquement.
<EphemeralBubble ephemeral={[ { id: '1', role: 'assistant', html: '<p>En reflexion...</p>' }]} />TokenBubble
Section intitulée « TokenBubble »Metriques de tokens en temps reel (requetes/min, tokens in/out, cache).
<script lang="ts"> import { TokenBubble } from '@webmcp-auto-ui/ui'; import { TokenTracker } from '@webmcp-auto-ui/agent';
const tracker = new TokenTracker(); let metrics = $state(tracker.metrics); tracker.subscribe(m => { metrics = m; });</script>
<TokenBubble {metrics} visible={true} />RemoteMCPserversDemo
Section intitulée « RemoteMCPserversDemo »Interface de connexion multi-serveurs MCP avec liste pre-configuree.
<RemoteMCPserversDemo servers={MCP_DEMO_SERVERS} connectedUrls={['https://mcp.example.com/mcp']} loading={[]} onconnect={(url) => { /* connecter */ }} onconnectall={() => { /* tout connecter */ }} ondisconnect={(url) => { /* deconnecter */ }}/>DiagnosticModal / DiagnosticIcon
Section intitulée « DiagnosticModal / DiagnosticIcon »Composants pour afficher les resultats de runDiagnostics du package agent.
<DiagnosticIcon diagnostics={diagnosticResults} /><DiagnosticModal diagnostics={diagnosticResults} open={showModal} />Primitives
Section intitulée « Primitives »Conteneur carte avec border et fond surface.
<Card><p>Contenu</p></Card>Conteneur avec barre de titre, collapsible optionnellement.
<Panel title="Statistiques" collapsible={true}> <div>Contenu du panneau</div></Panel>Props : title?: string, collapsible?: boolean, collapsed?: boolean, onclose?: () => void
Fenetre avec barre de titre, optionnellement deplaçable.
<Window title="Editeur" draggable={true}> <p>Contenu</p></Window>Props : title: string, draggable?: boolean, onmove?: (dx, dy) => void
NativeSelect
Section intitulée « NativeSelect »Select HTML natif stylise avec le design system.
<NativeSelect bind:value class="w-24"> <option value="low">Low</option> <option value="normal">Normal</option></NativeSelect>Tooltip au survol.
<Tooltip content="Information supplementaire"> <span>Survolez-moi</span></Tooltip>Button / Badge
Section intitulée « Button / Badge »Composants de base avec variantes (pattern shadcn-svelte) :
<Button variant="default" size="sm">Cliquer</Button><Badge variant="outline">Tag</Badge>Ensemble de composants pour modales accessibles :
<Dialog> <DialogTrigger><button>Ouvrir</button></DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Titre</DialogTitle> <DialogDescription>Description</DialogDescription> </DialogHeader> <p>Contenu</p> <DialogFooter><button>Confirmer</button></DialogFooter> </DialogContent></Dialog>SafeImage
Section intitulée « SafeImage »Image robuste avec validation d’URL et fallback. Valide les protocoles (http, https, data, /), affiche un placeholder si l’URL est invalide ou si l’image ne charge pas.
<SafeImage src="https://example.com/image.jpg" alt="Description" fallbackText="Image" hideOnError={false}/>TilingLayout
Section intitulée « TilingLayout »Grille de tuiles responsive.
<TilingLayout gap="4" columns={3}> <div>Widget 1</div> <div>Widget 2</div> <div>Widget 3</div></TilingLayout>Props : gap?: string, columns?: number, rows?: number
FloatingLayout
Section intitulée « FloatingLayout »Layout flottant avec fenetres deplaçables et redimensionnables par drag. Utilise un snippet children qui reçoit le contexte de chaque fenetre.
<script lang="ts"> import { FloatingLayout } from '@webmcp-auto-ui/ui'; import type { ManagedWindow } from '@webmcp-auto-ui/ui';
let fl: FloatingLayout; let windows = $state<ManagedWindow[]>([]);</script>
<FloatingLayout bind:this={fl} {windows} defaultWidth={380} defaultHeight={280}> {#snippet children(win, _lw, ctx)} <div class="bg-surface rounded-lg border"> <!-- Barre de titre (drag) --> <div onmousedown={(e) => ctx.ondragstart(e)} ondblclick={() => ctx.ontogglecollapse()}> {win.title} </div> <!-- Contenu (collapsible) --> {#if !ctx.collapsed} <div>Widget {win.id}</div> {/if} <!-- Poignee de redimensionnement --> <div onmousedown={(e) => ctx.onresizestart(e)} class="resize-handle"></div> </div> {/snippet}</FloatingLayout>Props : windows: ManagedWindow[], gap?: number, defaultWidth?: number, defaultHeight?: number, onmove?, onresize?
Contexte snippet :
interface SnippetCtx { ondragstart: (e: MouseEvent) => void; ontogglecollapse: () => void; onfittocontent: () => void; onresizestart: (e: MouseEvent) => void; collapsed: boolean;}Methodes exposees (via bind:this) :
fl.move(id, x, y): voidfl.resize(id, w, h): voidfl.toggleCollapse(id): voidfl.fitToContent(id): voidFlexLayout
Section intitulée « FlexLayout »Layout flex responsive avec largeur adaptative.
<FlexLayout {windows} minWidth={260} maxWidth={600} showSlider={true}> {#snippet children(win, _lw, ctx)} <div>Widget {win.id} (scale: {ctx.scale})</div> {/snippet}</FlexLayout>Props : windows, minWidth?: number, maxWidth?: number, gap?: number, showSlider?: boolean
StackLayout
Section intitulée « StackLayout »Layout empile : une fenetre a la fois ou defilement vertical.
<StackLayout {windows} mode="scroll" gap={8}> {#snippet children(win, _lw)} <div>{win.title}: contenu</div> {/snippet}</StackLayout>Props : windows, mode?: 'single' | 'scroll', gap?: number, padding?: number
GridLayout
Section intitulée « GridLayout »Grille CSS avec positionnement explicite des cellules.
<GridLayout rows={4} cols={4}> <div style="grid-row: 1; grid-column: 1 / 3;">Widget A</div> <div style="grid-row: 1 / 3; grid-column: 3 / 5;">Widget B</div></GridLayout>Types Window Manager
Section intitulée « Types Window Manager »interface ManagedWindow { id: string; title: string; visible: boolean; focused: boolean; folded: boolean; weight: number; createdAt: number; lastFocusedAt: number;}
interface LayoutWindow { id: string; x: number; y: number; width: number; height: number; zIndex: number; visible: boolean; folded: boolean;}
interface FloatingWindowState { x: number; y: number; width: number; height: number; zIndex: number;}FONC Message Bus
Section intitulée « FONC Message Bus »Systeme de messagerie inter-composants pour la communication entre widgets sans couplage direct.
import { bus } from '@webmcp-auto-ui/ui';import type { BusMessage } from '@webmcp-auto-ui/ui';API du bus
Section intitulée « API du bus »// Enregistrer un participant (widget, composant)const unregister = bus.register( 'widget_123', // ID unique 'widget', // Type de participant ['update', 'interact'], // Channels ecoutes (ou ['*'] pour tout) (msg: BusMessage) => { console.log(`Message de ${msg.from}: ${msg.channel}`, msg.payload); });
// Envoyer un message ciblebus.send('widget_123', 'widget_456', 'update', { newValue: 42 });
// Diffuser a tous les participantsbus.broadcast('widget_123', 'refresh', { timestamp: Date.now() });
// S'abonner sans s'enregistrer comme participantconst unsub = bus.subscribe(['update', 'interact'], (msg) => { /* ... */ });
// Lier des widgets (communication bidirectionnelle)const groupId = bus.link(['widget_1', 'widget_2', 'widget_3']);
// Delierbus.unlink('widget_1', groupId);
// Interroger les liensbus.getLinks('widget_1'); // string[] — groupesbus.getGroup(groupId); // string[] — widgets du groupebus.hasLinks('widget_1'); // booleanLe bus est utilise par LinkOverlay et LinkIndicators pour dessiner les liens visuels entre widgets.
LinkOverlay
Section intitulée « LinkOverlay »Overlay SVG plein ecran qui dessine des fleches bezier entre les widgets lies.
<LinkOverlay />S’abonne automatiquement aux evenements show-links et link du bus.
LinkIndicators
Section intitulée « LinkIndicators »Indicateurs visuels dans les barres de titre des fenetres.
<LinkIndicators busId="widget_123" />linkGroupColor
Section intitulée « linkGroupColor »Retourne une couleur HSL deterministe pour un group ID.
import { linkGroupColor } from '@webmcp-auto-ui/ui';const color = linkGroupColor('group_abc'); // "hsl(210, 70%, 60%)"Layout Adapter
Section intitulée « Layout Adapter »Singleton qui connecte les layouts aux outils de l’agent (canvas tool avec actions move, resize, style).
import { layoutAdapter } from '@webmcp-auto-ui/ui';
// Enregistrer les callbacks de layout (dans onMount)layoutAdapter.register({ move: (id, x, y) => floatingLayout?.move(id, x, y), resize: (id, w, h) => floatingLayout?.resize(id, w, h), style: (id, styles) => { const el = document.querySelector(`[data-block-id="${id}"]`); if (el instanceof HTMLElement) { for (const [k, v] of Object.entries(styles)) { el.style.setProperty(`--color-${k}`, v); } } },});
// Desenregistrer (dans onDestroy)layoutAdapter.unregister();Tutoriel : construire un chat agent complet
Section intitulée « Tutoriel : construire un chat agent complet »Ce tutoriel assemble les composants UI pour creer une interface de chat avec widgets, selection de modele, et progression agent.
Etape 1 : structure de base
Section intitulée « Etape 1 : structure de base »<script lang="ts"> import { ThemeProvider, LLMSelector, McpStatus, AgentProgress, ChatPanel, WidgetRenderer } from '@webmcp-auto-ui/ui'; import { canvas } from '@webmcp-auto-ui/sdk/canvas'; import { runAgentLoop, RemoteLLMProvider, autoui } from '@webmcp-auto-ui/agent'; import type { ChatFeedItem } from '@webmcp-auto-ui/ui';
let feed = $state<ChatFeedItem[]>([]); let input = $state(''); let timer = $state(0); let toolCount = $state(0); let lastTool = $state('');
const provider = new RemoteLLMProvider({ proxyUrl: '/api/chat', model: 'sonnet', });</script>Etape 2 : logique de chat
Section intitulée « Etape 2 : logique de chat »<script lang="ts"> // ... (suite du script precedent)
async function send(msg: string) { feed = [...feed, { role: 'user', html: `<p>${msg}</p>` }]; canvas.generating = true; const start = Date.now(); const interval = setInterval(() => { timer = (Date.now() - start) / 1000; }, 100);
try { const result = await runAgentLoop(msg, { provider, layers: [autoui.layer()], maxIterations: 5, callbacks: { onToolCall: (call) => { toolCount++; lastTool = call.name; }, onWidget: (type, data) => { feed = [...feed, { type, data }]; return { id: `w_${Date.now()}` }; }, onText: (text) => { feed = [...feed, { role: 'assistant', html: `<p>${text}</p>` }]; }, }, }); } finally { canvas.generating = false; clearInterval(interval); } }</script>Etape 3 : assembler l’UI
Section intitulée « Etape 3 : assembler l’UI »<ThemeProvider defaultMode="dark"> <div class="flex flex-col h-screen"> <!-- Barre superieure --> <header class="p-4 border-b flex items-center gap-4"> <LLMSelector value={canvas.llm} onchange={(m) => canvas.setLlm(m)} /> <McpStatus connected={canvas.mcpConnected} name={canvas.mcpName} /> {#if canvas.generating} <AgentProgress active elapsed={timer} toolCalls={toolCount} lastTool={lastTool} /> {/if} </header>
<!-- Zone principale --> <main class="flex-1 overflow-auto p-4"> <ChatPanel {feed} bind:input generating={canvas.generating} {timer} {toolCount} {lastTool} onsend={send} /> </main> </div></ThemeProvider>Integration avec les autres packages
Section intitulée « Integration avec les autres packages »graph LR UI["@webmcp-auto-ui/ui"] -->|WidgetRenderer| CORE["@webmcp-auto-ui/core"] UI -->|canvas store| SDK["@webmcp-auto-ui/sdk"] UI -->|autoui server| AGENT["@webmcp-auto-ui/agent"] UI -->|TokenTracker| AGENT AGENT -->|callbacks| UI SDK -->|MCP_DEMO_SERVERS| UI- WidgetRenderer utilise
mountWidgetde core pour les widgets vanilla - Les composants agent lisent l’etat du canvas store du SDK
- Le
TokenBubbles’abonne auTokenTrackerde l’agent - Le
RemoteMCPserversDemoutiliseMCP_DEMO_SERVERSdu SDK
Bonnes pratiques
Section intitulée « Bonnes pratiques »Combien de widgets sont disponibles ? 25 widgets natifs (9 simples + 16 riches), plus la possibilite d’ajouter des widgets custom via un serveur WebMCP.
Puis-je utiliser les widgets sans l’agent ?
Oui. Chaque widget est un composant Svelte autonome importable directement. Le WidgetRenderer est pratique mais pas obligatoire.
Comment ajouter un widget custom ?
Creez un serveur WebMCP avec createWebMcpServer() (package core), enregistrez votre widget avec registerWidget(), et passez le serveur dans la prop servers du WidgetRenderer.
Le bus FONC est-il persistant ? Non, le bus est en memoire. Les liens et les messages sont perdus au rechargement de la page. Pour persister des liens, stockez-les dans le canvas store.
Quel est le rapport entre les layouts ?
TilingLayout est une grille statique. FlexLayout est responsive avec un slider de taille. FloatingLayout permet le drag & drop libre. StackLayout empile verticalement. Choisissez selon le mode d’interaction souhaite.