local-knowledge-graph 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/HELP.md CHANGED
@@ -72,6 +72,7 @@ kg
72
72
  - **语义检索**:配置硅基流动 API Key 后支持语义+关键词混合检索(未配置时自动退化为关键词模式)
73
73
  - **最短路径 / 中心子图 / 推理**:挖掘实体间关联链路、层级范围与隐性关系
74
74
  - **撤销**:Ctrl+Z 或按钮,可连续撤销
75
+ - **浅色主题**:视图设置 → 界面主题 → 浅色,整站配色与 3D 画布同步切换,选择本地记忆
75
76
  - **保存点与回溯**:版本页签打保存点,随时回滚;每次保存点自动滚动备份到 `data/backups/`
76
77
  - **图片绑定**:实体信息卡上传本地图片
77
78
  - **导出**:RDF(Turtle)、整库 .db、单文件只读网页版
@@ -96,11 +97,16 @@ kg
96
97
  **Q:数据会上传到云端吗?**
97
98
  图谱数据只存在本机。仅当你在"检索"页签配置了语义检索 API Key 时,检索向量化的文本片段才会发往你配置的模型服务;OpenCode 补全公开知识时按其自身机制联网。其余全部本地完成。
98
99
 
100
+ **Q:GitHub 仓库和 npm 包版本如何保持一致?**
101
+ 仓库内置 GitHub Actions(`.github/workflows/publish.yml`):只要推送 `v*` 格式的标签,流水线会自动校验"标签版本 = package.json 版本"→ 跑全部测试 → 发布 npm → 创建 GitHub Release。即:改 package.json 版本 → 提交 → 打同名 tag → push,两端自动同步,人工只需打一次标签。
102
+
99
103
  **Q:如何回滚某次误操作?**
100
104
  优先用「撤销」(可连续);需要回到更早状态时在"版本"页签回溯到对应保存点。
101
105
 
102
106
  ## 版本历史摘要
103
107
 
108
+ - **v1.6.0**:界面主题新增浅色配色(视图设置一键切换,本地记忆);GitHub 仓库与 npm 包通过 Actions 自动同步(打 tag 即测试+发布+Release)
109
+
104
110
  - **v1.5.0**:npm 包化(`npm i -g local-knowledge-graph` + `kg` 一键安装启动);数据目录迁至用户目录(升级无损);应用内更新支持 npm 与 git 双模式
105
111
 
106
112
  - **v1.4.0**:设置与帮助面板内置版本号、检查更新、一键更新;项目目录新增本帮助文档
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "local-knowledge-graph",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "本地知识图谱整合器:3D可视化 + AI对话建图 + 智能检索 + Git回溯 + RDF合规,全流程本地运行",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/app.js CHANGED
@@ -111,6 +111,30 @@ function makeRingSprite(cssColor, isCenter) {
111
111
  return sprite;
112
112
  }
113
113
 
114
+ /* ================= 界面主题(深色/浅色,独立于3D样式主题) ================= */
115
+ const UI_THEME_KEY = 'kg_ui_theme_v1';
116
+ const UI_THEMES = {
117
+ dark: { grid1: 0x1c2a47, grid2: 0x141e35 },
118
+ light: { grid1: 0xb7c6dd, grid2: 0xcdd9ea },
119
+ };
120
+ let UI_THEME = 'dark';
121
+ try { if (localStorage.getItem(UI_THEME_KEY) === 'light') UI_THEME = 'light'; } catch (_) {}
122
+ document.documentElement.dataset.theme = UI_THEME;
123
+
124
+ function applyTheme(mode) {
125
+ UI_THEME = UI_THEMES[mode] ? mode : 'dark';
126
+ document.documentElement.dataset.theme = UI_THEME;
127
+ try { localStorage.setItem(UI_THEME_KEY, UI_THEME); } catch (_) {}
128
+ if (typeof scene !== 'undefined' && typeof grid !== 'undefined' && grid) {
129
+ const t = UI_THEMES[UI_THEME];
130
+ scene.remove(grid);
131
+ grid.dispose();
132
+ grid = new THREE.GridHelper(480, 48, t.grid1, t.grid2);
133
+ grid.position.y = -60;
134
+ scene.add(grid);
135
+ }
136
+ }
137
+
114
138
  /* ================= Three.js 场景 ================= */
115
139
  const wrap = $('canvas-wrap');
116
140
  const scene = new THREE.Scene();
@@ -129,9 +153,10 @@ scene.add(new THREE.AmbientLight(0xffffff, 0.55));
129
153
  const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
130
154
  dirLight.position.set(120, 200, 100);
131
155
  scene.add(dirLight);
132
- const grid = new THREE.GridHelper(480, 48, 0x1c2a47, 0x141e35);
156
+ let grid = new THREE.GridHelper(480, 48, 0x1c2a47, 0x141e35);
133
157
  grid.position.y = -60;
134
158
  scene.add(grid);
159
+ applyTheme(UI_THEME); // 按存储的主题重建网格(CSS背景经 data-theme 生效)
135
160
 
136
161
  let nodeGroup = new THREE.Group();
137
162
  let linkGroup = new THREE.Group();
@@ -1292,6 +1317,13 @@ function renderStylePanel() {
1292
1317
  </div>`).join('');
1293
1318
  p.innerHTML = `
1294
1319
  <h4>视图设置<span id="style-close" title="关闭">x</span></h4>
1320
+ <div class="style-sec">界面主题</div>
1321
+ <div class="style-row"><span class="sname">配色</span>
1322
+ <select id="ui-theme-select" style="flex:1">
1323
+ <option value="dark">深色(默认)</option>
1324
+ <option value="light">浅色</option>
1325
+ </select>
1326
+ </div>
1295
1327
  <div class="style-sec">实体 · 颜色 / 大小倍率</div>${entRows}
1296
1328
  <div class="style-sec">关系 · 颜色 / 虚线 / 段长 / 间隔 / 不透明度</div>${relRows}
1297
1329
  <div class="row">
@@ -1300,6 +1332,9 @@ function renderStylePanel() {
1300
1332
  </div>
1301
1333
  <div class="hint-text">修改即时生效并应用于3D画布与图例;「保存主题」写入浏览器本地存储。受WebGL限制线宽恒为1px,虚线可通过段长/间隔调节密度。</div>`;
1302
1334
  $('style-close').addEventListener('click', () => $('style-panel').classList.remove('show'));
1335
+ const themeSel = $('ui-theme-select');
1336
+ themeSel.value = UI_THEME;
1337
+ themeSel.addEventListener('change', () => { applyTheme(themeSel.value); toast(themeSel.value === 'light' ? '已切换浅色主题' : '已切换深色主题'); });
1303
1338
  $('style-save').addEventListener('click', () => {
1304
1339
  localStorage.setItem(THEME_KEY, JSON.stringify(currentThemeJson()));
1305
1340
  toast('主题已保存到本地');
package/public/style.css CHANGED
@@ -372,3 +372,112 @@
372
372
  @media (max-width: 768px) {
373
373
  #help-panel { width: calc(100vw - 24px); right: 12px; }
374
374
  }
375
+
376
+ /* ================= 浅色主题(html[data-theme="light"]) ================= */
377
+ html[data-theme="light"] body { background: #f2f6fc; color: #22304c; }
378
+ html[data-theme="light"] header { background: #ffffff; border-bottom-color: #dfe7f2; }
379
+ html[data-theme="light"] header h1 { color: #0b66c3; }
380
+ html[data-theme="light"] header .badge { color: #5a6c8c; border-color: #c9d6ea; }
381
+ html[data-theme="light"] header .badge.ok { color: #1a7f42; border-color: #8fd4a866; }
382
+ html[data-theme="light"] header .badge.off { color: #b25f0a; border-color: #e8b47866; }
383
+ html[data-theme="light"] button { background: #eaf1fb; color: #1f3f6e; border-color: #b9cfe9; }
384
+ html[data-theme="light"] button:hover { background: #d9e8fa; }
385
+ html[data-theme="light"] button.primary { background: #1f6feb; border-color: #388bfd; color: #ffffff; }
386
+ html[data-theme="light"] button.primary:hover { background: #2b7bff; }
387
+ html[data-theme="light"] button.danger { background: #fdeef2; border-color: #f0c2cd; color: #b3405a; }
388
+ html[data-theme="light"] button.danger:hover { background: #fadde5; }
389
+ html[data-theme="light"] #backdrop { background: rgba(30, 45, 70, 0.35); }
390
+ html[data-theme="light"] .panel { background: #ffffff; border-right-color: #dfe7f2; }
391
+ html[data-theme="light"] .panel.right { border-left-color: #dfe7f2; }
392
+ html[data-theme="light"] .tabs { border-bottom-color: #dfe7f2; }
393
+ html[data-theme="light"] .tabs div { color: #5a6c8c; }
394
+ html[data-theme="light"] .tabs div.active { color: #0b66c3; border-bottom-color: #1f6feb; background: #eaf3ff; }
395
+ html[data-theme="light"] #btn-inference.active { background: #f1eafa; border-color: #7c4dbc; color: #7c4dbc; }
396
+ html[data-theme="light"] .tabbody::-webkit-scrollbar-thumb, html[data-theme="light"] .messages::-webkit-scrollbar-thumb { background: #c4d3e8; }
397
+ html[data-theme="light"] .card { background: #f7fafd; border-color: #e2eaf5; }
398
+ html[data-theme="light"] .card h3 { color: #48608a; }
399
+ html[data-theme="light"] label { color: #5a6c8c; }
400
+ html[data-theme="light"] input, html[data-theme="light"] select, html[data-theme="light"] textarea {
401
+ background: #ffffff; border-color: #c9d6ea; color: #22304c;
402
+ }
403
+ html[data-theme="light"] .list-item { background: #ffffff; border-color: #e2eaf5; }
404
+ html[data-theme="light"] .list-item .name { color: #22304c; }
405
+ html[data-theme="light"] .list-item .sub { color: #7286a8; }
406
+ html[data-theme="light"] #canvas-wrap { background: radial-gradient(ellipse at center, #f2f6fc 0%, #dde8f5 100%); }
407
+ html[data-theme="light"] .legend { background: rgba(255, 255, 255, 0.88); border-color: #d5e1f0; color: #4a5f85; }
408
+ html[data-theme="light"] .legend b { color: #22304c; }
409
+ html[data-theme="light"] #info-card { background: rgba(255, 255, 255, 0.95); border-color: #c9d6ea; }
410
+ html[data-theme="light"] #info-card h4 { color: #0b66c3; }
411
+ html[data-theme="light"] #info-card .kv { color: #4a5f85; }
412
+ html[data-theme="light"] #info-card .kv b { color: #22304c; }
413
+ html[data-theme="light"] #empty-hint { color: #8aa0c4; }
414
+ html[data-theme="light"] #ego-bar { background: rgba(255, 255, 255, 0.95); border-color: #c9d6ea; }
415
+ html[data-theme="light"] #ego-bar .ego-name { color: #a06b00; }
416
+ html[data-theme="light"] #ego-bar label { color: #5a6c8c; }
417
+ html[data-theme="light"] #ego-bar .ego-hint { color: #8598b8; }
418
+ html[data-theme="light"] #img-grid img { border-color: #c9d6ea; background: #ffffff; }
419
+ html[data-theme="light"] #lightbox { background: rgba(240, 245, 252, 0.94); }
420
+ html[data-theme="light"] #lightbox img { background: #ffffff; }
421
+ html[data-theme="light"] #lightbox figcaption { color: #22304c; }
422
+ html[data-theme="light"] #lightbox .lb-close { background: rgba(255, 255, 255, 0.92); }
423
+ html[data-theme="light"] .msg .who { color: #7286a8; }
424
+ html[data-theme="light"] .msg .bubble { background: #f7fafd; border-color: #e2eaf5; }
425
+ html[data-theme="light"] .msg.user .bubble { background: #eaf3ff; border-color: #bcd7f7; }
426
+ html[data-theme="light"] .chip { background: #e6f7ec; color: #1a7f42; border-color: #8fd4a855; }
427
+ html[data-theme="light"] .chip.warn { background: #fdf1e3; color: #a3620a; border-color: #e8b47855; }
428
+ html[data-theme="light"] .chat-input { border-top-color: #dfe7f2; }
429
+ html[data-theme="light"] .hint-text { color: #8598b8; }
430
+ html[data-theme="light"] #file-chip { background: #eaf3ff; border-color: #9fc1ea; }
431
+ html[data-theme="light"] #file-chip-name { color: #0b66c3; }
432
+ html[data-theme="light"] .report { color: #4a5f85; background: #f2f7fd; border-color: #e2eaf5; }
433
+ html[data-theme="light"] .report b { color: #1a7f42; }
434
+ html[data-theme="light"] .report .rerr { color: #a3620a; }
435
+ html[data-theme="light"] #toast { background: #ffffff; border-color: #9fc1ea; color: #22304c; box-shadow: 0 4px 16px rgba(30, 50, 80, 0.18); }
436
+ html[data-theme="light"] #toast.err { border-color: #f0c2cd; color: #b3405a; background: #fdf1f4; }
437
+ html[data-theme="light"] .log-item { border-bottom-color: #e7eef8; }
438
+ html[data-theme="light"] .log-item .l1 { color: #48608a; }
439
+ html[data-theme="light"] .log-item .l2 { color: #7286a8; }
440
+ html[data-theme="light"] .ver-item { background: #ffffff; border-color: #e2eaf5; }
441
+ html[data-theme="light"] .ver-item code { color: #0b66c3; }
442
+ html[data-theme="light"] .ver-item .v2 { color: #33456b; }
443
+ html[data-theme="light"] .ver-item .v3 { color: #7286a8; }
444
+ html[data-theme="light"] #style-panel, html[data-theme="light"] #help-panel,
445
+ html[data-theme="light"] #path-panel, html[data-theme="light"] #tooltip3d {
446
+ background: rgba(255, 255, 255, 0.97); border-color: #c9d6ea;
447
+ box-shadow: 0 4px 16px rgba(30, 50, 80, 0.16);
448
+ }
449
+ html[data-theme="light"] .style-row .sname { color: #22304c; }
450
+ html[data-theme="light"] .style-sec { color: #48608a; }
451
+ html[data-theme="light"] .hp-head span:last-child:hover { color: #22304c; }
452
+ html[data-theme="light"] .hp-body h3 { color: #0b66c3; border-bottom-color: #e2eaf5; }
453
+ html[data-theme="light"] .hp-p, html[data-theme="light"] .hp-list { color: #33456b; }
454
+ html[data-theme="light"] .hp-list b { color: #22304c; }
455
+ html[data-theme="light"] .hp-line { color: #5a6c8c; }
456
+ html[data-theme="light"] .upd-box { border-color: #e2eaf5; background: #f2f7fd; }
457
+ html[data-theme="light"] .upd-ver { color: #22304c; }
458
+ html[data-theme="light"] .upd-ver b { color: #0b66c3; }
459
+ html[data-theme="light"] .upd-status { color: #5a6c8c; }
460
+ html[data-theme="light"] .ask-chip { border-color: #c9d6ea; color: #4a5f85; background: #eef3fa; }
461
+ html[data-theme="light"] .ask-chip.ok { border-color: #8fd4a8; color: #1a7f42; background: #e6f7ec; }
462
+ html[data-theme="light"] .ask-chip.timeout { border-color: #e8b478; color: #a3620a; background: #fdf1e3; }
463
+ html[data-theme="light"] .ask-chip.err, html[data-theme="light"] .ask-chip.degraded { border-color: #f0c2cd; color: #b3405a; background: #fdeef2; }
464
+ html[data-theme="light"] .ask-sec { color: #5a6c8c; border-bottom-color: #e2eaf5; }
465
+ html[data-theme="light"] .ask-synth { color: #33456b; background: #f2f7fd; border-color: #dfe7f2; }
466
+ html[data-theme="light"] .syn-ref { color: #0b66c3; border-bottom-color: #0b66c388; }
467
+ html[data-theme="light"] .tooltip3d, html[data-theme="light"] #tooltip3d { color: #22304c; }
468
+ html[data-theme="light"] #tooltip3d b { color: #0b66c3; }
469
+ html[data-theme="light"] #tooltip3d .tt-cat { color: #5a6c8c; }
470
+ html[data-theme="light"] #tooltip3d .tt-attr { color: #48608a; }
471
+ html[data-theme="light"] #path-panel .p-head { color: #0b66c3; }
472
+ html[data-theme="light"] #path-panel .p-head #path-close { color: #5a6c8c; }
473
+ html[data-theme="light"] #path-panel .p-ent { background: #eaf3ff; border-color: #c9d6ea; color: #22304c; }
474
+ html[data-theme="light"] #path-panel .p-ent:hover { border-color: #0b66c3; }
475
+ html[data-theme="light"] #path-panel .p-rel { color: #5a729c; }
476
+ html[data-theme="light"] .log-filter select, html[data-theme="light"] .log-filter input {
477
+ background: #ffffff; border-color: #c9d6ea; color: #22304c;
478
+ }
479
+ html[data-theme="light"] #file-menu { background: #ffffff; border-color: #c9d6ea; box-shadow: 0 8px 24px rgba(30, 50, 80, 0.18); }
480
+ html[data-theme="light"] #file-menu button { color: #22304c; border-bottom-color: #edf2f9; }
481
+ html[data-theme="light"] #file-menu button:active { background: #eaf3ff; }
482
+ html[data-theme="light"] .m-btn { background: #eaf3ff; }
483
+ html[data-theme="light"] #m-legend { background: rgba(255, 255, 255, 0.92); }