remons-markdown-editor 1.0.0-beta.2 → 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,36 +553,39 @@ 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
- var import_remons_markdown_plugins = require("remons-markdown-plugins");
492
561
  var import_remons_render_markdown = __toESM(require("remons-render-markdown"), 1);
493
- var import_style = require("remons-markdown-plugins/style.css");
494
562
  var import_dist = require("remons-render-markdown/dist/index.css");
495
- var import_jsx_runtime3 = require("react/jsx-runtime");
496
- (0, import_remons_render_markdown.initHighlighter)(import_remons_render_markdown.languagesCommon);
497
- var Preview = (0, import_react2.forwardRef)(({ content }, ref) => {
498
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref, className: "preview", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_remons_render_markdown.default, { content, isSlotMermaid: true, excludedSelectors: import_remons_markdown_plugins.excludedSelectors, customRenderers: [(md) => md.use(import_remons_markdown_plugins.registerAll)] }) });
563
+ var import_jsx_runtime2 = require("react/jsx-runtime");
564
+ var Preview = (0, import_react2.forwardRef)(({ content, previewOptions }, ref) => {
565
+ (0, import_react2.useEffect)(() => {
566
+ (0, import_remons_render_markdown.initHighlighter)({
567
+ ...import_remons_render_markdown.languagesCommon,
568
+ ...(previewOptions == null ? void 0 : previewOptions.languages) ? previewOptions == null ? void 0 : previewOptions.languages : {}
569
+ });
570
+ }, [previewOptions == null ? void 0 : previewOptions.languages]);
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 }) });
499
572
  });
500
573
  Preview.displayName = "Preview";
501
574
  var Preview_default = Preview;
502
575
 
503
576
  // src/components/Panel/SplitPanels.tsx
504
- var import_antd2 = require("antd");
505
- var import_jsx_runtime4 = require("react/jsx-runtime");
577
+ var import_antd = require("antd");
578
+ var import_jsx_runtime3 = require("react/jsx-runtime");
506
579
  function SplitPanels({ leftPanel, rightPanel }) {
507
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_antd2.Splitter, { className: "split-panels", children: [
508
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_antd2.Splitter.Panel, { defaultSize: "50%", min: "20%", max: "80%", children: leftPanel }),
509
- /* @__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 })
510
583
  ] });
511
584
  }
512
585
 
513
586
  // src/components/Toolbar/index.tsx
514
587
  var import_react6 = require("react");
515
- var import_antd5 = require("antd");
588
+ var import_antd4 = require("antd");
516
589
  var import_icons4 = require("@ant-design/icons");
517
590
 
518
591
  // src/components/Toolbar/hooks/useToolbar.ts
@@ -520,102 +593,8 @@ var import_react3 = require("react");
520
593
 
521
594
  // src/components/Toolbar/config.ts
522
595
  var import_icons = require("@ant-design/icons");
523
-
524
- // src/components/Toolbar/buttons.tsx
525
- function insertAtCursor(view, before, after = "", placeholder = "") {
526
- const { from, to } = view.state.selection.main;
527
- const selectedText = view.state.doc.sliceString(from, to);
528
- const insert = selectedText ? `${before}${selectedText}${after}` : `${before}${placeholder}${after}`;
529
- view.dispatch({ changes: { from, to, insert } });
530
- if (!selectedText && placeholder) {
531
- view.dispatch({ selection: { anchor: from + before.length } });
532
- }
533
- }
534
- function insertAtLineStart(view, text) {
535
- const { from } = view.state.selection.main;
536
- const line = view.state.doc.lineAt(from);
537
- view.dispatch({ changes: { from: line.from, to: line.from, insert: text } });
538
- view.dispatch({ selection: { anchor: line.from + text.length } });
539
- }
540
- async function uploadFileToServer(file) {
541
- var _a;
542
- const formData = new FormData();
543
- formData.append("file", file);
544
- const response = await fetch("https://remons.cn:3008/content/uploadMarkdownImg", {
545
- method: "POST",
546
- body: formData
547
- });
548
- if (!response.ok) return null;
549
- const result = await response.json();
550
- if (result.code === 200 && result.success && ((_a = result.data) == null ? void 0 : _a.path)) {
551
- return `https://remons.cn:3008${result.data.path}`;
552
- }
553
- return null;
554
- }
555
- function uploadImage(view) {
556
- const input = document.createElement("input");
557
- input.type = "file";
558
- input.accept = "image/*";
559
- input.style.display = "none";
560
- input.addEventListener("change", async () => {
561
- if (!input.files || input.files.length === 0) return;
562
- const file = input.files[0];
563
- const { from, to } = view.state.selection.main;
564
- const selectedText = view.state.doc.sliceString(from, to) || "\u63CF\u8FF0";
565
- const url = await uploadFileToServer(file);
566
- if (url) {
567
- const insert = `![${selectedText}](${url})`;
568
- view.dispatch({ changes: { from, to, insert } });
569
- }
570
- input.remove();
571
- });
572
- document.body.appendChild(input);
573
- input.click();
574
- }
575
- function wrapSelection(view, prefix, suffix) {
576
- const { from, to, empty } = view.state.selection.main;
577
- if (empty) return;
578
- const selected = view.state.doc.sliceString(from, to);
579
- view.dispatch({ changes: { from, to, insert: `${prefix}${selected}${suffix}` } });
580
- }
581
- function toggleWrap(view, openTag, closeTag) {
582
- const { from, to, empty } = view.state.selection.main;
583
- const doc = view.state.doc;
584
- if (!empty) {
585
- const selected = doc.sliceString(from, to);
586
- if (selected.startsWith(openTag) && selected.endsWith(closeTag)) {
587
- const inner = selected.slice(openTag.length, -closeTag.length);
588
- view.dispatch({ changes: { from, to, insert: inner } });
589
- return;
590
- }
591
- }
592
- {
593
- const cursorPos = empty ? from : to;
594
- const line = doc.lineAt(cursorPos);
595
- const lineText = doc.sliceString(line.from, line.to);
596
- const openIdx = lineText.indexOf(openTag);
597
- const closeIdx = lineText.lastIndexOf(closeTag);
598
- if (openIdx >= 0 && closeIdx > openIdx) {
599
- const absOpen = line.from + openIdx;
600
- const absClose = line.from + closeIdx;
601
- if (cursorPos >= absOpen + openTag.length && cursorPos <= absClose) {
602
- const inner = doc.sliceString(absOpen + openTag.length, absClose);
603
- view.dispatch({
604
- changes: {
605
- from: absOpen,
606
- to: absClose + closeTag.length,
607
- insert: inner
608
- }
609
- });
610
- return;
611
- }
612
- }
613
- }
614
- wrapSelection(view, openTag, closeTag);
615
- }
616
-
617
- // src/components/Toolbar/config.ts
618
- function createToolbarConfig() {
596
+ function createToolbarConfig(options) {
597
+ const hasUploadConfig = isUploadConfigured(options);
619
598
  return [
620
599
  {
621
600
  id: "heading",
@@ -640,7 +619,14 @@ function createToolbarConfig() {
640
619
  { id: "ol", label: "\u6709\u5E8F\u5217\u8868", icon: import_icons.OrderedListOutlined, handler: (v) => insertAtLineStart(v, "1. ") },
641
620
  { separatorBefore: true },
642
621
  { id: "link", label: "\u94FE\u63A5", icon: import_icons.LinkOutlined, handler: (v) => insertAtCursor(v, "[", "](url)", "\u6587\u672C") },
643
- { 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
+ },
644
630
  { separatorBefore: true },
645
631
  { id: "code", label: "\u884C\u5185\u4EE3\u7801", icon: import_icons.FieldStringOutlined, handler: (v) => wrapSelection(v, "`", "`") },
646
632
  {
@@ -655,7 +641,6 @@ function createToolbarConfig() {
655
641
  }
656
642
  },
657
643
  { id: "quote", label: "\u5F15\u7528", icon: import_icons.BlockOutlined, handler: (v) => insertAtLineStart(v, "> ") },
658
- { id: "textContainer", label: "\u6587\u5B57\u5BB9\u5668", icon: import_icons.TagsOutlined, handler: (v) => toggleWrap(v, "<text-container>", "</text-container>") },
659
644
  {
660
645
  id: "hr",
661
646
  label: "\u5206\u5272\u7EBF",
@@ -669,13 +654,20 @@ function createToolbarConfig() {
669
654
  }
670
655
 
671
656
  // src/components/Toolbar/hooks/useToolbar.ts
672
- function useToolbar(editorView, extraItems) {
657
+ function useToolbar(editorView, options) {
658
+ const { extraItems, uploadConfig } = options || {};
673
659
  const config = (0, import_react3.useMemo)(() => {
674
- const base = createToolbarConfig();
675
- return extraItems ? [...base, { separatorBefore: true }, ...extraItems] : base;
676
- }, [extraItems]);
660
+ const base = createToolbarConfig(uploadConfig);
661
+ return extraItems ? [...base, ...extraItems] : base;
662
+ }, [extraItems, uploadConfig]);
677
663
  const handleClick = (0, import_react3.useCallback)((button) => {
678
- 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) {
679
671
  editorView.focus();
680
672
  button.handler(editorView, editorView.state);
681
673
  }
@@ -684,16 +676,16 @@ function useToolbar(editorView, extraItems) {
684
676
  }
685
677
 
686
678
  // src/components/Toolbar/renderers/ToolbarItemRenderer.tsx
687
- var import_antd3 = require("antd");
679
+ var import_antd2 = require("antd");
688
680
  var import_icons2 = require("@ant-design/icons");
689
- var import_jsx_runtime5 = require("react/jsx-runtime");
681
+ var import_jsx_runtime4 = require("react/jsx-runtime");
690
682
  function ToolbarItemRenderer({ item, onClick }) {
691
683
  const Icon = item.icon;
692
684
  if (item.children && item.children.length > 0) {
693
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
694
- item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-separator" }),
695
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-group", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
696
- 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,
697
689
  {
698
690
  menu: {
699
691
  items: item.children.map((child) => {
@@ -701,10 +693,10 @@ function ToolbarItemRenderer({ item, onClick }) {
701
693
  return {
702
694
  type: "item",
703
695
  key: (_a = child.id) != null ? _a : "",
704
- 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: [
705
697
  child.icon && (() => {
706
698
  const ChildIcon = child.icon;
707
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChildIcon, {});
699
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChildIcon, {});
708
700
  })(),
709
701
  " ",
710
702
  child.label
@@ -713,34 +705,35 @@ function ToolbarItemRenderer({ item, onClick }) {
713
705
  })
714
706
  },
715
707
  placement: "bottomLeft",
716
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_antd3.Button, { className: "toolbar-button", type: "text", size: "small", title: item.label, children: [
717
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, {}),
718
- /* @__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 } })
719
711
  ] })
720
712
  }
721
- ) })
713
+ ) }) })
722
714
  ] });
723
715
  }
724
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
725
- item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-separator" }),
726
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-group", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
727
- 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,
728
721
  {
729
722
  id: item.id,
730
723
  className: "toolbar-button",
731
724
  type: "text",
732
725
  size: "small",
733
- title: item.label,
734
- onClick: () => onClick(item),
735
- 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, {})
736
729
  }
737
- ) })
730
+ ) }) })
738
731
  ] });
739
732
  }
740
733
 
741
734
  // src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx
742
735
  var import_react5 = __toESM(require("react"), 1);
743
- var import_antd4 = require("antd");
736
+ var import_antd3 = require("antd");
744
737
  var import_icons3 = require("@ant-design/icons");
745
738
 
746
739
  // src/components/Toolbar/renderers/TablePicker/index.tsx
@@ -750,24 +743,27 @@ var import_react4 = require("react");
750
743
  var index_module_default = {};
751
744
 
752
745
  // src/components/Toolbar/renderers/TablePicker/index.tsx
753
- var import_jsx_runtime6 = require("react/jsx-runtime");
754
- var MAX_ROW = 12;
755
- var MAX_COL = 12;
756
- var REFRESH_TIME = 200;
746
+ var import_jsx_runtime5 = require("react/jsx-runtime");
747
+ var MAX_ROW = 8;
748
+ var MAX_COL = 8;
757
749
  function TablePicker({ onSelect }) {
758
750
  const [hoverSize, setHoverSize] = (0, import_react4.useState)({ row: 0, col: 0 });
759
- const [prevHoverSize, setPrevHoverSize] = (0, import_react4.useState)({ row: 0, col: 0 });
760
751
  const [confirmedSize, setConfirmedSize] = (0, import_react4.useState)({ row: 0, col: 0 });
761
752
  const maxColRef = (0, import_react4.useRef)(0);
753
+ const prevRowRef = (0, import_react4.useRef)(0);
762
754
  const timerIdRef = (0, import_react4.useRef)(null);
763
755
  const refreshTimerRef = (0, import_react4.useRef)(null);
764
756
  const commitRef = (0, import_react4.useRef)(null);
765
- const clearTimer = (0, import_react4.useCallback)((flushFn) => {
757
+ const clearTimer = (0, import_react4.useCallback)(() => {
766
758
  if (timerIdRef.current) {
759
+ clearTimeout(timerIdRef.current);
767
760
  timerIdRef.current = null;
768
761
  }
769
- if (flushFn && maxColRef.current > 0) {
770
- 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;
771
767
  }
772
768
  }, []);
773
769
  (0, import_react4.useEffect)(
@@ -778,25 +774,21 @@ function TablePicker({ onSelect }) {
778
774
  []
779
775
  );
780
776
  function extend(col, row) {
777
+ if (row < prevRowRef.current || col < maxColRef.current) {
778
+ maxColRef.current = 0;
779
+ }
781
780
  if (col > maxColRef.current) {
782
781
  maxColRef.current = col;
783
- setHoverSize({ row, col });
784
- setConfirmedSize({ row, col });
785
- } else {
786
- setHoverSize({ row, col });
787
- if (row !== confirmedSize.row) {
788
- setConfirmedSize({ row, col: maxColRef.current });
789
- }
790
- if (row === 1) {
791
- commitRef.current = { cols: col, rows: 1 };
792
- setConfirmedSize({ row: 1, col });
793
- }
782
+ }
783
+ setHoverSize({ row, col });
784
+ setConfirmedSize({ row, col: maxColRef.current });
785
+ if (row === 1) {
786
+ commitRef.current = { cols: maxColRef.current, rows: 1 };
794
787
  }
795
788
  }
796
789
  function flash(col, dir, limit, finalCallback) {
797
790
  let current = dir === 1 ? 1 : limit;
798
791
  const step = dir === 1 ? 1 : -1;
799
- let timer = 0;
800
792
  refreshTimerRef.current = setInterval(() => {
801
793
  setHoverSize({ row: current, col });
802
794
  if (current > confirmedSize.row || dir === -1) {
@@ -805,45 +797,43 @@ function TablePicker({ onSelect }) {
805
797
  current += step;
806
798
  if (dir === 1 && current > limit || dir === -1 && current < 1) {
807
799
  clearInterval(refreshTimerRef.current);
808
- setHoverSize({ row: current - step, col });
809
- finalCallback(col, current - step);
800
+ refreshTimerRef.current = null;
801
+ const finalRow = current - step;
802
+ setHoverSize({ row: finalRow, col });
803
+ finalCallback(col, finalRow);
810
804
  }
811
805
  }, 50);
812
- clearTimeout(timer);
813
- return () => {
814
- clearInterval(refreshTimerRef.current);
815
- };
816
806
  }
817
807
  function onCellEnterCheck(index) {
818
808
  clearTimer();
809
+ clearFlash();
819
810
  const row = Math.floor(index / MAX_COL) + 1;
820
811
  const col = index % MAX_COL + 1;
821
- const isDown = row > hoverSize.row;
822
- 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) {
823
817
  extend(col, row);
824
- } else {
825
- if (isDown && (col > maxColRef.current || maxColRef.current >= MAX_COL)) {
818
+ } else if (isDown) {
819
+ if (col > maxColRef.current || maxColRef.current >= MAX_COL) {
826
820
  const finalRow = Math.min(row, MAX_ROW);
827
821
  flash(col, 1, finalRow, (c, r) => {
828
822
  setConfirmedSize({ row: r, col: c });
829
823
  });
830
- } else if (!isDown) {
831
- flash(col, -1, row, (c, r) => {
832
- setConfirmedSize({ row: r, col: c });
833
- if (r === 1) {
834
- commitRef.current = { cols: Math.min(MAX_COL, col), rows: 1 };
835
- setConfirmedSize({ row: 1, col: Math.min(MAX_COL, col) });
836
- }
837
- });
838
- }
839
- }
840
- if (col >= MAX_COL && row >= prevHoverSize.row) {
841
- if (row !== prevHoverSize.row) {
842
- maxColRef.current = 0;
843
- setPrevHoverSize({ row: 0, col: 0 });
824
+ } else {
825
+ extend(col, row);
844
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
+ });
845
836
  }
846
- setPrevHoverSize({ row, col });
847
837
  }
848
838
  function onMouseDown() {
849
839
  const size = commitRef.current || { cols: maxColRef.current, rows: confirmedSize.row };
@@ -851,13 +841,13 @@ function TablePicker({ onSelect }) {
851
841
  onSelect(size);
852
842
  }
853
843
  }
854
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
844
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
855
845
  "div",
856
846
  {
857
847
  className: index_module_default.container,
858
848
  onMouseDown,
859
849
  children: [
860
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
850
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
861
851
  "div",
862
852
  {
863
853
  className: index_module_default.grid,
@@ -866,7 +856,7 @@ function TablePicker({ onSelect }) {
866
856
  const row = Math.floor(i / MAX_COL) + 1;
867
857
  const col = i % MAX_COL + 1;
868
858
  const isSelected = row <= hoverSize.row && col <= maxColRef.current;
869
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
859
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
870
860
  "div",
871
861
  {
872
862
  className: `${index_module_default.cell} ${isSelected ? index_module_default.cellActive : ""}`,
@@ -877,7 +867,7 @@ function TablePicker({ onSelect }) {
877
867
  })
878
868
  }
879
869
  ),
880
- /* @__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: [
881
871
  hoverSize.row,
882
872
  " x ",
883
873
  maxColRef.current || 1
@@ -888,7 +878,7 @@ function TablePicker({ onSelect }) {
888
878
  }
889
879
 
890
880
  // src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx
891
- var import_jsx_runtime7 = require("react/jsx-runtime");
881
+ var import_jsx_runtime6 = require("react/jsx-runtime");
892
882
  function TableDropdown({ editorView }) {
893
883
  const [open, setOpen] = import_react5.default.useState(false);
894
884
  const openCountRef = (0, import_react5.useRef)(0);
@@ -907,15 +897,15 @@ ${dataRows}
907
897
  const { from } = editorView.state.selection.main;
908
898
  editorView.dispatch({ changes: { from, insert: table } });
909
899
  }, [editorView]);
910
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
911
- import_antd4.Dropdown,
900
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
901
+ import_antd3.Dropdown,
912
902
  {
913
903
  trigger: ["click"],
914
904
  open,
915
905
  onOpenChange: setOpen,
916
906
  popupRender: () => {
917
907
  openCountRef.current++;
918
- 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)(
919
909
  TablePicker,
920
910
  {
921
911
  onSelect: (size) => {
@@ -927,44 +917,41 @@ ${dataRows}
927
917
  ) });
928
918
  },
929
919
  placement: "bottomLeft",
930
- 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, {}) })
931
921
  }
932
922
  );
933
923
  }
934
924
 
935
925
  // src/components/Toolbar/index.tsx
936
- var import_jsx_runtime8 = require("react/jsx-runtime");
937
- function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
938
- 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 });
939
929
  const renderFullscreen = (0, import_react6.useCallback)(() => {
940
930
  if (!onToggleFullscreen) return null;
941
931
  const Icon = fullscreen ? import_icons4.FullscreenExitOutlined : import_icons4.FullscreenOutlined;
942
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
943
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "toolbar-separator" }),
944
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
945
- import_antd5.Button,
946
- {
947
- className: "toolbar-button",
948
- type: "text",
949
- size: "small",
950
- title: fullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
951
- onClick: () => {
952
- onToggleFullscreen();
953
- },
954
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, {})
955
- }
956
- )
957
- ] });
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
+ );
958
945
  }, [fullscreen, onToggleFullscreen]);
959
946
  function isSeparator(item) {
960
947
  return !!(!item.id && item.separatorBefore);
961
948
  }
962
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "toolbar", children: [
949
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "toolbar", children: [
963
950
  config.map((item, idx) => {
964
951
  if (isSeparator(item)) {
965
- 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}`);
966
953
  }
967
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
954
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
968
955
  ToolbarItemRenderer,
969
956
  {
970
957
  item,
@@ -973,16 +960,15 @@ function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
973
960
  item.id
974
961
  );
975
962
  }),
976
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "toolbar-separator" }),
977
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TableDropdown, { editorView }),
963
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableDropdown, { editorView }),
978
964
  renderFullscreen()
979
965
  ] });
980
966
  }
981
967
 
982
968
  // src/components/Dialog/EditDialog.tsx
983
969
  var import_react7 = require("react");
984
- var import_antd6 = require("antd");
985
- var import_jsx_runtime9 = require("react/jsx-runtime");
970
+ var import_antd5 = require("antd");
971
+ var import_jsx_runtime8 = require("react/jsx-runtime");
986
972
  function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
987
973
  const schema = componentRegistry_default.get(typeName);
988
974
  const [formProps, setFormProps] = (0, import_react7.useState)({});
@@ -1010,8 +996,8 @@ function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
1010
996
  if (!schema) {
1011
997
  return null;
1012
998
  }
1013
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1014
- import_antd6.Modal,
999
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1000
+ import_antd5.Modal,
1015
1001
  {
1016
1002
  title: `${schema.label} \u5C5E\u6027\u7F16\u8F91`,
1017
1003
  open: visible,
@@ -1051,35 +1037,11 @@ ${propsLine}
1051
1037
 
1052
1038
  // src/MarkdownEditor.tsx
1053
1039
  var import_icons5 = require("@ant-design/icons");
1054
- var import_jsx_runtime10 = require("react/jsx-runtime");
1055
- var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEditor({
1056
- defaultValue = "",
1057
- value,
1058
- onChange,
1059
- showPreview = true,
1060
- fullscreen: fullscreenProp = false
1061
- }, ref) {
1062
- const [content, setContent] = (0, import_react8.useState)(value != null ? value : defaultValue);
1063
- const [editorView, setEditorView] = (0, import_react8.useState)(null);
1064
- const scrollDOMRef = (0, import_react8.useRef)(null);
1065
- const previewElRef = (0, import_react8.useRef)(null);
1040
+
1041
+ // src/hooks/useFullscreen.ts
1042
+ var import_react8 = require("react");
1043
+ function useFullscreen() {
1066
1044
  const [fullscreen, setFullscreen] = (0, import_react8.useState)(false);
1067
- const [dialogVisible, setDialogVisible] = (0, import_react8.useState)(false);
1068
- const [dialogTypeName, setDialogTypeName] = (0, import_react8.useState)("");
1069
- const [dialogProps, setDialogProps] = (0, import_react8.useState)({});
1070
- const [dialogPropsRange, setDialogPropsRange] = (0, import_react8.useState)([0, 0]);
1071
- (0, import_react8.useEffect)(() => {
1072
- if (value !== void 0) {
1073
- setContent(value);
1074
- }
1075
- }, [value]);
1076
- const internalSetContent = (0, import_react8.useCallback)((newContent) => {
1077
- setContent(newContent);
1078
- onChange == null ? void 0 : onChange(newContent);
1079
- }, [onChange]);
1080
- (0, import_react8.useImperativeHandle)(ref, () => ({
1081
- getContent: () => content
1082
- }), [content]);
1083
1045
  const toggleFullscreen = (0, import_react8.useCallback)(() => {
1084
1046
  if (document.fullscreenElement) {
1085
1047
  document.exitFullscreen().catch((e) => {
@@ -1106,40 +1068,29 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
1106
1068
  document.removeEventListener("webkitfullscreenchange", handleFullscreenChange);
1107
1069
  };
1108
1070
  }, []);
1109
- const extraToolbarItems = componentRegistry_default.getAll().map((schema) => {
1110
- var _a, _b;
1111
- const defaultProps = {};
1112
- for (const field of schema.fields) {
1113
- defaultProps[field.key] = (_a = field.defaultValue) != null ? _a : "";
1114
- }
1115
- const iconNode = (_b = schema.icon) != null ? _b : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_icons5.ReadOutlined, {});
1116
- return {
1117
- id: `container-${schema.name}`,
1118
- label: schema.label,
1119
- icon: () => iconNode,
1120
- handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
1121
- };
1122
- });
1123
- const handleScrollReady = (0, import_react8.useCallback)((scrollDOM) => {
1124
- scrollDOMRef.current = scrollDOM;
1125
- }, []);
1126
- (0, import_react8.useEffect)(() => {
1127
- const source = scrollDOMRef.current;
1128
- 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;
1129
1081
  if (!source || !target) return;
1130
- let rafId = null;
1131
1082
  const listener = () => {
1132
- if (rafId) return;
1133
- rafId = requestAnimationFrame(() => {
1134
- const s = scrollDOMRef.current;
1135
- const t = previewElRef.current;
1083
+ if (rafIdRef.current) return;
1084
+ rafIdRef.current = requestAnimationFrame(() => {
1085
+ const s = sourceRef.current;
1086
+ const t = targetRef.current;
1136
1087
  if (!s || !t) {
1137
- rafId = null;
1088
+ rafIdRef.current = null;
1138
1089
  return;
1139
1090
  }
1140
1091
  const sourceScrollHeight = s.scrollHeight - s.clientHeight;
1141
1092
  if (sourceScrollHeight <= 0) {
1142
- rafId = null;
1093
+ rafIdRef.current = null;
1143
1094
  return;
1144
1095
  }
1145
1096
  const ratio = s.scrollTop / sourceScrollHeight;
@@ -1147,78 +1098,136 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
1147
1098
  if (targetScrollHeight > 0) {
1148
1099
  t.scrollTop = ratio * targetScrollHeight;
1149
1100
  }
1150
- rafId = null;
1101
+ rafIdRef.current = null;
1151
1102
  });
1152
1103
  };
1153
1104
  source.addEventListener("scroll", listener, { passive: true });
1154
1105
  return () => {
1155
1106
  source.removeEventListener("scroll", listener);
1156
- 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)
1157
1157
  };
1158
- }, [previewElRef.current]);
1159
- 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) => {
1160
1163
  previewElRef.current = node;
1161
1164
  }, []);
1162
- const handleViewReady = (0, import_react8.useCallback)((view) => {
1165
+ const handleViewReady = (0, import_react10.useCallback)((view) => {
1163
1166
  setEditorView(view);
1164
1167
  }, []);
1165
- const handleEditContainer = (0, import_react8.useCallback)((_view, typeName, propsLineFrom, propsLineTo, currentProps) => {
1168
+ const handleEditContainer = (0, import_react10.useCallback)((_view, typeName, propsLineFrom, propsLineTo, currentProps) => {
1166
1169
  setDialogTypeName(typeName);
1167
1170
  setDialogProps({ ...currentProps });
1168
1171
  setDialogPropsRange([propsLineFrom, propsLineTo]);
1169
1172
  setDialogVisible(true);
1170
1173
  }, []);
1171
- const handleDialogSave = (0, import_react8.useCallback)((newProps) => {
1174
+ const handleDialogSave = (0, import_react10.useCallback)((newProps) => {
1172
1175
  setDialogVisible(false);
1173
1176
  if (editorView) {
1174
1177
  updateContainerProps(editorView, dialogPropsRange[0], dialogPropsRange[1], newProps);
1175
1178
  }
1176
1179
  }, [editorView, dialogPropsRange]);
1177
- const handleDialogClose = (0, import_react8.useCallback)(() => {
1180
+ const handleDialogClose = (0, import_react10.useCallback)(() => {
1178
1181
  setDialogVisible(false);
1179
1182
  }, []);
1180
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: fullscreen ? "app fullscreen" : "app", children: [
1181
- /* @__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)(
1182
1185
  Toolbar,
1183
1186
  {
1184
1187
  editorView,
1185
1188
  fullscreen,
1186
1189
  onToggleFullscreen: fullscreenProp ? toggleFullscreen : void 0,
1187
- extraItems: extraToolbarItems
1190
+ extraItems: extraToolbarItems,
1191
+ uploadConfig
1188
1192
  }
1189
1193
  ),
1190
- showPreview ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1194
+ showPreview ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1191
1195
  SplitPanels,
1192
1196
  {
1193
- leftPanel: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1197
+ leftPanel: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1194
1198
  Editor,
1195
1199
  {
1196
1200
  value: content,
1197
1201
  onChange: internalSetContent,
1198
1202
  onScrollReady: handleScrollReady,
1199
1203
  onViewReady: handleViewReady,
1200
- onEditContainer: handleEditContainer
1204
+ onEditContainer: handleEditContainer,
1205
+ uploadConfig,
1206
+ onMessageNoUploadConfig: () => import_antd6.message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
1201
1207
  }
1202
1208
  ),
1203
- rightPanel: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1209
+ rightPanel: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1204
1210
  Preview_default,
1205
1211
  {
1206
1212
  content,
1207
- ref: handlePreviewRef
1213
+ ref: handlePreviewRef,
1214
+ previewOptions
1208
1215
  }
1209
1216
  )
1210
1217
  }
1211
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1218
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1212
1219
  Editor,
1213
1220
  {
1214
1221
  value: content,
1215
1222
  onChange: internalSetContent,
1216
1223
  onScrollReady: handleScrollReady,
1217
1224
  onViewReady: handleViewReady,
1218
- onEditContainer: handleEditContainer
1225
+ onEditContainer: handleEditContainer,
1226
+ uploadConfig,
1227
+ onMessageNoUploadConfig: () => import_antd6.message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
1219
1228
  }
1220
1229
  ),
1221
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1230
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1222
1231
  EditDialog,
1223
1232
  {
1224
1233
  visible: dialogVisible,
@@ -1232,10 +1241,10 @@ var MarkdownEditor_default = (0, import_react8.forwardRef)(function MarkdownEdit
1232
1241
  });
1233
1242
 
1234
1243
  // src/hooks/useRegisterToolbar.ts
1235
- var import_react9 = require("react");
1244
+ var import_react11 = require("react");
1236
1245
  function useRegisterToolbar(schemas) {
1237
1246
  const schemaList = Array.isArray(schemas) ? schemas : [schemas];
1238
- (0, import_react9.useEffect)(() => {
1247
+ (0, import_react11.useEffect)(() => {
1239
1248
  schemaList.forEach((schema) => {
1240
1249
  componentRegistry_default.register(schema.name, schema);
1241
1250
  });