@ctzhian/tiptap 2.13.6 → 2.13.7-beta.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.
@@ -6,5 +6,5 @@ type InsertImageProps = {
6
6
  attrs: ImageAttributes;
7
7
  updateAttributes: (attrs: ImageAttributes) => void;
8
8
  } & EditorFnProps;
9
- declare const InsertImage: ({ selected, attrs, updateAttributes, onUpload, onError, onValidateUrl }: InsertImageProps) => React.JSX.Element;
9
+ declare const InsertImage: ({ selected, attrs, updateAttributes, onUpload, onUploadImgUrl, onError, onValidateUrl }: InsertImageProps) => React.JSX.Element;
10
10
  export default InsertImage;
@@ -24,6 +24,7 @@ var InsertImage = function InsertImage(_ref) {
24
24
  attrs = _ref.attrs,
25
25
  updateAttributes = _ref.updateAttributes,
26
26
  onUpload = _ref.onUpload,
27
+ onUploadImgUrl = _ref.onUploadImgUrl,
27
28
  onError = _ref.onError,
28
29
  onValidateUrl = _ref.onValidateUrl;
29
30
  var _useState = useState(attrs.src || ''),
@@ -155,7 +156,7 @@ var InsertImage = function InsertImage(_ref) {
155
156
  }();
156
157
  var handleInsertLink = /*#__PURE__*/function () {
157
158
  var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
158
- var validatedUrl;
159
+ var finalUrl, abortController;
159
160
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
160
161
  while (1) switch (_context3.prev = _context3.next) {
161
162
  case 0:
@@ -166,31 +167,54 @@ var InsertImage = function InsertImage(_ref) {
166
167
  return _context3.abrupt("return");
167
168
  case 2:
168
169
  _context3.prev = 2;
169
- validatedUrl = editSrc.trim();
170
+ finalUrl = editSrc.trim();
170
171
  if (!onValidateUrl) {
171
172
  _context3.next = 8;
172
173
  break;
173
174
  }
174
175
  _context3.next = 7;
175
- return Promise.resolve(onValidateUrl(validatedUrl, 'image'));
176
+ return Promise.resolve(onValidateUrl(finalUrl, 'image'));
176
177
  case 7:
177
- validatedUrl = _context3.sent;
178
+ finalUrl = _context3.sent;
178
179
  case 8:
179
- _context3.next = 10;
180
- return updateImageAttributes(validatedUrl, editTitle.trim());
181
- case 10:
180
+ if (!(onUploadImgUrl && (finalUrl.startsWith('http://') || finalUrl.startsWith('https://') || finalUrl.startsWith('//')))) {
181
+ _context3.next = 23;
182
+ break;
183
+ }
184
+ setUploading(true);
182
185
  handleClosePopover();
183
- _context3.next = 16;
186
+ _context3.prev = 11;
187
+ abortController = new AbortController();
188
+ _context3.next = 15;
189
+ return onUploadImgUrl(finalUrl, abortController.signal);
190
+ case 15:
191
+ finalUrl = _context3.sent;
192
+ _context3.next = 18;
193
+ return updateImageAttributes(finalUrl, editTitle.trim());
194
+ case 18:
195
+ _context3.prev = 18;
196
+ setUploading(false);
197
+ return _context3.finish(18);
198
+ case 21:
199
+ _context3.next = 26;
184
200
  break;
185
- case 13:
186
- _context3.prev = 13;
201
+ case 23:
202
+ _context3.next = 25;
203
+ return updateImageAttributes(finalUrl, editTitle.trim());
204
+ case 25:
205
+ handleClosePopover();
206
+ case 26:
207
+ _context3.next = 31;
208
+ break;
209
+ case 28:
210
+ _context3.prev = 28;
187
211
  _context3.t0 = _context3["catch"](2);
188
212
  onError === null || onError === void 0 || onError(_context3.t0);
189
- case 16:
213
+ case 31:
190
214
  case "end":
191
215
  return _context3.stop();
192
216
  }
193
- }, _callee3, null, [[2, 13]]);
217
+ }, _callee3, null, [[2, 28], [11,, 18, 21]]);
194
218
  }));
195
219
  return function handleInsertLink() {
196
220
  return _ref4.apply(this, arguments);
@@ -77,6 +77,7 @@ var ImageViewWrapper = function ImageViewWrapper(_ref) {
77
77
  deleteNode = _ref.deleteNode,
78
78
  selected = _ref.selected,
79
79
  onUpload = _ref.onUpload,
80
+ onUploadImgUrl = _ref.onUploadImgUrl,
80
81
  _onError = _ref.onError,
81
82
  onValidateUrl = _ref.onValidateUrl,
82
83
  getPos = _ref.getPos;
@@ -304,6 +305,7 @@ var ImageViewWrapper = function ImageViewWrapper(_ref) {
304
305
  selected: selected,
305
306
  attrs: attrs,
306
307
  onUpload: onUpload,
308
+ onUploadImgUrl: onUploadImgUrl,
307
309
  updateAttributes: updateAttributes,
308
310
  onError: _onError,
309
311
  onValidateUrl: onValidateUrl
@@ -7,6 +7,6 @@ export interface UploadProgressAttributes {
7
7
  tempId: string;
8
8
  }
9
9
  export declare const getFileIcon: (fileType: string) => React.JSX.Element;
10
- export declare const getFileTypeText: (fileType: string) => "图片" | "视频" | "音频" | "文件";
10
+ export declare const getFileTypeText: (fileType: string) => "音频" | "图片" | "视频" | "文件";
11
11
  declare const UploadProgressView: React.FC<NodeViewProps>;
12
12
  export default UploadProgressView;
@@ -1,2 +1,2 @@
1
1
  import { GetExtensionsProps } from '../type';
2
- export declare const getExtensions: ({ limit, exclude, extensions: extensionsProps, editable, mentionItems, baseUrl, onMentionFilter, onUpload, onError, onTocUpdate, onAiWritingGetSuggestion, onValidateUrl, placeholder, mermaidOptions, youtubeOptions, tableOfContentsOptions, }: GetExtensionsProps) => any;
2
+ export declare const getExtensions: ({ limit, exclude, extensions: extensionsProps, editable, mentionItems, baseUrl, onMentionFilter, onUpload, onUploadImgUrl, onError, onTocUpdate, onAiWritingGetSuggestion, onValidateUrl, placeholder, mermaidOptions, youtubeOptions, tableOfContentsOptions, }: GetExtensionsProps) => any;
@@ -26,6 +26,7 @@ export var getExtensions = function getExtensions(_ref) {
26
26
  baseUrl = _ref$baseUrl === void 0 ? '' : _ref$baseUrl,
27
27
  onMentionFilter = _ref.onMentionFilter,
28
28
  onUpload = _ref.onUpload,
29
+ onUploadImgUrl = _ref.onUploadImgUrl,
29
30
  onError = _ref.onError,
30
31
  onTocUpdate = _ref.onTocUpdate,
31
32
  onAiWritingGetSuggestion = _ref.onAiWritingGetSuggestion,
@@ -76,6 +77,7 @@ export var getExtensions = function getExtensions(_ref) {
76
77
  }), ImageExtension({
77
78
  baseUrl: baseUrl,
78
79
  onUpload: onUpload,
80
+ onUploadImgUrl: onUploadImgUrl,
79
81
  onError: onError,
80
82
  onValidateUrl: onValidateUrl
81
83
  }), InlineAttachmentExtension({
@@ -190,6 +190,7 @@ var customImage = function customImage(props) {
190
190
  return ReactNodeViewRenderer(function (renderProps) {
191
191
  return ImageViewWrapper(_objectSpread(_objectSpread({}, renderProps), {}, {
192
192
  onUpload: props.onUpload,
193
+ onUploadImgUrl: props.onUploadImgUrl,
193
194
  onError: props.onError,
194
195
  onValidateUrl: props.onValidateUrl
195
196
  }));
@@ -202,7 +203,7 @@ var customImage = function customImage(props) {
202
203
  props: {
203
204
  handlePaste: function handlePaste(view, event) {
204
205
  var _event$clipboardData, _event$clipboardData2, _event$clipboardData3;
205
- if (!props.onUpload) return false;
206
+ if (!props.onUpload && !props.onUploadImgUrl) return false;
206
207
  var items = Array.from(((_event$clipboardData = event.clipboardData) === null || _event$clipboardData === void 0 ? void 0 : _event$clipboardData.items) || []);
207
208
  var imageFiles = items.map(function (item) {
208
209
  return item.getAsFile();
@@ -434,49 +435,99 @@ var customImage = function customImage(props) {
434
435
  }
435
436
  var imageUrls = extractImageUrls(htmlData);
436
437
 
437
- // 如果找到图片 URL,下载并处理图片
438
+ // 如果找到图片 URL,优先通过 onUploadImgUrl 走后端上传,否则前端下载再上传
438
439
  if (imageUrls.length > 0) {
439
440
  event.preventDefault();
440
441
  _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
441
- var downloadedFiles, validFiles, _parsed$content2, extensions, parsed;
442
+ var abortController, newUrls, modifiedHtml, _parsed$content2, extensions, parsed, _props$onError, _props$onError2, _parsed$content3, _extensions, _parsed, downloadedFiles, validFiles, _parsed2$content, _extensions2, _parsed2;
442
443
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
443
444
  while (1) switch (_context3.prev = _context3.next) {
444
445
  case 0:
445
- _context3.next = 2;
446
+ if (!props.onUploadImgUrl) {
447
+ _context3.next = 17;
448
+ break;
449
+ }
450
+ abortController = new AbortController();
451
+ _context3.prev = 2;
452
+ _context3.next = 5;
453
+ return Promise.all(imageUrls.map(function (url) {
454
+ return props.onUploadImgUrl(url, abortController.signal);
455
+ }));
456
+ case 5:
457
+ newUrls = _context3.sent;
458
+ modifiedHtml = htmlData;
459
+ imageUrls.forEach(function (oldUrl, i) {
460
+ modifiedHtml = modifiedHtml.split(oldUrl).join(newUrls[i]);
461
+ });
462
+ try {
463
+ extensions = editor.extensionManager.extensions;
464
+ parsed = generateJSON(modifiedHtml, extensions);
465
+ if (parsed !== null && parsed !== void 0 && (_parsed$content2 = parsed.content) !== null && _parsed$content2 !== void 0 && _parsed$content2.length) {
466
+ editor.chain().insertContentAt({
467
+ from: from,
468
+ to: to
469
+ }, parsed).focus().run();
470
+ }
471
+ } catch (parseError) {
472
+ console.error('[图片粘贴] 解析上传后的 HTML 失败:', parseError);
473
+ (_props$onError = props.onError) === null || _props$onError === void 0 || _props$onError.call(props, parseError);
474
+ }
475
+ _context3.next = 16;
476
+ break;
477
+ case 11:
478
+ _context3.prev = 11;
479
+ _context3.t0 = _context3["catch"](2);
480
+ console.error('[图片粘贴] URL 上传失败:', _context3.t0);
481
+ (_props$onError2 = props.onError) === null || _props$onError2 === void 0 || _props$onError2.call(props, _context3.t0);
482
+ try {
483
+ _extensions = editor.extensionManager.extensions;
484
+ _parsed = generateJSON(htmlData, _extensions);
485
+ if (_parsed !== null && _parsed !== void 0 && (_parsed$content3 = _parsed.content) !== null && _parsed$content3 !== void 0 && _parsed$content3.length) {
486
+ editor.chain().insertContentAt({
487
+ from: from,
488
+ to: to
489
+ }, _parsed).focus().run();
490
+ }
491
+ } catch (fallbackError) {
492
+ // 解析失败则不再处理
493
+ }
494
+ case 16:
495
+ return _context3.abrupt("return");
496
+ case 17:
497
+ _context3.next = 19;
446
498
  return Promise.all(imageUrls.map(function (url, i) {
447
499
  return downloadImageAsFile(url, i);
448
500
  }));
449
- case 2:
501
+ case 19:
450
502
  downloadedFiles = _context3.sent;
451
503
  validFiles = downloadedFiles.filter(function (f) {
452
504
  return f !== null;
453
505
  });
454
506
  if (!(validFiles.length === 0)) {
455
- _context3.next = 7;
507
+ _context3.next = 24;
456
508
  break;
457
509
  }
458
- // 下载失败:回退为直接粘贴原始 HTML(保留原始图片 URL)
459
510
  try {
460
- extensions = editor.extensionManager.extensions;
461
- parsed = generateJSON(htmlData, extensions);
462
- if (parsed !== null && parsed !== void 0 && (_parsed$content2 = parsed.content) !== null && _parsed$content2 !== void 0 && _parsed$content2.length) {
511
+ _extensions2 = editor.extensionManager.extensions;
512
+ _parsed2 = generateJSON(htmlData, _extensions2);
513
+ if (_parsed2 !== null && _parsed2 !== void 0 && (_parsed2$content = _parsed2.content) !== null && _parsed2$content !== void 0 && _parsed2$content.length) {
463
514
  editor.chain().insertContentAt({
464
515
  from: from,
465
516
  to: to
466
- }, parsed).focus().run();
517
+ }, _parsed2).focus().run();
467
518
  }
468
- } catch (error) {
469
- // 解析失败则不再处理,交给默认粘贴逻辑
519
+ } catch (err) {
520
+ // 解析失败则不再处理
470
521
  }
471
522
  return _context3.abrupt("return");
472
- case 7:
473
- _context3.next = 9;
523
+ case 24:
524
+ _context3.next = 26;
474
525
  return processImagePaste(validFiles, htmlData, from, to);
475
- case 9:
526
+ case 26:
476
527
  case "end":
477
528
  return _context3.stop();
478
529
  }
479
- }, _callee3);
530
+ }, _callee3, null, [[2, 11]]);
480
531
  }))();
481
532
  return true;
482
533
  }
@@ -484,12 +535,12 @@ var customImage = function customImage(props) {
484
535
  // 没有图片 URL,尝试处理 HTML 或纯文本
485
536
  // 先尝试使用 generateJSON 解析 HTML
486
537
  try {
487
- var _parsed$content3;
538
+ var _parsed$content4;
488
539
  var extensions = editor.extensionManager.extensions;
489
540
  var parsed = generateJSON(htmlData, extensions);
490
541
 
491
542
  // 检查解析后的内容是否包含有效文本
492
- if (parsed !== null && parsed !== void 0 && (_parsed$content3 = parsed.content) !== null && _parsed$content3 !== void 0 && _parsed$content3.length && hasValidTextContent(parsed.content)) {
543
+ if (parsed !== null && parsed !== void 0 && (_parsed$content4 = parsed.content) !== null && _parsed$content4 !== void 0 && _parsed$content4.length && hasValidTextContent(parsed.content)) {
493
544
  event.preventDefault();
494
545
  editor.chain().insertContentAt({
495
546
  from: from,
@@ -509,7 +560,9 @@ var customImage = function customImage(props) {
509
560
  return false; // 让默认粘贴处理
510
561
  }
511
562
 
512
- // 有图片文件,直接处理
563
+ // 有图片文件,直接处理(需要 onUpload)
564
+ if (imageFiles.length > 0 && !props.onUpload) return false;
565
+
513
566
  // 如果包含表格,让默认粘贴处理
514
567
  if (htmlData !== null && htmlData !== void 0 && htmlData.trim()) {
515
568
  var htmlLower = htmlData.toLowerCase();
@@ -1,4 +1,4 @@
1
1
  import { UseTiptapProps, UseTiptapReturn } from "../type";
2
2
  import { UseEditorOptions } from '@tiptap/react';
3
- declare const useTiptap: ({ editable, contentType, onSave, onError, onUpload, baseUrl, ...options }: UseTiptapProps & UseEditorOptions) => UseTiptapReturn;
3
+ declare const useTiptap: ({ editable, contentType, onSave, onError, onUpload, onUploadImgUrl, baseUrl, ...options }: UseTiptapProps & UseEditorOptions) => UseTiptapReturn;
4
4
  export default useTiptap;
@@ -1,5 +1,5 @@
1
1
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
- var _excluded = ["editable", "contentType", "onSave", "onError", "onUpload", "baseUrl"];
2
+ var _excluded = ["editable", "contentType", "onSave", "onError", "onUpload", "onUploadImgUrl", "baseUrl"];
3
3
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
4
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
5
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -19,6 +19,7 @@ var useTiptap = function useTiptap(_ref) {
19
19
  onSave = _ref.onSave,
20
20
  onError = _ref.onError,
21
21
  onUpload = _ref.onUpload,
22
+ onUploadImgUrl = _ref.onUploadImgUrl,
22
23
  _ref$baseUrl = _ref.baseUrl,
23
24
  baseUrl = _ref$baseUrl === void 0 ? '' : _ref$baseUrl,
24
25
  options = _objectWithoutProperties(_ref, _excluded);
@@ -27,11 +28,17 @@ var useTiptap = function useTiptap(_ref) {
27
28
  return withBaseUrl(url, baseUrl);
28
29
  });
29
30
  } : undefined;
31
+ var handleUploadImgUrl = onUploadImgUrl ? function (url, abortSignal) {
32
+ return onUploadImgUrl(url, abortSignal).then(function (newUrl) {
33
+ return withBaseUrl(newUrl, baseUrl);
34
+ });
35
+ } : undefined;
30
36
  var extensions = getExtensions(_objectSpread({
31
37
  editable: editable,
32
38
  onError: onError,
33
39
  baseUrl: baseUrl,
34
- onUpload: handleUpload
40
+ onUpload: handleUpload,
41
+ onUploadImgUrl: handleUploadImgUrl
35
42
  }, options));
36
43
  var editor = useEditor(_objectSpread(_objectSpread(_objectSpread({
37
44
  editable: editable,
@@ -55,6 +55,8 @@ export type ToolbarItemType = {
55
55
  export type UploadFunction = (file: File, onProgress?: (event: {
56
56
  progress: number;
57
57
  }) => void, abortSignal?: AbortSignal) => Promise<string>;
58
+ /** 图片 URL 上传:将图片链接交由后端处理并返回新 URL,可随时通过 abortSignal 中止请求 */
59
+ export type UploadImgUrlFunction = (url: string, abortSignal?: AbortSignal) => Promise<string>;
58
60
  export type TipType = 'error' | 'success' | 'info' | 'warning';
59
61
  export type OnTipFunction = (type: TipType, tip: string) => void;
60
62
  export type EditorProps = {
@@ -85,6 +87,10 @@ export type EditorFnProps = {
85
87
  * 上传处理
86
88
  */
87
89
  onUpload?: UploadFunction;
90
+ /**
91
+ * 图片链接上传:当为图片 URL 时,直接交由后端处理而非前端下载再上传
92
+ */
93
+ onUploadImgUrl?: UploadImgUrlFunction;
88
94
  /**
89
95
  * 目录更新
90
96
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctzhian/tiptap",
3
- "version": "2.13.6",
3
+ "version": "2.13.7-beta.0",
4
4
  "description": "基于 Tiptap 二次开发的编辑器组件",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",