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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@jitword/collab-editor

v1.0.4

Published

A comprehensive collaborative editor SDK with real-time collaboration features. Drop it into any Vue application and get a full-featured editor with Notion-like experience.

Downloads

494

Readme

@jitword/collab-editor

A comprehensive, production-ready collaborative editor SDK built with Vue 3 and Tiptap. Drop it into any Vue application and get a full-featured editor with real-time collaboration support.

npm version License: MIT

✨ Features

  • 📝 Rich Text Editing - Full WYSIWYG editor with Notion-like experience
  • 👥 Real-time Collaboration - Built-in collaborative editing support
  • 🎨 Categorized Toolbar - Organized tools (Home, Insert, View, Export)
  • 📋 Table of Contents - Auto-generated document outline with smooth scrolling
  • 📊 Word Count - Real-time paragraph and character statistics
  • 🤖 AI Integration Ready - Events for AI writing, version control, and sharing
  • ⌨️ Keyboard Shortcuts - Command+S (Mac) / Ctrl+S (Windows) to save
  • 📱 Responsive Design - Works seamlessly on desktop, tablet, and mobile
  • 🎯 Highly Configurable - Control every feature via props
  • 🔌 Event-Driven - Emit events for business logic integration
  • 💅 Beautiful UI - Based on Arco Design system
  • Built-in Modals - Share, AI Writer, and Document Management modals included
  • 🚀 Out-of-the-Box - Share and Read Mode work automatically

🏗️ Architecture Principles

🔐 Security: API keys and authentication stay in your code
🔌 Flexible: Supports any collaboration backend (Yjs, Socket.io, custom, etc.)
📦 Lightweight: SDK doesn't include specific network libraries
🎯 Clear Responsibilities: SDK handles UI, you handle data

📦 Installation

# Using npm
npm install @jitword/collab-editor vue

# Using yarn
yarn add @jitword/collab-editor vue

# Using pnpm
pnpm add @jitword/collab-editor vue

Note: @arco-design/web-vue is a peer dependency and will be installed automatically. All collaboration dependencies (yjs, y-websocket, @tiptap/extension-collaboration) are bundled in the package.

🚀 Quick Start

1. Import Styles and Register Components

// main.js
import { createApp } from 'vue'
import ArcoVue from '@arco-design/web-vue'
import '@arco-design/web-vue/dist/arco.css'
import '@jitword/collab-editor/dist/style.css'
import App from './App.vue'

const app = createApp(App)
app.use(ArcoVue)
app.mount('#app')

2. Basic Usage (Out-of-the-Box)

The simplest way to use the editor with built-in features:

<template>
  <CollabEditorApp
    v-model="content"
    :config="editorConfig"
    :onlineUsers="onlineUsers"
    :currentDocumentId="currentDocId"
    @ai-generate="handleAIGenerate"
    @editor-ready="handleEditorReady"
    style="height: 100vh;"
  />
</template>

<script setup>
import { ref } from 'vue'
import { CollabEditorApp, NotionKit } from '@jitword/collab-editor'

const content = ref({
  type: 'doc',
  content: [
    {
      type: 'paragraph',
      content: [{ type: 'text', text: 'Start editing...' }]
    }
  ]
})

const onlineUsers = ref([
  { id: 1, name: 'Alice', color: '3b82f6' },
  { id: 2, name: 'Bob', color: '10b981' }
])

const currentDocId = ref('doc-123')

const editorConfig = {
  appTitle: 'My Collaborative Editor',
  documentTitle: 'Untitled Document',
  roomName: 'my-room', // For share modal display
  enableAI: true,
  enableShare: true,
  enableReadMode: true,
  enableDocumentList: true,
  showOnlineUsers: true,
  extensions: [NotionKit]
}

// Handle AI generation (only business logic needed)
const handleAIGenerate = async ({ prompt, editor }) => {
  // Call your AI API
  const result = await callYourAIService(prompt)
  // Insert into editor
  editor.commands.insertContent(result)
}

const handleEditorReady = (editor) => {
  console.log('Editor ready:', editor)
}
</script>

What you get automatically:

  • ✅ Share button → Opens built-in share modal with QR code
  • ✅ AI button → Opens built-in AI writer modal
  • ✅ Read Mode button → Opens /read/{docId} in new tab
  • ✅ Online users display in header
  • ✅ All UI components styled and ready

📚 API Reference

Props

v-model (Object | String)

The editor content. Can be ProseMirror JSON or HTML string.

const content = ref({
  type: 'doc',
  content: [/* ... */]
})

config (Object)

Editor configuration object.

| Property | Type | Default | Description | |----------|------|---------|-------------| | appTitle | String | 'JitWord 协同文档' | Application title (desktop) | | appTitleShort | String | 'JitWord' | Short title (mobile) | | documentTitle | String | '未命名文档' | Current document title | | enableAI | Boolean | true | Show AI writing button | | enableVersions | Boolean | true | Show version history button | | enableShare | Boolean | true | Show share button | | enableReadMode | Boolean | true | Show read mode button | | enableAISettings | Boolean | true | Show AI settings button | | enableDocumentList | Boolean | false | Show document list button | | enableCreateDocument | Boolean | false | Show create document button | | createDocumentTooltip | String | '新建文档' | Tooltip for create button | | showOnlineUsers | Boolean | true | Display online users | | hideToc | Boolean | false | Hide table of contents | | hideFooter | Boolean | false | Hide footer (word count) | | hideHeader | Boolean | false | Hide header bar | | hideToolbar | Boolean | false | Hide formatting toolbar | | hideBubbleMenu | Boolean | false | Hide bubble menu | | editable | Boolean | true | Allow editing | | locale | String | 'en' | Editor locale | | theme | String | 'light' | Editor theme | | isMobile | Boolean | false | Mobile mode | | isTablet | Boolean | false | Tablet mode | | aiWriterBusy | Boolean | false | AI writer loading state | | extensions | Array | [NotionKit] | Tiptap extensions | | uploadFn | Function | null | Custom file upload handler | | defaultCategory | String | 'home' | Default toolbar category | | pageWidth | Number | 210 | Page width (mm) | | pageHeight | Number | 297 | Page height (mm) | | marginTop | Number | 25.4 | Top margin (mm) | | marginRight | Number | 31.7 | Right margin (mm) | | marginBottom | Number | 25.4 | Bottom margin (mm) | | marginLeft | Number | 31.7 | Left margin (mm) | | readMode | Boolean | false | Enable read-only mode (hides toolbar, shows simple header) | | readModeBadgeText | String | 'Read-only Mode' | Badge text shown in read mode |

Built-in Modal Configuration:

| Property | Type | Default | Description | |----------|------|---------|-------------| | useBuiltinModals | Boolean | true | Enable all built-in modals | | roomName | String | '' | Room name (shown in share modal) | | shareConfig | Object | {} | Share modal configuration (see below) | | aiConfig | Object | {} | AI modal configuration (see below) | | aiSettingsConfig | Object | {} | AI Settings modal configuration (see below) | | versionConfig | Object | {} | Version History modal configuration (see below) | | docsConfig | Object | {} | Document drawer configuration (see below) | | getReadModeUrl | Function | null | Custom read mode URL generator | | onReadModeClick | Function | null | Custom read mode handler |

onlineUsers (Array)

Array of online users for collaboration display.

const onlineUsers = ref([
  { id: 1, name: 'Alice', color: '3b82f6' }, // No # prefix
  { id: 2, name: 'Bob', color: '10b981' }
])

documents (Array)

Array of documents for built-in document drawer.

const documents = ref([
  { id: 'doc-1', name: 'My Document', updatedAt: Date.now() },
  { id: 'doc-2', name: 'Another Doc', updatedAt: Date.now() - 3600000 }
])

currentDocumentId (String)

Current document ID (required for read mode and document drawer).

const currentDocumentId = ref('doc-123')

isTrashView (Boolean)

Whether document drawer is showing trash view.

documentsLoading (Boolean)

Loading state for document drawer.

Events

Editor Events

| Event | Payload | Description | |-------|---------|-------------| | update:modelValue | content | Emitted when content changes | | editor-ready | editor | Emitted when editor is initialized |

Header Button Events

| Event | Payload | Description | |-------|---------|-------------| | ai-click | { editor } | AI writing button clicked (custom mode) | | version-click | { editor } | Version history button clicked | | share-click | { editor } | Share button clicked (custom mode) | | ai-settings-click | { editor } | AI settings button clicked | | read-mode-click | { editor } | Read mode button clicked (custom mode) | | document-list-click | { editor } | Document list button clicked (custom mode) | | create-document-click | { editor } | Create document button clicked | | mobile-menu-select | value | Mobile menu item selected |

Built-in Modal Events

| Event | Payload | Description | |-------|---------|-------------| | ai-generate | { prompt, editor } | User requested AI generation | | toggle-trash | - | Toggle trash view in document drawer | | select-document | docId | Document selected from drawer | | create-document | - | Create new document requested | | delete-document | docId | Soft delete document (move to trash) | | restore-document | docId | Restore document from trash | | permanent-delete-document | docId | Permanently delete document | | rename-document | docId, newName, onSuccess | Rename document | | save | { editor, content, currentDocumentId } | Save triggered by keyboard shortcut (Cmd+S / Ctrl+S) | | version-restore | content | Version restored, update editor content | | version-create | { documentId, content, title, description, isAutoSave, author } | Version created successfully | | version-update | { documentId, versionId, title, description } | Version metadata updated | | version-delete | { documentId, versionId, version } | Version deleted | | version-preview | { documentId, versionId, version, content } | Version previewed |

Slots

#headerLeftActions

Add custom actions to the left side of the header (after built-in buttons).

<CollabEditorApp>
  <template #headerLeftActions>
    <a-button @click="customAction">Custom</a-button>
  </template>
</CollabEditorApp>

#headerMiddleActions

Add custom actions to the middle of the header (after version button).

#headerMobileMenuItems

Add custom items to the mobile dropdown menu.

#modals

Add custom modal components with access to editor and config.

<CollabEditorApp>
  <template #modals="{ editor, config }">
    <YourCustomModal :editor="editor" />
  </template>
</CollabEditorApp>

🔌 Integration Examples

Built-in Modals (Recommended)

The SDK includes fully-functional modals for common features. Just provide the data and handle the events:

<template>
  <CollabEditorApp
    v-model="content"
    :config="editorConfig"
    :onlineUsers="onlineUsers"
    :documents="documents"
    :currentDocumentId="currentDocId"
    :isTrashView="isTrashView"
    :documentsLoading="docsLoading"
    @ai-generate="handleAIGenerate"
    @rename-document="handleRenameDocument"
    @delete-document="handleDeleteDocument"
    @restore-document="handleRestoreDocument"
    @select-document="handleSelectDocument"
  />
</template>

<script setup>
import { ref } from 'vue'
import { CollabEditorApp } from '@jitword/collab-editor'

const currentDocId = ref('doc-123')
const documents = ref([
  { id: 'doc-1', name: 'Project Proposal', updatedAt: Date.now() },
  { id: 'doc-2', name: 'Meeting Notes', updatedAt: Date.now() - 86400000 }
])
const isTrashView = ref(false)
const docsLoading = ref(false)

const editorConfig = {
  roomName: 'my-room',
  enableDocumentList: true,
  enableShare: true,
  enableAI: true,
  // Customize modals
  shareConfig: {
    showQRCode: true,
    enableReadLink: true,  // Enable read-only link tab
    editLinkLabel: '✏️ Edit Link',
    readLinkLabel: '👁️ Read-only Link',
    editDescription: 'Share this link with others to collaborate in real-time:',
    readDescription: 'Share this read-only link. Others can only view the document:',
    showStats: true
  },
  aiConfig: {
    placeholder: 'Describe what you want to write...',
    quickPrompts: [
      { icon: '📝', label: 'Write', value: 'Write a professional email' }
    ]
  },
  aiSettingsConfig: {
    title: '⚙️ AI Settings',
    okText: 'Save Settings',
    apiKeyPlaceholder: 'Enter your API key',
    hint: '🔒 Your API keys are stored locally in your browser',
    storageKey: 'my_app_ai_settings',
    autoSave: false
  },
  docsConfig: {
    title: 'My Documents',
    enableRename: true,
    enableDelete: true,
    enableTrash: true
  }
}

// Handle AI generation
const handleAIGenerate = async ({ prompt, editor }) => {
  const response = await fetch('/api/ai/generate', {
    method: 'POST',
    body: JSON.stringify({ prompt })
  })
  const { text } = await response.json()
  editor.commands.insertContent(text)
}

// Handle document rename
const handleRenameDocument = async (docId, newName, onSuccess) => {
  const response = await fetch(`/api/documents/${docId}`, {
    method: 'PATCH',
    body: JSON.stringify({ name: newName })
  })
  
  if (response.ok) {
    // Update local state
    const doc = documents.value.find(d => d.id === docId)
    if (doc) doc.name = newName
    // Notify success (closes edit mode)
    onSuccess()
  }
}

// Handle document delete
const handleDeleteDocument = async (docId) => {
  await fetch(`/api/documents/${docId}`, { method: 'DELETE' })
  // Reload documents
  loadDocuments()
}

// Handle document restore
const handleRestoreDocument = async (docId) => {
  await fetch(`/api/documents/${docId}/restore`, { method: 'POST' })
  loadDocuments()
}

// Handle document select
const handleSelectDocument = (docId) => {
  currentDocId.value = docId
  router.push(`/editor/${docId}`)
}
</script>

Custom Modals (Advanced)

If you need complete control, use custom modals:

<CollabEditorApp
  v-model="content"
  :config="{ useBuiltinModals: false }"
  @ai-click="customAIModal = true"
  @share-click="customShareModal = true"
>
  <template #modals="{ editor, config }">
    <MyCustomAIModal v-model:visible="customAIModal" :editor="editor" />
    <MyCustomShareModal v-model:visible="customShareModal" />
  </template>
</CollabEditorApp>

Read Mode Integration

The SDK handles read mode automatically:

Default behavior (opens /read/{docId}):

<CollabEditorApp
  :currentDocumentId="currentDocId"
  :config="{ enableReadMode: true }"
/>

Using built-in read mode UI (simple header, no toolbar):

<CollabEditorApp
  v-model="content"
  :config="{
    readMode: true,
    documentTitle: 'My Document',
    readModeBadgeText: 'Read-only Mode',
    editable: false  // Disable editing
  }"
/>

What happens in read mode:

  • ❌ Toolbar is hidden
  • ❌ Header buttons are hidden
  • ❌ Table of contents sidebar is hidden
  • ❌ Footer is hidden
  • ✅ Simple header with document title and "Read-only" badge
  • ✅ Document statistics (paragraphs & characters)
  • ✅ Clean, distraction-free reading experience

Custom URL pattern:

const editorConfig = {
  enableReadMode: true,
  getReadModeUrl: (docId) => `/documents/${docId}/preview`
}

Custom handler (e.g., open modal instead):

const editorConfig = {
  enableReadMode: true,
  onReadModeClick: ({ editor, currentDocumentId }) => {
    showReadModeModal.value = true
  }
}

Real-time Collaboration with Yjs

All collaboration dependencies are bundled! No need to install yjs, y-websocket, or Tiptap extensions separately.

import { 
  CollabEditorApp,
  Y,                  // Yjs CRDT
  WebsocketProvider,  // WebSocket provider
  Collaboration,      // Tiptap extension
  CollaborationCursor // Collaborative cursors
} from '@jitword/collab-editor'

const ydoc = new Y.Doc()
const provider = new WebsocketProvider(
  'wss://your-websocket-server.com',
  'document-id',
  ydoc
)

// Update online users from provider awareness
provider.awareness.on('change', () => {
  const states = Array.from(provider.awareness.getStates().entries())
  onlineUsers.value = states.map(([clientID, state]) => ({
    id: clientID,
    name: state.user?.name || `User ${clientID}`,
    color: state.user?.color || '3b82f6'
  }))
})

const editorConfig = {
  // ... other config
  extensions: [
    NotionKit,
    Collaboration.configure({ document: ydoc }),
    CollaborationCursor.configure({ provider })
  ]
}

Custom File Upload

const editorConfig = {
  // ... other config
  uploadFn: async (file) => {
    const formData = new FormData()
    formData.append('file', file)
    
    const response = await fetch('https://your-api.com/upload', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${yourToken}`
      },
      body: formData
    })
    
    const data = await response.json()
    return data.url // Return the uploaded file URL
  }
}

AI Integration

const handleAIClick = async ({ editor }) => {
  // Get selected text
  const { from, to } = editor.state.selection
  const selectedText = editor.state.doc.textBetween(from, to, ' ')
  
  // Call your AI service
  const response = await fetch('https://your-ai-api.com/complete', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${yourAIKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      prompt: selectedText,
      context: 'improve_writing'
    })
  })
  
  const result = await response.json()
  
  // Insert AI response
  editor.chain().focus().insertContent(result.text).run()
}

AI Settings Modal

The built-in AI Settings modal allows users to configure their AI provider, model, and API key:

const editorConfig = {
  enableAISettings: true,  // Show AI Settings button
  aiSettingsConfig: {
    title: '⚙️ AI Settings',
    okText: 'Save Settings',
    cancelText: 'Cancel',
    apiKeyPlaceholder: 'Enter your API key (saved locally)',
    hint: '🔒 Your API keys are stored locally in your browser and never sent to our servers',
    storageKey: 'my_app_ai_settings',  // localStorage key for settings
    autoSave: false  // false = manual save, true = auto-save on change
  }
}

Features:

  • Provider selection (DeepSeek, Kimi, OpenAI, Custom)
  • Model selection (auto-updates based on provider)
  • API key management (stored in localStorage)
  • Custom base URL for OpenAI-compatible endpoints
  • Temperature control (0-2)
  • Secure local storage (never sent to servers)

Usage:

  1. User clicks "AI Settings" button in header
  2. Modal opens with current settings
  3. User configures provider, model, and API key
  4. Settings are saved to localStorage
  5. AI Writer modal uses these settings for generation

Version History Modal

The built-in Version History modal provides a complete version control system with create, restore, preview, edit, and delete operations:

<template>
  <CollabEditorApp
    v-model="content"
    :config="editorConfig"
    :currentDocumentId="currentDocId"
    @version-restore="handleVersionRestore"
    @version-create="handleVersionCreate"
    @version-update="handleVersionUpdate"
    @version-delete="handleVersionDelete"
    @version-preview="handleVersionPreview"
  />
</template>

<script setup>
import { ref } from 'vue'
import { CollabEditorApp, Message } from '@jitword/collab-editor'

const currentDocId = ref('doc-123')
const content = ref({ type: 'doc', content: [] })

const editorConfig = {
  enableVersions: true,  // Show Versions button
  versionConfig: {
    title: 'Version History',
    saveButtonText: 'Save Version',
    emptyText: 'No version history yet',
    autoSaveLabel: 'Auto',
    restoreText: 'Restore',
    editText: 'Edit Info',
    deleteText: 'Delete',
    previewText: 'Preview',
    loadMoreText: 'Load More',
    
    // Required: Load versions from your backend
    onLoadVersions: async (documentId, page, pageSize) => {
      const response = await fetch(
        `/api/documents/${documentId}/versions?page=${page}&pageSize=${pageSize}`
      )
      const data = await response.json()
      
      return {
        versions: data.versions.map(v => ({
          id: v.id,
          title: v.title,
          description: v.description,
          createdAt: v.createdAt,  // ISO string
          author: v.author,
          size: v.size,  // bytes
          isAutoSave: v.isAutoSave  // boolean
        })),
        total: data.total
      }
    },
    
    // Required: Create new version
    onCreateVersion: async (versionData) => {
      await fetch(`/api/documents/${versionData.documentId}/versions`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          content: versionData.content,
          title: versionData.title,
          description: versionData.description,
          isAutoSave: versionData.isAutoSave
        })
      })
    },
    
    // Required: Get version content for preview/restore
    onGetVersion: async (documentId, versionId) => {
      const response = await fetch(
        `/api/documents/${documentId}/versions/${versionId}`
      )
      const data = await response.json()
      return data.content  // ProseMirror JSON or HTML
    },
    
    // Required: Restore version
    onRestoreVersion: async (content) => {
      // Update editor with restored content
      // This is typically handled in the event handler below
    },
    
    // Optional: Update version metadata
    onUpdateVersion: async (updateData) => {
      await fetch(
        `/api/documents/${updateData.documentId}/versions/${updateData.versionId}`,
        {
          method: 'PATCH',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            title: updateData.title,
            description: updateData.description
          })
        }
      )
    },
    
    // Optional: Delete version
    onDeleteVersion: async (documentId, versionId) => {
      await fetch(
        `/api/documents/${documentId}/versions/${versionId}`,
        { method: 'DELETE' }
      )
    }
  }
}

// Handle version restore - update editor content
const handleVersionRestore = (content) => {
  // Update the editor with restored version
  content.value = content
  Message.success('Version restored successfully!')
}

// Handle version create - track in analytics
const handleVersionCreate = (versionData) => {
  console.log('Version created:', versionData)
  // Optional: Send analytics, update UI, etc.
}

// Handle version update - track metadata changes
const handleVersionUpdate = (updateData) => {
  console.log('Version updated:', updateData)
}

// Handle version delete - cleanup
const handleVersionDelete = (deleteData) => {
  console.log('Version deleted:', deleteData)
}

// Handle version preview - track which versions users view
const handleVersionPreview = (previewData) => {
  console.log('Version previewed:', previewData)
}
</script>

Features:

  • ✅ Create new versions with title and description
  • ✅ Preview versions in modal
  • ✅ Restore to any previous version
  • ✅ Edit version metadata (title, description)
  • ✅ Delete versions with confirmation
  • ✅ Auto-save label for automatic backups
  • ✅ Pagination with "Load More"
  • ✅ Version statistics (creation time, author, size)

Version Events:

| Event | Payload | Description | |-------|---------|-------------| | @version-restore | content (Object) | Emitted when user restores a version. Update your editor content. | | @version-create | { documentId, content, title, description, isAutoSave, author } | Emitted after version is created. Use for analytics. | | @version-update | { documentId, versionId, title, description } | Emitted when version metadata is updated. | | @version-delete | { documentId, versionId, version } | Emitted when version is deleted. Handle cleanup. | | @version-preview | { documentId, versionId, version, content } | Emitted when user previews a version. Track analytics. |

How it works:

  1. User clicks "Versions" button in header → Opens drawer
  2. SDK calls onLoadVersions() to fetch version list
  3. User clicks "Save Version" → SDK calls onCreateVersion() → Emits @version-create
  4. User clicks "Preview" → SDK calls onGetVersion() → Shows modal → Emits @version-preview
  5. User clicks "Restore" → SDK calls onGetVersion() → Emits @version-restore → You update editor
  6. User clicks "Edit Info" → Updates metadata → SDK calls onUpdateVersion() → Emits @version-update
  7. User clicks "Delete" → Shows confirmation → SDK calls onDeleteVersion() → Emits @version-delete

Best Practices:

  • Store versions in your database with documentId as foreign key
  • Include metadata: title, description, author, createdAt, size
  • Mark auto-saves with isAutoSave: true for visual distinction
  • Implement pagination for performance (default 10 versions per page)
  • Use events for analytics and UI updates
  • Handle restore by updating your v-model content

Keyboard Shortcuts

The editor supports keyboard shortcuts out of the box:

Save Document (Command+S / Ctrl+S)

The save shortcut prevents the browser's default save dialog and emits a save event:

Option 1: Handle via event listener (recommended):

<template>
  <CollabEditorApp
    v-model="content"
    :config="editorConfig"
    :currentDocumentId="currentDocId"
    @save="handleSave"
  />
</template>

<script setup>
const handleSave = async ({ editor, content, currentDocumentId }) => {
  // Save to your backend
  await fetch(`/api/documents/${currentDocumentId}`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ content })
  })
  
  // Show success message
  Message.success('Document saved!')
}
</script>

Option 2: Provide custom handler in config:

const editorConfig = {
  // ... other config
  onSave: async ({ editor, content, currentDocumentId }) => {
    // Your custom save logic
    await saveToDatabase(currentDocumentId, content)
    showNotification('Saved!')
  }
}

What happens:

  • User presses Command+S (Mac) or Ctrl+S (Windows/Linux)
  • Browser's default save dialog is prevented
  • Your save handler is called with editor instance, content, and document ID
  • You can save to backend, localStorage, or any storage

🎨 Customization

Custom Theme

The SDK uses CSS variables for theming. You can override them:

:root {
  --color-border-1: #e5e7eb;
  --color-text-2: #6b7280;
  --color-text-3: #9ca3af;
}

Custom Toolbar Category

const editorConfig = {
  defaultCategory: 'insert', // 'home' | 'insert' | 'view' | 'export'
  // ... other config
}

📱 Responsive Design

The SDK automatically adapts to different screen sizes:

const isMobile = computed(() => window.innerWidth < 768)
const isTablet = computed(() => window.innerWidth >= 768 && window.innerWidth < 1024)

const editorConfig = {
  isMobile: isMobile.value,
  isTablet: isTablet.value,
  // ... other config
}

🛠️ Development

# Install dependencies
pnpm install

# Build SDK
pnpm run build

# Watch mode (for development)
pnpm run dev

# Run example
pnpm run dev:example

📄 License

MIT © JitWord

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

🔗 Links

💡 Next Steps

After publishing v1.0.0, we'll focus on:

  • 🔐 Security: Keep API keys and authentication in user code
  • 🔌 Flexibility: Support any collaboration backend (Yjs, Socket.io, custom)
  • 📦 Lightweight: Remove specific network libraries from SDK
  • 🎯 Clear Responsibilities: SDK handles UI, users handle data pnpm install

Build SDK

pnpm run build

Watch mode (for development)

pnpm run dev

Run example

pnpm run dev:example


## 📄 License

MIT © JitWord

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## 🔗 Links

- [GitHub Repository](https://github.com/jitword/collab-editor)
- [Documentation](https://docs.jitword.com)
- [Issue Tracker](https://github.com/jitword/collab-editor/issues)

## 💡 Next Steps

After publishing v1.0.0, we'll focus on:

- 🔐 **Security**: Keep API keys and authentication in user code
- 🔌 **Flexibility**: Support any collaboration backend (Yjs, Socket.io, custom)
- 📦 **Lightweight**: Remove specific network libraries from SDK
- 🎯 **Clear Responsibilities**: SDK handles UI, users handle data