### Clone Visualise Skill (User-Level) Source: https://github.com/bentossell/visualise/blob/main/README.md Clones the Visualise agent skill repository to the user's local agents directory. This is the recommended installation method for general use. ```bash git clone https://github.com/bentossell/visualise.git ~/.agents/skills/visualise ``` -------------------------------- ### MCP Server Wrapper for Visualisation (TypeScript) Source: https://github.com/bentossell/visualise/blob/main/references/client-implementation.md Sets up an MCP server with tools to read markdown files based on provided modules and render visual components. It uses the '@modelcontextprotocol/sdk' and 'zod' for validation. The server listens for incoming requests after defining the 'visualizer_read_me' and 'render_visual' tools. ```typescript import { Server } from '@modelcontextprotocol/sdk/server'; import { z } from 'zod'; import fs from 'fs'; const server = new Server({ name: 'inline-visualizer', version: '1.0.0' }); server.tool('visualizer_read_me', { modules: z.array(z.enum(['design-system', 'diagrams', 'components', 'charts'])), }, async ({ modules }) => { const docs = modules.map(m => fs.readFileSync(`./references/${m}.md`, 'utf-8')).join(' --- '); return { content: [{ type: 'text', text: docs }] }; }); server.tool('render_visual', { title: z.string(), widget_code: z.string(), }, async ({ title, widget_code }) => ({ content: [{ type: 'resource', resource: { uri: `visual://${title}`, mimeType: 'text/html', text: widget_code }}] })); server.listen(); ``` -------------------------------- ### Create Inline SVG Visualizations Source: https://github.com/bentossell/visualise/blob/main/references/charts.md Examples of lightweight, dependency-free SVG charts including a horizontal bar and a sparkline. ```svg Category A 72% ``` ```svg ``` -------------------------------- ### Streaming Support for Iframe - TypeScript Source: https://github.com/bentossell/visualise/blob/main/references/client-implementation.md Provides functions to manage streaming content into an iframe. `startStreaming` initializes the iframe document with CSS, `appendChunk` writes incoming data chunks, and `finishStreaming` closes the document, triggering script execution. ```typescript function startStreaming(iframe, isDark) { const doc = iframe.contentDocument; doc.open(); doc.write(``); // Leave open for appending } function appendChunk(iframe, chunk) { iframe.contentDocument.write(chunk); } function finishStreaming(iframe) { iframe.contentDocument.close(); // Triggers script execution } ``` -------------------------------- ### Theme CSS Generation Function (TypeScript) Source: https://github.com/bentossell/visualise/blob/main/references/client-implementation.md A TypeScript function that generates CSS variables for theming based on the system's dark mode preference. It defines color, font, and border-radius tokens for both dark and light themes. This function is used by the `VisualWidget` component. ```typescript function getThemeCSS(isDark: boolean): string { return isDark ? ` :root { --color-text-primary: #E5E7EB; --color-text-secondary: #9CA3AF; --color-text-tertiary: #6B7280; --color-text-info: #60A5FA; --color-text-success: #34D399; --color-text-warning: #FBBF24; --color-text-danger: #F87171; --color-background-primary: #1A1A1A; --color-background-secondary: #262626; --color-background-tertiary: #111111; --color-border-tertiary: rgba(255,255,255,0.15); --color-border-secondary: rgba(255,255,255,0.3); --font-sans: system-ui, -apple-system, sans-serif; --font-mono: 'SF Mono', Menlo, monospace; --border-radius-md: 8px; --border-radius-lg: 12px; }` : ` :root { --color-text-primary: #1F2937; --color-text-secondary: #6B7280; --color-text-tertiary: #9CA3AF; --color-text-info: #2563EB; --color-text-success: #059669; --color-text-warning: #D97706; --color-text-danger: #DC2626; --color-background-primary: #FFFFFF; --color-background-secondary: #F9FAFB; --color-background-tertiary: #F3F4F6; --color-border-tertiary: rgba(0,0,0,0.15); --color-border-secondary: rgba(0,0,0,0.3); --font-sans: system-ui, -apple-system, sans-serif; --font-mono: 'SF Mono', Menlo, monospace; --border-radius-md: 8px; --border-radius-lg: 12px; }`; } ``` -------------------------------- ### Detect Visualizer Output in Messages - React/JSX Source: https://github.com/bentossell/visualise/blob/main/references/client-implementation.md Parses chat message content to detect and separate content enclosed within '```visualizer\n...```' code fences. It splits the content and renders detected visualizer parts using a `VisualWidget` component and other content as Markdown. ```jsx function ChatMessage({ content }) { const parts = content.split(/```visualizer\n([\s\S]*?)```/g); return (
{parts.map((part, i) => { if (i % 2 === 1) { return sendMessage(text)} />; } return {part}; })}
); } ``` -------------------------------- ### Visualise Renderer Component (React/TypeScript) Source: https://github.com/bentossell/visualise/blob/main/references/client-implementation.md A React component that renders visualisations within a sandboxed iframe. It injects theme CSS, SVG classes, and widget code, and handles iframe resizing and communication bridges for sending prompts and opening links. It relies on external `getThemeCSS` and `SVG_CLASSES` variables. ```tsx import { useRef, useState, useEffect } from 'react'; export function VisualWidget({ code, title, onSendPrompt }) { const iframeRef = useRef(null); const [height, setHeight] = useState(200); const isDark = matchMedia('(prefers-color-scheme: dark)').matches; useEffect(() => { const iframe = iframeRef.current; if (!iframe) return; const doc = iframe.contentDocument; if (!doc) return; // Inject theme + SVG classes + widget code doc.open(); doc.write(`${code}`); doc.close(); // Bridge: sendPrompt from iframe → chat iframe.contentWindow.sendPrompt = (text) => onSendPrompt?.(text); iframe.contentWindow.openLink = (url) => window.open(url, '_blank'); // Auto-size let timer; const ro = new ResizeObserver(([entry]) => { clearTimeout(timer); timer = setTimeout(() => setHeight(Math.ceil(entry.contentRect.height) + 16), 50); }); if (doc.body) ro.observe(doc.body); return () => { ro.disconnect(); clearTimeout(timer); }; }, [code, isDark, onSendPrompt]); return (