@sendbird/actionbook-core 0.9.1 → 0.9.7
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 +41 -8
- package/dist/index.js.map +1 -1
- package/dist/ui/index.d.ts +5 -1
- package/dist/ui/index.js +1422 -333
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/ui/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var actionbookSchema = new Schema({
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
heading: {
|
|
17
|
-
content: "
|
|
17
|
+
content: "(text | jumpPoint | hardBreak)*",
|
|
18
18
|
group: "block",
|
|
19
19
|
attrs: { level: { default: 1 } },
|
|
20
20
|
parseDOM: [
|
|
@@ -456,6 +456,205 @@ function createPluginArray(plugins) {
|
|
|
456
456
|
// src/ui/components/ActionbookRenderer.tsx
|
|
457
457
|
import React from "react";
|
|
458
458
|
|
|
459
|
+
// src/jinja/conditionHighlighter.ts
|
|
460
|
+
var KEYWORDS = {
|
|
461
|
+
and: "AND",
|
|
462
|
+
or: "OR",
|
|
463
|
+
not: "NOT",
|
|
464
|
+
in: "IN",
|
|
465
|
+
is: "IS",
|
|
466
|
+
True: "BOOL",
|
|
467
|
+
False: "BOOL",
|
|
468
|
+
true: "BOOL",
|
|
469
|
+
false: "BOOL",
|
|
470
|
+
None: "NONE",
|
|
471
|
+
null: "NONE"
|
|
472
|
+
};
|
|
473
|
+
var CATEGORY_BY_TYPE = {
|
|
474
|
+
STRING: "value",
|
|
475
|
+
NUMBER: "value",
|
|
476
|
+
BOOL: "value",
|
|
477
|
+
NONE: "value",
|
|
478
|
+
IDENT: "variable",
|
|
479
|
+
AND: "operator",
|
|
480
|
+
OR: "operator",
|
|
481
|
+
NOT: "operator",
|
|
482
|
+
IN: "operator",
|
|
483
|
+
IS: "operator",
|
|
484
|
+
EQ: "operator",
|
|
485
|
+
NEQ: "operator",
|
|
486
|
+
LT: "operator",
|
|
487
|
+
GT: "operator",
|
|
488
|
+
LTE: "operator",
|
|
489
|
+
GTE: "operator",
|
|
490
|
+
LPAREN: "punctuation",
|
|
491
|
+
RPAREN: "punctuation"
|
|
492
|
+
};
|
|
493
|
+
var NEGATIVE_NUMBER_PRECEDERS = /* @__PURE__ */ new Set([
|
|
494
|
+
"AND",
|
|
495
|
+
"OR",
|
|
496
|
+
"NOT",
|
|
497
|
+
"IN",
|
|
498
|
+
"IS",
|
|
499
|
+
"EQ",
|
|
500
|
+
"NEQ",
|
|
501
|
+
"LT",
|
|
502
|
+
"GT",
|
|
503
|
+
"LTE",
|
|
504
|
+
"GTE",
|
|
505
|
+
"LPAREN"
|
|
506
|
+
]);
|
|
507
|
+
function canStartNegativeNumber(tokens) {
|
|
508
|
+
if (tokens.length === 0) {
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
const previous = tokens[tokens.length - 1];
|
|
512
|
+
return NEGATIVE_NUMBER_PRECEDERS.has(previous.type);
|
|
513
|
+
}
|
|
514
|
+
function finalizeTokens(input, rawTokens) {
|
|
515
|
+
const highlighted = [];
|
|
516
|
+
for (let i = 0; i < rawTokens.length; i++) {
|
|
517
|
+
const token = rawTokens[i];
|
|
518
|
+
const next = rawTokens[i + 1];
|
|
519
|
+
if (token.type === "IS" && next?.type === "NOT") {
|
|
520
|
+
highlighted.push({
|
|
521
|
+
text: input.slice(token.start, next.end),
|
|
522
|
+
start: token.start,
|
|
523
|
+
end: next.end,
|
|
524
|
+
category: "operator"
|
|
525
|
+
});
|
|
526
|
+
i++;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
highlighted.push({
|
|
530
|
+
text: token.text,
|
|
531
|
+
start: token.start,
|
|
532
|
+
end: token.end,
|
|
533
|
+
category: CATEGORY_BY_TYPE[token.type]
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
return highlighted;
|
|
537
|
+
}
|
|
538
|
+
function tokenizeCondition(input) {
|
|
539
|
+
const rawTokens = [];
|
|
540
|
+
let i = 0;
|
|
541
|
+
while (i < input.length) {
|
|
542
|
+
const char = input[i];
|
|
543
|
+
if (/\s/.test(char)) {
|
|
544
|
+
i++;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
if (char === '"' || char === "'") {
|
|
548
|
+
const start = i;
|
|
549
|
+
const quote = char;
|
|
550
|
+
i++;
|
|
551
|
+
while (i < input.length) {
|
|
552
|
+
if (input[i] === "\\" && i + 1 < input.length) {
|
|
553
|
+
i += 2;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (input[i] === quote) {
|
|
557
|
+
i++;
|
|
558
|
+
rawTokens.push({
|
|
559
|
+
type: "STRING",
|
|
560
|
+
text: input.slice(start, i),
|
|
561
|
+
start,
|
|
562
|
+
end: i
|
|
563
|
+
});
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
i++;
|
|
567
|
+
}
|
|
568
|
+
if (rawTokens[rawTokens.length - 1]?.start !== start) {
|
|
569
|
+
return finalizeTokens(input, rawTokens);
|
|
570
|
+
}
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
if (/[0-9]/.test(char) || char === "-" && i + 1 < input.length && /[0-9]/.test(input[i + 1]) && canStartNegativeNumber(rawTokens)) {
|
|
574
|
+
const start = i;
|
|
575
|
+
if (input[i] === "-") {
|
|
576
|
+
i++;
|
|
577
|
+
}
|
|
578
|
+
while (i < input.length && /[0-9]/.test(input[i])) {
|
|
579
|
+
i++;
|
|
580
|
+
}
|
|
581
|
+
if (i < input.length && input[i] === ".") {
|
|
582
|
+
i++;
|
|
583
|
+
while (i < input.length && /[0-9]/.test(input[i])) {
|
|
584
|
+
i++;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
rawTokens.push({
|
|
588
|
+
type: "NUMBER",
|
|
589
|
+
text: input.slice(start, i),
|
|
590
|
+
start,
|
|
591
|
+
end: i
|
|
592
|
+
});
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
if (/[a-zA-Z_]/.test(char)) {
|
|
596
|
+
const start = i;
|
|
597
|
+
i++;
|
|
598
|
+
while (i < input.length && /[a-zA-Z0-9_.]/.test(input[i])) {
|
|
599
|
+
i++;
|
|
600
|
+
}
|
|
601
|
+
const text2 = input.slice(start, i);
|
|
602
|
+
rawTokens.push({
|
|
603
|
+
type: KEYWORDS[text2] ?? "IDENT",
|
|
604
|
+
text: text2,
|
|
605
|
+
start,
|
|
606
|
+
end: i
|
|
607
|
+
});
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (i + 1 < input.length) {
|
|
611
|
+
const twoChar = input.slice(i, i + 2);
|
|
612
|
+
if (twoChar === "==") {
|
|
613
|
+
rawTokens.push({ type: "EQ", text: twoChar, start: i, end: i + 2 });
|
|
614
|
+
i += 2;
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
if (twoChar === "!=") {
|
|
618
|
+
rawTokens.push({ type: "NEQ", text: twoChar, start: i, end: i + 2 });
|
|
619
|
+
i += 2;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (twoChar === "<=") {
|
|
623
|
+
rawTokens.push({ type: "LTE", text: twoChar, start: i, end: i + 2 });
|
|
624
|
+
i += 2;
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (twoChar === ">=") {
|
|
628
|
+
rawTokens.push({ type: "GTE", text: twoChar, start: i, end: i + 2 });
|
|
629
|
+
i += 2;
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
if (char === "<") {
|
|
634
|
+
rawTokens.push({ type: "LT", text: char, start: i, end: i + 1 });
|
|
635
|
+
i++;
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
if (char === ">") {
|
|
639
|
+
rawTokens.push({ type: "GT", text: char, start: i, end: i + 1 });
|
|
640
|
+
i++;
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
if (char === "(") {
|
|
644
|
+
rawTokens.push({ type: "LPAREN", text: char, start: i, end: i + 1 });
|
|
645
|
+
i++;
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (char === ")") {
|
|
649
|
+
rawTokens.push({ type: "RPAREN", text: char, start: i, end: i + 1 });
|
|
650
|
+
i++;
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
return finalizeTokens(input, rawTokens);
|
|
654
|
+
}
|
|
655
|
+
return finalizeTokens(input, rawTokens);
|
|
656
|
+
}
|
|
657
|
+
|
|
459
658
|
// src/ui/components/icons.tsx
|
|
460
659
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
461
660
|
function IconTool({ size = 16, fill = "currentColor", style }) {
|
|
@@ -743,6 +942,172 @@ function renderInline(node, key2, renderNode) {
|
|
|
743
942
|
function renderInlineContent(content, keyPrefix, renderNode) {
|
|
744
943
|
return content.map((node, i) => renderInline(node, `${keyPrefix}-${i}`, renderNode));
|
|
745
944
|
}
|
|
945
|
+
var JINJA_RENDERER_STYLE_ID = "ab-renderer-jinja-styles";
|
|
946
|
+
var JINJA_RENDERER_STYLES = `
|
|
947
|
+
.ab-r-jinja-block {
|
|
948
|
+
margin: 8px 0;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
.ab-r-jinja-branch {
|
|
952
|
+
position: relative;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
.ab-r-jinja-header {
|
|
956
|
+
display: flex;
|
|
957
|
+
align-items: center;
|
|
958
|
+
gap: 12px;
|
|
959
|
+
padding: 8px;
|
|
960
|
+
border: 1px solid #E0E0E0;
|
|
961
|
+
border-radius: 4px;
|
|
962
|
+
background: #FFFFFF;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.ab-r-jinja-badge {
|
|
966
|
+
display: inline-flex;
|
|
967
|
+
align-items: center;
|
|
968
|
+
justify-content: center;
|
|
969
|
+
height: 32px;
|
|
970
|
+
padding: 0 8px;
|
|
971
|
+
border-radius: 4px;
|
|
972
|
+
font-family: "Roboto Mono", monospace;
|
|
973
|
+
font-size: 13px;
|
|
974
|
+
font-weight: 700;
|
|
975
|
+
line-height: 20px;
|
|
976
|
+
letter-spacing: -0.3px;
|
|
977
|
+
white-space: nowrap;
|
|
978
|
+
flex-shrink: 0;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.ab-r-jinja-badge-if,
|
|
982
|
+
.ab-r-jinja-badge-elif {
|
|
983
|
+
background: #E7F1FF;
|
|
984
|
+
color: #0D0D0D;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
.ab-r-jinja-badge-else {
|
|
988
|
+
background: #F7F7F7;
|
|
989
|
+
color: #424242;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.ab-r-jinja-condition {
|
|
993
|
+
flex: 1;
|
|
994
|
+
min-width: 0;
|
|
995
|
+
display: flex;
|
|
996
|
+
flex-wrap: wrap;
|
|
997
|
+
gap: 8px;
|
|
998
|
+
align-items: center;
|
|
999
|
+
font-family: "Roboto Mono", monospace;
|
|
1000
|
+
font-size: 13px;
|
|
1001
|
+
line-height: 20px;
|
|
1002
|
+
letter-spacing: -0.3px;
|
|
1003
|
+
color: #0D0D0D;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.ab-r-jinja-otherwise {
|
|
1007
|
+
flex: 1;
|
|
1008
|
+
color: #858585;
|
|
1009
|
+
font-family: "Roboto Mono", monospace;
|
|
1010
|
+
font-size: 13px;
|
|
1011
|
+
line-height: 20px;
|
|
1012
|
+
letter-spacing: -0.3px;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
.ab-r-jinja-body {
|
|
1016
|
+
display: flex;
|
|
1017
|
+
gap: 12px;
|
|
1018
|
+
padding: 0 8px;
|
|
1019
|
+
min-height: 32px;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
.ab-r-jinja-divider-col {
|
|
1023
|
+
width: 32px;
|
|
1024
|
+
flex-shrink: 0;
|
|
1025
|
+
display: flex;
|
|
1026
|
+
align-items: stretch;
|
|
1027
|
+
justify-content: center;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
.ab-r-jinja-divider {
|
|
1031
|
+
width: 1px;
|
|
1032
|
+
background: #E0E0E0;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
.ab-r-jinja-branch-last .ab-r-jinja-divider-col {
|
|
1036
|
+
align-items: flex-start;
|
|
1037
|
+
padding-left: 16px;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
.ab-r-jinja-branch-last .ab-r-jinja-divider {
|
|
1041
|
+
width: 16px;
|
|
1042
|
+
height: 28px;
|
|
1043
|
+
background: none;
|
|
1044
|
+
border-left: 1px solid #E0E0E0;
|
|
1045
|
+
border-bottom: 1px solid #E0E0E0;
|
|
1046
|
+
border-bottom-left-radius: 8px;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
.ab-r-jinja-content {
|
|
1050
|
+
flex: 1;
|
|
1051
|
+
min-width: 0;
|
|
1052
|
+
padding: 12px 0;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.ab-r-jinja-token-variable { color: #4141B2; }
|
|
1056
|
+
.ab-r-jinja-token-operator { color: #858585; }
|
|
1057
|
+
.ab-r-jinja-token-value { color: #0D0D0D; }
|
|
1058
|
+
.ab-r-jinja-token-punctuation { color: #858585; }
|
|
1059
|
+
`;
|
|
1060
|
+
var jinjaRendererStyleRefCount = 0;
|
|
1061
|
+
var jinjaRendererStyleEl = null;
|
|
1062
|
+
function ensureJinjaRendererStyles() {
|
|
1063
|
+
if (jinjaRendererStyleRefCount++ > 0) return;
|
|
1064
|
+
if (typeof document === "undefined") return;
|
|
1065
|
+
const existing = document.getElementById(JINJA_RENDERER_STYLE_ID);
|
|
1066
|
+
if (existing instanceof HTMLStyleElement) {
|
|
1067
|
+
jinjaRendererStyleEl = existing;
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
jinjaRendererStyleEl = document.createElement("style");
|
|
1071
|
+
jinjaRendererStyleEl.id = JINJA_RENDERER_STYLE_ID;
|
|
1072
|
+
jinjaRendererStyleEl.textContent = JINJA_RENDERER_STYLES;
|
|
1073
|
+
document.head.appendChild(jinjaRendererStyleEl);
|
|
1074
|
+
}
|
|
1075
|
+
function renderConditionTokens(condition) {
|
|
1076
|
+
const tokens = tokenizeCondition(condition);
|
|
1077
|
+
return tokens.map((token, i) => /* @__PURE__ */ jsx2("span", { className: `ab-r-jinja-token-${token.category}`, children: token.text }, `t-${i}-${token.start}`));
|
|
1078
|
+
}
|
|
1079
|
+
function renderJinjaBranch(branch, key2, isLast, renderNode) {
|
|
1080
|
+
const branchType = branch.branchType;
|
|
1081
|
+
const badgeLabel = branchType === "elif" ? "ELSE IF" : branchType.toUpperCase();
|
|
1082
|
+
return /* @__PURE__ */ jsxs2(
|
|
1083
|
+
"div",
|
|
1084
|
+
{
|
|
1085
|
+
className: `ab-r-jinja-branch${isLast ? " ab-r-jinja-branch-last" : ""}`,
|
|
1086
|
+
children: [
|
|
1087
|
+
/* @__PURE__ */ jsxs2("div", { className: "ab-r-jinja-header", children: [
|
|
1088
|
+
/* @__PURE__ */ jsx2("span", { className: `ab-r-jinja-badge ab-r-jinja-badge-${branchType}`, children: badgeLabel }),
|
|
1089
|
+
branchType === "else" ? /* @__PURE__ */ jsx2("span", { className: "ab-r-jinja-otherwise", children: "otherwise" }) : /* @__PURE__ */ jsx2("span", { className: "ab-r-jinja-condition", children: branch.condition ? renderConditionTokens(branch.condition) : null })
|
|
1090
|
+
] }),
|
|
1091
|
+
/* @__PURE__ */ jsxs2("div", { className: "ab-r-jinja-body", children: [
|
|
1092
|
+
/* @__PURE__ */ jsx2("div", { className: "ab-r-jinja-divider-col", children: /* @__PURE__ */ jsx2("div", { className: "ab-r-jinja-divider" }) }),
|
|
1093
|
+
/* @__PURE__ */ jsx2("div", { className: "ab-r-jinja-content", children: branch.content.map((child, i) => renderBlock(child, `${key2}-c${i}`, renderNode)) })
|
|
1094
|
+
] })
|
|
1095
|
+
]
|
|
1096
|
+
},
|
|
1097
|
+
key2
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
function renderJinjaIfBlock(node, key2, renderNode) {
|
|
1101
|
+
ensureJinjaRendererStyles();
|
|
1102
|
+
return /* @__PURE__ */ jsx2("div", { className: "ab-r-jinja-block", children: node.branches.map(
|
|
1103
|
+
(branch, i) => renderJinjaBranch(
|
|
1104
|
+
branch,
|
|
1105
|
+
`${key2}-b${i}`,
|
|
1106
|
+
i === node.branches.length - 1,
|
|
1107
|
+
renderNode
|
|
1108
|
+
)
|
|
1109
|
+
) }, key2);
|
|
1110
|
+
}
|
|
746
1111
|
function renderBlock(node, key2, renderNode) {
|
|
747
1112
|
const defaultRender = () => {
|
|
748
1113
|
switch (node.type) {
|
|
@@ -774,6 +1139,8 @@ function renderBlock(node, key2, renderNode) {
|
|
|
774
1139
|
return /* @__PURE__ */ jsx2("li", { children: node.content.map((child, i) => renderBlock(child, `${key2}-${i}`, renderNode)) }, key2);
|
|
775
1140
|
case "blockquote":
|
|
776
1141
|
return /* @__PURE__ */ jsx2("blockquote", { children: node.content.map((child, i) => renderBlock(child, `${key2}-${i}`, renderNode)) }, key2);
|
|
1142
|
+
case "jinjaIfBlock":
|
|
1143
|
+
return renderJinjaIfBlock(node, key2, renderNode);
|
|
777
1144
|
case "horizontalRule":
|
|
778
1145
|
return /* @__PURE__ */ jsx2("hr", {}, key2);
|
|
779
1146
|
case "table":
|
|
@@ -2367,6 +2734,39 @@ var ITALIC_STAR_RE = /(?:^|[^*])\*([^*]+)\*$/;
|
|
|
2367
2734
|
var ITALIC_UNDER_RE = /(?:^|[^_])_([^_]+)_$/;
|
|
2368
2735
|
var STRIKE_RE = /(?:^|[^~])~~([^~]+)~~$/;
|
|
2369
2736
|
var CODE_RE = /(?:^|[^`])`([^`]+)`$/;
|
|
2737
|
+
function findParentList(state, pos) {
|
|
2738
|
+
const $pos = state.doc.resolve(pos);
|
|
2739
|
+
const { bulletList: blType, orderedList: olType } = actionbookSchema.nodes;
|
|
2740
|
+
for (let d = $pos.depth; d > 0; d--) {
|
|
2741
|
+
const node = $pos.node(d);
|
|
2742
|
+
if (node.type === blType || node.type === olType) {
|
|
2743
|
+
return { depth: d, node };
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
return null;
|
|
2747
|
+
}
|
|
2748
|
+
function handleListInputRule(state, start, end, listType, attrs) {
|
|
2749
|
+
const resolvePos = Math.max(start, end - 1);
|
|
2750
|
+
const parentList = findParentList(state, resolvePos);
|
|
2751
|
+
if (!parentList) return null;
|
|
2752
|
+
const $from = state.doc.resolve(resolvePos);
|
|
2753
|
+
const { depth, node: listNode } = parentList;
|
|
2754
|
+
const paraContentSize = $from.parent.content.size;
|
|
2755
|
+
const matchedLen = end - start;
|
|
2756
|
+
if (paraContentSize !== matchedLen) return null;
|
|
2757
|
+
const listStart = $from.before(depth);
|
|
2758
|
+
const listEnd = $from.after(depth);
|
|
2759
|
+
if (listNode.type === listType) {
|
|
2760
|
+
const tr = state.tr.replaceWith(listStart, listEnd, actionbookSchema.nodes.paragraph.create());
|
|
2761
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(listStart)));
|
|
2762
|
+
return tr;
|
|
2763
|
+
} else {
|
|
2764
|
+
const tr = state.tr.delete(start, end);
|
|
2765
|
+
const mappedListPos = tr.mapping.map(listStart);
|
|
2766
|
+
tr.setNodeMarkup(mappedListPos, listType, attrs ?? null);
|
|
2767
|
+
return tr;
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2370
2770
|
function markInputRule(pattern, markType, markerLen) {
|
|
2371
2771
|
return new InputRule(pattern, (state, match, start, end) => {
|
|
2372
2772
|
const textContent2 = match[1];
|
|
@@ -2431,12 +2831,30 @@ function createInputRulesPlugin() {
|
|
|
2431
2831
|
return state.tr.delete(start, end).insert(start, list);
|
|
2432
2832
|
})
|
|
2433
2833
|
);
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
wrappingInputRule(
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2834
|
+
{
|
|
2835
|
+
const blType = actionbookSchema.nodes.bulletList;
|
|
2836
|
+
const fallback = wrappingInputRule(BULLET_LIST_RE, blType);
|
|
2837
|
+
rules.push(
|
|
2838
|
+
new InputRule(BULLET_LIST_RE, (state, match, start, end) => {
|
|
2839
|
+
const toggled = handleListInputRule(state, start, end, blType);
|
|
2840
|
+
if (toggled) return toggled;
|
|
2841
|
+
const handler = fallback.handler;
|
|
2842
|
+
return handler(state, match, start, end);
|
|
2843
|
+
})
|
|
2844
|
+
);
|
|
2845
|
+
}
|
|
2846
|
+
{
|
|
2847
|
+
const olType = actionbookSchema.nodes.orderedList;
|
|
2848
|
+
const fallback = wrappingInputRule(ORDERED_LIST_RE, olType, (m) => ({ start: +m[1] }));
|
|
2849
|
+
rules.push(
|
|
2850
|
+
new InputRule(ORDERED_LIST_RE, (state, match, start, end) => {
|
|
2851
|
+
const toggled = handleListInputRule(state, start, end, olType, { start: +match[1] });
|
|
2852
|
+
if (toggled) return toggled;
|
|
2853
|
+
const handler = fallback.handler;
|
|
2854
|
+
return handler(state, match, start, end);
|
|
2855
|
+
})
|
|
2856
|
+
);
|
|
2857
|
+
}
|
|
2440
2858
|
const jumpPointType = actionbookSchema.nodes.jumpPoint;
|
|
2441
2859
|
rules.push(
|
|
2442
2860
|
new InputRule(JUMP_POINT_RE2, (state, match, start, end) => {
|
|
@@ -2449,7 +2867,12 @@ function createInputRulesPlugin() {
|
|
|
2449
2867
|
}
|
|
2450
2868
|
});
|
|
2451
2869
|
if (exists) return null;
|
|
2452
|
-
|
|
2870
|
+
const tr = state.tr.delete(start, end).insert(start, jumpPointType.create({ id }));
|
|
2871
|
+
const afterAtom = start + 1;
|
|
2872
|
+
tr.insertText(" ", afterAtom);
|
|
2873
|
+
const cursorPos = afterAtom + 1;
|
|
2874
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(Math.min(cursorPos, tr.doc.content.size))));
|
|
2875
|
+
return tr;
|
|
2453
2876
|
})
|
|
2454
2877
|
);
|
|
2455
2878
|
rules.push(markInputRule(BOLD_STAR_RE, actionbookSchema.marks.bold, 2));
|
|
@@ -2468,18 +2891,110 @@ import { history, undo, redo } from "prosemirror-history";
|
|
|
2468
2891
|
import { keymap as keymap2 } from "prosemirror-keymap";
|
|
2469
2892
|
function createHistoryPlugin() {
|
|
2470
2893
|
return {
|
|
2471
|
-
name: "history",
|
|
2472
|
-
plugins: () => [
|
|
2473
|
-
history(),
|
|
2474
|
-
keymap2({ "Mod-z": undo, "Mod-Shift-z": redo, "Mod-y": redo })
|
|
2475
|
-
]
|
|
2894
|
+
name: "history",
|
|
2895
|
+
plugins: () => [
|
|
2896
|
+
history(),
|
|
2897
|
+
keymap2({ "Mod-z": undo, "Mod-Shift-z": redo, "Mod-y": redo })
|
|
2898
|
+
]
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
// src/ui/plugin/keymapPlugin.ts
|
|
2903
|
+
import { baseKeymap, chainCommands, newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock, toggleMark, setBlockType, joinBackward } from "prosemirror-commands";
|
|
2904
|
+
import { TextSelection as TextSelection2 } from "prosemirror-state";
|
|
2905
|
+
import { keymap as keymap3 } from "prosemirror-keymap";
|
|
2906
|
+
import { liftListItem, sinkListItem, splitListItem, wrapInList } from "prosemirror-schema-list";
|
|
2907
|
+
|
|
2908
|
+
// src/ui/plugin/noteBlockPlugin.tsx
|
|
2909
|
+
import { Selection } from "prosemirror-state";
|
|
2910
|
+
var NoteBlockView = class {
|
|
2911
|
+
dom;
|
|
2912
|
+
contentDOM;
|
|
2913
|
+
constructor(_node, _view, _getPos) {
|
|
2914
|
+
this.dom = document.createElement("div");
|
|
2915
|
+
this.dom.setAttribute("data-note-block", "");
|
|
2916
|
+
this.dom.className = "ab-note-block";
|
|
2917
|
+
this.dom.style.cssText = [
|
|
2918
|
+
"position: relative",
|
|
2919
|
+
"margin: 8px 0",
|
|
2920
|
+
"padding: 16px 24px",
|
|
2921
|
+
"background: #fff",
|
|
2922
|
+
"border: 1px dashed #ccc",
|
|
2923
|
+
"border-radius: 4px",
|
|
2924
|
+
"display: flex",
|
|
2925
|
+
"flex-direction: column",
|
|
2926
|
+
"gap: 4px"
|
|
2927
|
+
].join(";");
|
|
2928
|
+
const label = document.createElement("span");
|
|
2929
|
+
label.contentEditable = "false";
|
|
2930
|
+
label.textContent = "Note";
|
|
2931
|
+
label.style.cssText = [
|
|
2932
|
+
"font-family: SF Pro Text, -apple-system, BlinkMacSystemFont, sans-serif",
|
|
2933
|
+
"display: inline-flex",
|
|
2934
|
+
"align-items: center",
|
|
2935
|
+
"justify-content: center",
|
|
2936
|
+
"align-self: flex-start",
|
|
2937
|
+
"font-size: 12px",
|
|
2938
|
+
"font-weight: 400",
|
|
2939
|
+
"font-style: normal",
|
|
2940
|
+
"line-height: 16px",
|
|
2941
|
+
"color: #858585",
|
|
2942
|
+
"background: #f7f7f7",
|
|
2943
|
+
"border: 1px solid #e0e0e0",
|
|
2944
|
+
"border-radius: 4px",
|
|
2945
|
+
"padding: 2px 4px",
|
|
2946
|
+
"user-select: none",
|
|
2947
|
+
"height: 20px"
|
|
2948
|
+
].join(";");
|
|
2949
|
+
this.dom.appendChild(label);
|
|
2950
|
+
this.contentDOM = document.createElement("div");
|
|
2951
|
+
this.contentDOM.className = "ab-note-block-content";
|
|
2952
|
+
this.contentDOM.style.cssText = [
|
|
2953
|
+
"font-size: 14px",
|
|
2954
|
+
"font-style: italic",
|
|
2955
|
+
"font-weight: 400",
|
|
2956
|
+
"line-height: 20px",
|
|
2957
|
+
"letter-spacing: -0.1px",
|
|
2958
|
+
"color: #858585"
|
|
2959
|
+
].join(";");
|
|
2960
|
+
this.dom.appendChild(this.contentDOM);
|
|
2961
|
+
}
|
|
2962
|
+
};
|
|
2963
|
+
var exitNoteBlockOnEnter = (state, dispatch) => {
|
|
2964
|
+
const { $from } = state.selection;
|
|
2965
|
+
let noteBlockDepth = -1;
|
|
2966
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
2967
|
+
if ($from.node(d).type.name === "noteBlock") {
|
|
2968
|
+
noteBlockDepth = d;
|
|
2969
|
+
break;
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
if (noteBlockDepth < 0) return false;
|
|
2973
|
+
const parentNode = $from.parent;
|
|
2974
|
+
if (parentNode.type.name !== "paragraph" || parentNode.content.size !== 0) {
|
|
2975
|
+
return false;
|
|
2976
|
+
}
|
|
2977
|
+
if (!dispatch) return true;
|
|
2978
|
+
const paragraphStart = $from.before($from.depth);
|
|
2979
|
+
const paragraphEnd = $from.after($from.depth);
|
|
2980
|
+
const tr = state.tr.delete(paragraphStart, paragraphEnd);
|
|
2981
|
+
const noteBlockEnd = tr.mapping.map($from.after(noteBlockDepth));
|
|
2982
|
+
const newParagraph = state.schema.nodes.paragraph.create();
|
|
2983
|
+
tr.insert(noteBlockEnd, newParagraph);
|
|
2984
|
+
tr.setSelection(Selection.near(tr.doc.resolve(noteBlockEnd + 1)));
|
|
2985
|
+
dispatch(tr.scrollIntoView());
|
|
2986
|
+
return true;
|
|
2987
|
+
};
|
|
2988
|
+
function createNoteBlockPlugin() {
|
|
2989
|
+
return {
|
|
2990
|
+
name: "noteBlockNodeView",
|
|
2991
|
+
nodeViews: () => ({
|
|
2992
|
+
noteBlock: (node, view, getPos) => new NoteBlockView(node, view, getPos)
|
|
2993
|
+
})
|
|
2476
2994
|
};
|
|
2477
2995
|
}
|
|
2478
2996
|
|
|
2479
2997
|
// src/ui/plugin/keymapPlugin.ts
|
|
2480
|
-
import { baseKeymap, chainCommands, newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock, toggleMark, setBlockType, joinBackward } from "prosemirror-commands";
|
|
2481
|
-
import { keymap as keymap3 } from "prosemirror-keymap";
|
|
2482
|
-
import { liftListItem, sinkListItem, splitListItem, wrapInList } from "prosemirror-schema-list";
|
|
2483
2998
|
var TAB_CHAR = " ";
|
|
2484
2999
|
var { listItem, bulletList, orderedList, hardBreak, heading, paragraph, horizontalRule, blockquote } = actionbookSchema.nodes;
|
|
2485
3000
|
var { bold: boldMark, italic: italicMark, underline: underlineMark, strikethrough: strikethroughMark, code: codeMark } = actionbookSchema.marks;
|
|
@@ -2526,7 +3041,38 @@ var backspaceAfterLeafBlock = (state, dispatch) => {
|
|
|
2526
3041
|
}
|
|
2527
3042
|
return true;
|
|
2528
3043
|
};
|
|
3044
|
+
var backspaceDeleteEmptyBlock = (state, dispatch) => {
|
|
3045
|
+
const { $from } = state.selection;
|
|
3046
|
+
if (!state.selection.empty || $from.parentOffset !== 0) return false;
|
|
3047
|
+
if ($from.parent.type !== paragraph || $from.parent.content.size !== 0) return false;
|
|
3048
|
+
const deletableTypes = /* @__PURE__ */ new Set(["jinjaIfBlock", "jinjaIfBranch", "noteBlock", "blockquote"]);
|
|
3049
|
+
for (let d = $from.depth - 1; d > 0; d--) {
|
|
3050
|
+
const ancestor = $from.node(d);
|
|
3051
|
+
if (!deletableTypes.has(ancestor.type.name)) continue;
|
|
3052
|
+
if (ancestor.type.name === "jinjaIfBranch" && ancestor.childCount === 1 && ancestor.child(0).content.size === 0) {
|
|
3053
|
+
for (let dd = d - 1; dd > 0; dd--) {
|
|
3054
|
+
if ($from.node(dd).type.name === "jinjaIfBlock") {
|
|
3055
|
+
d = dd;
|
|
3056
|
+
break;
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
const blockNode = $from.node(d);
|
|
3061
|
+
const blockText = blockNode.textContent.trim();
|
|
3062
|
+
if (blockText.length > 0) return false;
|
|
3063
|
+
if (dispatch) {
|
|
3064
|
+
const blockStart = $from.before(d);
|
|
3065
|
+
const blockEnd = $from.after(d);
|
|
3066
|
+
const tr = state.tr.replaceWith(blockStart, blockEnd, paragraph.create());
|
|
3067
|
+
tr.setSelection(TextSelection2.near(tr.doc.resolve(blockStart)));
|
|
3068
|
+
dispatch(tr.scrollIntoView());
|
|
3069
|
+
}
|
|
3070
|
+
return true;
|
|
3071
|
+
}
|
|
3072
|
+
return false;
|
|
3073
|
+
};
|
|
2529
3074
|
var backspaceCommand = chainCommands(
|
|
3075
|
+
backspaceDeleteEmptyBlock,
|
|
2530
3076
|
backspaceAfterList,
|
|
2531
3077
|
backspaceAfterLeafBlock,
|
|
2532
3078
|
(state, dispatch) => {
|
|
@@ -2544,8 +3090,9 @@ var backspaceCommand = chainCommands(
|
|
|
2544
3090
|
);
|
|
2545
3091
|
var tabCommand = (state, dispatch) => {
|
|
2546
3092
|
const { $from } = state.selection;
|
|
2547
|
-
if (cursorDirectlyInListItem(state)
|
|
3093
|
+
if (cursorDirectlyInListItem(state)) {
|
|
2548
3094
|
if (sinkListItem(listItem)(state, dispatch)) return true;
|
|
3095
|
+
return true;
|
|
2549
3096
|
}
|
|
2550
3097
|
if (dispatch) dispatch(state.tr.insertText(TAB_CHAR));
|
|
2551
3098
|
return true;
|
|
@@ -2562,7 +3109,68 @@ var shiftTabCommand = (state, dispatch) => {
|
|
|
2562
3109
|
}
|
|
2563
3110
|
return true;
|
|
2564
3111
|
};
|
|
3112
|
+
var blockExitEnterCount = 0;
|
|
3113
|
+
var blockExitLastTime = 0;
|
|
3114
|
+
var BLOCK_EXIT_TYPES = /* @__PURE__ */ new Set(["blockquote", "jinjaIfBranch"]);
|
|
3115
|
+
var exitBlockOnDoubleEnter = (state, dispatch) => {
|
|
3116
|
+
const { $from } = state.selection;
|
|
3117
|
+
let blockDepth = -1;
|
|
3118
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
3119
|
+
if (BLOCK_EXIT_TYPES.has($from.node(d).type.name)) {
|
|
3120
|
+
blockDepth = d;
|
|
3121
|
+
break;
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
if (blockDepth < 0) {
|
|
3125
|
+
blockExitEnterCount = 0;
|
|
3126
|
+
return false;
|
|
3127
|
+
}
|
|
3128
|
+
if ($from.parent.type.name !== "paragraph" || $from.parent.content.size !== 0) {
|
|
3129
|
+
blockExitEnterCount = 0;
|
|
3130
|
+
return false;
|
|
3131
|
+
}
|
|
3132
|
+
const now = Date.now();
|
|
3133
|
+
if (now - blockExitLastTime > 2e3) blockExitEnterCount = 0;
|
|
3134
|
+
blockExitLastTime = now;
|
|
3135
|
+
blockExitEnterCount++;
|
|
3136
|
+
if (blockExitEnterCount < 2) return false;
|
|
3137
|
+
blockExitEnterCount = 0;
|
|
3138
|
+
if (!dispatch) return true;
|
|
3139
|
+
const paraStart = $from.before($from.depth);
|
|
3140
|
+
const paraEnd = $from.after($from.depth);
|
|
3141
|
+
const tr = state.tr.delete(paraStart, paraEnd);
|
|
3142
|
+
const mapped = tr.mapping.map($from.before($from.depth));
|
|
3143
|
+
const $m = tr.doc.resolve(Math.min(mapped, tr.doc.content.size));
|
|
3144
|
+
for (let d = $m.depth; d > 0; d--) {
|
|
3145
|
+
if (BLOCK_EXIT_TYPES.has($m.node(d).type.name)) {
|
|
3146
|
+
const last = $m.node(d).lastChild;
|
|
3147
|
+
if (last?.type.name === "paragraph" && last.content.size === 0) {
|
|
3148
|
+
const lastPos = $m.end(d) - last.nodeSize;
|
|
3149
|
+
tr.delete(lastPos, lastPos + last.nodeSize);
|
|
3150
|
+
}
|
|
3151
|
+
break;
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
let exitDepth = blockDepth;
|
|
3155
|
+
const blockTypeName = $from.node(blockDepth).type.name;
|
|
3156
|
+
if (blockTypeName === "jinjaIfBranch") {
|
|
3157
|
+
for (let d = blockDepth - 1; d > 0; d--) {
|
|
3158
|
+
if ($from.node(d).type.name === "jinjaIfBlock") {
|
|
3159
|
+
exitDepth = d;
|
|
3160
|
+
break;
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
const blockEnd = tr.mapping.map($from.after(exitDepth));
|
|
3165
|
+
const newPara = paragraph.create();
|
|
3166
|
+
tr.insert(blockEnd, newPara);
|
|
3167
|
+
tr.setSelection(TextSelection2.near(tr.doc.resolve(blockEnd + 1)));
|
|
3168
|
+
dispatch(tr.scrollIntoView());
|
|
3169
|
+
return true;
|
|
3170
|
+
};
|
|
2565
3171
|
var enterCommand = chainCommands(
|
|
3172
|
+
exitNoteBlockOnEnter,
|
|
3173
|
+
exitBlockOnDoubleEnter,
|
|
2566
3174
|
newlineInCode,
|
|
2567
3175
|
// Split list item on Enter; lift empty list item out of list
|
|
2568
3176
|
splitListItem(listItem),
|
|
@@ -4336,207 +4944,19 @@ function analyzeJinjaBlocks(doc2) {
|
|
|
4336
4944
|
});
|
|
4337
4945
|
}
|
|
4338
4946
|
|
|
4339
|
-
// src/jinja/conditionHighlighter.ts
|
|
4340
|
-
var KEYWORDS = {
|
|
4341
|
-
and: "AND",
|
|
4342
|
-
or: "OR",
|
|
4343
|
-
not: "NOT",
|
|
4344
|
-
in: "IN",
|
|
4345
|
-
is: "IS",
|
|
4346
|
-
True: "BOOL",
|
|
4347
|
-
False: "BOOL",
|
|
4348
|
-
true: "BOOL",
|
|
4349
|
-
false: "BOOL",
|
|
4350
|
-
None: "NONE",
|
|
4351
|
-
null: "NONE"
|
|
4352
|
-
};
|
|
4353
|
-
var CATEGORY_BY_TYPE = {
|
|
4354
|
-
STRING: "value",
|
|
4355
|
-
NUMBER: "value",
|
|
4356
|
-
BOOL: "value",
|
|
4357
|
-
NONE: "value",
|
|
4358
|
-
IDENT: "variable",
|
|
4359
|
-
AND: "operator",
|
|
4360
|
-
OR: "operator",
|
|
4361
|
-
NOT: "operator",
|
|
4362
|
-
IN: "operator",
|
|
4363
|
-
IS: "operator",
|
|
4364
|
-
EQ: "operator",
|
|
4365
|
-
NEQ: "operator",
|
|
4366
|
-
LT: "operator",
|
|
4367
|
-
GT: "operator",
|
|
4368
|
-
LTE: "operator",
|
|
4369
|
-
GTE: "operator",
|
|
4370
|
-
LPAREN: "punctuation",
|
|
4371
|
-
RPAREN: "punctuation"
|
|
4372
|
-
};
|
|
4373
|
-
var NEGATIVE_NUMBER_PRECEDERS = /* @__PURE__ */ new Set([
|
|
4374
|
-
"AND",
|
|
4375
|
-
"OR",
|
|
4376
|
-
"NOT",
|
|
4377
|
-
"IN",
|
|
4378
|
-
"IS",
|
|
4379
|
-
"EQ",
|
|
4380
|
-
"NEQ",
|
|
4381
|
-
"LT",
|
|
4382
|
-
"GT",
|
|
4383
|
-
"LTE",
|
|
4384
|
-
"GTE",
|
|
4385
|
-
"LPAREN"
|
|
4386
|
-
]);
|
|
4387
|
-
function canStartNegativeNumber(tokens) {
|
|
4388
|
-
if (tokens.length === 0) {
|
|
4389
|
-
return true;
|
|
4390
|
-
}
|
|
4391
|
-
const previous = tokens[tokens.length - 1];
|
|
4392
|
-
return NEGATIVE_NUMBER_PRECEDERS.has(previous.type);
|
|
4393
|
-
}
|
|
4394
|
-
function finalizeTokens(input, rawTokens) {
|
|
4395
|
-
const highlighted = [];
|
|
4396
|
-
for (let i = 0; i < rawTokens.length; i++) {
|
|
4397
|
-
const token = rawTokens[i];
|
|
4398
|
-
const next = rawTokens[i + 1];
|
|
4399
|
-
if (token.type === "IS" && next?.type === "NOT") {
|
|
4400
|
-
highlighted.push({
|
|
4401
|
-
text: input.slice(token.start, next.end),
|
|
4402
|
-
start: token.start,
|
|
4403
|
-
end: next.end,
|
|
4404
|
-
category: "operator"
|
|
4405
|
-
});
|
|
4406
|
-
i++;
|
|
4407
|
-
continue;
|
|
4408
|
-
}
|
|
4409
|
-
highlighted.push({
|
|
4410
|
-
text: token.text,
|
|
4411
|
-
start: token.start,
|
|
4412
|
-
end: token.end,
|
|
4413
|
-
category: CATEGORY_BY_TYPE[token.type]
|
|
4414
|
-
});
|
|
4415
|
-
}
|
|
4416
|
-
return highlighted;
|
|
4417
|
-
}
|
|
4418
|
-
function tokenizeCondition(input) {
|
|
4419
|
-
const rawTokens = [];
|
|
4420
|
-
let i = 0;
|
|
4421
|
-
while (i < input.length) {
|
|
4422
|
-
const char = input[i];
|
|
4423
|
-
if (/\s/.test(char)) {
|
|
4424
|
-
i++;
|
|
4425
|
-
continue;
|
|
4426
|
-
}
|
|
4427
|
-
if (char === '"' || char === "'") {
|
|
4428
|
-
const start = i;
|
|
4429
|
-
const quote = char;
|
|
4430
|
-
i++;
|
|
4431
|
-
while (i < input.length) {
|
|
4432
|
-
if (input[i] === "\\" && i + 1 < input.length) {
|
|
4433
|
-
i += 2;
|
|
4434
|
-
continue;
|
|
4435
|
-
}
|
|
4436
|
-
if (input[i] === quote) {
|
|
4437
|
-
i++;
|
|
4438
|
-
rawTokens.push({
|
|
4439
|
-
type: "STRING",
|
|
4440
|
-
text: input.slice(start, i),
|
|
4441
|
-
start,
|
|
4442
|
-
end: i
|
|
4443
|
-
});
|
|
4444
|
-
break;
|
|
4445
|
-
}
|
|
4446
|
-
i++;
|
|
4447
|
-
}
|
|
4448
|
-
if (rawTokens[rawTokens.length - 1]?.start !== start) {
|
|
4449
|
-
return finalizeTokens(input, rawTokens);
|
|
4450
|
-
}
|
|
4451
|
-
continue;
|
|
4452
|
-
}
|
|
4453
|
-
if (/[0-9]/.test(char) || char === "-" && i + 1 < input.length && /[0-9]/.test(input[i + 1]) && canStartNegativeNumber(rawTokens)) {
|
|
4454
|
-
const start = i;
|
|
4455
|
-
if (input[i] === "-") {
|
|
4456
|
-
i++;
|
|
4457
|
-
}
|
|
4458
|
-
while (i < input.length && /[0-9]/.test(input[i])) {
|
|
4459
|
-
i++;
|
|
4460
|
-
}
|
|
4461
|
-
if (i < input.length && input[i] === ".") {
|
|
4462
|
-
i++;
|
|
4463
|
-
while (i < input.length && /[0-9]/.test(input[i])) {
|
|
4464
|
-
i++;
|
|
4465
|
-
}
|
|
4466
|
-
}
|
|
4467
|
-
rawTokens.push({
|
|
4468
|
-
type: "NUMBER",
|
|
4469
|
-
text: input.slice(start, i),
|
|
4470
|
-
start,
|
|
4471
|
-
end: i
|
|
4472
|
-
});
|
|
4473
|
-
continue;
|
|
4474
|
-
}
|
|
4475
|
-
if (/[a-zA-Z_]/.test(char)) {
|
|
4476
|
-
const start = i;
|
|
4477
|
-
i++;
|
|
4478
|
-
while (i < input.length && /[a-zA-Z0-9_.]/.test(input[i])) {
|
|
4479
|
-
i++;
|
|
4480
|
-
}
|
|
4481
|
-
const text2 = input.slice(start, i);
|
|
4482
|
-
rawTokens.push({
|
|
4483
|
-
type: KEYWORDS[text2] ?? "IDENT",
|
|
4484
|
-
text: text2,
|
|
4485
|
-
start,
|
|
4486
|
-
end: i
|
|
4487
|
-
});
|
|
4488
|
-
continue;
|
|
4489
|
-
}
|
|
4490
|
-
if (i + 1 < input.length) {
|
|
4491
|
-
const twoChar = input.slice(i, i + 2);
|
|
4492
|
-
if (twoChar === "==") {
|
|
4493
|
-
rawTokens.push({ type: "EQ", text: twoChar, start: i, end: i + 2 });
|
|
4494
|
-
i += 2;
|
|
4495
|
-
continue;
|
|
4496
|
-
}
|
|
4497
|
-
if (twoChar === "!=") {
|
|
4498
|
-
rawTokens.push({ type: "NEQ", text: twoChar, start: i, end: i + 2 });
|
|
4499
|
-
i += 2;
|
|
4500
|
-
continue;
|
|
4501
|
-
}
|
|
4502
|
-
if (twoChar === "<=") {
|
|
4503
|
-
rawTokens.push({ type: "LTE", text: twoChar, start: i, end: i + 2 });
|
|
4504
|
-
i += 2;
|
|
4505
|
-
continue;
|
|
4506
|
-
}
|
|
4507
|
-
if (twoChar === ">=") {
|
|
4508
|
-
rawTokens.push({ type: "GTE", text: twoChar, start: i, end: i + 2 });
|
|
4509
|
-
i += 2;
|
|
4510
|
-
continue;
|
|
4511
|
-
}
|
|
4512
|
-
}
|
|
4513
|
-
if (char === "<") {
|
|
4514
|
-
rawTokens.push({ type: "LT", text: char, start: i, end: i + 1 });
|
|
4515
|
-
i++;
|
|
4516
|
-
continue;
|
|
4517
|
-
}
|
|
4518
|
-
if (char === ">") {
|
|
4519
|
-
rawTokens.push({ type: "GT", text: char, start: i, end: i + 1 });
|
|
4520
|
-
i++;
|
|
4521
|
-
continue;
|
|
4522
|
-
}
|
|
4523
|
-
if (char === "(") {
|
|
4524
|
-
rawTokens.push({ type: "LPAREN", text: char, start: i, end: i + 1 });
|
|
4525
|
-
i++;
|
|
4526
|
-
continue;
|
|
4527
|
-
}
|
|
4528
|
-
if (char === ")") {
|
|
4529
|
-
rawTokens.push({ type: "RPAREN", text: char, start: i, end: i + 1 });
|
|
4530
|
-
i++;
|
|
4531
|
-
continue;
|
|
4532
|
-
}
|
|
4533
|
-
return finalizeTokens(input, rawTokens);
|
|
4534
|
-
}
|
|
4535
|
-
return finalizeTokens(input, rawTokens);
|
|
4536
|
-
}
|
|
4537
|
-
|
|
4538
4947
|
// src/tree/documentTree.ts
|
|
4539
4948
|
var MAX_DEPTH6 = 128;
|
|
4949
|
+
var END_ACTION_RESOURCE_IDS = /* @__PURE__ */ new Set([
|
|
4950
|
+
"close-happy-tiger"
|
|
4951
|
+
// PredefinedToolKey.CloseConversation
|
|
4952
|
+
]);
|
|
4953
|
+
var END_ACTION_TEXTS = /* @__PURE__ */ new Set([
|
|
4954
|
+
"end conversation",
|
|
4955
|
+
"end_conversation"
|
|
4956
|
+
]);
|
|
4957
|
+
function isEndActionTag(tagType, resourceId, text2) {
|
|
4958
|
+
return tagType === "handoff" || END_ACTION_RESOURCE_IDS.has(resourceId) || END_ACTION_TEXTS.has(text2.toLowerCase());
|
|
4959
|
+
}
|
|
4540
4960
|
function extractInlineItems(content, blockIndex) {
|
|
4541
4961
|
const items = [];
|
|
4542
4962
|
for (const node of content) {
|
|
@@ -4548,10 +4968,10 @@ function extractInlineItems(content, blockIndex) {
|
|
|
4548
4968
|
children: []
|
|
4549
4969
|
});
|
|
4550
4970
|
} else if (node.type === "resourceTag") {
|
|
4551
|
-
const
|
|
4971
|
+
const endAction = isEndActionTag(node.tagType, node.resourceId, node.text);
|
|
4552
4972
|
items.push({
|
|
4553
|
-
type:
|
|
4554
|
-
label:
|
|
4973
|
+
type: endAction ? "endAction" : "resourceTag",
|
|
4974
|
+
label: endAction ? `End ${node.text}` : node.text,
|
|
4555
4975
|
blockIndex,
|
|
4556
4976
|
children: [],
|
|
4557
4977
|
meta: { tagType: node.tagType, resourceId: node.resourceId }
|
|
@@ -4585,14 +5005,15 @@ function extractBlockItems(blocks, startIndex, depth) {
|
|
|
4585
5005
|
break;
|
|
4586
5006
|
}
|
|
4587
5007
|
case "jinjaIfBlock": {
|
|
4588
|
-
const branches = block.branches.map((branch) => {
|
|
5008
|
+
const branches = block.branches.map((branch, branchIdx) => {
|
|
4589
5009
|
const branchLabel = branch.branchType === "else" ? "ELSE" : `${branch.branchType.toUpperCase()} ${branch.condition || ""}`.trim();
|
|
4590
5010
|
const branchChildren = extractBlockItems(branch.content, blockIndex, depth + 1);
|
|
4591
5011
|
return {
|
|
4592
5012
|
type: "jinjaBranch",
|
|
4593
5013
|
label: branchLabel,
|
|
4594
5014
|
blockIndex,
|
|
4595
|
-
children: branchChildren
|
|
5015
|
+
children: branchChildren,
|
|
5016
|
+
meta: { branchIndex: branchIdx }
|
|
4596
5017
|
};
|
|
4597
5018
|
});
|
|
4598
5019
|
items.push({
|
|
@@ -4640,11 +5061,12 @@ function buildDocumentTree(doc2) {
|
|
|
4640
5061
|
const hasStructuredJinja = items.some((n) => n.type === "jinjaIf");
|
|
4641
5062
|
if (!hasStructuredJinja) {
|
|
4642
5063
|
for (const s of structures) {
|
|
4643
|
-
const branches = s.branches.map((b) => ({
|
|
5064
|
+
const branches = s.branches.map((b, branchIdx) => ({
|
|
4644
5065
|
type: "jinjaBranch",
|
|
4645
5066
|
label: b.type === "else" ? "ELSE" : `${b.type.toUpperCase()} ${b.condition || ""}`.trim(),
|
|
4646
5067
|
blockIndex: b.tagBlockIndex,
|
|
4647
|
-
children: []
|
|
5068
|
+
children: [],
|
|
5069
|
+
meta: { branchIndex: branchIdx }
|
|
4648
5070
|
}));
|
|
4649
5071
|
const jinjaNode = {
|
|
4650
5072
|
type: "jinjaIf",
|
|
@@ -4748,6 +5170,7 @@ function textToSlice(text2, view) {
|
|
|
4748
5170
|
return null;
|
|
4749
5171
|
}
|
|
4750
5172
|
}
|
|
5173
|
+
var URL_RE = /^https?:\/\/[^\s]+$/i;
|
|
4751
5174
|
function createPlugin() {
|
|
4752
5175
|
return new Plugin({
|
|
4753
5176
|
key,
|
|
@@ -4762,6 +5185,14 @@ function createPlugin() {
|
|
|
4762
5185
|
return true;
|
|
4763
5186
|
}
|
|
4764
5187
|
}
|
|
5188
|
+
if (text2 && URL_RE.test(text2.trim())) {
|
|
5189
|
+
const url = text2.trim();
|
|
5190
|
+
const linkMark2 = actionbookSchema.marks.link.create({ href: url });
|
|
5191
|
+
const textNode = actionbookSchema.text(url, [linkMark2]);
|
|
5192
|
+
const slice2 = new Slice(Fragment.from(textNode), 0, 0);
|
|
5193
|
+
view.dispatch(view.state.tr.replaceSelection(slice2));
|
|
5194
|
+
return true;
|
|
5195
|
+
}
|
|
4765
5196
|
if (!text2) return false;
|
|
4766
5197
|
const slice = textToSlice(text2, view);
|
|
4767
5198
|
if (!slice) return false;
|
|
@@ -4807,7 +5238,7 @@ function createMarkdownClipboardPlugin() {
|
|
|
4807
5238
|
}
|
|
4808
5239
|
|
|
4809
5240
|
// src/ui/plugin/jumpPointPlugin.ts
|
|
4810
|
-
import { Plugin as Plugin2, PluginKey as PluginKey2, TextSelection as
|
|
5241
|
+
import { Plugin as Plugin2, PluginKey as PluginKey2, TextSelection as TextSelection3 } from "prosemirror-state";
|
|
4811
5242
|
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
4812
5243
|
var adjacentKey = new PluginKey2("jumpPointAdjacent");
|
|
4813
5244
|
var JUMP_POINT_ADJACENT_SPEC = { jumpPointAdjacent: true };
|
|
@@ -4870,6 +5301,9 @@ function createJumpPointEditPlugin() {
|
|
|
4870
5301
|
});
|
|
4871
5302
|
if (!duplicate) {
|
|
4872
5303
|
reconvertTr.replaceWith(safeFrom, safeTo, jumpPointType.create({ id }));
|
|
5304
|
+
const mappedCursor = reconvertTr.mapping.map(cursor);
|
|
5305
|
+
const clampedCursor = Math.min(mappedCursor, reconvertTr.doc.content.size);
|
|
5306
|
+
reconvertTr.setSelection(TextSelection3.near(reconvertTr.doc.resolve(clampedCursor)));
|
|
4873
5307
|
}
|
|
4874
5308
|
}
|
|
4875
5309
|
}
|
|
@@ -4902,7 +5336,7 @@ function explodeOnBackspace(state) {
|
|
|
4902
5336
|
const rawText = `^${id}`;
|
|
4903
5337
|
const tr = state.tr.replaceWith(nodeStart, nodeEnd, state.schema.text(rawText));
|
|
4904
5338
|
const cursorPos = Math.min(nodeStart + rawText.length, tr.doc.content.size);
|
|
4905
|
-
tr.setSelection(
|
|
5339
|
+
tr.setSelection(TextSelection3.near(tr.doc.resolve(cursorPos)));
|
|
4906
5340
|
return tr;
|
|
4907
5341
|
}
|
|
4908
5342
|
function explodeOnArrowLeft(state) {
|
|
@@ -4912,7 +5346,7 @@ function explodeOnArrowLeft(state) {
|
|
|
4912
5346
|
const rawText = `^${id}^`;
|
|
4913
5347
|
const tr = state.tr.replaceWith(nodeStart, nodeEnd, state.schema.text(rawText));
|
|
4914
5348
|
const cursorPos = Math.min(nodeStart + 1 + id.length, tr.doc.content.size);
|
|
4915
|
-
tr.setSelection(
|
|
5349
|
+
tr.setSelection(TextSelection3.near(tr.doc.resolve(cursorPos)));
|
|
4916
5350
|
tr.setMeta(jumpPointEditKey, { from: nodeStart, to: nodeStart + rawText.length });
|
|
4917
5351
|
return tr;
|
|
4918
5352
|
}
|
|
@@ -4961,21 +5395,30 @@ function createJumpPointAdjacentPlugin() {
|
|
|
4961
5395
|
// src/ui/plugin/jumpPointNodeViewPlugin.tsx
|
|
4962
5396
|
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
4963
5397
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
5398
|
+
function IconDiamondAlert({ size = 12, fill = "#D9352C" }) {
|
|
5399
|
+
return /* @__PURE__ */ jsxs3("svg", { width: size, height: size, viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
5400
|
+
/* @__PURE__ */ jsx4(
|
|
5401
|
+
"path",
|
|
5402
|
+
{
|
|
5403
|
+
d: "M5.293 1.293a1 1 0 0 1 1.414 0l4 4a1 1 0 0 1 0 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 0-1.414l4-4Z",
|
|
5404
|
+
fill
|
|
5405
|
+
}
|
|
5406
|
+
),
|
|
5407
|
+
/* @__PURE__ */ jsx4("path", { d: "M6 3.5v3", stroke: "#fff", strokeWidth: "1.2", strokeLinecap: "round" }),
|
|
5408
|
+
/* @__PURE__ */ jsx4("circle", { cx: "6", cy: "8.25", r: "0.625", fill: "#fff" })
|
|
5409
|
+
] });
|
|
5410
|
+
}
|
|
4964
5411
|
var JUMP_POINT_STYLE = {
|
|
4965
5412
|
display: "inline-flex",
|
|
4966
5413
|
alignItems: "center",
|
|
4967
5414
|
backgroundColor: "#FFF2B6",
|
|
4968
5415
|
border: "1px solid #FFC233",
|
|
4969
5416
|
color: "#AA5D04",
|
|
4970
|
-
borderRadius: "2px",
|
|
4971
|
-
padding: "2px",
|
|
4972
|
-
fontSize: "12px",
|
|
4973
|
-
lineHeight: "16px",
|
|
4974
5417
|
userSelect: "none",
|
|
4975
5418
|
cursor: "default",
|
|
4976
|
-
boxSizing: "border-box"
|
|
4977
|
-
height
|
|
4978
|
-
|
|
5419
|
+
boxSizing: "border-box"
|
|
5420
|
+
// height, padding, borderRadius, fontSize, lineHeight, overflow
|
|
5421
|
+
// are set via CSS class .ab-jump-point for heading-level overrides
|
|
4979
5422
|
};
|
|
4980
5423
|
var JUMP_POINT_ADJACENT_STYLE = {
|
|
4981
5424
|
...JUMP_POINT_STYLE,
|
|
@@ -4989,8 +5432,8 @@ var JUMP_POINT_DUPLICATE_STYLE = {
|
|
|
4989
5432
|
color: "#9D091E"
|
|
4990
5433
|
};
|
|
4991
5434
|
var LABEL_STYLE = {
|
|
4992
|
-
padding: "0 4px",
|
|
4993
5435
|
whiteSpace: "nowrap"
|
|
5436
|
+
// padding is set via CSS .ab-jp-label for heading-level overrides
|
|
4994
5437
|
};
|
|
4995
5438
|
var CLOSE_BTN_STYLE = {
|
|
4996
5439
|
display: "inline-flex",
|
|
@@ -5021,7 +5464,7 @@ var TOOLTIP_STYLE = {
|
|
|
5021
5464
|
fontSize: "14px",
|
|
5022
5465
|
lineHeight: "20px",
|
|
5023
5466
|
color: "#0D0D0D",
|
|
5024
|
-
|
|
5467
|
+
maxWidth: "240px",
|
|
5025
5468
|
zIndex: 100,
|
|
5026
5469
|
pointerEvents: "none"
|
|
5027
5470
|
};
|
|
@@ -5041,13 +5484,14 @@ function JumpPointNodeViewComponent({ node, view, getPos, decorations }) {
|
|
|
5041
5484
|
return /* @__PURE__ */ jsxs3(
|
|
5042
5485
|
"span",
|
|
5043
5486
|
{
|
|
5487
|
+
className: "ab-jump-point ab-jump-point-duplicate",
|
|
5044
5488
|
style: { ...JUMP_POINT_DUPLICATE_STYLE, position: "relative" },
|
|
5045
5489
|
id: `jp-${id}`,
|
|
5046
5490
|
onMouseEnter: () => setShowTooltip(true),
|
|
5047
5491
|
onMouseLeave: () => setShowTooltip(false),
|
|
5048
5492
|
children: [
|
|
5049
|
-
/* @__PURE__ */ jsx4(IconAnchor, { size: 12, fill: "#9D091E", style: { paddingLeft: "2px", flexShrink: 0 } }),
|
|
5050
|
-
/* @__PURE__ */ jsx4("span", { style: LABEL_STYLE, children: id }),
|
|
5493
|
+
/* @__PURE__ */ jsx4("span", { className: "ab-jp-icon", children: /* @__PURE__ */ jsx4(IconAnchor, { size: 12, fill: "#9D091E", style: { paddingLeft: "2px", flexShrink: 0 } }) }),
|
|
5494
|
+
/* @__PURE__ */ jsx4("span", { className: "ab-jp-label", style: LABEL_STYLE, children: id }),
|
|
5051
5495
|
/* @__PURE__ */ jsx4(
|
|
5052
5496
|
"button",
|
|
5053
5497
|
{
|
|
@@ -5057,8 +5501,8 @@ function JumpPointNodeViewComponent({ node, view, getPos, decorations }) {
|
|
|
5057
5501
|
e.stopPropagation();
|
|
5058
5502
|
handleDelete2();
|
|
5059
5503
|
},
|
|
5060
|
-
title: "Remove anchor",
|
|
5061
|
-
children: "
|
|
5504
|
+
title: "Remove duplicate anchor",
|
|
5505
|
+
children: /* @__PURE__ */ jsx4(IconDiamondAlert, { size: 12, fill: "#D9352C" })
|
|
5062
5506
|
}
|
|
5063
5507
|
),
|
|
5064
5508
|
showTooltip && /* @__PURE__ */ jsxs3("span", { style: TOOLTIP_STYLE, children: [
|
|
@@ -5071,14 +5515,14 @@ function JumpPointNodeViewComponent({ node, view, getPos, decorations }) {
|
|
|
5071
5515
|
);
|
|
5072
5516
|
}
|
|
5073
5517
|
if (isAdjacent) {
|
|
5074
|
-
return /* @__PURE__ */ jsxs3("span", { style: JUMP_POINT_ADJACENT_STYLE, id: `jp-${id}`, children: [
|
|
5075
|
-
/* @__PURE__ */ jsx4(IconAnchor, { size: 12, fill: "#AA5D04", style: { paddingLeft: "2px", flexShrink: 0 } }),
|
|
5076
|
-
/* @__PURE__ */ jsx4("span", { style: LABEL_STYLE, children: `^${id}^` })
|
|
5518
|
+
return /* @__PURE__ */ jsxs3("span", { className: "ab-jump-point ab-jump-point-adjacent", style: JUMP_POINT_ADJACENT_STYLE, id: `jp-${id}`, children: [
|
|
5519
|
+
/* @__PURE__ */ jsx4("span", { className: "ab-jp-icon", children: /* @__PURE__ */ jsx4(IconAnchor, { size: 12, fill: "#AA5D04", style: { paddingLeft: "2px", flexShrink: 0 } }) }),
|
|
5520
|
+
/* @__PURE__ */ jsx4("span", { className: "ab-jp-label", style: LABEL_STYLE, children: `^${id}^` })
|
|
5077
5521
|
] });
|
|
5078
5522
|
}
|
|
5079
|
-
return /* @__PURE__ */ jsxs3("span", { style: JUMP_POINT_STYLE, id: `jp-${id}`, children: [
|
|
5080
|
-
/* @__PURE__ */ jsx4(IconAnchor, { size: 12, fill: "#AA5D04", style: { paddingLeft: "2px", flexShrink: 0 } }),
|
|
5081
|
-
/* @__PURE__ */ jsx4("span", { style: LABEL_STYLE, children: id })
|
|
5523
|
+
return /* @__PURE__ */ jsxs3("span", { className: "ab-jump-point", style: JUMP_POINT_STYLE, id: `jp-${id}`, children: [
|
|
5524
|
+
/* @__PURE__ */ jsx4("span", { className: "ab-jp-icon", children: /* @__PURE__ */ jsx4(IconAnchor, { size: 12, fill: "#AA5D04", style: { paddingLeft: "2px", flexShrink: 0 } }) }),
|
|
5525
|
+
/* @__PURE__ */ jsx4("span", { className: "ab-jp-label", style: LABEL_STYLE, children: id })
|
|
5082
5526
|
] });
|
|
5083
5527
|
}
|
|
5084
5528
|
function createJumpPointNodeViewPlugin() {
|
|
@@ -5376,6 +5820,7 @@ function createJinjaDecorationPlugin() {
|
|
|
5376
5820
|
// src/ui/plugin/jinjaIfBlockPlugin.tsx
|
|
5377
5821
|
import { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
5378
5822
|
import { createRoot as createRoot2 } from "react-dom/client";
|
|
5823
|
+
import { TextSelection as TextSelection4 } from "prosemirror-state";
|
|
5379
5824
|
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
5380
5825
|
var PLACEHOLDER_TEXT = "Describe what AI agent should do when this condition is met";
|
|
5381
5826
|
var CONDITION_PLACEHOLDER = "Write a condition in natural language";
|
|
@@ -5782,7 +6227,12 @@ function insertNewBranch(view, nodePos, branchType, condition = "") {
|
|
|
5782
6227
|
const newBranch = createBranchNode(view.state.schema, branchType, condition);
|
|
5783
6228
|
if (!newBranch) return false;
|
|
5784
6229
|
const insertPos = context.branchPos + context.branchNode.nodeSize;
|
|
5785
|
-
view.
|
|
6230
|
+
const tr = view.state.tr.insert(insertPos, newBranch);
|
|
6231
|
+
const cursorPos = insertPos + 2;
|
|
6232
|
+
if (cursorPos <= tr.doc.content.size) {
|
|
6233
|
+
tr.setSelection(TextSelection4.near(tr.doc.resolve(cursorPos)));
|
|
6234
|
+
}
|
|
6235
|
+
view.dispatch(tr.scrollIntoView());
|
|
5786
6236
|
view.focus();
|
|
5787
6237
|
return true;
|
|
5788
6238
|
}
|
|
@@ -5829,6 +6279,7 @@ function ConditionDisplay({
|
|
|
5829
6279
|
const [isEditing, setIsEditing] = useState3(false);
|
|
5830
6280
|
const [draftValue, setDraftValue] = useState3(condition);
|
|
5831
6281
|
const inputRef = useRef2(null);
|
|
6282
|
+
const autoFocusedRef = useRef2(false);
|
|
5832
6283
|
useEffect2(() => {
|
|
5833
6284
|
if (!isEditing) {
|
|
5834
6285
|
setDraftValue(condition);
|
|
@@ -5840,6 +6291,12 @@ function ConditionDisplay({
|
|
|
5840
6291
|
inputRef.current?.select();
|
|
5841
6292
|
}
|
|
5842
6293
|
}, [isEditing]);
|
|
6294
|
+
useEffect2(() => {
|
|
6295
|
+
if (editable && !condition && !autoFocusedRef.current) {
|
|
6296
|
+
autoFocusedRef.current = true;
|
|
6297
|
+
setIsEditing(true);
|
|
6298
|
+
}
|
|
6299
|
+
}, []);
|
|
5843
6300
|
if (branchType === "else") {
|
|
5844
6301
|
return /* @__PURE__ */ jsx6("span", { className: "jinja-otherwise", children: "Otherwise" });
|
|
5845
6302
|
}
|
|
@@ -6135,15 +6592,25 @@ var JinjaIfBranchView = class {
|
|
|
6135
6592
|
}
|
|
6136
6593
|
render() {
|
|
6137
6594
|
const nodePos = this.getPos();
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6595
|
+
let context = null;
|
|
6596
|
+
let lastBranch = null;
|
|
6597
|
+
if (nodePos != null) {
|
|
6598
|
+
context = getBranchContext(this.view, nodePos);
|
|
6599
|
+
lastBranch = getLastBranchOfBlock(this.view, nodePos);
|
|
6600
|
+
let branchIdx = context?.branchIndex ?? -1;
|
|
6601
|
+
if (branchIdx < 0 && this.dom.parentElement) {
|
|
6602
|
+
const siblings = this.dom.parentElement.querySelectorAll(":scope > [data-jinja-branch]");
|
|
6603
|
+
branchIdx = Array.prototype.indexOf.call(siblings, this.dom);
|
|
6604
|
+
}
|
|
6605
|
+
if (branchIdx >= 0) {
|
|
6606
|
+
this.dom.setAttribute("data-branch-index", String(branchIdx));
|
|
6607
|
+
}
|
|
6608
|
+
}
|
|
6142
6609
|
const branchType = this.node.attrs.branchType;
|
|
6143
6610
|
const condition = String(this.node.attrs.condition ?? "");
|
|
6144
|
-
const isLast = lastBranch.index === context.branchIndex;
|
|
6145
|
-
const lastNonElseIdx = getLastNonElseBranchIndex(this.view, nodePos);
|
|
6146
|
-
const showAddButton = context.branchIndex === lastNonElseIdx;
|
|
6611
|
+
const isLast = context && lastBranch ? lastBranch.index === context.branchIndex : true;
|
|
6612
|
+
const lastNonElseIdx = context ? getLastNonElseBranchIndex(this.view, nodePos) : 0;
|
|
6613
|
+
const showAddButton = context ? context.branchIndex === lastNonElseIdx : true;
|
|
6147
6614
|
this.dom.classList.toggle("jinja-branch-last", isLast);
|
|
6148
6615
|
this.root.render(
|
|
6149
6616
|
/* @__PURE__ */ jsx6(
|
|
@@ -6153,16 +6620,19 @@ var JinjaIfBranchView = class {
|
|
|
6153
6620
|
condition,
|
|
6154
6621
|
editable: this.view.editable,
|
|
6155
6622
|
isLastBranch: showAddButton,
|
|
6156
|
-
hasElseBranch: getElseBranch(this.view, nodePos),
|
|
6623
|
+
hasElseBranch: context && nodePos != null ? getElseBranch(this.view, nodePos) : false,
|
|
6157
6624
|
onConditionChange: (value) => {
|
|
6158
6625
|
const currentPos = this.getPos();
|
|
6159
6626
|
if (currentPos == null) return;
|
|
6160
|
-
this.view.
|
|
6161
|
-
this.
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
)
|
|
6627
|
+
const tr = this.view.state.tr.setNodeMarkup(currentPos, void 0, {
|
|
6628
|
+
...this.node.attrs,
|
|
6629
|
+
condition: value
|
|
6630
|
+
});
|
|
6631
|
+
const contentPos = currentPos + 2;
|
|
6632
|
+
if (contentPos <= tr.doc.content.size) {
|
|
6633
|
+
tr.setSelection(TextSelection4.near(tr.doc.resolve(contentPos)));
|
|
6634
|
+
}
|
|
6635
|
+
this.view.dispatch(tr.scrollIntoView());
|
|
6166
6636
|
this.view.focus();
|
|
6167
6637
|
},
|
|
6168
6638
|
onDelete: () => {
|
|
@@ -6213,9 +6683,9 @@ function createJinjaIfBlockPlugin() {
|
|
|
6213
6683
|
|
|
6214
6684
|
// src/ui/plugin/linkPlugin.ts
|
|
6215
6685
|
import { InputRule as InputRule2, inputRules as inputRules2 } from "prosemirror-inputrules";
|
|
6216
|
-
import { Plugin as Plugin5, PluginKey as PluginKey5, TextSelection as
|
|
6217
|
-
var LINK_INPUT_RE =
|
|
6218
|
-
var LINK_FULL_RE =
|
|
6686
|
+
import { Plugin as Plugin5, PluginKey as PluginKey5, TextSelection as TextSelection5 } from "prosemirror-state";
|
|
6687
|
+
var LINK_INPUT_RE = /\\?\[([^\]]*?)\\?\]\(([^)\s]*?)(?:\s+"([^"]*)")?\)$/;
|
|
6688
|
+
var LINK_FULL_RE = /^\\?\[([^\]]*?)\\?\]\(([^)\s]*?)(?:\s+"([^"]*)")?\)$/;
|
|
6219
6689
|
var linkEditKey = new PluginKey5("linkEdit");
|
|
6220
6690
|
function getMarkRange($pos, type) {
|
|
6221
6691
|
const { parentOffset } = $pos;
|
|
@@ -6259,7 +6729,7 @@ function explodeLinkToRaw(state) {
|
|
|
6259
6729
|
const tr = state.tr;
|
|
6260
6730
|
tr.replaceWith(from, to, schema.text(rawText));
|
|
6261
6731
|
const newPos = Math.min(from + rawCursorOffset, tr.doc.content.size);
|
|
6262
|
-
tr.setSelection(
|
|
6732
|
+
tr.setSelection(TextSelection5.near(tr.doc.resolve(newPos)));
|
|
6263
6733
|
tr.setMeta(linkEditKey, { from, to: from + rawText.length });
|
|
6264
6734
|
return tr;
|
|
6265
6735
|
}
|
|
@@ -6272,8 +6742,8 @@ function createLinkEditPlugin() {
|
|
|
6272
6742
|
const meta = tr.getMeta(linkEditKey);
|
|
6273
6743
|
if (meta !== void 0) return { rawRange: meta };
|
|
6274
6744
|
if (prev.rawRange) {
|
|
6275
|
-
const from = tr.mapping.map(prev.rawRange.from);
|
|
6276
|
-
const to = tr.mapping.map(prev.rawRange.to);
|
|
6745
|
+
const from = tr.mapping.map(prev.rawRange.from, -1);
|
|
6746
|
+
const to = tr.mapping.map(prev.rawRange.to, -1);
|
|
6277
6747
|
if (from >= to) return { rawRange: null };
|
|
6278
6748
|
return { rawRange: { from, to } };
|
|
6279
6749
|
}
|
|
@@ -6284,7 +6754,7 @@ function createLinkEditPlugin() {
|
|
|
6284
6754
|
const pluginState = linkEditKey.getState(newState);
|
|
6285
6755
|
if (!pluginState?.rawRange) return null;
|
|
6286
6756
|
const { from, to } = pluginState.rawRange;
|
|
6287
|
-
if (newState.selection.anchor >= from && newState.selection.anchor
|
|
6757
|
+
if (newState.selection.anchor >= from && newState.selection.anchor < to) {
|
|
6288
6758
|
return null;
|
|
6289
6759
|
}
|
|
6290
6760
|
const docSize = newState.doc.content.size;
|
|
@@ -6428,12 +6898,13 @@ var STYLES = (
|
|
|
6428
6898
|
cursor: grab;
|
|
6429
6899
|
color: transparent;
|
|
6430
6900
|
border-radius: var(--ab-dh-handle-radius);
|
|
6431
|
-
transition:
|
|
6901
|
+
transition: none;
|
|
6432
6902
|
user-select: none;
|
|
6433
6903
|
pointer-events: auto;
|
|
6434
6904
|
touch-action: none;
|
|
6435
6905
|
opacity: 0;
|
|
6436
6906
|
pointer-events: none;
|
|
6907
|
+
overflow: visible;
|
|
6437
6908
|
}
|
|
6438
6909
|
.ab-drag-handle.ab-dh-active {
|
|
6439
6910
|
opacity: 1;
|
|
@@ -6444,6 +6915,72 @@ var STYLES = (
|
|
|
6444
6915
|
color: var(--ab-dh-color-visible) !important;
|
|
6445
6916
|
background: var(--ab-dh-bg-hover);
|
|
6446
6917
|
}
|
|
6918
|
+
.ab-drag-handle:hover::after {
|
|
6919
|
+
content: 'Drag to move';
|
|
6920
|
+
position: absolute;
|
|
6921
|
+
bottom: calc(100% + 4px);
|
|
6922
|
+
left: 0;
|
|
6923
|
+
background: #2e2e2e;
|
|
6924
|
+
color: #fff;
|
|
6925
|
+
font-family: 'SF Pro Text', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
6926
|
+
font-size: 12px;
|
|
6927
|
+
font-weight: 400;
|
|
6928
|
+
line-height: 16px;
|
|
6929
|
+
padding: 4px 8px;
|
|
6930
|
+
border-radius: 4px;
|
|
6931
|
+
white-space: nowrap;
|
|
6932
|
+
pointer-events: none;
|
|
6933
|
+
z-index: 30;
|
|
6934
|
+
}
|
|
6935
|
+
.ab-drag-handle:hover::before {
|
|
6936
|
+
content: '';
|
|
6937
|
+
position: absolute;
|
|
6938
|
+
bottom: calc(100% + 0px);
|
|
6939
|
+
left: 8px;
|
|
6940
|
+
width: 0;
|
|
6941
|
+
height: 0;
|
|
6942
|
+
border-left: 5px solid transparent;
|
|
6943
|
+
border-right: 5px solid transparent;
|
|
6944
|
+
border-top: 5px solid #2e2e2e;
|
|
6945
|
+
pointer-events: none;
|
|
6946
|
+
z-index: 31;
|
|
6947
|
+
}
|
|
6948
|
+
.ab-drag-handle.dragging::after,
|
|
6949
|
+
.ab-drag-handle.dragging::before {
|
|
6950
|
+
display: none;
|
|
6951
|
+
}
|
|
6952
|
+
.ab-dh-menu {
|
|
6953
|
+
position: absolute;
|
|
6954
|
+
top: calc(100% + 4px);
|
|
6955
|
+
left: 0;
|
|
6956
|
+
background: #fff;
|
|
6957
|
+
border-radius: 4px;
|
|
6958
|
+
box-shadow: 0px 8px 10px 1px rgba(13,13,13,0.12), 0px 3px 14px 2px rgba(13,13,13,0.08), 0px 3px 5px -3px rgba(13,13,13,0.04);
|
|
6959
|
+
padding: 8px 0;
|
|
6960
|
+
z-index: 40;
|
|
6961
|
+
min-width: 120px;
|
|
6962
|
+
}
|
|
6963
|
+
.ab-dh-menu-item {
|
|
6964
|
+
display: flex;
|
|
6965
|
+
align-items: center;
|
|
6966
|
+
width: 100%;
|
|
6967
|
+
padding: 6px 16px;
|
|
6968
|
+
border: none;
|
|
6969
|
+
background: #fff;
|
|
6970
|
+
cursor: pointer;
|
|
6971
|
+
font-family: 'SF Pro Text', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
6972
|
+
font-size: 14px;
|
|
6973
|
+
font-weight: 400;
|
|
6974
|
+
line-height: 20px;
|
|
6975
|
+
letter-spacing: -0.1px;
|
|
6976
|
+
color: #0d0d0d;
|
|
6977
|
+
white-space: nowrap;
|
|
6978
|
+
text-overflow: ellipsis;
|
|
6979
|
+
overflow: hidden;
|
|
6980
|
+
}
|
|
6981
|
+
.ab-dh-menu-item:hover {
|
|
6982
|
+
background: rgba(0,0,0,0.04);
|
|
6983
|
+
}
|
|
6447
6984
|
.ab-drag-handle:active, .ab-drag-handle.dragging {
|
|
6448
6985
|
cursor: grabbing;
|
|
6449
6986
|
color: var(--ab-dh-color-accent) !important;
|
|
@@ -6753,7 +7290,6 @@ var DragHandleController = class {
|
|
|
6753
7290
|
el.setAttribute("role", "button");
|
|
6754
7291
|
el.setAttribute("aria-roledescription", "drag handle");
|
|
6755
7292
|
el.setAttribute("aria-label", `\uBE14\uB85D ${idx + 1} \uC774\uB3D9`);
|
|
6756
|
-
el.setAttribute("title", "Drag to move");
|
|
6757
7293
|
el.setAttribute("tabindex", "-1");
|
|
6758
7294
|
el.innerHTML = GRIP_SVG;
|
|
6759
7295
|
el.style.left = `${handleLeft}px`;
|
|
@@ -6766,18 +7302,85 @@ var DragHandleController = class {
|
|
|
6766
7302
|
this.prevBlocks = newBlocks;
|
|
6767
7303
|
this.updateActiveHandle();
|
|
6768
7304
|
}
|
|
7305
|
+
activeMenu = null;
|
|
7306
|
+
activeMenuCleanup = null;
|
|
7307
|
+
dismissMenu() {
|
|
7308
|
+
if (this.activeMenu) {
|
|
7309
|
+
this.activeMenu.remove();
|
|
7310
|
+
this.activeMenu = null;
|
|
7311
|
+
}
|
|
7312
|
+
if (this.activeMenuCleanup) {
|
|
7313
|
+
this.activeMenuCleanup();
|
|
7314
|
+
this.activeMenuCleanup = null;
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7317
|
+
showMenu(el, blockIndex) {
|
|
7318
|
+
this.dismissMenu();
|
|
7319
|
+
const menu = document.createElement("div");
|
|
7320
|
+
menu.className = "ab-dh-menu";
|
|
7321
|
+
const deleteBtn = document.createElement("button");
|
|
7322
|
+
deleteBtn.className = "ab-dh-menu-item";
|
|
7323
|
+
deleteBtn.textContent = "Delete";
|
|
7324
|
+
deleteBtn.addEventListener("mousedown", (e) => {
|
|
7325
|
+
e.preventDefault();
|
|
7326
|
+
e.stopPropagation();
|
|
7327
|
+
this.deleteBlock(blockIndex);
|
|
7328
|
+
this.dismissMenu();
|
|
7329
|
+
});
|
|
7330
|
+
menu.appendChild(deleteBtn);
|
|
7331
|
+
el.appendChild(menu);
|
|
7332
|
+
this.activeMenu = menu;
|
|
7333
|
+
const onOutside = (e) => {
|
|
7334
|
+
if (!menu.contains(e.target)) {
|
|
7335
|
+
this.dismissMenu();
|
|
7336
|
+
}
|
|
7337
|
+
};
|
|
7338
|
+
setTimeout(() => document.addEventListener("mousedown", onOutside, true), 0);
|
|
7339
|
+
this.activeMenuCleanup = () => document.removeEventListener("mousedown", onOutside, true);
|
|
7340
|
+
}
|
|
7341
|
+
deleteBlock(blockIndex) {
|
|
7342
|
+
const blocks = this.collectBlocks();
|
|
7343
|
+
if (blockIndex >= blocks.length) return;
|
|
7344
|
+
const block = blocks[blockIndex];
|
|
7345
|
+
const from = block.offset;
|
|
7346
|
+
const to = block.offset + block.nodeSize;
|
|
7347
|
+
const tr = this.view.state.tr;
|
|
7348
|
+
tr.replaceWith(from, to, this.view.state.schema.nodes.paragraph.create());
|
|
7349
|
+
this.view.dispatch(tr);
|
|
7350
|
+
this.view.focus();
|
|
7351
|
+
}
|
|
6769
7352
|
bindHandleEvents(el, index) {
|
|
6770
7353
|
let currentIndex = index;
|
|
7354
|
+
let downX = 0;
|
|
7355
|
+
let downY = 0;
|
|
7356
|
+
let didDrag = false;
|
|
7357
|
+
let menuWasOpen = false;
|
|
6771
7358
|
const onPointerDown = (e) => {
|
|
7359
|
+
if (this.activeMenu && this.activeMenu.contains(e.target)) return;
|
|
6772
7360
|
e.preventDefault();
|
|
6773
7361
|
if (!this.view.editable) return;
|
|
7362
|
+
menuWasOpen = !!this.activeMenu;
|
|
7363
|
+
this.dismissMenu();
|
|
7364
|
+
downX = e.clientX;
|
|
7365
|
+
downY = e.clientY;
|
|
7366
|
+
didDrag = false;
|
|
6774
7367
|
el.setPointerCapture(e.pointerId);
|
|
6775
7368
|
this.prevBlocks = this.collectBlocks();
|
|
6776
7369
|
this.syncHandlePositions();
|
|
6777
7370
|
this.startDrag(currentIndex, el, e);
|
|
6778
7371
|
};
|
|
6779
|
-
const onPointerMove = (e) =>
|
|
6780
|
-
|
|
7372
|
+
const onPointerMove = (e) => {
|
|
7373
|
+
if (Math.abs(e.clientX - downX) > 3 || Math.abs(e.clientY - downY) > 3) {
|
|
7374
|
+
didDrag = true;
|
|
7375
|
+
}
|
|
7376
|
+
this.onPointerMove(e);
|
|
7377
|
+
};
|
|
7378
|
+
const onPointerUp = (e) => {
|
|
7379
|
+
this.onPointerUp(e);
|
|
7380
|
+
if (!didDrag && !menuWasOpen) {
|
|
7381
|
+
this.showMenu(el, currentIndex);
|
|
7382
|
+
}
|
|
7383
|
+
};
|
|
6781
7384
|
const onLostCapture = () => this.cancelDrag();
|
|
6782
7385
|
const onPointerCancel = () => this.cancelDrag();
|
|
6783
7386
|
el.addEventListener("pointerdown", onPointerDown);
|
|
@@ -7184,81 +7787,89 @@ function createTodoNodeViewPlugin() {
|
|
|
7184
7787
|
};
|
|
7185
7788
|
}
|
|
7186
7789
|
|
|
7187
|
-
// src/ui/plugin/
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
background: rgba(0, 0, 0, 0.015);
|
|
7200
|
-
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
|
7201
|
-
color: #999;
|
|
7202
|
-
font-style: italic;
|
|
7203
|
-
`;
|
|
7204
|
-
const label = document.createElement("span");
|
|
7205
|
-
label.contentEditable = "false";
|
|
7206
|
-
label.textContent = "Note";
|
|
7207
|
-
label.style.cssText = `
|
|
7208
|
-
display: inline-block;
|
|
7209
|
-
font-size: 11px;
|
|
7210
|
-
font-weight: 600;
|
|
7211
|
-
font-style: normal;
|
|
7212
|
-
color: #aaa;
|
|
7213
|
-
background: rgba(0, 0, 0, 0.03);
|
|
7214
|
-
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
7215
|
-
border-radius: 3px;
|
|
7216
|
-
padding: 1px 6px;
|
|
7217
|
-
margin-bottom: 6px;
|
|
7218
|
-
user-select: none;
|
|
7219
|
-
line-height: 1.4;
|
|
7220
|
-
`;
|
|
7221
|
-
this.dom.appendChild(label);
|
|
7222
|
-
this.contentDOM = document.createElement("div");
|
|
7223
|
-
this.contentDOM.className = "ab-note-block-content";
|
|
7224
|
-
this.dom.appendChild(this.contentDOM);
|
|
7790
|
+
// src/ui/plugin/placeholderPlugin.ts
|
|
7791
|
+
import { Plugin as Plugin7, PluginKey as PluginKey7 } from "prosemirror-state";
|
|
7792
|
+
import { Decoration as Decoration4, DecorationSet as DecorationSet4 } from "prosemirror-view";
|
|
7793
|
+
var PLACEHOLDER_TEXT2 = "Type to start writing, or press / to insert an action.";
|
|
7794
|
+
var pluginKey2 = new PluginKey7("placeholder");
|
|
7795
|
+
function buildDecorations4(state) {
|
|
7796
|
+
const { doc: doc2, selection } = state;
|
|
7797
|
+
if (!selection.empty) return DecorationSet4.empty;
|
|
7798
|
+
const $from = selection.$from;
|
|
7799
|
+
const parent = $from.parent;
|
|
7800
|
+
if (parent.type.name !== "paragraph" || parent.content.size !== 0) {
|
|
7801
|
+
return DecorationSet4.empty;
|
|
7225
7802
|
}
|
|
7226
|
-
|
|
7227
|
-
|
|
7803
|
+
const EXCLUDED_ANCESTORS = /* @__PURE__ */ new Set(["noteBlock", "blockquote", "jinjaIfBranch"]);
|
|
7804
|
+
for (let d = $from.depth - 1; d > 0; d--) {
|
|
7805
|
+
if (EXCLUDED_ANCESTORS.has($from.node(d).type.name)) {
|
|
7806
|
+
return DecorationSet4.empty;
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7809
|
+
const pos = $from.before($from.depth);
|
|
7810
|
+
const deco = Decoration4.node(pos, pos + parent.nodeSize, {
|
|
7811
|
+
class: "ab-empty-paragraph",
|
|
7812
|
+
"data-placeholder": PLACEHOLDER_TEXT2
|
|
7813
|
+
});
|
|
7814
|
+
return DecorationSet4.create(doc2, [deco]);
|
|
7815
|
+
}
|
|
7816
|
+
function createPlaceholderPlugin() {
|
|
7228
7817
|
return {
|
|
7229
|
-
name: "
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7818
|
+
name: "placeholder",
|
|
7819
|
+
plugins: () => [
|
|
7820
|
+
new Plugin7({
|
|
7821
|
+
key: pluginKey2,
|
|
7822
|
+
state: {
|
|
7823
|
+
init(_, state) {
|
|
7824
|
+
return buildDecorations4(state);
|
|
7825
|
+
},
|
|
7826
|
+
apply(tr, oldSet, _oldState, newState) {
|
|
7827
|
+
if (tr.docChanged || tr.selectionSet) {
|
|
7828
|
+
return buildDecorations4(newState);
|
|
7829
|
+
}
|
|
7830
|
+
return oldSet;
|
|
7831
|
+
}
|
|
7832
|
+
},
|
|
7833
|
+
props: {
|
|
7834
|
+
decorations(state) {
|
|
7835
|
+
return pluginKey2.getState(state) ?? DecorationSet4.empty;
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
})
|
|
7839
|
+
]
|
|
7233
7840
|
};
|
|
7234
7841
|
}
|
|
7235
7842
|
|
|
7236
7843
|
// src/ui/plugin/slashCommandPlugin.ts
|
|
7237
|
-
import { Plugin as
|
|
7238
|
-
var slashCommandKey = new
|
|
7844
|
+
import { Plugin as Plugin8, PluginKey as PluginKey8 } from "prosemirror-state";
|
|
7845
|
+
var slashCommandKey = new PluginKey8("slashCommand");
|
|
7239
7846
|
var TRIGGER_RE = /(?:^|\s)(\/[^\s]*)$/;
|
|
7240
7847
|
function deriveState(state, dismissedFrom) {
|
|
7848
|
+
const inactive = { active: false, range: null, query: "", parentType: "" };
|
|
7241
7849
|
const { selection } = state;
|
|
7242
|
-
if (!selection.empty) return
|
|
7850
|
+
if (!selection.empty) return inactive;
|
|
7243
7851
|
const { $from } = selection;
|
|
7244
7852
|
const parentType = $from.parent.type.name;
|
|
7245
|
-
|
|
7853
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
7854
|
+
if ($from.node(d).type.name === "noteBlock") return inactive;
|
|
7855
|
+
}
|
|
7246
7856
|
const textBefore = $from.parent.textBetween(0, $from.parentOffset, "\n", "\0");
|
|
7247
7857
|
const match = TRIGGER_RE.exec(textBefore);
|
|
7248
|
-
if (!match) return
|
|
7858
|
+
if (!match) return inactive;
|
|
7249
7859
|
const slashText = match[1];
|
|
7250
7860
|
const slashStartInParent = textBefore.lastIndexOf(slashText);
|
|
7251
7861
|
const from = $from.start() + slashStartInParent;
|
|
7252
7862
|
const to = $from.pos;
|
|
7253
|
-
if (dismissedFrom === from) return
|
|
7863
|
+
if (dismissedFrom === from) return inactive;
|
|
7254
7864
|
return {
|
|
7255
7865
|
active: true,
|
|
7256
7866
|
range: { from, to },
|
|
7257
|
-
query: slashText.slice(1)
|
|
7867
|
+
query: slashText.slice(1),
|
|
7868
|
+
parentType
|
|
7258
7869
|
};
|
|
7259
7870
|
}
|
|
7260
7871
|
function createSlashCommandPlugin() {
|
|
7261
|
-
const plugin = new
|
|
7872
|
+
const plugin = new Plugin8({
|
|
7262
7873
|
key: slashCommandKey,
|
|
7263
7874
|
state: {
|
|
7264
7875
|
init(_, state) {
|
|
@@ -7776,6 +8387,7 @@ function DocumentTreeView({ doc: doc2, className, onNavigate }) {
|
|
|
7776
8387
|
// src/ui/components/FloatingMenu.tsx
|
|
7777
8388
|
import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef4, useState as useState7 } from "react";
|
|
7778
8389
|
import { createPortal as createPortal2 } from "react-dom";
|
|
8390
|
+
import { TextSelection as TextSelection6 } from "prosemirror-state";
|
|
7779
8391
|
import { setBlockType as setBlockType2, toggleMark as toggleMark2, wrapIn } from "prosemirror-commands";
|
|
7780
8392
|
import { wrapInList as wrapInList2 } from "prosemirror-schema-list";
|
|
7781
8393
|
import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
@@ -7798,6 +8410,56 @@ function hasMark(state, type) {
|
|
|
7798
8410
|
if (empty) return false;
|
|
7799
8411
|
return state.doc.rangeHasMark(from, to, type);
|
|
7800
8412
|
}
|
|
8413
|
+
function getMarkRange2($pos, type) {
|
|
8414
|
+
const { parentOffset } = $pos;
|
|
8415
|
+
let child = $pos.parent.childAfter(parentOffset);
|
|
8416
|
+
if (parentOffset === child.offset && child.offset !== 0) {
|
|
8417
|
+
child = $pos.parent.childBefore(parentOffset);
|
|
8418
|
+
}
|
|
8419
|
+
if (!child.node) return null;
|
|
8420
|
+
const mark = child.node.marks.find((candidate) => candidate.type === type);
|
|
8421
|
+
if (!mark) return null;
|
|
8422
|
+
const parentStart = $pos.start();
|
|
8423
|
+
let { index: startIndex } = child;
|
|
8424
|
+
let startPos = parentStart + child.offset;
|
|
8425
|
+
let endIndex = startIndex + 1;
|
|
8426
|
+
let endPos = startPos + child.node.nodeSize;
|
|
8427
|
+
while (startIndex > 0 && mark.isInSet($pos.parent.child(startIndex - 1).marks)) {
|
|
8428
|
+
startIndex -= 1;
|
|
8429
|
+
startPos -= $pos.parent.child(startIndex).nodeSize;
|
|
8430
|
+
}
|
|
8431
|
+
while (endIndex < $pos.parent.childCount && mark.isInSet($pos.parent.child(endIndex).marks)) {
|
|
8432
|
+
endPos += $pos.parent.child(endIndex).nodeSize;
|
|
8433
|
+
endIndex += 1;
|
|
8434
|
+
}
|
|
8435
|
+
return { from: startPos, to: endPos, mark };
|
|
8436
|
+
}
|
|
8437
|
+
function getLinkAtCursor(state) {
|
|
8438
|
+
const { selection, doc: doc2 } = state;
|
|
8439
|
+
if (!selection.empty) return null;
|
|
8440
|
+
const range = getMarkRange2(doc2.resolve(selection.from), linkMark);
|
|
8441
|
+
if (!range) return null;
|
|
8442
|
+
return {
|
|
8443
|
+
href: range.mark.attrs.href || "",
|
|
8444
|
+
text: doc2.textBetween(range.from, range.to),
|
|
8445
|
+
from: range.from,
|
|
8446
|
+
to: range.to
|
|
8447
|
+
};
|
|
8448
|
+
}
|
|
8449
|
+
function normalizeLinkHref(href) {
|
|
8450
|
+
const trimmedHref = href.trim();
|
|
8451
|
+
if (!trimmedHref) return "";
|
|
8452
|
+
return trimmedHref.startsWith("#") || trimmedHref.startsWith("/") || /^https?:\/\//i.test(trimmedHref) ? trimmedHref : `https://${trimmedHref}`;
|
|
8453
|
+
}
|
|
8454
|
+
function getNonLinkMarksInRange(state, from, to) {
|
|
8455
|
+
let marks = [];
|
|
8456
|
+
state.doc.nodesBetween(from, to, (node) => {
|
|
8457
|
+
if (!node.isText) return;
|
|
8458
|
+
marks = node.marks.filter((mark) => mark.type !== linkMark);
|
|
8459
|
+
return false;
|
|
8460
|
+
});
|
|
8461
|
+
return marks;
|
|
8462
|
+
}
|
|
7801
8463
|
function activeHeadingLevel(state) {
|
|
7802
8464
|
const { $from } = state.selection;
|
|
7803
8465
|
const node = $from.parent;
|
|
@@ -8260,6 +8922,408 @@ function SelectionToolbar({ view, state, selectionRect }) {
|
|
|
8260
8922
|
}
|
|
8261
8923
|
);
|
|
8262
8924
|
}
|
|
8925
|
+
function LinkHoverTooltip({ view, link: link2, onEdit }) {
|
|
8926
|
+
const [hover, setHover] = useState7(false);
|
|
8927
|
+
const startCoords = view.coordsAtPos(link2.from);
|
|
8928
|
+
const endCoords = view.coordsAtPos(link2.to);
|
|
8929
|
+
const anchorTop = Math.min(startCoords.top, endCoords.top);
|
|
8930
|
+
const linkLeft = Math.min(startCoords.left, endCoords.left);
|
|
8931
|
+
const linkRight = Math.max(startCoords.right, endCoords.right);
|
|
8932
|
+
const tooltipHalfW = Math.min(174, Math.max(0, (window.innerWidth - VPORT_MARGIN2 * 2) / 2));
|
|
8933
|
+
const safeCenterX = Math.max(
|
|
8934
|
+
VPORT_MARGIN2 + tooltipHalfW,
|
|
8935
|
+
Math.min((linkLeft + linkRight) / 2, window.innerWidth - VPORT_MARGIN2 - tooltipHalfW)
|
|
8936
|
+
);
|
|
8937
|
+
return createPortal2(
|
|
8938
|
+
/* @__PURE__ */ jsxs9(
|
|
8939
|
+
"div",
|
|
8940
|
+
{
|
|
8941
|
+
...{ [FLOAT_ATTR]: "" },
|
|
8942
|
+
style: {
|
|
8943
|
+
position: "fixed",
|
|
8944
|
+
left: safeCenterX,
|
|
8945
|
+
top: Math.max(64, anchorTop - 4),
|
|
8946
|
+
transform: "translate(-50%, -100%)",
|
|
8947
|
+
zIndex: 1e3,
|
|
8948
|
+
display: "flex",
|
|
8949
|
+
flexDirection: "column",
|
|
8950
|
+
alignItems: "center",
|
|
8951
|
+
animation: "ab-float-in 0.12s ease",
|
|
8952
|
+
pointerEvents: "auto"
|
|
8953
|
+
},
|
|
8954
|
+
children: [
|
|
8955
|
+
/* @__PURE__ */ jsxs9(
|
|
8956
|
+
"div",
|
|
8957
|
+
{
|
|
8958
|
+
...{ [FLOAT_ATTR]: "" },
|
|
8959
|
+
style: {
|
|
8960
|
+
display: "flex",
|
|
8961
|
+
alignItems: "center",
|
|
8962
|
+
gap: 4,
|
|
8963
|
+
minHeight: 28,
|
|
8964
|
+
maxWidth: "min(348px, calc(100vw - 16px))",
|
|
8965
|
+
padding: "6px 8px",
|
|
8966
|
+
background: "#2E2E2E",
|
|
8967
|
+
borderRadius: 4,
|
|
8968
|
+
boxSizing: "border-box"
|
|
8969
|
+
},
|
|
8970
|
+
children: [
|
|
8971
|
+
/* @__PURE__ */ jsx10(
|
|
8972
|
+
"span",
|
|
8973
|
+
{
|
|
8974
|
+
...{ [FLOAT_ATTR]: "" },
|
|
8975
|
+
style: {
|
|
8976
|
+
color: "#FFFFFF",
|
|
8977
|
+
fontSize: 12,
|
|
8978
|
+
lineHeight: "16px",
|
|
8979
|
+
fontFamily: '"SF Pro Text", "SF Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
8980
|
+
whiteSpace: "normal",
|
|
8981
|
+
wordBreak: "break-word",
|
|
8982
|
+
overflowWrap: "anywhere",
|
|
8983
|
+
maxWidth: 300
|
|
8984
|
+
},
|
|
8985
|
+
children: link2.href
|
|
8986
|
+
}
|
|
8987
|
+
),
|
|
8988
|
+
/* @__PURE__ */ jsx10(
|
|
8989
|
+
"div",
|
|
8990
|
+
{
|
|
8991
|
+
...{ [FLOAT_ATTR]: "" },
|
|
8992
|
+
style: {
|
|
8993
|
+
width: 1,
|
|
8994
|
+
height: 16,
|
|
8995
|
+
background: "#424242",
|
|
8996
|
+
flexShrink: 0
|
|
8997
|
+
}
|
|
8998
|
+
}
|
|
8999
|
+
),
|
|
9000
|
+
/* @__PURE__ */ jsx10(
|
|
9001
|
+
"button",
|
|
9002
|
+
{
|
|
9003
|
+
type: "button",
|
|
9004
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9005
|
+
onMouseDown: (e) => {
|
|
9006
|
+
e.preventDefault();
|
|
9007
|
+
onEdit(link2);
|
|
9008
|
+
},
|
|
9009
|
+
onMouseEnter: () => setHover(true),
|
|
9010
|
+
onMouseLeave: () => setHover(false),
|
|
9011
|
+
style: {
|
|
9012
|
+
...BTN_RESET2,
|
|
9013
|
+
padding: "0 2px",
|
|
9014
|
+
color: "#FFFFFF",
|
|
9015
|
+
fontSize: 12,
|
|
9016
|
+
lineHeight: "16px",
|
|
9017
|
+
fontWeight: 600,
|
|
9018
|
+
fontFamily: '"SF Pro Text", "SF Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
9019
|
+
opacity: hover ? 0.78 : 1,
|
|
9020
|
+
flexShrink: 0
|
|
9021
|
+
},
|
|
9022
|
+
children: "Edit"
|
|
9023
|
+
}
|
|
9024
|
+
)
|
|
9025
|
+
]
|
|
9026
|
+
}
|
|
9027
|
+
),
|
|
9028
|
+
/* @__PURE__ */ jsx10(
|
|
9029
|
+
"div",
|
|
9030
|
+
{
|
|
9031
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9032
|
+
style: {
|
|
9033
|
+
width: 0,
|
|
9034
|
+
height: 0,
|
|
9035
|
+
borderLeft: "6px solid transparent",
|
|
9036
|
+
borderRight: "6px solid transparent",
|
|
9037
|
+
borderTop: "6px solid #2E2E2E",
|
|
9038
|
+
marginTop: -1
|
|
9039
|
+
}
|
|
9040
|
+
}
|
|
9041
|
+
)
|
|
9042
|
+
]
|
|
9043
|
+
}
|
|
9044
|
+
),
|
|
9045
|
+
document.body
|
|
9046
|
+
);
|
|
9047
|
+
}
|
|
9048
|
+
function LinkEditDialog({ view, link: link2, onClose }) {
|
|
9049
|
+
const [textValue, setTextValue] = useState7(link2.text);
|
|
9050
|
+
const [hrefValue, setHrefValue] = useState7(link2.href);
|
|
9051
|
+
const textInputRef = useRef4(null);
|
|
9052
|
+
useEffect4(() => {
|
|
9053
|
+
setTextValue(link2.text);
|
|
9054
|
+
setHrefValue(link2.href);
|
|
9055
|
+
}, [link2.from, link2.href, link2.text, link2.to]);
|
|
9056
|
+
useEffect4(() => {
|
|
9057
|
+
setTimeout(() => textInputRef.current?.focus(), 0);
|
|
9058
|
+
}, [link2.from, link2.to]);
|
|
9059
|
+
useEffect4(() => {
|
|
9060
|
+
const onDown = (e) => {
|
|
9061
|
+
if (!e.target.closest(`[${FLOAT_ATTR}]`)) {
|
|
9062
|
+
onClose();
|
|
9063
|
+
}
|
|
9064
|
+
};
|
|
9065
|
+
document.addEventListener("mousedown", onDown, true);
|
|
9066
|
+
return () => document.removeEventListener("mousedown", onDown, true);
|
|
9067
|
+
}, [onClose]);
|
|
9068
|
+
const startCoords = view.coordsAtPos(link2.from);
|
|
9069
|
+
const endCoords = view.coordsAtPos(link2.to);
|
|
9070
|
+
const anchorTop = Math.min(startCoords.top, endCoords.top);
|
|
9071
|
+
const anchorBottom = Math.max(startCoords.bottom, endCoords.bottom);
|
|
9072
|
+
const linkLeft = Math.min(startCoords.left, endCoords.left);
|
|
9073
|
+
const linkRight = Math.max(startCoords.right, endCoords.right);
|
|
9074
|
+
const dialogW = 280;
|
|
9075
|
+
const estimatedDialogH = 188;
|
|
9076
|
+
const centerX = (linkLeft + linkRight) / 2;
|
|
9077
|
+
const left = Math.max(
|
|
9078
|
+
VPORT_MARGIN2,
|
|
9079
|
+
Math.min(centerX - dialogW / 2, window.innerWidth - dialogW - VPORT_MARGIN2)
|
|
9080
|
+
);
|
|
9081
|
+
let top = anchorTop - estimatedDialogH - 12;
|
|
9082
|
+
if (top < VPORT_MARGIN2) {
|
|
9083
|
+
top = Math.min(anchorBottom + 12, window.innerHeight - estimatedDialogH - VPORT_MARGIN2);
|
|
9084
|
+
}
|
|
9085
|
+
const finalHref = normalizeLinkHref(hrefValue);
|
|
9086
|
+
const finalText = textValue.trim() ? textValue : finalHref;
|
|
9087
|
+
const canSave = Boolean(finalHref) && (finalHref !== link2.href || finalText !== link2.text);
|
|
9088
|
+
const currentLink = () => {
|
|
9089
|
+
const activeLink = getLinkAtCursor(view.state);
|
|
9090
|
+
if (activeLink && activeLink.from === link2.from && activeLink.to === link2.to) {
|
|
9091
|
+
return activeLink;
|
|
9092
|
+
}
|
|
9093
|
+
return link2;
|
|
9094
|
+
};
|
|
9095
|
+
const saveLink = () => {
|
|
9096
|
+
const activeLink = currentLink();
|
|
9097
|
+
const nextHref = normalizeLinkHref(hrefValue);
|
|
9098
|
+
const nextText = textValue.trim() ? textValue : nextHref;
|
|
9099
|
+
if (!nextHref) return;
|
|
9100
|
+
if (nextHref === activeLink.href && nextText === activeLink.text) return;
|
|
9101
|
+
const cursorOffset = Math.max(0, view.state.selection.from - activeLink.from);
|
|
9102
|
+
const tr = view.state.tr;
|
|
9103
|
+
if (nextText === activeLink.text) {
|
|
9104
|
+
tr.removeMark(activeLink.from, activeLink.to, linkMark);
|
|
9105
|
+
tr.addMark(activeLink.from, activeLink.to, linkMark.create({ href: nextHref }));
|
|
9106
|
+
const nextCursor = Math.min(activeLink.from + cursorOffset, activeLink.to);
|
|
9107
|
+
tr.setSelection(TextSelection6.near(tr.doc.resolve(nextCursor)));
|
|
9108
|
+
} else {
|
|
9109
|
+
const preservedMarks = getNonLinkMarksInRange(view.state, activeLink.from, activeLink.to);
|
|
9110
|
+
tr.insertText(nextText, activeLink.from, activeLink.to);
|
|
9111
|
+
const nextTo = activeLink.from + nextText.length;
|
|
9112
|
+
tr.removeMark(activeLink.from, nextTo, linkMark);
|
|
9113
|
+
for (const mark of preservedMarks) {
|
|
9114
|
+
tr.addMark(activeLink.from, nextTo, mark);
|
|
9115
|
+
}
|
|
9116
|
+
tr.addMark(activeLink.from, nextTo, linkMark.create({ href: nextHref }));
|
|
9117
|
+
const nextCursor = Math.min(activeLink.from + cursorOffset, nextTo);
|
|
9118
|
+
tr.setSelection(TextSelection6.near(tr.doc.resolve(nextCursor)));
|
|
9119
|
+
}
|
|
9120
|
+
view.dispatch(tr);
|
|
9121
|
+
onClose();
|
|
9122
|
+
view.focus();
|
|
9123
|
+
};
|
|
9124
|
+
const removeLink = () => {
|
|
9125
|
+
const activeLink = currentLink();
|
|
9126
|
+
view.dispatch(view.state.tr.removeMark(activeLink.from, activeLink.to, linkMark));
|
|
9127
|
+
onClose();
|
|
9128
|
+
view.focus();
|
|
9129
|
+
};
|
|
9130
|
+
const handleKeyDown = (e) => {
|
|
9131
|
+
if (e.key === "Escape") {
|
|
9132
|
+
e.preventDefault();
|
|
9133
|
+
onClose();
|
|
9134
|
+
view.focus();
|
|
9135
|
+
return;
|
|
9136
|
+
}
|
|
9137
|
+
if (e.key === "Enter") {
|
|
9138
|
+
e.preventDefault();
|
|
9139
|
+
saveLink();
|
|
9140
|
+
}
|
|
9141
|
+
};
|
|
9142
|
+
return createPortal2(
|
|
9143
|
+
/* @__PURE__ */ jsxs9(
|
|
9144
|
+
"div",
|
|
9145
|
+
{
|
|
9146
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9147
|
+
style: {
|
|
9148
|
+
position: "fixed",
|
|
9149
|
+
left,
|
|
9150
|
+
top,
|
|
9151
|
+
zIndex: 1001,
|
|
9152
|
+
width: dialogW,
|
|
9153
|
+
display: "flex",
|
|
9154
|
+
flexDirection: "column",
|
|
9155
|
+
gap: 16,
|
|
9156
|
+
padding: 16,
|
|
9157
|
+
background: "#FFFFFF",
|
|
9158
|
+
borderRadius: 4,
|
|
9159
|
+
boxShadow: "0px 8px 10px rgba(13,13,13,0.12), 0px 3px 14px rgba(13,13,13,0.08), 0px 3px 5px rgba(13,13,13,0.04)",
|
|
9160
|
+
boxSizing: "border-box",
|
|
9161
|
+
animation: "ab-float-in 0.12s ease"
|
|
9162
|
+
},
|
|
9163
|
+
children: [
|
|
9164
|
+
/* @__PURE__ */ jsxs9("div", { ...{ [FLOAT_ATTR]: "" }, style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
9165
|
+
/* @__PURE__ */ jsxs9(
|
|
9166
|
+
"div",
|
|
9167
|
+
{
|
|
9168
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9169
|
+
style: {
|
|
9170
|
+
fontSize: 12,
|
|
9171
|
+
lineHeight: "16px",
|
|
9172
|
+
fontWeight: 600,
|
|
9173
|
+
color: "#0D0D0D"
|
|
9174
|
+
},
|
|
9175
|
+
children: [
|
|
9176
|
+
"Text ",
|
|
9177
|
+
/* @__PURE__ */ jsx10("span", { style: { color: "#858585" }, children: "(optional)" })
|
|
9178
|
+
]
|
|
9179
|
+
}
|
|
9180
|
+
),
|
|
9181
|
+
/* @__PURE__ */ jsx10(
|
|
9182
|
+
"input",
|
|
9183
|
+
{
|
|
9184
|
+
ref: textInputRef,
|
|
9185
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9186
|
+
value: textValue,
|
|
9187
|
+
onChange: (e) => setTextValue(e.target.value),
|
|
9188
|
+
onKeyDown: handleKeyDown,
|
|
9189
|
+
style: {
|
|
9190
|
+
height: 32,
|
|
9191
|
+
border: "1px solid #CCCCCC",
|
|
9192
|
+
borderRadius: 4,
|
|
9193
|
+
padding: "0 16px",
|
|
9194
|
+
fontSize: 14,
|
|
9195
|
+
color: "#0D0D0D",
|
|
9196
|
+
outline: "none",
|
|
9197
|
+
boxSizing: "border-box"
|
|
9198
|
+
}
|
|
9199
|
+
}
|
|
9200
|
+
)
|
|
9201
|
+
] }),
|
|
9202
|
+
/* @__PURE__ */ jsxs9("div", { ...{ [FLOAT_ATTR]: "" }, style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
|
|
9203
|
+
/* @__PURE__ */ jsx10(
|
|
9204
|
+
"div",
|
|
9205
|
+
{
|
|
9206
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9207
|
+
style: {
|
|
9208
|
+
fontSize: 12,
|
|
9209
|
+
lineHeight: "16px",
|
|
9210
|
+
fontWeight: 600,
|
|
9211
|
+
color: "#0D0D0D"
|
|
9212
|
+
},
|
|
9213
|
+
children: "Link"
|
|
9214
|
+
}
|
|
9215
|
+
),
|
|
9216
|
+
/* @__PURE__ */ jsx10(
|
|
9217
|
+
"input",
|
|
9218
|
+
{
|
|
9219
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9220
|
+
value: hrefValue,
|
|
9221
|
+
onChange: (e) => setHrefValue(e.target.value),
|
|
9222
|
+
onKeyDown: handleKeyDown,
|
|
9223
|
+
style: {
|
|
9224
|
+
height: 32,
|
|
9225
|
+
border: "1px solid #CCCCCC",
|
|
9226
|
+
borderRadius: 4,
|
|
9227
|
+
padding: "0 16px",
|
|
9228
|
+
fontSize: 14,
|
|
9229
|
+
color: "#0D0D0D",
|
|
9230
|
+
outline: "none",
|
|
9231
|
+
boxSizing: "border-box"
|
|
9232
|
+
}
|
|
9233
|
+
}
|
|
9234
|
+
)
|
|
9235
|
+
] }),
|
|
9236
|
+
/* @__PURE__ */ jsxs9(
|
|
9237
|
+
"div",
|
|
9238
|
+
{
|
|
9239
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9240
|
+
style: {
|
|
9241
|
+
display: "flex",
|
|
9242
|
+
alignItems: "center",
|
|
9243
|
+
justifyContent: "space-between",
|
|
9244
|
+
gap: 12
|
|
9245
|
+
},
|
|
9246
|
+
children: [
|
|
9247
|
+
/* @__PURE__ */ jsx10(
|
|
9248
|
+
"button",
|
|
9249
|
+
{
|
|
9250
|
+
type: "button",
|
|
9251
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9252
|
+
onMouseDown: (e) => {
|
|
9253
|
+
e.preventDefault();
|
|
9254
|
+
removeLink();
|
|
9255
|
+
},
|
|
9256
|
+
style: {
|
|
9257
|
+
...BTN_RESET2,
|
|
9258
|
+
fontSize: 14,
|
|
9259
|
+
lineHeight: "20px",
|
|
9260
|
+
fontWeight: 600,
|
|
9261
|
+
color: "#0D0D0D"
|
|
9262
|
+
},
|
|
9263
|
+
children: "Remove link"
|
|
9264
|
+
}
|
|
9265
|
+
),
|
|
9266
|
+
/* @__PURE__ */ jsxs9("div", { ...{ [FLOAT_ATTR]: "" }, style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
9267
|
+
/* @__PURE__ */ jsx10(
|
|
9268
|
+
"button",
|
|
9269
|
+
{
|
|
9270
|
+
type: "button",
|
|
9271
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9272
|
+
onMouseDown: (e) => {
|
|
9273
|
+
e.preventDefault();
|
|
9274
|
+
onClose();
|
|
9275
|
+
view.focus();
|
|
9276
|
+
},
|
|
9277
|
+
style: {
|
|
9278
|
+
...BTN_RESET2,
|
|
9279
|
+
height: 32,
|
|
9280
|
+
padding: "0 13px",
|
|
9281
|
+
border: "1px solid #CCCCCC",
|
|
9282
|
+
borderRadius: 4,
|
|
9283
|
+
fontSize: 14,
|
|
9284
|
+
lineHeight: "20px",
|
|
9285
|
+
fontWeight: 600,
|
|
9286
|
+
color: "#0D0D0D"
|
|
9287
|
+
},
|
|
9288
|
+
children: "Cancel"
|
|
9289
|
+
}
|
|
9290
|
+
),
|
|
9291
|
+
/* @__PURE__ */ jsx10(
|
|
9292
|
+
"button",
|
|
9293
|
+
{
|
|
9294
|
+
type: "button",
|
|
9295
|
+
disabled: !canSave,
|
|
9296
|
+
...{ [FLOAT_ATTR]: "" },
|
|
9297
|
+
onMouseDown: (e) => {
|
|
9298
|
+
e.preventDefault();
|
|
9299
|
+
if (!canSave) return;
|
|
9300
|
+
saveLink();
|
|
9301
|
+
},
|
|
9302
|
+
style: {
|
|
9303
|
+
...BTN_RESET2,
|
|
9304
|
+
height: 32,
|
|
9305
|
+
padding: "0 13px",
|
|
9306
|
+
borderRadius: 4,
|
|
9307
|
+
fontSize: 14,
|
|
9308
|
+
lineHeight: "20px",
|
|
9309
|
+
fontWeight: 600,
|
|
9310
|
+
background: canSave ? "#6210CC" : "#ECECEC",
|
|
9311
|
+
color: canSave ? "#FFFFFF" : "#A6A6A6",
|
|
9312
|
+
cursor: canSave ? "pointer" : "default"
|
|
9313
|
+
},
|
|
9314
|
+
children: "Save"
|
|
9315
|
+
}
|
|
9316
|
+
)
|
|
9317
|
+
] })
|
|
9318
|
+
]
|
|
9319
|
+
}
|
|
9320
|
+
)
|
|
9321
|
+
]
|
|
9322
|
+
}
|
|
9323
|
+
),
|
|
9324
|
+
document.body
|
|
9325
|
+
);
|
|
9326
|
+
}
|
|
8263
9327
|
var BLOCK_ITEMS = [
|
|
8264
9328
|
{
|
|
8265
9329
|
shortLabel: "\xB6",
|
|
@@ -8478,6 +9542,7 @@ function BlockMenuItem({
|
|
|
8478
9542
|
}
|
|
8479
9543
|
function FloatingMenu({ view, editorState }) {
|
|
8480
9544
|
const [showEmptyHandle, setShowEmptyHandle] = useState7(false);
|
|
9545
|
+
const [editingLink, setEditingLink] = useState7(null);
|
|
8481
9546
|
const dwellTimerRef = useRef4(null);
|
|
8482
9547
|
const lastEmptyPosRef = useRef4(null);
|
|
8483
9548
|
const [, setScrollTick] = useState7(0);
|
|
@@ -8503,9 +9568,16 @@ function FloatingMenu({ view, editorState }) {
|
|
|
8503
9568
|
`;
|
|
8504
9569
|
document.head.appendChild(style);
|
|
8505
9570
|
}, []);
|
|
9571
|
+
const emptyPos = editorState ? emptyBlockCursorPos(editorState) : null;
|
|
9572
|
+
const hasSelection = editorState ? !editorState.selection.empty : false;
|
|
9573
|
+
const linkAtCursor = editorState && !hasSelection ? getLinkAtCursor(editorState) : null;
|
|
9574
|
+
useEffect4(() => {
|
|
9575
|
+
if (!editingLink) return;
|
|
9576
|
+
if (!linkAtCursor || linkAtCursor.from !== editingLink.from || linkAtCursor.to !== editingLink.to) {
|
|
9577
|
+
setEditingLink(null);
|
|
9578
|
+
}
|
|
9579
|
+
}, [editingLink, linkAtCursor]);
|
|
8506
9580
|
if (!view || !editorState) return null;
|
|
8507
|
-
const emptyPos = emptyBlockCursorPos(editorState);
|
|
8508
|
-
const hasSelection = !editorState.selection.empty;
|
|
8509
9581
|
if (emptyPos !== lastEmptyPosRef.current) {
|
|
8510
9582
|
lastEmptyPosRef.current = emptyPos;
|
|
8511
9583
|
if (dwellTimerRef.current) {
|
|
@@ -8529,23 +9601,39 @@ function FloatingMenu({ view, editorState }) {
|
|
|
8529
9601
|
selectionRect
|
|
8530
9602
|
}
|
|
8531
9603
|
),
|
|
8532
|
-
!hasSelection &&
|
|
9604
|
+
!hasSelection && linkAtCursor && !editingLink && /* @__PURE__ */ jsx10(
|
|
9605
|
+
LinkHoverTooltip,
|
|
9606
|
+
{
|
|
9607
|
+
view,
|
|
9608
|
+
link: linkAtCursor,
|
|
9609
|
+
onEdit: (link2) => setEditingLink(link2)
|
|
9610
|
+
}
|
|
9611
|
+
),
|
|
9612
|
+
!hasSelection && editingLink && /* @__PURE__ */ jsx10(
|
|
9613
|
+
LinkEditDialog,
|
|
9614
|
+
{
|
|
9615
|
+
view,
|
|
9616
|
+
link: editingLink,
|
|
9617
|
+
onClose: () => setEditingLink(null)
|
|
9618
|
+
}
|
|
9619
|
+
),
|
|
9620
|
+
!hasSelection && !linkAtCursor && showEmptyHandle && emptyPos !== null && /* @__PURE__ */ jsx10(EmptyParaHandle, { view, cursorPos: emptyPos })
|
|
8533
9621
|
] }),
|
|
8534
9622
|
document.body
|
|
8535
9623
|
);
|
|
8536
9624
|
}
|
|
8537
9625
|
|
|
8538
9626
|
// src/ui/plugin/inlineSuggestPlugin.ts
|
|
8539
|
-
import { Plugin as
|
|
8540
|
-
import { Decoration as
|
|
8541
|
-
var inlineSuggestKey = new
|
|
9627
|
+
import { Plugin as Plugin9, PluginKey as PluginKey9 } from "prosemirror-state";
|
|
9628
|
+
import { Decoration as Decoration5, DecorationSet as DecorationSet5 } from "prosemirror-view";
|
|
9629
|
+
var inlineSuggestKey = new PluginKey9("inlineSuggest");
|
|
8542
9630
|
var DEBOUNCE_MS = 600;
|
|
8543
9631
|
function createInlineSuggestPlugin(provider, endpoint, options) {
|
|
8544
9632
|
const { onContextChange } = options ?? {};
|
|
8545
9633
|
return {
|
|
8546
9634
|
name: "inlineSuggest",
|
|
8547
9635
|
plugins: () => [
|
|
8548
|
-
new
|
|
9636
|
+
new Plugin9({
|
|
8549
9637
|
key: inlineSuggestKey,
|
|
8550
9638
|
state: {
|
|
8551
9639
|
init: () => ({ suggestion: null, anchorPos: null }),
|
|
@@ -8561,13 +9649,13 @@ function createInlineSuggestPlugin(provider, endpoint, options) {
|
|
|
8561
9649
|
props: {
|
|
8562
9650
|
decorations(state) {
|
|
8563
9651
|
const ps = inlineSuggestKey.getState(state);
|
|
8564
|
-
if (!ps?.suggestion || !state.selection.empty) return
|
|
9652
|
+
if (!ps?.suggestion || !state.selection.empty) return DecorationSet5.empty;
|
|
8565
9653
|
const ghost = document.createElement("span");
|
|
8566
9654
|
ghost.textContent = ps.suggestion;
|
|
8567
9655
|
ghost.style.cssText = "color: rgba(0,0,0,0.3); pointer-events: none; user-select: none;";
|
|
8568
9656
|
ghost.setAttribute("aria-hidden", "true");
|
|
8569
|
-
return
|
|
8570
|
-
|
|
9657
|
+
return DecorationSet5.create(state.doc, [
|
|
9658
|
+
Decoration5.widget(state.selection.from, ghost, {
|
|
8571
9659
|
side: 1,
|
|
8572
9660
|
key: "inline-suggest"
|
|
8573
9661
|
})
|
|
@@ -8722,6 +9810,7 @@ export {
|
|
|
8722
9810
|
createLinkPlugin,
|
|
8723
9811
|
createMarkdownClipboardPlugin,
|
|
8724
9812
|
createNoteBlockPlugin,
|
|
9813
|
+
createPlaceholderPlugin,
|
|
8725
9814
|
createPluginArray,
|
|
8726
9815
|
createReactNodeView,
|
|
8727
9816
|
createSlashCommandPlugin,
|