@alggtta/editor

中文文档
7.0.6发布于 2026/11/06UNLICENSEDTypeScriptESM + CJS近 30 天下载 2155

Alggtta Editor with Tiptap 3.x and DaisyUI 5

原始使用文档
文档目录27 节
  1. Install
  2. TypeScript
  3. Basic Usage
  4. Next.js App Router
  5. Vite / CRA / React
  6. Visual Variants
  7. License UI
  8. Subpath Imports
  9. Embed anywhere (no React app)
  10. Optional Feature Composition
  11. Plugin Development
  12. Embed Frame Security
  13. Tailwind and DaisyUI
  14. Migration from v2
  15. onUpdate
  16. UniversalEditor
  17. Optional Features
  18. Optional citation document schema
  19. License
  20. Independent feature plugins
  21. Offline CSL core (optional /citations entry)
  22. Optional research source references
  23. Static bibliography and quick-copy output
  24. Offline citation style management
  25. Static RTF citation scan (CITE-07)
  26. Optional document DOI notice warnings
  27. PDF annotation domain proposals (READ-02 foundation)

@alggtta/editor

React/TypeScript Notion-style editor built on Tiptap 3, Tailwind CSS v4, and DaisyUI 5.

@alggtta/editor (core-first since v3) keeps the default package focused on the core editor. Heavy features such as database blocks, Mermaid, AI, and export helpers are available through opt-in subpath imports.

Install

npm install @alggtta/editor --save-exact

Install peer dependencies in your app:

npm install react react-dom @tiptap/[email protected] @tiptap/[email protected] @tiptap/[email protected] @tiptap/[email protected] @tiptap/[email protected] [email protected] [email protected] --save-exact

Keep all Tiptap singleton peers on exactly 3.31.3. Tiptap packages require matching patch versions. This patch fixes GHSA-cp6q-959q-f8rh; 3.28.x–3.29.x also have a known React NodeView caret regression. @tiptap/y-tiptap uses its independent 3.0.10 release line. [email protected] and [email protected] are also required singleton peers because the main entry eagerly links the collaboration extension graph.

Yarn Classic 1.x consumers must use the same exact peers with --ignore-optional:

yarn add --ignore-optional --exact @alggtta/editor react react-dom @tiptap/[email protected] @tiptap/[email protected] @tiptap/[email protected] @tiptap/[email protected] @tiptap/[email protected] [email protected] [email protected]

That flag skips every optional dependency in this Yarn install. Its important effect in this graph is preventing the redundant newer menu copies resolved from @tiptap/react's optional ranges; the two exact root menu packages above keep menu functionality available. Explicitly install any other optional package your app needs. This workaround is specific to Yarn Classic 1.x, not a general Yarn Modern requirement.

TypeScript

Minimum TypeScript: >=5.3

typescript is declared as an optional peer dependency — JavaScript consumers need nothing. TypeScript consumers need 5.3 or newer because the CommonJS declarations served to exports["*"].require.types (dist/**/*.d.cts) annotate their type-only edges to ESM-only packages (yjs, @tanstack/react-table) with with { "resolution-mode": "import" }. TypeScript only parses that attribute from 5.3 onward; on 5.2 and older it is a syntax error (TS1005) that skipLibCheck: true does not suppress, so the CJS type surface fails to load entirely.

Measured: 5.2.2 → TS1005 (tsc exit 2, with or without skipLibCheck); 5.3.2 → clean. The ESM declarations (dist/**/*.d.ts) carry no such attribute and work on older compilers — the floor applies only to the require condition.

Production usage requires an Alggtta token. Localhost development still works with a token-shaped value such as tok_dev.

Basic Usage

import { AlggttaEditor } from '@alggtta/editor'
import '@alggtta/editor/styles.css'

export function EditorPage() {
  return (
    <AlggttaEditor
      token={process.env.NEXT_PUBLIC_ALGGTTA_TOKEN ?? ''}
      content="<h1>Hello</h1><p>Type / for commands.</p>"
      onUpdate={({ editor, json, html, text }) => {
        console.log({ json, html, text })
      }}
    />
  )
}

Next.js App Router

Use the editor from a client component.

'use client'

import { AlggttaEditor } from '@alggtta/editor'
import '@alggtta/editor/styles.css'

export default function EditorClient() {
  return (
    <AlggttaEditor
      token={process.env.NEXT_PUBLIC_ALGGTTA_TOKEN ?? ''}
      variant="document"
      onUpdate={({ json }) => {
        void fetch('/api/document', {
          method: 'POST',
          body: JSON.stringify(json),
        })
      }}
    />
  )
}

Vite / CRA / React

import { AlggttaEditor } from '@alggtta/editor'
import '@alggtta/editor/styles.css'

export function App() {
  return (
    <AlggttaEditor
      token={import.meta.env.VITE_ALGGTTA_TOKEN}
      placeholder="Write something..."
    />
  )
}

Visual Variants

Use variant to fit the editor into different layouts:

<AlggttaEditor token={token} variant="document" />
<AlggttaEditor token={token} variant="embedded" />
<AlggttaEditor token={token} variant="bare" />
  • document: default document canvas with comfortable padding.
  • embedded: compact shell for dashboards and admin panels.
  • bare: minimal wrapper when your app owns the layout.

License UI

token is required. verifyEndpoint defaults to https://cdn.alggtta.com.

<AlggttaEditor
  token={token}
  licenseFallback={({ error }) => (
    <div role="alert">Editor license check failed: {error}</div>
  )}
/>

Subpath Imports

import { useAlggttaEditor, useAutosave } from '@alggtta/editor/headless'
import { definePlugin, resolvePlugins, PluginSlot } from '@alggtta/editor/plugin'
import { createSchedulePlugin, ScheduleEditor } from '@alggtta/editor/schedule'
import { createGuideFlowPlugin } from '@alggtta/editor/guideflow'
import { createMathPlugin } from '@alggtta/editor/math'
import { createDatabaseExtensions, databaseSlashCommands } from '@alggtta/editor/database'
import { createMermaidExtensions, mermaidSlashCommands } from '@alggtta/editor/mermaid'
import { createAIExtensions, getDocumentContext } from '@alggtta/editor/ai'
import { markdownToHtml, htmlToMarkdown } from '@alggtta/editor/export'
import { LicensedGate, useLicenseVerification } from '@alggtta/editor/license'
import { useComments, CommentSidebar } from '@alggtta/editor/comments'
import { createCollaborationExtensions, usePresence } from '@alggtta/editor/collab'
import { UniversalEditor } from '@alggtta/editor/legacy'

The main entry includes the core blocks: paragraph, headings 1-3, lists, task list, quote, divider, link, highlight, image, table, code block, slash menu, bubble menu, and drag handle. Pass preset="full" to also mount the advanced blocks (callout, toggle, columns, embed, file attachment, mention, wiki link, comments, find/replace, synced block) and their slash commands. Math (KaTeX) is excluded from full to keep the bundle lean — it's a heavy optional peer; add createMathPlugin() from @alggtta/editor/math via extensionConfig.plugins (or MathExtension/InlineMathExtension from @alggtta/editor/legacy via extensionConfig.customExtensions) if you need it.

For math on Node 20, use npm install --save-exact [email protected]. KaTeX 0.18.5 adds a CLI dependency requiring Node 22.12+, so an engine-strict Node 20 install of that version fails. The editor retains the compatible ^0.18.4 peer range; workspace/demo testing uses 0.18.5 on Node 22.

The main entry also exports the framework-agnostic mount(el, props) factory, the <alggtta-editor> Web Component (defineAlggttaEditorElement()), and the getWikiLinks(doc) link-enumeration API. To keep the core lean, the heavier page-chrome components (VersionHistory, PageMeta, TableOfContents) live in @alggtta/editor/legacy, and the version/migration utilities (createPageVersionFromAITransaction, MigrationRegistry) in @alggtta/editor/headless.

Embed anywhere (no React app)

The mount(el, props) factory and <alggtta-editor> Web Component work from any stack. The bundle served from cdn.alggtta.com keeps React external, so load the react / react-dom UMD globals first, then the editor. Every CDN asset is token-gated, so pass ?token= on both the script and the stylesheet — a <link> / <script> tag can't send an Authorization header (no token → 401; invalid, expired, or wrong-domain token → 403):

<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<link rel="stylesheet" href="https://cdn.alggtta.com/editor.css?token=YOUR_TOKEN" />
<script src="https://cdn.alggtta.com/editor.umd.js?token=YOUR_TOKEN"></script>
<alggtta-editor token="YOUR_TOKEN" preset="full"></alggtta-editor>
<!-- or imperatively: window.AlggttaEditor.mount('#el', { token, preset: 'full' }) -->

From npm you can call mount / defineAlggttaEditorElement directly (React resolved as a peer) — no UMD globals needed.

Building a wiki? The editor ships the primitives ([[ / @ triggers, getWikiLinks, autosave). Durable storage — backlinks, full-text search, and versions — is handled by the private companion package @alggtta/wiki-store, provided to licensees on request (contact https://alggtta.com).

Optional entries may require their matching peer dependencies, for example @tanstack/react-table@^9.1.2 for database blocks and mermaid for Mermaid diagrams. The database subpath uses TanStack Table v9's feature-explicit API and is not compatible with v8. Export/import UI loads html2pdf.js and mammoth only when the PDF or DOCX actions are used.

Optional Feature Composition

import { AlggttaEditor } from '@alggtta/editor'
import { createDatabaseExtensions, databaseSlashCommands } from '@alggtta/editor/database'

<AlggttaEditor
  token={token}
  extensionConfig={{
    customExtensions: createDatabaseExtensions(),
    customSlashCommands: databaseSlashCommands,
  }}
/>

Export UI is also opt-in. Import the dropdown from /export and inject it into the editor footer:

import { AlggttaEditor } from '@alggtta/editor'
import { ExportDropdown, exportSlashCommands } from '@alggtta/editor/export'

<AlggttaEditor
  token={token}
  extensionConfig={{
    customSlashCommands: exportSlashCommands,
  }}
  renderFooterControls={({ editor, allowedFeatures }) =>
    allowedFeatures.includes('*') || allowedFeatures.includes('export')
      ? <ExportDropdown editor={editor} />
      : null
  }
/>

Only expose optional commands for features you actually register or render.

Plugin Development

Use the lightweight /plugin entry to bundle custom Tiptap extensions and slash commands into a mount-time plugin. Plugin command ids and extension names must be unique; command ids also cannot collide with ids declared by the host's customSlashCommands.

import { AlggttaEditor } from '@alggtta/editor'
import { ClockIcon } from '@heroicons/react/24/outline'
import { Extension, definePlugin } from '@alggtta/editor/plugin'
import '@alggtta/editor/styles.css'

const timestampPlugin = definePlugin({
  id: 'com.example.timestamp',
  version: '1.0.0',
  extensions: [Extension.create({ name: 'exampleTimestampBehavior' })],
  slashCommands: [
    {
      id: 'com.example.timestamp.insert',
      title: 'Insert timestamp',
      description: 'Insert the current time',
      icon: ClockIcon,
      command: (editor) => {
        editor.chain().focus().insertContent(new Date().toISOString()).run()
      },
    },
  ],
})

export function PluginEditor() {
  return (
    <AlggttaEditor
      token={import.meta.env.VITE_ALGGTTA_TOKEN}
      extensionConfig={{ plugins: [timestampPlugin] }}
    />
  )
}

The plugin set is fixed when the editor is created; remount to change the schema. External plugin packages must keep React and all Tiptap/ProseMirror packages as peers/externals, must not import @tiptap/starter-kit at runtime, and must publish their CSS separately. Install trusted plugins only: they run with the host page's full JavaScript permissions. Declarative Web Component attributes and the hosted CDN/UMD build do not load arbitrary plugin packages.

See the public Plugin Development guide for a minimal plugin definition and registration example, browser-side static rendering, document compatibility, packaging, and security guidance.

Embed Frame Security

With preset="full", the embed block shows YouTube, Figma, CodePen and X/Twitter content in sandboxed iframes. Frame URLs are rebuilt from recognised provider links on fixed frame hosts, but that covers only the URL a frame starts on, and YouTube and Figma frames get allow-same-origin next to allow-scripts. Before you let embeds load, the host application must:

  • serve the editor page with Cross-Origin-Opener-Policy: same-origin;
  • send Content-Security-Policy: frame-ancestors 'none' (or X-Frame-Options: DENY) on every response that can carry user content;
  • never serve user-supplied HTML or SVG inline from the editor page's origin;
  • list the frame hosts it wants in CSP frame-src (https://www.youtube.com, https://www.figma.com, https://codepen.io, https://twitframe.com); default-src 'self' alone blocks every embed.

YouTube frames carry referrerpolicy="strict-origin-when-cross-origin", because YouTube refuses to play without a Referer; this overrides a page Referrer-Policy: no-referrer for those frames, so www.youtube.com learns the page origin (not its path or query). Because of allow-same-origin, the frames' allow list (accelerometer, autoplay, clipboard-write, encrypted-media, gyroscope, picture-in-picture) now takes effect for YouTube and Figma; cap it with your own Permissions-Policy header if needed (a feature whose allowlist omits a provider's origin, such as gyroscope=(), is not delegated to it). A framed document can navigate itself, or open a popup that inherits its sandbox, to any URL; the first three requirements keep such a page on your origin from reaching the editor page.

Tailwind and DaisyUI

The package ships compiled CSS at @alggtta/editor/styles.css. DaisyUI is internally prefixed with ue- to reduce conflicts with consuming apps. You can use the editor in apps with or without Tailwind/DaisyUI.

Migration from v2

onUpdate

// v2
<UniversalEditor onUpdate={(editor) => save(editor.getJSON())} />

// v3
<AlggttaEditor onUpdate={({ json }) => save(json)} />

UniversalEditor

AlggttaEditor is the recommended component (since v3). The previous UniversalEditor remains available from the legacy entry during migration:

import { UniversalEditor } from '@alggtta/editor/legacy'

Optional Features

Database, Mermaid, AI, export helpers, GuideFlow blocks, and schedule blocks are no longer part of the default core entry. Import the matching subpath and pass its extensions/commands explicitly.

Optional citation document schema

Import createCitationPlugin from @alggtta/editor/citations and add it to extensionConfig.plugins before loading documents with citation nodes. It is not installed by core or full presets. insertCitation({ references, displayText, styleId?, locale? }), updateCitation(instanceId, content) and deleteCitation(instanceId) use normal editor transactions and undo/redo. Each ordered reference carries libraryId, itemId and optional locator, label, prefix, suffix, suppressAuthor. A node-local schemaVersion: 1 and generated instanceId survive JSON/HTML reopen; clipboard paste creates new occurrence IDs. Raw duplicate-ID imports/transactions are rejected.

parseCitationData validates imported payloads. Validate stored content before initial mounting and use setContent(json, { errorOnInvalidContent: true }) for JSON replacement so an invalid import cannot clear an existing document. Citation node commands store literal display text; they do not fetch a library or format citations automatically. The same optional /citations entry exports renderCslDocument for offline CSL citation and bibliography rendering from host-supplied styles, locales and items. It does not connect Word/Docs. Invalid citation HTML falls back to visible plain text. Hosts must preserve the original document and block downgrade saves when the citation plugin or its node schema is unsupported. Complete that compatibility check before consuming a published exact editor version in Atlas. Document schema v3 includes citations with node-local payload v1; a registered v2-to-v3 migration preserves existing content. The root editor shows a read-only text preview and blocks autosave, host updates and localStorage writes when citation JSON cannot be safely loaded. Standalone Tiptap hosts need equivalent guards. Backend must advertise the paired document schema; the existing Atlas npm pin is unchanged.

License

Proprietary — Copyright © 2026 Alggtta. All rights reserved. Use is governed by the Alggtta Commercial License Agreement (EULA) and a valid, domain-bound license token issued by Alggtta. See LICENSE.

Versions up to and including 5.1.1 were published under the MIT License and those already-distributed copies remain MIT; this proprietary license applies to 6.0.0 and later.

Independent feature plugins

Install only the features you need through extensionConfig.plugins: createSchedulePlugin from @alggtta/editor/schedule, createGuideFlowPlugin from @alggtta/editor/guideflow, createMathPlugin from @alggtta/editor/math, and the createDatabasePlugin, createAIPlugin, createMermaidPlugin, createCommentsPlugin factories in their existing subpaths. Use the core preset when composing all of them. Schedule retains the existing guideflow license entitlement. createGuideFlowPlugin() installs the map, hotel, contact, divider and staff blocks, so together with createSchedulePlugin() it registers exactly the nodes of the legacy guideFlowExtensions bundle: moving from customExtensions: guideFlowExtensions needs no schema or storage migration. Install both when opening existing GuideFlow documents. Legacy exports are unchanged.

Plugins can supply React ui.toolbar, ui.sidebar, ui.footer and ui.overlay components, mounted and licensed with their extensions. Each contribution renders inside its own error boundary: a component that throws, or a React.lazy chunk that fails to load, is hidden and logged without unmounting the editor. Overlays render inside the themed editor root, and without UI contributions AlggttaEditor adds no slot wrapper or layout element around the editor surface and body. Headless hosts can use PluginSlot and useAlggttaEditor().plugins. See Plugin Development for migration and UI examples.

Offline CSL core (optional /citations entry)

mapResearchItemToCsl(id, metadata) returns { item, source, losses }; the original own JSON metadata remains available when a field or creator role has no faithful CSL representation. The offline compatibility catalog covers 37 research item types.

renderCslDocument({ style, locales, locale, items, citations, format }) creates a fresh document-scoped citeproc engine and returns every contextual citation plus bibliography entries. Supply independent CSL XML and offline locale XML (requested locale and en-US); there is no network lookup or fallback formatter. Citation clusters have stable id, items, and explicit positive ordered noteIndex for note styles; item options include locator, label, prefix/suffix and suppressAuthor. format is text (default) or restricted safe html.

The core is capped at 200 items/clusters, 100 references per cluster, 256000 characters per XML file, bounded XML nesting/macros and bounded own JSON input. Invalid/unsupported inputs raise CslError with a machine-readable code. XML checking enforces structural/security constraints and is not full CSL Relax NG schema validation. Dependent styles must first be resolved by the host to their independent parent. The synchronous processor has structural work bounds; hosts needing a hard wall-clock deadline should run it in their own worker.

This module does not change persisted citation nodes, embed fallback snapshots, install styles, or connect library APIs. Those are separate integration tasks. Consumers must preserve third-party notices and expose CSL_COMPONENT_NOTICE attribution in their citation UI. Style/locale fixture authors and provenance remain in the source fixtures.

refreshEditorCitations(editor, { style, locales, locale, items, noteIndices }) resolves CSL output for every existing occurrence in document order and replaces its display text in one document transaction. Supply authorized, current metadata as { libraryId, itemId, metadata } rows. The refresh retains occurrence identities, reference order, locators, prefixes, suffixes and author suppression. Missing references, invalid metadata/style or oversized display text abort the whole refresh. Actual footnote numbers are required in noteIndices for note styles. The refresh has its own undo event, separate from adjacent typing. It returns bibliography entries and mapping losses for host review; it neither inserts a bibliography node nor persists source metadata. No persisted schema changes or Atlas sibling-package links are introduced.

Optional research source references

Import createResearchNotesPlugin from @alggtta/editor/research-notes and add it to extensionConfig.plugins before loading researchReference nodes. This optional entry stores note or annotation source identities separately from citations, with an independent quote snapshot, visible label and optional human location. It performs no lookup, navigation, rendering of source URLs, or automatic refresh from later source edits. researchReferenceDocumentId projects only a note's explicit existing Document identity for the host's wiki flow; it never guesses a document from an item or annotation.

Insert/update/delete commands retain occurrence identity on updates and use normal undo/redo. Clipboard paste creates fresh occurrence identities. Strict node-local v1 parsing rejects unsupported fields/versions; HTML renders the label as escaped text. Hosts resolve library/source identities through their approved scoped APIs. The quote is a copy, not a live annotation editor. Visible label extraction agrees with the Backend document text renderer.

Global document schema v4 includes this optional vocabulary and preserves the reviewed v3 snapshot. The v3-to-v4 migration changes only the root version stamp. Unsupported, malformed or duplicate reference documents open as display-only previews, with host updates, autosave and localStorage writes blocked; the original JSON must be retained. Commands reject read-only/destroyed editors. Coordinate the Backend v4 contract and exact published npm consumer before enabling persistence. Notes APIs, readers, live annotation navigation and Atlas adoption are separate integrations.

Static bibliography and quick-copy output

renderCslQuickCopy is available from the existing optional @alggtta/editor/citations entry. Supply offline style/locales, mapped CSL items, 1–100 explicit selected item IDs in selection, and duplicates: 'first' | 'reject'. First-position deduplication reports duplicateIds; duplicate source item IDs always fail. Supply mode: 'bibliography' with bibliographyOrder: 'style' | 'selection', or mode: 'citation'. Selection ordering preserves grouped CSL entries, placing a group at its first selected member. Numeric labels are assigned by selected citation order; choosing style order does not renumber those labels.

The result contains paired plain text and restricted html, resolved selectedIds, optional bibliography entry identities, and static: true. Only selected items enter a standalone output. Styles without a bibliography fail rather than receive invented formatting. For a contextual citation, pass context: { citations, citationId } containing the whole ordered document, including the target cluster; its ordered item IDs must equal the deduplicated selection. Whole-context rendering preserves disambiguation, locator options and actual note indices. Context affects the chosen citation, but unselected bibliography output is never returned.

This is a snapshot API: it does not bind or edit a document, use the clipboard, write a file, fetch metadata/styles, or perform host navigation. Hosts explicitly regenerate after edits, choose clipboard/download behavior, and sanitize HTML at their established rendering boundary. It inherits the offline core's 200-item/document-cluster, XML and processor-work limits and absence of hard wall-clock isolation; assembled text plus HTML is limited to 2 MiB. RTF, linked bibliography editing, uncited/excluded document overrides and export-format Quick Copy remain separate host features. No new package entry or persisted node schema is introduced.

Offline citation style management

The optional @alggtta/editor/citations entry exports CslStyleManager and immutable style-library helpers. Start with emptyCslStyleLibrary(), install local .csl styles with installCslStyle, and install locale XML with installCslLocale. serializeCslStyleLibrary and readCslStyleLibrary round-trip the bounded profile without changing the original XML. Unknown/corrupt profiles must be retained and shown read-only; never replace them with an empty library automatically.

CslStyleManager is controlled by value and onChange(next, expectedRevision). The host must persist with an atomic revision comparison and keep the component keyed to its account/storage scope. Use disabled when editing permission is withdrawn. onApply receives the resolved independent style and offline locales; the host applies them through its existing document revision/permission checks and refreshEditorCitations. Installed parents cannot be removed while a dependent style uses them. A dependent style's default locale applies unless the user explicitly chooses another installed locale. en-US and the requested locale must be supplied explicitly.

Limits: 50 styles, 20 locales, 256000 characters per XML file, 1 MiB file input (fatal UTF-8 or BOM-marked UTF-16 decoding), and 8 MiB serialized library. XML safety/structure checks are not full CSL Relax NG conformance. This component searches installed styles and reads local files; online acquisition, cross-device storage, and Atlas integration through a reviewed exact npm version remain host work. It does not publish a package, fetch style/parent/locale URLs, or directly save document content.

Static RTF citation scan (CITE-07)

@alggtta/editor/citations exports scanRtfCitations, formatRtfCitations, and the optional offline RtfScanPanel. Supply up to 200 authorized CSL items and an installed style/locale snapshot. Scan results are process-bound immutable snapshots; every occurrence requires explicit ordered item IDs before formatting. Candidate matching supports {Author, YYYY}, and/&, et al., grouped references separated by semicolons, optional p./pp. locators, and n.d.. Ambiguous and unmatched references require a user choice; the formatter never silently chooses a candidate.

The input is a bounded 2 MiB RTF 1 byte-string, with Windows-1252 literals/hex escapes and signed Unicode escapes (including surrogate pairs); it is not a UTF-8 decoded file. Unsupported code pages, binary payloads, existing fields/objects/footnotes, malformed groups and oversized text are rejected. Header/footer, pictures, hidden text and ignorable destinations are preserved without scanning. Source character ranges are replaced without removing intervening formatting groups; other input bytes remain unchanged. The real CSL engine supplies restricted RTF formatting and static footnotes for note styles. {Bibliography} chooses the bibliography position; otherwise it is appended. Output is bounded to 4 MiB.

RtfScanPanel reads files locally and downloads a newly named Blob; it does not upload, overwrite the input, publish, or create live citation fields. Hosts must pass a new item/style snapshot when account/library authorization changes and set disabled on permission loss. Target/permission changes invalidate pending reads and revoke completed output URLs. Hosts implementing native file writes must separately enforce distinct input/output paths. Full RTF dialect support, live Word/LibreOffice fields, and actual Word/LibreOffice rendering are not claimed by these helpers.

Optional document DOI notice warnings

@alggtta/editor/citations exports createCitationWarningGate, CitationWarningPanel and inspectExistingCitationWarnings. The host supplies already-authorized current projections from Backend deea9b9fdbb641b4bfa22edf7ba044b7cb6ed5d0, with matching library/item/source revisions and a logical document ID/binding. This module never fetches providers, credentials or files. Bind a separate gate to each document and replace its state whenever the document session, source or notice binding changes; unknown/future/malformed projections block visibly. Do not reuse a gate across document identities. On authorization withdrawal, replace state with null to fail closed and invalidate approvals, or unmount the document.

Configure CitationExtension.configure({warningGate: gate, onWarning: decision => ...}) or createCitationPlugin({warningGate: gate, onWarning}). Known retraction/concern evidence, including stale/offline evidence, blocks silent insertCitation and updateCitation. Host UI explicitly calls gate.resolve(decision, 'proceed' | 'skip', 'each-time' | 'document'); then a separate user action may retry the current command. The UI does not automatically replay commands. One-shot authorization requires the same immutable document object and exact parsed proposal; dry-run .can() neither consumes approval nor emits decisions. A document-wide repeat choice takes effect only on a matching actual command and is bound to the same cloned evidence snapshot. Replacing state invalidates all decisions.

On reopening, explicitly call inspectExistingCitationWarnings(editor, gate) and display its bounded results, even for previously acknowledged warnings. Absence of notices is incomplete information, never a healthy verdict. CitationWarningPanel is a controlled, presentational component; the host owns acknowledgement state and current authorized document/source lifecycle. Generic JSON/HTML imports, clipboard paste, CSL batch refresh and external Office/Docs adapters are not protected by the insert/update command gate; hosts must inspect and enforce their own admission boundaries. Existing citation schema v1, occurrence identity, HTML and undo behavior are unchanged. No warning data or acknowledgement is persisted into citation nodes. Exact npm publication/adoption, Atlas host mounting and full original CITE-05 acceptance remain separate.

PDF annotation domain proposals (READ-02 foundation)

The optional @alggtta/editor/citations entry exports parsePdfAnnotationRecord, preparePdfAnnotationCreate, preparePdfAnnotationUpdate, and pdfAnnotationToResearchReference. These pure helpers accept own-data records; there are no PDF, filesystem, network, clipboard or editor write side effects. This profile supports highlight, underline and position-note proposals with a page from 1 to 500 and at most 256 positive normalized rectangles. Coordinates use the top-left origin on an unrotated page; the reader host owns transforms. Text is preserved verbatim (including Unicode order), bounded to 10,000 UTF-16 units for each quote/comment; tags are bounded to 50 and color is a six-digit hex value. Inputs reject unsupported future profiles, accessors, aliases, sparse arrays and invalid Unicode, with a 256KiB own-data string/key budget.

import {
  preparePdfAnnotationCreate, preparePdfAnnotationUpdate,
  pdfAnnotationToResearchReference,
} from '@alggtta/editor/citations'

const source = {
  libraryId, attachmentId, attachmentRevision, attachmentSha256,
  itemId: null, itemRevision: null, // or both confirmed parent values
}
const proposal = preparePdfAnnotationCreate(annotationId, source, {
  type: 'highlight', page: 1,
  rects: [{ x: 0.1, y: 0.2, width: 0.5, height: 0.03 }],
  quoteText: 'Selected text', comment: '', color: '#ffff00', tags: [],
})
// Explicitly authorize and save proposal in the host; only use its confirmed record.
const update = preparePdfAnnotationUpdate(confirmedRecord, source, confirmedRecord.revision, {
  comment: 'Reviewed comment',
})
const node = pdfAnnotationToResearchReference(confirmedRecord, source, occurrenceId, 'Author · p. 1')
// Insert node via the existing research-notes plugin after the host's explicit action.

Create uses expected revision 0; update requires the captured annotation revision and cannot change source identities, parent, attachment revision or SHA256. These are proposals, not saved records, authorization or durable deduplication. The host must verify the real private attachment/hash and current permissions, perform atomic revision CAS, and return a confirmed record. An old source binding fails rather than relocating a quote automatically.

Copy returns the existing researchReference node shape, retaining a separate quote snapshot and a caller-supplied occurrence ID. It adds no document schema. Geometry, attachment hash/revision and comment remain in the host's annotation record; the existing document source stores annotation ID/revision and optional parent item ID. Host navigation must resolve and verify that source record. An unsupported record must remain intact in host storage for repair, not be silently migrated. This source change requires an exact published npm version before Atlas consumption; a sibling source link is not a release boundary. Reader persistence, reopen/edit/permission tests, embedded PDF import/export, EPUB annotations, image/text/ink annotations, native clients and Reading Mode cache/position linkage are separate pending work.

版本与发布记录

@alggtta/editor 共发布 28 个版本,首次发布于 2026/03/02;过去 12 个月发布了 28 个版本。

版本发布日期距今
7.0.6latest2026/11/061 天前
7.0.52026/10/1920 天前
7.0.42026/10/1821 天前
7.0.32026/10/1821 天前
7.0.22026/10/1721 天前
7.0.12026/10/1721 天前

关于 @alggtta/editor 的常见问题

@alggtta/editor 是什么?

Alggtta Editor with Tiptap 3.x and DaisyUI 5

如何安装 @alggtta/editor?

在项目目录运行 npm install @alggtta/editor,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:yjs(^13.6.31)、react(^18.2.0 || ^19.0.0)、react-dom(^18.2.0 || ^19.0.0)、@tiptap/pm(3.31.3)、y-protocols(^1.0.6)、@tiptap/core(3.31.3)、@tiptap/react(3.31.3)、@tiptap/extension-bubble-menu(3.31.3)、@tiptap/extension-floating-menu(3.31.3)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@alggtta/editor 支持 TypeScript 吗?

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

@alggtta/editor 支持 ESM 还是 CommonJS?

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

@alggtta/editor 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@alggtta/editor 可以商用吗?

@alggtta/editor 使用 UNLICENSED 许可。作者声明为 UNLICENSED,即不授予公开使用许可(与公共领域的 Unlicense 不同)。使用前需获得作者授权。以上为概括,不构成法律意见,请以许可原文为准。

@alggtta/editor 的下载量有多少?

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

@alggtta/editor 还在维护吗?

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