remons-markdown-editor 1.0.0-beta.3 → 1.0.0-beta.4

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/dist/index.cjs CHANGED
@@ -35,18 +35,20 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/MarkdownEditor.tsx
38
- var import_react8 = require("react");
38
+ var import_react10 = require("react");
39
+ var import_antd6 = require("antd");
39
40
 
40
41
  // src/components/Editor/index.tsx
41
42
  var import_react = require("react");
42
43
  var import_codemirror = require("codemirror");
43
- var import_state2 = require("@codemirror/state");
44
+ var import_state3 = require("@codemirror/state");
44
45
 
45
46
  // src/components/Editor/codemirrorExtensions.ts
46
47
  var import_view2 = require("@codemirror/view");
47
48
  var import_commands = require("@codemirror/commands");
48
49
  var import_lang_markdown = require("@codemirror/lang-markdown");
49
50
  var import_theme_one_dark = require("@codemirror/theme-one-dark");
51
+ var import_state2 = require("@codemirror/state");
50
52
 
51
53
  // src/components/Editor/containerWidget.ts
52
54
  var import_view = require("@codemirror/view");
@@ -124,8 +126,6 @@ function findContainers(docText) {
124
126
  }
125
127
 
126
128
  // src/registry/componentRegistry.tsx
127
- var import_antd = require("antd");
128
- var import_jsx_runtime = require("react/jsx-runtime");
129
129
  var ComponentRegistry = class {
130
130
  constructor() {
131
131
  this.schemas = /* @__PURE__ */ new Map();
@@ -168,80 +168,6 @@ var ComponentRegistry = class {
168
168
  }
169
169
  };
170
170
  var componentRegistry = new ComponentRegistry();
171
- componentRegistry.register("linkCard", {
172
- name: "linkCard",
173
- label: "\u94FE\u63A5\u5361\u7247",
174
- fields: [
175
- { key: "title", defaultValue: "\u94FE\u63A5\u6807\u9898" },
176
- { key: "link", defaultValue: "https://example.com" },
177
- { key: "description", defaultValue: "\u94FE\u63A5\u63CF\u8FF0" },
178
- { key: "theme", defaultValue: "default" }
179
- ],
180
- renderDialog: (props, onChange) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
181
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
182
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u6807\u9898" }),
183
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input, { value: props.title, onChange: (e) => onChange("title", e.target.value) })
184
- ] }),
185
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
186
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u94FE\u63A5" }),
187
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input, { value: props.link, onChange: (e) => onChange("link", e.target.value) })
188
- ] }),
189
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
190
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u63CF\u8FF0" }),
191
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input.TextArea, { rows: 3, value: props.description, onChange: (e) => onChange("description", e.target.value) })
192
- ] }),
193
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
194
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u4E3B\u9898" }),
195
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
196
- import_antd.Select,
197
- {
198
- value: props.theme,
199
- onChange: (v) => onChange("theme", v),
200
- options: [
201
- { label: "\u9ED8\u8BA4", value: "default" },
202
- { label: "\u4E3B\u8981", value: "primary" },
203
- { label: "\u8B66\u544A", value: "warning" }
204
- ]
205
- }
206
- )
207
- ] })
208
- ] })
209
- });
210
- componentRegistry.register("note", {
211
- name: "note",
212
- label: "\u6CE8\u91CA",
213
- fields: [
214
- { key: "type", defaultValue: "info" },
215
- { key: "title", defaultValue: "\u6CE8\u610F" },
216
- { key: "content", defaultValue: "\u5907\u6CE8\u5185\u5BB9" }
217
- ],
218
- renderDialog: (props, onChange) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
219
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
220
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u7C7B\u578B" }),
221
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
222
- import_antd.Select,
223
- {
224
- value: props.type,
225
- onChange: (v) => onChange("type", v),
226
- options: [
227
- { label: "\u4FE1\u606F", value: "info" },
228
- { label: "\u63D0\u793A", value: "tip" },
229
- { label: "\u8B66\u544A", value: "warning" },
230
- { label: "\u5371\u9669", value: "danger" }
231
- ]
232
- }
233
- )
234
- ] }),
235
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
236
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u6807\u9898" }),
237
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input, { value: props.title, onChange: (e) => onChange("title", e.target.value) })
238
- ] }),
239
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
240
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 4 }, children: "\u5185\u5BB9" }),
241
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_antd.Input.TextArea, { rows: 3, value: props.content, onChange: (e) => onChange("content", e.target.value) })
242
- ] })
243
- ] })
244
- });
245
171
  var componentRegistry_default = componentRegistry;
246
172
 
247
173
  // src/components/Editor/containerWidget.ts
@@ -387,13 +313,151 @@ var containerWidgetPlugin = import_view.ViewPlugin.fromClass(
387
313
  }
388
314
  );
389
315
 
316
+ // src/components/Toolbar/buttons.tsx
317
+ function insertAtCursor(view, before, after = "", placeholder = "") {
318
+ const { from, to } = view.state.selection.main;
319
+ const selectedText = view.state.doc.sliceString(from, to);
320
+ const insert = selectedText ? `${before}${selectedText}${after}` : `${before}${placeholder}${after}`;
321
+ view.dispatch({ changes: { from, to, insert } });
322
+ if (!selectedText && placeholder) {
323
+ view.dispatch({ selection: { anchor: from + before.length } });
324
+ }
325
+ }
326
+ function insertAtLineStart(view, text) {
327
+ const { from } = view.state.selection.main;
328
+ const line = view.state.doc.lineAt(from);
329
+ view.dispatch({ changes: { from: line.from, to: line.from, insert: text } });
330
+ view.dispatch({ selection: { anchor: line.from + text.length } });
331
+ }
332
+ async function uploadFileToServer(file, uploadConfig) {
333
+ var _a;
334
+ if (uploadConfig.onUploadImage) {
335
+ return uploadConfig.onUploadImage(file);
336
+ }
337
+ if (uploadConfig.uploadUrl) {
338
+ const formData = new FormData();
339
+ formData.append("file", file);
340
+ const response = await fetch(uploadConfig.uploadUrl, {
341
+ method: "POST",
342
+ body: formData
343
+ });
344
+ if (!response.ok) return null;
345
+ const result = await response.json();
346
+ if (uploadConfig.onGetUploadUrl) {
347
+ return uploadConfig.onGetUploadUrl(result);
348
+ }
349
+ return ((_a = result.data) == null ? void 0 : _a.path) || null;
350
+ }
351
+ return null;
352
+ }
353
+ function isUploadConfigured(uploadConfig) {
354
+ return !!((uploadConfig == null ? void 0 : uploadConfig.uploadUrl) || (uploadConfig == null ? void 0 : uploadConfig.onUploadImage));
355
+ }
356
+ function uploadImage(view, uploadConfig) {
357
+ const input = document.createElement("input");
358
+ input.type = "file";
359
+ input.accept = "image/*";
360
+ input.style.display = "none";
361
+ input.addEventListener("change", async () => {
362
+ if (!input.files || input.files.length === 0) return;
363
+ const file = input.files[0];
364
+ const { from, to } = view.state.selection.main;
365
+ const selectedText = view.state.doc.sliceString(from, to) || "\u63CF\u8FF0";
366
+ const url = await uploadFileToServer(file, uploadConfig);
367
+ if (url) {
368
+ const insert = `![${selectedText}](${url})`;
369
+ view.dispatch({ changes: { from, to, insert } });
370
+ }
371
+ input.remove();
372
+ });
373
+ document.body.appendChild(input);
374
+ input.click();
375
+ }
376
+ function wrapSelection(view, prefix, suffix) {
377
+ const { from, to, empty } = view.state.selection.main;
378
+ if (empty) return;
379
+ const selected = view.state.doc.sliceString(from, to);
380
+ view.dispatch({ changes: { from, to, insert: `${prefix}${selected}${suffix}` } });
381
+ }
382
+ function toggleWrap(view, openTag, closeTag) {
383
+ const { from, to, empty } = view.state.selection.main;
384
+ const doc = view.state.doc;
385
+ if (!empty) {
386
+ const selected = doc.sliceString(from, to);
387
+ if (selected.startsWith(openTag) && selected.endsWith(closeTag)) {
388
+ const inner = selected.slice(openTag.length, -closeTag.length);
389
+ view.dispatch({ changes: { from, to, insert: inner } });
390
+ return;
391
+ }
392
+ }
393
+ {
394
+ const cursorPos = empty ? from : to;
395
+ const line = doc.lineAt(cursorPos);
396
+ const lineText = doc.sliceString(line.from, line.to);
397
+ const openIdx = lineText.indexOf(openTag);
398
+ const closeIdx = lineText.lastIndexOf(closeTag);
399
+ if (openIdx >= 0 && closeIdx > openIdx) {
400
+ const absOpen = line.from + openIdx;
401
+ const absClose = line.from + closeIdx;
402
+ if (cursorPos >= absOpen + openTag.length && cursorPos <= absClose) {
403
+ const inner = doc.sliceString(absOpen + openTag.length, absClose);
404
+ view.dispatch({
405
+ changes: {
406
+ from: absOpen,
407
+ to: absClose + closeTag.length,
408
+ insert: inner
409
+ }
410
+ });
411
+ return;
412
+ }
413
+ }
414
+ }
415
+ wrapSelection(view, openTag, closeTag);
416
+ }
417
+
390
418
  // src/components/Editor/codemirrorExtensions.ts
419
+ var uploadConfigFacet = import_state2.Facet.define({
420
+ combine: (values) => values[values.length - 1]
421
+ });
422
+ var messageFacet = import_state2.Facet.define({
423
+ combine: (values) => values[values.length - 1]
424
+ });
425
+ var pasteHandler = import_view2.EditorView.domEventHandlers({
426
+ paste: (event, view) => {
427
+ var _a;
428
+ const files = (_a = event.clipboardData) == null ? void 0 : _a.files;
429
+ if (!files) return false;
430
+ const imageFiles = Array.from(files).filter((f) => f.type.startsWith("image/"));
431
+ if (imageFiles.length === 0) return false;
432
+ event.preventDefault();
433
+ const uploadConfig = view.state.facet(uploadConfigFacet);
434
+ const showMessage = view.state.facet(messageFacet);
435
+ if (!isUploadConfigured(uploadConfig)) {
436
+ showMessage == null ? void 0 : showMessage();
437
+ return true;
438
+ }
439
+ const { from, to } = view.state.selection.main;
440
+ let offset = 0;
441
+ for (const file of imageFiles) {
442
+ uploadFileToServer(file, uploadConfig).then((url) => {
443
+ if (url) {
444
+ const name = file.name.replace(/\.[^.]+$/, "");
445
+ const insert = `![${name}](${url})`;
446
+ view.dispatch({ changes: { from: from + offset, to: to + offset, insert } });
447
+ offset += insert.length;
448
+ }
449
+ });
450
+ }
451
+ return true;
452
+ }
453
+ });
391
454
  var getCodeMirrorExtensions = (opts) => {
392
455
  const extensions = [
393
456
  (0, import_lang_markdown.markdown)(),
394
457
  import_theme_one_dark.oneDark,
395
458
  import_view2.keymap.of([...import_commands.defaultKeymap, import_commands.indentWithTab]),
396
459
  import_view2.EditorView.lineWrapping,
460
+ pasteHandler,
397
461
  import_view2.EditorView.baseTheme({
398
462
  ".cm-scroller": {
399
463
  height: "100%",
@@ -438,26 +502,32 @@ var getCodeMirrorExtensions = (opts) => {
438
502
  if (opts == null ? void 0 : opts.onEditContainer) {
439
503
  extensions.push(containerEditCallback.of(opts.onEditContainer));
440
504
  }
505
+ if (opts == null ? void 0 : opts.uploadConfig) {
506
+ extensions.push(uploadConfigFacet.of(opts.uploadConfig));
507
+ }
508
+ if (opts == null ? void 0 : opts.onMessageNoUploadConfig) {
509
+ extensions.push(messageFacet.of(opts.onMessageNoUploadConfig));
510
+ }
441
511
  return extensions;
442
512
  };
443
513
 
444
514
  // src/components/Editor/index.tsx
445
- var import_jsx_runtime2 = require("react/jsx-runtime");
446
- function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer }) {
515
+ var import_jsx_runtime = require("react/jsx-runtime");
516
+ function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer, uploadConfig, onMessageNoUploadConfig }) {
447
517
  const editorRef = (0, import_react.useRef)(null);
448
518
  const viewRef = (0, import_react.useRef)(null);
449
519
  (0, import_react.useEffect)(() => {
450
520
  if (!editorRef.current) return;
451
521
  const extensions = [
452
522
  import_codemirror.basicSetup,
453
- ...getCodeMirrorExtensions({ onEditContainer }),
523
+ ...getCodeMirrorExtensions({ onEditContainer, uploadConfig, onMessageNoUploadConfig }),
454
524
  import_codemirror.EditorView.updateListener.of((update) => {
455
525
  if (update.docChanged) {
456
526
  onChange(update.state.doc.toString());
457
527
  }
458
528
  })
459
529
  ];
460
- const state = import_state2.EditorState.create({
530
+ const state = import_state3.EditorState.create({
461
531
  doc: value,
462
532
  extensions
463
533
  });
@@ -483,14 +553,14 @@ function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer }
483
553
  });
484
554
  }
485
555
  }, [value]);
486
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: editorRef, className: "editor" });
556
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: editorRef, className: "editor" });
487
557
  }
488
558
 
489
559
  // src/components/Preview/index.tsx
490
560
  var import_react2 = require("react");
491
561
  var import_remons_render_markdown = __toESM(require("remons-render-markdown"), 1);
492
562
  var import_dist = require("remons-render-markdown/dist/index.css");
493
- var import_jsx_runtime3 = require("react/jsx-runtime");
563
+ var import_jsx_runtime2 = require("react/jsx-runtime");
494
564
  var Preview = (0, import_react2.forwardRef)(({ content, previewOptions }, ref) => {
495
565
  (0, import_react2.useEffect)(() => {
496
566
  (0, import_remons_render_markdown.initHighlighter)({
@@ -498,24 +568,24 @@ var Preview = (0, import_react2.forwardRef)(({ content, previewOptions }, ref) =
498
568
  ...(previewOptions == null ? void 0 : previewOptions.languages) ? previewOptions == null ? void 0 : previewOptions.languages : {}
499
569
  });
500
570
  }, [previewOptions == null ? void 0 : previewOptions.languages]);
501
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref, className: "preview", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_remons_render_markdown.default, { content, ...previewOptions }) });
571
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref, className: "preview", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_remons_render_markdown.default, { content, ...previewOptions }) });
502
572
  });
503
573
  Preview.displayName = "Preview";
504
574
  var Preview_default = Preview;
505
575
 
506
576
  // src/components/Panel/SplitPanels.tsx
507
- var import_antd2 = require("antd");
508
- var import_jsx_runtime4 = require("react/jsx-runtime");
577
+ var import_antd = require("antd");
578
+ var import_jsx_runtime3 = require("react/jsx-runtime");
509
579
  function SplitPanels({ leftPanel, rightPanel }) {
510
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_antd2.Splitter, { className: "split-panels", children: [
511
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_antd2.Splitter.Panel, { defaultSize: "50%", min: "20%", max: "80%", children: leftPanel }),
512
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_antd2.Splitter.Panel, { children: rightPanel })
580
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_antd.Splitter, { className: "split-panels", children: [
581
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd.Splitter.Panel, { defaultSize: "50%", min: "20%", max: "80%", children: leftPanel }),
582
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_antd.Splitter.Panel, { children: rightPanel })
513
583
  ] });
514
584
  }
515
585
 
516
586
  // src/components/Toolbar/index.tsx
517
587
  var import_react6 = require("react");
518
- var import_antd5 = require("antd");
588
+ var import_antd4 = require("antd");
519
589
  var import_icons4 = require("@ant-design/icons");
520
590
 
521
591
  // src/components/Toolbar/hooks/useToolbar.ts
@@ -523,102 +593,8 @@ var import_react3 = require("react");
523
593
 
524
594
  // src/components/Toolbar/config.ts
525
595
  var import_icons = require("@ant-design/icons");
526
-
527
- // src/components/Toolbar/buttons.tsx
528
- function insertAtCursor(view, before, after = "", placeholder = "") {
529
- const { from, to } = view.state.selection.main;
530
- const selectedText = view.state.doc.sliceString(from, to);
531
- const insert = selectedText ? `${before}${selectedText}${after}` : `${before}${placeholder}${after}`;
532
- view.dispatch({ changes: { from, to, insert } });
533
- if (!selectedText && placeholder) {
534
- view.dispatch({ selection: { anchor: from + before.length } });
535
- }
536
- }
537
- function insertAtLineStart(view, text) {
538
- const { from } = view.state.selection.main;
539
- const line = view.state.doc.lineAt(from);
540
- view.dispatch({ changes: { from: line.from, to: line.from, insert: text } });
541
- view.dispatch({ selection: { anchor: line.from + text.length } });
542
- }
543
- async function uploadFileToServer(file) {
544
- var _a;
545
- const formData = new FormData();
546
- formData.append("file", file);
547
- const response = await fetch("https://remons.cn:3008/content/uploadMarkdownImg", {
548
- method: "POST",
549
- body: formData
550
- });
551
- if (!response.ok) return null;
552
- const result = await response.json();
553
- if (result.code === 200 && result.success && ((_a = result.data) == null ? void 0 : _a.path)) {
554
- return `https://remons.cn:3008${result.data.path}`;
555
- }
556
- return null;
557
- }
558
- function uploadImage(view) {
559
- const input = document.createElement("input");
560
- input.type = "file";
561
- input.accept = "image/*";
562
- input.style.display = "none";
563
- input.addEventListener("change", async () => {
564
- if (!input.files || input.files.length === 0) return;
565
- const file = input.files[0];
566
- const { from, to } = view.state.selection.main;
567
- const selectedText = view.state.doc.sliceString(from, to) || "\u63CF\u8FF0";
568
- const url = await uploadFileToServer(file);
569
- if (url) {
570
- const insert = `![${selectedText}](${url})`;
571
- view.dispatch({ changes: { from, to, insert } });
572
- }
573
- input.remove();
574
- });
575
- document.body.appendChild(input);
576
- input.click();
577
- }
578
- function wrapSelection(view, prefix, suffix) {
579
- const { from, to, empty } = view.state.selection.main;
580
- if (empty) return;
581
- const selected = view.state.doc.sliceString(from, to);
582
- view.dispatch({ changes: { from, to, insert: `${prefix}${selected}${suffix}` } });
583
- }
584
- function toggleWrap(view, openTag, closeTag) {
585
- const { from, to, empty } = view.state.selection.main;
586
- const doc = view.state.doc;
587
- if (!empty) {
588
- const selected = doc.sliceString(from, to);
589
- if (selected.startsWith(openTag) && selected.endsWith(closeTag)) {
590
- const inner = selected.slice(openTag.length, -closeTag.length);
591
- view.dispatch({ changes: { from, to, insert: inner } });
592
- return;
593
- }
594
- }
595
- {
596
- const cursorPos = empty ? from : to;
597
- const line = doc.lineAt(cursorPos);
598
- const lineText = doc.sliceString(line.from, line.to);
599
- const openIdx = lineText.indexOf(openTag);
600
- const closeIdx = lineText.lastIndexOf(closeTag);
601
- if (openIdx >= 0 && closeIdx > openIdx) {
602
- const absOpen = line.from + openIdx;
603
- const absClose = line.from + closeIdx;
604
- if (cursorPos >= absOpen + openTag.length && cursorPos <= absClose) {
605
- const inner = doc.sliceString(absOpen + openTag.length, absClose);
606
- view.dispatch({
607
- changes: {
608
- from: absOpen,
609
- to: absClose + closeTag.length,
610
- insert: inner
611
- }
612
- });
613
- return;
614
- }
615
- }
616
- }
617
- wrapSelection(view, openTag, closeTag);
618
- }
619
-
620
- // src/components/Toolbar/config.ts
621
- function createToolbarConfig() {
596
+ function createToolbarConfig(options) {
597
+ const hasUploadConfig = isUploadConfigured(options);
622
598
  return [
623
599
  {
624
600
  id: "heading",
@@ -643,7 +619,14 @@ function createToolbarConfig() {
643
619
  { id: "ol", label: "\u6709\u5E8F\u5217\u8868", icon: import_icons.OrderedListOutlined, handler: (v) => insertAtLineStart(v, "1. ") },
644
620
  { separatorBefore: true },
645
621
  { id: "link", label: "\u94FE\u63A5", icon: import_icons.LinkOutlined, handler: (v) => insertAtCursor(v, "[", "](url)", "\u6587\u672C") },
646
- { id: "image", label: "\u56FE\u7247", icon: import_icons.PictureOutlined, handler: (v) => uploadImage(v) },
622
+ // 图片按钮:未配置上传时禁用并提示
623
+ {
624
+ id: "image",
625
+ label: hasUploadConfig ? "\u56FE\u7247" : "\u8BF7\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u5730\u5740",
626
+ icon: import_icons.PictureOutlined,
627
+ disabled: !hasUploadConfig,
628
+ handler: hasUploadConfig ? (v) => uploadImage(v, { uploadUrl: options == null ? void 0 : options.uploadUrl, onUploadImage: options == null ? void 0 : options.onUploadImage }) : void 0
629
+ },
647
630
  { separatorBefore: true },
648
631
  { id: "code", label: "\u884C\u5185\u4EE3\u7801", icon: import_icons.FieldStringOutlined, handler: (v) => wrapSelection(v, "`", "`") },
649
632
  {
@@ -658,7 +641,6 @@ function createToolbarConfig() {
658
641
  }
659
642
  },
660
643
  { id: "quote", label: "\u5F15\u7528", icon: import_icons.BlockOutlined, handler: (v) => insertAtLineStart(v, "> ") },
661
- { id: "textContainer", label: "\u6587\u5B57\u5BB9\u5668", icon: import_icons.TagsOutlined, handler: (v) => toggleWrap(v, "<text-container>", "</text-container>") },
662
644
  {
663
645
  id: "hr",
664
646
  label: "\u5206\u5272\u7EBF",
@@ -672,13 +654,20 @@ function createToolbarConfig() {
672
654
  }
673
655
 
674
656
  // src/components/Toolbar/hooks/useToolbar.ts
675
- function useToolbar(editorView, extraItems) {
657
+ function useToolbar(editorView, options) {
658
+ const { extraItems, uploadConfig } = options || {};
676
659
  const config = (0, import_react3.useMemo)(() => {
677
- const base = createToolbarConfig();
678
- return extraItems ? [...base, { separatorBefore: true }, ...extraItems] : base;
679
- }, [extraItems]);
660
+ const base = createToolbarConfig(uploadConfig);
661
+ return extraItems ? [...base, ...extraItems] : base;
662
+ }, [extraItems, uploadConfig]);
680
663
  const handleClick = (0, import_react3.useCallback)((button) => {
681
- if (button.handler && editorView) {
664
+ if (!editorView) return;
665
+ if (button.wrapTags) {
666
+ const [openTag, closeTag] = button.wrapTags;
667
+ toggleWrap(editorView, openTag, closeTag);
668
+ return;
669
+ }
670
+ if (button.handler) {
682
671
  editorView.focus();
683
672
  button.handler(editorView, editorView.state);
684
673
  }
@@ -687,16 +676,16 @@ function useToolbar(editorView, extraItems) {
687
676
  }
688
677
 
689
678
  // src/components/Toolbar/renderers/ToolbarItemRenderer.tsx
690
- var import_antd3 = require("antd");
679
+ var import_antd2 = require("antd");
691
680
  var import_icons2 = require("@ant-design/icons");
692
- var import_jsx_runtime5 = require("react/jsx-runtime");
681
+ var import_jsx_runtime4 = require("react/jsx-runtime");
693
682
  function ToolbarItemRenderer({ item, onClick }) {
694
683
  const Icon = item.icon;
695
684
  if (item.children && item.children.length > 0) {
696
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
697
- item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-separator" }),
698
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-group", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
699
- import_antd3.Dropdown,
685
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
686
+ item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "toolbar-separator" }),
687
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "toolbar-group", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_antd2.Tooltip, { title: item.label, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
688
+ import_antd2.Dropdown,
700
689
  {
701
690
  menu: {
702
691
  items: item.children.map((child) => {
@@ -704,10 +693,10 @@ function ToolbarItemRenderer({ item, onClick }) {
704
693
  return {
705
694
  type: "item",
706
695
  key: (_a = child.id) != null ? _a : "",
707
- label: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { onClick: () => onClick(child), children: [
696
+ label: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { onClick: () => onClick(child), children: [
708
697
  child.icon && (() => {
709
698
  const ChildIcon = child.icon;
710
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChildIcon, {});
699
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChildIcon, {});
711
700
  })(),
712
701
  " ",
713
702
  child.label
@@ -716,34 +705,35 @@ function ToolbarItemRenderer({ item, onClick }) {
716
705
  })
717
706
  },
718
707
  placement: "bottomLeft",
719
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_antd3.Button, { className: "toolbar-button", type: "text", size: "small", title: item.label, children: [
720
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, {}),
721
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_icons2.DownOutlined, { style: { marginLeft: 2, fontSize: 10 } })
708
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_antd2.Button, { className: "toolbar-button", type: "text", size: "small", children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, {}),
710
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons2.DownOutlined, { style: { marginLeft: 2, fontSize: 10 } })
722
711
  ] })
723
712
  }
724
- ) })
713
+ ) }) })
725
714
  ] });
726
715
  }
727
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
728
- item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-separator" }),
729
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-group", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
730
- import_antd3.Button,
716
+ const isDisabled = item.disabled;
717
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
718
+ item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "toolbar-separator" }),
719
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "toolbar-group", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_antd2.Tooltip, { title: item.label, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
720
+ import_antd2.Button,
731
721
  {
732
722
  id: item.id,
733
723
  className: "toolbar-button",
734
724
  type: "text",
735
725
  size: "small",
736
- title: item.label,
737
- onClick: () => onClick(item),
738
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, {})
726
+ disabled: isDisabled,
727
+ onClick: () => !isDisabled && onClick(item),
728
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, {})
739
729
  }
740
- ) })
730
+ ) }) })
741
731
  ] });
742
732
  }
743
733
 
744
734
  // src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx
745
735
  var import_react5 = __toESM(require("react"), 1);
746
- var import_antd4 = require("antd");
736
+ var import_antd3 = require("antd");
747
737
  var import_icons3 = require("@ant-design/icons");
748
738
 
749
739
  // src/components/Toolbar/renderers/TablePicker/index.tsx
@@ -753,24 +743,27 @@ var import_react4 = require("react");
753
743
  var index_module_default = {};
754
744
 
755
745
  // src/components/Toolbar/renderers/TablePicker/index.tsx
756
- var import_jsx_runtime6 = require("react/jsx-runtime");
757
- var MAX_ROW = 12;
758
- var MAX_COL = 12;
759
- var REFRESH_TIME = 200;
746
+ var import_jsx_runtime5 = require("react/jsx-runtime");
747
+ var MAX_ROW = 8;
748
+ var MAX_COL = 8;
760
749
  function TablePicker({ onSelect }) {
761
750
  const [hoverSize, setHoverSize] = (0, import_react4.useState)({ row: 0, col: 0 });
762
- const [prevHoverSize, setPrevHoverSize] = (0, import_react4.useState)({ row: 0, col: 0 });
763
751
  const [confirmedSize, setConfirmedSize] = (0, import_react4.useState)({ row: 0, col: 0 });
764
752
  const maxColRef = (0, import_react4.useRef)(0);
753
+ const prevRowRef = (0, import_react4.useRef)(0);
765
754
  const timerIdRef = (0, import_react4.useRef)(null);
766
755
  const refreshTimerRef = (0, import_react4.useRef)(null);
767
756
  const commitRef = (0, import_react4.useRef)(null);
768
- const clearTimer = (0, import_react4.useCallback)((flushFn) => {
757
+ const clearTimer = (0, import_react4.useCallback)(() => {
769
758
  if (timerIdRef.current) {
759
+ clearTimeout(timerIdRef.current);
770
760
  timerIdRef.current = null;
771
761
  }
772
- if (flushFn && maxColRef.current > 0) {
773
- timerIdRef.current = setTimeout(flushFn, REFRESH_TIME);
762
+ }, []);
763
+ const clearFlash = (0, import_react4.useCallback)(() => {
764
+ if (refreshTimerRef.current) {
765
+ clearInterval(refreshTimerRef.current);
766
+ refreshTimerRef.current = null;
774
767
  }
775
768
  }, []);
776
769
  (0, import_react4.useEffect)(
@@ -781,25 +774,21 @@ function TablePicker({ onSelect }) {
781
774
  []
782
775
  );
783
776
  function extend(col, row) {
777
+ if (row < prevRowRef.current || col < maxColRef.current) {
778
+ maxColRef.current = 0;
779
+ }
784
780
  if (col > maxColRef.current) {
785
781
  maxColRef.current = col;
786
- setHoverSize({ row, col });
787
- setConfirmedSize({ row, col });
788
- } else {
789
- setHoverSize({ row, col });
790
- if (row !== confirmedSize.row) {
791
- setConfirmedSize({ row, col: maxColRef.current });
792
- }
793
- if (row === 1) {
794
- commitRef.current = { cols: col, rows: 1 };
795
- setConfirmedSize({ row: 1, col });
796
- }
782
+ }
783
+ setHoverSize({ row, col });
784
+ setConfirmedSize({ row, col: maxColRef.current });
785
+ if (row === 1) {
786
+ commitRef.current = { cols: maxColRef.current, rows: 1 };
797
787
  }
798
788
  }
799
789
  function flash(col, dir, limit, finalCallback) {
800
790
  let current = dir === 1 ? 1 : limit;
801
791
  const step = dir === 1 ? 1 : -1;
802
- let timer = 0;
803
792
  refreshTimerRef.current = setInterval(() => {
804
793
  setHoverSize({ row: current, col });
805
794
  if (current > confirmedSize.row || dir === -1) {
@@ -808,45 +797,43 @@ function TablePicker({ onSelect }) {
808
797
  current += step;
809
798
  if (dir === 1 && current > limit || dir === -1 && current < 1) {
810
799
  clearInterval(refreshTimerRef.current);
811
- setHoverSize({ row: current - step, col });
812
- finalCallback(col, current - step);
800
+ refreshTimerRef.current = null;
801
+ const finalRow = current - step;
802
+ setHoverSize({ row: finalRow, col });
803
+ finalCallback(col, finalRow);
813
804
  }
814
805
  }, 50);
815
- clearTimeout(timer);
816
- return () => {
817
- clearInterval(refreshTimerRef.current);
818
- };
819
806
  }
820
807
  function onCellEnterCheck(index) {
821
808
  clearTimer();
809
+ clearFlash();
822
810
  const row = Math.floor(index / MAX_COL) + 1;
823
811
  const col = index % MAX_COL + 1;
824
- const isDown = row > hoverSize.row;
825
- if (Math.abs(row - hoverSize.row) <= 1) {
812
+ const prevRow = prevRowRef.current;
813
+ const isJump = Math.abs(row - prevRow) > 1;
814
+ const isDown = row > prevRow;
815
+ prevRowRef.current = row;
816
+ if (!isJump) {
826
817
  extend(col, row);
827
- } else {
828
- if (isDown && (col > maxColRef.current || maxColRef.current >= MAX_COL)) {
818
+ } else if (isDown) {
819
+ if (col > maxColRef.current || maxColRef.current >= MAX_COL) {
829
820
  const finalRow = Math.min(row, MAX_ROW);
830
821
  flash(col, 1, finalRow, (c, r) => {
831
822
  setConfirmedSize({ row: r, col: c });
832
823
  });
833
- } else if (!isDown) {
834
- flash(col, -1, row, (c, r) => {
835
- setConfirmedSize({ row: r, col: c });
836
- if (r === 1) {
837
- commitRef.current = { cols: Math.min(MAX_COL, col), rows: 1 };
838
- setConfirmedSize({ row: 1, col: Math.min(MAX_COL, col) });
839
- }
840
- });
841
- }
842
- }
843
- if (col >= MAX_COL && row >= prevHoverSize.row) {
844
- if (row !== prevHoverSize.row) {
845
- maxColRef.current = 0;
846
- setPrevHoverSize({ row: 0, col: 0 });
824
+ } else {
825
+ extend(col, row);
847
826
  }
827
+ } else {
828
+ maxColRef.current = 0;
829
+ flash(col, -1, row, (c, r) => {
830
+ setConfirmedSize({ row: r, col: c });
831
+ if (r === 1) {
832
+ commitRef.current = { cols: Math.min(MAX_COL, col), rows: 1 };
833
+ setConfirmedSize({ row: 1, col: Math.min(MAX_COL, col) });
834
+ }
835
+ });
848
836
  }
849
- setPrevHoverSize({ row, col });
850
837
  }
851
838
  function onMouseDown() {
852
839
  const size = commitRef.current || { cols: maxColRef.current, rows: confirmedSize.row };
@@ -854,13 +841,13 @@ function TablePicker({ onSelect }) {
854
841
  onSelect(size);
855
842
  }
856
843
  }
857
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
844
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
858
845
  "div",
859
846
  {
860
847
  className: index_module_default.container,
861
848
  onMouseDown,
862
849
  children: [
863
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
850
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
864
851
  "div",
865
852
  {
866
853
  className: index_module_default.grid,
@@ -869,7 +856,7 @@ function TablePicker({ onSelect }) {
869
856
  const row = Math.floor(i / MAX_COL) + 1;
870
857
  const col = i % MAX_COL + 1;
871
858
  const isSelected = row <= hoverSize.row && col <= maxColRef.current;
872
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
859
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
873
860
  "div",
874
861
  {
875
862
  className: `${index_module_default.cell} ${isSelected ? index_module_default.cellActive : ""}`,
@@ -880,7 +867,7 @@ function TablePicker({ onSelect }) {
880
867
  })
881
868
  }
882
869
  ),
883
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: index_module_default.label, children: [
870
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: index_module_default.label, children: [
884
871
  hoverSize.row,
885
872
  " x ",
886
873
  maxColRef.current || 1
@@ -891,7 +878,7 @@ function TablePicker({ onSelect }) {
891
878
  }
892
879
 
893
880
  // src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx
894
- var import_jsx_runtime7 = require("react/jsx-runtime");
881
+ var import_jsx_runtime6 = require("react/jsx-runtime");
895
882
  function TableDropdown({ editorView }) {
896
883
  const [open, setOpen] = import_react5.default.useState(false);
897
884
  const openCountRef = (0, import_react5.useRef)(0);
@@ -910,15 +897,15 @@ ${dataRows}
910
897
  const { from } = editorView.state.selection.main;
911
898
  editorView.dispatch({ changes: { from, insert: table } });
912
899
  }, [editorView]);
913
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
914
- import_antd4.Dropdown,
900
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
901
+ import_antd3.Dropdown,
915
902
  {
916
903
  trigger: ["click"],
917
904
  open,
918
905
  onOpenChange: setOpen,
919
906
  popupRender: () => {
920
907
  openCountRef.current++;
921
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { padding: 4 }, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
908
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { padding: 4 }, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
922
909
  TablePicker,
923
910
  {
924
911
  onSelect: (size) => {
@@ -930,44 +917,41 @@ ${dataRows}
930
917
  ) });
931
918
  },
932
919
  placement: "bottomLeft",
933
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_antd4.Button, { className: "toolbar-button", type: "text", size: "small", title: "\u8868\u683C", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_icons3.TableOutlined, {}) })
920
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_antd3.Button, { className: "toolbar-button", type: "text", size: "small", title: "\u8868\u683C", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_icons3.TableOutlined, {}) })
934
921
  }
935
922
  );
936
923
  }
937
924
 
938
925
  // src/components/Toolbar/index.tsx
939
- var import_jsx_runtime8 = require("react/jsx-runtime");
940
- function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
941
- const { config, handleClick } = useToolbar(editorView, extraItems);
926
+ var import_jsx_runtime7 = require("react/jsx-runtime");
927
+ function Toolbar({ editorView, fullscreen = true, onToggleFullscreen, extraItems, uploadConfig }) {
928
+ const { config, handleClick } = useToolbar(editorView, { extraItems, uploadConfig });
942
929
  const renderFullscreen = (0, import_react6.useCallback)(() => {
943
930
  if (!onToggleFullscreen) return null;
944
931
  const Icon = fullscreen ? import_icons4.FullscreenExitOutlined : import_icons4.FullscreenOutlined;
945
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
946
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "toolbar-separator" }),
947
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
948
- import_antd5.Button,
949
- {
950
- className: "toolbar-button",
951
- type: "text",
952
- size: "small",
953
- title: fullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
954
- onClick: () => {
955
- onToggleFullscreen();
956
- },
957
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, {})
958
- }
959
- )
960
- ] });
932
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
933
+ import_antd4.Button,
934
+ {
935
+ className: "toolbar-button",
936
+ type: "text",
937
+ size: "small",
938
+ title: fullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
939
+ onClick: () => {
940
+ onToggleFullscreen();
941
+ },
942
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, {})
943
+ }
944
+ );
961
945
  }, [fullscreen, onToggleFullscreen]);
962
946
  function isSeparator(item) {
963
947
  return !!(!item.id && item.separatorBefore);
964
948
  }
965
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "toolbar", children: [
949
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "toolbar", children: [
966
950
  config.map((item, idx) => {
967
951
  if (isSeparator(item)) {
968
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "toolbar-separator" }, `sep-${idx}`);
952
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "toolbar-separator" }, `sep-${idx}`);
969
953
  }
970
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
954
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
971
955
  ToolbarItemRenderer,
972
956
  {
973
957
  item,
@@ -976,16 +960,15 @@ function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
976
960
  item.id
977
961
  );
978
962
  }),
979
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "toolbar-separator" }),
980
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TableDropdown, { editorView }),
963
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableDropdown, { editorView }),
981
964
  renderFullscreen()
982
965
  ] });
983
966
  }
984
967
 
985
968
  // src/components/Dialog/EditDialog.tsx
986
969
  var import_react7 = require("react");
987
- var import_antd6 = require("antd");
988
- var import_jsx_runtime9 = require("react/jsx-runtime");
970
+ var import_antd5 = require("antd");
971
+ var import_jsx_runtime8 = require("react/jsx-runtime");
989
972
  function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
990
973
  const schema = componentRegistry_default.get(typeName);
991
974
  const [formProps, setFormProps] = (0, import_react7.useState)({});
@@ -1013,8 +996,8 @@ function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
1013
996
  if (!schema) {
1014
997
  return null;
1015
998
  }
1016
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1017
- import_antd6.Modal,
999
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1000
+ import_antd5.Modal,
1018
1001
  {
1019
1002
  title: `${schema.label} \u5C5E\u6027\u7F16\u8F91`,
1020
1003
  open: visible,
@@ -1054,36 +1037,11 @@ ${propsLine}
1054
1037
 
1055
1038
  // src/MarkdownEditor.tsx
1056
1039
  var import_icons5 = require("@ant-design/icons");
1057
- var import_jsx_runtime10 = require("react/jsx-runtime");
1058
- var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEditor({
1059
- defaultValue = "",
1060
- value,
1061
- onChange,
1062
- showPreview = true,
1063
- fullscreen: fullscreenProp = false,
1064
- previewOptions
1065
- }, ref) {
1066
- const [content, setContent] = (0, import_react8.useState)(value != null ? value : defaultValue);
1067
- const [editorView, setEditorView] = (0, import_react8.useState)(null);
1068
- const scrollDOMRef = (0, import_react8.useRef)(null);
1069
- const previewElRef = (0, import_react8.useRef)(null);
1040
+
1041
+ // src/hooks/useFullscreen.ts
1042
+ var import_react8 = require("react");
1043
+ function useFullscreen() {
1070
1044
  const [fullscreen, setFullscreen] = (0, import_react8.useState)(false);
1071
- const [dialogVisible, setDialogVisible] = (0, import_react8.useState)(false);
1072
- const [dialogTypeName, setDialogTypeName] = (0, import_react8.useState)("");
1073
- const [dialogProps, setDialogProps] = (0, import_react8.useState)({});
1074
- const [dialogPropsRange, setDialogPropsRange] = (0, import_react8.useState)([0, 0]);
1075
- (0, import_react8.useEffect)(() => {
1076
- if (value !== void 0) {
1077
- setContent(value);
1078
- }
1079
- }, [value]);
1080
- const internalSetContent = (0, import_react8.useCallback)((newContent) => {
1081
- setContent(newContent);
1082
- onChange == null ? void 0 : onChange(newContent);
1083
- }, [onChange]);
1084
- (0, import_react8.useImperativeHandle)(ref, () => ({
1085
- getContent: () => content
1086
- }), [content]);
1087
1045
  const toggleFullscreen = (0, import_react8.useCallback)(() => {
1088
1046
  if (document.fullscreenElement) {
1089
1047
  document.exitFullscreen().catch((e) => {
@@ -1110,40 +1068,29 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
1110
1068
  document.removeEventListener("webkitfullscreenchange", handleFullscreenChange);
1111
1069
  };
1112
1070
  }, []);
1113
- const extraToolbarItems = componentRegistry_default.getAll().map((schema) => {
1114
- var _a, _b;
1115
- const defaultProps = {};
1116
- for (const field of schema.fields) {
1117
- defaultProps[field.key] = (_a = field.defaultValue) != null ? _a : "";
1118
- }
1119
- const iconNode = (_b = schema.icon) != null ? _b : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons5.ReadOutlined, {});
1120
- return {
1121
- id: `container-${schema.name}`,
1122
- label: schema.label,
1123
- icon: () => iconNode,
1124
- handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
1125
- };
1126
- });
1127
- const handleScrollReady = (0, import_react8.useCallback)((scrollDOM) => {
1128
- scrollDOMRef.current = scrollDOM;
1129
- }, []);
1130
- (0, import_react8.useEffect)(() => {
1131
- const source = scrollDOMRef.current;
1132
- const target = previewElRef.current;
1071
+ return { fullscreen, toggleFullscreen };
1072
+ }
1073
+
1074
+ // src/hooks/useScrollSync.ts
1075
+ var import_react9 = require("react");
1076
+ function useScrollSync(sourceRef, targetRef) {
1077
+ const rafIdRef = (0, import_react9.useRef)(null);
1078
+ (0, import_react9.useEffect)(() => {
1079
+ const source = sourceRef.current;
1080
+ const target = targetRef.current;
1133
1081
  if (!source || !target) return;
1134
- let rafId = null;
1135
1082
  const listener = () => {
1136
- if (rafId) return;
1137
- rafId = requestAnimationFrame(() => {
1138
- const s = scrollDOMRef.current;
1139
- const t = previewElRef.current;
1083
+ if (rafIdRef.current) return;
1084
+ rafIdRef.current = requestAnimationFrame(() => {
1085
+ const s = sourceRef.current;
1086
+ const t = targetRef.current;
1140
1087
  if (!s || !t) {
1141
- rafId = null;
1088
+ rafIdRef.current = null;
1142
1089
  return;
1143
1090
  }
1144
1091
  const sourceScrollHeight = s.scrollHeight - s.clientHeight;
1145
1092
  if (sourceScrollHeight <= 0) {
1146
- rafId = null;
1093
+ rafIdRef.current = null;
1147
1094
  return;
1148
1095
  }
1149
1096
  const ratio = s.scrollTop / sourceScrollHeight;
@@ -1151,60 +1098,115 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
1151
1098
  if (targetScrollHeight > 0) {
1152
1099
  t.scrollTop = ratio * targetScrollHeight;
1153
1100
  }
1154
- rafId = null;
1101
+ rafIdRef.current = null;
1155
1102
  });
1156
1103
  };
1157
1104
  source.addEventListener("scroll", listener, { passive: true });
1158
1105
  return () => {
1159
1106
  source.removeEventListener("scroll", listener);
1160
- if (rafId) cancelAnimationFrame(rafId);
1107
+ if (rafIdRef.current) cancelAnimationFrame(rafIdRef.current);
1108
+ };
1109
+ }, [sourceRef, targetRef]);
1110
+ }
1111
+
1112
+ // src/MarkdownEditor.tsx
1113
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1114
+ var MarkdownEditor_default = (0, import_react10.forwardRef)(function MarkdownEditor({
1115
+ defaultValue = "",
1116
+ value,
1117
+ onChange,
1118
+ showPreview = true,
1119
+ fullscreen: fullscreenProp = false,
1120
+ previewOptions,
1121
+ uploadConfig
1122
+ }, ref) {
1123
+ const [content, setContent] = (0, import_react10.useState)(value != null ? value : defaultValue);
1124
+ const [editorView, setEditorView] = (0, import_react10.useState)(null);
1125
+ const scrollDOMRef = (0, import_react10.useRef)(null);
1126
+ const previewElRef = (0, import_react10.useRef)(null);
1127
+ const { fullscreen, toggleFullscreen } = useFullscreen();
1128
+ useScrollSync(scrollDOMRef, previewElRef);
1129
+ const [dialogVisible, setDialogVisible] = (0, import_react10.useState)(false);
1130
+ const [dialogTypeName, setDialogTypeName] = (0, import_react10.useState)("");
1131
+ const [dialogProps, setDialogProps] = (0, import_react10.useState)({});
1132
+ const [dialogPropsRange, setDialogPropsRange] = (0, import_react10.useState)([0, 0]);
1133
+ (0, import_react10.useEffect)(() => {
1134
+ if (value !== void 0) {
1135
+ setContent(value);
1136
+ }
1137
+ }, [value]);
1138
+ const internalSetContent = (0, import_react10.useCallback)((newContent) => {
1139
+ setContent(newContent);
1140
+ onChange == null ? void 0 : onChange(newContent);
1141
+ }, [onChange]);
1142
+ (0, import_react10.useImperativeHandle)(ref, () => ({
1143
+ getContent: () => content
1144
+ }), [content]);
1145
+ const extraToolbarItems = componentRegistry_default.getAll().map((schema) => {
1146
+ var _a, _b, _c;
1147
+ const defaultProps = {};
1148
+ for (const field of (_a = schema == null ? void 0 : schema.fields) != null ? _a : []) {
1149
+ defaultProps[field.key] = (_b = field.defaultValue) != null ? _b : "";
1150
+ }
1151
+ const iconNode = (_c = schema.icon) != null ? _c : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_icons5.ReadOutlined, {});
1152
+ return {
1153
+ id: `container-${schema.name}`,
1154
+ label: schema.label,
1155
+ icon: () => iconNode,
1156
+ handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
1161
1157
  };
1162
- }, [previewElRef.current]);
1163
- const handlePreviewRef = (0, import_react8.useCallback)((node) => {
1158
+ });
1159
+ const handleScrollReady = (0, import_react10.useCallback)((scrollDOM) => {
1160
+ scrollDOMRef.current = scrollDOM;
1161
+ }, []);
1162
+ const handlePreviewRef = (0, import_react10.useCallback)((node) => {
1164
1163
  previewElRef.current = node;
1165
1164
  }, []);
1166
- const handleViewReady = (0, import_react8.useCallback)((view) => {
1165
+ const handleViewReady = (0, import_react10.useCallback)((view) => {
1167
1166
  setEditorView(view);
1168
1167
  }, []);
1169
- const handleEditContainer = (0, import_react8.useCallback)((_view, typeName, propsLineFrom, propsLineTo, currentProps) => {
1168
+ const handleEditContainer = (0, import_react10.useCallback)((_view, typeName, propsLineFrom, propsLineTo, currentProps) => {
1170
1169
  setDialogTypeName(typeName);
1171
1170
  setDialogProps({ ...currentProps });
1172
1171
  setDialogPropsRange([propsLineFrom, propsLineTo]);
1173
1172
  setDialogVisible(true);
1174
1173
  }, []);
1175
- const handleDialogSave = (0, import_react8.useCallback)((newProps) => {
1174
+ const handleDialogSave = (0, import_react10.useCallback)((newProps) => {
1176
1175
  setDialogVisible(false);
1177
1176
  if (editorView) {
1178
1177
  updateContainerProps(editorView, dialogPropsRange[0], dialogPropsRange[1], newProps);
1179
1178
  }
1180
1179
  }, [editorView, dialogPropsRange]);
1181
- const handleDialogClose = (0, import_react8.useCallback)(() => {
1180
+ const handleDialogClose = (0, import_react10.useCallback)(() => {
1182
1181
  setDialogVisible(false);
1183
1182
  }, []);
1184
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: fullscreen ? "app fullscreen" : "app", children: [
1185
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1183
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: fullscreen ? "app fullscreen" : "app", children: [
1184
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1186
1185
  Toolbar,
1187
1186
  {
1188
1187
  editorView,
1189
1188
  fullscreen,
1190
1189
  onToggleFullscreen: fullscreenProp ? toggleFullscreen : void 0,
1191
- extraItems: extraToolbarItems
1190
+ extraItems: extraToolbarItems,
1191
+ uploadConfig
1192
1192
  }
1193
1193
  ),
1194
- showPreview ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1194
+ showPreview ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1195
1195
  SplitPanels,
1196
1196
  {
1197
- leftPanel: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1197
+ leftPanel: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1198
1198
  Editor,
1199
1199
  {
1200
1200
  value: content,
1201
1201
  onChange: internalSetContent,
1202
1202
  onScrollReady: handleScrollReady,
1203
1203
  onViewReady: handleViewReady,
1204
- onEditContainer: handleEditContainer
1204
+ onEditContainer: handleEditContainer,
1205
+ uploadConfig,
1206
+ onMessageNoUploadConfig: () => import_antd6.message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
1205
1207
  }
1206
1208
  ),
1207
- rightPanel: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1209
+ rightPanel: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1208
1210
  Preview_default,
1209
1211
  {
1210
1212
  content,
@@ -1213,17 +1215,19 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
1213
1215
  }
1214
1216
  )
1215
1217
  }
1216
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1218
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1217
1219
  Editor,
1218
1220
  {
1219
1221
  value: content,
1220
1222
  onChange: internalSetContent,
1221
1223
  onScrollReady: handleScrollReady,
1222
1224
  onViewReady: handleViewReady,
1223
- onEditContainer: handleEditContainer
1225
+ onEditContainer: handleEditContainer,
1226
+ uploadConfig,
1227
+ onMessageNoUploadConfig: () => import_antd6.message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
1224
1228
  }
1225
1229
  ),
1226
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1230
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1227
1231
  EditDialog,
1228
1232
  {
1229
1233
  visible: dialogVisible,
@@ -1237,10 +1241,10 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
1237
1241
  });
1238
1242
 
1239
1243
  // src/hooks/useRegisterToolbar.ts
1240
- var import_react9 = require("react");
1244
+ var import_react11 = require("react");
1241
1245
  function useRegisterToolbar(schemas) {
1242
1246
  const schemaList = Array.isArray(schemas) ? schemas : [schemas];
1243
- (0, import_react9.useEffect)(() => {
1247
+ (0, import_react11.useEffect)(() => {
1244
1248
  schemaList.forEach((schema) => {
1245
1249
  componentRegistry_default.register(schema.name, schema);
1246
1250
  });