#!/usr/bin/env node 'use strict'; const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const source = fs.readFileSync(path.join(__dirname, '..', 'public', 'app.js'), 'utf8'); function extractFunction(name) { const start = source.indexOf(`function ${name}(`); assert(start >= 0, `缺少前端函数 ${name}`); let depth = 0, parentheses = 0, signatureEnd = start; for (let i = source.indexOf('(', start); i < source.length; i += 1) { if (source[i] === '(') parentheses += 1; if (source[i] === ')' && --parentheses === 0) { signatureEnd = i; break; } } for (let i = source.indexOf('{', signatureEnd); i < source.length; i += 1) { if (source[i] === '{') depth += 1; if (source[i] === '}' && --depth === 0) return source.slice(start, i + 1); } throw new Error(`无法提取 ${name}`); } const names = ['getAssistantReplySections', 'getAssistantReplySource', 'getAssistantLastReplyText', 'shouldCollapseAssistantReplies']; const api = new Function(` const ASSISTANT_REPLY_COLLAPSE_CHARS = 6000; const ASSISTANT_REPLY_COLLAPSE_BLOCKS = 24; ${names.map(extractFunction).join('\n')} return { ${names.join(',')} }; `)(); const DIVIDER = '
'; const FIRST = '# 第一条回复\n\n' + '前文'.repeat(4000); const LAST = '# 最后一条回复\n\n第一自然段。\n\n第二自然段。\n\n```js\nconst value = 1;\n```'; let passed = 0; function test(description, fn) { fn(); passed += 1; console.log('通过:' + description); } test('末段按原生回复分界,保留 Markdown、多自然段与代码', () => { const content = FIRST + '\n\n' + DIVIDER + '\n\n' + LAST; const message = { __assistantContent: content }; assert.deepEqual(api.getAssistantReplySections(content), [FIRST, LAST]); assert.equal(api.getAssistantLastReplyText(message), LAST); assert(!api.getAssistantLastReplyText(message).includes('10:20:30')); assert(!api.getAssistantLastReplyText(message).includes('前文')); }); test('无原生分界的单条回复复制全文,长正文也不会按最后 p 截断', () => { assert.equal(api.getAssistantLastReplyText({ __assistantContent: LAST }), LAST); assert.equal(api.shouldCollapseAssistantReplies([FIRST], 30), true); }); test('复制保留 Markdown 首行缩进和行末空格,不改变代码及换行语义', () => { const indented = ' const value = 1;\n return value; '; assert.equal(api.getAssistantLastReplyText({ __assistantContent: indented }), indented); assert.equal(api.getAssistantLastReplyText({ __assistantContent: FIRST + '\n\n' + DIVIDER + '\n\n' + indented + '\n\n' }), indented); }); test('代码围栏内展示分界 HTML 不应误判成原生回复边界', () => { const code = '```html\n' + DIVIDER + '\n```\n\n说明仍属于同一回复。'; assert.deepEqual(api.getAssistantReplySections(code), [code]); assert.deepEqual(api.getAssistantReplySections(code + '\n' + DIVIDER + '\n' + LAST), [code, LAST]); const tilde = '~~~~html\n' + DIVIDER + '\n~~~~\n段落'; assert.deepEqual(api.getAssistantReplySections(tilde), [tilde]); }); test('分界 HTML 尚未传完时复制上一条正文,不夹带时间或半截标签', () => { for (const partial of ['