e0a5f57793
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as a monorepo subdirectory under desktop/. Source only — node_modules, dist, Cargo target and the upstream .git history are excluded. desktop/ keeps its own independent Cargo workspace (desktop/src-tauri with crates/*); it is not part of the backend boosapi crate, so a `cargo check` at the metapi root still compiles only the backend. Build the desktop app from desktop/src-tauri per desktop/README.md. Note: app icons are still placeholder assets pending the final BoosAPI logo. The updater signing keys live outside the repo and are not included.
120 lines
5.7 KiB
TypeScript
120 lines
5.7 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { getMarkdown, parseMarkdownToStructure } from '../src'
|
|
|
|
describe('thinking custom tag after inline html prefix', () => {
|
|
const thinkingContent = String.raw`勾股定理是数学中一个非常重要的定理,尤其在几何学中有着广泛的应用。它描述了直角三角形三边之间的关系。
|
|
|
|
### 勾股定理的定义:
|
|
在**直角三角形**中,斜边(即与直角相对的边)的平方等于另外两条直角边的平方和。
|
|
|
|
用公式表示为:
|
|
|
|
$$
|
|
a^2 + b^2 = c^2
|
|
$$
|
|
|
|
其中:
|
|
- $ a $ 和 $ b $ 是直角三角形的两条直角边;
|
|
- $ c $ 是斜边(即最长的一条边)。
|
|
|
|
---
|
|
|
|
### 举例说明:
|
|
比如一个直角三角形,两条直角边分别是3和4,那么斜边 $ c $ 可以通过勾股定理计算得出:
|
|
|
|
$$
|
|
c = \sqrt{3^2 + 4^2} = \sqrt{9 + 16} = \sqrt{25} = 5
|
|
$$
|
|
|
|
所以这个三角形的三边是3、4、5,是一个经典的勾股数(毕达哥拉斯三元组)。
|
|
|
|
|
|
如果你有兴趣,我还可以为你提供勾股定理的几种不同证明方式,或者讲解一些有趣的勾股数例子!`
|
|
|
|
it('keeps the full thinking markdown inside the thinking node and does not leak trailing block nodes', () => {
|
|
const markdown = `<usrhead></usrhead><br>介绍下勾股定理<br><br><br><br><aihead></aihead><thinking>${thinkingContent}</thinking>`
|
|
const tags = ['usrhead', 'aihead', 'thinking']
|
|
const md = getMarkdown('thinking-inline-prefix-regression', { customHtmlTags: tags })
|
|
const nodes = parseMarkdownToStructure(markdown, md, { customHtmlTags: tags, final: true }) as any[]
|
|
|
|
expect(nodes.map(node => node?.type)).toEqual(['paragraph', 'thinking'])
|
|
|
|
const prefix = nodes[0]
|
|
expect(prefix?.type).toBe('paragraph')
|
|
expect(prefix?.children?.map((node: any) => node?.type)).toEqual([
|
|
'usrhead',
|
|
'hardbreak',
|
|
'text',
|
|
'hardbreak',
|
|
'hardbreak',
|
|
'hardbreak',
|
|
'hardbreak',
|
|
'aihead',
|
|
])
|
|
expect(prefix?.children?.[2]?.content).toBe('介绍下勾股定理')
|
|
|
|
const thinking = nodes[1]
|
|
expect(thinking?.type).toBe('thinking')
|
|
expect(thinking?.content).toBe(thinkingContent)
|
|
expect(String(thinking?.raw ?? '')).toContain('<thinking>')
|
|
expect(String(thinking?.raw ?? '')).toContain('</thinking>')
|
|
expect(nodes.at(-1)?.type).toBe('thinking')
|
|
})
|
|
|
|
it('keeps streaming block markers inside thinking before </thinking> arrives', () => {
|
|
const markdown = `<usrhead></usrhead><br>介绍下勾股定理<br><br><br><br><aihead></aihead><thinking>${thinkingContent}`
|
|
const tags = ['usrhead', 'aihead', 'thinking']
|
|
const md = getMarkdown('thinking-inline-prefix-streaming-regression', { customHtmlTags: tags })
|
|
const nodes = parseMarkdownToStructure(markdown, md, { customHtmlTags: tags, final: false }) as any[]
|
|
|
|
expect(nodes.map(node => node?.type)).toEqual(['paragraph', 'thinking'])
|
|
|
|
const thinking = nodes[1]
|
|
expect(thinking?.type).toBe('thinking')
|
|
expect(thinking?.loading).toBe(true)
|
|
expect(String(thinking?.content ?? '')).toContain('### 勾股定理的定义:')
|
|
expect(String(thinking?.content ?? '')).toContain('---')
|
|
expect(String(thinking?.content ?? '')).toContain('$$\nc = \\sqrt{3^2 + 4^2} = \\sqrt{9 + 16} = \\sqrt{25} = 5\n$$')
|
|
expect(nodes.at(-1)?.type).toBe('thinking')
|
|
|
|
const innerNodes = parseMarkdownToStructure(String(thinking?.content ?? ''), getMarkdown('thinking-inline-prefix-streaming-inner'))
|
|
expect(innerNodes.some(node => node?.type === 'heading')).toBe(true)
|
|
expect(innerNodes.some(node => node?.type === 'thematic_break')).toBe(true)
|
|
expect(innerNodes.some(node => node?.type === 'math_block')).toBe(true)
|
|
})
|
|
|
|
it('keeps a stable top-level thinking node from the first streaming chunk when the prefix contains <br>', () => {
|
|
const tags = ['usrhead', 'aihead', 'thinking']
|
|
const md = getMarkdown('thinking-inline-prefix-streaming-stability', { customHtmlTags: tags })
|
|
const chunks = [
|
|
'<usrhead></usrhead><br>介绍下勾股定理<br><br><br><br><aihead></aihead><thinking>第',
|
|
'<usrhead></usrhead><br>介绍下勾股定理<br><br><br><br><aihead></aihead><thinking>第一段',
|
|
'<usrhead></usrhead><br>介绍下勾股定理<br><br><br><br><aihead></aihead><thinking>第一段\n\n### 标题',
|
|
]
|
|
|
|
for (const chunk of chunks) {
|
|
const nodes = parseMarkdownToStructure(chunk, md, { customHtmlTags: tags, final: false }) as any[]
|
|
expect(nodes.map(node => node?.type)).toEqual(['paragraph', 'thinking'])
|
|
expect(nodes[0]?.children?.some((node: any) => node?.type === 'thinking')).toBe(false)
|
|
expect(nodes[1]?.type).toBe('thinking')
|
|
expect(nodes[1]?.loading).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('keeps a stable top-level thinking node when the prefix is only <br> and the close arrives on the same line', () => {
|
|
const md = getMarkdown('thinking-br-prefix-same-line-stability', { customHtmlTags: ['thinking'] })
|
|
|
|
const openNodes = parseMarkdownToStructure('<br><thinking>hi', md, { customHtmlTags: ['thinking'], final: false }) as any[]
|
|
expect(openNodes.map(node => node?.type)).toEqual(['paragraph', 'thinking'])
|
|
expect(openNodes[0]?.children?.map((node: any) => node?.type)).toEqual(['hardbreak'])
|
|
expect(openNodes[1]?.type).toBe('thinking')
|
|
expect(openNodes[1]?.loading).toBe(true)
|
|
|
|
const closedNodes = parseMarkdownToStructure('<br><thinking>hi</thinking>', md, { customHtmlTags: ['thinking'], final: true }) as any[]
|
|
expect(closedNodes.map(node => node?.type)).toEqual(['paragraph', 'thinking'])
|
|
expect(closedNodes[0]?.children?.map((node: any) => node?.type)).toEqual(['hardbreak'])
|
|
expect(closedNodes[1]?.type).toBe('thinking')
|
|
expect(closedNodes[1]?.loading).toBe(false)
|
|
})
|
|
})
|