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.
Files changed (212) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +258 -64
  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 +258 -64
  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 +3597 -141
  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 +2189 -150
  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 +522 -27
  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/simulationWorker.js +241 -6
  50. package/dist/src/frontend/source_manager.js +190 -21
  51. package/dist/src/frontend/styles.css +2853 -72
  52. package/dist/src/frontend/workbench_state.mjs +101 -0
  53. package/dist/src/frontend/workspace_panes.js +10168 -0
  54. package/dist/src/frontend.locale.contract.test.js +62 -0
  55. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  56. package/dist/src/indexing/IndexLifecycle.js +195 -0
  57. package/dist/src/indexing/IndexLifecycle.test.js +49 -0
  58. package/dist/src/indexing/SegmentBuilder.js +64 -0
  59. package/dist/src/indexing/UnitBuilder.js +48 -0
  60. package/dist/src/indexing/types.js +2 -0
  61. package/dist/src/knowledge.api.contract.test.js +170 -0
  62. package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
  64. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  65. package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
  68. package/dist/src/learning/answerReleaseReview.js +4319 -0
  69. package/dist/src/learning/answerReleaseReview.test.js +2888 -0
  70. package/dist/src/learning/api.js +2 -0
  71. package/dist/src/learning/conversationComposer.js +1480 -0
  72. package/dist/src/learning/conversationComposer.test.js +1817 -0
  73. package/dist/src/learning/domains/ConversationManager.js +53 -0
  74. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  75. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  76. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  77. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  78. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  79. package/dist/src/learning/domains/TutorRouter.js +313 -0
  80. package/dist/src/learning/domains/index.js +33 -0
  81. package/dist/src/learning/domains/types.js +7 -0
  82. package/dist/src/learning/errors.js +29 -0
  83. package/dist/src/learning/evidenceContextAssembler.js +1176 -0
  84. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  85. package/dist/src/learning/graphContextAssembler.js +870 -0
  86. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  87. package/dist/src/learning/index.js +28 -0
  88. package/dist/src/learning/queryBackend.js +1898 -0
  89. package/dist/src/learning/queryBackend.test.js +955 -0
  90. package/dist/src/learning/ragContextPack.js +257 -0
  91. package/dist/src/learning/ragContextPack.test.js +160 -0
  92. package/dist/src/learning/ragPublicText.js +38 -0
  93. package/dist/src/learning/ragSufficiencyJudge.js +161 -0
  94. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  95. package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
  96. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
  97. package/dist/src/learning/requestNormalization.js +198 -0
  98. package/dist/src/learning/runtimeCapability.js +4677 -0
  99. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  100. package/dist/src/learning/store.js +1240 -0
  101. package/dist/src/learning/store.test.js +1126 -0
  102. package/dist/src/learning/tutorAdapter.js +2 -0
  103. package/dist/src/learning/types.js +2 -0
  104. package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
  105. package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
  106. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  107. package/dist/src/license.policy.contract.test.js +66 -0
  108. package/dist/src/memory/MemoryGovernance.js +74 -0
  109. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  110. package/dist/src/memory/types.js +2 -0
  111. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  112. package/dist/src/middleware/auth.js +17 -0
  113. package/dist/src/middleware/body-parser.js +45 -0
  114. package/dist/src/middleware/cors.js +44 -0
  115. package/dist/src/middleware/index.js +21 -0
  116. package/dist/src/middleware/request-trace.js +96 -0
  117. package/dist/src/notemd/AppConfigToml.js +6 -4
  118. package/dist/src/notemd/MermaidProcessor.js +400 -50
  119. package/dist/src/notemd/NotemdService.js +498 -13
  120. package/dist/src/notemd/PromptManager.js +15 -0
  121. package/dist/src/notemd/cli/commands.js +357 -0
  122. package/dist/src/notemd/cli/dispatcher.js +225 -0
  123. package/dist/src/notemd/cli/index.js +169 -0
  124. package/dist/src/notemd/cli/parser.js +68 -0
  125. package/dist/src/notemd/cli/types.js +2 -0
  126. package/dist/src/notemd/constants.js +43 -0
  127. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  128. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  129. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  130. package/dist/src/notemd/diagram/intent.js +95 -0
  131. package/dist/src/notemd/diagram/planner.js +71 -0
  132. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  133. package/dist/src/notemd/diagram/types.js +18 -0
  134. package/dist/src/notemd/index.js +26 -0
  135. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  136. package/dist/src/notemd/operations/cliContracts.js +17 -0
  137. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  138. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  139. package/dist/src/notemd/operations/registry.js +991 -0
  140. package/dist/src/notemd/operations/types.js +2 -0
  141. package/dist/src/notemd/providerDiagnostics.js +220 -0
  142. package/dist/src/notemd/providerProfiles.js +42 -0
  143. package/dist/src/notemd/providerTemplates.js +231 -0
  144. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  145. package/dist/src/notemd/search/SearchManager.js +13 -0
  146. package/dist/src/notemd/search/SearchProvider.js +2 -0
  147. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  148. package/dist/src/notemd.agent.manifest.test.js +85 -0
  149. package/dist/src/notemd.api.contract.test.js +14 -1
  150. package/dist/src/notemd.app_config_toml.test.js +2 -1
  151. package/dist/src/notemd.batch.workflow.test.js +117 -0
  152. package/dist/src/notemd.cli.e2e.test.js +136 -0
  153. package/dist/src/notemd.core.test.js +51 -0
  154. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  155. package/dist/src/notemd.providerTemplates.test.js +34 -0
  156. package/dist/src/notemd.server.integration.test.js +143 -35
  157. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  158. package/dist/src/pathbridge.handshake.contract.test.js +16 -2
  159. package/dist/src/pathmode.background.contract.test.js +69 -0
  160. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  161. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  162. package/dist/src/platform/ExportProfile.js +58 -0
  163. package/dist/src/platform/PlatformCapabilities.js +45 -0
  164. package/dist/src/platform/PlatformCapabilities.test.js +30 -0
  165. package/dist/src/platform/RenderMaterializer.js +33 -0
  166. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  167. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  168. package/dist/src/reader_renderer.js +404 -3
  169. package/dist/src/reader_renderer.test.js +87 -0
  170. package/dist/src/release.godot.mirror.contract.test.js +73 -0
  171. package/dist/src/resources/ResourceRegistry.js +223 -0
  172. package/dist/src/resources/ResourceRegistry.test.js +61 -0
  173. package/dist/src/resources/types.js +2 -0
  174. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  175. package/dist/src/routes/data.js +267 -0
  176. package/dist/src/routes/diagnostics.js +51 -0
  177. package/dist/src/routes/index.js +23 -0
  178. package/dist/src/routes/knowledge.js +968 -0
  179. package/dist/src/routes/markdown.js +287 -0
  180. package/dist/src/routes/notemd.js +565 -0
  181. package/dist/src/routes/registry.contract.test.js +130 -0
  182. package/dist/src/routes/render.js +285 -0
  183. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  184. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  185. package/dist/src/routes/settings.js +6 -0
  186. package/dist/src/routes/staticFiles.js +94 -0
  187. package/dist/src/routes/types.js +2 -0
  188. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  189. package/dist/src/server.js +11090 -1549
  190. package/dist/src/server.migration.test.js +193 -21
  191. package/dist/src/server.port.fallback.contract.test.js +63 -0
  192. package/dist/src/session/SessionStateStore.js +81 -0
  193. package/dist/src/session/SessionStateStore.test.js +58 -0
  194. package/dist/src/session/types.js +2 -0
  195. package/dist/src/settings.runtime.contract.test.js +50 -0
  196. package/dist/src/shared/types.contract.test.js +107 -0
  197. package/dist/src/shared/types.js +22 -0
  198. package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
  199. package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
  200. package/dist/src/source_manager.loadflow.test.js +46 -0
  201. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  202. package/dist/src/tauri.frontend.build.contract.test.js +60 -0
  203. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  204. package/dist/src/utils/RuntimePaths.js +4 -13
  205. package/dist/src/utils/platform.js +153 -0
  206. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  207. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  208. package/dist/src/workflows/types.js +2 -0
  209. package/dist/src/workspace/WorkspaceRegistry.js +122 -0
  210. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  211. package/dist/src/workspace/types.js +2 -0
  212. package/package.json +61 -10
@@ -373,75 +373,88 @@
373
373
  });
374
374
  }
375
375
 
376
- async function hydrateRuntimeFromTauri() {
377
- if (runtimeHydrationPromise) {
378
- return runtimeHydrationPromise;
376
+ function dispatchRuntimeReadyEvent() {
377
+ if (typeof window.dispatchEvent !== 'function' || typeof window.CustomEvent !== 'function') {
378
+ return;
379
379
  }
380
-
381
- runtimeHydrationPromise = (async () => {
382
- const invoke = getTauriInvoke();
383
- if (!invoke) {
384
- return finalizeRuntimeReady();
380
+ window.dispatchEvent(new CustomEvent('noteconnection:runtime-ready', {
381
+ detail: {
382
+ runtime: syncGlobalState(),
383
+ caps: window.__NC_RUNTIME_CAPS || null,
384
+ appConfig: syncGlobalAppState()
385
385
  }
386
+ }));
387
+ }
386
388
 
387
- try {
388
- const caps = await invokeTauriWithTimeout(
389
- () => invoke('get_runtime_capabilities'),
390
- 'get_runtime_capabilities',
391
- TAURI_RUNTIME_HYDRATE_TIMEOUT_MS
392
- );
393
- if (caps && typeof caps === 'object') {
394
- window.__NC_RUNTIME_CAPS = {
395
- ...(window.__NC_RUNTIME_CAPS || {}),
396
- ...caps
397
- };
398
- }
389
+ async function syncRuntimeFromTauri() {
390
+ const invoke = getTauriInvoke();
391
+ if (!invoke) {
392
+ return syncGlobalState();
393
+ }
399
394
 
400
- } catch (error) {
401
- console.warn('[RuntimeBridge] Failed to hydrate runtime capabilities from Tauri. Using runtime bridge defaults.', error);
395
+ try {
396
+ const caps = await invokeTauriWithTimeout(
397
+ () => invoke('get_runtime_capabilities'),
398
+ 'get_runtime_capabilities',
399
+ TAURI_RUNTIME_HYDRATE_TIMEOUT_MS
400
+ );
401
+ if (caps && typeof caps === 'object') {
402
+ window.__NC_RUNTIME_CAPS = {
403
+ ...(window.__NC_RUNTIME_CAPS || {}),
404
+ ...caps
405
+ };
402
406
  }
403
407
 
404
- try {
405
- const runtimeCaps = window.__NC_RUNTIME_CAPS || {};
406
- if (runtimeCaps.supports_sidecar) {
407
- const runtimeConfig = await invokeTauriWithTimeout(
408
- () => invoke('get_sidecar_runtime_config'),
409
- 'get_sidecar_runtime_config',
410
- TAURI_RUNTIME_HYDRATE_TIMEOUT_MS
411
- );
412
- setRuntimeConfig(runtimeConfig);
413
- }
414
- } catch (error) {
415
- console.warn('[RuntimeBridge] Failed to hydrate sidecar runtime config from Tauri. Using runtime bridge defaults.', error);
416
- }
408
+ } catch (error) {
409
+ console.warn('[RuntimeBridge] Failed to hydrate runtime capabilities from Tauri. Using runtime bridge defaults.', error);
410
+ }
417
411
 
418
- try {
419
- const appConfig = await invokeTauriWithTimeout(
420
- () => invoke('get_app_runtime_config'),
421
- 'get_app_runtime_config',
412
+ try {
413
+ const runtimeCaps = window.__NC_RUNTIME_CAPS || {};
414
+ if (runtimeCaps.supports_sidecar) {
415
+ const runtimeConfig = await invokeTauriWithTimeout(
416
+ () => invoke('get_sidecar_runtime_config'),
417
+ 'get_sidecar_runtime_config',
422
418
  TAURI_RUNTIME_HYDRATE_TIMEOUT_MS
423
419
  );
424
- setAppRuntimeConfig(appConfig);
425
- } catch (error) {
426
- console.warn('[RuntimeBridge] Failed to hydrate app runtime config from Tauri. Using runtime defaults.', error);
420
+ setRuntimeConfig(runtimeConfig);
427
421
  }
422
+ } catch (error) {
423
+ console.warn('[RuntimeBridge] Failed to hydrate sidecar runtime config from Tauri. Using runtime bridge defaults.', error);
424
+ }
428
425
 
429
- if (typeof window.dispatchEvent === 'function' && typeof window.CustomEvent === 'function') {
430
- window.dispatchEvent(new CustomEvent('noteconnection:runtime-ready', {
431
- detail: {
432
- runtime: syncGlobalState(),
433
- caps: window.__NC_RUNTIME_CAPS || null,
434
- appConfig: syncGlobalAppState()
435
- }
436
- }));
437
- }
426
+ try {
427
+ const appConfig = await invokeTauriWithTimeout(
428
+ () => invoke('get_app_runtime_config'),
429
+ 'get_app_runtime_config',
430
+ TAURI_RUNTIME_HYDRATE_TIMEOUT_MS
431
+ );
432
+ setAppRuntimeConfig(appConfig);
433
+ } catch (error) {
434
+ console.warn('[RuntimeBridge] Failed to hydrate app runtime config from Tauri. Using runtime defaults.', error);
435
+ }
438
436
 
439
- return finalizeRuntimeReady();
440
- })();
437
+ dispatchRuntimeReadyEvent();
438
+ return syncGlobalState();
439
+ }
440
+
441
+ async function hydrateRuntimeFromTauri() {
442
+ if (!runtimeHydrationPromise) {
443
+ runtimeHydrationPromise = syncRuntimeFromTauri().then(() => {
444
+ finalizeRuntimeReady();
445
+ return syncGlobalState();
446
+ });
447
+ }
441
448
 
442
449
  return runtimeHydrationPromise;
443
450
  }
444
451
 
452
+ async function refreshRuntimeFromTauri() {
453
+ await syncRuntimeFromTauri();
454
+ finalizeRuntimeReady();
455
+ return syncGlobalState();
456
+ }
457
+
445
458
  function whenReady() {
446
459
  if (!runtimeHydrationPromise) {
447
460
  void hydrateRuntimeFromTauri();
@@ -463,7 +476,7 @@
463
476
  parseBridgeEnvelope,
464
477
  sendBridgeMessage,
465
478
  whenReady,
466
- refreshFromTauri: hydrateRuntimeFromTauri,
479
+ refreshFromTauri: refreshRuntimeFromTauri,
467
480
  getBaseUrl: function () {
468
481
  return state.baseUrl;
469
482
  },
@@ -0,0 +1,279 @@
1
+ /**
2
+ * runtime_bridge.mjs - ES Module version.
3
+ * Unified RPC bridge over HTTP + WebSocket, with Tauri IPC fallback.
4
+ */
5
+
6
+ const DEFAULT_BASE_URL = 'http://127.0.0.1:3000';
7
+ const DEFAULT_BRIDGE_WS_URL = 'ws://127.0.0.1:9876';
8
+ const BRIDGE_RPC_VERSION = '2.0';
9
+ const BRIDGE_RPC_METHOD_PREFIX = 'noteconnection.';
10
+ const TAURI_RUNTIME_HYDRATE_TIMEOUT_MS = 2000;
11
+
12
+ function isPlainObject(value) {
13
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
14
+ }
15
+
16
+ function normalizeBridgeMessageType(rawType) {
17
+ return String(rawType || '').trim().replace(/^[/.]+/, '');
18
+ }
19
+
20
+ function toBridgeEnvelope(type, payload, meta) {
21
+ const normalizedType = normalizeBridgeMessageType(type);
22
+ if (!normalizedType) throw new Error('Bridge message type is required.');
23
+ const envelope = {
24
+ type: normalizedType,
25
+ payload: payload === undefined ? null : payload,
26
+ jsonrpc: BRIDGE_RPC_VERSION,
27
+ method: `${BRIDGE_RPC_METHOD_PREFIX}${normalizedType}`,
28
+ params: payload === undefined ? null : payload,
29
+ };
30
+ if (meta && Object.prototype.hasOwnProperty.call(meta, 'id')) {
31
+ envelope.id = meta.id;
32
+ }
33
+ return envelope;
34
+ }
35
+
36
+ function parseBridgeEnvelope(rawMessage) {
37
+ let decoded = rawMessage;
38
+ if (typeof decoded === 'string') decoded = JSON.parse(decoded);
39
+ if (!isPlainObject(decoded)) return null;
40
+ let type = normalizeBridgeMessageType(decoded.type);
41
+ let payload = Object.prototype.hasOwnProperty.call(decoded, 'payload') ? decoded.payload : undefined;
42
+ if (!type && typeof decoded.method === 'string' && decoded.method.startsWith(BRIDGE_RPC_METHOD_PREFIX)) {
43
+ type = normalizeBridgeMessageType(decoded.method.slice(BRIDGE_RPC_METHOD_PREFIX.length));
44
+ if (payload === undefined) payload = decoded.params;
45
+ }
46
+ if (!type) return null;
47
+ if (payload === undefined && Object.prototype.hasOwnProperty.call(decoded, 'params')) {
48
+ payload = decoded.params;
49
+ }
50
+ return { type, payload: payload === undefined ? null : payload, id: Object.prototype.hasOwnProperty.call(decoded, 'id') ? decoded.id : null, jsonrpc: decoded.jsonrpc === BRIDGE_RPC_VERSION, raw: decoded };
51
+ }
52
+
53
+ function sendBridgeMessage(socket, type, payload, meta) {
54
+ if (!socket || socket.readyState !== WebSocket.OPEN) return false;
55
+ socket.send(JSON.stringify(toBridgeEnvelope(type, payload, meta)));
56
+ return true;
57
+ }
58
+
59
+ function normalizeBaseUrl(rawUrl) {
60
+ const value = String(rawUrl || '').trim();
61
+ return value ? value.replace(/\/+$/, '') : DEFAULT_BASE_URL;
62
+ }
63
+
64
+ function normalizeBridgeWsUrl(rawUrl) {
65
+ const value = String(rawUrl || '').trim();
66
+ return value ? value.replace(/\/+$/, '') : DEFAULT_BRIDGE_WS_URL;
67
+ }
68
+
69
+ function normalizeLanguageCode(rawValue) {
70
+ return String(rawValue || '').trim().toLowerCase().startsWith('zh') ? 'zh' : 'en';
71
+ }
72
+
73
+ function normalizeBoolean(value, fallback) {
74
+ return typeof value === 'boolean' ? value : fallback;
75
+ }
76
+
77
+ function getDefaultAppRuntimeConfig() {
78
+ return {
79
+ language: 'en',
80
+ multiWindow: {
81
+ singleWindowMode: true,
82
+ hideTauriWhenPathmodeOpens: true,
83
+ restoreTauriWhenPathmodeExits: true,
84
+ confirmBeforeFullShutdownFromGodot: true,
85
+ syncLanguage: true,
86
+ },
87
+ };
88
+ }
89
+
90
+ const state = {
91
+ baseUrl: normalizeBaseUrl(window.__NC_SIDECAR_RUNTIME?.baseUrl),
92
+ bridgeWsUrl: normalizeBridgeWsUrl(window.__NC_SIDECAR_RUNTIME?.bridgeWsUrl),
93
+ authToken: String(window.__NC_SIDECAR_RUNTIME?.authToken || '').trim(),
94
+ host: String(window.__NC_SIDECAR_RUNTIME?.host || '127.0.0.1'),
95
+ port: Number(window.__NC_SIDECAR_RUNTIME?.port || 3000),
96
+ bridgePort: Number(window.__NC_SIDECAR_RUNTIME?.bridgePort || 9876),
97
+ };
98
+
99
+ const appState = getDefaultAppRuntimeConfig();
100
+ if (window.__NC_APP_CONFIG && typeof window.__NC_APP_CONFIG === 'object') {
101
+ const boot = window.__NC_APP_CONFIG;
102
+ appState.language = normalizeLanguageCode(boot.language || appState.language);
103
+ if (boot.multiWindow && typeof boot.multiWindow === 'object') {
104
+ const mw = boot.multiWindow;
105
+ appState.multiWindow.singleWindowMode = normalizeBoolean(mw.singleWindowMode, appState.multiWindow.singleWindowMode);
106
+ appState.multiWindow.hideTauriWhenPathmodeOpens = normalizeBoolean(mw.hideTauriWhenPathmodeOpens, appState.multiWindow.hideTauriWhenPathmodeOpens);
107
+ appState.multiWindow.restoreTauriWhenPathmodeExits = normalizeBoolean(mw.restoreTauriWhenPathmodeExits, appState.multiWindow.restoreTauriWhenPathmodeExits);
108
+ appState.multiWindow.confirmBeforeFullShutdownFromGodot = normalizeBoolean(mw.confirmBeforeFullShutdownFromGodot, appState.multiWindow.confirmBeforeFullShutdownFromGodot);
109
+ appState.multiWindow.syncLanguage = normalizeBoolean(mw.syncLanguage, appState.multiWindow.syncLanguage);
110
+ }
111
+ }
112
+
113
+ let runtimeReadyResolved = false;
114
+ let runtimeHydrationPromise = null;
115
+ let resolveRuntimeReady;
116
+ const runtimeReadyPromise = new Promise((resolve) => { resolveRuntimeReady = resolve; });
117
+
118
+ function syncGlobalState() {
119
+ window.__NC_SIDECAR_RUNTIME = { host: state.host, port: state.port, bridgePort: state.bridgePort, baseUrl: state.baseUrl, bridgeWsUrl: state.bridgeWsUrl, authToken: state.authToken };
120
+ return window.__NC_SIDECAR_RUNTIME;
121
+ }
122
+
123
+ function syncGlobalAppState() {
124
+ window.__NC_APP_CONFIG = { language: appState.language, multiWindow: { ...appState.multiWindow } };
125
+ return window.__NC_APP_CONFIG;
126
+ }
127
+
128
+ function finalizeRuntimeReady() {
129
+ if (runtimeReadyResolved) return runtimeReadyPromise;
130
+ runtimeReadyResolved = true;
131
+ resolveRuntimeReady(syncGlobalState());
132
+ return runtimeReadyPromise;
133
+ }
134
+
135
+ function setRuntimeConfig(nextConfig) {
136
+ if (!nextConfig || typeof nextConfig !== 'object') return syncGlobalState();
137
+ if (typeof nextConfig.host === 'string' && nextConfig.host.trim()) state.host = nextConfig.host.trim();
138
+ if (typeof nextConfig.port === 'number' && Number.isFinite(nextConfig.port) && nextConfig.port > 0) state.port = nextConfig.port;
139
+ if (typeof nextConfig.bridgePort === 'number' && Number.isFinite(nextConfig.bridgePort) && nextConfig.bridgePort > 0) state.bridgePort = nextConfig.bridgePort;
140
+ state.baseUrl = typeof nextConfig.baseUrl === 'string' ? normalizeBaseUrl(nextConfig.baseUrl) : normalizeBaseUrl(`http://${state.host}:${state.port}`);
141
+ state.bridgeWsUrl = typeof nextConfig.bridgeWsUrl === 'string' ? normalizeBridgeWsUrl(nextConfig.bridgeWsUrl) : normalizeBridgeWsUrl(`ws://${state.host}:${state.bridgePort}`);
142
+ if (typeof nextConfig.authToken === 'string') state.authToken = nextConfig.authToken.trim();
143
+ return syncGlobalState();
144
+ }
145
+
146
+ function getRuntimeConfig() { return syncGlobalState(); }
147
+ function setAppRuntimeConfig(nextConfig) { /* same logic as original */ return syncGlobalAppState(); }
148
+ function getAppRuntimeConfig() { return syncGlobalAppState(); }
149
+
150
+ function buildUrl(resourcePath, query) {
151
+ const normalizedPath = String(resourcePath || '').replace(/^\/+/, '');
152
+ const url = new URL(normalizedPath, `${state.baseUrl}/`);
153
+ if (query && typeof query === 'object') {
154
+ Object.entries(query).forEach(([key, value]) => {
155
+ if (value === undefined || value === null || value === '') return;
156
+ url.searchParams.set(key, String(value));
157
+ });
158
+ }
159
+ return url.toString();
160
+ }
161
+
162
+ function createAuthHeaders(extraHeaders) {
163
+ const headers = new Headers(extraHeaders || {});
164
+ if (state.authToken && !headers.has('X-NoteConnection-Token') && !headers.has('Authorization')) {
165
+ headers.set('X-NoteConnection-Token', state.authToken);
166
+ }
167
+ return headers;
168
+ }
169
+
170
+ function buildFetchOptions(init) {
171
+ const nextInit = { ...(init || {}) };
172
+ nextInit.headers = createAuthHeaders(nextInit.headers);
173
+ return nextInit;
174
+ }
175
+
176
+ function getBridgeWsUrl(clientTag) {
177
+ const rawWsUrl = String(state.bridgeWsUrl || '').trim();
178
+ if (!rawWsUrl) return '';
179
+ try {
180
+ const url = new URL(rawWsUrl);
181
+ const tag = String(clientTag || '').trim();
182
+ if (tag) url.searchParams.set('client', tag);
183
+ if (state.authToken) url.searchParams.set('token', state.authToken);
184
+ return url.toString();
185
+ } catch (_) { return rawWsUrl; }
186
+ }
187
+
188
+ function openBridgeSocket(clientTag, protocols) {
189
+ const wsUrl = getBridgeWsUrl(clientTag);
190
+ if (!wsUrl) return null;
191
+ return protocols ? new WebSocket(wsUrl, protocols) : new WebSocket(wsUrl);
192
+ }
193
+
194
+ function getTauriInvoke() {
195
+ return window.__TAURI__?.core?.invoke && typeof window.__TAURI__.core.invoke === 'function'
196
+ ? window.__TAURI__.core.invoke : null;
197
+ }
198
+
199
+ function invokeTauriWithTimeout(invokeCall, commandLabel, timeoutMs) {
200
+ return new Promise((resolve, reject) => {
201
+ let settled = false;
202
+ const timer = setTimeout(() => { if (!settled) { settled = true; reject(new Error(`Tauri invoke timed out (${commandLabel}).`)); } }, timeoutMs);
203
+ Promise.resolve(invokeCall()).then((result) => { if (!settled) { settled = true; clearTimeout(timer); resolve(result); } }).catch((error) => { if (!settled) { settled = true; clearTimeout(timer); reject(error); } });
204
+ });
205
+ }
206
+
207
+ function dispatchRuntimeReadyEvent() {
208
+ if (typeof window.dispatchEvent === 'function') {
209
+ window.dispatchEvent(new CustomEvent('noteconnection:runtime-ready', { detail: { runtime: syncGlobalState(), caps: window.__NC_RUNTIME_CAPS || null, appConfig: syncGlobalAppState() } }));
210
+ }
211
+ }
212
+
213
+ async function syncRuntimeFromTauri() {
214
+ const invoke = getTauriInvoke();
215
+ if (!invoke) return syncGlobalState();
216
+ try {
217
+ const caps = await invokeTauriWithTimeout(() => invoke('get_runtime_capabilities'), 'get_runtime_capabilities', TAURI_RUNTIME_HYDRATE_TIMEOUT_MS);
218
+ if (caps && typeof caps === 'object') window.__NC_RUNTIME_CAPS = { ...(window.__NC_RUNTIME_CAPS || {}), ...caps };
219
+ } catch (error) { console.warn('[RuntimeBridge] Tauri caps hydrate failed.', error); }
220
+ try {
221
+ if ((window.__NC_RUNTIME_CAPS || {}).supports_sidecar) {
222
+ const config = await invokeTauriWithTimeout(() => invoke('get_sidecar_runtime_config'), 'get_sidecar_runtime_config', TAURI_RUNTIME_HYDRATE_TIMEOUT_MS);
223
+ setRuntimeConfig(config);
224
+ }
225
+ } catch (error) { console.warn('[RuntimeBridge] Sidecar config hydrate failed.', error); }
226
+ try {
227
+ const appConfig = await invokeTauriWithTimeout(() => invoke('get_app_runtime_config'), 'get_app_runtime_config', TAURI_RUNTIME_HYDRATE_TIMEOUT_MS);
228
+ setAppRuntimeConfig(appConfig);
229
+ } catch (error) { console.warn('[RuntimeBridge] App config hydrate failed.', error); }
230
+ dispatchRuntimeReadyEvent();
231
+ return syncGlobalState();
232
+ }
233
+
234
+ async function hydrateRuntimeFromTauri() {
235
+ if (!runtimeHydrationPromise) {
236
+ runtimeHydrationPromise = syncRuntimeFromTauri().then(() => {
237
+ finalizeRuntimeReady();
238
+ return syncGlobalState();
239
+ });
240
+ }
241
+ return runtimeHydrationPromise;
242
+ }
243
+
244
+ async function refreshRuntimeFromTauri() {
245
+ await syncRuntimeFromTauri();
246
+ finalizeRuntimeReady();
247
+ return syncGlobalState();
248
+ }
249
+
250
+ function whenReady() {
251
+ if (!runtimeHydrationPromise) void hydrateRuntimeFromTauri();
252
+ return runtimeReadyPromise;
253
+ }
254
+
255
+ // Build the runtime object
256
+ const NoteConnectionRuntime = {
257
+ setRuntimeConfig, getRuntimeConfig,
258
+ setAppRuntimeConfig, getAppRuntimeConfig,
259
+ buildUrl, buildFetchOptions, createAuthHeaders,
260
+ getBridgeWsUrl, openBridgeSocket,
261
+ toBridgeEnvelope, parseBridgeEnvelope, sendBridgeMessage,
262
+ whenReady, refreshFromTauri: refreshRuntimeFromTauri,
263
+ getBaseUrl: () => state.baseUrl,
264
+ getAuthToken: () => state.authToken,
265
+ };
266
+
267
+ // Backward compatibility
268
+ window.NoteConnectionRuntime = NoteConnectionRuntime;
269
+ syncGlobalState();
270
+ syncGlobalAppState();
271
+
272
+ if (document.readyState === 'loading') {
273
+ document.addEventListener('DOMContentLoaded', () => { void hydrateRuntimeFromTauri(); }, { once: true });
274
+ } else {
275
+ void hydrateRuntimeFromTauri();
276
+ }
277
+
278
+ export { NoteConnectionRuntime, setRuntimeConfig, getRuntimeConfig, buildUrl, buildFetchOptions, createAuthHeaders, getBridgeWsUrl, openBridgeSocket, toBridgeEnvelope, parseBridgeEnvelope, sendBridgeMessage, whenReady, hydrateRuntimeFromTauri };
279
+ export default NoteConnectionRuntime;
@@ -9,6 +9,7 @@ const SETTINGS_STORAGE_KEY = "nc_settings";
9
9
  const FRONTEND_SETTINGS_ENDPOINT = "/api/frontend/settings";
10
10
  const RUNTIME_READY_TIMEOUT_MS = 2000;
11
11
  const REMOTE_SYNC_DEBOUNCE_MS = 200;
12
+ const RUNTIME_REQUEST_RETRY_DELAY_MS = 180;
12
13
 
13
14
  const defaultSettings = {
14
15
  physics: {
@@ -57,6 +58,86 @@ function clampNumber(value, minValue, maxValue, fallback) {
57
58
  return Math.max(minValue, Math.min(maxValue, numeric));
58
59
  }
59
60
 
61
+ function sleep(ms) {
62
+ return new Promise((resolve) => setTimeout(resolve, ms));
63
+ }
64
+
65
+ function hasExplicitRuntimeBackendBootstrap() {
66
+ if (typeof window === "undefined" || !window.__NC_SIDECAR_RUNTIME) {
67
+ return false;
68
+ }
69
+ const currentOrigin = String((window.location && window.location.origin) || "").trim().replace(/\/+$/, "");
70
+ const runtimeBaseUrl = String(window.__NC_SIDECAR_RUNTIME.baseUrl || "").trim().replace(/\/+$/, "");
71
+ return Boolean(currentOrigin && runtimeBaseUrl && currentOrigin === runtimeBaseUrl);
72
+ }
73
+
74
+ function isDetachedTauriCliPreviewPage() {
75
+ if (typeof window === "undefined" || window.__TAURI__) {
76
+ return false;
77
+ }
78
+ return Array.from(document.scripts || []).some((script) =>
79
+ /__tauri_cli/.test(String(script && (script.text || script.textContent) || ""))
80
+ );
81
+ }
82
+
83
+ function getRuntimeBaseOrigin() {
84
+ if (
85
+ !window.NoteConnectionRuntime ||
86
+ typeof window.NoteConnectionRuntime.getBaseUrl !== "function"
87
+ ) {
88
+ return "";
89
+ }
90
+ const baseUrl = String(window.NoteConnectionRuntime.getBaseUrl() || "").trim();
91
+ if (!baseUrl) {
92
+ return "";
93
+ }
94
+ try {
95
+ return new URL(baseUrl).origin;
96
+ } catch (_error) {
97
+ return "";
98
+ }
99
+ }
100
+
101
+ function isCapacitorNativeRuntime() {
102
+ if (typeof window === "undefined" || window.__TAURI__ || !window.Capacitor) {
103
+ return false;
104
+ }
105
+ try {
106
+ if (typeof window.Capacitor.getPlatform === "function") {
107
+ const platform = window.Capacitor.getPlatform();
108
+ if (platform && platform !== "web") {
109
+ return true;
110
+ }
111
+ }
112
+ if (typeof window.Capacitor.isNativePlatform === "function") {
113
+ return Boolean(window.Capacitor.isNativePlatform());
114
+ }
115
+ } catch (_error) {
116
+ return false;
117
+ }
118
+ return false;
119
+ }
120
+
121
+ function shouldEnableRuntimeSync() {
122
+ if (typeof window === "undefined") {
123
+ return false;
124
+ }
125
+ if (window.__TAURI__) {
126
+ return true;
127
+ }
128
+ if (isDetachedTauriCliPreviewPage() && window.location.origin !== getRuntimeBaseOrigin()) {
129
+ return false;
130
+ }
131
+ if (isCapacitorNativeRuntime()) {
132
+ return false;
133
+ }
134
+ if (hasExplicitRuntimeBackendBootstrap()) {
135
+ return true;
136
+ }
137
+ const origin = String((window.location && window.location.origin) || "").trim().toLowerCase();
138
+ return /^https?:\/\/(?:127\.0\.0\.1|localhost):3000$/.test(origin);
139
+ }
140
+
60
141
  function normalizeDegreeMode(value) {
61
142
  return String(value || "").trim().toLowerCase() === "total" ? "total" : "visible";
62
143
  }
@@ -187,7 +268,12 @@ class SettingsManager {
187
268
  this.remoteSyncDeferred = false;
188
269
  this.isHydrationComplete = false;
189
270
  this.preHydrationPatches = [];
271
+ this.runtimeSyncEnabled = shouldEnableRuntimeSync();
190
272
  this.settings = this.loadLocalSnapshot();
273
+ if (!this.runtimeSyncEnabled) {
274
+ this.isHydrationComplete = true;
275
+ return;
276
+ }
191
277
  this.hydrateFromRuntime().catch((error) => {
192
278
  console.warn("[Settings] Runtime settings hydration failed; using local snapshot.", error);
193
279
  }).finally(() => {
@@ -290,20 +376,49 @@ class SettingsManager {
290
376
  });
291
377
  }
292
378
 
293
- const response = await fetch(
294
- this.buildUrl(FRONTEND_SETTINGS_ENDPOINT),
295
- this.buildFetchOptions(requestInit)
296
- );
297
- if (!response.ok) {
298
- const message = await response.text().catch(() => "");
299
- throw new Error(message || `Frontend settings request failed (${method} ${response.status}).`);
300
- }
379
+ const maxAttempts = (window.__TAURI__ && method === "GET") ? 12 : ((window.__TAURI__ && method === "POST") ? 8 : 1);
380
+ let lastError = null;
381
+
382
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
383
+ try {
384
+ const response = await fetch(
385
+ this.buildUrl(FRONTEND_SETTINGS_ENDPOINT),
386
+ this.buildFetchOptions(requestInit)
387
+ );
388
+ if (!response.ok) {
389
+ const message = await response.text().catch(() => "");
390
+ const error = new Error(message || `Frontend settings request failed (${method} ${response.status}).`);
391
+ error.status = response.status;
392
+ throw error;
393
+ }
301
394
 
302
- const payload = await response.json().catch(() => null);
303
- if (!payload || payload.success !== true || !isObjectLike(payload.settings)) {
304
- throw new Error("Invalid frontend settings API response.");
395
+ const payload = await response.json().catch(() => null);
396
+ if (!payload || payload.success !== true || !isObjectLike(payload.settings)) {
397
+ throw new Error("Invalid frontend settings API response.");
398
+ }
399
+ return normalizeSettings(payload.settings);
400
+ } catch (error) {
401
+ lastError = error;
402
+ const status = Number(error && error.status);
403
+ const shouldRetry = Boolean(
404
+ window.__TAURI__
405
+ && attempt < maxAttempts
406
+ && (
407
+ !Number.isFinite(status)
408
+ || status === 404
409
+ || status === 502
410
+ || status === 503
411
+ || status === 504
412
+ )
413
+ );
414
+ if (!shouldRetry) {
415
+ throw error;
416
+ }
417
+ await sleep(Math.min(1000, RUNTIME_REQUEST_RETRY_DELAY_MS * attempt));
418
+ }
305
419
  }
306
- return normalizeSettings(payload.settings);
420
+
421
+ throw lastError || new Error("Frontend settings request failed.");
307
422
  }
308
423
 
309
424
  async hydrateFromRuntime() {
@@ -359,6 +474,9 @@ class SettingsManager {
359
474
  }
360
475
 
361
476
  scheduleRuntimePersist() {
477
+ if (!this.runtimeSyncEnabled) {
478
+ return;
479
+ }
362
480
  if (!this.isHydrationComplete) {
363
481
  this.remoteSyncDeferred = true;
364
482
  return;