pi-ui-extend 0.1.65 → 0.1.66

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.
@@ -1,120 +0,0 @@
1
- /**
2
- * Minimal markdown → Telegram HTML conversion.
3
- *
4
- * Telegram supports a small subset of HTML: <b>, <i>, <code>, <pre>, <a>, <s>, <u>.
5
- * See https://core.telegram.org/bots/api#html-style
6
- *
7
- * We escape < > & first, then re-introduce supported tags from common markdown
8
- * patterns. This is intentionally lossy — blockquotes, headings, lists render
9
- * as plain text. The goal is to keep streaming output legible on a phone screen.
10
- */
11
-
12
- const ENTITY_RE = /[<>&]/g;
13
-
14
- export function escapeHtml(value: string): string {
15
- return value.replace(ENTITY_RE, (ch) => {
16
- switch (ch) {
17
- case "<":
18
- return "&lt;";
19
- case ">":
20
- return "&gt;";
21
- case "&":
22
- return "&amp;";
23
- default:
24
- return ch;
25
- }
26
- });
27
- }
28
-
29
- /**
30
- * Convert a chunk of assistant-flavoured markdown to Telegram HTML.
31
- *
32
- * Handles:
33
- * ```lang\nfenced\n``` → <pre><code class="language-lang">…</code></pre>
34
- * `inline code` → <code>…</code>
35
- * **bold** → <b>…</b>
36
- * __bold__ / *italic* → <b>…</b> / <i>…</i>
37
- *
38
- * Everything else is HTML-escaped.
39
- */
40
- export function markdownToTelegram(value: string): string {
41
- if (!value) return "";
42
-
43
- // Pull out fenced code blocks first so their contents aren't mangled
44
- // by inline replacements.
45
- const fences: string[] = [];
46
- const fenced = value.replace(/```([a-zA-Z0-9_+-]*)\n?([\s\S]*?)```/g, (_m, lang, body) => {
47
- const langAttr = typeof lang === "string" && lang.trim() ? ` class="language-${escapeHtml(lang.trim())}"` : "";
48
- const rendered = `<pre><code${langAttr}>${escapeHtml(body)}</code></pre>`;
49
- fences.push(rendered);
50
- return `\u0000FENCE${fences.length - 1}\u0000`;
51
- });
52
-
53
- let out = escapeHtml(fenced);
54
-
55
- // Inline code (do before bold/italic so backticks inside ** stay escaped)
56
- out = out.replace(/`([^`\n]+?)`/g, (_m, code) => `<code>${code}</code>`);
57
-
58
- // Bold
59
- out = out.replace(/\*\*([^*\n]+?)\*\*/g, "<b>$1</b>");
60
- out = out.replace(/__([^_\n]+?)__/g, "<b>$1</b>");
61
-
62
- // Italic (single * or _)
63
- out = out.replace(/(^|[^*])\*([^*\n]+?)\*(?!\*)/g, "$1<i>$2</i>");
64
- out = out.replace(/(^|[^_])_([^_\n]+?)_(?!_)/g, "$1<i>$2</i>");
65
-
66
- // Restore fenced blocks
67
- out = out.replace(/\u0000FENCE(\d+)\u0000/g, (_m, idx) => fences[Number(idx)] ?? "");
68
-
69
- return out;
70
- }
71
-
72
- /** Telegram message size limit. */
73
- export const TELEGRAM_MESSAGE_MAX = 4096;
74
-
75
- /**
76
- * Split a rendered HTML payload into chunks that fit a single Telegram message.
77
- * Tries to break on blank lines first, then on newlines, then hard-splits.
78
- *
79
- * Each chunk is guaranteed to be <= maxChars characters.
80
- */
81
- export function chunkForTelegram(html: string, maxChars = TELEGRAM_MESSAGE_MAX): string[] {
82
- if (html.length <= maxChars) return [html];
83
-
84
- const chunks: string[] = [];
85
- let cursor = 0;
86
- while (cursor < html.length) {
87
- const remaining = html.length - cursor;
88
- if (remaining <= maxChars) {
89
- chunks.push(html.slice(cursor));
90
- break;
91
- }
92
-
93
- const window = html.slice(cursor, cursor + maxChars);
94
- let cut = -1;
95
-
96
- // Prefer blank line break
97
- const blank = window.lastIndexOf("\n\n");
98
- if (blank >= maxChars * 0.5) cut = blank + 1;
99
-
100
- // Then any newline
101
- if (cut < 0) {
102
- const nl = window.lastIndexOf("\n");
103
- if (nl >= maxChars * 0.5) cut = nl + 1;
104
- }
105
-
106
- // Then a space
107
- if (cut < 0) {
108
- const sp = window.lastIndexOf(" ");
109
- if (sp >= maxChars * 0.5) cut = sp + 1;
110
- }
111
-
112
- // Hard cut
113
- if (cut < 0) cut = maxChars;
114
-
115
- chunks.push(html.slice(cursor, cursor + cut));
116
- cursor += cut;
117
- }
118
-
119
- return chunks.filter((chunk) => chunk.length > 0);
120
- }