forked from boos/BoosAPI
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,81 @@
|
||||
export type D2Loader = () => Promise<any> | any
|
||||
|
||||
const defaultD2Loader: D2Loader = () => import('@terrastruct/d2')
|
||||
|
||||
let cachedD2: any = null
|
||||
let d2Loader: D2Loader | null = defaultD2Loader
|
||||
let pendingImport: Promise<any | null> | null = null
|
||||
|
||||
function resetCachedD2() {
|
||||
cachedD2 = null
|
||||
pendingImport = null
|
||||
}
|
||||
|
||||
function normalizeD2Module(mod: any) {
|
||||
if (!mod)
|
||||
return mod
|
||||
if (mod.D2 && typeof mod.D2 === 'function')
|
||||
return mod.D2
|
||||
if (mod.default && mod.default.D2 && typeof mod.default.D2 === 'function')
|
||||
return mod.default.D2
|
||||
const candidate = mod.default ?? mod
|
||||
if (typeof candidate === 'function')
|
||||
return candidate
|
||||
if (candidate?.D2 && typeof candidate.D2 === 'function')
|
||||
return candidate.D2
|
||||
return candidate
|
||||
}
|
||||
|
||||
export function setD2Loader(loader: D2Loader | null) {
|
||||
d2Loader = loader
|
||||
resetCachedD2()
|
||||
}
|
||||
|
||||
export function enableD2(loader?: D2Loader) {
|
||||
setD2Loader(loader ?? defaultD2Loader)
|
||||
}
|
||||
|
||||
export function disableD2() {
|
||||
setD2Loader(null)
|
||||
}
|
||||
|
||||
export function isD2Enabled() {
|
||||
return typeof d2Loader === 'function'
|
||||
}
|
||||
|
||||
export async function getD2() {
|
||||
if (cachedD2)
|
||||
return cachedD2
|
||||
if (pendingImport)
|
||||
return await pendingImport
|
||||
|
||||
const loader = d2Loader
|
||||
if (!loader)
|
||||
return null
|
||||
|
||||
pendingImport = (async () => {
|
||||
let mod: any
|
||||
try {
|
||||
mod = await loader()
|
||||
}
|
||||
catch (error) {
|
||||
if (loader === defaultD2Loader) {
|
||||
throw new Error('Optional dependency "@terrastruct/d2" is not installed. Please install it to enable D2 diagrams.')
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
if (!mod)
|
||||
return null
|
||||
|
||||
cachedD2 = normalizeD2Module(mod)
|
||||
return cachedD2
|
||||
})()
|
||||
|
||||
try {
|
||||
return await pendingImport
|
||||
}
|
||||
finally {
|
||||
pendingImport = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
let infographicPromise: Promise<any> | null = null
|
||||
let infographicInstance: any = null
|
||||
|
||||
export async function getInfographic() {
|
||||
if (infographicInstance)
|
||||
return infographicInstance
|
||||
if (infographicPromise)
|
||||
return await infographicPromise
|
||||
|
||||
infographicPromise = import('@antv/infographic')
|
||||
.then((mod) => {
|
||||
const defaultExport = (mod && (mod as any).default) ? (mod as any).default : mod
|
||||
|
||||
let resolved = defaultExport
|
||||
|
||||
if (typeof defaultExport === 'function' && defaultExport.prototype && defaultExport.prototype.render) {
|
||||
resolved = defaultExport
|
||||
}
|
||||
else if ((mod as any)?.Infographic) {
|
||||
resolved = (mod as any).Infographic
|
||||
}
|
||||
else if (defaultExport && defaultExport.Infographic) {
|
||||
resolved = defaultExport.Infographic
|
||||
}
|
||||
|
||||
infographicInstance = resolved
|
||||
return resolved
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('[markstream-angular] Failed to load @antv/infographic', error)
|
||||
return null
|
||||
})
|
||||
|
||||
return await infographicPromise
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
export type KatexLoader = () => Promise<any> | any
|
||||
|
||||
let katex: any = null
|
||||
let importAttempted = false
|
||||
let pendingImport: Promise<any | null> | null = null
|
||||
let katexLoader: KatexLoader | null = defaultKatexLoader
|
||||
|
||||
function normalizeKatexModule(mod: any) {
|
||||
const resolved = mod?.default ?? mod
|
||||
if (resolved && typeof resolved.renderToString === 'function')
|
||||
return resolved
|
||||
return null
|
||||
}
|
||||
|
||||
function getGlobalKatex() {
|
||||
try {
|
||||
const globalStore = globalThis as any
|
||||
return normalizeKatexModule(globalStore?.katex)
|
||||
}
|
||||
catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function defaultKatexLoader() {
|
||||
return (async () => {
|
||||
const globalKatex = getGlobalKatex()
|
||||
if (globalKatex)
|
||||
return globalKatex
|
||||
|
||||
const mod = await import('katex')
|
||||
try {
|
||||
await import('katex/contrib/mhchem')
|
||||
}
|
||||
catch {
|
||||
// Ignore optional mhchem support failures.
|
||||
}
|
||||
return normalizeKatexModule(mod)
|
||||
})()
|
||||
}
|
||||
|
||||
function resetCache() {
|
||||
katex = null
|
||||
importAttempted = false
|
||||
pendingImport = null
|
||||
}
|
||||
|
||||
export function setKatexLoader(loader: KatexLoader | null) {
|
||||
katexLoader = loader
|
||||
resetCache()
|
||||
}
|
||||
|
||||
export function enableKatex(loader?: KatexLoader) {
|
||||
setKatexLoader(loader ?? defaultKatexLoader)
|
||||
}
|
||||
|
||||
export function disableKatex() {
|
||||
setKatexLoader(null)
|
||||
}
|
||||
|
||||
export function isKatexEnabled() {
|
||||
return typeof katexLoader === 'function'
|
||||
}
|
||||
|
||||
export async function getKatex() {
|
||||
const globalKatex = getGlobalKatex()
|
||||
if (globalKatex) {
|
||||
katex = globalKatex
|
||||
return katex
|
||||
}
|
||||
|
||||
if (katex)
|
||||
return katex
|
||||
if (pendingImport)
|
||||
return await pendingImport
|
||||
if (importAttempted)
|
||||
return null
|
||||
|
||||
const loader = katexLoader
|
||||
if (!loader) {
|
||||
importAttempted = true
|
||||
return null
|
||||
}
|
||||
|
||||
pendingImport = (async () => {
|
||||
try {
|
||||
const result = await loader()
|
||||
const normalized = normalizeKatexModule(result) ?? result
|
||||
if (normalized) {
|
||||
katex = normalized
|
||||
return katex
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// Swallow loader errors so callers can gracefully fall back.
|
||||
}
|
||||
|
||||
importAttempted = true
|
||||
return null
|
||||
})()
|
||||
|
||||
try {
|
||||
return await pendingImport
|
||||
}
|
||||
finally {
|
||||
pendingImport = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
export type MermaidLoader = () => Promise<any> | any
|
||||
|
||||
const defaultMermaidLoader: MermaidLoader = () => import('mermaid')
|
||||
const GLOBAL_MERMAID_KEY = '__MARKSTREAM_ANGULAR_MERMAID__'
|
||||
|
||||
let cachedMermaid: any = null
|
||||
let importAttempted = false
|
||||
let lastInitKey: string | null = null
|
||||
let pendingImport: Promise<any | null> | null = null
|
||||
let mermaidLoader: MermaidLoader | null = defaultMermaidLoader
|
||||
|
||||
function computeInitKey(config: Record<string, any>) {
|
||||
const securityLevel = String(config?.securityLevel ?? 'loose')
|
||||
const htmlLabels = config?.flowchart?.htmlLabels
|
||||
return `${securityLevel}|htmlLabels:${htmlLabels === false ? '0' : '1'}`
|
||||
}
|
||||
|
||||
function normalizeMermaidModule(mod: any) {
|
||||
if (!mod)
|
||||
return mod
|
||||
|
||||
const candidate = mod?.default ?? mod
|
||||
if (candidate && (typeof candidate.render === 'function' || typeof candidate.parse === 'function' || typeof candidate.initialize === 'function'))
|
||||
return candidate
|
||||
|
||||
if (candidate?.mermaidAPI && (typeof candidate.mermaidAPI.render === 'function' || typeof candidate.mermaidAPI.parse === 'function')) {
|
||||
const api = candidate.mermaidAPI
|
||||
return {
|
||||
...candidate,
|
||||
render: api.render.bind(api),
|
||||
parse: api.parse ? api.parse.bind(api) : undefined,
|
||||
initialize: (opts: any) => {
|
||||
if (typeof candidate.initialize === 'function')
|
||||
return candidate.initialize(opts)
|
||||
return api.initialize ? api.initialize(opts) : undefined
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (mod?.mermaid && typeof mod.mermaid.render === 'function')
|
||||
return mod.mermaid
|
||||
|
||||
return candidate
|
||||
}
|
||||
|
||||
function patchInitialize(target: any) {
|
||||
if (!target)
|
||||
return
|
||||
|
||||
try {
|
||||
const originalInitialize = target?.initialize
|
||||
target.initialize = (opts: any) => {
|
||||
const merged = { suppressErrorRendering: true, ...(opts || {}) }
|
||||
if (typeof originalInitialize === 'function')
|
||||
return originalInitialize.call(target, merged)
|
||||
if (target?.mermaidAPI && typeof target.mermaidAPI.initialize === 'function')
|
||||
return target.mermaidAPI.initialize(merged)
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// Ignore patching failures and keep the original instance.
|
||||
}
|
||||
}
|
||||
|
||||
function ensureInitialized(instance: any, config?: Record<string, any>) {
|
||||
if (!instance || !config)
|
||||
return
|
||||
|
||||
const key = computeInitKey(config)
|
||||
if (lastInitKey === key)
|
||||
return
|
||||
|
||||
try {
|
||||
if (typeof instance.initialize === 'function')
|
||||
instance.initialize(config)
|
||||
else if (instance.mermaidAPI?.initialize)
|
||||
instance.mermaidAPI.initialize(config)
|
||||
lastInitKey = key
|
||||
}
|
||||
catch {
|
||||
// Mermaid may already be initialized; keep going.
|
||||
}
|
||||
}
|
||||
|
||||
function getGlobalMermaid() {
|
||||
try {
|
||||
const globalStore = globalThis as any
|
||||
return normalizeMermaidModule(globalStore?.[GLOBAL_MERMAID_KEY] ?? globalStore?.mermaid)
|
||||
}
|
||||
catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function setGlobalMermaid(instance: any) {
|
||||
try {
|
||||
;(globalThis as any)[GLOBAL_MERMAID_KEY] = instance
|
||||
}
|
||||
catch {
|
||||
// Ignore global cache writes when the host forbids mutation.
|
||||
}
|
||||
}
|
||||
|
||||
function resetCachedMermaid() {
|
||||
cachedMermaid = null
|
||||
importAttempted = false
|
||||
lastInitKey = null
|
||||
pendingImport = null
|
||||
try {
|
||||
delete (globalThis as any)[GLOBAL_MERMAID_KEY]
|
||||
}
|
||||
catch {
|
||||
// Ignore failures when the global object is sealed.
|
||||
}
|
||||
}
|
||||
|
||||
export function setMermaidLoader(loader: MermaidLoader | null) {
|
||||
mermaidLoader = loader
|
||||
resetCachedMermaid()
|
||||
}
|
||||
|
||||
export function enableMermaid(loader?: MermaidLoader) {
|
||||
setMermaidLoader(loader ?? defaultMermaidLoader)
|
||||
}
|
||||
|
||||
export function disableMermaid() {
|
||||
setMermaidLoader(null)
|
||||
}
|
||||
|
||||
export function isMermaidEnabled() {
|
||||
return typeof mermaidLoader === 'function'
|
||||
}
|
||||
|
||||
export async function getMermaid(initConfig?: Record<string, any>) {
|
||||
if (cachedMermaid) {
|
||||
ensureInitialized(cachedMermaid, initConfig)
|
||||
return cachedMermaid
|
||||
}
|
||||
|
||||
const globalMermaid = getGlobalMermaid()
|
||||
if (globalMermaid) {
|
||||
patchInitialize(globalMermaid)
|
||||
cachedMermaid = globalMermaid
|
||||
setGlobalMermaid(cachedMermaid)
|
||||
ensureInitialized(cachedMermaid, initConfig)
|
||||
return cachedMermaid
|
||||
}
|
||||
|
||||
if (pendingImport) {
|
||||
const instance = await pendingImport
|
||||
if (instance)
|
||||
ensureInitialized(instance, initConfig)
|
||||
return instance
|
||||
}
|
||||
|
||||
if (importAttempted)
|
||||
return null
|
||||
|
||||
const loader = mermaidLoader
|
||||
if (!loader) {
|
||||
importAttempted = true
|
||||
return null
|
||||
}
|
||||
|
||||
pendingImport = (async () => {
|
||||
try {
|
||||
const mod = await loader()
|
||||
const instance = normalizeMermaidModule(mod)
|
||||
if (!instance) {
|
||||
importAttempted = true
|
||||
return null
|
||||
}
|
||||
patchInitialize(instance)
|
||||
cachedMermaid = instance
|
||||
setGlobalMermaid(cachedMermaid)
|
||||
return cachedMermaid
|
||||
}
|
||||
catch {
|
||||
importAttempted = true
|
||||
return null
|
||||
}
|
||||
})()
|
||||
|
||||
try {
|
||||
const instance = await pendingImport
|
||||
if (instance)
|
||||
ensureInitialized(instance, initConfig)
|
||||
return instance
|
||||
}
|
||||
finally {
|
||||
pendingImport = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
let monacoModule: any = null
|
||||
let importAttempted = false
|
||||
let pendingImport: Promise<any | null> | null = null
|
||||
let workersPreloaded = false
|
||||
|
||||
async function preloadWorkers(mod: any) {
|
||||
if (workersPreloaded)
|
||||
return
|
||||
workersPreloaded = true
|
||||
const existingEnv = (globalThis as any)?.MonacoEnvironment
|
||||
if (existingEnv && (typeof existingEnv.getWorker === 'function' || typeof existingEnv.getWorkerUrl === 'function'))
|
||||
return
|
||||
if (typeof mod?.preloadMonacoWorkers === 'function')
|
||||
await mod.preloadMonacoWorkers()
|
||||
}
|
||||
|
||||
async function warmupShikiTokenizer(mod: any) {
|
||||
const getOrCreateHighlighter = mod?.getOrCreateHighlighter
|
||||
if (typeof getOrCreateHighlighter !== 'function')
|
||||
return true
|
||||
|
||||
try {
|
||||
const highlighter = await getOrCreateHighlighter(
|
||||
['vitesse-dark', 'vitesse-light'],
|
||||
['plaintext', 'text', 'javascript'],
|
||||
)
|
||||
|
||||
if (highlighter && typeof highlighter.codeToTokens === 'function') {
|
||||
highlighter.codeToTokens('const a = 1', { lang: 'javascript', theme: 'vitesse-dark' })
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
catch (error) {
|
||||
console.warn('[markstream-angular] Failed to warm up stream-monaco tokenizer; falling back to plain code rendering.', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUseMonaco() {
|
||||
if (monacoModule)
|
||||
return monacoModule
|
||||
if (pendingImport)
|
||||
return await pendingImport
|
||||
if (importAttempted)
|
||||
return null
|
||||
|
||||
pendingImport = (async () => {
|
||||
try {
|
||||
const imported: any = await import('stream-monaco')
|
||||
monacoModule = imported?.default ?? imported
|
||||
await preloadWorkers(monacoModule)
|
||||
|
||||
const ready = await warmupShikiTokenizer(monacoModule)
|
||||
if (!ready) {
|
||||
monacoModule = null
|
||||
importAttempted = true
|
||||
return null
|
||||
}
|
||||
|
||||
return monacoModule
|
||||
}
|
||||
catch {
|
||||
importAttempted = true
|
||||
return null
|
||||
}
|
||||
})()
|
||||
|
||||
try {
|
||||
return await pendingImport
|
||||
}
|
||||
finally {
|
||||
pendingImport = null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user