mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
docs: add Mermaid architecture diagrams and VitePress renderer
This commit is contained in:
@@ -1,4 +1,77 @@
|
|||||||
import DefaultTheme from 'vitepress/theme';
|
import DefaultTheme from 'vitepress/theme';
|
||||||
|
import { useRoute } from 'vitepress';
|
||||||
|
import { nextTick, onMounted, watch } from 'vue';
|
||||||
import '@catppuccin/vitepress/theme/macchiato/mauve.css';
|
import '@catppuccin/vitepress/theme/macchiato/mauve.css';
|
||||||
|
|
||||||
export default DefaultTheme;
|
let mermaidLoader: Promise<any> | null = null;
|
||||||
|
|
||||||
|
async function getMermaid() {
|
||||||
|
if (!mermaidLoader) {
|
||||||
|
mermaidLoader = import(
|
||||||
|
/* @vite-ignore */ 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'
|
||||||
|
).then((mod) => {
|
||||||
|
const mermaid = mod.default ?? mod;
|
||||||
|
mermaid.initialize({
|
||||||
|
startOnLoad: false,
|
||||||
|
securityLevel: 'loose',
|
||||||
|
});
|
||||||
|
return mermaid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return mermaidLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderMermaidBlocks() {
|
||||||
|
if (typeof document === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const blocks = Array.from(
|
||||||
|
document.querySelectorAll<HTMLElement>('div.language-mermaid'),
|
||||||
|
);
|
||||||
|
if (blocks.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mermaid = await getMermaid();
|
||||||
|
const nodes: HTMLElement[] = [];
|
||||||
|
|
||||||
|
for (const block of blocks) {
|
||||||
|
if (block.dataset.mermaidRendered === 'true') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const code = block.querySelector('pre code');
|
||||||
|
const source = code?.textContent?.trim();
|
||||||
|
if (!source) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mount = document.createElement('div');
|
||||||
|
mount.className = 'mermaid';
|
||||||
|
mount.textContent = source;
|
||||||
|
|
||||||
|
block.replaceChildren(mount);
|
||||||
|
block.dataset.mermaidRendered = 'true';
|
||||||
|
nodes.push(mount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes.length > 0) {
|
||||||
|
await mermaid.run({ nodes });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...DefaultTheme,
|
||||||
|
setup() {
|
||||||
|
const route = useRoute();
|
||||||
|
const render = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
renderMermaidBlocks().catch((error) => {
|
||||||
|
console.error('Failed to render Mermaid diagram:', error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(render);
|
||||||
|
watch(() => route.path, render);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -35,6 +35,33 @@ SubMiner uses a service-oriented Electron main-process architecture where `src/m
|
|||||||
- `src/jimaku/*`, `src/subsync/*`
|
- `src/jimaku/*`, `src/subsync/*`
|
||||||
- Domain-specific integration helpers.
|
- Domain-specific integration helpers.
|
||||||
|
|
||||||
|
## Flow Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Main["src/main.ts\n(composition root)"] --> Startup["runStartupBootstrapRuntimeService"]
|
||||||
|
Main --> Lifecycle["startAppLifecycleService"]
|
||||||
|
Lifecycle --> AppReady["runAppReadyRuntimeService"]
|
||||||
|
|
||||||
|
Main --> OverlayMgr["overlay-manager-service"]
|
||||||
|
Main --> Ipc["ipc-service / ipc-command-service"]
|
||||||
|
Main --> Mpv["mpv-service / mpv-control-service"]
|
||||||
|
Main --> Shortcuts["shortcut-service / overlay-shortcut-service"]
|
||||||
|
Main --> RuntimeOpts["runtime-options-ipc-service"]
|
||||||
|
Main --> Subtitle["subtitle-ws-service / secondary-subtitle-service"]
|
||||||
|
|
||||||
|
Main --> Config["src/config/*"]
|
||||||
|
Main --> Cli["src/cli/*"]
|
||||||
|
Main --> Trackers["src/window-trackers/*"]
|
||||||
|
Main --> Integrations["src/jimaku/* + src/subsync/*"]
|
||||||
|
|
||||||
|
OverlayMgr --> OverlayWindow["overlay-window-service"]
|
||||||
|
OverlayMgr --> OverlayVisibility["overlay-visibility-service"]
|
||||||
|
Mpv --> Subtitle
|
||||||
|
Ipc --> RuntimeOpts
|
||||||
|
Shortcuts --> OverlayMgr
|
||||||
|
```
|
||||||
|
|
||||||
## Composition Pattern
|
## Composition Pattern
|
||||||
|
|
||||||
Most runtime code follows a dependency-injection pattern:
|
Most runtime code follows a dependency-injection pattern:
|
||||||
@@ -59,6 +86,19 @@ This keeps side effects explicit and makes behavior easy to unit-test with fakes
|
|||||||
- Shutdown:
|
- Shutdown:
|
||||||
- `startAppLifecycleService` registers cleanup hooks (`will-quit`) while teardown behavior stays delegated to focused services from `main.ts`.
|
- `startAppLifecycleService` registers cleanup hooks (`will-quit`) while teardown behavior stays delegated to focused services from `main.ts`.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Args["CLI args"] --> Bootstrap["runStartupBootstrapRuntimeService"]
|
||||||
|
Bootstrap -->|generate-config| Exit["exit"]
|
||||||
|
Bootstrap -->|normal start| AppLifecycle["startAppLifecycleService"]
|
||||||
|
AppLifecycle --> Ready["runAppReadyRuntimeService"]
|
||||||
|
Ready --> Runtime["IPC + shortcuts + mpv events"]
|
||||||
|
Runtime --> Overlay["overlay visibility + mining actions"]
|
||||||
|
Runtime --> Subsync["subsync + secondary sub flows"]
|
||||||
|
Runtime --> WillQuit["app will-quit"]
|
||||||
|
WillQuit --> Cleanup["service-level cleanup + unregister"]
|
||||||
|
```
|
||||||
|
|
||||||
## Why This Design
|
## Why This Design
|
||||||
|
|
||||||
- Smaller blast radius: changing one feature usually touches one service.
|
- Smaller blast radius: changing one feature usually touches one service.
|
||||||
|
|||||||
Reference in New Issue
Block a user