@foxden-app/foxclaw 0.5.65 → 0.5.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.
- package/CHANGELOG.md +10 -0
- package/dist/controller/controller.js +8 -6
- package/dist/telegram/rich_markdown.js +54 -22
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable FoxClaw changes are listed here. Each release note is bilingual so GitHub Releases and the npm package are useful to both Chinese and English readers.
|
|
4
4
|
|
|
5
|
+
## 0.5.66 - 2026-06-29
|
|
6
|
+
|
|
7
|
+
### 中文
|
|
8
|
+
- 优化 Codex 需要用户确认时的 Telegram 富文本展示:问题保持顶层编号,候选项改为缩进的二级编号列表,避免多问题确认卡片看起来像一整串扁平 `1/2/3`。
|
|
9
|
+
- RichMessage Markdown 渲染器现在支持嵌套有序/无序列表,类似安装选项、配置选择这类两层列表会渲染成真正的 Telegram 嵌套列表。
|
|
10
|
+
|
|
11
|
+
### English
|
|
12
|
+
- Improved Telegram RichMessage rendering for Codex user-input prompts. Questions stay as top-level numbered items, while choices are indented as second-level numbered lists, avoiding a flat sequence of confusing `1/2/3` items.
|
|
13
|
+
- The RichMessage Markdown renderer now supports nested ordered and unordered lists, so setup and configuration prompts render as real Telegram nested lists.
|
|
14
|
+
|
|
5
15
|
## 0.5.65 - 2026-06-29
|
|
6
16
|
|
|
7
17
|
### 中文
|
|
@@ -11039,23 +11039,25 @@ function renderUserInputMessage(locale, record) {
|
|
|
11039
11039
|
record.questions.forEach((question, index) => {
|
|
11040
11040
|
lines.push('');
|
|
11041
11041
|
const title = question.header || question.question || question.id;
|
|
11042
|
-
lines.push(`${index + 1}. ${title}`);
|
|
11043
11042
|
if (question.header && question.question) {
|
|
11044
|
-
lines.push(question.question);
|
|
11043
|
+
lines.push(`${index + 1}. **${title}** ${question.question}`);
|
|
11044
|
+
}
|
|
11045
|
+
else {
|
|
11046
|
+
lines.push(`${index + 1}. **${title}**`);
|
|
11045
11047
|
}
|
|
11046
11048
|
if (question.isOther) {
|
|
11047
|
-
lines.push(t(locale, 'user_input_other_hint'));
|
|
11049
|
+
lines.push(` - ${t(locale, 'user_input_other_hint')}`);
|
|
11048
11050
|
}
|
|
11049
11051
|
if (question.isSecret) {
|
|
11050
|
-
lines.push(t(locale, 'user_input_secret_warning'));
|
|
11052
|
+
lines.push(` - ${t(locale, 'user_input_secret_warning')}`);
|
|
11051
11053
|
}
|
|
11052
11054
|
question.options.forEach((option, optionIndex) => {
|
|
11053
11055
|
const description = option.description ? ` - ${option.description}` : '';
|
|
11054
|
-
lines.push(
|
|
11056
|
+
lines.push(` ${optionIndex + 1}. ${option.label}${description}`);
|
|
11055
11057
|
});
|
|
11056
11058
|
const answer = record.answers.get(question.id);
|
|
11057
11059
|
if (answer) {
|
|
11058
|
-
lines.push(t(locale, 'user_input_selected', { value: answer }));
|
|
11060
|
+
lines.push(` - ${t(locale, 'user_input_selected', { value: answer })}`);
|
|
11059
11061
|
}
|
|
11060
11062
|
});
|
|
11061
11063
|
lines.push('');
|
|
@@ -31,22 +31,11 @@ export function renderTelegramMarkdownRichHtml(markdown) {
|
|
|
31
31
|
index += 1;
|
|
32
32
|
continue;
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
blocks.push(`<ul>${items.map(item => `<li>${renderInlineRichHtml(item)}</li>`).join('')}</ul>`);
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (/^\s*\d+[.)]\s+/.test(line)) {
|
|
44
|
-
const items = [];
|
|
45
|
-
while (index < lines.length && /^\s*\d+[.)]\s+/.test(lines[index])) {
|
|
46
|
-
items.push(stripListMarker(lines[index], true));
|
|
47
|
-
index += 1;
|
|
48
|
-
}
|
|
49
|
-
blocks.push(`<ol>${items.map(item => `<li>${renderInlineRichHtml(item)}</li>`).join('')}</ol>`);
|
|
34
|
+
const list = parseListMarker(line);
|
|
35
|
+
if (list) {
|
|
36
|
+
const rendered = consumeList(lines, index, list.indent, list.ordered);
|
|
37
|
+
blocks.push(rendered.html);
|
|
38
|
+
index = rendered.nextIndex;
|
|
50
39
|
continue;
|
|
51
40
|
}
|
|
52
41
|
if (/^\s*>\s?/.test(line)) {
|
|
@@ -64,8 +53,7 @@ export function renderTelegramMarkdownRichHtml(markdown) {
|
|
|
64
53
|
&& lines[index].trim()
|
|
65
54
|
&& !/^```/.test(lines[index])
|
|
66
55
|
&& !/^(#{1,4})\s+/.test(lines[index])
|
|
67
|
-
&&
|
|
68
|
-
&& !/^\s*\d+[.)]\s+/.test(lines[index])
|
|
56
|
+
&& !parseListMarker(lines[index])
|
|
69
57
|
&& !/^\s*>\s?/.test(lines[index])) {
|
|
70
58
|
paragraphLines.push(lines[index].trimEnd());
|
|
71
59
|
index += 1;
|
|
@@ -94,10 +82,54 @@ export function renderInlineRichHtml(markdown) {
|
|
|
94
82
|
html = html.replace(/\{\{FOXCLAW_RICH_CODE_(\d+)}}/g, (_match, tokenIndex) => tokens[Number(tokenIndex)] ?? '');
|
|
95
83
|
return html.replaceAll('\n', '<br>');
|
|
96
84
|
}
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
function consumeList(lines, startIndex, indent, ordered) {
|
|
86
|
+
const items = [];
|
|
87
|
+
let index = startIndex;
|
|
88
|
+
while (index < lines.length) {
|
|
89
|
+
const marker = parseListMarker(lines[index]);
|
|
90
|
+
if (!marker || marker.indent < indent) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
if (marker.indent > indent) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
if (marker.ordered !== ordered) {
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
let itemHtml = renderInlineRichHtml(marker.text);
|
|
100
|
+
index += 1;
|
|
101
|
+
while (index < lines.length) {
|
|
102
|
+
const nested = parseListMarker(lines[index]);
|
|
103
|
+
if (!nested || nested.indent <= indent) {
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
const rendered = consumeList(lines, index, nested.indent, nested.ordered);
|
|
107
|
+
itemHtml += rendered.html;
|
|
108
|
+
index = rendered.nextIndex;
|
|
109
|
+
}
|
|
110
|
+
items.push(`<li>${itemHtml}</li>`);
|
|
111
|
+
}
|
|
112
|
+
const tag = ordered ? 'ol' : 'ul';
|
|
113
|
+
return { html: `<${tag}>${items.join('')}</${tag}>`, nextIndex: index };
|
|
114
|
+
}
|
|
115
|
+
function parseListMarker(line) {
|
|
116
|
+
const ordered = line.match(/^(\s*)\d+[.)]\s+(.+)$/);
|
|
117
|
+
if (ordered) {
|
|
118
|
+
return {
|
|
119
|
+
indent: ordered[1].replaceAll('\t', ' ').length,
|
|
120
|
+
ordered: true,
|
|
121
|
+
text: ordered[2],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const unordered = line.match(/^(\s*)[-*+]\s+(.+)$/);
|
|
125
|
+
if (unordered) {
|
|
126
|
+
return {
|
|
127
|
+
indent: unordered[1].replaceAll('\t', ' ').length,
|
|
128
|
+
ordered: false,
|
|
129
|
+
text: unordered[2],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
101
133
|
}
|
|
102
134
|
function sanitizeCodeLanguage(language) {
|
|
103
135
|
const normalized = language.trim().replace(/[^A-Za-z0-9_+-]/g, '');
|