scream-code 0.8.8 → 0.8.9

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.
@@ -122344,7 +122344,27 @@ const dictionaries = {
122344
122344
  "badge.loop": "循环",
122345
122345
  "badge.goal": "目标",
122346
122346
  "badge.auto": "自动",
122347
- "badge.yes": "YES"
122347
+ "badge.yes": "YES",
122348
+ "kw.title": "知识图谱",
122349
+ "kw.entity": "实体 ",
122350
+ "kw.event": "事件 ",
122351
+ "kw.relation": "关系 ",
122352
+ "kw.search_placeholder": "搜索节点…",
122353
+ "kw.btn_reset": "重置",
122354
+ "kw.btn_expand": "展开",
122355
+ "kw.hint_drag": "拖拽平移 · 滚轮缩放 · 单击展开 · 双击查看详情",
122356
+ "kw.loading": "加载中…",
122357
+ "kw.back": "返回",
122358
+ "kw.detail_related": "关联",
122359
+ "kw.detail_description": "描述",
122360
+ "kw.detail_category": "分类",
122361
+ "kw.detail_keywords": "关键词",
122362
+ "kw.loading_timeout": "加载超时",
122363
+ "kw.no_data": "暂无数据",
122364
+ "kw.no_data_hint": "请先用 /knowledge 导入文档",
122365
+ "kw.lang_toggle": "English",
122366
+ "kw.embedding_downloading": "向量模型下载中…",
122367
+ "kw.embedding_ready": "向量模型已就绪"
122348
122368
  },
122349
122369
  en: {
122350
122370
  "status.not_set": "Not set",
@@ -123403,7 +123423,27 @@ const dictionaries = {
123403
123423
  "badge.loop": "Loop",
123404
123424
  "badge.goal": "Goal",
123405
123425
  "badge.auto": "Auto",
123406
- "badge.yes": "YES"
123426
+ "badge.yes": "YES",
123427
+ "kw.title": "Knowledge Graph",
123428
+ "kw.entity": "Entities ",
123429
+ "kw.event": "Events ",
123430
+ "kw.relation": "Relations ",
123431
+ "kw.search_placeholder": "Search nodes…",
123432
+ "kw.btn_reset": "Reset",
123433
+ "kw.btn_expand": "Expand",
123434
+ "kw.hint_drag": "Drag to pan · Scroll to zoom · Click to expand · Double-click for details",
123435
+ "kw.loading": "Loading…",
123436
+ "kw.back": "Back",
123437
+ "kw.detail_related": "Related",
123438
+ "kw.detail_description": "Description",
123439
+ "kw.detail_category": "Category",
123440
+ "kw.detail_keywords": "Keywords",
123441
+ "kw.loading_timeout": "Loading timed out",
123442
+ "kw.no_data": "No data",
123443
+ "kw.no_data_hint": "Please ingest documents with /knowledge first",
123444
+ "kw.lang_toggle": "中文",
123445
+ "kw.embedding_downloading": "Vector model downloading…",
123446
+ "kw.embedding_ready": "Vector model ready"
123407
123447
  }
123408
123448
  };
123409
123449
  function detectSystemLocale() {
@@ -124856,7 +124896,7 @@ function optionalBuildString(value) {
124856
124896
  return typeof value === "string" && value.length > 0 ? value : void 0;
124857
124897
  }
124858
124898
  const SCREAM_BUILD_INFO = {
124859
- version: optionalBuildString("0.8.8"),
124899
+ version: optionalBuildString("0.8.9"),
124860
124900
  channel: optionalBuildString(""),
124861
124901
  commit: optionalBuildString(""),
124862
124902
  buildTarget: optionalBuildString("darwin-arm64")
@@ -136483,6 +136523,11 @@ function openUrl(url) {
136483
136523
  //#endregion
136484
136524
  //#region src/tui/commands/knowledge-store.ts
136485
136525
  let knowledgeStoreInstance;
136526
+ let embeddingEngineInstance;
136527
+ let embeddingStatus = "loading";
136528
+ let loadPromise;
136529
+ let embeddingRetryTimer;
136530
+ const EMBEDDING_RETRY_MS = 300 * 1e3;
136486
136531
  function getEmbeddingCacheDir() {
136487
136532
  const dir = join(getDataDir(), "cache", "fastembed");
136488
136533
  mkdirSync(dir, { recursive: true });
@@ -136492,10 +136537,51 @@ async function getKnowledgeStore() {
136492
136537
  if (knowledgeStoreInstance === void 0) {
136493
136538
  knowledgeStoreInstance = new KnowledgeStore(getDataDir());
136494
136539
  await knowledgeStoreInstance.init();
136495
- knowledgeStoreInstance.setEmbeddingEngine(createFastEmbedEngine(getEmbeddingCacheDir()));
136540
+ embeddingEngineInstance = createFastEmbedEngine(getEmbeddingCacheDir());
136541
+ knowledgeStoreInstance.setEmbeddingEngine(embeddingEngineInstance);
136542
+ loadPromise = triggerEmbeddingLoad();
136496
136543
  }
136497
136544
  return knowledgeStoreInstance;
136498
136545
  }
136546
+ function getEmbeddingStatus() {
136547
+ return embeddingStatus;
136548
+ }
136549
+ async function triggerEmbeddingLoad() {
136550
+ if (embeddingEngineInstance === void 0) return;
136551
+ embeddingStatus = "loading";
136552
+ if (await embeddingEngineInstance.ensureReady()) {
136553
+ embeddingStatus = "ready";
136554
+ stopRetryTimer();
136555
+ return;
136556
+ }
136557
+ embeddingStatus = "failed";
136558
+ startRetryTimer();
136559
+ }
136560
+ function startRetryTimer() {
136561
+ if (embeddingRetryTimer !== void 0) return;
136562
+ embeddingRetryTimer = setInterval(() => {
136563
+ loadPromise = triggerEmbeddingLoad();
136564
+ }, EMBEDDING_RETRY_MS);
136565
+ embeddingRetryTimer.unref?.();
136566
+ }
136567
+ function stopRetryTimer() {
136568
+ if (embeddingRetryTimer !== void 0) {
136569
+ clearInterval(embeddingRetryTimer);
136570
+ embeddingRetryTimer = void 0;
136571
+ }
136572
+ }
136573
+ /**
136574
+ * Wait for the embedding model to become ready.
136575
+ * Returns the current status. Callers can compare against 'ready'.
136576
+ * On failure the cached promise is cleared so subsequent calls retry.
136577
+ */
136578
+ async function waitForEmbedding() {
136579
+ if (loadPromise !== void 0) await loadPromise;
136580
+ return embeddingStatus;
136581
+ }
136582
+ function ensureEmbeddingReady() {
136583
+ if (loadPromise === void 0) loadPromise = getKnowledgeStore().then(() => triggerEmbeddingLoad());
136584
+ }
136499
136585
  //#endregion
136500
136586
  //#region src/tui/commands/knowledge-web.ts
136501
136587
  /**
@@ -136519,7 +136605,7 @@ const HTML = `<!doctype html>
136519
136605
  <head>
136520
136606
  <meta charset="utf-8">
136521
136607
  <meta name="viewport" content="width=device-width,initial-scale=1">
136522
- <title>__MSG_kw_title__</title>
136608
+ <title data-msg="kw_title">__MSG_kw_title__</title>
136523
136609
  <style>
136524
136610
  :root{
136525
136611
  --bg:#fafafa;--bg-soft:#f0f0f0;
@@ -136543,7 +136629,7 @@ body{background:var(--bg)}
136543
136629
  transition:color .2s;
136544
136630
  }
136545
136631
  .label-3d.root{color:var(--ink);font-weight:600;font-size:11px}
136546
- .label-3d.selected{color:var(--accent);font-weight:600;background:rgba(255,255,255,.95)}
136632
+ .label-3d.selected{color:#0d7a3e;font-weight:600;background:rgba(255,255,255,.95)}
136547
136633
  .label-3d.hover{color:var(--ink);background:rgba(255,255,255,.95)}
136548
136634
 
136549
136635
  #toolbar{
@@ -136653,27 +136739,43 @@ body{background:var(--bg)}
136653
136739
  <canvas id="scene"></canvas>
136654
136740
  <div id="labels"></div>
136655
136741
  <div id="toolbar">
136656
- <span class="chip">__MSG_kw_entity__<b id="stat-ent">0</b></span>
136657
- <span class="chip">__MSG_kw_event__<b id="stat-evt">0</b></span>
136658
- <span class="chip">__MSG_kw_relation__<b id="stat-edg">0</b></span>
136742
+ <span class="chip"><span data-msg="kw_entity">__MSG_kw_entity__</span><b id="stat-ent">0</b></span>
136743
+ <span class="chip"><span data-msg="kw_event">__MSG_kw_event__</span><b id="stat-evt">0</b></span>
136744
+ <span class="chip"><span data-msg="kw_relation">__MSG_kw_relation__</span><b id="stat-edg">0</b></span>
136659
136745
  <span class="sep"></span>
136660
136746
  <div id="search-wrap">
136661
136747
  <svg id="search-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
136662
136748
  <input id="search-input" type="text" placeholder="__MSG_kw_search_placeholder__" autocomplete="off" spellcheck="false">
136663
136749
  </div>
136664
136750
  <span class="sep"></span>
136665
- <button id="btn-reset">__MSG_kw_btn_reset__</button>
136666
- <button id="btn-expand">__MSG_kw_btn_expand__</button>
136751
+ <button id="btn-reset" data-msg="kw_btn_reset">__MSG_kw_btn_reset__</button>
136752
+ <button id="btn-expand" data-msg="kw_btn_expand">__MSG_kw_btn_expand__</button>
136753
+ <span class="sep"></span>
136754
+ <button id="btn-lang">__MSG_kw_lang_toggle__</button>
136667
136755
  </div>
136668
- <div id="hint"><b>__MSG_kw_hint_drag__</b></div>
136756
+ <div id="hint"><b data-msg="kw_hint_drag">__MSG_kw_hint_drag__</b></div>
136669
136757
  <div id="tooltip"></div>
136670
136758
  <div id="minimap"><canvas id="minimap-canvas"></canvas></div>
136671
136759
  <div id="modal-mask"></div>
136672
136760
  <div id="modal"><button class="close" id="btn-close">&times;</button><div id="modal-body"></div></div>
136673
- <div id="loading"><span class="dot"></span><span class="dot"></span><span class="dot"></span>__MSG_kw_loading__</div>
136761
+ <div id="loading"><span class="dot"></span><span class="dot"></span><span class="dot"></span><span data-msg="kw_loading">__MSG_kw_loading__</span></div>
136674
136762
  <div id="error-msg"></div>
136675
136763
 
136676
136764
  <script>
136765
+ var DICT=__DICT_INJECT__;
136766
+ var curLang='__LOCALE_INJECT__';
136767
+ function M(k){return(DICT[curLang]&&DICT[curLang][k])||k}
136768
+ function applyLang(){
136769
+ document.title=M('kw_title');
136770
+ document.querySelectorAll('[data-msg]').forEach(function(el){el.textContent=M(el.dataset.msg)});
136771
+ var ph=document.getElementById('search-input');if(ph)ph.placeholder=M('kw_search_placeholder');
136772
+ document.getElementById('btn-lang').textContent=M('kw_lang_toggle');
136773
+ }
136774
+ function toggleLang(){
136775
+ curLang=curLang==='zh'?'en':'zh';
136776
+ applyLang();
136777
+ if(typeof render==='function')render();
136778
+ }
136677
136779
  (function(){
136678
136780
  'use strict';
136679
136781
 
@@ -136682,7 +136784,7 @@ var TYPE_COLORS={
136682
136784
  product:'#db2777',metric:'#9333ea',action:'#dc2626',work:'#7c3aed',
136683
136785
  group:'#4f46e5',subject:'#0d9488',tags:'#666'
136684
136786
  };
136685
- var INK='#1a1a1a',ACCENT='#0066ff',EVENT='#9333ea';
136787
+ var INK='#1a1a1a',ACCENT='#0066ff',EVENT='#9333ea',SELECTED='#0d7a3e';
136686
136788
 
136687
136789
  // ─── Canvas ──────────────────────────────────────────
136688
136790
  var canvas=document.getElementById('scene');
@@ -136701,7 +136803,7 @@ window.addEventListener('resize',resize);
136701
136803
 
136702
136804
  // 相机
136703
136805
  var camRotX=0.25,camRotY=0.4;
136704
- var camDist=700;
136806
+ var camDist=500;
136705
136807
  var camTargetX=0,camTargetY=0,camTargetZ=0;
136706
136808
  var fov=600;
136707
136809
 
@@ -136891,10 +136993,10 @@ function drawEdges(projMap,connSet){
136891
136993
  var inConn=!connSet||connSet.has(e.entityId)&&connSet.has(e.eventId);
136892
136994
  var opacity;
136893
136995
  if(isDir)opacity=0.9;
136894
- else if(connSet&&!inConn)opacity=0.03;
136895
- else opacity=0.18;
136996
+ else if(connSet&&!inConn)opacity=0.08;
136997
+ else opacity=0.35;
136896
136998
  ctx.strokeStyle='rgba(0,0,0,'+opacity+')';
136897
- ctx.lineWidth=isDir?1.2:0.6;
136999
+ ctx.lineWidth=isDir?1.5:0.8;
136898
137000
  ctx.beginPath();
136899
137001
  ctx.moveTo(pa.x,pa.y);
136900
137002
  ctx.lineTo(pb.x,pb.y);
@@ -136909,21 +137011,22 @@ function drawNode(p,color,radius,isRoot,isSelected,isHover,isDimmed,isMatch){
136909
137011
  if(r<1){r=1;}
136910
137012
  var alpha=isDimmed?0.2:1;
136911
137013
 
137014
+ var nodeColor=isSelected?SELECTED:color;
137015
+
136912
137016
  if(isRoot){
136913
- // root 节点稍大,带细环
136914
- ctx.fillStyle='rgba(0,0,0,'+alpha+')';
137017
+ ctx.fillStyle=isSelected?'rgba(13,122,62,'+alpha+')':'rgba(0,0,0,'+alpha+')';
136915
137018
  ctx.beginPath();ctx.arc(p.x,p.y,r,0,Math.PI*2);ctx.fill();
136916
- ctx.strokeStyle='rgba(0,0,0,'+(0.3*alpha)+')';
137019
+ ctx.strokeStyle=isSelected?'rgba(13,122,62,'+(0.5*alpha)+')':'rgba(0,0,0,'+(0.3*alpha)+')';
136917
137020
  ctx.lineWidth=0.8;
136918
137021
  ctx.beginPath();ctx.arc(p.x,p.y,r+4,0,Math.PI*2);ctx.stroke();
136919
137022
  }else{
136920
- ctx.fillStyle='rgba(0,0,0,'+(0.7*alpha)+')';
137023
+ ctx.fillStyle=isSelected?'rgba(13,122,62,'+(0.9*alpha)+')':'rgba(0,0,0,'+(0.7*alpha)+')';
136921
137024
  ctx.beginPath();ctx.arc(p.x,p.y,r,0,Math.PI*2);ctx.fill();
136922
137025
  }
136923
137026
 
136924
- // 选中:蓝色细环
137027
+ // 选中:深绿色环
136925
137028
  if(isSelected){
136926
- ctx.strokeStyle=ACCENT;
137029
+ ctx.strokeStyle=SELECTED;
136927
137030
  ctx.lineWidth=1.2;
136928
137031
  ctx.beginPath();ctx.arc(p.x,p.y,r+5,0,Math.PI*2);ctx.stroke();
136929
137032
  }
@@ -136965,7 +137068,7 @@ function drawNodes(projMap,connSet){
136965
137068
  if(it.type==='entity'){
136966
137069
  var ent=it.data;
136967
137070
  var color=TYPE_COLORS[ent.type]||INK;
136968
- var radius=ent.isRoot?5:2.5+Math.min(2.5,(ent.eventCount||0)*0.3);
137071
+ var radius=ent.isRoot?6:3+Math.min(4,(ent.eventCount||0)*0.5);
136969
137072
  var isSel=selId===ent.id;
136970
137073
  var isHov=hoveredId===ent.id;
136971
137074
  var isDim=(connSet&&!connSet.has(ent.id))||(searchMatches&&!searchMatches.has(ent.id)&&ent.id!==selId);
@@ -136977,7 +137080,7 @@ function drawNodes(projMap,connSet){
136977
137080
  var isHov=hoveredId===ev.id;
136978
137081
  var isDim=(connSet&&!connSet.has(ev.id))||(searchMatches&&!searchMatches.has(ev.id)&&ev.id!==selId);
136979
137082
  var isMatch=searchMatches&&searchMatches.has(ev.id);
136980
- drawNode(it.p,EVENT,3,false,isSel,isHov,isDim,isMatch);
137083
+ drawNode(it.p,EVENT,4,false,isSel,isHov,isDim,isMatch);
136981
137084
  }
136982
137085
  }
136983
137086
  }
@@ -137089,7 +137192,7 @@ function showTooltip(sx,sy,id){
137089
137192
  if(data.kind==='entity'){
137090
137193
  var e=eById[id];if(!e)return;
137091
137194
  html+='<div class="tt-name">'+esc(e.name)+'</div>';
137092
- html+='<div class="tt-meta">'+esc(e.type)+' · '+(e.eventCount||0)+' __MSG_kw_event__</div>';
137195
+ html+='<div class="tt-meta">'+esc(e.type)+' · '+(e.eventCount||0)+' '+M('kw_event')+'</div>';
137093
137196
  }else{
137094
137197
  var ev=evById[id];if(!ev)return;
137095
137198
  html+='<div class="tt-name">'+esc(ev.title)+'</div>';
@@ -137132,6 +137235,7 @@ function pickNode(sx,sy){
137132
137235
  }
137133
137236
 
137134
137237
  canvas.addEventListener('mousedown',function(e){
137238
+ stopAutoRotate();
137135
137239
  mouseDownPos={x:e.clientX,y:e.clientY};
137136
137240
  isDragging=false;
137137
137241
  dragStart={x:e.clientX,y:e.clientY,rotX:camRotX,rotY:camRotY,panX:camTargetX,panY:camTargetY};
@@ -137165,18 +137269,26 @@ canvas.addEventListener('mousemove',function(e){
137165
137269
  }
137166
137270
  });
137167
137271
 
137272
+ var clickTimer=null,dblClickGuard=false;
137168
137273
  canvas.addEventListener('mouseup',function(e){
137169
137274
  if(isPanning){isPanning=false;canvas.style.cursor='grab';return;}
137170
137275
  if(isDragging){startInertia();canvas.style.cursor='grab';return;}
137276
+ if(dblClickGuard)return;
137171
137277
  var dx=e.clientX-mouseDownPos.x,dy=e.clientY-mouseDownPos.y;
137172
137278
  if(dx*dx+dy*dy>25)return;
137173
137279
  var id=pickNode(e.clientX,e.clientY);
137174
137280
  if(id){
137175
- var data=nodeData.find(function(n){return n.id===id});
137176
- if(data)toggleNode(id,data.kind);
137177
- selId=id;navStack=[];
137178
- focusOn(id);rebuildLabels();
137179
- if(data)showModal(id,data.kind);
137281
+ if(clickTimer){clearTimeout(clickTimer);clickTimer=null;return;}
137282
+ clickTimer=setTimeout(function(){
137283
+ clickTimer=null;
137284
+ var data=nodeData.find(function(n){return n.id===id});
137285
+ if(data){
137286
+ if(data.kind==='entity'&&!expEnt.has(id))expEnt.add(id);
137287
+ if(data.kind==='event'&&!expEv.has(id))expEv.add(id);
137288
+ }
137289
+ selId=id;navStack=[];
137290
+ focusOn(id);rebuildLabels();
137291
+ },250);
137180
137292
  }else{
137181
137293
  selId=null;rebuildLabels();closeModal();
137182
137294
  }
@@ -137185,8 +137297,19 @@ canvas.addEventListener('mouseup',function(e){
137185
137297
  canvas.addEventListener('contextmenu',function(e){e.preventDefault();});
137186
137298
  canvas.addEventListener('dblclick',function(e){
137187
137299
  e.preventDefault();
137300
+ if(clickTimer){clearTimeout(clickTimer);clickTimer=null;}
137301
+ dblClickGuard=true;
137302
+ setTimeout(function(){dblClickGuard=false;},300);
137188
137303
  var id=pickNode(e.clientX,e.clientY);
137189
- if(!id){
137304
+ if(id){
137305
+ var data=nodeData.find(function(n){return n.id===id});
137306
+ if(data){
137307
+ if(!expEnt.has(id)&&!expEv.has(id)){expEnt.add(id);}
137308
+ selId=id;navStack=[];
137309
+ focusOn(id);rebuildLabels();
137310
+ showModal(id,data.kind);
137311
+ }
137312
+ }else{
137190
137313
  expEnt.clear();expEv.clear();selId=null;
137191
137314
  if(graphData)graphData.entities.forEach(function(en){expEnt.add(en.id)});
137192
137315
  closeModal();rebuildLabels();fitView();
@@ -137194,6 +137317,7 @@ canvas.addEventListener('dblclick',function(e){
137194
137317
  });
137195
137318
 
137196
137319
  canvas.addEventListener('wheel',function(e){
137320
+ stopAutoRotate();
137197
137321
  e.preventDefault();
137198
137322
  var factor=e.deltaY>0?1.12:0.89;
137199
137323
  var newDist=Math.max(100,Math.min(2500,camDist*factor));
@@ -137240,10 +137364,42 @@ function fitView(){
137240
137364
  ids.forEach(function(id){var p=nodePositions[id];if(!p)return;if(p.x<x0)x0=p.x;if(p.x>x1)x1=p.x;if(p.y<y0)y0=p.y;if(p.y>y1)y1=p.y;if(p.z<z0)z0=p.z;if(p.z>z1)z1=p.z});
137241
137365
  var cx=(x0+x1)/2,cy=(y0+y1)/2,cz=(z0+z1)/2;
137242
137366
  var span=Math.max(x1-x0,y1-y0,z1-z0);
137243
- camTargetX=cx;camTargetY=cy;camTargetZ=cz;
137244
- camDist=Math.max(span*1.6,350);
137367
+ var endTx=cx,endTy=cy,endTz=cz;
137368
+ var endDist=Math.max(span*1.6,350);
137369
+ var startTx=camTargetX,startTy=camTargetY,startTz=camTargetZ,startDist=camDist;
137370
+ var startRotX=camRotX,startRotY=camRotY;
137371
+ var endRotX=0.25,endRotY=0.4;
137372
+ var startTime=performance.now();
137373
+ if(focusAnim)cancelAnimationFrame(focusAnim);
137374
+ function step(now){
137375
+ var t=Math.min(1,(now-startTime)/800);
137376
+ var e=t<.5?2*t*t:1-Math.pow(-2*t+2,2)/2;
137377
+ camTargetX=startTx+(endTx-startTx)*e;
137378
+ camTargetY=startTy+(endTy-startTy)*e;
137379
+ camTargetZ=startTz+(endTz-startTz)*e;
137380
+ camDist=startDist+(endDist-startDist)*e;
137381
+ camRotX=startRotX+(endRotX-startRotX)*e;
137382
+ camRotY=startRotY+(endRotY-startRotY)*e;
137383
+ if(t<1)focusAnim=requestAnimationFrame(step);
137384
+ }
137385
+ focusAnim=requestAnimationFrame(step);
137245
137386
  }
137246
137387
 
137388
+ var autoRotRAF=null;
137389
+ function startAutoRotate(){
137390
+ var duration=5000,startTime=performance.now();
137391
+ var speed=0.008;
137392
+ function step(now){
137393
+ var elapsed=now-startTime;
137394
+ var t=Math.min(1,elapsed/duration);
137395
+ var fade=1-t;
137396
+ camRotY+=speed*fade;
137397
+ if(t<1)autoRotRAF=requestAnimationFrame(step);
137398
+ }
137399
+ autoRotRAF=requestAnimationFrame(step);
137400
+ }
137401
+ function stopAutoRotate(){if(autoRotRAF){cancelAnimationFrame(autoRotRAF);autoRotRAF=null;}}
137402
+
137247
137403
  // ─── 模态详情 ────────────────────────────────────────
137248
137404
  function showModal(id,kind,pushNav){
137249
137405
  if(pushNav!==false)navStack.push({id:id,kind:kind});
@@ -137251,23 +137407,24 @@ function showModal(id,kind,pushNav){
137251
137407
  var mask=document.getElementById('modal-mask');
137252
137408
  var body=document.getElementById('modal-body');
137253
137409
  var html='';
137254
- if(navStack.length>1)html+='<button class="back-btn" id="btn-back">← __MSG_kw_back__</button>';
137410
+ if(navStack.length>1)html+='<button class="back-btn" id="btn-back">← '+M('kw_back')+'</button>';
137255
137411
  if(kind==='entity'){
137256
137412
  var e=eById[id];
137257
137413
  html+='<div class="type-tag">'+esc(e.type)+'</div>';
137258
137414
  html+='<h3>'+esc(e.name)+'</h3>';
137259
- html+='<div class="field"><div class="label">__MSG_kw_detail_related__</div><div class="value">'+(e.eventCount||0)+'</div></div>';
137415
+ if(e.description)html+='<div class="field"><div class="label">'+M('kw_detail_description')+'</div><div class="value">'+esc(e.description)+'</div></div>';
137416
+ html+='<div class="field"><div class="label">'+M('kw_detail_related')+'</div><div class="value">'+(e.eventCount||0)+'</div></div>';
137260
137417
  var ce=evByEnt[id]||[];
137261
- if(ce.length){html+='<div class="divider"></div><div class="field"><div class="label">__MSG_kw_event__</div>';ce.forEach(function(eid){var ev=evById[eid];if(ev)html+='<div class="conn-item" data-id="'+eid+'" data-kind="event"><span>'+esc(ev.title)+'</span></div>'});html+='</div>'}
137418
+ if(ce.length){html+='<div class="divider"></div><div class="field"><div class="label">'+M('kw_event')+'</div>';ce.forEach(function(eid){var ev=evById[eid];if(ev)html+='<div class="conn-item" data-id="'+eid+'" data-kind="event"><span>'+esc(ev.title)+'</span></div>'});html+='</div>'}
137262
137419
  }else{
137263
137420
  var ev=evById[id];
137264
137421
  html+='<div class="type-tag event">EVENT</div>';
137265
137422
  html+='<h3>'+esc(ev.title)+'</h3>';
137266
- if(ev.summary)html+='<div class="field"><div class="label">__MSG_kw_detail_description__</div><div class="value">'+esc(ev.summary)+'</div></div>';
137267
- if(ev.category)html+='<div class="field"><div class="label">__MSG_kw_detail_category__</div><div class="value">'+esc(ev.category)+'</div></div>';
137268
- if(ev.keywords&&ev.keywords.length)html+='<div class="field"><div class="label">__MSG_kw_detail_keywords__</div><div class="value">'+ev.keywords.map(esc).join(' · ')+'</div></div>';
137423
+ if(ev.summary)html+='<div class="field"><div class="label">'+M('kw_detail_description')+'</div><div class="value">'+esc(ev.summary)+'</div></div>';
137424
+ if(ev.category)html+='<div class="field"><div class="label">'+M('kw_detail_category')+'</div><div class="value">'+esc(ev.category)+'</div></div>';
137425
+ if(ev.keywords&&ev.keywords.length)html+='<div class="field"><div class="label">'+M('kw_detail_keywords')+'</div><div class="value">'+ev.keywords.map(esc).join(' · ')+'</div></div>';
137269
137426
  var ce2=entByEv[id]||[];
137270
- if(ce2.length){html+='<div class="divider"></div><div class="field"><div class="label">__MSG_kw_detail_related__</div>';ce2.forEach(function(eid){var e=eById[eid];if(e)html+='<div class="conn-item" data-id="'+eid+'" data-kind="entity"><span>'+esc(e.name)+'</span><span class="badge">'+esc(e.type)+'</span></div>'});html+='</div>'}
137427
+ if(ce2.length){html+='<div class="divider"></div><div class="field"><div class="label">'+M('kw_detail_related')+'</div>';ce2.forEach(function(eid){var e=eById[eid];if(e)html+='<div class="conn-item" data-id="'+eid+'" data-kind="entity"><span>'+esc(e.name)+'</span><span class="badge">'+esc(e.type)+'</span></div>'});html+='</div>'}
137271
137428
  }
137272
137429
  body.innerHTML=html;
137273
137430
  modal.classList.add('open');mask.classList.add('open');
@@ -137502,7 +137659,7 @@ setTimeout(function(){
137502
137659
  if(document.getElementById('loading').style.display!=='none'){
137503
137660
  document.getElementById('loading').style.display='none';
137504
137661
  errorMsg.style.display='block';
137505
- errorMsg.innerHTML='<div style="font-size:14px;font-weight:600;margin-bottom:8px;">__MSG_kw_loading__</div><div style="color:#666;font-size:12px;">__MSG_kw_loading_timeout__</div>';
137662
+ errorMsg.innerHTML='<div style="font-size:14px;font-weight:600;margin-bottom:8px;">'+M('kw_loading')+'</div><div style="color:#666;font-size:12px;">'+M('kw_loading_timeout')+'</div>';
137506
137663
  }
137507
137664
  },8000);
137508
137665
 
@@ -137545,12 +137702,16 @@ fetch('/api/graph').then(function(r){return r.json()}).then(function(data){
137545
137702
  rebuildLabels();
137546
137703
  fitView();
137547
137704
  animate();
137705
+ startAutoRotate();
137548
137706
  }).catch(function(err){
137549
137707
  document.getElementById('loading').style.display='none';
137550
137708
  errorMsg.style.display='block';
137551
- errorMsg.innerHTML='<div style="font-size:14px;font-weight:600;margin-bottom:8px;">__MSG_kw_no_data__</div><div style="color:#666;font-size:12px;margin-bottom:12px;">'+esc(err.message)+'</div><div style="color:#999;font-size:11px;">__MSG_kw_no_data_hint__</div>';
137709
+ errorMsg.innerHTML='<div style="font-size:14px;font-weight:600;margin-bottom:8px;">'+M('kw_no_data')+'</div><div style="color:#666;font-size:12px;margin-bottom:12px;">'+esc(err.message)+'</div><div style="color:#999;font-size:11px;">'+M('kw_no_data_hint')+'</div>';
137552
137710
  });
137553
137711
 
137712
+ applyLang();
137713
+ document.getElementById('btn-lang').addEventListener('click',toggleLang);
137714
+
137554
137715
  })();
137555
137716
  <\/script>
137556
137717
  </body>
@@ -137570,13 +137731,14 @@ async function serveGraphJSON(store, res) {
137570
137731
  eventEntityMap.set(edge.eventId, list);
137571
137732
  }
137572
137733
  const data = {
137573
- entities: entities.map(({ id, sourceId, type, name, normalizedName, eventCount }) => ({
137734
+ entities: entities.map(({ id, sourceId, type, name, normalizedName, eventCount, description }) => ({
137574
137735
  id,
137575
137736
  sourceId,
137576
137737
  type,
137577
137738
  name,
137578
137739
  normalizedName,
137579
- eventCount
137740
+ eventCount,
137741
+ description
137580
137742
  })),
137581
137743
  events: events.map(({ id, sourceId, documentId, title, rank, summary, category, keywords }) => ({
137582
137744
  id,
@@ -137605,13 +137767,55 @@ async function serveGraphJSON(store, res) {
137605
137767
  res.end(JSON.stringify({ error: String(error) }));
137606
137768
  }
137607
137769
  }
137608
- function serveHTML(res) {
137770
+ function serveHTML(res, locale) {
137609
137771
  res.writeHead(200, {
137610
137772
  "Content-Type": "text/html; charset=utf-8",
137611
137773
  "Cache-Control": "no-store"
137612
137774
  });
137613
- const html = HTML.replace(/__MSG_([a-z0-9_]+)__/g, (_, key) => t(key));
137614
- res.end(html);
137775
+ const dictJson = JSON.stringify({
137776
+ zh: {
137777
+ kw_title: "知识图谱",
137778
+ kw_entity: "实体 ",
137779
+ kw_event: "事件 ",
137780
+ kw_relation: "关系 ",
137781
+ kw_search_placeholder: "搜索节点…",
137782
+ kw_btn_reset: "重置",
137783
+ kw_btn_expand: "展开",
137784
+ kw_hint_drag: "拖拽平移 · 滚轮缩放 · 单击展开 · 双击查看详情",
137785
+ kw_loading: "加载中…",
137786
+ kw_back: "返回",
137787
+ kw_detail_related: "关联",
137788
+ kw_detail_description: "描述",
137789
+ kw_detail_category: "分类",
137790
+ kw_detail_keywords: "关键词",
137791
+ kw_loading_timeout: "加载超时",
137792
+ kw_no_data: "暂无数据",
137793
+ kw_no_data_hint: "请先用 /knowledge 导入文档",
137794
+ kw_lang_toggle: "English"
137795
+ },
137796
+ en: {
137797
+ kw_title: "Knowledge Graph",
137798
+ kw_entity: "Entities ",
137799
+ kw_event: "Events ",
137800
+ kw_relation: "Relations ",
137801
+ kw_search_placeholder: "Search nodes…",
137802
+ kw_btn_reset: "Reset",
137803
+ kw_btn_expand: "Expand",
137804
+ kw_hint_drag: "Drag to pan · Scroll to zoom · Click to expand · Double-click for details",
137805
+ kw_loading: "Loading…",
137806
+ kw_back: "Back",
137807
+ kw_detail_related: "Related",
137808
+ kw_detail_description: "Description",
137809
+ kw_detail_category: "Category",
137810
+ kw_detail_keywords: "Keywords",
137811
+ kw_loading_timeout: "Loading timed out",
137812
+ kw_no_data: "No data",
137813
+ kw_no_data_hint: "Please ingest documents with /knowledge first",
137814
+ kw_lang_toggle: "中文"
137815
+ }
137816
+ });
137817
+ const injected = HTML.replace("__DICT_INJECT__", dictJson).replace("__LOCALE_INJECT__", locale);
137818
+ res.end(injected);
137615
137819
  }
137616
137820
  async function handleWeb(host) {
137617
137821
  const store = await getKnowledgeStore();
@@ -137622,7 +137826,7 @@ async function handleWeb(host) {
137622
137826
  serveGraphJSON(store, res);
137623
137827
  return;
137624
137828
  }
137625
- serveHTML(res);
137829
+ serveHTML(res, getLocale());
137626
137830
  });
137627
137831
  registerServer(server);
137628
137832
  await new Promise((resolve, reject) => {
@@ -138324,10 +138528,18 @@ async function handleKnowledgeCommand(host, _args) {
138324
138528
  description: t("knowledge.web_desc")
138325
138529
  }
138326
138530
  ];
138531
+ const formatEmbeddingHint = (status) => {
138532
+ switch (status) {
138533
+ case "ready": return "";
138534
+ case "loading": return " · " + t("kw.embedding_downloading");
138535
+ case "failed": return " · " + t("kw.embedding_failed");
138536
+ }
138537
+ };
138327
138538
  const showMenu = () => {
138539
+ const status = getEmbeddingStatus();
138328
138540
  const picker = new ChoicePickerComponent({
138329
138541
  title: t("knowledge.menu_title"),
138330
- hint: t("knowledge.menu_hint"),
138542
+ hint: t("knowledge.menu_hint") + formatEmbeddingHint(status),
138331
138543
  options,
138332
138544
  colors: host.state.theme.colors,
138333
138545
  onSelect: (value) => {
@@ -138352,6 +138564,15 @@ async function handleKnowledgeCommand(host, _args) {
138352
138564
  }
138353
138565
  });
138354
138566
  host.mountEditorReplacement(picker);
138567
+ if (status !== "ready") {
138568
+ ensureEmbeddingReady();
138569
+ waitForEmbedding().then((finalStatus) => {
138570
+ if (finalStatus === "ready") {
138571
+ host.showStatus(t("kw.embedding_ready"));
138572
+ showMenu();
138573
+ }
138574
+ });
138575
+ }
138355
138576
  };
138356
138577
  showMenu();
138357
138578
  }
@@ -145173,10 +145394,10 @@ var ErrorBannerComponent = class {
145173
145394
  };
145174
145395
  //#endregion
145175
145396
  //#region src/tui/components/chrome/plan-mode-banner.ts
145176
- const PLAN_LABEL = {
145177
- plan: t("planmode.plan"),
145178
- fusionplan: t("planmode.fusionplan")
145179
- };
145397
+ function getPlanLabel(mode) {
145398
+ if (mode === "fusionplan") return t("planmode.fusionplan");
145399
+ return t("planmode.plan");
145400
+ }
145180
145401
  var PlanModeBannerComponent = class {
145181
145402
  mode = "off";
145182
145403
  planPath;
@@ -145196,7 +145417,7 @@ var PlanModeBannerComponent = class {
145196
145417
  render(width) {
145197
145418
  if (this.mode === "off") return [];
145198
145419
  const tone = this.mode === "fusionplan" ? this.colors.fusionPlanMode : this.colors.planMode;
145199
- const label = PLAN_LABEL[this.mode];
145420
+ const label = getPlanLabel(this.mode);
145200
145421
  const prefix = `${chalk.hex(tone)(STATUS_BULLET)}${chalk.hex(tone).bold(label)}`;
145201
145422
  const basename = this.planPath !== void 0 && this.planPath.length > 0 ? path$1.basename(this.planPath) : void 0;
145202
145423
  if (basename === void 0 || basename.length === 0) return [truncateToWidth(prefix, width)];
@@ -146042,35 +146263,39 @@ function sessionRowsForPicker(sessions, currentSessionId, currentSessionHasConte
146042
146263
  }
146043
146264
  //#endregion
146044
146265
  //#region src/tui/reverse-rpc/approval/adapter.ts
146045
- const DEFAULT_APPROVAL_CHOICES = [
146046
- {
146047
- label: t("approval.allow_once"),
146048
- response: "approved"
146049
- },
146050
- {
146051
- label: t("approval.allow_session"),
146052
- response: "approved_for_session"
146053
- },
146054
- {
146266
+ function getDefaultApprovalChoices() {
146267
+ return [
146268
+ {
146269
+ label: t("approval.allow_once"),
146270
+ response: "approved"
146271
+ },
146272
+ {
146273
+ label: t("approval.allow_session"),
146274
+ response: "approved_for_session"
146275
+ },
146276
+ {
146277
+ label: t("approval.deny"),
146278
+ response: "rejected"
146279
+ },
146280
+ {
146281
+ label: t("approval.deny_feedback"),
146282
+ response: "rejected",
146283
+ requires_feedback: true
146284
+ }
146285
+ ];
146286
+ }
146287
+ function getPlanRejectChoices() {
146288
+ return [{
146055
146289
  label: t("approval.deny"),
146056
- response: "rejected"
146057
- },
146058
- {
146059
- label: t("approval.deny_feedback"),
146060
146290
  response: "rejected",
146291
+ selected_label: t("approval.deny")
146292
+ }, {
146293
+ label: t("approval.revise"),
146294
+ response: "rejected",
146295
+ selected_label: t("approval.revise"),
146061
146296
  requires_feedback: true
146062
- }
146063
- ];
146064
- const PLAN_REJECT_CHOICES = [{
146065
- label: t("approval.deny"),
146066
- response: "rejected",
146067
- selected_label: t("approval.deny")
146068
- }, {
146069
- label: t("approval.revise"),
146070
- response: "rejected",
146071
- selected_label: t("approval.revise"),
146072
- requires_feedback: true
146073
- }];
146297
+ }];
146298
+ }
146074
146299
  function adaptApprovalRequest(event) {
146075
146300
  const resolved = resolveDisplay(event.toolName, event.display, event.action);
146076
146301
  return {
@@ -146215,42 +146440,44 @@ function describeApproval(display, action) {
146215
146440
  default: return action;
146216
146441
  }
146217
146442
  }
146218
- const DANGER_PATTERNS = [
146219
- {
146220
- pattern: /\brm\s+(-[a-zA-Z]*[rRfF][a-zA-Z]*|--recursive|--force)/i,
146221
- label: t("approval.danger.recursive_delete")
146222
- },
146223
- {
146224
- pattern: /\bsudo\b/i,
146225
- label: "sudo"
146226
- },
146227
- {
146228
- pattern: /\b(curl|wget)\b[^|]*\|\s*(sh|bash|zsh)\b/i,
146229
- label: t("approval.danger.pipe_to_shell")
146230
- },
146231
- {
146232
- pattern: /\bdd\b[^|]*\bof=/i,
146233
- label: t("approval.danger.dd_write")
146234
- },
146235
- {
146236
- pattern: /\bmkfs\b/i,
146237
- label: "mkfs"
146238
- },
146239
- {
146240
- pattern: />\s*\/dev\/(sd|nvme|disk|hd)/i,
146241
- label: t("approval.danger.raw_device")
146242
- },
146243
- {
146244
- pattern: /\bchmod\s+-R?\s*777\b/i,
146245
- label: "chmod 777"
146246
- },
146247
- {
146248
- pattern: /:\(\)\s*\{\s*:\|:&\s*\}/i,
146249
- label: t("approval.danger.fork_bomb")
146250
- }
146251
- ];
146443
+ function getDangerPatterns() {
146444
+ return [
146445
+ {
146446
+ pattern: /\brm\s+(-[a-zA-Z]*[rRfF][a-zA-Z]*|--recursive|--force)/i,
146447
+ label: t("approval.danger.recursive_delete")
146448
+ },
146449
+ {
146450
+ pattern: /\bsudo\b/i,
146451
+ label: "sudo"
146452
+ },
146453
+ {
146454
+ pattern: /\b(curl|wget)\b[^|]*\|\s*(sh|bash|zsh)\b/i,
146455
+ label: t("approval.danger.pipe_to_shell")
146456
+ },
146457
+ {
146458
+ pattern: /\bdd\b[^|]*\bof=/i,
146459
+ label: t("approval.danger.dd_write")
146460
+ },
146461
+ {
146462
+ pattern: /\bmkfs\b/i,
146463
+ label: "mkfs"
146464
+ },
146465
+ {
146466
+ pattern: />\s*\/dev\/(sd|nvme|disk|hd)/i,
146467
+ label: t("approval.danger.raw_device")
146468
+ },
146469
+ {
146470
+ pattern: /\bchmod\s+-R?\s*777\b/i,
146471
+ label: "chmod 777"
146472
+ },
146473
+ {
146474
+ pattern: /:\(\)\s*\{\s*:\|:&\s*\}/i,
146475
+ label: t("approval.danger.fork_bomb")
146476
+ }
146477
+ ];
146478
+ }
146252
146479
  function detectDanger(command) {
146253
- for (const { pattern, label } of DANGER_PATTERNS) if (pattern.test(command)) return label;
146480
+ for (const { pattern, label } of getDangerPatterns()) if (pattern.test(command)) return label;
146254
146481
  }
146255
146482
  function adaptDisplay(display) {
146256
146483
  switch (display.kind) {
@@ -146327,7 +146554,7 @@ function adaptDisplay(display) {
146327
146554
  }
146328
146555
  function adaptChoices(toolName, display) {
146329
146556
  if (toolName === "ExitPlanMode" || display.kind === "plan_review") return adaptPlanReviewChoices(display);
146330
- return DEFAULT_APPROVAL_CHOICES.map((choice) => cloneChoice(choice));
146557
+ return getDefaultApprovalChoices().map((choice) => cloneChoice(choice));
146331
146558
  }
146332
146559
  function adaptPlanReviewChoices(display) {
146333
146560
  return [...display.kind === "plan_review" && display.options !== void 0 && display.options.length >= 2 ? display.options.map((option) => ({
@@ -146338,7 +146565,7 @@ function adaptPlanReviewChoices(display) {
146338
146565
  label: t("approval.approve"),
146339
146566
  response: "approved",
146340
146567
  selected_label: t("approval.approve")
146341
- }], ...PLAN_REJECT_CHOICES].map((choice) => cloneChoice(choice));
146568
+ }], ...getPlanRejectChoices()].map((choice) => cloneChoice(choice));
146342
146569
  }
146343
146570
  function cloneChoice(choice) {
146344
146571
  return { ...choice };
package/dist/main.mjs CHANGED
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
7
  //#region src/main.ts
8
8
  try {
9
- (await import("./app-DKYKW1st.mjs")).main();
9
+ (await import("./app-XROb-IiZ.mjs")).main();
10
10
  } catch (error) {
11
11
  process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
12
12
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",