Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 50 additions & 5 deletions packages/cherry-markdown/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ SuperDoc is a JavaScript Markdown editor for browser and browser-like Webview ru
- [WYSIWYG Mode](https://github.com/desirecore/super-doc/wiki/WYSIWYG-%E6%89%80%E8%A7%81%E5%8D%B3%E6%89%80%E5%BE%97%E7%BC%96%E8%BE%91%E6%A8%A1%E5%BC%8F)
- [Stream Rendering](https://github.com/desirecore/super-doc/wiki/Stream-%E6%B5%81%E5%BC%8F%E6%B8%B2%E6%9F%93%E6%A8%A1%E5%BC%8F)

-----
---

### **Out-of-the-box**

Expand All @@ -72,6 +72,7 @@ When the syntax that SuperDoc supports can not meet your needs, secondary develo
### Incremental / Progressive / Streaming rendering

After enabling streaming rendering, SuperDoc will automatically complete the following syntax elements to avoid exposing Markdown source code, ensuring stable output during the streaming process:

- Headings
- Bold and italic text
- Hyperlinks
Expand Down Expand Up @@ -115,7 +116,7 @@ After enabling streaming rendering, SuperDoc will automatically complete the fol
13. Theme switching
14. Input suggestion (autocomplete)
15. AI Chat scenario: stream-mode output supported
16. **WYSIWYG mode: one-click switch between Markdown and rich-text editing** *(SuperDoc)*
16. **WYSIWYG mode: one-click switch between Markdown and rich-text editing** _(SuperDoc)_

### Performance Feature

Expand Down Expand Up @@ -177,6 +178,50 @@ const cherryInstance = new Cherry({

The published editor and engine entries require browser DOM APIs. Raw Node.js/SSR (including CommonJS `require`) is not a supported runtime.

### Ribbon header actions

When `toolbarTabs` enables the Ribbon layout, applications can place document-level actions on the same row as the Ribbon tabs. SuperDoc owns the rendered DOM; the host provides only serializable labels/state plus callbacks, so no selector lookup, portal, or external DOM mount is required.

```javascript
const cherryInstance = new Cherry({
id: 'markdown-container',
toolbars: {
toolbarTabs: [
{ name: 'start', buttons: ['bold', 'italic'] },
{ name: 'insert', buttons: ['image', 'link'] },
],
ribbonHeaderActions: {
ariaLabel: 'Document actions',
actions: [
{
type: 'segmented',
id: 'review-mode',
ariaLabel: 'Review mode',
value: 'inline',
options: [
{ value: 'inline', label: 'Inline' },
{ value: 'compare', label: 'Compare' },
],
onChange: (value) => console.log(value),
},
{ id: 'save', label: 'Save', onClick: () => saveDocument() },
{
type: 'menu',
id: 'more',
label: 'More',
items: [{ id: 'export', label: 'Export', onClick: () => exportDocument() }],
},
],
},
},
});

// Update active/disabled/menu state without rebuilding the editor.
cherryInstance.setRibbonHeaderActions(nextRibbonHeaderActions);
```

The capability can be detected with `Cherry.capabilities.ribbonHeaderActions === true`.

## Lite Version

Because the size of the mermaid library is very large, the build product contains a core build package without built-in Mermaid. The core build can be imported in the following ways.
Expand Down Expand Up @@ -242,9 +287,9 @@ cherryInstance.setMarkdown('# welcome to SuperDoc!');

| Build | File | Mermaid | CodeMirror | Use Case |
| ------ | --------------------- | ------- | ---------- | ----------------- |
| Full | `super-doc.js` | ✅ | ✅ | General purpose |
| Core | `super-doc.core.js` | ❌ | ✅ | Without Mermaid |
| Stream | `super-doc.stream.js` | ❌ | ❌ | AI Chat streaming |
| Full | `super-doc.js` | ✅ | ✅ | General purpose |
| Core | `super-doc.core.js` | ❌ | ✅ | Without Mermaid |
| Stream | `super-doc.stream.js` | ❌ | ❌ | AI Chat streaming |

> Note: MathJax/KaTeX are external dependencies loaded dynamically via CDN and are not included in any build package.

Expand Down
11 changes: 10 additions & 1 deletion packages/cherry-markdown/build/verify-dist.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,27 @@ if (missingFiles.length === 0) {
'modeCommitted',
'awaitableRefreshPreviewer',
'compactLayout',
'ribbonHeaderActions',
'setRibbonHeaderActions',
]);
});
requireMarkers('dist/types/capabilities.d.ts', ['SUPER_DOC_CAPABILITIES', 'SuperDocCapabilities']);
requireMarkers('dist/types/Cherry.d.ts', ['static capabilities', 'SuperDocCapabilities']);
requireMarkers('types/cherry.d.ts', ['ModeCommittedPayload', 'modeCommitted', 'SuperDocCapabilities']);
requireMarkers('types/cherry.d.ts', [
'ModeCommittedPayload',
'modeCommitted',
'SuperDocCapabilities',
'CherryRibbonHeaderActions',
'setRibbonHeaderActions',
]);

const compactCss = readFileSync(join(packageDir, 'dist/super-doc.min.css'), 'utf8').replace(/\s/g, '');
[
'--editor-content-font-size:15px',
'--editor-content-line-height:24px',
'--editor-split-padding-inline:var(--spacing-lg)',
'--wysiwyg-content-padding-inline:clamp(var(--spacing-xl),4vw,var(--spacing-3xl))',
'.cherry-ribbon-header-actions{',
].forEach((marker) => {
if (!compactCss.includes(marker)) {
contentFailures.push(`dist/super-doc.min.css: missing ${marker}`);
Expand Down
25 changes: 23 additions & 2 deletions packages/cherry-markdown/build/verify-pack.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,14 @@ Object.entries(expectedEntrypoints).forEach(([field, value]) => {
});

const coreBundle = readPackedText('dist/super-doc.core.js');
['SUPER_DOC_CAPABILITIES', 'modeCommitted', 'awaitableRefreshPreviewer', 'compactLayout'].forEach((marker) => {
[
'SUPER_DOC_CAPABILITIES',
'modeCommitted',
'awaitableRefreshPreviewer',
'compactLayout',
'ribbonHeaderActions',
'setRibbonHeaderActions',
].forEach((marker) => {
if (!coreBundle.includes(marker)) fail(`core bundle is missing ${marker}`);
});

Expand All @@ -83,6 +90,7 @@ const compactCss = readPackedText('dist/super-doc.min.css').replace(/\s/g, '');
'--editor-content-line-height:24px',
'--editor-split-padding-inline:var(--spacing-lg)',
'--wysiwyg-content-padding-inline:clamp(var(--spacing-xl),4vw,var(--spacing-3xl))',
'.cherry-ribbon-header-actions{',
].forEach((marker) => {
if (!compactCss.includes(marker)) fail(`packed CSS is missing ${marker}`);
});
Expand All @@ -92,7 +100,13 @@ if (!capabilityTypes.includes('SUPER_DOC_CAPABILITIES') || !capabilityTypes.incl
fail('packed capability types are incomplete');
}
const publicTypes = readPackedText('types/cherry.d.ts');
['ModeCommittedPayload', 'modeCommitted', 'SuperDocCapabilities'].forEach((marker) => {
[
'ModeCommittedPayload',
'modeCommitted',
'SuperDocCapabilities',
'CherryRibbonHeaderActions',
'setRibbonHeaderActions',
].forEach((marker) => {
if (!publicTypes.includes(marker)) fail(`packed public types are missing ${marker}`);
});

Expand Down Expand Up @@ -134,6 +148,7 @@ try {
asyncModeSwitch: true,
awaitableRefreshPreviewer: true,
compactLayout: true,
ribbonHeaderActions: true,
};
if (JSON.stringify(publishedModule.SUPER_DOC_CAPABILITIES) !== JSON.stringify(expectedCapabilities)) {
fail('published ESM named SUPER_DOC_CAPABILITIES export is invalid');
Expand All @@ -145,6 +160,9 @@ try {
}
if (typeof Cherry.prototype.switchModel !== 'function') fail('published Cherry.switchModel is not public');
if (typeof Cherry.prototype.refreshPreviewer !== 'function') fail('published Cherry.refreshPreviewer is not public');
if (typeof Cherry.prototype.setRibbonHeaderActions !== 'function') {
fail('published Cherry.setRibbonHeaderActions is not public');
}
const mermaidRenderer = Cherry.config?.defaults?.engine?.syntax?.codeBlock?.customRenderer?.mermaid;
if (
typeof mermaidRenderer?.mermaidAPIRefs?.initialize !== 'function' ||
Expand Down Expand Up @@ -172,6 +190,9 @@ try {
if (typeof CommonJsCherry.prototype.refreshPreviewer !== 'function') {
fail('published UMD CommonJS Cherry.refreshPreviewer is not public');
}
if (typeof CommonJsCherry.prototype.setRibbonHeaderActions !== 'function') {
fail('published UMD CommonJS Cherry.setRibbonHeaderActions is not public');
}

const modeHarness = Object.assign(Object.create(Cherry.prototype), {
committedModel: 'edit&preview',
Expand Down
2 changes: 1 addition & 1 deletion packages/cherry-markdown/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@desirecore/super-doc",
"license": "Apache-2.0",
"version": "0.2.15",
"version": "0.2.16",
"type": "module",
"description": "A powerful Markdown editor with WYSIWYG support, based on cherry-markdown",
"repository": {
Expand Down
30 changes: 27 additions & 3 deletions packages/cherry-markdown/src/Cherry.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -702,9 +702,13 @@ const defaultConfig = {
{ buttons: ['paste', 'cut', 'copySelection'], large: ['paste'] },
{
buttons: [
'header', 'bold', 'italic', 'underline',
'header',
'bold',
'italic',
'underline',
{ strikethrough: ['strikethrough', 'sub', 'sup', 'ruby'] },
'color', 'size',
'color',
'size',
],
large: ['header'],
},
Expand All @@ -715,7 +719,25 @@ const defaultConfig = {
},
{
name: 'tabInsert',
buttons: ['image', 'audio', 'video', '|', 'link', 'hr', 'br', '|', 'code', 'formula', '|', 'toc', 'table', '|', 'footnote', 'pdf', 'word'],
buttons: [
'image',
'audio',
'video',
'|',
'link',
'hr',
'br',
'|',
'code',
'formula',
'|',
'toc',
'table',
'|',
'footnote',
'pdf',
'word',
],
},
{
name: 'tabDraw',
Expand All @@ -730,6 +752,8 @@ const defaultConfig = {
buttons: ['export'],
},
],
// Ribbon 标签行右侧的宿主动作。由编辑器本身渲染,嵌入方只提供数据与回调。
ribbonHeaderActions: false,
toolbarRight: [],
sidebar: false,
bubble: ['bold', 'italic', 'underline', 'strikethrough', 'sub', 'sup', 'quote', '|', 'size', 'color'], // array or false
Expand Down
16 changes: 16 additions & 0 deletions packages/cherry-markdown/src/Cherry.js
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,22 @@ export default class Cherry extends CherryStatic {
return true;
}

/**
* 更新 Ribbon 标签行右侧动作。动作 DOM 始终由 SuperDoc 创建和维护,嵌入方无需
* 查询编辑器内部节点或挂载外部组件。
* @public
* @param {import('~types/cherry').CherryRibbonHeaderActions} actions
* @returns {Boolean}
*/
setRibbonHeaderActions(actions) {
if (actions !== false && (!actions || typeof actions !== 'object' || !Array.isArray(actions.actions))) {
return false;
}
this.options.toolbars.ribbonHeaderActions = actions;
this.toolbar?.setRibbonHeaderActions?.(actions);
return true;
}

/**
* @private
* @returns {Toolbar}
Expand Down
1 change: 1 addition & 0 deletions packages/cherry-markdown/src/capabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export const SUPER_DOC_CAPABILITIES = Object.freeze({
asyncModeSwitch: true,
awaitableRefreshPreviewer: true,
compactLayout: true,
ribbonHeaderActions: true,
});
Loading
Loading