@openclaw/line 2026.7.2-beta.3 → 2026.7.2-beta.5

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.
Files changed (112) hide show
  1. package/README.md +1 -1
  2. package/dist/{accounts-BUBOoW5Q.js → accounts-CSH5sk8U.js} +9 -11
  3. package/dist/api.js +4 -3
  4. package/dist/{card-command-DQ-epsgt.js → card-command-BEMIm8D8.js} +1 -1
  5. package/dist/{channel-uT5RTtpi.js → channel-CTaWrzfl.js} +15 -11
  6. package/dist/channel-plugin-api.js +1 -1
  7. package/dist/channel.runtime-D4Paci2y.js +4 -0
  8. package/dist/contract-api.js +1 -1
  9. package/dist/{flex-templates-K1lS9i8L.js → flex-templates-DVy_wog4.js} +318 -107
  10. package/dist/index.js +1 -1
  11. package/dist/{markdown-to-line-D1vapcdP.js → markdown-to-line-Dn00k1bq.js} +239 -229
  12. package/dist/{monitor-Bk-lpoOZ.js → monitor-DzFU5UPV.js} +317 -323
  13. package/dist/monitor.runtime-B8xjVqev.js +2 -0
  14. package/dist/outbound.runtime-eHl7NNHu.js +2 -0
  15. package/dist/{probe-BslD77tJ.js → probe-CM03wo6r.js} +7 -9
  16. package/dist/probe.runtime-D13ZtXsd.js +2 -0
  17. package/dist/quick-reply-fallback-D86FwAfr.js +56 -0
  18. package/dist/{reply-payload-transform-r9sa-TYT.js → reply-payload-transform-BRM2fRvK.js} +3 -164
  19. package/dist/runtime-api.js +9 -8
  20. package/dist/send-receipt-CY2ey8XH.js +107 -0
  21. package/dist/setup-api.js +1 -1
  22. package/dist/{setup-surface-BEt4mazl.js → setup-surface-BHQOSD8R.js} +107 -67
  23. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupCreateRoute.d.ts.map +1 -1
  24. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupFailedType.d.ts.map +1 -1
  25. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupJobFailedType.d.ts.map +1 -1
  26. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupJobStatus.d.ts.map +1 -1
  27. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupStatus.d.ts.map +1 -1
  28. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexBoxBorderWidth.d.ts.map +1 -1
  29. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexBoxCornerRadius.d.ts.map +1 -1
  30. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexBubble.d.ts.map +1 -1
  31. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexIconSize.d.ts.map +1 -1
  32. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexSpanSize.d.ts.map +1 -1
  33. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexTextFontSize.d.ts.map +1 -1
  34. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/numberOfMessagesResponse.d.ts.map +1 -1
  35. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/postbackAction.d.ts.map +1 -1
  36. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/subscriptionPeriodDemographic.d.ts.map +1 -1
  37. package/node_modules/@line/bot-sdk/dist/cjs/version.d.ts +1 -1
  38. package/node_modules/@line/bot-sdk/dist/cjs/version.js +1 -1
  39. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/event.d.ts +2 -1
  40. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/event.d.ts.map +1 -1
  41. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.d.ts +24 -0
  42. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.d.ts.map +1 -0
  43. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.js +14 -0
  44. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.js.map +1 -0
  45. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.d.ts +1 -0
  46. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.d.ts.map +1 -1
  47. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.js +1 -0
  48. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.js.map +1 -1
  49. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupCreateRoute.d.ts.map +1 -1
  50. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupFailedType.d.ts.map +1 -1
  51. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupJobFailedType.d.ts.map +1 -1
  52. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupJobStatus.d.ts.map +1 -1
  53. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupStatus.d.ts.map +1 -1
  54. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexBoxBorderWidth.d.ts.map +1 -1
  55. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexBoxCornerRadius.d.ts.map +1 -1
  56. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexBubble.d.ts.map +1 -1
  57. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexIconSize.d.ts.map +1 -1
  58. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexSpanSize.d.ts.map +1 -1
  59. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexTextFontSize.d.ts.map +1 -1
  60. package/node_modules/@line/bot-sdk/dist/messaging-api/model/numberOfMessagesResponse.d.ts.map +1 -1
  61. package/node_modules/@line/bot-sdk/dist/messaging-api/model/postbackAction.d.ts.map +1 -1
  62. package/node_modules/@line/bot-sdk/dist/messaging-api/model/subscriptionPeriodDemographic.d.ts.map +1 -1
  63. package/node_modules/@line/bot-sdk/dist/version.d.ts +1 -1
  64. package/node_modules/@line/bot-sdk/dist/version.js +1 -1
  65. package/node_modules/@line/bot-sdk/dist/webhook/model/event.d.ts +2 -1
  66. package/node_modules/@line/bot-sdk/dist/webhook/model/event.d.ts.map +1 -1
  67. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.d.ts +24 -0
  68. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.d.ts.map +1 -0
  69. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.js +13 -0
  70. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.js.map +1 -0
  71. package/node_modules/@line/bot-sdk/dist/webhook/model/models.d.ts +1 -0
  72. package/node_modules/@line/bot-sdk/dist/webhook/model/models.d.ts.map +1 -1
  73. package/node_modules/@line/bot-sdk/dist/webhook/model/models.js +1 -0
  74. package/node_modules/@line/bot-sdk/dist/webhook/model/models.js.map +1 -1
  75. package/node_modules/@line/bot-sdk/lib/insight/model/getNumberOfFollowersResponse.ts +1 -1
  76. package/node_modules/@line/bot-sdk/lib/liff/model/liffScope.ts +1 -1
  77. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupCreateRoute.ts +1 -4
  78. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupFailedType.ts +1 -2
  79. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupJobFailedType.ts +1 -2
  80. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupJobStatus.ts +1 -4
  81. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupStatus.ts +1 -6
  82. package/node_modules/@line/bot-sdk/lib/messaging-api/model/botInfoResponse.ts +1 -1
  83. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexBoxBorderWidth.ts +1 -6
  84. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexBoxCornerRadius.ts +1 -7
  85. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexBubble.ts +1 -7
  86. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexIconSize.ts +1 -10
  87. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexSpanSize.ts +1 -10
  88. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexTextFontSize.ts +1 -10
  89. package/node_modules/@line/bot-sdk/lib/messaging-api/model/getAggregationUnitNameListResponse.ts +1 -1
  90. package/node_modules/@line/bot-sdk/lib/messaging-api/model/getFollowersResponse.ts +1 -1
  91. package/node_modules/@line/bot-sdk/lib/messaging-api/model/getJoinedMembershipUsersResponse.ts +1 -1
  92. package/node_modules/@line/bot-sdk/lib/messaging-api/model/issueLinkTokenResponse.ts +1 -1
  93. package/node_modules/@line/bot-sdk/lib/messaging-api/model/numberOfMessagesResponse.ts +2 -5
  94. package/node_modules/@line/bot-sdk/lib/messaging-api/model/postbackAction.ts +1 -4
  95. package/node_modules/@line/bot-sdk/lib/messaging-api/model/subscriptionPeriodDemographic.ts +1 -5
  96. package/node_modules/@line/bot-sdk/lib/version.ts +1 -1
  97. package/node_modules/@line/bot-sdk/lib/webhook/.openapi-generator/FILES +1 -0
  98. package/node_modules/@line/bot-sdk/lib/webhook/model/event.ts +2 -0
  99. package/node_modules/@line/bot-sdk/lib/webhook/model/messageEditedEvent.ts +32 -0
  100. package/node_modules/@line/bot-sdk/lib/webhook/model/models.ts +1 -0
  101. package/node_modules/@line/bot-sdk/lib/webhook/model/stickerMessageContent.ts +1 -1
  102. package/node_modules/@line/bot-sdk/package.json +3 -3
  103. package/node_modules/@types/node/README.md +1 -1
  104. package/node_modules/@types/node/fs/promises.d.ts +16 -5
  105. package/node_modules/@types/node/package.json +3 -3
  106. package/openclaw.plugin.json +0 -18
  107. package/package.json +71 -7
  108. package/dist/channel.runtime-BqNglqnr.js +0 -4
  109. package/dist/monitor.runtime-CHlqxGVL.js +0 -2
  110. package/dist/outbound.runtime-ByZ0Dsvu.js +0 -2
  111. package/dist/probe.runtime-CRhVGqwl.js +0 -2
  112. package/npm-shrinkwrap.json +0 -60
@@ -1,14 +1,25 @@
1
- import { i as resolveLineAccount } from "./accounts-BUBOoW5Q.js";
2
- import { c as validateLineMediaUrl, r as createLineSendReceipt } from "./reply-payload-transform-r9sa-TYT.js";
3
- import { c as uriAction, d as createReceiptCard, o as messageAction, s as postbackAction, t as toFlexMessage } from "./flex-templates-K1lS9i8L.js";
4
- import { stripMarkdown, stripMarkdown as stripMarkdown$1 } from "openclaw/plugin-sdk/text-chunking";
1
+ import { i as resolveLineAccount } from "./accounts-CSH5sk8U.js";
2
+ import { a as validateLineMediaUrl, t as createLineSendReceipt } from "./send-receipt-CY2ey8XH.js";
3
+ import { _ as postbackAction, g as normalizeLineMessageActions, h as normalizeLineAction, m as messageAction, o as createReceiptCard, v as uriAction } from "./flex-templates-DVy_wog4.js";
4
+ import { markdownToIRWithMeta, stripMarkdown, stripMarkdown as stripMarkdown$1 } from "openclaw/plugin-sdk/text-chunking";
5
5
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
6
- import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
7
6
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
8
7
  import { messagingApi } from "@line/bot-sdk";
9
8
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
10
9
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
11
10
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
11
+ //#region extensions/line/src/flex-templates/message.ts
12
+ /**
13
+ * Wrap a FlexContainer in a FlexMessage
14
+ */
15
+ function toFlexMessage(altText, contents) {
16
+ return {
17
+ type: "flex",
18
+ altText,
19
+ contents
20
+ };
21
+ }
22
+ //#endregion
12
23
  //#region extensions/line/src/template-messages.ts
13
24
  const COMPACT_TEMPLATE_TEXT_LIMIT = 60;
14
25
  const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
@@ -43,6 +54,19 @@ function formatProductCarouselText(description, price) {
43
54
  const descriptionText = truncateTemplateText(description, Math.max(0, COMPACT_TEMPLATE_TEXT_LIMIT - priceText.length - 1));
44
55
  return descriptionText ? `${descriptionText}\n${priceText}` : priceText;
45
56
  }
57
+ function normalizeCarouselColumnActions(column) {
58
+ return {
59
+ ...column,
60
+ actions: column.actions.map((action) => normalizeLineAction(action)),
61
+ defaultAction: column.defaultAction === void 0 ? void 0 : normalizeLineAction(column.defaultAction)
62
+ };
63
+ }
64
+ function normalizeImageCarouselColumnAction(column) {
65
+ return {
66
+ ...column,
67
+ action: normalizeLineAction(column.action, 12)
68
+ };
69
+ }
46
70
  /**
47
71
  * Create a confirm template (yes/no style dialog)
48
72
  */
@@ -50,7 +74,7 @@ function createConfirmTemplate(text, confirmAction, cancelAction, altText) {
50
74
  const template = {
51
75
  type: "confirm",
52
76
  text: truncateTemplateText(text, 240),
53
- actions: [confirmAction, cancelAction]
77
+ actions: [normalizeLineAction(confirmAction), normalizeLineAction(cancelAction)]
54
78
  };
55
79
  return {
56
80
  type: "template",
@@ -72,12 +96,12 @@ function createButtonTemplate(title, text, actions, options) {
72
96
  type: "buttons",
73
97
  ...normalizedTitle ? { title: truncateTemplateText(normalizedTitle, 40) } : {},
74
98
  text: truncateTemplateText(text, textLimit),
75
- actions: actions.slice(0, 4),
99
+ actions: actions.slice(0, 4).map((action) => normalizeLineAction(action)),
76
100
  thumbnailImageUrl: options?.thumbnailImageUrl,
77
101
  imageAspectRatio: options?.imageAspectRatio ?? "rectangle",
78
102
  imageSize: options?.imageSize ?? "cover",
79
103
  imageBackgroundColor: options?.imageBackgroundColor,
80
- defaultAction: options?.defaultAction
104
+ defaultAction: options?.defaultAction === void 0 ? void 0 : normalizeLineAction(options.defaultAction)
81
105
  };
82
106
  return {
83
107
  type: "template",
@@ -91,7 +115,7 @@ function createButtonTemplate(title, text, actions, options) {
91
115
  function createTemplateCarousel(columns, options) {
92
116
  const template = {
93
117
  type: "carousel",
94
- columns: columns.slice(0, 10),
118
+ columns: columns.slice(0, 10).map(normalizeCarouselColumnActions),
95
119
  imageAspectRatio: options?.imageAspectRatio ?? "rectangle",
96
120
  imageSize: options?.imageSize ?? "cover"
97
121
  };
@@ -112,10 +136,10 @@ function createCarouselColumn(params) {
112
136
  return {
113
137
  title: truncateOptionalTemplateText(params.title, 40),
114
138
  text: truncateTemplateText(params.text, textLimit),
115
- actions: params.actions.slice(0, 3),
139
+ actions: params.actions.slice(0, 3).map((action) => normalizeLineAction(action)),
116
140
  thumbnailImageUrl: params.thumbnailImageUrl,
117
141
  imageBackgroundColor: params.imageBackgroundColor,
118
- defaultAction: params.defaultAction
142
+ defaultAction: params.defaultAction === void 0 ? void 0 : normalizeLineAction(params.defaultAction)
119
143
  };
120
144
  }
121
145
  /**
@@ -124,7 +148,7 @@ function createCarouselColumn(params) {
124
148
  function createImageCarousel(columns, altText) {
125
149
  const template = {
126
150
  type: "image_carousel",
127
- columns: columns.slice(0, 10)
151
+ columns: columns.slice(0, 10).map(normalizeImageCarouselColumnAction)
128
152
  };
129
153
  return {
130
154
  type: "template",
@@ -138,7 +162,7 @@ function createImageCarousel(columns, altText) {
138
162
  function createImageCarouselColumn(imageUrl, action) {
139
163
  return {
140
164
  imageUrl,
141
- action
165
+ action: normalizeLineAction(action, 12)
142
166
  };
143
167
  }
144
168
  /**
@@ -320,9 +344,10 @@ function resolveLineReceiptKind(messages) {
320
344
  async function pushLineMessages(to, messages, opts, behavior = {}) {
321
345
  if (messages.length === 0) throw new Error("Message must be non-empty for LINE sends");
322
346
  const { account, client, chatId } = createLinePushContext(to, opts);
347
+ const normalizedMessages = messages.map(normalizeLineMessageActions);
323
348
  const pushRequest = client.pushMessage({
324
349
  to: chatId,
325
- messages
350
+ messages: normalizedMessages
326
351
  });
327
352
  if (behavior.errorContext) await pushRequest.catch((err) => {
328
353
  logLineHttpError(err, behavior.errorContext);
@@ -344,9 +369,10 @@ async function pushLineMessages(to, messages, opts, behavior = {}) {
344
369
  }
345
370
  async function replyLineMessages(replyToken, messages, opts, behavior = {}) {
346
371
  const { account, client } = createLineMessagingClient(opts);
372
+ const normalizedMessages = messages.map(normalizeLineMessageActions);
347
373
  await client.replyMessage({
348
374
  replyToken,
349
- messages
375
+ messages: normalizedMessages
350
376
  });
351
377
  recordLineOutboundActivity(account.accountId);
352
378
  if (opts.verbose) logVerbose(behavior.verboseMessage?.(messages.length) ?? `line: replied with ${messages.length} messages`);
@@ -493,88 +519,184 @@ async function getUserDisplayName(userId, opts) {
493
519
  }
494
520
  //#endregion
495
521
  //#region extensions/line/src/markdown-to-line.ts
496
- /**
497
- * Regex patterns for markdown detection
498
- */
499
- const MARKDOWN_TABLE_REGEX = /^\|(.+)\|[\r\n]+\|[-:\s|]+\|[\r\n]+((?:\|.+\|[\r\n]*)+)/gm;
500
- const MARKDOWN_CODE_BLOCK_REGEX = /```(\w*)\n([\s\S]*?)```/g;
501
- const MARKDOWN_LINK_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g;
502
- /**
503
- * Detect and extract markdown tables from text
504
- */
505
- function extractMarkdownTables(text) {
506
- const tables = [];
507
- let textWithoutTables = text;
508
- MARKDOWN_TABLE_REGEX.lastIndex = 0;
509
- let match;
510
- const matches = [];
511
- while ((match = MARKDOWN_TABLE_REGEX.exec(text)) !== null) {
512
- const fullMatch = expectDefined(match[0], "Markdown table match");
513
- const headerLine = expectDefined(match[1], "Markdown table header capture");
514
- const bodyLines = expectDefined(match[2], "Markdown table body capture");
515
- const headers = parseTableRow(headerLine);
516
- const rows = bodyLines.trim().split(/[\r\n]+/).filter((line) => line.trim()).map(parseTableRow);
517
- if (headers.length > 0 && rows.length > 0) matches.push({
518
- fullMatch,
519
- table: {
520
- headers,
521
- rows
522
- }
522
+ const LINE_MARKDOWN_OPTIONS = {
523
+ assistantTranscriptRoleHeaders: true,
524
+ autolink: false,
525
+ blockquotePrefix: "",
526
+ headingStyle: "none",
527
+ horizontalRuleText: "",
528
+ linkify: false,
529
+ preserveSourceBlockSpacing: true
530
+ };
531
+ const TRANSCRIPT_ROLE_PREFIX = "[assistant-authored transcript] ";
532
+ function parseLineMarkdown(text, tableMode = "block") {
533
+ return markdownToIRWithMeta(text, {
534
+ ...LINE_MARKDOWN_OPTIONS,
535
+ tableMode
536
+ });
537
+ }
538
+ function toMarkdownTable(table) {
539
+ return {
540
+ headers: table.headers,
541
+ rows: table.rows,
542
+ headerCells: table.headerCells,
543
+ rowCells: table.rowCells
544
+ };
545
+ }
546
+ function codeBlockSpans(ir) {
547
+ return ir.styles.filter((span) => span.style === "code_block");
548
+ }
549
+ function toCodeBlock(ir, span) {
550
+ return {
551
+ ...span.language ? { language: span.language } : {},
552
+ code: ir.text.slice(span.start, span.end).trim()
553
+ };
554
+ }
555
+ function rangesOverlap(left, right) {
556
+ return left.start < right.end && right.start < left.end;
557
+ }
558
+ function projectPlainText(ir, omitted = []) {
559
+ const insertions = [];
560
+ for (const link of ir.links) {
561
+ if (omitted.some((range) => rangesOverlap(range, link))) continue;
562
+ const href = link.href.trim();
563
+ const label = ir.text.slice(link.start, link.end).trim();
564
+ const comparableHref = href.startsWith("mailto:") ? href.slice(7) : href;
565
+ if (href && label && label !== href && label !== comparableHref) insertions.push({
566
+ position: link.end,
567
+ text: ` (${href})`
523
568
  });
524
569
  }
525
- for (const { fullMatch, table } of matches.toReversed()) {
526
- tables.unshift(table);
527
- textWithoutTables = textWithoutTables.replace(fullMatch, "");
570
+ for (const annotation of ir.annotations ?? []) if (annotation.type === "assistant_transcript_role" && !omitted.some((range) => rangesOverlap(range, annotation))) insertions.push({
571
+ position: annotation.start,
572
+ text: TRANSCRIPT_ROLE_PREFIX
573
+ });
574
+ const inlineCodeSpans = ir.styles.filter((span) => span.style === "code" && !omitted.some((range) => rangesOverlap(range, span)));
575
+ for (const span of inlineCodeSpans) if (stripMarkdown(ir.text.slice(span.start, span.end), { assistantTranscriptRoleHeaders: true }).startsWith(TRANSCRIPT_ROLE_PREFIX)) insertions.push({
576
+ position: span.start,
577
+ text: TRANSCRIPT_ROLE_PREFIX
578
+ });
579
+ insertions.sort((left, right) => left.position - right.position);
580
+ const underlineTags = [...ir.text.matchAll(/<\/?u>/gi)].map((match) => ({
581
+ start: match.index,
582
+ end: match.index + match[0].length
583
+ })).filter((tag) => !inlineCodeSpans.some((span) => rangesOverlap(tag, span)) && !omitted.some((span) => rangesOverlap(tag, span)));
584
+ const removed = [...omitted, ...underlineTags].toSorted((left, right) => left.start - right.start);
585
+ let output = "";
586
+ let cursor = 0;
587
+ let insertionIndex = 0;
588
+ const appendRange = (end) => {
589
+ while (insertionIndex < insertions.length) {
590
+ const insertion = insertions[insertionIndex];
591
+ if (!insertion || insertion.position > end) break;
592
+ if (insertion.position >= cursor) {
593
+ output += ir.text.slice(cursor, insertion.position) + insertion.text;
594
+ cursor = insertion.position;
595
+ }
596
+ insertionIndex += 1;
597
+ }
598
+ output += ir.text.slice(cursor, end);
599
+ cursor = end;
600
+ };
601
+ for (const range of removed) {
602
+ appendRange(range.start);
603
+ cursor = Math.max(cursor, range.end);
604
+ while (insertionIndex < insertions.length && (insertions[insertionIndex]?.position ?? cursor) < cursor) insertionIndex += 1;
605
+ }
606
+ appendRange(ir.text.length);
607
+ return output.trim();
608
+ }
609
+ function sameSpanStyle(left, right) {
610
+ return left.weight === right.weight && left.style === right.style && left.decoration === right.decoration;
611
+ }
612
+ function renderTableCell(cell, fallback) {
613
+ if (!cell?.text.trim()) return {
614
+ text: fallback,
615
+ hasMarkup: false
616
+ };
617
+ const codeSpans = cell.styles.filter((span) => span.style === "code");
618
+ const tags = [...cell.text.matchAll(/<\/?u>/gi)].map((match) => ({
619
+ start: match.index,
620
+ end: match.index + match[0].length,
621
+ closing: match[0][1] === "/"
622
+ })).filter((tag) => !codeSpans.some((span) => rangesOverlap(tag, span)));
623
+ const boundaries = /* @__PURE__ */ new Set([0, cell.text.length]);
624
+ for (const style of cell.styles) {
625
+ boundaries.add(style.start);
626
+ boundaries.add(style.end);
627
+ }
628
+ for (const link of cell.links) boundaries.add(link.end);
629
+ for (const tag of tags) {
630
+ boundaries.add(tag.start);
631
+ boundaries.add(tag.end);
632
+ }
633
+ const sortedBoundaries = [...boundaries].toSorted((left, right) => left - right);
634
+ const spans = [];
635
+ let underlineDepth = 0;
636
+ let hasMarkup = false;
637
+ const appendSpan = (span) => {
638
+ const previous = spans.at(-1);
639
+ if (previous && sameSpanStyle(previous, span)) previous.text = `${previous.text ?? ""}${span.text ?? ""}`;
640
+ else spans.push(span);
641
+ };
642
+ for (let index = 0; index < sortedBoundaries.length - 1; index += 1) {
643
+ const start = sortedBoundaries[index];
644
+ const end = sortedBoundaries[index + 1];
645
+ if (start === void 0 || end === void 0) continue;
646
+ const tag = tags.find((candidate) => candidate.start === start);
647
+ if (tag) {
648
+ underlineDepth += tag.closing ? -1 : 1;
649
+ hasMarkup = true;
650
+ continue;
651
+ }
652
+ const text = cell.text.slice(start, end);
653
+ if (text) {
654
+ const active = new Set(cell.styles.filter((style) => style.start <= start && style.end >= end).map((style) => style.style));
655
+ const weight = active.has("bold") ? "bold" : void 0;
656
+ const style = active.has("italic") ? "italic" : void 0;
657
+ const decoration = active.has("strikethrough") ? "line-through" : underlineDepth > 0 ? "underline" : void 0;
658
+ hasMarkup ||= weight !== void 0 || style !== void 0 || decoration !== void 0;
659
+ appendSpan({
660
+ type: "span",
661
+ text,
662
+ weight,
663
+ style,
664
+ decoration
665
+ });
666
+ }
667
+ for (const link of cell.links.filter((candidate) => candidate.end === end)) {
668
+ const href = link.href.trim();
669
+ const label = cell.text.slice(link.start, link.end).trim();
670
+ if (href && label && label !== href) {
671
+ appendSpan({
672
+ type: "span",
673
+ text: ` (${href})`
674
+ });
675
+ hasMarkup = true;
676
+ }
677
+ }
528
678
  }
529
679
  return {
530
- tables,
531
- textWithoutTables
680
+ text: spans.map((span) => span.text ?? "").join("").trim() || fallback,
681
+ ...hasMarkup ? { contents: spans } : {},
682
+ hasMarkup
532
683
  };
533
684
  }
534
- /**
535
- * Parse a single table row (pipe-separated values)
536
- */
537
- function parseTableRow(row) {
538
- return row.split("|").map((cell) => cell.trim()).filter((cell, index, arr) => {
539
- if (index === 0 && cell === "") return false;
540
- if (index === arr.length - 1 && cell === "") return false;
541
- return true;
542
- });
685
+ function plainTableCell(text) {
686
+ return parseLineMarkdown(text, "off").ir;
543
687
  }
544
- /**
545
- * Convert a markdown table to a LINE Flex Message bubble
546
- */
688
+ /** Convert a markdown table to a LINE Flex Message bubble. */
547
689
  function convertTableToFlexBubble(table) {
548
- const parseCell = (value) => {
549
- const raw = value?.trim() ?? "";
550
- if (!raw) return {
551
- text: "-",
552
- bold: false,
553
- hasMarkup: false
554
- };
555
- let hasMarkup = false;
556
- return {
557
- text: raw.replace(/\*\*(.+?)\*\*/g, (_, inner) => {
558
- hasMarkup = true;
559
- return String(inner);
560
- }).trim() || "-",
561
- bold: /^\*\*.+\*\*$/.test(raw),
562
- hasMarkup
563
- };
564
- };
565
- const headerCells = table.headers.map((header) => parseCell(header));
566
- const rowCells = table.rows.map((row) => row.map((cell) => parseCell(cell)));
690
+ const headerCells = (table.headerCells ?? table.headers.map(plainTableCell)).map((cell) => renderTableCell(cell, "-"));
691
+ const rowCells = (table.rowCells ?? table.rows.map((row) => row.map(plainTableCell))).map((row) => row.map((cell) => renderTableCell(cell, "-")));
567
692
  const hasInlineMarkup = headerCells.some((cell) => cell.hasMarkup) || rowCells.some((row) => row.some((cell) => cell.hasMarkup));
568
- if (table.headers.length === 2 && !hasInlineMarkup) {
569
- const items = rowCells.map((row) => ({
693
+ if (table.headers.length === 2 && !hasInlineMarkup) return createReceiptCard({
694
+ title: headerCells.map((cell) => cell.text).join(" / "),
695
+ items: rowCells.map((row) => ({
570
696
  name: row[0]?.text ?? "-",
571
697
  value: row[1]?.text ?? "-"
572
- }));
573
- return createReceiptCard({
574
- title: headerCells.map((cell) => cell.text).join(" / "),
575
- items
576
- });
577
- }
698
+ }))
699
+ });
578
700
  return {
579
701
  type: "bubble",
580
702
  body: {
@@ -587,6 +709,7 @@ function convertTableToFlexBubble(table) {
587
709
  contents: headerCells.map((cell) => ({
588
710
  type: "text",
589
711
  text: cell.text,
712
+ contents: cell.contents,
590
713
  weight: "bold",
591
714
  size: "sm",
592
715
  color: "#333333",
@@ -599,67 +722,32 @@ function convertTableToFlexBubble(table) {
599
722
  type: "separator",
600
723
  margin: "sm"
601
724
  },
602
- ...rowCells.slice(0, 10).map((row, rowIndex) => {
603
- return {
604
- type: "box",
605
- layout: "horizontal",
606
- contents: table.headers.map((_, colIndex) => {
607
- const cell = row[colIndex] ?? {
608
- text: "-",
609
- bold: false,
610
- hasMarkup: false
611
- };
612
- return {
613
- type: "text",
614
- text: cell.text,
615
- size: "sm",
616
- color: "#666666",
617
- flex: 1,
618
- wrap: true,
619
- weight: cell.bold ? "bold" : void 0
620
- };
621
- }),
622
- margin: rowIndex === 0 ? "md" : "sm"
623
- };
624
- })
725
+ ...rowCells.slice(0, 10).map((row, rowIndex) => ({
726
+ type: "box",
727
+ layout: "horizontal",
728
+ contents: table.headers.map((_, colIndex) => {
729
+ const cell = row[colIndex] ?? {
730
+ text: "-",
731
+ hasMarkup: false
732
+ };
733
+ return {
734
+ type: "text",
735
+ text: cell.text,
736
+ contents: cell.contents,
737
+ size: "sm",
738
+ color: "#666666",
739
+ flex: 1,
740
+ wrap: true
741
+ };
742
+ }),
743
+ margin: rowIndex === 0 ? "md" : "sm"
744
+ }))
625
745
  ],
626
746
  paddingAll: "lg"
627
747
  }
628
748
  };
629
749
  }
630
- /**
631
- * Detect and extract code blocks from text
632
- */
633
- function extractCodeBlocks(text) {
634
- const codeBlocks = [];
635
- let textWithoutCode = text;
636
- MARKDOWN_CODE_BLOCK_REGEX.lastIndex = 0;
637
- let match;
638
- const matches = [];
639
- while ((match = MARKDOWN_CODE_BLOCK_REGEX.exec(text)) !== null) {
640
- const fullMatch = expectDefined(match[0], "Markdown code block match");
641
- const language = match[1] || void 0;
642
- const code = expectDefined(match[2], "Markdown code body capture");
643
- matches.push({
644
- fullMatch,
645
- block: {
646
- language,
647
- code: code.trim()
648
- }
649
- });
650
- }
651
- for (const { fullMatch, block } of matches.toReversed()) {
652
- codeBlocks.unshift(block);
653
- textWithoutCode = textWithoutCode.replace(fullMatch, "");
654
- }
655
- return {
656
- codeBlocks,
657
- textWithoutCode
658
- };
659
- }
660
- /**
661
- * Convert a code block to a LINE Flex Message bubble
662
- */
750
+ /** Convert a code block to a LINE Flex Message bubble. */
663
751
  function convertCodeBlockToFlexBubble(block) {
664
752
  const titleText = block.language ? `Code (${block.language})` : "Code";
665
753
  const displayCode = block.code.length > 2e3 ? truncateUtf16Safe(block.code, 2e3) + "\n..." : block.code;
@@ -693,97 +781,19 @@ function convertCodeBlockToFlexBubble(block) {
693
781
  }
694
782
  };
695
783
  }
696
- /**
697
- * Extract markdown links from text
698
- */
699
- function extractLinks(text) {
700
- const links = [];
701
- MARKDOWN_LINK_REGEX.lastIndex = 0;
702
- let match;
703
- while ((match = MARKDOWN_LINK_REGEX.exec(text)) !== null) links.push({
704
- text: expectDefined(match[1], "Markdown link text capture"),
705
- url: expectDefined(match[2], "Markdown link URL capture")
706
- });
707
- return {
708
- links,
709
- textWithLinks: text.replace(MARKDOWN_LINK_REGEX, "$1")
710
- };
711
- }
712
- /**
713
- * Create a Flex Message with tappable link buttons
714
- */
715
- function convertLinksToFlexBubble(links) {
716
- return {
717
- type: "bubble",
718
- body: {
719
- type: "box",
720
- layout: "vertical",
721
- contents: [{
722
- type: "text",
723
- text: "Links",
724
- weight: "bold",
725
- size: "md",
726
- color: "#333333"
727
- }],
728
- paddingAll: "lg",
729
- paddingBottom: "sm"
730
- },
731
- footer: {
732
- type: "box",
733
- layout: "vertical",
734
- contents: links.slice(0, 4).map((link, index) => ({
735
- type: "button",
736
- action: uriAction(link.text, link.url),
737
- style: index === 0 ? "primary" : "secondary",
738
- margin: index > 0 ? "sm" : void 0
739
- })),
740
- paddingAll: "md"
741
- }
742
- };
743
- }
744
- /**
745
- * Main function: Process text for LINE output
746
- * - Extracts tables → Flex Messages
747
- * - Extracts code blocks → Flex Messages
748
- * - Strips remaining markdown
749
- * - Returns processed text + Flex Messages
750
- */
784
+ /** Parse once, route existing block surfaces to Flex, and project the remainder as plain text. */
751
785
  function processLineMessage(text) {
752
- const flexMessages = [];
753
- let processedText = text;
754
- const { tables, textWithoutTables } = extractMarkdownTables(processedText);
755
- processedText = textWithoutTables;
756
- for (const table of tables) {
757
- const bubble = convertTableToFlexBubble(table);
758
- flexMessages.push(toFlexMessage("Table", bubble));
759
- }
760
- const { codeBlocks, textWithoutCode } = extractCodeBlocks(processedText);
761
- processedText = textWithoutCode;
762
- for (const block of codeBlocks) {
763
- const bubble = convertCodeBlockToFlexBubble(block);
764
- flexMessages.push(toFlexMessage("Code", bubble));
765
- }
766
- const { textWithLinks } = extractLinks(processedText);
767
- processedText = textWithLinks;
768
- processedText = stripMarkdown(processedText, { assistantTranscriptRoleHeaders: true });
786
+ const { ir, tables } = parseLineMarkdown(text);
787
+ const codeSpans = codeBlockSpans(ir);
769
788
  return {
770
- text: processedText,
771
- flexMessages
789
+ text: projectPlainText(ir, codeSpans),
790
+ flexMessages: [...tables.map((table) => toFlexMessage("Table", convertTableToFlexBubble(toMarkdownTable(table)))), ...codeSpans.map((span) => toFlexMessage("Code", convertCodeBlockToFlexBubble(toCodeBlock(ir, span))))]
772
791
  };
773
792
  }
774
- /**
775
- * Check if text contains markdown that needs conversion
776
- */
793
+ /** Check if text contains markdown that needs conversion. */
777
794
  function hasMarkdownToConvert(text) {
778
- MARKDOWN_TABLE_REGEX.lastIndex = 0;
779
- if (MARKDOWN_TABLE_REGEX.test(text)) return true;
780
- MARKDOWN_CODE_BLOCK_REGEX.lastIndex = 0;
781
- if (MARKDOWN_CODE_BLOCK_REGEX.test(text)) return true;
782
- if (/\*\*[^*]+\*\*/.test(text)) return true;
783
- if (/~~[^~]+~~/.test(text)) return true;
784
- if (/^#{1,6}\s+/m.test(text)) return true;
785
- if (/^>\s+/m.test(text)) return true;
786
- return false;
795
+ const { ir, tables } = parseLineMarkdown(text);
796
+ return tables.length > 0 || ir.styles.length > 0 || ir.links.length > 0 || /<\/?u>/i.test(ir.text) || ir.text !== text.trimEnd();
787
797
  }
788
798
  //#endregion
789
- export { buildTemplateMessageFromPayload as A, createYesNoConfirm as B, pushMessagesLine as C, sendMessageLine as D, replyMessageLine as E, createImageCarousel as F, createImageCarouselColumn as I, createLinkMenu as L, createButtonTemplate as M, createCarouselColumn as N, showLoadingAnimation as O, createConfirmTemplate as P, createProductCarousel as R, pushMessageLine as S, pushTextMessageWithQuickReplies as T, getUserDisplayName as _, extractLinks as a, pushImageMessage as b, processLineMessage as c, createFlexMessage as d, createImageMessage as f, createVideoMessage as g, createTextMessageWithQuickReplies as h, extractCodeBlocks as i, createButtonMenu as j, resolveLineChannelAccessToken as k, stripMarkdown$1 as l, createQuickReplyItems as m, convertLinksToFlexBubble as n, extractMarkdownTables as o, createLocationMessage as p, convertTableToFlexBubble as r, hasMarkdownToConvert as s, convertCodeBlockToFlexBubble as t, createAudioMessage as u, getUserProfile as v, pushTemplateMessage as w, pushLocationMessage as x, pushFlexMessage as y, createTemplateCarousel as z };
799
+ export { createConfirmTemplate as A, sendMessageLine as C, createButtonMenu as D, buildTemplateMessageFromPayload as E, createTemplateCarousel as F, createYesNoConfirm as I, toFlexMessage as L, createImageCarouselColumn as M, createLinkMenu as N, createButtonTemplate as O, createProductCarousel as P, replyMessageLine as S, resolveLineChannelAccessToken as T, pushLocationMessage as _, stripMarkdown$1 as a, pushTemplateMessage as b, createImageMessage as c, createTextMessageWithQuickReplies as d, createVideoMessage as f, pushImageMessage as g, pushFlexMessage as h, processLineMessage as i, createImageCarousel as j, createCarouselColumn as k, createLocationMessage as l, getUserProfile as m, convertTableToFlexBubble as n, createAudioMessage as o, getUserDisplayName as p, hasMarkdownToConvert as r, createFlexMessage as s, convertCodeBlockToFlexBubble as t, createQuickReplyItems as u, pushMessageLine as v, showLoadingAnimation as w, pushTextMessageWithQuickReplies as x, pushMessagesLine as y };