Panel
Overlay de canto flutuante dentro de um Canvas. Superfície de card com borda pré-tematizada para controles no nível do canvas, indicadores de status ou widgets contextuais.
Visão geral
Panel é uma skin da Gremorie sobre o primitivo Panel do xyflow. Ele ancora em um canto da viewport do Canvas e permanece fixo conforme o canvas dá pan e zoom - o que o torna a superfície certa para controles no nível do canvas (salvar, exportar, ações de layout), indicadores de status (agente raciocinando, estado de deploy) ou widgets contextuais que devem estar sempre alcançáveis.
O chrome da Gremorie adiciona uma margem m-4, uma borda arredondada, uma superfície bg-card e padding p-1 para que o panel se leia como um tile autocontido em vez de conteúdo cru flutuando sobre o workflow.
Para barras de ação por nó, use Toolbar. Para controles de zoom e fit, use Controls. Panel cobre o resto.
Preview
'use client';import '@xyflow/react/dist/style.css';import { Canvas, Panel } from '@gremorie/rx-ai';import { Button } from '@gremorie/rx-forms';import { type Edge as FlowEdge, type Node as FlowNode, ReactFlowProvider,} from '@xyflow/react';import { PlusIcon, SparklesIcon } from 'lucide-react';const gridNodes: FlowNode[] = [ { id: '1', position: { x: 0, y: 0 }, data: { label: 'A' } }, { id: '2', position: { x: 200, y: 80 }, data: { label: 'B' } },];const gridEdges: FlowEdge[] = [{ id: 'e1-2', source: '1', target: '2' }];export function PanelPreview() { return ( <div className="h-[360px] w-full"> <ReactFlowProvider> <Canvas edges={gridEdges} nodes={gridNodes}> <Panel position="top-right"> <div className="flex items-center gap-1"> <Button size="sm" variant="ghost"> <PlusIcon className="size-4" /> Add node </Button> <Button size="sm" variant="ghost"> <SparklesIcon className="size-4" /> Auto-layout </Button> </div> </Panel> </Canvas> </ReactFlowProvider> </div> );}Anatomia
Panel docked overlay container — leaf (rendered inside Canvas)Instalação
bash npx gremorie@latest add rx-panel bash pnpm dlx gremorie@latest add rx-panel bash yarn dlx gremorie@latest add rx-panel bash bunx --bun gremorie@latest add rx-panel Requer @xyflow/react.
Uso
import { Canvas, Panel } from "@gremorie/rx-ai";
import { Button } from "@gremorie/rx-forms";
export function Workflow() {
return (
<Canvas nodes={nodes} edges={edges}>
<Panel position="top-right">
<Button size="sm" onClick={save}>Save</Button>
</Panel>
</Canvas>
);
}A edição Angular deste componente está planejada; a edição React está production-ready hoje.
API
<Panel>
Encaminha todas as PanelProps de @xyflow/react. O wrapper da Gremorie só adiciona chrome.
| Prop | Type | Default | Description |
|---|---|---|---|
position | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" | "top-left" | Canto ao qual o panel ancora. Default do xyflow. |
children | ReactNode | - | Conteúdo do panel. Botões, pills de status, mini-forms, etc. |
className | string | - | Classes extras. Mescladas com o chrome padrão via cn. |
Chrome padrão: m-4 overflow-hidden rounded-md border bg-card p-1.
Composição
<Canvas>é o pai. MúltiplosPanels podem coexistir - um por canto / lado.- O conteúdo é livre. Solte botões, dropdowns, pills de status, mini-forms, ou qualquer outra coisa.
- Para panels densos, considere aumentar o padding via
className="p-3". Op-1padrão é afinado para linhas de um único botão de ícone.
Variações
Ações de salvar / publicar
Uma linha curta de botões no canto superior direito que sobrevive a qualquer pan e zoom do canvas.
<Panel position="top-right" className="flex gap-2 p-2">
<Button size="sm" variant="outline" onClick={discard}>
Discard
</Button>
<Button size="sm" onClick={save}>
Save
</Button>
</Panel>Indicador de status
Pill de status no canto superior esquerdo que espelha o estado de execução do agente.
<Panel position="top-left" className="px-3 py-2">
<div className="flex items-center gap-2 text-xs font-medium">
<span className="size-2 animate-pulse rounded-full bg-emerald-500" />
Agent running
</div>
</Panel>Barra de inserção no bottom-center
Para uma affordance de "solte um novo nó aqui", coloque um único botão no bottom-center.
<Panel position="bottom-center" className="px-3 py-2">
<Button size="sm" onClick={addStep}>
<PlusIcon className="size-4" />
Add step
</Button>
</Panel>Mini-form / busca
Componha com primitivos de formulário para affordances de busca inline ou edição rápida.
<Panel position="top-left" className="p-2">
<Input className="w-48" placeholder="Search nodes..." />
</Panel>Acessibilidade
- Keyboard: o conteúdo do Panel é React comum. A ordem de tab segue o DOM. Os children devem carregar seus próprios labels (
aria-label,aria-describedby) conforme necessário. - Focus visible: a superfície do Panel não captura foco por si só. Seus children capturam.
- Screen readers: um Panel fixado no canto não tem um role de região embutido. Se o panel hospeda um status crítico do workflow, envolva-o em uma
<section aria-label="...">(ou passerole="region"+aria-labeldiretamente). - Reduced motion: não há animação na superfície. Transições custom que você adicionar via
classNamedevem respeitarprefers-reduced-motion.