@openclaw/line 2026.7.2-beta.2 → 2026.7.2-beta.4

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 (109) hide show
  1. package/dist/{accounts-DJVOv1JI.js → accounts-BUBOoW5Q.js} +95 -33
  2. package/dist/api.js +4 -3
  3. package/dist/basic-cards-Rb-Ow7LN.js +307 -0
  4. package/dist/{card-command-DQ-epsgt.js → card-command-Ch0TwcDW.js} +2 -1
  5. package/dist/{channel-BHR-GoJ0.js → channel-CNcv74n2.js} +22 -10
  6. package/dist/channel-plugin-api.js +1 -1
  7. package/dist/channel.runtime-BBCZvRa_.js +4 -0
  8. package/dist/contract-api.js +1 -1
  9. package/dist/{flex-templates-K1lS9i8L.js → flex-templates-KiKaVYgi.js} +1 -318
  10. package/dist/index.js +1 -1
  11. package/dist/{markdown-to-line-XceqeKRP.js → markdown-to-line-DWIdzBfy.js} +224 -220
  12. package/dist/{monitor-Db2rZsSX.js → monitor-BinAXvIY.js} +454 -288
  13. package/dist/monitor.runtime-DKj4FHkA.js +2 -0
  14. package/dist/outbound.runtime-DRyoVa81.js +2 -0
  15. package/dist/quick-reply-fallback-D86FwAfr.js +56 -0
  16. package/dist/{reply-payload-transform-r9sa-TYT.js → reply-payload-transform-d57WYHFm.js} +3 -164
  17. package/dist/runtime-api.js +10 -24
  18. package/dist/send-receipt-CY2ey8XH.js +107 -0
  19. package/dist/setup-api.js +1 -1
  20. package/dist/{setup-surface-B7W6pwn3.js → setup-surface-Bili5gWy.js} +64 -5
  21. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupCreateRoute.d.ts.map +1 -1
  22. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupFailedType.d.ts.map +1 -1
  23. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupJobFailedType.d.ts.map +1 -1
  24. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupJobStatus.d.ts.map +1 -1
  25. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupStatus.d.ts.map +1 -1
  26. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexBoxBorderWidth.d.ts.map +1 -1
  27. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexBoxCornerRadius.d.ts.map +1 -1
  28. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexBubble.d.ts.map +1 -1
  29. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexIconSize.d.ts.map +1 -1
  30. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexSpanSize.d.ts.map +1 -1
  31. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexTextFontSize.d.ts.map +1 -1
  32. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/numberOfMessagesResponse.d.ts.map +1 -1
  33. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/postbackAction.d.ts.map +1 -1
  34. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/subscriptionPeriodDemographic.d.ts.map +1 -1
  35. package/node_modules/@line/bot-sdk/dist/cjs/version.d.ts +1 -1
  36. package/node_modules/@line/bot-sdk/dist/cjs/version.js +1 -1
  37. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/event.d.ts +2 -1
  38. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/event.d.ts.map +1 -1
  39. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.d.ts +24 -0
  40. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.d.ts.map +1 -0
  41. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.js +14 -0
  42. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.js.map +1 -0
  43. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.d.ts +1 -0
  44. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.d.ts.map +1 -1
  45. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.js +1 -0
  46. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.js.map +1 -1
  47. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupCreateRoute.d.ts.map +1 -1
  48. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupFailedType.d.ts.map +1 -1
  49. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupJobFailedType.d.ts.map +1 -1
  50. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupJobStatus.d.ts.map +1 -1
  51. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupStatus.d.ts.map +1 -1
  52. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexBoxBorderWidth.d.ts.map +1 -1
  53. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexBoxCornerRadius.d.ts.map +1 -1
  54. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexBubble.d.ts.map +1 -1
  55. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexIconSize.d.ts.map +1 -1
  56. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexSpanSize.d.ts.map +1 -1
  57. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexTextFontSize.d.ts.map +1 -1
  58. package/node_modules/@line/bot-sdk/dist/messaging-api/model/numberOfMessagesResponse.d.ts.map +1 -1
  59. package/node_modules/@line/bot-sdk/dist/messaging-api/model/postbackAction.d.ts.map +1 -1
  60. package/node_modules/@line/bot-sdk/dist/messaging-api/model/subscriptionPeriodDemographic.d.ts.map +1 -1
  61. package/node_modules/@line/bot-sdk/dist/version.d.ts +1 -1
  62. package/node_modules/@line/bot-sdk/dist/version.js +1 -1
  63. package/node_modules/@line/bot-sdk/dist/webhook/model/event.d.ts +2 -1
  64. package/node_modules/@line/bot-sdk/dist/webhook/model/event.d.ts.map +1 -1
  65. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.d.ts +24 -0
  66. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.d.ts.map +1 -0
  67. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.js +13 -0
  68. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.js.map +1 -0
  69. package/node_modules/@line/bot-sdk/dist/webhook/model/models.d.ts +1 -0
  70. package/node_modules/@line/bot-sdk/dist/webhook/model/models.d.ts.map +1 -1
  71. package/node_modules/@line/bot-sdk/dist/webhook/model/models.js +1 -0
  72. package/node_modules/@line/bot-sdk/dist/webhook/model/models.js.map +1 -1
  73. package/node_modules/@line/bot-sdk/lib/insight/model/getNumberOfFollowersResponse.ts +1 -1
  74. package/node_modules/@line/bot-sdk/lib/liff/model/liffScope.ts +1 -1
  75. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupCreateRoute.ts +1 -4
  76. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupFailedType.ts +1 -2
  77. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupJobFailedType.ts +1 -2
  78. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupJobStatus.ts +1 -4
  79. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupStatus.ts +1 -6
  80. package/node_modules/@line/bot-sdk/lib/messaging-api/model/botInfoResponse.ts +1 -1
  81. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexBoxBorderWidth.ts +1 -6
  82. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexBoxCornerRadius.ts +1 -7
  83. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexBubble.ts +1 -7
  84. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexIconSize.ts +1 -10
  85. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexSpanSize.ts +1 -10
  86. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexTextFontSize.ts +1 -10
  87. package/node_modules/@line/bot-sdk/lib/messaging-api/model/getAggregationUnitNameListResponse.ts +1 -1
  88. package/node_modules/@line/bot-sdk/lib/messaging-api/model/getFollowersResponse.ts +1 -1
  89. package/node_modules/@line/bot-sdk/lib/messaging-api/model/getJoinedMembershipUsersResponse.ts +1 -1
  90. package/node_modules/@line/bot-sdk/lib/messaging-api/model/issueLinkTokenResponse.ts +1 -1
  91. package/node_modules/@line/bot-sdk/lib/messaging-api/model/numberOfMessagesResponse.ts +2 -5
  92. package/node_modules/@line/bot-sdk/lib/messaging-api/model/postbackAction.ts +1 -4
  93. package/node_modules/@line/bot-sdk/lib/messaging-api/model/subscriptionPeriodDemographic.ts +1 -5
  94. package/node_modules/@line/bot-sdk/lib/version.ts +1 -1
  95. package/node_modules/@line/bot-sdk/lib/webhook/.openapi-generator/FILES +1 -0
  96. package/node_modules/@line/bot-sdk/lib/webhook/model/event.ts +2 -0
  97. package/node_modules/@line/bot-sdk/lib/webhook/model/messageEditedEvent.ts +32 -0
  98. package/node_modules/@line/bot-sdk/lib/webhook/model/models.ts +1 -0
  99. package/node_modules/@line/bot-sdk/lib/webhook/model/stickerMessageContent.ts +1 -1
  100. package/node_modules/@line/bot-sdk/package.json +3 -3
  101. package/node_modules/@types/node/README.md +1 -1
  102. package/node_modules/@types/node/fs/promises.d.ts +16 -5
  103. package/node_modules/@types/node/package.json +3 -3
  104. package/npm-shrinkwrap.json +10 -10
  105. package/openclaw.plugin.json +0 -18
  106. package/package.json +71 -6
  107. package/dist/channel.runtime-DFgZ_Xwn.js +0 -4
  108. package/dist/monitor.runtime-CZuQgsEE.js +0 -2
  109. package/dist/outbound.runtime-DnpU0Cms.js +0 -2
@@ -1,13 +1,25 @@
1
- import { i as resolveLineAccount } from "./accounts-DJVOv1JI.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-BUBOoW5Q.js";
2
+ import { a as validateLineMediaUrl, t as createLineSendReceipt } from "./send-receipt-CY2ey8XH.js";
3
+ import { a as messageAction, o as postbackAction, s as uriAction, u as createReceiptCard } from "./flex-templates-KiKaVYgi.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";
9
+ import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
10
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
11
23
  //#region extensions/line/src/template-messages.ts
12
24
  const COMPACT_TEMPLATE_TEXT_LIMIT = 60;
13
25
  const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
@@ -221,6 +233,14 @@ function resolveLineChannelAccessToken(explicit, params) {
221
233
  //#region extensions/line/src/send.ts
222
234
  const userProfileCache = /* @__PURE__ */ new Map();
223
235
  const PROFILE_CACHE_TTL_MS = 300 * 1e3;
236
+ const PROFILE_CACHE_MAX_ENTRIES = 1e3;
237
+ function cacheUserProfile(userId, profile) {
238
+ userProfileCache.delete(userId);
239
+ userProfileCache.set(userId, profile);
240
+ if (userProfileCache.size <= PROFILE_CACHE_MAX_ENTRIES) return;
241
+ for (const [key, cached] of userProfileCache) if (profile.fetchedAt - cached.fetchedAt >= PROFILE_CACHE_TTL_MS) userProfileCache.delete(key);
242
+ pruneMapToMaxSize(userProfileCache, PROFILE_CACHE_MAX_ENTRIES);
243
+ }
224
244
  function normalizeTarget(to) {
225
245
  const trimmed = to.trim();
226
246
  if (!trimmed) throw new Error("Recipient is required for LINE sends");
@@ -469,7 +489,7 @@ async function getUserProfile(userId, opts) {
469
489
  displayName: profile.displayName,
470
490
  pictureUrl: profile.pictureUrl
471
491
  };
472
- userProfileCache.set(userId, {
492
+ cacheUserProfile(userId, {
473
493
  ...result,
474
494
  fetchedAt: Date.now()
475
495
  });
@@ -484,88 +504,184 @@ async function getUserDisplayName(userId, opts) {
484
504
  }
485
505
  //#endregion
486
506
  //#region extensions/line/src/markdown-to-line.ts
487
- /**
488
- * Regex patterns for markdown detection
489
- */
490
- const MARKDOWN_TABLE_REGEX = /^\|(.+)\|[\r\n]+\|[-:\s|]+\|[\r\n]+((?:\|.+\|[\r\n]*)+)/gm;
491
- const MARKDOWN_CODE_BLOCK_REGEX = /```(\w*)\n([\s\S]*?)```/g;
492
- const MARKDOWN_LINK_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g;
493
- /**
494
- * Detect and extract markdown tables from text
495
- */
496
- function extractMarkdownTables(text) {
497
- const tables = [];
498
- let textWithoutTables = text;
499
- MARKDOWN_TABLE_REGEX.lastIndex = 0;
500
- let match;
501
- const matches = [];
502
- while ((match = MARKDOWN_TABLE_REGEX.exec(text)) !== null) {
503
- const fullMatch = expectDefined(match[0], "Markdown table match");
504
- const headerLine = expectDefined(match[1], "Markdown table header capture");
505
- const bodyLines = expectDefined(match[2], "Markdown table body capture");
506
- const headers = parseTableRow(headerLine);
507
- const rows = bodyLines.trim().split(/[\r\n]+/).filter((line) => line.trim()).map(parseTableRow);
508
- if (headers.length > 0 && rows.length > 0) matches.push({
509
- fullMatch,
510
- table: {
511
- headers,
512
- rows
513
- }
507
+ const LINE_MARKDOWN_OPTIONS = {
508
+ assistantTranscriptRoleHeaders: true,
509
+ autolink: false,
510
+ blockquotePrefix: "",
511
+ headingStyle: "none",
512
+ horizontalRuleText: "",
513
+ linkify: false,
514
+ preserveSourceBlockSpacing: true
515
+ };
516
+ const TRANSCRIPT_ROLE_PREFIX = "[assistant-authored transcript] ";
517
+ function parseLineMarkdown(text, tableMode = "block") {
518
+ return markdownToIRWithMeta(text, {
519
+ ...LINE_MARKDOWN_OPTIONS,
520
+ tableMode
521
+ });
522
+ }
523
+ function toMarkdownTable(table) {
524
+ return {
525
+ headers: table.headers,
526
+ rows: table.rows,
527
+ headerCells: table.headerCells,
528
+ rowCells: table.rowCells
529
+ };
530
+ }
531
+ function codeBlockSpans(ir) {
532
+ return ir.styles.filter((span) => span.style === "code_block");
533
+ }
534
+ function toCodeBlock(ir, span) {
535
+ return {
536
+ ...span.language ? { language: span.language } : {},
537
+ code: ir.text.slice(span.start, span.end).trim()
538
+ };
539
+ }
540
+ function rangesOverlap(left, right) {
541
+ return left.start < right.end && right.start < left.end;
542
+ }
543
+ function projectPlainText(ir, omitted = []) {
544
+ const insertions = [];
545
+ for (const link of ir.links) {
546
+ if (omitted.some((range) => rangesOverlap(range, link))) continue;
547
+ const href = link.href.trim();
548
+ const label = ir.text.slice(link.start, link.end).trim();
549
+ const comparableHref = href.startsWith("mailto:") ? href.slice(7) : href;
550
+ if (href && label && label !== href && label !== comparableHref) insertions.push({
551
+ position: link.end,
552
+ text: ` (${href})`
514
553
  });
515
554
  }
516
- for (const { fullMatch, table } of matches.toReversed()) {
517
- tables.unshift(table);
518
- textWithoutTables = textWithoutTables.replace(fullMatch, "");
555
+ for (const annotation of ir.annotations ?? []) if (annotation.type === "assistant_transcript_role" && !omitted.some((range) => rangesOverlap(range, annotation))) insertions.push({
556
+ position: annotation.start,
557
+ text: TRANSCRIPT_ROLE_PREFIX
558
+ });
559
+ const inlineCodeSpans = ir.styles.filter((span) => span.style === "code" && !omitted.some((range) => rangesOverlap(range, span)));
560
+ for (const span of inlineCodeSpans) if (stripMarkdown(ir.text.slice(span.start, span.end), { assistantTranscriptRoleHeaders: true }).startsWith(TRANSCRIPT_ROLE_PREFIX)) insertions.push({
561
+ position: span.start,
562
+ text: TRANSCRIPT_ROLE_PREFIX
563
+ });
564
+ insertions.sort((left, right) => left.position - right.position);
565
+ const underlineTags = [...ir.text.matchAll(/<\/?u>/gi)].map((match) => ({
566
+ start: match.index,
567
+ end: match.index + match[0].length
568
+ })).filter((tag) => !inlineCodeSpans.some((span) => rangesOverlap(tag, span)) && !omitted.some((span) => rangesOverlap(tag, span)));
569
+ const removed = [...omitted, ...underlineTags].toSorted((left, right) => left.start - right.start);
570
+ let output = "";
571
+ let cursor = 0;
572
+ let insertionIndex = 0;
573
+ const appendRange = (end) => {
574
+ while (insertionIndex < insertions.length) {
575
+ const insertion = insertions[insertionIndex];
576
+ if (!insertion || insertion.position > end) break;
577
+ if (insertion.position >= cursor) {
578
+ output += ir.text.slice(cursor, insertion.position) + insertion.text;
579
+ cursor = insertion.position;
580
+ }
581
+ insertionIndex += 1;
582
+ }
583
+ output += ir.text.slice(cursor, end);
584
+ cursor = end;
585
+ };
586
+ for (const range of removed) {
587
+ appendRange(range.start);
588
+ cursor = Math.max(cursor, range.end);
589
+ while (insertionIndex < insertions.length && (insertions[insertionIndex]?.position ?? cursor) < cursor) insertionIndex += 1;
590
+ }
591
+ appendRange(ir.text.length);
592
+ return output.trim();
593
+ }
594
+ function sameSpanStyle(left, right) {
595
+ return left.weight === right.weight && left.style === right.style && left.decoration === right.decoration;
596
+ }
597
+ function renderTableCell(cell, fallback) {
598
+ if (!cell?.text.trim()) return {
599
+ text: fallback,
600
+ hasMarkup: false
601
+ };
602
+ const codeSpans = cell.styles.filter((span) => span.style === "code");
603
+ const tags = [...cell.text.matchAll(/<\/?u>/gi)].map((match) => ({
604
+ start: match.index,
605
+ end: match.index + match[0].length,
606
+ closing: match[0][1] === "/"
607
+ })).filter((tag) => !codeSpans.some((span) => rangesOverlap(tag, span)));
608
+ const boundaries = /* @__PURE__ */ new Set([0, cell.text.length]);
609
+ for (const style of cell.styles) {
610
+ boundaries.add(style.start);
611
+ boundaries.add(style.end);
612
+ }
613
+ for (const link of cell.links) boundaries.add(link.end);
614
+ for (const tag of tags) {
615
+ boundaries.add(tag.start);
616
+ boundaries.add(tag.end);
617
+ }
618
+ const sortedBoundaries = [...boundaries].toSorted((left, right) => left - right);
619
+ const spans = [];
620
+ let underlineDepth = 0;
621
+ let hasMarkup = false;
622
+ const appendSpan = (span) => {
623
+ const previous = spans.at(-1);
624
+ if (previous && sameSpanStyle(previous, span)) previous.text = `${previous.text ?? ""}${span.text ?? ""}`;
625
+ else spans.push(span);
626
+ };
627
+ for (let index = 0; index < sortedBoundaries.length - 1; index += 1) {
628
+ const start = sortedBoundaries[index];
629
+ const end = sortedBoundaries[index + 1];
630
+ if (start === void 0 || end === void 0) continue;
631
+ const tag = tags.find((candidate) => candidate.start === start);
632
+ if (tag) {
633
+ underlineDepth += tag.closing ? -1 : 1;
634
+ hasMarkup = true;
635
+ continue;
636
+ }
637
+ const text = cell.text.slice(start, end);
638
+ if (text) {
639
+ const active = new Set(cell.styles.filter((style) => style.start <= start && style.end >= end).map((style) => style.style));
640
+ const weight = active.has("bold") ? "bold" : void 0;
641
+ const style = active.has("italic") ? "italic" : void 0;
642
+ const decoration = active.has("strikethrough") ? "line-through" : underlineDepth > 0 ? "underline" : void 0;
643
+ hasMarkup ||= weight !== void 0 || style !== void 0 || decoration !== void 0;
644
+ appendSpan({
645
+ type: "span",
646
+ text,
647
+ weight,
648
+ style,
649
+ decoration
650
+ });
651
+ }
652
+ for (const link of cell.links.filter((candidate) => candidate.end === end)) {
653
+ const href = link.href.trim();
654
+ const label = cell.text.slice(link.start, link.end).trim();
655
+ if (href && label && label !== href) {
656
+ appendSpan({
657
+ type: "span",
658
+ text: ` (${href})`
659
+ });
660
+ hasMarkup = true;
661
+ }
662
+ }
519
663
  }
520
664
  return {
521
- tables,
522
- textWithoutTables
665
+ text: spans.map((span) => span.text ?? "").join("").trim() || fallback,
666
+ ...hasMarkup ? { contents: spans } : {},
667
+ hasMarkup
523
668
  };
524
669
  }
525
- /**
526
- * Parse a single table row (pipe-separated values)
527
- */
528
- function parseTableRow(row) {
529
- return row.split("|").map((cell) => cell.trim()).filter((cell, index, arr) => {
530
- if (index === 0 && cell === "") return false;
531
- if (index === arr.length - 1 && cell === "") return false;
532
- return true;
533
- });
670
+ function plainTableCell(text) {
671
+ return parseLineMarkdown(text, "off").ir;
534
672
  }
535
- /**
536
- * Convert a markdown table to a LINE Flex Message bubble
537
- */
673
+ /** Convert a markdown table to a LINE Flex Message bubble. */
538
674
  function convertTableToFlexBubble(table) {
539
- const parseCell = (value) => {
540
- const raw = value?.trim() ?? "";
541
- if (!raw) return {
542
- text: "-",
543
- bold: false,
544
- hasMarkup: false
545
- };
546
- let hasMarkup = false;
547
- return {
548
- text: raw.replace(/\*\*(.+?)\*\*/g, (_, inner) => {
549
- hasMarkup = true;
550
- return String(inner);
551
- }).trim() || "-",
552
- bold: /^\*\*.+\*\*$/.test(raw),
553
- hasMarkup
554
- };
555
- };
556
- const headerCells = table.headers.map((header) => parseCell(header));
557
- const rowCells = table.rows.map((row) => row.map((cell) => parseCell(cell)));
675
+ const headerCells = (table.headerCells ?? table.headers.map(plainTableCell)).map((cell) => renderTableCell(cell, "-"));
676
+ const rowCells = (table.rowCells ?? table.rows.map((row) => row.map(plainTableCell))).map((row) => row.map((cell) => renderTableCell(cell, "-")));
558
677
  const hasInlineMarkup = headerCells.some((cell) => cell.hasMarkup) || rowCells.some((row) => row.some((cell) => cell.hasMarkup));
559
- if (table.headers.length === 2 && !hasInlineMarkup) {
560
- const items = rowCells.map((row) => ({
678
+ if (table.headers.length === 2 && !hasInlineMarkup) return createReceiptCard({
679
+ title: headerCells.map((cell) => cell.text).join(" / "),
680
+ items: rowCells.map((row) => ({
561
681
  name: row[0]?.text ?? "-",
562
682
  value: row[1]?.text ?? "-"
563
- }));
564
- return createReceiptCard({
565
- title: headerCells.map((cell) => cell.text).join(" / "),
566
- items
567
- });
568
- }
683
+ }))
684
+ });
569
685
  return {
570
686
  type: "bubble",
571
687
  body: {
@@ -578,6 +694,7 @@ function convertTableToFlexBubble(table) {
578
694
  contents: headerCells.map((cell) => ({
579
695
  type: "text",
580
696
  text: cell.text,
697
+ contents: cell.contents,
581
698
  weight: "bold",
582
699
  size: "sm",
583
700
  color: "#333333",
@@ -590,67 +707,32 @@ function convertTableToFlexBubble(table) {
590
707
  type: "separator",
591
708
  margin: "sm"
592
709
  },
593
- ...rowCells.slice(0, 10).map((row, rowIndex) => {
594
- return {
595
- type: "box",
596
- layout: "horizontal",
597
- contents: table.headers.map((_, colIndex) => {
598
- const cell = row[colIndex] ?? {
599
- text: "-",
600
- bold: false,
601
- hasMarkup: false
602
- };
603
- return {
604
- type: "text",
605
- text: cell.text,
606
- size: "sm",
607
- color: "#666666",
608
- flex: 1,
609
- wrap: true,
610
- weight: cell.bold ? "bold" : void 0
611
- };
612
- }),
613
- margin: rowIndex === 0 ? "md" : "sm"
614
- };
615
- })
710
+ ...rowCells.slice(0, 10).map((row, rowIndex) => ({
711
+ type: "box",
712
+ layout: "horizontal",
713
+ contents: table.headers.map((_, colIndex) => {
714
+ const cell = row[colIndex] ?? {
715
+ text: "-",
716
+ hasMarkup: false
717
+ };
718
+ return {
719
+ type: "text",
720
+ text: cell.text,
721
+ contents: cell.contents,
722
+ size: "sm",
723
+ color: "#666666",
724
+ flex: 1,
725
+ wrap: true
726
+ };
727
+ }),
728
+ margin: rowIndex === 0 ? "md" : "sm"
729
+ }))
616
730
  ],
617
731
  paddingAll: "lg"
618
732
  }
619
733
  };
620
734
  }
621
- /**
622
- * Detect and extract code blocks from text
623
- */
624
- function extractCodeBlocks(text) {
625
- const codeBlocks = [];
626
- let textWithoutCode = text;
627
- MARKDOWN_CODE_BLOCK_REGEX.lastIndex = 0;
628
- let match;
629
- const matches = [];
630
- while ((match = MARKDOWN_CODE_BLOCK_REGEX.exec(text)) !== null) {
631
- const fullMatch = expectDefined(match[0], "Markdown code block match");
632
- const language = match[1] || void 0;
633
- const code = expectDefined(match[2], "Markdown code body capture");
634
- matches.push({
635
- fullMatch,
636
- block: {
637
- language,
638
- code: code.trim()
639
- }
640
- });
641
- }
642
- for (const { fullMatch, block } of matches.toReversed()) {
643
- codeBlocks.unshift(block);
644
- textWithoutCode = textWithoutCode.replace(fullMatch, "");
645
- }
646
- return {
647
- codeBlocks,
648
- textWithoutCode
649
- };
650
- }
651
- /**
652
- * Convert a code block to a LINE Flex Message bubble
653
- */
735
+ /** Convert a code block to a LINE Flex Message bubble. */
654
736
  function convertCodeBlockToFlexBubble(block) {
655
737
  const titleText = block.language ? `Code (${block.language})` : "Code";
656
738
  const displayCode = block.code.length > 2e3 ? truncateUtf16Safe(block.code, 2e3) + "\n..." : block.code;
@@ -684,97 +766,19 @@ function convertCodeBlockToFlexBubble(block) {
684
766
  }
685
767
  };
686
768
  }
687
- /**
688
- * Extract markdown links from text
689
- */
690
- function extractLinks(text) {
691
- const links = [];
692
- MARKDOWN_LINK_REGEX.lastIndex = 0;
693
- let match;
694
- while ((match = MARKDOWN_LINK_REGEX.exec(text)) !== null) links.push({
695
- text: expectDefined(match[1], "Markdown link text capture"),
696
- url: expectDefined(match[2], "Markdown link URL capture")
697
- });
698
- return {
699
- links,
700
- textWithLinks: text.replace(MARKDOWN_LINK_REGEX, "$1")
701
- };
702
- }
703
- /**
704
- * Create a Flex Message with tappable link buttons
705
- */
706
- function convertLinksToFlexBubble(links) {
707
- return {
708
- type: "bubble",
709
- body: {
710
- type: "box",
711
- layout: "vertical",
712
- contents: [{
713
- type: "text",
714
- text: "Links",
715
- weight: "bold",
716
- size: "md",
717
- color: "#333333"
718
- }],
719
- paddingAll: "lg",
720
- paddingBottom: "sm"
721
- },
722
- footer: {
723
- type: "box",
724
- layout: "vertical",
725
- contents: links.slice(0, 4).map((link, index) => ({
726
- type: "button",
727
- action: uriAction(link.text, link.url),
728
- style: index === 0 ? "primary" : "secondary",
729
- margin: index > 0 ? "sm" : void 0
730
- })),
731
- paddingAll: "md"
732
- }
733
- };
734
- }
735
- /**
736
- * Main function: Process text for LINE output
737
- * - Extracts tables → Flex Messages
738
- * - Extracts code blocks → Flex Messages
739
- * - Strips remaining markdown
740
- * - Returns processed text + Flex Messages
741
- */
769
+ /** Parse once, route existing block surfaces to Flex, and project the remainder as plain text. */
742
770
  function processLineMessage(text) {
743
- const flexMessages = [];
744
- let processedText = text;
745
- const { tables, textWithoutTables } = extractMarkdownTables(processedText);
746
- processedText = textWithoutTables;
747
- for (const table of tables) {
748
- const bubble = convertTableToFlexBubble(table);
749
- flexMessages.push(toFlexMessage("Table", bubble));
750
- }
751
- const { codeBlocks, textWithoutCode } = extractCodeBlocks(processedText);
752
- processedText = textWithoutCode;
753
- for (const block of codeBlocks) {
754
- const bubble = convertCodeBlockToFlexBubble(block);
755
- flexMessages.push(toFlexMessage("Code", bubble));
756
- }
757
- const { textWithLinks } = extractLinks(processedText);
758
- processedText = textWithLinks;
759
- processedText = stripMarkdown(processedText, { assistantTranscriptRoleHeaders: true });
771
+ const { ir, tables } = parseLineMarkdown(text);
772
+ const codeSpans = codeBlockSpans(ir);
760
773
  return {
761
- text: processedText,
762
- flexMessages
774
+ text: projectPlainText(ir, codeSpans),
775
+ flexMessages: [...tables.map((table) => toFlexMessage("Table", convertTableToFlexBubble(toMarkdownTable(table)))), ...codeSpans.map((span) => toFlexMessage("Code", convertCodeBlockToFlexBubble(toCodeBlock(ir, span))))]
763
776
  };
764
777
  }
765
- /**
766
- * Check if text contains markdown that needs conversion
767
- */
778
+ /** Check if text contains markdown that needs conversion. */
768
779
  function hasMarkdownToConvert(text) {
769
- MARKDOWN_TABLE_REGEX.lastIndex = 0;
770
- if (MARKDOWN_TABLE_REGEX.test(text)) return true;
771
- MARKDOWN_CODE_BLOCK_REGEX.lastIndex = 0;
772
- if (MARKDOWN_CODE_BLOCK_REGEX.test(text)) return true;
773
- if (/\*\*[^*]+\*\*/.test(text)) return true;
774
- if (/~~[^~]+~~/.test(text)) return true;
775
- if (/^#{1,6}\s+/m.test(text)) return true;
776
- if (/^>\s+/m.test(text)) return true;
777
- return false;
780
+ const { ir, tables } = parseLineMarkdown(text);
781
+ return tables.length > 0 || ir.styles.length > 0 || ir.links.length > 0 || /<\/?u>/i.test(ir.text) || ir.text !== text.trimEnd();
778
782
  }
779
783
  //#endregion
780
- 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 };
784
+ 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 };