@sendbird/actionbook-core 0.8.1 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/ast/types.ts
2
- var RESOURCE_TAG_TYPES = ["tool", "manual", "agent_message_template", "handoff", "time_diff"];
2
+ var RESOURCE_TAG_TYPES = ["tool", "manual", "agent_message_template", "handoff", "time_diff", "time_difference"];
3
3
  var JUMP_POINT_ID_PATTERN = /^[A-Za-z_]+$/;
4
4
 
5
5
  // src/ast/builders.ts
@@ -60,6 +60,10 @@ var blockquote = (...content) => ({
60
60
  content
61
61
  });
62
62
  var horizontalRule = () => ({ type: "horizontalRule" });
63
+ var noteBlock = (...content) => ({
64
+ type: "noteBlock",
65
+ content
66
+ });
63
67
  var tableCell = (header, ...content) => ({
64
68
  type: "tableCell",
65
69
  ...header ? { header: true } : {},
@@ -102,6 +106,7 @@ function getChildren(node) {
102
106
  case "doc":
103
107
  case "blockquote":
104
108
  case "listItem":
109
+ case "noteBlock":
105
110
  return node.content;
106
111
  case "paragraph":
107
112
  case "heading":
@@ -146,6 +151,7 @@ function map(node, fn, _depth = 0) {
146
151
  return { ...mapped, content: newChildren };
147
152
  case "blockquote":
148
153
  case "listItem":
154
+ case "noteBlock":
149
155
  return { ...mapped, content: newChildren };
150
156
  case "paragraph":
151
157
  case "heading":
@@ -241,7 +247,9 @@ var BLOCK_TYPES = /* @__PURE__ */ new Set([
241
247
  "listItem",
242
248
  "blockquote",
243
249
  "horizontalRule",
244
- "jinjaIfBlock"
250
+ "codeBlock",
251
+ "jinjaIfBlock",
252
+ "noteBlock"
245
253
  ]);
246
254
  var INLINE_TYPES = /* @__PURE__ */ new Set(["text", "resourceTag", "jumpPoint", "hardBreak", "jinjaIfInline"]);
247
255
  function isBlock(node) {
@@ -304,7 +312,8 @@ function validateMark(mark, path) {
304
312
  }
305
313
  return errors;
306
314
  }
307
- var VALID_RESOURCE_TAG_TYPES = /* @__PURE__ */ new Set(["tool", "manual", "agent_message_template", "handoff"]);
315
+ var VALID_RESOURCE_TAG_TYPES = /* @__PURE__ */ new Set(["tool", "manual", "agent_message_template", "handoff", "end_call", "time_diff", "time_difference"]);
316
+ var RESOURCE_ID_OPTIONAL_TYPES = /* @__PURE__ */ new Set(["handoff", "end_call"]);
308
317
  function validateNode(node, path, depth) {
309
318
  if (depth > MAX_VALIDATION_DEPTH) {
310
319
  return [{ path, message: `Maximum validation depth (${MAX_VALIDATION_DEPTH}) exceeded` }];
@@ -359,6 +368,15 @@ function validateNode(node, path, depth) {
359
368
  break;
360
369
  case "horizontalRule":
361
370
  break;
371
+ case "codeBlock":
372
+ break;
373
+ case "noteBlock":
374
+ if (!Array.isArray(node.content)) {
375
+ errors.push({ path, message: "noteBlock must have content array" });
376
+ } else {
377
+ node.content.forEach((child, i) => errors.push(...validateNode(child, `${path}.content[${i}]`, depth + 1)));
378
+ }
379
+ break;
362
380
  case "table": {
363
381
  const tbl = node;
364
382
  if (!Array.isArray(tbl.content) || tbl.content.length === 0) {
@@ -390,7 +408,7 @@ function validateNode(node, path, depth) {
390
408
  if (!VALID_RESOURCE_TAG_TYPES.has(node.tagType)) {
391
409
  errors.push({ path: `${path}.tagType`, message: `Invalid resource tag type: ${node.tagType}` });
392
410
  }
393
- if (typeof node.resourceId !== "string" || node.resourceId.length === 0) {
411
+ if (typeof node.resourceId !== "string" || !RESOURCE_ID_OPTIONAL_TYPES.has(node.tagType) && node.resourceId.length === 0) {
394
412
  errors.push({ path: `${path}.resourceId`, message: "resourceTag requires a non-empty resourceId" });
395
413
  }
396
414
  if (typeof node.text !== "string" || node.text.length === 0) {
@@ -2081,7 +2099,7 @@ function listItemWithTaskListItem(node, parent, state, info) {
2081
2099
 
2082
2100
  // src/markdown/plugins/resourceTag.ts
2083
2101
  var RESOURCE_TAG_RE = /\{\{([^:}]+):([^:}]*):([^}]+)\}\}/g;
2084
- var VALID_TYPES = /* @__PURE__ */ new Set(["tool", "manual", "agent_message_template", "handoff", "time_diff"]);
2102
+ var VALID_TYPES = /* @__PURE__ */ new Set(["tool", "manual", "agent_message_template", "handoff", "end_call", "time_diff"]);
2085
2103
  function splitTextWithResourceTags(text2) {
2086
2104
  const results = [];
2087
2105
  let lastIndex = 0;
@@ -2244,10 +2262,11 @@ function convertBlock(node, depth = 0) {
2244
2262
  case "list": {
2245
2263
  const list = node;
2246
2264
  const items = list.children.map((li) => convertListItem(li, depth + 1));
2265
+ const spread = list.spread ?? false;
2247
2266
  if (list.ordered) {
2248
- return [{ type: "orderedList", start: list.start ?? 1, content: items }];
2267
+ return [{ type: "orderedList", start: list.start ?? 1, spread, content: items }];
2249
2268
  }
2250
- return [{ type: "bulletList", content: items }];
2269
+ return [{ type: "bulletList", spread, content: items }];
2251
2270
  }
2252
2271
  case "blockquote": {
2253
2272
  const bq = node;
@@ -2256,6 +2275,10 @@ function convertBlock(node, depth = 0) {
2256
2275
  }
2257
2276
  case "thematicBreak":
2258
2277
  return [{ type: "horizontalRule" }];
2278
+ case "code": {
2279
+ const codeNode = node;
2280
+ return [{ type: "codeBlock", content: codeNode.value, ...codeNode.lang ? { language: codeNode.lang } : {} }];
2281
+ }
2259
2282
  case "jinjaIfBlockMdast": {
2260
2283
  const jNode = node;
2261
2284
  const branches = jNode.branches.map((b) => ({
@@ -2296,7 +2319,11 @@ function convertBlock(node, depth = 0) {
2296
2319
  function convertListItem(node, depth = 0) {
2297
2320
  if (depth > MAX_DEPTH2) throw new DepthError(depth);
2298
2321
  const content = node.children.flatMap((child) => convertBlock(child, depth + 1));
2299
- const base = { type: "listItem", content: content.length > 0 ? content : [{ type: "paragraph", content: [] }] };
2322
+ const base = {
2323
+ type: "listItem",
2324
+ spread: node.spread ?? false,
2325
+ content: content.length > 0 ? content : [{ type: "paragraph", content: [] }]
2326
+ };
2300
2327
  if (typeof node.checked === "boolean") {
2301
2328
  return { ...base, checked: node.checked };
2302
2329
  }
@@ -2395,31 +2422,39 @@ function blockToMdast(node, depth = 0) {
2395
2422
  return [{ type: "paragraph", children: node.content.flatMap((c) => inlineToMdast(c, depth + 1)) }];
2396
2423
  case "heading":
2397
2424
  return [{ type: "heading", depth: node.level, children: node.content.flatMap((c) => inlineToMdast(c, depth + 1)) }];
2398
- case "bulletList":
2425
+ case "bulletList": {
2426
+ const items = node.content.map((li) => listItemToMdast(li, depth + 1));
2399
2427
  return [
2400
2428
  {
2401
2429
  type: "list",
2402
2430
  ordered: false,
2403
- spread: false,
2404
- children: node.content.map((li) => listItemToMdast(li, depth + 1))
2431
+ spread: node.spread ?? false,
2432
+ children: items
2405
2433
  }
2406
2434
  ];
2407
- case "orderedList":
2435
+ }
2436
+ case "orderedList": {
2437
+ const items = node.content.map((li) => listItemToMdast(li, depth + 1));
2408
2438
  return [
2409
2439
  {
2410
2440
  type: "list",
2411
2441
  ordered: true,
2412
2442
  start: node.start,
2413
- spread: false,
2414
- children: node.content.map((li) => listItemToMdast(li, depth + 1))
2443
+ spread: node.spread ?? false,
2444
+ children: items
2415
2445
  }
2416
2446
  ];
2447
+ }
2417
2448
  case "listItem":
2418
2449
  return listItemToMdast(node, depth).children.flatMap((child) => [child]);
2419
2450
  case "blockquote":
2420
2451
  return [{ type: "blockquote", children: node.content.flatMap((c) => blockToMdast(c, depth + 1)) }];
2421
2452
  case "horizontalRule":
2422
2453
  return [{ type: "thematicBreak" }];
2454
+ case "codeBlock": {
2455
+ const cb = node;
2456
+ return [{ type: "code", value: cb.content, ...cb.language ? { lang: cb.language } : {} }];
2457
+ }
2423
2458
  case "jinjaIfBlock": {
2424
2459
  const jNode = node;
2425
2460
  const result = [];
@@ -2443,14 +2478,18 @@ function blockToMdast(node, depth = 0) {
2443
2478
  }));
2444
2479
  return [{ type: "table", children: rows }];
2445
2480
  }
2481
+ case "noteBlock":
2482
+ return [];
2446
2483
  }
2447
2484
  }
2448
2485
  function listItemToMdast(node, depth = 0) {
2449
2486
  if (depth > MAX_DEPTH2) throw new DepthError(depth);
2487
+ const children = node.content.flatMap((child) => blockToMdast(child, depth + 1));
2488
+ const isSpread = node.spread ?? false;
2450
2489
  const result = {
2451
2490
  type: "listItem",
2452
- spread: false,
2453
- children: node.content.flatMap((child) => blockToMdast(child, depth + 1))
2491
+ spread: isSpread,
2492
+ children
2454
2493
  };
2455
2494
  if (typeof node.checked === "boolean") {
2456
2495
  result.checked = node.checked;
@@ -2811,6 +2850,23 @@ var gfmNoAutolinkFromMarkdown = () => [
2811
2850
  gfmTaskListItemFromMarkdown()
2812
2851
  ];
2813
2852
  var MAX_DEPTH4 = 128;
2853
+ var BRACKET_LINK_RE = /\[([^\]]+)\]\(([^)]*\s[^)]*)\)/g;
2854
+ function splitTextWithBracketLinks(value) {
2855
+ const results = [];
2856
+ let lastIndex = 0;
2857
+ for (const m of value.matchAll(BRACKET_LINK_RE)) {
2858
+ const idx = m.index;
2859
+ if (idx > lastIndex) results.push({ type: "text", value: value.slice(lastIndex, idx) });
2860
+ results.push({
2861
+ type: "link",
2862
+ url: m[2],
2863
+ children: [{ type: "text", value: m[1] }]
2864
+ });
2865
+ lastIndex = idx + m[0].length;
2866
+ }
2867
+ if (lastIndex < value.length) results.push({ type: "text", value: value.slice(lastIndex) });
2868
+ return results;
2869
+ }
2814
2870
  function combinedTokensFromMarkdown(node, depth = 0) {
2815
2871
  if (depth > MAX_DEPTH4) return;
2816
2872
  if (!node.children) return;
@@ -2822,13 +2878,20 @@ function combinedTokensFromMarkdown(node, depth = 0) {
2822
2878
  for (const child of node.children) {
2823
2879
  const c = child;
2824
2880
  if (c.type === "text") {
2825
- const afterResourceTags = splitTextWithResourceTags(child.value);
2826
- for (const part of afterResourceTags) {
2827
- if (part.type === "text") {
2828
- const afterJumpPoints = splitTextWithJumpPoints(part.value);
2829
- newChildren.push(...afterJumpPoints);
2830
- } else {
2831
- newChildren.push(part);
2881
+ const afterLinks = splitTextWithBracketLinks(child.value);
2882
+ for (const linkPart of afterLinks) {
2883
+ if (linkPart.type !== "text") {
2884
+ newChildren.push(linkPart);
2885
+ continue;
2886
+ }
2887
+ const afterResourceTags = splitTextWithResourceTags(linkPart.value);
2888
+ for (const part of afterResourceTags) {
2889
+ if (part.type === "text") {
2890
+ const afterJumpPoints = splitTextWithJumpPoints(part.value);
2891
+ newChildren.push(...afterJumpPoints);
2892
+ } else {
2893
+ newChildren.push(part);
2894
+ }
2832
2895
  }
2833
2896
  }
2834
2897
  } else {
@@ -2870,12 +2933,20 @@ function textHandler(node, parent, state, info) {
2870
2933
  state.unsafe = originalUnsafe;
2871
2934
  return result;
2872
2935
  }
2936
+ function linkHandler(node, parent, state, info) {
2937
+ const childrenText = state.containerPhrasing(node, { ...info, before: "[", after: "]" });
2938
+ const url = node.url ?? "";
2939
+ const title = node.title;
2940
+ const titlePart = title ? ` "${title.replace(/"/g, '\\"')}"` : "";
2941
+ return `[${childrenText}](${url}${titlePart})`;
2942
+ }
2873
2943
  function serializeToMarkdown(doc2) {
2874
2944
  const mdastTree = toMdast(doc2);
2875
- const result = toMarkdown(mdastTree, {
2945
+ const raw = toMarkdown(mdastTree, {
2876
2946
  bullet: "-",
2877
2947
  rule: "-",
2878
2948
  listItemIndent: "one",
2949
+ incrementListMarker: true,
2879
2950
  emphasis: "*",
2880
2951
  strong: "*",
2881
2952
  resourceLink: true,
@@ -2887,11 +2958,13 @@ function serializeToMarkdown(doc2) {
2887
2958
  ],
2888
2959
  handlers: {
2889
2960
  text: textHandler,
2961
+ link: linkHandler,
2890
2962
  ...resourceTagToMarkdown().handlers,
2891
2963
  ...jumpPointToMarkdown().handlers
2892
2964
  },
2893
2965
  extensions: [gfmNoAutolinkToMarkdown()]
2894
2966
  });
2967
+ let result = raw.replace(/^(\s*)\\>/gm, "$1&gt;").replace(/(\S) *>/g, (match, pre) => `${pre} &gt;`).replace(/\\\[([^\]]*)\]/g, "\\[$1\\]").replace(/&#x20;/g, " ");
2895
2968
  return result;
2896
2969
  }
2897
2970
 
@@ -2927,6 +3000,13 @@ function serializeToJSON(doc2) {
2927
3000
  // src/compat/prosemirror.ts
2928
3001
  var MAX_DEPTH5 = 128;
2929
3002
  var ALLOWED_URL_PROTOCOLS3 = /^(https?:|mailto:|tel:|#|\/)/i;
3003
+ var LIST_TYPES = /* @__PURE__ */ new Set(["bulletList", "orderedList"]);
3004
+ function isLooseList(items) {
3005
+ return items.some((li) => {
3006
+ const nonListBlocks = li.content.filter((b) => !LIST_TYPES.has(b.type));
3007
+ return nonListBlocks.length > 1;
3008
+ });
3009
+ }
2930
3010
  function convertPMMark(mark) {
2931
3011
  switch (mark.type) {
2932
3012
  case "bold":
@@ -2965,12 +3045,17 @@ function convertPMInline(node, depth) {
2965
3045
  }
2966
3046
  case "inlineToolTag": {
2967
3047
  const attrs = node.attrs ?? {};
3048
+ const tagType = attrs.type ?? "tool";
3049
+ const text2 = attrs.text ?? "";
3050
+ if (!RESOURCE_TAG_TYPES.includes(tagType)) {
3051
+ return text2 ? [{ type: "text", text: text2 }] : [];
3052
+ }
2968
3053
  return [
2969
3054
  {
2970
3055
  type: "resourceTag",
2971
- tagType: attrs.type ?? "tool",
3056
+ tagType,
2972
3057
  resourceId: attrs.resourceId ?? "",
2973
- text: attrs.text ?? ""
3058
+ text: text2
2974
3059
  }
2975
3060
  ];
2976
3061
  }
@@ -3001,20 +3086,27 @@ function convertPMBlock(node, depth = 0) {
3001
3086
  const level = node.attrs?.level ?? 1;
3002
3087
  return [{ type: "heading", level, content: children.flatMap((c) => convertPMInline(c, depth + 1)) }];
3003
3088
  }
3004
- case "bulletList":
3089
+ case "bulletList": {
3090
+ const items = children.map((c) => convertPMListItem(c, depth + 1));
3091
+ const spread = isLooseList(items);
3005
3092
  return [
3006
3093
  {
3007
3094
  type: "bulletList",
3008
- content: children.map((c) => convertPMListItem(c, depth + 1))
3095
+ ...spread ? { spread: true } : {},
3096
+ content: items.map((li) => spread ? { ...li, spread: true } : li)
3009
3097
  }
3010
3098
  ];
3099
+ }
3011
3100
  case "orderedList": {
3012
3101
  const start = node.attrs?.start ?? 1;
3102
+ const items = children.map((c) => convertPMListItem(c, depth + 1));
3103
+ const spread = isLooseList(items);
3013
3104
  return [
3014
3105
  {
3015
3106
  type: "orderedList",
3016
3107
  start,
3017
- content: children.map((c) => convertPMListItem(c, depth + 1))
3108
+ ...spread ? { spread: true } : {},
3109
+ content: items.map((li) => spread ? { ...li, spread: true } : li)
3018
3110
  }
3019
3111
  ];
3020
3112
  }
@@ -3025,6 +3117,12 @@ function convertPMBlock(node, depth = 0) {
3025
3117
  return [{ type: "blockquote", content: children.flatMap((c) => convertPMBlock(c, depth + 1)) }];
3026
3118
  case "horizontalRule":
3027
3119
  return [{ type: "horizontalRule" }];
3120
+ case "codeBlock": {
3121
+ const textContent2 = children.map((c) => c.text ?? "").join("");
3122
+ return [{ type: "codeBlock", content: textContent2, ...node.attrs?.language ? { language: node.attrs.language } : {} }];
3123
+ }
3124
+ case "noteBlock":
3125
+ return [{ type: "noteBlock", content: children.flatMap((c) => convertPMBlock(c, depth + 1)) }];
3028
3126
  case "table": {
3029
3127
  const rows = children.map((rowNode) => {
3030
3128
  const cells = (rowNode.content ?? []).map((cellNode) => {
@@ -3041,6 +3139,22 @@ function convertPMBlock(node, depth = 0) {
3041
3139
  });
3042
3140
  return [{ type: "table", content: rows }];
3043
3141
  }
3142
+ case "jinjaIfBlock": {
3143
+ const branches = children.map((branchNode) => {
3144
+ const branchType = branchNode.attrs?.branchType ?? "if";
3145
+ const condition = branchNode.attrs?.condition;
3146
+ const branchContent = (branchNode.content ?? []).flatMap((c) => convertPMBlock(c, depth + 2));
3147
+ return {
3148
+ branchType,
3149
+ ...branchType !== "else" && condition ? { condition } : {},
3150
+ content: branchContent
3151
+ };
3152
+ });
3153
+ if (branches.length === 0) return [];
3154
+ return [{ type: "jinjaIfBlock", branches }];
3155
+ }
3156
+ case "jinjaIfBranch":
3157
+ return children.flatMap((c) => convertPMBlock(c, depth + 1));
3044
3158
  default:
3045
3159
  if (children.length > 0) {
3046
3160
  return children.flatMap((c) => convertPMBlock(c, depth + 1));
@@ -3052,11 +3166,11 @@ function convertPMListItem(node, depth = 0) {
3052
3166
  if (depth > MAX_DEPTH5) return { type: "listItem", content: [{ type: "paragraph", content: [] }] };
3053
3167
  const children = node.content ?? [];
3054
3168
  const content = children.flatMap((c) => convertPMBlock(c, depth + 1));
3055
- const checked = node.attrs?.checked;
3056
3169
  const base = {
3057
3170
  type: "listItem",
3058
3171
  content: content.length > 0 ? content : [{ type: "paragraph", content: [] }]
3059
3172
  };
3173
+ const checked = node.attrs?.checked;
3060
3174
  if (typeof checked === "boolean") {
3061
3175
  base.checked = checked;
3062
3176
  }
@@ -3117,13 +3231,14 @@ function deserializeFromJSON(json) {
3117
3231
  // src/operations/apply.ts
3118
3232
  var MAX_PATH_DEPTH = 64;
3119
3233
  var VALID_CHILDREN = {
3120
- doc: /* @__PURE__ */ new Set(["paragraph", "heading", "bulletList", "orderedList", "blockquote", "horizontalRule", "table", "jinjaIfBlock"]),
3234
+ doc: /* @__PURE__ */ new Set(["paragraph", "heading", "bulletList", "orderedList", "blockquote", "horizontalRule", "codeBlock", "table", "jinjaIfBlock", "noteBlock"]),
3121
3235
  paragraph: /* @__PURE__ */ new Set(["text", "resourceTag", "jumpPoint", "hardBreak", "jinjaIfInline"]),
3122
3236
  heading: /* @__PURE__ */ new Set(["text", "resourceTag", "jumpPoint", "hardBreak", "jinjaIfInline"]),
3123
3237
  bulletList: /* @__PURE__ */ new Set(["listItem"]),
3124
3238
  orderedList: /* @__PURE__ */ new Set(["listItem"]),
3125
- listItem: /* @__PURE__ */ new Set(["paragraph", "heading", "bulletList", "orderedList", "blockquote", "horizontalRule", "table", "jinjaIfBlock"]),
3126
- blockquote: /* @__PURE__ */ new Set(["paragraph", "heading", "bulletList", "orderedList", "blockquote", "horizontalRule", "table", "jinjaIfBlock"]),
3239
+ listItem: /* @__PURE__ */ new Set(["paragraph", "heading", "bulletList", "orderedList", "blockquote", "horizontalRule", "codeBlock", "table", "jinjaIfBlock", "noteBlock"]),
3240
+ blockquote: /* @__PURE__ */ new Set(["paragraph", "heading", "bulletList", "orderedList", "blockquote", "horizontalRule", "codeBlock", "table", "jinjaIfBlock", "noteBlock"]),
3241
+ noteBlock: /* @__PURE__ */ new Set(["paragraph", "heading", "bulletList", "orderedList", "blockquote", "horizontalRule", "table", "jinjaIfBlock"]),
3127
3242
  table: /* @__PURE__ */ new Set(["tableRow"]),
3128
3243
  tableRow: /* @__PURE__ */ new Set(["tableCell"]),
3129
3244
  tableCell: /* @__PURE__ */ new Set(["text", "resourceTag", "jumpPoint", "hardBreak", "jinjaIfInline"])
@@ -3787,6 +3902,205 @@ function evaluateCondition(condition, variables) {
3787
3902
  }
3788
3903
  }
3789
3904
 
3905
+ // src/jinja/conditionHighlighter.ts
3906
+ var KEYWORDS3 = {
3907
+ and: "AND",
3908
+ or: "OR",
3909
+ not: "NOT",
3910
+ in: "IN",
3911
+ is: "IS",
3912
+ True: "BOOL",
3913
+ False: "BOOL",
3914
+ true: "BOOL",
3915
+ false: "BOOL",
3916
+ None: "NONE",
3917
+ null: "NONE"
3918
+ };
3919
+ var CATEGORY_BY_TYPE = {
3920
+ STRING: "value",
3921
+ NUMBER: "value",
3922
+ BOOL: "value",
3923
+ NONE: "value",
3924
+ IDENT: "variable",
3925
+ AND: "operator",
3926
+ OR: "operator",
3927
+ NOT: "operator",
3928
+ IN: "operator",
3929
+ IS: "operator",
3930
+ EQ: "operator",
3931
+ NEQ: "operator",
3932
+ LT: "operator",
3933
+ GT: "operator",
3934
+ LTE: "operator",
3935
+ GTE: "operator",
3936
+ LPAREN: "punctuation",
3937
+ RPAREN: "punctuation"
3938
+ };
3939
+ var NEGATIVE_NUMBER_PRECEDERS = /* @__PURE__ */ new Set([
3940
+ "AND",
3941
+ "OR",
3942
+ "NOT",
3943
+ "IN",
3944
+ "IS",
3945
+ "EQ",
3946
+ "NEQ",
3947
+ "LT",
3948
+ "GT",
3949
+ "LTE",
3950
+ "GTE",
3951
+ "LPAREN"
3952
+ ]);
3953
+ function canStartNegativeNumber(tokens) {
3954
+ if (tokens.length === 0) {
3955
+ return true;
3956
+ }
3957
+ const previous = tokens[tokens.length - 1];
3958
+ return NEGATIVE_NUMBER_PRECEDERS.has(previous.type);
3959
+ }
3960
+ function finalizeTokens(input, rawTokens) {
3961
+ const highlighted = [];
3962
+ for (let i = 0; i < rawTokens.length; i++) {
3963
+ const token = rawTokens[i];
3964
+ const next = rawTokens[i + 1];
3965
+ if (token.type === "IS" && next?.type === "NOT") {
3966
+ highlighted.push({
3967
+ text: input.slice(token.start, next.end),
3968
+ start: token.start,
3969
+ end: next.end,
3970
+ category: "operator"
3971
+ });
3972
+ i++;
3973
+ continue;
3974
+ }
3975
+ highlighted.push({
3976
+ text: token.text,
3977
+ start: token.start,
3978
+ end: token.end,
3979
+ category: CATEGORY_BY_TYPE[token.type]
3980
+ });
3981
+ }
3982
+ return highlighted;
3983
+ }
3984
+ function tokenizeCondition(input) {
3985
+ const rawTokens = [];
3986
+ let i = 0;
3987
+ while (i < input.length) {
3988
+ const char = input[i];
3989
+ if (/\s/.test(char)) {
3990
+ i++;
3991
+ continue;
3992
+ }
3993
+ if (char === '"' || char === "'") {
3994
+ const start = i;
3995
+ const quote = char;
3996
+ i++;
3997
+ while (i < input.length) {
3998
+ if (input[i] === "\\" && i + 1 < input.length) {
3999
+ i += 2;
4000
+ continue;
4001
+ }
4002
+ if (input[i] === quote) {
4003
+ i++;
4004
+ rawTokens.push({
4005
+ type: "STRING",
4006
+ text: input.slice(start, i),
4007
+ start,
4008
+ end: i
4009
+ });
4010
+ break;
4011
+ }
4012
+ i++;
4013
+ }
4014
+ if (rawTokens[rawTokens.length - 1]?.start !== start) {
4015
+ return finalizeTokens(input, rawTokens);
4016
+ }
4017
+ continue;
4018
+ }
4019
+ if (/[0-9]/.test(char) || char === "-" && i + 1 < input.length && /[0-9]/.test(input[i + 1]) && canStartNegativeNumber(rawTokens)) {
4020
+ const start = i;
4021
+ if (input[i] === "-") {
4022
+ i++;
4023
+ }
4024
+ while (i < input.length && /[0-9]/.test(input[i])) {
4025
+ i++;
4026
+ }
4027
+ if (i < input.length && input[i] === ".") {
4028
+ i++;
4029
+ while (i < input.length && /[0-9]/.test(input[i])) {
4030
+ i++;
4031
+ }
4032
+ }
4033
+ rawTokens.push({
4034
+ type: "NUMBER",
4035
+ text: input.slice(start, i),
4036
+ start,
4037
+ end: i
4038
+ });
4039
+ continue;
4040
+ }
4041
+ if (/[a-zA-Z_]/.test(char)) {
4042
+ const start = i;
4043
+ i++;
4044
+ while (i < input.length && /[a-zA-Z0-9_.]/.test(input[i])) {
4045
+ i++;
4046
+ }
4047
+ const text2 = input.slice(start, i);
4048
+ rawTokens.push({
4049
+ type: KEYWORDS3[text2] ?? "IDENT",
4050
+ text: text2,
4051
+ start,
4052
+ end: i
4053
+ });
4054
+ continue;
4055
+ }
4056
+ if (i + 1 < input.length) {
4057
+ const twoChar = input.slice(i, i + 2);
4058
+ if (twoChar === "==") {
4059
+ rawTokens.push({ type: "EQ", text: twoChar, start: i, end: i + 2 });
4060
+ i += 2;
4061
+ continue;
4062
+ }
4063
+ if (twoChar === "!=") {
4064
+ rawTokens.push({ type: "NEQ", text: twoChar, start: i, end: i + 2 });
4065
+ i += 2;
4066
+ continue;
4067
+ }
4068
+ if (twoChar === "<=") {
4069
+ rawTokens.push({ type: "LTE", text: twoChar, start: i, end: i + 2 });
4070
+ i += 2;
4071
+ continue;
4072
+ }
4073
+ if (twoChar === ">=") {
4074
+ rawTokens.push({ type: "GTE", text: twoChar, start: i, end: i + 2 });
4075
+ i += 2;
4076
+ continue;
4077
+ }
4078
+ }
4079
+ if (char === "<") {
4080
+ rawTokens.push({ type: "LT", text: char, start: i, end: i + 1 });
4081
+ i++;
4082
+ continue;
4083
+ }
4084
+ if (char === ">") {
4085
+ rawTokens.push({ type: "GT", text: char, start: i, end: i + 1 });
4086
+ i++;
4087
+ continue;
4088
+ }
4089
+ if (char === "(") {
4090
+ rawTokens.push({ type: "LPAREN", text: char, start: i, end: i + 1 });
4091
+ i++;
4092
+ continue;
4093
+ }
4094
+ if (char === ")") {
4095
+ rawTokens.push({ type: "RPAREN", text: char, start: i, end: i + 1 });
4096
+ i++;
4097
+ continue;
4098
+ }
4099
+ return finalizeTokens(input, rawTokens);
4100
+ }
4101
+ return finalizeTokens(input, rawTokens);
4102
+ }
4103
+
3790
4104
  // src/jinja/evaluateNodes.ts
3791
4105
  function evaluateJinjaNodes(doc2, variables) {
3792
4106
  const evaluated = map(doc2, (node) => {
@@ -4296,6 +4610,140 @@ function findDuplicateJumpPoints(doc2) {
4296
4610
  return Array.from(duplicates);
4297
4611
  }
4298
4612
 
4613
+ // src/tree/documentTree.ts
4614
+ var MAX_DEPTH6 = 128;
4615
+ function extractInlineItems(content, blockIndex) {
4616
+ const items = [];
4617
+ for (const node of content) {
4618
+ if (node.type === "jumpPoint") {
4619
+ items.push({
4620
+ type: "jumpPoint",
4621
+ label: node.id,
4622
+ blockIndex,
4623
+ children: []
4624
+ });
4625
+ } else if (node.type === "resourceTag") {
4626
+ const isEndAction = node.tagType === "handoff";
4627
+ items.push({
4628
+ type: isEndAction ? "endAction" : "resourceTag",
4629
+ label: isEndAction ? `End ${node.text}` : node.text,
4630
+ blockIndex,
4631
+ children: [],
4632
+ meta: { tagType: node.tagType, resourceId: node.resourceId }
4633
+ });
4634
+ }
4635
+ }
4636
+ return items;
4637
+ }
4638
+ function extractBlockItems(blocks, startIndex, depth) {
4639
+ if (depth > MAX_DEPTH6) return [];
4640
+ const items = [];
4641
+ for (let i = 0; i < blocks.length; i++) {
4642
+ const block = blocks[i];
4643
+ const blockIndex = startIndex + i;
4644
+ switch (block.type) {
4645
+ case "heading": {
4646
+ const label = textContent(block) || `Heading ${block.level}`;
4647
+ const inlineItems = extractInlineItems(block.content, blockIndex);
4648
+ items.push({
4649
+ type: "heading",
4650
+ label,
4651
+ blockIndex,
4652
+ children: inlineItems,
4653
+ meta: { level: block.level }
4654
+ });
4655
+ break;
4656
+ }
4657
+ case "paragraph": {
4658
+ const inlineItems = extractInlineItems(block.content, blockIndex);
4659
+ items.push(...inlineItems);
4660
+ break;
4661
+ }
4662
+ case "jinjaIfBlock": {
4663
+ const branches = block.branches.map((branch) => {
4664
+ const branchLabel = branch.branchType === "else" ? "ELSE" : `${branch.branchType.toUpperCase()} ${branch.condition || ""}`.trim();
4665
+ const branchChildren = extractBlockItems(branch.content, blockIndex, depth + 1);
4666
+ return {
4667
+ type: "jinjaBranch",
4668
+ label: branchLabel,
4669
+ blockIndex,
4670
+ children: branchChildren
4671
+ };
4672
+ });
4673
+ items.push({
4674
+ type: "jinjaIf",
4675
+ label: "Jinja if",
4676
+ blockIndex,
4677
+ children: branches
4678
+ });
4679
+ break;
4680
+ }
4681
+ case "noteBlock": {
4682
+ const noteContent = textContent(block);
4683
+ items.push({
4684
+ type: "noteBlock",
4685
+ label: noteContent ? `Note: ${noteContent.slice(0, 30)}` : "Note",
4686
+ blockIndex,
4687
+ children: []
4688
+ });
4689
+ break;
4690
+ }
4691
+ case "bulletList":
4692
+ case "orderedList": {
4693
+ for (const li of block.content) {
4694
+ const childItems = extractBlockItems(li.content, blockIndex, depth + 1);
4695
+ items.push(...childItems);
4696
+ }
4697
+ break;
4698
+ }
4699
+ case "blockquote": {
4700
+ const childItems = extractBlockItems(block.content, blockIndex, depth + 1);
4701
+ items.push(...childItems);
4702
+ break;
4703
+ }
4704
+ default:
4705
+ break;
4706
+ }
4707
+ }
4708
+ return items;
4709
+ }
4710
+ function buildDocumentTree(doc2) {
4711
+ const items = extractBlockItems(doc2.content, 0, 0);
4712
+ try {
4713
+ const structures = analyzeJinjaBlocks(doc2);
4714
+ if (structures.length > 0) {
4715
+ const hasStructuredJinja = items.some((n) => n.type === "jinjaIf");
4716
+ if (!hasStructuredJinja) {
4717
+ for (const s of structures) {
4718
+ const branches = s.branches.map((b) => ({
4719
+ type: "jinjaBranch",
4720
+ label: b.type === "else" ? "ELSE" : `${b.type.toUpperCase()} ${b.condition || ""}`.trim(),
4721
+ blockIndex: b.tagBlockIndex,
4722
+ children: []
4723
+ }));
4724
+ const jinjaNode = {
4725
+ type: "jinjaIf",
4726
+ label: "Jinja if",
4727
+ blockIndex: s.ifTagBlockIndex,
4728
+ children: branches
4729
+ };
4730
+ let inserted = false;
4731
+ for (let i = 0; i < items.length; i++) {
4732
+ if (items[i].blockIndex > s.ifTagBlockIndex) {
4733
+ items.splice(i, 0, jinjaNode);
4734
+ inserted = true;
4735
+ break;
4736
+ }
4737
+ }
4738
+ if (!inserted) items.push(jinjaNode);
4739
+ }
4740
+ }
4741
+ }
4742
+ } catch {
4743
+ }
4744
+ return items;
4745
+ }
4746
+
4299
4747
  // src/tree/generator.ts
4300
4748
  var nodeIdCounter = 0;
4301
4749
  function generateNodeId() {
@@ -5192,6 +5640,20 @@ function findMatchingJinjaBranch(tree, containerNode, context) {
5192
5640
 
5193
5641
  // src/actionbookToAST.ts
5194
5642
  var EMPTY_DOC = { type: "doc", content: [{ type: "paragraph", content: [] }] };
5643
+ var JINJA_BLOCK_TAG_RE = /^\s*\{%\s*(if|elif|else|endif)\s/m;
5644
+ function needsJinjaReparse(doc2, instruction) {
5645
+ if (!instruction || !JINJA_BLOCK_TAG_RE.test(instruction)) return false;
5646
+ function hasJinjaBlock(nodes) {
5647
+ for (const node of nodes) {
5648
+ if (node.type === "jinjaIfBlock") return true;
5649
+ if ("content" in node && Array.isArray(node.content)) {
5650
+ if (hasJinjaBlock(node.content)) return true;
5651
+ }
5652
+ }
5653
+ return false;
5654
+ }
5655
+ return !hasJinjaBlock(doc2.content);
5656
+ }
5195
5657
  var ActionbookConversionError = class extends Error {
5196
5658
  constructor(message) {
5197
5659
  super(message);
@@ -5221,6 +5683,9 @@ function actionbookToAST(manual) {
5221
5683
  if (errors2.length > 0) {
5222
5684
  throw new ActionbookConversionError(`Schema validation failed: ${errors2[0].message}`);
5223
5685
  }
5686
+ if (v2.instruction && needsJinjaReparse(doc3, v2.instruction)) {
5687
+ return parseMarkdown(v2.instruction, { jinjaNodes: true });
5688
+ }
5224
5689
  return doc3;
5225
5690
  }
5226
5691
  const doc2 = fromProseMirrorJSON(obj);
@@ -5228,10 +5693,13 @@ function actionbookToAST(manual) {
5228
5693
  if (errors.length > 0) {
5229
5694
  throw new ActionbookConversionError(`Schema validation after PM conversion failed: ${errors[0].message}`);
5230
5695
  }
5696
+ if (v2.instruction && needsJinjaReparse(doc2, v2.instruction)) {
5697
+ return parseMarkdown(v2.instruction, { jinjaNodes: true });
5698
+ }
5231
5699
  return doc2;
5232
5700
  }
5233
5701
  if (v2.instruction) {
5234
- return parseMarkdown(v2.instruction);
5702
+ return parseMarkdown(v2.instruction, { jinjaNodes: true });
5235
5703
  }
5236
5704
  return EMPTY_DOC;
5237
5705
  }
@@ -5247,6 +5715,7 @@ export {
5247
5715
  applyTransaction,
5248
5716
  blockquote,
5249
5717
  bold,
5718
+ buildDocumentTree,
5250
5719
  bulletList,
5251
5720
  chunkByHeading,
5252
5721
  code,
@@ -5287,6 +5756,7 @@ export {
5287
5756
  listItem,
5288
5757
  map,
5289
5758
  nodeAtPath,
5759
+ noteBlock,
5290
5760
  orderedList,
5291
5761
  paragraph,
5292
5762
  parentPath,
@@ -5309,6 +5779,7 @@ export {
5309
5779
  textContent,
5310
5780
  toMdast,
5311
5781
  todoItem,
5782
+ tokenizeCondition,
5312
5783
  underline,
5313
5784
  validate,
5314
5785
  visit