
Company News
Socket Partners with Replit to Block Malicious Packages in AI-Powered Development
Replit is integrating Socket Firewall into its AI-powered development experience to help protect builders from malicious open source packages.
@weave-md/stream
Advanced tools
Lightweight utilities for parsing and rendering streamed Weave content
Lightweight utilities for parsing and rendering streamed Weave content.
Weave's node: links parse as standard markdown—but chat interfaces need to render them. This package handles that:
[see details](node:details?display=overlay)
When an LLM generates Weave content in a chat interface, the host app needs to:
node: URLs to understand what's being referencedThis package provides zero-dependency utilities to do exactly that.
React is an optional peer dependency (only needed for the React component).
@weave-md/parse| @weave-md/stream | @weave-md/parse | |
|---|---|---|
| Purpose | Chat interface embedding | Full Weave tooling |
| Input | Single stream with stacked sections | One file per section |
| Output | { id, title, content }[] | Full AST with positions, diagnostics |
| Validation | None (trusts LLM output) | Strict (errors on missing id, duplicates) |
| Dependencies | Zero | unified, remark, micromark, etc. |
This package provides a lightweight alternative that supports stacked sections in a single stream—ideal for LLM output.
npm install @weave-md/stream
import { parseNodeUrl } from '@weave-md/stream'
const parsed = parseNodeUrl('node:intro?display=overlay')
// { id: 'intro', display: 'overlay' }
// Returns null for non-node URLs
parseNodeUrl('https://example.com') // null
import { splitSections } from '@weave-md/stream'
const markdown = `
---
id: intro
title: Introduction
---
This is the intro. See [details](node:details).
---
id: details
title: Details
---
Here are the details.
`
const sections = splitSections(markdown)
// [
// { id: 'intro', title: 'Introduction', content: 'This is the intro...' },
// { id: 'details', title: 'Details', content: 'Here are the details.' }
// ]
The content field contains the raw markdown body with frontmatter stripped.
Streaming behavior: When called on incomplete streamed content:
content may be incomplete (still being streamed)splitSections() as more content arrivesGraceful degradation: Node links render immediately as styled text, even before their target section exists. Once the target section is parsed, the link becomes interactive (clickable, shows preview, etc.).
import { parseNodeUrl, WeaveLink, createDisplayConfig } from '@weave-md/stream/react'
import ReactMarkdown from 'react-markdown'
// Configure display fallbacks for narrow viewports
const displayConfig = createDisplayConfig({
supported: ['inline', 'overlay', 'footnote'],
fallbacks: {
sidenote: 'footnote',
margin: 'footnote',
panel: 'overlay',
stretch: 'overlay',
}
})
function ChatMessage({ content, sections }) {
return (
<ReactMarkdown
components={{
a: ({ href, children }) => {
const parsed = parseNodeUrl(href)
if (parsed) {
return (
<WeaveLink
{...parsed}
resolveSection={(id) => sections.find(s => s.id === id)}
displayConfig={displayConfig}
onClick={(id, display) => handleNodeClick(id, display)}
>
{children}
</WeaveLink>
)
}
return <a href={href}>{children}</a>
}
}}
>
{content}
</ReactMarkdown>
)
}
Or render your own way:
a: ({ href, children }) => {
const parsed = parseNodeUrl(href)
if (parsed) {
return <span className="weave-link" data-target={parsed.id}>{children}</span>
}
return <a href={href}>{children}</a>
}
import MarkdownIt from 'markdown-it'
import { weaveLinkPlugin } from '@weave-md/stream/markdown-it'
const md = new MarkdownIt()
md.use(weaveLinkPlugin)
md.render('[see intro](node:intro?display=overlay)')
// <span class="weave-link" data-target="intro" data-display="overlay">see intro</span>
parseNodeUrl(href: string): ParsedNodeUrl | nullParses a node: URL and extracts its components.
interface ParsedNodeUrl {
id: string
display?: 'inline' | 'overlay' | 'footnote' | 'sidenote' | 'margin' | 'stretch' | 'panel'
export?: 'appendix' | 'inline' | 'omit'
}
Returns null if the URL doesn't start with node:.
splitSections(markdown: string): Section[]Extracts sections from markdown with stacked frontmatter.
interface Section {
id: string
title?: string
peek?: string
content: string // Raw markdown body (frontmatter stripped)
}
Heuristic: A new section starts when --- appears after a blank line, followed by a line starting with id:.
createDisplayConfig(options): DisplayConfigConfigures which display types are supported and their fallbacks.
type DisplayType = 'inline' | 'overlay' | 'footnote' | 'sidenote' | 'margin' | 'stretch' | 'panel'
interface DisplayConfigOptions {
// Which display types this environment supports
supported?: DisplayType[]
// Fallback for each unsupported type (defaults to 'overlay')
fallbacks?: Partial<Record<DisplayType, DisplayType>>
}
// Returns resolved display type given the requested type
interface DisplayConfig {
resolve: (requested: DisplayType | undefined) => DisplayType
}
Example: Narrow viewport config
import { createDisplayConfig } from '@weave-md/stream'
const narrowConfig = createDisplayConfig({
supported: ['inline', 'overlay', 'footnote'],
fallbacks: {
sidenote: 'footnote', // sidenote → footnote
margin: 'footnote', // margin → footnote
panel: 'overlay', // panel → overlay
stretch: 'overlay', // stretch → overlay
}
})
narrowConfig.resolve('sidenote') // → 'footnote'
narrowConfig.resolve('overlay') // → 'overlay'
narrowConfig.resolve(undefined) // → 'inline' (default)
Example: Wide viewport config
const wideConfig = createDisplayConfig({
supported: ['inline', 'overlay', 'footnote', 'sidenote', 'margin', 'panel'],
fallbacks: {
stretch: 'panel', // stretch not supported, use panel
}
})
Example: Responsive config with React
function useDisplayConfig() {
const isNarrow = useMediaQuery('(max-width: 768px)')
return useMemo(() => createDisplayConfig(
isNarrow
? { supported: ['inline', 'overlay', 'footnote'], fallbacks: { sidenote: 'footnote', margin: 'footnote', panel: 'overlay' } }
: { supported: ['inline', 'overlay', 'footnote', 'sidenote', 'margin', 'panel'] }
), [isNarrow])
}
WeaveLink (React component)interface WeaveLinkProps {
id: string
display?: string
children: ReactNode
resolveSection?: (id: string) => { title?: string; peek?: string } | null
displayConfig?: DisplayConfig // Resolves display type with fallbacks
onClick?: (id: string, display: DisplayType) => void // Only fires when resolved
}
The component automatically determines resolved state from resolveSection:
resolveSection returns a section object → resolved, interactiveresolveSection returns null or is not provided → pending, non-interactiveWhen displayConfig is provided, the effective display type is resolved before rendering and passed to onClick.
weaveLinkPlugin (markdown-it plugin)Transforms node: links into styled spans with data attributes.
Minimal React components for rendering node link content:
import {
Overlay,
Panel,
Footnotes,
FootnoteRef,
InlineExpand
} from '@weave-md/stream/react'
OverlayFloating modal panel for content.
const triggerRef = useRef<HTMLSpanElement>(null)
<Overlay open={isOpen} onClose={() => setOpen(false)} triggerRef={triggerRef}>
<Markdown>{section.content}</Markdown>
</Overlay>
PanelSlide-in side panel (like VS Code peek).
<Panel open={isOpen} onClose={() => setOpen(false)} title="Details" position="right">
<Markdown>{section.content}</Markdown>
</Panel>
Footnotes & FootnoteRefCollected footnotes at bottom with inline references.
// Inline reference
<FootnoteRef id="intro" number={1} text="see details" onClick={() => scrollTo('#fn-intro')} />
// Footnotes section
<Footnotes footnotes={[
{ id: 'intro', number: 1, title: 'Introduction', content: <Markdown>...</Markdown> }
]} />
InlineExpandExpandable inline content.
<InlineExpand
expanded={isExpanded}
onToggle={() => setExpanded(!isExpanded)}
trigger="See more"
>
<Markdown>{section.content}</Markdown>
</InlineExpand>
These are minimal implementations for chat interfaces.
In chat contexts, node links can't navigate to a separate page. The components:
data-target and data-display attributes for custom handlingresolveSection callback for apps that can show previewsStreaming-safe rendering: During streaming, node links may reference sections that don't exist yet. The component renders gracefully in both states:
This prevents broken UI during streaming while enabling full interactivity once content is complete.
The host app controls what happens on click (show overlay, scroll to section, etc.).
For simple use cases, import the static CSS:
import '@weave-md/stream/styles.css'
For full control, use the theming API similar to Mermaid's approach:
import { getStyles, injectStyles } from '@weave-md/stream/react'
// Inject styles into document head
injectStyles({
theme: 'default', // 'default' | 'dark' | 'none'
themeVariables: {
linkColor: '#ff6600',
linkHoverBg: 'rgba(255, 102, 0, 0.1)',
},
themeCSS: '.weave-link { font-weight: 500; }'
})
Or get the CSS string to inject yourself:
const css = getStyles({
themeVariables: { linkColor: '#ff6600' }
})
// Insert into <style> tag, Shadow DOM, etc.
interface ThemeVariables {
linkColor?: string // Link text color
linkHoverColor?: string // Link text color on hover
linkHoverBg?: string // Background color on hover
linkFocusColor?: string // Focus outline color
linkActiveBg?: string // Background when active/pressed
pendingOpacity?: string // Opacity for unresolved links
fontFamily?: string // Font family for link text
}
| Theme | Description |
|---|---|
default | Blue links, light backgrounds |
dark | Light blue links, dark-friendly backgrounds |
none | No base styles, only CSS variables |
Append raw CSS after generated styles:
injectStyles({
themeCSS: `
.weave-link[data-display="overlay"]::after {
content: " 🔗";
}
`
})
import { getScopedStyles } from '@weave-md/stream/react'
const css = getScopedStyles('.my-chat-container', {
themeVariables: { linkColor: '#ff6600' }
})
// Generates: .my-chat-container .weave-link { ... }
The components use the class weave-link with data attributes:
data-target — The target section IDdata-display — The display mode (if specified)data-resolved — Whether the target section exists ("true" or "false")If you prefer writing your own CSS:
.weave-link {
color: var(--weave-link-color, #0066cc);
text-decoration: underline;
text-decoration-style: dotted;
}
.weave-link[data-resolved="true"] {
cursor: pointer;
}
.weave-link[data-resolved="true"]:hover {
background-color: var(--weave-link-hover-bg, rgba(0, 102, 204, 0.1));
}
.weave-link[data-resolved="false"] {
cursor: default;
opacity: var(--weave-link-pending-opacity, 0.7);
}
Important: This package adapts Weave content for constrained environments—it should NOT influence how LLMs write Weave.
LLMs should write full Weave format using any display type that makes semantic sense:
[see details](node:details?display=sidenote) <!-- Use sidenote if it fits the content -->
The host app then adapts via displayConfig:
Include this (or similar) when prompting LLMs to generate node links:
When writing node links, use any display type that fits the content semantically:
- inline, overlay, sidenote, margin, panel, footnote, stretch
- The host environment will adapt display types as needed for its viewport
- Do not self-limit to what you think the chat interface supports
This ensures LLMs write semantically appropriate node links while hosts handle runtime adaptation.
Note: For guidance on the full Weave format (
:math[...], media blocks, etc.), see the main Weave documentation. This package only handles node links.
This package only handles node links. Other Weave format features are separate:
| Feature | Solution |
|---|---|
:math[...] | Use remark-math-inline |
:sub[A]{B} | Use remark-substitute |
```math blocks | Handle in your code component |
| Media blocks | Handle in your code component |
| GFM | Use remark-gfm |
Recommended: Add remark-math-inline and remark-substitute to your markdown pipeline so LLMs can stream full Weave syntax without the parser getting confused by unfamiliar constructs.
MIT
FAQs
Lightweight utilities for parsing and rendering streamed Weave content
We found that @weave-md/stream demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Replit is integrating Socket Firewall into its AI-powered development experience to help protect builders from malicious open source packages.

Security News
npm confirmed a tooling bug incorrectly marked several one-character packages as security holders and said it was working on a rollback.

Research
/Security News
Newer packages in this compromise use native extensions and .pth loaders to execute JavaScript stealers in developer environments.