@ctzhian/tiptap 2.13.6 → 2.13.7

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();
@@ -427,56 +428,108 @@ var customImage = function customImage(props) {
427
428
  };
428
429
  }();
429
430
 
430
- // 如果没有图片文件,尝试从 HTML 中提取图片 URL 或处理文本粘贴
431
+ // 如果没有图片文件,尝试从 HTML 中提取图片 URL
431
432
  if (imageFiles.length === 0) {
432
433
  if (!htmlData) {
433
434
  return false; // 没有 HTML 数据,让默认粘贴处理
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
+ if (htmlData) {
484
+ _extensions = editor.extensionManager.extensions;
485
+ _parsed = generateJSON(htmlData, _extensions);
486
+ if (_parsed !== null && _parsed !== void 0 && (_parsed$content3 = _parsed.content) !== null && _parsed$content3 !== void 0 && _parsed$content3.length) {
487
+ editor.chain().insertContentAt({
488
+ from: from,
489
+ to: to
490
+ }, _parsed).focus().run();
491
+ }
492
+ }
493
+ } catch (fallbackError) {
494
+ // 解析失败则不再处理
495
+ }
496
+ case 16:
497
+ return _context3.abrupt("return");
498
+ case 17:
499
+ _context3.next = 19;
446
500
  return Promise.all(imageUrls.map(function (url, i) {
447
501
  return downloadImageAsFile(url, i);
448
502
  }));
449
- case 2:
503
+ case 19:
450
504
  downloadedFiles = _context3.sent;
451
505
  validFiles = downloadedFiles.filter(function (f) {
452
506
  return f !== null;
453
507
  });
454
508
  if (!(validFiles.length === 0)) {
455
- _context3.next = 7;
509
+ _context3.next = 24;
456
510
  break;
457
511
  }
458
- // 下载失败:回退为直接粘贴原始 HTML(保留原始图片 URL)
459
512
  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) {
513
+ _extensions2 = editor.extensionManager.extensions;
514
+ _parsed2 = generateJSON(htmlData, _extensions2);
515
+ if (_parsed2 !== null && _parsed2 !== void 0 && (_parsed2$content = _parsed2.content) !== null && _parsed2$content !== void 0 && _parsed2$content.length) {
463
516
  editor.chain().insertContentAt({
464
517
  from: from,
465
518
  to: to
466
- }, parsed).focus().run();
519
+ }, _parsed2).focus().run();
467
520
  }
468
- } catch (error) {
469
- // 解析失败则不再处理,交给默认粘贴逻辑
521
+ } catch (err) {
522
+ // 解析失败则不再处理
470
523
  }
471
524
  return _context3.abrupt("return");
472
- case 7:
473
- _context3.next = 9;
525
+ case 24:
526
+ _context3.next = 26;
474
527
  return processImagePaste(validFiles, htmlData, from, to);
475
- case 9:
528
+ case 26:
476
529
  case "end":
477
530
  return _context3.stop();
478
531
  }
479
- }, _callee3);
532
+ }, _callee3, null, [[2, 11]]);
480
533
  }))();
481
534
  return true;
482
535
  }
@@ -484,18 +537,20 @@ var customImage = function customImage(props) {
484
537
  // 没有图片 URL,尝试处理 HTML 或纯文本
485
538
  // 先尝试使用 generateJSON 解析 HTML
486
539
  try {
487
- var _parsed$content3;
488
- var extensions = editor.extensionManager.extensions;
489
- var parsed = generateJSON(htmlData, extensions);
540
+ if (htmlData) {
541
+ var _parsed$content4;
542
+ var extensions = editor.extensionManager.extensions;
543
+ var parsed = generateJSON(htmlData, extensions);
490
544
 
491
- // 检查解析后的内容是否包含有效文本
492
- if (parsed !== null && parsed !== void 0 && (_parsed$content3 = parsed.content) !== null && _parsed$content3 !== void 0 && _parsed$content3.length && hasValidTextContent(parsed.content)) {
493
- event.preventDefault();
494
- editor.chain().insertContentAt({
495
- from: from,
496
- to: to
497
- }, parsed).focus().run();
498
- return true;
545
+ // 检查解析后的内容是否包含有效文本
546
+ if (parsed !== null && parsed !== void 0 && (_parsed$content4 = parsed.content) !== null && _parsed$content4 !== void 0 && _parsed$content4.length && hasValidTextContent(parsed.content)) {
547
+ event.preventDefault();
548
+ editor.chain().insertContentAt({
549
+ from: from,
550
+ to: to
551
+ }, parsed).focus().run();
552
+ return true;
553
+ }
499
554
  }
500
555
  } catch (error) {
501
556
  // HTML 解析失败,继续尝试纯文本
@@ -509,7 +564,9 @@ var customImage = function customImage(props) {
509
564
  return false; // 让默认粘贴处理
510
565
  }
511
566
 
512
- // 有图片文件,直接处理
567
+ // 有图片文件,直接处理(需要 onUpload)
568
+ if (imageFiles.length > 0 && !props.onUpload) return false;
569
+
513
570
  // 如果包含表格,让默认粘贴处理
514
571
  if (htmlData !== null && htmlData !== void 0 && htmlData.trim()) {
515
572
  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",
4
4
  "description": "基于 Tiptap 二次开发的编辑器组件",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",