noteconnection 1.6.8 → 1.8.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/LICENSE +674 -21
- package/README.md +258 -64
- package/dist/src/agent_workspace.contract.parity.test.js +475 -0
- package/dist/src/agent_workspace.frontend.test.js +9989 -0
- package/dist/src/agent_workspace.locale.contract.test.js +95 -0
- package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
- package/dist/src/copy.assets.contract.test.js +130 -0
- package/dist/src/core/PathBridge.js +28 -3
- package/dist/src/export/WorkspaceExportBundle.js +1130 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
- package/dist/src/export/index.js +18 -0
- package/dist/src/export/types.js +2 -0
- package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
- package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
- package/dist/src/foundation.release.evidence.contract.test.js +406 -0
- package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
- package/dist/src/frontend/README.md +258 -64
- package/dist/src/frontend/agent_workspace.js +5353 -0
- package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
- package/dist/src/frontend/analysis.js +31 -4
- package/dist/src/frontend/app.js +3597 -141
- package/dist/src/frontend/focus_mode_interactions.js +33 -0
- package/dist/src/frontend/godot_future_path_renderer.js +540 -0
- package/dist/src/frontend/godot_tree_interactions.js +178 -0
- package/dist/src/frontend/graph_state.mjs +105 -0
- package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
- package/dist/src/frontend/i18n.mjs +186 -0
- package/dist/src/frontend/index.html +641 -266
- package/dist/src/frontend/layout_gpu.js +12 -4
- package/dist/src/frontend/locales/en.json +721 -5
- package/dist/src/frontend/locales/zh.json +721 -5
- package/dist/src/frontend/main.mjs +60 -0
- package/dist/src/frontend/markdown_runtime.js +827 -0
- package/dist/src/frontend/notemd.css +49 -0
- package/dist/src/frontend/notemd.html +7 -1
- package/dist/src/frontend/notemd.js +64 -0
- package/dist/src/frontend/path.html +107 -0
- package/dist/src/frontend/path_app.js +2189 -150
- package/dist/src/frontend/path_layout.mjs +143 -0
- package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
- package/dist/src/frontend/path_modules_bridge.js +486 -0
- package/dist/src/frontend/path_state.mjs +118 -0
- package/dist/src/frontend/path_styles.css +146 -0
- package/dist/src/frontend/path_worker_bridge.mjs +85 -0
- package/dist/src/frontend/reader.js +522 -27
- package/dist/src/frontend/runtime_bridge.js +67 -54
- package/dist/src/frontend/runtime_bridge.mjs +279 -0
- package/dist/src/frontend/settings.js +130 -12
- package/dist/src/frontend/simulationWorker.js +241 -6
- package/dist/src/frontend/source_manager.js +190 -21
- package/dist/src/frontend/styles.css +2853 -72
- package/dist/src/frontend/workbench_state.mjs +101 -0
- package/dist/src/frontend/workspace_panes.js +10168 -0
- package/dist/src/frontend.locale.contract.test.js +62 -0
- package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
- package/dist/src/indexing/IndexLifecycle.js +195 -0
- package/dist/src/indexing/IndexLifecycle.test.js +49 -0
- package/dist/src/indexing/SegmentBuilder.js +64 -0
- package/dist/src/indexing/UnitBuilder.js +48 -0
- package/dist/src/indexing/types.js +2 -0
- package/dist/src/knowledge.api.contract.test.js +170 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
- package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
- package/dist/src/learning/answerReleaseReview.js +4319 -0
- package/dist/src/learning/answerReleaseReview.test.js +2888 -0
- package/dist/src/learning/api.js +2 -0
- package/dist/src/learning/conversationComposer.js +1480 -0
- package/dist/src/learning/conversationComposer.test.js +1817 -0
- package/dist/src/learning/domains/ConversationManager.js +53 -0
- package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
- package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
- package/dist/src/learning/domains/MasteryEngine.js +387 -0
- package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
- package/dist/src/learning/domains/QualityEvaluator.js +307 -0
- package/dist/src/learning/domains/TutorRouter.js +313 -0
- package/dist/src/learning/domains/index.js +33 -0
- package/dist/src/learning/domains/types.js +7 -0
- package/dist/src/learning/errors.js +29 -0
- package/dist/src/learning/evidenceContextAssembler.js +1176 -0
- package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
- package/dist/src/learning/graphContextAssembler.js +870 -0
- package/dist/src/learning/graphContextAssembler.test.js +1033 -0
- package/dist/src/learning/index.js +28 -0
- package/dist/src/learning/queryBackend.js +1898 -0
- package/dist/src/learning/queryBackend.test.js +955 -0
- package/dist/src/learning/ragContextPack.js +257 -0
- package/dist/src/learning/ragContextPack.test.js +160 -0
- package/dist/src/learning/ragPublicText.js +38 -0
- package/dist/src/learning/ragSufficiencyJudge.js +161 -0
- package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
- package/dist/src/learning/requestNormalization.js +198 -0
- package/dist/src/learning/runtimeCapability.js +4677 -0
- package/dist/src/learning/runtimeCapability.test.js +3635 -0
- package/dist/src/learning/store.js +1240 -0
- package/dist/src/learning/store.test.js +1126 -0
- package/dist/src/learning/tutorAdapter.js +2 -0
- package/dist/src/learning/types.js +2 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
- package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
- package/dist/src/lfs.asset.policy.contract.test.js +153 -0
- package/dist/src/license.policy.contract.test.js +66 -0
- package/dist/src/memory/MemoryGovernance.js +74 -0
- package/dist/src/memory/MemoryGovernance.test.js +46 -0
- package/dist/src/memory/types.js +2 -0
- package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
- package/dist/src/middleware/auth.js +17 -0
- package/dist/src/middleware/body-parser.js +45 -0
- package/dist/src/middleware/cors.js +44 -0
- package/dist/src/middleware/index.js +21 -0
- package/dist/src/middleware/request-trace.js +96 -0
- package/dist/src/notemd/AppConfigToml.js +6 -4
- package/dist/src/notemd/MermaidProcessor.js +400 -50
- package/dist/src/notemd/NotemdService.js +498 -13
- package/dist/src/notemd/PromptManager.js +15 -0
- package/dist/src/notemd/cli/commands.js +357 -0
- package/dist/src/notemd/cli/dispatcher.js +225 -0
- package/dist/src/notemd/cli/index.js +169 -0
- package/dist/src/notemd/cli/parser.js +68 -0
- package/dist/src/notemd/cli/types.js +2 -0
- package/dist/src/notemd/constants.js +43 -0
- package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
- package/dist/src/notemd/diagram/diagramSpec.js +79 -0
- package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
- package/dist/src/notemd/diagram/intent.js +95 -0
- package/dist/src/notemd/diagram/planner.js +71 -0
- package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
- package/dist/src/notemd/diagram/types.js +18 -0
- package/dist/src/notemd/index.js +26 -0
- package/dist/src/notemd/operations/capabilityManifest.js +23 -0
- package/dist/src/notemd/operations/cliContracts.js +17 -0
- package/dist/src/notemd/operations/configProfileCommands.js +85 -0
- package/dist/src/notemd/operations/registry.contract.test.js +95 -0
- package/dist/src/notemd/operations/registry.js +991 -0
- package/dist/src/notemd/operations/types.js +2 -0
- package/dist/src/notemd/providerDiagnostics.js +220 -0
- package/dist/src/notemd/providerProfiles.js +42 -0
- package/dist/src/notemd/providerTemplates.js +231 -0
- package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
- package/dist/src/notemd/search/SearchManager.js +13 -0
- package/dist/src/notemd/search/SearchProvider.js +2 -0
- package/dist/src/notemd/search/TavilyProvider.js +44 -0
- package/dist/src/notemd.agent.manifest.test.js +85 -0
- package/dist/src/notemd.api.contract.test.js +14 -1
- package/dist/src/notemd.app_config_toml.test.js +2 -1
- package/dist/src/notemd.batch.workflow.test.js +117 -0
- package/dist/src/notemd.cli.e2e.test.js +136 -0
- package/dist/src/notemd.core.test.js +51 -0
- package/dist/src/notemd.diagram.pipeline.test.js +233 -0
- package/dist/src/notemd.providerTemplates.test.js +34 -0
- package/dist/src/notemd.server.integration.test.js +143 -35
- package/dist/src/notemd.workflow.pipeline.test.js +162 -0
- package/dist/src/pathbridge.handshake.contract.test.js +16 -2
- package/dist/src/pathmode.background.contract.test.js +69 -0
- package/dist/src/pathmode.settings.api.contract.test.js +9 -0
- package/dist/src/pkg.sidecar.contract.test.js +9 -3
- package/dist/src/platform/ExportProfile.js +58 -0
- package/dist/src/platform/PlatformCapabilities.js +45 -0
- package/dist/src/platform/PlatformCapabilities.test.js +30 -0
- package/dist/src/platform/RenderMaterializer.js +33 -0
- package/dist/src/platform/RenderMaterializer.test.js +32 -0
- package/dist/src/query_backend.external_http.integration.test.js +410 -0
- package/dist/src/reader_renderer.js +404 -3
- package/dist/src/reader_renderer.test.js +87 -0
- package/dist/src/release.godot.mirror.contract.test.js +73 -0
- package/dist/src/resources/ResourceRegistry.js +223 -0
- package/dist/src/resources/ResourceRegistry.test.js +61 -0
- package/dist/src/resources/types.js +2 -0
- package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
- package/dist/src/routes/data.js +267 -0
- package/dist/src/routes/diagnostics.js +51 -0
- package/dist/src/routes/index.js +23 -0
- package/dist/src/routes/knowledge.js +968 -0
- package/dist/src/routes/markdown.js +287 -0
- package/dist/src/routes/notemd.js +565 -0
- package/dist/src/routes/registry.contract.test.js +130 -0
- package/dist/src/routes/render.js +285 -0
- package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
- package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
- package/dist/src/routes/settings.js +6 -0
- package/dist/src/routes/staticFiles.js +94 -0
- package/dist/src/routes/types.js +2 -0
- package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
- package/dist/src/server.js +11090 -1549
- package/dist/src/server.migration.test.js +193 -21
- package/dist/src/server.port.fallback.contract.test.js +63 -0
- package/dist/src/session/SessionStateStore.js +81 -0
- package/dist/src/session/SessionStateStore.test.js +58 -0
- package/dist/src/session/types.js +2 -0
- package/dist/src/settings.runtime.contract.test.js +50 -0
- package/dist/src/shared/types.contract.test.js +107 -0
- package/dist/src/shared/types.js +22 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
- package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
- package/dist/src/source_manager.loadflow.test.js +46 -0
- package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
- package/dist/src/tauri.frontend.build.contract.test.js +60 -0
- package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
- package/dist/src/utils/RuntimePaths.js +4 -13
- package/dist/src/utils/platform.js +153 -0
- package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
- package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
- package/dist/src/workflows/types.js +2 -0
- package/dist/src/workspace/WorkspaceRegistry.js +122 -0
- package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
- package/dist/src/workspace/types.js +2 -0
- package/package.json +61 -10
|
@@ -9,12 +9,19 @@ class Reader {
|
|
|
9
9
|
this.contentBox = document.getElementById('reading-content-box');
|
|
10
10
|
this.body = document.getElementById('reading-body');
|
|
11
11
|
this.title = document.getElementById('reading-title');
|
|
12
|
+
this._touchZoomBoundBody = null;
|
|
12
13
|
|
|
13
14
|
this.isLocked = true;
|
|
14
15
|
this.currentZoom = 0.5;
|
|
15
16
|
this._protocolRenderState = null;
|
|
16
17
|
this._protocolScrollHandler = null;
|
|
17
18
|
this._mermaidInitialized = false;
|
|
19
|
+
this._mermaidRenderStats = {
|
|
20
|
+
frontendRender: 0,
|
|
21
|
+
frontendRun: 0,
|
|
22
|
+
backendPng: 0,
|
|
23
|
+
failed: 0,
|
|
24
|
+
};
|
|
18
25
|
|
|
19
26
|
this.init();
|
|
20
27
|
}
|
|
@@ -45,14 +52,50 @@ class Reader {
|
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
// Touch Gestures (Pinch to Zoom)
|
|
48
|
-
this.
|
|
55
|
+
this.bindTouchZoomSurface();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
refreshDomRefs() {
|
|
59
|
+
this.window = document.getElementById('reading-window');
|
|
60
|
+
this.contentBox = document.getElementById('reading-content-box');
|
|
61
|
+
this.body = document.getElementById('reading-body');
|
|
62
|
+
this.title = document.getElementById('reading-title');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ensureReaderStructure() {
|
|
66
|
+
this.refreshDomRefs();
|
|
67
|
+
if (!this.window || !this.contentBox || !this.title) {
|
|
68
|
+
throw new Error('Reader shell structure is unavailable in the current document.');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const existingBody = document.getElementById('reading-body');
|
|
72
|
+
const bodyDetached = !!(existingBody && !this.contentBox.contains(existingBody));
|
|
73
|
+
if (!existingBody || bodyDetached) {
|
|
74
|
+
const replacementBody = document.createElement('div');
|
|
75
|
+
replacementBody.id = 'reading-body';
|
|
76
|
+
replacementBody.className = `reading-body ${this.isLocked ? 'locked' : 'unlocked'}`;
|
|
77
|
+
replacementBody.style.fontSize = `${this.currentZoom}rem`;
|
|
78
|
+
this.contentBox.appendChild(replacementBody);
|
|
79
|
+
this.body = replacementBody;
|
|
80
|
+
} else {
|
|
81
|
+
this.body = existingBody;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.bindTouchZoomSurface();
|
|
85
|
+
return this.body;
|
|
49
86
|
}
|
|
50
|
-
|
|
51
|
-
|
|
87
|
+
|
|
88
|
+
bindTouchZoomSurface() {
|
|
89
|
+
const body = this.ensureReaderStructureBodyOnly();
|
|
90
|
+
if (!body || this._touchZoomBoundBody === body) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this._touchZoomBoundBody = body;
|
|
94
|
+
|
|
52
95
|
let initialDistance = 0;
|
|
53
96
|
let initialZoom = 1.0;
|
|
54
|
-
|
|
55
|
-
|
|
97
|
+
|
|
98
|
+
body.addEventListener('touchstart', (e) => {
|
|
56
99
|
if (e.touches.length === 2) {
|
|
57
100
|
initialDistance = Math.hypot(
|
|
58
101
|
e.touches[0].pageX - e.touches[1].pageX,
|
|
@@ -63,7 +106,7 @@ class Reader {
|
|
|
63
106
|
}
|
|
64
107
|
}, { passive: false });
|
|
65
108
|
|
|
66
|
-
|
|
109
|
+
body.addEventListener('touchmove', (e) => {
|
|
67
110
|
if (e.touches.length === 2) {
|
|
68
111
|
const currentDistance = Math.hypot(
|
|
69
112
|
e.touches[0].pageX - e.touches[1].pageX,
|
|
@@ -83,14 +126,158 @@ class Reader {
|
|
|
83
126
|
}
|
|
84
127
|
}, { passive: false });
|
|
85
128
|
|
|
86
|
-
|
|
129
|
+
body.addEventListener('touchend', (e) => {
|
|
87
130
|
if (e.touches.length < 2) {
|
|
88
131
|
initialDistance = 0;
|
|
89
132
|
}
|
|
90
133
|
});
|
|
91
134
|
}
|
|
92
135
|
|
|
136
|
+
ensureReaderStructureBodyOnly() {
|
|
137
|
+
this.refreshDomRefs();
|
|
138
|
+
return this.body || null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
isMermaidErrorArtifactText(text) {
|
|
142
|
+
const normalized = String(text || '').toLowerCase();
|
|
143
|
+
if (!normalized) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
return (
|
|
147
|
+
normalized.includes('syntax error in text') ||
|
|
148
|
+
normalized.includes('lexical error on line') ||
|
|
149
|
+
normalized.includes('parse error on line') ||
|
|
150
|
+
normalized.includes('mermaid version') ||
|
|
151
|
+
normalized.includes('diagram syntax error')
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
resolveMermaidErrorArtifactHost(node) {
|
|
156
|
+
if (!node || typeof node.closest !== 'function') {
|
|
157
|
+
return node;
|
|
158
|
+
}
|
|
159
|
+
return (
|
|
160
|
+
node.closest('.mermaid-render-host-offscreen') ||
|
|
161
|
+
node.closest('.mermaid') ||
|
|
162
|
+
node.closest('.mermaid-render-failed') ||
|
|
163
|
+
node.closest('.reader-block') ||
|
|
164
|
+
node.closest('svg') ||
|
|
165
|
+
node
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
isProtectedMermaidSuppressionHost(host) {
|
|
170
|
+
if (!host || host.nodeType !== Node.ELEMENT_NODE) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
return (
|
|
174
|
+
host === document.body ||
|
|
175
|
+
host === document.documentElement ||
|
|
176
|
+
host === document.head ||
|
|
177
|
+
host.id === 'graph-wrapper' ||
|
|
178
|
+
host.id === 'path-container' ||
|
|
179
|
+
host.id === 'reading-window' ||
|
|
180
|
+
host.id === 'reading-content-box' ||
|
|
181
|
+
host.id === 'reading-body'
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
collectMermaidErrorArtifactHosts(root) {
|
|
186
|
+
const hosts = [];
|
|
187
|
+
const hostSet = new Set();
|
|
188
|
+
const candidates = [];
|
|
189
|
+
if (!root) {
|
|
190
|
+
return hosts;
|
|
191
|
+
}
|
|
192
|
+
if (
|
|
193
|
+
root.nodeType === Node.ELEMENT_NODE &&
|
|
194
|
+
root !== document.body &&
|
|
195
|
+
root !== document.documentElement &&
|
|
196
|
+
root !== document.head
|
|
197
|
+
) {
|
|
198
|
+
candidates.push(root);
|
|
199
|
+
}
|
|
200
|
+
if (typeof root.querySelectorAll === 'function') {
|
|
201
|
+
root.querySelectorAll('svg, .mermaid, div, section, article, aside, img, foreignObject').forEach((node) => {
|
|
202
|
+
candidates.push(node);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
candidates.forEach((candidate) => {
|
|
206
|
+
if (!candidate || candidate.nodeType !== Node.ELEMENT_NODE) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const text = String(
|
|
210
|
+
candidate.textContent ||
|
|
211
|
+
candidate.getAttribute?.('alt') ||
|
|
212
|
+
candidate.getAttribute?.('aria-label') ||
|
|
213
|
+
''
|
|
214
|
+
).trim();
|
|
215
|
+
const hasErrorClass = Boolean(
|
|
216
|
+
candidate.classList?.contains('error-icon') ||
|
|
217
|
+
candidate.querySelector?.('.error-icon')
|
|
218
|
+
);
|
|
219
|
+
if (!hasErrorClass && !this.isMermaidErrorArtifactText(text)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const host = this.resolveMermaidErrorArtifactHost(candidate);
|
|
223
|
+
if (!host || hostSet.has(host) || this.isProtectedMermaidSuppressionHost(host)) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
hostSet.add(host);
|
|
227
|
+
hosts.push(host);
|
|
228
|
+
});
|
|
229
|
+
return hosts;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
suppressLeakedMermaidErrorArtifacts(options = {}) {
|
|
233
|
+
const root = options.root || document.body;
|
|
234
|
+
const preserveNode = options.preserveNode || null;
|
|
235
|
+
const hosts = this.collectMermaidErrorArtifactHosts(root);
|
|
236
|
+
hosts.forEach((host) => {
|
|
237
|
+
if (!host || host.closest?.('.mermaid-render-failed')) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (preserveNode && (host === preserveNode || preserveNode.contains(host) || host.contains(preserveNode))) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (host.parentNode) {
|
|
244
|
+
host.parentNode.removeChild(host);
|
|
245
|
+
} else if (host.style) {
|
|
246
|
+
host.style.display = 'none';
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
return hosts.length;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
createOffscreenMermaidRenderHost(widthHint) {
|
|
253
|
+
const hostWidth = Math.max(480, Number(widthHint) || 960);
|
|
254
|
+
const host = document.createElement('div');
|
|
255
|
+
host.className = 'mermaid-render-host-offscreen';
|
|
256
|
+
host.style.position = 'fixed';
|
|
257
|
+
host.style.left = '-20000px';
|
|
258
|
+
host.style.top = '0';
|
|
259
|
+
host.style.width = String(hostWidth) + 'px';
|
|
260
|
+
host.style.minWidth = String(hostWidth) + 'px';
|
|
261
|
+
host.style.height = 'auto';
|
|
262
|
+
host.style.overflow = 'visible';
|
|
263
|
+
host.style.opacity = '0';
|
|
264
|
+
host.style.pointerEvents = 'none';
|
|
265
|
+
host.style.background = 'transparent';
|
|
266
|
+
host.style.fontFamily = '"Microsoft YaHei UI", "Microsoft YaHei", "PingFang SC", "Noto Sans CJK SC", "Segoe UI", sans-serif';
|
|
267
|
+
const mountRoot = (
|
|
268
|
+
(this.window && this.window.ownerDocument && this.window.ownerDocument.body) ||
|
|
269
|
+
document.body ||
|
|
270
|
+
document.documentElement
|
|
271
|
+
);
|
|
272
|
+
if (!mountRoot || typeof mountRoot.appendChild !== 'function') {
|
|
273
|
+
throw new Error('Reader Mermaid offscreen host mount root is unavailable.');
|
|
274
|
+
}
|
|
275
|
+
mountRoot.appendChild(host);
|
|
276
|
+
return host;
|
|
277
|
+
}
|
|
278
|
+
|
|
93
279
|
async open(node) {
|
|
280
|
+
this.ensureReaderStructure();
|
|
94
281
|
const nodeLike = this.normalizeNodeInput(node);
|
|
95
282
|
this.title.innerText = nodeLike.label;
|
|
96
283
|
|
|
@@ -119,6 +306,13 @@ class Reader {
|
|
|
119
306
|
let rendered = false;
|
|
120
307
|
if (filePath) {
|
|
121
308
|
rendered = await this.tryRenderViaMarkdownProtocol(nodeLike, filePath, resolvedTarget, sessionId);
|
|
309
|
+
if (rendered && this.isReaderBodyVisiblyEmpty()) {
|
|
310
|
+
console.warn('[Reader] Markdown protocol path completed without visible content. Falling back to raw markdown render.', {
|
|
311
|
+
nodeId: nodeLike.id,
|
|
312
|
+
filePath,
|
|
313
|
+
});
|
|
314
|
+
rendered = false;
|
|
315
|
+
}
|
|
122
316
|
}
|
|
123
317
|
if (!rendered) {
|
|
124
318
|
const rawContent = await this.loadRawContentFallback(nodeLike);
|
|
@@ -134,7 +328,7 @@ class Reader {
|
|
|
134
328
|
|
|
135
329
|
normalizeNodeInput(nodeLike) {
|
|
136
330
|
if (typeof nodeLike === 'string') {
|
|
137
|
-
|
|
331
|
+
nodeLike = {
|
|
138
332
|
id: nodeLike,
|
|
139
333
|
label: nodeLike,
|
|
140
334
|
content: '',
|
|
@@ -143,15 +337,41 @@ class Reader {
|
|
|
143
337
|
}
|
|
144
338
|
const safeNode = (nodeLike && typeof nodeLike === 'object') ? nodeLike : {};
|
|
145
339
|
const nodeId = String(safeNode.id || safeNode.label || 'unknown-node');
|
|
340
|
+
const hydratedNode = this.lookupNodeDataById(nodeId);
|
|
341
|
+
const hydratedMetadata = (hydratedNode && hydratedNode.metadata && typeof hydratedNode.metadata === 'object')
|
|
342
|
+
? hydratedNode.metadata
|
|
343
|
+
: {};
|
|
344
|
+
const safeMetadata = (safeNode.metadata && typeof safeNode.metadata === 'object') ? safeNode.metadata : {};
|
|
146
345
|
return {
|
|
346
|
+
...(hydratedNode && typeof hydratedNode === 'object' ? hydratedNode : {}),
|
|
147
347
|
...safeNode,
|
|
148
348
|
id: nodeId,
|
|
149
|
-
label: String(safeNode.label || nodeId),
|
|
349
|
+
label: String(safeNode.label || hydratedNode?.label || nodeId),
|
|
150
350
|
content: typeof safeNode.content === 'string' ? safeNode.content : '',
|
|
151
|
-
metadata:
|
|
351
|
+
metadata: {
|
|
352
|
+
...hydratedMetadata,
|
|
353
|
+
...safeMetadata,
|
|
354
|
+
},
|
|
152
355
|
};
|
|
153
356
|
}
|
|
154
357
|
|
|
358
|
+
lookupNodeDataById(nodeId) {
|
|
359
|
+
const normalizedNodeId = String(nodeId || '').trim();
|
|
360
|
+
if (!normalizedNodeId) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
const sourceData = (typeof graphData !== 'undefined') ? graphData : window.graphData;
|
|
364
|
+
if (!sourceData || !Array.isArray(sourceData.nodes)) {
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
const matchedNode = sourceData.nodes.find((item) => (
|
|
368
|
+
item &&
|
|
369
|
+
(String(item.id || '').trim() === normalizedNodeId ||
|
|
370
|
+
String(item.label || '').trim() === normalizedNodeId)
|
|
371
|
+
));
|
|
372
|
+
return matchedNode && typeof matchedNode === 'object' ? matchedNode : null;
|
|
373
|
+
}
|
|
374
|
+
|
|
155
375
|
extractNodeFilePath(nodeLike) {
|
|
156
376
|
if (!nodeLike || typeof nodeLike !== 'object') return '';
|
|
157
377
|
const metadata = (nodeLike.metadata && typeof nodeLike.metadata === 'object') ? nodeLike.metadata : {};
|
|
@@ -160,6 +380,16 @@ class Reader {
|
|
|
160
380
|
return String(nodeLike.filepath || nodeLike.filePath || '').trim();
|
|
161
381
|
}
|
|
162
382
|
|
|
383
|
+
isReaderBodyVisiblyEmpty() {
|
|
384
|
+
if (!this.body) {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
return (
|
|
388
|
+
this.body.children.length === 0 &&
|
|
389
|
+
String(this.body.textContent || '').trim().length === 0
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
163
393
|
getRuntimeBaseUrl() {
|
|
164
394
|
if (window.NoteConnectionRuntime && typeof window.NoteConnectionRuntime.getBaseUrl === 'function') {
|
|
165
395
|
return window.NoteConnectionRuntime.getBaseUrl();
|
|
@@ -225,6 +455,89 @@ class Reader {
|
|
|
225
455
|
});
|
|
226
456
|
}
|
|
227
457
|
|
|
458
|
+
normalizeMermaidDefinition(source) {
|
|
459
|
+
if (window.pathModules && window.pathModules.utils && typeof window.pathModules.utils.normalizeBridgeMermaidDefinition === 'function') {
|
|
460
|
+
return window.pathModules.utils.normalizeBridgeMermaidDefinition(source);
|
|
461
|
+
}
|
|
462
|
+
return String(source || '').trim();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
getMermaidDefinitionCandidates(source) {
|
|
466
|
+
const candidates = [];
|
|
467
|
+
const normalized = this.normalizeMermaidDefinition(source);
|
|
468
|
+
const raw = String(source || '').replace(/\r\n?/g, '\n').trim();
|
|
469
|
+
[normalized, raw].forEach((candidate) => {
|
|
470
|
+
const next = String(candidate || '').trim();
|
|
471
|
+
if (!next || candidates.includes(next)) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
candidates.push(next);
|
|
475
|
+
});
|
|
476
|
+
return candidates;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async canParseMermaidDefinition(definition) {
|
|
480
|
+
if (!window.mermaid || typeof mermaid.parse !== 'function') {
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
try {
|
|
484
|
+
await mermaid.parse(definition);
|
|
485
|
+
return true;
|
|
486
|
+
} catch (_error) {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
isMermaidErrorSvgMarkup(markup) {
|
|
492
|
+
const text = String(markup || '').toLowerCase();
|
|
493
|
+
if (!text) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
return (
|
|
497
|
+
text.includes('syntax error in text') ||
|
|
498
|
+
text.includes('lexical error on line') ||
|
|
499
|
+
text.includes('parse error on line') ||
|
|
500
|
+
text.includes('mermaid version') ||
|
|
501
|
+
text.includes('class="error-icon"') ||
|
|
502
|
+
text.includes('id="error-icon"')
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
renderCompactMermaidFailure(wrapper, error, source) {
|
|
507
|
+
wrapper.classList.add('mermaid-render-failed');
|
|
508
|
+
wrapper.innerHTML = '';
|
|
509
|
+
|
|
510
|
+
const notice = document.createElement('div');
|
|
511
|
+
notice.className = 'mermaid-render-notice';
|
|
512
|
+
|
|
513
|
+
const title = document.createElement('div');
|
|
514
|
+
title.className = 'mermaid-render-notice-title';
|
|
515
|
+
title.textContent = 'Mermaid diagram unavailable.';
|
|
516
|
+
notice.appendChild(title);
|
|
517
|
+
|
|
518
|
+
const summary = document.createElement('div');
|
|
519
|
+
summary.className = 'mermaid-render-notice-summary';
|
|
520
|
+
summary.textContent = 'Rendering failed in the current runtime. Details are available on demand.';
|
|
521
|
+
notice.appendChild(summary);
|
|
522
|
+
|
|
523
|
+
const details = document.createElement('details');
|
|
524
|
+
details.className = 'mermaid-render-notice-details';
|
|
525
|
+
|
|
526
|
+
const detailsSummary = document.createElement('summary');
|
|
527
|
+
detailsSummary.textContent = 'Render details';
|
|
528
|
+
details.appendChild(detailsSummary);
|
|
529
|
+
|
|
530
|
+
const detailText = document.createElement('pre');
|
|
531
|
+
detailText.className = 'mermaid-render-notice-code';
|
|
532
|
+
const errorMessage = String(error && error.message ? error.message : error || 'Unknown error').trim();
|
|
533
|
+
const sourceSnippet = String(source || '').trim().slice(0, 320);
|
|
534
|
+
detailText.textContent = errorMessage + (sourceSnippet ? `\n\n${sourceSnippet}` : '');
|
|
535
|
+
details.appendChild(detailText);
|
|
536
|
+
|
|
537
|
+
notice.appendChild(details);
|
|
538
|
+
wrapper.appendChild(notice);
|
|
539
|
+
}
|
|
540
|
+
|
|
228
541
|
async tryRenderViaMarkdownProtocol(nodeLike, filePath, resolvedTarget, sessionId) {
|
|
229
542
|
const config = this.getReadingProtocolConfig();
|
|
230
543
|
if (String(config.markdownEngine || 'auto').toLowerCase() === 'legacy') {
|
|
@@ -306,13 +619,14 @@ class Reader {
|
|
|
306
619
|
|
|
307
620
|
cleanupProtocolState() {
|
|
308
621
|
this._protocolRenderState = null;
|
|
309
|
-
if (this._protocolScrollHandler) {
|
|
622
|
+
if (this._protocolScrollHandler && this.body) {
|
|
310
623
|
this.body.removeEventListener('scroll', this._protocolScrollHandler);
|
|
311
624
|
this._protocolScrollHandler = null;
|
|
312
625
|
}
|
|
313
626
|
}
|
|
314
627
|
|
|
315
628
|
async renderProtocolBlocks(blocks, position, currentFilePath, sessionId) {
|
|
629
|
+
this.ensureReaderStructure();
|
|
316
630
|
if (!Array.isArray(blocks) || blocks.length === 0) {
|
|
317
631
|
return;
|
|
318
632
|
}
|
|
@@ -509,8 +823,21 @@ class Reader {
|
|
|
509
823
|
this.body.addEventListener('scroll', this._protocolScrollHandler, { passive: true });
|
|
510
824
|
}
|
|
511
825
|
|
|
826
|
+
autoFixInlineMermaidFenceAfterBlockMath(markdownText) {
|
|
827
|
+
const source = String(markdownText || '');
|
|
828
|
+
if (!source) {
|
|
829
|
+
return source;
|
|
830
|
+
}
|
|
831
|
+
if (!source.includes('```mermaid') || !source.includes('$$')) {
|
|
832
|
+
return source;
|
|
833
|
+
}
|
|
834
|
+
return source.replace(/\$\$[ \t]*```mermaid\b/g, '$$\n```mermaid');
|
|
835
|
+
}
|
|
836
|
+
|
|
512
837
|
normalizeProtocolBlock(block) {
|
|
513
|
-
const source =
|
|
838
|
+
const source = this.autoFixInlineMermaidFenceAfterBlockMath(
|
|
839
|
+
String(block && block.text ? block.text : '')
|
|
840
|
+
);
|
|
514
841
|
const type = String(block && block.type ? block.type : '').trim().toLowerCase();
|
|
515
842
|
const normalizedType = type || 'paragraph';
|
|
516
843
|
if (!source) {
|
|
@@ -712,7 +1039,11 @@ class Reader {
|
|
|
712
1039
|
}
|
|
713
1040
|
|
|
714
1041
|
async renderRawMarkdown(rawContent, currentFilePath, sessionId) {
|
|
715
|
-
|
|
1042
|
+
this.ensureReaderStructure();
|
|
1043
|
+
const sanitizedMarkdown = this.autoFixInlineMermaidFenceAfterBlockMath(
|
|
1044
|
+
String(rawContent || '*No content available.*')
|
|
1045
|
+
);
|
|
1046
|
+
const markdownText = this.transformWikiLinks(sanitizedMarkdown);
|
|
716
1047
|
this.body.innerHTML = marked.parse(markdownText);
|
|
717
1048
|
this.bindWikiLinks(this.body, currentFilePath || this.getRuntimeBaseUrl());
|
|
718
1049
|
this.renderMathInContainer(this.body);
|
|
@@ -734,10 +1065,8 @@ class Reader {
|
|
|
734
1065
|
}
|
|
735
1066
|
|
|
736
1067
|
async renderMermaidInContainer(container) {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
if (!this._mermaidInitialized) {
|
|
1068
|
+
const hasFrontendMermaid = Boolean(window.mermaid);
|
|
1069
|
+
if (hasFrontendMermaid && !this._mermaidInitialized) {
|
|
741
1070
|
mermaid.initialize({
|
|
742
1071
|
startOnLoad: false,
|
|
743
1072
|
theme: 'dark',
|
|
@@ -747,6 +1076,8 @@ class Reader {
|
|
|
747
1076
|
this._mermaidInitialized = true;
|
|
748
1077
|
}
|
|
749
1078
|
|
|
1079
|
+
this.suppressLeakedMermaidErrorArtifacts({ root: document.body });
|
|
1080
|
+
|
|
750
1081
|
let mermaidBlocks = Array.from(container.querySelectorAll('pre code.language-mermaid, pre code.lang-mermaid'));
|
|
751
1082
|
if (mermaidBlocks.length === 0 && container.dataset && container.dataset.codeLanguage === 'mermaid') {
|
|
752
1083
|
const fallbackBlock = container.querySelector('pre code');
|
|
@@ -758,7 +1089,8 @@ class Reader {
|
|
|
758
1089
|
for (const block of mermaidBlocks) {
|
|
759
1090
|
const txt = document.createElement('textarea');
|
|
760
1091
|
txt.innerHTML = block.innerHTML;
|
|
761
|
-
const
|
|
1092
|
+
const candidateDefinitions = this.getMermaidDefinitionCandidates(txt.value);
|
|
1093
|
+
const graphDefinition = candidateDefinitions[0] || '';
|
|
762
1094
|
const parentPre = block.parentElement;
|
|
763
1095
|
if (!parentPre || !parentPre.parentNode) continue;
|
|
764
1096
|
|
|
@@ -766,25 +1098,165 @@ class Reader {
|
|
|
766
1098
|
const renderId = `reader-mermaid-${this._mermaidRenderCounter}`;
|
|
767
1099
|
const wrapper = document.createElement('div');
|
|
768
1100
|
wrapper.className = 'mermaid';
|
|
769
|
-
wrapper.id = renderId;
|
|
770
1101
|
parentPre.parentNode.replaceChild(wrapper, parentPre);
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
1102
|
+
let rendered = false;
|
|
1103
|
+
const renderErrors = [];
|
|
1104
|
+
let renderSource = '';
|
|
1105
|
+
const section = wrapper.closest('.reader-block');
|
|
1106
|
+
const blockId = section && section.dataset ? String(section.dataset.blockId || '').trim() : '';
|
|
1107
|
+
|
|
1108
|
+
if (hasFrontendMermaid) {
|
|
1109
|
+
for (let candidateIndex = 0; candidateIndex < candidateDefinitions.length; candidateIndex += 1) {
|
|
1110
|
+
const candidate = candidateDefinitions[candidateIndex];
|
|
1111
|
+
const offscreenHost = this.createOffscreenMermaidRenderHost(
|
|
1112
|
+
wrapper.clientWidth || parentPre.clientWidth || container.clientWidth || 960
|
|
1113
|
+
);
|
|
1114
|
+
try {
|
|
1115
|
+
wrapper.innerHTML = '';
|
|
1116
|
+
const renderedResult = await mermaid.render(`${renderId}-${candidateIndex}`, candidate, offscreenHost);
|
|
1117
|
+
if (renderedResult && typeof renderedResult.svg === 'string' && renderedResult.svg.trim()) {
|
|
1118
|
+
if (this.isMermaidErrorSvgMarkup(renderedResult.svg)) {
|
|
1119
|
+
throw new Error('Mermaid frontend renderer returned an error SVG instead of a diagram.');
|
|
1120
|
+
}
|
|
1121
|
+
wrapper.innerHTML = renderedResult.svg;
|
|
1122
|
+
}
|
|
1123
|
+
if (/<svg[\s>]/i.test(String(wrapper.innerHTML || ''))) {
|
|
1124
|
+
rendered = true;
|
|
1125
|
+
renderSource = 'frontend-render';
|
|
1126
|
+
this._mermaidRenderStats.frontendRender += 1;
|
|
1127
|
+
break;
|
|
1128
|
+
}
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
renderErrors.push(error);
|
|
1131
|
+
wrapper.innerHTML = '';
|
|
1132
|
+
this.suppressLeakedMermaidErrorArtifacts({
|
|
1133
|
+
root: document.body,
|
|
1134
|
+
preserveNode: wrapper,
|
|
1135
|
+
});
|
|
1136
|
+
} finally {
|
|
1137
|
+
offscreenHost.remove();
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
} else {
|
|
1141
|
+
renderErrors.push(new Error('Mermaid runtime is unavailable in current webview.'));
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
if (!rendered && hasFrontendMermaid) {
|
|
1145
|
+
for (let candidateIndex = 0; candidateIndex < candidateDefinitions.length; candidateIndex += 1) {
|
|
1146
|
+
const candidate = candidateDefinitions[candidateIndex];
|
|
1147
|
+
const canParse = await this.canParseMermaidDefinition(candidate);
|
|
1148
|
+
if (!canParse) {
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
try {
|
|
1152
|
+
wrapper.innerHTML = '';
|
|
1153
|
+
wrapper.textContent = candidate;
|
|
1154
|
+
await mermaid.run({ nodes: [wrapper] });
|
|
1155
|
+
rendered = /<svg[\s>]/i.test(String(wrapper.innerHTML || '')) &&
|
|
1156
|
+
!this.isMermaidErrorSvgMarkup(String(wrapper.innerHTML || ''));
|
|
1157
|
+
if (rendered) {
|
|
1158
|
+
renderSource = 'frontend-run';
|
|
1159
|
+
this._mermaidRenderStats.frontendRun += 1;
|
|
1160
|
+
break;
|
|
1161
|
+
}
|
|
1162
|
+
} catch (error) {
|
|
1163
|
+
renderErrors.push(error);
|
|
1164
|
+
wrapper.innerHTML = '';
|
|
1165
|
+
this.suppressLeakedMermaidErrorArtifacts({
|
|
1166
|
+
root: document.body,
|
|
1167
|
+
preserveNode: wrapper,
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
if (!rendered) {
|
|
1174
|
+
wrapper.innerHTML = '';
|
|
1175
|
+
const backendRendered = await this.renderMermaidViaBackend(graphDefinition);
|
|
1176
|
+
if (backendRendered && backendRendered.pngBase64) {
|
|
1177
|
+
const altRenderer = backendRendered.renderer ? ` (${backendRendered.renderer})` : '';
|
|
1178
|
+
wrapper.innerHTML = `<img class="mermaid-fallback-image" src="data:image/png;base64,${backendRendered.pngBase64}" alt="Mermaid diagram${altRenderer}" />`;
|
|
1179
|
+
rendered = true;
|
|
1180
|
+
renderSource = backendRendered.renderer ? `backend-${backendRendered.renderer}` : 'backend-png';
|
|
1181
|
+
this._mermaidRenderStats.backendPng += 1;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
if (!rendered) {
|
|
1186
|
+
const lastError = renderErrors.length > 0 ? renderErrors[renderErrors.length - 1] : null;
|
|
1187
|
+
this.renderCompactMermaidFailure(wrapper, lastError, graphDefinition);
|
|
1188
|
+
if (lastError) {
|
|
1189
|
+
console.error('Mermaid error:', lastError);
|
|
1190
|
+
}
|
|
1191
|
+
renderSource = 'failed';
|
|
1192
|
+
this._mermaidRenderStats.failed += 1;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
this.suppressLeakedMermaidErrorArtifacts({
|
|
1196
|
+
root: document.body,
|
|
1197
|
+
preserveNode: wrapper,
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
if (renderSource) {
|
|
1201
|
+
wrapper.dataset.renderSource = renderSource;
|
|
1202
|
+
const blockSuffix = blockId ? ` block=${blockId}` : '';
|
|
1203
|
+
console.info(`[Reader] Mermaid render source: ${renderSource}${blockSuffix}`);
|
|
777
1204
|
}
|
|
778
1205
|
}
|
|
779
1206
|
if (mermaidBlocks.length > 0) {
|
|
1207
|
+
console.info(
|
|
1208
|
+
`[Reader] Mermaid render stats: frontend-render=${this._mermaidRenderStats.frontendRender}, frontend-run=${this._mermaidRenderStats.frontendRun}, backend-png=${this._mermaidRenderStats.backendPng}, failed=${this._mermaidRenderStats.failed}`
|
|
1209
|
+
);
|
|
780
1210
|
this.initMermaidZoom();
|
|
781
1211
|
}
|
|
782
1212
|
}
|
|
783
1213
|
|
|
1214
|
+
async renderMermaidViaBackend(graphDefinition) {
|
|
1215
|
+
const source = this.normalizeMermaidDefinition(graphDefinition || '');
|
|
1216
|
+
if (!source) {
|
|
1217
|
+
return null;
|
|
1218
|
+
}
|
|
1219
|
+
try {
|
|
1220
|
+
const response = await fetch(
|
|
1221
|
+
this.buildRuntimeUrl('/api/render/mermaid'),
|
|
1222
|
+
this.buildRuntimeFetchOptions({
|
|
1223
|
+
method: 'POST',
|
|
1224
|
+
headers: {
|
|
1225
|
+
'Content-Type': 'application/json',
|
|
1226
|
+
},
|
|
1227
|
+
body: JSON.stringify({
|
|
1228
|
+
source,
|
|
1229
|
+
renderer: 'auto',
|
|
1230
|
+
}),
|
|
1231
|
+
})
|
|
1232
|
+
);
|
|
1233
|
+
const payload = await response.json().catch(() => null);
|
|
1234
|
+
if (!response.ok || !payload || typeof payload.pngBase64 !== 'string' || !payload.pngBase64.trim()) {
|
|
1235
|
+
return null;
|
|
1236
|
+
}
|
|
1237
|
+
return {
|
|
1238
|
+
pngBase64: payload.pngBase64.trim(),
|
|
1239
|
+
renderer: typeof payload.renderer === 'string' ? payload.renderer.trim() : '',
|
|
1240
|
+
};
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
console.warn('[Reader] Mermaid backend fallback failed:', error);
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
784
1247
|
initMermaidZoom() {
|
|
1248
|
+
if (!this.body) {
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
785
1251
|
const mermaidDivs = this.body.querySelectorAll('.mermaid');
|
|
786
1252
|
mermaidDivs.forEach(div => {
|
|
1253
|
+
if (div.dataset && div.dataset.readerZoomBound === '1') {
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
787
1256
|
div.style.cursor = 'zoom-in';
|
|
1257
|
+
if (div.dataset) {
|
|
1258
|
+
div.dataset.readerZoomBound = '1';
|
|
1259
|
+
}
|
|
788
1260
|
div.addEventListener('click', (e) => {
|
|
789
1261
|
e.stopPropagation(); // Prevent Reader close
|
|
790
1262
|
this.openMermaidOverlay(div.innerHTML);
|
|
@@ -793,6 +1265,11 @@ class Reader {
|
|
|
793
1265
|
}
|
|
794
1266
|
|
|
795
1267
|
openMermaidOverlay(svgContent) {
|
|
1268
|
+
const existingOverlay = document.getElementById('mermaid-zoom-overlay');
|
|
1269
|
+
if (existingOverlay && existingOverlay.parentNode) {
|
|
1270
|
+
existingOverlay.parentNode.removeChild(existingOverlay);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
796
1273
|
// Create Overlay
|
|
797
1274
|
const overlay = document.createElement('div');
|
|
798
1275
|
overlay.id = 'mermaid-zoom-overlay';
|
|
@@ -802,15 +1279,29 @@ class Reader {
|
|
|
802
1279
|
const container = document.createElement('div');
|
|
803
1280
|
container.className = 'mermaid-zoom-container';
|
|
804
1281
|
container.innerHTML = svgContent;
|
|
1282
|
+
container.addEventListener('click', (event) => {
|
|
1283
|
+
event.stopPropagation();
|
|
1284
|
+
});
|
|
805
1285
|
overlay.appendChild(container);
|
|
806
1286
|
|
|
807
1287
|
// Close Button
|
|
808
1288
|
const closeBtn = document.createElement('button');
|
|
809
1289
|
closeBtn.innerText = '×';
|
|
810
1290
|
closeBtn.className = 'mermaid-close-btn';
|
|
811
|
-
closeBtn.onclick = () =>
|
|
1291
|
+
closeBtn.onclick = (event) => {
|
|
1292
|
+
event.stopPropagation();
|
|
1293
|
+
if (overlay.parentNode) {
|
|
1294
|
+
overlay.parentNode.removeChild(overlay);
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
812
1297
|
overlay.appendChild(closeBtn);
|
|
813
1298
|
|
|
1299
|
+
overlay.addEventListener('click', () => {
|
|
1300
|
+
if (overlay.parentNode) {
|
|
1301
|
+
overlay.parentNode.removeChild(overlay);
|
|
1302
|
+
}
|
|
1303
|
+
});
|
|
1304
|
+
|
|
814
1305
|
document.body.appendChild(overlay);
|
|
815
1306
|
|
|
816
1307
|
// Pan/Zoom State
|
|
@@ -931,7 +1422,9 @@ class Reader {
|
|
|
931
1422
|
if (!this._sessionId) this._sessionId = 0;
|
|
932
1423
|
this._sessionId += 1;
|
|
933
1424
|
this.cleanupProtocolState();
|
|
934
|
-
this.window
|
|
1425
|
+
if (this.window) {
|
|
1426
|
+
this.window.style.display = 'none';
|
|
1427
|
+
}
|
|
935
1428
|
}
|
|
936
1429
|
|
|
937
1430
|
toggleLock() {
|
|
@@ -940,6 +1433,7 @@ class Reader {
|
|
|
940
1433
|
}
|
|
941
1434
|
|
|
942
1435
|
updateLockState() {
|
|
1436
|
+
this.ensureReaderStructure();
|
|
943
1437
|
const btn = document.getElementById('btn-reader-lock');
|
|
944
1438
|
const zoomBtns = document.querySelectorAll('#btn-reader-zoom-in, #btn-reader-zoom-out');
|
|
945
1439
|
|
|
@@ -965,6 +1459,7 @@ class Reader {
|
|
|
965
1459
|
}
|
|
966
1460
|
|
|
967
1461
|
updateZoom() {
|
|
1462
|
+
this.ensureReaderStructure();
|
|
968
1463
|
this.body.style.fontSize = `${this.currentZoom}rem`;
|
|
969
1464
|
// Scale images if needed, but CSS 'resize' handles explicit image resizing in unlocked mode.
|
|
970
1465
|
// Font size scaling handles text content scaling.
|