noteconnection 1.7.0 → 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.
Files changed (211) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +242 -62
  3. package/dist/src/agent_workspace.contract.parity.test.js +475 -0
  4. package/dist/src/agent_workspace.frontend.test.js +9989 -0
  5. package/dist/src/agent_workspace.locale.contract.test.js +95 -0
  6. package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
  7. package/dist/src/copy.assets.contract.test.js +130 -0
  8. package/dist/src/core/PathBridge.js +28 -3
  9. package/dist/src/export/WorkspaceExportBundle.js +1130 -0
  10. package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
  11. package/dist/src/export/index.js +18 -0
  12. package/dist/src/export/types.js +2 -0
  13. package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
  14. package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
  15. package/dist/src/foundation.release.evidence.contract.test.js +406 -0
  16. package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
  17. package/dist/src/frontend/README.md +242 -62
  18. package/dist/src/frontend/agent_workspace.js +5353 -0
  19. package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
  20. package/dist/src/frontend/analysis.js +31 -4
  21. package/dist/src/frontend/app.js +2178 -62
  22. package/dist/src/frontend/focus_mode_interactions.js +33 -0
  23. package/dist/src/frontend/godot_future_path_renderer.js +540 -0
  24. package/dist/src/frontend/godot_tree_interactions.js +178 -0
  25. package/dist/src/frontend/graph_state.mjs +105 -0
  26. package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
  27. package/dist/src/frontend/i18n.mjs +186 -0
  28. package/dist/src/frontend/index.html +641 -266
  29. package/dist/src/frontend/layout_gpu.js +12 -4
  30. package/dist/src/frontend/locales/en.json +721 -5
  31. package/dist/src/frontend/locales/zh.json +721 -5
  32. package/dist/src/frontend/main.mjs +60 -0
  33. package/dist/src/frontend/markdown_runtime.js +827 -0
  34. package/dist/src/frontend/notemd.css +49 -0
  35. package/dist/src/frontend/notemd.html +7 -1
  36. package/dist/src/frontend/notemd.js +64 -0
  37. package/dist/src/frontend/path.html +107 -0
  38. package/dist/src/frontend/path_app.js +2108 -130
  39. package/dist/src/frontend/path_layout.mjs +143 -0
  40. package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
  41. package/dist/src/frontend/path_modules_bridge.js +486 -0
  42. package/dist/src/frontend/path_state.mjs +118 -0
  43. package/dist/src/frontend/path_styles.css +146 -0
  44. package/dist/src/frontend/path_worker_bridge.mjs +85 -0
  45. package/dist/src/frontend/reader.js +393 -33
  46. package/dist/src/frontend/runtime_bridge.js +67 -54
  47. package/dist/src/frontend/runtime_bridge.mjs +279 -0
  48. package/dist/src/frontend/settings.js +130 -12
  49. package/dist/src/frontend/source_manager.js +190 -21
  50. package/dist/src/frontend/styles.css +2720 -72
  51. package/dist/src/frontend/workbench_state.mjs +101 -0
  52. package/dist/src/frontend/workspace_panes.js +10168 -0
  53. package/dist/src/frontend.locale.contract.test.js +62 -0
  54. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  55. package/dist/src/indexing/IndexLifecycle.js +195 -0
  56. package/dist/src/indexing/IndexLifecycle.test.js +49 -0
  57. package/dist/src/indexing/SegmentBuilder.js +64 -0
  58. package/dist/src/indexing/UnitBuilder.js +48 -0
  59. package/dist/src/indexing/types.js +2 -0
  60. package/dist/src/knowledge.api.contract.test.js +170 -0
  61. package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
  62. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  64. package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
  65. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
  67. package/dist/src/learning/answerReleaseReview.js +4319 -0
  68. package/dist/src/learning/answerReleaseReview.test.js +2888 -0
  69. package/dist/src/learning/api.js +2 -0
  70. package/dist/src/learning/conversationComposer.js +1480 -0
  71. package/dist/src/learning/conversationComposer.test.js +1817 -0
  72. package/dist/src/learning/domains/ConversationManager.js +53 -0
  73. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  74. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  75. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  76. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  77. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  78. package/dist/src/learning/domains/TutorRouter.js +313 -0
  79. package/dist/src/learning/domains/index.js +33 -0
  80. package/dist/src/learning/domains/types.js +7 -0
  81. package/dist/src/learning/errors.js +29 -0
  82. package/dist/src/learning/evidenceContextAssembler.js +1176 -0
  83. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  84. package/dist/src/learning/graphContextAssembler.js +870 -0
  85. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  86. package/dist/src/learning/index.js +28 -0
  87. package/dist/src/learning/queryBackend.js +1898 -0
  88. package/dist/src/learning/queryBackend.test.js +955 -0
  89. package/dist/src/learning/ragContextPack.js +257 -0
  90. package/dist/src/learning/ragContextPack.test.js +160 -0
  91. package/dist/src/learning/ragPublicText.js +38 -0
  92. package/dist/src/learning/ragSufficiencyJudge.js +161 -0
  93. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  94. package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
  95. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
  96. package/dist/src/learning/requestNormalization.js +198 -0
  97. package/dist/src/learning/runtimeCapability.js +4677 -0
  98. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  99. package/dist/src/learning/store.js +1240 -0
  100. package/dist/src/learning/store.test.js +1126 -0
  101. package/dist/src/learning/tutorAdapter.js +2 -0
  102. package/dist/src/learning/types.js +2 -0
  103. package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
  104. package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
  105. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  106. package/dist/src/license.policy.contract.test.js +66 -0
  107. package/dist/src/memory/MemoryGovernance.js +74 -0
  108. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  109. package/dist/src/memory/types.js +2 -0
  110. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  111. package/dist/src/middleware/auth.js +17 -0
  112. package/dist/src/middleware/body-parser.js +45 -0
  113. package/dist/src/middleware/cors.js +44 -0
  114. package/dist/src/middleware/index.js +21 -0
  115. package/dist/src/middleware/request-trace.js +96 -0
  116. package/dist/src/notemd/AppConfigToml.js +6 -4
  117. package/dist/src/notemd/MermaidProcessor.js +400 -50
  118. package/dist/src/notemd/NotemdService.js +498 -13
  119. package/dist/src/notemd/PromptManager.js +15 -0
  120. package/dist/src/notemd/cli/commands.js +357 -0
  121. package/dist/src/notemd/cli/dispatcher.js +225 -0
  122. package/dist/src/notemd/cli/index.js +169 -0
  123. package/dist/src/notemd/cli/parser.js +68 -0
  124. package/dist/src/notemd/cli/types.js +2 -0
  125. package/dist/src/notemd/constants.js +43 -0
  126. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  127. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  128. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  129. package/dist/src/notemd/diagram/intent.js +95 -0
  130. package/dist/src/notemd/diagram/planner.js +71 -0
  131. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  132. package/dist/src/notemd/diagram/types.js +18 -0
  133. package/dist/src/notemd/index.js +26 -0
  134. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  135. package/dist/src/notemd/operations/cliContracts.js +17 -0
  136. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  137. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  138. package/dist/src/notemd/operations/registry.js +991 -0
  139. package/dist/src/notemd/operations/types.js +2 -0
  140. package/dist/src/notemd/providerDiagnostics.js +220 -0
  141. package/dist/src/notemd/providerProfiles.js +42 -0
  142. package/dist/src/notemd/providerTemplates.js +231 -0
  143. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  144. package/dist/src/notemd/search/SearchManager.js +13 -0
  145. package/dist/src/notemd/search/SearchProvider.js +2 -0
  146. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  147. package/dist/src/notemd.agent.manifest.test.js +85 -0
  148. package/dist/src/notemd.api.contract.test.js +14 -1
  149. package/dist/src/notemd.app_config_toml.test.js +2 -1
  150. package/dist/src/notemd.batch.workflow.test.js +117 -0
  151. package/dist/src/notemd.cli.e2e.test.js +136 -0
  152. package/dist/src/notemd.core.test.js +51 -0
  153. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  154. package/dist/src/notemd.providerTemplates.test.js +34 -0
  155. package/dist/src/notemd.server.integration.test.js +143 -35
  156. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  157. package/dist/src/pathbridge.handshake.contract.test.js +16 -2
  158. package/dist/src/pathmode.background.contract.test.js +69 -0
  159. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  160. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  161. package/dist/src/platform/ExportProfile.js +58 -0
  162. package/dist/src/platform/PlatformCapabilities.js +45 -0
  163. package/dist/src/platform/PlatformCapabilities.test.js +30 -0
  164. package/dist/src/platform/RenderMaterializer.js +33 -0
  165. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  166. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  167. package/dist/src/reader_renderer.js +404 -3
  168. package/dist/src/reader_renderer.test.js +87 -0
  169. package/dist/src/release.godot.mirror.contract.test.js +73 -0
  170. package/dist/src/resources/ResourceRegistry.js +223 -0
  171. package/dist/src/resources/ResourceRegistry.test.js +61 -0
  172. package/dist/src/resources/types.js +2 -0
  173. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  174. package/dist/src/routes/data.js +267 -0
  175. package/dist/src/routes/diagnostics.js +51 -0
  176. package/dist/src/routes/index.js +23 -0
  177. package/dist/src/routes/knowledge.js +968 -0
  178. package/dist/src/routes/markdown.js +287 -0
  179. package/dist/src/routes/notemd.js +565 -0
  180. package/dist/src/routes/registry.contract.test.js +130 -0
  181. package/dist/src/routes/render.js +285 -0
  182. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  183. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  184. package/dist/src/routes/settings.js +6 -0
  185. package/dist/src/routes/staticFiles.js +94 -0
  186. package/dist/src/routes/types.js +2 -0
  187. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  188. package/dist/src/server.js +11090 -1549
  189. package/dist/src/server.migration.test.js +193 -21
  190. package/dist/src/server.port.fallback.contract.test.js +63 -0
  191. package/dist/src/session/SessionStateStore.js +81 -0
  192. package/dist/src/session/SessionStateStore.test.js +58 -0
  193. package/dist/src/session/types.js +2 -0
  194. package/dist/src/settings.runtime.contract.test.js +50 -0
  195. package/dist/src/shared/types.contract.test.js +107 -0
  196. package/dist/src/shared/types.js +22 -0
  197. package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
  198. package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
  199. package/dist/src/source_manager.loadflow.test.js +46 -0
  200. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  201. package/dist/src/tauri.frontend.build.contract.test.js +60 -0
  202. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  203. package/dist/src/utils/RuntimePaths.js +4 -13
  204. package/dist/src/utils/platform.js +153 -0
  205. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  206. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  207. package/dist/src/workflows/types.js +2 -0
  208. package/dist/src/workspace/WorkspaceRegistry.js +122 -0
  209. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  210. package/dist/src/workspace/types.js +2 -0
  211. package/package.json +53 -10
@@ -9,6 +9,7 @@ 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;
@@ -51,14 +52,50 @@ class Reader {
51
52
  }
52
53
 
53
54
  // Touch Gestures (Pinch to Zoom)
54
- this.initTouchZoom();
55
+ this.bindTouchZoomSurface();
55
56
  }
56
-
57
- initTouchZoom() {
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;
86
+ }
87
+
88
+ bindTouchZoomSurface() {
89
+ const body = this.ensureReaderStructureBodyOnly();
90
+ if (!body || this._touchZoomBoundBody === body) {
91
+ return;
92
+ }
93
+ this._touchZoomBoundBody = body;
94
+
58
95
  let initialDistance = 0;
59
96
  let initialZoom = 1.0;
60
-
61
- this.body.addEventListener('touchstart', (e) => {
97
+
98
+ body.addEventListener('touchstart', (e) => {
62
99
  if (e.touches.length === 2) {
63
100
  initialDistance = Math.hypot(
64
101
  e.touches[0].pageX - e.touches[1].pageX,
@@ -69,7 +106,7 @@ class Reader {
69
106
  }
70
107
  }, { passive: false });
71
108
 
72
- this.body.addEventListener('touchmove', (e) => {
109
+ body.addEventListener('touchmove', (e) => {
73
110
  if (e.touches.length === 2) {
74
111
  const currentDistance = Math.hypot(
75
112
  e.touches[0].pageX - e.touches[1].pageX,
@@ -89,14 +126,158 @@ class Reader {
89
126
  }
90
127
  }, { passive: false });
91
128
 
92
- this.body.addEventListener('touchend', (e) => {
129
+ body.addEventListener('touchend', (e) => {
93
130
  if (e.touches.length < 2) {
94
131
  initialDistance = 0;
95
132
  }
96
133
  });
97
134
  }
98
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
+
99
279
  async open(node) {
280
+ this.ensureReaderStructure();
100
281
  const nodeLike = this.normalizeNodeInput(node);
101
282
  this.title.innerText = nodeLike.label;
102
283
 
@@ -125,6 +306,13 @@ class Reader {
125
306
  let rendered = false;
126
307
  if (filePath) {
127
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
+ }
128
316
  }
129
317
  if (!rendered) {
130
318
  const rawContent = await this.loadRawContentFallback(nodeLike);
@@ -140,7 +328,7 @@ class Reader {
140
328
 
141
329
  normalizeNodeInput(nodeLike) {
142
330
  if (typeof nodeLike === 'string') {
143
- return {
331
+ nodeLike = {
144
332
  id: nodeLike,
145
333
  label: nodeLike,
146
334
  content: '',
@@ -149,15 +337,41 @@ class Reader {
149
337
  }
150
338
  const safeNode = (nodeLike && typeof nodeLike === 'object') ? nodeLike : {};
151
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 : {};
152
345
  return {
346
+ ...(hydratedNode && typeof hydratedNode === 'object' ? hydratedNode : {}),
153
347
  ...safeNode,
154
348
  id: nodeId,
155
- label: String(safeNode.label || nodeId),
349
+ label: String(safeNode.label || hydratedNode?.label || nodeId),
156
350
  content: typeof safeNode.content === 'string' ? safeNode.content : '',
157
- metadata: (safeNode.metadata && typeof safeNode.metadata === 'object') ? safeNode.metadata : {},
351
+ metadata: {
352
+ ...hydratedMetadata,
353
+ ...safeMetadata,
354
+ },
158
355
  };
159
356
  }
160
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
+
161
375
  extractNodeFilePath(nodeLike) {
162
376
  if (!nodeLike || typeof nodeLike !== 'object') return '';
163
377
  const metadata = (nodeLike.metadata && typeof nodeLike.metadata === 'object') ? nodeLike.metadata : {};
@@ -166,6 +380,16 @@ class Reader {
166
380
  return String(nodeLike.filepath || nodeLike.filePath || '').trim();
167
381
  }
168
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
+
169
393
  getRuntimeBaseUrl() {
170
394
  if (window.NoteConnectionRuntime && typeof window.NoteConnectionRuntime.getBaseUrl === 'function') {
171
395
  return window.NoteConnectionRuntime.getBaseUrl();
@@ -231,6 +455,89 @@ class Reader {
231
455
  });
232
456
  }
233
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
+
234
541
  async tryRenderViaMarkdownProtocol(nodeLike, filePath, resolvedTarget, sessionId) {
235
542
  const config = this.getReadingProtocolConfig();
236
543
  if (String(config.markdownEngine || 'auto').toLowerCase() === 'legacy') {
@@ -312,13 +619,14 @@ class Reader {
312
619
 
313
620
  cleanupProtocolState() {
314
621
  this._protocolRenderState = null;
315
- if (this._protocolScrollHandler) {
622
+ if (this._protocolScrollHandler && this.body) {
316
623
  this.body.removeEventListener('scroll', this._protocolScrollHandler);
317
624
  this._protocolScrollHandler = null;
318
625
  }
319
626
  }
320
627
 
321
628
  async renderProtocolBlocks(blocks, position, currentFilePath, sessionId) {
629
+ this.ensureReaderStructure();
322
630
  if (!Array.isArray(blocks) || blocks.length === 0) {
323
631
  return;
324
632
  }
@@ -731,6 +1039,7 @@ class Reader {
731
1039
  }
732
1040
 
733
1041
  async renderRawMarkdown(rawContent, currentFilePath, sessionId) {
1042
+ this.ensureReaderStructure();
734
1043
  const sanitizedMarkdown = this.autoFixInlineMermaidFenceAfterBlockMath(
735
1044
  String(rawContent || '*No content available.*')
736
1045
  );
@@ -767,6 +1076,8 @@ class Reader {
767
1076
  this._mermaidInitialized = true;
768
1077
  }
769
1078
 
1079
+ this.suppressLeakedMermaidErrorArtifacts({ root: document.body });
1080
+
770
1081
  let mermaidBlocks = Array.from(container.querySelectorAll('pre code.language-mermaid, pre code.lang-mermaid'));
771
1082
  if (mermaidBlocks.length === 0 && container.dataset && container.dataset.codeLanguage === 'mermaid') {
772
1083
  const fallbackBlock = container.querySelector('pre code');
@@ -778,7 +1089,8 @@ class Reader {
778
1089
  for (const block of mermaidBlocks) {
779
1090
  const txt = document.createElement('textarea');
780
1091
  txt.innerHTML = block.innerHTML;
781
- const graphDefinition = txt.value;
1092
+ const candidateDefinitions = this.getMermaidDefinitionCandidates(txt.value);
1093
+ const graphDefinition = candidateDefinitions[0] || '';
782
1094
  const parentPre = block.parentElement;
783
1095
  if (!parentPre || !parentPre.parentNode) continue;
784
1096
 
@@ -794,36 +1106,72 @@ class Reader {
794
1106
  const blockId = section && section.dataset ? String(section.dataset.blockId || '').trim() : '';
795
1107
 
796
1108
  if (hasFrontendMermaid) {
797
- try {
798
- const renderedResult = await mermaid.render(renderId, graphDefinition);
799
- if (renderedResult && typeof renderedResult.svg === 'string' && renderedResult.svg.trim()) {
800
- wrapper.innerHTML = renderedResult.svg;
801
- rendered = true;
802
- renderSource = 'frontend-render';
803
- this._mermaidRenderStats.frontendRender += 1;
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();
804
1138
  }
805
- } catch (error) {
806
- renderErrors.push(error);
807
1139
  }
808
1140
  } else {
809
1141
  renderErrors.push(new Error('Mermaid runtime is unavailable in current webview.'));
810
1142
  }
811
1143
 
812
1144
  if (!rendered && hasFrontendMermaid) {
813
- try {
814
- wrapper.textContent = graphDefinition;
815
- await mermaid.run({ nodes: [wrapper] });
816
- rendered = /<svg[\s>]/i.test(String(wrapper.innerHTML || ''));
817
- if (rendered) {
818
- renderSource = 'frontend-run';
819
- this._mermaidRenderStats.frontendRun += 1;
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
+ });
820
1169
  }
821
- } catch (error) {
822
- renderErrors.push(error);
823
1170
  }
824
1171
  }
825
1172
 
826
1173
  if (!rendered) {
1174
+ wrapper.innerHTML = '';
827
1175
  const backendRendered = await this.renderMermaidViaBackend(graphDefinition);
828
1176
  if (backendRendered && backendRendered.pngBase64) {
829
1177
  const altRenderer = backendRendered.renderer ? ` (${backendRendered.renderer})` : '';
@@ -836,7 +1184,7 @@ class Reader {
836
1184
 
837
1185
  if (!rendered) {
838
1186
  const lastError = renderErrors.length > 0 ? renderErrors[renderErrors.length - 1] : null;
839
- wrapper.innerText = `Mermaid render error: ${String(lastError && lastError.message ? lastError.message : 'Unknown error')}`;
1187
+ this.renderCompactMermaidFailure(wrapper, lastError, graphDefinition);
840
1188
  if (lastError) {
841
1189
  console.error('Mermaid error:', lastError);
842
1190
  }
@@ -844,6 +1192,11 @@ class Reader {
844
1192
  this._mermaidRenderStats.failed += 1;
845
1193
  }
846
1194
 
1195
+ this.suppressLeakedMermaidErrorArtifacts({
1196
+ root: document.body,
1197
+ preserveNode: wrapper,
1198
+ });
1199
+
847
1200
  if (renderSource) {
848
1201
  wrapper.dataset.renderSource = renderSource;
849
1202
  const blockSuffix = blockId ? ` block=${blockId}` : '';
@@ -859,7 +1212,7 @@ class Reader {
859
1212
  }
860
1213
 
861
1214
  async renderMermaidViaBackend(graphDefinition) {
862
- const source = String(graphDefinition || '').trim();
1215
+ const source = this.normalizeMermaidDefinition(graphDefinition || '');
863
1216
  if (!source) {
864
1217
  return null;
865
1218
  }
@@ -892,6 +1245,9 @@ class Reader {
892
1245
  }
893
1246
 
894
1247
  initMermaidZoom() {
1248
+ if (!this.body) {
1249
+ return;
1250
+ }
895
1251
  const mermaidDivs = this.body.querySelectorAll('.mermaid');
896
1252
  mermaidDivs.forEach(div => {
897
1253
  if (div.dataset && div.dataset.readerZoomBound === '1') {
@@ -1066,7 +1422,9 @@ class Reader {
1066
1422
  if (!this._sessionId) this._sessionId = 0;
1067
1423
  this._sessionId += 1;
1068
1424
  this.cleanupProtocolState();
1069
- this.window.style.display = 'none';
1425
+ if (this.window) {
1426
+ this.window.style.display = 'none';
1427
+ }
1070
1428
  }
1071
1429
 
1072
1430
  toggleLock() {
@@ -1075,6 +1433,7 @@ class Reader {
1075
1433
  }
1076
1434
 
1077
1435
  updateLockState() {
1436
+ this.ensureReaderStructure();
1078
1437
  const btn = document.getElementById('btn-reader-lock');
1079
1438
  const zoomBtns = document.querySelectorAll('#btn-reader-zoom-in, #btn-reader-zoom-out');
1080
1439
 
@@ -1100,6 +1459,7 @@ class Reader {
1100
1459
  }
1101
1460
 
1102
1461
  updateZoom() {
1462
+ this.ensureReaderStructure();
1103
1463
  this.body.style.fontSize = `${this.currentZoom}rem`;
1104
1464
  // Scale images if needed, but CSS 'resize' handles explicit image resizing in unlocked mode.
1105
1465
  // Font size scaling handles text content scaling.