npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

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

About

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

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

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

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

Open Software & Tools

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

© 2026 – Pkg Stats / Ryan Hefner

@import-ai/omnibox-editor

v0.0.28

Published

A OmniBox editor

Downloads

4,335

Readme

@import-ai/omnibox-editor

A React block editor MVP built on Tiptap. It includes an editor UI, Markdown/JSON conversion helpers, table editing, image upload, math, diagrams, table of contents, optional AI writing hooks, and optional Yjs collaboration support.

Features

  • React component with a batteries-included Tiptap editor experience
  • Editable and read-only rendering modes
  • Markdown input, Tiptap JSON input, and Markdown export helpers
  • GitHub-like Markdown styling for headings, lists, links, code, blockquotes, tables, images, and math
  • Notion-like table editing with row/column handles, context menus, resizing, merge/split, sorting, alignment, and fit-to-width actions
  • Image upload pipeline with progress, abort, success, error, size, and count controls
  • KaTeX math rendering and in-editor formula editing
  • Mermaid/ECharts-style diagram rendering from code blocks
  • Optional table of contents sidebar
  • Optional AI writing integration through host-provided handlers
  • Optional Yjs collaboration and collaborative cursors
  • English and Simplified Chinese UI copy with translation overrides

Installation

npm install @import-ai/omnibox-editor
pnpm add @import-ai/omnibox-editor
yarn add @import-ai/omnibox-editor

react and react-dom are peer dependencies and must be installed by the consuming app.

npm install react react-dom

Optional diagram renderers

Mermaid and ECharts previews are loaded on demand (dynamic import()). They are optional peer dependencies:

npm install mermaid echarts

Without them, the editor still works; mermaid/echarts code blocks show a render error instead of a preview. Install them only in apps that need diagram previews so hosts do not need special Vite manualChunks for these libraries unless they want further tuning.

Quick Start

Import the component and the compiled CSS once in your app.

import { OmniboxEditor } from "@import-ai/omnibox-editor"
import "@import-ai/omnibox-editor/style.css"

export function App() {
  return (
    <OmniboxEditor
      placeholder="Start writing..."
      onUpdate={({ json, html, markdown }) => {
        console.log(json, html, markdown)
      }}
    />
  )
}

Recommended Production Data Flow

Use Tiptap JSON as the canonical format in production. It preserves editor-specific structures such as tables, math, images, alignment, colors, and custom nodes more reliably than plain Markdown.

import { OmniboxEditor, contentToMarkdown } from "@import-ai/omnibox-editor"
import "@import-ai/omnibox-editor/style.css"

export function ResourceEditor({
  initialContent,
  saveResource,
}: {
  initialContent: string
  saveResource: (content: unknown) => Promise<void>
}) {
  return (
    <OmniboxEditor
      content={initialContent}
      onUpdate={({ json }) => {
        void saveResource(json)
      }}
    />
  )
}

export function exportMarkdown(savedContent: unknown) {
  return contentToMarkdown(savedContent as Parameters<typeof contentToMarkdown>[0], {
    debug: false,
  })
}

For backward compatibility, content accepts both existing Markdown strings and serialized JSON strings. New writes should store json from onUpdate.

Basic Usage

Markdown Input

<OmniboxEditor content={"# Hello\n\nThis content starts as Markdown."} />

Tiptap JSON Input

<OmniboxEditor
  content={{
    type: "doc",
    content: [
      {
        type: "heading",
        attrs: { level: 1 },
        content: [{ type: "text", text: "Hello" }],
      },
    ],
  }}
/>

Serialized JSON String Input

<OmniboxEditor content={JSON.stringify(savedTiptapJson)} />

Read-Only Rendering

<OmniboxEditor content={savedContent} editable={false} showHeader={false} />

Embedded Mode

Use variant="embedded" when the editor is rendered inside an existing page shell or panel.

<OmniboxEditor
  variant="embedded"
  content={savedContent}
  contentWidth="100%"
  showHeader={false}
  showToc={false}
/>

Component API

import type { OmniboxEditorProps } from "@import-ai/omnibox-editor"

| Prop | Type | Default | Description | | --- | --- | --- | --- | | content | Content \| string | demo content | Initial content. When collaboration is disabled, changing this prop to a different value replaces the editor content. Accepts Markdown, serialized JSON, Tiptap JSON documents, or Tiptap node arrays. | | editable | boolean | true | Enables editing. Set to false for read-only rendering. | | placeholder | string | locale placeholder | Placeholder shown in empty editable blocks. | | onUpdate | (payload) => void | undefined | Called after editor content changes. Use payload.json as the preferred saved value. | | imageUpload | UploadFunction | undefined | Custom image uploader that returns the final image URL. When omitted, upload controls are hidden and pasted image files report an error through onImageUploadError. | | imageUploadMaxSize | number | package default | Maximum image size in bytes. | | imageUploadLimit | number | 3 | Maximum number of images accepted per upload operation. | | onImageUploadError | (error: Error) => void | logs to console | Called when image upload fails. | | onImageUploadSuccess | (url: string) => void | undefined | Called when image upload succeeds. | | linkBase | string | undefined | Base URL used to resolve relative links and image sources while parsing Markdown/content. | | locale | "en" \| "zh-CN" \| string | "zh-CN" | Built-in UI locale. | | translations | OmniboxEditorTranslations | undefined | Overrides individual UI labels. | | theme | "light" \| "dark" | undefined | Applies package dark-mode classes when set to "dark". | | variant | "page" \| "embedded" | "page" | Page mode shows the default editor chrome; embedded mode is more compact. | | contentWidth | number \| string | CSS default | Sets the editor content width. Numbers are treated as pixels. | | showHeader | boolean | true in page mode | Controls the top editor header. | | showToc | boolean | true in page mode | Controls the table of contents sidebar. | | mentionUsers | OmniboxEditorMentionUser[] | undefined | Users shown in the mention dropdown. | | user | OmniboxEditorCollaborationUser | undefined | Current user identity for local UI and collaboration fallback. | | collaboration | false \| object | undefined | Yjs collaboration config. | | ai | boolean \| OmniboxEditorAiConfig | undefined | Enables the AI UI and optionally provides an AI submit handler. |

Update Payload

onUpdate receives the live editor instance plus multiple content formats.

import type { OmniboxEditorUpdatePayload } from "@import-ai/omnibox-editor"

type OmniboxEditorUpdatePayload = {
  editor: Editor
  json: JSONContent
  html: string
  markdown: string
}

Recommended usage:

  • Save json to your backend.
  • Use markdown for export/download/copy workflows.
  • Use html only when your application explicitly needs HTML output.
  • Debounce or autosave outside the package if the backend should not receive every keystroke.

Markdown and JSON Helpers

The package exports conversion helpers for import/export workflows.

import {
  contentToMarkdown,
  contentToTiptapJson,
  htmlTableToTiptapNode,
  markdownToTiptapJson,
  markdownWithHtmlTablesToTiptapJson,
  tiptapJsonToMarkdown,
  type MarkdownParseOptions,
  type TiptapJsonContent,
} from "@import-ai/omnibox-editor"

| API | Description | | --- | --- | | markdownToTiptapJson(markdown, options?) | Parses Markdown into a Tiptap JSON document using Tiptap's official MarkdownManager, then applies package normalization for links, images, tables, and diagrams. | | markdownWithHtmlTablesToTiptapJson(markdown, options?) | Parses Markdown that may contain raw HTML <table> blocks. HTML tables are converted into Tiptap table nodes. | | contentToTiptapJson(content, options?) | Accepts Markdown, serialized JSON, or JSON content and returns a normalized Tiptap JSON document. Use this when the input format is unknown. | | tiptapJsonToMarkdown(json, options?) | Serializes Tiptap JSON back to Markdown. Merged tables are emitted as HTML tables when Markdown tables cannot represent them safely. | | contentToMarkdown(content, options?) | Accepts Markdown, serialized JSON, or JSON content and returns Markdown. Use this for export from saved server content. | | htmlTableToTiptapNode(html) | Converts a single HTML table string into a Tiptap table node. |

Markdown Parse Options

type MarkdownParseOptions = {
  debug?: boolean
  linkBase?: string
}

| Option | Default | Description | | --- | --- | --- | | debug | true | Logs conversion input/output to the console. Set debug: false in production import/export paths. | | linkBase | undefined | Resolves relative links and image sources against a base URL. |

Production example:

const json = contentToTiptapJson(serverContent, {
  linkBase: "https://example.com/resources/current-folder",
  debug: false,
})

const markdown = contentToMarkdown(json, { debug: false })

Image Upload

Provide imageUpload to connect the editor to your storage service.

import { OmniboxEditor, type UploadFunction } from "@import-ai/omnibox-editor"
import "@import-ai/omnibox-editor/style.css"

const uploadImage: UploadFunction = async (file, onProgress, abortSignal) => {
  const formData = new FormData()
  formData.append("file", file)

  const response = await fetch("/api/uploads", {
    method: "POST",
    body: formData,
    signal: abortSignal,
  })

  if (!response.ok) {
    throw new Error("Image upload failed")
  }

  onProgress?.({ progress: 100 })

  const result = (await response.json()) as { url: string }
  return result.url
}

export function Editor() {
  return (
    <OmniboxEditor
      imageUpload={uploadImage}
      imageUploadMaxSize={10 * 1024 * 1024}
      imageUploadLimit={5}
      onImageUploadError={(error) => {
        console.error(error)
      }}
    />
  )
}

The upload function must resolve with the final URL that should be stored in the document.

AI Integration

The package renders the AI UI only when ai is enabled. The host application owns the actual model call.

import type { OmniboxEditorAiSubmitPayload } from "@import-ai/omnibox-editor"

async function handleAiSubmit({
  action,
  prompt,
  signal,
  onChunk,
}: OmniboxEditorAiSubmitPayload) {
  const response = await fetch("/api/ai/write", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ action, prompt }),
    signal,
  })

  if (!response.ok || !response.body) {
    throw new Error("AI request failed")
  }

  const reader = response.body.getReader()
  const decoder = new TextDecoder()

  while (true) {
    const { done, value } = await reader.read()
    if (done) break
    onChunk?.(decoder.decode(value))
  }
}

<OmniboxEditor
  ai={{
    enabled: true,
    onSubmit: handleAiSubmit,
  }}
/>

onSubmit can stream plain Markdown through onChunk, or provide structured Tiptap JSON through onContent / onContentPreview.

Collaboration

Collaboration uses Yjs through Tiptap's collaboration extensions. Pass a Yjs document and, optionally, a provider with awareness support.

import * as Y from "yjs"
import { OmniboxEditor } from "@import-ai/omnibox-editor"
import "@import-ai/omnibox-editor/style.css"

const document = new Y.Doc()

<OmniboxEditor
  collaboration={{
    document,
    provider,
    user: {
      id: "user-1",
      name: "Ada",
      color: "#3b82f6",
      avatar: "https://example.com/avatar.png",
    },
  }}
/>

When collaboration is enabled, the package disables StarterKit undo/redo and does not sync external content prop updates after initialization. Treat the Yjs document as the source of truth.

Mentions

<OmniboxEditor
  mentionUsers={[
    {
      id: "user-1",
      name: "Ada Lovelace",
      color: "#3b82f6",
      avatar: "https://example.com/avatar.png",
      position: "Engineer",
    },
  ]}
/>

Internationalization

Use locale for the built-in language, and translations for targeted overrides.

<OmniboxEditor
  locale="zh-CN"
  translations={{
    placeholder: "开始写点什么...",
    uploadFailed: "图片上传失败",
  }}
/>

You can also read the merged labels:

import { getEditorTranslations } from "@import-ai/omnibox-editor"

const labels = getEditorTranslations("zh-CN", {
  placeholder: "开始写点什么...",
})

Styling

The package ships compiled CSS at:

import "@import-ai/omnibox-editor/style.css"

The CSS includes editor layout, toolbar UI, table controls, GitHub-like Markdown rules, image states, KaTeX styles, and component variables. It is generated from Tailwind CSS v4 layers and does not require Sass.

Set dark mode with the component prop:

<OmniboxEditor theme="dark" />

Constrain content width with contentWidth:

<OmniboxEditor contentWidth={860} />
<OmniboxEditor contentWidth="min(100%, 920px)" />

Exported API

Components

export { OmniboxEditor }

Conversion Helpers

export {
  contentToMarkdown,
  contentToTiptapJson,
  htmlTableToTiptapNode,
  markdownToTiptapJson,
  markdownWithHtmlTablesToTiptapJson,
  tiptapJsonToMarkdown,
}

Utilities

export { getEditorTranslations }

Types

export type {
  EditorProviderProps,
  MarkdownParseOptions,
  OmniboxEditorAiAction,
  OmniboxEditorAiConfig,
  OmniboxEditorAiFeature,
  OmniboxEditorAiSubmitPayload,
  OmniboxEditorCollaborationConfig,
  OmniboxEditorCollaborationProvider,
  OmniboxEditorCollaborationUser,
  OmniboxEditorLocale,
  OmniboxEditorMentionUser,
  OmniboxEditorProps,
  OmniboxEditorTheme,
  OmniboxEditorTranslations,
  OmniboxEditorUpdatePayload,
  TiptapJsonContent,
  UploadFunction,
}

Production Checklist

  • Import @import-ai/omnibox-editor/style.css once at the application entry.
  • Save onUpdate().json as the canonical document value.
  • Use contentToMarkdown(content, { debug: false }) for Markdown export.
  • Set debug: false in conversion helpers used by production code.
  • Provide a real imageUpload implementation before enabling image uploads for users.
  • Enforce file size, MIME type, authentication, authorization, and malware scanning on the upload API.
  • Debounce autosave outside the editor if you persist on onUpdate.
  • Use editable={false} for read-only views.
  • Pass linkBase when rendering legacy Markdown with relative links or images.
  • Keep collaboration state in Yjs when collaboration is enabled.
  • Keep AI credentials and model calls on your server; never expose secrets in the browser.

Browser and SSR Notes

@import-ai/omnibox-editor is a browser-facing React component. In SSR frameworks, render it only on the client.

Next.js example:

"use client"

import { OmniboxEditor } from "@import-ai/omnibox-editor"
import "@import-ai/omnibox-editor/style.css"

export default function EditorPage() {
  return <OmniboxEditor />
}

Local Development

From the repository root:

pnpm install
pnpm --filter @import-ai/omnibox-editor test
pnpm --filter @import-ai/omnibox-editor build

Create a local tarball:

pnpm --filter @import-ai/omnibox-editor pack:local

License

MIT