Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c68e8eb
feat: integrate vite devtools
arashsheyda May 29, 2026
8cc0777
chore: lint
arashsheyda May 31, 2026
f12b101
fix(react): enhance Comark for ssr support
arashsheyda May 31, 2026
b72b265
fix: update docs
arashsheyda May 31, 2026
ae9e572
fix: test
arashsheyda May 31, 2026
076e4ab
fix: update docs and remove push to app
arashsheyda May 31, 2026
21b81e4
fix: build
arashsheyda Jun 2, 2026
f3f5471
feat: enhance devtools UI and functionality
arashsheyda Jun 4, 2026
f06d6d2
chore: rename file
arashsheyda Jun 4, 2026
e7f3718
chore: lint
arashsheyda Jun 4, 2026
a780b7d
fix: build
arashsheyda Jun 4, 2026
d1cbcbe
Merge branch 'main' into feat/vite-devtools-renderer
farnabaz Jun 12, 2026
bf43d02
update types
farnabaz Jun 12, 2026
be67885
feat: allow editing directly from devtools
farnabaz Jun 12, 2026
e1f2b7e
feat(devtools): enhance markdown parsing and highlighting
arashsheyda Jun 13, 2026
7201cfa
chore: lint
arashsheyda Jun 13, 2026
0108644
fix: update docs
arashsheyda Jun 13, 2026
180a91a
Merge branch 'main' into feat/vite-devtools-renderer
atinux Jun 18, 2026
70979f8
Merge remote-tracking branch 'origin/main' into feat/vite-devtools-re…
arashsheyda Jun 20, 2026
2f2fb02
feat: support devtools for agnular
arashsheyda Jun 20, 2026
8428c6a
fix: address pr suggestion
arashsheyda Jun 20, 2026
ea76f8a
fix: update devtools style
arashsheyda Jun 20, 2026
ae6322a
fix: build style
arashsheyda Jun 20, 2026
1d3c6c5
chore: lint
arashsheyda Jun 20, 2026
a658cf8
chore: lint
arashsheyda Jun 20, 2026
f288d64
fix: update bundle test
arashsheyda Jun 22, 2026
7f00c23
fix: address pr suggestion 2
arashsheyda Jun 22, 2026
c8bb31e
Merge remote-tracking branch 'origin/main' into feat/vite-devtools-re…
arashsheyda Jun 22, 2026
d9905de
chore: update bundle test
arashsheyda Jun 22, 2026
0c3d24c
Merge branch 'main' into feat/vite-devtools-renderer
farnabaz Jul 28, 2026
f279c89
fix: test
farnabaz Jul 28, 2026
4bb4b88
fix: use comark context
farnabaz Jul 29, 2026
e385ea9
fix: reduce delay
farnabaz Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ logs

# Comark
packages/comark/skills
packages/comark/src/devtools/renderer/styles.ts
.vercel
.env*
28 changes: 26 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ packages/comark/
β”‚ β”‚ β”œβ”€β”€ index.ts # Re-exports (comark/ast entry point)
β”‚ β”‚ β”œβ”€β”€ types.ts # ComarkTree, ComarkNode, ComarkElement, ComarkText
β”‚ β”‚ └── utils.ts # textContent(), visit() tree utilities
β”‚ β”œβ”€β”€ devtools/ # Vite DevTools integration
β”‚ β”‚ β”œβ”€β”€ index.ts # Public exports (comark/devtools entry point)
β”‚ β”‚ β”œβ”€β”€ bridge.ts # Context ↔ HMR bridge for the Vite DevTools panel
β”‚ β”‚ β”œβ”€β”€ client.ts # Injected client entry (connectDevtools)
β”‚ β”‚ β”œβ”€β”€ vite.ts # Vite plugin with RPC endpoints (comark/devtools/vite entry point)
β”‚ β”‚ β”œβ”€β”€ renderer.ts # Re-export barrel (comark/devtools/renderer entry point)
β”‚ β”‚ β”œβ”€β”€ types.ts # Shared TypeScript interfaces for RPC and panel
β”‚ β”‚ β”œβ”€β”€ constants.ts # Icon assets and Shiki theme/lang config
β”‚ β”‚ └── renderer/ # DevTools panel UI (internal)
β”‚ β”‚ β”œβ”€β”€ index.ts # Entry point, exports DevtoolsPanel
β”‚ β”‚ β”œβ”€β”€ panel.ts # Panel class: tabs, editor, polling, RPC orchestration
β”‚ β”‚ β”œβ”€β”€ dom.ts # DOM element factories (editor, tab bar, states)
β”‚ β”‚ β”œβ”€β”€ output.ts # AST rendering, regex fallback highlighter
β”‚ β”‚ β”œβ”€β”€ styles.ts # CSS styles (imports styles.css)
β”‚ β”‚ β”œβ”€β”€ styles.css # Panel stylesheet
β”‚ β”‚ └── theme.ts # Light/dark/auto theme toggle
β”‚ β”œβ”€β”€ plugins/ # Built-in and optional plugins
β”‚ β”‚ β”œβ”€β”€ alert.ts # Alert/callout blocks
β”‚ β”‚ β”œβ”€β”€ emoji.ts # Emoji shortcodes
Expand All @@ -76,9 +92,10 @@ packages/comark/

| Peer | Required by |
|------|-------------|
| `shiki` | `comark/plugins/highlight` |
| `shiki` | `comark/plugins/highlight`, `comark/devtools/vite` (devtools Shiki highlighting) |
| `katex` | `comark/plugins/math` |
| `beautiful-mermaid` | `comark/plugins/mermaid` |
| `@vitejs/devtools-kit` | `comark/devtools/vite` (Vite DevTools integration) |

All are optional β€” only install what you use.

Expand Down Expand Up @@ -380,6 +397,13 @@ import emoji from 'comark/plugins/emoji'
import toc from 'comark/plugins/toc'
import alert from 'comark/plugins/alert'

// Vite DevTools integration
import { comarkDevtools } from 'comark/devtools/vite'
// Ambient live-document context (used by renderers via comarkKey)
import { createComarkContext } from 'comark'
// Optional: manual bridge connect (normally injected by comarkDevtools())
import { connectDevtools } from 'comark/devtools'

// NOTE: All framework packages re-export every core plugin via their own subpath.
// Prefer the framework-specific path when using a framework renderer:
// @comark/vue/plugins/highlight, @comark/react/plugins/highlight, etc.
Expand All @@ -402,7 +426,7 @@ import math, { Math } from '@comark/vue/plugins/math'
import mermaid, { Mermaid } from '@comark/vue/plugins/mermaid'

// React β€” renderer + plugin wrappers (plugin fn + React component)
import { Comark, ComarkRenderer, defineComarkComponent } from '@comark/react'
import { Comark, ComarkServer, ComarkClient, ComarkRenderer, defineComarkComponent } from '@comark/react'
import math, { Math } from '@comark/react/plugins/math'
import mermaid, { Mermaid } from '@comark/react/plugins/mermaid'

Expand Down
29 changes: 26 additions & 3 deletions docs/content/3.rendering/5.react.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ bun add @comark/react

## `<Comark>`

The `<Comark>` component is the simplest way to render markdown in React. It handles parsing and rendering automatically.
The `<Comark>` component is the simplest way to render markdown in React. It handles parsing and rendering automatically, and works in both client-side (Vite, CRA) and server-side (Next.js) contexts.

::warning{to="#code-comarkrenderer"}
`<Comark>` is an **async** component. You can also use the `<ComarkRenderer>` component to handle parsing yourself.
::info
For React Server Components (Next.js `app/` directory), you can also use [`<ComarkServer>`](#code-comarkserver) to parse markdown on the server with `await`.
::

```tsx [App.tsx]
Expand Down Expand Up @@ -371,6 +371,29 @@ export default async function Page({ params }: { params: { slug: string } }) {

---

## `<ComarkServer>`

Async server-only component that parses markdown on the server using `await`. Only works in React Server Components (Next.js `app/` directory). For client-side usage, use [`<Comark>`](#code-comark).

Accepts the same props as `<Comark>`. When `streaming` is true, it delegates to the client-side `<ComarkClient>`.

```tsx [app/docs/[slug]/page.tsx]
import { ComarkServer } from '@comark/react'
import Alert from '@/components/Alert'

export default async function Page({ params }: { params: { slug: string } }) {
const content = await getDocContent(params.slug)

return <ComarkServer components={{ alert: Alert }}>{content}</ComarkServer>
}
```

::warning
`<ComarkServer>` is an `async` function component and will throw in client-only environments (Vite, CRA). Use `<Comark>` instead for universal usage.
::

---

## `<ComarkRenderer>`

Renders a pre-parsed `ComarkTree` without any parsing. Use it when you parse on the server, in a build step, or via an API, so no parser or plugin code is shipped to the browser.
Expand Down
130 changes: 130 additions & 0 deletions docs/content/7.kb/5.devtools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
title: Vite DevTools
description: Interactive Comark playground panel inside Vite DevTools for real-time markdown editing and AST inspection.
seo:
title: Vite DevTools Integration
navigation:
icon: i-lucide-wrench
links:
- label: Vite DevTools
icon: i-lucide-external-link
to: https://devtools.vite.dev/
color: neutral
variant: soft
---

Comark integrates with [Vite DevTools](https://devtools.vite.dev/) to provide an interactive playground panel for real-time markdown editing and AST inspection. This is a development tool that uses the Vite dev server's HMR and RPC capabilities.

## Setup

Install the `@vitejs/devtools` peer dependency alongside `comark`:

::code-group

```bash [pnpm]
pnpm add -D @vitejs/devtools
```

```bash [npm]
npm install -D @vitejs/devtools
```

```bash [yarn]
yarn add -D @vitejs/devtools
```

::

Add both plugins to your Vite config:

```ts [vite.config.ts]
import { defineConfig } from 'vite'
import { DevTools } from '@vitejs/devtools'
import { comarkDevtools } from 'comark/devtools/vite'

export default defineConfig({
plugins: [
DevTools(),
comarkDevtools(),
],
})
```

> [!TIP]
> If you use `@comark/vue`, its Vite plugin already includes `comarkDevtools()` automatically - no extra config needed:
> ```ts [vite.config.ts]
> import { defineConfig } from 'vite'
> import vue from '@vitejs/plugin-vue'
> import { DevTools } from '@vitejs/devtools'
> import comark from '@comark/vue/vite'
>
> export default defineConfig({
> plugins: [vue(), DevTools(), comark()],
> })
> ```

## Features

### Markdown Editor

The panel includes a markdown editor with [Shiki](https://shiki.style/)-powered syntax highlighting using the `mdc` grammar. A transparent `<textarea>` overlays a syntax-highlighted `<pre>` element, providing native editing (cursor, selection, undo/redo) with rich colors.

Highlighting updates are near-instant (16ms debounce) via a dedicated Shiki RPC call, while instance updates are debounced at 300ms to avoid excessive parsing. If Shiki is unavailable, a regex-based fallback highlighter provides basic coloring.

### AST Tab

Displays the full parsed AST as syntax-highlighted JSON. Useful for understanding how Comark transforms your markdown and inspecting node structure.

### Push to App

When a live `<Comark>` instance is connected, edits in the devtools editor are pushed to the running application in real time. The Vite plugin parses the markdown on the server and broadcasts the updated tree to the browser via HMR, where the framework renderer (Vue, React, Svelte, or Angular) re-renders the component with the new content.

Parse errors during editing (e.g. malformed frontmatter YAML) are caught gracefully - the previous tree is preserved so the editor never breaks mid-edit.

### Live Instance Connection

When a Comark component (`<Comark>` from `@comark/vue`, `@comark/react`, `@comark/svelte`, or `@comark/angular`) is mounted on the page, the DevTools panel automatically detects it via polling.

A green dot in the instance bar indicates a connected instance. The editor loads the instance's current markdown source so you can inspect and experiment with it.

## How It Works

The devtools integration has three layers:

| Layer | Module | Role |
|---|---|---|
| **Context bridge** | `comark/devtools` (`connectDevtools`) | Browser-side bridge that mirrors `globalThis.comarkContext` over HMR |
| **Vite Plugin** | `comark/devtools/vite` | Dev server plugin that injects the client bridge and exposes RPC endpoints |
| **Panel** | `comark/devtools/renderer` | Internal β€” UI panel rendered inside the Vite DevTools iframe (not intended for end users) |

### Live documents (no per-renderer registration)

Framework renderers never talk to devtools directly. They only subscribe to the ambient context via `comarkKey` / `meta.key`:

```ts
import { createComarkContext, parse } from 'comark'

// Driver (app, collab, agent, or the Vite DevTools bridge):
const ctx = createComarkContext() // installs globalThis.comarkContext
const doc = ctx.get('page', await parse('# Hello'))

// In the UI:
// <ComarkRenderer :tree="tree" comark-key="page" />
// Panel edits call doc.set(newTree); the mounted renderer re-renders.
```

When `comarkDevtools()` is in your Vite config, it injects a client entry that connects `import.meta.hot` to the ambient context β€” listing keys, reverse-rendering markdown for the panel, and applying panel edits via `doc.set`.

### RPC Endpoints

The Vite plugin registers three RPC endpoints:

| Endpoint | Type | Description |
|---|---|---|
| `comark:highlight` | query | Syntax-highlight markdown via Shiki with the `mdc` grammar (dual light/dark themes) |
| `comark:list-instances` | query | List all registered Comark instances on the page |
| `comark:update-instance` | mutation | Parse markdown and broadcast the updated tree to the live instance via HMR |

## Theme Support

The panel includes a theme toggle (light/dark/auto) in the top-right corner. Auto mode follows the system preference via `prefers-color-scheme`.
2 changes: 1 addition & 1 deletion examples/1.frameworks/nextjs/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import './.next/dev/types/routes.d.ts'
import './.next/types/routes.d.ts'

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
1 change: 1 addition & 0 deletions examples/2.vite/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@angular/compiler": "catalog:",
"@angular/compiler-cli": "catalog:",
"@tailwindcss/vite": "catalog:",
"@vitejs/devtools": "catalog:",
"tailwindcss": "catalog:",
"typescript": "catalog:",
"vite": "catalog:"
Expand Down
4 changes: 4 additions & 0 deletions examples/2.vite/angular/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { DevTools } from '@vitejs/devtools'
import { defineConfig } from 'vite'
import angular from '@analogjs/vite-plugin-angular'
import tailwindcss from '@tailwindcss/vite'
import { resolve } from 'node:path'
import { comarkDevtools } from 'comark/devtools/vite'
import type { Plugin } from 'vite'

/**
Expand Down Expand Up @@ -56,6 +58,7 @@ function escapeRegExp(s: string): string {

export default defineConfig({
plugins: [
DevTools(),
pnpmWorkspaceCompat(),
angular({
tsconfig: './tsconfig.json',
Expand All @@ -66,5 +69,6 @@ export default defineConfig({
transformFilter: (_code, id) => !id.includes('/define.ts'),
}),
tailwindcss(),
comarkDevtools(),
],
})
2 changes: 2 additions & 0 deletions examples/2.vite/html/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"shiki": "catalog:"
},
"devDependencies": {
"@vitejs/devtools": "catalog:",
"comark": "catalog:",
"typescript": "catalog:",
"vite": "catalog:"
}
Expand Down
6 changes: 5 additions & 1 deletion examples/2.vite/html/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { DevTools } from '@vitejs/devtools'
import { defineConfig } from 'vite'
import { comarkDevtools } from 'comark/devtools/vite'

export default defineConfig({})
export default defineConfig({
plugins: [DevTools(), comarkDevtools()],
})
2 changes: 2 additions & 0 deletions examples/2.vite/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
"devDependencies": {
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@vitejs/devtools": "catalog:",
"@vitejs/plugin-react": "catalog:",
"autoprefixer": "catalog:",
"postcss": "catalog:",
"tailwindcss": "catalog:",
"typescript": "catalog:",
"vite": "catalog:"
}
Expand Down
4 changes: 3 additions & 1 deletion examples/2.vite/react/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { DevTools } from '@vitejs/devtools'
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { comarkDevtools } from 'comark/devtools/vite'

export default defineConfig({
plugins: [react(), tailwindcss()],
plugins: [DevTools(), react(), comarkDevtools(), tailwindcss()],
})
2 changes: 2 additions & 0 deletions examples/2.vite/svelte/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "catalog:",
"@vitejs/devtools": "catalog:",
"autoprefixer": "catalog:",
"comark": "catalog:",
"postcss": "catalog:",
"svelte": "catalog:",
"typescript": "catalog:",
Expand Down
4 changes: 3 additions & 1 deletion examples/2.vite/svelte/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { DevTools } from '@vitejs/devtools'
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import tailwindcss from '@tailwindcss/vite'
import { comarkDevtools } from 'comark/devtools/vite'

export default defineConfig({
plugins: [svelte(), tailwindcss()],
plugins: [DevTools(), svelte(), comarkDevtools(), tailwindcss()],
})
3 changes: 2 additions & 1 deletion examples/2.vite/vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
"@comark/vue": "latest",
"@comark/vue": "workspace:*",
"@nuxt/ui": "catalog:",
"@shikijs/langs": "catalog:",
"shiki": "catalog:",
Expand All @@ -18,6 +18,7 @@
"vue-router": "catalog:"
},
"devDependencies": {
"@vitejs/devtools": "catalog:",
"@vitejs/plugin-vue": "catalog:",
"autoprefixer": "catalog:",
"postcss": "catalog:",
Expand Down
Loading
Loading