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,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<meta
|
||||
name="description"
|
||||
content="markstream-vue2 playground for Vue 2.7 streaming Markdown, Mermaid, KaTeX, and large-document rendering demos."
|
||||
/>
|
||||
<title>markstream-vue2 Playground | Vue 2.7 demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
[build]
|
||||
publish = "dist"
|
||||
command = "npx pnpm i && pnpm --filter stream-markdown-parser build && pnpm --filter markstream-vue2 build && npm run build"
|
||||
|
||||
[build.environment]
|
||||
NPM_FLAGS = "--version"
|
||||
NODE_VERSION = "22"
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
status = 200
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "playground-vue2",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --port 3334 --open",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"katex": "^0.16.44",
|
||||
"markstream-vue2": "workspace:*",
|
||||
"mermaid": "^11.13.0",
|
||||
"stream-monaco": "latest",
|
||||
"vue": "2.7.16",
|
||||
"vue-demi": "^0.14.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@unocss/reset": "^0.61.9",
|
||||
"@vitejs/plugin-vue2": "^2.3.4",
|
||||
"@vue/compiler-sfc": "2.7.16",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"postcss-nesting": "^13.0.2",
|
||||
"vite": "^4.5.14",
|
||||
"vite-plugin-monaco-editor-esm": "^2.0.2",
|
||||
"vue-template-compiler": "^2.7.16"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
plugins: [require('postcss-nesting'), require('tailwindcss'), require('autoprefixer')],
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Cloudflare Pages 重定向规则
|
||||
# SPA 路由: 将所有请求重定向到 index.html
|
||||
# https://developers.cloudflare.com/pages/configuration/redirects/
|
||||
|
||||
/* /index.html 200
|
||||
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<style>
|
||||
path { fill: #222; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #ffffff; }
|
||||
}
|
||||
</style>
|
||||
<path d="M27.562 26L17.17 8.928l2.366-3.888L17.828 4L16 7.005L14.17 4l-1.708 1.04l2.366 3.888L4.438 26H2v2h28v-2zM16 10.85L25.22 26H17v-8h-2v8H6.78z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 347 B |
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="Vue Markdown Renderer">
|
||||
<title>Vue Markdown Renderer</title>
|
||||
<defs>
|
||||
<linearGradient id="g-vue" x1="0" x2="1" y1="0" y2="1">
|
||||
<stop offset="0" stop-color="#41b883" />
|
||||
<stop offset="1" stop-color="#2c9f67" />
|
||||
</linearGradient>
|
||||
<linearGradient id="g-accent" x1="0" x2="1" y1="0" y2="1">
|
||||
<stop offset="0" stop-color="#3aa8ff" />
|
||||
<stop offset="1" stop-color="#5cc6ff" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- background card -->
|
||||
<rect x="6" y="6" width="116" height="116" rx="16" fill="#0b1220" />
|
||||
<rect x="12" y="12" width="104" height="104" rx="12" fill="#ffffff" />
|
||||
|
||||
<!-- folded corner -->
|
||||
<path d="M92 12 H108 V28" fill="#f6f7f8" opacity="0.9" />
|
||||
<path d="M92 12 L108 28" stroke="#e6e6e6" stroke-width="1.5" fill="none" />
|
||||
|
||||
<!-- document area -->
|
||||
<rect x="28" y="26" width="72" height="76" rx="6" fill="#ffffff" />
|
||||
|
||||
<!-- Vue chevron (simplified) -->
|
||||
<g transform="translate(34,30)">
|
||||
<path d="M10 0 L24 24 L0 24 Z" fill="url(#g-vue)" />
|
||||
<path d="M10 6 L24 30 L0 30 Z" fill="#063" opacity="0.95" />
|
||||
</g>
|
||||
|
||||
<!-- Markdown hash (#) drawn with strokes (reliable across renderers) -->
|
||||
<g transform="translate(64,46)" stroke="#0b1220" stroke-width="4" stroke-linecap="round">
|
||||
<line x1="-12" y1="-4" x2="12" y2="-4" />
|
||||
<line x1="-12" y1="8" x2="12" y2="8" />
|
||||
<line x1="-6" y1="-12" x2="-6" y2="20" />
|
||||
<line x1="6" y1="-12" x2="6" y2="20" />
|
||||
</g>
|
||||
|
||||
<!-- small code brackets to indicate renderer -->
|
||||
<g transform="translate(36,84)" stroke="#0b1220" stroke-width="3" fill="none" stroke-linecap="round">
|
||||
<path d="M0 0 L8 8 L0 16" />
|
||||
<path d="M56 0 L48 8 L56 16" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,209 @@
|
||||
<script>
|
||||
import MarkdownRender, { setCustomComponents } from 'markstream-vue2'
|
||||
import { streamContent } from '../../playground/src/const/markdown'
|
||||
import TestLab from './components/TestLab.vue'
|
||||
import ThinkingNode from './components/ThinkingNode.vue'
|
||||
|
||||
const markdownSource = typeof streamContent === 'string' ? streamContent : String(streamContent || '')
|
||||
setCustomComponents('vue2-demo', { thinking: ThinkingNode })
|
||||
|
||||
export default {
|
||||
name: 'Vue2Playground',
|
||||
components: {
|
||||
MarkdownRender,
|
||||
TestLab,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
content: '',
|
||||
delay: 16,
|
||||
chunkSize: 2,
|
||||
timer: null,
|
||||
running: true,
|
||||
currentPath: typeof window !== 'undefined' ? window.location.pathname : '/',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isTestPage() {
|
||||
return this.normalizePath(this.currentPath) === '/test'
|
||||
},
|
||||
totalLength() {
|
||||
return markdownSource.length
|
||||
},
|
||||
progress() {
|
||||
if (!this.totalLength)
|
||||
return 0
|
||||
return Math.min(100, Math.round((this.content.length / this.totalLength) * 100))
|
||||
},
|
||||
normalizedChunkSize() {
|
||||
const n = Math.floor(Number(this.chunkSize) || 1)
|
||||
return Math.max(1, n)
|
||||
},
|
||||
isDone() {
|
||||
return this.content.length >= this.totalLength
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
delay() {
|
||||
if (this.running && !this.isTestPage)
|
||||
this.restartStream()
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.syncPath()
|
||||
window.addEventListener('popstate', this.handlePopState)
|
||||
if (!this.isTestPage)
|
||||
this.startStream()
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('popstate', this.handlePopState)
|
||||
this.stopStream()
|
||||
},
|
||||
methods: {
|
||||
normalizePath(path) {
|
||||
if (!path)
|
||||
return '/'
|
||||
const normalized = path.replace(/\/+$/, '')
|
||||
return normalized || '/'
|
||||
},
|
||||
syncPath() {
|
||||
if (typeof window === 'undefined')
|
||||
return
|
||||
this.currentPath = this.normalizePath(window.location.pathname)
|
||||
},
|
||||
handlePopState() {
|
||||
this.syncPath()
|
||||
if (this.isTestPage)
|
||||
this.stopStream()
|
||||
else if (!this.content.length)
|
||||
this.startStream()
|
||||
},
|
||||
navigate(path) {
|
||||
const nextPath = this.normalizePath(path)
|
||||
if (typeof window === 'undefined') {
|
||||
this.currentPath = nextPath
|
||||
return
|
||||
}
|
||||
if (nextPath !== this.normalizePath(window.location.pathname))
|
||||
window.history.pushState({}, '', nextPath)
|
||||
this.currentPath = nextPath
|
||||
if (this.isTestPage)
|
||||
this.stopStream()
|
||||
else if (!this.content.length)
|
||||
this.startStream()
|
||||
},
|
||||
goToTest() {
|
||||
this.navigate('/test')
|
||||
},
|
||||
goHome() {
|
||||
this.navigate('/')
|
||||
},
|
||||
tick() {
|
||||
if (this.isDone) {
|
||||
this.stopStream()
|
||||
return
|
||||
}
|
||||
const start = this.content.length
|
||||
const nextChunk = markdownSource.slice(start, start + this.normalizedChunkSize)
|
||||
this.content += nextChunk
|
||||
},
|
||||
startStream() {
|
||||
this.stopStream()
|
||||
this.running = true
|
||||
this.timer = window.setInterval(() => this.tick(), this.delay)
|
||||
},
|
||||
stopStream() {
|
||||
if (this.timer != null) {
|
||||
window.clearInterval(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
this.running = false
|
||||
},
|
||||
toggleStream() {
|
||||
if (this.running)
|
||||
this.stopStream()
|
||||
else this.startStream()
|
||||
},
|
||||
restartStream() {
|
||||
if (!this.running)
|
||||
return
|
||||
this.startStream()
|
||||
},
|
||||
resetStream() {
|
||||
this.content = ''
|
||||
this.startStream()
|
||||
},
|
||||
fillAll() {
|
||||
this.content = markdownSource
|
||||
this.stopStream()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TestLab v-if="isTestPage" @navigate-home="goHome" />
|
||||
|
||||
<div v-else class="page">
|
||||
<header class="header">
|
||||
<div class="header-main">
|
||||
<div class="title">
|
||||
markstream-vue2 playground
|
||||
</div>
|
||||
<button type="button" class="btn" @click="goToTest">
|
||||
Open /test
|
||||
</button>
|
||||
</div>
|
||||
<div class="sub">
|
||||
Vue 2.6 demo: streaming markdown into the renderer
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="layout">
|
||||
<section class="panel controls">
|
||||
<h2>Stream controls</h2>
|
||||
<div class="field">
|
||||
<label for="delay">Delay (ms)</label>
|
||||
<input id="delay" v-model.number="delay" type="number" min="4" max="200">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="chunk">Chunk size</label>
|
||||
<input id="chunk" v-model.number="chunkSize" type="number" min="1" max="16">
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button type="button" class="btn" @click="toggleStream">
|
||||
{{ running ? 'Pause' : 'Resume' }}
|
||||
</button>
|
||||
<button type="button" class="btn" @click="resetStream">
|
||||
Reset
|
||||
</button>
|
||||
<button type="button" class="btn ghost" @click="fillAll">
|
||||
Render all
|
||||
</button>
|
||||
</div>
|
||||
<div class="status">
|
||||
<div class="progress">
|
||||
<div class="bar" :style="{ width: `${progress}%` }" />
|
||||
</div>
|
||||
<div class="meta">
|
||||
{{ content.length }} / {{ totalLength }} ({{ progress }}%)
|
||||
</div>
|
||||
</div>
|
||||
<p class="note">
|
||||
This demo focuses on baseline Vue 2 rendering. Mermaid and KaTeX are
|
||||
optional peer dependencies and render when installed.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="panel preview">
|
||||
<MarkdownRender
|
||||
:content="content"
|
||||
:final="isDone"
|
||||
:typewriter="false"
|
||||
custom-id="vue2-demo"
|
||||
:custom-html-tags="['thinking']"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,141 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
|
||||
background: #f7f7fb;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
padding: 1.5rem 2rem 2.5rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.header-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.sub {
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
|
||||
gap: 1.5rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08);
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.controls h2 {
|
||||
margin: 0 0 1rem;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
margin-bottom: 0.9rem;
|
||||
}
|
||||
|
||||
.field label {
|
||||
font-size: 0.9rem;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.field input {
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 0.7rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: none;
|
||||
background: #111827;
|
||||
color: #fff;
|
||||
padding: 0.45rem 0.9rem;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn.ghost {
|
||||
background: #e5e7eb;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #e5e7eb;
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bar {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #3b82f6, #6366f1);
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.note {
|
||||
margin-top: 0.9rem;
|
||||
font-size: 0.85rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.preview {
|
||||
min-height: 70vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.header-main {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,101 @@
|
||||
<script>
|
||||
import { NestedRenderer } from 'markstream-vue2'
|
||||
|
||||
export default {
|
||||
name: 'ThinkingNode',
|
||||
components: {
|
||||
NestedRenderer,
|
||||
},
|
||||
props: {
|
||||
node: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="thinking-node p-4 my-4 bg-blue-50 dark:bg-blue-900/40 rounded-md border-l-4 border-blue-400 flex items-start gap-3">
|
||||
<div class="flex-shrink-0 mt-1">
|
||||
<div class="w-9 h-9 rounded-full bg-blue-200 dark:bg-blue-700 flex items-center justify-center text-blue-700 dark:text-blue-100">
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path d="M12 3C7.03 3 3 6.58 3 11c0 1.86.66 3.57 1.77 4.98L4 21l5.2-1.9C10.06 19.35 11 19.5 12 19.5c4.97 0 9-3.58 9-8.5S16.97 3 12 3z" stroke="currentColor" stroke-width="0.8" fill="currentColor" opacity="0.9" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-baseline gap-3">
|
||||
<strong class="text-sm">Thinking</strong>
|
||||
<span class="text-xs text-slate-500 dark:text-slate-300">(assistant)</span>
|
||||
<span class="ml-2" aria-hidden="true">
|
||||
<span class="thinking-dots" :class="[node.loading ? 'visible' : 'hidden']" aria-hidden="true">
|
||||
<span class="dot dot-1" />
|
||||
<span class="dot dot-2" />
|
||||
<span class="dot dot-3" />
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-1 text-sm leading-relaxed text-slate-800 dark:text-slate-100">
|
||||
<span v-if="node.loading" class="sr-only" aria-live="polite">Thinking…</span>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div :key="node.loading ? 'loading' : 'ready'" class="content-area">
|
||||
<NestedRenderer
|
||||
:node="node"
|
||||
custom-id="vue2-demo"
|
||||
:custom-html-tags="['thinking']"
|
||||
:typewriter="false"
|
||||
:batch-rendering="false"
|
||||
:viewport-priority="false"
|
||||
:defer-nodes-until-visible="false"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.thinking-node {
|
||||
color: #0f172a;
|
||||
}
|
||||
.dark .thinking-node {
|
||||
color: #e6f0ff;
|
||||
}
|
||||
|
||||
.thinking-dots {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 36px;
|
||||
justify-content: flex-start;
|
||||
height: 12px;
|
||||
transition: opacity 160ms linear, transform 160ms linear;
|
||||
opacity: 0;
|
||||
}
|
||||
.thinking-dots .dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 9999px;
|
||||
background: #1e3a8a;
|
||||
opacity: 0.25;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.thinking-dots.visible { opacity: 1; }
|
||||
.thinking-dots.hidden { opacity: 0; transform: translateY(0); }
|
||||
.thinking-dots.visible .dot-1 { animation: think-bounce 1s infinite ease-in-out; animation-delay: 0s }
|
||||
.thinking-dots.visible .dot-2 { animation: think-bounce 1s infinite ease-in-out; animation-delay: 0.12s }
|
||||
.thinking-dots.visible .dot-3 { animation: think-bounce 1s infinite ease-in-out; animation-delay: 0.24s }
|
||||
.dark .thinking-dots .dot { background: #bfdbfe; opacity: 0.28 }
|
||||
|
||||
@keyframes think-bounce {
|
||||
0%, 80%, 100% { transform: translateY(0); opacity: 0.25 }
|
||||
40% { transform: translateY(-6px); opacity: 1 }
|
||||
}
|
||||
|
||||
.content-area { min-height: 1.25rem; }
|
||||
.fade-enter-from, .fade-leave-to { opacity: 0 }
|
||||
.fade-enter-active, .fade-leave-active { transition: opacity 160ms ease }
|
||||
.fade-enter-to, .fade-leave-from { opacity: 1 }
|
||||
</style>
|
||||
@@ -0,0 +1,15 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module 'markstream-vue2/workers/*?worker&inline' {
|
||||
const WorkerFactory: {
|
||||
new (): Worker
|
||||
}
|
||||
export default WorkerFactory
|
||||
}
|
||||
|
||||
declare module 'markstream-vue2/workers/*?worker' {
|
||||
const WorkerFactory: {
|
||||
new (): Worker
|
||||
}
|
||||
export default WorkerFactory
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { setKaTeXWorker, setMermaidWorker, VueRendererMarkdown } from 'markstream-vue2'
|
||||
import KatexWorker from 'markstream-vue2/workers/katexRenderer.worker?worker&inline'
|
||||
import MermaidWorker from 'markstream-vue2/workers/mermaidParser.worker?worker&inline'
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import 'markstream-vue2/index.css'
|
||||
import 'katex/dist/katex.min.css'
|
||||
import 'monaco-editor/min/vs/editor/editor.main.css'
|
||||
import '@unocss/reset/tailwind.css'
|
||||
import './styles/main.css'
|
||||
import './app.css'
|
||||
|
||||
Vue.use(VueRendererMarkdown)
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
setKaTeXWorker(new KatexWorker())
|
||||
setMermaidWorker(new MermaidWorker())
|
||||
}
|
||||
|
||||
new Vue({
|
||||
render: h => h(App),
|
||||
}).$mount('#app')
|
||||
@@ -0,0 +1,2 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -0,0 +1,17 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: ['selector'],
|
||||
safelist: [
|
||||
'border-gray-400/5',
|
||||
'hover:bg-[var(--vscode-editor-selectionBackground)]',
|
||||
],
|
||||
content: [
|
||||
'./src/**/*.{vue,js,ts,jsx,tsx}',
|
||||
'../packages/markstream-vue2/src/**/*.{vue,js,ts,jsx,tsx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"jsx": "preserve",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowJs": true,
|
||||
"isolatedModules": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*", "vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { resolve } from 'node:path'
|
||||
import vue2 from '@vitejs/plugin-vue2'
|
||||
import { defineConfig } from 'vite'
|
||||
import monacoEditorPlugin from 'vite-plugin-monaco-editor-esm'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
let vueCompiler: any
|
||||
try {
|
||||
vueCompiler = require('@vue/compiler-sfc')
|
||||
}
|
||||
catch {
|
||||
try {
|
||||
vueCompiler = require('vue/compiler-sfc')
|
||||
}
|
||||
catch {
|
||||
vueCompiler = require('vue-template-compiler')
|
||||
}
|
||||
}
|
||||
const vueRuntime = require.resolve('vue/dist/vue.runtime.esm.js')
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue2({ compiler: vueCompiler, script: { babelParserPlugins: ['typescript'] } }),
|
||||
monacoEditorPlugin({
|
||||
languageWorkers: [
|
||||
'editorWorkerService',
|
||||
'typescript',
|
||||
'css',
|
||||
'html',
|
||||
'json',
|
||||
],
|
||||
customDistPath(root, buildOutDir) {
|
||||
return resolve(buildOutDir, 'monacoeditorwork')
|
||||
},
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
modulePreload: false,
|
||||
},
|
||||
worker: {
|
||||
format: 'es',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
entryFileNames: 'workers/[name].js',
|
||||
chunkFileNames: 'workers/[name].js',
|
||||
assetFileNames: 'workers/[name][extname]',
|
||||
},
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ['stream-monaco'],
|
||||
},
|
||||
resolve: {
|
||||
dedupe: ['vue'],
|
||||
alias: [
|
||||
{
|
||||
find: /^vue$/,
|
||||
replacement: vueRuntime,
|
||||
},
|
||||
{
|
||||
find: /^markstream-vue2\/index\.css$/,
|
||||
replacement: resolve(__dirname, '../packages/markstream-vue2/dist/index.css'),
|
||||
},
|
||||
{
|
||||
find: /^markstream-vue2\/index\.tailwind\.css$/,
|
||||
replacement: resolve(__dirname, '../packages/markstream-vue2/dist/index.tailwind.css'),
|
||||
},
|
||||
{
|
||||
find: /^markstream-vue2\/tailwind$/,
|
||||
replacement: resolve(__dirname, '../packages/markstream-vue2/dist/tailwind.ts'),
|
||||
},
|
||||
{
|
||||
find: /^markstream-vue2$/,
|
||||
replacement: resolve(__dirname, '../packages/markstream-vue2/src'),
|
||||
},
|
||||
{
|
||||
find: /^markstream-vue2\//,
|
||||
replacement: `${resolve(__dirname, '../packages/markstream-vue2/src')}/`,
|
||||
},
|
||||
{
|
||||
find: 'stream-markdown-parser',
|
||||
replacement: resolve(__dirname, '../packages/markdown-parser/src/index.ts'),
|
||||
},
|
||||
{
|
||||
find: 'stream-markdown-parser/',
|
||||
replacement: resolve(__dirname, '../packages/markdown-parser/src/'),
|
||||
},
|
||||
],
|
||||
},
|
||||
server: {
|
||||
fs: {
|
||||
allow: [resolve(__dirname, '..')],
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user