Skip to content

Commit 8f945db

Browse files
committed
fix(reader,pathmode): harden mermaid render chain and single-window switch
1 parent fded7eb commit 8f945db

5 files changed

Lines changed: 237 additions & 35 deletions

File tree

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "noteconnection",
3-
"version": "1.6.8",
3+
"version": "1.6.9",
44
"description": "Hierarchical Knowledge Graph Visualization System",
55
"main": "dist/src/server.js",
66
"bin": {
@@ -55,6 +55,8 @@
5555
"verify:fixrisk:issues": "node scripts/verify-fixrisk-issues.js",
5656
"verify:fixrisk:issues:strict": "node scripts/verify-fixrisk-issues.js --strict-pending",
5757
"verify:fixrisk:issues:strict:evidence": "node scripts/verify-fixrisk-issues.js --strict-pending --require-evidence-root",
58+
"verify:markdown:mermaid:fence": "node scripts/verify-markdown-mermaid-fence.js",
59+
"fix:markdown:mermaid:fence": "node scripts/verify-markdown-mermaid-fence.js --fix",
5860
"verify:pathbridge:strict": "node scripts/verify-pathbridge-strict-schema.js",
5961
"generate:sbom": "node scripts/generate-sbom.js",
6062
"generate:sbom:attestation": "node scripts/generate-sbom-attestation.js",

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"productName": "NoteConnection",
4-
"version": "1.6.8",
4+
"version": "1.6.9",
55
"identifier": "com.jacobinwwey.noteconnection",
66
"build": {
77
"beforeDevCommand": "",

src/frontend/app.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3808,21 +3808,25 @@ if (btnPathMode) {
38083808
// 单窗口切换:隐藏 Tauri,显示 Godot。
38093809
if (window.__TAURI__ && window.__TAURI__.core && typeof window.__TAURI__.core.invoke === 'function') {
38103810
try {
3811-
// 1. Hide the Tauri window via Rust IPC.
3812-
await window.__TAURI__.core.invoke('toggle_pathmode_window', { showGodot: true });
3813-
// 2. Send setWindowVisible to Godot via PathBridge WebSocket.
3811+
let bridgeVisibilityReady = true;
38143812
if (
38153813
multiWindowOptions.singleWindowMode &&
38163814
window.pathApp &&
3817-
window.pathApp.ws &&
3818-
window.pathApp.ws.readyState === WebSocket.OPEN
3815+
typeof window.pathApp.requestBridgeWindowVisibility === 'function'
38193816
) {
3820-
window.pathApp.ws.send(JSON.stringify({
3821-
type: 'setWindowVisible',
3822-
payload: { visible: true }
3823-
}));
3817+
bridgeVisibilityReady = await window.pathApp.requestBridgeWindowVisibility(true, {
3818+
waitMs: 2500,
3819+
reason: 'enter-pathmode'
3820+
});
3821+
}
3822+
3823+
if (multiWindowOptions.singleWindowMode && !bridgeVisibilityReady) {
3824+
console.warn('[Path Mode] Bridge window visibility message was not delivered in time; skip hiding Tauri to avoid black-screen switch.');
3825+
} else {
3826+
// Hide the Tauri window via Rust IPC only after window-visibility intent is delivered.
3827+
await window.__TAURI__.core.invoke('toggle_pathmode_window', { showGodot: true });
3828+
console.log('[Path Mode] Single-window toggle: Tauri hidden, Godot shown.');
38243829
}
3825-
console.log('[Path Mode] Single-window toggle: Tauri hidden, Godot shown.');
38263830
} catch (err) {
38273831
console.warn('[Path Mode] toggle_pathmode_window failed:', err);
38283832
}

src/frontend/path_app.js

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ window.pathApp = {
3636
},
3737
bridgeLanguageListenerRegistered: false,
3838
bridgeMermaidRenderQueue: Promise.resolve(),
39+
pendingWindowVisibility: null,
3940
semanticA11yLastSummaryKey: '',
4041
semanticA11yLastAnnouncementAt: 0,
4142

@@ -101,6 +102,7 @@ window.pathApp = {
101102
this._sendBridgeMessage('identify', this._getBridgeIdentifyPayload('frontend'));
102103
this._ensureLanguageSyncListener();
103104
this.syncLanguageWithBridge();
105+
this._flushPendingWindowVisibility('socket-open');
104106
};
105107
this.ws.onmessage = (e) => {
106108
try {
@@ -254,6 +256,7 @@ window.pathApp = {
254256
this._sendBridgeMessage('identify', this._getBridgeIdentifyPayload('frontend'));
255257
this._ensureLanguageSyncListener();
256258
this.syncLanguageWithBridge();
259+
this._flushPendingWindowVisibility('socket-reuse');
257260
}
258261
},
259262

@@ -497,6 +500,68 @@ window.pathApp = {
497500
return true;
498501
},
499502

503+
_waitForBridgeSocketOpen: async function(timeoutMs = 2500) {
504+
const budget = Math.max(0, Number(timeoutMs) || 0);
505+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
506+
return true;
507+
}
508+
if (budget === 0) {
509+
return false;
510+
}
511+
this._connectBridgeSocket();
512+
const startedAt = Date.now();
513+
while (Date.now() - startedAt < budget) {
514+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
515+
return true;
516+
}
517+
await new Promise((resolve) => setTimeout(resolve, 80));
518+
this._connectBridgeSocket();
519+
}
520+
return this.ws && this.ws.readyState === WebSocket.OPEN;
521+
},
522+
523+
_flushPendingWindowVisibility: function(reason = 'flush') {
524+
if (typeof this.pendingWindowVisibility !== 'boolean') {
525+
return false;
526+
}
527+
const visible = this.pendingWindowVisibility;
528+
const sent = this._sendBridgeMessage('setWindowVisible', { visible });
529+
if (sent) {
530+
console.log(`[PathApp] Flushed pending setWindowVisible(${visible}) via ${reason}.`);
531+
this.pendingWindowVisibility = null;
532+
return true;
533+
}
534+
return false;
535+
},
536+
537+
requestBridgeWindowVisibility: async function(visible, options = {}) {
538+
const targetVisible = visible === true;
539+
const waitMs = Math.max(0, Number(options.waitMs) || 0);
540+
const reason = String(options.reason || 'manual');
541+
542+
const sentImmediately = this._sendBridgeMessage('setWindowVisible', { visible: targetVisible });
543+
if (sentImmediately) {
544+
console.log(`[PathApp] Sent setWindowVisible(${targetVisible}) immediately. reason=${reason}`);
545+
this.pendingWindowVisibility = null;
546+
return true;
547+
}
548+
549+
this.pendingWindowVisibility = targetVisible;
550+
this._connectBridgeSocket();
551+
if (waitMs === 0) {
552+
console.warn(`[PathApp] Deferred setWindowVisible(${targetVisible}) until socket opens. reason=${reason}`);
553+
return false;
554+
}
555+
556+
const ready = await this._waitForBridgeSocketOpen(waitMs);
557+
if (!ready) {
558+
console.warn(`[PathApp] Bridge socket not ready for setWindowVisible(${targetVisible}). reason=${reason}`);
559+
return false;
560+
}
561+
const flushed = this._flushPendingWindowVisibility(`wait-${reason}`);
562+
return flushed;
563+
},
564+
500565
_getBridgeMermaidConfig: function(theme = 'dark') {
501566
return {
502567
startOnLoad: false,
@@ -1656,15 +1721,11 @@ window.pathApp = {
16561721
// 单窗口切换:隐藏 Godot,显示 Tauri。
16571722
if (window.__TAURI__ && window.__TAURI__.core && typeof window.__TAURI__.core.invoke === 'function') {
16581723
// 1. Hide Godot window via PathBridge WebSocket.
1659-
if (
1660-
multiWindowOptions.singleWindowMode &&
1661-
this.ws &&
1662-
this.ws.readyState === WebSocket.OPEN
1663-
) {
1664-
this.ws.send(JSON.stringify({
1665-
type: 'setWindowVisible',
1666-
payload: { visible: false }
1667-
}));
1724+
if (multiWindowOptions.singleWindowMode) {
1725+
void this.requestBridgeWindowVisibility(false, {
1726+
waitMs: 1200,
1727+
reason: 'exit-pathmode'
1728+
});
16681729
}
16691730
// 2. Restore Tauri window via Rust IPC.
16701731
if (multiWindowOptions.restoreTauriWhenPathmodeExits) {

0 commit comments

Comments
 (0)