npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@inkin/core

v0.6.0

Published

Editable React diagrams from a typed schema. v0.6.0 adds a bidirectional Mermaid bridge at the framework-agnostic @inkin/core/mermaid subpath: `fromMermaid(text)` parses Mermaid flowchart / graph / stateDiagram source into a validated Diagram (best-effort

Readme

inkin

Editable React diagrams from a typed schema. Published as the @inkin/core npm package.

You are here: @inkin/[email protected] — the Mermaid bridge release. A new framework-agnostic subpath, @inkin/core/mermaid, converts between Mermaid flowchart / graph / stateDiagram source and inkin's Diagram in both directions — fromMermaid(text) and toMermaid(diagram). Paste existing Mermaid markdown, edit it visually with the editor chrome, export it back. Best-effort import (out-of-subset features degrade with a console.warn, not a failure). Consumers who don't import the bridge pay zero bytes. Schema, renderer, flow animation, and <DiagramStudio> props are all unchanged. See the Mermaid bridge section and the release roadmap below.

What this gives you (today, at 0.6.0)

  • A drop-in React component, <DiagramStudio>, that renders a typed Diagram with xyflow-powered pan/zoom, optional minimap and controls, custom node/edge/cluster shapes, dark + light themes, and SVG export via a ref handle
  • In-place editing when you supply onChange — drag to move, drag handles to connect, Delete or Backspace to remove with cascade, double-click any label to edit it inline. Arrow-key nudges and Esc-cancel come for free. Same component, two visible UIs.
  • Editor chrome auto-mounts in editable mode (since 0.4.0) — a contextual Inspector (label / sublabel / shape / cluster) and a Palette toolbar (Add Node / Add Cluster). Opt out per-panel via inspector="off" / palette="off". Drag a node into a cluster to reassign it.
  • Animated data-flow tokens (since 0.5.0) — populate diagram.flows with ordered edge ids and one <circle> per flow loops along the composed path. Per-flow duration / delay / color. Honors prefers-reduced-motion: reduce. See the Flow animation section.
  • Bidirectional Mermaid bridge (new in 0.6.0) — fromMermaid / toMermaid at @inkin/core/mermaid parse and emit Mermaid flowchart / stateDiagram source. Best-effort import; framework-agnostic; zero bytes if unimported. See the Mermaid bridge section.
  • A zod 4 schema for graph-shaped diagrams (nodes, edges, optional clusters and animated flows) — still at @inkin/core/schema, framework-agnostic
  • A parse() validator with field-path-precise errors that AI agents and humans can self-correct from
  • Auto-layout powered by @dagrejs/dagre (the maintained dagre fork) behind a pluggable LayoutEngine interface
  • A JSON Schema Draft 2020-12 export ready to drop into OpenAI / Anthropic / Gemini tool-use APIs
  • The schema subpath has zero React, zero DOM, zero CSS — safe in Node, edge runtimes, Bun, Deno, and the browser

Why a typed JSON schema instead of Mermaid / PlantUML / D2 syntax? Text-based diagram DSLs force AI agents to emit valid grammar — one misplaced bracket and the whole diagram fails silently. inkin's zod-validated JSON shape returns token-isolated field-path errors (diagram.nodes[3].id — Required) that agents can self-correct in a single round, instead of regenerating from scratch. Same advantage applies to human authoring: an autocomplete IDE catches mistakes at the keystroke level, not after rendering.

Install

pnpm add @inkin/core
# or: npm install @inkin/core / yarn add @inkin/core

react and react-dom (>=18) are peer dependencies — your app already has them. Everything else (xyflow, zustand, dagre, zod, html-to-image) is pulled in transitively.

React quickstart

Three lines of code, one CSS import, one component:

import { DiagramStudio } from '@inkin/core'
import '@inkin/core/styles.css'

export function Demo() {
  return (
    <div style={{ width: '100%', height: 600 }}>
      <DiagramStudio
        value={{
          schemaVersion: 1,
          nodes: [
            { id: 'a', label: 'Start' },
            { id: 'b', label: 'Middle' },
            { id: 'c', label: 'End', shape: 'terminal' },
          ],
          edges: [
            { from: 'a', to: 'b', label: 'go' },
            { from: 'b', to: 'c', label: 'finish' },
          ],
        }}
      />
    </div>
  )
}

That's a complete read-only diagram with pan, zoom, and viewport controls. The wrapper must have explicit width and height — xyflow measures the parent on first mount; if either dimension reads 0 before CSS applies (common in iframes / sandboxes / flex parents without min-width: 0), it logs a harmless one-shot [React Flow] error#004 warning. Setting both upfront avoids the warning. Typical sizes: height: 600 for an embed; width: '100vw', height: '100vh' for a full-page canvas.

Props

| Prop | Type | Default | Notes | |---|---|---|---| | value | DiagramInput | required | The diagram to render — the unparsed object literal you'd hand to parse(). Defaulted fields (Node.shape, Edge.style) can be omitted. Validated on every reference change; failures render an inline error panel instead of a blank canvas. | | onChange | (next: Diagram) => void | — | Editing toggle. Supply to enable drag / connect / delete / inline edit. Omit for the read-only experience. The callback receives the parsed-and-validated next Diagram; consumers update their state inside it. | | theme | 'dark' \| 'light' | 'dark' | Reflected as data-inkin-theme on the wrapper; all theme tokens are scoped to that attribute. | | layout | 'auto' \| 'manual' | 'auto' | 'auto' runs dagre on any node without a position. 'manual' trusts the diagram as-is. | | minimap | boolean | false | Show xyflow's minimap overlay. | | controls | boolean | true | Show xyflow's viewport controls (zoom in/out, fit-view). | | inspector | 'right' \| 'left' \| 'off' | 'right' editable / 'off' read-only | Contextual editor panel position. New in 0.4.0. Renders fields per selection: label / sublabel / shape / cluster (nodes), label / style (edges), label (clusters). Multi-select shows shared values or a multiple values placeholder. | | palette | 'left' \| 'top' \| 'off' | 'left' editable / 'off' read-only | Creation toolbar position. New in 0.4.0. Two tools: Add Node (click on canvas to place), Add Cluster. Esc cancels an armed tool. | | className | string | — | Appended to the wrapper element. | | ref | Ref<DiagramStudioRef> | — | Imperative handle exposing toSvg(options?). |

SVG export

import { useRef } from 'react'
import { DiagramStudio, type DiagramStudioRef } from '@inkin/core'

function ExportButton() {
  const ref = useRef<DiagramStudioRef>(null)

  return (
    <>
      <DiagramStudio ref={ref} value={diagram} />
      <button onClick={async () => {
        const svg = await ref.current?.toSvg()
        // download / display / copy to clipboard
      }}>
        Download SVG
      </button>
    </>
  )
}

Next.js App Router

<DiagramStudio> is client-only (xyflow touches window / document at import time). The component file ships 'use client' so the App Router treats it correctly out of the box. If you import it from a Server Component anyway, dynamic-import without SSR:

import dynamic from 'next/dynamic'

const DiagramStudio = dynamic(
  () => import('@inkin/core').then((m) => m.DiagramStudio),
  { ssr: false },
)

Editable mode — supply onChange

import { type Diagram, type DiagramInput, DiagramStudio } from '@inkin/core'
import '@inkin/core/styles.css'
import { useState } from 'react'

export function EditableDemo() {
  const [diagram, setDiagram] = useState<DiagramInput>({
    schemaVersion: 1,
    nodes: [
      { id: 'a', label: 'Idea',  position: { x: 0,   y: 0 } },
      { id: 'b', label: 'Ship', shape: 'terminal', position: { x: 250, y: 0 } },
    ],
    edges: [{ from: 'a', to: 'b', label: 'go' }],
  })

  return (
    <div style={{ width: '100%', height: 600 }}>
      <DiagramStudio
        value={diagram}
        onChange={(next: Diagram) => setDiagram(next)}
      />
    </div>
  )
}

What turns on with onChange:

  • Drag a node body to move it. Children stay inside their cluster (drag-end fires one onChange, no jitter during the drag).

  • Drag from a node handle to another node to create a labeled edge. Parallel edges get auto-generated explicit ids (a->b#2).

  • Drag a node into a cluster's bounds to reassign it (new in 0.4.0). MoveNode + cluster reassignment microtask-batch into one onChange.

  • Click + Delete / Backspace removes a node along with every incident edge (cascade) and prunes flows that reference removed edges.

  • Double-click a label, sublabel, or edge label to edit it inline. Enter or blur commits, Esc cancels. Empty strings are valid.

  • Tab to focus a node, arrows to nudge it by 10 px, Enter to edit its label, Esc to cancel-edit or clear selection.

  • Inspector + Palette auto-mount (new in 0.4.0). The Inspector reflects the current selection — type into a TextInput and press Enter to commit a label change; pick a dropdown to change shape / style / cluster. The Palette has Add Node + Add Cluster tools — click a tool, then click the canvas to place. Newly-placed entities are auto-selected so the Inspector opens populated for the rename. Opt out per-panel:

    <DiagramStudio value={diagram} onChange={setDiagram} inspector="off" />
  • Clusters are first-class (new in 0.4.0) — click the header to select, drag the header to move (children follow), double-click to rename inline, Delete to remove (children kept, cluster field cleared). Add Node inside a cluster auto-parents the new node.

  • Shift-click to multi-select. Inspector header shows the count + a Clear button; bulk edits flow through one microtask-batched onChange. A visible "Applies to all N selected nodes" banner appears before you type so the bulk effect is never silent.

Editor chrome (Inspector + Palette)

When you supply onChange, two panels mount as flex siblings of the canvas inside the same wrapper element — they reserve their own width so no node or edge label is ever hidden behind them:

┌───────────────────────────────────────────────────────────────┐
│ ┌───────────┐ ┌───────────────────────────┐ ┌───────────────┐ │
│ │  + Node   │ │                           │ │ NODE   Clear  │ │
│ │  + Cluster│ │     [diagram canvas]      │ │ Label         │ │
│ └───────────┘ │                           │ │ Shape   ▼     │ │
│               │                           │ │ Cluster ▼     │ │
│               └───────────────────────────┘ └───────────────┘ │
└───────────────────────────────────────────────────────────────┘

Sub-480 px viewports auto-collapse to a stacked column so neither panel eats the canvas width on mobile. Pass inspector="left" / palette="top" to flip orientation, or "off" to suppress per panel. Explicit non-"off" values in read-only mode log a one-time console.warn.

Cluster affordances (all in editable mode):

  • Click the cluster's header to select it.
  • Drag from the header to move the cluster — children move with it.
  • Double-click the header to rename inline.
  • Delete (or Backspace) removes the cluster; child nodes stay in the diagram with their cluster field cleared.
  • Click "+ Cluster" in the Palette, then click on the canvas — the new cluster materializes at the click point.
  • Click "+ Node" in the Palette, then click inside an existing cluster's bounds — the new node auto-parents into that cluster.

Multi-select: Shift-click extends the selection. The Inspector header shows the count and a Clear button; bulk edits commit to every selected entity via one microtask-batched onChange, with a visible "Applies to all N selected nodes" banner before you type.

Every editing event flows through a pure schema reducer and is re-validated before onChange fires — invalid diagrams can never escape from the editor. Persistence is whatever your onChange does: a useState, a localStorage.setItem, a fetch to your backend — see the examples app for a working playground.

Flow animation

New in 0.5.0. Declare a flows?: Flow[] field on your Diagram and one animated <circle> per flow traces its composed path through the listed edges. Pure CSS offset-path + offset-distance keyframes — no animation library, no JavaScript animation loop. The feature is fully data-driven; no new <DiagramStudio> prop.

import { DiagramStudio } from '@inkin/core'
import '@inkin/core/styles.css'

export function ArchitectureMap() {
  return (
    <div style={{ width: '100%', height: 600 }}>
      <DiagramStudio
        value={{
          schemaVersion: 1,
          clusters: [
            { id: 'edge', label: 'edge' },
            { id: 'app', label: 'application' },
            { id: 'data', label: 'data' },
          ],
          nodes: [
            { id: 'browser', label: 'Browser', cluster: 'edge' },
            { id: 'api', label: 'API Gateway', cluster: 'app' },
            { id: 'web', label: 'Web Service', cluster: 'app' },
            { id: 'worker', label: 'Worker', cluster: 'app' },
            { id: 'db', label: 'Database', cluster: 'data', shape: 'terminal' },
          ],
          edges: [
            { id: 'req-in', from: 'browser', to: 'api', label: 'HTTPS' },
            { id: 'req-svc', from: 'api', to: 'web', style: 'dashed' },
            { id: 'svc-db', from: 'web', to: 'db' },
            { id: 'wkr-db', from: 'worker', to: 'db', label: 'consume queue' },
          ],
          flows: [
            // Sync request path: browser → API → web → db. 6.5 s loop.
            { id: 'request', edges: ['req-in', 'req-svc', 'svc-db'], duration: 6500 },
            // Async queue drain — staggered half a loop so the two
            // tokens are never visually overlaid on the same frame.
            { id: 'queue-drain', edges: ['wkr-db'], duration: 6500, delay: 3250 },
          ],
        }}
      />
    </div>
  )
}

Two blue tokens. The request token traces the three-edge sync path continuously; the queue-drain token starts at the worker's right handle for the first 3.25 seconds (its delay), then begins its loop half a phase out of step with the request token.

| Flow field | Type | Default | Notes | |---|---|---|---| | id | string | required | Schema-unique identifier. | | edges | string[] (length ≥ 1) | required | Ordered edge ids the token traverses. Each id must resolve to an Edge — either the explicit Edge.id field or the auto-derived ${from}->${to} form. Adjacent edges (target of [i] = source of [i+1]) trace as one smooth continuous path; non-adjacent sequences render a straight connector segment between disjoint endpoints. | | duration | number (ms) | 7000 | One full loop time. | | delay | number (ms) | 0 | Offset before the first iteration begins — handy for staggering parallel flows. | | color | string | var(--inkin-accent-primary) | Any CSS color (hex, rgb(), hsl()) or CSS custom property expression (var(...)). Drives both the token's fill AND its drop-shadow glow via currentColor. | | label | string | — | Schema-only in 0.5.0; reserved for a future flow-editor surface that consumes it. |

Cascade behavior — when a user deletes an edge that one of the flows references, the pruneFlows reducer cascade (shipped in 0.3.0) strips the deleted edge id from the affected flow's edges array before onChange fires. A flow whose edges array becomes empty is removed entirely. 0.5.0 is the first release where consumers can see this happen live: the token shortens its path, then disappears when the last referenced edge goes away.

Theming — the token radius is themeable via the --inkin-flow-token-radius CSS custom property (default 6px, declared in both dark.css and light.css). Override the same way you'd override any other theme token:

[data-inkin-theme='dark'] {
  --inkin-flow-token-radius: 9px;
}

The CSS r property is supported in Chromium 86+, Firefox 100+, and Safari 16+ — well within the inkin browser matrix.

Reduced motion@media (prefers-reduced-motion: reduce) { .flowToken { animation: none; } } is baked into the consolidated CSS. Users opting in at the OS level see each token as a static dot at the start of its path with color and drop-shadow glow intact — visible and in position, but motionless. No code change needed.

What's not in 0.5.0 — there is no Inspector field for Flow.edges, no Palette "add flow" tool, no patch variants for adding / editing / removing flows. Authoring is declarative-only — mutate value.flows in your own state and pass the new value down. The full flow editor lands in 1.1.0 after the 1.0.0 schema freeze; the master plan reserves the post-stable enhancement queue for this kind of additive feature so the patch surface ships at most once.

Schema-only quickstart (framework-agnostic)

The schema kernel from 0.1.0 is still available at the same subpath, unchanged:

import { parse, layout, type Diagram } from '@inkin/core/schema'

const draft: Diagram = parse({
  schemaVersion: 1,
  nodes: [
    { id: 'pending', label: 'Pending' },
    { id: 'running', label: 'Running' },
    { id: 'complete', label: 'Complete', shape: 'terminal' },
  ],
  edges: [
    { from: 'pending', to: 'running', label: 'dequeue' },
    { from: 'running', to: 'complete', label: 'success' },
  ],
})

const positioned = layout(draft)
// → every node now has a `position: { x, y }` from dagre

@inkin/core (the React surface) also re-exports the common schema essentials (Diagram, parse, safeParse, InkinValidationError, ValidationIssue) for convenience — you only need the subpath when you want the layout engine, JSON Schema, or individual zod schemas.

AI tool-use

import { diagramJsonSchema, parse } from '@inkin/core/schema'

// OpenAI / Anthropic / Gemini function-calling — drop in directly
const tool = {
  name: 'create_diagram',
  description: 'Produce an inkin Diagram object.',
  input_schema: diagramJsonSchema,
}

// after the model responds, validate before doing anything else
const diagram = parse(model.output)
// → throws InkinValidationError with field-path issues; agent can self-correct in one round

For tools that prefer fetching the schema as a static file rather than importing it, the same content ships as @inkin/core/diagram.schema.json:

import schema from '@inkin/core/diagram.schema.json' with { type: 'json' }
// or fetch it from your CDN of choice: unpkg, jsdelivr, etc.

Mermaid bridge

New in 0.6.0. Convert between Mermaid flowchart / graph / stateDiagram source and inkin's Diagram in both directions — paste existing Mermaid markdown, edit it visually with the editor chrome, export it back. Two pure functions at the framework-agnostic @inkin/core/mermaid subpath (no React / xyflow / DOM pulled in; consumers who don't import it pay zero bytes):

import { fromMermaid, toMermaid } from '@inkin/core/mermaid'
import { DiagramStudio } from '@inkin/core'

// --- import: Mermaid text → inkin Diagram ---
const result = fromMermaid(`flowchart LR
  browser[Browser] --> api[API Gateway]
  api -.-> web[Web Service]
  web --> db[(Database)]`)

if (result.ok) {
  // result.diagram is a validated inkin Diagram — render it editable
  return <DiagramStudio value={result.diagram} onChange={setDiagram} />
}
// else: result.issues lists field-path-precise syntax errors

// --- export: inkin Diagram → Mermaid text ---
const text = toMermaid(diagram)              // 'flowchart TB\n…'
const lr = toMermaid(diagram, { direction: 'LR' })

fromMermaid is a best-effort import. Well-formed Mermaid that uses a feature outside inkin's supported subset (styling, click handlers, notes, exotic shapes, nested clusters) is dropped or degraded with a one-time console.warn rather than failing — only malformed input returns { ok: false, issues }. So a real-world diagram pasted from elsewhere renders something usable, with clear diagnostics about what didn't translate.

Supported Mermaid syntax

Flowchart / graphflowchart and graph headers, all directions (TB / BT / LR / RL / TD):

| Mermaid | inkin | Notes | |---|---|---| | A[label] | rect node | faithful | | A((label)) / A(((label))) | terminal node | faithful (circle / double-circle) | | A(label) / A{label} / A{{label}} / A[(label)] / A[[label]] / A([label]) / A>label] | rect / terminal (nearest) | degraded + warn — inkin has two shapes, not Mermaid's dozen | | A --> B | solid edge | faithful | | A -.-> B | dashed edge | faithful (dotted → dashed) | | A ==> B / A ~~~ B | solid edge | degraded + warn (no thick / invisible style) | | A --- B (no arrowhead) | solid edge | degraded + warn (inkin always renders arrows) | | A -->\|label\| B, A -- label --> B | edge with label | faithful | | subgraph X[label] … end | cluster | faithful (nested subgraphs flattened + warn) |

State diagramstateDiagram / stateDiagram-v2:

| Mermaid | inkin | Notes | |---|---|---| | [*] | terminal sentinel node | start (__start__) / end (__end__) by position | | state X / state "Name" as X | rect node | faithful | | X : description | node label | faithful | | A --> B : event | edge with label | faithful | | state X { … } | cluster | compound state → cluster (nested flattened + warn) | | state X <<choice>> / <<fork>> / <<join>> | rect node | degraded + warn (no pseudostate shape) |

Not imported (dropped with a warn): classDef / class / style / linkStyle styling, click / href interactivity, note left of / note right of, per-subgraph direction, the -- concurrency divider, accessibility annotations. Trapezoid / parallelogram / ellipse shapes degrade to rect.

Round-trip is semantic, not byte-identical: fromMermaid(toMermaid(d)) re-parses to a Diagram equal to d (whitespace, declaration order, and the flowchart-vs-stateDiagram header may differ — inkin's Diagram is a flat graph, so toMermaid always emits a flowchart). Animated flows have no Mermaid equivalent and are dropped on export with a warn.

Source & attribution: the inkin parser uses the Mermaid grammar (flowchart + stateDiagram) as its syntactic spec, and the round-trip test corpus is adapted from Mermaid's own parser fixtures. Mermaid is MIT-licensed (© 2015 Knut Sveidqvist and Mermaid contributors). The parser, converter, and emitter implementations are original to inkin.

Mermaid + AI

Some models emit Mermaid more reliably than raw JSON. Compose the bridge with safeParse for a one-import round-trip:

import { fromMermaid } from '@inkin/core/mermaid'

const result = fromMermaid(modelOutput) // model returned Mermaid text
if (result.ok) {
  render(result.diagram)
} else {
  // feed result.issues back to the model for a single-round correction
}

Validation errors

parse() throws InkinValidationError with a multi-line message and a structured issues[] array:

inkin: invalid Diagram
  - diagram.nodes[3].id — Required
  - diagram.edges[1].from — edge.from references unknown node id "noep"

The issues[] array is the agent-friendly version: each entry has path (e.g. "diagram.nodes[3].id") and message. Use safeParse() if you'd rather not throw.

When <DiagramStudio> receives an invalid value, it renders the same field-path errors inline (as a role="alert" panel) instead of a blank canvas — DX commitment from the plan: "Error-on-mistake, not silent-render."

Schema shape

| Field | Type | Notes | |---|---|---| | schemaVersion | 1 (literal) | tied to package MAJOR; 2 would ship with [email protected] | | nodes | Node[] | { id, label, sublabel?, position?, cluster?, shape } | | edges | Edge[] | { id?, from, to, label?, style } | | clusters? | Cluster[] | { id, label, parent? } (nested rendering: 1.2.0) | | flows? | Flow[] | { id, label?, edges, duration, delay, color? } — animated tokens since 0.5.0; see Flow animation |

shape is 'rect' \| 'terminal'; style is 'solid' \| 'dashed'. Both have sensible defaults.

Custom layout

import { layout, createDagreLayout, type LayoutEngine } from '@inkin/core/schema'

const myLayout = createDagreLayout({ direction: 'TB', nodesep: 30, ranksep: 60 })
const positioned = layout(draft, myLayout)

// or roll your own engine for elkjs, hand-positioning, etc.
const noop: LayoutEngine = { layout: (d) => d }

Theming

@inkin/core/styles.css defines every visible color, border, radius, and font as a --inkin-* CSS custom property scoped to [data-inkin-theme="dark"|"light"]. Override any of them in your own CSS to rebrand without forking:

[data-inkin-theme='dark'] {
  --inkin-accent-primary: #ff00aa;
  --inkin-bg-node: #1a0d22;
}

The theme attribute lives on the <DiagramStudio> wrapper, so two instances on the same page can use different themes side by side.

Release roadmap

| Version | Headline | What gets added to @inkin/core | Status | |---|---|---|---| | 0.1.0 | Schema kernel (AI-ready) | @inkin/core/schema subpath | ✅ shipped | | 0.2.0 | Read-only <DiagramStudio> React renderer | bare @inkin/core root entry (React surface), @inkin/core/styles.css | ✅ shipped | | 0.3.0 | Core editing (drag, connect, delete, inline label) | onChange prop; DiagramInput type; editing layer | ✅ shipped | | 0.4.0 | Editor chrome (InspectorPanel, Palette, ui primitives) | root entry grows | ✅ shipped | | 0.5.0 | Flow animation (CSS offset-path tokens) | <FlowLayer> overlay, --inkin-flow-token-radius theme token | ✅ shipped | | 0.6.0 | Mermaid bidirectional bridge — you are here | @inkin/core/mermaid subpath (fromMermaid / toMermaid) | ✅ shipped | | 1.0.0 | Stable — schema and root API frozen, semver guarantee begins | polish only | planned |

Post-stable: undo/redo, copy/paste, PNG export, flow-editor UI (Inspector / Palette surface for adding, editing, and removing flows), a built-in Mermaid import/export affordance on <DiagramStudio> (+ a hosted standalone playground for non-coding end users), layout="elk", custom node/edge type registry.

Security

To report a security vulnerability, please use the private vulnerability reporting feature on the GitHub repository. Do not open a public issue for security-sensitive reports.

License

MIT