remons-markdown-editor 1.0.0-beta.16 → 1.0.0-beta.18

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.
@@ -8,6 +8,31 @@ export declare function insertAtCursor(view: EditorView, before: string, after?:
8
8
  * 在当前行行首插入文本
9
9
  */
10
10
  export declare function insertAtLineStart(view: EditorView, text: string): void;
11
+ /**
12
+ * 切换标题:
13
+ * - 当前行已是目标级别标题 → 移除行首 # 前缀(变回普通文本)
14
+ * - 当前行是其他级别标题 → 替换为目标级别
15
+ * - 当前行是普通文本 → 插入目标级别前缀
16
+ * @param view - CodeMirror 编辑器实例
17
+ * @param level - 标题级别(1-6)
18
+ */
19
+ export declare function toggleHeading(view: EditorView, level: number): void;
20
+ /**
21
+ * 切换任务列表(checkbox 待办)
22
+ * - 支持多行:对选区内每一行应用 `- [ ] `
23
+ * - 普通列表项(如 `- 内容`)直接转换为 `- [ ] 内容`
24
+ * - 切换:若所有目标行都已是任务项,则移除行首前缀
25
+ */
26
+ export declare function toggleTaskList(view: EditorView): void;
27
+ /**
28
+ * 切换行首前缀(支持多行选区)
29
+ * - 已是对应前缀的行 → 移除前缀(变回普通文本)
30
+ * - 未加前缀的行 → 在行首插入前缀
31
+ * @param view - CodeMirror 编辑器实例
32
+ * @param pattern - 匹配行首前缀的正则
33
+ * @param insertText - 未匹配时插入的前缀文本
34
+ */
35
+ export declare function toggleLinePrefix(view: EditorView, pattern: RegExp, insertText: string): void;
11
36
  /**
12
37
  * 上传图片
13
38
  * @param file - 要上传的文件
package/dist/index.cjs CHANGED
@@ -333,6 +333,104 @@ function insertAtLineStart(view, text) {
333
333
  view.dispatch({ selection: { anchor: line.from + text.length } });
334
334
  }
335
335
  /**
336
+ * 切换标题:
337
+ * - 当前行已是目标级别标题 → 移除行首 # 前缀(变回普通文本)
338
+ * - 当前行是其他级别标题 → 替换为目标级别
339
+ * - 当前行是普通文本 → 插入目标级别前缀
340
+ * @param view - CodeMirror 编辑器实例
341
+ * @param level - 标题级别(1-6)
342
+ */
343
+ function toggleHeading(view, level) {
344
+ const line = view.state.doc.lineAt(view.state.selection.main.from);
345
+ const m = line.text.match(/^(\s*)(#{1,6})(\s+|\s*$)/);
346
+ const indent = m ? m[1] : "";
347
+ const text = "#".repeat(level) + " ";
348
+ if (m && m[2].length === level) view.dispatch({ changes: {
349
+ from: line.from,
350
+ to: line.from + m[0].length,
351
+ insert: indent
352
+ } });
353
+ else {
354
+ view.dispatch({ changes: {
355
+ from: line.from,
356
+ to: m ? line.from + m[0].length : line.from,
357
+ insert: indent + text
358
+ } });
359
+ view.dispatch({ selection: { anchor: line.from + indent.length + text.length } });
360
+ }
361
+ }
362
+ /**
363
+ * 切换任务列表(checkbox 待办)
364
+ * - 支持多行:对选区内每一行应用 `- [ ] `
365
+ * - 普通列表项(如 `- 内容`)直接转换为 `- [ ] 内容`
366
+ * - 切换:若所有目标行都已是任务项,则移除行首前缀
367
+ */
368
+ function toggleTaskList(view) {
369
+ const doc = view.state.doc;
370
+ const { from, to } = view.state.selection.main;
371
+ const first = doc.lineAt(from);
372
+ const last = doc.lineAt(to);
373
+ const lines = [];
374
+ for (let n = first.number; n <= last.number; n++) lines.push(doc.line(n));
375
+ const taskRe = /^(\s*)([-*+])\s\[( |x)\]\s?/;
376
+ const allTask = lines.every((line) => taskRe.test(line.text));
377
+ const changes = [];
378
+ for (const line of lines) {
379
+ const taskMatch = line.text.match(taskRe);
380
+ if (taskMatch) changes.push({
381
+ from: line.from,
382
+ to: line.from + taskMatch[0].length,
383
+ insert: allTask ? "" : `${taskMatch[1]}${taskMatch[2]} [ ] `
384
+ });
385
+ else {
386
+ const bulletMatch = line.text.match(/^(\s*)([-*+])\s/);
387
+ if (bulletMatch) {
388
+ const idx = line.from + bulletMatch[1].length + bulletMatch[2].length;
389
+ changes.push({
390
+ from: idx,
391
+ to: idx + 1,
392
+ insert: " [ ] "
393
+ });
394
+ } else changes.push({
395
+ from: line.from,
396
+ to: line.from,
397
+ insert: "- [ ] "
398
+ });
399
+ }
400
+ }
401
+ view.dispatch({ changes });
402
+ }
403
+ /**
404
+ * 切换行首前缀(支持多行选区)
405
+ * - 已是对应前缀的行 → 移除前缀(变回普通文本)
406
+ * - 未加前缀的行 → 在行首插入前缀
407
+ * @param view - CodeMirror 编辑器实例
408
+ * @param pattern - 匹配行首前缀的正则
409
+ * @param insertText - 未匹配时插入的前缀文本
410
+ */
411
+ function toggleLinePrefix(view, pattern, insertText) {
412
+ const doc = view.state.doc;
413
+ const { from, to } = view.state.selection.main;
414
+ const first = doc.lineAt(from);
415
+ const last = doc.lineAt(Math.max(to, from));
416
+ const changes = [];
417
+ for (let n = first.number; n <= last.number; n++) {
418
+ const line = doc.line(n);
419
+ const m = line.text.match(pattern);
420
+ if (m) changes.push({
421
+ from: line.from,
422
+ to: line.from + m[0].length,
423
+ insert: ""
424
+ });
425
+ else changes.push({
426
+ from: line.from,
427
+ to: line.from,
428
+ insert: insertText
429
+ });
430
+ }
431
+ view.dispatch({ changes });
432
+ }
433
+ /**
336
434
  * 上传图片
337
435
  * @param file - 要上传的文件
338
436
  * @param uploadConfig - 上传配置
@@ -616,39 +714,33 @@ function createToolbarConfig(options) {
616
714
  children: [
617
715
  {
618
716
  id: "heading1",
619
- label: "标题 1",
620
- icon: _ant_design_icons.FontSizeOutlined,
621
- handler: (v) => insertAtLineStart(v, "# ")
717
+ label: "H1",
718
+ handler: (v) => toggleHeading(v, 1)
622
719
  },
623
720
  {
624
721
  id: "heading2",
625
- label: "标题 2",
626
- icon: _ant_design_icons.FontSizeOutlined,
627
- handler: (v) => insertAtLineStart(v, "## ")
722
+ label: "H2",
723
+ handler: (v) => toggleHeading(v, 2)
628
724
  },
629
725
  {
630
726
  id: "heading3",
631
- label: "标题 3",
632
- icon: _ant_design_icons.FontSizeOutlined,
633
- handler: (v) => insertAtLineStart(v, "### ")
727
+ label: "H3",
728
+ handler: (v) => toggleHeading(v, 3)
634
729
  },
635
730
  {
636
731
  id: "heading4",
637
- label: "标题 4",
638
- icon: _ant_design_icons.FontSizeOutlined,
639
- handler: (v) => insertAtLineStart(v, "#### ")
732
+ label: "H4",
733
+ handler: (v) => toggleHeading(v, 4)
640
734
  },
641
735
  {
642
736
  id: "heading5",
643
- label: "标题 5",
644
- icon: _ant_design_icons.FontSizeOutlined,
645
- handler: (v) => insertAtLineStart(v, "##### ")
737
+ label: "H5",
738
+ handler: (v) => toggleHeading(v, 5)
646
739
  },
647
740
  {
648
741
  id: "heading6",
649
- label: "标题 6",
650
- icon: _ant_design_icons.FontSizeOutlined,
651
- handler: (v) => insertAtLineStart(v, "###### ")
742
+ label: "H6",
743
+ handler: (v) => toggleHeading(v, 6)
652
744
  }
653
745
  ]
654
746
  },
@@ -657,38 +749,44 @@ function createToolbarConfig(options) {
657
749
  id: "bold",
658
750
  label: "加粗",
659
751
  icon: _ant_design_icons.BoldOutlined,
660
- handler: (v) => wrapSelection(v, "**", "**")
752
+ handler: (v) => toggleWrap(v, "**", "**")
661
753
  },
662
754
  {
663
755
  id: "italic",
664
756
  label: "斜体",
665
757
  icon: _ant_design_icons.ItalicOutlined,
666
- handler: (v) => wrapSelection(v, "*", "*")
758
+ handler: (v) => toggleWrap(v, "*", "*")
667
759
  },
668
760
  {
669
761
  id: "strikethrough",
670
762
  label: "删除线",
671
763
  icon: _ant_design_icons.StrikethroughOutlined,
672
- handler: (v) => wrapSelection(v, "~~", "~~")
764
+ handler: (v) => toggleWrap(v, "~~", "~~")
673
765
  },
674
766
  {
675
767
  id: "underline",
676
768
  label: "下划线",
677
769
  icon: _ant_design_icons.UnderlineOutlined,
678
- handler: (v) => wrapSelection(v, "<u>", "</u>")
770
+ handler: (v) => toggleWrap(v, "<u>", "</u>")
679
771
  },
680
772
  { separatorBefore: true },
681
773
  {
682
774
  id: "ul",
683
775
  label: "无序列表",
684
776
  icon: _ant_design_icons.UnorderedListOutlined,
685
- handler: (v) => insertAtLineStart(v, "- ")
777
+ handler: (v) => toggleLinePrefix(v, /^(\s*)-(?!\s*\[)\s/, "- ")
686
778
  },
687
779
  {
688
780
  id: "ol",
689
781
  label: "有序列表",
690
782
  icon: _ant_design_icons.OrderedListOutlined,
691
- handler: (v) => insertAtLineStart(v, "1. ")
783
+ handler: (v) => toggleLinePrefix(v, /^(\s*)\d+\.\s/, "1. ")
784
+ },
785
+ {
786
+ id: "checkbox",
787
+ label: "待办",
788
+ icon: _ant_design_icons.CheckSquareOutlined,
789
+ handler: (v) => toggleTaskList(v)
692
790
  },
693
791
  { separatorBefore: true },
694
792
  {