feat(tg): 思考块改为可折叠引用块(与工具调用区分)
TG 不支持按消息自定义引用块颜色(竖线颜色跟随客户端主题),按用户
选择采用官方可折叠引用块:默认收起为「💭 思考过程 ▾」,点击展开,
与工具调用的普通蓝色竖线引用块在交互上明确区分。
- rich 模式:Telegram 原生 markdown 可折叠引用语法(**> 开头 + 行尾 ||)
- HTML 模式:markdownToHtml 转换为 <blockquote expandable>
- 思考文本内的 || 全角化,避免被解析成 spoiler 实体
This commit is contained in:
+11
-3
@@ -75,8 +75,10 @@ const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
const REASONING_QUOTE_MAX_CHARS = 800;
|
||||
/**
|
||||
* Assistant message → markdown for delivery. Reasoning (thinking) blocks are
|
||||
* kept, one `> ` quote line each — Telegram renders them as the accent-bar
|
||||
* quote style the phone user expects for bash/thinking-style chatter.
|
||||
* kept as EXPANDABLE quotes (`**>`…`||` per Telegram's native markdown spec,
|
||||
* `<blockquote expandable>` in HTML mode) so they stay collapsed on the phone
|
||||
* and visually distinct from tool-call quotes; ordinary quotes in assistant
|
||||
* text remain plain blockquotes.
|
||||
*/
|
||||
function contentToText(content) {
|
||||
const parts = [];
|
||||
@@ -90,7 +92,13 @@ function contentToText(content) {
|
||||
continue;
|
||||
if (thinking.length > REASONING_QUOTE_MAX_CHARS)
|
||||
thinking = `${thinking.slice(0, REASONING_QUOTE_MAX_CHARS)}…`;
|
||||
parts.push(thinking.split('\n').map((line) => `> ${line}`).join('\n'));
|
||||
// `||` would open/close spoiler entities in native markdown — neutralize.
|
||||
const lines = thinking.replace(/\|\|/g, '||').split('\n');
|
||||
const last = lines.length - 1;
|
||||
parts.push([
|
||||
'**>💭 思考过程',
|
||||
...lines.map((line, i) => `> ${line}${i === last ? '||' : ''}`),
|
||||
].join('\n'));
|
||||
}
|
||||
}
|
||||
return parts.join('\n\n');
|
||||
|
||||
+37
-5
@@ -12,22 +12,54 @@ function inlineToHtml(text) {
|
||||
html = html.replace(/\*\*([^*\n]+)\*\*/g, '<b>$1</b>');
|
||||
return html;
|
||||
}
|
||||
/** Convert `> …` line groups into `<blockquote>` (Telegram renders it with the accent-colored left bar). */
|
||||
/**
|
||||
* Convert `> …` line groups into `<blockquote>` (Telegram renders it with the
|
||||
* accent-colored left bar). The native-markdown expandable-quote form
|
||||
* (`**>first line` … `last line||`) becomes `<blockquote expandable>` — used
|
||||
* for thinking blocks so they stay collapsed and distinct from tool-call
|
||||
* quotes (Telegram has no per-message quote color).
|
||||
*/
|
||||
const EXPANDABLE_OPEN = '**>';
|
||||
function blocksToHtml(text) {
|
||||
const lines = text.split('\n');
|
||||
const out = [];
|
||||
let quote = [];
|
||||
let expandable = false;
|
||||
const flush = () => {
|
||||
if (quote.length === 0)
|
||||
return;
|
||||
out.push(`<blockquote>${inlineToHtml(quote.join('\n'))}</blockquote>`);
|
||||
const open = expandable ? '<blockquote expandable>' : '<blockquote>';
|
||||
out.push(`${open}${inlineToHtml(quote.join('\n'))}</blockquote>`);
|
||||
quote = [];
|
||||
expandable = false;
|
||||
};
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('> '))
|
||||
quote.push(line.slice(2));
|
||||
else if (line === '>')
|
||||
if (line.startsWith(EXPANDABLE_OPEN)) {
|
||||
flush();
|
||||
expandable = true;
|
||||
let content = line.slice(EXPANDABLE_OPEN.length);
|
||||
if (content.endsWith('||')) {
|
||||
quote.push(content.slice(0, -2));
|
||||
flush();
|
||||
}
|
||||
else {
|
||||
quote.push(content);
|
||||
}
|
||||
}
|
||||
else if (line.startsWith('> ')) {
|
||||
let content = line.slice(2);
|
||||
let closed = false;
|
||||
if (expandable && content.endsWith('||')) {
|
||||
content = content.slice(0, -2);
|
||||
closed = true;
|
||||
}
|
||||
quote.push(content);
|
||||
if (closed)
|
||||
flush();
|
||||
}
|
||||
else if (line === '>') {
|
||||
quote.push('');
|
||||
}
|
||||
else {
|
||||
flush();
|
||||
out.push(inlineToHtml(line));
|
||||
|
||||
Reference in New Issue
Block a user