npm package: @owomark/view
Description: Rendering engine, preview engine, DOM view layer, and official base theme for OwoMark.
Latest version: 0.1.4

# @owomark/view

Rendering engine and interactive view layer for OwoMark. Provides the Gold Processor Factory for Markdown → HTML rendering, an incremental preview engine, and a DOM-backed editor view.

Install the official package: `@owomark/view`.

## Install

```bash
npm install @owomark/view
```

For MDX support, also install the optional peer dependencies:

```bash
npm install @mdx-js/mdx react react-dom
```

## Quick Start

### Official CSS Entry

```ts
import '@owomark/view/style.css';
```

`style.css` is the official base theme bundle. It includes the structural CSS, built-in MDX component styles, side-annotation/slash-menu styles, and the light/dark preset token values. New users should start here.

### Markdown → HTML (Static Rendering)

```ts
import { createOwoMarkProcessor } from '@owomark/view/static';

const processor = createOwoMarkProcessor();
const result = await processor.process('# Hello\n\nA paragraph with **bold**.');
console.log(String(result));
// <h1 id="hello">Hello</h1>\n<p>A paragraph with <strong>bold</strong>.</p>
```

### MDX → HTML

Enable MDX to render JSX components and expressions at build time:

```ts
import { createOwoMarkProcessor } from '@owomark/view/static';

const processor = createOwoMarkProcessor({ enableMdx: true });
const result = await processor.process(`
<Callout type="info">
Today is {new Date().getFullYear()}. MDX expressions just work.
</Callout>
`);
console.log(String(result));
// Built-in Callout component renders to styled HTML
```

### With Custom Components

```ts
import { createElement } from 'react';
import { createOwoMarkProcessor } from '@owomark/view/static';

const Chart = ({ data }) => createElement('div', { className: 'chart' }, `Chart: ${data}`);

const processor = createOwoMarkProcessor({
  enableMdx: true,
  mdxComponents: {
    Chart,              // Add your own component
    // Callout,         // Built-ins are still available unless you override them
  },
});

const result = await processor.process('<Chart data="revenue" />');
```

## Gold Processor Factory

The Gold Processor Factory is the single source of truth for OwoMark's rendering pipeline. Both preview and production consume the same factory.

### `createOwoMarkProcessor(options?)`

Returns an `OwoMarkProcessor` with a single `process(source) → Promise<{ value, toString }>` method.

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `mode` | `'preview' \| 'production'` | `'preview'` | Rendering mode |
| `enableMdx` | `boolean` | `false` | Enable MDX rendering (JSX components + expressions) |
| `mdxComponents` | `Record<string, ComponentType>` | `{}` | Additional/override components (merged with built-ins) |
| `enableMath` | `boolean` | `true` | Enable KaTeX math rendering (`$...$`, `$$...$$`) |
| `enableSideAnnotation` | `boolean` | `true` | Enable side annotation syntax |
| `preserveSoftLineBreaks` | `boolean` | `false` | When false, plain newlines become `<br>` |
| `codeTheme` | `string` | `'vitesse-light'` | Shiki theme for code highlighting |
| `extraRemarkPlugins` | `PluginEntry[]` | `[]` | Additional remark plugins |
| `extraRehypePlugins` | `PluginEntry[]` | `[]` | Additional rehype plugins |

### `getOwoMarkPlugins(options?)`

Returns `{ remarkPlugins, rehypePlugins }` arrays for use with `@mdx-js/mdx` compile paths directly. Excludes `remark-parse` and `remark-mdx` (MDX manages its own parser).

## Theme Packages

`@owomark/view` owns the official base theme. Third-party themes should layer on top of it instead of replacing it.

### Using a Third-Party Theme

```ts
import '@owomark/view/style.css';
import 'owomark-theme-acme/style.css';
```

Load `@owomark/view/style.css` first, then the theme package. The theme package should override `--owo-*` tokens and only add narrowly scoped visual overrides when tokens are not enough.

### Authoring `owomark-theme-xxx`

A theme package should:

- depend on `@owomark/view` and require consumers to import `@owomark/view/style.css` first
- publish token overrides and theme classes, not a copy of OwoMark's structural CSS
- target the public `--owo-*` token contract instead of private internal selectors
- treat `@owomark/view/style.css`, `@owomark/view/owomark.css`, `@owomark/view/mdx-components.css`, and exported theme helpers as the supported extension surface

Minimal example:

```css
.owo-theme-acme {
  --owo-brand: #0f766e;
  --owo-text: #123047;
  --owo-surface: #f6fffb;
  --owo-surface-raised: #ecfdf5;
  --owo-border: #99f6e4;
  --owo-border-strong: #5eead4;
}
```

Then pass the class to OwoMark:

```tsx
import { OwoMarkEditor } from '@owomark/react';
import '@owomark/view/style.css';
import 'owomark-theme-acme/style.css';

<OwoMarkEditor value={md} onChange={setMd} theme="owo-theme-acme" />;
```

Compatibility rules for third-party themes:

- Do not re-implement editor layout, MDX component skeletons, or side-annotation structure unless you are intentionally shipping a full replacement theme.
- Do not assume private DOM structure is stable beyond documented classes and token hooks.
- Prefer base tokens such as `--owo-brand`, `--owo-text`, and `--owo-border`; only override component-level tokens when the editor needs to diverge from the shared base theme.

Stable token families:

- `--owo-*`: shared base tokens for brand, text, surface, border, semantic colors, and mono font
- `--owo-editor-*`: editor-only tokens such as caret, selection, heading, placeholder, and editor surfaces
- `--owo-syntax-*`: syntax-highlight tokens
- `--owo-toolbar-*`: toolbar tokens
- `--owo-panel-*`: panel/popover tokens

## MDX Support

When `enableMdx: true`, the processor delegates to `@mdx-js/mdx` `evaluate()` + `renderToStaticMarkup()`. The output is still an HTML string — callers see no difference.

### Built-in Components

Import the base styles for built-in components:

```ts
import '@owomark/view/mdx-components.css';
```

#### Theming

MDX component styles consume `--owo-*` base tokens with hardcoded fallbacks. This means:

- **Zero-config**: Components render correctly without any theme import (fallback colors are embedded).
- **With theme**: Import `@owomark/view/style.css` and components automatically adapt to light/dark mode.
- **Custom brand**: Set `--owo-brand` on a wrapper element to change the accent color across Callout (info), Steps, and Tabs simultaneously.

| Component | Props | Description |
|-----------|-------|-------------|
| `<Callout>` | `type?: 'info' \| 'warn' \| 'error' \| 'success'` | Styled callout box with icon |
| `<CodeDemo>` | `title?: string, language?: string, code: string` | Code demonstration block |
| `<Steps>` | (children: `<Step>`) | Numbered step-by-step guide with vertical line |
| `<Step>` | `title?: string` | Single step within `<Steps>` |
| `<Tabs>` | (children: `<Tab>`) | CSS-only tab switcher (no JS runtime) |
| `<Tab>` | `label?: string` | Single tab panel within `<Tabs>` |
| `<FileTree>` | (children: indented text) | Directory tree visualization |
| `<Kbd>` | (children: key name) | Keyboard key indicator |

```mdx
<Callout type="warn">
Watch out — this is a warning.
</Callout>

<Steps>
  <Step title="Install">Run `npm install @owomark/view`</Step>
  <Step title="Configure">Enable MDX in your processor options</Step>
</Steps>

<Tabs>
  <Tab label="npm">`npm install @owomark/view`</Tab>
  <Tab label="pnpm">`pnpm add @owomark/view`</Tab>
</Tabs>

<FileTree>
{`src/
  components/
  lib/
  styles/
package.json`}
</FileTree>

Press <Kbd>Ctrl</Kbd> + <Kbd>S</Kbd> to save.

<CodeDemo title="Hello World" language="python" code="print('hello')" />
```

### Component Override

User-provided components override built-ins with the same name:

```ts
import { createElement } from 'react';

const MyCallout = ({ type, children }) =>
  createElement('aside', { className: `my-callout-${type}` }, children);

const processor = createOwoMarkProcessor({
  enableMdx: true,
  mdxComponents: { Callout: MyCallout }, // Replaces built-in Callout
});
```

### Unregistered Components

Components used in MDX but not registered (built-in or user-provided) will:

1. Print a `[owomark-mdx] Unregistered MDX component: <Name>` warning
2. Render as `<div data-mdx-missing="Name">children</div>`
3. Not interrupt the build

### MDX + Side Annotations

The `{` and `=>` side annotation types conflict with MDX expression syntax. Use keyword forms instead:

| Symbol | Keyword | Example |
|--------|---------|---------|
| `{` | `left-brace` | `(>left-brace annotation text)` |
| `=>` | `fat-arrow` | `(>fat-arrow annotation text)` |

All other side annotation types (`}`, `]`, `->`, etc.) work unchanged in MDX mode.

### Peer Dependencies

MDX support requires these optional peer dependencies:

- `@mdx-js/mdx` ^3.0.0
- `react` ^18.0.0 or ^19.0.0
- `react-dom` ^18.0.0 or ^19.0.0

When `enableMdx` is false (default), these are not needed.

## Plugin Chain

The Gold Processor applies plugins in this order:

**Remark** (Markdown → mdast):
`remark-parse` → `remark-gfm` → `remark-math`* → `remark-directive` → `remark-mdx`† → `soft-breaks`‡ → `remark-side-annotation`* → extras

**Rehype** (hast → HTML):
`rehype-katex`* → `rehype-math-display-fix`* → `rehype-slug` → `rehype-pretty-code` → `rehype-side-annotation`* → extras → `rehype-stringify`

<small>* = gated by feature flag · † = production mode only · ‡ = when `preserveSoftLineBreaks` is false</small>

## Exports

### `@owomark/view/static` (SSR-safe)

```ts
import {
  createOwoMarkProcessor,
  getOwoMarkPlugins,
  // Built-in MDX components
  DEFAULT_MDX_COMPONENTS,
  Callout, CodeDemo, Steps, Step, Tabs, Tab, FileTree, Kbd,
  // Internal plugins (for advanced use)
  remarkSideAnnotation,
  rehypeSideAnnotation,
  remarkConvertSoftBreaksToHardBreaks,
  rehypeMathDisplayFix,
} from '@owomark/view/static';
```

### `@owomark/view` (Full — includes editor + preview engine)

All static exports plus the interactive view engine, preview engine, DOM patcher, and side annotation positioner.