@clevertask/scribe

中文文档
0.1.37发布于 2026/11/09MITTypeScriptES Module构建来源已验证近 30 天下载 2158

A Radix-based Tiptap rich text editor with a Notion-style block interface for viewing and creating content. Perfect for diverse needs like notes, documents, AI chat, Markdown parsing, and comments.

原始使用文档
文档目录27 节
  1. Features
  2. Table of Contents
  3. Installation
  4. Headless schema
  5. Headless table transforms
  6. Usage
  7. Basic usage
  8. With ref
  9. Using Your App Theme
  10. Table Authoring
  11. Checkboxes while reading
  12. Links
  13. Legacy preview compatibility
  14. Experimental Table of Contents
  15. Math Expressions
  16. Typing Delimiters (Input Rules)
  17. Markdown Delimiters
  18. External Undo and Redo Ownership
  19. Props
  20. Helper Functions
  21. md2html
  22. html2md
  23. convertLegacyMathDelimiters
  24. Roadmap
  25. Release Process
  26. License
  27. Credits

@clevertask/scribe

A versatile, block-based rich text editor for diverse applications, built with Tiptap and inspired by Notion's intuitive interface. @clevertask/scribe allows you to seamlessly view, create, and edit rich text content, with added support for Markdown parsing.

[!WARNING] Scribe is experimental and has not reached version 1.0. Its APIs, default extensions, editor behavior, and built-in UI can change between releases, including minor releases. Pin an exact package version and test each upgrade before deployment. Forks and contributions are welcome.

Features

  • Block-based Editing: Enjoy a familiar and intuitive Notion-style editing experience.
  • Markdown Support: Parse and render Markdown content effortlessly.
  • Markdown Paste: Paste plain-text markdown into the editor and have it converted into rich content automatically.
  • Table Authoring: Insert, resize, and edit tables with controls that stay next to the active table.
  • Ordinary Links: Edit display text and URL with an accessible popover and native Tiptap paste behavior.
  • Versatile Integration: Easily integrate @clevertask/scribe into any project requiring rich text editing.
  • View and Edit: Seamlessly switch between viewing and editing modes.
  • Experimental Table of Contents: Subscribe to heading changes and render an app-owned table of contents outside the editor.

Table of Contents

Installation

npm install --save-exact @clevertask/scribe @tiptap/[email protected]

Scribe shares this exact ProseMirror runtime with consumer extensions. Keeping one installed version prevents identity conflicts between built-in and custom plugins.

Headless schema

Use the schema subpath when a server or migration tool must parse Scribe content without loading the React editor or Scribe's stylesheet:

npm install --save-exact @clevertask/scribe @tiptap/[email protected] @tiptap/[email protected] @tiptap/[email protected]
import { createScribeSchemaExtensions } from "@clevertask/scribe/schema";
import { generateJSON } from "@tiptap/html";

const extensions = createScribeSchemaExtensions({ enableUndoRedo: false });
const document = generateJSON(storedHtml, extensions);

The returned extensions define Scribe's persistent nodes and marks without loading Scribe's React editor UI. Use them with Tiptap's server-side HTML utilities; this is not a headless interactive editor. Scribe's menus, placeholders, suggestions, and table-of-contents behavior are not part of this entry point. Consumer-owned nodes must be appended to the list. For example, an application that stores its own resource-reference node must provide that extension before parsing or rendering content containing those references.

Pin Scribe and Tiptap to the exact versions used by the writing clients. This entry point makes the schema reusable; it does not make different schema versions interchangeable.

Scribe preserves inline code together with other text marks, such as bold and links. This applies only to inline code; code-block text remains unmarked.

Servers can also inspect Scribe's document-node capabilities through the same headless entry:

import {
  createScribeDocumentNodeCapabilityManifest,
  createScribeSchemaExtensions,
} from "@clevertask/scribe/schema";
import { getSchema } from "@tiptap/core";

const extensions = createScribeSchemaExtensions({ enableUndoRedo: false });
const capabilities = createScribeDocumentNodeCapabilityManifest(getSchema(extensions));

capabilities.paragraph.potentialOperations; // [{ type: "replace_content" }]
capabilities.callout.potentialOperations; // [{ type: "set_attributes", attributes: ["variant"] }]
capabilities.taskItem.potentialOperations; // [{ type: "set_attributes", attributes: ["checked"] }]
capabilities.tableCell.potentialOperations; // []

The manifest intentionally keeps structural list changes application-owned. A consumer can use the task-item attribute capability while defining its own bounded policy for adding or moving task-list items.

These are structural possibilities, not application permissions. An application must still check authorization, the current node and its ancestors, protected descendants, revision visibility, parser support, and size limits before it offers or performs a write.

If an application appends a consumer-owned node to the Scribe schema, it must also pass an exact capability declaration to createScribeDocumentNodeCapabilityManifest. Use defineScribeDocumentNodeCapability to type that declaration. Manifest creation fails when a node is missing, duplicated, or no longer matches the schema. A custom node can declare no potential operations and remain explicitly read-only.

Headless table transforms

The same headless entry can insert or delete an exact logical table row or column and can merge or split cells without mounting an editor or accessing the DOM:

import { applyScribeTableTransform, createScribeSchemaExtensions } from "@clevertask/scribe/schema";
import { getSchema } from "@tiptap/core";

const schema = getSchema(createScribeSchemaExtensions({ enableUndoRedo: false }));
const document = schema.nodeFromJSON(storedDocument);
const tablePosition = 42; // ProseMirror position immediately before the table node.
const result = applyScribeTableTransform(document, tablePosition, {
  type: "insert_row",
  index: 1,
});

persist(result.document);

Rows and columns use zero-based logical coordinates from ProseMirror's table grid, not physical child indexes. An insertion index may equal the current row or column count; deletion indexes must name an existing logical row or column. Merge rectangles use half-open bounds: topRow and leftColumn are included, while bottomRowExclusive and rightColumnExclusive are excluded. A split's row and column must identify the merged cell's top-left logical coordinate. Existing rowspans and colspans can therefore affect more than one physical cell even though the requested logical boundary remains exact. The returned before and after geometry reports logical rows, logical columns, and physical cell count.

applyScribeTableTransform is an immutable, exact-snapshot operation: it returns a new ProseMirror document and leaves its input unchanged. It does not rebase coordinates or detect that another writer changed the stored document after the input snapshot was read. On a concurrent change, discard the candidate, read the latest document, resolve the intended table and coordinates again, and rerun the transform. Invalid coordinates, malformed or nested tables, and operations that do not apply throw ScribeTableTransformError.

This API owns only schema-valid table geometry. The host application owns authorization, protected node rules, revision and conflict checks, destructive previews, payload limits, Yjs conversion and persistence, and durable readback. In CleverTask, those checks belong to the collaboration and API layers rather than Scribe.

Usage

Basic usage

import "@radix-ui/themes/styles.css";
import "@clevertask/scribe/styles.css";
import { Theme } from "@radix-ui/themes";
import { Scribe, ScribeRef } from "@clevertask/scribe";

function App() {
  const onContentChange = useCallback(
    ({ markdownContent, htmlContent, jsonContent }: ScribeOnChangeContents) => {
      console.log(markdownContent, htmlContent, jsonContent);
    },
    [],
  );

  return (
    <Theme>
      <Scribe onContentChange={onContentChange} />
    </Theme>
  );
}

With ref

import "@radix-ui/themes/styles.css";
import "@clevertask/scribe/styles.css";
import { Theme } from "@radix-ui/themes";
import { Scribe, ScribeOnChangeContents } from "@clevertask/scribe";

function App() {
  const editor = useRef<ScribeRef>(null);

  const resetContent = useCallback(() => {
    editor.current.resetContent();
  }, []);

  return (
    <>
      <Theme>
        <Scribe ref={editor} />
      </Theme>
      <button onClick={resetContent}>Reset content</button>
    </>
  );
}

Using Your App Theme

import "@radix-ui/themes/styles.css";
import "@clevertask/scribe/styles.css";
import { Theme } from "@radix-ui/themes";
import { Scribe } from "@clevertask/scribe";

function App() {
  return (
    <Theme appearance="dark">
      <Scribe />
    </Theme>
  );
}

Table Authoring

Type /table to insert a 3 × 3 table with a header row. Selecting a table cell opens nearby controls for adding or deleting rows and columns, toggling the header row, and deleting the table. Drag a column boundary to resize it.

The Table layout menu stores each table's explicit layout choice. Auto keeps the existing sizing behavior. Fit to width wraps the table into the available editor width and temporarily disables column resizing. Scroll horizontally retains authored column widths, gives columns without a saved width a readable 128px minimum, and scrolls inside the editor when the table needs more room. Switching layouts retains the table's saved column widths.

Limit height caps the table and enables vertical scrolling; shorter tables stay compact. In Table layout → Maximum height…, enter a whole number from 120 to 2000 pixels and choose Apply to set that table's maximum. Use default clears its override and uses the host's --scribe-table-max-height, with a 360px fallback. Turning Limit height off retains the saved maximum for when it is enabled again. Changing the maximum leaves the limit toggle, width layout, and sticky header preference unchanged.

Layout and height choices apply in both editing and read-only views, and remain intact when content is saved and loaded as JSON, HTML, or Markdown. Hosts can use editor.commands.setTableLayout("scroll") (accepts "auto", "fit", or "scroll"), editor.commands.setTableHeightLimit(true) (accepts a boolean), and editor.commands.setTableMaxHeight(600) (accepts a whole number from 120 to 2000, or null to restore the default) while the selection is inside an editable table. The optional maxHeight table attribute is serialized as data-table-max-height in HTML; existing tables without it keep the host default.

Sticky header row keeps the first row visible while scrolling, independently of Limit height. New tables created with /table or insertTable start with it enabled when their entire first row contains header cells. Existing and imported tables retain their saved setting; tables without a saved sticky preference stay off. Toggle it in Table layout. With Limit height on, the header stays at the top of the table's own scroll area. With it off, the header follows the document's vertical scroll area until the table ends. This also works with horizontal scrolling and retains saved column widths. The preference stays saved if the header row is removed and becomes active again when it is restored. It works in editing and read-only views. Hosts can use editor.commands.setTableStickyHeaderRow(true) to enable it or false to clear it while the selection is inside an editable table. For document scrolling, set --scribe-table-sticky-header-offset on the editor's container to leave room above the header for a document toolbar; the default is 0px.

Keyboard users can press Alt + F10 while editing a table to focus its controls, use the arrow, Home, and End keys to move between actions, and press Escape to return to the active cell.

Simple headed tables without layout, height, or sticky-header preferences serialize as GFM Markdown. Tables with those preferences (including a saved maximum while Limit height is off or an inactive sticky preference), merged cells, multiple blocks in a cell, resized columns, or other structures that GFM cannot represent are kept as sanitized raw HTML inside the Markdown output so their structure is not silently lost.

Scribe's insertTable command and /table action do not create a table while the selection is already inside another table. Existing documents containing nested tables remain loadable so old content is not destroyed. This guard covers Scribe-owned authoring paths only: arbitrary consumer calls to insertContent, Markdown table paste, and rich-HTML table paste remain trusted integration points. Applications that require a hard no-nested-table guarantee must validate or filter those paths too.

Checkboxes while reading

Text remains read-only when editable={false}. To let a user check task-list items on content they are allowed to update, explicitly provide readOnlyTaskItemChecks:

<Scribe
  content={canonicalHtml}
  editable={false}
  showBarMenu={false}
  readOnlyTaskItemChecks={
    canUpdateContent ? { disabled: isSaving, onChange: handleCheckboxChange } : undefined
  }
/>

The exported ScribeReadOnlyTaskItemCheck event contains checkboxIndex (zero-based task-item order, including nested items), previousChecked, and checked. Scribe applies only that checkbox attribute locally before invoking the callback, without focusing the text editor. The callback is synchronous; the application owns permission checks, asynchronous persistence, pending state, error messages, and restoring or reconciling authoritative content after a failure. Use a synchronous in-flight guard and disabled while saving. Supplying no capability disables read-only checkboxes; disabling or removing it takes effect without recreating the editor.

The index describes the document before the action, not a permanent item ID. Persist against the application's original canonical content/revision so concurrent changes cannot target a different item. Do not substitute editor.getHTML() for exact stored HTML: serialization can normalize it. Only the dedicated callback identifies this checkbox action. Ordinary onContentChange updates remain programmatic in read mode, including programmatic recovery and checkbox changes.

This option applies to Scribe-owned editors. It does not replace a caller-owned externalEditor's node views, enable arbitrary read-only writes, or change the headless schema or editable-mode checkbox behavior.

Scribe uses Tiptap's Link extension for ordinary links and URL paste. Clicking an existing link opens its popover with focus on the dialog, keeping the software keyboard closed. Tap either field to start typing, then choose Apply or Remove link. The popover follows the visible viewport when the keyboard opens, scrolling its form when space is limited. Open link ↗ opens the address currently shown in the URL field in a new tab without applying changes to the document. The action is disabled for empty or invalid addresses. The toolbar Link action still focuses the URL immediately. An unchanged label preserves the link's inline formatting. Keyboard users can press Alt + F10 from a link to open the form and Escape to return to the document.

Pasting recognizable URL-only content over selected text applies a link using native Tiptap behavior; ordinary text replaces the selection. Rich clipboard content follows Tiptap's parsed content handling. Scribe does not fetch metadata or create Compact/Preview card presentations. Automatic links and URL paste use Tiptap's native URL/protocol recognition and safety checks, including email and FTP links. The manual URL form accepts HTTP(S), bare domains and root-relative links. Application-owned task and document reference extensions remain separate: URL paste links eligible text around selected inline atoms without marking the atoms themselves, while an atom-only selection follows normal paste replacement.

Legacy preview compatibility

Saved externalLinkPreview nodes still load in the interactive and headless schemas and render as ordinary links using their authored linkText and exact href. Their node identity, display value and metadata remain serialized in HTML, JSON and Markdown. Reading a document does not convert these nodes or discard metadata. No stored favicon or preview image is requested.

The exported ExternalLinkPreview extension, constants, types and externalLinkPreview options remain for compatibility. autoPreviewOnPaste, resolve and shouldPreview are deprecated and ignored; HTMLAttributes still applies to the legacy wrapper. A caller-owned externalEditor must register the compatibility extension to read existing preview nodes.

The deprecated insertExternalLinkPreview and updateExternalLinkPreview commands retain the legacy node shape and attributes, displayed as ordinary links, without fetching metadata. setExternalLinkDisplay("compact" | "card") and refreshExternalLinkPreview return false. setExternalLinkDisplay("plain") and convertExternalLinkPreviewToPlain remain explicit, undoable conversions to ordinary Link-marked text. There is no automatic content migration.

Experimental Table of Contents

Scribe can expose table-of-contents data without rendering a table-of-contents block inside the editable document. Enable the experimental API with enableTableOfContents, keep the latest items in your app state, and call scrollToTableOfContentsItem when a user selects an entry.

import { Scribe, ScribeRef, ScribeTableOfContentsItem } from "@clevertask/scribe";
import { useRef, useState } from "react";

function DocumentEditor() {
  const scribe = useRef<ScribeRef>(null);
  const [tableOfContentsItems, setTableOfContentsItems] = useState<ScribeTableOfContentsItem[]>([]);

  return (
    <>
      <Scribe
        ref={scribe}
        enableTableOfContents
        onTableOfContentsChange={setTableOfContentsItems}
      />

      {tableOfContentsItems.length > 0 ? (
        <nav aria-label="Table of contents">
          {tableOfContentsItems.map((item) => (
            <button
              key={item.id}
              type="button"
              onClick={() => scribe.current?.scrollToTableOfContentsItem(item)}
            >
              {item.textContent}
            </button>
          ))}
        </nav>
      ) : null}
    </>
  );
}

The table of contents currently tracks default TipTap heading nodes only. Each item includes the heading text, depth, document position, DOM node, and active/scrolled state. This API is marked experimental while we validate the contract in real document surfaces.

Math Expressions

Scribe's default UI is styled with Radix Themes components. Load @radix-ui/themes/styles.css once in your app alongside @clevertask/scribe/styles.css, and render Scribe somewhere inside a Radix <Theme>.

Scribe ships with @tiptap/extension-mathematics. The extension renders math when it receives math nodes in the HTML:

<span data-type="inline-math" data-latex="\alpha"></span>
<div data-type="block-math" data-latex="\sum_{i=1}^{n} x_i"></div>

Typing Delimiters (Input Rules)

When typing directly in the editor, the built-in input rules use:

Inline: $$\alpha$$
Block: $$$\sum_{i=1}^{n} x_i$$$

Markdown Delimiters

If you are parsing markdown with the Tiptap Markdown extension (not md2html), the tokenizer expects:

Inline: $\alpha$
Block: $$\sum_{i=1}^{n} x_i$$

If your content arrives as HTML (for example from a server), use the helper below to convert legacy delimiters into the HTML nodes that the math extension understands.

External Undo and Redo Ownership

Scribe enables its built-in undo and redo history by default. Set enableUndoRedo={false} when a different extension owns history, such as Tiptap Collaboration:

<Scribe enableUndoRedo={false} extensions={[Collaboration.configure({ document: ydoc })]} />

Scribe reads this option when it creates the editor. Remount Scribe to change it. If you pass an externalEditor, configure history on that editor instead. Do not add another ordinary history extension through extensions when collaboration owns undo and redo.

The createScribeEditor helper accepts the same enableUndoRedo option.

Props

PropTypeDefaultDescription
contentstringundefinedThe initial content of the editor. Controlled updates through this prop are deprecated; prefer ScribeRef.setContent for programmatic updates after mount.
ariaLabelstringRich text editorSets the accessible name for the rich-text editing surface. Use a unique label when a page contains more than one editor.
onContentChange(content: ScribeOnChangeContents) => void;undefinedA callback function triggered whenever the editor's content changes. It receives an object containing the current content in various formats (jsonContent, htmlContent, markdownContent). Internal table-of-contents metadata updates are ignored.
editablebooleantrueControls whether the editor is editable.
readOnlyTaskItemChecksScribeReadOnlyTaskItemChecksundefinedOpts a Scribe-owned read-only editor into checkbox-only actions. See Checkboxes while reading for persistence and pending-state responsibilities.
autoFocusbooleanfalseControls whether the editor should automatically focus when mounted.
extensionsExtension[]undefinedYou can set your own extensions for the text editor. For more information, check the tip tap extensions docs
externalEditorEditorundefinedUses a caller-owned Tiptap editor. The caller remains responsible for its extension and plugin lifecycle, including table resizing, and for destroying it.
enableUndoRedobooleantrueEnables Scribe's built-in undo and redo history. Set it to false when another extension owns history. This creation-time option does not configure a caller-owned externalEditor.
externalLinkPreviewPartial<ExternalLinkPreviewOptions>undefinedDeprecated compatibility options. Only HTMLAttributes affects legacy wrappers; metadata resolution, preview policy and automatic previews are ignored.
editorPropsEditorPropsundefinedA tiptap-based prop to handle advanced use cases, you can read about it on their documentation
showBarMenubooleantrueDetermines whether to show the text editor top menu bar or not. This menu bar shows options to format the text
placeholderTextstringType "/" for commands...Change the initial placeholder for your text editor
editorContentStyleReact.CSSPropertiesundefinedYou can send a CSS object to add styles to the editor content container. Useful if you want to limit the editor's height.
editorContentClassNamestringundefinedThe same idea of editorContentStyle but with classes.
mainContainerStyleReact.CSSPropertiesundefinedYou can send a CSS object to style the main editor container
mainContainerClassNamestringundefinedThe same idea of mainContainerStyle but with classes.
onKeyDownKeyboardEventHandlerundefinedA callback function that is triggered when a key is pressed within the editor. This allows you to handle custom keyboard shortcuts. For example, you can use this prop to implement a "send message" functionality when Ctrl + Enter is pressed.
enableTableOfContentsbooleanfalseExperimental. Enables the app-owned table-of-contents API for heading nodes.
onTableOfContentsChange(items: ScribeTableOfContentsItem[], isCreate?: boolean) => voidundefinedExperimental. Receives table-of-contents items whenever heading text, structure, or active/scrolled state changes.

Helper Functions

md2html

export declare function md2html(md: string): string;

Convert markdown to html. Useful if you're rendering an AI-based response, or if you were storing content on markdown in your database and want to show it on the text editor. This function sanitizes the content to prevent XSS attacks.

Editable Scribe instances also use this conversion internally when you paste plain-text markdown into the editor.

Usage Example:

import { md2html, Scribe } from "@clevertask/scribe";
import { Flex, Heading } from "@radix-ui/themes";
import { Message, useChat } from "@ai-sdk/react";

const ChatMessages = () => {
  const { messages } = useChat({
    /* For more info, see https://sdk.vercel.ai/docs/reference/ai-sdk-ui/use-chat */
  });

  return messages.map((message) => (
    <Flex key={message.id} direction="column" mb="4">
      <Heading size="4">{`${message.role}: `}</Heading>
      <Scribe editable={false} showBarMenu={false} content={md2html(message.content)} />
    </Flex>
  ));
};

html2md

export declare function html2md(html: string): string;

Convert html to markdown. Useful if you want to send a text to an AI model by keeping the text format with markdown. This function sanitizes the content to prevent XSS attacks.

Usage Example:

import { html2md, Scribe } from "@clevertask/scribe";

const md = html2md("<h1>Hello world</h1>"); // Output: # Hello world

Note: The Scribe component already exposes a property called markdownContent when the onContentChange is used. In fact, the markdownContent is the output of the usage of the html2md function.


convertLegacyMathDelimiters

export declare function convertLegacyMathDelimiters(input: string): string;

Convert legacy math delimiters into the HTML nodes required by the mathematics extension. This is useful when you receive HTML from a server that contains legacy math like \(...\) or \[...\].

This helper is primarily for consumers upgrading from older Scribe versions who stored math expressions using the legacy formats. It aims to be accurate, but the previous format was ambiguous (no explicit $$ delimiters), so conversion is best-effort and not guaranteed in every case.

Supported legacy delimiters:

  • \(...\) for inline math
  • \[...\] for block math
  • (...) and [...] when the content looks like LaTeX (contains \, ^, or _)

Usage Example:

import { convertLegacyMathDelimiters, md2html, Scribe } from "@clevertask/scribe";

const html = md2html(convertLegacyMathDelimiters(rawMarkdown));

// OR

const html = convertLegacyMathDelimiters(htmlContent);

<Scribe editable={false} showBarMenu={false} content={html} />;

If you already receive HTML from the server, call convertLegacyMathDelimiters directly on that HTML before rendering.

Roadmap

We're constantly working to improve @clevertask/scribe. Here are some features we're planning to implement:

  • New default blocks/extensions: Such as image, video, callout, and table blocks
  • E2E tests: It will ensure this component's working as expected.

We're excited about these upcoming features and welcome any feedback or contributions from the community. If you have any suggestions or would like to contribute to any of these features, please open an issue or submit a pull request on our GitHub repository.

Release Process

Publishing is split into two explicit GitHub Actions after a change reaches main:

  1. Run Create Release Version from main and enter the version to release.
  2. Wait for it to commit the version, create the tag, and create the GitHub release.
  3. Run Publish Package from the immutable v<version> tag and choose the latest or next npm tag.
  4. Confirm the new version is available in the npm registry before updating consumers.

License

MIT

Credits

This project is built on top of the excellent BlockEditor repository by Sachin Chaurasiya. We extend our sincere gratitude for their work. <3

版本与发布记录

@clevertask/scribe 共发布 54 个版本,首次发布于 2024/12/09;过去 12 个月发布了 42 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.1.37latest2026/11/09今天
0.1.362026/11/091 天前
0.1.352026/10/2318 天前
0.1.342026/10/2318 天前
0.1.332026/10/2120 天前
0.1.322026/10/2021 天前

关于 @clevertask/scribe 的常见问题

@clevertask/scribe 是什么?

A Radix-based Tiptap rich text editor with a Notion-style block interface for viewing and creating content. Perfect for diverse needs like notes, documents, AI chat, Markdown parsing, and comments.

如何安装 @clevertask/scribe?

在项目目录运行 npm install @clevertask/scribe,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(>=18.3.1 <20.0.0)、react-dom(>=18.3.1 <20.0.0)、@tiptap/pm(3.32.2)、@radix-ui/themes(>=3.3.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@clevertask/scribe 支持 TypeScript 吗?

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

@clevertask/scribe 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@clevertask/scribe 可以商用吗?

@clevertask/scribe 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@clevertask/scribe 的下载量有多少?

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

@clevertask/scribe 还在维护吗?

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