@infinitedusky/indusk-mcp 0.8.5 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/cli.js
CHANGED
|
@@ -33,6 +33,13 @@ program
|
|
|
33
33
|
const { update } = await import("./commands/update.js");
|
|
34
34
|
await update(process.cwd());
|
|
35
35
|
});
|
|
36
|
+
program
|
|
37
|
+
.command("init-docs")
|
|
38
|
+
.description("Scaffold a VitePress documentation site with Mermaid, llms.txt, and FullscreenDiagram")
|
|
39
|
+
.action(async () => {
|
|
40
|
+
const { initDocs } = await import("./commands/init-docs.js");
|
|
41
|
+
await initDocs(process.cwd());
|
|
42
|
+
});
|
|
36
43
|
program
|
|
37
44
|
.command("check-gates")
|
|
38
45
|
.description("Validate plan execution gates — reports incomplete verification, context, and document items")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initDocs(projectRoot: string): Promise<void>;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
export async function initDocs(projectRoot) {
|
|
5
|
+
const projectName = basename(projectRoot);
|
|
6
|
+
const docsDir = join(projectRoot, `apps/${projectName}-docs`);
|
|
7
|
+
if (existsSync(docsDir)) {
|
|
8
|
+
console.info(`Docs app already exists at apps/${projectName}-docs/`);
|
|
9
|
+
console.info("Run 'update' to sync templates.");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.info(`Scaffolding docs site at apps/${projectName}-docs/\n`);
|
|
13
|
+
// Create directory structure
|
|
14
|
+
const dirs = [
|
|
15
|
+
"src/.vitepress/components",
|
|
16
|
+
"src/.vitepress/theme",
|
|
17
|
+
"src/guide",
|
|
18
|
+
"src/reference/skills",
|
|
19
|
+
"src/reference/tools",
|
|
20
|
+
"src/decisions",
|
|
21
|
+
"src/lessons",
|
|
22
|
+
];
|
|
23
|
+
for (const dir of dirs) {
|
|
24
|
+
mkdirSync(join(docsDir, dir), { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
// package.json
|
|
27
|
+
writeFileSync(join(docsDir, "package.json"), JSON.stringify({
|
|
28
|
+
name: `${projectName}-docs`,
|
|
29
|
+
version: "0.1.0",
|
|
30
|
+
private: true,
|
|
31
|
+
type: "module",
|
|
32
|
+
scripts: {
|
|
33
|
+
dev: "vitepress dev src --port 4173",
|
|
34
|
+
build: "vitepress build src",
|
|
35
|
+
preview: "vitepress preview src",
|
|
36
|
+
},
|
|
37
|
+
devDependencies: {
|
|
38
|
+
mermaid: "^10.2.2",
|
|
39
|
+
panzoom: "^9.4.3",
|
|
40
|
+
vitepress: "^1.6.3",
|
|
41
|
+
"vitepress-plugin-llms": "^1.12.0",
|
|
42
|
+
"vitepress-plugin-mermaid": "^2.0.10",
|
|
43
|
+
vue: "^3.4.15",
|
|
44
|
+
},
|
|
45
|
+
}, null, "\t") + "\n");
|
|
46
|
+
// .vitepress/config.ts
|
|
47
|
+
writeFileSync(join(docsDir, "src/.vitepress/config.ts"), `import { defineConfig } from "vitepress";
|
|
48
|
+
import llmstxt from "vitepress-plugin-llms";
|
|
49
|
+
import { withMermaid } from "vitepress-plugin-mermaid";
|
|
50
|
+
|
|
51
|
+
const config = defineConfig({
|
|
52
|
+
title: "${projectName}",
|
|
53
|
+
description: "Documentation for ${projectName}",
|
|
54
|
+
base: "/",
|
|
55
|
+
lastUpdated: true,
|
|
56
|
+
cleanUrls: true,
|
|
57
|
+
ignoreDeadLinks: true,
|
|
58
|
+
|
|
59
|
+
markdown: {
|
|
60
|
+
lineNumbers: true,
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
mermaid: {
|
|
64
|
+
theme: "default",
|
|
65
|
+
securityLevel: "strict",
|
|
66
|
+
maxTextSize: 50000,
|
|
67
|
+
flowchart: {
|
|
68
|
+
useMaxWidth: true,
|
|
69
|
+
htmlLabels: true,
|
|
70
|
+
},
|
|
71
|
+
sequence: {
|
|
72
|
+
actorFontWeight: "bold",
|
|
73
|
+
messageFontSize: 14,
|
|
74
|
+
actorFontSize: 14,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
themeConfig: {
|
|
79
|
+
search: {
|
|
80
|
+
provider: "local",
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
nav: [
|
|
84
|
+
{ text: "Guide", link: "/guide/" },
|
|
85
|
+
{ text: "Reference", link: "/reference/" },
|
|
86
|
+
{ text: "Decisions", link: "/decisions/" },
|
|
87
|
+
{ text: "Lessons", link: "/lessons/" },
|
|
88
|
+
],
|
|
89
|
+
|
|
90
|
+
sidebar: {
|
|
91
|
+
"/guide/": [
|
|
92
|
+
{
|
|
93
|
+
text: "Guide",
|
|
94
|
+
items: [
|
|
95
|
+
{ text: "Overview", link: "/guide/" },
|
|
96
|
+
{ text: "Getting Started", link: "/guide/getting-started" },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
"/reference/": [
|
|
101
|
+
{
|
|
102
|
+
text: "Reference",
|
|
103
|
+
items: [
|
|
104
|
+
{ text: "Overview", link: "/reference/" },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
"/decisions/": [
|
|
109
|
+
{
|
|
110
|
+
text: "Architecture Decisions",
|
|
111
|
+
items: [{ text: "Overview", link: "/decisions/" }],
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
"/lessons/": [
|
|
115
|
+
{
|
|
116
|
+
text: "Lessons Learned",
|
|
117
|
+
items: [{ text: "Overview", link: "/lessons/" }],
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
vite: {
|
|
124
|
+
plugins: [llmstxt()],
|
|
125
|
+
server: {
|
|
126
|
+
allowedHosts: [".orb.local"],
|
|
127
|
+
},
|
|
128
|
+
optimizeDeps: {
|
|
129
|
+
include: ["mermaid"],
|
|
130
|
+
},
|
|
131
|
+
ssr: {
|
|
132
|
+
noExternal: ["mermaid"],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
export default withMermaid(config);
|
|
138
|
+
`);
|
|
139
|
+
// .vitepress/theme/index.ts
|
|
140
|
+
writeFileSync(join(docsDir, "src/.vitepress/theme/index.ts"), `import type { Theme } from "vitepress";
|
|
141
|
+
import DefaultTheme from "vitepress/theme";
|
|
142
|
+
import FullscreenDiagram from "../components/FullscreenDiagram.vue";
|
|
143
|
+
|
|
144
|
+
export default {
|
|
145
|
+
extends: DefaultTheme,
|
|
146
|
+
enhanceApp({ app }) {
|
|
147
|
+
app.component("FullscreenDiagram", FullscreenDiagram);
|
|
148
|
+
},
|
|
149
|
+
} satisfies Theme;
|
|
150
|
+
`);
|
|
151
|
+
// FullscreenDiagram.vue — copy from templates
|
|
152
|
+
const fullscreenDiagramPath = join(docsDir, "src/.vitepress/components/FullscreenDiagram.vue");
|
|
153
|
+
const templateComponent = join(projectRoot, "apps/indusk-mcp/templates/FullscreenDiagram.vue");
|
|
154
|
+
// If we have the template in the package, use it; otherwise inline a minimal version
|
|
155
|
+
if (existsSync(templateComponent)) {
|
|
156
|
+
const content = readFileSync(templateComponent, "utf-8");
|
|
157
|
+
writeFileSync(fullscreenDiagramPath, content);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// Use the bundled template from the package
|
|
161
|
+
const { dirname } = await import("node:path");
|
|
162
|
+
const { fileURLToPath } = await import("node:url");
|
|
163
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
164
|
+
const packageRoot = join(__dirname, "../../..");
|
|
165
|
+
const bundledTemplate = join(packageRoot, "templates/FullscreenDiagram.vue");
|
|
166
|
+
if (existsSync(bundledTemplate)) {
|
|
167
|
+
const content = readFileSync(bundledTemplate, "utf-8");
|
|
168
|
+
writeFileSync(fullscreenDiagramPath, content);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
writeFileSync(fullscreenDiagramPath, `<template>
|
|
172
|
+
<div class="diagram-container">
|
|
173
|
+
<div class="diagram"><slot></slot></div>
|
|
174
|
+
</div>
|
|
175
|
+
</template>
|
|
176
|
+
|
|
177
|
+
<style scoped>
|
|
178
|
+
.diagram-container { position: relative; width: 100%; }
|
|
179
|
+
.diagram { width: 100%; padding: 1rem; border-radius: 8px; border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft); }
|
|
180
|
+
</style>
|
|
181
|
+
`);
|
|
182
|
+
console.info(" warn: FullscreenDiagram created with minimal template (panzoom not included)");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Starter pages
|
|
186
|
+
writeFileSync(join(docsDir, "src/index.md"), `---
|
|
187
|
+
layout: home
|
|
188
|
+
hero:
|
|
189
|
+
name: ${projectName}
|
|
190
|
+
tagline: Project documentation
|
|
191
|
+
actions:
|
|
192
|
+
- theme: brand
|
|
193
|
+
text: Get Started
|
|
194
|
+
link: /guide/getting-started
|
|
195
|
+
- theme: alt
|
|
196
|
+
text: Reference
|
|
197
|
+
link: /reference/
|
|
198
|
+
---
|
|
199
|
+
`);
|
|
200
|
+
writeFileSync(join(docsDir, "src/guide/index.md"), `# Guide
|
|
201
|
+
|
|
202
|
+
How-to guides for working with ${projectName}.
|
|
203
|
+
|
|
204
|
+
- [Getting Started](/guide/getting-started) — set up the project and start developing
|
|
205
|
+
`);
|
|
206
|
+
writeFileSync(join(docsDir, "src/guide/getting-started.md"), `# Getting Started
|
|
207
|
+
|
|
208
|
+
## Prerequisites
|
|
209
|
+
|
|
210
|
+
- Node.js 22+
|
|
211
|
+
- pnpm
|
|
212
|
+
|
|
213
|
+
## Setup
|
|
214
|
+
|
|
215
|
+
\`\`\`bash
|
|
216
|
+
pnpm install
|
|
217
|
+
\`\`\`
|
|
218
|
+
|
|
219
|
+
## Next Steps
|
|
220
|
+
|
|
221
|
+
Start building!
|
|
222
|
+
`);
|
|
223
|
+
writeFileSync(join(docsDir, "src/reference/index.md"), `# Reference
|
|
224
|
+
|
|
225
|
+
Reference documentation for ${projectName}.
|
|
226
|
+
`);
|
|
227
|
+
writeFileSync(join(docsDir, "src/decisions/index.md"), `# Architecture Decisions
|
|
228
|
+
|
|
229
|
+
Architecture decision records for ${projectName}. Each decision documents what was chosen, what was rejected, and why.
|
|
230
|
+
`);
|
|
231
|
+
writeFileSync(join(docsDir, "src/lessons/index.md"), `# Lessons Learned
|
|
232
|
+
|
|
233
|
+
Insights from building ${projectName}. Each lesson captures what we learned, what surprised us, and what we'd do differently.
|
|
234
|
+
`);
|
|
235
|
+
console.info(" created: package.json");
|
|
236
|
+
console.info(" created: .vitepress/config.ts (mermaid + llms plugin)");
|
|
237
|
+
console.info(" created: .vitepress/theme/index.ts");
|
|
238
|
+
console.info(" created: .vitepress/components/FullscreenDiagram.vue");
|
|
239
|
+
console.info(" created: starter pages (index, guide, reference, decisions, lessons)");
|
|
240
|
+
// Check if pnpm-workspace.yaml includes this app
|
|
241
|
+
const workspacePath = join(projectRoot, "pnpm-workspace.yaml");
|
|
242
|
+
if (existsSync(workspacePath)) {
|
|
243
|
+
const workspace = readFileSync(workspacePath, "utf-8");
|
|
244
|
+
if (!workspace.includes("apps/*") && !workspace.includes(`apps/${projectName}-docs`)) {
|
|
245
|
+
console.info(`\n note: add 'apps/${projectName}-docs' to pnpm-workspace.yaml if not covered by a glob`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Install dependencies
|
|
249
|
+
console.info("\n[Installing dependencies]");
|
|
250
|
+
try {
|
|
251
|
+
execSync("pnpm install", {
|
|
252
|
+
cwd: projectRoot,
|
|
253
|
+
stdio: "inherit",
|
|
254
|
+
timeout: 120000,
|
|
255
|
+
});
|
|
256
|
+
console.info(" done: dependencies installed");
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
console.info(" warn: pnpm install failed — run manually");
|
|
260
|
+
}
|
|
261
|
+
console.info(`\nDocs site ready at apps/${projectName}-docs/`);
|
|
262
|
+
console.info("\nNext steps:");
|
|
263
|
+
console.info(` 1. pnpm turbo dev --filter=${projectName}-docs`);
|
|
264
|
+
console.info(" 2. Edit src/guide/getting-started.md with your setup instructions");
|
|
265
|
+
console.info(" 3. Add reference pages as you build features");
|
|
266
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="diagram-container">
|
|
3
|
+
<div class="diagram" ref="diagramRef">
|
|
4
|
+
<slot></slot>
|
|
5
|
+
</div>
|
|
6
|
+
<button class="expand-btn" @click="toggleExpand" :title="isExpanded ? 'Close' : 'Expand'">
|
|
7
|
+
<svg v-if="!isExpanded" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
8
|
+
<path fill="currentColor" d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/>
|
|
9
|
+
</svg>
|
|
10
|
+
<svg v-else xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
11
|
+
<path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
|
|
12
|
+
</svg>
|
|
13
|
+
</button>
|
|
14
|
+
|
|
15
|
+
<Teleport to="body">
|
|
16
|
+
<div v-if="isExpanded" class="modal-overlay" @click.self="toggleExpand">
|
|
17
|
+
<div class="modal-content">
|
|
18
|
+
<div class="expanded-diagram">
|
|
19
|
+
<div ref="expandedDiagramRef" v-html="diagramHTML"></div>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="zoom-controls">
|
|
22
|
+
<button @click="zoomOut" :disabled="zoom <= 0.05" title="Zoom Out">
|
|
23
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
24
|
+
<path fill="currentColor" d="M19 13H5v-2h14v2z"/>
|
|
25
|
+
</svg>
|
|
26
|
+
</button>
|
|
27
|
+
<span class="zoom-level">{{ Math.round(zoom * 100) }}%</span>
|
|
28
|
+
<button @click="zoomIn" :disabled="zoom >= 3" title="Zoom In">
|
|
29
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
30
|
+
<path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
|
31
|
+
</svg>
|
|
32
|
+
</button>
|
|
33
|
+
<button @click="resetZoom" title="Reset Zoom">
|
|
34
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
35
|
+
<path fill="currentColor" d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>
|
|
36
|
+
</svg>
|
|
37
|
+
</button>
|
|
38
|
+
</div>
|
|
39
|
+
<button class="close-btn" @click="toggleExpand">
|
|
40
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
41
|
+
<path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
|
|
42
|
+
</svg>
|
|
43
|
+
</button>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</Teleport>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<script setup>
|
|
51
|
+
import panzoom from "panzoom";
|
|
52
|
+
import { nextTick, ref, watch } from "vue";
|
|
53
|
+
|
|
54
|
+
const isExpanded = ref(false);
|
|
55
|
+
const diagramRef = ref(null);
|
|
56
|
+
const expandedDiagramRef = ref(null);
|
|
57
|
+
const zoom = ref(1);
|
|
58
|
+
const panzoomInstance = ref(null);
|
|
59
|
+
const diagramHTML = ref("");
|
|
60
|
+
|
|
61
|
+
const cloneDiagram = () => {
|
|
62
|
+
if (!diagramRef.value) return;
|
|
63
|
+
diagramHTML.value = diagramRef.value.innerHTML;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const toggleExpand = () => {
|
|
67
|
+
if (!isExpanded.value) {
|
|
68
|
+
cloneDiagram();
|
|
69
|
+
}
|
|
70
|
+
isExpanded.value = !isExpanded.value;
|
|
71
|
+
if (isExpanded.value) {
|
|
72
|
+
document.body.style.overflow = "hidden";
|
|
73
|
+
nextTick(() => {
|
|
74
|
+
if (expandedDiagramRef.value) {
|
|
75
|
+
const svgElement =
|
|
76
|
+
expandedDiagramRef.value.querySelector("svg") || expandedDiagramRef.value.firstChild;
|
|
77
|
+
if (svgElement) {
|
|
78
|
+
panzoomInstance.value = panzoom(svgElement, {
|
|
79
|
+
bounds: true,
|
|
80
|
+
boundsPadding: 0.1,
|
|
81
|
+
minZoom: 0.05,
|
|
82
|
+
maxZoom: 10,
|
|
83
|
+
});
|
|
84
|
+
panzoomInstance.value.on("zoom", () => {
|
|
85
|
+
zoom.value = panzoomInstance.value.getTransform().scale;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
document.body.style.overflow = "";
|
|
92
|
+
if (panzoomInstance.value) {
|
|
93
|
+
panzoomInstance.value.dispose();
|
|
94
|
+
panzoomInstance.value = null;
|
|
95
|
+
}
|
|
96
|
+
resetZoom();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const zoomIn = () => {
|
|
101
|
+
if (panzoomInstance.value) {
|
|
102
|
+
const currentZoom = panzoomInstance.value.getTransform().scale;
|
|
103
|
+
if (currentZoom < 10) {
|
|
104
|
+
panzoomInstance.value.smoothZoom(0, 0, 1.1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const zoomOut = () => {
|
|
110
|
+
if (panzoomInstance.value) {
|
|
111
|
+
const currentZoom = panzoomInstance.value.getTransform().scale;
|
|
112
|
+
if (currentZoom > 0.05) {
|
|
113
|
+
panzoomInstance.value.smoothZoom(0, 0, 0.9);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const resetZoom = () => {
|
|
119
|
+
if (panzoomInstance.value) {
|
|
120
|
+
panzoomInstance.value.zoomAbs(0, 0, 1);
|
|
121
|
+
panzoomInstance.value.moveTo(0, 0);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
watch(isExpanded, (newValue) => {
|
|
126
|
+
if (!newValue) {
|
|
127
|
+
resetZoom();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
</script>
|
|
131
|
+
|
|
132
|
+
<style scoped>
|
|
133
|
+
.diagram-container {
|
|
134
|
+
position: relative;
|
|
135
|
+
width: 100%;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.diagram {
|
|
139
|
+
width: 100%;
|
|
140
|
+
padding: 1rem;
|
|
141
|
+
border-radius: 8px;
|
|
142
|
+
border: 1px solid var(--vp-c-divider);
|
|
143
|
+
background: var(--vp-c-bg-soft);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.expand-btn {
|
|
147
|
+
position: absolute;
|
|
148
|
+
top: 1rem;
|
|
149
|
+
right: 1rem;
|
|
150
|
+
background: var(--vp-c-bg);
|
|
151
|
+
border: 1px solid var(--vp-c-divider);
|
|
152
|
+
border-radius: 4px;
|
|
153
|
+
padding: 8px;
|
|
154
|
+
cursor: pointer;
|
|
155
|
+
opacity: 0.9;
|
|
156
|
+
transition: opacity 0.2s;
|
|
157
|
+
z-index: 10;
|
|
158
|
+
color: var(--vp-c-text-1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.expand-btn:hover {
|
|
162
|
+
opacity: 1;
|
|
163
|
+
background: var(--vp-c-bg-soft);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.modal-overlay {
|
|
167
|
+
position: fixed;
|
|
168
|
+
top: 0;
|
|
169
|
+
left: 0;
|
|
170
|
+
width: 100vw;
|
|
171
|
+
height: 100vh;
|
|
172
|
+
background: rgba(0, 0, 0, 0.85);
|
|
173
|
+
display: flex;
|
|
174
|
+
align-items: center;
|
|
175
|
+
justify-content: center;
|
|
176
|
+
z-index: 1000;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.modal-content {
|
|
180
|
+
position: relative;
|
|
181
|
+
background: var(--vp-c-bg);
|
|
182
|
+
padding: 2rem;
|
|
183
|
+
border-radius: 8px;
|
|
184
|
+
width: 95vw;
|
|
185
|
+
height: 95vh;
|
|
186
|
+
display: flex;
|
|
187
|
+
flex-direction: column;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.expanded-diagram {
|
|
191
|
+
flex: 1;
|
|
192
|
+
overflow: hidden;
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
justify-content: center;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.expanded-diagram > div {
|
|
199
|
+
cursor: move;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.expanded-diagram :deep(svg) {
|
|
203
|
+
display: block;
|
|
204
|
+
width: auto;
|
|
205
|
+
height: auto;
|
|
206
|
+
min-width: 1000px;
|
|
207
|
+
max-width: 90%;
|
|
208
|
+
max-height: 90%;
|
|
209
|
+
margin: auto;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.expanded-diagram :deep(.mermaid) {
|
|
213
|
+
display: flex;
|
|
214
|
+
justify-content: center;
|
|
215
|
+
align-items: center;
|
|
216
|
+
width: 100%;
|
|
217
|
+
height: 100%;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.zoom-controls {
|
|
221
|
+
position: absolute;
|
|
222
|
+
bottom: 1rem;
|
|
223
|
+
left: 50%;
|
|
224
|
+
transform: translateX(-50%);
|
|
225
|
+
display: flex;
|
|
226
|
+
align-items: center;
|
|
227
|
+
gap: 0.5rem;
|
|
228
|
+
background: var(--vp-c-bg);
|
|
229
|
+
padding: 0.5rem;
|
|
230
|
+
border-radius: 8px;
|
|
231
|
+
border: 1px solid var(--vp-c-divider);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.zoom-controls button {
|
|
235
|
+
background: var(--vp-c-bg-soft);
|
|
236
|
+
border: 1px solid var(--vp-c-divider);
|
|
237
|
+
border-radius: 4px;
|
|
238
|
+
padding: 4px;
|
|
239
|
+
cursor: pointer;
|
|
240
|
+
opacity: 0.7;
|
|
241
|
+
transition: opacity 0.2s;
|
|
242
|
+
color: var(--vp-c-text-1);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.zoom-controls button:hover {
|
|
246
|
+
opacity: 1;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.zoom-controls button:disabled {
|
|
250
|
+
opacity: 0.3;
|
|
251
|
+
cursor: not-allowed;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.zoom-level {
|
|
255
|
+
min-width: 4rem;
|
|
256
|
+
text-align: center;
|
|
257
|
+
font-size: 0.9rem;
|
|
258
|
+
color: var(--vp-c-text-2);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.close-btn {
|
|
262
|
+
position: absolute;
|
|
263
|
+
top: 1rem;
|
|
264
|
+
right: 1rem;
|
|
265
|
+
background: var(--vp-c-bg-soft);
|
|
266
|
+
border: 1px solid var(--vp-c-divider);
|
|
267
|
+
border-radius: 4px;
|
|
268
|
+
padding: 8px;
|
|
269
|
+
cursor: pointer;
|
|
270
|
+
opacity: 0.7;
|
|
271
|
+
transition: opacity 0.2s;
|
|
272
|
+
z-index: 10;
|
|
273
|
+
color: var(--vp-c-text-1);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.close-btn:hover {
|
|
277
|
+
opacity: 1;
|
|
278
|
+
}
|
|
279
|
+
</style>
|