Skip to content

@vizel/core ​

Framework-agnostic core package containing Tiptap extensions, types, utilities, and styles.

Installation ​

bash
npm install @vizel/core

TIP

You typically don't need to install this package directly. It's included as a dependency of @vizel/react, @vizel/vue, and @vizel/svelte.

Styles ​

Default Styles ​

typescript
import '@vizel/core/styles.css';

This stylesheet includes CSS variables and component styles.

Components Only ​

typescript
import '@vizel/core/components.css';

This stylesheet provides component styles without CSS variable definitions. Use it with custom theming or shadcn/ui.


Extensions ​

Vizel provides pre-configured Tiptap extensions.

createVizelExtensions ​

This function creates the default set of Vizel extensions.

typescript
import { createVizelExtensions } from '@vizel/core';

const extensions = await createVizelExtensions({
  placeholder: 'Start writing...',
  features: {
    markdown: true,
    mathematics: true,
  },
});

Base Extensions (Always Included) ​

ExtensionDescription
DocumentDocument node
ParagraphParagraph node
TextText node
BoldBold mark
ItalicItalic mark
StrikeStrikethrough mark
UnderlineUnderline mark
CodeInline code mark
HeadingHeadings (default levels: [1, 2, 3], configurable up to 6)
BulletListBullet list
OrderedListNumbered list
ListItemList item
BlockquoteBlock quote
HorizontalRuleHorizontal divider
HardBreakLine break
HistoryUndo/redo
PlaceholderPlaceholder text
DropcursorDrop cursor
GapcursorGap cursor

Feature Extensions ​

FeatureExtensions
slashCommandSlashCommand
tableTable, TableRow, TableCell, TableHeader
linkLink
imageImage, ImageResize
codeBlockCodeBlockLowlight
characterCountCharacterCount
textColorColor, Highlight
taskListTaskList, TaskItem
dragHandleDragHandle
markdownMarkdown
mathematicsMathematics
embedEmbed
detailsDetails, DetailsContent, DetailsSummary
diagramDiagram
wikiLinkWikiLink
commentCommentMark
calloutCallout
mentionMention
tableOfContentsTableOfContents
superscriptSuperscript
subscriptSubscript
typographyTypography
collaborationYjs extensions (Collaboration, CollaborationCaret)

Types ​

VizelEditorOptions ​

typescript
import type { VizelEditorOptions } from '@vizel/core';

See Type Definitions for full interface.

VizelFeatureOptions ​

typescript
import type { VizelFeatureOptions } from '@vizel/core';

See Type Definitions for full interface.

VizelMarkdownFlavor ​

typescript
import type { VizelMarkdownFlavor } from '@vizel/core';

type VizelMarkdownFlavor = "commonmark" | "gfm" | "obsidian" | "docusaurus";

Supported Markdown output flavors. Controls how content is serialized when exporting Markdown. The default is "gfm".

VizelFlavorConfig ​

typescript
import type { VizelFlavorConfig } from '@vizel/core';

interface VizelFlavorConfig {
  /** How callout/admonition blocks are serialized */
  calloutFormat: VizelCalloutMarkdownFormat;
  /** Whether wiki links are serialized as [[page]] (true) or standard links (false) */
  wikiLinkSerialize: boolean;
}

Flavor-specific configuration resolved from a VizelMarkdownFlavor. Used internally by extensions to determine how to serialize Markdown.

VizelCalloutMarkdownFormat ​

typescript
import type { VizelCalloutMarkdownFormat } from '@vizel/core';

type VizelCalloutMarkdownFormat =
  | "github-alerts"
  | "obsidian-callouts"
  | "directives"
  | "blockquote-fallback";

Callout/admonition output format for Markdown serialization.

JSONContent ​

Tiptap's JSON content format. Import directly from @tiptap/core:

typescript
import type { JSONContent } from '@tiptap/core';

const content: JSONContent = {
  type: 'doc',
  content: [
    { type: 'paragraph', content: [{ type: 'text', text: 'Hello' }] }
  ],
};

VizelSaveStatus ​

typescript
import type { VizelSaveStatus } from '@vizel/core';

type VizelSaveStatus = 'saved' | 'saving' | 'unsaved' | 'error';

Theme Types ​

typescript
import type { VizelTheme, VizelResolvedTheme } from '@vizel/core';

type VizelTheme = 'light' | 'dark' | 'system';
type VizelResolvedTheme = 'light' | 'dark';

Utilities ​

groupByConsecutiveField ​

This function groups items into consecutive runs by a string field. Items with the same field value that appear sequentially are placed in the same group. Non-adjacent items with the same value are placed in separate groups.

typescript
import { groupByConsecutiveField } from '@vizel/core';

const items = [
  { group: "a", name: "1" },
  { group: "a", name: "2" },
  { group: "b", name: "3" },
  { group: "a", name: "4" },
];

const groups = groupByConsecutiveField(items, "group");
// [
//   [{ group: "a", name: "1" }, { group: "a", name: "2" }],
//   [{ group: "b", name: "3" }],
//   [{ group: "a", name: "4" }],
// ]

resolveVizelFeatures ​

This function resolves feature options to extension configuration.

typescript
import { resolveVizelFeatures } from '@vizel/core';

const resolved = resolveVizelFeatures({
  features: {
    markdown: true,
    mathematics: { katexOptions: { strict: false } },
  },
  createSlashMenuRenderer: mySlashMenuRenderer,
});

resolveVizelFlavorConfig ​

This function resolves flavor-specific configuration from a Markdown flavor name.

typescript
import { resolveVizelFlavorConfig } from '@vizel/core';

const config = resolveVizelFlavorConfig('obsidian');
// config.calloutFormat === "obsidian-callouts"
// config.wikiLinkSerialize === true

// Defaults to "gfm" when called without arguments
const defaultConfig = resolveVizelFlavorConfig();
// defaultConfig.calloutFormat === "github-alerts"
// defaultConfig.wikiLinkSerialize === false

getVizelEditorState ​

This function returns the current editor state.

typescript
import { getVizelEditorState } from '@vizel/core';

const state = getVizelEditorState(editor);
// {
//   isFocused: boolean,
//   isEmpty: boolean,
//   canUndo: boolean,
//   canRedo: boolean,
//   characterCount: number,
//   wordCount: number,
// }

formatVizelRelativeTime ​

This function formats a date as relative time.

typescript
import { formatVizelRelativeTime } from '@vizel/core';

formatVizelRelativeTime(new Date(Date.now() - 60000));
// "1m ago"

createVizelRelativeTimeTicker ​

Start an interval ticker that emits a relative-time string every intervalMs (default 10000) and once synchronously. Returns a disposer.

typescript
import { createVizelRelativeTimeTicker } from '@vizel/core';

const stop = createVizelRelativeTimeTicker({
  getDate: () => lastSaved,
  getLocale: () => locale,
  onTick: (text) => { relativeTime = text; },
});

// Call stop() in your framework's cleanup hook.

resolveVizelSaveIndicatorView ​

Pure resolver that maps (status, locale, lastSaved, relativeTime, showTimestamp) to a view-model object { iconName, isSpinner, text, shouldShowTimestamp }. Consumed by every framework's VizelSaveIndicator.

applyVizelColorToEditor ​

Apply a color to the current selection. Routes through the appropriate Tiptap command and special-cases the "inherit" / "transparent" remove sentinels. Concrete colors are also recorded as recent picks.

typescript
import { applyVizelColorToEditor } from '@vizel/core';

applyVizelColorToEditor(editor, "textColor", "#FF0000");
applyVizelColorToEditor(editor, "highlight", "transparent"); // unsets

loadVizelEmbedScripts ​

Re-parent any <script> tags inside an oEmbed container so the browser actually executes them (innerHTML-inserted scripts are inert). Invokes the Twitter widgets bootstrap when the provider is "twitter".

typescript
import { loadVizelEmbedScripts } from '@vizel/core';

loadVizelEmbedScripts(containerEl, data.provider);

resolveVizelEmbedView ​

Resolve a VizelEmbedData value into a discriminated union view-model covering the five render branches (loading, oEmbed, OGP, title-link, plain-link). Framework components iterate the resolved variant.

typescript
import { resolveVizelEmbedView, type VizelEmbedViewModel } from '@vizel/core';

const view = resolveVizelEmbedView(data);
switch (view.kind) {
  case "loading": /* ... */ break;
  case "oembed": /* view.html, view.isVideo */ break;
  case "ogp": /* view.url, view.title, view.image, ... */ break;
  case "title": /* view.url, view.title */ break;
  case "link": /* view.url */ break;
}

resolveVizelFindReplaceLabels ​

Returns a complete labels object for the find/replace UI, falling back to English defaults whenever the supplied partial locale omits a key.

typescript
import { resolveVizelFindReplaceLabels } from '@vizel/core';

const labels = resolveVizelFindReplaceLabels(locale?.findReplace);
// labels.label, labels.findPlaceholder, labels.replaceAriaLabel, ...

mountVizelEditorView ​

Append a Tiptap editor's view.dom into a container element, mirror isEditable, and return a disposer that removes the DOM only when it is still parented to the container.

typescript
import { mountVizelEditorView } from '@vizel/core';

const dispose = mountVizelEditorView(editor, containerEl);
// dispose() on unmount or editor swap.

Bubble-menu actions ​

These exports mirror the toolbar-action helpers and drive every framework's VizelBubbleMenuDefault.

VizelBubbleMenuAction ​

typescript
interface VizelBubbleMenuAction {
  id: string;
  label: string;
  icon: VizelIconName;
  group: string;
  isActive: (editor: Editor) => boolean;
  run: (editor: Editor) => void;
  shortcut: string;
  /** Optional precondition: drop the action when this extension is absent. */
  requiresExtension?: string;
}

createVizelBubbleMenuActions ​

Returns the default action list with locale-resolved labels (or English defaults when the locale is omitted).

typescript
import { createVizelBubbleMenuActions } from '@vizel/core';

const actions = createVizelBubbleMenuActions(locale);
// [{ id: "bold", label: "Bold", icon: "bold", shortcut: "Mod+B", ... }, ...]

filterVizelBubbleMenuActions ​

Filters actions down to those whose requiresExtension precondition is satisfied by the supplied editor.

typescript
import { filterVizelBubbleMenuActions } from '@vizel/core';

const usable = filterVizelBubbleMenuActions(actions, editor);

groupVizelBubbleMenuActions ​

Groups actions for visual separation in the bubble menu (mirrors groupVizelToolbarActions).


Interactions (framework-agnostic primitives) ​

These exports live under @vizel/core/interactions and provide the state-machine + event-subscription primitives the React, Vue, and Svelte adapters share. They return plain objects with subscribe callbacks — no framework runtime imports.

createVizelEditorTransactionStore ​

Returns { subscribe(cb), getVersion() } that ticks a monotonically-incremented version counter on every Tiptap transaction event. Frameworks wire this into their reactivity primitive (useSyncExternalStore, ref, $state).

typescript
import { createVizelEditorTransactionStore } from '@vizel/core';

const store = createVizelEditorTransactionStore(() => editor);
const unsubscribe = store.subscribe(() => { /* re-render */ });

createVizelDismissibleController ​

Attaches mousedown / keydown listeners that dismiss when the user clicks outside the supplied elements or presses Escape. Returns a disposer.

typescript
import { createVizelDismissibleController } from '@vizel/core';

const dispose = createVizelDismissibleController({
  getElements: () => [popoverEl, triggerEl],
  onDismiss: () => setOpen(false),
});

resolveVizelListNavigation / resolveVizelGridNavigation ​

Pure resolvers that map a key (ArrowUp / ArrowDown / Home / End, plus ArrowLeft / ArrowRight for the grid variant) and the current index to the next focused index, or null when the key isn't handled.

typescript
import { resolveVizelListNavigation, resolveVizelGridNavigation } from '@vizel/core';

const next = resolveVizelListNavigation(event.key, selectedIndex, items.length);
if (next !== null) setSelectedIndex(next);

createVizelEditorSubscription ​

Attach a Tiptap editor event listener with a clean disposer; supports an optional fireImmediately flag.

typescript
import { createVizelEditorSubscription } from '@vizel/core';

const dispose = createVizelEditorSubscription({
  getEditor: () => editor,
  event: "update",
  handler: () => { /* ... */ },
});

Theme Utilities ​

getVizelSystemTheme ​

This function returns the system color scheme preference.

typescript
import { getVizelSystemTheme } from '@vizel/core';

const theme = getVizelSystemTheme();
// 'light' | 'dark'

resolveVizelTheme ​

This function resolves a theme setting to an actual theme value.

typescript
import { resolveVizelTheme } from '@vizel/core';

resolveVizelTheme('system', 'dark');
// 'dark'

applyVizelTheme ​

This function applies a theme to the DOM.

typescript
import { applyVizelTheme } from '@vizel/core';

applyVizelTheme('dark', 'html');

getVizelThemeInitScript ​

This function returns an inline script that prevents flash of unstyled content.

typescript
import { getVizelThemeInitScript } from '@vizel/core';

const script = getVizelThemeInitScript('my-theme-key');
// Include in <head>

Image Utilities ​

createVizelImageUploader ​

This function creates an image upload handler.

typescript
import { createVizelImageUploader } from '@vizel/core';

const upload = createVizelImageUploader({
  onUpload: async (file) => {
    // Upload file
    return 'https://example.com/image.png';
  },
  maxFileSize: 5 * 1024 * 1024,
  allowedTypes: ['image/jpeg', 'image/png'],
});

validateVizelImageFile ​

This function validates an image file against size and type constraints.

typescript
import { validateVizelImageFile } from '@vizel/core';

const result = validateVizelImageFile(file, {
  maxFileSize: 5 * 1024 * 1024,
  allowedTypes: ['image/jpeg', 'image/png'],
});

if (!result.valid) {
  console.error(result.error);
}

Color Utilities ​

getVizelRecentColors ​

This function retrieves recent colors from localStorage.

typescript
import { getVizelRecentColors } from '@vizel/core';

const colors = getVizelRecentColors('text');
// ['#ff0000', '#00ff00', ...]

addVizelRecentColor ​

This function adds a color to the recent colors list.

typescript
import { addVizelRecentColor } from '@vizel/core';

addVizelRecentColor('text', '#ff0000');

Embed Utilities ​

detectVizelEmbedProvider ​

This function detects the oEmbed provider from a URL.

typescript
import { detectVizelEmbedProvider } from '@vizel/core';

const provider = detectVizelEmbedProvider('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
// { name: 'YouTube', ... }

Constants ​

VIZEL_DEFAULT_FLAVOR ​

This constant defines the default Markdown output flavor.

typescript
import { VIZEL_DEFAULT_FLAVOR } from '@vizel/core';
// "gfm"

VIZEL_TEXT_COLORS ​

This constant defines the default text color palette.

typescript
import { VIZEL_TEXT_COLORS } from '@vizel/core';
// Array of { name: string, color: string }

VIZEL_HIGHLIGHT_COLORS ​

This constant defines the default highlight color palette.

typescript
import { VIZEL_HIGHLIGHT_COLORS } from '@vizel/core';
// Array of { name: string, color: string }

vizelDefaultEmbedProviders ​

This constant defines the default oEmbed providers.

typescript
import { vizelDefaultEmbedProviders } from '@vizel/core';
// YouTube, Vimeo, Twitter, etc.

Toolbar ​

VizelToolbarAction ​

Type definition for a toolbar action.

typescript
import type { VizelToolbarAction } from '@vizel/core';

interface VizelToolbarAction {
  id: string;
  label: string;
  icon: VizelIconName;
  group: string;
  isActive: (editor: Editor) => boolean;
  isEnabled: (editor: Editor) => boolean;
  run: (editor: Editor) => void;
  shortcut?: string;
}

VizelToolbarDropdownAction ​

Type definition for a dropdown toolbar action that groups multiple sub-actions.

typescript
import type { VizelToolbarDropdownAction } from '@vizel/core';

interface VizelToolbarDropdownAction {
  id: string;
  label: string;
  icon: VizelIconName;
  group: string;
  type: 'dropdown';
  options: VizelToolbarAction[];
  getActiveOption?: (editor: Editor) => VizelToolbarAction | undefined;
}

VizelToolbarActionItem ​

Union type for any toolbar item — either a simple action or a dropdown.

typescript
import type { VizelToolbarActionItem } from '@vizel/core';

type VizelToolbarActionItem = VizelToolbarAction | VizelToolbarDropdownAction;

isVizelToolbarDropdownAction ​

Type guard to check if a toolbar item is a dropdown action.

typescript
import { isVizelToolbarDropdownAction } from '@vizel/core';

if (isVizelToolbarDropdownAction(action)) {
  // action is VizelToolbarDropdownAction
  action.options.forEach(opt => /* ... */);
}

vizelDefaultToolbarActions ​

This constant provides the default toolbar actions including undo/redo, formatting, headings, lists, and blocks.

typescript
import { vizelDefaultToolbarActions } from '@vizel/core';
// Array of VizelToolbarAction

Default groups: history, format, heading, list, block

groupVizelToolbarActions ​

This function groups toolbar actions by their group property for rendering with dividers. Supports both simple actions and dropdown actions.

typescript
import { groupVizelToolbarActions } from '@vizel/core';

const groups = groupVizelToolbarActions(actions);
// VizelToolbarActionItem[][] - each sub-array is a group

Find & Replace ​

A standalone Tiptap extension for text search and replacement within the editor. This extension is not included in createVizelExtensions() — you add it manually.

createVizelFindReplaceExtension ​

This function creates the Find & Replace extension.

typescript
import { createVizelFindReplaceExtension } from '@vizel/core';

const findReplace = createVizelFindReplaceExtension({
  caseSensitive: false,
  onResultsChange: ({ total, current }) => {
    console.log(`Match ${current} of ${total}`);
  },
});

Options ​

OptionTypeDefaultDescription
caseSensitivebooleanfalseEnable case-sensitive search
onResultsChange(results: { total: number; current: number }) => void—Called when search results change

Editor Commands ​

The extension registers the following commands on the editor:

CommandParametersDescription
openFindReplacemode?: 'find' | 'replace'Open the Find & Replace panel
closeFindReplace—Close the panel
findquery: stringSearch for text in the document
findNext—Navigate to the next match
findPrevious—Navigate to the previous match
replacetext: stringReplace the current match
replaceAlltext: stringReplace all matches
setFindCaseSensitivecaseSensitive: booleanToggle case-sensitive search
clearFind—Clear the search state

Keyboard Shortcuts ​

ShortcutAction
Mod-fOpen Find panel
Mod-Shift-hOpen Replace panel

getVizelFindReplaceState ​

This function returns the current Find & Replace plugin state from the editor.

typescript
import { getVizelFindReplaceState } from '@vizel/core';

const state = getVizelFindReplaceState(editor.state);
// {
//   query: string,
//   matches: VizelFindMatch[],
//   activeIndex: number,
//   caseSensitive: boolean,
//   isOpen: boolean,
//   mode: 'find' | 'replace',
// }

Types ​

typescript
import type {
  VizelFindReplaceOptions,
  VizelFindReplaceState,
  VizelFindMatch,
} from '@vizel/core';

import { vizelFindReplacePluginKey } from '@vizel/core';
TypeDescription
VizelFindReplaceOptionsConfiguration options for the extension
VizelFindReplaceStatePlugin state containing query, matches, activeIndex, and more
VizelFindMatchSingle match with from and to positions
vizelFindReplacePluginKeyProseMirror PluginKey for accessing the plugin state

A mark extension for wiki-style [[page-name]] links. Supports display text aliases with [[page-name|display text]] syntax.

createVizelWikiLinkExtension ​

This function creates a configured Wiki Link extension.

typescript
import { createVizelWikiLinkExtension } from '@vizel/core';

const wikiLink = createVizelWikiLinkExtension({
  resolveLink: (page) => `/wiki/${encodeURIComponent(page)}`,
  pageExists: (page) => knownPages.has(page),
  onLinkClick: (page, event) => {
    event.preventDefault();
    router.push(`/wiki/${page}`);
  },
});

Options ​

OptionTypeDefaultDescription
resolveLink(pageName: string) => string(p) => '#' + pResolve a page name to a URL
pageExists(pageName: string) => boolean() => trueCheck whether a page exists (for visual differentiation)
getPageSuggestions(query: string) => VizelWikiLinkSuggestion[]—Return autocomplete suggestions
onLinkClick(pageName: string, event: MouseEvent) => void—Called when a wiki link is clicked
existingClassstring'vizel-wiki-link--existing'CSS class for existing page links
newClassstring'vizel-wiki-link--new'CSS class for non-existing page links
HTMLAttributesRecord<string, unknown>{}Additional HTML attributes

Editor Commands ​

CommandParametersDescription
setWikiLinkpageName: string, displayText?: stringInsert a wiki link at the current position
unsetWikiLink—Remove the wiki link mark from the selection

Types ​

typescript
import type { VizelWikiLinkOptions, VizelWikiLinkSuggestion } from '@vizel/core';
import { VizelWikiLink, vizelWikiLinkPluginKey } from '@vizel/core';
TypeDescription
VizelWikiLinkOptionsConfiguration options for the extension
VizelWikiLinkSuggestionPage suggestion with name and optional label
VizelWikiLinkTiptap Mark extension class
vizelWikiLinkPluginKeyProseMirror PluginKey for the wiki link plugin

Comments ​

Comment management module for adding, resolving, and replying to text annotations. This module provides a handler factory pattern with pluggable storage backends.

createVizelCommentHandlers ​

This function creates comment handler methods for an editor.

typescript
import { createVizelCommentHandlers } from '@vizel/core';
import type { VizelCommentState } from '@vizel/core';

const handlers = createVizelCommentHandlers(
  () => editor,
  {
    storage: 'localStorage',
    key: 'my-comments',
    onAdd: (comment) => console.log('Added:', comment.id),
  },
  (partial) => setState((prev) => ({ ...prev, ...partial }))
);

// Add a comment to the current text selection
await handlers.addComment('Needs clarification', 'Alice');

// Resolve a comment
await handlers.resolveComment(commentId);

// Reply to a comment
await handlers.replyToComment(commentId, 'Fixed in latest commit', 'Bob');

Parameters ​

ParameterTypeDescription
getEditor() => Editor | null | undefinedGetter function for the editor instance
optionsVizelCommentOptionsConfiguration options
onStateChange(state: Partial<VizelCommentState>) => voidCallback to update reactive state

Return Value ​

MethodSignatureDescription
addComment(text: string, author?: string) => Promise<VizelComment | null>Add a comment to the current selection
removeComment(commentId: string) => Promise<void>Remove a comment and its mark
resolveComment(commentId: string) => Promise<boolean>Mark a comment as resolved
reopenComment(commentId: string) => Promise<boolean>Reopen a resolved comment
replyToComment(commentId: string, text: string, author?: string) => Promise<VizelCommentReply | null>Add a reply to a comment
setActiveComment(commentId: string | null) => voidSet the currently active comment
loadComments() => Promise<VizelComment[]>Load all comments from storage
getCommentById(commentId: string) => VizelComment | undefinedGet a comment by its ID

getVizelCommentStorageBackend ​

This function creates a normalized storage backend from the storage option.

typescript
import { getVizelCommentStorageBackend } from '@vizel/core';

const backend = getVizelCommentStorageBackend('localStorage', 'my-comments');
const comments = await backend.load();
await backend.save(comments);

Comment Extension ​

This extension adds a mark to the editor for highlighting commented text.

typescript
import { createVizelCommentExtension } from '@vizel/core';

const commentExtension = createVizelCommentExtension();

Types ​

typescript
import type {
  VizelComment,
  VizelCommentReply,
  VizelCommentState,
  VizelCommentOptions,
  VizelCommentStorage,
} from '@vizel/core';

import {
  VIZEL_DEFAULT_COMMENT_OPTIONS,
  vizelCommentPluginKey,
} from '@vizel/core';
TypeDescription
VizelCommentComment annotation with id, text, author, createdAt, resolved, replies
VizelCommentReplyReply with id, text, author, createdAt
VizelCommentStateState object containing comments, activeCommentId, isLoading, error
VizelCommentOptionsConfiguration for enabled, storage, key, and event callbacks
VizelCommentStorage'localStorage', 'sessionStorage', or a custom { save, load } backend (both methods required)

Version History ​

Version history module for saving, restoring, and managing document snapshots. This module uses the same handler factory pattern and pluggable storage backends as Comments.

createVizelVersionHistoryHandlers ​

This function creates version history handler methods for an editor.

typescript
import { createVizelVersionHistoryHandlers } from '@vizel/core';
import type { VizelVersionHistoryState } from '@vizel/core';

const handlers = createVizelVersionHistoryHandlers(
  () => editor,
  {
    maxVersions: 20,
    storage: 'localStorage',
    onSave: (snapshot) => console.log('Saved version:', snapshot.id),
  },
  (partial) => setState((prev) => ({ ...prev, ...partial }))
);

// Save the current document state
await handlers.saveVersion('Initial draft', 'Alice');

// List all versions
const versions = await handlers.loadVersions();

// Restore a specific version
await handlers.restoreVersion(versions[0].id);

Parameters ​

ParameterTypeDescription
getEditor() => Editor | null | undefinedGetter function for the editor instance
optionsVizelVersionHistoryOptionsConfiguration options
onStateChange(state: Partial<VizelVersionHistoryState>) => voidCallback to update reactive state

Return Value ​

MethodSignatureDescription
saveVersion(description?: string, author?: string) => Promise<VizelVersionSnapshot | null>Save the current document as a new version
restoreVersion(versionId: string) => Promise<boolean>Restore the document to a specific version
loadVersions() => Promise<VizelVersionSnapshot[]>Load all versions from storage
deleteVersion(versionId: string) => Promise<void>Delete a specific version
clearVersions() => Promise<void>Delete all versions

getVizelVersionStorageBackend ​

This function creates a normalized storage backend from the storage option.

typescript
import { getVizelVersionStorageBackend } from '@vizel/core';

const backend = getVizelVersionStorageBackend('localStorage', 'my-versions');
const snapshots = await backend.load();
await backend.save(snapshots);

Types ​

typescript
import type {
  VizelVersionSnapshot,
  VizelVersionHistoryState,
  VizelVersionHistoryOptions,
  VizelVersionStorage,
} from '@vizel/core';

import { VIZEL_DEFAULT_VERSION_HISTORY_OPTIONS } from '@vizel/core';
TypeDescription
VizelVersionSnapshotSnapshot with id, content (JSONContent), timestamp, description, author
VizelVersionHistoryStateState object containing snapshots, isLoading, error
VizelVersionHistoryOptionsConfiguration for enabled, maxVersions, storage, key, and callbacks
VizelVersionStorage'localStorage', 'sessionStorage', or a custom { save, load } backend (both methods required)

Default Options ​

typescript
VIZEL_DEFAULT_VERSION_HISTORY_OPTIONS = {
  enabled: true,
  maxVersions: 50,
  storage: 'localStorage',
  key: 'vizel-versions',
};

Collaboration ​

Real-time multi-user editing module built on Yjs. This module provides types, state management, and lifecycle utilities for integrating Yjs-based collaboration.

WARNING

You must install your own compatible versions of yjs, a Yjs provider (e.g. y-websocket), @tiptap/extension-collaboration, and @tiptap/extension-collaboration-caret.

createVizelCollaborationHandlers ​

This function creates collaboration handler methods for tracking provider state and managing lifecycle.

typescript
import { createVizelCollaborationHandlers } from '@vizel/core';
import type { VizelCollaborationState } from '@vizel/core';

const handlers = createVizelCollaborationHandlers(
  () => wsProvider,
  {
    user: { name: 'Alice', color: '#ff0000' },
    onConnect: () => console.log('Connected'),
    onPeersChange: (count) => console.log('Peers:', count),
  },
  (partial) => setState((prev) => ({ ...prev, ...partial }))
);

// Start listening to provider events
const unsubscribe = handlers.subscribe();

// Connect to the server
handlers.connect();

// Later, clean up
unsubscribe();

Parameters ​

ParameterTypeDescription
getProvider() => VizelYjsProvider | null | undefinedGetter function for the Yjs provider
optionsVizelCollaborationOptionsCollaboration options including user info
onStateChange(state: Partial<VizelCollaborationState>) => voidCallback to update reactive state

Return Value ​

MethodSignatureDescription
connect() => voidConnect to the collaboration server
disconnect() => voidDisconnect from the server
updateUser(user: VizelCollaborationUser) => voidUpdate the current user's cursor information
subscribe() => () => voidSubscribe to provider events; returns an unsubscribe function

Types ​

typescript
import type {
  VizelCollaborationUser,
  VizelCollaborationOptions,
  VizelCollaborationState,
  VizelYjsProvider,
  VizelYjsAwareness,
} from '@vizel/core';

import { VIZEL_DEFAULT_COLLABORATION_OPTIONS } from '@vizel/core';
TypeDescription
VizelCollaborationUserUser info with name and color
VizelCollaborationOptionsConfiguration for enabled, user, and event callbacks
VizelCollaborationStateState containing isConnected, isSynced, peerCount, error
VizelYjsProviderInterface matching the Yjs WebSocket provider API
VizelYjsAwarenessInterface matching the Yjs Awareness API

VizelCollaborationOptions ​

OptionTypeDefaultDescription
enabledbooleantrueEnable collaboration tracking
userVizelCollaborationUser—Current user info for cursor display (required)
onConnect() => void—Called when connected
onDisconnect() => void—Called when disconnected
onSynced() => void—Called when initial document sync completes
onError(error: Error) => void—Called when an error occurs
onPeersChange(count: number) => void—Called when the number of connected peers changes

Plugin System ​

A plugin architecture for extending Vizel with third-party functionality. The plugin system supports dependency resolution, lifecycle hooks, style injection, and transaction handling.

VizelPluginManager ​

The main class for managing plugins.

typescript
import { VizelPluginManager } from '@vizel/core';
import type { VizelPlugin } from '@vizel/core';

const manager = new VizelPluginManager();

// Register a plugin
manager.register(myPlugin);

// Pass plugin extensions to the editor
const editor = useVizelEditor({
  extensions: manager.getExtensions(),
});

// Connect the editor to enable lifecycle hooks
if (editor) manager.setEditor(editor);

// Clean up
manager.destroy();

Methods ​

MethodSignatureDescription
register(plugin: VizelPlugin) => voidRegister a plugin (validates, injects styles, calls onInstall if the editor is connected)
unregister(pluginName: string) => voidUnregister a plugin (checks dependents, removes styles, calls onUninstall)
setEditor(editor: Editor) => voidConnect an editor instance and call onInstall on all plugins
destroy() => voidDisconnect the editor and clean up all plugins
getExtensions() => ExtensionsReturn aggregated extensions from all plugins in dependency order
getPlugin(name: string) => VizelPlugin | undefinedGet a registered plugin by name
listPlugins() => VizelPlugin[]List all registered plugins
hasPlugin(name: string) => booleanCheck whether a plugin is registered

VizelPlugin Interface ​

typescript
import type { VizelPlugin } from '@vizel/core';

const myPlugin: VizelPlugin = {
  name: 'my-vizel-plugin',
  version: '1.0.0',
  description: 'Adds cool feature to Vizel',
  extensions: [MyExtension],
  styles: '.my-plugin { color: red; }',
  dependencies: ['other-plugin'],
  onInstall: (editor) => console.log('Installed'),
  onUninstall: (editor) => console.log('Uninstalled'),
  onTransaction: ({ editor, transaction }) => { /* handle transaction */ },
};
FieldTypeRequiredDescription
namestringYesUnique identifier (kebab-case)
versionstringYesSemver version (e.g. '1.0.0')
descriptionstringNoHuman-readable description
extensionsExtensionsNoTiptap extensions to add
stylesstringNoCSS styles to inject
onInstall(editor: Editor) => voidNoCalled when the editor installs the plugin
onUninstall(editor: Editor) => voidNoCalled when the editor uninstalls the plugin
onTransaction(props: { editor: Editor; transaction: Transaction }) => voidNoCalled on each editor transaction
dependenciesstring[]NoPlugin names that you must register first

validateVizelPlugin ​

This function validates a plugin's required fields and format. It throws a descriptive error if validation fails.

typescript
import { validateVizelPlugin } from '@vizel/core';

validateVizelPlugin(myPlugin); // throws if invalid

resolveVizelPluginDependencies ​

This function resolves plugin dependencies via topological sort. It throws if it detects a circular dependency or a missing dependency.

typescript
import { resolveVizelPluginDependencies } from '@vizel/core';

const ordered = resolveVizelPluginDependencies(plugins);
// Returns plugins in dependency-first order

Internationalization (i18n) ​

VizelLocale ​

Type definition for all translatable UI strings. The locale is organized by component section:

typescript
import type { VizelLocale } from '@vizel/core';
SectionDescriptionKey Fields
toolbarToolbar button labelsariaLabel, undo, redo, bold, italic, strikethrough, underline, code, heading1-heading3, bulletList, numberedList, taskList, quote, codeBlock, horizontalRule, moreActions
nodeTypesNode type selector labelstext, heading1-heading6, bulletList, numberedList, taskList, quote, code
blockMenuBlock context menulabel, delete, duplicate, copy, cut, turnInto
slashMenuSlash command menunoResults, groups, items, enterImageUrl, enterUrl, enterEmbedUrl
findReplaceFind and replace panellabel, findPlaceholder, replacePlaceholder, noResults, and button labels
codeBlockCode block UIlanguagePlaceholder, hideLineNumbers, showLineNumbers, copyCode, copied
dragHandleDrag handleariaLabel
saveIndicatorSave indicatorsaved, saving, unsaved, error
nodeSelectorNode selector dropdownchangeBlockType, blockTypes, currentBlockType
relativeTimeRelative time formattingjustNow, secondsAgo, minutesAgo, hoursAgo, daysAgo
bubbleMenuBubble menu labelsariaLabel, bold, italic, strikethrough, underline, code, link, superscript, subscript
colorPickerColor picker labelstextColor, highlight, textColorPalette, highlightPalette, recent, hexPlaceholder, apply, applyAriaLabel
linkEditorLink editor popupurlPlaceholder, apply, applyAriaLabel, removeLink, removeLinkAriaLabel, openInNewTab, visit, visitTitle, embedAsRichContent

vizelEnLocale ​

The default English locale constant.

typescript
import { vizelEnLocale } from '@vizel/core';

console.log(vizelEnLocale.toolbar.bold); // "Bold"
console.log(vizelEnLocale.bubbleMenu.superscript); // "Superscript"
console.log(vizelEnLocale.colorPicker.textColor); // "Text Color"
console.log(vizelEnLocale.linkEditor.urlPlaceholder); // "Enter URL..."

createVizelLocale ​

This function merges a partial locale with the English defaults, producing a complete VizelLocale.

typescript
import { createVizelLocale } from '@vizel/core';

const jaLocale = createVizelLocale({
  toolbar: { ariaLabel: '書式設定', bold: '太字', italic: '斜体' },
  bubbleMenu: { ariaLabel: 'テキスト書式', bold: '太字', italic: '斜体', link: 'リンク', superscript: '上付き', subscript: '下付き' },
  colorPicker: { textColor: '文字色', highlight: 'ハイライト' },
  linkEditor: { urlPlaceholder: 'URLを入力...', apply: '適用' },
});

VizelLocalePartial ​

Deep partial type that makes all nested locale properties optional. Used as the parameter type for createVizelLocale().

typescript
import type { VizelLocalePartial } from '@vizel/core';

const partial: VizelLocalePartial = {
  bubbleMenu: { superscript: 'Hochgestellt' },
  colorPicker: { textColor: 'Textfarbe' },
};

Importing from Tiptap ​

For convenience, @vizel/core re-exports commonly used Tiptap types and classes: Editor, Extensions, JSONContent, BubbleMenuPlugin, SuggestionOptions, and SuggestionProps. You can import them from either @vizel/core or directly from @tiptap/core:

typescript
// Option 1: Import from @vizel/core (convenience re-exports)
import { createVizelExtensions, getVizelEditorState } from '@vizel/core';
import type { Editor, JSONContent, Extensions, VizelEditorOptions } from '@vizel/core';

// Option 2: Import Tiptap types directly
import type { Editor, JSONContent, Extensions } from '@tiptap/core';

For types not re-exported by Vizel, import them directly from the relevant @tiptap/* package.


Next Steps ​

Released under the MIT License.