feat(desktop): embed BoosAPI Desktop client as a subproject

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.
This commit is contained in:
boos
2026-06-22 11:32:57 +08:00
parent d9c78ecc73
commit e0a5f57793
2305 changed files with 449355 additions and 0 deletions
@@ -0,0 +1,113 @@
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { mount } from '@vue/test-utils'
import { getMarkdown, parseMarkdownToStructure } from 'stream-markdown-parser'
import { describe, expect, it, vi } from 'vitest'
import NodeRenderer from '../packages/markstream-vue2/src/components/NodeRenderer/NodeRenderer.vue'
import { flushAll } from './setup/flush-all'
describe('markstream-vue2 text node streaming consistency', () => {
it('keeps the stale-delta settle hook wired to outer stream ticks', () => {
const nodeRendererSource = readFileSync(
resolve(process.cwd(), 'packages/markstream-vue2/src/components/NodeRenderer/NodeRenderer.vue'),
'utf8',
)
const textNodeSource = readFileSync(
resolve(process.cwd(), 'packages/markstream-vue2/src/components/TextNode/TextNode.vue'),
'utf8',
)
expect(nodeRendererSource).toContain('const streamRenderVersion = ref(0)')
expect(nodeRendererSource).toContain('provide(\'markstreamStreamVersion\', streamRenderVersion)')
expect(nodeRendererSource).toContain('[() => props.content, () => props.nodes]')
expect(textNodeSource).toContain('inject<{ value?: number } | undefined>(\'markstreamStreamVersion\', undefined)')
expect(textNodeSource).toContain('[() => props.node.content, streamStateKey, typewriterEnabled, () => inheritedStreamVersion?.value]')
expect(textNodeSource).toContain('if (normalized === previousContent)')
expect(textNodeSource).toContain('if (streamedDelta.value)')
expect(textNodeSource).toContain('settleStreamedDelta()')
})
it('keeps explanatory list text visible while an inline code span is still streaming', async () => {
const originalWarn = console.warn
const originalError = console.error
const warnSpy = vi.spyOn(console, 'warn').mockImplementation((message?: any, ...args: any[]) => {
const text = String(message ?? '')
if (
text.includes('provide() can only be used inside setup().')
|| text.includes('onMounted is called when there is no active component instance')
|| text.includes('onBeforeUnmount is called when there is no active component instance')
) {
return
}
originalWarn.call(console, message, ...args)
})
const errorSpy = vi.spyOn(console, 'error').mockImplementation((message?: any, ...args: any[]) => {
const text = String(message ?? '')
if (
text.includes('provide() can only be used inside setup().')
|| text.includes('onMounted is called when there is no active component instance')
|| text.includes('onBeforeUnmount is called when there is no active component instance')
) {
return
}
originalError.call(console, message, ...args)
})
const initial = `- **计算工具验证**
通过数学计算工具确认结果:`
const mid = `${initial}
\`363 ÷ 15,135 × 100 = 2.39841427...`
const final = `${mid}\``
const md = getMarkdown('vue2-inline-code-explanatory-text-stability')
const getListItemChildren = (content: string) => {
const nodes = parseMarkdownToStructure(content, md, { final: false }) as any[]
return (nodes[0] as any)?.items?.[0]?.children ?? []
}
const initialNodes = getListItemChildren(initial)
const midNodes = getListItemChildren(mid)
const finalNodes = getListItemChildren(final)
const getParagraphChildren = (nodes: any[]) => (nodes[0] as any)?.children ?? []
expect(getParagraphChildren(midNodes).some((child: any) => child?.type === 'inline_code')).toBe(true)
expect(getParagraphChildren(finalNodes).some((child: any) => child?.type === 'inline_code')).toBe(true)
const wrapper = mount(NodeRenderer as any, {
props: {
nodes: initialNodes as any,
batchRendering: false,
deferNodesUntilVisible: false,
viewportPriority: false,
maxLiveNodes: 0,
},
})
try {
await flushAll()
await wrapper.setProps({
nodes: midNodes as any,
})
await flushAll()
const midText = wrapper.text().replace(/\s*\n\s*/g, '')
expect(midText).toContain('计算工具验证通过数学计算工具确认结果:')
expect(midText.match(/通过数学计算工具确认结果:/g) ?? []).toHaveLength(1)
expect(wrapper.get('.strong-node').text()).toBe('计算工具验证')
await wrapper.setProps({
nodes: finalNodes as any,
})
await flushAll()
const finalText = wrapper.text().replace(/\s*\n\s*/g, '')
expect(finalText).toContain('计算工具验证通过数学计算工具确认结果:')
expect(finalText.match(/通过数学计算工具确认结果:/g) ?? []).toHaveLength(1)
expect(wrapper.get('.strong-node').text()).toBe('计算工具验证')
}
finally {
wrapper.unmount()
warnSpy.mockRestore()
errorSpy.mockRestore()
}
})
})