Files
openclaw-zero-token/markdown/whatsapp.ts
sjhu 571e14a236 feat: upgrade to upstream v2026.3.28
Major upgrade from e26988a38 to upstream v2026.3.28 (f9b107928).
Key changes:
- Upstream src/, ui/, extensions/ (89 bundled extensions)
- Zero-token web providers preserved in src/zero-token/
- AskOnce plugin restored and registered as CLI command
- Added missing packages: @anthropic-ai/vertex-sdk, @modelcontextprotocol/sdk
- Fixed tsconfig rootDir, skipLibCheck for plugin-sdk DTS build
- Added askonce to bundled plugin metadata and package.json exports
- Fixed AskOnce CLI command registration (missing commands metadata)
- Restored AskOnce adapter imports (correct 5-level relative paths)
- Removed stale migration artifacts from root directory
2026-03-30 17:58:12 +08:00

78 lines
2.4 KiB
TypeScript

import { escapeRegExp } from "../utils.js";
/**
* Convert standard Markdown formatting to WhatsApp-compatible markup.
*
* WhatsApp uses its own formatting syntax:
* bold: *text*
* italic: _text_
* strikethrough: ~text~
* monospace: ```text```
*
* Standard Markdown uses:
* bold: **text** or __text__
* italic: *text* or _text_
* strikethrough: ~~text~~
* code: `text` (inline) or ```text``` (block)
*
* The conversion preserves fenced code blocks and inline code,
* then converts bold and strikethrough markers.
*/
/** Placeholder tokens used during conversion to protect code spans. */
const FENCE_PLACEHOLDER = "\x00FENCE";
const INLINE_CODE_PLACEHOLDER = "\x00CODE";
/**
* Convert standard Markdown bold/italic/strikethrough to WhatsApp formatting.
*
* Order of operations matters:
* 1. Protect fenced code blocks (```...```) — already WhatsApp-compatible
* 2. Protect inline code (`...`) — leave as-is
* 3. Convert **bold** → *bold* and __bold__ → *bold*
* 4. Convert ~~strike~~ → ~strike~
* 5. Restore protected spans
*
* Italic *text* and _text_ are left alone since WhatsApp uses _text_ for italic
* and single * is already WhatsApp bold — no conversion needed for single markers.
*/
export function markdownToWhatsApp(text: string): string {
if (!text) {
return text;
}
// 1. Extract and protect fenced code blocks
const fences: string[] = [];
let result = text.replace(/```[\s\S]*?```/g, (match) => {
fences.push(match);
return `${FENCE_PLACEHOLDER}${fences.length - 1}`;
});
// 2. Extract and protect inline code
const inlineCodes: string[] = [];
result = result.replace(/`[^`\n]+`/g, (match) => {
inlineCodes.push(match);
return `${INLINE_CODE_PLACEHOLDER}${inlineCodes.length - 1}`;
});
// 3. Convert **bold** → *bold* and __bold__ → *bold*
result = result.replace(/\*\*(.+?)\*\*/g, "*$1*");
result = result.replace(/__(.+?)__/g, "*$1*");
// 4. Convert ~~strikethrough~~ → ~strikethrough~
result = result.replace(/~~(.+?)~~/g, "~$1~");
// 5. Restore inline code
result = result.replace(
new RegExp(`${escapeRegExp(INLINE_CODE_PLACEHOLDER)}(\\d+)`, "g"),
(_, idx) => inlineCodes[Number(idx)] ?? "",
);
// 6. Restore fenced code blocks
result = result.replace(
new RegExp(`${escapeRegExp(FENCE_PLACEHOLDER)}(\\d+)`, "g"),
(_, idx) => fences[Number(idx)] ?? "",
);
return result;
}