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,111 @@
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { getMarkdown, parseMarkdownToStructure } from 'stream-markdown-parser'
import { describe, expect, it } from 'vitest'
const md = getMarkdown('e2e')
const FIXTURES_DIR = resolve(__dirname, 'fixtures')
describe('e2e markdown parsing (fixtures)', () => {
const files = [
'headings.md',
'code-diff.md',
'table.md',
'admonition.md',
'math.md',
'footnotes.md',
'image-link.md',
'checkbox.md',
'mermaid.md',
// edge-case fixtures
'unclosed-fence.md',
'trailing-backticks.md',
'unmatched-brackets.md',
'escaped-brackets.md',
'nested-lists-edge.md',
'fence-with-meta.md',
]
for (const f of files) {
it(`parses fixture ${f}`, () => {
const src = readFileSync(resolve(FIXTURES_DIR, f), 'utf8')
const nodes = parseMarkdownToStructure(src, md)
expect(nodes).toBeInstanceOf(Array)
// Ensure we have at least one top-level node
expect(nodes.length).toBeGreaterThan(0)
// Create a stable, minimal serialization for snapshot
// Helper to find first descendant text for a node
const findFirstText = (node: any): string | undefined => {
if (!node)
return undefined
if (node.type === 'text' && typeof node.content === 'string')
return node.content.slice(0, 80)
if (Array.isArray(node.children)) {
for (const c of node.children) {
const t = findFirstText(c)
if (t)
return t
}
}
return undefined
}
const pickMeta = (meta: any) => {
if (!meta || typeof meta !== 'object')
return undefined
const keys = ['diff', 'language', 'closed', 'unclosed']
const out: any = {}
for (const k of keys) {
if (k in meta)
out[k] = meta[k]
}
return Object.keys(out).length ? out : undefined
}
const minimal = nodes.map((n: any) => {
const out: any = { type: n.type }
out.firstText = findFirstText(n)
if (n.meta)
out.meta = pickMeta(n.meta)
if (n.type === 'heading') {
out.level = (n as any).level
out.children = (n as any).children?.map((c: any) => c.type)
}
else if (n.type === 'list') {
out.ordered = (n as any).ordered
out.items = (n as any).items?.length ?? 0
}
else if (n.type === 'code_block') {
out.language = (n as any).language
out.diff = !!(n as any).diff
// include a short preview of code
out.preview = (n as any).code ? (n as any).code.slice(0, 120) : undefined
}
else if (n.type === 'table') {
out.rows = (n as any).rows?.length ?? 0
}
else if (n.type === 'footnote') {
out.items = (n as any).items?.length ?? 0
}
else {
// generic: record if has children
out.children = (n as any).children ? (n as any).children.map((item: any) => item.type) : undefined
}
// Tolerate environment differences for escaped backslash in a specific fixture
if (f === 'escaped-brackets.md' && typeof out.firstText === 'string') {
// Normalize to snapshot’s expected form (no visible backslash)
out.firstText = out.firstText
.replace('literal backslash \\ and', 'literal backslash and')
.replace('literal backslash and', 'literal backslash and')
}
return out
})
// In some environments, a literal backslash may be normalized in text nodes.
// Keep snapshot tolerant to a missing visual backslash by stringifying the object.
expect(minimal).toMatchSnapshot(f)
})
}
})