@lark-apaas/coding-template-nestjs-react-fullstack 0.1.35 → 0.1.36-alpha.20260901035538

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.
Files changed (33) hide show
  1. package/package.json +1 -1
  2. package/template/client/src/components/ui/markdown.tsx +43 -0
  3. package/template/client/src/index.css +0 -1
  4. package/template/nest-cli.json +10 -9
  5. package/template/package-lock.json +10339 -18979
  6. package/template/package.json +7 -70
  7. package/template/scripts/build.sh +12 -0
  8. package/template/vite.config.ts +1 -1
  9. package/template/client/src/components/business-ui/tiptap-editor/README.md +0 -324
  10. package/template/client/src/components/business-ui/tiptap-editor/components/attachment-toolbar-button.tsx +0 -74
  11. package/template/client/src/components/business-ui/tiptap-editor/components/blockquote-toolbar-button.tsx +0 -41
  12. package/template/client/src/components/business-ui/tiptap-editor/components/code-block-toolbar-button.tsx +0 -41
  13. package/template/client/src/components/business-ui/tiptap-editor/components/color-highlight-toolbar-button.tsx +0 -141
  14. package/template/client/src/components/business-ui/tiptap-editor/components/heading-toolbar-button.tsx +0 -99
  15. package/template/client/src/components/business-ui/tiptap-editor/components/horizontal-rule-toolbar-button.tsx +0 -39
  16. package/template/client/src/components/business-ui/tiptap-editor/components/image-upload-toolbar-button.tsx +0 -65
  17. package/template/client/src/components/business-ui/tiptap-editor/components/link-edit-form.tsx +0 -127
  18. package/template/client/src/components/business-ui/tiptap-editor/components/link-hover-toolbar.tsx +0 -380
  19. package/template/client/src/components/business-ui/tiptap-editor/components/link-toolbar-button.tsx +0 -96
  20. package/template/client/src/components/business-ui/tiptap-editor/components/list-toolbar-button.tsx +0 -75
  21. package/template/client/src/components/business-ui/tiptap-editor/components/mark-toolbar-button.tsx +0 -118
  22. package/template/client/src/components/business-ui/tiptap-editor/components/text-align-toolbar-button.tsx +0 -84
  23. package/template/client/src/components/business-ui/tiptap-editor/components/undo-redo-toolbar-button.tsx +0 -54
  24. package/template/client/src/components/business-ui/tiptap-editor/extensions/attachment.tsx +0 -539
  25. package/template/client/src/components/business-ui/tiptap-editor/extensions/code-block-shiki.tsx +0 -236
  26. package/template/client/src/components/business-ui/tiptap-editor/extensions/complete-kit.ts +0 -260
  27. package/template/client/src/components/business-ui/tiptap-editor/extensions/image.tsx +0 -456
  28. package/template/client/src/components/business-ui/tiptap-editor/hooks/use-tiptap-editor.ts +0 -47
  29. package/template/client/src/components/business-ui/tiptap-editor/index.ts +0 -38
  30. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor-complete.tsx +0 -112
  31. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor.tsx +0 -169
  32. package/template/client/src/components/ui/streamdown.tsx +0 -186
  33. package/template/client/src/lib/shiki.ts +0 -92
@@ -5,7 +5,6 @@
5
5
  "flags": {
6
6
  "supportBusinessUser": true,
7
7
  "supportScrollReveal": true,
8
- "supportTiptapAndStreamdown": true,
9
8
  "customToaster": true
10
9
  },
11
10
  "scripts": {
@@ -19,24 +18,20 @@
19
18
  "build:server": "NODE_ENV=production nest build",
20
19
  "build:client": "NODE_ENV=production vite build --config vite.config.ts",
21
20
  "start": "NODE_ENV=production node main.js",
22
- "test": "jest",
23
- "test:watch": "jest --watch",
24
- "test:e2e": "jest --config test/e2e/jest.config.js",
25
21
  "eslint": "eslint . --quiet",
26
22
  "stylelint": "stylelint client/src/**/*.css --quiet",
27
23
  "type:check": "concurrently -n \"server,client\" -c \"blue,green\" \"npm run type:check:server\" \"npm run type:check:client\"",
28
24
  "type:check:client": "tsc --noEmit --project tsconfig.app.json",
29
25
  "type:check:server": "tsc --noEmit --project tsconfig.node.json",
30
26
  "lint": "node ./scripts/lint.js",
31
- "format": "prettier --write \"{server,client,test}/**/*.{js,ts,tsx,json,md}\"",
32
27
  "precommit": "node scripts/hooks/run-precommit.js",
33
28
  "upgrade": "npx -y @lark-apaas/fullstack-cli sync --disable-gen-openapi"
34
29
  },
35
30
  "dependencies": {
36
31
  "@lark-apaas/fullstack-nestjs-core": "^1.1.60",
37
32
  "@nestjs/axios": "^4.0.1",
38
- "@nestjs/common": "^10.4.20",
39
33
  "@nestjs/cache-manager": "^3.1.3",
34
+ "@nestjs/common": "^10.4.20",
40
35
  "@nestjs/config": "^3.3.0",
41
36
  "@nestjs/core": "^10.4.20",
42
37
  "@nestjs/platform-express": "^10.4.20",
@@ -56,17 +51,11 @@
56
51
  "tslib": "^2.8.1"
57
52
  },
58
53
  "devDependencies": {
59
- "@dnd-kit/core": "^6.3.1",
60
- "@dnd-kit/modifiers": "^9.0.0",
61
- "@dnd-kit/sortable": "^10.0.0",
62
- "@headlessui/react": "^2.2.9",
63
- "@heroicons/react": "^2.2.0",
64
54
  "@hookform/resolvers": "^5.2.2",
65
- "@lark-apaas/client-toolkit": "^1.2.69",
55
+ "@lark-apaas/client-toolkit": "^1.2.67",
56
+ "@lark-apaas/coding-preset-vite-react": "1.0.20-alpha.20260827110844",
66
57
  "@lark-apaas/fullstack-presets": "^1.1.0",
67
- "@lark-apaas/fullstack-vite-preset": "^1.0.37",
68
58
  "@nestjs/cli": "^10.4.9",
69
- "@nestjs/testing": "^10.4.20",
70
59
  "@radix-ui/react-accordion": "^1.2.12",
71
60
  "@radix-ui/react-alert-dialog": "^1.1.15",
72
61
  "@radix-ui/react-aspect-ratio": "^1.1.7",
@@ -93,33 +82,11 @@
93
82
  "@radix-ui/react-toggle": "^1.1.10",
94
83
  "@radix-ui/react-toggle-group": "^1.1.11",
95
84
  "@radix-ui/react-tooltip": "^1.2.8",
96
- "@reduxjs/toolkit": "^2.9.0",
97
- "@shikijs/langs": "^3.21.0",
98
- "@shikijs/themes": "^3.21.0",
99
- "@swc/cli": "^0.5.2",
100
- "@swc/core": "^1.16.1",
101
85
  "@tailwindcss/postcss": "^4.1.13",
102
86
  "@tailwindcss/typography": "^0.5.19",
103
- "@tiptap/core": "^3.14.0",
104
- "@tiptap/extension-code-block": "^3.15.3",
105
- "@tiptap/extension-code-block-lowlight": "^3.14.0",
106
- "@tiptap/extension-color": "^3.14.0",
107
- "@tiptap/extension-highlight": "^3.14.0",
108
- "@tiptap/extension-image": "^3.14.0",
109
- "@tiptap/extension-link": "^3.14.0",
110
- "@tiptap/extension-placeholder": "^3.14.0",
111
- "@tiptap/extension-task-item": "^3.15.3",
112
- "@tiptap/extension-task-list": "^3.15.3",
113
- "@tiptap/extension-text-align": "^3.14.0",
114
- "@tiptap/extension-text-style": "^3.14.0",
115
- "@tiptap/extension-underline": "^3.14.0",
116
- "@tiptap/pm": "^3.15.3",
117
- "@tiptap/react": "^3.14.0",
118
- "@tiptap/starter-kit": "^3.14.0",
119
87
  "@types/crypto-js": "^4.2.2",
120
88
  "@types/express": "^5.0.3",
121
89
  "@types/hbs": "^4.0.5",
122
- "@types/jest": "^29.5.14",
123
90
  "@types/node": "^22.18.6",
124
91
  "@types/react": "^19.2.0",
125
92
  "@types/react-dom": "^19.2.0",
@@ -127,79 +94,49 @@
127
94
  "class-variance-authority": "^0.7.1",
128
95
  "clsx": "~2.0.1",
129
96
  "cmdk": "^1.1.1",
130
- "cobe": "^0.6.5",
131
97
  "concurrently": "^9.2.1",
132
- "css-loader": "^7.1.2",
133
98
  "date-fns": "^4.1.0",
134
99
  "echarts": "~5.6.0",
135
100
  "echarts-for-react": "~3.0.2",
136
101
  "embla-carousel-react": "^8.6.0",
137
102
  "es-toolkit": "^1.43.0",
138
103
  "eslint": "^9.35.0",
139
- "eslint-config-prettier": "^9.1.2",
140
- "form-data": "^4.0.4",
141
104
  "framer-motion": "~11.13.5",
142
105
  "gsap": "^3.13.0",
143
- "html2canvas": "^1.4.1",
144
106
  "input-otp": "^1.4.2",
145
- "jest": "^29.7.0",
146
- "js-base64": "^3.7.8",
147
- "jspdf": "^3.0.3",
148
107
  "lodash": "^4.17.21",
149
108
  "lucide-react": "^0.554.0",
150
109
  "nanoid": "^5.1.6",
151
110
  "next-themes": "^0.4.6",
152
- "pinyin-pro": "^3.27.0",
153
111
  "postcss": "^8.5.6",
154
112
  "postcss-import": "^16.1.1",
155
- "postcss-loader": "^8.2.0",
156
- "prettier": "^3.6.2",
157
- "qrcode.react": "^4.2.0",
158
113
  "radix-ui": "^1.4.3",
159
114
  "react": "^19.2.0",
160
- "react-countup": "^6.5.3",
161
115
  "react-day-picker": "^9.11.1",
162
116
  "react-dom": "^19.2.0",
163
- "react-dropzone": "^14.3.5",
164
117
  "react-error-boundary": "^6.0.0",
165
118
  "react-hook-form": "^7.65.0",
166
- "react-redux": "^9.2.0",
167
- "react-refresh": "^0.17.0",
119
+ "react-markdown": "^10.1.0",
168
120
  "react-resizable-panels": "^3.0.6",
169
121
  "react-router-dom": "^6.30.1",
170
122
  "recharts": "^2.15.4",
123
+ "remark-gfm": "^4.0.1",
171
124
  "rxjs": "^7.8.2",
172
- "shiki": "^3.21.0",
173
125
  "sonner": "^2.0.7",
174
- "streamdown": "^1.6.10",
175
- "style-loader": "^4.0.0",
176
126
  "stylelint": "^16.11.0",
177
127
  "tailwind-merge": "~3.4.0",
178
128
  "tailwindcss": "^4.1.13",
179
- "ts-jest": "^29.4.3",
180
- "ts-node": "^10.9.2",
181
129
  "tw-animate-css": "^1.3.8",
182
130
  "typescript": "^5.9.2",
183
131
  "typescript-eslint": "^8.44.0",
184
132
  "uuid": "11.0.5",
185
133
  "vaul": "^1.1.2",
186
- "vite": "^7.3.1",
187
- "xlsx": "^0.18.5",
188
- "xlsx-js-style": "^1.2.0",
189
- "zod": "^3.25.76",
190
- "zustand": "~5.0.8"
134
+ "vite": "^8",
135
+ "zod": "^3.25.76"
191
136
  },
192
137
  "overrides": {
193
138
  "dayjs": "^1.11.15"
194
139
  },
195
- "jest": {
196
- "preset": "ts-jest",
197
- "testEnvironment": "node",
198
- "testMatch": [
199
- "<rootDir>/server/**/*.spec.ts",
200
- "<rootDir>/test/unit/**/*.spec.ts"
201
- ]
202
- },
203
140
  "engines": {
204
141
  "node": ">=22.0.0",
205
142
  "npm": ">=10.0.0"
@@ -94,6 +94,18 @@ cp "$ROOT_DIR/dist/client/"*.html "$OUT_DIR/dist/client/" || true
94
94
  # 拷贝 run.sh 文件
95
95
  cp "$ROOT_DIR/scripts/run.sh" "$OUT_DIR/"
96
96
 
97
+ # 拷贝 shared/static → OUT_DIR/shared/static
98
+ # Nest server 生产从 process.cwd() + shared/static 读 (nestjs-core static.controller.ts:100),
99
+ # 生产 cwd = $OUT_DIR (dist/server), 所以目标是 dist/server/shared/static。
100
+ # coding-preset 的 static-assets plugin 之前 closeBundle 拷贝, 现在删除后由 build.sh 显式做。
101
+ # 排除 .ts/.tsx/.js/.jsx: 避免 shared/static/ 里若有开发脚本被误传到生产。
102
+ if [ -d "$ROOT_DIR/shared/static" ]; then
103
+ mkdir -p "$OUT_DIR/shared/static"
104
+ rsync -a --exclude='*.ts' --exclude='*.tsx' --exclude='*.js' --exclude='*.jsx' \
105
+ "$ROOT_DIR/shared/static/" "$OUT_DIR/shared/static/"
106
+ echo " Static → dist/server/shared/static/"
107
+ fi
108
+
97
109
  # 清理无用文件
98
110
  rm -rf "$ROOT_DIR/dist/scripts"
99
111
  rm -rf "$ROOT_DIR/dist/tsconfig.node.tsbuildinfo"
@@ -1,5 +1,5 @@
1
1
  import path from 'path';
2
- import { defineConfig } from '@lark-apaas/fullstack-vite-preset';
2
+ import { defineConfig } from '@lark-apaas/coding-preset-vite-react';
3
3
 
4
4
  export default defineConfig({
5
5
  resolve: {
@@ -1,324 +0,0 @@
1
- # Tiptap Editor
2
-
3
- > 基于 [Tiptap](https://tiptap.dev) 构建的富文本编辑器组件,提供开箱即用和可组合两种使用模式。
4
-
5
- ## 快速开始
6
-
7
- ### 推荐:使用 `TiptapEditorComplete`
8
-
9
- 对于大多数场景,直接使用 `TiptapEditorComplete` 即可。它封装了完整的工具栏和样式,开箱即用。
10
-
11
- ```tsx
12
- import { TiptapEditorComplete } from '@/components/business-ui/tiptap-editor';
13
-
14
- function MyEditor() {
15
- const [value, setValue] = React.useState('');
16
-
17
- return (
18
- <TiptapEditorComplete
19
- value={value}
20
- onValueChange={setValue}
21
- placeholder="请输入内容..."
22
- />
23
- );
24
- }
25
- ```
26
-
27
- #### `TiptapEditorComplete` Props
28
-
29
- | Prop | 类型 | 默认值 | 描述 |
30
- | :-------------- | :------------------------ | :----- | :----------------------- |
31
- | `value` | `string` | - | 受控值 (HTML 字符串) |
32
- | `defaultValue` | `string` | - | 非受控默认值 |
33
- | `onValueChange` | `(value: string) => void` | - | 内容变更回调 |
34
- | `placeholder` | `string` | - | 占位符文本 |
35
- | `readOnly` | `boolean` | false | 只读模式 |
36
- | `aria-disabled` | `boolean` | - | 禁用状态 |
37
- | `aria-invalid` | `boolean` | - | 无效状态(用于表单校验) |
38
- | `className` | `string` | - | 自定义样式类 |
39
-
40
- #### ⚠️ 样式注意事项
41
-
42
- **组件已包含完整的边框、内边距、Hover 边框、聚焦环等等丰富且完整的视觉样式,请勿在外层添加额外包装!**
43
-
44
- ```tsx
45
- // ❌ 错误:不要用 div 包裹并添加 border、padding、overflow-auto 等样式
46
- // 例如这段代码会导致展示多层 border,编辑器聚焦时聚焦环被截断等问题
47
- <div className="border rounded-lg overflow-auto">
48
- <TiptapEditorComplete ... />
49
- </div>
50
-
51
- // ✅ 正确:直接使用,只建议添加 w-full、h-auto 等尺寸类
52
- // 完整版组件默认具有 max-h-140 min-h-80,必要时可覆盖。
53
- <TiptapEditorComplete className="w-full max-h-100" ... />
54
- ```
55
-
56
- ---
57
-
58
- ## 组合式 API(高级用法)
59
-
60
- > ⚠️ **警告**:必须优先使用 `TiptapEditorComplete`!仅当用户**明确要求自定义工具栏**或**添加特殊功能**时,才使用组合式 API。绝大多数场景下,`TiptapEditorComplete` 已能满足需求。
61
-
62
- 当需要 **自定义工具栏布局** 或 **添加特殊功能** 时,使用组合式 API。
63
-
64
- ```tsx
65
- import {
66
- TiptapEditor,
67
- TiptapEditorContent,
68
- TiptapEditorToolbar,
69
- TiptapEditorToolbarSeparator,
70
- UndoRedoToolbarButton,
71
- MarkToolbarButton,
72
- HeadingToolbarButton,
73
- ListToolbarButton,
74
- TextAlignToolbarButton,
75
- ColorHighlightToolbarButton,
76
- BlockquoteToolbarButton,
77
- HorizontalRuleToolbarButton,
78
- CodeBlockToolbarButton,
79
- LinkToolbarButton,
80
- ImageUploadToolbarButton,
81
- AttachmentToolbarButton,
82
- CompleteKit,
83
- } from '@/components/business-ui/tiptap-editor';
84
-
85
- function CustomEditor() {
86
- // placeholder 需要通过 CompleteKit 配置
87
- const extensions = [CompleteKit.configure({ placeholder: { placeholder: '请输入内容...' } })];
88
-
89
- return (
90
- <TiptapEditor extensions={extensions}>
91
- <CustomToolbar />
92
- <TiptapEditorContent />
93
- </TiptapEditor>
94
- );
95
- }
96
-
97
- /** 自定义工具栏:可根据需求自由组合按钮 */
98
- function CustomToolbar() {
99
- return (
100
- <TiptapEditorToolbar>
101
- {/* 撤销/重做 */}
102
- <UndoRedoToolbarButton action="undo" />
103
- <UndoRedoToolbarButton action="redo" />
104
- <TiptapEditorToolbarSeparator />
105
-
106
- {/* 标题/正文 */}
107
- <HeadingToolbarButton />
108
- <TiptapEditorToolbarSeparator />
109
-
110
- {/* 文本对齐 & 列表 */}
111
- <TextAlignToolbarButton />
112
- <ListToolbarButton />
113
- <TiptapEditorToolbarSeparator />
114
-
115
- {/* 文本格式化: 加粗、删除线、斜体、下划线、颜色 */}
116
- <MarkToolbarButton format="bold" />
117
- <MarkToolbarButton format="strike" />
118
- <MarkToolbarButton format="italic" />
119
- <MarkToolbarButton format="underline" />
120
- <ColorHighlightToolbarButton />
121
- <TiptapEditorToolbarSeparator />
122
-
123
- {/* 块级元素: 引用、分割线、代码块 */}
124
- <BlockquoteToolbarButton />
125
- <HorizontalRuleToolbarButton />
126
- <CodeBlockToolbarButton />
127
-
128
- {/* 链接 & 媒体: 图片、附件 */}
129
- <LinkToolbarButton />
130
- <ImageUploadToolbarButton />
131
- <AttachmentToolbarButton />
132
- </TiptapEditorToolbar>
133
- );
134
- }
135
- ```
136
-
137
- ### 核心组件
138
-
139
- | 组件 | 描述 |
140
- | :----------------------------- | :--------------------------------------- |
141
- | `TiptapEditor` | 根组件,提供 Editor Context 和编辑器实例 |
142
- | `TiptapEditorToolbar` | 工具栏容器,用于放置工具栏按钮 |
143
- | `TiptapEditorContent` | 编辑区域,渲染可编辑内容 |
144
- | `TiptapEditorToolbarSeparator` | 工具栏分隔符,用于视觉分组 |
145
-
146
- ### `TiptapEditor` Props
147
-
148
- | Prop | 类型 | 默认值 | 描述 |
149
- | :-------------- | :------------------------ | :-------------- | :--------------------- |
150
- | `value` | `string` | - | 受控值 (HTML) |
151
- | `defaultValue` | `string` | - | 非受控默认值 |
152
- | `onValueChange` | `(value: string) => void` | - | 内容变更回调 |
153
- | `extensions` | `Extensions` | `[CompleteKit]` | 自定义 Tiptap 扩展 |
154
- | `readOnly` | `boolean` | `false` | 只读模式 |
155
- | `autoFocus` | `boolean` | - | 自动聚焦 |
156
- | `asChild` | `boolean` | `false` | 使用子元素作为渲染容器 |
157
-
158
- ---
159
-
160
- ## 工具栏按钮参考
161
-
162
- 所有工具栏按钮均已在 `index.ts` 导出,可直接组合使用:
163
-
164
- | 组件 | 功能 | Props / 说明 |
165
- | :---------------------------- | :------------- | :---------------------------------------------------------------- |
166
- | `UndoRedoToolbarButton` | 撤销 / 重做 | `action: "undo" \| "redo"` |
167
- | `HeadingToolbarButton` | 标题级别选择 | 下拉菜单,支持正文和 H1-H6 |
168
- | `MarkToolbarButton` | 文本格式 | `format: "bold" \| "italic" \| "underline" \| "strike" \| "code"` |
169
- | `ListToolbarButton` | 列表 | 下拉菜单,支持无序、有序、任务列表 |
170
- | `TextAlignToolbarButton` | 文本对齐 | 下拉菜单,支持左、中、右、两端对齐 |
171
- | `ColorHighlightToolbarButton` | 文字颜色与高亮 | 颜色选择器 |
172
- | `LinkToolbarButton` | 超链接 | 点击弹出链接编辑表单 |
173
- | `BlockquoteToolbarButton` | 引用块 | 切换引用样式 |
174
- | `CodeBlockToolbarButton` | 代码块 | 插入代码块(Shiki 语法高亮) |
175
- | `HorizontalRuleToolbarButton` | 水平分割线 | 插入分割线 |
176
- | `ImageUploadToolbarButton` | 图片上传 | 需配置 `image.upload` 函数 |
177
- | `AttachmentToolbarButton` | 附件上传 | 需配置 `attachment.upload` 函数,支持 `accept` prop |
178
-
179
- ---
180
-
181
- ## 扩展 (Extensions)
182
-
183
- ### CompleteKit
184
-
185
- `CompleteKit` 是预配置的扩展集合,包含所有常用编辑功能。它是 `TiptapEditor` 的默认扩展。
186
-
187
- ```tsx
188
- import { TiptapEditor, TiptapEditorContent, CompleteKit } from '@/components/business-ui/tiptap-editor';
189
-
190
- // 使用默认配置
191
- <TiptapEditor extensions={[CompleteKit]}>
192
- <TiptapEditorContent />
193
- </TiptapEditor>
194
-
195
- // 自定义配置
196
- <TiptapEditor
197
- extensions={[
198
- CompleteKit.configure({
199
- // 禁用图片扩展
200
- image: false,
201
- // 自定义 placeholder
202
- placeholder: { placeholder: '开始输入...' },
203
- // 限制标题级别
204
- heading: { levels: [1, 2, 3] },
205
- // 禁用链接
206
- link: false,
207
- }),
208
- ]}
209
- >
210
- <TiptapEditorContent />
211
- </TiptapEditor>
212
- ```
213
-
214
- #### CompleteKit 包含的扩展
215
-
216
- | 类别 | 扩展 |
217
- | :------- | :---------------------------------------------------------- |
218
- | **基础** | Document, Paragraph, Text, HardBreak, Dropcursor, Gapcursor |
219
- | **历史** | Undo, Redo |
220
- | **标题** | Heading (H1-H6) |
221
- | **文本** | Bold, Italic, Underline, Strike, Code |
222
- | **颜色** | TextStyle, Color, Highlight |
223
- | **对齐** | TextAlign |
224
- | **列表** | BulletList, OrderedList, ListItem, TaskList, TaskItem |
225
- | **块级** | Blockquote, HorizontalRule, CodeBlock (Shiki) |
226
- | **链接** | Link (autolink, openOnClick) |
227
- | **媒体** | Image, Attachment |
228
- | **其他** | Placeholder |
229
-
230
- #### CompleteKit 配置选项
231
-
232
- ```typescript
233
- interface CompleteKitOptions {
234
- // 设置为 false 可禁用对应扩展
235
- heading?: { levels: number[] } | false;
236
- link?: { autolink?: boolean; openOnClick?: boolean } | false;
237
- codeBlock?: false;
238
- taskList?: Partial<TaskListOptions> | false;
239
- taskItem?: { nested?: boolean } | false;
240
- textStyle?: Partial<TextStyleOptions> | false;
241
- color?: Partial<ColorOptions> | false;
242
- highlight?: { multicolor?: boolean } | false;
243
- textAlign?: { types: string[] } | false;
244
- image?: Partial<ImageOptions> | false;
245
- attachment?: Partial<AttachmentOptions> | false;
246
- placeholder?: { placeholder?: string } | false;
247
- // ... 以及所有 StarterKit 选项
248
- }
249
- ```
250
-
251
- ---
252
-
253
- ## 目录结构
254
-
255
- ```text
256
- tiptap-editor/
257
- ├── index.ts # 统一导出入口
258
- ├── tiptap-editor.tsx # 核心组件 (TiptapEditor, TiptapEditorToolbar, TiptapEditorContent)
259
- ├── tiptap-editor-complete.tsx # 完整封装版 (TiptapEditorComplete)
260
- ├── components/ # 富文本依赖的组件,其中工具栏按钮以 toolbar-button 结尾
261
- ├── extensions/ # 自定义 Tiptap 扩展
262
- │ ├── ...
263
- │ └── complete-kit.ts # 完整扩展集合
264
- └── hooks/
265
- └── use-tiptap-editor.ts # 编辑器实例 Hook
266
- ```
267
-
268
- ---
269
-
270
- ## 开发指南
271
-
272
- ### 添加新的工具栏按钮
273
-
274
- 1. 在 `components/` 目录创建新组件:
275
-
276
- ```tsx
277
- // components/business-ui/tiptap-editor/components/my-toolbar-button.tsx
278
- 'use client';
279
-
280
- import { useTiptapEditor } from '@/components/business-ui/tiptap-editor/hooks/use-tiptap-editor';
281
- import { Button } from '@/components/ui/button';
282
-
283
- export function MyToolbarButton() {
284
- const { editor } = useTiptapEditor();
285
-
286
- if (!editor) return null;
287
-
288
- return (
289
- <Button
290
- variant="ghost"
291
- size="sm"
292
- className="size-6 px-0"
293
- disabled={!editor.can().myCommand()}
294
- onClick={() => editor.chain().focus().myCommand().run()}
295
- >
296
- {/* Icon */}
297
- </Button>
298
- );
299
- }
300
- ```
301
-
302
- 2. 在 `index.ts` 中导出:
303
-
304
- ```tsx
305
- export { MyToolbarButton } from '@/components/business-ui/tiptap-editor/components/my-toolbar-button';
306
- ```
307
-
308
- ### 添加自定义扩展
309
-
310
- 1. 在 `extensions/` 目录创建扩展文件
311
- 2. 如需集成到 CompleteKit,修改 `extensions/complete-kit.ts`
312
- 3. 通过 `extensions` prop 传入:
313
-
314
- ```tsx
315
- import { MyExtension } from './extensions/my-extension';
316
-
317
- <TiptapEditor extensions={[CompleteKit, MyExtension]} />;
318
- ```
319
-
320
- ---
321
-
322
- ## 相关资源
323
-
324
- - [Tiptap LLM 文档](https://tiptap.dev/llms.txt)
@@ -1,74 +0,0 @@
1
- 'use client';
2
-
3
- import * as React from 'react';
4
- import { Paperclip } from 'lucide-react';
5
- import { toast } from 'sonner';
6
-
7
- import { useTiptapEditor } from '@/components/business-ui/tiptap-editor/hooks/use-tiptap-editor';
8
- import { Button } from '@/components/ui/button';
9
- import {
10
- Tooltip,
11
- TooltipContent,
12
- TooltipProvider,
13
- TooltipTrigger,
14
- } from '@/components/ui/tooltip';
15
-
16
- interface AttachmentToolbarButtonProps {
17
- accept?: string;
18
- }
19
-
20
- export function AttachmentToolbarButton({
21
- accept,
22
- }: AttachmentToolbarButtonProps) {
23
- const { editor } = useTiptapEditor();
24
- const fileInputRef = React.useRef<HTMLInputElement>(null);
25
-
26
- if (!editor) return null;
27
-
28
- const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
29
- const files = e.target.files;
30
- if (!files || files.length === 0) return;
31
-
32
- const ok = editor
33
- .chain()
34
- .focus()
35
- .insertAttachments(Array.from(files))
36
- .run();
37
- if (!ok) {
38
- toast.error('插入附件失败(请确认 attachment 扩展已启用且提供 upload)');
39
- }
40
-
41
- if (fileInputRef.current) {
42
- fileInputRef.current.value = '';
43
- }
44
- };
45
-
46
- return (
47
- <TooltipProvider>
48
- <Tooltip delayDuration={700}>
49
- <TooltipTrigger asChild>
50
- <Button
51
- variant="ghost"
52
- size="sm"
53
- className="size-6 px-0"
54
- onClick={() => fileInputRef.current?.click()}
55
- >
56
- <Paperclip className="size-4" />
57
- <span className="sr-only">上传附件</span>
58
- </Button>
59
- </TooltipTrigger>
60
- <TooltipContent>
61
- <p>上传附件</p>
62
- </TooltipContent>
63
- </Tooltip>
64
- <input
65
- type="file"
66
- multiple
67
- accept={accept}
68
- ref={fileInputRef}
69
- className="hidden"
70
- onChange={handleFileChange}
71
- />
72
- </TooltipProvider>
73
- );
74
- }
@@ -1,41 +0,0 @@
1
- 'use client';
2
-
3
- import { Quote } from 'lucide-react';
4
-
5
- import { useTiptapEditor } from '@/components/business-ui/tiptap-editor/hooks/use-tiptap-editor';
6
- import { Toggle } from '@/components/ui/toggle';
7
- import {
8
- Tooltip,
9
- TooltipContent,
10
- TooltipProvider,
11
- TooltipTrigger,
12
- } from '@/components/ui/tooltip';
13
-
14
- export function BlockquoteToolbarButton() {
15
- const { editor } = useTiptapEditor();
16
- if (!editor) return null;
17
-
18
- return (
19
- <TooltipProvider>
20
- <Tooltip delayDuration={700}>
21
- <TooltipTrigger asChild>
22
- <Toggle
23
- size="sm"
24
- pressed={editor.isActive('blockquote')}
25
- onPressedChange={() =>
26
- editor.chain().focus().toggleBlockquote().run()
27
- }
28
- disabled={!editor.can().toggleBlockquote()}
29
- aria-label="引用"
30
- className="size-6 p-0"
31
- >
32
- <Quote className="size-4" />
33
- </Toggle>
34
- </TooltipTrigger>
35
- <TooltipContent>
36
- <p>引用(⌘+Shift+&gt;)</p>
37
- </TooltipContent>
38
- </Tooltip>
39
- </TooltipProvider>
40
- );
41
- }
@@ -1,41 +0,0 @@
1
- 'use client';
2
-
3
- import { CodeXml } from 'lucide-react';
4
-
5
- import { useTiptapEditor } from '@/components/business-ui/tiptap-editor/hooks/use-tiptap-editor';
6
- import { Toggle } from '@/components/ui/toggle';
7
- import {
8
- Tooltip,
9
- TooltipContent,
10
- TooltipProvider,
11
- TooltipTrigger,
12
- } from '@/components/ui/tooltip';
13
-
14
- export function CodeBlockToolbarButton() {
15
- const { editor } = useTiptapEditor();
16
- if (!editor) return null;
17
-
18
- return (
19
- <TooltipProvider>
20
- <Tooltip delayDuration={700}>
21
- <TooltipTrigger asChild>
22
- <Toggle
23
- size="sm"
24
- pressed={editor.isActive('codeBlock')}
25
- onPressedChange={() =>
26
- editor.chain().focus().toggleCodeBlock().run()
27
- }
28
- disabled={!editor.can().toggleCodeBlock()}
29
- aria-label="代码块"
30
- className="size-6 p-0"
31
- >
32
- <CodeXml className="size-4" />
33
- </Toggle>
34
- </TooltipTrigger>
35
- <TooltipContent>
36
- <p>代码块</p>
37
- </TooltipContent>
38
- </Tooltip>
39
- </TooltipProvider>
40
- );
41
- }