@fileverse-dev/ddoc

中文文档
7.2.2发布于 2026/11/09TypeScriptESM + CJS构建来源已验证近 30 天下载 5764

DDoc

原始使用文档
文档目录20 节
  1. 𓆏 Features include:
  2. Usage
  3. Prerequisites
  4. Install & import
  5. Sizing
  6. Peer Dependencies
  7. Update Tailwind Config
  8. Migrating from 4.x
  9. Migrating from 6.x
  10. Core Props
  11. Collaboration Props
  12. UI/UX Props
  13. Document Styling
  14. Usage Example
  15. Custom Fonts
  16. Usage Example
  17. Comments & Collaboration Props
  18. Authentication Props
  19. Utility Props
  20. Steps to run this example locally

Fileverse dDocs

ddocs.new is your privacy-focused, open-source alternative to Google Docs. It is an end-to-end encrypted document editor that is optimized for multiplayer collaboration without compromising on speed or people's control over their data.

𓆏 Features include:

  • End-to-end encryption with post-quantum cryptography
  • Real-time and async collaboration
  • Suggestion mode (tracked changes) and commenting
  • Markdown and LaTeX support
  • MermaidJS diagram support
  • Import/export of .docx, .md, .pdf, .html and more.
  • Dark mode and color themes
  • Offline/online editing (continue writing and opening older docs even with no WiFi)
  • Cross-device syncing (desktop and mobile)
  • Mobile optimized webapp & PWA
  • Private access permissions enabled through zero-knowledge proofs
  • Private social recovery of account/files, enabled through zero-knowledge proofs
  • Version history
  • End-to-end encrypted, programmable API optimised for agents (opt-in)
ddocsNew

This repository contains:

  • /package – The core package code.
  • Example & demo source code to showcase dDocs functionalities.

Usage

Prerequisites

Your app runs Tailwind CSS 3.4 and owns the @tailwind base/components/utilities entry. The package ships only editor CSS; preflight, utilities, the @fileverse/ui styles, and the KaTeX stylesheet come from your build.

Install & import

import { DdocEditor } from '@fileverse-dev/ddoc';

Load stylesheets in this order (Vite emits CSS in import order; in Next put the first two in the root layout and the last two in the editor route):

import './globals.css';                 // your tailwind entry
import '@fileverse/ui/styles/base';     // tokens and base rules
import 'katex/dist/katex.min.css';      // math; fonts are served as files
import '@fileverse-dev/ddoc/styles';    // editor CSS

Sizing

DdocEditor fills its parent: width: 100%; height: 100%. The parent must have a definite height. The editor never reads the viewport, so a navbar above it or a footer below it is ordinary layout:

<div style={{ height: '100dvh', display: 'flex', flexDirection: 'column' }}>
  <div style={{ flex: 1, minHeight: 0 }}>
    <DdocEditor renderNavbar={renderNavbar} />
  </div>
  <footer>…</footer>
</div>

If the editor renders with no height, its parent has none.

renderNavbar output is the editor's first row by default, rendered as is: the editor adds no styling, so return one element with your own background, padding and border. In flow it sits in an unstyled row that only animates the collapse. Without renderNavbar nothing is rendered. To place the navbar elsewhere, pass the element to render into. Hold it in state, not in a ref, so the editor re-renders when it attaches:

const [navbarEl, setNavbarEl] = useState<HTMLElement | null>(null);

<header ref={setNavbarEl} />
<DdocEditor navbarContainer={navbarEl} renderNavbar={renderNavbar} />

The layout follows the editor's width, not the window's: in a narrow panel it uses its narrow layout. Known limits: one DdocEditor per page; in a narrow editor on a wide screen the comment sheet still covers the whole viewport.

Peer Dependencies

This package requires the following peer dependencies to be installed in your project:

npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities @fileverse/ui @fileverse/crypto viem framer-motion frimousse
PackageVersion
@dnd-kit/core>=6.3.1
@dnd-kit/sortable>=10.0.0
@dnd-kit/utilities>=3.2.2
@fileverse/ui^5.4.2
@fileverse/crypto>=0.0.21
viem>=2.13.8
framer-motion>=11.2.10
frimousse>=0.3.0

The peer range is @fileverse/ui@^5.4.2. If you ever test a prerelease pairing, pin both packages exactly: a prerelease never satisfies a caret range.

These are externalized from the bundle to avoid duplication when your app already uses them. If you don't have them installed, npm (v7+) will auto-install them for you.

Update Tailwind Config

module.exports = {
  presets: [require('@fileverse-dev/ddoc/tailwind')],
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
    './node_modules/@fileverse-dev/ddoc/dist/**/*.{js,mjs}',
    './node_modules/@fileverse/ui/dist/**/*.{js,mjs}',
  ],
};

Both dist globs are required, not the single index.es.js entry: the dist is chunked (index.es.js statically imports use-headless-editor-<hash>.mjs), so a single-file entry leaves that chunk's classes ungenerated.

The preset composes @fileverse/ui/tailwind (class-based dark mode, animate plugin, design-system classes) and adds the mobile: 960px screen the editor uses.

tailwindcss@^3.4.0 is an optional peer: the preset runs inside your own Tailwind build, which also supplies the postcss and postcss-js the ui preset loads. Tailwind 4 is not supported.

Migrating from 4.x

5.0 stops shipping preflight, Tailwind utilities, the ui stylesheet, the KaTeX stylesheet, and the html, body, and * resets. To upgrade:

  1. Add the preset and the two content entries above.

  2. Import @fileverse/ui/styles/base and katex/dist/katex.min.css yourself, in the order above.

  3. The package no longer ships these global declarations:

    • * { margin: 0; padding: 0 }
    • html, body { height: 100%; width: 100% }
    • html, body { overflow: hidden }
    • body { position: static; user-select: none; -moz-osx-font-smoothing: grayscale }
    • *, ::before, ::after { border-color: hsl(var(--color-border-default)) }

    If your shell relied on overflow: hidden or user-select: none, add them to your own global stylesheet. The rest are covered by Tailwind preflight or the @fileverse/ui/styles/base sheet.

  4. Remove any workaround that re-declared mobile: classes; the preset generates them.

  5. Add katex to your own dependencies (npm install katex@^0.16.11); the package no longer guarantees it is hoisted.

  6. If you styled anything from the old bundle by class name: .custom-scrollbar is now .ddoc-scrollbar, and .highlight-comment-bg, .is-active, .custom-border-bg, .animate-fade-in-out, .placeholder-disabled, .tooltip:before were removed as unused.

  7. The composed @fileverse/ui preset defines rounded-sm as calc(var(--radius) - 4px) (4px) instead of Tailwind's default 2px, so any surface using ddoc's rounded-sm utilities now renders 4px corners.

  8. Third-party CSS ddoc imports (the highlight.js theme, tippy) still ships global .hljs* and .tippy-box selectors — this is outside the package's own selector-ownership guarantee.

  9. List rules (ul, ol, and the nested-list counters) are now scoped to :where(.ProseMirror, .presentation-mode) and no longer style ol/ul outside the editor or presentation surfaces. If your app relied on ddoc's global list rules elsewhere, own them yourself.

You should now be set to use dDocs!

Migrating from 6.x

7.0 changes how the editor is sized. No prop was removed.

  • Give the editor a parent with a definite height (see Sizing). It no longer sizes itself from 100dvh.
  • Put your footer after the editor in normal flow. footerHeight has no effect and can be removed.
  • The navbar and toolbars are no longer inside #editor-canvas. CSS that used #editor-canvas as their ancestor should use .ddoc-editor-root.
  • The editor no longer wraps your navbar in <nav id="Navbar">. It renders what renderNavbar returns, so move the wrapper's styling (color-bg-default p-2 flex gap-10 items-center justify-between w-full border-b color-border-default) and the id, if you select on it, onto your own element. The Escape-returns-focus-to-editor handler went with it, and hiding now collapses an unstyled row (.ddoc-navbar-row) around your element. Without renderNavbar nothing is rendered. The navbar's height comes from its content; it was a fixed 64px, 46px below lg.
  • editorCanvasClassNames should not set a height.
  • zoomLevel, isNavbarVisible and isPreviewMode are optional. Every UI state can be left out, or controlled with a value and an onXChange callback. The setX props still work and are deprecated.
  • Your Tailwind build needs no config change. The new ddoc-* variants (container-query breakpoints that follow the editor's width) come from the @fileverse-dev/ddoc/tailwind preset you already use.

Behaviour that changes without a code change on your side:

  • UI features that did nothing without their setX prop now work on internal state. The Split View toggle shows whenever live collaboration is off (it needed setIsSplitView before), and the comment drawer, the outline and presentation mode open without a setter. To keep one fixed, pass its value with no callback, for example isPresentationMode={false}. For Split View, isSplitView={false} with no callback also hides the toolbar toggle.
  • onFocusModeChange and the deprecated onFocusMode also fire when isFocusMode is not passed.
  • #editor-canvas is now the document scroller (it carries data-editor-scroll-container), and the root (.ddoc-editor-root) is overflow: hidden.
  • A navbar portalled with navbarContainer is positioned and layered by you; the editor only renders into the element. Focus mode and the toolbar's collapse toggle do not hide it: hide your own element from onFocusModeChange and onNavbarVisibleChange if you want that, or pass showNavbarToggle={false} to remove the collapse toggle.
  • The toolbars no longer slide or fade when hidden; their rows are removed and the canvas takes the space. An in-flow navbar still slides up and fades over 300ms. For a portalled navbar the animation is yours: put ddoc-navbar-row on your element and add ddoc-navbar-row--hidden to collapse it.
  • The mobile tab panel sits at the editor's bottom edge. It no longer adds 24px + env(safe-area-inset-bottom), so your footer must carry the safe-area inset.
  • The editor's responsive classes use :where() in their window fallback, so they need Chrome 88+, Safari 14+ or Firefox 78+.

dDocProps Interface

The DdocProps interface is a TypeScript interface that defines the properties for a page-related component. It includes properties for handling preview mode, managing publishing data, and optionally storing metadata and content associated with the page.

Core Props

PropertyTypeDescription
initialContentJSONContentInitial content of the editor
onChange(changes: JSONContent, chunk?: any) => voidCallback triggered on editor content changes
refReact.RefObjectReference to access editor instance
isPreviewModebooleanControls if editor is in preview/read-only mode. Optional, defaults to false
editorCanvasClassNamesstringAdditional CSS classes for editor canvas
ignoreCorruptedDatabooleanWhether to ignore corrupted data during loading
onInvalidContentError(error: any) => voidCallback for handling invalid content errors

Collaboration Props

PropertyTypeDescription
enableCollaborationbooleanEnables real-time collaboration features
collaborationIdstringUnique ID for collaboration session (required when collaboration enabled)
usernamestringUser's display name for collaboration
setUsername(username: string) => voidFunction to update username
walletAddressstringUser's wallet address
onCollaboratorChange(collaborators?: IDocCollabUsers[]) => voidCallback when collaborators change
enableIndexeddbSyncbooleanEnables IndexedDB sync for offline support
ddocIdstringUnique document ID (required for IndexedDB sync)

UI/UX Props

Every UI state can be left out (the editor keeps it internally), or controlled with a value and an onXChange callback. The setX props still work and are deprecated.

PropertyTypeDescription
zoomLevelstringCurrent zoom level of the editor. Optional, defaults to '1'
onZoomLevelChange(zoomLevel: string) => voidZoom changed
setZoomLevelReact.Dispatch<SetStateAction<string>>Deprecated, use onZoomLevelChange
isNavbarVisiblebooleanControls navbar visibility. Optional, defaults to true
onNavbarVisibleChange(visible: boolean) => voidNavbar shown or hidden
setIsNavbarVisibleReact.Dispatch<SetStateAction<boolean>>Deprecated, use onNavbarVisibleChange
renderNavbar() => JSX.ElementCustom navbar renderer. Without it no navbar is rendered
navbarContainerHTMLElement | nullElement to render the navbar into. Omit for in-flow. null renders nothing until it attaches
showNavbarTogglebooleanShow the toolbar's navbar collapse chevron. Default true
renderThemeToggle() => JSX.ElementCustom theme toggle renderer
showTOCbooleanControls the outline
onShowTOCChange(show: boolean) => voidOutline shown or hidden
setShowTOCReact.Dispatch<SetStateAction<boolean>>Deprecated, use onShowTOCChange
commentDrawerOpenbooleanControls the comment drawer
onCommentDrawerOpenChange(open: boolean) => voidComment drawer opened or closed
setCommentDrawerOpenReact.Dispatch<SetStateAction<boolean>>Deprecated, use onCommentDrawerOpenChange
isPresentationModebooleanControls presentation mode
onPresentationModeChange(active: boolean) => voidPresentation mode entered or left
setIsPresentationModeReact.Dispatch<SetStateAction<boolean>>Deprecated, use onPresentationModeChange
isSplitViewbooleanControls Split View
onSplitViewChange(active: boolean) => voidSplit View entered or left
setIsSplitViewReact.Dispatch<SetStateAction<boolean>>Deprecated, use onSplitViewChange
isFocusModebooleanControls focus mode
onFocusModeChange(isFocusMode: boolean) => voidFocus mode changed
onFocusMode(isFocusMode: boolean) => voidDeprecated, use onFocusModeChange
onStatsChange(stats: { words; characters; selectedWords; pages: number | null }) => voidActive-tab stats; fires when any field changes. pages is null until measured
setWordCount, setCharacterCount, setSelectedWordCount, setPageCountReact.Dispatch<SetStateAction<number>>Deprecated, use onStatsChange
footerHeightstringDeprecated, no effect. Put the footer after the editor in normal flow
sharedSlidesLinkstringLink for shared presentation slides
documentStylingDocumentStylingCustom styling for document appearance
fontsFontDescriptor[]Consumer-provided font catalog (see Custom Fonts)

Document Styling

The documentStyling prop allows you to customize the visual appearance of your document with three distinct styling areas:

interface ThemeVariantValue {
  light: string;
  dark: string;
}

interface DocumentStyling {
  /**
   * Background styling for the outer document area.
   * Supports CSS background values including gradients.
   * Example: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
   */
  background?: string | ThemeVariantValue;

  /**
   * Background color for the editor canvas/content area.
   * Should be a solid color value.
   * Example: "#ffffff" or "rgb(255, 255, 255)"
   */
  canvasBackground?: string | ThemeVariantValue;

  /**
   * Text color for the editor content.
   * Example: "#333333" or "rgb(51, 51, 51)"
   */
  textColor?: string | ThemeVariantValue;

  /**
   * Font family for the editor content.
   * Example: "Inter, sans-serif" or "'Times New Roman', serif"
   */
  fontFamily?: string;
}

Usage Example

<DdocEditor
  documentStyling={{
    background: {
      light: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
      dark: 'linear-gradient(135deg, #2a3145 0%, #3a2f59 100%)',
    },
    canvasBackground: { light: '#ffffff', dark: '#1e1f22' },
    textColor: { light: '#333333', dark: '#e8ebec' },
    fontFamily: 'Inter, sans-serif',
  }}
  // ... other props
/>

Note: Document styling works in both regular editor mode and presentation mode. In presentation mode, only canvasBackground, textColor, and fontFamily are applied to maintain clean slide appearance.

Custom Fonts

The editor ships with a system-font baseline only (Arial, Calibri, Georgia, Times New Roman, etc.) and makes no third-party font requests — there is no Google Fonts @import. To offer additional fonts, pass a fonts catalog. Each font is self-hosted by your app and loaded on demand via the CSS Font Loading API: a font's woff2 is fetched only when it's selected in the picker or when a document (including a remote collaborator's change) actually renders text in it.

type FontDescriptor = {
  /** Display name shown in the picker, e.g. "Poppins". */
  name: string;
  /** CSS font-family stack stored in the content, e.g. "Poppins, sans-serif". */
  family: string;
  /**
   * woff2 source(s). Omit for a pure system font (no loading).
   *   - string: a single file covering all weights (e.g. a variable font).
   *   - Record<number, string>: a per-weight map, e.g. { 400: url400, 700: url700 }.
   */
  url?: string | Record<number, string>;
  /**
   * Optional SVG preview rendered in the picker. Any React node that renders an
   * <svg>. Falls back to the font name in the default font when absent.
   */
  preview?: React.ReactNode;
};

Usage Example

import { DdocEditor, FontDescriptor } from '@fileverse-dev/ddoc';
// Self-host the woff2 files however your bundler prefers (e.g. @fontsource/*).
import poppins400 from '@fontsource/poppins/files/poppins-latin-400-normal.woff2';
import poppins700 from '@fontsource/poppins/files/poppins-latin-700-normal.woff2';

const fonts: FontDescriptor[] = [
  {
    name: 'Poppins',
    family: 'Poppins, sans-serif',
    url: { 400: poppins400, 700: poppins700 },
    preview: <PoppinsPreview />, // optional SVG
  },
];

<DdocEditor fonts={fonts} /* ...other props */ />;

Notes:

  • The picker lists the system baseline and your catalog together, sorted A–Z (with Default pinned to the top).
  • The CSS face name is derived from the first token of family, so name is purely cosmetic and can differ (e.g. name: 'Poppins (Brand)', family: 'Poppins, sans-serif').
  • The catalog also drives PDF/print export, which emits @font-face rules for the registered fonts.
  • Host your woff2 files same-origin so they resolve in both the editor and the print iframe.

Comments & Collaboration Props

PropertyTypeDescription
initialCommentsIComment[]Initial comments to display
setInitialComments(comments: IComment[]) => voidFunction to update comments
onCommentReply(id: string, reply: IComment) => voidCallback for comment replies
onNewComment(comment: IComment) => voidCallback for new comments
commentDrawerOpenbooleanControls comment drawer visibility
onCommentDrawerOpenChange(open: boolean) => voidComment drawer opened or closed
setCommentDrawerOpen(isOpen: boolean) => voidDeprecated, use onCommentDrawerOpenChange
onResolveComment(commentId: string) => voidCallback when resolving comments
onUnresolveComment(commentId: string) => voidCallback when unresolving comments
onDeleteComment(commentId: string) => voidCallback when deleting comments
disableInlineCommentbooleanDisables inline commenting feature

Authentication Props

PropertyTypeDescription
isConnectedbooleanUser connection status
isLoadingbooleanAuthentication loading state
connectViaUsername(username: string) => Promise<void>Username-based authentication
connectViaWallet() => Promise<void>Wallet-based authentication
isDDocOwnerbooleanIndicates if user owns the document

Utility Props

PropertyTypeDescription
setCharacterCountReact.Dispatch<SetStateAction<number>>Deprecated, use onStatsChange. Updates character count
setWordCountReact.Dispatch<SetStateAction<number>>Deprecated, use onStatsChange. Updates word count
setPageCountReact.Dispatch<SetStateAction<number>>Deprecated, use onStatsChange. Updates approx. export page count
ensResolutionUrlstringURL for ENS name resolution
ipfsImageUploadFn (file: File) => Promise<IpfsImageUploadResponse>function for secure image uploads
ipfsImageFetchFn (_data: IpfsImageFetchPayload) => Promise<{ url: string;file: File;}>function for fetch secure image from IPFS
onError(error: string) => voidGeneral error handler
onInlineComment() => voidCallback for inline comments
onMarkdownExport() => voidCallback for markdown export
onMarkdownImport() => voidCallback for markdown import
onPdfExport() => voidCallback for pdf export
onSlidesShare() => voidCallback for slides sharing
onComment() => voidGeneral comment callback

Steps to run this example locally

  • npm i
  • npm run dev

It will open up a vite server, that will have the Ddoc Editor.

⚠️ This repository is currently undergoing rapid development, with frequent updates and changes. We recommend not to use in production yet.

版本与发布记录

@fileverse-dev/ddoc 共发布 1,358 个版本,首次发布于 2024/06/05;过去 12 个月发布了 785 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
7.2.2latest2026/11/095 天前
7.2.12026/11/068 天前
7.2.02026/11/068 天前
7.1.6-pages.32026/11/059 天前
7.1.6-pages.22026/11/059 天前
7.1.6-pages.12026/11/0410 天前

其他发布通道:patch-12 → 2.1.4-patch-12,patch-13 → 2.1.4-patch-13,patch-14 → 2.1.4-patch-14,patch-15 → 2.1.4-patch-15,patch-16 → 2.1.4-patch-16,patch-17 → 2.1.4-patch-17。

关于 @fileverse-dev/ddoc 的常见问题

@fileverse-dev/ddoc 是什么?

DDoc

如何安装 @fileverse-dev/ddoc?

在项目目录运行 npm install @fileverse-dev/ddoc,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:yjs(>=13.6.30 <14)、viem(>=2.35.0)、react(^18.2.0 || ^19.0.0)、mermaid(11.14.0)、frimousse(>=0.3.0)、react-dom(^18.2.0 || ^19.0.0)、@dnd-kit/core(>=6.3.1)、@fileverse/ui(^5.4.2)、framer-motion(>=11.2.10)、@fileverse/ens(0.0.4)、@dnd-kit/sortable(>=10.0.0)、@fileverse/crypto(>=0.0.21)、@dnd-kit/utilities(>=3.2.2)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@fileverse-dev/ddoc 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@fileverse-dev/ddoc 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@fileverse-dev/ddoc 可以商用吗?

@fileverse-dev/ddoc 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@fileverse-dev/ddoc 的下载量有多少?

根据 npm 官方下载统计,@fileverse-dev/ddoc 在 2026/10/15 – 2026/11/13 这 30 天内被下载 5,764 次,日均约 192 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@fileverse-dev/ddoc 还在维护吗?

最新版本 7.2.2 发布于 2026/11/09(5 天前);至今共发布 1358 个版本,其中过去 12 个月发布了 785 个;首次发布于 2024/06/05。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。