diff --git a/.github/workflows/pr-merge.yml b/.github/workflows/pr-merge.yml
new file mode 100644
index 00000000..f4651ed4
--- /dev/null
+++ b/.github/workflows/pr-merge.yml
@@ -0,0 +1,79 @@
+name: PR Closed
+
+on:
+ pull_request_target:
+ types:
+ - closed
+
+jobs:
+ remove_assets:
+ runs-on: ubuntu-latest
+
+ steps:
+ # 检出仓库代码
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ # 打印 PR 详细信息
+ - name: Print PR details
+ run: |
+ echo "The PR ID is ${{ github.event.pull_request.id }}"
+ echo "The PR number is ${{ github.event.pull_request.number }}"
+ echo "The PR title is ${{ github.event.pull_request.title }}"
+ echo "The PR branch is ${{ github.event.pull_request.head.ref }}"
+
+ # 安装 cos-nodejs-sdk-v5
+ - name: Install cos-nodejs-sdk-v5
+ run: npm install cos-nodejs-sdk-v5
+
+ # 删除对应的资源
+ - name: Delete Resources On COS
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const COS = require('cos-nodejs-sdk-v5');
+
+ const cos = new COS({
+ SecretId: process.env.COS_SECRETID,
+ SecretKey: process.env.COS_SECRETKEY,
+ });
+
+ const bucket = 'cherrymd-1301618266';
+ const region = 'ap-singapore';
+ const prNumber = '${{ github.event.pull_request.number }}';
+ const prefix = `pr${prNumber}/`;
+
+ // List objects in the bucket with the specified prefix
+ cos.getBucket({
+ Bucket: bucket,
+ Region: region,
+ Prefix: prefix,
+ }, (err, data) => {
+ if (err) {
+ console.error('Error listing objects:', err);
+ return;
+ }
+
+ const objectsToDelete = data.Contents.map(item => ({ Key: item.Key }));
+
+ if (objectsToDelete.length === 0) {
+ console.log('No objects to delete.');
+ return;
+ }
+
+ // Delete the listed objects
+ cos.deleteMultipleObject({
+ Bucket: bucket,
+ Region: region,
+ Objects: objectsToDelete,
+ }, (err, data) => {
+ if (err) {
+ console.error('Error deleting objects:', err);
+ } else {
+ console.log('Successfully deleted objects:', data);
+ }
+ });
+ });
+ env:
+ COS_SECRETID: ${{ secrets.COS_SECRETID }}
+ COS_SECRETKEY: ${{ secrets.COS_SECRETKEY }}
diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml
index e3a87db1..9d8bf7a5 100644
--- a/.github/workflows/pr-test.yml
+++ b/.github/workflows/pr-test.yml
@@ -6,6 +6,8 @@ on:
jobs:
build:
+ # 不需要在fork仓库的pr中运行
+ if: github.repository == 'Tencent/cherry-markdown'
runs-on: ubuntu-latest
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
diff --git a/README.JP.md b/README.JP.md
new file mode 100644
index 00000000..38a31cda
--- /dev/null
+++ b/README.JP.md
@@ -0,0 +1,294 @@
+
+
+# Cherry Markdown Writer
+
+[![Cloud Studio Template](https://cs-res.codehub.cn/common/assets/icon-badge.svg)](https://cloudstudio.net#https://github.com/Tencent/cherry-markdown)
+
+日本語 | [English](./README.md) | [简体中文](./README.CN.md)
+
+### ドキュメント
+- [初識cherry markdown 編集器](https://github.com/Tencent/cherry-markdown/wiki/%E5%88%9D%E8%AF%86cherry-markdown-%E7%BC%96%E8%BE%91%E5%99%A8)
+- [hello world](https://github.com/Tencent/cherry-markdown/wiki/hello-world)
+- [画像&ファイルアップロードインターフェースの設定](https://github.com/Tencent/cherry-markdown/wiki/%E9%85%8D%E7%BD%AE%E5%9B%BE%E7%89%87&%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%8E%A5%E5%8F%A3)
+- [ツールバーの調整](https://github.com/Tencent/cherry-markdown/wiki/%E8%B0%83%E6%95%B4%E5%B7%A5%E5%85%B7%E6%A0%8F)
+- [カスタムシンタックス](https://github.com/Tencent/cherry-markdown/wiki/%E8%87%AA%E5%AE%9A%E4%B9%89%E8%AF%AD%E6%B3%95)
+- [設定項目の全解](https://github.com/Tencent/cherry-markdown/wiki/%E9%85%8D%E7%BD%AE%E9%A1%B9%E5%85%A8%E8%A7%A3)
+- [テーマの設定](https://github.com/Tencent/cherry-markdown/wiki/%E9%85%8D%E7%BD%AE%E4%B8%BB%E9%A2%98)
+- [コードブロックシンタックスの拡張](https://github.com/Tencent/cherry-markdown/wiki/%E6%89%A9%E5%B1%95%E4%BB%A3%E7%A0%81%E5%9D%97%E8%AF%AD%E6%B3%95)
+- [イベント&コールバック](https://github.com/Tencent/cherry-markdown/wiki/%E4%BA%8B%E4%BB%B6&%E5%9B%9E%E8%B0%83)
+- [API](https://tencent.github.io/cherry-markdown/examples/api.html)
+
+### デモ
+
+- [フルモデル](https://tencent.github.io/cherry-markdown/examples/index.html)
+- [ベーシック](https://tencent.github.io/cherry-markdown/examples/basic.html)
+- [モバイル](https://tencent.github.io/cherry-markdown/examples/h5.html)
+- [複数インスタンス](https://tencent.github.io/cherry-markdown/examples/multiple.html)
+- [ツールバーなしエディタ](https://tencent.github.io/cherry-markdown/examples/notoolbar.html)
+- [純プレビュー](https://tencent.github.io/cherry-markdown/examples/preview_only.html)
+- [XSS](https://tencent.github.io/cherry-markdown/examples/xss.html)(デフォルトでは許可されていません)
+- [画像WYSIWYG](https://tencent.github.io/cherry-markdown/examples/img.html)
+- [テーブルWYSIWYG](https://tencent.github.io/cherry-markdown/examples/table.html)
+- [自動番号付きヘッダー](https://tencent.github.io/cherry-markdown/examples/head_num.html)
+- [ストリーム入力モード(AIチャートシナリオ)](https://tencent.github.io/cherry-markdown/examples/ai_chat.html)
+
+-----
+
+## 紹介
+
+Cherry Markdown Editorは、Javascriptで書かれたMarkdownエディタです。Cherry Markdown Editorは、すぐに使える、軽量でシンプル、拡張が容易などの利点があります。ブラウザやサーバー(NodeJs)で動作します。
+
+### **すぐに使える**
+
+開発者は非常に簡単な方法でCherry Markdown Editorを呼び出してインスタンス化できます。インスタンス化されたエディタは、デフォルトでほとんどの一般的なMarkdownシンタックス(タイトル、目次、フローチャート、数式など)をサポートします。
+
+### **拡張が容易**
+
+Cherry Markdown Editorがサポートするシンタックスが開発者のニーズを満たさない場合、迅速に二次開発や機能拡張を行うことができます。同時に、Cherry Markdown Editorは純粋なJavaScriptで実装されるべきであり、angular、vue、reactなどのフレームワーク技術に依存すべきではありません。フレームワークはコンテナ環境を提供するだけです。
+
+## 特徴
+
+### シンタックスの特徴
+
+1. 画像のズーム、配置、引用
+2. テーブルの内容に基づいてチャートを生成
+3. フォントの色とサイズの調整
+4. フォントの背景色、上付き文字、下付き文字
+5. チェックリストの挿入
+6. オーディオやビデオの挿入
+
+### 複数のモード
+
+1. スクロール同期付きのライブプレビュー
+2. プレビューのみのモード
+3. ツールバーなしのモード(ミニマリスト編集モード)
+4. モバイルプレビューモード
+
+### 機能の特徴
+
+1. リッチテキストからコピーしてMarkdownテキストとして貼り付け
+2. クラシック改行&通常改行
+3. マルチカーソル編集
+4. 画像サイズの編集
+5. 画像やPDFとしてエクスポート
+6. 新しい行の先頭に表示されるフロートツールバー
+7. テキストを選択すると表示されるバブルツールバー
+
+### パフォーマンスの特徴
+
+1. 部分的なレンダリング
+2. 部分的な更新
+
+### セキュリティ
+
+Cherry Markdownには組み込みのセキュリティフックがあり、ホワイトリストのフィルタリングとDomPurifyを使用してスキャンとフィルタリングを行います。
+
+### スタイルテーマ
+
+Cherry Markdownにはさまざまなスタイルテーマが用意されています。
+
+### 特徴の表示
+
+詳細については[こちら](https://github.com/Tencent/cherry-markdown/wiki/%E7%89%B9%E6%80%A7%E5%B1%95%E7%A4%BA-features)をクリックしてください。
+
+## インストール
+
+yarnを使用
+
+```bash
+yarn add cherry-markdown
+```
+
+npmを使用
+
+```bash
+npm install cherry-markdown --save
+```
+
+`mermaid`描画とテーブル自動チャート機能を有効にする必要がある場合は、`mermaid`と`echarts`パッケージを同時に追加する必要があります。
+
+現在、**Cherry**が推奨するプラグインバージョンは`echarts@4.6.0`、`mermaid@9.4.3`です。
+
+```bash
+# mermaid依存関係をインストールしてmermaid描画機能を有効にする
+yarn add mermaid@9.4.3
+# echarts依存関係をインストールしてテーブル自動チャート機能を有効にする
+yarn add echarts@4.6.0
+```
+
+## クイックスタート
+
+### ブラウザ
+
+#### UMD
+
+```html
+
+
+
+
+```
+
+#### ESM
+
+```javascript
+import 'cherry-markdown/dist/cherry-markdown.css';
+import Cherry from 'cherry-markdown';
+const cherryInstance = new Cherry({
+ id: 'markdown-container',
+ value: '# welcome to cherry editor!',
+});
+```
+
+### Node
+
+```javascript
+const { default: CherryEngine } = require('cherry-markdown/dist/cherry-markdown.engine.core.common');
+const cherryEngineInstance = new CherryEngine();
+const htmlContent = cherryEngineInstance.makeHtml('# welcome to cherry editor!');
+```
+
+## ライトバージョンの使用
+
+mermaidライブラリのサイズが非常に大きいため、cherryビルド製品にはmermaidを内蔵しないコアビルドパッケージが含まれています。コアビルドは以下の方法でインポートできます。
+
+### フルモード(UIインターフェース付き)
+
+```javascript
+import 'cherry-markdown/dist/cherry-markdown.css';
+import Cherry from 'cherry-markdown/dist/cherry-markdown.core';
+const cherryInstance = new Cherry({
+ id: 'markdown-container',
+ value: '# welcome to cherry editor!',
+});
+```
+
+### エンジンモード(シンタックスコンパイルのみ)
+
+```javascript
+// Cherryエンジンコアビルドをインポート
+// エンジンの設定項目はCherryの設定項目と同じです。以下のドキュメント内容はCherryコアパッケージのみを紹介します。
+import CherryEngine from 'cherry-markdown/dist/cherry-markdown.engine.core';
+const cherryEngineInstance = new CherryEngine();
+const htmlContent = cherryEngineInstance.makeHtml('# welcome to cherry editor!');
+
+// --> welcome to cherry editor!
+```
+
+### ⚠️ mermaidについて
+
+コアビルドパッケージにはmermaid依存関係が含まれていないため、関連プラグインを手動でインポートする必要があります。
+
+```javascript
+import 'cherry-markdown/dist/cherry-markdown.css';
+import Cherry from 'cherry-markdown/dist/cherry-markdown.core';
+import CherryMermaidPlugin from 'cherry-markdown/dist/addons/cherry-code-block-mermaid-plugin';
+import mermaid from 'mermaid';
+
+// プラグインの登録はCherryのインスタンス化前に行う必要があります
+Cherry.usePlugin(CherryMermaidPlugin, {
+ mermaid, // mermaidオブジェクトを渡す
+ // mermaidAPI: mermaid.mermaidAPI, // mermaid APIを渡すこともできます
+ // 同時にここでmermaidの動作を設定できます。詳細はmermaid公式ドキュメントを参照してください。
+ // theme: 'neutral',
+ // sequence: { useMaxWidth: false, showSequenceNumbers: true }
+});
+
+const cherryInstance = new Cherry({
+ id: 'markdown-container',
+ value: '# welcome to cherry editor!',
+});
+```
+
+### 動的インポート
+
+**推奨** 動的インポートを使用します。以下はwebpackの動的インポートの例です。
+
+```javascript
+import 'cherry-markdown/dist/cherry-markdown.css';
+import Cherry from 'cherry-markdown/dist/cherry-markdown.core';
+
+const registerPlugin = async () => {
+ const [{ default: CherryMermaidPlugin }, mermaid] = await Promise.all([
+ import('cherry-markdown/src/addons/cherry-code-block-mermaid-plugin'),
+ import('mermaid'),
+ ]);
+ Cherry.usePlugin(CherryMermaidPlugin, {
+ mermaid, // mermaidオブジェクトを渡す
+ });
+};
+
+registerPlugin().then(() => {
+ // プラグインの登録はCherryのインスタンス化前に行う必要があります
+ const cherryInstance = new Cherry({
+ id: 'markdown-container',
+ value: '# welcome to cherry editor!',
+ });
+});
+```
+
+## 設定
+`/src/Cherry.config.js`を参照するか、[こちら](https://github.com/Tencent/cherry-markdown/wiki/%E9%85%8D%E7%BD%AE%E9%A1%B9%E5%85%A8%E8%A7%A3)をクリックしてください。
+
+## 例
+
+詳細な例については[こちら](https://github.com/Tencent/cherry-markdown/wiki)をクリックしてください。
+
+### クライアント
+開発中です。詳細は`/client/`ディレクトリを参照してください。
+
+## 拡張
+
+### カスタムシンタックス
+[こちら](https://github.com/Tencent/cherry-markdown/wiki/%E8%87%AA%E5%AE%9A%E4%B9%89%E8%AF%AD%E6%B3%95)をクリックしてください。
+
+### カスタムツールバー
+[こちら](https://github.com/Tencent/cherry-markdown/wiki/%E8%B0%83%E6%95%B4%E5%B7%A5%E5%85%B7%E6%A0%8F)をクリックしてください。
+
+## ユニットテスト
+
+Jestはそのアサーション、非同期サポート、スナップショット機能のために選ばれました。ユニットテストにはCommonMarkテストとスナップショットテストが含まれます。
+
+### CommonMarkテスト
+
+`yarn run test:commonmark`を実行して公式のCommonMarkスイートをテストします。このコマンドは高速で実行されます。
+
+スイートは`test/suites/commonmark.spec.json`にあります。例えば:
+
+```json
+{
+ "markdown": " \tfoo\tbaz\t\tbim\n",
+ "html": "foo\tbaz\t\tbim\n
\n",
+ "example": 2,
+ "start_line": 363,
+ "end_line": 368,
+ "section": "Tabs"
+},
+```
+
+この場合、Jestは`Cherry.makeHtml(" \tfoo\tbaz\t\tbim\n")`によって生成されたHTMLを期待される結果`"foo\tbaz\t \tbim\n
\n"`と比較します。Cherry Markdownのマッチャーは`data-line`などのプライベート属性を無視しています。
+
+CommonMarkの仕様とスイートは次の場所から取得できます:https://spec.commonmark.org/ 。
+
+### スナップショットテスト
+
+`yarn run test:snapshot`を実行してスナップショットテストを実行します。`test/core/hooks/List.spec.ts`のようにスナップショットスイートを書くことができます。最初の実行時にスナップショットが自動的に生成されます。その後、Jestはスナップショットと生成されたHTMLを比較できます。スナップショットを再生成する必要がある場合は、`test/core/hooks/__snapshots__`の下にある古いスナップショットを削除してこのコマンドを再度実行します。
+
+スナップショットテストは遅く実行されます。エラーが発生しやすく、Cherry Markdownの特別なシンタックスを含むフックをテストするためにのみ使用されるべきです。
+
+## 貢献
+
+より強力なMarkdownエディタを構築するために参加してください。もちろん、機能リクエストを提出することもできます。作業を始める前に[こちら](https://github.com/Tencent/cherry-markdown/wiki/%E5%88%9D%E8%AF%86cherry-markdown-%E7%BC%96%E8%BE%91%E5%99%A8)を読んでください。
+
+## Stargazers over time
+
+[![Stargazers over time](https://starchart.cc/Tencent/cherry-markdown.svg)](https://starchart.cc/Tencent/cherry-markdown)
+
+## ライセンス
+
+Apache-2.0
diff --git a/README.md b/README.md
index 567614ca..123c8dad 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
[![Cloud Studio Template](https://cs-res.codehub.cn/common/assets/icon-badge.svg)](https://cloudstudio.net#https://github.com/Tencent/cherry-markdown)
-English | [简体中文](./README.CN.md)
+English | [简体中文](./README.CN.md) | [日本語](./README.JP.md)
### Document
- [初识cherry markdown 编辑器](https://github.com/Tencent/cherry-markdown/wiki/%E5%88%9D%E8%AF%86cherry-markdown-%E7%BC%96%E8%BE%91%E5%99%A8)
diff --git a/examples/api.html b/examples/api.html
index 12f5a589..31989e9b 100644
--- a/examples/api.html
+++ b/examples/api.html
@@ -270,6 +270,56 @@ toolbar.toolbarHandlers.italic()
+
+
toolbar.toolbarHandlers.strikethrough()
+
向cherry编辑器中插入删除线语法
+
+
+
+
+
toolbar.toolbarHandlers.size(fontSize: int)
+
向cherry编辑器中插入字体大小语法
+
+
+
+
+
toolbar.toolbarHandlers.color(param:string)
+
+ 向cherry编辑器中插入字体颜色或字体背景色语法
+
+
+
+ param |
+ 效果 |
+
+
+
+
+ 'color: #c2255c' |
+ 字体颜色 |
+
+
+ 'background-color: #c2255c' |
+ 字体背景颜色 |
+
+
+
+
+
+
+
toolbar.toolbarHandlers.header(level:int)
向cherry编辑器中插入标题语法
@@ -283,11 +333,65 @@
toolbar.toolbarHandlers.header(level:int)
-
toolbar.toolbarHandlers.strikethrough()
-
向cherry编辑器中插入删除线语法
+
toolbar.toolbarHandlers.quote()
+
向cherry编辑器中插入引用语法
+
+
+
+
+
toolbar.toolbarHandlers.panel(param:string)
+
+ 向cherry编辑器中插入对齐方式或信息面板语法
+
+
+
+ param |
+ 效果 |
+
+
+
+
+ 'left' |
+ 左对齐 |
+
+
+ 'center' |
+ 居中对齐 |
+
+
+ 'right' |
+ 右对齐 |
+
+
+ 'primary' |
+ 首选项 |
+
+
+ 'info' |
+ 一般信息 |
+
+
+ 'warning' |
+ 警告 |
+
+
+ 'danger' |
+ 危险 |
+
+
+ 'success' |
+ 成功 |
+
+
+
+
+cherryObj.toolbar.toolbarHandlers.panel('success')
试一试
diff --git a/examples/cherry-markdown-react-demo/.gitignore b/examples/cherry-markdown-react-demo/.gitignore
new file mode 100644
index 00000000..4d29575d
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/.gitignore
@@ -0,0 +1,23 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/examples/cherry-markdown-react-demo/README.md b/examples/cherry-markdown-react-demo/README.md
new file mode 100644
index 00000000..a732af20
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/README.md
@@ -0,0 +1,8 @@
+# 介绍
+这是cherry-markdown的react示例
+
+# 开始
+```
+npm install
+npm start
+```
\ No newline at end of file
diff --git a/examples/cherry-markdown-react-demo/package.json b/examples/cherry-markdown-react-demo/package.json
new file mode 100644
index 00000000..6db4cf3c
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "cherry-markdown-react-demo",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "cherry-markdown": "^0.8.44",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-scripts": "5.0.1"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/examples/cherry-markdown-react-demo/public/favicon.ico b/examples/cherry-markdown-react-demo/public/favicon.ico
new file mode 100644
index 00000000..aa8c8987
Binary files /dev/null and b/examples/cherry-markdown-react-demo/public/favicon.ico differ
diff --git a/examples/cherry-markdown-react-demo/public/github.svg b/examples/cherry-markdown-react-demo/public/github.svg
new file mode 100644
index 00000000..eeb3811a
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/public/github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/cherry-markdown-react-demo/public/index.html b/examples/cherry-markdown-react-demo/public/index.html
new file mode 100644
index 00000000..d35cfde0
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/public/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cherry React Demo
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/cherry-markdown-react-demo/public/logo--color.png b/examples/cherry-markdown-react-demo/public/logo--color.png
new file mode 100644
index 00000000..786b7a5b
Binary files /dev/null and b/examples/cherry-markdown-react-demo/public/logo--color.png differ
diff --git a/examples/cherry-markdown-react-demo/public/manifest.json b/examples/cherry-markdown-react-demo/public/manifest.json
new file mode 100644
index 00000000..c4e4f94b
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/public/manifest.json
@@ -0,0 +1,15 @@
+{
+ "short_name": "React App",
+ "name": "Create React App Sample",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
\ No newline at end of file
diff --git a/examples/cherry-markdown-react-demo/src/App.css b/examples/cherry-markdown-react-demo/src/App.css
new file mode 100644
index 00000000..7e566de8
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/src/App.css
@@ -0,0 +1,178 @@
+:root {
+ --width: 1400px;
+ --color: #e26862;
+}
+
+header {
+ height: 60px;
+ border-bottom: 1px solid var(--color);
+ display: flex;
+ justify-content: center;
+}
+
+h2 {
+ margin-top: 8px;
+ margin-bottom: 8px;
+}
+
+h3 {
+ margin-top: 6px;
+ margin-bottom: 6px;
+}
+
+.header-wrapper {
+ height: 100%;
+ width: var(--width);
+ display: flex;
+ justify-content: start;
+ align-items: center;
+}
+
+.header-logo {
+ height: 60%;
+ width: auto;
+ object-fit: contain;
+}
+
+.header-repo-a {
+ height: 100%;
+ display: flex;
+ align-items: center;
+ margin-left: auto;
+}
+
+.header-text {
+ font-size: 20px;
+ text-decoration: none;
+ color: inherit;
+ padding: 10px;
+
+ margin-left: 15px;
+ border-radius: 5px;
+}
+
+.header-text:hover {
+ background-color: var(--color);
+ color: white;
+}
+
+.header-text:nth-child(2) {
+ background-color: var(--color);
+ color: white;
+}
+
+main {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ width: 100%;
+}
+
+.main-wrapper {
+ width: var(--width)
+}
+
+.title-wrapper {
+ width: 100%;
+}
+
+.title {
+ display: flex;
+ justify-content: start;
+}
+
+.menu {
+ border: 1px solid gray;
+ padding: 8px;
+ border-radius: 5px;
+ margin-bottom: 10px;
+}
+
+
+#markdown-container {
+ width: 100%;
+ height: 800px !important;
+}
+
+.custom-select-container {
+ display: inline-block;
+ min-width: 250px;
+ text-align: center;
+ position: relative;
+ z-index: 100;
+ margin-left: 5px;
+}
+
+.custom-select {
+ padding: 5px;
+ border-bottom: 1px solid var(--color);
+}
+
+.custom-option-wrapper {
+ position: absolute;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ text-align: center;
+ background: white;
+ border: 1px solid var(--color);
+ border-bottom-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+}
+
+.custom-option {
+ list-style-type: none;
+ margin: 6px 10px;
+ border-bottom: 1px solid gainsboro;
+ cursor: pointer;
+ line-height: 30px;
+ border-radius: 5px;
+}
+
+.custom-option:hover {
+ /* background-color: gray; */
+ color: #ffffff;
+ background-color: var(--color);
+}
+
+
+.code-input {
+ width: 99%;
+ max-width: 99%;
+ padding: 5px;
+ font-size: 20px;
+}
+
+
+.demo-desc {
+ font-size: 17px;
+ white-space: pre-wrap;
+}
+
+
+.run-btn {
+ background-color: white;
+ font-size: 20px;
+ color: white;
+ border: 1px solid var(--color);
+ background-color: var(--color);
+ border-radius: 5px;
+ margin-left: 5px;
+ cursor: pointer;
+}
+
+.run-btn:disabled {
+ background-color: gainsboro;
+ border: 1px solid gainsboro;
+ cursor: default;
+}
+
+.run-btn:disabled:hover {
+ background-color: gainsboro;
+ border: 1px solid gainsboro;
+}
+
+.run-btn:hover {
+ background-color: red;
+ border: 1px solid red;
+}
\ No newline at end of file
diff --git a/examples/cherry-markdown-react-demo/src/App.jsx b/examples/cherry-markdown-react-demo/src/App.jsx
new file mode 100644
index 00000000..04d749ca
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/src/App.jsx
@@ -0,0 +1,40 @@
+import { useEffect, useState, useRef } from 'react';
+import './App.css';
+import Header from './components/Header'
+import 'cherry-markdown/dist/cherry-markdown.css';
+import Cherry from 'cherry-markdown';
+import Title from './components/Title';
+import Menu from "./components/Menu";
+
+function App() {
+ const editorRef = useRef(null);
+ const [editor, setEditor] = useState(null);
+ useEffect(() => {
+ if (editor == null) {
+ // 初始化编辑器
+ const config = {
+ el: editorRef.current,
+ value: '',
+ callback: {
+ afterChange: (md, html) => console.log('change'),
+ },
+ };
+ setEditor(new Cherry(config));
+ }
+ }, []);
+ return (
+ <>
+
+
+
+
+
+ {/* 该div作为编辑器的最外层容器 */}
+
+
+
+ >
+ );
+}
+
+export default App;
\ No newline at end of file
diff --git a/examples/cherry-markdown-react-demo/src/components/Header.jsx b/examples/cherry-markdown-react-demo/src/components/Header.jsx
new file mode 100644
index 00000000..18f9efcf
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/src/components/Header.jsx
@@ -0,0 +1,16 @@
+export default () => {
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/examples/cherry-markdown-react-demo/src/components/Menu.jsx b/examples/cherry-markdown-react-demo/src/components/Menu.jsx
new file mode 100644
index 00000000..269512a5
--- /dev/null
+++ b/examples/cherry-markdown-react-demo/src/components/Menu.jsx
@@ -0,0 +1,496 @@
+import { useEffect, useRef, useState } from "react"
+
+const Select = ({ options, onChange, selected }) => {
+ const [showOptions, setShowOptions] = useState(false);
+ return (
+
+
{ setShowOptions(prev => !prev); }}
+ >
+ {options[selected]}
+
+ {showOptions && (
+
+ {options.map((option, idx) => {
+ return (
+ - { onChange(idx); setShowOptions(false); }}
+ >
+ {option}
+
+ )
+ })}
+
+ )}
+
+ );
+}
+
+const getFullCode = (code) => `\`\`\`javascript
+import { useEffect, useState, useRef } from 'react';
+import 'cherry-markdown/dist/cherry-markdown.css';
+import Cherry from 'cherry-markdown';
+export default () => {
+ const editorRef = useRef(null);
+ const [cherry, setCherry] = useState(null);
+ useEffect(() => {
+ if (cherry == null) {
+ // 初始化编辑器
+ const config = {
+ el: editorRef.current,
+ value: '',
+ callback: {
+ afterChange: (md, html) => console.log('change'),
+ },
+ };
+ const cherryObj = new Cherry(config);
+ setCherry(cherryObj);
+ // api操作如下
+ ${code}
+ }
+ },[])
+ return ();
+}
+\`\`\``
+
+const data = [
+ {
+ name: "Cherry API", api: [
+ {
+ name: "setMarkdown", title: `setMarkdown(content:string, keepCursor = false)`,
+ desc: `设置内容,直接编辑器中的全部文本。setValue(content:string, keepCursor = false)有同样的功能,keepCursor = true 时更新内容的时候保持光标位置`,
+ code: `cherryObj.setMarkdown("初始内容");
+setTimeout(()=>{cherryObj.setMarkdown("2秒后替换成的新内容")},2000);`,
+ markdown: `# Cherry API
+## setMarkdown(content:string, keepCursor = false)
+设置内容
+${getFullCode(`cherryObj.setMarkdown("初始内容");
+ setTimeout(()=>{cherryObj.setMarkdown("2秒后替换成的新内容")},2000);`)}
+
+ `
+ },
+ {
+ name: "insert",
+ title: `insert(content: string, isSelect = false, anchor = false, focus = true)`,
+ desc: `插入内容\nisSelect = true 选中刚插入的内容\nanchor = false 在光标处插入内容\nanchor = [1, 3] 在第2行第4个字符处插入内容`,
+ code: `cherryObj.insert("在光标处插入内容");
+cherryObj.insert("在第二行插入内容,并选中插入的内容", true, [1, 0]);`,
+ markdown: `# Cherry API
+## insert(content: string, isSelect = false, anchor = false, focus = true)
+在光标处或者指定行 + 偏移量插入内容
+ > insert(\`content\`, \`isSelect\`, \`anchor\`, \`focus\`)
+- \`content\` 被插入的文本
+- \`isSelect\` 是否选中刚插入的内容,默认false,不选中
+- \`anchor\` [x,y] 代表x+1行,y+1字符偏移量,默认false 会从光标处插入
+- \`focus\` 保持编辑器处于focus状态,默认true,选中编辑器(用户可以继续输入)
+${getFullCode(`cherryObj.insert("在光标处插入内容");
+ cherryObj.insert("在第二行插入内容,并选中插入的内容", true, [1, 0]);`)}`,
+ },
+ {
+ name: "getMarkdown",
+ title: `getMarkdown()`,
+ desc: `获取markdown内容`,
+ code: `alert(cherryObj.getMarkdown());
+console.log(cherryObj.getMarkdown());`,
+ markdown: `# Cherry API
+## getMarkdown()
+获取markdown内容
+${getFullCode(`alert(cherryObj.getMarkdown());
+ console.log(cherryObj.getMarkdown());`)}`,
+ },
+ {
+ name: "getHtml",
+ title: `getHtml()`,
+ desc: `获取渲染后的html内容`,
+ code: `alert(cherryObj.getHtml());
+console.log(cherryObj.getHtml());`,
+ markdown: `# Cherry API
+## getHtml()
+获取渲染后的html内容
+${getFullCode(`alert(cherryObj.getHtml());
+ console.log(cherryObj.getHtml());`)}`,
+ },
+ {
+ name: "destroy",
+ title: `destroy()`,
+ desc: `销毁函数`,
+ code: `cherryObj.destroy();`,
+ markdown: `# Cherry API
+## destroy()
+销毁函数
+${getFullCode(`// cherryObj.destroy(); `)}`,
+ },
+ {
+ name: "resetToolbar",
+ title: `resetToolbar(type:string, toolbar:array)`,
+ desc: `重置工具栏
+type 修改工具栏的类型 {'toolbar'|'toolbarRight'|'sidebar'|'bubble'|'float'}
+toolbar 工具栏配置`,
+ code: `cherryObj.resetToolbar('toolbar', ['bold', 'table']);`,
+ markdown: `# Cherry API
+## resetToolbar(type:string, toolbar:array)
+重置工具栏
+type 修改工具栏的类型 {'toolbar'|'toolbarRight'|'sidebar'|'bubble'|'float'}
+toolbar 工具栏配置
+${getFullCode(`cherryObj.resetToolbar('toolbar', ['bold', 'table']);`)}`,
+ },
+ {
+ name: "export",
+ title: `export(type:string)`,
+ desc: `导出预览区域的内容,type:{'pdf'|'img'}`,
+ code: `if(confirm('导出pdf')) {
+ cherryObj.export();
+}else if(confirm('导出长图')) {
+ cherryObj.export('img');
+}`,
+ markdown: `# Cherry API
+## export(type:string)
+导出预览区域的内容,type:{'pdf'|'img'}
+${getFullCode(`if(confirm('导出pdf')) {
+ cherryObj.export();
+ }else if(confirm('导出长图')) {
+ cherryObj.export('img');
+ }`
+ )}`,
+ },
+ {
+ name: "switchModel",
+ title: `switchModel(model:string)`,
+ desc: `切换模式:{'edit&preview'|'editOnly'|'previewOnly'}`,
+ code: `if(confirm('只读模式')) {
+ cherryObj.switchModel('previewOnly');
+}else if(confirm('纯编辑模式')) {
+ cherryObj.switchModel('editOnly');
+}else if(confirm('双栏编辑模式')) {
+ cherryObj.switchModel('edit&preview');
+}`,
+ markdown: `# Cherry API
+## switchModel(model:string)
+切换模式:{'edit&preview'|'editOnly'|'previewOnly'}
+${getFullCode(`if(confirm('只读模式')) {
+ cherryObj.switchModel('previewOnly');
+ }else if(confirm('纯编辑模式')) {
+ cherryObj.switchModel('editOnly');
+ }else if(confirm('双栏编辑模式')) {
+ cherryObj.switchModel('edit&preview');
+ }`
+ )}`,
+ },
+ {
+ name: "getToc",
+ title: `getToc()`,
+ desc: `获取由标题组成的目录`,
+ code: `alert(cherryObj.getToc());
+console.log(cherryObj.getToc());`,
+ markdown: `# Cherry API
+## getToc()
+获取由标题组成的目录
+${getFullCode(`alert(cherryObj.getToc());
+ console.log(cherryObj.getToc());`)}`,
+ },
+ {
+ name: "getCodeMirror",
+ title: `getCodeMirror()`,
+ desc: `获取左侧编辑器实例`,
+ code: `alert(cherryObj.getCodeMirror());
+console.log(cherryObj.getCodeMirror());`,
+ markdown: `# Cherry API
+## getCodeMirror()
+获取左侧编辑器实例
+${getFullCode(`alert(cherryObj.getCodeMirror());
+ console.log(cherryObj.getCodeMirror());`)}`,
+ },
+ {
+ name: "getPreviewer",
+ title: `getPreviewer()`,
+ desc: `获取右侧预览区对象实例`,
+ code: `alert(cherryObj.getPreviewer());
+console.log(cherryObj.getPreviewer());`,
+ markdown: `# Cherry API
+## getPreviewer()
+获取右侧预览区对象实例
+${getFullCode(`alert(cherryObj.getPreviewer());
+ console.log(cherryObj.getPreviewer());`)}`,
+ },
+
+ ],
+ },
+ {
+ name: "Cherry.Previewer API",
+ api: [{
+ name: "Previewer.scrollToId",
+ title: `Previewer.scrollToId(id:string)`,
+ desc: `滚动到对应id的元素位置
+id 可以为带#号hash,也可以是id值`,
+ code: `// 查看可跳转的id
+console.log(cherryObj.getToc());
+// 两种方式都可获得previewer对象
+console.log(cherryObj.previewer == cherryObj.getPreviewer())
+//两种写法都可以
+// cherryObj.previewer.scrollToId('#test-scroll');
+cherryObj.previewer.scrollToId('test-scroll');`,
+ markdown: `# Cherry API
+## Previewer.scrollToId(id:string)
+滚动到对应id的元素位置
+id 可以为带#号hash,也可以是id值
+// 查看可跳转的id
+${getFullCode(`// 查看可跳转的id
+ console.log(cherryObj.getToc());
+ // 两种方式都可获得previewer对象
+ console.log(cherryObj.previewer == cherryObj.getPreviewer())
+ // 两种写法都可以
+ // cherryObj.previewer.scrollToId('#test-scroll');
+ cherryObj.previewer.scrollToId('test-scroll');`)}
+# Test Scroll`,
+ }]
+ },
+ {
+ name: "Cherry.engine API",
+ api: [
+ {
+ name: "makeHtml",
+ title: `engine.makeHtml(markdown:string)`,
+ desc: `将markdown字符串渲染成Html`,
+ code: `alert(cherryObj.engine.makeHtml('This is \`inline code\`'));
+console.log(cherryObj.engine.makeHtml('This is \`inline code\`'));`,
+ markdown: `# Cherry.engine API
+## engine.makeHtml(markdown:string)
+将markdown字符串渲染成Html
+${getFullCode(`alert(cherryObj.engine.makeHtml('This is \`inline code\`'));
+ console.log(cherryObj.engine.makeHtml('This is\`inline code\`'));`)}`,
+ },
+ {
+ name: "makeMarkdown",
+ title: `engine.makeMarkdown(html:string)`,
+ desc: `将html字符串渲染成markdown`,
+ code: `var html = \` < p > This isinline code
>\`;
+alert(cherryObj.engine.makeMarkdown(html));
+console.log(cherryObj.engine.makeMarkdown(html));`,
+ markdown: `# Cherry.engine API
+## engine.makeMarkdown(html:string)
+将html字符串渲染成markdown
+${getFullCode(`var html = \` < p > This is < code > inline code > >\`;
+ alert(cherryObj.engine.makeMarkdown(html));
+ console.log(cherryObj.engine.makeMarkdown(html));`)}`,
+ },
+ ],
+ },
+ {
+ name: "Cherry.toolbar.toolbarHandlers API",
+ api: [
+ {
+ name: "bold",
+ title: `toolbar.toolbarHandlers.bold()`,
+ desc: `在cherry编辑区域的选定文本处插入加粗语法`,
+ code: `cherryObj.toolbar.toolbarHandlers.bold()`,
+ markdown: `# Cherry.toolbar.toolbarHandlers API
+## toolbar.toolbarHandlers.bold()
+在cherry编辑区域的选定文本处插入加粗语法
+${getFullCode(`cherryObj.toolbar.toolbarHandlers.bold()`)}`,
+ },
+ {
+ name: "italic",
+ title: `toolbar.toolbarHandlers.italic()`,
+ desc: `在cherry编辑区域的选定文本处插入斜体语法`,
+ code: `cherryObj.toolbar.toolbarHandlers.italic()`,
+ markdown: `# Cherry.toolbar.toolbarHandlers API
+## toolbar.toolbarHandlers.italic()
+在cherry编辑区域的选定文本处插入斜体语法
+${getFullCode(`cherryObj.toolbar.toolbarHandlers.italic()`)}`,
+ },
+ {
+ name: "header",
+ title: `toolbar.toolbarHandlers.header(level: int)`,
+ desc: `在cherry编辑区域的选定文本处插入标题语法`,
+ code: `cherryObj.toolbar.toolbarHandlers.header(1)
+// cherryObj.toolbar.toolbarHandlers.header(2)
+// cherryObj.toolbar.toolbarHandlers.header(4)`,
+ markdown: `# Cherry.toolbar.toolbarHandlers API
+## toolbar.toolbarHandlers.header(level: int)
+
+在cherry编辑区域的选定文本处插入标题语法
+
+${getFullCode(`cherryObj.toolbar.toolbarHandlers.header(1)
+ // cherryObj.toolbar.toolbarHandlers.header(2)
+ // cherryObj.toolbar.toolbarHandlers.header(4)`)}`,
+ },
+ {
+ name: "strikethrough",
+ title: `toolbar.toolbarHandlers.strikethrough()`,
+ desc: `在cherry编辑区域的选定文本处插入删除线语法`,
+ code: `cherryObj.toolbar.toolbarHandlers.strikethrough()`,
+ markdown: `# Cherry.toolbar.toolbarHandlers API
+## toolbar.toolbarHandlers.strikethrough()
+在cherry编辑区域的选定文本处插入删除线语法
+${getFullCode(`cherryObj.toolbar.toolbarHandlers.strikethrough()`)}`,
+ },
+ {
+ name: "list",
+ title: `toolbar.toolbarHandlers.list(type: string)`,
+ desc: `在cherry编辑区域的选定文本处插入有序、无序列表或者checklist语法`,
+ code: `if(confirm('有序列表')) {
+ cherryObj.toolbar.toolbarHandlers.list(1);
+ }else if (confirm('无序列表')) {
+ cherryObj.toolbar.toolbarHandlers.list('2');
+ } else if (confirm('checklist')) {
+ cherryObj.toolbar.toolbarHandlers.list(3);
+ } `,
+ markdown: `# Cherry.toolbar.toolbarHandlers API
+## toolbar.toolbarHandlers.list(type: string)
+在cherry编辑区域的选定文本处插入有序、无序列表或者checklist语法
+| level | 效果 |
+|:-:|:-:|
+| 1 | ol 列表 |
+| 2 | ul 列表 |
+| 3 | checklist |
+${getFullCode(`if(confirm('有序列表')) {
+ cherryObj.toolbar.toolbarHandlers.list(1);
+ }else if (confirm('无序列表')) {
+ cherryObj.toolbar.toolbarHandlers.list('2');
+ } else if (confirm('checklist')) {
+ cherryObj.toolbar.toolbarHandlers.list(3);
+ } `)}`,
+ },
+ {
+ name: "insert",
+ title: `toolbar.toolbarHandlers.insert(type: string)`,
+ desc: `在cherry编辑区域的光标处插入特定语法`,
+ code: `if (confirm('插入3*4的表格')) {
+ cherryObj.toolbar.toolbarHandlers.insert('normal-table-3*4');
+} else if (confirm('插入checklist')) {
+ cherryObj.toolbar.toolbarHandlers.insert('checklist');
+} `,
+ markdown: `# Cherry.toolbar.toolbarHandlers API
+## toolbar.toolbarHandlers.insert(type: string)
+在cherry编辑区域的光标处插入特定语法:
+
+| type | 效果 |
+|:-:|:-:|
+| 'hr' | 删除线 |
+| 'br' | 强制换行 |
+| 'code' | 代码块 |
+| 'formula' | 行内公式 |
+| 'checklist' | 检查项 |
+| 'toc' | 目录 |
+| 'link' | 超链接 |
+| 'image' | 图片 |
+| 'video' | 视频 |
+| 'audio' | 音频 |
+| 'normal-table' | 插入3行5列的表格 |
+| 'normal-table-row*col' | 如 \`normal-table-2*4\` 插入2行(包含表头是3行)4列的表格 |
+
+${getFullCode(`if (confirm('插入3*4的表格')) {
+ cherryObj.toolbar.toolbarHandlers.insert('normal-table-3*4');
+ } else if (confirm('插入checklist')) {
+ cherryObj.toolbar.toolbarHandlers.insert('checklist');
+ } `)}`,
+ },
+ {
+ name: "graph",
+ title: `toolbar.toolbarHandlers.graph(type:string)`,
+ desc: `在cherry编辑区域的光标处插入画图语法`,
+ code: `cherryObj.toolbar.toolbarHandlers.graph(1)
+// cherryObj.toolbar.toolbarHandlers.graph('2')
+// cherryObj.toolbar.toolbarHandlers.graph(4)`,
+ markdown: `# Cherry.toolbar.toolbarHandlers API
+## toolbar.toolbarHandlers.graph(type:string)
+在cherry编辑区域的光标处插入画图语法
+
+|id |效果 |
+|:-:|:-:|
+|'1' |流程图 |
+|'2' |时序图 |
+|'3' |状态图 |
+|'4' |类图 |
+|'5' |饼图 |
+|'6' |甘特图 |
+\`\`\`mermaid
+graph LR
+ A[公司] -->| 下 班 | B(菜市场)
+ B --> C{看见
卖西瓜的}
+ C -->|Yes| D[买一个包子]
+ C -->|No| E[买一斤包子]
+\`\`\`
+${getFullCode(`cherryObj.toolbar.toolbarHandlers.graph(1)
+ // cherryObj.toolbar.toolbarHandlers.graph('2')
+ // cherryObj.toolbar.toolbarHandlers.graph(4)`)}`,
+ },
+ ]
+ }
+]
+
+const CodeTextArea = ({ value, onChange }) => {
+ const ref = useRef(null);
+ useEffect(() => {
+ // 高度适应内容
+ const resize = () => {
+ if (ref.current) {
+ ref.current.style.height = 'auto'; // 重置高度
+ ref.current.style.height = `${ref.current.scrollHeight}px`; // 设置新高度
+ }
+ };
+ resize();
+ }, [value]);
+ return (
+