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:
@@ -0,0 +1,299 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { defineComponent, h } from 'vue'
|
||||
import NodeRenderer from '../packages/markstream-vue2/src/components/NodeRenderer/NodeRenderer.vue'
|
||||
import { removeCustomComponents, setCustomComponents } from '../packages/markstream-vue2/src/utils/nodeComponents'
|
||||
import { flushAll } from './setup/flush-all'
|
||||
|
||||
const customId = 'vue2-heavy-props-test'
|
||||
|
||||
const CustomMermaidProbe = defineComponent({
|
||||
name: 'CustomMermaidProbe',
|
||||
props: {
|
||||
node: { type: Object, required: true },
|
||||
showHeader: Boolean,
|
||||
showZoomControls: Boolean,
|
||||
renderDebounceMs: Number,
|
||||
previewPollDelayMs: Number,
|
||||
estimatedPreviewHeightPx: Number,
|
||||
},
|
||||
render() {
|
||||
return h('div', {
|
||||
'class': 'custom-mermaid-probe',
|
||||
'data-language': String((this as any).node?.language ?? ''),
|
||||
'data-show-header': String(this.showHeader),
|
||||
'data-show-zoom-controls': String(this.showZoomControls),
|
||||
'data-render-debounce-ms': String(this.renderDebounceMs),
|
||||
'data-preview-poll-delay-ms': String(this.previewPollDelayMs),
|
||||
'data-estimated-preview-height': String(this.estimatedPreviewHeightPx ?? ''),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const EstimatedPreviewProbe = defineComponent({
|
||||
name: 'EstimatedPreviewProbe',
|
||||
props: {
|
||||
node: { type: Object, required: true },
|
||||
estimatedPreviewHeightPx: Number,
|
||||
},
|
||||
render() {
|
||||
return h('div', {
|
||||
'class': 'estimated-preview-probe',
|
||||
'data-language': String((this as any).node?.language ?? ''),
|
||||
'data-estimated-preview-height': String((this as any).estimatedPreviewHeightPx ?? ''),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const ExactLanguageProbe = defineComponent({
|
||||
name: 'ExactLanguageProbe',
|
||||
props: {
|
||||
node: { type: Object, required: true },
|
||||
showHeader: Boolean,
|
||||
},
|
||||
render() {
|
||||
return h('div', {
|
||||
'class': 'exact-language-probe',
|
||||
'data-language': String((this as any).node?.language ?? ''),
|
||||
'data-show-header': String((this as any).showHeader),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const GenericCodeBlockProbe = defineComponent({
|
||||
name: 'GenericCodeBlockProbe',
|
||||
props: {
|
||||
node: { type: Object, required: true },
|
||||
showHeader: Boolean,
|
||||
},
|
||||
render() {
|
||||
return h('div', {
|
||||
'class': 'generic-code-block-probe',
|
||||
'data-language': String((this as any).node?.language ?? ''),
|
||||
'data-show-header': String((this as any).showHeader),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const CustomD2Probe = defineComponent({
|
||||
name: 'CustomD2Probe',
|
||||
props: {
|
||||
node: { type: Object, required: true },
|
||||
themeId: Number,
|
||||
},
|
||||
render() {
|
||||
return h('div', {
|
||||
'class': 'custom-d2-probe',
|
||||
'data-language': String((this as any).node?.language ?? ''),
|
||||
'data-theme-id': String((this as any).themeId),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const CustomD2LangProbe = defineComponent({
|
||||
name: 'CustomD2LangProbe',
|
||||
props: {
|
||||
node: { type: Object, required: true },
|
||||
themeId: Number,
|
||||
},
|
||||
render() {
|
||||
return h('div', {
|
||||
'class': 'custom-d2lang-probe',
|
||||
'data-language': String((this as any).node?.language ?? ''),
|
||||
'data-theme-id': String((this as any).themeId),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
describe('markstream-vue2 heavy-node prop forwarding', () => {
|
||||
afterEach(() => {
|
||||
removeCustomComponents(customId)
|
||||
})
|
||||
|
||||
it('renders a reserved Mermaid shell before the async component resolves', () => {
|
||||
const wrapper = mount(NodeRenderer as any, {
|
||||
props: {
|
||||
nodes: [
|
||||
{
|
||||
type: 'code_block',
|
||||
language: 'mermaid',
|
||||
code: 'flowchart TD\nA-->B\nB-->C\nC-->D\nD-->E\nE-->F\nF-->G\nG-->H\nH-->I\nI-->J\nJ-->K\nK-->L\n',
|
||||
raw: '```mermaid\nflowchart TD\nA-->B\n```',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const shell = wrapper.get('[data-markstream-mermaid="1"]')
|
||||
expect(shell.attributes('data-markstream-mode')).toBe('pending')
|
||||
expect((shell.get('.mermaid-preview-area').element as HTMLElement).style.height).toBe('500px')
|
||||
})
|
||||
|
||||
it('renders a reserved Infographic shell before the async component resolves', () => {
|
||||
const wrapper = mount(NodeRenderer as any, {
|
||||
props: {
|
||||
nodes: [
|
||||
{
|
||||
type: 'code_block',
|
||||
language: 'infographic',
|
||||
code: [
|
||||
'# Release progress',
|
||||
'- Plan: complete',
|
||||
'- Build: active',
|
||||
'- Verify: pending',
|
||||
].join('\n'),
|
||||
raw: '```infographic\n# Release progress\n- Plan: complete\n```',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const shell = wrapper.get('[data-markstream-infographic="1"]')
|
||||
expect(shell.attributes('data-markstream-mode')).toBe('pending')
|
||||
expect((shell.get('.infographic-preview').element as HTMLElement).style.height).toBe('500px')
|
||||
})
|
||||
|
||||
it('injects stable preview height estimates for Mermaid and Infographic custom renderers', async () => {
|
||||
setCustomComponents(customId, {
|
||||
mermaid: EstimatedPreviewProbe as any,
|
||||
infographic: EstimatedPreviewProbe as any,
|
||||
})
|
||||
|
||||
const wrapper = mount(NodeRenderer as any, {
|
||||
props: {
|
||||
customId,
|
||||
nodes: [
|
||||
{
|
||||
type: 'code_block',
|
||||
language: 'mermaid',
|
||||
code: 'flowchart TD\nA-->B\nB-->C\nC-->D\nD-->E\nE-->F\nF-->G\nG-->H\nH-->I\nI-->J\nJ-->K\nK-->L\n',
|
||||
raw: '```mermaid\nflowchart TD\nA-->B\n```',
|
||||
},
|
||||
{
|
||||
type: 'code_block',
|
||||
language: 'infographic',
|
||||
code: [
|
||||
'# Release progress',
|
||||
'- Plan: complete',
|
||||
'- Build: active',
|
||||
'- Verify: pending',
|
||||
].join('\n'),
|
||||
raw: '```infographic\n# Release progress\n- Plan: complete\n```',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
await flushAll()
|
||||
|
||||
const probes = wrapper.findAll('.estimated-preview-probe')
|
||||
expect(probes).toHaveLength(2)
|
||||
expect(probes[0].attributes('data-language')).toBe('mermaid')
|
||||
expect(probes[0].attributes('data-estimated-preview-height')).toBe('500')
|
||||
expect(probes[1].attributes('data-language')).toBe('infographic')
|
||||
expect(probes[1].attributes('data-estimated-preview-height')).toBe('500')
|
||||
})
|
||||
|
||||
it('prefers exact language overrides over code_block fallback for custom languages', async () => {
|
||||
setCustomComponents(customId, {
|
||||
echarts: ExactLanguageProbe as any,
|
||||
code_block: GenericCodeBlockProbe as any,
|
||||
})
|
||||
|
||||
const wrapper = mount(NodeRenderer as any, {
|
||||
props: {
|
||||
customId,
|
||||
codeBlockProps: {
|
||||
showHeader: false,
|
||||
},
|
||||
nodes: [
|
||||
{
|
||||
type: 'code_block',
|
||||
language: 'echarts',
|
||||
code: 'option = {}',
|
||||
raw: '```echarts\noption = {}\n```',
|
||||
},
|
||||
{
|
||||
type: 'code_block',
|
||||
language: 'ts',
|
||||
code: 'export const value = 1',
|
||||
raw: '```ts\nexport const value = 1\n```',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
await flushAll()
|
||||
|
||||
const exact = wrapper.get('.exact-language-probe')
|
||||
const generic = wrapper.get('.generic-code-block-probe')
|
||||
expect(exact.attributes('data-language')).toBe('echarts')
|
||||
expect(exact.attributes('data-show-header')).toBe('false')
|
||||
expect(generic.attributes('data-language')).toBe('ts')
|
||||
expect(generic.attributes('data-show-header')).toBe('false')
|
||||
})
|
||||
|
||||
it('lets d2lang exact overrides beat d2 fallback while keeping d2 props', async () => {
|
||||
setCustomComponents(customId, {
|
||||
d2: CustomD2Probe as any,
|
||||
d2lang: CustomD2LangProbe as any,
|
||||
})
|
||||
|
||||
const wrapper = mount(NodeRenderer as any, {
|
||||
props: {
|
||||
customId,
|
||||
d2Props: {
|
||||
themeId: 7,
|
||||
},
|
||||
nodes: [
|
||||
{
|
||||
type: 'code_block',
|
||||
language: 'd2lang',
|
||||
code: 'a -> b',
|
||||
raw: '```d2lang\na -> b\n```',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
await flushAll()
|
||||
|
||||
expect(wrapper.find('.custom-d2-probe').exists()).toBe(false)
|
||||
const exact = wrapper.get('.custom-d2lang-probe')
|
||||
expect(exact.attributes('data-language')).toBe('d2lang')
|
||||
expect(exact.attributes('data-theme-id')).toBe('7')
|
||||
})
|
||||
|
||||
it('forwards mermaidProps to custom mermaid renderers', async () => {
|
||||
setCustomComponents(customId, { mermaid: CustomMermaidProbe })
|
||||
|
||||
const wrapper = mount(NodeRenderer as any, {
|
||||
props: {
|
||||
customId,
|
||||
nodes: [
|
||||
{
|
||||
type: 'code_block',
|
||||
language: 'mermaid',
|
||||
code: 'graph LR\nA-->B\n',
|
||||
raw: '```mermaid\ngraph LR\nA-->B\n```',
|
||||
},
|
||||
],
|
||||
mermaidProps: {
|
||||
showHeader: false,
|
||||
showZoomControls: false,
|
||||
renderDebounceMs: 180,
|
||||
previewPollDelayMs: 500,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await flushAll()
|
||||
|
||||
const probe = wrapper.get('.custom-mermaid-probe')
|
||||
expect(probe.attributes('data-show-header')).toBe('false')
|
||||
expect(probe.attributes('data-show-zoom-controls')).toBe('false')
|
||||
expect(probe.attributes('data-render-debounce-ms')).toBe('180')
|
||||
expect(probe.attributes('data-preview-poll-delay-ms')).toBe('500')
|
||||
expect(probe.attributes('data-estimated-preview-height')).toBe('360')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user