noteconnection 1.6.0 → 1.6.2
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/README.md +70 -4
- package/dist/src/core/PathBridge.js +11 -0
- package/dist/src/frontend/README.md +70 -4
- package/dist/src/frontend/app.js +72 -11
- package/dist/src/frontend/locales/en.json +9 -6
- package/dist/src/frontend/locales/zh.json +9 -6
- package/dist/src/frontend/path_app.js +173 -48
- package/dist/src/frontend/runtime_bridge.js +126 -1
- package/dist/src/sbom.attestation.policy.contract.test.js +41 -0
- package/dist/src/server.migration.test.js +6 -0
- package/package.json +38 -35
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 2026-03-
|
|
1
|
+
# 2026-03-24 v1.6.0
|
|
2
2
|
|
|
3
3
|
# NoteConnection Knowledge Graph
|
|
4
4
|
|
|
@@ -279,9 +279,26 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
279
279
|
Managing your knowledge base source is now easier than ever.
|
|
280
280
|
|
|
281
281
|
- **First Run Setup**: On first launch, you will be prompted to select your `Knowledge_Base` folder.
|
|
282
|
-
- **Persistent Config**: Your
|
|
282
|
+
- **Persistent Config (`app_config.toml`)**: Your KB path, language, and multi-window preferences are saved in `%LOCALAPPDATA%/NoteConnection/app_config.toml` (Windows default) and remembered across restarts.
|
|
283
|
+
- **Legacy Auto-Migration**: If a legacy `kb_config.json` exists in the same config directory, NoteConnection automatically migrates it to `app_config.toml`.
|
|
283
284
|
- **Change Anytime**: Use the **File > Change Knowledge Base...** menu option to switch folders instantly.
|
|
284
285
|
- **Reset**: Use **File > Reset to Default** to return to the bundled demo notes.
|
|
286
|
+
- **Config Path Overrides**: Set `NOTE_CONNECTION_CONFIG_PATH` (full file path) or `NOTE_CONNECTION_CONFIG_DIR` (directory) to customize where `app_config.toml` is stored.
|
|
287
|
+
- **Window Behavior Tuning**: Edit `[multi_window]` in `app_config.toml` (`single_window_mode`, `hide_tauri_when_pathmode_opens`, `restore_tauri_when_pathmode_exits`, `confirm_before_full_shutdown_from_godot`, `sync_language`).
|
|
288
|
+
- **Detailed Config Guide**: See [`docs/en/app_config.toml_guide.md`](docs/en/app_config.toml_guide.md) and template [`docs/examples/app_config.template.toml`](docs/examples/app_config.template.toml).
|
|
289
|
+
|
|
290
|
+
```toml
|
|
291
|
+
# Minimal recommended app_config.toml
|
|
292
|
+
knowledge_base_path = "E:/Knowledge_project/NoteConnection_app/Knowledge_Base"
|
|
293
|
+
user_language = "en"
|
|
294
|
+
|
|
295
|
+
[multi_window]
|
|
296
|
+
single_window_mode = true
|
|
297
|
+
hide_tauri_when_pathmode_opens = true
|
|
298
|
+
restore_tauri_when_pathmode_exits = true
|
|
299
|
+
confirm_before_full_shutdown_from_godot = true
|
|
300
|
+
sync_language = true
|
|
301
|
+
```
|
|
285
302
|
|
|
286
303
|
## 🏗️ Build & Deployment
|
|
287
304
|
|
|
@@ -294,6 +311,16 @@ For developers building from source, NoteConnection offers two build modes:
|
|
|
294
311
|
- **GPU Dev Start (`npm run tauri:dev:mini:gpu`)**: Recommended GPU-enabled Tauri development command.
|
|
295
312
|
- **Do not use** `npm run tauri:dev:mini --gpu` because npm treats `--gpu` as config and prints warnings.
|
|
296
313
|
|
|
314
|
+
## 📚 Documentation Architecture (Diataxis + MkDocs)
|
|
315
|
+
|
|
316
|
+
- Canonical long-form docs remain under `docs/en/*` and `docs/zh/*`.
|
|
317
|
+
- Diataxis navigation pages are maintained under `docs/diataxis/<lang>/*`.
|
|
318
|
+
- Mapping governance is versioned in `docs/diataxis-map.json`.
|
|
319
|
+
- Run mapping validation: `npm run docs:diataxis:check`.
|
|
320
|
+
- Run local docs site preview: `npm run docs:site:serve`.
|
|
321
|
+
- Build static docs site: `npm run docs:site:build`.
|
|
322
|
+
- CI policy gate for docs mapping and site build: `.github/workflows/docs-diataxis-site.yml`.
|
|
323
|
+
|
|
297
324
|
## 🛠️ Hardware & Driver Requirements (AMDGPU)
|
|
298
325
|
|
|
299
326
|
For optimal performance with "GPU Optimised Rendering", especially on AMD RDNA cards (like RX 7900XT):
|
|
@@ -309,6 +336,11 @@ For optimal performance with "GPU Optimised Rendering", especially on AMD RDNA c
|
|
|
309
336
|
|
|
310
337
|
### v1.6.0 - Unified Runtime, NoteMD Integration & Release Hardening (2026-03-23)
|
|
311
338
|
|
|
339
|
+
- **Tag Compare Snapshot (`v1.3.0..v1.6.0`)**:
|
|
340
|
+
- `107` commits, `301` files changed, `+125,957 / -10,083` churn.
|
|
341
|
+
- File-level status: `241` added, `56` modified, `3` deleted, `1` renamed.
|
|
342
|
+
- Largest engineering footprint: `src/`, `docs/`, `scripts/`, `path_mode/`, `src-tauri/`.
|
|
343
|
+
|
|
312
344
|
- **Single-Window Runtime Orchestration**:
|
|
313
345
|
- Implemented Tauri <-> Godot visibility handoff so only one primary window is shown at a time.
|
|
314
346
|
- Added Godot close-confirm flow ("Return to main interface" vs "Close all windows") to prevent accidental full shutdown.
|
|
@@ -323,6 +355,7 @@ For optimal performance with "GPU Optimised Rendering", especially on AMD RDNA c
|
|
|
323
355
|
- **Reliability & Security Gates**:
|
|
324
356
|
- Expanded CI/workflow coverage for FixRisk operational readiness, mobile e2e contracts, wasm parity, SBOM, attestation, and signature/privacy checks.
|
|
325
357
|
- Added broad contract-level regression coverage across mobile/runtime/pathbridge/storage layers.
|
|
358
|
+
- Included pre-release CI compatibility fixes for runtime bridge invoke-contract assertions and unsigned SBOM transparency policy handling.
|
|
326
359
|
- **Build Performance & Developer Experience**:
|
|
327
360
|
- Added low-memory Tauri build wrappers and release-profile safeguards for constrained environments.
|
|
328
361
|
- Added sidecar readiness preflight to skip redundant rebuilds during dev startup, reducing warm `tauri:dev:mini:gpu` startup latency.
|
|
@@ -854,7 +887,7 @@ For optimal performance with "GPU Optimised Rendering", especially on AMD RDNA c
|
|
|
854
887
|
## 中文文档
|
|
855
888
|
|
|
856
889
|
|
|
857
|
-
# 2026-03-
|
|
890
|
+
# 2026-03-24 v1.6.0
|
|
858
891
|
# NoteConnection: 层级知识图谱可视化系统
|
|
859
892
|
|
|
860
893
|
<img width="606" height="309" alt="banner" src="https://github.com/user-attachments/assets/92e90de5-2b1a-4398-8e8b-6e142c92b6a2" />
|
|
@@ -1120,9 +1153,26 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
1120
1153
|
管理知识库源现在变得更加简单。
|
|
1121
1154
|
|
|
1122
1155
|
- **首次运行设置**: 首次启动时,系统会提示您选择 `Knowledge_Base` 文件夹。
|
|
1123
|
-
-
|
|
1156
|
+
- **持久化配置 (`app_config.toml`)**: KB 路径、语言及多窗口偏好默认保存到 `%LOCALAPPDATA%/NoteConnection/app_config.toml`(Windows),重启后自动恢复。
|
|
1157
|
+
- **旧配置自动迁移**: 若同目录存在旧版 `kb_config.json`,启动时会自动迁移到 `app_config.toml`。
|
|
1124
1158
|
- **随时更改**: 使用 **文件 > 更改知识库...** 菜单选项即时切换文件夹。
|
|
1125
1159
|
- **重置**: 使用 **文件 > 重置为默认** 返回由捆绑的演示笔记。
|
|
1160
|
+
- **配置路径覆盖**: 可通过 `NOTE_CONNECTION_CONFIG_PATH`(完整文件路径)或 `NOTE_CONNECTION_CONFIG_DIR`(目录)自定义 `app_config.toml` 位置。
|
|
1161
|
+
- **窗口行为可调**: 在 `app_config.toml` 的 `[multi_window]` 段调整 `single_window_mode`、`hide_tauri_when_pathmode_opens`、`restore_tauri_when_pathmode_exits`、`confirm_before_full_shutdown_from_godot`、`sync_language`。
|
|
1162
|
+
- **详细配置说明**: 参见 [`docs/zh/app_config.toml_guide.md`](docs/zh/app_config.toml_guide.md) 与模板 [`docs/examples/app_config.template.toml`](docs/examples/app_config.template.toml)。
|
|
1163
|
+
|
|
1164
|
+
```toml
|
|
1165
|
+
# 推荐最小 app_config.toml
|
|
1166
|
+
knowledge_base_path = "E:/Knowledge_project/NoteConnection_app/Knowledge_Base"
|
|
1167
|
+
user_language = "en"
|
|
1168
|
+
|
|
1169
|
+
[multi_window]
|
|
1170
|
+
single_window_mode = true
|
|
1171
|
+
hide_tauri_when_pathmode_opens = true
|
|
1172
|
+
restore_tauri_when_pathmode_exits = true
|
|
1173
|
+
confirm_before_full_shutdown_from_godot = true
|
|
1174
|
+
sync_language = true
|
|
1175
|
+
```
|
|
1126
1176
|
|
|
1127
1177
|
## 🏗️ 构建与部署 (Build & Deployment)
|
|
1128
1178
|
|
|
@@ -1135,6 +1185,16 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
1135
1185
|
- **GPU 开发启动(推荐)** (`npm run tauri:dev:mini:gpu`)。
|
|
1136
1186
|
- **不要使用** `npm run tauri:dev:mini --gpu`,该写法会被 npm 当作配置参数并触发告警。
|
|
1137
1187
|
|
|
1188
|
+
## 📚 文档架构(Diataxis + MkDocs)
|
|
1189
|
+
|
|
1190
|
+
- 权威长文档仍保持在 `docs/en/*` 与 `docs/zh/*`。
|
|
1191
|
+
- Diataxis 导航页维护在 `docs/diataxis/<lang>/*`。
|
|
1192
|
+
- 映射治理文件为 `docs/diataxis-map.json`。
|
|
1193
|
+
- 映射一致性校验:`npm run docs:diataxis:check`。
|
|
1194
|
+
- 本地预览文档站点:`npm run docs:site:serve`。
|
|
1195
|
+
- 构建静态文档站点:`npm run docs:site:build`。
|
|
1196
|
+
- CI 文档治理工作流:`.github/workflows/docs-diataxis-site.yml`。
|
|
1197
|
+
|
|
1138
1198
|
---
|
|
1139
1199
|
|
|
1140
1200
|
<a id="changelog-zh"></a>
|
|
@@ -1143,6 +1203,11 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
1143
1203
|
|
|
1144
1204
|
### v1.6.0 - 单窗口运行时、NoteMD 集成与发布加固 (2026-03-23)
|
|
1145
1205
|
|
|
1206
|
+
- **Tag 对比快照(`v1.3.0..v1.6.0`)**:
|
|
1207
|
+
- `107` 个提交、`301` 个变更文件、`+125,957 / -10,083` 代码/文档变更量。
|
|
1208
|
+
- 文件状态分布:新增 `241`、修改 `56`、删除 `3`、重命名 `1`。
|
|
1209
|
+
- 主要工程变更面集中在:`src/`、`docs/`、`scripts/`、`path_mode/`、`src-tauri/`。
|
|
1210
|
+
|
|
1146
1211
|
- **单窗口运行时编排**:
|
|
1147
1212
|
- 实现 Tauri <-> Godot 的可见性切换,同一时刻仅显示一个主窗口。
|
|
1148
1213
|
- 增加 Godot 关闭确认流程(“返回主界面” / “关闭全部窗口”),避免误操作导致全局退出。
|
|
@@ -1157,6 +1222,7 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
1157
1222
|
- **可靠性与安全门禁**:
|
|
1158
1223
|
- 扩展 CI/工作流:FixRisk 运维就绪、移动端 e2e 合约、wasm parity、SBOM、attestation、签名与隐私清单校验。
|
|
1159
1224
|
- 新增多层合约回归覆盖(mobile/runtime/pathbridge/storage)。
|
|
1225
|
+
- 纳入发布前 CI 兼容修复:runtime bridge invoke 契约断言兼容与无签名 SBOM transparency 条件化策略。
|
|
1160
1226
|
- **构建性能与开发体验**:
|
|
1161
1227
|
- 增加低内存 Tauri 构建包装器与 release 配置保护,提升受限内存环境可构建性。
|
|
1162
1228
|
- 增加 sidecar 预检,避免开发期重复重建,缩短 `tauri:dev:mini:gpu` 热启动耗时。
|
|
@@ -180,6 +180,7 @@ const ALLOWED_CONFIG_STRATEGY_VALUES = new Set(['foundational', 'core']);
|
|
|
180
180
|
const ALLOWED_CONFIG_LAYOUT_VALUES = new Set(['vertical', 'horizontal', 'radial', 'orbital']);
|
|
181
181
|
const ALLOWED_READING_MODE_VALUES = new Set(['window', 'fullscreen']);
|
|
182
182
|
const ALLOWED_READER_RENDER_MODE_VALUES = new Set(['render', 'source']);
|
|
183
|
+
const ALLOWED_CONFIG_LANGUAGE_VALUES = new Set(['en', 'zh']);
|
|
183
184
|
const ALLOWED_BACKGROUND_FILE_EXTENSIONS = ['.exr', '.hdr'];
|
|
184
185
|
const CONFIG_TARGET_ID_MAX_LENGTH = 512;
|
|
185
186
|
const CONFIG_SHORTCUT_MAX_LENGTH = 64;
|
|
@@ -195,6 +196,7 @@ const ALLOWED_CONFIG_KEYS = new Set([
|
|
|
195
196
|
'mode',
|
|
196
197
|
'strategy',
|
|
197
198
|
'layout',
|
|
199
|
+
'language',
|
|
198
200
|
'targetId',
|
|
199
201
|
'target_id',
|
|
200
202
|
'targetIds',
|
|
@@ -345,6 +347,15 @@ function validateConfigurePayload(payload, policy) {
|
|
|
345
347
|
return `configure payload.layout must be one of: ${Array.from(ALLOWED_CONFIG_LAYOUT_VALUES).join(', ')}.`;
|
|
346
348
|
}
|
|
347
349
|
}
|
|
350
|
+
if (payload.language !== undefined) {
|
|
351
|
+
if (!isNonEmptyString(payload.language)) {
|
|
352
|
+
return 'configure payload.language must be a non-empty string when provided.';
|
|
353
|
+
}
|
|
354
|
+
const normalizedLanguage = payload.language.trim().toLowerCase();
|
|
355
|
+
if (!ALLOWED_CONFIG_LANGUAGE_VALUES.has(normalizedLanguage)) {
|
|
356
|
+
return `configure payload.language must be one of: ${Array.from(ALLOWED_CONFIG_LANGUAGE_VALUES).join(', ')}.`;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
348
359
|
if (payload.targetId !== undefined && typeof payload.targetId !== 'string') {
|
|
349
360
|
return 'configure payload.targetId must be a string when provided.';
|
|
350
361
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 2026-03-
|
|
1
|
+
# 2026-03-24 v1.6.0
|
|
2
2
|
|
|
3
3
|
# NoteConnection Knowledge Graph
|
|
4
4
|
|
|
@@ -279,9 +279,26 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
279
279
|
Managing your knowledge base source is now easier than ever.
|
|
280
280
|
|
|
281
281
|
- **First Run Setup**: On first launch, you will be prompted to select your `Knowledge_Base` folder.
|
|
282
|
-
- **Persistent Config**: Your
|
|
282
|
+
- **Persistent Config (`app_config.toml`)**: Your KB path, language, and multi-window preferences are saved in `%LOCALAPPDATA%/NoteConnection/app_config.toml` (Windows default) and remembered across restarts.
|
|
283
|
+
- **Legacy Auto-Migration**: If a legacy `kb_config.json` exists in the same config directory, NoteConnection automatically migrates it to `app_config.toml`.
|
|
283
284
|
- **Change Anytime**: Use the **File > Change Knowledge Base...** menu option to switch folders instantly.
|
|
284
285
|
- **Reset**: Use **File > Reset to Default** to return to the bundled demo notes.
|
|
286
|
+
- **Config Path Overrides**: Set `NOTE_CONNECTION_CONFIG_PATH` (full file path) or `NOTE_CONNECTION_CONFIG_DIR` (directory) to customize where `app_config.toml` is stored.
|
|
287
|
+
- **Window Behavior Tuning**: Edit `[multi_window]` in `app_config.toml` (`single_window_mode`, `hide_tauri_when_pathmode_opens`, `restore_tauri_when_pathmode_exits`, `confirm_before_full_shutdown_from_godot`, `sync_language`).
|
|
288
|
+
- **Detailed Config Guide**: See [`docs/en/app_config.toml_guide.md`](docs/en/app_config.toml_guide.md) and template [`docs/examples/app_config.template.toml`](docs/examples/app_config.template.toml).
|
|
289
|
+
|
|
290
|
+
```toml
|
|
291
|
+
# Minimal recommended app_config.toml
|
|
292
|
+
knowledge_base_path = "E:/Knowledge_project/NoteConnection_app/Knowledge_Base"
|
|
293
|
+
user_language = "en"
|
|
294
|
+
|
|
295
|
+
[multi_window]
|
|
296
|
+
single_window_mode = true
|
|
297
|
+
hide_tauri_when_pathmode_opens = true
|
|
298
|
+
restore_tauri_when_pathmode_exits = true
|
|
299
|
+
confirm_before_full_shutdown_from_godot = true
|
|
300
|
+
sync_language = true
|
|
301
|
+
```
|
|
285
302
|
|
|
286
303
|
## 🏗️ Build & Deployment
|
|
287
304
|
|
|
@@ -294,6 +311,16 @@ For developers building from source, NoteConnection offers two build modes:
|
|
|
294
311
|
- **GPU Dev Start (`npm run tauri:dev:mini:gpu`)**: Recommended GPU-enabled Tauri development command.
|
|
295
312
|
- **Do not use** `npm run tauri:dev:mini --gpu` because npm treats `--gpu` as config and prints warnings.
|
|
296
313
|
|
|
314
|
+
## 📚 Documentation Architecture (Diataxis + MkDocs)
|
|
315
|
+
|
|
316
|
+
- Canonical long-form docs remain under `docs/en/*` and `docs/zh/*`.
|
|
317
|
+
- Diataxis navigation pages are maintained under `docs/diataxis/<lang>/*`.
|
|
318
|
+
- Mapping governance is versioned in `docs/diataxis-map.json`.
|
|
319
|
+
- Run mapping validation: `npm run docs:diataxis:check`.
|
|
320
|
+
- Run local docs site preview: `npm run docs:site:serve`.
|
|
321
|
+
- Build static docs site: `npm run docs:site:build`.
|
|
322
|
+
- CI policy gate for docs mapping and site build: `.github/workflows/docs-diataxis-site.yml`.
|
|
323
|
+
|
|
297
324
|
## 🛠️ Hardware & Driver Requirements (AMDGPU)
|
|
298
325
|
|
|
299
326
|
For optimal performance with "GPU Optimised Rendering", especially on AMD RDNA cards (like RX 7900XT):
|
|
@@ -309,6 +336,11 @@ For optimal performance with "GPU Optimised Rendering", especially on AMD RDNA c
|
|
|
309
336
|
|
|
310
337
|
### v1.6.0 - Unified Runtime, NoteMD Integration & Release Hardening (2026-03-23)
|
|
311
338
|
|
|
339
|
+
- **Tag Compare Snapshot (`v1.3.0..v1.6.0`)**:
|
|
340
|
+
- `107` commits, `301` files changed, `+125,957 / -10,083` churn.
|
|
341
|
+
- File-level status: `241` added, `56` modified, `3` deleted, `1` renamed.
|
|
342
|
+
- Largest engineering footprint: `src/`, `docs/`, `scripts/`, `path_mode/`, `src-tauri/`.
|
|
343
|
+
|
|
312
344
|
- **Single-Window Runtime Orchestration**:
|
|
313
345
|
- Implemented Tauri <-> Godot visibility handoff so only one primary window is shown at a time.
|
|
314
346
|
- Added Godot close-confirm flow ("Return to main interface" vs "Close all windows") to prevent accidental full shutdown.
|
|
@@ -323,6 +355,7 @@ For optimal performance with "GPU Optimised Rendering", especially on AMD RDNA c
|
|
|
323
355
|
- **Reliability & Security Gates**:
|
|
324
356
|
- Expanded CI/workflow coverage for FixRisk operational readiness, mobile e2e contracts, wasm parity, SBOM, attestation, and signature/privacy checks.
|
|
325
357
|
- Added broad contract-level regression coverage across mobile/runtime/pathbridge/storage layers.
|
|
358
|
+
- Included pre-release CI compatibility fixes for runtime bridge invoke-contract assertions and unsigned SBOM transparency policy handling.
|
|
326
359
|
- **Build Performance & Developer Experience**:
|
|
327
360
|
- Added low-memory Tauri build wrappers and release-profile safeguards for constrained environments.
|
|
328
361
|
- Added sidecar readiness preflight to skip redundant rebuilds during dev startup, reducing warm `tauri:dev:mini:gpu` startup latency.
|
|
@@ -854,7 +887,7 @@ For optimal performance with "GPU Optimised Rendering", especially on AMD RDNA c
|
|
|
854
887
|
## 中文文档
|
|
855
888
|
|
|
856
889
|
|
|
857
|
-
# 2026-03-
|
|
890
|
+
# 2026-03-24 v1.6.0
|
|
858
891
|
# NoteConnection: 层级知识图谱可视化系统
|
|
859
892
|
|
|
860
893
|
<img width="606" height="309" alt="banner" src="https://github.com/user-attachments/assets/92e90de5-2b1a-4398-8e8b-6e142c92b6a2" />
|
|
@@ -1120,9 +1153,26 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
1120
1153
|
管理知识库源现在变得更加简单。
|
|
1121
1154
|
|
|
1122
1155
|
- **首次运行设置**: 首次启动时,系统会提示您选择 `Knowledge_Base` 文件夹。
|
|
1123
|
-
-
|
|
1156
|
+
- **持久化配置 (`app_config.toml`)**: KB 路径、语言及多窗口偏好默认保存到 `%LOCALAPPDATA%/NoteConnection/app_config.toml`(Windows),重启后自动恢复。
|
|
1157
|
+
- **旧配置自动迁移**: 若同目录存在旧版 `kb_config.json`,启动时会自动迁移到 `app_config.toml`。
|
|
1124
1158
|
- **随时更改**: 使用 **文件 > 更改知识库...** 菜单选项即时切换文件夹。
|
|
1125
1159
|
- **重置**: 使用 **文件 > 重置为默认** 返回由捆绑的演示笔记。
|
|
1160
|
+
- **配置路径覆盖**: 可通过 `NOTE_CONNECTION_CONFIG_PATH`(完整文件路径)或 `NOTE_CONNECTION_CONFIG_DIR`(目录)自定义 `app_config.toml` 位置。
|
|
1161
|
+
- **窗口行为可调**: 在 `app_config.toml` 的 `[multi_window]` 段调整 `single_window_mode`、`hide_tauri_when_pathmode_opens`、`restore_tauri_when_pathmode_exits`、`confirm_before_full_shutdown_from_godot`、`sync_language`。
|
|
1162
|
+
- **详细配置说明**: 参见 [`docs/zh/app_config.toml_guide.md`](docs/zh/app_config.toml_guide.md) 与模板 [`docs/examples/app_config.template.toml`](docs/examples/app_config.template.toml)。
|
|
1163
|
+
|
|
1164
|
+
```toml
|
|
1165
|
+
# 推荐最小 app_config.toml
|
|
1166
|
+
knowledge_base_path = "E:/Knowledge_project/NoteConnection_app/Knowledge_Base"
|
|
1167
|
+
user_language = "en"
|
|
1168
|
+
|
|
1169
|
+
[multi_window]
|
|
1170
|
+
single_window_mode = true
|
|
1171
|
+
hide_tauri_when_pathmode_opens = true
|
|
1172
|
+
restore_tauri_when_pathmode_exits = true
|
|
1173
|
+
confirm_before_full_shutdown_from_godot = true
|
|
1174
|
+
sync_language = true
|
|
1175
|
+
```
|
|
1126
1176
|
|
|
1127
1177
|
## 🏗️ 构建与部署 (Build & Deployment)
|
|
1128
1178
|
|
|
@@ -1135,6 +1185,16 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
1135
1185
|
- **GPU 开发启动(推荐)** (`npm run tauri:dev:mini:gpu`)。
|
|
1136
1186
|
- **不要使用** `npm run tauri:dev:mini --gpu`,该写法会被 npm 当作配置参数并触发告警。
|
|
1137
1187
|
|
|
1188
|
+
## 📚 文档架构(Diataxis + MkDocs)
|
|
1189
|
+
|
|
1190
|
+
- 权威长文档仍保持在 `docs/en/*` 与 `docs/zh/*`。
|
|
1191
|
+
- Diataxis 导航页维护在 `docs/diataxis/<lang>/*`。
|
|
1192
|
+
- 映射治理文件为 `docs/diataxis-map.json`。
|
|
1193
|
+
- 映射一致性校验:`npm run docs:diataxis:check`。
|
|
1194
|
+
- 本地预览文档站点:`npm run docs:site:serve`。
|
|
1195
|
+
- 构建静态文档站点:`npm run docs:site:build`。
|
|
1196
|
+
- CI 文档治理工作流:`.github/workflows/docs-diataxis-site.yml`。
|
|
1197
|
+
|
|
1138
1198
|
---
|
|
1139
1199
|
|
|
1140
1200
|
<a id="changelog-zh"></a>
|
|
@@ -1143,6 +1203,11 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
1143
1203
|
|
|
1144
1204
|
### v1.6.0 - 单窗口运行时、NoteMD 集成与发布加固 (2026-03-23)
|
|
1145
1205
|
|
|
1206
|
+
- **Tag 对比快照(`v1.3.0..v1.6.0`)**:
|
|
1207
|
+
- `107` 个提交、`301` 个变更文件、`+125,957 / -10,083` 代码/文档变更量。
|
|
1208
|
+
- 文件状态分布:新增 `241`、修改 `56`、删除 `3`、重命名 `1`。
|
|
1209
|
+
- 主要工程变更面集中在:`src/`、`docs/`、`scripts/`、`path_mode/`、`src-tauri/`。
|
|
1210
|
+
|
|
1146
1211
|
- **单窗口运行时编排**:
|
|
1147
1212
|
- 实现 Tauri <-> Godot 的可见性切换,同一时刻仅显示一个主窗口。
|
|
1148
1213
|
- 增加 Godot 关闭确认流程(“返回主界面” / “关闭全部窗口”),避免误操作导致全局退出。
|
|
@@ -1157,6 +1222,7 @@ npm start -- --path "E:/Knowledge/ObsidianVault" --no-gpu
|
|
|
1157
1222
|
- **可靠性与安全门禁**:
|
|
1158
1223
|
- 扩展 CI/工作流:FixRisk 运维就绪、移动端 e2e 合约、wasm parity、SBOM、attestation、签名与隐私清单校验。
|
|
1159
1224
|
- 新增多层合约回归覆盖(mobile/runtime/pathbridge/storage)。
|
|
1225
|
+
- 纳入发布前 CI 兼容修复:runtime bridge invoke 契约断言兼容与无签名 SBOM transparency 条件化策略。
|
|
1160
1226
|
- **构建性能与开发体验**:
|
|
1161
1227
|
- 增加低内存 Tauri 构建包装器与 release 配置保护,提升受限内存环境可构建性。
|
|
1162
1228
|
- 增加 sidecar 预检,避免开发期重复重建,缩短 `tauri:dev:mini:gpu` 热启动耗时。
|
package/dist/src/frontend/app.js
CHANGED
|
@@ -458,6 +458,40 @@ function isGraphA11yZhMode() {
|
|
|
458
458
|
return String(window.i18n.currentLanguage).toLowerCase().startsWith('zh');
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
+
function getRuntimeAppConfig() {
|
|
462
|
+
if (
|
|
463
|
+
window.NoteConnectionRuntime &&
|
|
464
|
+
typeof window.NoteConnectionRuntime.getAppRuntimeConfig === 'function'
|
|
465
|
+
) {
|
|
466
|
+
return window.NoteConnectionRuntime.getAppRuntimeConfig();
|
|
467
|
+
}
|
|
468
|
+
if (window.__NC_APP_CONFIG && typeof window.__NC_APP_CONFIG === 'object') {
|
|
469
|
+
return window.__NC_APP_CONFIG;
|
|
470
|
+
}
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function resolveRuntimeMultiWindowOptions() {
|
|
475
|
+
const defaults = {
|
|
476
|
+
singleWindowMode: true,
|
|
477
|
+
hideTauriWhenPathmodeOpens: true,
|
|
478
|
+
restoreTauriWhenPathmodeExits: true,
|
|
479
|
+
confirmBeforeFullShutdownFromGodot: true,
|
|
480
|
+
syncLanguage: true
|
|
481
|
+
};
|
|
482
|
+
const config = getRuntimeAppConfig();
|
|
483
|
+
if (!config || typeof config !== 'object' || !config.multiWindow || typeof config.multiWindow !== 'object') {
|
|
484
|
+
return defaults;
|
|
485
|
+
}
|
|
486
|
+
const next = { ...defaults };
|
|
487
|
+
Object.keys(defaults).forEach((key) => {
|
|
488
|
+
if (typeof config.multiWindow[key] === 'boolean') {
|
|
489
|
+
next[key] = config.multiWindow[key];
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
return next;
|
|
493
|
+
}
|
|
494
|
+
|
|
461
495
|
function getGraphRendererMode() {
|
|
462
496
|
const checked = document.querySelector('input[name="rendererMode"]:checked');
|
|
463
497
|
if (!checked || (checked.value !== 'svg' && checked.value !== 'canvas')) {
|
|
@@ -967,10 +1001,13 @@ document.querySelectorAll('input[name="degreeMode"]').forEach(radio => {
|
|
|
967
1001
|
// Localization is now handled by i18n.js
|
|
968
1002
|
// We just need to listen for changes to update dynamic UI components like Analysis Panel
|
|
969
1003
|
if (window.i18n) {
|
|
970
|
-
window.i18n.onLanguageChange(() => {
|
|
1004
|
+
window.i18n.onLanguageChange((newLang) => {
|
|
971
1005
|
if (typeof window.updateAnalysisUI === 'function') {
|
|
972
1006
|
window.updateAnalysisUI();
|
|
973
1007
|
}
|
|
1008
|
+
if (window.pathApp && typeof window.pathApp.syncLanguageWithBridge === 'function') {
|
|
1009
|
+
window.pathApp.syncLanguageWithBridge(newLang);
|
|
1010
|
+
}
|
|
974
1011
|
scheduleGraphSemanticA11yRefresh('Language changed');
|
|
975
1012
|
});
|
|
976
1013
|
}
|
|
@@ -3525,6 +3562,22 @@ if (
|
|
|
3525
3562
|
window.__TAURI__.event.listen('notemd-open-request', () => {
|
|
3526
3563
|
showEmbeddedNoteMD({ source: 'tauri-event' });
|
|
3527
3564
|
});
|
|
3565
|
+
window.__TAURI__.event.listen('app-language-updated', async (event) => {
|
|
3566
|
+
const language = event && event.payload && typeof event.payload.language === 'string'
|
|
3567
|
+
? event.payload.language
|
|
3568
|
+
: '';
|
|
3569
|
+
if (!language || !window.i18n || typeof window.i18n.setLanguage !== 'function') {
|
|
3570
|
+
return;
|
|
3571
|
+
}
|
|
3572
|
+
if (window.i18n.currentLanguage === language) {
|
|
3573
|
+
return;
|
|
3574
|
+
}
|
|
3575
|
+
try {
|
|
3576
|
+
await window.i18n.setLanguage(language);
|
|
3577
|
+
} catch (error) {
|
|
3578
|
+
console.warn('[i18n] Failed to apply app-language-updated event payload:', error);
|
|
3579
|
+
}
|
|
3580
|
+
});
|
|
3528
3581
|
}
|
|
3529
3582
|
|
|
3530
3583
|
if (btnNotemd) {
|
|
@@ -3571,8 +3624,10 @@ if (btnPathMode) {
|
|
|
3571
3624
|
// We check `nodes` length directly because it is the effective runtime source here.
|
|
3572
3625
|
const hasData = (typeof nodes !== 'undefined' && nodes.length > 0);
|
|
3573
3626
|
|
|
3574
|
-
if (!hasData) {
|
|
3575
|
-
const msg = (window.i18n && window.i18n.
|
|
3627
|
+
if (!hasData) {
|
|
3628
|
+
const msg = (window.i18n && typeof window.i18n.t === 'function')
|
|
3629
|
+
? window.i18n.t('pathMode.loadKbFirst')
|
|
3630
|
+
: 'Please load a Knowledge Base first.';
|
|
3576
3631
|
|
|
3577
3632
|
// Inline Feedback
|
|
3578
3633
|
let feedbackEl = document.getElementById('path-mode-feedback');
|
|
@@ -3600,6 +3655,7 @@ if (btnPathMode) {
|
|
|
3600
3655
|
// Check for active selection for Diffusion Learning
|
|
3601
3656
|
const highlightState = window.highlightManager ? window.highlightManager.getState() : null;
|
|
3602
3657
|
const selectedNode = (highlightState && highlightState.currentNode) ? highlightState.currentNode : null;
|
|
3658
|
+
const multiWindowOptions = resolveRuntimeMultiWindowOptions();
|
|
3603
3659
|
|
|
3604
3660
|
console.log('[Path Mode] Entering...', selectedNode ? `Target: ${selectedNode.id}` : 'Domain Mode');
|
|
3605
3661
|
|
|
@@ -3634,14 +3690,19 @@ if (btnPathMode) {
|
|
|
3634
3690
|
if (window.__TAURI__ && window.__TAURI__.core && typeof window.__TAURI__.core.invoke === 'function') {
|
|
3635
3691
|
try {
|
|
3636
3692
|
// 1. Hide the Tauri window via Rust IPC.
|
|
3637
|
-
await window.__TAURI__.core.invoke('toggle_pathmode_window', { showGodot: true });
|
|
3638
|
-
// 2. Send setWindowVisible to Godot via PathBridge WebSocket.
|
|
3639
|
-
if (
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3693
|
+
await window.__TAURI__.core.invoke('toggle_pathmode_window', { showGodot: true });
|
|
3694
|
+
// 2. Send setWindowVisible to Godot via PathBridge WebSocket.
|
|
3695
|
+
if (
|
|
3696
|
+
multiWindowOptions.singleWindowMode &&
|
|
3697
|
+
window.pathApp &&
|
|
3698
|
+
window.pathApp.ws &&
|
|
3699
|
+
window.pathApp.ws.readyState === WebSocket.OPEN
|
|
3700
|
+
) {
|
|
3701
|
+
window.pathApp.ws.send(JSON.stringify({
|
|
3702
|
+
type: 'setWindowVisible',
|
|
3703
|
+
payload: { visible: true }
|
|
3704
|
+
}));
|
|
3705
|
+
}
|
|
3645
3706
|
console.log('[Path Mode] Single-window toggle: Tauri hidden, Godot shown.');
|
|
3646
3707
|
} catch (err) {
|
|
3647
3708
|
console.warn('[Path Mode] toggle_pathmode_window failed:', err);
|
|
@@ -211,12 +211,15 @@
|
|
|
211
211
|
"controls": "Controls"
|
|
212
212
|
}
|
|
213
213
|
},
|
|
214
|
-
"notifications": {
|
|
215
|
-
"buildSuccess": "Build Success! Reloading interface...",
|
|
216
|
-
"buildFailed": "Build failed. Please check your knowledge base path.",
|
|
217
|
-
"noData": "No data available. Please load a knowledge base first."
|
|
218
|
-
},
|
|
219
|
-
"
|
|
214
|
+
"notifications": {
|
|
215
|
+
"buildSuccess": "Build Success! Reloading interface...",
|
|
216
|
+
"buildFailed": "Build failed. Please check your knowledge base path.",
|
|
217
|
+
"noData": "No data available. Please load a knowledge base first."
|
|
218
|
+
},
|
|
219
|
+
"pathMode": {
|
|
220
|
+
"loadKbFirst": "Please load a Knowledge Base first."
|
|
221
|
+
},
|
|
222
|
+
"manual": {
|
|
220
223
|
"loading": "Loading documentation...",
|
|
221
224
|
"error": "Error Loading Documentation",
|
|
222
225
|
"errorDetail": "Could not load User_Manual.md or README.md.",
|
|
@@ -211,12 +211,15 @@
|
|
|
211
211
|
"controls": "控制"
|
|
212
212
|
}
|
|
213
213
|
},
|
|
214
|
-
"notifications": {
|
|
215
|
-
"buildSuccess": "构建成功!重新加载界面...",
|
|
216
|
-
"buildFailed": "构建失败。请检查您的知识库路径。",
|
|
217
|
-
"noData": "无可用数据。请先加载知识库。"
|
|
218
|
-
},
|
|
219
|
-
"
|
|
214
|
+
"notifications": {
|
|
215
|
+
"buildSuccess": "构建成功!重新加载界面...",
|
|
216
|
+
"buildFailed": "构建失败。请检查您的知识库路径。",
|
|
217
|
+
"noData": "无可用数据。请先加载知识库。"
|
|
218
|
+
},
|
|
219
|
+
"pathMode": {
|
|
220
|
+
"loadKbFirst": "请先加载知识库。"
|
|
221
|
+
},
|
|
222
|
+
"nodes": "节点",
|
|
220
223
|
"edges": "边",
|
|
221
224
|
"show_all": "显示全部",
|
|
222
225
|
"show_in": "仅入度",
|
|
@@ -34,7 +34,8 @@ window.pathApp = {
|
|
|
34
34
|
autoReconstruct: true,
|
|
35
35
|
retainHistory: true
|
|
36
36
|
},
|
|
37
|
-
|
|
37
|
+
bridgeLanguageListenerRegistered: false,
|
|
38
|
+
bridgeMermaidRenderQueue: Promise.resolve(),
|
|
38
39
|
semanticA11yLastSummaryKey: '',
|
|
39
40
|
semanticA11yLastAnnouncementAt: 0,
|
|
40
41
|
|
|
@@ -95,10 +96,12 @@ window.pathApp = {
|
|
|
95
96
|
this.ws = socket;
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
this.ws.onopen = () => {
|
|
99
|
-
console.log('[PathApp] Connected to Bridge');
|
|
100
|
-
this._sendBridgeMessage('identify', this._getBridgeIdentifyPayload('frontend'));
|
|
101
|
-
|
|
99
|
+
this.ws.onopen = () => {
|
|
100
|
+
console.log('[PathApp] Connected to Bridge');
|
|
101
|
+
this._sendBridgeMessage('identify', this._getBridgeIdentifyPayload('frontend'));
|
|
102
|
+
this._ensureLanguageSyncListener();
|
|
103
|
+
this.syncLanguageWithBridge();
|
|
104
|
+
};
|
|
102
105
|
this.ws.onmessage = (e) => {
|
|
103
106
|
try {
|
|
104
107
|
const msg = this._parseBridgeIncomingMessage(e.data);
|
|
@@ -246,20 +249,24 @@ window.pathApp = {
|
|
|
246
249
|
console.warn('[PathApp] Bridge socket error:', err);
|
|
247
250
|
};
|
|
248
251
|
|
|
249
|
-
if (hasActiveSocket && this.ws.readyState === WebSocket.OPEN) {
|
|
250
|
-
console.log('[PathApp] Reusing existing Bridge socket');
|
|
251
|
-
this._sendBridgeMessage('identify', this._getBridgeIdentifyPayload('frontend'));
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (!this._supportsSidecarBridge()) {
|
|
257
|
-
console.log('[PathApp] Sidecar bridge is disabled for this runtime; skipping setupWebSocket.');
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
252
|
+
if (hasActiveSocket && this.ws.readyState === WebSocket.OPEN) {
|
|
253
|
+
console.log('[PathApp] Reusing existing Bridge socket');
|
|
254
|
+
this._sendBridgeMessage('identify', this._getBridgeIdentifyPayload('frontend'));
|
|
255
|
+
this._ensureLanguageSyncListener();
|
|
256
|
+
this.syncLanguageWithBridge();
|
|
257
|
+
}
|
|
258
|
+
},
|
|
260
259
|
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
setupWebSocket: function() {
|
|
261
|
+
if (!this._supportsSidecarBridge()) {
|
|
262
|
+
console.log('[PathApp] Sidecar bridge is disabled for this runtime; skipping setupWebSocket.');
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
this._ensureLanguageSyncListener();
|
|
267
|
+
|
|
268
|
+
const bridge = (typeof window !== 'undefined') ? window.NoteConnectionRuntime : null;
|
|
269
|
+
const waitForRuntime = this._isTauriMode() && bridge && typeof bridge.whenReady === 'function';
|
|
263
270
|
|
|
264
271
|
if (!waitForRuntime) {
|
|
265
272
|
this._connectBridgeSocket();
|
|
@@ -344,14 +351,110 @@ window.pathApp = {
|
|
|
344
351
|
return '';
|
|
345
352
|
},
|
|
346
353
|
|
|
347
|
-
_getBridgeIdentifyPayload: function(clientTag) {
|
|
348
|
-
const payload = { client: clientTag };
|
|
349
|
-
const authToken = this._getBridgeAuthToken();
|
|
350
|
-
if (authToken) {
|
|
351
|
-
payload.token = authToken;
|
|
352
|
-
}
|
|
353
|
-
return payload;
|
|
354
|
-
},
|
|
354
|
+
_getBridgeIdentifyPayload: function(clientTag) {
|
|
355
|
+
const payload = { client: clientTag };
|
|
356
|
+
const authToken = this._getBridgeAuthToken();
|
|
357
|
+
if (authToken) {
|
|
358
|
+
payload.token = authToken;
|
|
359
|
+
}
|
|
360
|
+
return payload;
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
_normalizeLanguageCode: function(rawLanguage) {
|
|
364
|
+
const value = String(rawLanguage || '').trim().toLowerCase();
|
|
365
|
+
return value.startsWith('zh') ? 'zh' : 'en';
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
_getActiveLanguage: function() {
|
|
369
|
+
if (window.i18n && typeof window.i18n.currentLanguage === 'string') {
|
|
370
|
+
return this._normalizeLanguageCode(window.i18n.currentLanguage);
|
|
371
|
+
}
|
|
372
|
+
const languageSelect = document.getElementById('set-language');
|
|
373
|
+
if (languageSelect && typeof languageSelect.value === 'string') {
|
|
374
|
+
return this._normalizeLanguageCode(languageSelect.value);
|
|
375
|
+
}
|
|
376
|
+
const appConfig = this._getAppRuntimeConfig();
|
|
377
|
+
if (appConfig && typeof appConfig.language === 'string') {
|
|
378
|
+
return this._normalizeLanguageCode(appConfig.language);
|
|
379
|
+
}
|
|
380
|
+
return 'en';
|
|
381
|
+
},
|
|
382
|
+
|
|
383
|
+
_getAppRuntimeConfig: function() {
|
|
384
|
+
if (
|
|
385
|
+
typeof window !== 'undefined' &&
|
|
386
|
+
window.NoteConnectionRuntime &&
|
|
387
|
+
typeof window.NoteConnectionRuntime.getAppRuntimeConfig === 'function'
|
|
388
|
+
) {
|
|
389
|
+
return window.NoteConnectionRuntime.getAppRuntimeConfig();
|
|
390
|
+
}
|
|
391
|
+
if (typeof window !== 'undefined' && window.__NC_APP_CONFIG && typeof window.__NC_APP_CONFIG === 'object') {
|
|
392
|
+
return window.__NC_APP_CONFIG;
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
_resolveMultiWindowOptions: function() {
|
|
398
|
+
const defaults = {
|
|
399
|
+
singleWindowMode: true,
|
|
400
|
+
hideTauriWhenPathmodeOpens: true,
|
|
401
|
+
restoreTauriWhenPathmodeExits: true,
|
|
402
|
+
confirmBeforeFullShutdownFromGodot: true,
|
|
403
|
+
syncLanguage: true
|
|
404
|
+
};
|
|
405
|
+
const appConfig = this._getAppRuntimeConfig();
|
|
406
|
+
if (!appConfig || !appConfig.multiWindow || typeof appConfig.multiWindow !== 'object') {
|
|
407
|
+
return defaults;
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
singleWindowMode: typeof appConfig.multiWindow.singleWindowMode === 'boolean'
|
|
411
|
+
? appConfig.multiWindow.singleWindowMode
|
|
412
|
+
: defaults.singleWindowMode,
|
|
413
|
+
hideTauriWhenPathmodeOpens: typeof appConfig.multiWindow.hideTauriWhenPathmodeOpens === 'boolean'
|
|
414
|
+
? appConfig.multiWindow.hideTauriWhenPathmodeOpens
|
|
415
|
+
: defaults.hideTauriWhenPathmodeOpens,
|
|
416
|
+
restoreTauriWhenPathmodeExits: typeof appConfig.multiWindow.restoreTauriWhenPathmodeExits === 'boolean'
|
|
417
|
+
? appConfig.multiWindow.restoreTauriWhenPathmodeExits
|
|
418
|
+
: defaults.restoreTauriWhenPathmodeExits,
|
|
419
|
+
confirmBeforeFullShutdownFromGodot: typeof appConfig.multiWindow.confirmBeforeFullShutdownFromGodot === 'boolean'
|
|
420
|
+
? appConfig.multiWindow.confirmBeforeFullShutdownFromGodot
|
|
421
|
+
: defaults.confirmBeforeFullShutdownFromGodot,
|
|
422
|
+
syncLanguage: typeof appConfig.multiWindow.syncLanguage === 'boolean'
|
|
423
|
+
? appConfig.multiWindow.syncLanguage
|
|
424
|
+
: defaults.syncLanguage
|
|
425
|
+
};
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
_buildLanguageConfigurePayload: function(rawLanguage) {
|
|
429
|
+
return {
|
|
430
|
+
language: this._normalizeLanguageCode(rawLanguage || this._getActiveLanguage())
|
|
431
|
+
};
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
syncLanguageWithBridge: function(rawLanguage) {
|
|
435
|
+
const options = this._resolveMultiWindowOptions();
|
|
436
|
+
if (!options.syncLanguage) {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
const payload = this._buildLanguageConfigurePayload(rawLanguage);
|
|
443
|
+
return this._sendBridgeMessage('configure', payload);
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
_ensureLanguageSyncListener: function() {
|
|
447
|
+
if (this.bridgeLanguageListenerRegistered) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (!window.i18n || typeof window.i18n.onLanguageChange !== 'function') {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
this.bridgeLanguageListenerRegistered = true;
|
|
454
|
+
window.i18n.onLanguageChange((lang) => {
|
|
455
|
+
this.syncLanguageWithBridge(lang);
|
|
456
|
+
});
|
|
457
|
+
},
|
|
355
458
|
|
|
356
459
|
_getRuntimeBridgeAdapter: function() {
|
|
357
460
|
if (
|
|
@@ -1543,40 +1646,60 @@ window.pathApp = {
|
|
|
1543
1646
|
|
|
1544
1647
|
if (pathContainer) pathContainer.style.display = 'none';
|
|
1545
1648
|
if (graphWrapper) graphWrapper.style.display = 'block';
|
|
1546
|
-
if (sidebar) {
|
|
1547
|
-
sidebar.style.transform = 'translateX(100%)';
|
|
1548
|
-
sidebar.style.display = 'none';
|
|
1549
|
-
}
|
|
1649
|
+
if (sidebar) {
|
|
1650
|
+
sidebar.style.transform = 'translateX(100%)';
|
|
1651
|
+
sidebar.style.display = 'none';
|
|
1652
|
+
}
|
|
1653
|
+
const multiWindowOptions = this._resolveMultiWindowOptions();
|
|
1550
1654
|
|
|
1551
1655
|
// Single-window toggle: hide Godot, show Tauri.
|
|
1552
1656
|
// 单窗口切换:隐藏 Godot,显示 Tauri。
|
|
1553
1657
|
if (window.__TAURI__ && window.__TAURI__.core && typeof window.__TAURI__.core.invoke === 'function') {
|
|
1554
1658
|
// 1. Hide Godot window via PathBridge WebSocket.
|
|
1555
|
-
if (
|
|
1659
|
+
if (
|
|
1660
|
+
multiWindowOptions.singleWindowMode &&
|
|
1661
|
+
this.ws &&
|
|
1662
|
+
this.ws.readyState === WebSocket.OPEN
|
|
1663
|
+
) {
|
|
1556
1664
|
this.ws.send(JSON.stringify({
|
|
1557
1665
|
type: 'setWindowVisible',
|
|
1558
1666
|
payload: { visible: false }
|
|
1559
1667
|
}));
|
|
1560
1668
|
}
|
|
1561
1669
|
// 2. Restore Tauri window via Rust IPC.
|
|
1562
|
-
|
|
1563
|
-
.
|
|
1564
|
-
|
|
1670
|
+
if (multiWindowOptions.restoreTauriWhenPathmodeExits) {
|
|
1671
|
+
window.__TAURI__.core.invoke('toggle_pathmode_window', { showGodot: false })
|
|
1672
|
+
.then(() => console.log('[PathApp] Single-window toggle: Godot hidden, Tauri restored.'))
|
|
1673
|
+
.catch((err) => console.warn('[PathApp] toggle_pathmode_window restore failed:', err));
|
|
1674
|
+
}
|
|
1565
1675
|
}
|
|
1566
1676
|
|
|
1567
|
-
window.dispatchEvent(new Event('resize'));
|
|
1568
|
-
},
|
|
1677
|
+
window.dispatchEvent(new Event('resize'));
|
|
1678
|
+
},
|
|
1569
1679
|
|
|
1570
1680
|
applyRemoteConfigure: function(config) {
|
|
1571
1681
|
if (!config || typeof config !== 'object') return;
|
|
1572
1682
|
|
|
1573
|
-
const incomingTargetId = typeof config.targetId === 'string'
|
|
1574
|
-
? config.targetId
|
|
1575
|
-
: (typeof config.target_id === 'string' ? config.target_id : null);
|
|
1576
|
-
|
|
1577
|
-
if (typeof config.
|
|
1578
|
-
|
|
1579
|
-
|
|
1683
|
+
const incomingTargetId = typeof config.targetId === 'string'
|
|
1684
|
+
? config.targetId
|
|
1685
|
+
: (typeof config.target_id === 'string' ? config.target_id : null);
|
|
1686
|
+
|
|
1687
|
+
if (typeof config.language === 'string') {
|
|
1688
|
+
const normalizedLanguage = this._normalizeLanguageCode(config.language);
|
|
1689
|
+
if (
|
|
1690
|
+
window.i18n &&
|
|
1691
|
+
typeof window.i18n.setLanguage === 'function' &&
|
|
1692
|
+
window.i18n.currentLanguage !== normalizedLanguage
|
|
1693
|
+
) {
|
|
1694
|
+
void window.i18n.setLanguage(normalizedLanguage).catch((error) => {
|
|
1695
|
+
console.warn('[PathApp] Failed to apply language from remote configure payload:', error);
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
if (typeof config.mode === 'string') {
|
|
1701
|
+
this.runtimeConfig.mode = config.mode === 'diffusion' ? 'diffusion' : 'domain';
|
|
1702
|
+
}
|
|
1580
1703
|
if (typeof config.strategy === 'string') {
|
|
1581
1704
|
this.runtimeConfig.strategy = config.strategy === 'core' ? 'core' : 'foundational';
|
|
1582
1705
|
}
|
|
@@ -3056,11 +3179,13 @@ window.pathApp = {
|
|
|
3056
3179
|
return;
|
|
3057
3180
|
}
|
|
3058
3181
|
|
|
3059
|
-
this.ws.onopen = () => {
|
|
3060
|
-
console.log('[PathApp] Early WS Connected to Bridge');
|
|
3061
|
-
this._sendBridgeMessage('identify', this._getBridgeIdentifyPayload('frontend-early'));
|
|
3062
|
-
|
|
3063
|
-
|
|
3182
|
+
this.ws.onopen = () => {
|
|
3183
|
+
console.log('[PathApp] Early WS Connected to Bridge');
|
|
3184
|
+
this._sendBridgeMessage('identify', this._getBridgeIdentifyPayload('frontend-early'));
|
|
3185
|
+
this._ensureLanguageSyncListener();
|
|
3186
|
+
this.syncLanguageWithBridge();
|
|
3187
|
+
|
|
3188
|
+
const initialCentralId = this._getPreferredStandaloneCentralId(preferredCentralId);
|
|
3064
3189
|
if (initialCentralId) {
|
|
3065
3190
|
this.centralNodeId = initialCentralId;
|
|
3066
3191
|
this.sendPathToBridgeStandalone(initialCentralId);
|
|
@@ -98,6 +98,31 @@
|
|
|
98
98
|
return value.replace(/\/+$/, '');
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
function normalizeLanguageCode(rawValue) {
|
|
102
|
+
const value = String(rawValue || '').trim().toLowerCase();
|
|
103
|
+
return value.startsWith('zh') ? 'zh' : 'en';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalizeBoolean(value, fallback) {
|
|
107
|
+
if (typeof value === 'boolean') {
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
return fallback;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getDefaultAppRuntimeConfig() {
|
|
114
|
+
return {
|
|
115
|
+
language: 'en',
|
|
116
|
+
multiWindow: {
|
|
117
|
+
singleWindowMode: true,
|
|
118
|
+
hideTauriWhenPathmodeOpens: true,
|
|
119
|
+
restoreTauriWhenPathmodeExits: true,
|
|
120
|
+
confirmBeforeFullShutdownFromGodot: true,
|
|
121
|
+
syncLanguage: true
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
101
126
|
const state = {
|
|
102
127
|
baseUrl: normalizeBaseUrl(window.__NC_SIDECAR_RUNTIME && window.__NC_SIDECAR_RUNTIME.baseUrl),
|
|
103
128
|
bridgeWsUrl: normalizeBridgeWsUrl(window.__NC_SIDECAR_RUNTIME && window.__NC_SIDECAR_RUNTIME.bridgeWsUrl),
|
|
@@ -106,6 +131,34 @@
|
|
|
106
131
|
port: Number((window.__NC_SIDECAR_RUNTIME && window.__NC_SIDECAR_RUNTIME.port) || 3000),
|
|
107
132
|
bridgePort: Number((window.__NC_SIDECAR_RUNTIME && window.__NC_SIDECAR_RUNTIME.bridgePort) || 9876)
|
|
108
133
|
};
|
|
134
|
+
const appState = getDefaultAppRuntimeConfig();
|
|
135
|
+
if (window.__NC_APP_CONFIG && typeof window.__NC_APP_CONFIG === 'object') {
|
|
136
|
+
const bootAppConfig = window.__NC_APP_CONFIG;
|
|
137
|
+
appState.language = normalizeLanguageCode(bootAppConfig.language || appState.language);
|
|
138
|
+
if (bootAppConfig.multiWindow && typeof bootAppConfig.multiWindow === 'object') {
|
|
139
|
+
const bootMultiWindow = bootAppConfig.multiWindow;
|
|
140
|
+
appState.multiWindow.singleWindowMode = normalizeBoolean(
|
|
141
|
+
bootMultiWindow.singleWindowMode,
|
|
142
|
+
appState.multiWindow.singleWindowMode
|
|
143
|
+
);
|
|
144
|
+
appState.multiWindow.hideTauriWhenPathmodeOpens = normalizeBoolean(
|
|
145
|
+
bootMultiWindow.hideTauriWhenPathmodeOpens,
|
|
146
|
+
appState.multiWindow.hideTauriWhenPathmodeOpens
|
|
147
|
+
);
|
|
148
|
+
appState.multiWindow.restoreTauriWhenPathmodeExits = normalizeBoolean(
|
|
149
|
+
bootMultiWindow.restoreTauriWhenPathmodeExits,
|
|
150
|
+
appState.multiWindow.restoreTauriWhenPathmodeExits
|
|
151
|
+
);
|
|
152
|
+
appState.multiWindow.confirmBeforeFullShutdownFromGodot = normalizeBoolean(
|
|
153
|
+
bootMultiWindow.confirmBeforeFullShutdownFromGodot,
|
|
154
|
+
appState.multiWindow.confirmBeforeFullShutdownFromGodot
|
|
155
|
+
);
|
|
156
|
+
appState.multiWindow.syncLanguage = normalizeBoolean(
|
|
157
|
+
bootMultiWindow.syncLanguage,
|
|
158
|
+
appState.multiWindow.syncLanguage
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
109
162
|
|
|
110
163
|
let runtimeReadyResolved = false;
|
|
111
164
|
let runtimeHydrationPromise = null;
|
|
@@ -126,6 +179,20 @@
|
|
|
126
179
|
return window.__NC_SIDECAR_RUNTIME;
|
|
127
180
|
}
|
|
128
181
|
|
|
182
|
+
function syncGlobalAppState() {
|
|
183
|
+
window.__NC_APP_CONFIG = {
|
|
184
|
+
language: appState.language,
|
|
185
|
+
multiWindow: {
|
|
186
|
+
singleWindowMode: appState.multiWindow.singleWindowMode,
|
|
187
|
+
hideTauriWhenPathmodeOpens: appState.multiWindow.hideTauriWhenPathmodeOpens,
|
|
188
|
+
restoreTauriWhenPathmodeExits: appState.multiWindow.restoreTauriWhenPathmodeExits,
|
|
189
|
+
confirmBeforeFullShutdownFromGodot: appState.multiWindow.confirmBeforeFullShutdownFromGodot,
|
|
190
|
+
syncLanguage: appState.multiWindow.syncLanguage
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
return window.__NC_APP_CONFIG;
|
|
194
|
+
}
|
|
195
|
+
|
|
129
196
|
function finalizeRuntimeReady() {
|
|
130
197
|
if (runtimeReadyResolved) {
|
|
131
198
|
return runtimeReadyPromise;
|
|
@@ -170,6 +237,44 @@
|
|
|
170
237
|
return syncGlobalState();
|
|
171
238
|
}
|
|
172
239
|
|
|
240
|
+
function setAppRuntimeConfig(nextConfig) {
|
|
241
|
+
const merged = getDefaultAppRuntimeConfig();
|
|
242
|
+
if (nextConfig && typeof nextConfig === 'object') {
|
|
243
|
+
merged.language = normalizeLanguageCode(nextConfig.language || merged.language);
|
|
244
|
+
const nextMultiWindow = nextConfig.multiWindow && typeof nextConfig.multiWindow === 'object'
|
|
245
|
+
? nextConfig.multiWindow
|
|
246
|
+
: {};
|
|
247
|
+
merged.multiWindow.singleWindowMode = normalizeBoolean(
|
|
248
|
+
nextMultiWindow.singleWindowMode,
|
|
249
|
+
merged.multiWindow.singleWindowMode
|
|
250
|
+
);
|
|
251
|
+
merged.multiWindow.hideTauriWhenPathmodeOpens = normalizeBoolean(
|
|
252
|
+
nextMultiWindow.hideTauriWhenPathmodeOpens,
|
|
253
|
+
merged.multiWindow.hideTauriWhenPathmodeOpens
|
|
254
|
+
);
|
|
255
|
+
merged.multiWindow.restoreTauriWhenPathmodeExits = normalizeBoolean(
|
|
256
|
+
nextMultiWindow.restoreTauriWhenPathmodeExits,
|
|
257
|
+
merged.multiWindow.restoreTauriWhenPathmodeExits
|
|
258
|
+
);
|
|
259
|
+
merged.multiWindow.confirmBeforeFullShutdownFromGodot = normalizeBoolean(
|
|
260
|
+
nextMultiWindow.confirmBeforeFullShutdownFromGodot,
|
|
261
|
+
merged.multiWindow.confirmBeforeFullShutdownFromGodot
|
|
262
|
+
);
|
|
263
|
+
merged.multiWindow.syncLanguage = normalizeBoolean(
|
|
264
|
+
nextMultiWindow.syncLanguage,
|
|
265
|
+
merged.multiWindow.syncLanguage
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
appState.language = merged.language;
|
|
270
|
+
appState.multiWindow = merged.multiWindow;
|
|
271
|
+
return syncGlobalAppState();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function getAppRuntimeConfig() {
|
|
275
|
+
return syncGlobalAppState();
|
|
276
|
+
}
|
|
277
|
+
|
|
173
278
|
function buildUrl(resourcePath, query) {
|
|
174
279
|
const normalizedPath = String(resourcePath || '').replace(/^\/+/, '');
|
|
175
280
|
const url = new URL(normalizedPath, `${state.baseUrl}/`);
|
|
@@ -292,6 +397,11 @@
|
|
|
292
397
|
};
|
|
293
398
|
}
|
|
294
399
|
|
|
400
|
+
} catch (error) {
|
|
401
|
+
console.warn('[RuntimeBridge] Failed to hydrate runtime capabilities from Tauri. Using runtime bridge defaults.', error);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
try {
|
|
295
405
|
const runtimeCaps = window.__NC_RUNTIME_CAPS || {};
|
|
296
406
|
if (runtimeCaps.supports_sidecar) {
|
|
297
407
|
const runtimeConfig = await invokeTauriWithTimeout(
|
|
@@ -305,11 +415,23 @@
|
|
|
305
415
|
console.warn('[RuntimeBridge] Failed to hydrate sidecar runtime config from Tauri. Using runtime bridge defaults.', error);
|
|
306
416
|
}
|
|
307
417
|
|
|
418
|
+
try {
|
|
419
|
+
const appConfig = await invokeTauriWithTimeout(
|
|
420
|
+
() => invoke('get_app_runtime_config'),
|
|
421
|
+
'get_app_runtime_config',
|
|
422
|
+
TAURI_RUNTIME_HYDRATE_TIMEOUT_MS
|
|
423
|
+
);
|
|
424
|
+
setAppRuntimeConfig(appConfig);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.warn('[RuntimeBridge] Failed to hydrate app runtime config from Tauri. Using runtime defaults.', error);
|
|
427
|
+
}
|
|
428
|
+
|
|
308
429
|
if (typeof window.dispatchEvent === 'function' && typeof window.CustomEvent === 'function') {
|
|
309
430
|
window.dispatchEvent(new CustomEvent('noteconnection:runtime-ready', {
|
|
310
431
|
detail: {
|
|
311
432
|
runtime: syncGlobalState(),
|
|
312
|
-
caps: window.__NC_RUNTIME_CAPS || null
|
|
433
|
+
caps: window.__NC_RUNTIME_CAPS || null,
|
|
434
|
+
appConfig: syncGlobalAppState()
|
|
313
435
|
}
|
|
314
436
|
}));
|
|
315
437
|
}
|
|
@@ -330,6 +452,8 @@
|
|
|
330
452
|
window.NoteConnectionRuntime = {
|
|
331
453
|
setRuntimeConfig,
|
|
332
454
|
getRuntimeConfig,
|
|
455
|
+
setAppRuntimeConfig,
|
|
456
|
+
getAppRuntimeConfig,
|
|
333
457
|
buildUrl,
|
|
334
458
|
buildFetchOptions,
|
|
335
459
|
createAuthHeaders,
|
|
@@ -349,6 +473,7 @@
|
|
|
349
473
|
};
|
|
350
474
|
|
|
351
475
|
syncGlobalState();
|
|
476
|
+
syncGlobalAppState();
|
|
352
477
|
|
|
353
478
|
if (document.readyState === 'loading') {
|
|
354
479
|
document.addEventListener('DOMContentLoaded', () => {
|
|
@@ -41,6 +41,15 @@ const child_process_1 = require("child_process");
|
|
|
41
41
|
function readJson(filePath) {
|
|
42
42
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
43
43
|
}
|
|
44
|
+
function buildUnsignedGeneratorEnv() {
|
|
45
|
+
return {
|
|
46
|
+
...process.env,
|
|
47
|
+
NOTE_CONNECTION_SBOM_SIGNING_PRIVATE_KEY_PEM: '',
|
|
48
|
+
NOTE_CONNECTION_SBOM_SIGNING_PRIVATE_KEY_FILE: '',
|
|
49
|
+
NOTE_CONNECTION_SBOM_ATTESTATION_ENABLE_TRANSPARENCY_LOG: '0',
|
|
50
|
+
NOTE_CONNECTION_SBOM_ATTESTATION_TRANSPARENCY_LOG_PATH: '',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
44
53
|
describe('sbom attestation policy contract', () => {
|
|
45
54
|
const repoRoot = path.resolve(__dirname, '..');
|
|
46
55
|
const packageJsonPath = path.join(repoRoot, 'package.json');
|
|
@@ -48,6 +57,33 @@ describe('sbom attestation policy contract', () => {
|
|
|
48
57
|
const verifierPath = path.join(repoRoot, 'scripts', 'verify-sbom-attestation.js');
|
|
49
58
|
const migrationWorkflowPath = path.join(repoRoot, '.github', 'workflows', 'migration-gates.yml');
|
|
50
59
|
const npmPublishWorkflowPath = path.join(repoRoot, '.github', 'workflows', 'npm-publish.yml');
|
|
60
|
+
let noteConnectionEnvSnapshot;
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
// Keep contract tests hermetic even when CI injects NOTE_CONNECTION_* policy env vars.
|
|
63
|
+
noteConnectionEnvSnapshot = {};
|
|
64
|
+
for (const key of Object.keys(process.env)) {
|
|
65
|
+
if (!key.startsWith('NOTE_CONNECTION_')) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
noteConnectionEnvSnapshot[key] = process.env[key];
|
|
69
|
+
delete process.env[key];
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
for (const key of Object.keys(process.env)) {
|
|
74
|
+
if (key.startsWith('NOTE_CONNECTION_')) {
|
|
75
|
+
delete process.env[key];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const [key, value] of Object.entries(noteConnectionEnvSnapshot || {})) {
|
|
79
|
+
if (typeof value === 'undefined') {
|
|
80
|
+
delete process.env[key];
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
process.env[key] = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
51
87
|
test('exports attestation generation/verification scripts and gate wiring', () => {
|
|
52
88
|
const packageJson = readJson(packageJsonPath);
|
|
53
89
|
const scripts = packageJson.scripts || {};
|
|
@@ -124,6 +160,7 @@ describe('sbom attestation policy contract', () => {
|
|
|
124
160
|
expect(npmPublishWorkflow).toContain('NOTE_CONNECTION_SBOM_ATTESTATION_VERIFY_TRANSPARENCY_LOG_INCLUSION');
|
|
125
161
|
expect(npmPublishWorkflow).toContain('NOTE_CONNECTION_SBOM_ATTESTATION_TRANSPARENCY_EXPECT_SCHEMA');
|
|
126
162
|
expect(npmPublishWorkflow).toContain('NOTE_CONNECTION_SBOM_ATTESTATION_TRANSPARENCY_EXPECT_VERSION');
|
|
163
|
+
expect(npmPublishWorkflow).toContain('npx jest --runInBand');
|
|
127
164
|
});
|
|
128
165
|
test('attestation generator and verifier work in unsigned strict policy mode', () => {
|
|
129
166
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-sbom-attestation-'));
|
|
@@ -142,6 +179,7 @@ describe('sbom attestation policy contract', () => {
|
|
|
142
179
|
cwd: repoRoot,
|
|
143
180
|
encoding: 'utf8',
|
|
144
181
|
stdio: 'pipe',
|
|
182
|
+
env: buildUnsignedGeneratorEnv(),
|
|
145
183
|
});
|
|
146
184
|
expect(generateResult.status).toBe(0);
|
|
147
185
|
const verifyResult = (0, child_process_1.spawnSync)(process.execPath, [
|
|
@@ -183,6 +221,7 @@ describe('sbom attestation policy contract', () => {
|
|
|
183
221
|
cwd: repoRoot,
|
|
184
222
|
encoding: 'utf8',
|
|
185
223
|
stdio: 'pipe',
|
|
224
|
+
env: buildUnsignedGeneratorEnv(),
|
|
186
225
|
});
|
|
187
226
|
expect(generateResult.status).toBe(0);
|
|
188
227
|
const verifyResult = (0, child_process_1.spawnSync)(process.execPath, [
|
|
@@ -232,6 +271,7 @@ describe('sbom attestation policy contract', () => {
|
|
|
232
271
|
cwd: repoRoot,
|
|
233
272
|
encoding: 'utf8',
|
|
234
273
|
stdio: 'pipe',
|
|
274
|
+
env: buildUnsignedGeneratorEnv(),
|
|
235
275
|
});
|
|
236
276
|
expect(generateResult.status).toBe(0);
|
|
237
277
|
const verifyResult = (0, child_process_1.spawnSync)(process.execPath, [
|
|
@@ -275,6 +315,7 @@ describe('sbom attestation policy contract', () => {
|
|
|
275
315
|
cwd: repoRoot,
|
|
276
316
|
encoding: 'utf8',
|
|
277
317
|
stdio: 'pipe',
|
|
318
|
+
env: buildUnsignedGeneratorEnv(),
|
|
278
319
|
});
|
|
279
320
|
expect(generateResult.status).toBe(0);
|
|
280
321
|
const verifyResult = (0, child_process_1.spawnSync)(process.execPath, [
|
|
@@ -243,6 +243,7 @@ describe('server migration settings routes', () => {
|
|
|
243
243
|
let buildGraphMock;
|
|
244
244
|
let renderMathPngMock;
|
|
245
245
|
let renderMermaidPngMock;
|
|
246
|
+
let copyPngToClipboardMock;
|
|
246
247
|
let originalArgv;
|
|
247
248
|
beforeAll(async () => {
|
|
248
249
|
temp = new TempDir('noteconnection-server');
|
|
@@ -283,6 +284,7 @@ describe('server migration settings routes', () => {
|
|
|
283
284
|
width: 640,
|
|
284
285
|
height: 360
|
|
285
286
|
});
|
|
287
|
+
copyPngToClipboardMock = jest.fn().mockResolvedValue(undefined);
|
|
286
288
|
jest.resetModules();
|
|
287
289
|
originalArgv = [...process.argv];
|
|
288
290
|
process.argv = process.argv.slice(0, 2);
|
|
@@ -296,6 +298,9 @@ describe('server migration settings routes', () => {
|
|
|
296
298
|
renderMathPng: renderMathPngMock,
|
|
297
299
|
renderMermaidPng: renderMermaidPngMock
|
|
298
300
|
}));
|
|
301
|
+
jest.doMock('./native_clipboard', () => ({
|
|
302
|
+
copyPngToClipboard: copyPngToClipboardMock
|
|
303
|
+
}));
|
|
299
304
|
const serverModule = require('./server');
|
|
300
305
|
server = await serverModule.startServer({ port });
|
|
301
306
|
buildGraphMock.mockClear();
|
|
@@ -319,6 +324,7 @@ describe('server migration settings routes', () => {
|
|
|
319
324
|
jest.dontMock('./index');
|
|
320
325
|
jest.dontMock('./core/PathBridge');
|
|
321
326
|
jest.dontMock('./reader_renderer');
|
|
327
|
+
jest.dontMock('./native_clipboard');
|
|
322
328
|
process.argv = originalArgv;
|
|
323
329
|
temp.cleanup();
|
|
324
330
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noteconnection",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "Hierarchical Knowledge Graph Visualization System",
|
|
5
5
|
"main": "dist/src/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
"start": "node scripts/start-server.js",
|
|
11
11
|
"start:prod": "node dist/src/server.js",
|
|
12
12
|
"tauri": "tauri",
|
|
13
|
-
"tauri:dev": "npm run build && npm run ensure:sidecar:dev && npm run cleanup:tauri:sidecars && npx kill-port 3000 && npx tauri dev",
|
|
14
|
-
"tauri:dev:mini": "npm run build:mini && npm run ensure:sidecar:dev && npm run cleanup:tauri:sidecars && npx kill-port 3000 && npx tauri dev",
|
|
13
|
+
"tauri:dev": "npm run build && npm run ensure:sidecar:dev && npm run cleanup:tauri:sidecars && npx kill-port 3000 && npx tauri dev",
|
|
14
|
+
"tauri:dev:mini": "npm run build:mini && npm run ensure:sidecar:dev && npm run cleanup:tauri:sidecars && npx kill-port 3000 && npx tauri dev",
|
|
15
15
|
"tauri:dev:gpu": "set NOTE_CONNECTION_GPU=1&& npm run tauri:dev",
|
|
16
16
|
"tauri:dev:mini:gpu": "set NOTE_CONNECTION_GPU=1&& npm run tauri:dev:mini",
|
|
17
|
-
"tauri:build": "npm run build && npm run build:sidecar && npm run cleanup:tauri:sidecars && node scripts/run-tauri-build.js",
|
|
18
|
-
"tauri:build:mini": "npm run build:mini && npm run build:sidecar && npm run cleanup:tauri:sidecars && node scripts/run-tauri-build.js",
|
|
17
|
+
"tauri:build": "npm run build && npm run build:sidecar && npm run cleanup:tauri:sidecars && node scripts/run-tauri-build.js",
|
|
18
|
+
"tauri:build:mini": "npm run build:mini && npm run build:sidecar && npm run cleanup:tauri:sidecars && node scripts/run-tauri-build.js",
|
|
19
19
|
"verify:android:env": "node scripts/verify-tauri-android-prereqs.js",
|
|
20
20
|
"verify:capacitor:device": "node scripts/verify-capacitor-device-acceptance.js",
|
|
21
21
|
"capture:capacitor:evidence": "node scripts/capture-capacitor-device-evidence.js",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"build:mini": "node scripts/copy-reader-runtime-assets.js && tsc && node scripts/bundle_path_core.js && node scripts/copy-assets.js --mini && node scripts/sync-wasm-parity-artifact.js",
|
|
34
34
|
"prepare:godot:bin": "node scripts/ensure-godot-sidecar.js",
|
|
35
35
|
"verify:tauri:bin": "node scripts/validate-tauri-sidecars.js",
|
|
36
|
-
"build:sidecar": "node scripts/copy-reader-runtime-assets.js && tsc && node scripts/build-sidecar.js && npm run prepare:godot:bin && npm run verify:tauri:bin",
|
|
37
|
-
"build:sidecar:all": "node scripts/copy-reader-runtime-assets.js && tsc && node scripts/build-sidecar.js --all && npm run prepare:godot:bin && node scripts/validate-tauri-sidecars.js --all",
|
|
38
|
-
"ensure:sidecar:dev": "node scripts/ensure-sidecar-ready.js",
|
|
36
|
+
"build:sidecar": "node scripts/copy-reader-runtime-assets.js && tsc && node scripts/build-sidecar.js && npm run prepare:godot:bin && npm run verify:tauri:bin",
|
|
37
|
+
"build:sidecar:all": "node scripts/copy-reader-runtime-assets.js && tsc && node scripts/build-sidecar.js --all && npm run prepare:godot:bin && node scripts/validate-tauri-sidecars.js --all",
|
|
38
|
+
"ensure:sidecar:dev": "node scripts/ensure-sidecar-ready.js",
|
|
39
39
|
"pathmode:dev": "node -r ts-node/register src/server.ts --pathmode",
|
|
40
40
|
"pathmode:test": "jest --testPathPatterns=Path",
|
|
41
41
|
"smoke:sidecar:relaunch": "node scripts/smoke-sidecar-relaunch.js",
|
|
@@ -51,28 +51,31 @@
|
|
|
51
51
|
"benchmark:wasm:parity:history:release": "node scripts/benchmark-wasm-parity.js --require-wasm-adapter 1 --history-window 90 --minimum-history-samples 5 --history-strict-samples 15 --history-maturity-fail-tier enforced --history-performance-fail-mode always --history-max-records 3000 --history-max-age-days 180 --max-candidate-to-history-graph-p95-ratio 1.25 --max-candidate-to-history-layout-p95-ratio 1.25 --max-candidate-to-history-graph-p99-ratio 1.25 --max-candidate-to-history-layout-p99-ratio 1.25",
|
|
52
52
|
"calibrate:graphmetrics:tiering": "node scripts/calibrate-graphmetrics-tiering.js",
|
|
53
53
|
"test:wasm:parity:gates": "npm run verify:wasm:parity:strict && npm run benchmark:wasm:parity:strict:perf",
|
|
54
|
-
"verify:detox:pipeline": "node scripts/verify-detox-pipeline.js",
|
|
55
|
-
"verify:fixrisk:issues": "node scripts/verify-fixrisk-issues.js",
|
|
56
|
-
"verify:fixrisk:issues:strict": "node scripts/verify-fixrisk-issues.js --strict-pending",
|
|
57
|
-
"verify:fixrisk:issues:strict:evidence": "node scripts/verify-fixrisk-issues.js --strict-pending --require-evidence-root",
|
|
58
|
-
"verify:pathbridge:strict": "node scripts/verify-pathbridge-strict-schema.js",
|
|
59
|
-
"generate:sbom": "node scripts/generate-sbom.js",
|
|
60
|
-
"generate:sbom:attestation": "node scripts/generate-sbom-attestation.js",
|
|
61
|
-
"verify:sbom": "node scripts/verify-sbom-policy.js",
|
|
62
|
-
"verify:sbom:attestation": "node scripts/verify-sbom-attestation.js",
|
|
63
|
-
"test:e2e:detox": "node scripts/run-detox-e2e.js",
|
|
64
|
-
"test:e2e:detox:run": "node scripts/run-detox-e2e.js --run",
|
|
65
|
-
"verify:privacy:manifest": "node scripts/verify-privacy-manifest.js",
|
|
66
|
-
"verify:sidecar:signatures": "node scripts/verify-sidecar-signatures.js",
|
|
67
|
-
"ops:fixrisk:close": "node scripts/run-fixrisk-ops-closure.js",
|
|
68
|
-
"ops:fixrisk:close:dry": "node scripts/run-fixrisk-ops-closure.js --dry-run",
|
|
69
|
-
"test:mobile:contracts": "jest src/mobile.pipeline.test.ts src/runtime.capabilities.test.ts src/source_manager.loadflow.test.ts src/capacitor.runtime.contract.test.ts src/android.pathmode.contract.test.ts src/android.pathmode.smoke.contract.test.ts src/graph.accessibility.contract.test.ts src/detox.pipeline.contract.test.ts src/privacy.manifest.contract.test.ts --runInBand && npm run verify:detox:pipeline && npm run verify:privacy:manifest",
|
|
70
|
-
"test:migration": "jest src/core/Graph.test.ts src/core/PathEngine.test.ts src/core/TreeLayout.test.ts src/backend/algorithms/CycleDetection.test.ts src/backend/algorithms/TopologicalSort.test.ts src/backend/algorithms/WasmParityHistory.test.ts src/utils/RuntimePaths.test.ts src/server.migration.test.ts src/pkg.sidecar.contract.test.ts src/pkg.snapshot.safety.contract.test.ts src/mobile.pipeline.test.ts src/capacitor.device.utils.contract.test.ts src/capacitor.evidence.contract.test.ts src/runtime.capabilities.test.ts src/runtime.heap.policy.contract.test.ts src/runtime.spool.policy.contract.test.ts src/runtime.transport.adapter.contract.test.ts src/storage.provider.contract.test.ts src/storage.provider.capacitor.content.contract.test.ts src/storage.provider.capacitor.worker.contract.test.ts src/wasm.parity.runtime.contract.test.ts src/wasm.parity.runtime.functional.test.ts src/wasm.parity.output.equivalence.contract.test.ts src/wasm.parity.benchmark.contract.test.ts src/wasm.parity.benchmark.guards.contract.test.ts src/wasm.parity.history.gate.contract.test.ts src/wasm.parity.artifact.probe.contract.test.ts src/wasm.parity.artifact.provisioning.contract.test.ts src/source_manager.loadflow.test.ts src/capacitor.runtime.contract.test.ts src/welcome.loadflow.test.ts src/pathmode.history.contract.test.ts src/android.pathmode.contract.test.ts src/android.pathmode.smoke.contract.test.ts src/pathbridge.handshake.contract.test.ts src/pathbridge.strict.policy.contract.test.ts src/graph.accessibility.contract.test.ts src/detox.pipeline.contract.test.ts src/privacy.manifest.contract.test.ts src/server.port.fallback.contract.test.ts src/sidecar.signature.contract.test.ts src/sbom.policy.contract.test.ts src/sbom.attestation.policy.contract.test.ts src/sidecar.relaunch.contract.test.ts src/tauri.test.runner.contract.test.ts --runInBand",
|
|
71
|
-
"test:tauri": "node scripts/run-tauri-tests.js",
|
|
72
|
-
"test:gates": "npm run test:migration && npm run test:wasm:parity:gates && npm run test:tauri && npm run verify:android:env && npm run verify:detox:pipeline && npm run verify:privacy:manifest && npm run verify:pathbridge:strict && npm run verify:sbom -- --contract-only && npm run verify:sbom:attestation -- --contract-only && npm run verify:sidecar:signatures -- --contract-only",
|
|
73
|
-
"prepublishOnly": "npm run build:mini",
|
|
54
|
+
"verify:detox:pipeline": "node scripts/verify-detox-pipeline.js",
|
|
55
|
+
"verify:fixrisk:issues": "node scripts/verify-fixrisk-issues.js",
|
|
56
|
+
"verify:fixrisk:issues:strict": "node scripts/verify-fixrisk-issues.js --strict-pending",
|
|
57
|
+
"verify:fixrisk:issues:strict:evidence": "node scripts/verify-fixrisk-issues.js --strict-pending --require-evidence-root",
|
|
58
|
+
"verify:pathbridge:strict": "node scripts/verify-pathbridge-strict-schema.js",
|
|
59
|
+
"generate:sbom": "node scripts/generate-sbom.js",
|
|
60
|
+
"generate:sbom:attestation": "node scripts/generate-sbom-attestation.js",
|
|
61
|
+
"verify:sbom": "node scripts/verify-sbom-policy.js",
|
|
62
|
+
"verify:sbom:attestation": "node scripts/verify-sbom-attestation.js",
|
|
63
|
+
"test:e2e:detox": "node scripts/run-detox-e2e.js",
|
|
64
|
+
"test:e2e:detox:run": "node scripts/run-detox-e2e.js --run",
|
|
65
|
+
"verify:privacy:manifest": "node scripts/verify-privacy-manifest.js",
|
|
66
|
+
"verify:sidecar:signatures": "node scripts/verify-sidecar-signatures.js",
|
|
67
|
+
"ops:fixrisk:close": "node scripts/run-fixrisk-ops-closure.js",
|
|
68
|
+
"ops:fixrisk:close:dry": "node scripts/run-fixrisk-ops-closure.js --dry-run",
|
|
69
|
+
"test:mobile:contracts": "jest src/mobile.pipeline.test.ts src/runtime.capabilities.test.ts src/source_manager.loadflow.test.ts src/capacitor.runtime.contract.test.ts src/android.pathmode.contract.test.ts src/android.pathmode.smoke.contract.test.ts src/graph.accessibility.contract.test.ts src/detox.pipeline.contract.test.ts src/privacy.manifest.contract.test.ts --runInBand && npm run verify:detox:pipeline && npm run verify:privacy:manifest",
|
|
70
|
+
"test:migration": "jest src/core/Graph.test.ts src/core/PathEngine.test.ts src/core/TreeLayout.test.ts src/backend/algorithms/CycleDetection.test.ts src/backend/algorithms/TopologicalSort.test.ts src/backend/algorithms/WasmParityHistory.test.ts src/utils/RuntimePaths.test.ts src/server.migration.test.ts src/pkg.sidecar.contract.test.ts src/pkg.snapshot.safety.contract.test.ts src/mobile.pipeline.test.ts src/capacitor.device.utils.contract.test.ts src/capacitor.evidence.contract.test.ts src/runtime.capabilities.test.ts src/runtime.heap.policy.contract.test.ts src/runtime.spool.policy.contract.test.ts src/runtime.transport.adapter.contract.test.ts src/storage.provider.contract.test.ts src/storage.provider.capacitor.content.contract.test.ts src/storage.provider.capacitor.worker.contract.test.ts src/wasm.parity.runtime.contract.test.ts src/wasm.parity.runtime.functional.test.ts src/wasm.parity.output.equivalence.contract.test.ts src/wasm.parity.benchmark.contract.test.ts src/wasm.parity.benchmark.guards.contract.test.ts src/wasm.parity.history.gate.contract.test.ts src/wasm.parity.artifact.probe.contract.test.ts src/wasm.parity.artifact.provisioning.contract.test.ts src/source_manager.loadflow.test.ts src/capacitor.runtime.contract.test.ts src/welcome.loadflow.test.ts src/pathmode.history.contract.test.ts src/android.pathmode.contract.test.ts src/android.pathmode.smoke.contract.test.ts src/pathbridge.handshake.contract.test.ts src/pathbridge.strict.policy.contract.test.ts src/graph.accessibility.contract.test.ts src/detox.pipeline.contract.test.ts src/privacy.manifest.contract.test.ts src/server.port.fallback.contract.test.ts src/sidecar.signature.contract.test.ts src/sbom.policy.contract.test.ts src/sbom.attestation.policy.contract.test.ts src/sidecar.relaunch.contract.test.ts src/tauri.test.runner.contract.test.ts --runInBand",
|
|
71
|
+
"test:tauri": "node scripts/run-tauri-tests.js",
|
|
72
|
+
"test:gates": "npm run test:migration && npm run test:wasm:parity:gates && npm run test:tauri && npm run verify:android:env && npm run verify:detox:pipeline && npm run verify:privacy:manifest && npm run verify:pathbridge:strict && npm run verify:sbom -- --contract-only && npm run verify:sbom:attestation -- --contract-only && npm run verify:sidecar:signatures -- --contract-only",
|
|
73
|
+
"prepublishOnly": "npm run build:mini",
|
|
74
74
|
"test": "jest",
|
|
75
|
-
"cleanup:tauri:sidecars": "node scripts/cleanup-tauri-sidecars.js"
|
|
75
|
+
"cleanup:tauri:sidecars": "node scripts/cleanup-tauri-sidecars.js",
|
|
76
|
+
"docs:diataxis:check": "node scripts/verify-diataxis-map.js",
|
|
77
|
+
"docs:site:build": "node scripts/run-mkdocs.js build --config-file mkdocs.yml",
|
|
78
|
+
"docs:site:serve": "node scripts/run-mkdocs.js serve --config-file mkdocs.yml"
|
|
76
79
|
},
|
|
77
80
|
"pkg": {
|
|
78
81
|
"scripts": [
|
|
@@ -84,12 +87,12 @@
|
|
|
84
87
|
"graph_data.json"
|
|
85
88
|
]
|
|
86
89
|
},
|
|
87
|
-
"files": [
|
|
88
|
-
"dist/src",
|
|
89
|
-
"dist/amdgpu",
|
|
90
|
-
"LICENSE",
|
|
91
|
-
"README.md"
|
|
92
|
-
],
|
|
90
|
+
"files": [
|
|
91
|
+
"dist/src",
|
|
92
|
+
"dist/amdgpu",
|
|
93
|
+
"LICENSE",
|
|
94
|
+
"README.md"
|
|
95
|
+
],
|
|
93
96
|
"keywords": [
|
|
94
97
|
"knowledge-graph",
|
|
95
98
|
"visualization",
|