From c68e8eb2157a10e268277b3d7debf569f226036d Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Fri, 29 May 2026 16:38:37 -0600 Subject: [PATCH 01/28] feat: integrate vite devtools --- examples/2.vite/html/package.json | 2 + examples/2.vite/html/vite.config.ts | 6 +- examples/2.vite/react/package.json | 1 + examples/2.vite/react/vite.config.ts | 4 +- examples/2.vite/svelte/package.json | 2 + examples/2.vite/svelte/vite.config.ts | 4 +- examples/2.vite/vue/package.json | 1 + examples/2.vite/vue/vite.config.ts | 2 + packages/comark-react/package.json | 1 + .../src/components/ComarkClient.tsx | 48 +- packages/comark-react/tsconfig.json | 1 + .../src/components/Comark.svelte | 43 +- packages/comark-vue/src/components/Comark.ts | 33 +- .../src/components/ComarkRenderer.ts | 39 + packages/comark-vue/src/vite.ts | 140 +- packages/comark-vue/tsconfig.json | 1 + packages/comark/package.json | 17 +- .../comark/src/devtools/constants/index.ts | 13 + packages/comark/src/devtools/index.ts | 4 + packages/comark/src/devtools/register.ts | 88 ++ packages/comark/src/devtools/registry.ts | 119 ++ .../comark/src/devtools/renderer/index.ts | 296 ++++ .../comark/src/devtools/renderer/output.ts | 117 ++ .../comark/src/devtools/renderer/styles.css | 326 ++++ .../comark/src/devtools/renderer/styles.ts | 3 + .../comark/src/devtools/renderer/theme.ts | 44 + packages/comark/src/devtools/vite.ts | 113 ++ pnpm-lock.yaml | 1348 ++++++++++++++--- pnpm-workspace.yaml | 2 + scripts/sync-devtools-css.mjs | 33 + 30 files changed, 2541 insertions(+), 310 deletions(-) create mode 100644 packages/comark/src/devtools/constants/index.ts create mode 100644 packages/comark/src/devtools/index.ts create mode 100644 packages/comark/src/devtools/register.ts create mode 100644 packages/comark/src/devtools/registry.ts create mode 100644 packages/comark/src/devtools/renderer/index.ts create mode 100644 packages/comark/src/devtools/renderer/output.ts create mode 100644 packages/comark/src/devtools/renderer/styles.css create mode 100644 packages/comark/src/devtools/renderer/styles.ts create mode 100644 packages/comark/src/devtools/renderer/theme.ts create mode 100644 packages/comark/src/devtools/vite.ts create mode 100644 scripts/sync-devtools-css.mjs diff --git a/examples/2.vite/html/package.json b/examples/2.vite/html/package.json index 10dabc0f..5ab14f66 100644 --- a/examples/2.vite/html/package.json +++ b/examples/2.vite/html/package.json @@ -15,6 +15,8 @@ "shiki": "catalog:" }, "devDependencies": { + "@vitejs/devtools": "catalog:", + "comark": "catalog:", "typescript": "catalog:", "vite": "catalog:" } diff --git a/examples/2.vite/html/vite.config.ts b/examples/2.vite/html/vite.config.ts index 4f1b25a0..1a6dc17d 100644 --- a/examples/2.vite/html/vite.config.ts +++ b/examples/2.vite/html/vite.config.ts @@ -1,3 +1,7 @@ +import { DevTools } from '@vitejs/devtools' import { defineConfig } from 'vite' +import { comarkDevtools } from 'comark/vite' -export default defineConfig({}) +export default defineConfig({ + plugins: [DevTools(), comarkDevtools()], +}) diff --git a/examples/2.vite/react/package.json b/examples/2.vite/react/package.json index a7d9122c..28fe176e 100644 --- a/examples/2.vite/react/package.json +++ b/examples/2.vite/react/package.json @@ -18,6 +18,7 @@ "devDependencies": { "@types/react": "catalog:", "@types/react-dom": "catalog:", + "@vitejs/devtools": "catalog:", "@vitejs/plugin-react": "catalog:", "autoprefixer": "catalog:", "postcss": "catalog:", diff --git a/examples/2.vite/react/vite.config.ts b/examples/2.vite/react/vite.config.ts index 0616e595..222182ff 100644 --- a/examples/2.vite/react/vite.config.ts +++ b/examples/2.vite/react/vite.config.ts @@ -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/vite' export default defineConfig({ - plugins: [react(), tailwindcss()], + plugins: [DevTools(), react(), comarkDevtools(), tailwindcss()], }) diff --git a/examples/2.vite/svelte/package.json b/examples/2.vite/svelte/package.json index 648645ae..4cd084f1 100644 --- a/examples/2.vite/svelte/package.json +++ b/examples/2.vite/svelte/package.json @@ -16,7 +16,9 @@ }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "catalog:", + "@vitejs/devtools": "catalog:", "autoprefixer": "catalog:", + "comark": "catalog:", "postcss": "catalog:", "svelte": "catalog:", "typescript": "catalog:", diff --git a/examples/2.vite/svelte/vite.config.ts b/examples/2.vite/svelte/vite.config.ts index bcf9f726..e90d7f3a 100644 --- a/examples/2.vite/svelte/vite.config.ts +++ b/examples/2.vite/svelte/vite.config.ts @@ -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/vite' export default defineConfig({ - plugins: [svelte(), tailwindcss()], + plugins: [DevTools(), svelte(), comarkDevtools(), tailwindcss()], }) diff --git a/examples/2.vite/vue/package.json b/examples/2.vite/vue/package.json index 516d6a71..973358fe 100644 --- a/examples/2.vite/vue/package.json +++ b/examples/2.vite/vue/package.json @@ -18,6 +18,7 @@ "vue-router": "catalog:" }, "devDependencies": { + "@vitejs/devtools": "catalog:", "@vitejs/plugin-vue": "catalog:", "autoprefixer": "catalog:", "postcss": "catalog:", diff --git a/examples/2.vite/vue/vite.config.ts b/examples/2.vite/vue/vite.config.ts index 7b90510a..bf84fd32 100644 --- a/examples/2.vite/vue/vite.config.ts +++ b/examples/2.vite/vue/vite.config.ts @@ -1,3 +1,4 @@ +import { DevTools } from '@vitejs/devtools' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import ui from '@nuxt/ui/vite' @@ -6,6 +7,7 @@ import { fileURLToPath } from 'node:url' export default defineConfig({ plugins: [ + DevTools(), vue(), comark(), ui({ diff --git a/packages/comark-react/package.json b/packages/comark-react/package.json index 04125b02..ebc02749 100644 --- a/packages/comark-react/package.json +++ b/packages/comark-react/package.json @@ -52,6 +52,7 @@ "@types/react-dom": "catalog:", "react": "catalog:", "react-dom": "catalog:", + "vite": "catalog:", "vitest": "catalog:" }, "peerDependencies": { diff --git a/packages/comark-react/src/components/ComarkClient.tsx b/packages/comark-react/src/components/ComarkClient.tsx index ae2c26eb..2cd49096 100644 --- a/packages/comark-react/src/components/ComarkClient.tsx +++ b/packages/comark-react/src/components/ComarkClient.tsx @@ -1,10 +1,11 @@ 'use client' -import { use, useDeferredValue, useMemo, Suspense } from 'react' +import { use, useDeferredValue, useEffect, useMemo, useRef, useState, Suspense } from 'react' import { parse } from 'comark' import type { ComarkTree } from 'comark' import { ComarkRenderer } from './ComarkRenderer.tsx' import type { ComarkProps } from './Comark' +import type { RegisteredInstance } from 'comark/devtools' interface ComarkContentProps extends Omit { parsePromise: Promise @@ -36,15 +37,58 @@ function ComarkContent({ export function ComarkClient({ children, markdown = '', options = {}, plugins = [], ...rest }: ComarkProps) { const content = children ? String(children) : markdown + const [devtoolsOverride, setDevtoolsOverride] = useState(null) + const effectiveContent = devtoolsOverride ?? content // Re-creates the promise only when content changes. // Note: options/plugins should be stable references (defined outside render or memoized). - const parsePromise = useMemo(() => parse(content, { ...options, plugins }), [content]) + const parsePromise = useMemo(() => parse(effectiveContent, { ...options, plugins }), [effectiveContent]) // Keep showing the previous parsed result while a new parse is pending — // prevents blank flashes during rapid streaming updates. const deferredPromise = useDeferredValue(parsePromise) + // Devtools instance registration (dev mode only) + const instanceRef = useRef(null) + useEffect(() => { + if (!import.meta.hot) return + let cancelled = false + + import('comark/devtools').then(({ registerDevtoolsInstance }) => { + if (cancelled) return + // We resolve the current parse to get the tree for registration + parsePromise.then((tree) => { + if (cancelled) return + registerDevtoolsInstance({ + hot: import.meta.hot!, + tree, + markdown: effectiveContent, + onUpdate: (md: string) => setDevtoolsOverride(md), + }).then((handle) => { + if (cancelled) { + handle?.unregister() + return + } + instanceRef.current = handle + }) + }) + }) + + return () => { + cancelled = true + instanceRef.current?.unregister() + instanceRef.current = null + } + }, []) // eslint-disable-line react-hooks/exhaustive-deps + + // Update instance when tree changes + useEffect(() => { + if (!instanceRef.current) return + parsePromise.then((tree) => { + instanceRef.current?.update({ tree, markdown: effectiveContent }) + }) + }, [parsePromise, effectiveContent]) + return ( import type { ComarkTree, ComarkPlugin, ComponentManifest } from 'comark' import { parse } from 'comark' + import type { RegisteredInstance } from 'comark/devtools' import ComarkRenderer from './ComarkRenderer.svelte' let { @@ -51,8 +52,9 @@ This is an alert component } = $props() let parsed: ComarkTree | null = $state(null) + let devtoolsOverride: string | null = $state(null) - let content = $derived((markdown || '').trim()) + let content = $derived((devtoolsOverride ?? (markdown || '')).trim()) let requestVersion = 0 let appliedVersion = 0 @@ -68,6 +70,45 @@ This is an alert component } }) }) + + // Devtools instance registration (dev mode only) + let devtoolsHandle: RegisteredInstance | null = $state(null) + + if (import.meta.hot) { + const hot = import.meta.hot + $effect(() => { + let cancelled = false + + import('comark/devtools').then(({ registerDevtoolsInstance }) => { + if (cancelled) return + registerDevtoolsInstance({ + hot, + tree: { nodes: [], frontmatter: {}, meta: {} }, + markdown: '', + onUpdate: (md: string) => { devtoolsOverride = md }, + }).then((handle) => { + if (cancelled) { + handle?.unregister() + return + } + devtoolsHandle = handle + }) + }) + + return () => { + cancelled = true + devtoolsHandle?.unregister() + devtoolsHandle = null + } + }) + + // Update instance when tree changes + $effect(() => { + if (devtoolsHandle && parsed) { + devtoolsHandle.update({ tree: parsed, markdown: content }) + } + }) + } {#if parsed} diff --git a/packages/comark-vue/src/components/Comark.ts b/packages/comark-vue/src/components/Comark.ts index 7dc02cd1..0059c043 100644 --- a/packages/comark-vue/src/components/Comark.ts +++ b/packages/comark-vue/src/components/Comark.ts @@ -1,5 +1,5 @@ import type { PropType } from 'vue' -import { computed, defineComponent, h, shallowRef, watch } from 'vue' +import { computed, defineComponent, h, onScopeDispose, provide, ref, shallowRef, watch } from 'vue' import { createSerializedParse } from 'comark' import type { ParseOptions, ComponentManifest, ComarkTree } from 'comark' import { ComarkRenderer } from './ComarkRenderer.ts' @@ -183,15 +183,40 @@ export const Comark: ComarkComponent = defineComponent({ }) const parsed = shallowRef(null) + const devtoolsOverride = ref(null) const parse = createSerializedParse({ ...props.options, plugins: props.plugins }) + const effectiveMarkdown = computed(() => devtoolsOverride.value ?? markdown.value) + watch( - () => [markdown.value, props.streaming] as const, - () => parse(markdown.value, { streaming: props.streaming }).then((result) => (parsed.value = result)) + () => [effectiveMarkdown.value, props.streaming] as const, + () => parse(effectiveMarkdown.value, { streaming: props.streaming }).then((result) => (parsed.value = result)) ) - await parse(markdown.value, { streaming: props.streaming }).then((result) => (parsed.value = result)) + await parse(effectiveMarkdown.value, { streaming: props.streaming }).then((result) => (parsed.value = result)) + + // Devtools instance registration (dev mode only) + provide('__comark_devtools_registered__', true) + + if (import.meta.hot) { + const { registerDevtoolsInstance } = await import('comark/devtools') + const handle = await registerDevtoolsInstance({ + hot: import.meta.hot, + tree: parsed.value || { nodes: [], frontmatter: {}, meta: {} }, + markdown: effectiveMarkdown.value, + onUpdate: (md: string) => { + devtoolsOverride.value = md + }, + }) + + if (handle) { + watch(parsed, (tree) => { + if (tree) handle.update({ tree, markdown: effectiveMarkdown.value }) + }) + onScopeDispose(() => handle.unregister()) + } + } return () => { // Render using ComarkRenderer diff --git a/packages/comark-vue/src/components/ComarkRenderer.ts b/packages/comark-vue/src/components/ComarkRenderer.ts index 59dab93b..ec414cd8 100644 --- a/packages/comark-vue/src/components/ComarkRenderer.ts +++ b/packages/comark-vue/src/components/ComarkRenderer.ts @@ -15,8 +15,10 @@ import { h, inject, onErrorCaptured, + onScopeDispose, ref, toRaw, + watch, } from 'vue' import { findLastTextNodeAndAppendNode, getCaret } from '../utils/caret.ts' import { pascalCase, resolveAttributes } from 'comark/utils' @@ -368,6 +370,43 @@ export const ComarkRenderer: ComarkRendererComponent = defineComponent({ const comark = inject('comark', { components: {}, componentManifest: () => null }) + // Devtools: register this instance if no parent Comark already registered + const parentRegistered = inject('__comark_devtools_registered__', false) + if (!parentRegistered && import.meta.hot) { + let devtoolsHandle: { unregister: () => void } | null = null + let disposed = false + const renderMdPromise = import('comark/render') + + import('comark/devtools').then(({ registerDevtoolsInstanceFromTree }) => { + if (disposed) return + registerDevtoolsInstanceFromTree({ + hot: import.meta.hot!, + tree: props.tree, + }).then((handle) => { + if (disposed) { + handle?.unregister() + return + } + devtoolsHandle = handle + if (handle) { + watch( + () => props.tree, + (tree) => { + renderMdPromise.then(({ renderMarkdown }) => { + renderMarkdown(tree).then((md) => handle.update({ tree, markdown: md })) + }) + } + ) + } + }) + }) + + onScopeDispose(() => { + disposed = true + devtoolsHandle?.unregister() + }) + } + const components = computed(() => ({ ...comark?.components, ...props.components, diff --git a/packages/comark-vue/src/vite.ts b/packages/comark-vue/src/vite.ts index bfe825a3..4a1ff6b7 100644 --- a/packages/comark-vue/src/vite.ts +++ b/packages/comark-vue/src/vite.ts @@ -4,6 +4,7 @@ import { join, basename } from 'node:path' import type { Plugin, ResolvedConfig } from 'vite' import type { ElementNode, DirectiveNode, TransformContext } from '@vue/compiler-core' import { existsSync } from 'node:fs' +import { comarkDevtools } from 'comark/vite' const runtimeDir = fileURLToPath(new URL('./utils', import.meta.url)) @@ -121,7 +122,7 @@ function generateComponentsModule(files: string[]): string { * }) * ``` */ -export default function comark(opts: { prose?: boolean } = {}): Plugin { +export default function comark(opts: { prose?: boolean } = {}): Plugin[] { let proseDir: string let proseFilesCache: string[] | null = null @@ -140,72 +141,75 @@ export default function comark(opts: { prose?: boolean } = {}): Plugin { } } - return { - name: 'comark-vue', - enforce: 'pre', - - configResolved(config: ResolvedConfig) { - if (existsSync(join(config.root, 'src', 'components', 'prose'))) { - proseDir = join(config.root, 'src', 'components', 'prose') - } - if (existsSync(join(config.root, 'components', 'prose'))) { - proseDir = join(config.root, 'components', 'prose') - } - - const vuePlugin = config.plugins.find((p) => p.name === 'vite:vue') - if (!vuePlugin) { - console.warn('[comark-vue] @vitejs/plugin-vue not found. Make sure vue() is in your plugins.') - return - } - - const vueOptions = (vuePlugin as any).api?.options - if (!vueOptions) return - - vueOptions.template ??= {} - vueOptions.template.compilerOptions ??= {} - vueOptions.template.compilerOptions.nodeTransforms ??= [] - vueOptions.template.compilerOptions.nodeTransforms.unshift(viteComarkSlot) - }, - - resolveId(id) { - if (id === VIRTUAL_COMPONENTS_ID) return RESOLVED_COMPONENTS_ID + return [ + { + name: 'comark-vue', + enforce: 'pre', + + configResolved(config: ResolvedConfig) { + if (existsSync(join(config.root, 'src', 'components', 'prose'))) { + proseDir = join(config.root, 'src', 'components', 'prose') + } + if (existsSync(join(config.root, 'components', 'prose'))) { + proseDir = join(config.root, 'components', 'prose') + } + + const vuePlugin = config.plugins.find((p) => p.name === 'vite:vue') + if (!vuePlugin) { + console.warn('[comark-vue] @vitejs/plugin-vue not found. Make sure vue() is in your plugins.') + return + } + + const vueOptions = (vuePlugin as any).api?.options + if (!vueOptions) return + + vueOptions.template ??= {} + vueOptions.template.compilerOptions ??= {} + vueOptions.template.compilerOptions.nodeTransforms ??= [] + vueOptions.template.compilerOptions.nodeTransforms.unshift(viteComarkSlot) + }, + + resolveId(id) { + if (id === VIRTUAL_COMPONENTS_ID) return RESOLVED_COMPONENTS_ID + }, + + async load(id) { + if (id !== RESOLVED_COMPONENTS_ID) return + const files = await resolveProseFiles() + return generateComponentsModule(files) + }, + + async transform(code) { + if (opts.prose === false) return null + if (!code.includes('createApp') || !code.includes('.mount(')) return null + // Skip if already injected + if (code.includes(VIRTUAL_COMPONENTS_ID)) return null + + const files = await resolveProseFiles() + if (!files.length) return null + + const injected = + `import __comarkProse from ${JSON.stringify(VIRTUAL_COMPONENTS_ID)};\n` + + code.replace(/\.mount\s*\(/, '.use(__comarkProse).mount(') + + return { code: injected, map: null } + }, + + configureServer(server) { + if (!proseDir) return + + server.watcher.add(proseDir) + + const invalidate = (file: string) => { + invalidateCache(file) + const mod = server.moduleGraph.getModuleById(RESOLVED_COMPONENTS_ID) + if (mod) server.moduleGraph.invalidateModule(mod) + } + + server.watcher.on('add', invalidate) + server.watcher.on('unlink', invalidate) + }, }, - - async load(id) { - if (id !== RESOLVED_COMPONENTS_ID) return - const files = await resolveProseFiles() - return generateComponentsModule(files) - }, - - async transform(code) { - if (opts.prose === false) return null - if (!code.includes('createApp') || !code.includes('.mount(')) return null - // Skip if already injected - if (code.includes(VIRTUAL_COMPONENTS_ID)) return null - - const files = await resolveProseFiles() - if (!files.length) return null - - const injected = - `import __comarkProse from ${JSON.stringify(VIRTUAL_COMPONENTS_ID)};\n` + - code.replace(/\.mount\s*\(/, '.use(__comarkProse).mount(') - - return { code: injected, map: null } - }, - - configureServer(server) { - if (!proseDir) return - - server.watcher.add(proseDir) - - const invalidate = (file: string) => { - invalidateCache(file) - const mod = server.moduleGraph.getModuleById(RESOLVED_COMPONENTS_ID) - if (mod) server.moduleGraph.invalidateModule(mod) - } - - server.watcher.on('add', invalidate) - server.watcher.on('unlink', invalidate) - }, - } + comarkDevtools(), + ] } diff --git a/packages/comark-vue/tsconfig.json b/packages/comark-vue/tsconfig.json index 759a528d..2073b8a6 100644 --- a/packages/comark-vue/tsconfig.json +++ b/packages/comark-vue/tsconfig.json @@ -6,6 +6,7 @@ "moduleResolution": "bundler", "strict": true, "strictNullChecks": true, + "types": ["vite/client"], "esModuleInterop": true, "skipDefaultLibCheck": true, "skipLibCheck": true, diff --git a/packages/comark/package.json b/packages/comark/package.json index 82c3b496..3c45a558 100644 --- a/packages/comark/package.json +++ b/packages/comark/package.json @@ -30,6 +30,9 @@ "types": "./dist/index.d.ts", "exports": { ".": "./dist/index.js", + "./vite": "./dist/devtools/vite.js", + "./devtools": "./dist/devtools/index.js", + "./devtools-renderer": "./dist/devtools/renderer/index.js", "./plugins/*": "./dist/plugins/*.js", "./utils": "./dist/utils/index.js", "./parse": "./dist/parse.js", @@ -40,7 +43,7 @@ }, "scripts": { "stub": "node ../../scripts/stub.mjs", - "build": "tsc", + "build": "node ../../scripts/sync-devtools-css.mjs && tsc", "dev": "tsc --watch", "test": "vitest run", "prepack": "tsc", @@ -58,17 +61,21 @@ "@shikijs/twoslash": "catalog:", "@types/js-yaml": "catalog:", "@types/markdown-it": "catalog:", + "@vitejs/devtools-kit": "catalog:", "github-slugger": "catalog:", "hast-util-to-string": "catalog:", "minimark": "catalog:", "tsx": "catalog:", "twoslash": "catalog:", + "vite": "catalog:", "vitest": "catalog:" }, "peerDependencies": { + "@vitejs/devtools-kit": "catalog:", "beautiful-mermaid": "catalog:", "katex": "catalog:", - "shiki": "catalog:" + "shiki": "catalog:", + "vite": "catalog:" }, "peerDependenciesMeta": { "shiki": { @@ -79,6 +86,12 @@ }, "katex": { "optional": true + }, + "@vitejs/devtools-kit": { + "optional": true + }, + "vite": { + "optional": true } } } diff --git a/packages/comark/src/devtools/constants/index.ts b/packages/comark/src/devtools/constants/index.ts new file mode 100644 index 00000000..5b5eb96b --- /dev/null +++ b/packages/comark/src/devtools/constants/index.ts @@ -0,0 +1,13 @@ +/** Inline Lucide SVG icons (MIT licensed) — used in the devtools renderer */ +export const LUCIDE_MOON = + '' +export const LUCIDE_SUN = + '' +export const LUCIDE_MONITOR = + '' + +/** Comark logo SVG icons for the Vite DevTools dock */ +const COMARK_SVG = (color: string) => + `` +export const COMARK_LIGHT_ICON = `data:image/svg+xml,${encodeURIComponent(COMARK_SVG('#000'))}` +export const COMARK_DARK_ICON = `data:image/svg+xml,${encodeURIComponent(COMARK_SVG('#fff'))}` diff --git a/packages/comark/src/devtools/index.ts b/packages/comark/src/devtools/index.ts new file mode 100644 index 00000000..c45fed1b --- /dev/null +++ b/packages/comark/src/devtools/index.ts @@ -0,0 +1,4 @@ +export { getDevtoolsRegistry } from './registry.ts' +export { registerDevtoolsInstance, registerDevtoolsInstanceFromTree } from './register.ts' +export type { ComarkInstance, ComarkInstanceSummary } from './registry.ts' +export type { RegisterInstanceOptions, RegisteredInstance } from './register.ts' diff --git a/packages/comark/src/devtools/register.ts b/packages/comark/src/devtools/register.ts new file mode 100644 index 00000000..cc2fc82b --- /dev/null +++ b/packages/comark/src/devtools/register.ts @@ -0,0 +1,88 @@ +import type { ComarkTree } from '../types.ts' +import type { ComarkInstance } from './registry.ts' + +/** Minimal subset of Vite's `import.meta.hot` used by the helper */ +interface HotHandle { + send(event: string, data?: any): void + on(event: string, cb: (...args: any[]) => void): void +} + +export interface RegisterInstanceOptions { + /** The HMR handle (`import.meta.hot`). Pass `null` to skip registration. */ + hot: HotHandle | null | undefined + /** Initial tree (may be empty for string renderers) */ + tree: ComarkTree + /** Initial markdown source, if known */ + markdown?: string + /** Callback invoked when the devtools pushes an edit */ + onUpdate?: (markdown: string) => void +} + +export interface RegisteredInstance { + /** The assigned instance id */ + id: string + /** Update the instance's tree and/or markdown */ + update(patch: { tree?: ComarkTree; markdown?: string }): void + /** Unregister the instance */ + unregister(): void +} + +/** + * Register a Comark devtools instance. + * + * Handles the full lifecycle: + * 1. Dynamic import of the registry (keeps it tree-shakable in prod) + * 2. HMR connection + * 3. Instance registration + * 4. Returns update/unregister handles + * + * @returns A promise resolving to the registered instance handles, + * or `null` if HMR is not available. + */ +export async function registerDevtoolsInstance(options: RegisterInstanceOptions): Promise { + if (!options.hot) return null + + const { getDevtoolsRegistry } = await import('./registry.ts') + const registry = getDevtoolsRegistry() + registry.connectHMR(options.hot) + + const id = registry.nextId() + + const label = typeof location !== 'undefined' ? location.hash || location.pathname : undefined + + const instance: ComarkInstance = { + id, + label, + tree: options.tree, + markdown: options.markdown, + } + + if (options.onUpdate) { + instance.update = options.onUpdate + } + + const unregister = registry.register(instance) + + return { + id, + update(patch) { + registry.update(id, patch) + }, + unregister, + } +} + +/** + * Convenience: register an instance and reverse-render the tree to markdown. + * Used by renderers that only have a tree (no original markdown source). + */ +export async function registerDevtoolsInstanceFromTree( + options: Omit +): Promise { + if (!options.hot) return null + + const { renderMarkdown } = await import('../render.ts') + const markdown = await renderMarkdown(options.tree) + + return registerDevtoolsInstance({ ...options, markdown }) +} diff --git a/packages/comark/src/devtools/registry.ts b/packages/comark/src/devtools/registry.ts new file mode 100644 index 00000000..603dd7d5 --- /dev/null +++ b/packages/comark/src/devtools/registry.ts @@ -0,0 +1,119 @@ +import type { ComarkTree } from '../types.ts' + +/** Minimal subset of Vite's `import.meta.hot` API used by the registry */ +interface HotModule { + send(event: string, data?: any): void + on(event: string, cb: (...args: any[]) => void): void +} + +export interface ComarkInstance { + /** Unique instance identifier */ + id: string + /** Human-readable label (e.g. current URL) */ + label?: string + /** Current parsed tree */ + tree: ComarkTree + /** Current markdown source (if available) */ + markdown?: string + /** Callback to update the markdown source and trigger re-parse */ + update?: (markdown: string) => void +} + +export interface ComarkInstanceSummary { + id: string + label?: string + markdown?: string + nodeCount: number + /** Whether the instance accepts live updates (has an onUpdate callback) */ + updatable: boolean +} + +type InstanceListener = (instances: Map) => void + +class ComarkDevtoolsRegistry { + readonly instances = new Map() + private listeners = new Set() + private hot: HotModule | null = null + private counter = 0 + + nextId(): string { + return `comark-${++this.counter}` + } + + /** + * Provide the caller's `import.meta.hot` handle so the registry can + * push instance data to the Vite dev server and receive updates. + * Called by each renderer that mounts — always updates the handle + * so the registry stays connected even after HMR module disposal. + */ + connectHMR(hot: HotModule): void { + this.hot = hot + + // Re-register the update listener on the new handle + hot.on('comark:update', (data: { id: string; markdown: string }) => { + const instance = this.instances.get(data.id) + if (instance?.update) { + instance.update(data.markdown) + } + }) + } + + register(instance: ComarkInstance): () => void { + this.instances.set(instance.id, instance) + this.notify() + return () => { + this.instances.delete(instance.id) + // Reset counter when all instances are removed so IDs stay low + if (this.instances.size === 0) { + this.counter = 0 + } + this.notify() + } + } + + update(id: string, patch: Partial>): void { + const instance = this.instances.get(id) + if (!instance) return + Object.assign(instance, patch) + this.notify() + } + + subscribe(listener: InstanceListener): () => void { + this.listeners.add(listener) + return () => this.listeners.delete(listener) + } + + serialize(): ComarkInstanceSummary[] { + return Array.from(this.instances.values()).map((inst) => ({ + id: inst.id, + label: inst.label, + markdown: inst.markdown, + nodeCount: inst.tree?.nodes?.length || 0, + updatable: typeof inst.update === 'function', + })) + } + + private notify(): void { + for (const listener of this.listeners) { + listener(this.instances) + } + this.pushToHMR() + } + + private pushToHMR(): void { + const data = this.serialize() + this.hot?.send('comark:instances', data) + } +} + +/** + * Global devtools registry. Only active in dev mode — renderers should + * guard registration behind `import.meta.hot` or `import.meta.env.DEV`. + */ +export function getDevtoolsRegistry(): ComarkDevtoolsRegistry { + const g = globalThis as any + if (!g.__COMARK_DEVTOOLS__) { + g.__COMARK_DEVTOOLS__ = new ComarkDevtoolsRegistry() + } + return g.__COMARK_DEVTOOLS__ +} diff --git a/packages/comark/src/devtools/renderer/index.ts b/packages/comark/src/devtools/renderer/index.ts new file mode 100644 index 00000000..42fd4e51 --- /dev/null +++ b/packages/comark/src/devtools/renderer/index.ts @@ -0,0 +1,296 @@ +import type { DockClientScriptContext } from '@vitejs/devtools-kit/client' +import { STYLES } from './styles.ts' +import { applyTheme, createThemeToggle } from './theme.ts' +import { countNodes, renderAST, renderInfo, renderRoundtrip } from './output.ts' +import type { Theme } from './theme.ts' + +interface InstanceSummary { + id: string + label?: string + markdown?: string + nodeCount: number + updatable: boolean +} + +type ComarkTree = { + nodes: unknown[] + frontmatter: Record + meta: Record +} + +const DEFAULT_MARKDOWN = `# Hello World + +This is a **Comark** playground inside Vite DevTools. + +- Supports *inline* formatting +- Component syntax like \`::alert\` + +::alert{type="info"} +Edit this markdown to see the parsed AST in real time. +:: +` + +export default function setup(ctx: DockClientScriptContext) { + let editor: HTMLTextAreaElement + let outputEl: HTMLElement + let statusDot: HTMLElement + let statusText: HTMLElement + let activeTab = 'ast' + let parseTimer: ReturnType | undefined + let lastTree: ComarkTree | null = null + let lastRoundtrip: string | null = null + let lastError: string | null = null + let tabButtons: HTMLButtonElement[] = [] + + // Live instance tracking + let activeInstance: InstanceSummary | null = null + let instanceDot: HTMLElement + let instanceLabel: HTMLElement + let pushBtn: HTMLButtonElement + let pollInterval: ReturnType | undefined + + // Theme state + let theme: Theme = 'auto' + let rootEl: HTMLElement + + ctx.current.events.on('dom:panel:mounted', (panel: HTMLElement) => { + cleanup() + + // Inject styles + const style = document.createElement('style') + style.textContent = STYLES + panel.appendChild(style) + + // Root container + const root = document.createElement('div') + root.className = 'comark-devtools' + rootEl = root + applyTheme(rootEl, theme) + panel.appendChild(root) + + // --- Editor pane --- + const editorPane = document.createElement('div') + editorPane.className = 'comark-editor-pane' + root.appendChild(editorPane) + + // Instance bar + const instanceBar = document.createElement('div') + instanceBar.className = 'comark-instance-bar' + editorPane.appendChild(instanceBar) + + instanceDot = document.createElement('span') + instanceDot.className = 'comark-instance-dot' + instanceDot.dataset.connected = 'false' + instanceBar.appendChild(instanceDot) + + instanceLabel = document.createElement('span') + instanceLabel.className = 'comark-instance-label' + instanceLabel.textContent = 'No instance' + instanceBar.appendChild(instanceLabel) + + pushBtn = document.createElement('button') + pushBtn.className = 'comark-push-btn' + pushBtn.textContent = 'Push to app' + pushBtn.title = 'Push edited markdown back to the live component' + pushBtn.style.display = 'none' + pushBtn.addEventListener('click', pushToInstance) + instanceBar.appendChild(pushBtn) + + // Theme toggle + const themeToggle = createThemeToggle(theme, (t) => { + theme = t + applyTheme(rootEl, theme) + }) + instanceBar.appendChild(themeToggle) + + // Editor header + const editorHeader = document.createElement('div') + editorHeader.className = 'comark-pane-header' + editorHeader.textContent = 'Markdown' + editorPane.appendChild(editorHeader) + + // Editor textarea + editor = document.createElement('textarea') + editor.className = 'comark-editor' + editor.placeholder = 'Type markdown here...' + editor.value = DEFAULT_MARKDOWN + editor.spellcheck = false + editor.addEventListener('input', () => scheduleParse()) + editor.addEventListener('keydown', (e) => { + if (e.key === 'Tab') { + e.preventDefault() + const start = editor.selectionStart + const end = editor.selectionEnd + editor.value = editor.value.substring(0, start) + ' ' + editor.value.substring(end) + editor.selectionStart = editor.selectionEnd = start + 2 + scheduleParse() + } + }) + editorPane.appendChild(editor) + + // --- Output pane --- + const outputPane = document.createElement('div') + outputPane.className = 'comark-output-pane' + root.appendChild(outputPane) + + // Tabs + const tabs = document.createElement('div') + tabs.className = 'comark-tabs' + outputPane.appendChild(tabs) + + const tabDefs = [ + { id: 'ast', label: 'AST' }, + { id: 'roundtrip', label: 'Roundtrip' }, + { id: 'info', label: 'Info' }, + ] + + for (const tab of tabDefs) { + const btn = document.createElement('button') + btn.className = 'comark-tab' + btn.textContent = tab.label + btn.dataset.tab = tab.id + btn.dataset.active = String(tab.id === activeTab) + btn.addEventListener('click', () => switchTab(tab.id)) + tabs.appendChild(btn) + tabButtons.push(btn) + } + + // Output area + outputEl = document.createElement('div') + outputEl.className = 'comark-output' + outputPane.appendChild(outputEl) + + // Status bar + const statusBar = document.createElement('div') + statusBar.className = 'comark-status' + outputPane.appendChild(statusBar) + + statusDot = document.createElement('span') + statusDot.className = 'comark-status-dot' + statusBar.appendChild(statusDot) + + statusText = document.createElement('span') + statusText.textContent = 'Ready' + statusBar.appendChild(statusText) + + // Initial parse + start polling + parseMarkdown() + pollInstances() + pollInterval = setInterval(pollInstances, 1500) + }) + + ctx.current.events.on('entry:deactivated', cleanup) + + function cleanup() { + clearInterval(pollInterval) + pollInterval = undefined + clearTimeout(parseTimer) + parseTimer = undefined + activeInstance = null + tabButtons = [] + } + + function scheduleParse() { + clearTimeout(parseTimer) + parseTimer = setTimeout(parseMarkdown, 300) + } + + async function pollInstances() { + let instances: InstanceSummary[] = [] + try { + const data = (await ctx.rpc.call('comark:list-instances')) as InstanceSummary[] + instances = data || [] + } catch { + instances = [] + } + + const current = instances[0] || null + const prevId = activeInstance?.id + + activeInstance = current + + if (current) { + instanceDot.dataset.connected = 'true' + instanceLabel.textContent = current.label || current.id + pushBtn.style.display = current.updatable ? '' : 'none' + + if (current.id !== prevId && current.markdown) { + editor.value = current.markdown + scheduleParse() + } + } else { + instanceDot.dataset.connected = 'false' + instanceLabel.textContent = 'No instance' + pushBtn.style.display = 'none' + } + } + + async function pushToInstance() { + if (!activeInstance) return + try { + await ctx.rpc.call('comark:update-instance', { + id: activeInstance.id, + markdown: editor.value, + }) + } catch { + // Silently ignore push errors + } + } + + async function parseMarkdown() { + const markdown = editor.value + statusDot.dataset.state = 'parsing' + statusText.textContent = 'Parsing...' + lastError = null + + try { + const [tree, roundtrip] = await Promise.all([ + ctx.rpc.call('comark:parse', markdown) as Promise, + ctx.rpc.call('comark:render-markdown', markdown) as Promise, + ]) + + lastTree = tree + lastRoundtrip = roundtrip + lastError = null + + const nodeCount = countNodes(tree.nodes) + statusDot.dataset.state = '' + statusText.textContent = `${nodeCount} node${nodeCount !== 1 ? 's' : ''} · ${markdown.length} chars` + } catch (err) { + lastError = err instanceof Error ? err.message : String(err) + statusDot.dataset.state = 'error' + statusText.textContent = 'Parse error' + } + + updateOutput() + } + + function switchTab(tabId: string) { + activeTab = tabId + for (const btn of tabButtons) { + btn.dataset.active = String(btn.dataset.tab === tabId) + } + updateOutput() + } + + function updateOutput() { + if (lastError) { + outputEl.replaceChildren() + const errEl = document.createElement('div') + errEl.className = 'comark-error' + errEl.textContent = lastError + outputEl.appendChild(errEl) + return + } + + outputEl.replaceChildren() + + if (activeTab === 'ast' && lastTree) { + renderAST(outputEl, lastTree) + } else if (activeTab === 'roundtrip' && lastRoundtrip !== null) { + renderRoundtrip(outputEl, lastRoundtrip, editor.value) + } else if (activeTab === 'info' && lastTree) { + renderInfo(outputEl, lastTree, editor.value.length) + } + } +} diff --git a/packages/comark/src/devtools/renderer/output.ts b/packages/comark/src/devtools/renderer/output.ts new file mode 100644 index 00000000..bbda1edd --- /dev/null +++ b/packages/comark/src/devtools/renderer/output.ts @@ -0,0 +1,117 @@ +type ComarkTree = { + nodes: unknown[] + frontmatter: Record + meta: Record +} + +export function renderAST(container: HTMLElement, tree: ComarkTree): void { + const pre = document.createElement('pre') + pre.innerHTML = syntaxHighlightJSON(tree) + container.appendChild(pre) +} + +export function renderRoundtrip(container: HTMLElement, roundtrip: string, original: string): void { + const pre = document.createElement('pre') + pre.textContent = roundtrip + container.appendChild(pre) + + const isMatch = original.trim() === roundtrip.trim() + const indicator = document.createElement('div') + indicator.style.cssText = ` + margin-top: 12px; + padding: 8px 12px; + border-radius: 6px; + font-size: 12px; + font-weight: 500; + background: ${isMatch ? 'rgba(16, 185, 129, 0.1)' : 'rgba(245, 158, 11, 0.1)'}; + color: ${isMatch ? '#10b981' : '#f59e0b'}; + ` + indicator.textContent = isMatch + ? '✓ Roundtrip match — parse → render produces identical output' + : '⚠ Roundtrip mismatch — output differs from input (whitespace or formatting changes)' + container.appendChild(indicator) +} + +export function renderInfo(container: HTMLElement, tree: ComarkTree, inputLength: number): void { + const table = document.createElement('table') + table.className = 'comark-info-table' + + const rows = [ + ['Nodes', String(tree.nodes.length)], + ['Total nodes (deep)', String(countNodes(tree.nodes))], + ['Input length', `${inputLength} chars`], + ['Frontmatter keys', Object.keys(tree.frontmatter).join(', ') || '(none)'], + ['Meta keys', Object.keys(tree.meta).join(', ') || '(none)'], + ] + + if (Object.keys(tree.frontmatter).length > 0) { + rows.push(['Frontmatter', JSON.stringify(tree.frontmatter, null, 2)]) + } + + for (const [key, value] of rows) { + const tr = document.createElement('tr') + const td1 = document.createElement('td') + td1.textContent = key + const td2 = document.createElement('td') + td2.textContent = value + if (key === 'Frontmatter') { + const pre = document.createElement('pre') + pre.style.margin = '0' + pre.textContent = value + td2.textContent = '' + td2.appendChild(pre) + } + tr.appendChild(td1) + tr.appendChild(td2) + table.appendChild(tr) + } + + container.appendChild(table) +} + +export function countNodes(nodes: unknown[]): number { + let count = 0 + for (const node of nodes) { + count++ + if (Array.isArray(node) && node.length > 2) { + count += countNodes(node.slice(2) as unknown[]) + } + } + return count +} + +function syntaxHighlightJSON(obj: unknown, indent = 0): string { + const pad = ' '.repeat(indent) + + if (obj === null || obj === undefined) { + return `null` + } + if (typeof obj === 'boolean') { + return `${obj}` + } + if (typeof obj === 'number') { + return `${obj}` + } + if (typeof obj === 'string') { + return `${escapeHTML(JSON.stringify(obj))}` + } + if (Array.isArray(obj)) { + if (obj.length === 0) return '[]' + const items = obj.map((item) => `${pad} ${syntaxHighlightJSON(item, indent + 1)}`) + return `[\n${items.join(',\n')}\n${pad}]` + } + if (typeof obj === 'object') { + const entries = Object.entries(obj as Record) + if (entries.length === 0) return '{}' + const items = entries.map( + ([key, val]) => + `${pad} ${escapeHTML(JSON.stringify(key))}: ${syntaxHighlightJSON(val, indent + 1)}` + ) + return `{\n${items.join(',\n')}\n${pad}}` + } + return String(obj) +} + +function escapeHTML(str: string): string { + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') +} diff --git a/packages/comark/src/devtools/renderer/styles.css b/packages/comark/src/devtools/renderer/styles.css new file mode 100644 index 00000000..337f8d91 --- /dev/null +++ b/packages/comark/src/devtools/renderer/styles.css @@ -0,0 +1,326 @@ +.comark-devtools { + --comark-devtools-bg: #ffffff; + --comark-devtools-bg-soft: #fafafa; + --comark-devtools-bg-muted: #f4f4f5; + --comark-devtools-text: #18181b; + --comark-devtools-text-muted: #71717a; + --comark-devtools-border: #e4e4e7; + --comark-devtools-primary: #eab308; + --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12); + --comark-devtools-accent: #ca8a04; + --comark-devtools-error: #ef4444; + --comark-devtools-warning: #f59e0b; + --comark-devtools-success: #22c55e; + + display: flex; + height: 100%; + font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif; + color: var(--comark-devtools-text); + background: var(--comark-devtools-bg); + font-size: 13px; +} + +.comark-devtools *, +.comark-devtools *::before, +.comark-devtools *::after { + box-sizing: border-box; +} + +/* Editor pane */ +.comark-editor-pane { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; + border-right: 1px solid var(--comark-devtools-border); +} + +.comark-pane-header { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + border-bottom: 1px solid var(--comark-devtools-border); + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--comark-devtools-text-muted); + flex-shrink: 0; +} + +.comark-editor { + flex: 1; + width: 100%; + padding: 12px; + border: none; + resize: none; + outline: none; + background: transparent; + color: inherit; + font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace; + font-size: 13px; + line-height: 1.6; + tab-size: 2; +} + +.comark-editor::placeholder { + color: var(--comark-devtools-text-muted); + opacity: 0.5; +} + +/* Output pane */ +.comark-output-pane { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; + overflow: hidden; +} + +/* Tabs */ +.comark-tabs { + display: flex; + border-bottom: 1px solid var(--comark-devtools-border); + flex-shrink: 0; +} + +.comark-tab { + padding: 6px 12px; + border: none; + border-bottom: 2px solid transparent; + background: transparent; + color: var(--comark-devtools-text-muted); + cursor: pointer; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + transition: + color 0.15s, + border-color 0.15s; +} + +.comark-tab:hover { + color: var(--comark-devtools-text); +} + +.comark-tab[data-active='true'] { + color: var(--comark-devtools-text); + border-bottom-color: var(--comark-devtools-primary); +} + +/* Status bar */ +.comark-status { + display: flex; + align-items: center; + gap: 12px; + padding: 4px 12px; + border-top: 1px solid var(--comark-devtools-border); + font-size: 11px; + color: var(--comark-devtools-text-muted); + flex-shrink: 0; +} + +.comark-status-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--comark-devtools-success); + flex-shrink: 0; +} + +.comark-status-dot[data-state='error'] { + background: var(--comark-devtools-error); +} + +.comark-status-dot[data-state='parsing'] { + background: var(--comark-devtools-warning); +} + +/* Output content */ +.comark-output { + flex: 1; + overflow: auto; + padding: 12px; +} + +.comark-output pre { + margin: 0; + font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace; + font-size: 12px; + line-height: 1.6; + white-space: pre-wrap; + word-break: break-word; +} + +/* AST tree */ +.comark-tree-key { + color: #9333ea; +} +.comark-tree-string { + color: #16a34a; +} +.comark-tree-number { + color: #2563eb; +} +.comark-tree-null { + color: #a1a1aa; + font-style: italic; +} +.comark-tree-bool { + color: #dc2626; +} + +/* Info table */ +.comark-info-table { + width: 100%; + border-collapse: collapse; + font-size: 12px; +} + +.comark-info-table td { + padding: 6px 12px; + border-bottom: 1px solid var(--comark-devtools-border); +} + +.comark-info-table td:first-child { + font-weight: 600; + width: 140px; + color: var(--comark-devtools-text-muted); +} + +/* Error display */ +.comark-error { + color: var(--comark-devtools-error); + padding: 12px; + font-size: 12px; + font-family: 'Geist Mono', ui-monospace, monospace; + white-space: pre-wrap; +} + +/* Instance bar */ +.comark-instance-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 12px; + border-bottom: 1px solid var(--comark-devtools-border); + font-size: 11px; + flex-shrink: 0; + background: var(--comark-devtools-bg-soft); +} + +.comark-instance-dot { + width: 6px; + height: 6px; + border-radius: 50%; + flex-shrink: 0; +} + +.comark-instance-dot[data-connected='true'] { + background: var(--comark-devtools-success); + animation: comark-pulse 2s infinite; +} + +.comark-instance-dot[data-connected='false'] { + background: #a1a1aa; +} + +@keyframes comark-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +.comark-instance-label { + font-weight: 500; + color: var(--comark-devtools-text-muted); +} + +.comark-push-btn { + padding: 2px 8px; + border: 1px solid var(--comark-devtools-border); + border-radius: 4px; + background: var(--comark-devtools-bg); + color: inherit; + font-size: 11px; + cursor: pointer; + transition: + background 0.15s, + border-color 0.15s; +} + +.comark-push-btn:hover { + background: var(--comark-devtools-bg-muted); + border-color: var(--comark-devtools-primary); +} + +.comark-push-btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.comark-theme-toggle { + margin-left: auto; + display: flex; + align-items: center; + justify-content: center; + width: 26px; + height: 26px; + padding: 0; + border: 1px solid var(--comark-devtools-border); + border-radius: 6px; + background: transparent; + color: var(--comark-devtools-text-muted); + cursor: pointer; + transition: + color 0.15s, + border-color 0.15s, + background 0.15s; +} + +.comark-theme-toggle:hover { + color: var(--comark-devtools-text); + border-color: var(--comark-devtools-primary); + background: var(--comark-devtools-primary-soft); +} + +/* Dark theme via data attribute */ +.comark-devtools[data-theme='dark'], +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) { + --comark-devtools-bg: #18181b; + --comark-devtools-bg-soft: #27272a; + --comark-devtools-bg-muted: #3f3f46; + --comark-devtools-text: #fafafa; + --comark-devtools-text-muted: #a1a1aa; + --comark-devtools-border: #3f3f46; + --comark-devtools-primary: #facc15; + --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12); + --comark-devtools-accent: #fde047; +} + +.comark-devtools[data-theme='dark'] .comark-tree-key, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key { + color: #c084fc; +} +.comark-devtools[data-theme='dark'] .comark-tree-string, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string { + color: #4ade80; +} +.comark-devtools[data-theme='dark'] .comark-tree-number, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number { + color: #60a5fa; +} +.comark-devtools[data-theme='dark'] .comark-tree-null, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null { + color: #71717a; +} +.comark-devtools[data-theme='dark'] .comark-tree-bool, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool { + color: #f87171; +} diff --git a/packages/comark/src/devtools/renderer/styles.ts b/packages/comark/src/devtools/renderer/styles.ts new file mode 100644 index 00000000..a594ba04 --- /dev/null +++ b/packages/comark/src/devtools/renderer/styles.ts @@ -0,0 +1,3 @@ +// @generated — do not edit. Edit styles.css instead, then run: node scripts/sync-devtools-css.mjs +export const STYLES = + '.comark-devtools {\n --comark-devtools-bg: #ffffff;\n --comark-devtools-bg-soft: #fafafa;\n --comark-devtools-bg-muted: #f4f4f5;\n --comark-devtools-text: #18181b;\n --comark-devtools-text-muted: #71717a;\n --comark-devtools-border: #e4e4e7;\n --comark-devtools-primary: #eab308;\n --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n --comark-devtools-accent: #ca8a04;\n --comark-devtools-error: #ef4444;\n --comark-devtools-warning: #f59e0b;\n --comark-devtools-success: #22c55e;\n\n display: flex;\n height: 100%;\n font-family: \'Geist\', \'Public Sans\', ui-sans-serif, system-ui, sans-serif;\n color: var(--comark-devtools-text);\n background: var(--comark-devtools-bg);\n font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n box-sizing: border-box;\n}\n\n/* Editor pane */\n.comark-editor-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n border-right: 1px solid var(--comark-devtools-border);\n}\n\n.comark-pane-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-editor {\n flex: 1;\n width: 100%;\n padding: 12px;\n border: none;\n resize: none;\n outline: none;\n background: transparent;\n color: inherit;\n font-family: \'Geist Mono\', ui-monospace, \'SF Mono\', Monaco, Menlo, monospace;\n font-size: 13px;\n line-height: 1.6;\n tab-size: 2;\n}\n\n.comark-editor::placeholder {\n color: var(--comark-devtools-text-muted);\n opacity: 0.5;\n}\n\n/* Output pane */\n.comark-output-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n overflow: hidden;\n}\n\n/* Tabs */\n.comark-tabs {\n display: flex;\n border-bottom: 1px solid var(--comark-devtools-border);\n flex-shrink: 0;\n}\n\n.comark-tab {\n padding: 6px 12px;\n border: none;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n transition: color 0.15s, border-color 0.15s;\n}\n\n.comark-tab:hover {\n color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active="true"] {\n color: var(--comark-devtools-text);\n border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Status bar */\n.comark-status {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 4px 12px;\n border-top: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--comark-devtools-success);\n flex-shrink: 0;\n}\n\n.comark-status-dot[data-state="error"] {\n background: var(--comark-devtools-error);\n}\n\n.comark-status-dot[data-state="parsing"] {\n background: var(--comark-devtools-warning);\n}\n\n/* Output content */\n.comark-output {\n flex: 1;\n overflow: auto;\n padding: 12px;\n}\n\n.comark-output pre {\n margin: 0;\n font-family: \'Geist Mono\', ui-monospace, \'SF Mono\', Monaco, Menlo, monospace;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key { color: #9333ea; }\n.comark-tree-string { color: #16a34a; }\n.comark-tree-number { color: #2563eb; }\n.comark-tree-null { color: #a1a1aa; font-style: italic; }\n.comark-tree-bool { color: #dc2626; }\n\n/* Info table */\n.comark-info-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.comark-info-table td {\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n}\n\n.comark-info-table td:first-child {\n font-weight: 600;\n width: 140px;\n color: var(--comark-devtools-text-muted);\n}\n\n/* Error display */\n.comark-error {\n color: var(--comark-devtools-error);\n padding: 12px;\n font-size: 12px;\n font-family: \'Geist Mono\', ui-monospace, monospace;\n white-space: pre-wrap;\n}\n\n/* Instance bar */\n.comark-instance-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n flex-shrink: 0;\n background: var(--comark-devtools-bg-soft);\n}\n\n.comark-instance-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected="true"] {\n background: var(--comark-devtools-success);\n animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected="false"] {\n background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n\n.comark-instance-label {\n font-weight: 500;\n color: var(--comark-devtools-text-muted);\n}\n\n.comark-push-btn {\n padding: 2px 8px;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 4px;\n background: var(--comark-devtools-bg);\n color: inherit;\n font-size: 11px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s;\n}\n\n.comark-push-btn:hover {\n background: var(--comark-devtools-bg-muted);\n border-color: var(--comark-devtools-primary);\n}\n\n.comark-push-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.comark-theme-toggle {\n margin-left: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 6px;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n transition: color 0.15s, border-color 0.15s, background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n color: var(--comark-devtools-text);\n border-color: var(--comark-devtools-primary);\n background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme="dark"],\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) {\n --comark-devtools-bg: #18181b;\n --comark-devtools-bg-soft: #27272a;\n --comark-devtools-bg-muted: #3f3f46;\n --comark-devtools-text: #fafafa;\n --comark-devtools-text-muted: #a1a1aa;\n --comark-devtools-border: #3f3f46;\n --comark-devtools-primary: #facc15;\n --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme="dark"] .comark-tree-key,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-key { color: #c084fc; }\n.comark-devtools[data-theme="dark"] .comark-tree-string,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-string { color: #4ade80; }\n.comark-devtools[data-theme="dark"] .comark-tree-number,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-number { color: #60a5fa; }\n.comark-devtools[data-theme="dark"] .comark-tree-null,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-null { color: #71717a; }\n.comark-devtools[data-theme="dark"] .comark-tree-bool,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-bool { color: #f87171; }\n' diff --git a/packages/comark/src/devtools/renderer/theme.ts b/packages/comark/src/devtools/renderer/theme.ts new file mode 100644 index 00000000..70c22f26 --- /dev/null +++ b/packages/comark/src/devtools/renderer/theme.ts @@ -0,0 +1,44 @@ +import { LUCIDE_MOON, LUCIDE_MONITOR, LUCIDE_SUN } from '../constants/index.ts' + +export type Theme = 'auto' | 'light' | 'dark' + +export function applyTheme(rootEl: HTMLElement, theme: Theme): void { + if (theme === 'auto') { + delete rootEl.dataset.theme + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches + rootEl.dataset.autoDark = String(prefersDark) + } else { + rootEl.dataset.theme = theme + delete rootEl.dataset.autoDark + } +} + +export function getThemeIcon(theme: Theme): string { + if (theme === 'dark') return LUCIDE_MOON + if (theme === 'light') return LUCIDE_SUN + return LUCIDE_MONITOR +} + +export function createThemeToggle(initial: Theme, onChange: (theme: Theme) => void): HTMLButtonElement { + let theme = initial + + const btn = document.createElement('button') + btn.className = 'comark-theme-toggle' + btn.title = 'Toggle dark mode' + btn.innerHTML = getThemeIcon(theme) + + btn.addEventListener('click', () => { + if (theme === 'auto') { + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches + theme = prefersDark ? 'light' : 'dark' + } else if (theme === 'dark') { + theme = 'light' + } else { + theme = 'dark' + } + btn.innerHTML = getThemeIcon(theme) + onChange(theme) + }) + + return btn +} diff --git a/packages/comark/src/devtools/vite.ts b/packages/comark/src/devtools/vite.ts new file mode 100644 index 00000000..e66455e2 --- /dev/null +++ b/packages/comark/src/devtools/vite.ts @@ -0,0 +1,113 @@ +/// +import type { Plugin, ViteDevServer } from 'vite' +import type { ComarkTree } from '../types.ts' +import type { ComarkInstanceSummary } from './registry.ts' +import { COMARK_DARK_ICON, COMARK_LIGHT_ICON } from './constants/index.ts' + +declare module '@vitejs/devtools-kit' { + interface DevToolsRpcServerFunctions { + 'comark:parse': (markdown: string) => Promise + 'comark:render-markdown': (markdown: string) => Promise + 'comark:list-instances': () => Promise + 'comark:update-instance': (args: { id: string; markdown: string }) => Promise<{ ok: boolean }> + } +} + +/** + * Vite plugin that registers a Comark playground panel in Vite DevTools. + * + * The panel provides an interactive markdown editor with live AST inspection, + * powered by the `comark` parser running on the Vite dev server via RPC. + * + * Works with any framework (Vue, React, Svelte, etc.) — just add it to + * your Vite config alongside `@vitejs/devtools`. + * + * @example + * ```ts + * import { DevTools } from '@vitejs/devtools' + * import { comarkDevtools } from 'comark/vite' + * + * export default defineConfig({ + * plugins: [DevTools(), comarkDevtools()], + * }) + * ``` + */ +export function comarkDevtools(): Plugin { + // Instance data pushed from the user's app via HMR + let instancesData: ComarkInstanceSummary[] = [] + let server: ViteDevServer | undefined + + return { + name: 'comark:devtools', + + configureServer(_server: ViteDevServer) { + server = _server + + // Receive instance data from the user's app (sent by the devtools registry) + server.hot.on('comark:instances', (data: ComarkInstanceSummary[]) => { + instancesData = data + }) + }, + + devtools: { + setup(ctx) { + // RPC: parse markdown and return the tree + ctx.rpc.register({ + name: 'comark:parse', + type: 'query', + handler: async (markdown: string) => { + const { parse } = await import('../index.ts') + return await parse(markdown) + }, + }) + + // RPC: render tree back to markdown (roundtrip) + ctx.rpc.register({ + name: 'comark:render-markdown', + type: 'query', + handler: async (markdown: string) => { + const { parse } = await import('../index.ts') + const { renderMarkdown } = await import('../render.ts') + const tree = await parse(markdown) + return await renderMarkdown(tree) + }, + }) + + // RPC: list live Comark instances on the page + ctx.rpc.register({ + name: 'comark:list-instances', + type: 'query', + handler: () => instancesData, + }) + + // RPC: update an instance's markdown (push from devtools to the user's app) + ctx.rpc.register({ + name: 'comark:update-instance', + type: 'mutation', + handler: (args: { id: string; markdown: string }) => { + server?.hot.send('comark:update', { + id: args.id, + markdown: args.markdown, + }) + return { ok: true } + }, + }) + + // Register the dock entry with a custom renderer + ctx.docks.register({ + id: 'comark', + title: 'Comark', + icon: { + light: COMARK_LIGHT_ICON, + dark: COMARK_DARK_ICON, + }, + type: 'custom-render', + renderer: { + importFrom: 'comark/devtools-renderer', + importName: 'default', + }, + }) + }, + }, + } as Plugin +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4db2219..e939e95a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,6 +99,12 @@ catalogs: '@vercel/speed-insights': specifier: ^2.0.0 version: 2.0.0 + '@vitejs/devtools': + specifier: ^0.1.15 + version: 0.1.24 + '@vitejs/devtools-kit': + specifier: ^0.1.15 + version: 0.1.24 '@vitejs/plugin-react': specifier: ^6.0.1 version: 6.0.1 @@ -311,7 +317,7 @@ importers: version: 25.5.2 '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) comark: specifier: workspace:* version: link:packages/comark @@ -326,7 +332,7 @@ importers: version: 1.0.34 nuxt: specifier: 'catalog:' - version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) oxfmt: specifier: 'catalog:' version: 0.46.0 @@ -380,22 +386,22 @@ importers: version: 1.7.0 '@nuxt/content': specifier: 'catalog:' - version: 3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2) + version: 3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)) '@nuxt/ui': specifier: 'catalog:' - version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) + version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) '@nuxtjs/mcp-toolkit': specifier: 'catalog:' - version: 0.13.4(h3@1.15.11)(magicast@0.5.2)(zod@4.4.3) + version: 0.13.4(h3@2.0.1-rc.22)(magicast@0.5.2)(zod@4.4.3) '@nuxtlabs/monarch-mdc': specifier: 'catalog:' version: 0.9.0 '@vercel/analytics': specifier: 'catalog:' - version: 2.0.1(48d27f7e56382153c2674fe90a76fbae) + version: 2.0.1(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3))(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(react@19.2.5)(svelte@5.55.2)(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@vercel/speed-insights': specifier: 'catalog:' - version: 2.0.0(48d27f7e56382153c2674fe90a76fbae) + version: 2.0.0(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3))(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(react@19.2.5)(svelte@5.55.2)(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@vueuse/core': specifier: 'catalog:' version: 14.2.1(vue@3.5.32(typescript@5.9.3)) @@ -407,13 +413,13 @@ importers: version: link:../packages/comark docus: specifier: 'catalog:' - version: 5.11.0(b927d32a0b0e4e2a56ba354600fb5b9f) + version: 5.11.0(4c350040598c37923cc896b5f7dc5856) markdown-it-math: specifier: 'catalog:' version: 5.2.1 nuxt: specifier: 'catalog:' - version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) nuxt-studio: specifier: 'catalog:' version: 1.6.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vue@3.5.32(typescript@5.9.3)) @@ -435,7 +441,7 @@ importers: devDependencies: '@nuxt/devtools-kit': specifier: 'catalog:' - version: 4.0.0-alpha.4(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.0.0-alpha.4(magicast@0.5.2)(vite@8.0.8) '@types/json-schema': specifier: 'catalog:' version: 7.0.15 @@ -450,13 +456,13 @@ importers: dependencies: '@astrojs/react': specifier: 'catalog:' - version: 5.0.3(@types/node@25.5.2)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.6.1)(lightningcss@1.32.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 5.0.3(@types/node@25.5.2)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.6.1)(lightningcss@1.32.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.2.2(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.2.2(vite@8.0.8) astro: specifier: 'catalog:' - version: 6.1.5(@types/node@25.5.2)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.4) + version: 6.1.5(@types/node@25.5.2)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.4) comark: specifier: workspace:* version: link:../../../packages/comark @@ -484,7 +490,7 @@ importers: version: link:../../../packages/comark next: specifier: 'catalog:' - version: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3) react: specifier: 'catalog:' version: 19.2.5 @@ -518,10 +524,10 @@ importers: version: link:../../../packages/comark-nuxt '@nuxt/ui': specifier: 'catalog:' - version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) + version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) nuxt: specifier: 'catalog:' - version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) shiki: specifier: 'catalog:' version: 4.0.2 @@ -537,13 +543,13 @@ importers: devDependencies: '@sveltejs/adapter-auto': specifier: 'catalog:' - version: 7.0.1(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + version: 7.0.1(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8)) '@sveltejs/kit': specifier: 'catalog:' - version: 2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8) '@sveltejs/vite-plugin-svelte': specifier: 'catalog:' - version: 7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 7.0.0(svelte@5.55.2)(vite@8.0.8) svelte: specifier: 'catalog:' version: 5.55.2 @@ -555,7 +561,7 @@ importers: version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) examples/1.frameworks/vitepress: dependencies: @@ -568,7 +574,7 @@ importers: devDependencies: vitepress: specifier: 'catalog:' - version: 1.6.4(@algolia/client-search@5.49.1)(@types/node@25.5.2)(change-case@5.4.4)(fuse.js@7.3.0)(lightningcss@1.32.0)(postcss@8.5.14)(search-insights@2.17.3)(terser@5.46.0)(typescript@5.9.3) + version: 1.6.4(@algolia/client-search@5.49.1)(@types/node@25.5.2)(change-case@5.4.4)(fuse.js@7.3.0)(lightningcss@1.32.0)(postcss@8.5.14)(sass@1.97.3)(search-insights@2.17.3)(terser@5.46.0)(typescript@5.9.3) examples/2.vite/ansi: dependencies: @@ -596,7 +602,7 @@ importers: version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) examples/2.vite/html: dependencies: @@ -613,12 +619,18 @@ importers: specifier: 'catalog:' version: 4.0.2 devDependencies: + '@vitejs/devtools': + specifier: 'catalog:' + version: 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@pnpm/logger@1001.0.1)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(typescript@5.9.3)(vite@8.0.8) + comark: + specifier: workspace:* + version: link:../../../packages/comark typescript: specifier: 'catalog:' version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) examples/2.vite/react: dependencies: @@ -627,7 +639,7 @@ importers: version: link:../../../packages/comark-react '@tailwindcss/vite': specifier: 'catalog:' - version: 4.2.2(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.2.2(vite@8.0.8) comark: specifier: workspace:* version: link:../../../packages/comark @@ -644,9 +656,12 @@ importers: '@types/react-dom': specifier: 'catalog:' version: 19.2.3(@types/react@19.2.14) + '@vitejs/devtools': + specifier: 'catalog:' + version: 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@pnpm/logger@1001.0.1)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(typescript@5.9.3)(vite@8.0.8) '@vitejs/plugin-react': specifier: 'catalog:' - version: 6.0.1(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 6.0.1(vite@8.0.8) autoprefixer: specifier: 'catalog:' version: 10.4.27(postcss@8.5.9) @@ -658,7 +673,7 @@ importers: version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) examples/2.vite/svelte: dependencies: @@ -670,17 +685,23 @@ importers: version: 4.0.2 '@tailwindcss/vite': specifier: 'catalog:' - version: 4.2.2(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.2.2(vite@8.0.8) tailwindcss: specifier: 'catalog:' version: 4.2.2 devDependencies: '@sveltejs/vite-plugin-svelte': specifier: 'catalog:' - version: 7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 7.0.0(svelte@5.55.2)(vite@8.0.8) + '@vitejs/devtools': + specifier: 'catalog:' + version: 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@pnpm/logger@1001.0.1)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(typescript@5.9.3)(vite@8.0.8) autoprefixer: specifier: 'catalog:' version: 10.4.27(postcss@8.5.9) + comark: + specifier: workspace:* + version: link:../../../packages/comark postcss: specifier: 'catalog:' version: 8.5.9 @@ -692,7 +713,7 @@ importers: version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) examples/2.vite/vue: dependencies: @@ -701,7 +722,7 @@ importers: version: link:../../../packages/comark-vue '@nuxt/ui': specifier: 'catalog:' - version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) + version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) '@shikijs/langs': specifier: 'catalog:' version: 4.0.2 @@ -718,9 +739,12 @@ importers: specifier: 'catalog:' version: 5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)) devDependencies: + '@vitejs/devtools': + specifier: 'catalog:' + version: 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@pnpm/logger@1001.0.1)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(typescript@5.9.3)(vite@8.0.8) '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) autoprefixer: specifier: 'catalog:' version: 10.4.27(postcss@8.5.9) @@ -732,7 +756,7 @@ importers: version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -766,13 +790,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) typescript: specifier: 'catalog:' version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -788,13 +812,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) typescript: specifier: 'catalog:' version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -822,13 +846,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) typescript: specifier: 'catalog:' version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -840,7 +864,7 @@ importers: version: link:../../../packages/comark-vue '@nuxt/ui': specifier: 'catalog:' - version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) + version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) '@shikijs/themes': specifier: 'catalog:' version: 4.0.2 @@ -862,13 +886,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) typescript: specifier: 'catalog:' version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -887,13 +911,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) typescript: specifier: 'catalog:' version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -905,7 +929,7 @@ importers: version: link:../../../packages/comark-vue '@tailwindcss/vite': specifier: 'catalog:' - version: 4.2.2(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.2.2(vite@8.0.8) beautiful-mermaid: specifier: 'catalog:' version: 1.1.3 @@ -918,7 +942,7 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) autoprefixer: specifier: 'catalog:' version: 10.4.27(postcss@8.5.9) @@ -930,7 +954,7 @@ importers: version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -946,13 +970,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) typescript: specifier: 'catalog:' version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -964,7 +988,7 @@ importers: version: link:../../../packages/comark-vue '@nuxt/ui': specifier: 'catalog:' - version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) + version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) '@shikijs/langs': specifier: 'catalog:' version: 4.0.2 @@ -995,13 +1019,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: 'catalog:' - version: 6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) typescript: specifier: 'catalog:' version: 5.9.3 vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue-tsc: specifier: 'catalog:' version: 3.2.6(typescript@5.9.3) @@ -1016,13 +1040,13 @@ importers: version: link:../../../packages/comark-nuxt '@nuxt/ui': specifier: 'catalog:' - version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) + version: 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) ai: specifier: 'catalog:' version: 6.0.168(zod@4.4.3) nuxt: specifier: 'catalog:' - version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) packages/comark: dependencies: @@ -1060,6 +1084,9 @@ importers: '@types/markdown-it': specifier: 'catalog:' version: 14.1.2 + '@vitejs/devtools-kit': + specifier: 'catalog:' + version: 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(typescript@5.9.3)(vite@8.0.8) github-slugger: specifier: 'catalog:' version: 2.0.0 @@ -1075,9 +1102,12 @@ importers: twoslash: specifier: 'catalog:' version: 0.3.6(typescript@5.9.3) + vite: + specifier: 'catalog:' + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vitest: specifier: 'catalog:' - version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) packages/comark-ansi: dependencies: @@ -1096,7 +1126,7 @@ importers: devDependencies: vitest: specifier: 'catalog:' - version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) packages/comark-html: dependencies: @@ -1115,7 +1145,7 @@ importers: version: 0.16.45 vitest: specifier: 'catalog:' - version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) packages/comark-nuxt: dependencies: @@ -1130,14 +1160,14 @@ importers: version: link:../comark nuxt: specifier: ^4.0.0 - version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) devDependencies: '@vue/server-renderer': specifier: ^3.5.32 version: 3.5.32(vue@3.5.32(typescript@5.9.3)) vitest: specifier: 'catalog:' - version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) vue: specifier: ^3.5.32 version: 3.5.32(typescript@5.9.3) @@ -1169,9 +1199,12 @@ importers: react-dom: specifier: 'catalog:' version: 19.2.5(react@19.2.5) + vite: + specifier: 'catalog:' + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vitest: specifier: 'catalog:' - version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) packages/comark-svelte: dependencies: @@ -1193,13 +1226,13 @@ importers: version: 2.5.7(svelte@5.55.2)(typescript@5.9.3) '@sveltejs/vite-plugin-svelte': specifier: 'catalog:' - version: 7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 7.0.0(svelte@5.55.2)(vite@8.0.8) '@vitest/browser': specifier: 'catalog:' - version: 4.1.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.4) + version: 4.1.4(vite@8.0.8)(vitest@4.1.4) '@vitest/browser-playwright': specifier: 'catalog:' - version: 4.1.4(playwright@1.59.1)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.4) + version: 4.1.4(playwright@1.59.1)(vite@8.0.8)(vitest@4.1.4) release-it: specifier: 'catalog:' version: 19.2.4(@types/node@25.5.2)(magicast@0.5.2) @@ -1211,7 +1244,7 @@ importers: version: 4.4.6(picomatch@4.0.4)(svelte@5.55.2)(typescript@5.9.3) vitest: specifier: 'catalog:' - version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) vitest-browser-svelte: specifier: 'catalog:' version: 2.1.0(svelte@5.55.2)(vitest@4.1.4) @@ -1239,10 +1272,10 @@ importers: version: 3.5.32(vue@3.5.32(typescript@5.9.3)) vite: specifier: 'catalog:' - version: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + version: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vitest: specifier: 'catalog:' - version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) vue: specifier: ^3.5.32 version: 3.5.32(typescript@5.9.3) @@ -2160,6 +2193,10 @@ packages: '@floating-ui/vue@1.1.11': resolution: {integrity: sha512-HzHKCNVxnGS35r9fCHBc3+uCnjw9IWIlCPL683cGgM9Kgj2BiAl8x1mS7vtvP6F9S/e/q4O6MApwSHj8hNLGfw==} + '@gwhitney/detect-indent@7.0.1': + resolution: {integrity: sha512-7bQW+gkKa2kKZPeJf6+c6gFK9ARxQfn+FKy9ScTBppyKRWH2KzsmweXUoklqeEiHiNVWaeP5csIdsNq6w7QhzA==} + engines: {node: '>=12.20'} + '@hono/node-server@1.19.11': resolution: {integrity: sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==} engines: {node: '>=18.14.1'} @@ -3127,6 +3164,12 @@ packages: cpu: [arm] os: [android] + '@oxc-parser/binding-android-arm-eabi@0.126.0': + resolution: {integrity: sha512-svyoHt25J4741QJ5aa4R+h0iiBeSRt63Lr3aAZcxy2c/NeSE1IfDeMnSij6rIg7EjxkdlXzz613wUjeCeilBNA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + '@oxc-parser/binding-android-arm-eabi@0.128.0': resolution: {integrity: sha512-aca6ZvzmCBUGOANQRiRQRZuRKYI3ENhcit6GisnknOOmcezfQc7xJ4dxlPU7MV7mOvrC7RNR1u3LAD7xyaiCxA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3145,6 +3188,12 @@ packages: cpu: [arm64] os: [android] + '@oxc-parser/binding-android-arm64@0.126.0': + resolution: {integrity: sha512-hPEBRKgplp1mG9GkINFsr4JVMDNrGJLOqfDaadTWpAoTnzYR5Rmv8RMvB3hJZpiNvbk1aacopdHUP1pggMQ/cw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@oxc-parser/binding-android-arm64@0.128.0': resolution: {integrity: sha512-BbeDmuohoJ7Rz/it5wnkj69i/OsCPS3Z51nLEzwO/Y6YshtC4JU+15oNwhY8v4LRKRYclRc7ggOikwrsJ/eOEQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3163,6 +3212,12 @@ packages: cpu: [arm64] os: [darwin] + '@oxc-parser/binding-darwin-arm64@0.126.0': + resolution: {integrity: sha512-ccRpu9sdYmznePJQG5halhs0FW5tw5a8zRSoZXOzM1OjoeZ4jiRRruFiPclsD59edoVAK1l83dvfjWz1nQi6lg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@oxc-parser/binding-darwin-arm64@0.128.0': resolution: {integrity: sha512-tRUHPt80417QmvNpoSslJT1VY8NUbWdrWR+L14Zn+RbOTcaqB8E6PYE/ZGN8jjWBzqporiA/H4MfO50ew/NCNA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3181,6 +3236,12 @@ packages: cpu: [x64] os: [darwin] + '@oxc-parser/binding-darwin-x64@0.126.0': + resolution: {integrity: sha512-CHB4zVjNSKqx8Fw9pHowzQQnjjuq04i4Ng0Avj+DixlwhwAoMYqlFbocYIlbg+q3zOLGlm7vEHm83jqEMitnyg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@oxc-parser/binding-darwin-x64@0.128.0': resolution: {integrity: sha512-rWI2Hb1Nt3U/vKsjyNvZzDC8i/l144U20DKjhzaTmwIhIiSRGeroPWWiImwypmKLqrw8GuIixbWJkpGWLbkzrQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3199,6 +3260,12 @@ packages: cpu: [x64] os: [freebsd] + '@oxc-parser/binding-freebsd-x64@0.126.0': + resolution: {integrity: sha512-RQ3nEJdcDKBfBjmLJ3Vl1d0KQERPV1P8eUrnBm7+VTYyoaJSPLVFuPg1mlD1hk3n0/879VLFMfusFkBal4ssWQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@oxc-parser/binding-freebsd-x64@0.128.0': resolution: {integrity: sha512-hhpdVMaNCLgQxjgNPeeFzSeJMmZPc5lKfv0NGSI3egZq9EdnEGqeC8JsYsQjK7PoQgbvZ17xlj0SO5ziH5Obkg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3217,6 +3284,12 @@ packages: cpu: [arm] os: [linux] + '@oxc-parser/binding-linux-arm-gnueabihf@0.126.0': + resolution: {integrity: sha512-onipc2wCDA7Bauzb4KK1mab0GsEDf4ujiIfWECdnmY/2LlzAoX3xdQRLAUyEDB1kn3yilHBrkmXDdHluyHXxiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@oxc-parser/binding-linux-arm-gnueabihf@0.128.0': resolution: {integrity: sha512-093zNw0zZ/e/obML+rhlSdmnzR0mVZluPcAkxunEc5E3F0yBVsFn24Y1ILfsEte11Ud041qn/gp2OJ1jxNqUng==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3235,6 +3308,12 @@ packages: cpu: [arm] os: [linux] + '@oxc-parser/binding-linux-arm-musleabihf@0.126.0': + resolution: {integrity: sha512-5BuJJPohrV5NJ8lmcYOMbfRCUGoYH5J9HZHeuqOLwkHXWAuPMN3X1h8bC/2mWjmosdbfTtmyIdX3spS/TkqKNg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@oxc-parser/binding-linux-arm-musleabihf@0.128.0': resolution: {integrity: sha512-fq7DmKmfC+dvD97IXrgbph6Jzwe0EDu+PYMofmzZ6fv5X1k9vtaqLpDGMuICO9MmUnyKAQmVl+wIv2RNy4Dz8g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3255,6 +3334,13 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-arm64-gnu@0.126.0': + resolution: {integrity: sha512-r2KApRgm2pOJaduRm6GOT8x0whcr67AyejNkSdzPt34GJ+Y3axcXN2mwlTs+8lfO/SSmpO5ZJGYiHYnxEE0jkw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-arm64-gnu@0.128.0': resolution: {integrity: sha512-Xvm48jJah8TlIrURIjNOP/gNiGe6aKvCB+r06VliflFo8Kq7VOLE8PxtgShJzZIqubrgdMdYfvuPPozn7F6MbQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3276,6 +3362,13 @@ packages: os: [linux] libc: [musl] + '@oxc-parser/binding-linux-arm64-musl@0.126.0': + resolution: {integrity: sha512-FQ+MMh7MT0Dr/u8+RWmWKlfoeWPQyHDbhhxJShJlYtROXXPHsRs9EvmQOZZ3sx4Nn7JU8NX+oyw2YzQ7anBJcA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@oxc-parser/binding-linux-arm64-musl@0.128.0': resolution: {integrity: sha512-M7iwBGmYJTx+pKOYFjI0buop4gJvlmcVzFGaXPt21DKpQkbQZG1f63Yg7LloIYT/t9yLxCw0Lhfx/RFlAlMSjA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3297,6 +3390,13 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-ppc64-gnu@0.126.0': + resolution: {integrity: sha512-Wv/T8C98hRQhGTlx2XFyLn5raRMp9U1lOQD+YnXNgAr7wHbJJpZ8mDBU7Rw+M3WytGcGTFcr6kqgfyQeHVtLbQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-ppc64-gnu@0.128.0': resolution: {integrity: sha512-21LGNIZb1Pcfk5/EGsqabrxv4yqQOWis1407JJrClS7XpFCrbvr74YAB1V+m54cYbwvO6UWwQqS4WecxiyfCRg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3318,6 +3418,13 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-riscv64-gnu@0.126.0': + resolution: {integrity: sha512-DHx1rT1zauW0ZbLHOiQh5AC9Xs3UkWx2XmfZHs+7nnWYr3sagrufoUQC+/XPwwjMIlCFXiFGM0sFh3TyOCZwqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-riscv64-gnu@0.128.0': resolution: {integrity: sha512-gyHjOTFpg9bTTYjxPmQirvufb89+VdZwVfcMtAUyPr6F5H8ZswvCQshK4qOW+Q+2Xyb33hduRgY/eFHJQjU/vQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3339,6 +3446,13 @@ packages: os: [linux] libc: [musl] + '@oxc-parser/binding-linux-riscv64-musl@0.126.0': + resolution: {integrity: sha512-umDc2mTShH0U2zcEYf8mIJ163seLJNn54ZUZYeI5jD4qlg9izPwoLrC2aNPKlMJTu6u/ysmQWiEvIiaAG+INkw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + '@oxc-parser/binding-linux-riscv64-musl@0.128.0': resolution: {integrity: sha512-X6Q2oKUrP5GyDd2xniuEBLk6aFQCZ97W2+aVXGgJXdjx5t4/oFuA9ri0wLOUrBIX+qdSuK581snMBio4z910eA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3360,6 +3474,13 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-s390x-gnu@0.126.0': + resolution: {integrity: sha512-PXXeWayclRtO1pxQEeCpiqIglQdhK2mAI2VX5xnsWdImzSB5GpoQ8TNw7vTCKk2k+GZuxl+q1knncidjCyUP9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-s390x-gnu@0.128.0': resolution: {integrity: sha512-BdzTmqxfxoYkpgokoLaSnOX6T+R3/goL42klre2tnG+kHbG2TXS0VN+P5BPofH1axdKOHy5ei4ENZrjmCOt2lA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3381,6 +3502,13 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-x64-gnu@0.126.0': + resolution: {integrity: sha512-wzocjxm34TbB3bFlqG65JiLtvf6ZDg2ZxRkLLbgXwDQUNU+0MPjQN8zy/0jBKNA5fnPLk3XeVdZ7Uin+7+CVkg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-x64-gnu@0.128.0': resolution: {integrity: sha512-OO1nW2Q7sSYYvJZpDHdvyFSdRaVcQqRijZSSmWVMqFxPYy8cEF45zJ9fcdIYuzIT3jYq6YRhEFm/VMWNWhE22Q==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3402,6 +3530,13 @@ packages: os: [linux] libc: [musl] + '@oxc-parser/binding-linux-x64-musl@0.126.0': + resolution: {integrity: sha512-e83uftP60jmkPs2+CW6T6A1GYzN2H6IumDAiTntv9WyHR73PI3ImHNBkYqnA3ukeKI3xjcCbhSh9QeJWmufxGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@oxc-parser/binding-linux-x64-musl@0.128.0': resolution: {integrity: sha512-4NehAe404MRdoZVS9DW8C5XbJwbXIc/KfVlYdpi5vE4081zc9Y0YzKVqyOYj/Puye7/Do+ohaONBFWlEHYl9hw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3421,6 +3556,12 @@ packages: cpu: [arm64] os: [openharmony] + '@oxc-parser/binding-openharmony-arm64@0.126.0': + resolution: {integrity: sha512-4WiOILHnPrTDY2/L4mE6PZCYwLN1d3ghma6BuTJ452CCgzRMt3uFplCtR+o3r9zdUWJYb370UizpI9CUcWXr1A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@oxc-parser/binding-openharmony-arm64@0.128.0': resolution: {integrity: sha512-kVbqgW9xLL8bh8oc7aYOJilRKXE5G33+tE0jan+duo/9OriaFRpijcCwT2waWs2oqYROYq0GlE7/p3ywoshVeg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3437,6 +3578,11 @@ packages: engines: {node: '>=14.0.0'} cpu: [wasm32] + '@oxc-parser/binding-wasm32-wasi@0.126.0': + resolution: {integrity: sha512-Y17hhnrQTrxgAxAyAq401vnN9URsAL4s5AjqpG1NDsXSlhe1yBNnns+rC2P6xcMoitgX5nKH2ryYt9oiFRlzLw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + '@oxc-parser/binding-wasm32-wasi@0.128.0': resolution: {integrity: sha512-L38ojghJYHmgiz6fJd7jwLB/ESDBpB02NdFxh+smqVM6P2anCEvHn0jhaSrt5eVNR1Ak8+moOeftUlofeyvniA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3454,6 +3600,12 @@ packages: cpu: [arm64] os: [win32] + '@oxc-parser/binding-win32-arm64-msvc@0.126.0': + resolution: {integrity: sha512-Znug1u1iRvT4VC3jANz6nhGBHsFwEFMxuimYpJFwMtsB6H5FcEoZRMmH26tHkSTD03JvDmG+gB65W3ajLjPcSw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@oxc-parser/binding-win32-arm64-msvc@0.128.0': resolution: {integrity: sha512-xgvO35GyHBtjlQ5AEpaYr7Rll1rvY7zqIhT6ty8E3ezBW2J1SFLjIDEvI/tcgDg6oaseDAqVcM+jU1HuCekgZw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3472,6 +3624,12 @@ packages: cpu: [ia32] os: [win32] + '@oxc-parser/binding-win32-ia32-msvc@0.126.0': + resolution: {integrity: sha512-qrw7mx5hFFTxVSXToOA40hpnjgNB/DJprZchtB4rDKNLKqkD3F26HbzaQeH1nxAKej0efSZfJd5Sw3qdtOLGhw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + '@oxc-parser/binding-win32-ia32-msvc@0.128.0': resolution: {integrity: sha512-OY+3eM2SN72prHKRB22mPz8o5A/7dJ+f5DFLBVvggyZhEaNDAH9IB+ElMjmOkOIwf5MDCUAowCK7pAncNxzpBA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3490,6 +3648,12 @@ packages: cpu: [x64] os: [win32] + '@oxc-parser/binding-win32-x64-msvc@0.126.0': + resolution: {integrity: sha512-ibB1s+mPUFXvS7MFJO2jpw/aCNs/P6ifnWlRyTYB+WYBpniOiCcHQQskZneJtwcjQMDRol3RGG3ihoYnzXSY4w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@oxc-parser/binding-win32-x64-msvc@0.128.0': resolution: {integrity: sha512-NE9ny+cPUCCObXa0IKLfj0tCdPd7pe/dz9ZpkxpUOymB3miNeMPybdlYYTBSGJUalMWeBM85/4JcCErCNTqOXw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3505,6 +3669,9 @@ packages: '@oxc-project/types@0.124.0': resolution: {integrity: sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==} + '@oxc-project/types@0.126.0': + resolution: {integrity: sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==} + '@oxc-project/types@0.128.0': resolution: {integrity: sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==} @@ -4113,6 +4280,56 @@ packages: engines: {node: '>=18'} hasBin: true + '@pnpm/constants@1001.3.1': + resolution: {integrity: sha512-2hf0s4pVrVEH8RvdJJ7YRKjQdiG8m0iAT26TTqXnCbK30kKwJW69VLmP5tED5zstmDRXcOeH5eRcrpkdwczQ9g==} + engines: {node: '>=18.12'} + + '@pnpm/core-loggers@1001.0.9': + resolution: {integrity: sha512-pW58m3ssrwVjwhlmTXDW1dh1sv2y6R2Gl5YvQInjM2d01/5mre/sYAY4MK3XfgEShZJQxv6wVXDUvyHHJ0oizg==} + engines: {node: '>=18.12'} + peerDependencies: + '@pnpm/logger': '>=1001.0.0 <1002.0.0' + + '@pnpm/error@1000.1.0': + resolution: {integrity: sha512-Dqc2IJJPjUatwc9Letw+vG29rnaMrDGi5g6WCx1HiZYm0obXbTmLygeRafMbgf+sLKXrWE1shOeiayQuczBdoA==} + engines: {node: '>=18.12'} + + '@pnpm/graceful-fs@1000.1.0': + resolution: {integrity: sha512-EsMX4slK0qJN2AR0/AYohY5m0HQNYGMNe+jhN74O994zp22/WbX+PbkIKyw3UQn39yQm2+z6SgwklDxbeapsmQ==} + engines: {node: '>=18.12'} + + '@pnpm/logger@1001.0.1': + resolution: {integrity: sha512-gdwlAMXC4Wc0s7Dmg/4wNybMEd/4lSd9LsXQxeg/piWY0PPXjgz1IXJWnVScx6dZRaaodWP3c1ornrw8mZdFZw==} + engines: {node: '>=18.12'} + + '@pnpm/manifest-utils@1002.0.5': + resolution: {integrity: sha512-2DSwQ6pP73IuJS5mCCtPd5fibJwuAdufXKuSL/Oq1n6AggCqy8616Xea1X3RH3z5dL4mn7Z4EZ+vnX8jX3Wrfw==} + engines: {node: '>=18.12'} + peerDependencies: + '@pnpm/logger': ^1001.0.1 + + '@pnpm/read-project-manifest@1001.2.6': + resolution: {integrity: sha512-BcNO50lAkE4m9JaJ0WmG3m/DH/qLSvMgZywtmb/dfyyLVu5nDZfDqmOd8U+f1NhLcLMbBK6AnS3hyUqZYvw9Vg==} + engines: {node: '>=18.12'} + peerDependencies: + '@pnpm/logger': ^1001.0.1 + + '@pnpm/semver.peer-range@1000.0.0': + resolution: {integrity: sha512-r6VzkrdH7ZKjPmAogTNvxuV/UyS/xwHNme+ZuEFiG0UthZgqudDftYtKmG20fcfrjG1lgJbbWICA8KvZy7mmbw==} + engines: {node: '>=18.12'} + + '@pnpm/text.comments-parser@1000.0.0': + resolution: {integrity: sha512-ivv/esrETOq9uMiKOC0ddVZ1BktEGsfsMQ9RWmrDpwPiqFSqWsIspnquxTBmm5GflC5N06fbqjGOpulZVYo3vQ==} + engines: {node: '>=18.12'} + + '@pnpm/types@1001.3.0': + resolution: {integrity: sha512-NLTXheat/u7OEGg5M5vF6Z85zx8uKUZE0+whtX/sbFV2XL48RdnOWGPTKYuVVkv8M+launaLUTgGEXNs/ess2w==} + engines: {node: '>=18.12'} + + '@pnpm/write-project-manifest@1000.0.16': + resolution: {integrity: sha512-zG68fk03ryot7TWUl9S/ShQ91uHWzIL9sVr2aQCuNHJo8G9kjsG6S0p58Zj/voahdDQeakZYYBSJ0mjNZeiJnw==} + engines: {node: '>=18.12'} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -4125,6 +4342,13 @@ packages: '@poppinss/exception@1.2.3': resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + '@publint/pack@0.1.4': + resolution: {integrity: sha512-HDVTWq3H0uTXiU0eeSQntcVUTPP3GamzeXI41+x7uU9J65JgWQh3qWZHblR1i0npXfFtF+mxBiU2nJH8znxWnQ==} + engines: {node: '>=18'} + + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + '@release-it/conventional-changelog@10.0.6': resolution: {integrity: sha512-aUb0IkcsBTMcOH5PPQ9Jv9lEOOVu2+rSgkE1ny+dzsTziQm2BhDRAtaFK/dw/HflthuXMWrqhhyfJhAV1AOEPQ==} engines: {node: ^20.12.0 || >=22.0.0} @@ -4310,6 +4534,9 @@ packages: cpu: [x64] os: [win32] + '@rolldown/debug@1.0.3': + resolution: {integrity: sha512-mQ4V7ODNxW4o09we34Dw9I29ByK/m7yTHT8Nqt+wwWCcxPsiGoixUsFDiruxGQwrjk6XYgwi/Cf0Prg0x5ABsA==} + '@rolldown/pluginutils@1.0.0-rc.15': resolution: {integrity: sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==} @@ -5344,6 +5571,11 @@ packages: peerDependencies: vue: ^3.5.32 + '@valibot/to-json-schema@1.7.0': + resolution: {integrity: sha512-Y3pPVibbIOHzohrlxSINvO7w/bvXkoYS3BQHoImV9ynE+bXKf171bdMucPurV2zp7gdmt0L1HCcNAsbo7cFRQw==} + peerDependencies: + valibot: ^1.4.0 + '@vercel/analytics@2.0.1': resolution: {integrity: sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==} peerDependencies: @@ -5408,6 +5640,20 @@ packages: vue-router: optional: true + '@vitejs/devtools-kit@0.1.24': + resolution: {integrity: sha512-sHM4i80Rrx4HTv/c2d28pQpeMz99GQe/2lVvJvna9t/YcoVouqpsms8oKiF/NcX8474A5gx3TtJHXWvqbov1dg==} + peerDependencies: + vite: '*' + + '@vitejs/devtools-rolldown@0.1.24': + resolution: {integrity: sha512-KN3Bd7O0/xAq9KKjH9R1hrbw5GK3eKq563IExw8jf/FjiTllppR/iNUnlg6JHBLG3wVBFrhxL6bHjgQUAaBDBQ==} + + '@vitejs/devtools@0.1.24': + resolution: {integrity: sha512-XPsNo8fTXtshuQrK5ddnlZzE9wtGJWs1Xfu9g1bMKZoM/XI+ool1zPSM1k8RHE09LPiUf/5E29KykResyyKx/w==} + hasBin: true + peerDependencies: + vite: '*' + '@vitejs/plugin-react@5.2.0': resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5979,6 +6225,9 @@ packages: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} + bole@5.0.29: + resolution: {integrity: sha512-eYR9i2ubLv5/4TFGyZsQ1cVH4jF9+qLJA72Aow+E7ZZQfqHqQNUZeX3w+pVWF76PQyjl5eDKf2xylyOOX76ozA==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -6039,6 +6288,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -6393,6 +6646,14 @@ packages: resolution: {integrity: sha512-1+BhOB8ahCn4O0cep0Sh2l9KCOfOdY+BXJnKMHFFzDEouSr/el18QwXEMRlOj9UY5nCeA8UN3a/82rUWRBeyBw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + data-uri-to-buffer@6.0.2: resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} engines: {node: '>= 14'} @@ -6511,6 +6772,14 @@ packages: devalue@5.8.1: resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} + devframe@0.2.2: + resolution: {integrity: sha512-nB5xJR0XREJSVD7Me7j9UUY1NIpPlBGYI/b6EMigeoVPaUv7/RwKf/uyc/94P00yMMxQzSMy/94NzWemDd70SQ==} + peerDependencies: + '@modelcontextprotocol/sdk': ^1.0.0 + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -6521,6 +6790,10 @@ packages: resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} engines: {node: '>=0.3.1'} + diff@9.0.0: + resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==} + engines: {node: '>=0.3.1'} + dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} @@ -6691,6 +6964,9 @@ packages: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} @@ -6891,6 +7167,9 @@ packages: resolution: {integrity: sha512-XXyd9d3ie/JeIIjm6WeKalvapGGFI4ShAjPJM78vgUFYzoEsuNSjvvVTuht0XZcwbVdOnEEGzhxwguRbxkIcDg==} hasBin: true + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-string-truncated-width@1.2.1: resolution: {integrity: sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow==} @@ -7148,6 +7427,16 @@ packages: h3@1.15.8: resolution: {integrity: sha512-iOH6Vl8mGd9nNfu9C0IZ+GuOAfJHcyf3VriQxWaSWIB76Fg4BnFuk4cxBxjmQSSxJS664+pgjP6e7VBnUzFfcg==} + h3@2.0.1-rc.22: + resolution: {integrity: sha512-Esv0DMIuPkCTSWCA0vO73vcTqwzH1wjSrAO1TXNu/K3up1sZHa9EKMapbmxCDYBeymC3fVTk4qxp7ogQWQ+KgA==} + engines: {node: '>=20.11.1'} + hasBin: true + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -7306,6 +7595,9 @@ packages: image-meta@0.2.2: resolution: {integrity: sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA==} + immutable@5.1.6: + resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} + impound@1.1.5: resolution: {integrity: sha512-5AUn+QE0UofqNHu5f2Skf6Svukdg4ehOIq8O0EtqIx4jta0CDZYBPqpIHt0zrlUTiFVYlLpeH39DoikXBjPKpA==} @@ -7313,6 +7605,9 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + individual@3.0.0: + resolution: {integrity: sha512-rUY5vtT748NMRbEMrTNiFfy29BgGZwGXUi2NFUVMWQrogSLzlJvQV9eeMWi+g1aVaQ53tpyLAQtd5x/JH0Nh1g==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -7361,6 +7656,9 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -7461,6 +7759,10 @@ packages: resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} engines: {node: '>=18'} + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -7526,6 +7828,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-to-typescript-lite@15.0.0: resolution: {integrity: sha512-5mMORSQm9oTLyjM4mWnyNBi2T042Fhg1/0gCIB6X8U/LVpM2A+Nmj2yEyArqVouDmFThDxpEXcnTgSrjkGJRFA==} @@ -7678,6 +7983,9 @@ packages: linebreak@1.1.0: resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} @@ -7736,6 +8044,9 @@ packages: resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} + logs-sdk@0.0.6: + resolution: {integrity: sha512-G4M1C9aLLBOIWpmw/Lqk4zrap/T2IJsoUOuUDjRcVSLy6lHQqxr3wCqIT1FvvpYTUYpEwvu4utsMY42jTNvx8Q==} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -8372,6 +8683,10 @@ packages: resolution: {integrity: sha512-l3cbgK5wUvWDVNWM/JFU77qDdGZK1wudnLsFcrRyNo/bL1CyU8pC25vDhMHikVY29lbK2InTWsX42RxVSutUdQ==} engines: {node: ^20.19.0 || >=22.12.0} + oxc-parser@0.126.0: + resolution: {integrity: sha512-FktCvLby/mOHyuijZt22+nOt10dS24gGUZE3XwIbUg7Kf4+rer3/5T7RgwzazlNuVsCjPloZ3p8E+4ONT3A8Kw==} + engines: {node: ^20.19.0 || >=22.12.0} + oxc-parser@0.128.0: resolution: {integrity: sha512-XkOw3eiIxAgQ19WRew/Bq9wc5Ga/guaWIzDBzq80z1PyuDNGvWBpPby9k6YGwV8A8uMw+Nlq3xqlzuDYmUFYUw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -8450,6 +8765,10 @@ packages: parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parse-latin@7.0.0: resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} @@ -8836,6 +9155,11 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + publint@0.3.21: + resolution: {integrity: sha512-OqejcnMV6E9zel2oCrUOJEiiFkGiAAni0A6ibfQNh1k9Gu5z4F+Yso8lllam7AzmV6Do0vp7u3UpZNRBwuXaHQ==} + engines: {node: '>=18'} + hasBin: true + pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -8854,6 +9178,9 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -8897,6 +9224,10 @@ packages: resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} engines: {node: '>=0.10.0'} + read-yaml-file@2.1.0: + resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} + engines: {node: '>=10.13'} + readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -9110,6 +9441,11 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sass@1.97.3: + resolution: {integrity: sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==} + engines: {node: '>=14.0.0'} + hasBin: true + satori@0.19.3: resolution: {integrity: sha512-dKr8TNYSyceWqBoTHWntjy25xaiWMw5GF+f8QOqFsov9OpTswLs7xdbvZudGRp9jkzbhv/4mVjVZYFtpruGKiA==} engines: {node: '>=16'} @@ -9329,6 +9665,10 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + splitpanes@4.0.4: resolution: {integrity: sha512-RbysugZhjbCw5fgplvk3hOXr41stahQDtZhHVkhnnJI6H4wlGDhM2kIpbehy7v92duy9GnMa8zIhHigIV1TWtg==} peerDependencies: @@ -9339,6 +9679,11 @@ packages: engines: {node: '>=20.16.0'} hasBin: true + srvx@0.11.16: + resolution: {integrity: sha512-bp07zRuycfTY43IjAvvTFnmnJi8ikW0VFiHwOhhYcVW/L4xQ1XY4PAd4Nuum1rsA17C39zL7x+CDhrn5AL32Rw==} + engines: {node: '>=20.16.0'} + hasBin: true + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -9400,6 +9745,13 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-comments-strings@1.2.0: + resolution: {integrity: sha512-zwF4bmnyEjZwRhaak9jUWNxc0DoeKBJ7lwSN/LEc8dQXZcUFG6auaaTQJokQWXopLdM3iTx01nQT8E4aL29DAQ==} + strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} @@ -9689,6 +10041,12 @@ packages: ultrahtml@1.6.0: resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + + unconfig@7.5.0: + resolution: {integrity: sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==} + uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} @@ -9965,6 +10323,14 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + valibot@1.4.1: + resolution: {integrity: sha512-klCmFTz2jeDluy9RwX+F884TCiogtdBJ/YaxSx1EOBYXa3NXNWj8kR1jjN8rzluwojJVWWaHJ4r1U5LfICnM3g==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -10126,6 +10492,46 @@ packages: yaml: optional: true + vite@7.3.2: + resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vite@8.0.8: resolution: {integrity: sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -10305,6 +10711,11 @@ packages: peerDependencies: typescript: '>=5.0.0' + vue-virtual-scroller@3.0.4: + resolution: {integrity: sha512-3qh3c9VUVysuXynaa4fVZ3ncx3VgD7EPRiQcj+jUVZl5u/TTkD3c27XvSEu3JGJfsJt/vVTVziZ3djiiHtW4cQ==} + peerDependencies: + vue: ^3.5.32 + vue@3.5.32: resolution: {integrity: sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==} peerDependencies: @@ -10388,6 +10799,14 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + write-yaml-file@5.0.0: + resolution: {integrity: sha512-FdNA4RyH1L43TlvGG8qOMIfcEczwA5ij+zLXUy3Z83CjxhLvcV7/Q/8pk22wnCgYw7PJhtK+7lhO+qqyT4NdvQ==} + engines: {node: '>=16.14'} + ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -10412,15 +10831,27 @@ packages: utf-8-validate: optional: true - wsl-utils@0.1.0: - resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} - engines: {node: '>=18'} - - xmlhttprequest-ssl@2.1.2: - resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} - engines: {node: '>=0.4.0'} - - xss@1.0.15: + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + xss@1.0.15: resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} engines: {node: '>= 0.10.0'} hasBin: true @@ -10758,17 +11189,17 @@ snapshots: dependencies: prismjs: 1.30.0 - '@astrojs/react@5.0.3(@types/node@25.5.2)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.6.1)(lightningcss@1.32.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)': + '@astrojs/react@5.0.3(@types/node@25.5.2)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.6.1)(lightningcss@1.32.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)': dependencies: '@astrojs/internal-helpers': 0.8.0 '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@vitejs/plugin-react': 5.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@vitejs/plugin-react': 5.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) devalue: 5.6.4 react: 19.2.5 react-dom: 19.2.5(react@19.2.5) ultrahtml: 1.6.0 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) transitivePeerDependencies: - '@types/node' - jiti @@ -11378,6 +11809,8 @@ snapshots: - '@vue/composition-api' - vue + '@gwhitney/detect-indent@7.0.1': {} + '@hono/node-server@1.19.11(hono@4.12.5)': dependencies: hono: 4.12.5 @@ -11850,6 +12283,13 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)': + dependencies: + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 + '@tybys/wasm-util': 0.10.1 + optional: true + '@next/env@16.2.3': {} '@next/swc-darwin-arm64@16.2.3': @@ -11930,7 +12370,7 @@ snapshots: - magicast - supports-color - '@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2)': + '@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3))': dependencies: '@nuxt/kit': 4.4.4(magicast@0.5.2) '@nuxtjs/mdc': 0.21.1(magicast@0.5.2) @@ -11980,7 +12420,9 @@ snapshots: zod: 3.25.76 zod-to-json-schema: 3.25.2(zod@3.25.76) optionalDependencies: + '@valibot/to-json-schema': 1.7.0(valibot@1.4.1(typescript@5.9.3)) better-sqlite3: 12.6.2 + valibot: 1.4.1(typescript@5.9.3) transitivePeerDependencies: - bufferutil - drizzle-orm @@ -11991,27 +12433,27 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@3.2.4(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@nuxt/devtools-kit@3.2.4(magicast@0.5.2)(vite@8.0.8)': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) execa: 8.0.1 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) transitivePeerDependencies: - magicast - '@nuxt/devtools-kit@4.0.0-alpha.3(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@nuxt/devtools-kit@4.0.0-alpha.3(magicast@0.5.2)(vite@8.0.8)': dependencies: '@nuxt/kit': 4.4.4(magicast@0.5.2) tinyexec: 1.1.2 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) transitivePeerDependencies: - magicast - '@nuxt/devtools-kit@4.0.0-alpha.4(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@nuxt/devtools-kit@4.0.0-alpha.4(magicast@0.5.2)(vite@8.0.8)': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) tinyexec: 1.1.1 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) transitivePeerDependencies: - magicast @@ -12026,9 +12468,9 @@ snapshots: pkg-types: 2.3.0 semver: 7.7.4 - '@nuxt/devtools@3.2.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))': + '@nuxt/devtools@3.2.4(@vitejs/devtools@0.1.24)(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))': dependencies: - '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@8.0.8) '@nuxt/devtools-wizard': 3.2.4 '@nuxt/kit': 4.4.2(magicast@0.5.2) '@vue/devtools-core': 8.1.0(vue@3.5.32(typescript@5.9.3)) @@ -12056,24 +12498,26 @@ snapshots: sirv: 3.0.2 structured-clone-es: 2.0.0 tinyglobby: 0.2.15 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vite-plugin-inspect: 11.3.3(@nuxt/kit@4.4.2(magicast@0.5.2))(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - vite-plugin-vue-tracer: 1.3.0(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite-plugin-inspect: 11.3.3(@nuxt/kit@4.4.2(magicast@0.5.2))(vite@8.0.8) + vite-plugin-vue-tracer: 1.3.0(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) which: 6.0.1 ws: 8.19.0 + optionalDependencies: + '@vitejs/devtools': 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@pnpm/logger@1001.0.1)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(typescript@5.9.3)(vite@8.0.8) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - vue - '@nuxt/fonts@0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@nuxt/fonts@0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8)': dependencies: - '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@8.0.8) '@nuxt/kit': 4.4.4(magicast@0.5.2) consola: 3.4.2 defu: 6.1.7 - fontless: 0.2.1(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + fontless: 0.2.1(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(vite@8.0.8) h3: 1.15.11 magic-regexp: 0.10.0 ofetch: 1.5.1 @@ -12107,13 +12551,13 @@ snapshots: - uploadthing - vite - '@nuxt/icon@2.2.1(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))': + '@nuxt/icon@2.2.1(magicast@0.5.2)(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))': dependencies: '@iconify/collections': 1.0.657 '@iconify/types': 2.0.0 '@iconify/utils': 3.1.0 '@iconify/vue': 5.0.0(vue@3.5.32(typescript@5.9.3)) - '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@8.0.8) '@nuxt/kit': 4.4.4(magicast@0.5.2) consola: 3.4.2 local-pkg: 1.1.2 @@ -12240,7 +12684,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/nitro-server@4.4.2(@babel/core@7.29.0)(better-sqlite3@12.6.2)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(rolldown@1.0.0-rc.15)(typescript@5.9.3)': + '@nuxt/nitro-server@4.4.2(@babel/core@7.29.0)(better-sqlite3@12.6.2)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(rolldown@1.0.0-rc.15)(typescript@5.9.3)': dependencies: '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) '@nuxt/devalue': 2.0.2 @@ -12259,7 +12703,7 @@ snapshots: klona: 2.0.6 mocked-exports: 0.1.1 nitropack: 2.13.1(better-sqlite3@12.6.2)(rolldown@1.0.0-rc.15) - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) nypm: 0.6.5 ohash: 2.0.11 pathe: 2.0.3 @@ -12326,18 +12770,18 @@ snapshots: rc9: 3.0.0 std-env: 3.10.0 - '@nuxt/ui@4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3)': + '@nuxt/ui@4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3)': dependencies: '@floating-ui/dom': 1.7.6 '@iconify/vue': 5.0.0(vue@3.5.32(typescript@5.9.3)) - '@nuxt/fonts': 0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - '@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + '@nuxt/fonts': 0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8) + '@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) '@nuxt/kit': 4.4.4(magicast@0.5.2) '@nuxt/schema': 4.4.2 '@nuxtjs/color-mode': 3.5.2(magicast@0.5.2) '@standard-schema/spec': 1.1.0 '@tailwindcss/postcss': 4.2.4 - '@tailwindcss/vite': 4.2.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@tailwindcss/vite': 4.2.4(vite@8.0.8) '@tanstack/vue-table': 8.21.3(vue@3.5.32(typescript@5.9.3)) '@tanstack/vue-virtual': 3.13.24(vue@3.5.32(typescript@5.9.3)) '@tiptap/core': 3.22.5(@tiptap/pm@3.22.5) @@ -12395,7 +12839,8 @@ snapshots: optionalDependencies: '@internationalized/date': 3.12.0 '@internationalized/number': 3.6.5 - '@nuxt/content': 3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2) + '@nuxt/content': 3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)) + valibot: 1.4.1(typescript@5.9.3) vue-router: 4.6.4(vue@3.5.32(typescript@5.9.3)) zod: 4.4.3 transitivePeerDependencies: @@ -12440,18 +12885,18 @@ snapshots: - vue - yjs - '@nuxt/ui@4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3)': + '@nuxt/ui@4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.2)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3)': dependencies: '@floating-ui/dom': 1.7.6 '@iconify/vue': 5.0.0(vue@3.5.32(typescript@5.9.3)) - '@nuxt/fonts': 0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - '@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + '@nuxt/fonts': 0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8) + '@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) '@nuxt/kit': 4.4.4(magicast@0.5.2) '@nuxt/schema': 4.4.2 '@nuxtjs/color-mode': 3.5.2(magicast@0.5.2) '@standard-schema/spec': 1.1.0 '@tailwindcss/postcss': 4.2.4 - '@tailwindcss/vite': 4.2.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@tailwindcss/vite': 4.2.4(vite@8.0.8) '@tanstack/vue-table': 8.21.3(vue@3.5.32(typescript@5.9.3)) '@tanstack/vue-virtual': 3.13.24(vue@3.5.32(typescript@5.9.3)) '@tiptap/core': 3.22.5(@tiptap/pm@3.22.5) @@ -12509,7 +12954,8 @@ snapshots: optionalDependencies: '@internationalized/date': 3.12.0 '@internationalized/number': 3.6.5 - '@nuxt/content': 3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2) + '@nuxt/content': 3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)) + valibot: 1.4.1(typescript@5.9.3) vue-router: 5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)) zod: 4.4.3 transitivePeerDependencies: @@ -12554,18 +13000,18 @@ snapshots: - vue - yjs - '@nuxt/ui@4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3)': + '@nuxt/ui@4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3)': dependencies: '@floating-ui/dom': 1.7.6 '@iconify/vue': 5.0.0(vue@3.5.32(typescript@5.9.3)) - '@nuxt/fonts': 0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - '@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + '@nuxt/fonts': 0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8) + '@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) '@nuxt/kit': 4.4.4(magicast@0.5.2) '@nuxt/schema': 4.4.2 '@nuxtjs/color-mode': 3.5.2(magicast@0.5.2) '@standard-schema/spec': 1.1.0 '@tailwindcss/postcss': 4.2.4 - '@tailwindcss/vite': 4.2.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@tailwindcss/vite': 4.2.4(vite@8.0.8) '@tanstack/vue-table': 8.21.3(vue@3.5.32(typescript@5.9.3)) '@tanstack/vue-virtual': 3.13.24(vue@3.5.32(typescript@5.9.3)) '@tiptap/core': 3.22.5(@tiptap/pm@3.22.5) @@ -12623,7 +13069,8 @@ snapshots: optionalDependencies: '@internationalized/date': 3.12.0 '@internationalized/number': 3.6.5 - '@nuxt/content': 3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2) + '@nuxt/content': 3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)) + valibot: 1.4.1(typescript@5.9.3) vue-router: 4.6.4(vue@3.5.32(typescript@5.9.3)) zod: 4.4.3 transitivePeerDependencies: @@ -12668,18 +13115,18 @@ snapshots: - vue - yjs - '@nuxt/ui@4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3)': + '@nuxt/ui@4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3)': dependencies: '@floating-ui/dom': 1.7.6 '@iconify/vue': 5.0.0(vue@3.5.32(typescript@5.9.3)) - '@nuxt/fonts': 0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - '@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + '@nuxt/fonts': 0.14.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(vite@8.0.8) + '@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) '@nuxt/kit': 4.4.4(magicast@0.5.2) '@nuxt/schema': 4.4.2 '@nuxtjs/color-mode': 3.5.2(magicast@0.5.2) '@standard-schema/spec': 1.1.0 '@tailwindcss/postcss': 4.2.4 - '@tailwindcss/vite': 4.2.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@tailwindcss/vite': 4.2.4(vite@8.0.8) '@tanstack/vue-table': 8.21.3(vue@3.5.32(typescript@5.9.3)) '@tanstack/vue-virtual': 3.13.24(vue@3.5.32(typescript@5.9.3)) '@tiptap/core': 3.22.5(@tiptap/pm@3.22.5) @@ -12737,7 +13184,8 @@ snapshots: optionalDependencies: '@internationalized/date': 3.12.0 '@internationalized/number': 3.6.5 - '@nuxt/content': 3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2) + '@nuxt/content': 3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)) + valibot: 1.4.1(typescript@5.9.3) vue-router: 5.0.4(@vue/compiler-sfc@3.5.33)(vue@3.5.32(typescript@5.9.3)) zod: 4.4.3 transitivePeerDependencies: @@ -12782,12 +13230,12 @@ snapshots: - vue - yjs - '@nuxt/vite-builder@4.4.2(16010c41461ccb0f6d66aa3ed9aadfa5)': + '@nuxt/vite-builder@4.4.2(61a0d016d92e24b83f8cc63bc530fd72)': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) '@rollup/plugin-replace': 6.0.3(rollup@4.59.0) - '@vitejs/plugin-vue': 6.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) - '@vitejs/plugin-vue-jsx': 5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.5(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': 5.1.4(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) autoprefixer: 10.4.27(postcss@8.5.9) consola: 3.4.2 cssnano: 7.1.3(postcss@8.5.9) @@ -12800,7 +13248,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.2 mocked-exports: 0.1.1 - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) nypm: 0.6.5 pathe: 2.0.3 pkg-types: 2.3.0 @@ -12809,9 +13257,9 @@ snapshots: std-env: 4.0.0 ufo: 1.6.4 unenv: 2.0.0-rc.24 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vite-node: 5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vite-plugin-checker: 0.12.0(eslint@10.2.0(jiti@2.6.1))(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3)) + vite: 7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite-node: 5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite-plugin-checker: 0.12.0(eslint@10.2.0(jiti@2.6.1))(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(typescript@5.9.3)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3)) vue: 3.5.32(typescript@5.9.3) vue-bundle-renderer: 2.2.0 optionalDependencies: @@ -12914,11 +13362,11 @@ snapshots: - uploadthing - vue - '@nuxtjs/mcp-toolkit@0.13.4(h3@1.15.11)(magicast@0.5.2)(zod@4.4.3)': + '@nuxtjs/mcp-toolkit@0.13.4(h3@2.0.1-rc.22)(magicast@0.5.2)(zod@4.4.3)': dependencies: '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) '@nuxt/kit': 4.4.2(magicast@0.5.2) - h3: 1.15.11 + h3: 2.0.1-rc.22 tinyglobby: 0.2.15 zod: 4.4.3 transitivePeerDependencies: @@ -12976,15 +13424,15 @@ snapshots: - magicast - supports-color - '@nuxtjs/robots@6.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))(zod@4.4.3)': + '@nuxtjs/robots@6.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))(zod@4.4.3)': dependencies: '@fingerprintjs/botd': 2.0.0 '@nuxt/kit': 4.4.4(magicast@0.5.2) consola: 3.4.2 defu: 6.1.7 h3: 1.15.11 - nuxt-site-config: 4.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))(zod@4.4.3) - nuxtseo-shared: 5.1.3(89f4238dd5c0bee98b97096b2ddd1a18) + nuxt-site-config: 4.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))(zod@4.4.3) + nuxtseo-shared: 5.1.3(7b50e1790f19ed56ed72494e091cb5f9) pathe: 2.0.3 pkg-types: 2.3.1 ufo: 1.6.4 @@ -13134,6 +13582,9 @@ snapshots: '@oxc-parser/binding-android-arm-eabi@0.117.0': optional: true + '@oxc-parser/binding-android-arm-eabi@0.126.0': + optional: true + '@oxc-parser/binding-android-arm-eabi@0.128.0': optional: true @@ -13143,6 +13594,9 @@ snapshots: '@oxc-parser/binding-android-arm64@0.117.0': optional: true + '@oxc-parser/binding-android-arm64@0.126.0': + optional: true + '@oxc-parser/binding-android-arm64@0.128.0': optional: true @@ -13152,6 +13606,9 @@ snapshots: '@oxc-parser/binding-darwin-arm64@0.117.0': optional: true + '@oxc-parser/binding-darwin-arm64@0.126.0': + optional: true + '@oxc-parser/binding-darwin-arm64@0.128.0': optional: true @@ -13161,6 +13618,9 @@ snapshots: '@oxc-parser/binding-darwin-x64@0.117.0': optional: true + '@oxc-parser/binding-darwin-x64@0.126.0': + optional: true + '@oxc-parser/binding-darwin-x64@0.128.0': optional: true @@ -13170,6 +13630,9 @@ snapshots: '@oxc-parser/binding-freebsd-x64@0.117.0': optional: true + '@oxc-parser/binding-freebsd-x64@0.126.0': + optional: true + '@oxc-parser/binding-freebsd-x64@0.128.0': optional: true @@ -13179,6 +13642,9 @@ snapshots: '@oxc-parser/binding-linux-arm-gnueabihf@0.117.0': optional: true + '@oxc-parser/binding-linux-arm-gnueabihf@0.126.0': + optional: true + '@oxc-parser/binding-linux-arm-gnueabihf@0.128.0': optional: true @@ -13188,6 +13654,9 @@ snapshots: '@oxc-parser/binding-linux-arm-musleabihf@0.117.0': optional: true + '@oxc-parser/binding-linux-arm-musleabihf@0.126.0': + optional: true + '@oxc-parser/binding-linux-arm-musleabihf@0.128.0': optional: true @@ -13197,6 +13666,9 @@ snapshots: '@oxc-parser/binding-linux-arm64-gnu@0.117.0': optional: true + '@oxc-parser/binding-linux-arm64-gnu@0.126.0': + optional: true + '@oxc-parser/binding-linux-arm64-gnu@0.128.0': optional: true @@ -13206,6 +13678,9 @@ snapshots: '@oxc-parser/binding-linux-arm64-musl@0.117.0': optional: true + '@oxc-parser/binding-linux-arm64-musl@0.126.0': + optional: true + '@oxc-parser/binding-linux-arm64-musl@0.128.0': optional: true @@ -13215,6 +13690,9 @@ snapshots: '@oxc-parser/binding-linux-ppc64-gnu@0.117.0': optional: true + '@oxc-parser/binding-linux-ppc64-gnu@0.126.0': + optional: true + '@oxc-parser/binding-linux-ppc64-gnu@0.128.0': optional: true @@ -13224,6 +13702,9 @@ snapshots: '@oxc-parser/binding-linux-riscv64-gnu@0.117.0': optional: true + '@oxc-parser/binding-linux-riscv64-gnu@0.126.0': + optional: true + '@oxc-parser/binding-linux-riscv64-gnu@0.128.0': optional: true @@ -13233,6 +13714,9 @@ snapshots: '@oxc-parser/binding-linux-riscv64-musl@0.117.0': optional: true + '@oxc-parser/binding-linux-riscv64-musl@0.126.0': + optional: true + '@oxc-parser/binding-linux-riscv64-musl@0.128.0': optional: true @@ -13242,6 +13726,9 @@ snapshots: '@oxc-parser/binding-linux-s390x-gnu@0.117.0': optional: true + '@oxc-parser/binding-linux-s390x-gnu@0.126.0': + optional: true + '@oxc-parser/binding-linux-s390x-gnu@0.128.0': optional: true @@ -13251,6 +13738,9 @@ snapshots: '@oxc-parser/binding-linux-x64-gnu@0.117.0': optional: true + '@oxc-parser/binding-linux-x64-gnu@0.126.0': + optional: true + '@oxc-parser/binding-linux-x64-gnu@0.128.0': optional: true @@ -13260,6 +13750,9 @@ snapshots: '@oxc-parser/binding-linux-x64-musl@0.117.0': optional: true + '@oxc-parser/binding-linux-x64-musl@0.126.0': + optional: true + '@oxc-parser/binding-linux-x64-musl@0.128.0': optional: true @@ -13269,6 +13762,9 @@ snapshots: '@oxc-parser/binding-openharmony-arm64@0.117.0': optional: true + '@oxc-parser/binding-openharmony-arm64@0.126.0': + optional: true + '@oxc-parser/binding-openharmony-arm64@0.128.0': optional: true @@ -13285,6 +13781,13 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.1 optional: true + '@oxc-parser/binding-wasm32-wasi@0.126.0': + dependencies: + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + optional: true + '@oxc-parser/binding-wasm32-wasi@0.128.0': dependencies: '@emnapi/core': 1.10.0 @@ -13298,6 +13801,9 @@ snapshots: '@oxc-parser/binding-win32-arm64-msvc@0.117.0': optional: true + '@oxc-parser/binding-win32-arm64-msvc@0.126.0': + optional: true + '@oxc-parser/binding-win32-arm64-msvc@0.128.0': optional: true @@ -13307,6 +13813,9 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc@0.117.0': optional: true + '@oxc-parser/binding-win32-ia32-msvc@0.126.0': + optional: true + '@oxc-parser/binding-win32-ia32-msvc@0.128.0': optional: true @@ -13316,6 +13825,9 @@ snapshots: '@oxc-parser/binding-win32-x64-msvc@0.117.0': optional: true + '@oxc-parser/binding-win32-x64-msvc@0.126.0': + optional: true + '@oxc-parser/binding-win32-x64-msvc@0.128.0': optional: true @@ -13325,6 +13837,8 @@ snapshots: '@oxc-project/types@0.124.0': {} + '@oxc-project/types@0.126.0': {} + '@oxc-project/types@0.128.0': {} '@oxc-transform/binding-android-arm-eabi@0.112.0': @@ -13642,6 +14156,70 @@ snapshots: dependencies: playwright: 1.58.2 + '@pnpm/constants@1001.3.1': {} + + '@pnpm/core-loggers@1001.0.9(@pnpm/logger@1001.0.1)': + dependencies: + '@pnpm/logger': 1001.0.1 + '@pnpm/types': 1001.3.0 + + '@pnpm/error@1000.1.0': + dependencies: + '@pnpm/constants': 1001.3.1 + + '@pnpm/graceful-fs@1000.1.0': + dependencies: + graceful-fs: 4.2.11 + + '@pnpm/logger@1001.0.1': + dependencies: + bole: 5.0.29 + split2: 4.2.0 + + '@pnpm/manifest-utils@1002.0.5(@pnpm/logger@1001.0.1)': + dependencies: + '@pnpm/core-loggers': 1001.0.9(@pnpm/logger@1001.0.1) + '@pnpm/error': 1000.1.0 + '@pnpm/logger': 1001.0.1 + '@pnpm/semver.peer-range': 1000.0.0 + '@pnpm/types': 1001.3.0 + semver: 7.7.4 + + '@pnpm/read-project-manifest@1001.2.6(@pnpm/logger@1001.0.1)': + dependencies: + '@gwhitney/detect-indent': 7.0.1 + '@pnpm/error': 1000.1.0 + '@pnpm/graceful-fs': 1000.1.0 + '@pnpm/logger': 1001.0.1 + '@pnpm/manifest-utils': 1002.0.5(@pnpm/logger@1001.0.1) + '@pnpm/text.comments-parser': 1000.0.0 + '@pnpm/types': 1001.3.0 + '@pnpm/write-project-manifest': 1000.0.16 + fast-deep-equal: 3.1.3 + is-windows: 1.0.2 + json5: 2.2.3 + parse-json: 5.2.0 + read-yaml-file: 2.1.0 + strip-bom: 4.0.0 + + '@pnpm/semver.peer-range@1000.0.0': + dependencies: + semver: 7.7.4 + + '@pnpm/text.comments-parser@1000.0.0': + dependencies: + strip-comments-strings: 1.2.0 + + '@pnpm/types@1001.3.0': {} + + '@pnpm/write-project-manifest@1000.0.16': + dependencies: + '@pnpm/text.comments-parser': 1000.0.0 + '@pnpm/types': 1001.3.0 + json5: 2.2.3 + write-file-atomic: 5.0.1 + write-yaml-file: 5.0.0 + '@polka/url@1.0.0-next.29': {} '@poppinss/colors@4.1.6': @@ -13656,6 +14234,12 @@ snapshots: '@poppinss/exception@1.2.3': {} + '@publint/pack@0.1.4': {} + + '@quansync/fs@1.0.0': + dependencies: + quansync: 1.0.0 + '@release-it/conventional-changelog@10.0.6(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.3.0)(release-it@19.2.4(@types/node@25.5.2)(magicast@0.5.2))': dependencies: '@conventional-changelog/git-client': 2.6.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.3.0) @@ -13774,6 +14358,8 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.0.0-rc.15': optional: true + '@rolldown/debug@1.0.3': {} + '@rolldown/pluginutils@1.0.0-rc.15': {} '@rolldown/pluginutils@1.0.0-rc.2': {} @@ -14064,15 +14650,15 @@ snapshots: dependencies: acorn: 8.16.0 - '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': + '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8))': dependencies: - '@sveltejs/kit': 2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@sveltejs/kit': 2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8) - '@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8)': dependencies: '@standard-schema/spec': 1.1.0 '@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0) - '@sveltejs/vite-plugin-svelte': 7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@sveltejs/vite-plugin-svelte': 7.0.0(svelte@5.55.2)(vite@8.0.8) '@types/cookie': 0.6.0 acorn: 8.16.0 cookie: 0.6.0 @@ -14084,7 +14670,7 @@ snapshots: set-cookie-parser: 3.1.0 sirv: 3.0.2 svelte: 5.55.2 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) optionalDependencies: '@opentelemetry/api': 1.9.0 typescript: 5.9.3 @@ -14100,14 +14686,14 @@ snapshots: transitivePeerDependencies: - typescript - '@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8)': dependencies: deepmerge: 4.3.1 magic-string: 0.30.21 obug: 2.1.1 svelte: 5.55.2 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vitefu: 1.1.2(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vitefu: 1.1.2(vite@8.0.8) '@swc/helpers@0.5.15': dependencies: @@ -14255,19 +14841,19 @@ snapshots: postcss: 8.5.14 tailwindcss: 4.2.4 - '@tailwindcss/vite@4.2.2(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@tailwindcss/vite@4.2.2(vite@8.0.8)': dependencies: '@tailwindcss/node': 4.2.2 '@tailwindcss/oxide': 4.2.2 tailwindcss: 4.2.2 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - '@tailwindcss/vite@4.2.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@tailwindcss/vite@4.2.4(vite@8.0.8)': dependencies: '@tailwindcss/node': 4.2.4 '@tailwindcss/oxide': 4.2.4 tailwindcss: 4.2.4 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) '@takumi-rs/core-darwin-arm64@1.1.2': optional: true @@ -14719,11 +15305,15 @@ snapshots: unhead: 2.1.12 vue: 3.5.32(typescript@5.9.3) - '@vercel/analytics@2.0.1(48d27f7e56382153c2674fe90a76fbae)': - optionalDependencies: - '@sveltejs/kit': 2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + '@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3))': + dependencies: + valibot: 1.4.1(typescript@5.9.3) + + ? '@vercel/analytics@2.0.1(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3))(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(react@19.2.5)(svelte@5.55.2)(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))' + : optionalDependencies: + '@sveltejs/kit': 2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8) + next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) react: 19.2.5 svelte: 5.55.2 vue: 3.5.32(typescript@5.9.3) @@ -14750,17 +15340,132 @@ snapshots: '@vercel/oidc@3.2.0': {} - '@vercel/speed-insights@2.0.0(48d27f7e56382153c2674fe90a76fbae)': - optionalDependencies: - '@sveltejs/kit': 2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + ? '@vercel/speed-insights@2.0.0(@sveltejs/kit@2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8))(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3))(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(react@19.2.5)(svelte@5.55.2)(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))' + : optionalDependencies: + '@sveltejs/kit': 2.60.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.2)(vite@8.0.8))(svelte@5.55.2)(typescript@5.9.3)(vite@8.0.8) + next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) react: 19.2.5 svelte: 5.55.2 vue: 3.5.32(typescript@5.9.3) vue-router: 4.6.4(vue@3.5.32(typescript@5.9.3)) - '@vitejs/plugin-react@5.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@vitejs/devtools-kit@0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(typescript@5.9.3)(vite@8.0.8)': + dependencies: + birpc: 4.0.0 + devframe: 0.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(typescript@5.9.3) + logs-sdk: 0.0.6 + mlly: 1.8.2 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + tinyexec: 1.1.2 + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - crossws + - typescript + - utf-8-validate + + '@vitejs/devtools-rolldown@0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@pnpm/logger@1001.0.1)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(typescript@5.9.3)(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@floating-ui/dom': 1.7.6 + '@pnpm/read-project-manifest': 1001.2.6(@pnpm/logger@1001.0.1) + '@rolldown/debug': 1.0.3 + '@vitejs/devtools-kit': 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(typescript@5.9.3)(vite@8.0.8) + birpc: 4.0.0 + cac: 7.0.0 + d3-shape: 3.2.0 + devframe: 0.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(typescript@5.9.3) + diff: 9.0.0 + get-port-please: 3.2.0 + h3: 2.0.1-rc.22 + logs-sdk: 0.0.6 + mlly: 1.8.2 + mrmime: 2.0.1 + p-limit: 7.3.0 + pathe: 2.0.3 + publint: 0.3.21 + split2: 4.2.0 + tinyglobby: 0.2.16 + unconfig: 7.5.0 + unstorage: 1.17.5(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0) + vue-virtual-scroller: 3.0.4(vue@3.5.32(typescript@5.9.3)) + ws: 8.21.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@modelcontextprotocol/sdk' + - '@netlify/blobs' + - '@planetscale/database' + - '@pnpm/logger' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - crossws + - db0 + - idb-keyval + - ioredis + - typescript + - uploadthing + - utf-8-validate + - vite + - vue + + '@vitejs/devtools@0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@pnpm/logger@1001.0.1)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(typescript@5.9.3)(vite@8.0.8)': + dependencies: + '@vitejs/devtools-kit': 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(typescript@5.9.3)(vite@8.0.8) + '@vitejs/devtools-rolldown': 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@pnpm/logger@1001.0.1)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(typescript@5.9.3)(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) + birpc: 4.0.0 + cac: 7.0.0 + devframe: 0.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(typescript@5.9.3) + h3: 2.0.1-rc.22 + logs-sdk: 0.0.6 + mlly: 1.8.2 + obug: 2.1.1 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + tinyexec: 1.1.2 + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vue: 3.5.32(typescript@5.9.3) + ws: 8.21.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@modelcontextprotocol/sdk' + - '@netlify/blobs' + - '@planetscale/database' + - '@pnpm/logger' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - crossws + - db0 + - idb-keyval + - ioredis + - typescript + - uploadthing + - utf-8-validate + + '@vitejs/plugin-react@5.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -14768,67 +15473,67 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@6.0.1(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@vitejs/plugin-react@6.0.1(vite@8.0.8)': dependencies: '@rolldown/pluginutils': 1.0.0-rc.7 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - '@vitejs/plugin-vue-jsx@5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))': + '@vitejs/plugin-vue-jsx@5.1.4(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) '@rolldown/pluginutils': 1.0.0-rc.15 '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.0) - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue: 3.5.32(typescript@5.9.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(terser@5.46.0))(vue@3.5.32(typescript@5.9.3))': + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0))(vue@3.5.32(typescript@5.9.3))': dependencies: - vite: 5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(terser@5.46.0) + vite: 5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0) vue: 3.5.32(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.5(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.2 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue: 3.5.32(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.5(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.5(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.2 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue: 3.5.32(typescript@5.9.3) - '@vitest/browser-playwright@4.1.4(playwright@1.59.1)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.4)': + '@vitest/browser-playwright@4.1.4(playwright@1.59.1)(vite@8.0.8)(vitest@4.1.4)': dependencies: - '@vitest/browser': 4.1.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.4) - '@vitest/mocker': 4.1.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@vitest/browser': 4.1.4(vite@8.0.8)(vitest@4.1.4) + '@vitest/mocker': 4.1.4(vite@8.0.8) playwright: 1.59.1 tinyrainbow: 3.1.0 - vitest: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + vitest: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.4)': + '@vitest/browser@4.1.4(vite@8.0.8)(vitest@4.1.4)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@vitest/mocker': 4.1.4(vite@8.0.8) '@vitest/utils': 4.1.4 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + vitest: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) ws: 8.19.0 transitivePeerDependencies: - bufferutil @@ -14845,13 +15550,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))': + '@vitest/mocker@4.1.4(vite@8.0.8)': dependencies: '@vitest/spy': 4.1.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) '@vitest/pretty-format@4.1.4': dependencies: @@ -15287,7 +15992,7 @@ snapshots: '@babel/parser': 7.29.2 ast-kit: 2.2.0 - astro@6.1.5(@types/node@25.5.2)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.4): + astro@6.1.5(@types/node@25.5.2)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.4): dependencies: '@astrojs/compiler': 3.0.1 '@astrojs/internal-helpers': 0.8.0 @@ -15338,8 +16043,8 @@ snapshots: unist-util-visit: 5.1.0 unstorage: 1.17.5(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0) vfile: 6.0.3 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vitefu: 1.1.2(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vitefu: 1.1.2(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.4.3 @@ -15495,6 +16200,11 @@ snapshots: transitivePeerDependencies: - supports-color + bole@5.0.29: + dependencies: + fast-safe-stringify: 2.1.1 + individual: 3.0.0 + boolbase@1.0.0: {} brace-expansion@2.0.2: @@ -15573,6 +16283,8 @@ snapshots: cac@6.7.14: {} + cac@7.0.0: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -15927,6 +16639,12 @@ snapshots: culori@4.0.2: {} + d3-path@3.1.0: {} + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + data-uri-to-buffer@6.0.2: {} db0@0.3.4(better-sqlite3@12.6.2): @@ -15998,6 +16716,25 @@ snapshots: devalue@5.8.1: {} + devframe@0.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(typescript@5.9.3): + dependencies: + '@valibot/to-json-schema': 1.7.0(valibot@1.4.1(typescript@5.9.3)) + birpc: 4.0.0 + cac: 7.0.0 + h3: 2.0.1-rc.22 + logs-sdk: 0.0.6 + mrmime: 2.0.1 + pathe: 2.0.3 + valibot: 1.4.1(typescript@5.9.3) + ws: 8.21.0 + optionalDependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) + transitivePeerDependencies: + - bufferutil + - crossws + - typescript + - utf-8-validate + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -16006,9 +16743,11 @@ snapshots: diff@8.0.3: {} + diff@9.0.0: {} + dlv@1.1.3: {} - docus@5.11.0(b927d32a0b0e4e2a56ba354600fb5b9f): + docus@5.11.0(4c350040598c37923cc896b5f7dc5856): dependencies: '@ai-sdk/gateway': 3.0.116(zod@4.4.3) '@ai-sdk/mcp': 1.0.39(zod@4.4.3) @@ -16016,14 +16755,14 @@ snapshots: '@iconify-json/lucide': 1.2.105 '@iconify-json/simple-icons': 1.2.80 '@iconify-json/vscode-icons': 1.2.45 - '@nuxt/content': 3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2) + '@nuxt/content': 3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)) '@nuxt/image': 2.0.0(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2) '@nuxt/kit': 4.4.4(magicast@0.5.2) - '@nuxt/ui': 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(better-sqlite3@12.6.2)(magicast@0.5.2))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) + '@nuxt/ui': 4.7.1(@internationalized/date@3.12.0)(@internationalized/number@3.6.5)(@nuxt/content@3.13.0(@valibot/to-json-schema@1.7.0(valibot@1.4.1(typescript@5.9.3)))(better-sqlite3@12.6.2)(magicast@0.5.2)(valibot@1.4.1(typescript@5.9.3)))(@tiptap/extensions@3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)(y-protocols@1.0.7(yjs@13.6.29))(yjs@13.6.29))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.6.2))(embla-carousel@8.6.0)(focus-trap@7.8.0)(ioredis@5.10.0)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tailwindcss@4.2.4)(typescript@5.9.3)(valibot@1.4.1(typescript@5.9.3))(vite@8.0.8)(vue-router@4.6.4(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))(yjs@13.6.29)(zod@4.4.3) '@nuxtjs/i18n': 10.2.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@vue/compiler-dom@3.5.33)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(magicast@0.5.2)(rollup@4.59.0)(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)) - '@nuxtjs/mcp-toolkit': 0.13.4(h3@1.15.11)(magicast@0.5.2)(zod@4.4.3) + '@nuxtjs/mcp-toolkit': 0.13.4(h3@2.0.1-rc.22)(magicast@0.5.2)(zod@4.4.3) '@nuxtjs/mdc': 0.21.1(magicast@0.5.2) - '@nuxtjs/robots': 6.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))(zod@4.4.3) + '@nuxtjs/robots': 6.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))(zod@4.4.3) '@shikijs/core': 4.0.2 '@shikijs/engine-javascript': 4.0.2 '@shikijs/langs': 4.0.2 @@ -16036,9 +16775,9 @@ snapshots: exsolve: 1.0.8 git-url-parse: 16.1.0 motion-v: 2.2.1(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vue@3.5.32(typescript@5.9.3)) - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) nuxt-llms: 0.2.0(magicast@0.5.2) - nuxt-og-image: 6.5.0(e7d4dc8e55e2ab84f05f794bb3a537a6) + nuxt-og-image: 6.5.0(e6b246098479abe5cf3d793933821ae3) pathe: 2.0.3 pkg-types: 2.3.1 scule: 1.3.0 @@ -16279,6 +17018,10 @@ snapshots: entities@8.0.0: {} + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + error-stack-parser-es@1.0.5: {} errx@0.1.0: {} @@ -16580,6 +17323,8 @@ snapshots: fast-npm-meta@1.4.2: {} + fast-safe-stringify@2.1.1: {} + fast-string-truncated-width@1.2.1: {} fast-string-truncated-width@3.0.3: {} @@ -16676,7 +17421,7 @@ snapshots: dependencies: tiny-inflate: 1.0.3 - fontless@0.2.1(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)): + fontless@0.2.1(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(vite@8.0.8): dependencies: consola: 3.4.2 css-tree: 3.2.1 @@ -16692,7 +17437,7 @@ snapshots: unifont: 0.7.4 unstorage: 1.17.5(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0) optionalDependencies: - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -16888,6 +17633,11 @@ snapshots: ufo: 1.6.4 uncrypto: 0.1.3 + h3@2.0.1-rc.22: + dependencies: + rou3: 0.8.1 + srvx: 0.11.16 + handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -17134,6 +17884,9 @@ snapshots: image-meta@0.2.2: {} + immutable@5.1.6: + optional: true + impound@1.1.5: dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -17144,6 +17897,8 @@ snapshots: imurmurhash@0.1.4: {} + individual@3.0.0: {} + inherits@2.0.4: {} ini@1.3.8: {} @@ -17231,6 +17986,8 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 + is-arrayish@0.2.1: {} + is-callable@1.2.7: {} is-core-module@2.16.1: @@ -17300,6 +18057,8 @@ snapshots: is-what@5.5.0: {} + is-windows@1.0.2: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 @@ -17366,6 +18125,8 @@ snapshots: json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-to-typescript-lite@15.0.0: dependencies: '@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15) @@ -17492,6 +18253,8 @@ snapshots: unicode-trie: 2.0.0 optional: true + lines-and-columns@1.2.4: {} + linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 @@ -17558,6 +18321,12 @@ snapshots: is-unicode-supported: 2.1.0 yoctocolors: 2.1.2 + logs-sdk@0.0.6: + dependencies: + magic-string: 0.30.21 + oxc-parser: 0.126.0 + unplugin: 3.0.0 + longest-streak@3.1.0: {} lru-cache@10.4.3: {} @@ -18072,7 +18841,7 @@ snapshots: dependencies: type-fest: 2.19.0 - next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.97.3): dependencies: '@next/env': 16.2.3 '@swc/helpers': 0.5.15 @@ -18093,6 +18862,7 @@ snapshots: '@next/swc-win32-x64-msvc': 16.2.3 '@opentelemetry/api': 1.9.0 '@playwright/test': 1.58.2 + sass: 1.97.3 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -18278,7 +19048,7 @@ snapshots: transitivePeerDependencies: - magicast - nuxt-og-image@6.5.0(e7d4dc8e55e2ab84f05f794bb3a537a6): + nuxt-og-image@6.5.0(e6b246098479abe5cf3d793933821ae3): dependencies: '@clack/prompts': 1.3.0 '@nuxt/kit': 4.4.4(magicast@0.5.2) @@ -18294,8 +19064,8 @@ snapshots: magic-string: 0.30.21 magicast: 0.5.2 mocked-exports: 0.1.1 - nuxt-site-config: 4.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))(zod@4.4.3) - nuxtseo-shared: 5.1.3(89f4238dd5c0bee98b97096b2ddd1a18) + nuxt-site-config: 4.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))(zod@4.4.3) + nuxtseo-shared: 5.1.3(7b50e1790f19ed56ed72494e091cb5f9) nypm: 0.6.6 ofetch: 1.5.1 ohash: 2.0.11 @@ -18316,7 +19086,7 @@ snapshots: '@resvg/resvg-js': 2.6.2 '@resvg/resvg-wasm': 2.6.2 '@takumi-rs/core': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - fontless: 0.2.1(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + fontless: 0.2.1(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(vite@8.0.8) playwright-core: 1.59.1 satori: 0.19.3 sharp: 0.34.5 @@ -18340,12 +19110,12 @@ snapshots: - magicast - vue - nuxt-site-config@4.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))(zod@4.4.3): + nuxt-site-config@4.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))(zod@4.4.3): dependencies: '@nuxt/kit': 4.4.4(magicast@0.5.2) h3: 1.15.11 nuxt-site-config-kit: 4.0.8(magicast@0.5.2)(vue@3.5.32(typescript@5.9.3)) - nuxtseo-shared: 5.1.3(89f4238dd5c0bee98b97096b2ddd1a18) + nuxtseo-shared: 5.1.3(7b50e1790f19ed56ed72494e091cb5f9) pathe: 2.0.3 pkg-types: 2.3.1 site-config-stack: 4.0.8(vue@3.5.32(typescript@5.9.3)) @@ -18402,16 +19172,16 @@ snapshots: - uploadthing - vue - nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4): + nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4): dependencies: '@dxup/nuxt': 0.4.0(magicast@0.5.2)(typescript@5.9.3) '@nuxt/cli': 3.34.0(@nuxt/schema@4.4.2)(cac@6.7.14)(magicast@0.5.2) - '@nuxt/devtools': 3.2.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)) + '@nuxt/devtools': 3.2.4(@vitejs/devtools@0.1.24)(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)) '@nuxt/kit': 4.4.2(magicast@0.5.2) - '@nuxt/nitro-server': 4.4.2(@babel/core@7.29.0)(better-sqlite3@12.6.2)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(rolldown@1.0.0-rc.15)(typescript@5.9.3) + '@nuxt/nitro-server': 4.4.2(@babel/core@7.29.0)(better-sqlite3@12.6.2)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(rolldown@1.0.0-rc.15)(typescript@5.9.3) '@nuxt/schema': 4.4.2 '@nuxt/telemetry': 2.7.0(@nuxt/kit@4.4.2(magicast@0.5.2)) - '@nuxt/vite-builder': 4.4.2(16010c41461ccb0f6d66aa3ed9aadfa5) + '@nuxt/vite-builder': 4.4.2(61a0d016d92e24b83f8cc63bc530fd72) '@unhead/vue': 2.1.12(vue@3.5.32(typescript@5.9.3)) '@vue/shared': 3.5.32 c12: 3.3.3(magicast@0.5.2) @@ -18532,16 +19302,16 @@ snapshots: - xml2js - yaml - nuxtseo-shared@5.1.3(89f4238dd5c0bee98b97096b2ddd1a18): + nuxtseo-shared@5.1.3(7b50e1790f19ed56ed72494e091cb5f9): dependencies: '@clack/prompts': 1.3.0 - '@nuxt/devtools-kit': 4.0.0-alpha.3(magicast@0.5.2)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@nuxt/devtools-kit': 4.0.0-alpha.3(magicast@0.5.2)(vite@8.0.8) '@nuxt/kit': 4.4.4(magicast@0.5.2) '@nuxt/schema': 4.4.2 birpc: 4.0.0 consola: 3.4.2 defu: 6.1.7 - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4) ofetch: 1.5.1 pathe: 2.0.3 pkg-types: 2.3.1 @@ -18551,7 +19321,7 @@ snapshots: ufo: 1.6.4 vue: 3.5.32(typescript@5.9.3) optionalDependencies: - nuxt-site-config: 4.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3))(zod@4.4.3) + nuxt-site-config: 4.0.8(@nuxt/schema@4.4.2)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(@vue/compiler-sfc@3.5.33)(better-sqlite3@12.6.2)(cac@6.7.14)(db0@0.3.4(better-sqlite3@12.6.2))(eslint@10.2.0(jiti@2.6.1))(ioredis@5.10.0)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(rolldown@1.0.0-rc.15)(rollup-plugin-visualizer@6.0.11(rolldown@1.0.0-rc.15)(rollup@4.59.0))(rollup@4.59.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(vite@8.0.8)(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.4))(vite@8.0.8)(vue@3.5.32(typescript@5.9.3))(zod@4.4.3) zod: 4.4.3 transitivePeerDependencies: - magicast @@ -18734,6 +19504,31 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc': 0.117.0 '@oxc-parser/binding-win32-x64-msvc': 0.117.0 + oxc-parser@0.126.0: + dependencies: + '@oxc-project/types': 0.126.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.126.0 + '@oxc-parser/binding-android-arm64': 0.126.0 + '@oxc-parser/binding-darwin-arm64': 0.126.0 + '@oxc-parser/binding-darwin-x64': 0.126.0 + '@oxc-parser/binding-freebsd-x64': 0.126.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.126.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.126.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.126.0 + '@oxc-parser/binding-linux-arm64-musl': 0.126.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.126.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.126.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.126.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.126.0 + '@oxc-parser/binding-linux-x64-gnu': 0.126.0 + '@oxc-parser/binding-linux-x64-musl': 0.126.0 + '@oxc-parser/binding-openharmony-arm64': 0.126.0 + '@oxc-parser/binding-wasm32-wasi': 0.126.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.126.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.126.0 + '@oxc-parser/binding-win32-x64-msvc': 0.126.0 + oxc-parser@0.128.0: dependencies: '@oxc-project/types': 0.128.0 @@ -18931,6 +19726,13 @@ snapshots: is-decimal: 2.0.1 is-hexadecimal: 2.0.1 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parse-latin@7.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -19335,6 +20137,13 @@ snapshots: proxy-from-env@1.1.0: {} + publint@0.3.21: + dependencies: + '@publint/pack': 0.1.4 + package-manager-detector: 1.6.0 + picocolors: 1.1.1 + sade: 1.8.1 + pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -19350,6 +20159,8 @@ snapshots: quansync@0.2.11: {} + quansync@1.0.0: {} + queue-microtask@1.2.3: {} radix3@1.1.2: {} @@ -19398,6 +20209,11 @@ snapshots: react@19.2.5: {} + read-yaml-file@2.1.0: + dependencies: + js-yaml: 4.1.1 + strip-bom: 4.0.0 + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -19780,6 +20596,15 @@ snapshots: safer-buffer@2.1.2: {} + sass@1.97.3: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.6 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + optional: true + satori@0.19.3: dependencies: '@shuding/opentype.js': 1.4.0-beta.0 @@ -20067,12 +20892,16 @@ snapshots: speakingurl@14.0.1: {} + split2@4.2.0: {} + splitpanes@4.0.4(vue@3.5.32(typescript@5.9.3)): dependencies: vue: 3.5.32(typescript@5.9.3) srvx@0.11.12: {} + srvx@0.11.16: {} + stackback@0.0.2: {} standard-as-callback@2.1.0: {} @@ -20139,6 +20968,10 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-bom@4.0.0: {} + + strip-comments-strings@1.2.0: {} + strip-final-newline@3.0.0: {} strip-json-comments@2.0.1: {} @@ -20429,6 +21262,19 @@ snapshots: ultrahtml@1.6.0: {} + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + quansync: 1.0.0 + + unconfig@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + defu: 6.1.7 + jiti: 2.6.1 + quansync: 1.0.0 + unconfig-core: 7.5.0 + uncrypto@0.1.3: {} unctx@2.5.0: @@ -20491,7 +21337,7 @@ snapshots: pkg-types: 2.3.0 scule: 1.3.0 strip-literal: 3.1.0 - tinyglobby: 0.2.15 + tinyglobby: 0.2.16 unplugin: 2.3.11 unplugin-utils: 0.3.1 @@ -20679,6 +21525,10 @@ snapshots: util-deprecate@1.0.2: {} + valibot@1.4.1(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 @@ -20709,23 +21559,23 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)): + vite-dev-rpc@1.1.0(vite@8.0.8): dependencies: birpc: 2.9.0 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vite-hot-client: 2.1.0(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite-hot-client: 2.1.0(vite@8.0.8) - vite-hot-client@2.1.0(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)): + vite-hot-client@2.1.0(vite@8.0.8): dependencies: - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vite-node@5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4): + vite-node@5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4): dependencies: cac: 6.7.14 es-module-lexer: 2.0.0 obug: 2.1.1 pathe: 2.0.3 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) transitivePeerDependencies: - '@types/node' - jiti @@ -20739,7 +21589,7 @@ snapshots: - tsx - yaml - vite-plugin-checker@0.12.0(eslint@10.2.0(jiti@2.6.1))(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3)): + vite-plugin-checker@0.12.0(eslint@10.2.0(jiti@2.6.1))(meow@13.2.0)(optionator@0.9.4)(oxlint@1.61.0)(typescript@5.9.3)(vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue-tsc@3.2.6(typescript@5.9.3)): dependencies: '@babel/code-frame': 7.29.0 chokidar: 4.0.3 @@ -20747,8 +21597,8 @@ snapshots: picocolors: 1.1.1 picomatch: 4.0.4 tiny-invariant: 1.3.3 - tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + tinyglobby: 0.2.16 + vite: 7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vscode-uri: 3.1.0 optionalDependencies: eslint: 10.2.0(jiti@2.6.1) @@ -20758,7 +21608,7 @@ snapshots: typescript: 5.9.3 vue-tsc: 3.2.6(typescript@5.9.3) - vite-plugin-inspect@11.3.3(@nuxt/kit@4.4.2(magicast@0.5.2))(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)): + vite-plugin-inspect@11.3.3(@nuxt/kit@4.4.2(magicast@0.5.2))(vite@8.0.8): dependencies: ansis: 4.2.0 debug: 4.4.3 @@ -20768,24 +21618,24 @@ snapshots: perfect-debounce: 2.1.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vite-dev-rpc: 1.1.0(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite-dev-rpc: 1.1.0(vite@8.0.8) optionalDependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) transitivePeerDependencies: - supports-color - vite-plugin-vue-tracer@1.3.0(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vue@3.5.32(typescript@5.9.3)): + vite-plugin-vue-tracer@1.3.0(vite@8.0.8)(vue@3.5.32(typescript@5.9.3)): dependencies: estree-walker: 3.0.3 exsolve: 1.0.8 magic-string: 0.30.21 pathe: 2.0.3 source-map-js: 1.2.1 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) vue: 3.5.32(typescript@5.9.3) - vite@5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(terser@5.46.0): + vite@5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0): dependencies: esbuild: 0.21.5 postcss: 8.5.9 @@ -20794,26 +21644,46 @@ snapshots: '@types/node': 25.5.2 fsevents: 2.3.3 lightningcss: 1.32.0 + sass: 1.97.3 terser: 5.46.0 - vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4): + vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 postcss: 8.5.9 rollup: 4.59.0 - tinyglobby: 0.2.15 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 25.5.2 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.32.0 + sass: 1.97.3 + terser: 5.46.0 + tsx: 4.21.0 + yaml: 2.8.4 + + vite@7.3.2(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.9 + rollup: 4.59.0 + tinyglobby: 0.2.16 optionalDependencies: '@types/node': 25.5.2 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 + sass: 1.97.3 terser: 5.46.0 tsx: 4.21.0 yaml: 2.8.4 - vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4): + vite@8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -20822,22 +21692,24 @@ snapshots: tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.5.2 + '@vitejs/devtools': 0.1.24(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@pnpm/logger@1001.0.1)(db0@0.3.4(better-sqlite3@12.6.2))(ioredis@5.10.0)(typescript@5.9.3)(vite@8.0.8) esbuild: 0.27.3 fsevents: 2.3.3 jiti: 2.6.1 + sass: 1.97.3 terser: 5.46.0 tsx: 4.21.0 yaml: 2.8.4 - vitefu@1.1.2(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)): + vitefu@1.1.2(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)): optionalDependencies: - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vitefu@1.1.2(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)): + vitefu@1.1.2(vite@8.0.8): optionalDependencies: - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) - vitepress@1.6.4(@algolia/client-search@5.49.1)(@types/node@25.5.2)(change-case@5.4.4)(fuse.js@7.3.0)(lightningcss@1.32.0)(postcss@8.5.14)(search-insights@2.17.3)(terser@5.46.0)(typescript@5.9.3): + vitepress@1.6.4(@algolia/client-search@5.49.1)(@types/node@25.5.2)(change-case@5.4.4)(fuse.js@7.3.0)(lightningcss@1.32.0)(postcss@8.5.14)(sass@1.97.3)(search-insights@2.17.3)(terser@5.46.0)(typescript@5.9.3): dependencies: '@docsearch/css': 3.8.2 '@docsearch/js': 3.8.2(@algolia/client-search@5.49.1)(search-insights@2.17.3) @@ -20846,7 +21718,7 @@ snapshots: '@shikijs/transformers': 2.5.0 '@shikijs/types': 2.5.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(terser@5.46.0))(vue@3.5.32(typescript@5.9.3)) + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0))(vue@3.5.32(typescript@5.9.3)) '@vue/devtools-api': 7.7.9 '@vue/shared': 3.5.32 '@vueuse/core': 12.8.2(typescript@5.9.3) @@ -20855,7 +21727,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.2.0 shiki: 2.5.0 - vite: 5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(terser@5.46.0) + vite: 5.4.21(@types/node@25.5.2)(lightningcss@1.32.0)(sass@1.97.3)(terser@5.46.0) vue: 3.5.32(typescript@5.9.3) optionalDependencies: postcss: 8.5.14 @@ -20891,12 +21763,12 @@ snapshots: '@playwright/test': 1.58.2 '@testing-library/svelte-core': 1.0.0(svelte@5.55.2) svelte: 5.55.2 - vitest: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + vitest: 4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8) - vitest@4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)): + vitest@4.1.4(@opentelemetry/api@1.9.0)(@types/node@25.5.2)(@vitest/browser-playwright@4.1.4)(vite@8.0.8): dependencies: '@vitest/expect': 4.1.4 - '@vitest/mocker': 4.1.4(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) + '@vitest/mocker': 4.1.4(vite@8.0.8) '@vitest/pretty-format': 4.1.4 '@vitest/runner': 4.1.4 '@vitest/snapshot': 4.1.4 @@ -20913,12 +21785,12 @@ snapshots: tinyexec: 1.1.1 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vite: 8.0.8(@types/node@25.5.2)(@vitejs/devtools@0.1.24)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 25.5.2 - '@vitest/browser-playwright': 4.1.4(playwright@1.59.1)(vite@8.0.8(@types/node@25.5.2)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))(vitest@4.1.4) + '@vitest/browser-playwright': 4.1.4(playwright@1.59.1)(vite@8.0.8)(vitest@4.1.4) transitivePeerDependencies: - msw @@ -20990,6 +21862,10 @@ snapshots: '@vue/language-core': 3.2.6 typescript: 5.9.3 + vue-virtual-scroller@3.0.4(vue@3.5.32(typescript@5.9.3)): + dependencies: + vue: 3.5.32(typescript@5.9.3) + vue@3.5.32(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.32 @@ -21072,10 +21948,22 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + write-yaml-file@5.0.0: + dependencies: + js-yaml: 4.1.1 + write-file-atomic: 5.0.1 + ws@8.18.3: {} ws@8.19.0: {} + ws@8.21.0: {} + wsl-utils@0.1.0: dependencies: is-wsl: 3.1.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c83c936d..d1f6254c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -40,6 +40,8 @@ catalog: typescript: ^5.9.3 tsx: ^4.21.0 "@vitejs/plugin-react": ^6.0.1 + "@vitejs/devtools": ^0.1.15 + "@vitejs/devtools-kit": ^0.1.15 # Types "@types/json-schema": ^7.0.15 diff --git a/scripts/sync-devtools-css.mjs b/scripts/sync-devtools-css.mjs new file mode 100644 index 00000000..5958d0e5 --- /dev/null +++ b/scripts/sync-devtools-css.mjs @@ -0,0 +1,33 @@ +#!/usr/bin/env node +// Generates styles.ts from styles.css for the devtools renderer. +// The CSS file is the source of truth (for editor autocompletion). +// Run from repo root: node scripts/sync-devtools-css.mjs + +import { readFileSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const root = fileURLToPath(new URL('..', import.meta.url)).replace(/\/$/, '') +const cssPath = join(root, 'packages/comark/src/devtools/renderer/styles.css') +const tsPath = join(root, 'packages/comark/src/devtools/renderer/styles.ts') + +const css = readFileSync(cssPath, 'utf-8') + +const output = `// @generated — do not edit. Edit styles.css instead, then run: node scripts/sync-devtools-css.mjs +export const STYLES = ${JSON.stringify(css)} +` + +const existing = (() => { + try { + return readFileSync(tsPath, 'utf-8') + } catch { + return '' + } +})() + +if (existing === output) { + console.log('[sync-devtools-css] styles.ts is up to date') +} else { + writeFileSync(tsPath, output) + console.log('[sync-devtools-css] styles.ts updated from styles.css') +} From 8cc0777fb96174e537f82b16a9d44f34d0974f3a Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Sat, 30 May 2026 23:20:37 -0600 Subject: [PATCH 02/28] chore: lint --- packages/comark/src/devtools/renderer/styles.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/comark/src/devtools/renderer/styles.ts b/packages/comark/src/devtools/renderer/styles.ts index a594ba04..8ebfaf09 100644 --- a/packages/comark/src/devtools/renderer/styles.ts +++ b/packages/comark/src/devtools/renderer/styles.ts @@ -1,3 +1,2 @@ // @generated — do not edit. Edit styles.css instead, then run: node scripts/sync-devtools-css.mjs -export const STYLES = - '.comark-devtools {\n --comark-devtools-bg: #ffffff;\n --comark-devtools-bg-soft: #fafafa;\n --comark-devtools-bg-muted: #f4f4f5;\n --comark-devtools-text: #18181b;\n --comark-devtools-text-muted: #71717a;\n --comark-devtools-border: #e4e4e7;\n --comark-devtools-primary: #eab308;\n --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n --comark-devtools-accent: #ca8a04;\n --comark-devtools-error: #ef4444;\n --comark-devtools-warning: #f59e0b;\n --comark-devtools-success: #22c55e;\n\n display: flex;\n height: 100%;\n font-family: \'Geist\', \'Public Sans\', ui-sans-serif, system-ui, sans-serif;\n color: var(--comark-devtools-text);\n background: var(--comark-devtools-bg);\n font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n box-sizing: border-box;\n}\n\n/* Editor pane */\n.comark-editor-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n border-right: 1px solid var(--comark-devtools-border);\n}\n\n.comark-pane-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-editor {\n flex: 1;\n width: 100%;\n padding: 12px;\n border: none;\n resize: none;\n outline: none;\n background: transparent;\n color: inherit;\n font-family: \'Geist Mono\', ui-monospace, \'SF Mono\', Monaco, Menlo, monospace;\n font-size: 13px;\n line-height: 1.6;\n tab-size: 2;\n}\n\n.comark-editor::placeholder {\n color: var(--comark-devtools-text-muted);\n opacity: 0.5;\n}\n\n/* Output pane */\n.comark-output-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n overflow: hidden;\n}\n\n/* Tabs */\n.comark-tabs {\n display: flex;\n border-bottom: 1px solid var(--comark-devtools-border);\n flex-shrink: 0;\n}\n\n.comark-tab {\n padding: 6px 12px;\n border: none;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n transition: color 0.15s, border-color 0.15s;\n}\n\n.comark-tab:hover {\n color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active="true"] {\n color: var(--comark-devtools-text);\n border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Status bar */\n.comark-status {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 4px 12px;\n border-top: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--comark-devtools-success);\n flex-shrink: 0;\n}\n\n.comark-status-dot[data-state="error"] {\n background: var(--comark-devtools-error);\n}\n\n.comark-status-dot[data-state="parsing"] {\n background: var(--comark-devtools-warning);\n}\n\n/* Output content */\n.comark-output {\n flex: 1;\n overflow: auto;\n padding: 12px;\n}\n\n.comark-output pre {\n margin: 0;\n font-family: \'Geist Mono\', ui-monospace, \'SF Mono\', Monaco, Menlo, monospace;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key { color: #9333ea; }\n.comark-tree-string { color: #16a34a; }\n.comark-tree-number { color: #2563eb; }\n.comark-tree-null { color: #a1a1aa; font-style: italic; }\n.comark-tree-bool { color: #dc2626; }\n\n/* Info table */\n.comark-info-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.comark-info-table td {\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n}\n\n.comark-info-table td:first-child {\n font-weight: 600;\n width: 140px;\n color: var(--comark-devtools-text-muted);\n}\n\n/* Error display */\n.comark-error {\n color: var(--comark-devtools-error);\n padding: 12px;\n font-size: 12px;\n font-family: \'Geist Mono\', ui-monospace, monospace;\n white-space: pre-wrap;\n}\n\n/* Instance bar */\n.comark-instance-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n flex-shrink: 0;\n background: var(--comark-devtools-bg-soft);\n}\n\n.comark-instance-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected="true"] {\n background: var(--comark-devtools-success);\n animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected="false"] {\n background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n\n.comark-instance-label {\n font-weight: 500;\n color: var(--comark-devtools-text-muted);\n}\n\n.comark-push-btn {\n padding: 2px 8px;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 4px;\n background: var(--comark-devtools-bg);\n color: inherit;\n font-size: 11px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s;\n}\n\n.comark-push-btn:hover {\n background: var(--comark-devtools-bg-muted);\n border-color: var(--comark-devtools-primary);\n}\n\n.comark-push-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.comark-theme-toggle {\n margin-left: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 6px;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n transition: color 0.15s, border-color 0.15s, background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n color: var(--comark-devtools-text);\n border-color: var(--comark-devtools-primary);\n background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme="dark"],\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) {\n --comark-devtools-bg: #18181b;\n --comark-devtools-bg-soft: #27272a;\n --comark-devtools-bg-muted: #3f3f46;\n --comark-devtools-text: #fafafa;\n --comark-devtools-text-muted: #a1a1aa;\n --comark-devtools-border: #3f3f46;\n --comark-devtools-primary: #facc15;\n --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme="dark"] .comark-tree-key,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-key { color: #c084fc; }\n.comark-devtools[data-theme="dark"] .comark-tree-string,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-string { color: #4ade80; }\n.comark-devtools[data-theme="dark"] .comark-tree-number,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-number { color: #60a5fa; }\n.comark-devtools[data-theme="dark"] .comark-tree-null,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-null { color: #71717a; }\n.comark-devtools[data-theme="dark"] .comark-tree-bool,\n.comark-devtools:not([data-theme="light"]):is([data-auto-dark="true"]) .comark-tree-bool { color: #f87171; }\n' +export const STYLES = ".comark-devtools {\n --comark-devtools-bg: #ffffff;\n --comark-devtools-bg-soft: #fafafa;\n --comark-devtools-bg-muted: #f4f4f5;\n --comark-devtools-text: #18181b;\n --comark-devtools-text-muted: #71717a;\n --comark-devtools-border: #e4e4e7;\n --comark-devtools-primary: #eab308;\n --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n --comark-devtools-accent: #ca8a04;\n --comark-devtools-error: #ef4444;\n --comark-devtools-warning: #f59e0b;\n --comark-devtools-success: #22c55e;\n\n display: flex;\n height: 100%;\n font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif;\n color: var(--comark-devtools-text);\n background: var(--comark-devtools-bg);\n font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n box-sizing: border-box;\n}\n\n/* Editor pane */\n.comark-editor-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n border-right: 1px solid var(--comark-devtools-border);\n}\n\n.comark-pane-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-editor {\n flex: 1;\n width: 100%;\n padding: 12px;\n border: none;\n resize: none;\n outline: none;\n background: transparent;\n color: inherit;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 13px;\n line-height: 1.6;\n tab-size: 2;\n}\n\n.comark-editor::placeholder {\n color: var(--comark-devtools-text-muted);\n opacity: 0.5;\n}\n\n/* Output pane */\n.comark-output-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n overflow: hidden;\n}\n\n/* Tabs */\n.comark-tabs {\n display: flex;\n border-bottom: 1px solid var(--comark-devtools-border);\n flex-shrink: 0;\n}\n\n.comark-tab {\n padding: 6px 12px;\n border: none;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n transition:\n color 0.15s,\n border-color 0.15s;\n}\n\n.comark-tab:hover {\n color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active='true'] {\n color: var(--comark-devtools-text);\n border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Status bar */\n.comark-status {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 4px 12px;\n border-top: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--comark-devtools-success);\n flex-shrink: 0;\n}\n\n.comark-status-dot[data-state='error'] {\n background: var(--comark-devtools-error);\n}\n\n.comark-status-dot[data-state='parsing'] {\n background: var(--comark-devtools-warning);\n}\n\n/* Output content */\n.comark-output {\n flex: 1;\n overflow: auto;\n padding: 12px;\n}\n\n.comark-output pre {\n margin: 0;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key {\n color: #9333ea;\n}\n.comark-tree-string {\n color: #16a34a;\n}\n.comark-tree-number {\n color: #2563eb;\n}\n.comark-tree-null {\n color: #a1a1aa;\n font-style: italic;\n}\n.comark-tree-bool {\n color: #dc2626;\n}\n\n/* Info table */\n.comark-info-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.comark-info-table td {\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n}\n\n.comark-info-table td:first-child {\n font-weight: 600;\n width: 140px;\n color: var(--comark-devtools-text-muted);\n}\n\n/* Error display */\n.comark-error {\n color: var(--comark-devtools-error);\n padding: 12px;\n font-size: 12px;\n font-family: 'Geist Mono', ui-monospace, monospace;\n white-space: pre-wrap;\n}\n\n/* Instance bar */\n.comark-instance-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n flex-shrink: 0;\n background: var(--comark-devtools-bg-soft);\n}\n\n.comark-instance-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected='true'] {\n background: var(--comark-devtools-success);\n animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected='false'] {\n background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n.comark-instance-label {\n font-weight: 500;\n color: var(--comark-devtools-text-muted);\n}\n\n.comark-push-btn {\n padding: 2px 8px;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 4px;\n background: var(--comark-devtools-bg);\n color: inherit;\n font-size: 11px;\n cursor: pointer;\n transition:\n background 0.15s,\n border-color 0.15s;\n}\n\n.comark-push-btn:hover {\n background: var(--comark-devtools-bg-muted);\n border-color: var(--comark-devtools-primary);\n}\n\n.comark-push-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.comark-theme-toggle {\n margin-left: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 6px;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n transition:\n color 0.15s,\n border-color 0.15s,\n background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n color: var(--comark-devtools-text);\n border-color: var(--comark-devtools-primary);\n background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme='dark'],\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) {\n --comark-devtools-bg: #18181b;\n --comark-devtools-bg-soft: #27272a;\n --comark-devtools-bg-muted: #3f3f46;\n --comark-devtools-text: #fafafa;\n --comark-devtools-text-muted: #a1a1aa;\n --comark-devtools-border: #3f3f46;\n --comark-devtools-primary: #facc15;\n --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme='dark'] .comark-tree-key,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key {\n color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-string,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string {\n color: #4ade80;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-number,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number {\n color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-null,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null {\n color: #71717a;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-bool,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool {\n color: #f87171;\n}\n" From f12b101c97f27ba22dd67431c352ab014acfc7aa Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Sat, 30 May 2026 23:21:19 -0600 Subject: [PATCH 03/28] fix(react): enhance Comark for ssr support --- .../comark-react/src/components/Comark.tsx | 19 ++++++++++++++++--- packages/comark-react/src/index.ts | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/comark-react/src/components/Comark.tsx b/packages/comark-react/src/components/Comark.tsx index eb1d3f36..ded716e5 100644 --- a/packages/comark-react/src/components/Comark.tsx +++ b/packages/comark-react/src/components/Comark.tsx @@ -65,8 +65,9 @@ export interface ComarkProps { /** * Comark component * - * Async server component that parses markdown on the server and renders it. - * When `streaming` is true, delegates to ComarkClient for client-side re-rendering. + * Works in both client and server contexts. In client contexts (Vite, CRA, etc.), + * it parses markdown on the client using React hooks. In React Server Components + * (Next.js, etc.), use `ComarkServer` for async server-side parsing. * * @example * ```tsx @@ -93,7 +94,19 @@ export interface ComarkProps { * } * ``` */ -export async function Comark({ +export function Comark(props: ComarkProps) { + return +} + +/** + * Async server-only Comark component. + * + * Parses markdown on the server using `await`. Only works in React Server + * Components (Next.js `app/` directory, etc.). For client-side usage, use `Comark`. + * + * When `streaming` is true, delegates to `ComarkClient` for client-side re-rendering. + */ +export async function ComarkServer({ children, markdown = '', options = {}, diff --git a/packages/comark-react/src/index.ts b/packages/comark-react/src/index.ts index 747265f4..b0052073 100644 --- a/packages/comark-react/src/index.ts +++ b/packages/comark-react/src/index.ts @@ -7,7 +7,7 @@ import type { ParseOptions } from 'comark' export { ComarkRenderer } from './components/ComarkRenderer.tsx' export { ComarkClient } from './components/ComarkClient.tsx' -export { Comark } +export { Comark, ComarkServer } from './components/Comark.tsx' export type * from 'comark' interface DefineComarkComponentOptions extends ParseOptions { From b72b265a312a3def1f8f99afb3a898775f8c3a0c Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Sat, 30 May 2026 23:38:27 -0600 Subject: [PATCH 04/28] fix: update docs --- AGENTS.md | 7 ++++++- docs/content/3.rendering/5.react.md | 29 ++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6dd6cfdd..fb94780c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -328,6 +328,11 @@ 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/vite' +// Devtools registry (used internally by framework renderers) +import { registerDevtoolsInstance, getDevtoolsRegistry } 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. @@ -350,7 +355,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' diff --git a/docs/content/3.rendering/5.react.md b/docs/content/3.rendering/5.react.md index e1ccb92e..3fecbc80 100644 --- a/docs/content/3.rendering/5.react.md +++ b/docs/content/3.rendering/5.react.md @@ -43,10 +43,10 @@ bun add @comark/react ## `` -The `` component is the simplest way to render markdown in React. It handles parsing and rendering automatically. +The `` 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"} -`` is an **async** component. You can also use the `` component to handle parsing yourself. +::info +For React Server Components (Next.js `app/` directory), you can also use [``](#code-comarkserver) to parse markdown on the server with `await`. :: ```tsx [App.tsx] @@ -368,6 +368,29 @@ export default async function Page({ params }: { params: { slug: string } }) { --- +## `` + +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 [``](#code-comark). + +Accepts the same props as ``. When `streaming` is true, it delegates to the client-side ``. + +```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 {content} +} +``` + +::warning +`` is an `async` function component and will throw in client-only environments (Vite, CRA). Use `` instead for universal usage. +:: + +--- + ## `` Renders a pre-parsed `ComarkTree` without any parsing. Use it when you parse on the server, in a build step, or via an API — no parser or plugin code is shipped to the browser. From ae9e572a571df72e1ae112f38a3dae7ea4a9e852 Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Sun, 31 May 2026 00:09:48 -0600 Subject: [PATCH 05/28] fix: test --- packages/comark-vue/test/vite.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/comark-vue/test/vite.test.ts b/packages/comark-vue/test/vite.test.ts index 1ffb5240..1fc3eaf2 100644 --- a/packages/comark-vue/test/vite.test.ts +++ b/packages/comark-vue/test/vite.test.ts @@ -16,7 +16,8 @@ describe('@comark/vue vite plugin', () => { }) const { default: comark } = await import('../src/vite.ts') - const plugin = comark() + const plugins = comark() + const plugin = Array.isArray(plugins) ? plugins[0] : plugins const vueOptions: Record = {} plugin.configResolved?.({ From 076e4ab8d0584bc98ae7740396fdef822d5d57ac Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Sun, 31 May 2026 00:59:57 -0600 Subject: [PATCH 06/28] fix: update docs and remove push to app --- AGENTS.md | 12 ++ docs/content/4.plugins/1.built-in/devtools.md | 143 ++++++++++++++++++ docs/content/4.plugins/index.md | 4 + .../src/components/ComarkClient.tsx | 14 +- .../src/components/Comark.svelte | 4 +- packages/comark-vue/src/components/Comark.ts | 18 +-- .../src/components/ComarkRenderer.ts | 1 + packages/comark-vue/test/vite.test.ts | 27 ++-- packages/comark/src/devtools/register.ts | 6 - packages/comark/src/devtools/registry.ts | 13 -- .../comark/src/devtools/renderer/index.ts | 24 --- .../comark/src/devtools/renderer/styles.css | 23 --- .../comark/src/devtools/renderer/styles.ts | 3 +- packages/comark/src/devtools/vite.ts | 19 +-- packages/comark/test/utils/index.ts | 1 - 15 files changed, 191 insertions(+), 121 deletions(-) create mode 100644 docs/content/4.plugins/1.built-in/devtools.md diff --git a/AGENTS.md b/AGENTS.md index fb94780c..be4993f2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,6 +52,17 @@ 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) +│ │ ├── registry.ts # Global singleton registry for live instances +│ │ ├── register.ts # Instance registration helpers +│ │ ├── vite.ts # Vite plugin with RPC endpoints (comark/vite entry point) +│ │ ├── constants/ # Icon assets +│ │ └── renderer/ # DevTools panel UI (comark/devtools-renderer entry point) +│ │ ├── index.ts # Panel setup, editor, tabs, push-to-app +│ │ ├── output.ts # AST, roundtrip, info rendering +│ │ ├── styles.ts # CSS styles +│ │ └── theme.ts # Light/dark/auto theme toggle │ ├── plugins/ # Built-in and optional plugins │ │ ├── alert.ts # Alert/callout blocks │ │ ├── emoji.ts # Emoji shortcodes @@ -78,6 +89,7 @@ packages/comark/ | `shiki` | `comark/plugins/highlight` | | `katex` | `comark/plugins/math` | | `beautiful-mermaid` | `comark/plugins/mermaid` | +| `@vitejs/devtools-kit` | `comark/vite` (Vite DevTools integration) | All are optional — only install what you use. diff --git a/docs/content/4.plugins/1.built-in/devtools.md b/docs/content/4.plugins/1.built-in/devtools.md new file mode 100644 index 00000000..0a703e25 --- /dev/null +++ b/docs/content/4.plugins/1.built-in/devtools.md @@ -0,0 +1,143 @@ +--- +title: Vite DevTools +description: Interactive Comark playground panel inside Vite DevTools for real-time markdown editing and AST inspection. +seo: + title: Vite DevTools Plugin +navigation: + icon: i-lucide-wrench +links: + - label: Vite DevTools + icon: i-lucide-external-link + to: https://devtools.vite.dev/ + color: neutral + variant: soft + - label: Plugins + icon: i-lucide-plug + to: /plugins + color: neutral + variant: soft +--- + +The Vite DevTools plugin registers an interactive Comark panel inside [Vite DevTools](https://devtools.vite.dev/). It provides a markdown playground with live AST inspection and roundtrip rendering. + +## 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/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 full-featured markdown editor on the left side. Changes are parsed in real time (300ms debounce) by the Comark parser running on the Vite dev server via RPC. + +### AST Tab + +Displays the full parsed AST with syntax-highlighted JSON. Useful for understanding how Comark transforms your markdown and inspecting node structure. + +### Roundtrip Tab + +Shows the result of parsing the markdown and then rendering it back to a markdown string via `renderMarkdown()`. A match indicator shows whether the roundtrip produces identical output. + +### Info Tab + +Displays metadata about the current parse result: + +- Node count (top-level and deep) +- Input character count +- Frontmatter keys and values +- Meta keys + +### Live Instance Connection + +When a Comark component (`` from `@comark/vue`, `@comark/react`, or `@comark/svelte`) is mounted on the page, the DevTools panel automatically detects it. + +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 | +|---|---|---| +| **Registry** | `comark/devtools` | Browser-side singleton that tracks live Comark instances and communicates with the dev server via HMR | +| **Vite Plugin** | `comark/vite` | Dev server plugin that exposes RPC endpoints for parsing, rendering, and listing instances | +| **Renderer** | `comark/devtools-renderer` | UI panel rendered inside the Vite DevTools iframe | + +### Instance Registration + +Framework renderers automatically register with the devtools registry in dev mode. Registration is guarded behind `import.meta.hot` so it's tree-shaken in production builds. + +```ts +// Simplified registration flow (handled internally by each renderer) +import { registerDevtoolsInstance } from 'comark/devtools' + +const handle = await registerDevtoolsInstance({ + hot: import.meta.hot, + tree: parsedTree, + markdown: sourceMarkdown, +}) + +// Update when tree changes +handle.update({ tree: newTree, markdown: newMarkdown }) + +// Cleanup on unmount +handle.unregister() +``` + +### RPC Endpoints + +The Vite plugin registers three RPC endpoints: + +| Endpoint | Type | Description | +|---|---|---| +| `comark:parse` | query | Parse markdown string → `ComarkTree` | +| `comark:render-markdown` | query | Parse then render back to markdown (roundtrip) | +| `comark:list-instances` | query | List all registered Comark instances | + +## 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`. diff --git a/docs/content/4.plugins/index.md b/docs/content/4.plugins/index.md index 26f57b5e..bf506cdd 100644 --- a/docs/content/4.plugins/index.md +++ b/docs/content/4.plugins/index.md @@ -69,6 +69,10 @@ Comark's plugin system extends markdown functionality with specialized features. Generate hierarchical TOC from headings automatically :: + ::card{icon="i-lucide-wrench" title="Vite DevTools" to="/plugins/built-in/devtools"} + Interactive markdown playground with AST inspection and live push-to-app inside Vite DevTools + :: + :: ## Guides diff --git a/packages/comark-react/src/components/ComarkClient.tsx b/packages/comark-react/src/components/ComarkClient.tsx index 2cd49096..5118e4ba 100644 --- a/packages/comark-react/src/components/ComarkClient.tsx +++ b/packages/comark-react/src/components/ComarkClient.tsx @@ -1,6 +1,6 @@ 'use client' -import { use, useDeferredValue, useEffect, useMemo, useRef, useState, Suspense } from 'react' +import { use, useDeferredValue, useEffect, useMemo, useRef, Suspense } from 'react' import { parse } from 'comark' import type { ComarkTree } from 'comark' import { ComarkRenderer } from './ComarkRenderer.tsx' @@ -37,12 +37,10 @@ function ComarkContent({ export function ComarkClient({ children, markdown = '', options = {}, plugins = [], ...rest }: ComarkProps) { const content = children ? String(children) : markdown - const [devtoolsOverride, setDevtoolsOverride] = useState(null) - const effectiveContent = devtoolsOverride ?? content // Re-creates the promise only when content changes. // Note: options/plugins should be stable references (defined outside render or memoized). - const parsePromise = useMemo(() => parse(effectiveContent, { ...options, plugins }), [effectiveContent]) + const parsePromise = useMemo(() => parse(content, { ...options, plugins }), [content]) // Keep showing the previous parsed result while a new parse is pending — // prevents blank flashes during rapid streaming updates. @@ -56,14 +54,12 @@ export function ComarkClient({ children, markdown = '', options = {}, plugins = import('comark/devtools').then(({ registerDevtoolsInstance }) => { if (cancelled) return - // We resolve the current parse to get the tree for registration parsePromise.then((tree) => { if (cancelled) return registerDevtoolsInstance({ hot: import.meta.hot!, tree, - markdown: effectiveContent, - onUpdate: (md: string) => setDevtoolsOverride(md), + markdown: content, }).then((handle) => { if (cancelled) { handle?.unregister() @@ -85,9 +81,9 @@ export function ComarkClient({ children, markdown = '', options = {}, plugins = useEffect(() => { if (!instanceRef.current) return parsePromise.then((tree) => { - instanceRef.current?.update({ tree, markdown: effectiveContent }) + instanceRef.current?.update({ tree, markdown: content }) }) - }, [parsePromise, effectiveContent]) + }, [parsePromise, content]) return ( diff --git a/packages/comark-svelte/src/components/Comark.svelte b/packages/comark-svelte/src/components/Comark.svelte index 383dcba8..179a7a10 100644 --- a/packages/comark-svelte/src/components/Comark.svelte +++ b/packages/comark-svelte/src/components/Comark.svelte @@ -52,9 +52,8 @@ This is an alert component } = $props() let parsed: ComarkTree | null = $state(null) - let devtoolsOverride: string | null = $state(null) - let content = $derived((devtoolsOverride ?? (markdown || '')).trim()) + let content = $derived((markdown || '').trim()) let requestVersion = 0 let appliedVersion = 0 @@ -85,7 +84,6 @@ This is an alert component hot, tree: { nodes: [], frontmatter: {}, meta: {} }, markdown: '', - onUpdate: (md: string) => { devtoolsOverride = md }, }).then((handle) => { if (cancelled) { handle?.unregister() diff --git a/packages/comark-vue/src/components/Comark.ts b/packages/comark-vue/src/components/Comark.ts index 0059c043..e4a858d9 100644 --- a/packages/comark-vue/src/components/Comark.ts +++ b/packages/comark-vue/src/components/Comark.ts @@ -1,5 +1,5 @@ import type { PropType } from 'vue' -import { computed, defineComponent, h, onScopeDispose, provide, ref, shallowRef, watch } from 'vue' +import { computed, defineComponent, h, onScopeDispose, provide, shallowRef, watch } from 'vue' import { createSerializedParse } from 'comark' import type { ParseOptions, ComponentManifest, ComarkTree } from 'comark' import { ComarkRenderer } from './ComarkRenderer.ts' @@ -183,18 +183,15 @@ export const Comark: ComarkComponent = defineComponent({ }) const parsed = shallowRef(null) - const devtoolsOverride = ref(null) const parse = createSerializedParse({ ...props.options, plugins: props.plugins }) - const effectiveMarkdown = computed(() => devtoolsOverride.value ?? markdown.value) - watch( - () => [effectiveMarkdown.value, props.streaming] as const, - () => parse(effectiveMarkdown.value, { streaming: props.streaming }).then((result) => (parsed.value = result)) + () => [markdown.value, props.streaming] as const, + () => parse(markdown.value, { streaming: props.streaming }).then((result) => (parsed.value = result)) ) - await parse(effectiveMarkdown.value, { streaming: props.streaming }).then((result) => (parsed.value = result)) + await parse(markdown.value, { streaming: props.streaming }).then((result) => (parsed.value = result)) // Devtools instance registration (dev mode only) provide('__comark_devtools_registered__', true) @@ -204,15 +201,12 @@ export const Comark: ComarkComponent = defineComponent({ const handle = await registerDevtoolsInstance({ hot: import.meta.hot, tree: parsed.value || { nodes: [], frontmatter: {}, meta: {} }, - markdown: effectiveMarkdown.value, - onUpdate: (md: string) => { - devtoolsOverride.value = md - }, + markdown: markdown.value, }) if (handle) { watch(parsed, (tree) => { - if (tree) handle.update({ tree, markdown: effectiveMarkdown.value }) + if (tree) handle.update({ tree, markdown: markdown.value }) }) onScopeDispose(() => handle.unregister()) } diff --git a/packages/comark-vue/src/components/ComarkRenderer.ts b/packages/comark-vue/src/components/ComarkRenderer.ts index ec414cd8..faa3e5f5 100644 --- a/packages/comark-vue/src/components/ComarkRenderer.ts +++ b/packages/comark-vue/src/components/ComarkRenderer.ts @@ -1,3 +1,4 @@ +/// import type { PropType, VNode } from 'vue' import type { ComponentManifest, diff --git a/packages/comark-vue/test/vite.test.ts b/packages/comark-vue/test/vite.test.ts index 1fc3eaf2..f9b7642c 100644 --- a/packages/comark-vue/test/vite.test.ts +++ b/packages/comark-vue/test/vite.test.ts @@ -20,17 +20,22 @@ describe('@comark/vue vite plugin', () => { const plugin = Array.isArray(plugins) ? plugins[0] : plugins const vueOptions: Record = {} - plugin.configResolved?.({ - root: '/repo', - plugins: [ - { - name: 'vite:vue', - api: { - options: vueOptions, + const configResolved = + typeof plugin.configResolved === 'function' ? plugin.configResolved : (plugin.configResolved as any)?.handler + configResolved?.call( + {} as any, + { + root: '/repo', + plugins: [ + { + name: 'vite:vue', + api: { + options: vueOptions, + }, }, - }, - ], - } as any) + ], + } as any + ) const transform = vueOptions.template.compilerOptions.nodeTransforms[0] @@ -52,7 +57,7 @@ describe('@comark/vue vite plugin', () => { const run = transform(node, context) run?.() - expect(node.codegenNode.callee).toBe('_renderComarkSlot') + expect((node as any).codegenNode.callee).toBe('_renderComarkSlot') expect(context.imports).toHaveLength(1) expect(context.imports[0]).toMatchObject({ exp: '{ renderSlot as _renderComarkSlot }', diff --git a/packages/comark/src/devtools/register.ts b/packages/comark/src/devtools/register.ts index cc2fc82b..853e83a3 100644 --- a/packages/comark/src/devtools/register.ts +++ b/packages/comark/src/devtools/register.ts @@ -14,8 +14,6 @@ export interface RegisterInstanceOptions { tree: ComarkTree /** Initial markdown source, if known */ markdown?: string - /** Callback invoked when the devtools pushes an edit */ - onUpdate?: (markdown: string) => void } export interface RegisteredInstance { @@ -57,10 +55,6 @@ export async function registerDevtoolsInstance(options: RegisterInstanceOptions) markdown: options.markdown, } - if (options.onUpdate) { - instance.update = options.onUpdate - } - const unregister = registry.register(instance) return { diff --git a/packages/comark/src/devtools/registry.ts b/packages/comark/src/devtools/registry.ts index 603dd7d5..9dc0fa4a 100644 --- a/packages/comark/src/devtools/registry.ts +++ b/packages/comark/src/devtools/registry.ts @@ -15,8 +15,6 @@ export interface ComarkInstance { tree: ComarkTree /** Current markdown source (if available) */ markdown?: string - /** Callback to update the markdown source and trigger re-parse */ - update?: (markdown: string) => void } export interface ComarkInstanceSummary { @@ -24,8 +22,6 @@ export interface ComarkInstanceSummary { label?: string markdown?: string nodeCount: number - /** Whether the instance accepts live updates (has an onUpdate callback) */ - updatable: boolean } type InstanceListener = (instances: Map) => void @@ -48,14 +44,6 @@ class ComarkDevtoolsRegistry { */ connectHMR(hot: HotModule): void { this.hot = hot - - // Re-register the update listener on the new handle - hot.on('comark:update', (data: { id: string; markdown: string }) => { - const instance = this.instances.get(data.id) - if (instance?.update) { - instance.update(data.markdown) - } - }) } register(instance: ComarkInstance): () => void { @@ -89,7 +77,6 @@ class ComarkDevtoolsRegistry { label: inst.label, markdown: inst.markdown, nodeCount: inst.tree?.nodes?.length || 0, - updatable: typeof inst.update === 'function', })) } diff --git a/packages/comark/src/devtools/renderer/index.ts b/packages/comark/src/devtools/renderer/index.ts index 42fd4e51..9d91320d 100644 --- a/packages/comark/src/devtools/renderer/index.ts +++ b/packages/comark/src/devtools/renderer/index.ts @@ -9,7 +9,6 @@ interface InstanceSummary { label?: string markdown?: string nodeCount: number - updatable: boolean } type ComarkTree = { @@ -46,7 +45,6 @@ export default function setup(ctx: DockClientScriptContext) { let activeInstance: InstanceSummary | null = null let instanceDot: HTMLElement let instanceLabel: HTMLElement - let pushBtn: HTMLButtonElement let pollInterval: ReturnType | undefined // Theme state @@ -88,14 +86,6 @@ export default function setup(ctx: DockClientScriptContext) { instanceLabel.textContent = 'No instance' instanceBar.appendChild(instanceLabel) - pushBtn = document.createElement('button') - pushBtn.className = 'comark-push-btn' - pushBtn.textContent = 'Push to app' - pushBtn.title = 'Push edited markdown back to the live component' - pushBtn.style.display = 'none' - pushBtn.addEventListener('click', pushToInstance) - instanceBar.appendChild(pushBtn) - // Theme toggle const themeToggle = createThemeToggle(theme, (t) => { theme = t @@ -212,7 +202,6 @@ export default function setup(ctx: DockClientScriptContext) { if (current) { instanceDot.dataset.connected = 'true' instanceLabel.textContent = current.label || current.id - pushBtn.style.display = current.updatable ? '' : 'none' if (current.id !== prevId && current.markdown) { editor.value = current.markdown @@ -221,19 +210,6 @@ export default function setup(ctx: DockClientScriptContext) { } else { instanceDot.dataset.connected = 'false' instanceLabel.textContent = 'No instance' - pushBtn.style.display = 'none' - } - } - - async function pushToInstance() { - if (!activeInstance) return - try { - await ctx.rpc.call('comark:update-instance', { - id: activeInstance.id, - markdown: editor.value, - }) - } catch { - // Silently ignore push errors } } diff --git a/packages/comark/src/devtools/renderer/styles.css b/packages/comark/src/devtools/renderer/styles.css index 337f8d91..69696de3 100644 --- a/packages/comark/src/devtools/renderer/styles.css +++ b/packages/comark/src/devtools/renderer/styles.css @@ -242,29 +242,6 @@ color: var(--comark-devtools-text-muted); } -.comark-push-btn { - padding: 2px 8px; - border: 1px solid var(--comark-devtools-border); - border-radius: 4px; - background: var(--comark-devtools-bg); - color: inherit; - font-size: 11px; - cursor: pointer; - transition: - background 0.15s, - border-color 0.15s; -} - -.comark-push-btn:hover { - background: var(--comark-devtools-bg-muted); - border-color: var(--comark-devtools-primary); -} - -.comark-push-btn:disabled { - opacity: 0.4; - cursor: not-allowed; -} - .comark-theme-toggle { margin-left: auto; display: flex; diff --git a/packages/comark/src/devtools/renderer/styles.ts b/packages/comark/src/devtools/renderer/styles.ts index 8ebfaf09..aed1348c 100644 --- a/packages/comark/src/devtools/renderer/styles.ts +++ b/packages/comark/src/devtools/renderer/styles.ts @@ -1,2 +1,3 @@ // @generated — do not edit. Edit styles.css instead, then run: node scripts/sync-devtools-css.mjs -export const STYLES = ".comark-devtools {\n --comark-devtools-bg: #ffffff;\n --comark-devtools-bg-soft: #fafafa;\n --comark-devtools-bg-muted: #f4f4f5;\n --comark-devtools-text: #18181b;\n --comark-devtools-text-muted: #71717a;\n --comark-devtools-border: #e4e4e7;\n --comark-devtools-primary: #eab308;\n --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n --comark-devtools-accent: #ca8a04;\n --comark-devtools-error: #ef4444;\n --comark-devtools-warning: #f59e0b;\n --comark-devtools-success: #22c55e;\n\n display: flex;\n height: 100%;\n font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif;\n color: var(--comark-devtools-text);\n background: var(--comark-devtools-bg);\n font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n box-sizing: border-box;\n}\n\n/* Editor pane */\n.comark-editor-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n border-right: 1px solid var(--comark-devtools-border);\n}\n\n.comark-pane-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-editor {\n flex: 1;\n width: 100%;\n padding: 12px;\n border: none;\n resize: none;\n outline: none;\n background: transparent;\n color: inherit;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 13px;\n line-height: 1.6;\n tab-size: 2;\n}\n\n.comark-editor::placeholder {\n color: var(--comark-devtools-text-muted);\n opacity: 0.5;\n}\n\n/* Output pane */\n.comark-output-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n overflow: hidden;\n}\n\n/* Tabs */\n.comark-tabs {\n display: flex;\n border-bottom: 1px solid var(--comark-devtools-border);\n flex-shrink: 0;\n}\n\n.comark-tab {\n padding: 6px 12px;\n border: none;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n transition:\n color 0.15s,\n border-color 0.15s;\n}\n\n.comark-tab:hover {\n color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active='true'] {\n color: var(--comark-devtools-text);\n border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Status bar */\n.comark-status {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 4px 12px;\n border-top: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--comark-devtools-success);\n flex-shrink: 0;\n}\n\n.comark-status-dot[data-state='error'] {\n background: var(--comark-devtools-error);\n}\n\n.comark-status-dot[data-state='parsing'] {\n background: var(--comark-devtools-warning);\n}\n\n/* Output content */\n.comark-output {\n flex: 1;\n overflow: auto;\n padding: 12px;\n}\n\n.comark-output pre {\n margin: 0;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key {\n color: #9333ea;\n}\n.comark-tree-string {\n color: #16a34a;\n}\n.comark-tree-number {\n color: #2563eb;\n}\n.comark-tree-null {\n color: #a1a1aa;\n font-style: italic;\n}\n.comark-tree-bool {\n color: #dc2626;\n}\n\n/* Info table */\n.comark-info-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.comark-info-table td {\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n}\n\n.comark-info-table td:first-child {\n font-weight: 600;\n width: 140px;\n color: var(--comark-devtools-text-muted);\n}\n\n/* Error display */\n.comark-error {\n color: var(--comark-devtools-error);\n padding: 12px;\n font-size: 12px;\n font-family: 'Geist Mono', ui-monospace, monospace;\n white-space: pre-wrap;\n}\n\n/* Instance bar */\n.comark-instance-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n flex-shrink: 0;\n background: var(--comark-devtools-bg-soft);\n}\n\n.comark-instance-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected='true'] {\n background: var(--comark-devtools-success);\n animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected='false'] {\n background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n.comark-instance-label {\n font-weight: 500;\n color: var(--comark-devtools-text-muted);\n}\n\n.comark-push-btn {\n padding: 2px 8px;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 4px;\n background: var(--comark-devtools-bg);\n color: inherit;\n font-size: 11px;\n cursor: pointer;\n transition:\n background 0.15s,\n border-color 0.15s;\n}\n\n.comark-push-btn:hover {\n background: var(--comark-devtools-bg-muted);\n border-color: var(--comark-devtools-primary);\n}\n\n.comark-push-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.comark-theme-toggle {\n margin-left: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 6px;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n transition:\n color 0.15s,\n border-color 0.15s,\n background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n color: var(--comark-devtools-text);\n border-color: var(--comark-devtools-primary);\n background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme='dark'],\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) {\n --comark-devtools-bg: #18181b;\n --comark-devtools-bg-soft: #27272a;\n --comark-devtools-bg-muted: #3f3f46;\n --comark-devtools-text: #fafafa;\n --comark-devtools-text-muted: #a1a1aa;\n --comark-devtools-border: #3f3f46;\n --comark-devtools-primary: #facc15;\n --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme='dark'] .comark-tree-key,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key {\n color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-string,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string {\n color: #4ade80;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-number,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number {\n color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-null,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null {\n color: #71717a;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-bool,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool {\n color: #f87171;\n}\n" +export const STYLES = + ".comark-devtools {\n --comark-devtools-bg: #ffffff;\n --comark-devtools-bg-soft: #fafafa;\n --comark-devtools-bg-muted: #f4f4f5;\n --comark-devtools-text: #18181b;\n --comark-devtools-text-muted: #71717a;\n --comark-devtools-border: #e4e4e7;\n --comark-devtools-primary: #eab308;\n --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n --comark-devtools-accent: #ca8a04;\n --comark-devtools-error: #ef4444;\n --comark-devtools-warning: #f59e0b;\n --comark-devtools-success: #22c55e;\n\n display: flex;\n height: 100%;\n font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif;\n color: var(--comark-devtools-text);\n background: var(--comark-devtools-bg);\n font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n box-sizing: border-box;\n}\n\n/* Editor pane */\n.comark-editor-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n border-right: 1px solid var(--comark-devtools-border);\n}\n\n.comark-pane-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-editor {\n flex: 1;\n width: 100%;\n padding: 12px;\n border: none;\n resize: none;\n outline: none;\n background: transparent;\n color: inherit;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 13px;\n line-height: 1.6;\n tab-size: 2;\n}\n\n.comark-editor::placeholder {\n color: var(--comark-devtools-text-muted);\n opacity: 0.5;\n}\n\n/* Output pane */\n.comark-output-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n overflow: hidden;\n}\n\n/* Tabs */\n.comark-tabs {\n display: flex;\n border-bottom: 1px solid var(--comark-devtools-border);\n flex-shrink: 0;\n}\n\n.comark-tab {\n padding: 6px 12px;\n border: none;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n transition:\n color 0.15s,\n border-color 0.15s;\n}\n\n.comark-tab:hover {\n color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active='true'] {\n color: var(--comark-devtools-text);\n border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Status bar */\n.comark-status {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 4px 12px;\n border-top: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--comark-devtools-success);\n flex-shrink: 0;\n}\n\n.comark-status-dot[data-state='error'] {\n background: var(--comark-devtools-error);\n}\n\n.comark-status-dot[data-state='parsing'] {\n background: var(--comark-devtools-warning);\n}\n\n/* Output content */\n.comark-output {\n flex: 1;\n overflow: auto;\n padding: 12px;\n}\n\n.comark-output pre {\n margin: 0;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key {\n color: #9333ea;\n}\n.comark-tree-string {\n color: #16a34a;\n}\n.comark-tree-number {\n color: #2563eb;\n}\n.comark-tree-null {\n color: #a1a1aa;\n font-style: italic;\n}\n.comark-tree-bool {\n color: #dc2626;\n}\n\n/* Info table */\n.comark-info-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.comark-info-table td {\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n}\n\n.comark-info-table td:first-child {\n font-weight: 600;\n width: 140px;\n color: var(--comark-devtools-text-muted);\n}\n\n/* Error display */\n.comark-error {\n color: var(--comark-devtools-error);\n padding: 12px;\n font-size: 12px;\n font-family: 'Geist Mono', ui-monospace, monospace;\n white-space: pre-wrap;\n}\n\n/* Instance bar */\n.comark-instance-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n flex-shrink: 0;\n background: var(--comark-devtools-bg-soft);\n}\n\n.comark-instance-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected='true'] {\n background: var(--comark-devtools-success);\n animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected='false'] {\n background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n.comark-instance-label {\n font-weight: 500;\n color: var(--comark-devtools-text-muted);\n}\n\n.comark-theme-toggle {\n margin-left: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 6px;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n transition:\n color 0.15s,\n border-color 0.15s,\n background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n color: var(--comark-devtools-text);\n border-color: var(--comark-devtools-primary);\n background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme='dark'],\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) {\n --comark-devtools-bg: #18181b;\n --comark-devtools-bg-soft: #27272a;\n --comark-devtools-bg-muted: #3f3f46;\n --comark-devtools-text: #fafafa;\n --comark-devtools-text-muted: #a1a1aa;\n --comark-devtools-border: #3f3f46;\n --comark-devtools-primary: #facc15;\n --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme='dark'] .comark-tree-key,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key {\n color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-string,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string {\n color: #4ade80;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-number,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number {\n color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-null,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null {\n color: #71717a;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-bool,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool {\n color: #f87171;\n}\n" diff --git a/packages/comark/src/devtools/vite.ts b/packages/comark/src/devtools/vite.ts index e66455e2..05b32f5e 100644 --- a/packages/comark/src/devtools/vite.ts +++ b/packages/comark/src/devtools/vite.ts @@ -9,7 +9,6 @@ declare module '@vitejs/devtools-kit' { 'comark:parse': (markdown: string) => Promise 'comark:render-markdown': (markdown: string) => Promise 'comark:list-instances': () => Promise - 'comark:update-instance': (args: { id: string; markdown: string }) => Promise<{ ok: boolean }> } } @@ -35,16 +34,13 @@ declare module '@vitejs/devtools-kit' { export function comarkDevtools(): Plugin { // Instance data pushed from the user's app via HMR let instancesData: ComarkInstanceSummary[] = [] - let server: ViteDevServer | undefined return { name: 'comark:devtools', configureServer(_server: ViteDevServer) { - server = _server - // Receive instance data from the user's app (sent by the devtools registry) - server.hot.on('comark:instances', (data: ComarkInstanceSummary[]) => { + _server.hot.on('comark:instances', (data: ComarkInstanceSummary[]) => { instancesData = data }) }, @@ -80,19 +76,6 @@ export function comarkDevtools(): Plugin { handler: () => instancesData, }) - // RPC: update an instance's markdown (push from devtools to the user's app) - ctx.rpc.register({ - name: 'comark:update-instance', - type: 'mutation', - handler: (args: { id: string; markdown: string }) => { - server?.hot.send('comark:update', { - id: args.id, - markdown: args.markdown, - }) - return { ok: true } - }, - }) - // Register the dock entry with a custom renderer ctx.docks.register({ id: 'comark', diff --git a/packages/comark/test/utils/index.ts b/packages/comark/test/utils/index.ts index ba4a90d1..1bf93e19 100644 --- a/packages/comark/test/utils/index.ts +++ b/packages/comark/test/utils/index.ts @@ -1,4 +1,3 @@ -// @ts-expect-error - ignore @nuxtjs/mdc types import type { MDCRoot } from '@nuxtjs/mdc' import type { ComarkTree } from 'comark' import remarkGFM from 'remark-gfm' From 21b81e4d9d14828964b6198fff38b469ea97bdba Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Mon, 1 Jun 2026 18:22:27 -0600 Subject: [PATCH 07/28] fix: build --- examples/1.frameworks/nextjs/next-env.d.ts | 2 +- packages/comark-react/src/components/ComarkClient.tsx | 5 +++-- packages/comark-svelte/src/components/Comark.svelte | 4 ++-- packages/comark-vue/src/components/Comark.ts | 5 +++-- packages/comark-vue/src/components/ComarkRenderer.ts | 5 +++-- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/examples/1.frameworks/nextjs/next-env.d.ts b/examples/1.frameworks/nextjs/next-env.d.ts index a3e4680c..9edff1c7 100644 --- a/examples/1.frameworks/nextjs/next-env.d.ts +++ b/examples/1.frameworks/nextjs/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -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. diff --git a/packages/comark-react/src/components/ComarkClient.tsx b/packages/comark-react/src/components/ComarkClient.tsx index 5118e4ba..33917ff6 100644 --- a/packages/comark-react/src/components/ComarkClient.tsx +++ b/packages/comark-react/src/components/ComarkClient.tsx @@ -49,7 +49,8 @@ export function ComarkClient({ children, markdown = '', options = {}, plugins = // Devtools instance registration (dev mode only) const instanceRef = useRef(null) useEffect(() => { - if (!import.meta.hot) return + const hot = (import.meta as Record).hot + if (!hot) return let cancelled = false import('comark/devtools').then(({ registerDevtoolsInstance }) => { @@ -57,7 +58,7 @@ export function ComarkClient({ children, markdown = '', options = {}, plugins = parsePromise.then((tree) => { if (cancelled) return registerDevtoolsInstance({ - hot: import.meta.hot!, + hot, tree, markdown: content, }).then((handle) => { diff --git a/packages/comark-svelte/src/components/Comark.svelte b/packages/comark-svelte/src/components/Comark.svelte index 179a7a10..1a5785f8 100644 --- a/packages/comark-svelte/src/components/Comark.svelte +++ b/packages/comark-svelte/src/components/Comark.svelte @@ -73,8 +73,8 @@ This is an alert component // Devtools instance registration (dev mode only) let devtoolsHandle: RegisteredInstance | null = $state(null) - if (import.meta.hot) { - const hot = import.meta.hot + const hot = (import.meta as Record).hot + if (hot) { $effect(() => { let cancelled = false diff --git a/packages/comark-vue/src/components/Comark.ts b/packages/comark-vue/src/components/Comark.ts index e4a858d9..85e04a2d 100644 --- a/packages/comark-vue/src/components/Comark.ts +++ b/packages/comark-vue/src/components/Comark.ts @@ -196,10 +196,11 @@ export const Comark: ComarkComponent = defineComponent({ // Devtools instance registration (dev mode only) provide('__comark_devtools_registered__', true) - if (import.meta.hot) { + const hot = (import.meta as Record).hot + if (hot) { const { registerDevtoolsInstance } = await import('comark/devtools') const handle = await registerDevtoolsInstance({ - hot: import.meta.hot, + hot, tree: parsed.value || { nodes: [], frontmatter: {}, meta: {} }, markdown: markdown.value, }) diff --git a/packages/comark-vue/src/components/ComarkRenderer.ts b/packages/comark-vue/src/components/ComarkRenderer.ts index faa3e5f5..d13c1fd6 100644 --- a/packages/comark-vue/src/components/ComarkRenderer.ts +++ b/packages/comark-vue/src/components/ComarkRenderer.ts @@ -373,7 +373,8 @@ export const ComarkRenderer: ComarkRendererComponent = defineComponent({ // Devtools: register this instance if no parent Comark already registered const parentRegistered = inject('__comark_devtools_registered__', false) - if (!parentRegistered && import.meta.hot) { + const hot = (import.meta as Record).hot + if (!parentRegistered && hot) { let devtoolsHandle: { unregister: () => void } | null = null let disposed = false const renderMdPromise = import('comark/render') @@ -381,7 +382,7 @@ export const ComarkRenderer: ComarkRendererComponent = defineComponent({ import('comark/devtools').then(({ registerDevtoolsInstanceFromTree }) => { if (disposed) return registerDevtoolsInstanceFromTree({ - hot: import.meta.hot!, + hot, tree: props.tree, }).then((handle) => { if (disposed) { From f3f54716b264b5d3db2518fdbc6e60ebefe73bf9 Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Wed, 3 Jun 2026 18:51:30 -0600 Subject: [PATCH 08/28] feat: enhance devtools UI and functionality --- .../comark/src/devtools/constants/index.ts | 63 ++++ packages/comark/src/devtools/index.ts | 3 +- packages/comark/src/devtools/register.ts | 27 +- packages/comark/src/devtools/registry.ts | 39 +-- packages/comark/src/devtools/renderer/dom.ts | 112 ++++++ .../comark/src/devtools/renderer/index.ts | 270 +-------------- .../comark/src/devtools/renderer/output.ts | 158 +++++---- .../comark/src/devtools/renderer/panel.ts | 255 ++++++++++++++ .../comark/src/devtools/renderer/styles.css | 319 ++++++++++++------ .../comark/src/devtools/renderer/styles.ts | 3 +- .../comark/src/devtools/renderer/theme.ts | 6 +- packages/comark/src/devtools/types.ts | 59 ++++ packages/comark/src/devtools/vite.ts | 40 ++- 13 files changed, 852 insertions(+), 502 deletions(-) create mode 100644 packages/comark/src/devtools/renderer/dom.ts create mode 100644 packages/comark/src/devtools/renderer/panel.ts create mode 100644 packages/comark/src/devtools/types.ts diff --git a/packages/comark/src/devtools/constants/index.ts b/packages/comark/src/devtools/constants/index.ts index 5b5eb96b..e791b33b 100644 --- a/packages/comark/src/devtools/constants/index.ts +++ b/packages/comark/src/devtools/constants/index.ts @@ -11,3 +11,66 @@ const COMARK_SVG = (color: string) => `` export const COMARK_LIGHT_ICON = `data:image/svg+xml,${encodeURIComponent(COMARK_SVG('#000'))}` export const COMARK_DARK_ICON = `data:image/svg+xml,${encodeURIComponent(COMARK_SVG('#fff'))}` + +export const DEVTOOLS_SHIKI_THEMES = ['vitesse-light', 'vitesse-dark'] as const + +/** Languages loaded by the devtools Shiki highlighter */ +export const DEVTOOLS_SHIKI_LANGS = [ + 'bash', + 'cpp', + 'csharp', + 'css', + 'csv', + 'diff', + 'docker', + 'dockerfile', + 'git-commit', + 'git-rebase', + 'go', + 'graphql', + 'html', + 'http', + 'ini', + 'java', + 'javascript', + 'js', + 'json', + 'json5', + 'jsonc', + 'jsonl', + 'jsx', + 'log', + 'lua', + 'makefile', + 'markdown', + 'md', + 'mdc', + 'mdx', + 'mermaid', + 'nginx', + 'php', + 'postcss', + 'powershell', + 'proto', + 'python', + 'r', + 'ruby', + 'rust', + 'sass', + 'scss', + 'shell', + 'shellscript', + 'shellsession', + 'sql', + 'ssh-config', + 'svelte', + 'swift', + 'toml', + 'ts', + 'tsx', + 'typescript', + 'vue', + 'vue-html', + 'xml', + 'yaml', +] as const diff --git a/packages/comark/src/devtools/index.ts b/packages/comark/src/devtools/index.ts index c45fed1b..19cc0792 100644 --- a/packages/comark/src/devtools/index.ts +++ b/packages/comark/src/devtools/index.ts @@ -1,4 +1,3 @@ export { getDevtoolsRegistry } from './registry.ts' export { registerDevtoolsInstance, registerDevtoolsInstanceFromTree } from './register.ts' -export type { ComarkInstance, ComarkInstanceSummary } from './registry.ts' -export type { RegisterInstanceOptions, RegisteredInstance } from './register.ts' +export type { ComarkInstance, ComarkInstanceSummary, RegisterInstanceOptions, RegisteredInstance } from './types.ts' diff --git a/packages/comark/src/devtools/register.ts b/packages/comark/src/devtools/register.ts index 853e83a3..b6e299ae 100644 --- a/packages/comark/src/devtools/register.ts +++ b/packages/comark/src/devtools/register.ts @@ -1,29 +1,4 @@ -import type { ComarkTree } from '../types.ts' -import type { ComarkInstance } from './registry.ts' - -/** Minimal subset of Vite's `import.meta.hot` used by the helper */ -interface HotHandle { - send(event: string, data?: any): void - on(event: string, cb: (...args: any[]) => void): void -} - -export interface RegisterInstanceOptions { - /** The HMR handle (`import.meta.hot`). Pass `null` to skip registration. */ - hot: HotHandle | null | undefined - /** Initial tree (may be empty for string renderers) */ - tree: ComarkTree - /** Initial markdown source, if known */ - markdown?: string -} - -export interface RegisteredInstance { - /** The assigned instance id */ - id: string - /** Update the instance's tree and/or markdown */ - update(patch: { tree?: ComarkTree; markdown?: string }): void - /** Unregister the instance */ - unregister(): void -} +import type { ComarkInstance, RegisterInstanceOptions, RegisteredInstance } from './types.ts' /** * Register a Comark devtools instance. diff --git a/packages/comark/src/devtools/registry.ts b/packages/comark/src/devtools/registry.ts index 9dc0fa4a..bf967b77 100644 --- a/packages/comark/src/devtools/registry.ts +++ b/packages/comark/src/devtools/registry.ts @@ -1,37 +1,18 @@ -import type { ComarkTree } from '../types.ts' - -/** Minimal subset of Vite's `import.meta.hot` API used by the registry */ -interface HotModule { - send(event: string, data?: any): void - on(event: string, cb: (...args: any[]) => void): void -} - -export interface ComarkInstance { - /** Unique instance identifier */ - id: string - /** Human-readable label (e.g. current URL) */ - label?: string - /** Current parsed tree */ - tree: ComarkTree - /** Current markdown source (if available) */ - markdown?: string -} - -export interface ComarkInstanceSummary { - id: string - label?: string - markdown?: string - nodeCount: number -} - -type InstanceListener = (instances: Map) => void +import type { ComarkInstance, ComarkInstanceSummary, HotModule, InstanceListener } from './types.ts' +/** + * Singleton registry tracking all live Comark instances in the browser. + * + * Instances are registered by framework renderers (Vue, React, Svelte) and + * their data is pushed to the Vite dev server over HMR for the devtools panel. + */ class ComarkDevtoolsRegistry { readonly instances = new Map() private listeners = new Set() private hot: HotModule | null = null private counter = 0 + /** Generate the next sequential instance id (e.g. `comark-1`, `comark-2`) */ nextId(): string { return `comark-${++this.counter}` } @@ -46,6 +27,7 @@ class ComarkDevtoolsRegistry { this.hot = hot } + /** Add an instance to the registry and return an unregister callback */ register(instance: ComarkInstance): () => void { this.instances.set(instance.id, instance) this.notify() @@ -59,6 +41,7 @@ class ComarkDevtoolsRegistry { } } + /** Merge a partial update into an existing instance's data */ update(id: string, patch: Partial>): void { const instance = this.instances.get(id) if (!instance) return @@ -66,11 +49,13 @@ class ComarkDevtoolsRegistry { this.notify() } + /** Subscribe to instance changes; returns an unsubscribe callback */ subscribe(listener: InstanceListener): () => void { this.listeners.add(listener) return () => this.listeners.delete(listener) } + /** Serialize all instances into lightweight summaries for RPC transport */ serialize(): ComarkInstanceSummary[] { return Array.from(this.instances.values()).map((inst) => ({ id: inst.id, diff --git a/packages/comark/src/devtools/renderer/dom.ts b/packages/comark/src/devtools/renderer/dom.ts new file mode 100644 index 00000000..c48e4f5a --- /dev/null +++ b/packages/comark/src/devtools/renderer/dom.ts @@ -0,0 +1,112 @@ +/** DOM element factories for the Comark devtools panel */ + +/** Create the tab bar with buttons for each tab definition */ +export function createTabBar( + tabDefs: { id: string; label: string }[], + activeTab: string, + onSwitch: (tabId: string) => void, +): { tabs: HTMLDivElement; tabButtons: HTMLButtonElement[] } { + const tabs = document.createElement('div') + tabs.className = 'comark-tabs' + + const tabButtons: HTMLButtonElement[] = [] + + for (const tab of tabDefs) { + const btn = document.createElement('button') + btn.className = 'comark-tab' + btn.textContent = tab.label + btn.dataset.tab = tab.id + btn.dataset.active = String(tab.id === activeTab) + btn.addEventListener('click', () => onSwitch(tab.id)) + tabs.appendChild(btn) + tabButtons.push(btn) + } + + return { tabs, tabButtons } +} + +/** Create the right-aligned section of the tab bar (instance dot + label) */ +export function createTabRight(): { + container: HTMLDivElement + dot: HTMLElement + label: HTMLElement +} { + const container = document.createElement('div') + container.className = 'comark-tab-right' + + const dot = document.createElement('span') + dot.className = 'comark-instance-dot' + dot.dataset.connected = 'false' + container.appendChild(dot) + + const label = document.createElement('span') + label.className = 'comark-instance-label' + label.textContent = '' + container.appendChild(label) + + return { container, dot, label } +} + +/** Create the markdown editor with a transparent textarea overlaying a syntax-highlighted `
` */
+export function createEditor(
+  onInput: () => void,
+  onScroll: (scrollTop: number, scrollLeft: number) => void,
+): { wrap: HTMLDivElement; textarea: HTMLTextAreaElement; highlight: HTMLPreElement } {
+  const wrap = document.createElement('div')
+  wrap.className = 'comark-editor-wrap'
+
+  const highlight = document.createElement('pre')
+  highlight.className = 'comark-editor-highlight'
+  highlight.setAttribute('aria-hidden', 'true')
+  wrap.appendChild(highlight)
+
+  const textarea = document.createElement('textarea')
+  textarea.className = 'comark-editor'
+  textarea.placeholder = 'Type markdown here...'
+  textarea.value = ''
+  textarea.spellcheck = false
+  textarea.addEventListener('input', onInput)
+  textarea.addEventListener('scroll', () => {
+    onScroll(textarea.scrollTop, textarea.scrollLeft)
+  })
+  textarea.addEventListener('keydown', (e) => {
+    if (e.key === 'Tab') {
+      e.preventDefault()
+      const start = textarea.selectionStart
+      const end = textarea.selectionEnd
+      textarea.value = textarea.value.substring(0, start) + '  ' + textarea.value.substring(end)
+      textarea.selectionStart = textarea.selectionEnd = start + 2
+      onInput()
+    }
+  })
+  wrap.appendChild(textarea)
+
+  return { wrap, textarea, highlight }
+}
+
+/** Create the "Connecting to Comark…" loading spinner element */
+export function createLoadingState(): HTMLDivElement {
+  const el = document.createElement('div')
+  el.className = 'comark-loading-state'
+  el.innerHTML = [
+    '
', + '

Connecting to Comark…

', + ].join('') + return el +} + +/** Create the empty-state placeholder shown when no `` instance is detected */ +export function createEmptyState(): HTMLDivElement { + const el = document.createElement('div') + el.className = 'comark-empty-state' + el.innerHTML = [ + '
', + '', + '', + '', + '', + '
', + '

No <Comark> detected on the current page.

', + ].join('') + return el +} diff --git a/packages/comark/src/devtools/renderer/index.ts b/packages/comark/src/devtools/renderer/index.ts index 9d91320d..ad86fddb 100644 --- a/packages/comark/src/devtools/renderer/index.ts +++ b/packages/comark/src/devtools/renderer/index.ts @@ -1,272 +1,6 @@ import type { DockClientScriptContext } from '@vitejs/devtools-kit/client' -import { STYLES } from './styles.ts' -import { applyTheme, createThemeToggle } from './theme.ts' -import { countNodes, renderAST, renderInfo, renderRoundtrip } from './output.ts' -import type { Theme } from './theme.ts' - -interface InstanceSummary { - id: string - label?: string - markdown?: string - nodeCount: number -} - -type ComarkTree = { - nodes: unknown[] - frontmatter: Record - meta: Record -} - -const DEFAULT_MARKDOWN = `# Hello World - -This is a **Comark** playground inside Vite DevTools. - -- Supports *inline* formatting -- Component syntax like \`::alert\` - -::alert{type="info"} -Edit this markdown to see the parsed AST in real time. -:: -` +import { DevtoolsPanel } from './panel.ts' export default function setup(ctx: DockClientScriptContext) { - let editor: HTMLTextAreaElement - let outputEl: HTMLElement - let statusDot: HTMLElement - let statusText: HTMLElement - let activeTab = 'ast' - let parseTimer: ReturnType | undefined - let lastTree: ComarkTree | null = null - let lastRoundtrip: string | null = null - let lastError: string | null = null - let tabButtons: HTMLButtonElement[] = [] - - // Live instance tracking - let activeInstance: InstanceSummary | null = null - let instanceDot: HTMLElement - let instanceLabel: HTMLElement - let pollInterval: ReturnType | undefined - - // Theme state - let theme: Theme = 'auto' - let rootEl: HTMLElement - - ctx.current.events.on('dom:panel:mounted', (panel: HTMLElement) => { - cleanup() - - // Inject styles - const style = document.createElement('style') - style.textContent = STYLES - panel.appendChild(style) - - // Root container - const root = document.createElement('div') - root.className = 'comark-devtools' - rootEl = root - applyTheme(rootEl, theme) - panel.appendChild(root) - - // --- Editor pane --- - const editorPane = document.createElement('div') - editorPane.className = 'comark-editor-pane' - root.appendChild(editorPane) - - // Instance bar - const instanceBar = document.createElement('div') - instanceBar.className = 'comark-instance-bar' - editorPane.appendChild(instanceBar) - - instanceDot = document.createElement('span') - instanceDot.className = 'comark-instance-dot' - instanceDot.dataset.connected = 'false' - instanceBar.appendChild(instanceDot) - - instanceLabel = document.createElement('span') - instanceLabel.className = 'comark-instance-label' - instanceLabel.textContent = 'No instance' - instanceBar.appendChild(instanceLabel) - - // Theme toggle - const themeToggle = createThemeToggle(theme, (t) => { - theme = t - applyTheme(rootEl, theme) - }) - instanceBar.appendChild(themeToggle) - - // Editor header - const editorHeader = document.createElement('div') - editorHeader.className = 'comark-pane-header' - editorHeader.textContent = 'Markdown' - editorPane.appendChild(editorHeader) - - // Editor textarea - editor = document.createElement('textarea') - editor.className = 'comark-editor' - editor.placeholder = 'Type markdown here...' - editor.value = DEFAULT_MARKDOWN - editor.spellcheck = false - editor.addEventListener('input', () => scheduleParse()) - editor.addEventListener('keydown', (e) => { - if (e.key === 'Tab') { - e.preventDefault() - const start = editor.selectionStart - const end = editor.selectionEnd - editor.value = editor.value.substring(0, start) + ' ' + editor.value.substring(end) - editor.selectionStart = editor.selectionEnd = start + 2 - scheduleParse() - } - }) - editorPane.appendChild(editor) - - // --- Output pane --- - const outputPane = document.createElement('div') - outputPane.className = 'comark-output-pane' - root.appendChild(outputPane) - - // Tabs - const tabs = document.createElement('div') - tabs.className = 'comark-tabs' - outputPane.appendChild(tabs) - - const tabDefs = [ - { id: 'ast', label: 'AST' }, - { id: 'roundtrip', label: 'Roundtrip' }, - { id: 'info', label: 'Info' }, - ] - - for (const tab of tabDefs) { - const btn = document.createElement('button') - btn.className = 'comark-tab' - btn.textContent = tab.label - btn.dataset.tab = tab.id - btn.dataset.active = String(tab.id === activeTab) - btn.addEventListener('click', () => switchTab(tab.id)) - tabs.appendChild(btn) - tabButtons.push(btn) - } - - // Output area - outputEl = document.createElement('div') - outputEl.className = 'comark-output' - outputPane.appendChild(outputEl) - - // Status bar - const statusBar = document.createElement('div') - statusBar.className = 'comark-status' - outputPane.appendChild(statusBar) - - statusDot = document.createElement('span') - statusDot.className = 'comark-status-dot' - statusBar.appendChild(statusDot) - - statusText = document.createElement('span') - statusText.textContent = 'Ready' - statusBar.appendChild(statusText) - - // Initial parse + start polling - parseMarkdown() - pollInstances() - pollInterval = setInterval(pollInstances, 1500) - }) - - ctx.current.events.on('entry:deactivated', cleanup) - - function cleanup() { - clearInterval(pollInterval) - pollInterval = undefined - clearTimeout(parseTimer) - parseTimer = undefined - activeInstance = null - tabButtons = [] - } - - function scheduleParse() { - clearTimeout(parseTimer) - parseTimer = setTimeout(parseMarkdown, 300) - } - - async function pollInstances() { - let instances: InstanceSummary[] = [] - try { - const data = (await ctx.rpc.call('comark:list-instances')) as InstanceSummary[] - instances = data || [] - } catch { - instances = [] - } - - const current = instances[0] || null - const prevId = activeInstance?.id - - activeInstance = current - - if (current) { - instanceDot.dataset.connected = 'true' - instanceLabel.textContent = current.label || current.id - - if (current.id !== prevId && current.markdown) { - editor.value = current.markdown - scheduleParse() - } - } else { - instanceDot.dataset.connected = 'false' - instanceLabel.textContent = 'No instance' - } - } - - async function parseMarkdown() { - const markdown = editor.value - statusDot.dataset.state = 'parsing' - statusText.textContent = 'Parsing...' - lastError = null - - try { - const [tree, roundtrip] = await Promise.all([ - ctx.rpc.call('comark:parse', markdown) as Promise, - ctx.rpc.call('comark:render-markdown', markdown) as Promise, - ]) - - lastTree = tree - lastRoundtrip = roundtrip - lastError = null - - const nodeCount = countNodes(tree.nodes) - statusDot.dataset.state = '' - statusText.textContent = `${nodeCount} node${nodeCount !== 1 ? 's' : ''} · ${markdown.length} chars` - } catch (err) { - lastError = err instanceof Error ? err.message : String(err) - statusDot.dataset.state = 'error' - statusText.textContent = 'Parse error' - } - - updateOutput() - } - - function switchTab(tabId: string) { - activeTab = tabId - for (const btn of tabButtons) { - btn.dataset.active = String(btn.dataset.tab === tabId) - } - updateOutput() - } - - function updateOutput() { - if (lastError) { - outputEl.replaceChildren() - const errEl = document.createElement('div') - errEl.className = 'comark-error' - errEl.textContent = lastError - outputEl.appendChild(errEl) - return - } - - outputEl.replaceChildren() - - if (activeTab === 'ast' && lastTree) { - renderAST(outputEl, lastTree) - } else if (activeTab === 'roundtrip' && lastRoundtrip !== null) { - renderRoundtrip(outputEl, lastRoundtrip, editor.value) - } else if (activeTab === 'info' && lastTree) { - renderInfo(outputEl, lastTree, editor.value.length) - } - } + new DevtoolsPanel(ctx) } diff --git a/packages/comark/src/devtools/renderer/output.ts b/packages/comark/src/devtools/renderer/output.ts index bbda1edd..f2014924 100644 --- a/packages/comark/src/devtools/renderer/output.ts +++ b/packages/comark/src/devtools/renderer/output.ts @@ -1,74 +1,13 @@ -type ComarkTree = { - nodes: unknown[] - frontmatter: Record - meta: Record -} +import type { ComarkTree } from '../types.ts' +/** Render a Comark AST as syntax-highlighted JSON into the given container */ export function renderAST(container: HTMLElement, tree: ComarkTree): void { const pre = document.createElement('pre') pre.innerHTML = syntaxHighlightJSON(tree) container.appendChild(pre) } -export function renderRoundtrip(container: HTMLElement, roundtrip: string, original: string): void { - const pre = document.createElement('pre') - pre.textContent = roundtrip - container.appendChild(pre) - - const isMatch = original.trim() === roundtrip.trim() - const indicator = document.createElement('div') - indicator.style.cssText = ` - margin-top: 12px; - padding: 8px 12px; - border-radius: 6px; - font-size: 12px; - font-weight: 500; - background: ${isMatch ? 'rgba(16, 185, 129, 0.1)' : 'rgba(245, 158, 11, 0.1)'}; - color: ${isMatch ? '#10b981' : '#f59e0b'}; - ` - indicator.textContent = isMatch - ? '✓ Roundtrip match — parse → render produces identical output' - : '⚠ Roundtrip mismatch — output differs from input (whitespace or formatting changes)' - container.appendChild(indicator) -} - -export function renderInfo(container: HTMLElement, tree: ComarkTree, inputLength: number): void { - const table = document.createElement('table') - table.className = 'comark-info-table' - - const rows = [ - ['Nodes', String(tree.nodes.length)], - ['Total nodes (deep)', String(countNodes(tree.nodes))], - ['Input length', `${inputLength} chars`], - ['Frontmatter keys', Object.keys(tree.frontmatter).join(', ') || '(none)'], - ['Meta keys', Object.keys(tree.meta).join(', ') || '(none)'], - ] - - if (Object.keys(tree.frontmatter).length > 0) { - rows.push(['Frontmatter', JSON.stringify(tree.frontmatter, null, 2)]) - } - - for (const [key, value] of rows) { - const tr = document.createElement('tr') - const td1 = document.createElement('td') - td1.textContent = key - const td2 = document.createElement('td') - td2.textContent = value - if (key === 'Frontmatter') { - const pre = document.createElement('pre') - pre.style.margin = '0' - pre.textContent = value - td2.textContent = '' - td2.appendChild(pre) - } - tr.appendChild(td1) - tr.appendChild(td2) - table.appendChild(tr) - } - - container.appendChild(table) -} - +/** Recursively count the total number of nodes in a Comark AST */ export function countNodes(nodes: unknown[]): number { let count = 0 for (const node of nodes) { @@ -112,6 +51,97 @@ function syntaxHighlightJSON(obj: unknown, indent = 0): string { return String(obj) } +/** Regex-based fallback markdown highlighter used when Shiki is unavailable */ +export function highlightMarkdown(source: string): string { + const lines = source.split('\n') + const result: string[] = [] + let inCodeBlock = false + + for (const line of lines) { + if (/^```/.test(line)) { + if (inCodeBlock) { + // Closing fence + result.push(`${escapeHTML(line)}`) + inCodeBlock = false + } else { + // Opening fence — highlight language name + const match = line.match(/^(```)(\w*)(.*)/) + if (match && match[2]) { + result.push( + `${escapeHTML(match[1])}` + + `${escapeHTML(match[2])}` + + (match[3] ? `${escapeHTML(match[3])}` : '') + ) + } else { + result.push(`${escapeHTML(line)}`) + } + inCodeBlock = true + } + } else if (inCodeBlock) { + result.push(`${escapeHTML(line)}`) + } else { + result.push(highlightLine(line)) + } + } + + return result.join('\n') +} + +function highlightLine(line: string): string { + const escaped = escapeHTML(line) + + // Frontmatter delimiters + if (/^---\s*$/.test(line)) { + return `${escaped}` + } + + // Headings + if (/^#{1,6}\s/.test(line)) { + return `${escaped}` + } + + // Component syntax (::name or ::name{...}) + if (/^:{2,3}\w*/.test(line)) { + return `${escaped}` + } + + // Code block fences — handled by highlightMarkdown statefully + + // Blockquotes + if (/^>\s?/.test(line)) { + return `${highlightInline(escaped)}` + } + + // List items + if (/^\s*[-*+]\s/.test(line) || /^\s*\d+\.\s/.test(line)) { + const match = escaped.match(/^(\s*(?:[-*+]|\d+\.)\s)(.*)$/) + if (match) { + return `${match[1]}${highlightInline(match[2])}` + } + } + + return highlightInline(escaped) +} + +function highlightInline(text: string): string { + return ( + text + // Bold (**...**) + .replace(/(\*\*|__)(.*?)\1/g, '$1$2$1') + // Italic (*...* or _..._) — avoid matching ** or __ + .replace(/(?*$1*') + // Inline code (`...`) + .replace(/(`)(.*?)(`)/g, '$1$2$3') + // Links ([text](url)) + .replace( + /(\[)(.*?)(\]\()(.*?)(\))/g, + '$1$2$3$4$5' + ) + // Component attributes ({key="value"}) + .replace(/(\{)(.*?)(\})/g, '$1$2$3') + ) +} + function escapeHTML(str: string): string { return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') } diff --git a/packages/comark/src/devtools/renderer/panel.ts b/packages/comark/src/devtools/renderer/panel.ts new file mode 100644 index 00000000..d4135d8e --- /dev/null +++ b/packages/comark/src/devtools/renderer/panel.ts @@ -0,0 +1,255 @@ +import type { DockClientScriptContext } from '@vitejs/devtools-kit/client' +import type { ComarkInstanceSummary, ComarkTree, ViewState, Theme } from '../types.ts' +import { STYLES } from './styles.ts' +import { applyTheme, createThemeToggle } from './theme.ts' +import { renderAST, highlightMarkdown } from './output.ts' +import { createTabBar, createTabRight, createEditor, createLoadingState, createEmptyState } from './dom.ts' + +const TAB_DEFS = [ + { id: 'markdown', label: 'Markdown' }, + { id: 'ast', label: 'AST' }, +] as const + +const POLL_INTERVAL_MS = 1500 +const PARSE_DEBOUNCE_MS = 300 + +/** + * Manages the Comark devtools panel lifecycle, state, and DOM. + * + * Handles tab switching, instance polling, markdown editing with + * live parsing via RPC, and AST visualization. + */ +export class DevtoolsPanel { + private ctx: DockClientScriptContext + + // DOM refs + private rootEl!: HTMLElement + private contentEl!: HTMLElement + private editor!: HTMLTextAreaElement + private editorHighlight!: HTMLPreElement + private editorWrap!: HTMLDivElement + private instanceDot!: HTMLElement + private instanceLabel!: HTMLElement + private loadingEl!: HTMLDivElement + private emptyState!: HTMLDivElement + private tabButtons: HTMLButtonElement[] = [] + + // State + private activeTab = 'markdown' + private parseTimer: ReturnType | undefined + private lastTree: ComarkTree | null = null + private lastHighlightHtml: string | null = null + private lastError: string | null = null + private activeInstance: ComarkInstanceSummary | null = null + private initialLoading = true + private pollInterval: ReturnType | undefined + private theme: Theme = 'auto' + private currentView: ViewState | null = null + private astOutputEl: HTMLElement | null = null + + constructor(ctx: DockClientScriptContext) { + this.ctx = ctx + ctx.current.events.on('dom:panel:mounted', (panel: HTMLElement) => this.mount(panel)) + ctx.current.events.on('entry:deactivated', () => this.cleanup()) + } + + private mount(panel: HTMLElement): void { + this.cleanup() + + // Inject styles + const style = document.createElement('style') + style.textContent = STYLES + panel.appendChild(style) + + // Root container + const root = document.createElement('div') + root.className = 'comark-devtools' + this.rootEl = root + applyTheme(this.rootEl, this.theme) + panel.appendChild(root) + + // Tab bar + const { tabs, tabButtons } = createTabBar( + [...TAB_DEFS], + this.activeTab, + (id) => this.switchTab(id), + ) + this.tabButtons = tabButtons + root.appendChild(tabs) + + // Tab bar right section: instance status + theme toggle + const tabRight = createTabRight() + this.instanceDot = tabRight.dot + this.instanceLabel = tabRight.label + + const themeToggle = createThemeToggle(this.theme, (t) => { + this.theme = t + applyTheme(this.rootEl, this.theme) + }) + tabRight.container.appendChild(themeToggle) + tabs.appendChild(tabRight.container) + + // Content area + this.contentEl = document.createElement('div') + this.contentEl.className = 'comark-content' + root.appendChild(this.contentEl) + + // Editor + const editorResult = createEditor( + () => { + this.syncHighlight() + this.scheduleParse() + }, + (scrollTop, scrollLeft) => { + this.editorHighlight.scrollTop = scrollTop + this.editorHighlight.scrollLeft = scrollLeft + }, + ) + this.editorWrap = editorResult.wrap + this.editor = editorResult.textarea + this.editorHighlight = editorResult.highlight + this.syncHighlight() + + // Loading & empty states + this.loadingEl = createLoadingState() + this.emptyState = createEmptyState() + + // Initial render + polling + this.updateView() + this.pollInstances() + this.pollInterval = setInterval(() => this.pollInstances(), POLL_INTERVAL_MS) + } + + private cleanup(): void { + clearInterval(this.pollInterval) + this.pollInterval = undefined + clearTimeout(this.parseTimer) + this.parseTimer = undefined + this.activeInstance = null + this.initialLoading = true + this.tabButtons = [] + this.currentView = null + } + + private scheduleParse(): void { + clearTimeout(this.parseTimer) + this.parseTimer = setTimeout(() => this.parseMarkdown(), PARSE_DEBOUNCE_MS) + } + + private syncHighlight(): void { + if (this.lastHighlightHtml) { + this.editorHighlight.innerHTML = this.lastHighlightHtml + '\n' + } else { + this.editorHighlight.innerHTML = highlightMarkdown(this.editor.value) + '\n' + } + } + + private async pollInstances(): Promise { + let instances: ComarkInstanceSummary[] = [] + try { + const data = (await this.ctx.rpc.call('comark:list-instances')) as ComarkInstanceSummary[] + instances = data || [] + } catch { + instances = [] + } + + this.initialLoading = false + + const current = instances[0] || null + const prevId = this.activeInstance?.id + + this.activeInstance = current + + if (current) { + this.instanceDot.dataset.connected = 'true' + this.instanceLabel.textContent = current.label || current.id + + if (current.id !== prevId && current.markdown) { + this.editor.value = current.markdown + this.syncHighlight() + this.scheduleParse() + } + } else { + this.instanceDot.dataset.connected = 'false' + this.instanceLabel.textContent = '' + } + + this.updateView() + } + + private async parseMarkdown(): Promise { + this.lastError = null + + try { + const [tree, highlightHtml] = await Promise.all([ + this.ctx.rpc.call('comark:parse', this.editor.value) as Promise, + this.ctx.rpc.call('comark:highlight', this.editor.value) as Promise, + ]) + this.lastTree = tree + this.lastHighlightHtml = highlightHtml + this.lastError = null + } catch (err) { + this.lastError = err instanceof Error ? err.message : String(err) + } + + this.syncHighlight() + this.updateView() + } + + private switchTab(tabId: string): void { + this.activeTab = tabId + this.currentView = null // Force rebuild on tab switch + for (const btn of this.tabButtons) { + btn.dataset.active = String(btn.dataset.tab === tabId) + } + this.updateView() + } + + private resolveDesiredView(): ViewState { + if (this.initialLoading) return 'loading' + if (!this.activeInstance) return 'empty' + if (this.activeTab === 'markdown') return 'markdown' + if (this.lastError) return 'ast-error' + return 'ast' + } + + private updateView(): void { + if (!this.contentEl) return + + const desired = this.resolveDesiredView() + + // Only rebuild DOM when the view type changes + if (desired !== this.currentView) { + this.contentEl.replaceChildren() + this.currentView = desired + this.astOutputEl = null + + if (desired === 'loading') { + this.contentEl.appendChild(this.loadingEl) + } else if (desired === 'empty') { + this.contentEl.appendChild(this.emptyState) + } else if (desired === 'markdown') { + this.contentEl.appendChild(this.editorWrap) + } else if (desired === 'ast-error') { + const errEl = document.createElement('div') + errEl.className = 'comark-error' + errEl.textContent = this.lastError! + this.contentEl.appendChild(errEl) + } else if (desired === 'ast' && this.lastTree) { + this.astOutputEl = document.createElement('div') + this.astOutputEl.className = 'comark-output' + renderAST(this.astOutputEl, this.lastTree) + this.contentEl.appendChild(this.astOutputEl) + } + } else { + // Same view — update content in place + if (desired === 'ast-error') { + const errEl = this.contentEl.querySelector('.comark-error') + if (errEl) errEl.textContent = this.lastError! + } else if (desired === 'ast' && this.lastTree && this.astOutputEl) { + this.astOutputEl.replaceChildren() + renderAST(this.astOutputEl, this.lastTree) + } + } + } +} diff --git a/packages/comark/src/devtools/renderer/styles.css b/packages/comark/src/devtools/renderer/styles.css index 69696de3..917c2931 100644 --- a/packages/comark/src/devtools/renderer/styles.css +++ b/packages/comark/src/devtools/renderer/styles.css @@ -13,6 +13,7 @@ --comark-devtools-success: #22c55e; display: flex; + flex-direction: column; height: 100%; font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif; color: var(--comark-devtools-text); @@ -26,61 +27,10 @@ box-sizing: border-box; } -/* Editor pane */ -.comark-editor-pane { - flex: 1; - display: flex; - flex-direction: column; - min-width: 0; - border-right: 1px solid var(--comark-devtools-border); -} - -.comark-pane-header { - display: flex; - align-items: center; - gap: 8px; - padding: 6px 12px; - border-bottom: 1px solid var(--comark-devtools-border); - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--comark-devtools-text-muted); - flex-shrink: 0; -} - -.comark-editor { - flex: 1; - width: 100%; - padding: 12px; - border: none; - resize: none; - outline: none; - background: transparent; - color: inherit; - font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace; - font-size: 13px; - line-height: 1.6; - tab-size: 2; -} - -.comark-editor::placeholder { - color: var(--comark-devtools-text-muted); - opacity: 0.5; -} - -/* Output pane */ -.comark-output-pane { - flex: 1; - display: flex; - flex-direction: column; - min-width: 0; - overflow: hidden; -} - /* Tabs */ .comark-tabs { display: flex; + align-items: center; border-bottom: 1px solid var(--comark-devtools-border); flex-shrink: 0; } @@ -110,32 +60,128 @@ border-bottom-color: var(--comark-devtools-primary); } -/* Status bar */ -.comark-status { +/* Tab bar right section */ +.comark-tab-right { + margin-left: auto; display: flex; align-items: center; - gap: 12px; - padding: 4px 12px; - border-top: 1px solid var(--comark-devtools-border); - font-size: 11px; - color: var(--comark-devtools-text-muted); - flex-shrink: 0; + gap: 8px; + padding: 0 12px; } -.comark-status-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--comark-devtools-success); - flex-shrink: 0; +/* Content area */ +.comark-content { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + overflow: hidden; } -.comark-status-dot[data-state='error'] { - background: var(--comark-devtools-error); +/* Editor overlay */ +.comark-editor-wrap { + flex: 1; + position: relative; + overflow: hidden; } -.comark-status-dot[data-state='parsing'] { - background: var(--comark-devtools-warning); +.comark-editor-highlight, +.comark-editor { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + padding: 12px; + margin: 0; + border: none; + font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace; + font-size: 13px; + line-height: 1.6; + tab-size: 2; + white-space: pre-wrap; + word-break: break-word; + overflow: auto; +} + +.comark-editor-highlight { + color: var(--comark-devtools-text); + pointer-events: none; +} + +.comark-editor { + color: transparent; + caret-color: var(--comark-devtools-text); + background: transparent; + resize: none; + outline: none; + -webkit-text-fill-color: transparent; +} + +.comark-editor::placeholder { + color: var(--comark-devtools-text-muted); + -webkit-text-fill-color: var(--comark-devtools-text-muted); + opacity: 0.5; +} + +/* Shiki dual-theme support */ +.comark-editor-highlight span { + color: var(--shiki-light); +} +.comark-devtools[data-theme='dark'] .comark-editor-highlight span, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-editor-highlight span { + color: var(--shiki-dark); +} + +/* Markdown syntax highlighting (regex fallback) */ +.comark-md-heading { + color: #9333ea; + font-weight: 600; +} +.comark-md-bold { + color: var(--comark-devtools-text); + font-weight: 700; +} +.comark-md-italic { + font-style: italic; +} +.comark-md-code { + color: #d97706; + background: var(--comark-devtools-bg-muted); + border-radius: 3px; + padding: 1px 3px; +} +.comark-md-code-fence { + color: #d97706; +} +.comark-md-code-lang { + color: #059669; + font-weight: 600; +} +.comark-md-code-content { + color: var(--comark-devtools-text-muted); +} +.comark-md-link { + color: #2563eb; +} +.comark-md-url { + opacity: 0.6; +} +.comark-md-component { + color: #059669; + font-weight: 600; +} +.comark-md-attrs { + color: #71717a; +} +.comark-md-blockquote { + color: #71717a; + font-style: italic; +} +.comark-md-list-marker { + color: #d97706; +} +.comark-md-frontmatter { + color: #71717a; } /* Output content */ @@ -172,24 +218,6 @@ color: #dc2626; } -/* Info table */ -.comark-info-table { - width: 100%; - border-collapse: collapse; - font-size: 12px; -} - -.comark-info-table td { - padding: 6px 12px; - border-bottom: 1px solid var(--comark-devtools-border); -} - -.comark-info-table td:first-child { - font-weight: 600; - width: 140px; - color: var(--comark-devtools-text-muted); -} - /* Error display */ .comark-error { color: var(--comark-devtools-error); @@ -199,18 +227,7 @@ white-space: pre-wrap; } -/* Instance bar */ -.comark-instance-bar { - display: flex; - align-items: center; - gap: 8px; - padding: 4px 12px; - border-bottom: 1px solid var(--comark-devtools-border); - font-size: 11px; - flex-shrink: 0; - background: var(--comark-devtools-bg-soft); -} - +/* Instance status */ .comark-instance-dot { width: 6px; height: 6px; @@ -238,12 +255,76 @@ } .comark-instance-label { + font-size: 11px; font-weight: 500; color: var(--comark-devtools-text-muted); } +/* Loading state */ +.comark-loading-state { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 14px; + color: var(--comark-devtools-text-muted); + padding: 32px; + user-select: none; +} + +.comark-loading-spinner { + width: 28px; + height: 28px; + border: 3px solid var(--comark-devtools-border); + border-top-color: var(--comark-devtools-primary); + border-radius: 50%; + animation: comark-spin 0.8s linear infinite; +} + +@keyframes comark-spin { + to { transform: rotate(360deg); } +} + +.comark-loading-text { + margin: 0; + font-size: 13px; +} + +/* Empty state */ +.comark-empty-state { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + color: var(--comark-devtools-text-muted); + padding: 32px; + user-select: none; +} + +.comark-empty-icon { + opacity: 0.25; +} + +.comark-empty-title { + margin: 0; + font-size: 13px; + text-align: center; + line-height: 1.5; +} + +.comark-empty-state code { + font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace; + font-size: 12px; + padding: 2px 6px; + border-radius: 4px; + background: var(--comark-devtools-bg-muted); +} + +/* Theme toggle */ .comark-theme-toggle { - margin-left: auto; display: flex; align-items: center; justify-content: center; @@ -301,3 +382,39 @@ .comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool { color: #f87171; } +.comark-devtools[data-theme='dark'] .comark-md-heading, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-heading { + color: #c084fc; +} +.comark-devtools[data-theme='dark'] .comark-md-code, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code { + color: #fbbf24; +} +.comark-devtools[data-theme='dark'] .comark-md-code-fence, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-fence { + color: #fbbf24; +} +.comark-devtools[data-theme='dark'] .comark-md-code-lang, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-lang { + color: #34d399; +} +.comark-devtools[data-theme='dark'] .comark-md-link, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-link { + color: #60a5fa; +} +.comark-devtools[data-theme='dark'] .comark-md-component, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-component { + color: #34d399; +} +.comark-devtools[data-theme='dark'] .comark-md-list-marker, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-list-marker { + color: #fbbf24; +} +.comark-devtools[data-theme='dark'] .comark-md-blockquote, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-blockquote { + color: #a1a1aa; +} +.comark-devtools[data-theme='dark'] .comark-md-frontmatter, +.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-frontmatter { + color: #a1a1aa; +} diff --git a/packages/comark/src/devtools/renderer/styles.ts b/packages/comark/src/devtools/renderer/styles.ts index aed1348c..da59b6e9 100644 --- a/packages/comark/src/devtools/renderer/styles.ts +++ b/packages/comark/src/devtools/renderer/styles.ts @@ -1,3 +1,2 @@ // @generated — do not edit. Edit styles.css instead, then run: node scripts/sync-devtools-css.mjs -export const STYLES = - ".comark-devtools {\n --comark-devtools-bg: #ffffff;\n --comark-devtools-bg-soft: #fafafa;\n --comark-devtools-bg-muted: #f4f4f5;\n --comark-devtools-text: #18181b;\n --comark-devtools-text-muted: #71717a;\n --comark-devtools-border: #e4e4e7;\n --comark-devtools-primary: #eab308;\n --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n --comark-devtools-accent: #ca8a04;\n --comark-devtools-error: #ef4444;\n --comark-devtools-warning: #f59e0b;\n --comark-devtools-success: #22c55e;\n\n display: flex;\n height: 100%;\n font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif;\n color: var(--comark-devtools-text);\n background: var(--comark-devtools-bg);\n font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n box-sizing: border-box;\n}\n\n/* Editor pane */\n.comark-editor-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n border-right: 1px solid var(--comark-devtools-border);\n}\n\n.comark-pane-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-editor {\n flex: 1;\n width: 100%;\n padding: 12px;\n border: none;\n resize: none;\n outline: none;\n background: transparent;\n color: inherit;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 13px;\n line-height: 1.6;\n tab-size: 2;\n}\n\n.comark-editor::placeholder {\n color: var(--comark-devtools-text-muted);\n opacity: 0.5;\n}\n\n/* Output pane */\n.comark-output-pane {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n overflow: hidden;\n}\n\n/* Tabs */\n.comark-tabs {\n display: flex;\n border-bottom: 1px solid var(--comark-devtools-border);\n flex-shrink: 0;\n}\n\n.comark-tab {\n padding: 6px 12px;\n border: none;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n transition:\n color 0.15s,\n border-color 0.15s;\n}\n\n.comark-tab:hover {\n color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active='true'] {\n color: var(--comark-devtools-text);\n border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Status bar */\n.comark-status {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 4px 12px;\n border-top: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n color: var(--comark-devtools-text-muted);\n flex-shrink: 0;\n}\n\n.comark-status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--comark-devtools-success);\n flex-shrink: 0;\n}\n\n.comark-status-dot[data-state='error'] {\n background: var(--comark-devtools-error);\n}\n\n.comark-status-dot[data-state='parsing'] {\n background: var(--comark-devtools-warning);\n}\n\n/* Output content */\n.comark-output {\n flex: 1;\n overflow: auto;\n padding: 12px;\n}\n\n.comark-output pre {\n margin: 0;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key {\n color: #9333ea;\n}\n.comark-tree-string {\n color: #16a34a;\n}\n.comark-tree-number {\n color: #2563eb;\n}\n.comark-tree-null {\n color: #a1a1aa;\n font-style: italic;\n}\n.comark-tree-bool {\n color: #dc2626;\n}\n\n/* Info table */\n.comark-info-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.comark-info-table td {\n padding: 6px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n}\n\n.comark-info-table td:first-child {\n font-weight: 600;\n width: 140px;\n color: var(--comark-devtools-text-muted);\n}\n\n/* Error display */\n.comark-error {\n color: var(--comark-devtools-error);\n padding: 12px;\n font-size: 12px;\n font-family: 'Geist Mono', ui-monospace, monospace;\n white-space: pre-wrap;\n}\n\n/* Instance bar */\n.comark-instance-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 12px;\n border-bottom: 1px solid var(--comark-devtools-border);\n font-size: 11px;\n flex-shrink: 0;\n background: var(--comark-devtools-bg-soft);\n}\n\n.comark-instance-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected='true'] {\n background: var(--comark-devtools-success);\n animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected='false'] {\n background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n.comark-instance-label {\n font-weight: 500;\n color: var(--comark-devtools-text-muted);\n}\n\n.comark-theme-toggle {\n margin-left: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 6px;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n transition:\n color 0.15s,\n border-color 0.15s,\n background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n color: var(--comark-devtools-text);\n border-color: var(--comark-devtools-primary);\n background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme='dark'],\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) {\n --comark-devtools-bg: #18181b;\n --comark-devtools-bg-soft: #27272a;\n --comark-devtools-bg-muted: #3f3f46;\n --comark-devtools-text: #fafafa;\n --comark-devtools-text-muted: #a1a1aa;\n --comark-devtools-border: #3f3f46;\n --comark-devtools-primary: #facc15;\n --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme='dark'] .comark-tree-key,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key {\n color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-string,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string {\n color: #4ade80;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-number,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number {\n color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-null,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null {\n color: #71717a;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-bool,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool {\n color: #f87171;\n}\n" +export const STYLES = ".comark-devtools {\n --comark-devtools-bg: #ffffff;\n --comark-devtools-bg-soft: #fafafa;\n --comark-devtools-bg-muted: #f4f4f5;\n --comark-devtools-text: #18181b;\n --comark-devtools-text-muted: #71717a;\n --comark-devtools-border: #e4e4e7;\n --comark-devtools-primary: #eab308;\n --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n --comark-devtools-accent: #ca8a04;\n --comark-devtools-error: #ef4444;\n --comark-devtools-warning: #f59e0b;\n --comark-devtools-success: #22c55e;\n\n display: flex;\n flex-direction: column;\n height: 100%;\n font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif;\n color: var(--comark-devtools-text);\n background: var(--comark-devtools-bg);\n font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n box-sizing: border-box;\n}\n\n/* Tabs */\n.comark-tabs {\n display: flex;\n align-items: center;\n border-bottom: 1px solid var(--comark-devtools-border);\n flex-shrink: 0;\n}\n\n.comark-tab {\n padding: 6px 12px;\n border: none;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n transition:\n color 0.15s,\n border-color 0.15s;\n}\n\n.comark-tab:hover {\n color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active='true'] {\n color: var(--comark-devtools-text);\n border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Tab bar right section */\n.comark-tab-right {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 12px;\n}\n\n/* Content area */\n.comark-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n}\n\n/* Editor overlay */\n.comark-editor-wrap {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n.comark-editor-highlight,\n.comark-editor {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n padding: 12px;\n margin: 0;\n border: none;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 13px;\n line-height: 1.6;\n tab-size: 2;\n white-space: pre-wrap;\n word-break: break-word;\n overflow: auto;\n}\n\n.comark-editor-highlight {\n color: var(--comark-devtools-text);\n pointer-events: none;\n}\n\n.comark-editor {\n color: transparent;\n caret-color: var(--comark-devtools-text);\n background: transparent;\n resize: none;\n outline: none;\n -webkit-text-fill-color: transparent;\n}\n\n.comark-editor::placeholder {\n color: var(--comark-devtools-text-muted);\n -webkit-text-fill-color: var(--comark-devtools-text-muted);\n opacity: 0.5;\n}\n\n/* Shiki dual-theme support */\n.comark-editor-highlight span {\n color: var(--shiki-light);\n}\n.comark-devtools[data-theme='dark'] .comark-editor-highlight span,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-editor-highlight span {\n color: var(--shiki-dark);\n}\n\n/* Markdown syntax highlighting (regex fallback) */\n.comark-md-heading {\n color: #9333ea;\n font-weight: 600;\n}\n.comark-md-bold {\n color: var(--comark-devtools-text);\n font-weight: 700;\n}\n.comark-md-italic {\n font-style: italic;\n}\n.comark-md-code {\n color: #d97706;\n background: var(--comark-devtools-bg-muted);\n border-radius: 3px;\n padding: 1px 3px;\n}\n.comark-md-code-fence {\n color: #d97706;\n}\n.comark-md-code-lang {\n color: #059669;\n font-weight: 600;\n}\n.comark-md-code-content {\n color: var(--comark-devtools-text-muted);\n}\n.comark-md-link {\n color: #2563eb;\n}\n.comark-md-url {\n opacity: 0.6;\n}\n.comark-md-component {\n color: #059669;\n font-weight: 600;\n}\n.comark-md-attrs {\n color: #71717a;\n}\n.comark-md-blockquote {\n color: #71717a;\n font-style: italic;\n}\n.comark-md-list-marker {\n color: #d97706;\n}\n.comark-md-frontmatter {\n color: #71717a;\n}\n\n/* Output content */\n.comark-output {\n flex: 1;\n overflow: auto;\n padding: 12px;\n}\n\n.comark-output pre {\n margin: 0;\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key {\n color: #9333ea;\n}\n.comark-tree-string {\n color: #16a34a;\n}\n.comark-tree-number {\n color: #2563eb;\n}\n.comark-tree-null {\n color: #a1a1aa;\n font-style: italic;\n}\n.comark-tree-bool {\n color: #dc2626;\n}\n\n/* Error display */\n.comark-error {\n color: var(--comark-devtools-error);\n padding: 12px;\n font-size: 12px;\n font-family: 'Geist Mono', ui-monospace, monospace;\n white-space: pre-wrap;\n}\n\n/* Instance status */\n.comark-instance-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected='true'] {\n background: var(--comark-devtools-success);\n animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected='false'] {\n background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n.comark-instance-label {\n font-size: 11px;\n font-weight: 500;\n color: var(--comark-devtools-text-muted);\n}\n\n/* Loading state */\n.comark-loading-state {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 14px;\n color: var(--comark-devtools-text-muted);\n padding: 32px;\n user-select: none;\n}\n\n.comark-loading-spinner {\n width: 28px;\n height: 28px;\n border: 3px solid var(--comark-devtools-border);\n border-top-color: var(--comark-devtools-primary);\n border-radius: 50%;\n animation: comark-spin 0.8s linear infinite;\n}\n\n@keyframes comark-spin {\n to { transform: rotate(360deg); }\n}\n\n.comark-loading-text {\n margin: 0;\n font-size: 13px;\n}\n\n/* Empty state */\n.comark-empty-state {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n color: var(--comark-devtools-text-muted);\n padding: 32px;\n user-select: none;\n}\n\n.comark-empty-icon {\n opacity: 0.25;\n}\n\n.comark-empty-title {\n margin: 0;\n font-size: 13px;\n text-align: center;\n line-height: 1.5;\n}\n\n.comark-empty-state code {\n font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n font-size: 12px;\n padding: 2px 6px;\n border-radius: 4px;\n background: var(--comark-devtools-bg-muted);\n}\n\n/* Theme toggle */\n.comark-theme-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: 1px solid var(--comark-devtools-border);\n border-radius: 6px;\n background: transparent;\n color: var(--comark-devtools-text-muted);\n cursor: pointer;\n transition:\n color 0.15s,\n border-color 0.15s,\n background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n color: var(--comark-devtools-text);\n border-color: var(--comark-devtools-primary);\n background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme='dark'],\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) {\n --comark-devtools-bg: #18181b;\n --comark-devtools-bg-soft: #27272a;\n --comark-devtools-bg-muted: #3f3f46;\n --comark-devtools-text: #fafafa;\n --comark-devtools-text-muted: #a1a1aa;\n --comark-devtools-border: #3f3f46;\n --comark-devtools-primary: #facc15;\n --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme='dark'] .comark-tree-key,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key {\n color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-string,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string {\n color: #4ade80;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-number,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number {\n color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-null,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null {\n color: #71717a;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-bool,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool {\n color: #f87171;\n}\n.comark-devtools[data-theme='dark'] .comark-md-heading,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-heading {\n color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code {\n color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-fence,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-fence {\n color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-lang,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-lang {\n color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-link,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-link {\n color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-component,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-component {\n color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-list-marker,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-list-marker {\n color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-blockquote,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-blockquote {\n color: #a1a1aa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-frontmatter,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-frontmatter {\n color: #a1a1aa;\n}\n" diff --git a/packages/comark/src/devtools/renderer/theme.ts b/packages/comark/src/devtools/renderer/theme.ts index 70c22f26..f0accc5a 100644 --- a/packages/comark/src/devtools/renderer/theme.ts +++ b/packages/comark/src/devtools/renderer/theme.ts @@ -1,7 +1,7 @@ +import type { Theme } from '../types.ts' import { LUCIDE_MOON, LUCIDE_MONITOR, LUCIDE_SUN } from '../constants/index.ts' -export type Theme = 'auto' | 'light' | 'dark' - +/** Apply a color scheme to the devtools root element via `data-*` attributes */ export function applyTheme(rootEl: HTMLElement, theme: Theme): void { if (theme === 'auto') { delete rootEl.dataset.theme @@ -13,12 +13,14 @@ export function applyTheme(rootEl: HTMLElement, theme: Theme): void { } } +/** Return the Lucide SVG icon string matching the given theme */ export function getThemeIcon(theme: Theme): string { if (theme === 'dark') return LUCIDE_MOON if (theme === 'light') return LUCIDE_SUN return LUCIDE_MONITOR } +/** Create a button that cycles through `auto, light, dark` themes */ export function createThemeToggle(initial: Theme, onChange: (theme: Theme) => void): HTMLButtonElement { let theme = initial diff --git a/packages/comark/src/devtools/types.ts b/packages/comark/src/devtools/types.ts new file mode 100644 index 00000000..200c3912 --- /dev/null +++ b/packages/comark/src/devtools/types.ts @@ -0,0 +1,59 @@ +import type { ComarkTree } from '../types.ts' + +export type { ComarkTree } + +/** Minimal subset of Vite's `import.meta.hot` API used by the devtools bridge */ +export interface HotModule { + send(event: string, data?: any): void + on(event: string, cb: (...args: any[]) => void): void +} + +/** A live Comark renderer instance tracked by the devtools registry */ +export interface ComarkInstance { + /** Unique instance identifier (e.g. `comark-1`) */ + id: string + /** Human-readable label — typically the current page URL path or hash */ + label?: string + /** Current parsed AST */ + tree: ComarkTree + /** Current markdown source (if available) */ + markdown?: string +} + +/** Serializable snapshot of a {@link ComarkInstance} sent over RPC */ +export interface ComarkInstanceSummary { + id: string + label?: string + markdown?: string + /** Total number of top-level AST nodes */ + nodeCount: number +} + +/** Options accepted by {@link registerDevtoolsInstance} */ +export interface RegisterInstanceOptions { + /** The HMR handle (`import.meta.hot`). Pass `null` to skip registration. */ + hot: HotModule | null | undefined + /** Initial tree (may be empty for string renderers) */ + tree: ComarkTree + /** Initial markdown source, if known */ + markdown?: string +} + +/** Handles returned after a successful instance registration */ +export interface RegisteredInstance { + /** The assigned instance id */ + id: string + /** Push an updated tree and/or markdown source to the devtools */ + update(patch: { tree?: ComarkTree; markdown?: string }): void + /** Remove the instance from the devtools registry */ + unregister(): void +} + +/** Callback invoked whenever the set of tracked instances changes */ +export type InstanceListener = (instances: Map) => void + +/** Active view in the devtools panel */ +export type ViewState = 'loading' | 'empty' | 'markdown' | 'ast-error' | 'ast' + +/** Color scheme preference for the devtools panel UI */ +export type Theme = 'auto' | 'light' | 'dark' diff --git a/packages/comark/src/devtools/vite.ts b/packages/comark/src/devtools/vite.ts index 05b32f5e..01a1d3b7 100644 --- a/packages/comark/src/devtools/vite.ts +++ b/packages/comark/src/devtools/vite.ts @@ -1,17 +1,33 @@ /// import type { Plugin, ViteDevServer } from 'vite' -import type { ComarkTree } from '../types.ts' -import type { ComarkInstanceSummary } from './registry.ts' -import { COMARK_DARK_ICON, COMARK_LIGHT_ICON } from './constants/index.ts' +import type { Highlighter } from 'shiki' +import type { ComarkTree, ComarkInstanceSummary } from './types.ts' +import { COMARK_DARK_ICON, COMARK_LIGHT_ICON, DEVTOOLS_SHIKI_LANGS, DEVTOOLS_SHIKI_THEMES } from './constants/index.ts' declare module '@vitejs/devtools-kit' { interface DevToolsRpcServerFunctions { 'comark:parse': (markdown: string) => Promise - 'comark:render-markdown': (markdown: string) => Promise + 'comark:highlight': (markdown: string) => Promise 'comark:list-instances': () => Promise } } +// Lazy Shiki highlighter singleton +let highlighterPromise: Promise | null = null +function getHighlighter(): Promise { + if (!highlighterPromise) { + highlighterPromise = import('shiki') + .then((shiki) => + shiki.createHighlighter({ + themes: [...DEVTOOLS_SHIKI_THEMES], + langs: [...DEVTOOLS_SHIKI_LANGS], + }) + ) + .catch(() => null) + } + return highlighterPromise +} + /** * Vite plugin that registers a Comark playground panel in Vite DevTools. * @@ -57,15 +73,19 @@ export function comarkDevtools(): Plugin { }, }) - // RPC: render tree back to markdown (roundtrip) + // RPC: highlight markdown using Shiki with the mdc grammar ctx.rpc.register({ - name: 'comark:render-markdown', + name: 'comark:highlight', type: 'query', handler: async (markdown: string) => { - const { parse } = await import('../index.ts') - const { renderMarkdown } = await import('../render.ts') - const tree = await parse(markdown) - return await renderMarkdown(tree) + const highlighter = await getHighlighter() + if (!highlighter) return null + const html = highlighter.codeToHtml(markdown, { + lang: 'mdc', + themes: { light: DEVTOOLS_SHIKI_THEMES[0], dark: DEVTOOLS_SHIKI_THEMES[1] }, + defaultColor: false, + }) + return html.replace(/^]*>]*>/, '').replace(/<\/code><\/pre>$/, '') }, }) From f06d6d22e3c42b09816b6693cc9a8d787ba815e5 Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Wed, 3 Jun 2026 18:53:43 -0600 Subject: [PATCH 09/28] chore: rename file --- .../comark/src/devtools/{constants/index.ts => constants.ts} | 0 packages/comark/src/devtools/vite.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/comark/src/devtools/{constants/index.ts => constants.ts} (100%) diff --git a/packages/comark/src/devtools/constants/index.ts b/packages/comark/src/devtools/constants.ts similarity index 100% rename from packages/comark/src/devtools/constants/index.ts rename to packages/comark/src/devtools/constants.ts diff --git a/packages/comark/src/devtools/vite.ts b/packages/comark/src/devtools/vite.ts index 01a1d3b7..8a920953 100644 --- a/packages/comark/src/devtools/vite.ts +++ b/packages/comark/src/devtools/vite.ts @@ -2,7 +2,7 @@ import type { Plugin, ViteDevServer } from 'vite' import type { Highlighter } from 'shiki' import type { ComarkTree, ComarkInstanceSummary } from './types.ts' -import { COMARK_DARK_ICON, COMARK_LIGHT_ICON, DEVTOOLS_SHIKI_LANGS, DEVTOOLS_SHIKI_THEMES } from './constants/index.ts' +import { COMARK_DARK_ICON, COMARK_LIGHT_ICON, DEVTOOLS_SHIKI_LANGS, DEVTOOLS_SHIKI_THEMES } from './constants.ts' declare module '@vitejs/devtools-kit' { interface DevToolsRpcServerFunctions { From e7f37184851cfed53e4acdf270275f907fff7d17 Mon Sep 17 00:00:00 2001 From: Arash Ari Sheyda Date: Wed, 3 Jun 2026 18:59:55 -0600 Subject: [PATCH 10/28] chore: lint --- examples/1.frameworks/nextjs/next-env.d.ts | 2 +- packages/comark/src/devtools/renderer/dom.ts | 4 ++-- packages/comark/src/devtools/renderer/output.ts | 4 ++-- packages/comark/src/devtools/renderer/panel.ts | 8 ++------ packages/comark/src/devtools/renderer/styles.css | 4 +++- packages/comark/src/devtools/renderer/styles.ts | 3 ++- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/examples/1.frameworks/nextjs/next-env.d.ts b/examples/1.frameworks/nextjs/next-env.d.ts index 9edff1c7..c4e7c0eb 100644 --- a/examples/1.frameworks/nextjs/next-env.d.ts +++ b/examples/1.frameworks/nextjs/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/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. diff --git a/packages/comark/src/devtools/renderer/dom.ts b/packages/comark/src/devtools/renderer/dom.ts index c48e4f5a..c681ddb4 100644 --- a/packages/comark/src/devtools/renderer/dom.ts +++ b/packages/comark/src/devtools/renderer/dom.ts @@ -4,7 +4,7 @@ export function createTabBar( tabDefs: { id: string; label: string }[], activeTab: string, - onSwitch: (tabId: string) => void, + onSwitch: (tabId: string) => void ): { tabs: HTMLDivElement; tabButtons: HTMLButtonElement[] } { const tabs = document.createElement('div') tabs.className = 'comark-tabs' @@ -50,7 +50,7 @@ export function createTabRight(): { /** Create the markdown editor with a transparent textarea overlaying a syntax-highlighted `
` */
 export function createEditor(
   onInput: () => void,
-  onScroll: (scrollTop: number, scrollLeft: number) => void,
+  onScroll: (scrollTop: number, scrollLeft: number) => void
 ): { wrap: HTMLDivElement; textarea: HTMLTextAreaElement; highlight: HTMLPreElement } {
   const wrap = document.createElement('div')
   wrap.className = 'comark-editor-wrap'
diff --git a/packages/comark/src/devtools/renderer/output.ts b/packages/comark/src/devtools/renderer/output.ts
index f2014924..8ce75f3b 100644
--- a/packages/comark/src/devtools/renderer/output.ts
+++ b/packages/comark/src/devtools/renderer/output.ts
@@ -69,8 +69,8 @@ export function highlightMarkdown(source: string): string {
         if (match && match[2]) {
           result.push(
             `${escapeHTML(match[1])}` +
-            `${escapeHTML(match[2])}` +
-            (match[3] ? `${escapeHTML(match[3])}` : '')
+              `${escapeHTML(match[2])}` +
+              (match[3] ? `${escapeHTML(match[3])}` : '')
           )
         } else {
           result.push(`${escapeHTML(line)}`)
diff --git a/packages/comark/src/devtools/renderer/panel.ts b/packages/comark/src/devtools/renderer/panel.ts
index d4135d8e..33a0bf0c 100644
--- a/packages/comark/src/devtools/renderer/panel.ts
+++ b/packages/comark/src/devtools/renderer/panel.ts
@@ -69,11 +69,7 @@ export class DevtoolsPanel {
     panel.appendChild(root)
 
     // Tab bar
-    const { tabs, tabButtons } = createTabBar(
-      [...TAB_DEFS],
-      this.activeTab,
-      (id) => this.switchTab(id),
-    )
+    const { tabs, tabButtons } = createTabBar([...TAB_DEFS], this.activeTab, (id) => this.switchTab(id))
     this.tabButtons = tabButtons
     root.appendChild(tabs)
 
@@ -103,7 +99,7 @@ export class DevtoolsPanel {
       (scrollTop, scrollLeft) => {
         this.editorHighlight.scrollTop = scrollTop
         this.editorHighlight.scrollLeft = scrollLeft
-      },
+      }
     )
     this.editorWrap = editorResult.wrap
     this.editor = editorResult.textarea
diff --git a/packages/comark/src/devtools/renderer/styles.css b/packages/comark/src/devtools/renderer/styles.css
index 917c2931..0234dd6a 100644
--- a/packages/comark/src/devtools/renderer/styles.css
+++ b/packages/comark/src/devtools/renderer/styles.css
@@ -283,7 +283,9 @@
 }
 
 @keyframes comark-spin {
-  to { transform: rotate(360deg); }
+  to {
+    transform: rotate(360deg);
+  }
 }
 
 .comark-loading-text {
diff --git a/packages/comark/src/devtools/renderer/styles.ts b/packages/comark/src/devtools/renderer/styles.ts
index da59b6e9..3e1c2f01 100644
--- a/packages/comark/src/devtools/renderer/styles.ts
+++ b/packages/comark/src/devtools/renderer/styles.ts
@@ -1,2 +1,3 @@
 // @generated — do not edit. Edit styles.css instead, then run: node scripts/sync-devtools-css.mjs
-export const STYLES = ".comark-devtools {\n  --comark-devtools-bg: #ffffff;\n  --comark-devtools-bg-soft: #fafafa;\n  --comark-devtools-bg-muted: #f4f4f5;\n  --comark-devtools-text: #18181b;\n  --comark-devtools-text-muted: #71717a;\n  --comark-devtools-border: #e4e4e7;\n  --comark-devtools-primary: #eab308;\n  --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n  --comark-devtools-accent: #ca8a04;\n  --comark-devtools-error: #ef4444;\n  --comark-devtools-warning: #f59e0b;\n  --comark-devtools-success: #22c55e;\n\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n  font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif;\n  color: var(--comark-devtools-text);\n  background: var(--comark-devtools-bg);\n  font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n  box-sizing: border-box;\n}\n\n/* Tabs */\n.comark-tabs {\n  display: flex;\n  align-items: center;\n  border-bottom: 1px solid var(--comark-devtools-border);\n  flex-shrink: 0;\n}\n\n.comark-tab {\n  padding: 6px 12px;\n  border: none;\n  border-bottom: 2px solid transparent;\n  background: transparent;\n  color: var(--comark-devtools-text-muted);\n  cursor: pointer;\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  transition:\n    color 0.15s,\n    border-color 0.15s;\n}\n\n.comark-tab:hover {\n  color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active='true'] {\n  color: var(--comark-devtools-text);\n  border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Tab bar right section */\n.comark-tab-right {\n  margin-left: auto;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 0 12px;\n}\n\n/* Content area */\n.comark-content {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  min-height: 0;\n  overflow: hidden;\n}\n\n/* Editor overlay */\n.comark-editor-wrap {\n  flex: 1;\n  position: relative;\n  overflow: hidden;\n}\n\n.comark-editor-highlight,\n.comark-editor {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  padding: 12px;\n  margin: 0;\n  border: none;\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  tab-size: 2;\n  white-space: pre-wrap;\n  word-break: break-word;\n  overflow: auto;\n}\n\n.comark-editor-highlight {\n  color: var(--comark-devtools-text);\n  pointer-events: none;\n}\n\n.comark-editor {\n  color: transparent;\n  caret-color: var(--comark-devtools-text);\n  background: transparent;\n  resize: none;\n  outline: none;\n  -webkit-text-fill-color: transparent;\n}\n\n.comark-editor::placeholder {\n  color: var(--comark-devtools-text-muted);\n  -webkit-text-fill-color: var(--comark-devtools-text-muted);\n  opacity: 0.5;\n}\n\n/* Shiki dual-theme support */\n.comark-editor-highlight span {\n  color: var(--shiki-light);\n}\n.comark-devtools[data-theme='dark'] .comark-editor-highlight span,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-editor-highlight span {\n  color: var(--shiki-dark);\n}\n\n/* Markdown syntax highlighting (regex fallback) */\n.comark-md-heading {\n  color: #9333ea;\n  font-weight: 600;\n}\n.comark-md-bold {\n  color: var(--comark-devtools-text);\n  font-weight: 700;\n}\n.comark-md-italic {\n  font-style: italic;\n}\n.comark-md-code {\n  color: #d97706;\n  background: var(--comark-devtools-bg-muted);\n  border-radius: 3px;\n  padding: 1px 3px;\n}\n.comark-md-code-fence {\n  color: #d97706;\n}\n.comark-md-code-lang {\n  color: #059669;\n  font-weight: 600;\n}\n.comark-md-code-content {\n  color: var(--comark-devtools-text-muted);\n}\n.comark-md-link {\n  color: #2563eb;\n}\n.comark-md-url {\n  opacity: 0.6;\n}\n.comark-md-component {\n  color: #059669;\n  font-weight: 600;\n}\n.comark-md-attrs {\n  color: #71717a;\n}\n.comark-md-blockquote {\n  color: #71717a;\n  font-style: italic;\n}\n.comark-md-list-marker {\n  color: #d97706;\n}\n.comark-md-frontmatter {\n  color: #71717a;\n}\n\n/* Output content */\n.comark-output {\n  flex: 1;\n  overflow: auto;\n  padding: 12px;\n}\n\n.comark-output pre {\n  margin: 0;\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 12px;\n  line-height: 1.6;\n  white-space: pre-wrap;\n  word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key {\n  color: #9333ea;\n}\n.comark-tree-string {\n  color: #16a34a;\n}\n.comark-tree-number {\n  color: #2563eb;\n}\n.comark-tree-null {\n  color: #a1a1aa;\n  font-style: italic;\n}\n.comark-tree-bool {\n  color: #dc2626;\n}\n\n/* Error display */\n.comark-error {\n  color: var(--comark-devtools-error);\n  padding: 12px;\n  font-size: 12px;\n  font-family: 'Geist Mono', ui-monospace, monospace;\n  white-space: pre-wrap;\n}\n\n/* Instance status */\n.comark-instance-dot {\n  width: 6px;\n  height: 6px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected='true'] {\n  background: var(--comark-devtools-success);\n  animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected='false'] {\n  background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n  0%,\n  100% {\n    opacity: 1;\n  }\n  50% {\n    opacity: 0.5;\n  }\n}\n\n.comark-instance-label {\n  font-size: 11px;\n  font-weight: 500;\n  color: var(--comark-devtools-text-muted);\n}\n\n/* Loading state */\n.comark-loading-state {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 14px;\n  color: var(--comark-devtools-text-muted);\n  padding: 32px;\n  user-select: none;\n}\n\n.comark-loading-spinner {\n  width: 28px;\n  height: 28px;\n  border: 3px solid var(--comark-devtools-border);\n  border-top-color: var(--comark-devtools-primary);\n  border-radius: 50%;\n  animation: comark-spin 0.8s linear infinite;\n}\n\n@keyframes comark-spin {\n  to { transform: rotate(360deg); }\n}\n\n.comark-loading-text {\n  margin: 0;\n  font-size: 13px;\n}\n\n/* Empty state */\n.comark-empty-state {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 16px;\n  color: var(--comark-devtools-text-muted);\n  padding: 32px;\n  user-select: none;\n}\n\n.comark-empty-icon {\n  opacity: 0.25;\n}\n\n.comark-empty-title {\n  margin: 0;\n  font-size: 13px;\n  text-align: center;\n  line-height: 1.5;\n}\n\n.comark-empty-state code {\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 12px;\n  padding: 2px 6px;\n  border-radius: 4px;\n  background: var(--comark-devtools-bg-muted);\n}\n\n/* Theme toggle */\n.comark-theme-toggle {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 26px;\n  height: 26px;\n  padding: 0;\n  border: 1px solid var(--comark-devtools-border);\n  border-radius: 6px;\n  background: transparent;\n  color: var(--comark-devtools-text-muted);\n  cursor: pointer;\n  transition:\n    color 0.15s,\n    border-color 0.15s,\n    background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n  color: var(--comark-devtools-text);\n  border-color: var(--comark-devtools-primary);\n  background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme='dark'],\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) {\n  --comark-devtools-bg: #18181b;\n  --comark-devtools-bg-soft: #27272a;\n  --comark-devtools-bg-muted: #3f3f46;\n  --comark-devtools-text: #fafafa;\n  --comark-devtools-text-muted: #a1a1aa;\n  --comark-devtools-border: #3f3f46;\n  --comark-devtools-primary: #facc15;\n  --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n  --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme='dark'] .comark-tree-key,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key {\n  color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-string,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string {\n  color: #4ade80;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-number,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number {\n  color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-null,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null {\n  color: #71717a;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-bool,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool {\n  color: #f87171;\n}\n.comark-devtools[data-theme='dark'] .comark-md-heading,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-heading {\n  color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-fence,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-fence {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-lang,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-lang {\n  color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-link,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-link {\n  color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-component,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-component {\n  color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-list-marker,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-list-marker {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-blockquote,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-blockquote {\n  color: #a1a1aa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-frontmatter,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-frontmatter {\n  color: #a1a1aa;\n}\n"
+export const STYLES =
+  ".comark-devtools {\n  --comark-devtools-bg: #ffffff;\n  --comark-devtools-bg-soft: #fafafa;\n  --comark-devtools-bg-muted: #f4f4f5;\n  --comark-devtools-text: #18181b;\n  --comark-devtools-text-muted: #71717a;\n  --comark-devtools-border: #e4e4e7;\n  --comark-devtools-primary: #eab308;\n  --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n  --comark-devtools-accent: #ca8a04;\n  --comark-devtools-error: #ef4444;\n  --comark-devtools-warning: #f59e0b;\n  --comark-devtools-success: #22c55e;\n\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n  font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif;\n  color: var(--comark-devtools-text);\n  background: var(--comark-devtools-bg);\n  font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n  box-sizing: border-box;\n}\n\n/* Tabs */\n.comark-tabs {\n  display: flex;\n  align-items: center;\n  border-bottom: 1px solid var(--comark-devtools-border);\n  flex-shrink: 0;\n}\n\n.comark-tab {\n  padding: 6px 12px;\n  border: none;\n  border-bottom: 2px solid transparent;\n  background: transparent;\n  color: var(--comark-devtools-text-muted);\n  cursor: pointer;\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  transition:\n    color 0.15s,\n    border-color 0.15s;\n}\n\n.comark-tab:hover {\n  color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active='true'] {\n  color: var(--comark-devtools-text);\n  border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Tab bar right section */\n.comark-tab-right {\n  margin-left: auto;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 0 12px;\n}\n\n/* Content area */\n.comark-content {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  min-height: 0;\n  overflow: hidden;\n}\n\n/* Editor overlay */\n.comark-editor-wrap {\n  flex: 1;\n  position: relative;\n  overflow: hidden;\n}\n\n.comark-editor-highlight,\n.comark-editor {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  padding: 12px;\n  margin: 0;\n  border: none;\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  tab-size: 2;\n  white-space: pre-wrap;\n  word-break: break-word;\n  overflow: auto;\n}\n\n.comark-editor-highlight {\n  color: var(--comark-devtools-text);\n  pointer-events: none;\n}\n\n.comark-editor {\n  color: transparent;\n  caret-color: var(--comark-devtools-text);\n  background: transparent;\n  resize: none;\n  outline: none;\n  -webkit-text-fill-color: transparent;\n}\n\n.comark-editor::placeholder {\n  color: var(--comark-devtools-text-muted);\n  -webkit-text-fill-color: var(--comark-devtools-text-muted);\n  opacity: 0.5;\n}\n\n/* Shiki dual-theme support */\n.comark-editor-highlight span {\n  color: var(--shiki-light);\n}\n.comark-devtools[data-theme='dark'] .comark-editor-highlight span,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-editor-highlight span {\n  color: var(--shiki-dark);\n}\n\n/* Markdown syntax highlighting (regex fallback) */\n.comark-md-heading {\n  color: #9333ea;\n  font-weight: 600;\n}\n.comark-md-bold {\n  color: var(--comark-devtools-text);\n  font-weight: 700;\n}\n.comark-md-italic {\n  font-style: italic;\n}\n.comark-md-code {\n  color: #d97706;\n  background: var(--comark-devtools-bg-muted);\n  border-radius: 3px;\n  padding: 1px 3px;\n}\n.comark-md-code-fence {\n  color: #d97706;\n}\n.comark-md-code-lang {\n  color: #059669;\n  font-weight: 600;\n}\n.comark-md-code-content {\n  color: var(--comark-devtools-text-muted);\n}\n.comark-md-link {\n  color: #2563eb;\n}\n.comark-md-url {\n  opacity: 0.6;\n}\n.comark-md-component {\n  color: #059669;\n  font-weight: 600;\n}\n.comark-md-attrs {\n  color: #71717a;\n}\n.comark-md-blockquote {\n  color: #71717a;\n  font-style: italic;\n}\n.comark-md-list-marker {\n  color: #d97706;\n}\n.comark-md-frontmatter {\n  color: #71717a;\n}\n\n/* Output content */\n.comark-output {\n  flex: 1;\n  overflow: auto;\n  padding: 12px;\n}\n\n.comark-output pre {\n  margin: 0;\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 12px;\n  line-height: 1.6;\n  white-space: pre-wrap;\n  word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key {\n  color: #9333ea;\n}\n.comark-tree-string {\n  color: #16a34a;\n}\n.comark-tree-number {\n  color: #2563eb;\n}\n.comark-tree-null {\n  color: #a1a1aa;\n  font-style: italic;\n}\n.comark-tree-bool {\n  color: #dc2626;\n}\n\n/* Error display */\n.comark-error {\n  color: var(--comark-devtools-error);\n  padding: 12px;\n  font-size: 12px;\n  font-family: 'Geist Mono', ui-monospace, monospace;\n  white-space: pre-wrap;\n}\n\n/* Instance status */\n.comark-instance-dot {\n  width: 6px;\n  height: 6px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected='true'] {\n  background: var(--comark-devtools-success);\n  animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected='false'] {\n  background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n  0%,\n  100% {\n    opacity: 1;\n  }\n  50% {\n    opacity: 0.5;\n  }\n}\n\n.comark-instance-label {\n  font-size: 11px;\n  font-weight: 500;\n  color: var(--comark-devtools-text-muted);\n}\n\n/* Loading state */\n.comark-loading-state {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 14px;\n  color: var(--comark-devtools-text-muted);\n  padding: 32px;\n  user-select: none;\n}\n\n.comark-loading-spinner {\n  width: 28px;\n  height: 28px;\n  border: 3px solid var(--comark-devtools-border);\n  border-top-color: var(--comark-devtools-primary);\n  border-radius: 50%;\n  animation: comark-spin 0.8s linear infinite;\n}\n\n@keyframes comark-spin {\n  to { transform: rotate(360deg); }\n}\n\n.comark-loading-text {\n  margin: 0;\n  font-size: 13px;\n}\n\n/* Empty state */\n.comark-empty-state {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 16px;\n  color: var(--comark-devtools-text-muted);\n  padding: 32px;\n  user-select: none;\n}\n\n.comark-empty-icon {\n  opacity: 0.25;\n}\n\n.comark-empty-title {\n  margin: 0;\n  font-size: 13px;\n  text-align: center;\n  line-height: 1.5;\n}\n\n.comark-empty-state code {\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 12px;\n  padding: 2px 6px;\n  border-radius: 4px;\n  background: var(--comark-devtools-bg-muted);\n}\n\n/* Theme toggle */\n.comark-theme-toggle {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 26px;\n  height: 26px;\n  padding: 0;\n  border: 1px solid var(--comark-devtools-border);\n  border-radius: 6px;\n  background: transparent;\n  color: var(--comark-devtools-text-muted);\n  cursor: pointer;\n  transition:\n    color 0.15s,\n    border-color 0.15s,\n    background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n  color: var(--comark-devtools-text);\n  border-color: var(--comark-devtools-primary);\n  background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme='dark'],\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) {\n  --comark-devtools-bg: #18181b;\n  --comark-devtools-bg-soft: #27272a;\n  --comark-devtools-bg-muted: #3f3f46;\n  --comark-devtools-text: #fafafa;\n  --comark-devtools-text-muted: #a1a1aa;\n  --comark-devtools-border: #3f3f46;\n  --comark-devtools-primary: #facc15;\n  --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n  --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme='dark'] .comark-tree-key,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key {\n  color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-string,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string {\n  color: #4ade80;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-number,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number {\n  color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-null,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null {\n  color: #71717a;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-bool,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool {\n  color: #f87171;\n}\n.comark-devtools[data-theme='dark'] .comark-md-heading,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-heading {\n  color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-fence,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-fence {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-lang,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-lang {\n  color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-link,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-link {\n  color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-component,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-component {\n  color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-list-marker,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-list-marker {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-blockquote,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-blockquote {\n  color: #a1a1aa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-frontmatter,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-frontmatter {\n  color: #a1a1aa;\n}\n"

From a780b7dbdd00c76e86e43ffac67fecc012fe33ad Mon Sep 17 00:00:00 2001
From: Arash Ari Sheyda 
Date: Wed, 3 Jun 2026 19:03:04 -0600
Subject: [PATCH 11/28] fix: build

---
 packages/comark/src/devtools/renderer/styles.ts | 2 +-
 packages/comark/src/devtools/renderer/theme.ts  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/comark/src/devtools/renderer/styles.ts b/packages/comark/src/devtools/renderer/styles.ts
index 3e1c2f01..056c20ec 100644
--- a/packages/comark/src/devtools/renderer/styles.ts
+++ b/packages/comark/src/devtools/renderer/styles.ts
@@ -1,3 +1,3 @@
 // @generated — do not edit. Edit styles.css instead, then run: node scripts/sync-devtools-css.mjs
 export const STYLES =
-  ".comark-devtools {\n  --comark-devtools-bg: #ffffff;\n  --comark-devtools-bg-soft: #fafafa;\n  --comark-devtools-bg-muted: #f4f4f5;\n  --comark-devtools-text: #18181b;\n  --comark-devtools-text-muted: #71717a;\n  --comark-devtools-border: #e4e4e7;\n  --comark-devtools-primary: #eab308;\n  --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n  --comark-devtools-accent: #ca8a04;\n  --comark-devtools-error: #ef4444;\n  --comark-devtools-warning: #f59e0b;\n  --comark-devtools-success: #22c55e;\n\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n  font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif;\n  color: var(--comark-devtools-text);\n  background: var(--comark-devtools-bg);\n  font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n  box-sizing: border-box;\n}\n\n/* Tabs */\n.comark-tabs {\n  display: flex;\n  align-items: center;\n  border-bottom: 1px solid var(--comark-devtools-border);\n  flex-shrink: 0;\n}\n\n.comark-tab {\n  padding: 6px 12px;\n  border: none;\n  border-bottom: 2px solid transparent;\n  background: transparent;\n  color: var(--comark-devtools-text-muted);\n  cursor: pointer;\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  transition:\n    color 0.15s,\n    border-color 0.15s;\n}\n\n.comark-tab:hover {\n  color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active='true'] {\n  color: var(--comark-devtools-text);\n  border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Tab bar right section */\n.comark-tab-right {\n  margin-left: auto;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 0 12px;\n}\n\n/* Content area */\n.comark-content {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  min-height: 0;\n  overflow: hidden;\n}\n\n/* Editor overlay */\n.comark-editor-wrap {\n  flex: 1;\n  position: relative;\n  overflow: hidden;\n}\n\n.comark-editor-highlight,\n.comark-editor {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  padding: 12px;\n  margin: 0;\n  border: none;\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  tab-size: 2;\n  white-space: pre-wrap;\n  word-break: break-word;\n  overflow: auto;\n}\n\n.comark-editor-highlight {\n  color: var(--comark-devtools-text);\n  pointer-events: none;\n}\n\n.comark-editor {\n  color: transparent;\n  caret-color: var(--comark-devtools-text);\n  background: transparent;\n  resize: none;\n  outline: none;\n  -webkit-text-fill-color: transparent;\n}\n\n.comark-editor::placeholder {\n  color: var(--comark-devtools-text-muted);\n  -webkit-text-fill-color: var(--comark-devtools-text-muted);\n  opacity: 0.5;\n}\n\n/* Shiki dual-theme support */\n.comark-editor-highlight span {\n  color: var(--shiki-light);\n}\n.comark-devtools[data-theme='dark'] .comark-editor-highlight span,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-editor-highlight span {\n  color: var(--shiki-dark);\n}\n\n/* Markdown syntax highlighting (regex fallback) */\n.comark-md-heading {\n  color: #9333ea;\n  font-weight: 600;\n}\n.comark-md-bold {\n  color: var(--comark-devtools-text);\n  font-weight: 700;\n}\n.comark-md-italic {\n  font-style: italic;\n}\n.comark-md-code {\n  color: #d97706;\n  background: var(--comark-devtools-bg-muted);\n  border-radius: 3px;\n  padding: 1px 3px;\n}\n.comark-md-code-fence {\n  color: #d97706;\n}\n.comark-md-code-lang {\n  color: #059669;\n  font-weight: 600;\n}\n.comark-md-code-content {\n  color: var(--comark-devtools-text-muted);\n}\n.comark-md-link {\n  color: #2563eb;\n}\n.comark-md-url {\n  opacity: 0.6;\n}\n.comark-md-component {\n  color: #059669;\n  font-weight: 600;\n}\n.comark-md-attrs {\n  color: #71717a;\n}\n.comark-md-blockquote {\n  color: #71717a;\n  font-style: italic;\n}\n.comark-md-list-marker {\n  color: #d97706;\n}\n.comark-md-frontmatter {\n  color: #71717a;\n}\n\n/* Output content */\n.comark-output {\n  flex: 1;\n  overflow: auto;\n  padding: 12px;\n}\n\n.comark-output pre {\n  margin: 0;\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 12px;\n  line-height: 1.6;\n  white-space: pre-wrap;\n  word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key {\n  color: #9333ea;\n}\n.comark-tree-string {\n  color: #16a34a;\n}\n.comark-tree-number {\n  color: #2563eb;\n}\n.comark-tree-null {\n  color: #a1a1aa;\n  font-style: italic;\n}\n.comark-tree-bool {\n  color: #dc2626;\n}\n\n/* Error display */\n.comark-error {\n  color: var(--comark-devtools-error);\n  padding: 12px;\n  font-size: 12px;\n  font-family: 'Geist Mono', ui-monospace, monospace;\n  white-space: pre-wrap;\n}\n\n/* Instance status */\n.comark-instance-dot {\n  width: 6px;\n  height: 6px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected='true'] {\n  background: var(--comark-devtools-success);\n  animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected='false'] {\n  background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n  0%,\n  100% {\n    opacity: 1;\n  }\n  50% {\n    opacity: 0.5;\n  }\n}\n\n.comark-instance-label {\n  font-size: 11px;\n  font-weight: 500;\n  color: var(--comark-devtools-text-muted);\n}\n\n/* Loading state */\n.comark-loading-state {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 14px;\n  color: var(--comark-devtools-text-muted);\n  padding: 32px;\n  user-select: none;\n}\n\n.comark-loading-spinner {\n  width: 28px;\n  height: 28px;\n  border: 3px solid var(--comark-devtools-border);\n  border-top-color: var(--comark-devtools-primary);\n  border-radius: 50%;\n  animation: comark-spin 0.8s linear infinite;\n}\n\n@keyframes comark-spin {\n  to { transform: rotate(360deg); }\n}\n\n.comark-loading-text {\n  margin: 0;\n  font-size: 13px;\n}\n\n/* Empty state */\n.comark-empty-state {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 16px;\n  color: var(--comark-devtools-text-muted);\n  padding: 32px;\n  user-select: none;\n}\n\n.comark-empty-icon {\n  opacity: 0.25;\n}\n\n.comark-empty-title {\n  margin: 0;\n  font-size: 13px;\n  text-align: center;\n  line-height: 1.5;\n}\n\n.comark-empty-state code {\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 12px;\n  padding: 2px 6px;\n  border-radius: 4px;\n  background: var(--comark-devtools-bg-muted);\n}\n\n/* Theme toggle */\n.comark-theme-toggle {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 26px;\n  height: 26px;\n  padding: 0;\n  border: 1px solid var(--comark-devtools-border);\n  border-radius: 6px;\n  background: transparent;\n  color: var(--comark-devtools-text-muted);\n  cursor: pointer;\n  transition:\n    color 0.15s,\n    border-color 0.15s,\n    background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n  color: var(--comark-devtools-text);\n  border-color: var(--comark-devtools-primary);\n  background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme='dark'],\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) {\n  --comark-devtools-bg: #18181b;\n  --comark-devtools-bg-soft: #27272a;\n  --comark-devtools-bg-muted: #3f3f46;\n  --comark-devtools-text: #fafafa;\n  --comark-devtools-text-muted: #a1a1aa;\n  --comark-devtools-border: #3f3f46;\n  --comark-devtools-primary: #facc15;\n  --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n  --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme='dark'] .comark-tree-key,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key {\n  color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-string,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string {\n  color: #4ade80;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-number,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number {\n  color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-null,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null {\n  color: #71717a;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-bool,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool {\n  color: #f87171;\n}\n.comark-devtools[data-theme='dark'] .comark-md-heading,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-heading {\n  color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-fence,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-fence {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-lang,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-lang {\n  color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-link,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-link {\n  color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-component,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-component {\n  color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-list-marker,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-list-marker {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-blockquote,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-blockquote {\n  color: #a1a1aa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-frontmatter,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-frontmatter {\n  color: #a1a1aa;\n}\n"
+  ".comark-devtools {\n  --comark-devtools-bg: #ffffff;\n  --comark-devtools-bg-soft: #fafafa;\n  --comark-devtools-bg-muted: #f4f4f5;\n  --comark-devtools-text: #18181b;\n  --comark-devtools-text-muted: #71717a;\n  --comark-devtools-border: #e4e4e7;\n  --comark-devtools-primary: #eab308;\n  --comark-devtools-primary-soft: rgba(234, 179, 8, 0.12);\n  --comark-devtools-accent: #ca8a04;\n  --comark-devtools-error: #ef4444;\n  --comark-devtools-warning: #f59e0b;\n  --comark-devtools-success: #22c55e;\n\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n  font-family: 'Geist', 'Public Sans', ui-sans-serif, system-ui, sans-serif;\n  color: var(--comark-devtools-text);\n  background: var(--comark-devtools-bg);\n  font-size: 13px;\n}\n\n.comark-devtools *,\n.comark-devtools *::before,\n.comark-devtools *::after {\n  box-sizing: border-box;\n}\n\n/* Tabs */\n.comark-tabs {\n  display: flex;\n  align-items: center;\n  border-bottom: 1px solid var(--comark-devtools-border);\n  flex-shrink: 0;\n}\n\n.comark-tab {\n  padding: 6px 12px;\n  border: none;\n  border-bottom: 2px solid transparent;\n  background: transparent;\n  color: var(--comark-devtools-text-muted);\n  cursor: pointer;\n  font-size: 11px;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.05em;\n  transition:\n    color 0.15s,\n    border-color 0.15s;\n}\n\n.comark-tab:hover {\n  color: var(--comark-devtools-text);\n}\n\n.comark-tab[data-active='true'] {\n  color: var(--comark-devtools-text);\n  border-bottom-color: var(--comark-devtools-primary);\n}\n\n/* Tab bar right section */\n.comark-tab-right {\n  margin-left: auto;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 0 12px;\n}\n\n/* Content area */\n.comark-content {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  min-height: 0;\n  overflow: hidden;\n}\n\n/* Editor overlay */\n.comark-editor-wrap {\n  flex: 1;\n  position: relative;\n  overflow: hidden;\n}\n\n.comark-editor-highlight,\n.comark-editor {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  padding: 12px;\n  margin: 0;\n  border: none;\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 13px;\n  line-height: 1.6;\n  tab-size: 2;\n  white-space: pre-wrap;\n  word-break: break-word;\n  overflow: auto;\n}\n\n.comark-editor-highlight {\n  color: var(--comark-devtools-text);\n  pointer-events: none;\n}\n\n.comark-editor {\n  color: transparent;\n  caret-color: var(--comark-devtools-text);\n  background: transparent;\n  resize: none;\n  outline: none;\n  -webkit-text-fill-color: transparent;\n}\n\n.comark-editor::placeholder {\n  color: var(--comark-devtools-text-muted);\n  -webkit-text-fill-color: var(--comark-devtools-text-muted);\n  opacity: 0.5;\n}\n\n/* Shiki dual-theme support */\n.comark-editor-highlight span {\n  color: var(--shiki-light);\n}\n.comark-devtools[data-theme='dark'] .comark-editor-highlight span,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-editor-highlight span {\n  color: var(--shiki-dark);\n}\n\n/* Markdown syntax highlighting (regex fallback) */\n.comark-md-heading {\n  color: #9333ea;\n  font-weight: 600;\n}\n.comark-md-bold {\n  color: var(--comark-devtools-text);\n  font-weight: 700;\n}\n.comark-md-italic {\n  font-style: italic;\n}\n.comark-md-code {\n  color: #d97706;\n  background: var(--comark-devtools-bg-muted);\n  border-radius: 3px;\n  padding: 1px 3px;\n}\n.comark-md-code-fence {\n  color: #d97706;\n}\n.comark-md-code-lang {\n  color: #059669;\n  font-weight: 600;\n}\n.comark-md-code-content {\n  color: var(--comark-devtools-text-muted);\n}\n.comark-md-link {\n  color: #2563eb;\n}\n.comark-md-url {\n  opacity: 0.6;\n}\n.comark-md-component {\n  color: #059669;\n  font-weight: 600;\n}\n.comark-md-attrs {\n  color: #71717a;\n}\n.comark-md-blockquote {\n  color: #71717a;\n  font-style: italic;\n}\n.comark-md-list-marker {\n  color: #d97706;\n}\n.comark-md-frontmatter {\n  color: #71717a;\n}\n\n/* Output content */\n.comark-output {\n  flex: 1;\n  overflow: auto;\n  padding: 12px;\n}\n\n.comark-output pre {\n  margin: 0;\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 12px;\n  line-height: 1.6;\n  white-space: pre-wrap;\n  word-break: break-word;\n}\n\n/* AST tree */\n.comark-tree-key {\n  color: #9333ea;\n}\n.comark-tree-string {\n  color: #16a34a;\n}\n.comark-tree-number {\n  color: #2563eb;\n}\n.comark-tree-null {\n  color: #a1a1aa;\n  font-style: italic;\n}\n.comark-tree-bool {\n  color: #dc2626;\n}\n\n/* Error display */\n.comark-error {\n  color: var(--comark-devtools-error);\n  padding: 12px;\n  font-size: 12px;\n  font-family: 'Geist Mono', ui-monospace, monospace;\n  white-space: pre-wrap;\n}\n\n/* Instance status */\n.comark-instance-dot {\n  width: 6px;\n  height: 6px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}\n\n.comark-instance-dot[data-connected='true'] {\n  background: var(--comark-devtools-success);\n  animation: comark-pulse 2s infinite;\n}\n\n.comark-instance-dot[data-connected='false'] {\n  background: #a1a1aa;\n}\n\n@keyframes comark-pulse {\n  0%,\n  100% {\n    opacity: 1;\n  }\n  50% {\n    opacity: 0.5;\n  }\n}\n\n.comark-instance-label {\n  font-size: 11px;\n  font-weight: 500;\n  color: var(--comark-devtools-text-muted);\n}\n\n/* Loading state */\n.comark-loading-state {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 14px;\n  color: var(--comark-devtools-text-muted);\n  padding: 32px;\n  user-select: none;\n}\n\n.comark-loading-spinner {\n  width: 28px;\n  height: 28px;\n  border: 3px solid var(--comark-devtools-border);\n  border-top-color: var(--comark-devtools-primary);\n  border-radius: 50%;\n  animation: comark-spin 0.8s linear infinite;\n}\n\n@keyframes comark-spin {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n.comark-loading-text {\n  margin: 0;\n  font-size: 13px;\n}\n\n/* Empty state */\n.comark-empty-state {\n  flex: 1;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 16px;\n  color: var(--comark-devtools-text-muted);\n  padding: 32px;\n  user-select: none;\n}\n\n.comark-empty-icon {\n  opacity: 0.25;\n}\n\n.comark-empty-title {\n  margin: 0;\n  font-size: 13px;\n  text-align: center;\n  line-height: 1.5;\n}\n\n.comark-empty-state code {\n  font-family: 'Geist Mono', ui-monospace, 'SF Mono', Monaco, Menlo, monospace;\n  font-size: 12px;\n  padding: 2px 6px;\n  border-radius: 4px;\n  background: var(--comark-devtools-bg-muted);\n}\n\n/* Theme toggle */\n.comark-theme-toggle {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 26px;\n  height: 26px;\n  padding: 0;\n  border: 1px solid var(--comark-devtools-border);\n  border-radius: 6px;\n  background: transparent;\n  color: var(--comark-devtools-text-muted);\n  cursor: pointer;\n  transition:\n    color 0.15s,\n    border-color 0.15s,\n    background 0.15s;\n}\n\n.comark-theme-toggle:hover {\n  color: var(--comark-devtools-text);\n  border-color: var(--comark-devtools-primary);\n  background: var(--comark-devtools-primary-soft);\n}\n\n/* Dark theme via data attribute */\n.comark-devtools[data-theme='dark'],\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) {\n  --comark-devtools-bg: #18181b;\n  --comark-devtools-bg-soft: #27272a;\n  --comark-devtools-bg-muted: #3f3f46;\n  --comark-devtools-text: #fafafa;\n  --comark-devtools-text-muted: #a1a1aa;\n  --comark-devtools-border: #3f3f46;\n  --comark-devtools-primary: #facc15;\n  --comark-devtools-primary-soft: rgba(250, 204, 21, 0.12);\n  --comark-devtools-accent: #fde047;\n}\n\n.comark-devtools[data-theme='dark'] .comark-tree-key,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-key {\n  color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-string,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-string {\n  color: #4ade80;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-number,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-number {\n  color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-null,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-null {\n  color: #71717a;\n}\n.comark-devtools[data-theme='dark'] .comark-tree-bool,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-tree-bool {\n  color: #f87171;\n}\n.comark-devtools[data-theme='dark'] .comark-md-heading,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-heading {\n  color: #c084fc;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-fence,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-fence {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-code-lang,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-code-lang {\n  color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-link,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-link {\n  color: #60a5fa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-component,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-component {\n  color: #34d399;\n}\n.comark-devtools[data-theme='dark'] .comark-md-list-marker,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-list-marker {\n  color: #fbbf24;\n}\n.comark-devtools[data-theme='dark'] .comark-md-blockquote,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-blockquote {\n  color: #a1a1aa;\n}\n.comark-devtools[data-theme='dark'] .comark-md-frontmatter,\n.comark-devtools:not([data-theme='light']):is([data-auto-dark='true']) .comark-md-frontmatter {\n  color: #a1a1aa;\n}\n"
diff --git a/packages/comark/src/devtools/renderer/theme.ts b/packages/comark/src/devtools/renderer/theme.ts
index f0accc5a..adf2a384 100644
--- a/packages/comark/src/devtools/renderer/theme.ts
+++ b/packages/comark/src/devtools/renderer/theme.ts
@@ -1,5 +1,5 @@
 import type { Theme } from '../types.ts'
-import { LUCIDE_MOON, LUCIDE_MONITOR, LUCIDE_SUN } from '../constants/index.ts'
+import { LUCIDE_MOON, LUCIDE_MONITOR, LUCIDE_SUN } from '../constants.ts'
 
 /** Apply a color scheme to the devtools root element via `data-*` attributes */
 export function applyTheme(rootEl: HTMLElement, theme: Theme): void {

From bf43d02d8e666a0aff977d6323d2311399d22961 Mon Sep 17 00:00:00 2001
From: Farnabaz 
Date: Fri, 12 Jun 2026 13:24:05 +0200
Subject: [PATCH 12/28] update types

---
 packages/comark/src/devtools/types.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/comark/src/devtools/types.ts b/packages/comark/src/devtools/types.ts
index 200c3912..66f997b7 100644
--- a/packages/comark/src/devtools/types.ts
+++ b/packages/comark/src/devtools/types.ts
@@ -15,7 +15,7 @@ export interface ComarkInstance {
   /** Human-readable label — typically the current page URL path or hash */
   label?: string
   /** Current parsed AST */
-  tree: ComarkTree
+  tree: ComarkTree | { nodes: ComarkTree['nodes'] }
   /** Current markdown source (if available) */
   markdown?: string
 }
@@ -34,7 +34,7 @@ export interface RegisterInstanceOptions {
   /** The HMR handle (`import.meta.hot`). Pass `null` to skip registration. */
   hot: HotModule | null | undefined
   /** Initial tree (may be empty for string renderers) */
-  tree: ComarkTree
+  tree: ComarkTree | { nodes: ComarkTree['nodes'] }
   /** Initial markdown source, if known */
   markdown?: string
 }
@@ -44,7 +44,7 @@ export interface RegisteredInstance {
   /** The assigned instance id */
   id: string
   /** Push an updated tree and/or markdown source to the devtools */
-  update(patch: { tree?: ComarkTree; markdown?: string }): void
+  update(patch: { tree?: ComarkTree | { nodes: ComarkTree['nodes'] }; markdown?: string }): void
   /** Remove the instance from the devtools registry */
   unregister(): void
 }

From be67885c144f7a82f511eaa109dcbeae20d65b5d Mon Sep 17 00:00:00 2001
From: Farnabaz 
Date: Fri, 12 Jun 2026 16:16:02 +0200
Subject: [PATCH 13/28] feat: allow editing directly from devtools

---
 examples/2.vite/react/package.json            |  1 +
 examples/2.vite/vue/src/pages/blog/slug.vue   | 26 +++++++--
 .../src/components/ComarkClient.tsx           | 48 +---------------
 .../src/components/ComarkRenderer.tsx         | 57 ++++++++++++++++++-
 .../src/components/Comark.svelte              | 41 +------------
 .../src/components/ComarkRenderer.svelte      | 45 +++++++++++++++
 packages/comark-vue/src/components/Comark.ts  | 22 +------
 .../src/components/ComarkRenderer.ts          | 18 ++++--
 packages/comark/src/devtools/register.ts      |  1 +
 packages/comark/src/devtools/registry.ts      | 15 ++++-
 .../comark/src/devtools/renderer/panel.ts     | 16 ++++--
 packages/comark/src/devtools/types.ts         |  7 +++
 packages/comark/src/devtools/vite.ts          | 30 ++++++----
 pnpm-lock.yaml                                | 11 ++--
 14 files changed, 198 insertions(+), 140 deletions(-)

diff --git a/examples/2.vite/react/package.json b/examples/2.vite/react/package.json
index 28fe176e..10c7bbcc 100644
--- a/examples/2.vite/react/package.json
+++ b/examples/2.vite/react/package.json
@@ -22,6 +22,7 @@
     "@vitejs/plugin-react": "catalog:",
     "autoprefixer": "catalog:",
     "postcss": "catalog:",
+    "tailwindcss": "catalog:",
     "typescript": "catalog:",
     "vite": "catalog:"
   }
diff --git a/examples/2.vite/vue/src/pages/blog/slug.vue b/examples/2.vite/vue/src/pages/blog/slug.vue
index 447eab1f..9427fd59 100644
--- a/examples/2.vite/vue/src/pages/blog/slug.vue
+++ b/examples/2.vite/vue/src/pages/blog/slug.vue
@@ -3,9 +3,27 @@ import { useRoute } from 'vue-router'
 import { ComarkRenderer } from '@comark/vue'
 import Alert from '@/components/Alert.vue'
 import { getPost } from '@/lib/posts'
+import { ref } from 'vue'
 
 const route = useRoute()
 const post = await getPost(route.params.slug as string)
+
+const title = ref(post.tree.frontmatter.title as string)
+const pubDate = ref(post.tree.frontmatter.pubDate as string)
+const tags = ref(post.tree.frontmatter.tags as string)
+
+if (import.meta.hot) {
+  import.meta.hot.on('comark:update', (data) => {
+    // This is not needed if ComarkRenderer render everything
+    // But in case user renders title/description themselves
+    // They can listen to the update event and update the post manually
+    if (data.tree) {
+      title.value = data.tree.frontmatter.title as string
+      pubDate.value = data.tree.frontmatter.pubDate as string
+      tags.value = data.tree.frontmatter.tags as string
+    }
+  })
+}