remons-markdown-editor 1.0.0-beta.24 → 1.0.0-beta.25

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
@@ -6,6 +6,7 @@ import { Decoration, EditorView as EditorView$1, ViewPlugin, WidgetType, keymap
6
6
  import { defaultKeymap, indentWithTab } from "@codemirror/commands";
7
7
  import { markdown } from "@codemirror/lang-markdown";
8
8
  import { oneDark } from "@codemirror/theme-one-dark";
9
+ import { syntaxTree } from "@codemirror/language";
9
10
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
11
  import RenderMarkdown, { initHighlighter, languagesCommon } from "remons-render-markdown";
11
12
  import "remons-render-markdown/dist/index.css";
@@ -405,6 +406,116 @@ function toggleLinePrefix(view, pattern, insertText) {
405
406
  }
406
407
  view.dispatch({ changes });
407
408
  }
409
+ /** Markdown 列表行:缩进 + 列表标记(无序/有序)+ 空白 */
410
+ var LIST_LINE_RE = /^(\s*)([-*+]|\d+\.)([ \t]+)/;
411
+ /** 列表缩进台阶:4 个空格,确保缩进后能被解析为子列表 */
412
+ var LIST_INDENT_WIDTH = 4;
413
+ /** 判断位置是否位于代码块内(代码块内不参与列表缩进逻辑) */
414
+ function isInCodeBlock(state, pos) {
415
+ let node = syntaxTree(state).resolveInner(pos, 1);
416
+ while (node) {
417
+ if (node.name === "FencedCode" || node.name === "CodeBlock") return true;
418
+ node = node.parent;
419
+ }
420
+ return false;
421
+ }
422
+ /**
423
+ * 向上查找同缩进的前一个列表项,返回其序号
424
+ * - 跳过更深的子列表行和空行
425
+ * - 遇到非列表内容或更浅层列表时结束
426
+ */
427
+ function findPrevSiblingOrder(state, plans, lineNo, indent) {
428
+ const doc = state.doc;
429
+ for (let n = lineNo - 1; n >= 1; n--) {
430
+ const plan = plans.get(n);
431
+ if (plan) {
432
+ if (plan.nextIndent === indent) return plan.nextOrder;
433
+ if (plan.nextIndent < indent) return null;
434
+ continue;
435
+ }
436
+ const text = doc.line(n).text;
437
+ if (!text.trim()) continue;
438
+ const m = text.match(LIST_LINE_RE);
439
+ if (!m) return null;
440
+ const lineIndent = m[1].length;
441
+ if (lineIndent === indent) return /^\d+\.$/.test(m[2]) ? parseInt(m[2], 10) : null;
442
+ if (lineIndent < indent) return null;
443
+ }
444
+ return null;
445
+ }
446
+ /**
447
+ * 计算列表行缩进/反缩进的变更
448
+ * - 仅当所有选区行都是列表行时生效,否则返回 null 交给默认缩进行为
449
+ * - 有序列表缩进后重新编号,保证新一级子列表从 1 开始
450
+ */
451
+ function planListIndent(view, direction) {
452
+ const { state } = view;
453
+ const doc = state.doc;
454
+ const plans = /* @__PURE__ */ new Map();
455
+ for (const range of state.selection.ranges) {
456
+ const first = doc.lineAt(range.from).number;
457
+ const last = doc.lineAt(range.to).number;
458
+ for (let n = first; n <= last; n++) {
459
+ if (plans.has(n)) continue;
460
+ const line = doc.line(n);
461
+ if (isInCodeBlock(state, line.from)) return null;
462
+ const m = line.text.match(LIST_LINE_RE);
463
+ if (!m) return null;
464
+ const indent = m[1].length;
465
+ plans.set(n, {
466
+ indent,
467
+ nextIndent: direction === 1 ? indent + LIST_INDENT_WIDTH : Math.max(0, indent - LIST_INDENT_WIDTH),
468
+ order: /^\d+\.$/.test(m[2]) ? parseInt(m[2], 10) : null,
469
+ nextOrder: null
470
+ });
471
+ }
472
+ }
473
+ if (plans.size === 0) return null;
474
+ const ordered = [...plans.entries()].sort((a, b) => a[0] - b[0]);
475
+ for (const [lineNo, info] of ordered) {
476
+ if (info.order === null) continue;
477
+ const prevOrder = findPrevSiblingOrder(state, plans, lineNo, info.nextIndent);
478
+ info.nextOrder = prevOrder === null ? 1 : prevOrder + 1;
479
+ }
480
+ return ordered.map(([lineNo, info]) => {
481
+ const line = doc.line(lineNo);
482
+ const m = line.text.match(LIST_LINE_RE);
483
+ const useNextOrder = (direction === 1 || info.indent > 0) && info.order !== null && info.nextOrder !== null && info.order !== info.nextOrder;
484
+ return {
485
+ from: line.from,
486
+ to: line.from + m[0].length,
487
+ insert: " ".repeat(info.nextIndent) + (useNextOrder ? `${info.nextOrder}.` : m[2]) + m[3]
488
+ };
489
+ });
490
+ }
491
+ /**
492
+ * Tab:列表行缩进一级(4 空格)
493
+ * - 普通行返回 false,继续走默认缩进
494
+ * - 有序列表缩进后重排序号,子列表从 1 开始
495
+ */
496
+ function indentMarkdownList(view) {
497
+ const changes = planListIndent(view, 1);
498
+ if (!changes) return false;
499
+ view.dispatch({
500
+ changes,
501
+ userEvent: "input.indent"
502
+ });
503
+ return true;
504
+ }
505
+ /**
506
+ * Shift-Tab:列表行反缩进一级
507
+ * - 普通行返回 false,继续走默认缩进
508
+ * - 有序列表反缩进后并入上一层并重排序号
509
+ */
510
+ function dedentMarkdownList(view) {
511
+ const changes = planListIndent(view, -1);
512
+ if (!changes) return false;
513
+ view.dispatch({
514
+ changes,
515
+ userEvent: "delete.dedent"
516
+ });
517
+ return true;
518
+ }
408
519
  /**
409
520
  * 上传图片
410
521
  * @param file - 要上传的文件
@@ -552,7 +663,18 @@ var getCodeMirrorExtensions = (opts) => {
552
663
  const extensions = [
553
664
  markdown(),
554
665
  oneDark,
555
- keymap.of([...defaultKeymap, indentWithTab]),
666
+ keymap.of([
667
+ {
668
+ key: "Tab",
669
+ run: indentMarkdownList
670
+ },
671
+ {
672
+ key: "Shift-Tab",
673
+ run: dedentMarkdownList
674
+ },
675
+ ...defaultKeymap,
676
+ indentWithTab
677
+ ]),
556
678
  EditorView$1.lineWrapping,
557
679
  pasteHandler,
558
680
  EditorView$1.baseTheme({
@@ -690,6 +812,8 @@ function Quote() {
690
812
  function InlineCode() {
691
813
  return IconComponent("inlinecode");
692
814
  }
815
+ /** 未配置图片上传时的提示文案 */
816
+ var UPLOAD_CONFIG_MISSING_MESSAGE = "请先配置图片上传功能";
693
817
  /**
694
818
  * 创建工具栏配置
695
819
  * @param options - 配置选项
@@ -787,13 +911,9 @@ function createToolbarConfig(options) {
787
911
  },
788
912
  {
789
913
  id: "image",
790
- label: hasUploadConfig ? "图片" : "请配置图片上传地址",
914
+ label: "图片",
791
915
  icon: PictureOutlined,
792
- disabled: !hasUploadConfig,
793
- handler: hasUploadConfig ? (v) => uploadImage(v, {
794
- uploadUrl: options?.uploadUrl,
795
- onUploadImage: options?.onUploadImage
796
- }) : void 0
916
+ handler: hasUploadConfig && options ? (v) => uploadImage(v, options) : () => message.warning(UPLOAD_CONFIG_MISSING_MESSAGE)
797
917
  },
798
918
  { separatorBefore: true },
799
919
  {