yuque-rich-text 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +447 -103
- package/lib/components/lake-rich/editor-plugin.d.ts +46 -0
- package/lib/components/lake-rich/lake-rich.d.ts +5 -0
- package/lib/components/lake-rich/load.d.ts +8 -0
- package/lib/components/lake-rich/slash-options.d.ts +46 -0
- package/lib/components/lake-rich/template.d.ts +4 -0
- package/lib/core/create-lake-editor.d.ts +17 -0
- package/lib/core/editor-plugin.d.ts +46 -0
- package/lib/core/index.d.ts +6 -0
- package/lib/core/load.d.ts +11 -0
- package/lib/core/slash-options.d.ts +46 -0
- package/lib/core/template.d.ts +4 -0
- package/lib/core/types.d.ts +102 -0
- package/lib/create-lake-editor-xZsm8nT1.js +341 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +6 -0
- package/lib/lake-rich-view-vdfdFEom.js +184 -0
- package/lib/react/index.d.ts +3 -0
- package/lib/react/lake-rich.d.ts +32 -0
- package/lib/react.d.ts +6 -0
- package/lib/react.js +135 -0
- package/lib/vue/index.d.ts +5 -0
- package/lib/vue/lake-rich-view.d.ts +96 -0
- package/lib/vue/lake-rich.d.ts +139 -0
- package/lib/vue.d.ts +6 -0
- package/lib/vue.js +7 -0
- package/package.json +70 -12
- package/.github/workflows/publish.yml +0 -77
- package/.vscode/extensions.json +0 -3
- package/demo/App.vue +0 -51
- package/demo/assets/vue.svg +0 -1
- package/demo/main.ts +0 -5
- package/demo/style.css +0 -34
- package/demo/vite-env.d.ts +0 -1
- package/index.html +0 -21
- package/public/Images/preview.png +0 -0
- package/public/vite.svg +0 -1
- package/src/components/lake-rich/editor-plugin.ts +0 -205
- package/src/components/lake-rich/lake-rich-view.vue +0 -14
- package/src/components/lake-rich/lake-rich.ts +0 -286
- package/src/components/lake-rich/load.ts +0 -32
- package/src/components/lake-rich/slash-options.ts +0 -96
- package/src/components/lake-rich/template.ts +0 -92
- package/src/index.ts +0 -6
- package/tsconfig.json +0 -37
- package/vite.config.ts +0 -73
package/README.md
CHANGED
|
@@ -1,120 +1,464 @@
|
|
|
1
|
-
#
|
|
1
|
+
# yuque-rich-text
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/yuque-rich-text)
|
|
4
|
+
[](./LICENSE)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
**Yuque (语雀) Lake** rich-text **editor** and **viewer** for **Vue 3** and **React**.
|
|
7
|
+
|
|
8
|
+
Extracted from the official [yuque-chrome-extension](https://github.com/yuque/yuque-chrome-extension) so you can embed the same Lake editing experience in your own apps.
|
|
9
|
+
|
|
10
|
+
> **Unofficial third-party package.** Not affiliated with, maintained by, or endorsed by Yuque / 语雀.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Features](#features)
|
|
17
|
+
- [Screenshots](#screenshots)
|
|
18
|
+
- [Installation](#installation)
|
|
19
|
+
- [Prerequisites (CDN assets)](#prerequisites-cdn-assets)
|
|
20
|
+
- [Quick Start](#quick-start)
|
|
21
|
+
- [Vue 3](#vue-3)
|
|
22
|
+
- [React](#react)
|
|
23
|
+
- [Upload configuration](#upload-configuration)
|
|
24
|
+
- [API Reference](#api-reference)
|
|
25
|
+
- [Props](#props)
|
|
26
|
+
- [Events / Callbacks](#events--callbacks)
|
|
27
|
+
- [Imperative API (`ref`)](#imperative-api-ref)
|
|
28
|
+
- [Viewer mode](#viewer-mode)
|
|
29
|
+
- [Package exports](#package-exports)
|
|
30
|
+
- [Development](#development)
|
|
31
|
+
- [Disclaimer](#disclaimer)
|
|
32
|
+
- [License](#license)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
| Feature | Description |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| **Vue 3 & React** | First-class adapters with a shared core |
|
|
41
|
+
| **Editor + Viewer** | Full WYSIWYG editing or read-only preview |
|
|
42
|
+
| **Configurable upload** | Custom **upload URLs** and/or **upload callbacks** for image & video |
|
|
43
|
+
| **Lake format** | Native Yuque Lake document format + HTML interop |
|
|
44
|
+
| **Imperative API** | `setContent` / `getContent` / `appendContent` / `wordCount` / … |
|
|
45
|
+
| **Iframe isolation** | Editor runs in a sandboxed iframe with Lake runtime |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Screenshots
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# npm
|
|
59
|
+
npm install yuque-rich-text
|
|
60
|
+
|
|
61
|
+
# pnpm
|
|
62
|
+
pnpm add yuque-rich-text
|
|
63
|
+
|
|
64
|
+
# yarn
|
|
65
|
+
yarn add yuque-rich-text
|
|
8
66
|
```
|
|
9
67
|
|
|
10
|
-
|
|
11
|
-

|
|
68
|
+
**Peer dependencies** (install what you use):
|
|
12
69
|
|
|
13
|
-
|
|
70
|
+
```bash
|
|
71
|
+
# Vue 3
|
|
72
|
+
npm install vue@^3
|
|
73
|
+
|
|
74
|
+
# React
|
|
75
|
+
npm install react react-dom
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Prerequisites (CDN assets)
|
|
81
|
+
|
|
82
|
+
Lake depends on global scripts/styles. Include them in your host HTML (or inject equivalently at runtime).
|
|
83
|
+
|
|
84
|
+
### Styles (`<head>`)
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<link
|
|
88
|
+
rel="stylesheet"
|
|
89
|
+
type="text/css"
|
|
90
|
+
href="https://gw.alipayobjects.com/render/p/yuyan_npm/@alipay_lakex-doc/1.71.0/umd/doc.css"
|
|
91
|
+
/>
|
|
92
|
+
<link
|
|
93
|
+
rel="stylesheet"
|
|
94
|
+
type="text/css"
|
|
95
|
+
href="https://gw.alipayobjects.com/os/lib/antd/4.24.13/dist/antd.css"
|
|
96
|
+
/>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Scripts (before app bootstrap, typically end of `<body>`)
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<script crossorigin src="https://unpkg.com/react@18.2.0/umd/react.production.min.js"></script>
|
|
103
|
+
<script crossorigin src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js"></script>
|
|
104
|
+
<script src="https://gw.alipayobjects.com/render/p/yuyan_v/180020010000005484/7.1.4/CodeMirror.js"></script>
|
|
105
|
+
<script src="https://ur.alipay.com/tracert_a385.js"></script>
|
|
106
|
+
<script src="https://mdn.alipayobjects.com/design_kitchencore/afts/file/ANSZQ7GHQPMAAAAAAAAAAAAADhulAQBr"></script>
|
|
107
|
+
<script src="https://gw.alipayobjects.com/render/p/yuyan_npm/@alipay_lakex-doc/1.71.0/umd/doc.umd.js"></script>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
> The editor iframe also loads these assets via its internal template. Host-page inclusion is still recommended for consistent global `Doc` availability in some environments.
|
|
111
|
+
|
|
112
|
+
---
|
|
14
113
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
uploadImage?: (params: { data: string | File }) => Promise<{
|
|
21
|
-
url: string;
|
|
22
|
-
size: number;
|
|
23
|
-
filename: string;
|
|
24
|
-
}>;
|
|
25
|
-
uploadVideo?: (params: { data: string | File }) => Promise<{
|
|
26
|
-
url: string;
|
|
27
|
-
size: number;
|
|
28
|
-
filename: string;
|
|
29
|
-
}>;
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Emit
|
|
34
|
-
|
|
35
|
-
```js
|
|
36
|
-
export interface EditorEmits{
|
|
37
|
-
onChange?: (value: string) => void;
|
|
38
|
-
onLoad?: () => void;
|
|
39
|
-
onSave?: () => void;
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Expose
|
|
44
|
-
|
|
45
|
-
```js
|
|
46
|
-
export interface IEditorRef {
|
|
47
|
-
/**
|
|
48
|
-
* 追加html到文档
|
|
49
|
-
* @param html html内容
|
|
50
|
-
* @param breakLine 是否前置一个换行符
|
|
51
|
-
*/
|
|
52
|
-
appendContent: (html: string, breakLine?: boolean) => void;
|
|
53
|
-
/**
|
|
54
|
-
* 设置文档内容,将清空旧的内容
|
|
55
|
-
* @param html html内容
|
|
56
|
-
*/
|
|
57
|
-
setContent: (content: string, type?: "text/lake" | "text/html") => void;
|
|
58
|
-
/**
|
|
59
|
-
* 获取文档内容
|
|
60
|
-
* @param type 内容的格式
|
|
61
|
-
* @return 文档内容
|
|
62
|
-
*/
|
|
63
|
-
getContent: (type: "lake" | "text/html") => Promise<string>;
|
|
64
|
-
/**
|
|
65
|
-
* 判断当前文档是否是空文档
|
|
66
|
-
* @return true表示当前是空文档
|
|
67
|
-
*/
|
|
68
|
-
isEmpty: () => boolean;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 获取额外信息
|
|
72
|
-
* @return
|
|
73
|
-
*/
|
|
74
|
-
getSummaryContent: () => string;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* 统计字数
|
|
78
|
-
* @return
|
|
79
|
-
*/
|
|
80
|
-
wordCount: () => number;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* 聚焦到文档开头
|
|
84
|
-
* @param {number} offset 偏移多少个段落,可以将选区落到开头的第offset个段落上, 默认是0
|
|
85
|
-
* @return
|
|
86
|
-
*/
|
|
87
|
-
focusToStart: (offset?: number) => void;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* 插入换行符
|
|
91
|
-
* @return
|
|
92
|
-
*/
|
|
93
|
-
insertBreakLine: () => void;
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### 编辑器
|
|
98
|
-
> 注意不可在onChange事件中修改value的值,否则会进入无限递归。
|
|
99
|
-
```js
|
|
114
|
+
## Quick Start
|
|
115
|
+
|
|
116
|
+
### Vue 3
|
|
117
|
+
|
|
118
|
+
```vue
|
|
100
119
|
<template>
|
|
101
|
-
|
|
120
|
+
<div class="editor-wrap">
|
|
121
|
+
<YuqueRichText
|
|
122
|
+
ref="editorRef"
|
|
123
|
+
:value="content"
|
|
124
|
+
:image-upload-u-r-l="imageUploadURL"
|
|
125
|
+
:video-upload-u-r-l="videoUploadURL"
|
|
126
|
+
:upload-image="uploadImage"
|
|
127
|
+
@on-change="onChange"
|
|
128
|
+
@on-load="onLoad"
|
|
129
|
+
@on-save="onSave"
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
102
132
|
</template>
|
|
103
133
|
|
|
104
134
|
<script setup lang="ts">
|
|
105
|
-
import { ref
|
|
135
|
+
import { ref } from "vue";
|
|
106
136
|
import { YuqueRichText } from "yuque-rich-text";
|
|
137
|
+
// or: import { YuqueRichText } from 'yuque-rich-text/vue'
|
|
138
|
+
import type { IEditorRef } from "yuque-rich-text";
|
|
139
|
+
|
|
140
|
+
const editorRef = ref<IEditorRef>();
|
|
141
|
+
const content = ref("<p>Hello Lake</p>");
|
|
142
|
+
|
|
143
|
+
/** Prefer kebab for multi-cap props, or use camelCase in script-bound objects */
|
|
144
|
+
const imageUploadURL = "/api/v1/upload/image";
|
|
145
|
+
const videoUploadURL = "/api/v1/upload/video";
|
|
146
|
+
|
|
147
|
+
const uploadImage = async (params: { data: string | File }) => {
|
|
148
|
+
const form = new FormData();
|
|
149
|
+
form.append("file", params.data);
|
|
150
|
+
const res = await fetch(imageUploadURL, { method: "POST", body: form });
|
|
151
|
+
const json = await res.json();
|
|
152
|
+
return {
|
|
153
|
+
url: json.url,
|
|
154
|
+
size: json.size ?? 0,
|
|
155
|
+
filename: json.filename ?? "image.png",
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const onChange = (value: string) => {
|
|
160
|
+
// Do NOT write back into `content` from onChange with the same binding
|
|
161
|
+
// if you also pass `:value="content"` without guards — that can loop.
|
|
162
|
+
console.log("change", value);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const onLoad = () => {
|
|
166
|
+
editorRef.value?.focusToStart();
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const onSave = () => {
|
|
170
|
+
// Ctrl/Cmd + Enter
|
|
171
|
+
console.log(editorRef.value?.getContent("lake"));
|
|
172
|
+
};
|
|
173
|
+
</script>
|
|
174
|
+
|
|
175
|
+
<style scoped>
|
|
176
|
+
.editor-wrap {
|
|
177
|
+
height: 480px;
|
|
178
|
+
border: 1px solid #e5e7eb;
|
|
179
|
+
border-radius: 8px;
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
}
|
|
182
|
+
</style>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> **Tip:** Vue prop names like `imageUploadURL` are exposed as `image-upload-u-r-l` in templates. You can also pass a nested `upload` object to avoid awkward kebab names (see [Upload configuration](#upload-configuration)).
|
|
186
|
+
|
|
187
|
+
Using nested `upload` (recommended in Vue templates):
|
|
188
|
+
|
|
189
|
+
```vue
|
|
190
|
+
<YuqueRichText
|
|
191
|
+
ref="editorRef"
|
|
192
|
+
:value="content"
|
|
193
|
+
:upload="{
|
|
194
|
+
imageUploadURL: '/api/v1/upload/image',
|
|
195
|
+
videoUploadURL: '/api/v1/upload/video',
|
|
196
|
+
uploadImage,
|
|
197
|
+
}"
|
|
198
|
+
@on-change="onChange"
|
|
199
|
+
/>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### React
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { useRef, useState } from "react";
|
|
206
|
+
import { LakeRich, type IEditorRef } from "yuque-rich-text/react";
|
|
207
|
+
|
|
208
|
+
export function DocEditor() {
|
|
209
|
+
const editorRef = useRef<IEditorRef>(null);
|
|
210
|
+
const [value, setValue] = useState("<p>Hello Lake</p>");
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<div style={{ height: 480 }}>
|
|
214
|
+
<LakeRich
|
|
215
|
+
ref={editorRef}
|
|
216
|
+
value={value}
|
|
217
|
+
imageUploadURL="/api/v1/upload/image"
|
|
218
|
+
videoUploadURL="/api/v1/upload/video"
|
|
219
|
+
uploadImage={async ({ data }) => {
|
|
220
|
+
const form = new FormData();
|
|
221
|
+
form.append("file", data);
|
|
222
|
+
const res = await fetch("/api/v1/upload/image", {
|
|
223
|
+
method: "POST",
|
|
224
|
+
body: form,
|
|
225
|
+
});
|
|
226
|
+
const json = await res.json();
|
|
227
|
+
return {
|
|
228
|
+
url: json.url,
|
|
229
|
+
size: json.size ?? 0,
|
|
230
|
+
filename: json.filename ?? "image.png",
|
|
231
|
+
};
|
|
232
|
+
}}
|
|
233
|
+
onChange={setValue}
|
|
234
|
+
onLoad={() => editorRef.current?.focusToStart()}
|
|
235
|
+
onSave={() => {
|
|
236
|
+
console.log(editorRef.current?.getContent("lake"));
|
|
237
|
+
}}
|
|
238
|
+
/>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Upload configuration
|
|
247
|
+
|
|
248
|
+
Previously the editor hard-coded `/api/upload/image` and `/api/upload/video`. You can now configure **URLs** and/or **custom upload functions**.
|
|
249
|
+
|
|
250
|
+
### Option A — Upload URLs only
|
|
251
|
+
|
|
252
|
+
Lake will POST to your endpoints using its built-in uploader:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
{
|
|
256
|
+
imageUploadURL: "https://your.cdn.example/upload/image",
|
|
257
|
+
imageCrawlURL: "https://your.cdn.example/crawl/image", // optional; defaults to imageUploadURL
|
|
258
|
+
videoUploadURL: "https://your.cdn.example/upload/video",
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Option B — Custom upload callbacks
|
|
263
|
+
|
|
264
|
+
Full control over auth headers, form fields, response mapping:
|
|
107
265
|
|
|
108
|
-
|
|
109
|
-
|
|
266
|
+
```ts
|
|
267
|
+
async function uploadImage(params: { data: string | File }) {
|
|
268
|
+
// params.data is File | base64 string depending on Lake path
|
|
269
|
+
const res = await yourUploader(params.data);
|
|
270
|
+
return {
|
|
271
|
+
url: res.publicUrl,
|
|
272
|
+
size: res.bytes,
|
|
273
|
+
filename: res.name,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
When `uploadImage` / `uploadVideo` is provided, it is passed to Lake as `createUploadPromise` and takes precedence for the upload pipeline.
|
|
279
|
+
|
|
280
|
+
### Option C — Nested `upload` object
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
const upload = {
|
|
284
|
+
imageUploadURL: "/api/upload/image",
|
|
285
|
+
imageCrawlURL: "/api/upload/crawl",
|
|
286
|
+
videoUploadURL: "/api/upload/video",
|
|
287
|
+
uploadImage: async ({ data }) => { /* ... */ },
|
|
288
|
+
uploadVideo: async ({ data }) => { /* ... */ },
|
|
289
|
+
};
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Precedence:** flat props (`imageUploadURL`, `uploadImage`, …) override the same keys inside `upload`.
|
|
293
|
+
|
|
294
|
+
| Field | Type | Default | Description |
|
|
295
|
+
| --- | --- | --- | --- |
|
|
296
|
+
| `imageUploadURL` | `string` | `/api/upload/image` | Image upload endpoint |
|
|
297
|
+
| `imageCrawlURL` | `string` | same as `imageUploadURL` | Remote image crawl / proxy |
|
|
298
|
+
| `videoUploadURL` | `string` | `/api/upload/video` | Video upload endpoint |
|
|
299
|
+
| `uploadImage` | `(params) => Promise<UploadResult>` | — | Custom image uploader |
|
|
300
|
+
| `uploadVideo` | `(params) => Promise<UploadResult>` | — | Custom video uploader |
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
interface UploadResult {
|
|
304
|
+
url: string;
|
|
305
|
+
size: number;
|
|
306
|
+
filename: string;
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## API Reference
|
|
313
|
+
|
|
314
|
+
### Props
|
|
315
|
+
|
|
316
|
+
| Prop | Type | Default | Description |
|
|
317
|
+
| --- | --- | --- | --- |
|
|
318
|
+
| `value` | `string` | `''` | Document content (Lake / HTML as accepted by Lake) |
|
|
319
|
+
| `isview` | `boolean` | `false` | `true` → read-only viewer |
|
|
320
|
+
| `isView` | `boolean` | — | React-only alias of `isview` |
|
|
321
|
+
| `placeholder` | `string` | `'输入内容...'` | Empty-editor placeholder |
|
|
322
|
+
| `defaultFontsize` | `number` | `14` | Default font size (px) |
|
|
323
|
+
| `upload` | `UploadConfig` | — | Nested upload config |
|
|
324
|
+
| `imageUploadURL` | `string` | see above | Image upload URL |
|
|
325
|
+
| `imageCrawlURL` | `string` | see above | Image crawl URL |
|
|
326
|
+
| `videoUploadURL` | `string` | see above | Video upload URL |
|
|
327
|
+
| `uploadImage` | `function` | — | Custom image upload |
|
|
328
|
+
| `uploadVideo` | `function` | — | Custom video upload |
|
|
329
|
+
|
|
330
|
+
### Events / Callbacks
|
|
331
|
+
|
|
332
|
+
| Name | Payload | Description |
|
|
333
|
+
| --- | --- | --- |
|
|
334
|
+
| `onChange` | `(value: string)` | Content changed (Lake string with meta) |
|
|
335
|
+
| `onLoad` | `()` | Editor / viewer ready |
|
|
336
|
+
| `onSave` | `()` | `Ctrl+Enter` (Windows/Linux) or `Cmd+Enter` (macOS*) |
|
|
337
|
+
|
|
338
|
+
\*Save chord follows the original extension behavior.
|
|
339
|
+
|
|
340
|
+
Vue also emits `update:value` for optional `v-model:value` usage. Avoid uncontrolled two-way binding loops: if you bind both `:value` and `@onChange` that immediately writes the same source, guard with equality checks.
|
|
341
|
+
|
|
342
|
+
### Imperative API (`ref`)
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
interface IEditorRef {
|
|
346
|
+
appendContent: (html: string, breakLine?: boolean) => void;
|
|
347
|
+
setContent: (content: string, type?: "text/lake" | "text/html") => void;
|
|
348
|
+
getContent: (type: "lake" | "text/html" | "description") => string;
|
|
349
|
+
isEmpty: () => boolean;
|
|
350
|
+
getSummaryContent: () => string;
|
|
351
|
+
wordCount: () => number;
|
|
352
|
+
focusToStart: (offset?: number) => void;
|
|
353
|
+
insertBreakLine: () => void;
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
| Method | Description |
|
|
358
|
+
| --- | --- |
|
|
359
|
+
| `appendContent(html, breakLine?)` | Append HTML; optional leading break |
|
|
360
|
+
| `setContent(content, type?)` | Replace document (`text/html` default) |
|
|
361
|
+
| `getContent(type)` | Read content as Lake / HTML / description |
|
|
362
|
+
| `isEmpty()` | Whether document is empty |
|
|
363
|
+
| `getSummaryContent()` | Summary in Lake format |
|
|
364
|
+
| `wordCount()` | Word count |
|
|
365
|
+
| `focusToStart(offset?)` | Focus start (optional paragraph offset) |
|
|
366
|
+
| `insertBreakLine()` | Insert line break at selection |
|
|
367
|
+
|
|
368
|
+
Call imperative methods **after** `onLoad`.
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Viewer mode
|
|
373
|
+
|
|
374
|
+
### Vue
|
|
375
|
+
|
|
376
|
+
```vue
|
|
377
|
+
<script setup lang="ts">
|
|
378
|
+
import { YuqueRichText, YuqueRichTextView } from "yuque-rich-text";
|
|
110
379
|
</script>
|
|
380
|
+
|
|
381
|
+
<template>
|
|
382
|
+
<!-- Option 1 -->
|
|
383
|
+
<YuqueRichText :isview="true" :value="htmlOrLake" />
|
|
384
|
+
|
|
385
|
+
<!-- Option 2: dedicated viewer component -->
|
|
386
|
+
<YuqueRichTextView :value="htmlOrLake" />
|
|
387
|
+
</template>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### React
|
|
391
|
+
|
|
392
|
+
```tsx
|
|
393
|
+
import { LakeRich, LakeRichView } from "yuque-rich-text/react";
|
|
394
|
+
|
|
395
|
+
<LakeRich isView value={content} />
|
|
396
|
+
// or
|
|
397
|
+
<LakeRichView value={content} />
|
|
111
398
|
```
|
|
112
399
|
|
|
113
|
-
|
|
114
|
-
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Package exports
|
|
403
|
+
|
|
404
|
+
| Subpath | Framework | Primary symbols |
|
|
405
|
+
| --- | --- | --- |
|
|
406
|
+
| `yuque-rich-text` | Vue 3 (default, backward compatible) | `YuqueRichText`, `YuqueRichTextView` |
|
|
407
|
+
| `yuque-rich-text/vue` | Vue 3 | same as default |
|
|
408
|
+
| `yuque-rich-text/react` | React | `LakeRich`, `LakeRichView` |
|
|
409
|
+
|
|
410
|
+
Shared types (`IEditorRef`, `UploadConfig`, …) are re-exported from each entry.
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Development
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
# install
|
|
418
|
+
pnpm install # or npm install
|
|
419
|
+
|
|
420
|
+
# local demo
|
|
421
|
+
pnpm dev
|
|
422
|
+
|
|
423
|
+
# library build → ./lib
|
|
424
|
+
pnpm build:lib
|
|
425
|
+
|
|
426
|
+
# static demo build
|
|
427
|
+
pnpm build:demo
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Project layout
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
src/
|
|
434
|
+
core/ # Framework-agnostic Lake mount + types
|
|
435
|
+
vue/ # Vue 3 adapter
|
|
436
|
+
react/ # React adapter
|
|
437
|
+
components/ # Legacy paths (re-exports)
|
|
438
|
+
demo/ # Vite playground
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Disclaimer
|
|
444
|
+
|
|
445
|
+
This is an **unofficial third-party** integration for [Yuque](https://www.yuque.com). It is **not** affiliated with, maintained by, or endorsed by Yuque.
|
|
446
|
+
|
|
447
|
+
- **Use at your own risk.** Authors are not responsible for ToS violations or damages.
|
|
448
|
+
- Do **not** use if Yuque prohibits this usage in your context.
|
|
449
|
+
- This project does not redistribute Yuque’s proprietary application source; it loads publicly referenced Lake runtime assets in the same manner as the open-source browser extension.
|
|
450
|
+
|
|
451
|
+
Please review [Yuque’s Terms of Service](https://www.yuque.com) before production use.
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## License
|
|
456
|
+
|
|
457
|
+
MIT
|
|
458
|
+
|
|
459
|
+
---
|
|
115
460
|
|
|
116
|
-
|
|
117
|
-
- **Do not use** if `[www.yuque.com]` prohibits third-party modifications.
|
|
118
|
-
- This project does not redistribute any copyrighted materials from `[www.yuque.com]`.
|
|
461
|
+
## Credits
|
|
119
462
|
|
|
120
|
-
|
|
463
|
+
- [yuque-chrome-extension](https://github.com/yuque/yuque-chrome-extension) — original editor integration
|
|
464
|
+
- Lake / lakex-doc runtime — Alipay / Yuque ecosystem
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
declare class IEditorPlugin {
|
|
2
|
+
editor: any;
|
|
3
|
+
init(): void;
|
|
4
|
+
}
|
|
5
|
+
declare class IKernelPlugin {
|
|
6
|
+
kernel: any;
|
|
7
|
+
init(kernel: any): void;
|
|
8
|
+
}
|
|
9
|
+
declare class IRendererPlugin {
|
|
10
|
+
editor: any;
|
|
11
|
+
init(): void;
|
|
12
|
+
}
|
|
13
|
+
declare class CommandClass {
|
|
14
|
+
static UNAVAILABLE: string;
|
|
15
|
+
static EXECUTED: string;
|
|
16
|
+
static NOT_EXECUTED: string;
|
|
17
|
+
static UNKNOWN: string;
|
|
18
|
+
readonly kernel: any;
|
|
19
|
+
readonly editing: any;
|
|
20
|
+
destroy(): void;
|
|
21
|
+
execute(editing: any, ...args: any[]): any;
|
|
22
|
+
getValue(job: any, ...args: any[]): any;
|
|
23
|
+
}
|
|
24
|
+
export type IEditorPluginCls = new () => IEditorPlugin;
|
|
25
|
+
export type IKernelPluginCls = (new () => IKernelPlugin) & {
|
|
26
|
+
PluginName: string;
|
|
27
|
+
};
|
|
28
|
+
export type IRendererPluginCls = new () => IRendererPlugin;
|
|
29
|
+
export type ICommandCls = new () => CommandClass;
|
|
30
|
+
export declare function InjectEditorPlugin({ EditorPlugin, KernelPlugin, PositionUtil, OpenEditorFactory, toolbarItems, Command, SelectionUtil }: {
|
|
31
|
+
EditorPlugin: IEditorPluginCls;
|
|
32
|
+
KernelPlugin: IKernelPluginCls;
|
|
33
|
+
Plugins: Record<string, any>;
|
|
34
|
+
Command: ICommandCls;
|
|
35
|
+
SelectionUtil: any;
|
|
36
|
+
PositionUtil: any;
|
|
37
|
+
OpenEditorFactory: {
|
|
38
|
+
editorPlugins: IEditorPluginCls[];
|
|
39
|
+
kernelPlugins: IKernelPluginCls[];
|
|
40
|
+
registerEditorPlugin: (plugins: IEditorPluginCls[]) => void;
|
|
41
|
+
registerRenderPlugin: (plugins: IRendererPluginCls[]) => void;
|
|
42
|
+
registerKernelPlugin: (plugins: IKernelPluginCls[]) => void;
|
|
43
|
+
};
|
|
44
|
+
toolbarItems: Record<string, string>;
|
|
45
|
+
}, doc: Document): void;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const slash: {
|
|
2
|
+
cardSelect: {
|
|
3
|
+
general: {
|
|
4
|
+
groups: ({
|
|
5
|
+
type: string;
|
|
6
|
+
show: string;
|
|
7
|
+
items: string[];
|
|
8
|
+
readonly title?: string;
|
|
9
|
+
name?: undefined;
|
|
10
|
+
} | {
|
|
11
|
+
readonly title: string;
|
|
12
|
+
name: string;
|
|
13
|
+
type: string;
|
|
14
|
+
items: (string | {
|
|
15
|
+
name: string;
|
|
16
|
+
allowSelector: boolean;
|
|
17
|
+
})[];
|
|
18
|
+
show?: undefined;
|
|
19
|
+
} | {
|
|
20
|
+
readonly title: string;
|
|
21
|
+
name: string;
|
|
22
|
+
type: string;
|
|
23
|
+
items: (string | {
|
|
24
|
+
name: string;
|
|
25
|
+
childMenus: string[];
|
|
26
|
+
})[];
|
|
27
|
+
show?: undefined;
|
|
28
|
+
})[];
|
|
29
|
+
};
|
|
30
|
+
table: {
|
|
31
|
+
groups: ({
|
|
32
|
+
type: string;
|
|
33
|
+
show: string;
|
|
34
|
+
items: string[];
|
|
35
|
+
readonly title?: string;
|
|
36
|
+
name?: undefined;
|
|
37
|
+
} | {
|
|
38
|
+
readonly title: string;
|
|
39
|
+
name: string;
|
|
40
|
+
type: string;
|
|
41
|
+
items: string[];
|
|
42
|
+
show?: undefined;
|
|
43
|
+
})[];
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iframe的内容
|
|
3
|
+
*/
|
|
4
|
+
export declare const templateHtml = "\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <title></title>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"https://gw.alipayobjects.com/render/p/yuyan_npm/@alipay_lakex-doc/1.71.0/umd/doc.css\"/>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"https://gw.alipayobjects.com/os/lib/antd/4.24.13/dist/antd.css\"/>\n <style>\n body {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n -webkit-font-smoothing: antialiased;\n }\n .toolbar-container {\n display: none;\n }\n #toolbar {\n flex: 1;\n }\n #root {\n flex: 1;\n overflow: hidden;\n }\n #child {\n display: flex;\n align-items: center;\n padding: 0 16px;\n }\n .ne-layout-mode-fixed .ne-engine, .ne-layout-mode-adapt .ne-engine {\n padding-top: 16px;\n }\n .ne-layout-mode-fixed .ne-editor-body, .ne-layout-mode-adapt .ne-editor-body {\n height: 100%;\n }\n .ne-ui-overlay-button {\n width: 28px !important;\n height: 28px !important;\n padding: 0 !important;;\n border: none !important;;\n }\n ::selection {\n color: #fff !important;\n background: #1677ff !important;\n }\n .continue-button:hover, .continue-button:focus {\n color: #00B96B;\n border-color: #00B96B;\n }\n .ne-layout-mode-fixed .ne-editor-wrap {\n padding: 16px 16px 0;\n height: 100%;\n }\n .ne-layout-mode-fixed .ne-engine, .ne-layout-mode-adapt .ne-engine {\n padding: 16px 24px 0;\n min-height: calc(100vh - 10px)\n }\n .ne-layout-mode-fixed .ne-editor-wrap-content {\n min-width: 317px;\n }\n .ne-layout-mode-fixed .ne-editor-outer-wrap-box {\n min-width: 317px;\n }\n .ne-layout-mode-fixed .ne-editor-outer-wrap-box, .ne-layout-mode-adapt .ne-editor-outer-wrap-box,\n .ne-layout-mode-fixed .ne-editor-wrap-content, .ne-layout-mode-adapt .ne-editor-wrap-content {\n min-width: 317px;\n }\n .ne-editor-wrap {\n overscroll-behavior: contain;\n }\n </style>\n</head>\n<body>\n <div class=\"toolbar-container\">\n <div id=\"toolbar\"></div>\n <div id=\"child\"></div>\n </div>\n <div id=\"root\"></div>\n<script crossorigin src=\"https://unpkg.com/react@18.2.0/umd/react.production.min.js\"></script>\n<script crossorigin src=\"https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js\"></script>\n<script src=\"https://gw.alipayobjects.com/render/p/yuyan_v/180020010000005484/7.1.4/CodeMirror.js\"></script>\n<script src=\"https://ur.alipay.com/tracert_a385.js\"></script>\n<script src=\"https://mdn.alipayobjects.com/design_kitchencore/afts/file/ANSZQ7GHQPMAAAAAAAAAAAAADhulAQBr\"></script>\n<script src=\"https://gw.alipayobjects.com/render/p/yuyan_npm/@alipay_lakex-doc/1.71.0/umd/doc.umd.js\"></script>\n</body>\n</html>\n";
|