@lark-apaas/coding-template-nestjs-react-fullstack 0.1.36 → 0.1.37-alpha.20260908071834

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 (32) 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 +1 -1
  5. package/template/package-lock.json +11497 -19348
  6. package/template/package.json +19 -76
  7. package/template/vite.config.ts +1 -1
  8. package/template/client/src/components/business-ui/tiptap-editor/README.md +0 -324
  9. package/template/client/src/components/business-ui/tiptap-editor/components/attachment-toolbar-button.tsx +0 -74
  10. package/template/client/src/components/business-ui/tiptap-editor/components/blockquote-toolbar-button.tsx +0 -41
  11. package/template/client/src/components/business-ui/tiptap-editor/components/code-block-toolbar-button.tsx +0 -41
  12. package/template/client/src/components/business-ui/tiptap-editor/components/color-highlight-toolbar-button.tsx +0 -141
  13. package/template/client/src/components/business-ui/tiptap-editor/components/heading-toolbar-button.tsx +0 -99
  14. package/template/client/src/components/business-ui/tiptap-editor/components/horizontal-rule-toolbar-button.tsx +0 -39
  15. package/template/client/src/components/business-ui/tiptap-editor/components/image-upload-toolbar-button.tsx +0 -65
  16. package/template/client/src/components/business-ui/tiptap-editor/components/link-edit-form.tsx +0 -127
  17. package/template/client/src/components/business-ui/tiptap-editor/components/link-hover-toolbar.tsx +0 -380
  18. package/template/client/src/components/business-ui/tiptap-editor/components/link-toolbar-button.tsx +0 -96
  19. package/template/client/src/components/business-ui/tiptap-editor/components/list-toolbar-button.tsx +0 -75
  20. package/template/client/src/components/business-ui/tiptap-editor/components/mark-toolbar-button.tsx +0 -118
  21. package/template/client/src/components/business-ui/tiptap-editor/components/text-align-toolbar-button.tsx +0 -84
  22. package/template/client/src/components/business-ui/tiptap-editor/components/undo-redo-toolbar-button.tsx +0 -54
  23. package/template/client/src/components/business-ui/tiptap-editor/extensions/attachment.tsx +0 -539
  24. package/template/client/src/components/business-ui/tiptap-editor/extensions/code-block-shiki.tsx +0 -236
  25. package/template/client/src/components/business-ui/tiptap-editor/extensions/complete-kit.ts +0 -260
  26. package/template/client/src/components/business-ui/tiptap-editor/extensions/image.tsx +0 -456
  27. package/template/client/src/components/business-ui/tiptap-editor/hooks/use-tiptap-editor.ts +0 -47
  28. package/template/client/src/components/business-ui/tiptap-editor/index.ts +0 -38
  29. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor-complete.tsx +0 -112
  30. package/template/client/src/components/business-ui/tiptap-editor/tiptap-editor.tsx +0 -169
  31. package/template/client/src/components/ui/streamdown.tsx +0 -186
  32. 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
55
  "@lark-apaas/client-toolkit": "^1.2.69",
56
+ "@lark-apaas/coding-preset-vite-react": "1.0.22-alpha.20260903092505",
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,112 +82,66 @@
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
85
  "@swc/cli": "^0.5.2",
100
86
  "@swc/core": "^1.16.1",
101
87
  "@tailwindcss/postcss": "^4.1.13",
102
88
  "@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
89
  "@types/crypto-js": "^4.2.2",
120
90
  "@types/express": "^5.0.3",
121
91
  "@types/hbs": "^4.0.5",
122
- "@types/jest": "^29.5.14",
123
92
  "@types/node": "^22.18.6",
124
93
  "@types/react": "^19.2.0",
125
94
  "@types/react-dom": "^19.2.0",
126
95
  "autoprefixer": "^10.4.21",
127
96
  "class-variance-authority": "^0.7.1",
128
- "clsx": "~2.0.1",
97
+ "clsx": "^2.1.1",
129
98
  "cmdk": "^1.1.1",
130
- "cobe": "^0.6.5",
131
99
  "concurrently": "^9.2.1",
132
- "css-loader": "^7.1.2",
133
100
  "date-fns": "^4.1.0",
134
- "echarts": "~5.6.0",
101
+ "echarts": "^6.1.0",
135
102
  "echarts-for-react": "~3.0.2",
136
103
  "embla-carousel-react": "^8.6.0",
137
- "es-toolkit": "^1.43.0",
138
104
  "eslint": "^9.35.0",
139
- "eslint-config-prettier": "^9.1.2",
140
- "form-data": "^4.0.4",
141
- "framer-motion": "~11.13.5",
105
+ "framer-motion": "^12.38.0",
142
106
  "gsap": "^3.13.0",
143
- "html2canvas": "^1.4.1",
144
107
  "input-otp": "^1.4.2",
145
- "jest": "^29.7.0",
146
- "js-base64": "^3.7.8",
147
- "jspdf": "^3.0.3",
148
108
  "lodash": "^4.17.21",
149
- "lucide-react": "^0.554.0",
109
+ "lucide-react": "0.577.0",
150
110
  "nanoid": "^5.1.6",
151
111
  "next-themes": "^0.4.6",
152
- "pinyin-pro": "^3.27.0",
153
112
  "postcss": "^8.5.6",
154
113
  "postcss-import": "^16.1.1",
155
- "postcss-loader": "^8.2.0",
156
- "prettier": "^3.6.2",
157
- "qrcode.react": "^4.2.0",
158
114
  "radix-ui": "^1.4.3",
159
115
  "react": "^19.2.0",
160
- "react-countup": "^6.5.3",
161
116
  "react-day-picker": "^9.11.1",
162
117
  "react-dom": "^19.2.0",
163
- "react-dropzone": "^14.3.5",
164
118
  "react-error-boundary": "^6.0.0",
165
119
  "react-hook-form": "^7.65.0",
166
- "react-redux": "^9.2.0",
167
- "react-refresh": "^0.17.0",
120
+ "react-markdown": "^10.1.0",
168
121
  "react-resizable-panels": "^3.0.6",
169
- "react-router-dom": "^6.30.1",
122
+ "react-router-dom": "^7.13.2",
170
123
  "recharts": "^2.15.4",
124
+ "remark-gfm": "^4.0.1",
171
125
  "rxjs": "^7.8.2",
172
- "shiki": "^3.21.0",
173
126
  "sonner": "^2.0.7",
174
- "streamdown": "^1.6.10",
175
- "style-loader": "^4.0.0",
176
- "stylelint": "^16.11.0",
177
- "tailwind-merge": "~3.4.0",
127
+ "stylelint": "^17.3.0",
128
+ "tailwind-merge": "^3.5.0",
178
129
  "tailwindcss": "^4.1.13",
179
- "ts-jest": "^29.4.3",
180
- "ts-node": "^10.9.2",
181
130
  "tw-animate-css": "^1.3.8",
182
131
  "typescript": "^5.9.2",
183
132
  "typescript-eslint": "^8.44.0",
184
133
  "uuid": "11.0.5",
185
134
  "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"
135
+ "vite": "^8",
136
+ "zod": "^4.3.6"
191
137
  },
192
138
  "overrides": {
193
- "dayjs": "^1.11.15"
194
- },
195
- "jest": {
196
- "preset": "ts-jest",
197
- "testEnvironment": "node",
198
- "testMatch": [
199
- "<rootDir>/server/**/*.spec.ts",
200
- "<rootDir>/test/unit/**/*.spec.ts"
201
- ]
139
+ "dayjs": "^1.11.15",
140
+ "clsx": "^2.1.1",
141
+ "tailwind-merge": "^3.5.0",
142
+ "zod": "^4.3.6",
143
+ "echarts": "^6.1.0",
144
+ "stylelint": "^17.3.0"
202
145
  },
203
146
  "engines": {
204
147
  "node": ">=22.0.0",
@@ -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
- }