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.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/MarkdownEditor.tsx
2
- import { useState as useState3, useRef as useRef4, useCallback as useCallback5, useEffect as useEffect4, forwardRef as forwardRef2, useImperativeHandle } from "react";
2
+ import { useState as useState4, useRef as useRef5, useCallback as useCallback6, useEffect as useEffect6, forwardRef as forwardRef2, useImperativeHandle } from "react";
3
+ import { message } from "antd";
3
4
 
4
5
  // src/components/Editor/index.tsx
5
6
  import { useEffect, useRef } from "react";
@@ -11,6 +12,7 @@ import { EditorView, keymap } from "@codemirror/view";
11
12
  import { defaultKeymap, indentWithTab } from "@codemirror/commands";
12
13
  import { markdown } from "@codemirror/lang-markdown";
13
14
  import { oneDark } from "@codemirror/theme-one-dark";
15
+ import { Facet as Facet2 } from "@codemirror/state";
14
16
 
15
17
  // src/components/Editor/containerWidget.ts
16
18
  import { ViewPlugin, Decoration, WidgetType } from "@codemirror/view";
@@ -88,8 +90,6 @@ function findContainers(docText) {
88
90
  }
89
91
 
90
92
  // src/registry/componentRegistry.tsx
91
- import { Input, Select } from "antd";
92
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
93
93
  var ComponentRegistry = class {
94
94
  constructor() {
95
95
  this.schemas = /* @__PURE__ */ new Map();
@@ -132,80 +132,6 @@ var ComponentRegistry = class {
132
132
  }
133
133
  };
134
134
  var componentRegistry = new ComponentRegistry();
135
- componentRegistry.register("linkCard", {
136
- name: "linkCard",
137
- label: "\u94FE\u63A5\u5361\u7247",
138
- fields: [
139
- { key: "title", defaultValue: "\u94FE\u63A5\u6807\u9898" },
140
- { key: "link", defaultValue: "https://example.com" },
141
- { key: "description", defaultValue: "\u94FE\u63A5\u63CF\u8FF0" },
142
- { key: "theme", defaultValue: "default" }
143
- ],
144
- renderDialog: (props, onChange) => /* @__PURE__ */ jsxs(Fragment, { children: [
145
- /* @__PURE__ */ jsxs("div", { style: { marginBottom: 12 }, children: [
146
- /* @__PURE__ */ jsx("div", { style: { marginBottom: 4 }, children: "\u6807\u9898" }),
147
- /* @__PURE__ */ jsx(Input, { value: props.title, onChange: (e) => onChange("title", e.target.value) })
148
- ] }),
149
- /* @__PURE__ */ jsxs("div", { style: { marginBottom: 12 }, children: [
150
- /* @__PURE__ */ jsx("div", { style: { marginBottom: 4 }, children: "\u94FE\u63A5" }),
151
- /* @__PURE__ */ jsx(Input, { value: props.link, onChange: (e) => onChange("link", e.target.value) })
152
- ] }),
153
- /* @__PURE__ */ jsxs("div", { style: { marginBottom: 12 }, children: [
154
- /* @__PURE__ */ jsx("div", { style: { marginBottom: 4 }, children: "\u63CF\u8FF0" }),
155
- /* @__PURE__ */ jsx(Input.TextArea, { rows: 3, value: props.description, onChange: (e) => onChange("description", e.target.value) })
156
- ] }),
157
- /* @__PURE__ */ jsxs("div", { children: [
158
- /* @__PURE__ */ jsx("div", { style: { marginBottom: 4 }, children: "\u4E3B\u9898" }),
159
- /* @__PURE__ */ jsx(
160
- Select,
161
- {
162
- value: props.theme,
163
- onChange: (v) => onChange("theme", v),
164
- options: [
165
- { label: "\u9ED8\u8BA4", value: "default" },
166
- { label: "\u4E3B\u8981", value: "primary" },
167
- { label: "\u8B66\u544A", value: "warning" }
168
- ]
169
- }
170
- )
171
- ] })
172
- ] })
173
- });
174
- componentRegistry.register("note", {
175
- name: "note",
176
- label: "\u6CE8\u91CA",
177
- fields: [
178
- { key: "type", defaultValue: "info" },
179
- { key: "title", defaultValue: "\u6CE8\u610F" },
180
- { key: "content", defaultValue: "\u5907\u6CE8\u5185\u5BB9" }
181
- ],
182
- renderDialog: (props, onChange) => /* @__PURE__ */ jsxs(Fragment, { children: [
183
- /* @__PURE__ */ jsxs("div", { style: { marginBottom: 12 }, children: [
184
- /* @__PURE__ */ jsx("div", { style: { marginBottom: 4 }, children: "\u7C7B\u578B" }),
185
- /* @__PURE__ */ jsx(
186
- Select,
187
- {
188
- value: props.type,
189
- onChange: (v) => onChange("type", v),
190
- options: [
191
- { label: "\u4FE1\u606F", value: "info" },
192
- { label: "\u63D0\u793A", value: "tip" },
193
- { label: "\u8B66\u544A", value: "warning" },
194
- { label: "\u5371\u9669", value: "danger" }
195
- ]
196
- }
197
- )
198
- ] }),
199
- /* @__PURE__ */ jsxs("div", { style: { marginBottom: 12 }, children: [
200
- /* @__PURE__ */ jsx("div", { style: { marginBottom: 4 }, children: "\u6807\u9898" }),
201
- /* @__PURE__ */ jsx(Input, { value: props.title, onChange: (e) => onChange("title", e.target.value) })
202
- ] }),
203
- /* @__PURE__ */ jsxs("div", { children: [
204
- /* @__PURE__ */ jsx("div", { style: { marginBottom: 4 }, children: "\u5185\u5BB9" }),
205
- /* @__PURE__ */ jsx(Input.TextArea, { rows: 3, value: props.content, onChange: (e) => onChange("content", e.target.value) })
206
- ] })
207
- ] })
208
- });
209
135
  var componentRegistry_default = componentRegistry;
210
136
 
211
137
  // src/components/Editor/containerWidget.ts
@@ -351,13 +277,151 @@ var containerWidgetPlugin = ViewPlugin.fromClass(
351
277
  }
352
278
  );
353
279
 
280
+ // src/components/Toolbar/buttons.tsx
281
+ function insertAtCursor(view, before, after = "", placeholder = "") {
282
+ const { from, to } = view.state.selection.main;
283
+ const selectedText = view.state.doc.sliceString(from, to);
284
+ const insert = selectedText ? `${before}${selectedText}${after}` : `${before}${placeholder}${after}`;
285
+ view.dispatch({ changes: { from, to, insert } });
286
+ if (!selectedText && placeholder) {
287
+ view.dispatch({ selection: { anchor: from + before.length } });
288
+ }
289
+ }
290
+ function insertAtLineStart(view, text) {
291
+ const { from } = view.state.selection.main;
292
+ const line = view.state.doc.lineAt(from);
293
+ view.dispatch({ changes: { from: line.from, to: line.from, insert: text } });
294
+ view.dispatch({ selection: { anchor: line.from + text.length } });
295
+ }
296
+ async function uploadFileToServer(file, uploadConfig) {
297
+ var _a;
298
+ if (uploadConfig.onUploadImage) {
299
+ return uploadConfig.onUploadImage(file);
300
+ }
301
+ if (uploadConfig.uploadUrl) {
302
+ const formData = new FormData();
303
+ formData.append("file", file);
304
+ const response = await fetch(uploadConfig.uploadUrl, {
305
+ method: "POST",
306
+ body: formData
307
+ });
308
+ if (!response.ok) return null;
309
+ const result = await response.json();
310
+ if (uploadConfig.onGetUploadUrl) {
311
+ return uploadConfig.onGetUploadUrl(result);
312
+ }
313
+ return ((_a = result.data) == null ? void 0 : _a.path) || null;
314
+ }
315
+ return null;
316
+ }
317
+ function isUploadConfigured(uploadConfig) {
318
+ return !!((uploadConfig == null ? void 0 : uploadConfig.uploadUrl) || (uploadConfig == null ? void 0 : uploadConfig.onUploadImage));
319
+ }
320
+ function uploadImage(view, uploadConfig) {
321
+ const input = document.createElement("input");
322
+ input.type = "file";
323
+ input.accept = "image/*";
324
+ input.style.display = "none";
325
+ input.addEventListener("change", async () => {
326
+ if (!input.files || input.files.length === 0) return;
327
+ const file = input.files[0];
328
+ const { from, to } = view.state.selection.main;
329
+ const selectedText = view.state.doc.sliceString(from, to) || "\u63CF\u8FF0";
330
+ const url = await uploadFileToServer(file, uploadConfig);
331
+ if (url) {
332
+ const insert = `![${selectedText}](${url})`;
333
+ view.dispatch({ changes: { from, to, insert } });
334
+ }
335
+ input.remove();
336
+ });
337
+ document.body.appendChild(input);
338
+ input.click();
339
+ }
340
+ function wrapSelection(view, prefix, suffix) {
341
+ const { from, to, empty } = view.state.selection.main;
342
+ if (empty) return;
343
+ const selected = view.state.doc.sliceString(from, to);
344
+ view.dispatch({ changes: { from, to, insert: `${prefix}${selected}${suffix}` } });
345
+ }
346
+ function toggleWrap(view, openTag, closeTag) {
347
+ const { from, to, empty } = view.state.selection.main;
348
+ const doc = view.state.doc;
349
+ if (!empty) {
350
+ const selected = doc.sliceString(from, to);
351
+ if (selected.startsWith(openTag) && selected.endsWith(closeTag)) {
352
+ const inner = selected.slice(openTag.length, -closeTag.length);
353
+ view.dispatch({ changes: { from, to, insert: inner } });
354
+ return;
355
+ }
356
+ }
357
+ {
358
+ const cursorPos = empty ? from : to;
359
+ const line = doc.lineAt(cursorPos);
360
+ const lineText = doc.sliceString(line.from, line.to);
361
+ const openIdx = lineText.indexOf(openTag);
362
+ const closeIdx = lineText.lastIndexOf(closeTag);
363
+ if (openIdx >= 0 && closeIdx > openIdx) {
364
+ const absOpen = line.from + openIdx;
365
+ const absClose = line.from + closeIdx;
366
+ if (cursorPos >= absOpen + openTag.length && cursorPos <= absClose) {
367
+ const inner = doc.sliceString(absOpen + openTag.length, absClose);
368
+ view.dispatch({
369
+ changes: {
370
+ from: absOpen,
371
+ to: absClose + closeTag.length,
372
+ insert: inner
373
+ }
374
+ });
375
+ return;
376
+ }
377
+ }
378
+ }
379
+ wrapSelection(view, openTag, closeTag);
380
+ }
381
+
354
382
  // src/components/Editor/codemirrorExtensions.ts
383
+ var uploadConfigFacet = Facet2.define({
384
+ combine: (values) => values[values.length - 1]
385
+ });
386
+ var messageFacet = Facet2.define({
387
+ combine: (values) => values[values.length - 1]
388
+ });
389
+ var pasteHandler = EditorView.domEventHandlers({
390
+ paste: (event, view) => {
391
+ var _a;
392
+ const files = (_a = event.clipboardData) == null ? void 0 : _a.files;
393
+ if (!files) return false;
394
+ const imageFiles = Array.from(files).filter((f) => f.type.startsWith("image/"));
395
+ if (imageFiles.length === 0) return false;
396
+ event.preventDefault();
397
+ const uploadConfig = view.state.facet(uploadConfigFacet);
398
+ const showMessage = view.state.facet(messageFacet);
399
+ if (!isUploadConfigured(uploadConfig)) {
400
+ showMessage == null ? void 0 : showMessage();
401
+ return true;
402
+ }
403
+ const { from, to } = view.state.selection.main;
404
+ let offset = 0;
405
+ for (const file of imageFiles) {
406
+ uploadFileToServer(file, uploadConfig).then((url) => {
407
+ if (url) {
408
+ const name = file.name.replace(/\.[^.]+$/, "");
409
+ const insert = `![${name}](${url})`;
410
+ view.dispatch({ changes: { from: from + offset, to: to + offset, insert } });
411
+ offset += insert.length;
412
+ }
413
+ });
414
+ }
415
+ return true;
416
+ }
417
+ });
355
418
  var getCodeMirrorExtensions = (opts) => {
356
419
  const extensions = [
357
420
  markdown(),
358
421
  oneDark,
359
422
  keymap.of([...defaultKeymap, indentWithTab]),
360
423
  EditorView.lineWrapping,
424
+ pasteHandler,
361
425
  EditorView.baseTheme({
362
426
  ".cm-scroller": {
363
427
  height: "100%",
@@ -402,19 +466,25 @@ var getCodeMirrorExtensions = (opts) => {
402
466
  if (opts == null ? void 0 : opts.onEditContainer) {
403
467
  extensions.push(containerEditCallback.of(opts.onEditContainer));
404
468
  }
469
+ if (opts == null ? void 0 : opts.uploadConfig) {
470
+ extensions.push(uploadConfigFacet.of(opts.uploadConfig));
471
+ }
472
+ if (opts == null ? void 0 : opts.onMessageNoUploadConfig) {
473
+ extensions.push(messageFacet.of(opts.onMessageNoUploadConfig));
474
+ }
405
475
  return extensions;
406
476
  };
407
477
 
408
478
  // src/components/Editor/index.tsx
409
- import { jsx as jsx2 } from "react/jsx-runtime";
410
- function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer }) {
479
+ import { jsx } from "react/jsx-runtime";
480
+ function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer, uploadConfig, onMessageNoUploadConfig }) {
411
481
  const editorRef = useRef(null);
412
482
  const viewRef = useRef(null);
413
483
  useEffect(() => {
414
484
  if (!editorRef.current) return;
415
485
  const extensions = [
416
486
  basicSetup,
417
- ...getCodeMirrorExtensions({ onEditContainer }),
487
+ ...getCodeMirrorExtensions({ onEditContainer, uploadConfig, onMessageNoUploadConfig }),
418
488
  EditorView2.updateListener.of((update) => {
419
489
  if (update.docChanged) {
420
490
  onChange(update.state.doc.toString());
@@ -447,14 +517,14 @@ function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer }
447
517
  });
448
518
  }
449
519
  }, [value]);
450
- return /* @__PURE__ */ jsx2("div", { ref: editorRef, className: "editor" });
520
+ return /* @__PURE__ */ jsx("div", { ref: editorRef, className: "editor" });
451
521
  }
452
522
 
453
523
  // src/components/Preview/index.tsx
454
524
  import { forwardRef, useEffect as useEffect2 } from "react";
455
525
  import RenderMarkdown, { languagesCommon, initHighlighter } from "remons-render-markdown";
456
526
  import "remons-render-markdown/dist/index.css";
457
- import { jsx as jsx3 } from "react/jsx-runtime";
527
+ import { jsx as jsx2 } from "react/jsx-runtime";
458
528
  var Preview = forwardRef(({ content, previewOptions }, ref) => {
459
529
  useEffect2(() => {
460
530
  initHighlighter({
@@ -462,18 +532,18 @@ var Preview = forwardRef(({ content, previewOptions }, ref) => {
462
532
  ...(previewOptions == null ? void 0 : previewOptions.languages) ? previewOptions == null ? void 0 : previewOptions.languages : {}
463
533
  });
464
534
  }, [previewOptions == null ? void 0 : previewOptions.languages]);
465
- return /* @__PURE__ */ jsx3("div", { ref, className: "preview", children: /* @__PURE__ */ jsx3(RenderMarkdown, { content, ...previewOptions }) });
535
+ return /* @__PURE__ */ jsx2("div", { ref, className: "preview", children: /* @__PURE__ */ jsx2(RenderMarkdown, { content, ...previewOptions }) });
466
536
  });
467
537
  Preview.displayName = "Preview";
468
538
  var Preview_default = Preview;
469
539
 
470
540
  // src/components/Panel/SplitPanels.tsx
471
541
  import { Splitter } from "antd";
472
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
542
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
473
543
  function SplitPanels({ leftPanel, rightPanel }) {
474
- return /* @__PURE__ */ jsxs2(Splitter, { className: "split-panels", children: [
475
- /* @__PURE__ */ jsx4(Splitter.Panel, { defaultSize: "50%", min: "20%", max: "80%", children: leftPanel }),
476
- /* @__PURE__ */ jsx4(Splitter.Panel, { children: rightPanel })
544
+ return /* @__PURE__ */ jsxs(Splitter, { className: "split-panels", children: [
545
+ /* @__PURE__ */ jsx3(Splitter.Panel, { defaultSize: "50%", min: "20%", max: "80%", children: leftPanel }),
546
+ /* @__PURE__ */ jsx3(Splitter.Panel, { children: rightPanel })
477
547
  ] });
478
548
  }
479
549
 
@@ -499,105 +569,10 @@ import {
499
569
  BlockOutlined,
500
570
  MinusOutlined,
501
571
  FieldStringOutlined,
502
- FontSizeOutlined,
503
- TagsOutlined
572
+ FontSizeOutlined
504
573
  } from "@ant-design/icons";
505
-
506
- // src/components/Toolbar/buttons.tsx
507
- function insertAtCursor(view, before, after = "", placeholder = "") {
508
- const { from, to } = view.state.selection.main;
509
- const selectedText = view.state.doc.sliceString(from, to);
510
- const insert = selectedText ? `${before}${selectedText}${after}` : `${before}${placeholder}${after}`;
511
- view.dispatch({ changes: { from, to, insert } });
512
- if (!selectedText && placeholder) {
513
- view.dispatch({ selection: { anchor: from + before.length } });
514
- }
515
- }
516
- function insertAtLineStart(view, text) {
517
- const { from } = view.state.selection.main;
518
- const line = view.state.doc.lineAt(from);
519
- view.dispatch({ changes: { from: line.from, to: line.from, insert: text } });
520
- view.dispatch({ selection: { anchor: line.from + text.length } });
521
- }
522
- async function uploadFileToServer(file) {
523
- var _a;
524
- const formData = new FormData();
525
- formData.append("file", file);
526
- const response = await fetch("https://remons.cn:3008/content/uploadMarkdownImg", {
527
- method: "POST",
528
- body: formData
529
- });
530
- if (!response.ok) return null;
531
- const result = await response.json();
532
- if (result.code === 200 && result.success && ((_a = result.data) == null ? void 0 : _a.path)) {
533
- return `https://remons.cn:3008${result.data.path}`;
534
- }
535
- return null;
536
- }
537
- function uploadImage(view) {
538
- const input = document.createElement("input");
539
- input.type = "file";
540
- input.accept = "image/*";
541
- input.style.display = "none";
542
- input.addEventListener("change", async () => {
543
- if (!input.files || input.files.length === 0) return;
544
- const file = input.files[0];
545
- const { from, to } = view.state.selection.main;
546
- const selectedText = view.state.doc.sliceString(from, to) || "\u63CF\u8FF0";
547
- const url = await uploadFileToServer(file);
548
- if (url) {
549
- const insert = `![${selectedText}](${url})`;
550
- view.dispatch({ changes: { from, to, insert } });
551
- }
552
- input.remove();
553
- });
554
- document.body.appendChild(input);
555
- input.click();
556
- }
557
- function wrapSelection(view, prefix, suffix) {
558
- const { from, to, empty } = view.state.selection.main;
559
- if (empty) return;
560
- const selected = view.state.doc.sliceString(from, to);
561
- view.dispatch({ changes: { from, to, insert: `${prefix}${selected}${suffix}` } });
562
- }
563
- function toggleWrap(view, openTag, closeTag) {
564
- const { from, to, empty } = view.state.selection.main;
565
- const doc = view.state.doc;
566
- if (!empty) {
567
- const selected = doc.sliceString(from, to);
568
- if (selected.startsWith(openTag) && selected.endsWith(closeTag)) {
569
- const inner = selected.slice(openTag.length, -closeTag.length);
570
- view.dispatch({ changes: { from, to, insert: inner } });
571
- return;
572
- }
573
- }
574
- {
575
- const cursorPos = empty ? from : to;
576
- const line = doc.lineAt(cursorPos);
577
- const lineText = doc.sliceString(line.from, line.to);
578
- const openIdx = lineText.indexOf(openTag);
579
- const closeIdx = lineText.lastIndexOf(closeTag);
580
- if (openIdx >= 0 && closeIdx > openIdx) {
581
- const absOpen = line.from + openIdx;
582
- const absClose = line.from + closeIdx;
583
- if (cursorPos >= absOpen + openTag.length && cursorPos <= absClose) {
584
- const inner = doc.sliceString(absOpen + openTag.length, absClose);
585
- view.dispatch({
586
- changes: {
587
- from: absOpen,
588
- to: absClose + closeTag.length,
589
- insert: inner
590
- }
591
- });
592
- return;
593
- }
594
- }
595
- }
596
- wrapSelection(view, openTag, closeTag);
597
- }
598
-
599
- // src/components/Toolbar/config.ts
600
- function createToolbarConfig() {
574
+ function createToolbarConfig(options) {
575
+ const hasUploadConfig = isUploadConfigured(options);
601
576
  return [
602
577
  {
603
578
  id: "heading",
@@ -622,7 +597,14 @@ function createToolbarConfig() {
622
597
  { id: "ol", label: "\u6709\u5E8F\u5217\u8868", icon: OrderedListOutlined, handler: (v) => insertAtLineStart(v, "1. ") },
623
598
  { separatorBefore: true },
624
599
  { id: "link", label: "\u94FE\u63A5", icon: LinkOutlined, handler: (v) => insertAtCursor(v, "[", "](url)", "\u6587\u672C") },
625
- { id: "image", label: "\u56FE\u7247", icon: PictureOutlined, handler: (v) => uploadImage(v) },
600
+ // 图片按钮:未配置上传时禁用并提示
601
+ {
602
+ id: "image",
603
+ label: hasUploadConfig ? "\u56FE\u7247" : "\u8BF7\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u5730\u5740",
604
+ icon: PictureOutlined,
605
+ disabled: !hasUploadConfig,
606
+ handler: hasUploadConfig ? (v) => uploadImage(v, { uploadUrl: options == null ? void 0 : options.uploadUrl, onUploadImage: options == null ? void 0 : options.onUploadImage }) : void 0
607
+ },
626
608
  { separatorBefore: true },
627
609
  { id: "code", label: "\u884C\u5185\u4EE3\u7801", icon: FieldStringOutlined, handler: (v) => wrapSelection(v, "`", "`") },
628
610
  {
@@ -637,7 +619,6 @@ function createToolbarConfig() {
637
619
  }
638
620
  },
639
621
  { id: "quote", label: "\u5F15\u7528", icon: BlockOutlined, handler: (v) => insertAtLineStart(v, "> ") },
640
- { id: "textContainer", label: "\u6587\u5B57\u5BB9\u5668", icon: TagsOutlined, handler: (v) => toggleWrap(v, "<text-container>", "</text-container>") },
641
622
  {
642
623
  id: "hr",
643
624
  label: "\u5206\u5272\u7EBF",
@@ -651,13 +632,20 @@ function createToolbarConfig() {
651
632
  }
652
633
 
653
634
  // src/components/Toolbar/hooks/useToolbar.ts
654
- function useToolbar(editorView, extraItems) {
635
+ function useToolbar(editorView, options) {
636
+ const { extraItems, uploadConfig } = options || {};
655
637
  const config = useMemo(() => {
656
- const base = createToolbarConfig();
657
- return extraItems ? [...base, { separatorBefore: true }, ...extraItems] : base;
658
- }, [extraItems]);
638
+ const base = createToolbarConfig(uploadConfig);
639
+ return extraItems ? [...base, ...extraItems] : base;
640
+ }, [extraItems, uploadConfig]);
659
641
  const handleClick = useCallback((button) => {
660
- if (button.handler && editorView) {
642
+ if (!editorView) return;
643
+ if (button.wrapTags) {
644
+ const [openTag, closeTag] = button.wrapTags;
645
+ toggleWrap(editorView, openTag, closeTag);
646
+ return;
647
+ }
648
+ if (button.handler) {
661
649
  editorView.focus();
662
650
  button.handler(editorView, editorView.state);
663
651
  }
@@ -666,15 +654,15 @@ function useToolbar(editorView, extraItems) {
666
654
  }
667
655
 
668
656
  // src/components/Toolbar/renderers/ToolbarItemRenderer.tsx
669
- import { Button, Dropdown } from "antd";
657
+ import { Button, Dropdown, Tooltip } from "antd";
670
658
  import { DownOutlined } from "@ant-design/icons";
671
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
659
+ import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
672
660
  function ToolbarItemRenderer({ item, onClick }) {
673
661
  const Icon = item.icon;
674
662
  if (item.children && item.children.length > 0) {
675
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
676
- item.separatorBefore && /* @__PURE__ */ jsx5("div", { className: "toolbar-separator" }),
677
- /* @__PURE__ */ jsx5("div", { className: "toolbar-group", children: /* @__PURE__ */ jsx5(
663
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
664
+ item.separatorBefore && /* @__PURE__ */ jsx4("div", { className: "toolbar-separator" }),
665
+ /* @__PURE__ */ jsx4("div", { className: "toolbar-group", children: /* @__PURE__ */ jsx4(Tooltip, { title: item.label, children: /* @__PURE__ */ jsx4(
678
666
  Dropdown,
679
667
  {
680
668
  menu: {
@@ -683,10 +671,10 @@ function ToolbarItemRenderer({ item, onClick }) {
683
671
  return {
684
672
  type: "item",
685
673
  key: (_a = child.id) != null ? _a : "",
686
- label: /* @__PURE__ */ jsxs3("span", { onClick: () => onClick(child), children: [
674
+ label: /* @__PURE__ */ jsxs2("span", { onClick: () => onClick(child), children: [
687
675
  child.icon && (() => {
688
676
  const ChildIcon = child.icon;
689
- return /* @__PURE__ */ jsx5(ChildIcon, {});
677
+ return /* @__PURE__ */ jsx4(ChildIcon, {});
690
678
  })(),
691
679
  " ",
692
680
  child.label
@@ -695,28 +683,29 @@ function ToolbarItemRenderer({ item, onClick }) {
695
683
  })
696
684
  },
697
685
  placement: "bottomLeft",
698
- children: /* @__PURE__ */ jsxs3(Button, { className: "toolbar-button", type: "text", size: "small", title: item.label, children: [
699
- /* @__PURE__ */ jsx5(Icon, {}),
700
- /* @__PURE__ */ jsx5(DownOutlined, { style: { marginLeft: 2, fontSize: 10 } })
686
+ children: /* @__PURE__ */ jsxs2(Button, { className: "toolbar-button", type: "text", size: "small", children: [
687
+ /* @__PURE__ */ jsx4(Icon, {}),
688
+ /* @__PURE__ */ jsx4(DownOutlined, { style: { marginLeft: 2, fontSize: 10 } })
701
689
  ] })
702
690
  }
703
- ) })
691
+ ) }) })
704
692
  ] });
705
693
  }
706
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
707
- item.separatorBefore && /* @__PURE__ */ jsx5("div", { className: "toolbar-separator" }),
708
- /* @__PURE__ */ jsx5("div", { className: "toolbar-group", children: /* @__PURE__ */ jsx5(
694
+ const isDisabled = item.disabled;
695
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
696
+ item.separatorBefore && /* @__PURE__ */ jsx4("div", { className: "toolbar-separator" }),
697
+ /* @__PURE__ */ jsx4("div", { className: "toolbar-group", children: /* @__PURE__ */ jsx4(Tooltip, { title: item.label, children: /* @__PURE__ */ jsx4(
709
698
  Button,
710
699
  {
711
700
  id: item.id,
712
701
  className: "toolbar-button",
713
702
  type: "text",
714
703
  size: "small",
715
- title: item.label,
716
- onClick: () => onClick(item),
717
- children: /* @__PURE__ */ jsx5(Icon, {})
704
+ disabled: isDisabled,
705
+ onClick: () => !isDisabled && onClick(item),
706
+ children: /* @__PURE__ */ jsx4(Icon, {})
718
707
  }
719
- ) })
708
+ ) }) })
720
709
  ] });
721
710
  }
722
711
 
@@ -732,24 +721,27 @@ import { useState, useRef as useRef2, useEffect as useEffect3, useCallback as us
732
721
  var index_module_default = {};
733
722
 
734
723
  // src/components/Toolbar/renderers/TablePicker/index.tsx
735
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
736
- var MAX_ROW = 12;
737
- var MAX_COL = 12;
738
- var REFRESH_TIME = 200;
724
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
725
+ var MAX_ROW = 8;
726
+ var MAX_COL = 8;
739
727
  function TablePicker({ onSelect }) {
740
728
  const [hoverSize, setHoverSize] = useState({ row: 0, col: 0 });
741
- const [prevHoverSize, setPrevHoverSize] = useState({ row: 0, col: 0 });
742
729
  const [confirmedSize, setConfirmedSize] = useState({ row: 0, col: 0 });
743
730
  const maxColRef = useRef2(0);
731
+ const prevRowRef = useRef2(0);
744
732
  const timerIdRef = useRef2(null);
745
733
  const refreshTimerRef = useRef2(null);
746
734
  const commitRef = useRef2(null);
747
- const clearTimer = useCallback2((flushFn) => {
735
+ const clearTimer = useCallback2(() => {
748
736
  if (timerIdRef.current) {
737
+ clearTimeout(timerIdRef.current);
749
738
  timerIdRef.current = null;
750
739
  }
751
- if (flushFn && maxColRef.current > 0) {
752
- timerIdRef.current = setTimeout(flushFn, REFRESH_TIME);
740
+ }, []);
741
+ const clearFlash = useCallback2(() => {
742
+ if (refreshTimerRef.current) {
743
+ clearInterval(refreshTimerRef.current);
744
+ refreshTimerRef.current = null;
753
745
  }
754
746
  }, []);
755
747
  useEffect3(
@@ -760,25 +752,21 @@ function TablePicker({ onSelect }) {
760
752
  []
761
753
  );
762
754
  function extend(col, row) {
755
+ if (row < prevRowRef.current || col < maxColRef.current) {
756
+ maxColRef.current = 0;
757
+ }
763
758
  if (col > maxColRef.current) {
764
759
  maxColRef.current = col;
765
- setHoverSize({ row, col });
766
- setConfirmedSize({ row, col });
767
- } else {
768
- setHoverSize({ row, col });
769
- if (row !== confirmedSize.row) {
770
- setConfirmedSize({ row, col: maxColRef.current });
771
- }
772
- if (row === 1) {
773
- commitRef.current = { cols: col, rows: 1 };
774
- setConfirmedSize({ row: 1, col });
775
- }
760
+ }
761
+ setHoverSize({ row, col });
762
+ setConfirmedSize({ row, col: maxColRef.current });
763
+ if (row === 1) {
764
+ commitRef.current = { cols: maxColRef.current, rows: 1 };
776
765
  }
777
766
  }
778
767
  function flash(col, dir, limit, finalCallback) {
779
768
  let current = dir === 1 ? 1 : limit;
780
769
  const step = dir === 1 ? 1 : -1;
781
- let timer = 0;
782
770
  refreshTimerRef.current = setInterval(() => {
783
771
  setHoverSize({ row: current, col });
784
772
  if (current > confirmedSize.row || dir === -1) {
@@ -787,45 +775,43 @@ function TablePicker({ onSelect }) {
787
775
  current += step;
788
776
  if (dir === 1 && current > limit || dir === -1 && current < 1) {
789
777
  clearInterval(refreshTimerRef.current);
790
- setHoverSize({ row: current - step, col });
791
- finalCallback(col, current - step);
778
+ refreshTimerRef.current = null;
779
+ const finalRow = current - step;
780
+ setHoverSize({ row: finalRow, col });
781
+ finalCallback(col, finalRow);
792
782
  }
793
783
  }, 50);
794
- clearTimeout(timer);
795
- return () => {
796
- clearInterval(refreshTimerRef.current);
797
- };
798
784
  }
799
785
  function onCellEnterCheck(index) {
800
786
  clearTimer();
787
+ clearFlash();
801
788
  const row = Math.floor(index / MAX_COL) + 1;
802
789
  const col = index % MAX_COL + 1;
803
- const isDown = row > hoverSize.row;
804
- if (Math.abs(row - hoverSize.row) <= 1) {
790
+ const prevRow = prevRowRef.current;
791
+ const isJump = Math.abs(row - prevRow) > 1;
792
+ const isDown = row > prevRow;
793
+ prevRowRef.current = row;
794
+ if (!isJump) {
805
795
  extend(col, row);
806
- } else {
807
- if (isDown && (col > maxColRef.current || maxColRef.current >= MAX_COL)) {
796
+ } else if (isDown) {
797
+ if (col > maxColRef.current || maxColRef.current >= MAX_COL) {
808
798
  const finalRow = Math.min(row, MAX_ROW);
809
799
  flash(col, 1, finalRow, (c, r) => {
810
800
  setConfirmedSize({ row: r, col: c });
811
801
  });
812
- } else if (!isDown) {
813
- flash(col, -1, row, (c, r) => {
814
- setConfirmedSize({ row: r, col: c });
815
- if (r === 1) {
816
- commitRef.current = { cols: Math.min(MAX_COL, col), rows: 1 };
817
- setConfirmedSize({ row: 1, col: Math.min(MAX_COL, col) });
818
- }
819
- });
820
- }
821
- }
822
- if (col >= MAX_COL && row >= prevHoverSize.row) {
823
- if (row !== prevHoverSize.row) {
824
- maxColRef.current = 0;
825
- setPrevHoverSize({ row: 0, col: 0 });
802
+ } else {
803
+ extend(col, row);
826
804
  }
805
+ } else {
806
+ maxColRef.current = 0;
807
+ flash(col, -1, row, (c, r) => {
808
+ setConfirmedSize({ row: r, col: c });
809
+ if (r === 1) {
810
+ commitRef.current = { cols: Math.min(MAX_COL, col), rows: 1 };
811
+ setConfirmedSize({ row: 1, col: Math.min(MAX_COL, col) });
812
+ }
813
+ });
827
814
  }
828
- setPrevHoverSize({ row, col });
829
815
  }
830
816
  function onMouseDown() {
831
817
  const size = commitRef.current || { cols: maxColRef.current, rows: confirmedSize.row };
@@ -833,13 +819,13 @@ function TablePicker({ onSelect }) {
833
819
  onSelect(size);
834
820
  }
835
821
  }
836
- return /* @__PURE__ */ jsxs4(
822
+ return /* @__PURE__ */ jsxs3(
837
823
  "div",
838
824
  {
839
825
  className: index_module_default.container,
840
826
  onMouseDown,
841
827
  children: [
842
- /* @__PURE__ */ jsx6(
828
+ /* @__PURE__ */ jsx5(
843
829
  "div",
844
830
  {
845
831
  className: index_module_default.grid,
@@ -848,7 +834,7 @@ function TablePicker({ onSelect }) {
848
834
  const row = Math.floor(i / MAX_COL) + 1;
849
835
  const col = i % MAX_COL + 1;
850
836
  const isSelected = row <= hoverSize.row && col <= maxColRef.current;
851
- return /* @__PURE__ */ jsx6(
837
+ return /* @__PURE__ */ jsx5(
852
838
  "div",
853
839
  {
854
840
  className: `${index_module_default.cell} ${isSelected ? index_module_default.cellActive : ""}`,
@@ -859,7 +845,7 @@ function TablePicker({ onSelect }) {
859
845
  })
860
846
  }
861
847
  ),
862
- /* @__PURE__ */ jsxs4("div", { className: index_module_default.label, children: [
848
+ /* @__PURE__ */ jsxs3("div", { className: index_module_default.label, children: [
863
849
  hoverSize.row,
864
850
  " x ",
865
851
  maxColRef.current || 1
@@ -870,7 +856,7 @@ function TablePicker({ onSelect }) {
870
856
  }
871
857
 
872
858
  // src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx
873
- import { jsx as jsx7 } from "react/jsx-runtime";
859
+ import { jsx as jsx6 } from "react/jsx-runtime";
874
860
  function TableDropdown({ editorView }) {
875
861
  const [open, setOpen] = React.useState(false);
876
862
  const openCountRef = useRef3(0);
@@ -889,7 +875,7 @@ ${dataRows}
889
875
  const { from } = editorView.state.selection.main;
890
876
  editorView.dispatch({ changes: { from, insert: table } });
891
877
  }, [editorView]);
892
- return /* @__PURE__ */ jsx7(
878
+ return /* @__PURE__ */ jsx6(
893
879
  Dropdown2,
894
880
  {
895
881
  trigger: ["click"],
@@ -897,7 +883,7 @@ ${dataRows}
897
883
  onOpenChange: setOpen,
898
884
  popupRender: () => {
899
885
  openCountRef.current++;
900
- return /* @__PURE__ */ jsx7("div", { style: { padding: 4 }, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx7(
886
+ return /* @__PURE__ */ jsx6("div", { style: { padding: 4 }, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx6(
901
887
  TablePicker,
902
888
  {
903
889
  onSelect: (size) => {
@@ -909,44 +895,41 @@ ${dataRows}
909
895
  ) });
910
896
  },
911
897
  placement: "bottomLeft",
912
- children: /* @__PURE__ */ jsx7(Button2, { className: "toolbar-button", type: "text", size: "small", title: "\u8868\u683C", children: /* @__PURE__ */ jsx7(TableOutlined, {}) })
898
+ children: /* @__PURE__ */ jsx6(Button2, { className: "toolbar-button", type: "text", size: "small", title: "\u8868\u683C", children: /* @__PURE__ */ jsx6(TableOutlined, {}) })
913
899
  }
914
900
  );
915
901
  }
916
902
 
917
903
  // src/components/Toolbar/index.tsx
918
- import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
919
- function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
920
- const { config, handleClick } = useToolbar(editorView, extraItems);
904
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
905
+ function Toolbar({ editorView, fullscreen = true, onToggleFullscreen, extraItems, uploadConfig }) {
906
+ const { config, handleClick } = useToolbar(editorView, { extraItems, uploadConfig });
921
907
  const renderFullscreen = useCallback4(() => {
922
908
  if (!onToggleFullscreen) return null;
923
909
  const Icon = fullscreen ? FullscreenExitOutlined : FullscreenOutlined;
924
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
925
- /* @__PURE__ */ jsx8("div", { className: "toolbar-separator" }),
926
- /* @__PURE__ */ jsx8(
927
- Button3,
928
- {
929
- className: "toolbar-button",
930
- type: "text",
931
- size: "small",
932
- title: fullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
933
- onClick: () => {
934
- onToggleFullscreen();
935
- },
936
- children: /* @__PURE__ */ jsx8(Icon, {})
937
- }
938
- )
939
- ] });
910
+ return /* @__PURE__ */ jsx7(
911
+ Button3,
912
+ {
913
+ className: "toolbar-button",
914
+ type: "text",
915
+ size: "small",
916
+ title: fullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
917
+ onClick: () => {
918
+ onToggleFullscreen();
919
+ },
920
+ children: /* @__PURE__ */ jsx7(Icon, {})
921
+ }
922
+ );
940
923
  }, [fullscreen, onToggleFullscreen]);
941
924
  function isSeparator(item) {
942
925
  return !!(!item.id && item.separatorBefore);
943
926
  }
944
- return /* @__PURE__ */ jsxs5("div", { className: "toolbar", children: [
927
+ return /* @__PURE__ */ jsxs4("div", { className: "toolbar", children: [
945
928
  config.map((item, idx) => {
946
929
  if (isSeparator(item)) {
947
- return /* @__PURE__ */ jsx8("div", { className: "toolbar-separator" }, `sep-${idx}`);
930
+ return /* @__PURE__ */ jsx7("div", { className: "toolbar-separator" }, `sep-${idx}`);
948
931
  }
949
- return /* @__PURE__ */ jsx8(
932
+ return /* @__PURE__ */ jsx7(
950
933
  ToolbarItemRenderer,
951
934
  {
952
935
  item,
@@ -955,8 +938,7 @@ function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
955
938
  item.id
956
939
  );
957
940
  }),
958
- /* @__PURE__ */ jsx8("div", { className: "toolbar-separator" }),
959
- /* @__PURE__ */ jsx8(TableDropdown, { editorView }),
941
+ /* @__PURE__ */ jsx7(TableDropdown, { editorView }),
960
942
  renderFullscreen()
961
943
  ] });
962
944
  }
@@ -964,7 +946,7 @@ function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }) {
964
946
  // src/components/Dialog/EditDialog.tsx
965
947
  import { useState as useState2, useMemo as useMemo2 } from "react";
966
948
  import { Modal } from "antd";
967
- import { jsx as jsx9 } from "react/jsx-runtime";
949
+ import { jsx as jsx8 } from "react/jsx-runtime";
968
950
  function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
969
951
  const schema = componentRegistry_default.get(typeName);
970
952
  const [formProps, setFormProps] = useState2({});
@@ -992,7 +974,7 @@ function EditDialog({ visible, typeName, currentProps, onSave, onClose }) {
992
974
  if (!schema) {
993
975
  return null;
994
976
  }
995
- return /* @__PURE__ */ jsx9(
977
+ return /* @__PURE__ */ jsx8(
996
978
  Modal,
997
979
  {
998
980
  title: `${schema.label} \u5C5E\u6027\u7F16\u8F91`,
@@ -1033,36 +1015,11 @@ ${propsLine}
1033
1015
 
1034
1016
  // src/MarkdownEditor.tsx
1035
1017
  import { ReadOutlined } from "@ant-design/icons";
1036
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1037
- var MarkdownEditor_default = forwardRef2(function MarkdownEditor({
1038
- defaultValue = "",
1039
- value,
1040
- onChange,
1041
- showPreview = true,
1042
- fullscreen: fullscreenProp = false,
1043
- previewOptions
1044
- }, ref) {
1045
- const [content, setContent] = useState3(value != null ? value : defaultValue);
1046
- const [editorView, setEditorView] = useState3(null);
1047
- const scrollDOMRef = useRef4(null);
1048
- const previewElRef = useRef4(null);
1018
+
1019
+ // src/hooks/useFullscreen.ts
1020
+ import { useState as useState3, useEffect as useEffect4, useCallback as useCallback5 } from "react";
1021
+ function useFullscreen() {
1049
1022
  const [fullscreen, setFullscreen] = useState3(false);
1050
- const [dialogVisible, setDialogVisible] = useState3(false);
1051
- const [dialogTypeName, setDialogTypeName] = useState3("");
1052
- const [dialogProps, setDialogProps] = useState3({});
1053
- const [dialogPropsRange, setDialogPropsRange] = useState3([0, 0]);
1054
- useEffect4(() => {
1055
- if (value !== void 0) {
1056
- setContent(value);
1057
- }
1058
- }, [value]);
1059
- const internalSetContent = useCallback5((newContent) => {
1060
- setContent(newContent);
1061
- onChange == null ? void 0 : onChange(newContent);
1062
- }, [onChange]);
1063
- useImperativeHandle(ref, () => ({
1064
- getContent: () => content
1065
- }), [content]);
1066
1023
  const toggleFullscreen = useCallback5(() => {
1067
1024
  if (document.fullscreenElement) {
1068
1025
  document.exitFullscreen().catch((e) => {
@@ -1089,40 +1046,29 @@ var MarkdownEditor_default = forwardRef2(function MarkdownEditor({
1089
1046
  document.removeEventListener("webkitfullscreenchange", handleFullscreenChange);
1090
1047
  };
1091
1048
  }, []);
1092
- const extraToolbarItems = componentRegistry_default.getAll().map((schema) => {
1093
- var _a, _b;
1094
- const defaultProps = {};
1095
- for (const field of schema.fields) {
1096
- defaultProps[field.key] = (_a = field.defaultValue) != null ? _a : "";
1097
- }
1098
- const iconNode = (_b = schema.icon) != null ? _b : /* @__PURE__ */ jsx10(ReadOutlined, {});
1099
- return {
1100
- id: `container-${schema.name}`,
1101
- label: schema.label,
1102
- icon: () => iconNode,
1103
- handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
1104
- };
1105
- });
1106
- const handleScrollReady = useCallback5((scrollDOM) => {
1107
- scrollDOMRef.current = scrollDOM;
1108
- }, []);
1109
- useEffect4(() => {
1110
- const source = scrollDOMRef.current;
1111
- const target = previewElRef.current;
1049
+ return { fullscreen, toggleFullscreen };
1050
+ }
1051
+
1052
+ // src/hooks/useScrollSync.ts
1053
+ import { useEffect as useEffect5, useRef as useRef4 } from "react";
1054
+ function useScrollSync(sourceRef, targetRef) {
1055
+ const rafIdRef = useRef4(null);
1056
+ useEffect5(() => {
1057
+ const source = sourceRef.current;
1058
+ const target = targetRef.current;
1112
1059
  if (!source || !target) return;
1113
- let rafId = null;
1114
1060
  const listener = () => {
1115
- if (rafId) return;
1116
- rafId = requestAnimationFrame(() => {
1117
- const s = scrollDOMRef.current;
1118
- const t = previewElRef.current;
1061
+ if (rafIdRef.current) return;
1062
+ rafIdRef.current = requestAnimationFrame(() => {
1063
+ const s = sourceRef.current;
1064
+ const t = targetRef.current;
1119
1065
  if (!s || !t) {
1120
- rafId = null;
1066
+ rafIdRef.current = null;
1121
1067
  return;
1122
1068
  }
1123
1069
  const sourceScrollHeight = s.scrollHeight - s.clientHeight;
1124
1070
  if (sourceScrollHeight <= 0) {
1125
- rafId = null;
1071
+ rafIdRef.current = null;
1126
1072
  return;
1127
1073
  }
1128
1074
  const ratio = s.scrollTop / sourceScrollHeight;
@@ -1130,60 +1076,115 @@ var MarkdownEditor_default = forwardRef2(function MarkdownEditor({
1130
1076
  if (targetScrollHeight > 0) {
1131
1077
  t.scrollTop = ratio * targetScrollHeight;
1132
1078
  }
1133
- rafId = null;
1079
+ rafIdRef.current = null;
1134
1080
  });
1135
1081
  };
1136
1082
  source.addEventListener("scroll", listener, { passive: true });
1137
1083
  return () => {
1138
1084
  source.removeEventListener("scroll", listener);
1139
- if (rafId) cancelAnimationFrame(rafId);
1085
+ if (rafIdRef.current) cancelAnimationFrame(rafIdRef.current);
1086
+ };
1087
+ }, [sourceRef, targetRef]);
1088
+ }
1089
+
1090
+ // src/MarkdownEditor.tsx
1091
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
1092
+ var MarkdownEditor_default = forwardRef2(function MarkdownEditor({
1093
+ defaultValue = "",
1094
+ value,
1095
+ onChange,
1096
+ showPreview = true,
1097
+ fullscreen: fullscreenProp = false,
1098
+ previewOptions,
1099
+ uploadConfig
1100
+ }, ref) {
1101
+ const [content, setContent] = useState4(value != null ? value : defaultValue);
1102
+ const [editorView, setEditorView] = useState4(null);
1103
+ const scrollDOMRef = useRef5(null);
1104
+ const previewElRef = useRef5(null);
1105
+ const { fullscreen, toggleFullscreen } = useFullscreen();
1106
+ useScrollSync(scrollDOMRef, previewElRef);
1107
+ const [dialogVisible, setDialogVisible] = useState4(false);
1108
+ const [dialogTypeName, setDialogTypeName] = useState4("");
1109
+ const [dialogProps, setDialogProps] = useState4({});
1110
+ const [dialogPropsRange, setDialogPropsRange] = useState4([0, 0]);
1111
+ useEffect6(() => {
1112
+ if (value !== void 0) {
1113
+ setContent(value);
1114
+ }
1115
+ }, [value]);
1116
+ const internalSetContent = useCallback6((newContent) => {
1117
+ setContent(newContent);
1118
+ onChange == null ? void 0 : onChange(newContent);
1119
+ }, [onChange]);
1120
+ useImperativeHandle(ref, () => ({
1121
+ getContent: () => content
1122
+ }), [content]);
1123
+ const extraToolbarItems = componentRegistry_default.getAll().map((schema) => {
1124
+ var _a, _b, _c;
1125
+ const defaultProps = {};
1126
+ for (const field of (_a = schema == null ? void 0 : schema.fields) != null ? _a : []) {
1127
+ defaultProps[field.key] = (_b = field.defaultValue) != null ? _b : "";
1128
+ }
1129
+ const iconNode = (_c = schema.icon) != null ? _c : /* @__PURE__ */ jsx9(ReadOutlined, {});
1130
+ return {
1131
+ id: `container-${schema.name}`,
1132
+ label: schema.label,
1133
+ icon: () => iconNode,
1134
+ handler: (view, _state) => insertContainer(view, schema.name, defaultProps)
1140
1135
  };
1141
- }, [previewElRef.current]);
1142
- const handlePreviewRef = useCallback5((node) => {
1136
+ });
1137
+ const handleScrollReady = useCallback6((scrollDOM) => {
1138
+ scrollDOMRef.current = scrollDOM;
1139
+ }, []);
1140
+ const handlePreviewRef = useCallback6((node) => {
1143
1141
  previewElRef.current = node;
1144
1142
  }, []);
1145
- const handleViewReady = useCallback5((view) => {
1143
+ const handleViewReady = useCallback6((view) => {
1146
1144
  setEditorView(view);
1147
1145
  }, []);
1148
- const handleEditContainer = useCallback5((_view, typeName, propsLineFrom, propsLineTo, currentProps) => {
1146
+ const handleEditContainer = useCallback6((_view, typeName, propsLineFrom, propsLineTo, currentProps) => {
1149
1147
  setDialogTypeName(typeName);
1150
1148
  setDialogProps({ ...currentProps });
1151
1149
  setDialogPropsRange([propsLineFrom, propsLineTo]);
1152
1150
  setDialogVisible(true);
1153
1151
  }, []);
1154
- const handleDialogSave = useCallback5((newProps) => {
1152
+ const handleDialogSave = useCallback6((newProps) => {
1155
1153
  setDialogVisible(false);
1156
1154
  if (editorView) {
1157
1155
  updateContainerProps(editorView, dialogPropsRange[0], dialogPropsRange[1], newProps);
1158
1156
  }
1159
1157
  }, [editorView, dialogPropsRange]);
1160
- const handleDialogClose = useCallback5(() => {
1158
+ const handleDialogClose = useCallback6(() => {
1161
1159
  setDialogVisible(false);
1162
1160
  }, []);
1163
- return /* @__PURE__ */ jsxs6("div", { className: fullscreen ? "app fullscreen" : "app", children: [
1164
- /* @__PURE__ */ jsx10(
1161
+ return /* @__PURE__ */ jsxs5("div", { className: fullscreen ? "app fullscreen" : "app", children: [
1162
+ /* @__PURE__ */ jsx9(
1165
1163
  Toolbar,
1166
1164
  {
1167
1165
  editorView,
1168
1166
  fullscreen,
1169
1167
  onToggleFullscreen: fullscreenProp ? toggleFullscreen : void 0,
1170
- extraItems: extraToolbarItems
1168
+ extraItems: extraToolbarItems,
1169
+ uploadConfig
1171
1170
  }
1172
1171
  ),
1173
- showPreview ? /* @__PURE__ */ jsx10(
1172
+ showPreview ? /* @__PURE__ */ jsx9(
1174
1173
  SplitPanels,
1175
1174
  {
1176
- leftPanel: /* @__PURE__ */ jsx10(
1175
+ leftPanel: /* @__PURE__ */ jsx9(
1177
1176
  Editor,
1178
1177
  {
1179
1178
  value: content,
1180
1179
  onChange: internalSetContent,
1181
1180
  onScrollReady: handleScrollReady,
1182
1181
  onViewReady: handleViewReady,
1183
- onEditContainer: handleEditContainer
1182
+ onEditContainer: handleEditContainer,
1183
+ uploadConfig,
1184
+ onMessageNoUploadConfig: () => message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
1184
1185
  }
1185
1186
  ),
1186
- rightPanel: /* @__PURE__ */ jsx10(
1187
+ rightPanel: /* @__PURE__ */ jsx9(
1187
1188
  Preview_default,
1188
1189
  {
1189
1190
  content,
@@ -1192,17 +1193,19 @@ var MarkdownEditor_default = forwardRef2(function MarkdownEditor({
1192
1193
  }
1193
1194
  )
1194
1195
  }
1195
- ) : /* @__PURE__ */ jsx10(
1196
+ ) : /* @__PURE__ */ jsx9(
1196
1197
  Editor,
1197
1198
  {
1198
1199
  value: content,
1199
1200
  onChange: internalSetContent,
1200
1201
  onScrollReady: handleScrollReady,
1201
1202
  onViewReady: handleViewReady,
1202
- onEditContainer: handleEditContainer
1203
+ onEditContainer: handleEditContainer,
1204
+ uploadConfig,
1205
+ onMessageNoUploadConfig: () => message.warning("\u8BF7\u5148\u914D\u7F6E\u56FE\u7247\u4E0A\u4F20\u529F\u80FD")
1203
1206
  }
1204
1207
  ),
1205
- /* @__PURE__ */ jsx10(
1208
+ /* @__PURE__ */ jsx9(
1206
1209
  EditDialog,
1207
1210
  {
1208
1211
  visible: dialogVisible,
@@ -1216,10 +1219,10 @@ var MarkdownEditor_default = forwardRef2(function MarkdownEditor({
1216
1219
  });
1217
1220
 
1218
1221
  // src/hooks/useRegisterToolbar.ts
1219
- import { useEffect as useEffect5 } from "react";
1222
+ import { useEffect as useEffect7 } from "react";
1220
1223
  function useRegisterToolbar(schemas) {
1221
1224
  const schemaList = Array.isArray(schemas) ? schemas : [schemas];
1222
- useEffect5(() => {
1225
+ useEffect7(() => {
1223
1226
  schemaList.forEach((schema) => {
1224
1227
  componentRegistry_default.register(schema.name, schema);
1225
1228
  });