Files
boos e0a5f57793 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.
2026-06-22 11:32:57 +08:00

3.8 KiB
Raw Permalink Blame History

Code Block Rendering

Overview

Code blocks can be rendered in three ways depending on which optional dependencies you install and how you configure the library:

  • Monaco (recommended for large/interactive code blocks): installs and uses stream-monaco to provide an editor-like, incremental rendering experience. The library lazy-loads stream-monaco at runtime when available.
  • Shiki (MarkdownCodeBlockNode): install stream-markdown and override the code_block node via setCustomComponents to use a lightweight Markdown-driven renderer.
  • Fallback (no extra deps): if neither optional package is installed, code blocks render as plain <pre><code> blocks (basic styling / no Monaco features).
  • Install:
pnpm add stream-monaco
# or
npm i stream-monaco
  • Behavior: when stream-monaco is present the built-in CodeBlockNode will use Monaco-based streaming updates for large or frequently-updated code blocks.

  • Vite worker note: Monaco and some worker-backed features require appropriate worker bundling configuration in your bundler (Vite) so the editor/workers are available at runtime. See /nuxt-ssr for guidance and examples of configuring workers and client-only initialization.

  • See also: /guide/monaco for worker bundling tips and preload snippets.

Shiki mode (MarkdownCodeBlockNode)

  • Install:
pnpm add stream-markdown
# or
npm i stream-markdown
  • Override the code_block node via setCustomComponents to register the Shiki-based renderer:
import { MarkdownCodeBlockNode, setCustomComponents } from 'markstream-vue'

setCustomComponents({ code_block: MarkdownCodeBlockNode })

Once set, MarkdownCodeBlockNode (powered by Shiki via stream-markdown) will be used for code_block nodes. You can also supply your own component that uses stream-markdown directly.

Language icon lazy loading

To keep the main bundle smaller, infrequent language icons are split into an async chunk:

  • Common languages (JS/TS/HTML/CSS/JSON/Python/etc.) stay in the main bundle.
  • Rare languages load on demand and will update icon output automatically after the async chunk resolves.
  • If you prefer to avoid first-hit fallback icons, preload once during app idle:
import { preloadExtendedLanguageIcons } from 'markstream-vue'

if (typeof window !== 'undefined')
  void preloadExtendedLanguageIcons()

Vue CLI 4 (Webpack 4) notes

If you use Vue CLI 4 (Webpack 4), it’s recommended to use the Shiki mode for code blocks and override code_block to avoid Monaco + legacy-bundler edge cases.

Key pitfalls and fixes (see playground-vue2-cli):

  • Webpack 4 doesn’t support package.json#exports → prefer dist/* paths via resolve.alias.
  • ESM-only packages (like stream-markdown) may not be discoverable via require.resolve() inside vue.config.js (CJS) → use a filesystem fallback to find node_modules/stream-markdown, and alias it to dist/index.js.
  • If you use IgnorePlugin to skip optional deps, don’t accidentally ignore stream-markdown (otherwise you’ll get webpackMissingModule at runtime).

Fallback

If you don't install either optional package the renderer falls back to a simple pre/code representation.

Try this — simple CodeBlock render:

<script setup lang="ts">
import type { CodeBlockNodeProps } from 'markstream-vue'
import { CodeBlockNode } from 'markstream-vue'

const node = {
  type: 'code_block',
  language: 'js',
  code: 'console.log(42)',
  raw: 'console.log(42)',
} satisfies CodeBlockNodeProps['node']
</script>

<template>
  <CodeBlockNode :node="node" />
</template>