smithery/ssshooter

Streaming Mindmap Rendering

Implement real-time streaming mindmap rendering using Mind Elixir in web applications. Supports streaming text parsing and incremental updates.

Installation

$ npx skills add smithery/ssshooter --skill streaming-mindmap-rendering

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 5,296 B
  • docs SUMMARY.md 178 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Streaming Mindmap Rendering

This skill guides you through implementing a streaming mindmap renderer using mind-elixir. This technique allows you to display a mindmap that grows in real-time as data is generated by an AI model or fetched from a stream.

Prerequisites

  • React (or any frontend framework, examples use React)
  • mind-elixir library

1. Install Dependencies

First, ensure you have mind-elixir installed.

npm install mind-elixir

2. Component Structure

Create a wrapper component for mind-elixir to handle the lifecycle and updates.

import MindElixir, { type MindElixirData, type MindElixirInstance } from 'mind-elixir'
import { useEffect, useRef } from 'react'

export function MindmapRenderer({ data }: { data: MindElixirData | null }) {
  const elRef = useRef<HTMLDivElement>(null)
  const meRef = useRef<MindElixirInstance | null>(null)

  useEffect(() => {
    if (!elRef.current) return

    meRef.current = new MindElixir({
      el: elRef.current,
      direction: MindElixir.RIGHT,
    })

    // Initial empty state or loading state
    meRef.current.init(data || { nodeData: { topic: 'Loading...', id: 'root' } })

    return () => {
      // Cleanup if necessary
    }
  }, [])

  // Update effect
  useEffect(() => {
    if (meRef.current && data) {
      // Refresh the graph with new data
      meRef.current.refresh(data)
    }
  }, [data])

  return <div ref={elRef} style={{ height: '500px', width: '100%' }} />
}

3. Streaming & Parsing Logic

The core of this skill is efficiently handling the stream and parsing potentially incomplete data.

Data Formats

Mind Elixir supports two main formats:

  1. JSON (Native): Hierarchical tree structure. Hard to stream because JSON is invalid until complete.
  2. Plain Text (Recommended for Streaming): Indentation-based or markdown-list-based text. Easier to parse partially.

Plain Text Format Example

- Root Node
  - Child Node 1
    - Child Node 1-1
    - Child Node 1-2
    - Child Node 1-3
    - }:2 Summary of first two nodes
  - Child Node 2
    - Child Node 2-1 [^id1]
    - Child Node 2-2 [^id2]
    - Child Node 2-3 {color: "#e87a90"}
    - > [^id1] <-Bidirectional Link-> [^id2]
  - Child Node 3
    - Child Node 3-1 [^id3]
    - Child Node 3-2 [^id4]
    - Child Node 3-3 [^id5]
    - > [^id3] >-Unidirectional Link-> [^id4]
    - > [^id3] <-Unidirectional Link-< [^id5]
  - Child Node 4
    - Child Node 4-1 [^id6]
    - Child Node 4-2 [^id7]
    - Child Node 4-3 [^id8]
    - } Summary of all previous nodes
    - Child Node 4-4
  - > [^id1] <-Link position is not restricted, as long as the id can be found during rendering-> [^id8]

Parsing Implementation

Use mind-elixir/plaintextConverter (or a custom parser) to convert text to the Mind Elixir JSON format.

````typescript import { plaintextToMindElixir } from 'mind-elixir/plaintextConverter'

// Helper to clean Markdown code blocks if your stream includes them function cleanStreamContent(content: string): string { return content .replace(/^``[\w]*\n?/gm, '') .replace(/``$/gm, '') .trim() }

// State hooks in your parent component const [mindmapData, setMindmapData] = useState<MindElixirData | null>(null) const accumulatedText = useRef('') const lastRenderTime = useRef(0)

// Streaming function (Generic Example) async function startStreaming(url: string) { const response = await fetch(url) const reader = response.body?.getReader() const decoder = new TextDecoder()

if (!reader) return

while (true) { const { done, value } = await reader.read() if (done) break

const chunk = decoder.decode(value) accumulatedText.current += chunk

// Throttle updates to avoid freezing the UI const now = Date.now() if (now - lastRenderTime.current > 500) { // 500ms throttle updateMindmap() lastRenderTime.current = now } }

// Final update updateMindmap() }

function updateMindmap() { try { const cleanText = cleanStreamContent(accumulatedText.current) const data = plaintextToMindElixir(cleanText) setMindmapData(data) // This triggers the useEffect in MindmapRenderer } catch (e) { // Ignore parse errors from incomplete chunks console.warn('Partial parse error ignored') } } ````

4. Optimization Tips

  • Throttling: Do not re-parse and re-render on every single byte. Use a throttle (e.g., 200-500ms).
  • Stable Root: Ensure the parsing logic maintains a stable root ID if possible, to prevent the whole graph from flashing.
  • Scroll to Last: To follow the generation, you can programmatically scroll to the last added node.
// Scroll to last node (inside MindmapRenderer update effect)
const lastNode = findLastNode(data.nodeData) // Implement traversal to find last node
if (lastNode?.id) {
  const nodeEle = meRef.current.findEle(lastNode.id)
  if (nodeEle) meRef.current.scrollIntoView(nodeEle)
}

5. Integrating with AI Prompts

When generating mindmaps with LLMs, instruct the model to use the plaintext format.