zen-gitsync 2.17.34 → 2.17.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +17 -3
  2. package/package.json +4 -1
  3. package/src/cli/ai/agent.js +34 -2
  4. package/src/cli/ai/cli.integration.test.js +30 -0
  5. package/src/cli/ai/paste.js +372 -0
  6. package/src/cli/ai/paste.test.js +276 -0
  7. package/src/cli/ai/termui.js +44 -6
  8. package/src/cli/ai/termui.test.js +39 -3
  9. package/src/ui/public/assets/{AgentEngineSelector-DVuUEDHz.js → AgentEngineSelector-DVs4LGHZ.js} +1 -1
  10. package/src/ui/public/assets/{AgentView-xdFrDybl.js → AgentView-Blp40VRr.js} +1 -1
  11. package/src/ui/public/assets/{AppVersionBadge-CCqQiZPO.js → AppVersionBadge-BEnoFzwh.js} +2 -2
  12. package/src/ui/public/assets/{BranchSelector-GD8NqzJj.js → BranchSelector-C7eXeFif.js} +1 -1
  13. package/src/ui/public/assets/{CommitForm-DNuJSePt.js → CommitForm-CK9jLJOC.js} +1 -1
  14. package/src/ui/public/assets/{CommonDialog-Cg3qSsFG.js → CommonDialog-BTeIpgGT.js} +1 -1
  15. package/src/ui/public/assets/{EditorView-ivXCi959.js → EditorView-D00zvntf.js} +1 -1
  16. package/src/ui/public/assets/{FileDiffViewer-DS1-m59W.js → FileDiffViewer-zqgG9ok3.js} +1 -1
  17. package/src/ui/public/assets/{FlowExecutionViewer-DXhO7HwA.js → FlowExecutionViewer-VFnxkbIs.js} +1 -1
  18. package/src/ui/public/assets/{FlowOrchestrationWorkspace-DIJboi94.js → FlowOrchestrationWorkspace-CIIdKK3v.js} +1 -1
  19. package/src/ui/public/assets/{IconButton-BVgngz_Y.js → IconButton-Cgc5v9vb.js} +1 -1
  20. package/src/ui/public/assets/{LogList-DGRGqcb_.js → LogList-DyQv-Yec.js} +1 -1
  21. package/src/ui/public/assets/{MindmapView-CRtCsbfO.js → MindmapView-EC2Wk_Qi.js} +1 -1
  22. package/src/ui/public/assets/{MonitorView-Ml1e4m41.js → MonitorView-Z5ZbGtaU.js} +1 -1
  23. package/src/ui/public/assets/{ProjectStartupButton-BDoX_Equ.js → ProjectStartupButton-Ci19q1bL.js} +1 -1
  24. package/src/ui/public/assets/{RecentDirectoriesChat-CwPIeHuv.js → RecentDirectoriesChat-DT0sguEG.js} +1 -1
  25. package/src/ui/public/assets/{RemoteManagerDialog-B9JvGgsK.js → RemoteManagerDialog-BRQpjMSK.js} +1 -1
  26. package/src/ui/public/assets/{RemoteRepoCard-BvZJtHNN.js → RemoteRepoCard-yK90_VfS.js} +1 -1
  27. package/src/ui/public/assets/{SourceMapView-CxTHZDOP.js → SourceMapView-C55ixUwz.js} +1 -1
  28. package/src/ui/public/assets/{SvgIcon-dJcILWS2.js → SvgIcon-ub85vovN.js} +1 -1
  29. package/src/ui/public/assets/{TemplateManager-BqrwTyxG.js → TemplateManager-uDno_64z.js} +1 -1
  30. package/src/ui/public/assets/{UserInputNode-D28zKp0v.js → UserInputNode-Ce55MMEZ.js} +1 -1
  31. package/src/ui/public/assets/WorkbenchView-CtJD3pz-.js +228 -0
  32. package/src/ui/public/assets/WorkbenchView-D1Pf6Etd.css +1 -0
  33. package/src/ui/public/assets/{_plugin-vue_export-helper-Be8qXN0e.js → _plugin-vue_export-helper-VuE2vqnn.js} +2 -2
  34. package/src/ui/public/assets/{agentConversations-BAvoMaCc.js → agentConversations-DcW54Tlt.js} +1 -1
  35. package/src/ui/public/assets/{configStore-Bj7SEMRL.js → configStore-De_8eQuS.js} +1 -1
  36. package/src/ui/public/assets/{gitStore-BaBKqLlD.js → gitStore-BP6TATf7.js} +1 -1
  37. package/src/ui/public/assets/{index-B0MdMohu.js → index-Bq_-TrTP.js} +5 -5
  38. package/src/ui/public/assets/{previewSandbox-DKUIjkxY.js → previewSandbox-DcVlFmag.js} +1 -1
  39. package/src/ui/public/assets/{toolsStore-MahCZx-D.js → toolsStore-CQclze0T.js} +1 -1
  40. package/src/ui/public/index.html +11 -11
  41. package/src/ui/server/routes/workbench/hiddenProjects.js +87 -0
  42. package/src/ui/server/routes/workbench/hiddenProjects.test.js +60 -0
  43. package/src/ui/server/routes/workbench/index.js +69 -1
  44. package/src/ui/server/routes/workbench/shared.js +8 -0
  45. package/src/ui/public/assets/WorkbenchView-BhWKWSFS.js +0 -220
  46. package/src/ui/public/assets/WorkbenchView-Bx5r6sZ5.css +0 -1
@@ -1,5 +1,5 @@
1
1
  const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/office-docx-CTz4O0Ml.js","assets/rolldown-runtime-C0FnF6B9.js","assets/element-plus-Buzx8xRf.js","assets/dagre-CDSwApuC.js","assets/element-plus-Qexo5hiM.css","assets/office-excel-DaZCapUD.js","assets/office-pptx-CFwyD4jf.js"])))=>i.map(i=>d[i]);
2
- import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{$n as t,Bn as n,Dn as r,En as i,Fn as a,Fr as o,Ht as s,In as c,J as l,Jr as u,On as d,Pn as f,Tn as p,W as m,Xr as h,Yn as g,Zn as _,br as v,cn as y,cr as b,ei as x,kn as S,nr as C,or as w,pr as T,qn as E,sn as D,wt as O,xn as k}from"./element-plus-Buzx8xRf.js";import{_ as A}from"./vendor-wERX2bc3.js";import{t as j}from"./_plugin-vue_export-helper-Be8qXN0e.js";import{ir as M}from"./dagre-CDSwApuC.js";import{n as N,r as P}from"./flow-mindmap-D-65aZb7.js";var F=new Set([`png`,`jpg`,`jpeg`,`gif`,`webp`,`bmp`,`ico`,`svg`]);function ee(e){if(!e)return!1;let t=e.split(`.`).pop()?.toLowerCase()??``;return F.has(t)}function te(e){return`/api/editor/raw?path=${encodeURIComponent(e)}`}var I=new Set([`doc`,`docx`,`xls`,`xlsx`,`ppt`,`pptx`,`odt`,`ods`,`odp`]);function L(e){return e.split(/[\\/]/).pop()?.split(`.`).pop()?.toLowerCase()||``}function R(e){return I.has(L(e))}function z(e,t={}){let n=new URLSearchParams({source:t.source||`worktree`,file:e});return t.rev&&n.set(`rev`,t.rev),t.side&&n.set(`side`,t.side),`/api/office/preview?${n.toString()}`}function B(e,t={}){let n=new URLSearchParams({source:t.source||`worktree`,file:e});return t.rev&&n.set(`rev`,t.rev),t.side&&n.set(`side`,t.side),`/api/office/raw?${n.toString()}`}n(),T();var V=c({__name:`ImagePreview`,props:{filePath:{type:String,required:!0},fileName:{type:String,required:!1}},setup(e,{expose:t}){t();let n=e,r=v(null),i=v(1),a=v(0),o=v(0),c=v(!1),u=v({x:0,y:0}),d=v(null),f=v(!1),m=v(null),h=()=>{if(!r.value||!d.value)return;let e=r.value.clientWidth-32,t=r.value.clientHeight-32;if(e<=0||t<=0)return;let n=Math.min(e/d.value.w,t/d.value.h,1);i.value=n,a.value=0,o.value=0},_=()=>{i.value=Math.min(i.value*1.2,8)},b=()=>{i.value=Math.max(i.value/1.2,.1)},x=()=>{i.value=1,a.value=0,o.value=0,h()},S=e=>{e.preventDefault(),e.deltaY<0?_():b()},C=e=>{e.button===0&&(c.value=!0,u.value={x:e.clientX-a.value,y:e.clientY-o.value})},T=e=>{c.value&&(a.value=e.clientX-u.value.x,o.value=e.clientY-u.value.y)},k=()=>{c.value=!1},A=e=>{let t=e.target;d.value={w:t.naturalWidth,h:t.naturalHeight},f.value=!1,requestAnimationFrame(()=>h())},j=()=>{f.value=!0},M=p(()=>n.fileName||n.filePath?.split(`/`).pop()||``),N=p(()=>te(n.filePath));g(()=>{window.addEventListener(`mousemove`,T),window.addEventListener(`mouseup`,k),window.addEventListener(`resize`,h)}),E(()=>{window.removeEventListener(`mousemove`,T),window.removeEventListener(`mouseup`,k),window.removeEventListener(`resize`,h)}),w(()=>n.filePath,()=>{d.value=null,f.value=!1,i.value=1,a.value=0,o.value=0});let P={props:n,containerRef:r,scale:i,translateX:a,translateY:o,isDragging:c,dragStart:u,naturalSize:d,loadError:f,imgRef:m,fit:h,zoomIn:_,zoomOut:b,reset:x,onWheel:S,onMouseDown:C,onMouseMove:T,onMouseUp:k,onImgLoad:A,onImgError:j,displayName:M,imageUrl:N,get ElIcon(){return l},get ZoomIn(){return D},get ZoomOut(){return y},get Refresh(){return s},get FullScreen(){return O}};return Object.defineProperty(P,"__isScriptSetup",{enumerable:!1,value:!0}),P}});n(),o();var H={class:`image-preview-toolbar`},U=[`title`],W={key:0,class:`image-preview-dim`},G={key:1,class:`image-preview-dim`},K={key:0,class:`image-preview-error`},q={class:`image-preview-error-path`},J=[`src`,`alt`],Y={class:`image-preview-footer`},X={class:`image-preview-zoom`};function Z(e,t,n,r,a,o){let s=m;return _(),S(`div`,{class:`image-preview`,ref:`containerRef`,onWheel:r.onWheel},[d(` 顶部工具条 `),i(`div`,H,[i(`span`,{class:`image-preview-name`,title:r.props.filePath},x(r.displayName),9,U),r.naturalSize?(_(),S(`span`,W,x(r.naturalSize.w)+` × `+x(r.naturalSize.h)+` px`,1)):(_(),S(`span`,G,x(Math.round(r.scale*100))+`%`,1)),t[0]||=i(`div`,{class:`image-preview-spacer`},null,-1),f(s,{content:`放大`,placement:`top`},{default:b(()=>[i(`button`,{class:`image-preview-btn`,onClick:r.zoomIn},[f(r.ElIcon,null,{default:b(()=>[f(r.ZoomIn)]),_:1})])]),_:1}),f(s,{content:`缩小`,placement:`top`},{default:b(()=>[i(`button`,{class:`image-preview-btn`,onClick:r.zoomOut},[f(r.ElIcon,null,{default:b(()=>[f(r.ZoomOut)]),_:1})])]),_:1}),f(s,{content:`适应窗口`,placement:`top`},{default:b(()=>[i(`button`,{class:`image-preview-btn`,onClick:r.reset},[f(r.ElIcon,null,{default:b(()=>[f(r.Refresh)]),_:1})])]),_:1})]),d(` 图片区域 `),i(`div`,{class:u([`image-preview-stage`,{"is-dragging":r.isDragging}]),onMousedown:r.onMouseDown},[r.loadError?(_(),S(`div`,K,[f(r.ElIcon,{size:`32`,color:`var(--color-danger)`},{default:b(()=>[f(r.FullScreen)]),_:1}),t[1]||=i(`p`,null,`图片加载失败`,-1),i(`p`,q,x(r.props.filePath),1)])):(_(),S(`img`,{key:1,ref:`imgRef`,src:r.imageUrl,alt:r.displayName,class:`image-preview-img`,style:h({transform:`translate(${r.translateX}px, ${r.translateY}px) scale(${r.scale})`}),onLoad:r.onImgLoad,onError:r.onImgError,draggable:`false`},null,44,J))],34),d(` 底部提示 `),i(`div`,Y,[t[2]||=i(`span`,null,`滚轮缩放 · 拖拽平移 · 双击重置`,-1),i(`span`,X,x(Math.round(r.scale*100))+`%`,1)])],544)}var ne=j(V,[[`render`,Z],[`__scopeId`,`data-v-88f42dc3`],[`__file`,`C:/workspace/github_workspace/xz333221/zen-gitsync/src/ui/client/src/components/ImagePreview.vue`]]);n();var re=c({__name:`MarkdownPreview`,props:{content:{type:String,required:!0},allowHtml:{type:Boolean,required:!1,default:!0}},setup(e,{expose:t}){t();let n=e;function r(e){if(typeof document>`u`)return e;let t=document.createElement(`template`);return t.innerHTML=e,t.content.querySelectorAll(`script, style, iframe, object, embed, form, input, button, textarea, select`).forEach(e=>e.remove()),t.content.querySelectorAll(`*`).forEach(e=>{for(let t of Array.from(e.attributes)){let n=t.name.toLowerCase();if(n.startsWith(`on`)||n===`style`||n===`srcdoc`){e.removeAttribute(t.name);continue}if(n===`href`||n===`src`||n===`xlink:href`){let r=t.value.trim().replace(/[\u0000-\u0020]+/g,``);/^(https?:|mailto:|tel:|#|\/|\.\/|\.\.\/)/i.test(r)||n===`src`&&/^data:image\/(?:png|gif|jpe?g|webp);base64,/i.test(r)||e.removeAttribute(t.name)}}e.getAttribute(`target`)===`_blank`&&e.setAttribute(`rel`,`noopener noreferrer`)}),t.innerHTML}let i={props:n,sanitizeRenderedHtml:r,segments:p(()=>{let e=n.content||``,t=[],i=`@@FLOWDASH_MINDMAP_BLOCK_`,a=e.replace(/```[ \t]*mindmap[ \t]*\n([\s\S]*?)```/gi,(e,n)=>{let r=t.length;return t.push({id:r,md:n}),`${i}${r}@@`}),o=A(a,{className:!1,html:n.allowHtml}),s=n.allowHtml?o:r(o),c=RegExp(`(?:<p>)?\\s*${i}(\\d+)@@\\s*(?:</p>)?`,`g`),l=[],u=0,d;for(;(d=c.exec(s))!==null;){d.index>u&&l.push({type:`html`,html:s.slice(u,d.index)});let e=Number(d[1]);l.push({type:`mindmap`,id:e,md:t[e].md,data:N(t[e].md)}),u=d.index+d[0].length}return u<s.length&&l.push({type:`html`,html:s.slice(u)}),l.length===0&&l.push({type:`html`,html:`<p class="md-empty">无内容</p>`}),l}),get MindMap(){return P}};return Object.defineProperty(i,"__isScriptSetup",{enumerable:!1,value:!0}),i}});n();var ie={class:`md-preview`},ae=[`innerHTML`],oe={key:1,class:`md-mindmap`};function se(e,n,r,a,o,s){return _(),S(`div`,ie,[(_(!0),S(k,null,t(a.segments,(e,t)=>(_(),S(k,{key:t},[e.type===`html`?(_(),S(`div`,{key:0,class:`md-segment`,innerHTML:e.html},null,8,ae)):(_(),S(`div`,oe,[n[0]||=i(`div`,{class:`md-mindmap-title`},`四维导图`,-1),f(a.MindMap,{data:e.data,"preview-mode":``,class:`md-mindmap-canvas`},null,8,[`data`])]))],64))),128))])}var ce=j(re,[[`render`,se],[`__scopeId`,`data-v-c5fe3d4b`],[`__file`,`C:/workspace/github_workspace/xz333221/zen-gitsync/src/ui/client/src/components/MarkdownPreview.vue`]]);n(),T();var le=c({__name:`OfficePreview`,props:{filePath:{type:String,required:!0},source:{type:String,required:!1,default:`worktree`},rev:{type:String,required:!1,default:``},side:{type:String,required:!1,default:`new`}},setup(t,{expose:n}){n();let r=t,i=a(()=>M(()=>import(`./office-docx-CTz4O0Ml.js`).then(t=>e(t.t(),1)),__vite__mapDeps([0,1,2,3,4]))),o=a(()=>M(()=>import(`./office-excel-DaZCapUD.js`).then(t=>e(t.t(),1)),__vite__mapDeps([5,1,0,2,3,4]))),s=a(()=>M(()=>import(`./office-pptx-CFwyD4jf.js`).then(t=>e(t.t(),1)),__vite__mapDeps([6,1,0,2,3,4]))),c=p(()=>L(r.filePath)),l=p(()=>c.value===`docx`?i:c.value===`xls`||c.value===`xlsx`?o:c.value===`pptx`?s:null),u=p(()=>B(r.filePath,{source:r.source,rev:r.rev,side:r.side})),d=p(()=>z(r.filePath,{source:r.source,rev:r.rev,side:r.side})),f=v(null),m=v(``),h=v(!1),g=v(!1),_=v(!1),y=0;function b(){m.value&&URL.revokeObjectURL(m.value),m.value=``}async function x(e){try{let t=await fetch(d.value);if(!t.ok)throw Error(`HTTP ${t.status}`);if(e!==y)return;m.value=URL.createObjectURL(await t.blob()),_.value=!0,g.value=!1}catch{e===y&&(g.value=!0)}}async function S(){let e=++y;if(h.value=!0,g.value=!1,_.value=!1,f.value=null,b(),!l.value){await x(e),e===y&&(h.value=!1);return}try{let t=await fetch(u.value);if(!t.ok)throw Error(`HTTP ${t.status}`);if(e!==y)return;f.value=await t.arrayBuffer()}catch{await x(e)}finally{e===y&&(h.value=!1)}}function C(){if(!_.value){let e=y;f.value=null,h.value=!0,x(e).finally(()=>{e===y&&(h.value=!1)})}}function T(){S()}w(u,()=>{S()},{immediate:!0}),E(()=>{y++,b()});let D={props:r,DocxPreview:i,ExcelPreview:o,PptxPreview:s,ext:c,officeComponent:l,rawUrl:u,fallbackUrl:d,sourceData:f,fallbackBlobUrl:m,loading:h,failed:g,usingFallback:_,get requestId(){return y},set requestId(e){y=e},revokeFallbackUrl:b,fetchFallback:x,loadPreview:S,onRenderError:C,reload:T};return Object.defineProperty(D,"__isScriptSetup",{enumerable:!1,value:!0}),D}});n(),o();var ue={class:`office-preview`},de={class:`office-preview-toolbar`},fe={class:`office-preview-badge`},pe={key:0,class:`office-preview-side`},me={key:0,class:`office-preview-state`},Q={key:1,class:`office-preview-state office-preview-error`},he=[`src`];function ge(e,t,n,a,o,s){return _(),S(`div`,ue,[i(`div`,de,[t[0]||=i(`span`,{class:`office-preview-title`},`Office 预览`,-1),i(`span`,fe,x(a.ext.toUpperCase()),1),n.source===`git`?(_(),S(`span`,pe,x(n.side===`old`?`旧版本`:`新版本`),1)):d(`v-if`,!0),i(`button`,{class:`office-preview-reload`,type:`button`,onClick:a.reload},`刷新`)]),a.loading?(_(),S(`div`,me,`正在生成预览…`)):a.failed?(_(),S(`div`,Q,[...t[1]||=[i(`p`,null,`无法预览此 Office 文件。`,-1),i(`p`,{class:`office-preview-hint`},`支持 docx、xls/xlsx、pptx;老格式需要安装 LibreOffice 才能兜底预览。`,-1)]])):a.usingFallback?(_(),S(`iframe`,{key:2,class:`office-preview-frame`,src:a.fallbackBlobUrl,title:`Office 文件 PDF 预览`},null,8,he)):a.officeComponent&&a.sourceData?(_(),r(C(a.officeComponent),{key:3,class:`office-preview-document`,src:a.sourceData,onError:a.onRenderError},null,40,[`src`])):d(`v-if`,!0)])}var _e=j(le,[[`render`,ge],[`__scopeId`,`data-v-93e099c2`],[`__file`,`C:/workspace/github_workspace/xz333221/zen-gitsync/src/ui/client/src/components/OfficePreview.vue`]]),ve=`allow-scripts allow-forms allow-popups allow-modals`,$=`<script>(function(){
2
+ import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{$n as t,Bn as n,Dn as r,En as i,Fn as a,Fr as o,Ht as s,In as c,J as l,Jr as u,On as d,Pn as f,Tn as p,W as m,Xr as h,Yn as g,Zn as _,br as v,cn as y,cr as b,ei as x,kn as S,nr as C,or as w,pr as T,qn as E,sn as D,wt as O,xn as k}from"./element-plus-Buzx8xRf.js";import{_ as A}from"./vendor-wERX2bc3.js";import{t as j}from"./_plugin-vue_export-helper-VuE2vqnn.js";import{ir as M}from"./dagre-CDSwApuC.js";import{n as N,r as P}from"./flow-mindmap-D-65aZb7.js";var F=new Set([`png`,`jpg`,`jpeg`,`gif`,`webp`,`bmp`,`ico`,`svg`]);function ee(e){if(!e)return!1;let t=e.split(`.`).pop()?.toLowerCase()??``;return F.has(t)}function te(e){return`/api/editor/raw?path=${encodeURIComponent(e)}`}var I=new Set([`doc`,`docx`,`xls`,`xlsx`,`ppt`,`pptx`,`odt`,`ods`,`odp`]);function L(e){return e.split(/[\\/]/).pop()?.split(`.`).pop()?.toLowerCase()||``}function R(e){return I.has(L(e))}function z(e,t={}){let n=new URLSearchParams({source:t.source||`worktree`,file:e});return t.rev&&n.set(`rev`,t.rev),t.side&&n.set(`side`,t.side),`/api/office/preview?${n.toString()}`}function B(e,t={}){let n=new URLSearchParams({source:t.source||`worktree`,file:e});return t.rev&&n.set(`rev`,t.rev),t.side&&n.set(`side`,t.side),`/api/office/raw?${n.toString()}`}n(),T();var V=c({__name:`ImagePreview`,props:{filePath:{type:String,required:!0},fileName:{type:String,required:!1}},setup(e,{expose:t}){t();let n=e,r=v(null),i=v(1),a=v(0),o=v(0),c=v(!1),u=v({x:0,y:0}),d=v(null),f=v(!1),m=v(null),h=()=>{if(!r.value||!d.value)return;let e=r.value.clientWidth-32,t=r.value.clientHeight-32;if(e<=0||t<=0)return;let n=Math.min(e/d.value.w,t/d.value.h,1);i.value=n,a.value=0,o.value=0},_=()=>{i.value=Math.min(i.value*1.2,8)},b=()=>{i.value=Math.max(i.value/1.2,.1)},x=()=>{i.value=1,a.value=0,o.value=0,h()},S=e=>{e.preventDefault(),e.deltaY<0?_():b()},C=e=>{e.button===0&&(c.value=!0,u.value={x:e.clientX-a.value,y:e.clientY-o.value})},T=e=>{c.value&&(a.value=e.clientX-u.value.x,o.value=e.clientY-u.value.y)},k=()=>{c.value=!1},A=e=>{let t=e.target;d.value={w:t.naturalWidth,h:t.naturalHeight},f.value=!1,requestAnimationFrame(()=>h())},j=()=>{f.value=!0},M=p(()=>n.fileName||n.filePath?.split(`/`).pop()||``),N=p(()=>te(n.filePath));g(()=>{window.addEventListener(`mousemove`,T),window.addEventListener(`mouseup`,k),window.addEventListener(`resize`,h)}),E(()=>{window.removeEventListener(`mousemove`,T),window.removeEventListener(`mouseup`,k),window.removeEventListener(`resize`,h)}),w(()=>n.filePath,()=>{d.value=null,f.value=!1,i.value=1,a.value=0,o.value=0});let P={props:n,containerRef:r,scale:i,translateX:a,translateY:o,isDragging:c,dragStart:u,naturalSize:d,loadError:f,imgRef:m,fit:h,zoomIn:_,zoomOut:b,reset:x,onWheel:S,onMouseDown:C,onMouseMove:T,onMouseUp:k,onImgLoad:A,onImgError:j,displayName:M,imageUrl:N,get ElIcon(){return l},get ZoomIn(){return D},get ZoomOut(){return y},get Refresh(){return s},get FullScreen(){return O}};return Object.defineProperty(P,"__isScriptSetup",{enumerable:!1,value:!0}),P}});n(),o();var H={class:`image-preview-toolbar`},U=[`title`],W={key:0,class:`image-preview-dim`},G={key:1,class:`image-preview-dim`},K={key:0,class:`image-preview-error`},q={class:`image-preview-error-path`},J=[`src`,`alt`],Y={class:`image-preview-footer`},X={class:`image-preview-zoom`};function Z(e,t,n,r,a,o){let s=m;return _(),S(`div`,{class:`image-preview`,ref:`containerRef`,onWheel:r.onWheel},[d(` 顶部工具条 `),i(`div`,H,[i(`span`,{class:`image-preview-name`,title:r.props.filePath},x(r.displayName),9,U),r.naturalSize?(_(),S(`span`,W,x(r.naturalSize.w)+` × `+x(r.naturalSize.h)+` px`,1)):(_(),S(`span`,G,x(Math.round(r.scale*100))+`%`,1)),t[0]||=i(`div`,{class:`image-preview-spacer`},null,-1),f(s,{content:`放大`,placement:`top`},{default:b(()=>[i(`button`,{class:`image-preview-btn`,onClick:r.zoomIn},[f(r.ElIcon,null,{default:b(()=>[f(r.ZoomIn)]),_:1})])]),_:1}),f(s,{content:`缩小`,placement:`top`},{default:b(()=>[i(`button`,{class:`image-preview-btn`,onClick:r.zoomOut},[f(r.ElIcon,null,{default:b(()=>[f(r.ZoomOut)]),_:1})])]),_:1}),f(s,{content:`适应窗口`,placement:`top`},{default:b(()=>[i(`button`,{class:`image-preview-btn`,onClick:r.reset},[f(r.ElIcon,null,{default:b(()=>[f(r.Refresh)]),_:1})])]),_:1})]),d(` 图片区域 `),i(`div`,{class:u([`image-preview-stage`,{"is-dragging":r.isDragging}]),onMousedown:r.onMouseDown},[r.loadError?(_(),S(`div`,K,[f(r.ElIcon,{size:`32`,color:`var(--color-danger)`},{default:b(()=>[f(r.FullScreen)]),_:1}),t[1]||=i(`p`,null,`图片加载失败`,-1),i(`p`,q,x(r.props.filePath),1)])):(_(),S(`img`,{key:1,ref:`imgRef`,src:r.imageUrl,alt:r.displayName,class:`image-preview-img`,style:h({transform:`translate(${r.translateX}px, ${r.translateY}px) scale(${r.scale})`}),onLoad:r.onImgLoad,onError:r.onImgError,draggable:`false`},null,44,J))],34),d(` 底部提示 `),i(`div`,Y,[t[2]||=i(`span`,null,`滚轮缩放 · 拖拽平移 · 双击重置`,-1),i(`span`,X,x(Math.round(r.scale*100))+`%`,1)])],544)}var ne=j(V,[[`render`,Z],[`__scopeId`,`data-v-88f42dc3`],[`__file`,`C:/workspace/github_workspace/xz333221/zen-gitsync/src/ui/client/src/components/ImagePreview.vue`]]);n();var re=c({__name:`MarkdownPreview`,props:{content:{type:String,required:!0},allowHtml:{type:Boolean,required:!1,default:!0}},setup(e,{expose:t}){t();let n=e;function r(e){if(typeof document>`u`)return e;let t=document.createElement(`template`);return t.innerHTML=e,t.content.querySelectorAll(`script, style, iframe, object, embed, form, input, button, textarea, select`).forEach(e=>e.remove()),t.content.querySelectorAll(`*`).forEach(e=>{for(let t of Array.from(e.attributes)){let n=t.name.toLowerCase();if(n.startsWith(`on`)||n===`style`||n===`srcdoc`){e.removeAttribute(t.name);continue}if(n===`href`||n===`src`||n===`xlink:href`){let r=t.value.trim().replace(/[\u0000-\u0020]+/g,``);/^(https?:|mailto:|tel:|#|\/|\.\/|\.\.\/)/i.test(r)||n===`src`&&/^data:image\/(?:png|gif|jpe?g|webp);base64,/i.test(r)||e.removeAttribute(t.name)}}e.getAttribute(`target`)===`_blank`&&e.setAttribute(`rel`,`noopener noreferrer`)}),t.innerHTML}let i={props:n,sanitizeRenderedHtml:r,segments:p(()=>{let e=n.content||``,t=[],i=`@@FLOWDASH_MINDMAP_BLOCK_`,a=e.replace(/```[ \t]*mindmap[ \t]*\n([\s\S]*?)```/gi,(e,n)=>{let r=t.length;return t.push({id:r,md:n}),`${i}${r}@@`}),o=A(a,{className:!1,html:n.allowHtml}),s=n.allowHtml?o:r(o),c=RegExp(`(?:<p>)?\\s*${i}(\\d+)@@\\s*(?:</p>)?`,`g`),l=[],u=0,d;for(;(d=c.exec(s))!==null;){d.index>u&&l.push({type:`html`,html:s.slice(u,d.index)});let e=Number(d[1]);l.push({type:`mindmap`,id:e,md:t[e].md,data:N(t[e].md)}),u=d.index+d[0].length}return u<s.length&&l.push({type:`html`,html:s.slice(u)}),l.length===0&&l.push({type:`html`,html:`<p class="md-empty">无内容</p>`}),l}),get MindMap(){return P}};return Object.defineProperty(i,"__isScriptSetup",{enumerable:!1,value:!0}),i}});n();var ie={class:`md-preview`},ae=[`innerHTML`],oe={key:1,class:`md-mindmap`};function se(e,n,r,a,o,s){return _(),S(`div`,ie,[(_(!0),S(k,null,t(a.segments,(e,t)=>(_(),S(k,{key:t},[e.type===`html`?(_(),S(`div`,{key:0,class:`md-segment`,innerHTML:e.html},null,8,ae)):(_(),S(`div`,oe,[n[0]||=i(`div`,{class:`md-mindmap-title`},`四维导图`,-1),f(a.MindMap,{data:e.data,"preview-mode":``,class:`md-mindmap-canvas`},null,8,[`data`])]))],64))),128))])}var ce=j(re,[[`render`,se],[`__scopeId`,`data-v-c5fe3d4b`],[`__file`,`C:/workspace/github_workspace/xz333221/zen-gitsync/src/ui/client/src/components/MarkdownPreview.vue`]]);n(),T();var le=c({__name:`OfficePreview`,props:{filePath:{type:String,required:!0},source:{type:String,required:!1,default:`worktree`},rev:{type:String,required:!1,default:``},side:{type:String,required:!1,default:`new`}},setup(t,{expose:n}){n();let r=t,i=a(()=>M(()=>import(`./office-docx-CTz4O0Ml.js`).then(t=>e(t.t(),1)),__vite__mapDeps([0,1,2,3,4]))),o=a(()=>M(()=>import(`./office-excel-DaZCapUD.js`).then(t=>e(t.t(),1)),__vite__mapDeps([5,1,0,2,3,4]))),s=a(()=>M(()=>import(`./office-pptx-CFwyD4jf.js`).then(t=>e(t.t(),1)),__vite__mapDeps([6,1,0,2,3,4]))),c=p(()=>L(r.filePath)),l=p(()=>c.value===`docx`?i:c.value===`xls`||c.value===`xlsx`?o:c.value===`pptx`?s:null),u=p(()=>B(r.filePath,{source:r.source,rev:r.rev,side:r.side})),d=p(()=>z(r.filePath,{source:r.source,rev:r.rev,side:r.side})),f=v(null),m=v(``),h=v(!1),g=v(!1),_=v(!1),y=0;function b(){m.value&&URL.revokeObjectURL(m.value),m.value=``}async function x(e){try{let t=await fetch(d.value);if(!t.ok)throw Error(`HTTP ${t.status}`);if(e!==y)return;m.value=URL.createObjectURL(await t.blob()),_.value=!0,g.value=!1}catch{e===y&&(g.value=!0)}}async function S(){let e=++y;if(h.value=!0,g.value=!1,_.value=!1,f.value=null,b(),!l.value){await x(e),e===y&&(h.value=!1);return}try{let t=await fetch(u.value);if(!t.ok)throw Error(`HTTP ${t.status}`);if(e!==y)return;f.value=await t.arrayBuffer()}catch{await x(e)}finally{e===y&&(h.value=!1)}}function C(){if(!_.value){let e=y;f.value=null,h.value=!0,x(e).finally(()=>{e===y&&(h.value=!1)})}}function T(){S()}w(u,()=>{S()},{immediate:!0}),E(()=>{y++,b()});let D={props:r,DocxPreview:i,ExcelPreview:o,PptxPreview:s,ext:c,officeComponent:l,rawUrl:u,fallbackUrl:d,sourceData:f,fallbackBlobUrl:m,loading:h,failed:g,usingFallback:_,get requestId(){return y},set requestId(e){y=e},revokeFallbackUrl:b,fetchFallback:x,loadPreview:S,onRenderError:C,reload:T};return Object.defineProperty(D,"__isScriptSetup",{enumerable:!1,value:!0}),D}});n(),o();var ue={class:`office-preview`},de={class:`office-preview-toolbar`},fe={class:`office-preview-badge`},pe={key:0,class:`office-preview-side`},me={key:0,class:`office-preview-state`},Q={key:1,class:`office-preview-state office-preview-error`},he=[`src`];function ge(e,t,n,a,o,s){return _(),S(`div`,ue,[i(`div`,de,[t[0]||=i(`span`,{class:`office-preview-title`},`Office 预览`,-1),i(`span`,fe,x(a.ext.toUpperCase()),1),n.source===`git`?(_(),S(`span`,pe,x(n.side===`old`?`旧版本`:`新版本`),1)):d(`v-if`,!0),i(`button`,{class:`office-preview-reload`,type:`button`,onClick:a.reload},`刷新`)]),a.loading?(_(),S(`div`,me,`正在生成预览…`)):a.failed?(_(),S(`div`,Q,[...t[1]||=[i(`p`,null,`无法预览此 Office 文件。`,-1),i(`p`,{class:`office-preview-hint`},`支持 docx、xls/xlsx、pptx;老格式需要安装 LibreOffice 才能兜底预览。`,-1)]])):a.usingFallback?(_(),S(`iframe`,{key:2,class:`office-preview-frame`,src:a.fallbackBlobUrl,title:`Office 文件 PDF 预览`},null,8,he)):a.officeComponent&&a.sourceData?(_(),r(C(a.officeComponent),{key:3,class:`office-preview-document`,src:a.sourceData,onError:a.onRenderError},null,40,[`src`])):d(`v-if`,!0)])}var _e=j(le,[[`render`,ge],[`__scopeId`,`data-v-93e099c2`],[`__file`,`C:/workspace/github_workspace/xz333221/zen-gitsync/src/ui/client/src/components/OfficePreview.vue`]]),ve=`allow-scripts allow-forms allow-popups allow-modals`,$=`<script>(function(){
3
3
  try{window.localStorage.getItem('__zk')}catch(e){
4
4
  var d={};var s={getItem:function(k){return Object.prototype.hasOwnProperty.call(d,k)?d[k]:null},setItem:function(k,v){d[k]=String(v)},removeItem:function(k){delete d[k]},clear:function(){d={}},key:function(i){var ks=Object.keys(d);return i<ks.length?ks[i]:null}};
5
5
  Object.defineProperty(window,'localStorage',{get:function(){return s},configurable:true});
@@ -1 +1 @@
1
- import{br as e,pr as t}from"./element-plus-Buzx8xRf.js";import{S as n}from"./vendor-wERX2bc3.js";import{n as r}from"./_plugin-vue_export-helper-Be8qXN0e.js";import{a as i,i as a}from"./configStore-Bj7SEMRL.js";t();var o=6e5;function s(e){if(!e||typeof e!=`object`)return null;let t=e;return typeof t.name!=`string`||!t.name?null:{name:t.name,detail:typeof t.detail==`string`&&t.detail?t.detail:null,provider:typeof t.provider==`string`&&t.provider?t.provider:null,source:t.source===`config`||t.source===`state`?t.source:null}}var c=n(`tools`,()=>{let t=e(!1),n=e(!1),c=e(!1),l=e(!1),u=e(!1),d=e(!1),f=e(!1),p=e(null),m=e(!1),h=e(``),g=e({}),_=e({}),v=e({}),y=e(!1),b=0,x=null;function S(e=!1){return!e&&Date.now()-b<3e5?Promise.resolve():x||(y.value=!0,x=(async()=>{try{let e=await(await fetch(`/api/latest-tool-versions`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({})})).json();e.success&&e.latest&&typeof e.latest==`object`&&(v.value=e.latest,b=Date.now())}catch{}finally{y.value=!1,x=null}})(),x)}let C=null;function w(){return C||(m.value=!0,C=(async()=>{try{let e=await(await fetch(`/api/check-tools`)).json();e.success&&(t.value=!!e.vscode,n.value=!!e.claude,c.value=!!e.codex,l.value=!!e.opencode,u.value=!!e.kimi,d.value=!!e.zcode,f.value=!!e.dsh,h.value=typeof e.platform==`string`?e.platform:``,g.value=e.installers&&typeof e.installers==`object`?e.installers:{},_.value=e.versions&&typeof e.versions==`object`?e.versions:{},p.value=Date.now())}catch{}finally{m.value=!1,C=null}})(),C)}function T(e){switch(e){case`vscode`:return t.value;case`claude`:return n.value;case`codex`:return c.value;case`opencode`:return l.value;case`kimi`:return u.value;case`zcode`:return d.value;case`dsh`:return f.value}}let E=e({}),D=e(!1),O=0,k=null;function A(e){if(!D.value)return{status:`unknown`};if(!i(e))return{status:`unset`};let t=E.value[e];return t?{status:`set`,info:t}:{status:`unset`}}function j(e=!1){return!e&&D.value&&Date.now()-O<6e5?Promise.resolve():k||(k=(async()=>{try{let e=await(await fetch(`/api/workbench/executor-models`)).json();if(e?.success&&e.models&&typeof e.models==`object`){let t={};for(let n of a)t[n.id]=s(e.models[n.id]);E.value=t,D.value=!0,O=Date.now()}}catch{}finally{k=null}})(),k)}function M(e){let t=A(e);return t.status===`unknown`?``:t.status===`set`?t.info.name:r(`@42BB9:未在配置中指定`)}function N(e){let t=A(e);return t.status===`set`?[t.info.source===`state`?r(`@42BB9:CLI 内最近使用`):``,t.info.detail,t.info.provider].filter(Boolean).join(` · `):``}let P=null;function F(){P||=(w(),j(),setInterval(()=>{w(),j()},o))}function I(){P&&=(clearInterval(P),null)}return{vscodeAvailable:t,claudeAvailable:n,codexAvailable:c,opencodeAvailable:l,kimiAvailable:u,zcodeAvailable:d,dshAvailable:f,lastCheckedAt:p,isChecking:m,platform:h,installers:g,versions:_,latestVersions:v,isFetchingLatest:y,isToolAvailable:T,executorModels:E,executorModelsReady:D,executorModelState:A,executorModelText:M,executorModelDetail:N,fetchExecutorModels:j,checkTools:w,fetchLatestVersions:S,startPolling:F,stopPolling:I}});export{c as t};
1
+ import{br as e,pr as t}from"./element-plus-Buzx8xRf.js";import{S as n}from"./vendor-wERX2bc3.js";import{n as r}from"./_plugin-vue_export-helper-VuE2vqnn.js";import{a as i,i as a}from"./configStore-De_8eQuS.js";t();var o=6e5;function s(e){if(!e||typeof e!=`object`)return null;let t=e;return typeof t.name!=`string`||!t.name?null:{name:t.name,detail:typeof t.detail==`string`&&t.detail?t.detail:null,provider:typeof t.provider==`string`&&t.provider?t.provider:null,source:t.source===`config`||t.source===`state`?t.source:null}}var c=n(`tools`,()=>{let t=e(!1),n=e(!1),c=e(!1),l=e(!1),u=e(!1),d=e(!1),f=e(!1),p=e(null),m=e(!1),h=e(``),g=e({}),_=e({}),v=e({}),y=e(!1),b=0,x=null;function S(e=!1){return!e&&Date.now()-b<3e5?Promise.resolve():x||(y.value=!0,x=(async()=>{try{let e=await(await fetch(`/api/latest-tool-versions`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({})})).json();e.success&&e.latest&&typeof e.latest==`object`&&(v.value=e.latest,b=Date.now())}catch{}finally{y.value=!1,x=null}})(),x)}let C=null;function w(){return C||(m.value=!0,C=(async()=>{try{let e=await(await fetch(`/api/check-tools`)).json();e.success&&(t.value=!!e.vscode,n.value=!!e.claude,c.value=!!e.codex,l.value=!!e.opencode,u.value=!!e.kimi,d.value=!!e.zcode,f.value=!!e.dsh,h.value=typeof e.platform==`string`?e.platform:``,g.value=e.installers&&typeof e.installers==`object`?e.installers:{},_.value=e.versions&&typeof e.versions==`object`?e.versions:{},p.value=Date.now())}catch{}finally{m.value=!1,C=null}})(),C)}function T(e){switch(e){case`vscode`:return t.value;case`claude`:return n.value;case`codex`:return c.value;case`opencode`:return l.value;case`kimi`:return u.value;case`zcode`:return d.value;case`dsh`:return f.value}}let E=e({}),D=e(!1),O=0,k=null;function A(e){if(!D.value)return{status:`unknown`};if(!i(e))return{status:`unset`};let t=E.value[e];return t?{status:`set`,info:t}:{status:`unset`}}function j(e=!1){return!e&&D.value&&Date.now()-O<6e5?Promise.resolve():k||(k=(async()=>{try{let e=await(await fetch(`/api/workbench/executor-models`)).json();if(e?.success&&e.models&&typeof e.models==`object`){let t={};for(let n of a)t[n.id]=s(e.models[n.id]);E.value=t,D.value=!0,O=Date.now()}}catch{}finally{k=null}})(),k)}function M(e){let t=A(e);return t.status===`unknown`?``:t.status===`set`?t.info.name:r(`@42BB9:未在配置中指定`)}function N(e){let t=A(e);return t.status===`set`?[t.info.source===`state`?r(`@42BB9:CLI 内最近使用`):``,t.info.detail,t.info.provider].filter(Boolean).join(` · `):``}let P=null;function F(){P||=(w(),j(),setInterval(()=>{w(),j()},o))}function I(){P&&=(clearInterval(P),null)}return{vscodeAvailable:t,claudeAvailable:n,codexAvailable:c,opencodeAvailable:l,kimiAvailable:u,zcodeAvailable:d,dshAvailable:f,lastCheckedAt:p,isChecking:m,platform:h,installers:g,versions:_,latestVersions:v,isFetchingLatest:y,isToolAvailable:T,executorModels:E,executorModelsReady:D,executorModelState:A,executorModelText:M,executorModelDetail:N,fetchExecutorModels:j,checkTools:w,fetchLatestVersions:S,startPolling:F,stopPolling:I}});export{c as t};
@@ -10,26 +10,26 @@
10
10
  <meta name="theme-color" content="#141820" media="(prefers-color-scheme: dark)" />
11
11
  <title>Zen GitSync</title>
12
12
  <!-- 字体改为自托管(@font-face 见 src/main.css),不再依赖 Google Fonts 外链 -->
13
- <script type="module" crossorigin src="/assets/index-B0MdMohu.js"></script>
13
+ <script type="module" crossorigin src="/assets/index-Bq_-TrTP.js"></script>
14
14
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/dagre-CDSwApuC.js">
16
16
  <link rel="modulepreload" crossorigin href="/assets/element-plus-Buzx8xRf.js">
17
17
  <link rel="modulepreload" crossorigin href="/assets/vendor-wERX2bc3.js">
18
- <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-Be8qXN0e.js">
19
- <link rel="modulepreload" crossorigin href="/assets/SvgIcon-dJcILWS2.js">
20
- <link rel="modulepreload" crossorigin href="/assets/IconButton-BVgngz_Y.js">
21
- <link rel="modulepreload" crossorigin href="/assets/configStore-Bj7SEMRL.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-VuE2vqnn.js">
19
+ <link rel="modulepreload" crossorigin href="/assets/SvgIcon-ub85vovN.js">
20
+ <link rel="modulepreload" crossorigin href="/assets/IconButton-Cgc5v9vb.js">
21
+ <link rel="modulepreload" crossorigin href="/assets/configStore-De_8eQuS.js">
22
22
  <link rel="modulepreload" crossorigin href="/assets/socket-io-BFqQXqAp.js">
23
- <link rel="modulepreload" crossorigin href="/assets/gitStore-BaBKqLlD.js">
23
+ <link rel="modulepreload" crossorigin href="/assets/gitStore-BP6TATf7.js">
24
24
  <link rel="modulepreload" crossorigin href="/assets/fileIcon-B5p5pGN9.js">
25
25
  <link rel="modulepreload" crossorigin href="/assets/flow-mindmap-D-65aZb7.js">
26
- <link rel="modulepreload" crossorigin href="/assets/previewSandbox-DKUIjkxY.js">
26
+ <link rel="modulepreload" crossorigin href="/assets/previewSandbox-DcVlFmag.js">
27
27
  <link rel="modulepreload" crossorigin href="/assets/monaco-BD8Gy_t7.js">
28
28
  <link rel="modulepreload" crossorigin href="/assets/useThemeObserver-CWioG2jb.js">
29
- <link rel="modulepreload" crossorigin href="/assets/CommonDialog-Cg3qSsFG.js">
30
- <link rel="modulepreload" crossorigin href="/assets/FileDiffViewer-DS1-m59W.js">
31
- <link rel="modulepreload" crossorigin href="/assets/toolsStore-MahCZx-D.js">
32
- <link rel="modulepreload" crossorigin href="/assets/TemplateManager-BqrwTyxG.js">
29
+ <link rel="modulepreload" crossorigin href="/assets/CommonDialog-BTeIpgGT.js">
30
+ <link rel="modulepreload" crossorigin href="/assets/FileDiffViewer-zqgG9ok3.js">
31
+ <link rel="modulepreload" crossorigin href="/assets/toolsStore-CQclze0T.js">
32
+ <link rel="modulepreload" crossorigin href="/assets/TemplateManager-uDno_64z.js">
33
33
  <link rel="modulepreload" crossorigin href="/assets/vue-flow-KUZI4f_9.js">
34
34
  <link rel="stylesheet" crossorigin href="/assets/element-plus-Qexo5hiM.css">
35
35
  <link rel="stylesheet" crossorigin href="/assets/vendor-CP-WuGZG.css">
@@ -0,0 +1,87 @@
1
+ // Copyright 2026 xz333221
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // 「用户从工作台清单里移除掉的死项目」——一份很小的落盘名单。
16
+ //
17
+ // 为什么需要它(而不是只从常用目录里删掉那一条就完事):
18
+ // 项目清单是**两份来源的并集**(projectRegistry.buildProjectEntries)——
19
+ // config.json 的 recentDirectories,加上 tasks.json 里任务自带的 projectPath。
20
+ // 一个目录被删/被移走之后,只删常用目录那一份通常**根本不会让这行消失**:
21
+ // 只要还有任务记着这个路径,它照样从任务那半边被重新生成(实测用户点完删除,
22
+ // 那行原地不动 —— 看起来就像按钮坏了)。想让它真正从清单里走掉,
23
+ // 就必须有一份"用户明确表示不要看见它"的记录。
24
+ //
25
+ // 为什么不改任务(把 projectPath 清空)来达到同样效果:
26
+ // 那是把**一条会失败的事实改成一条会静默成功的事实**。任务带着一个不存在的目录时,
27
+ // 执行会当场报「项目目录不存在」;清空之后 resolveTaskRepoPath 会回退到当前项目,
28
+ // 于是"点执行 → 代码改到了另一个仓库里",而界面上没有任何提示。
29
+ // projectRegistry.js 开头那段注释把这个回退称为"比直接报错危险得多",这里不能自己破它。
30
+ // 隐藏只动"看不看得见",一条任务、一个 job、一份历史都不碰。
31
+ //
32
+ // 为什么只在目录**仍然不存在**期间隐藏(filterHiddenProjects 的第二个条件):
33
+ // 目录哪天被重新克隆回来了,这一行就该自己回来 —— 用户没有永久放弃这个项目,
34
+ // 只是清掉了一条死条目。没有这条自愈,用户就得手动把隐藏名单清一次才能再见它。
35
+
36
+ import { HIDDEN_PROJECTS_FILE, readJson, writeJson } from './shared.js';
37
+
38
+ /** 上限:只可能积累"被删掉的死目录",几十条足够。满了丢最旧的(下面 unshift 的顺序即新旧) */
39
+ export const MAX_HIDDEN_PROJECTS = 200;
40
+
41
+ /**
42
+ * 读隐藏名单。文件不存在 / 读失败 / 结构不对,一律当"没藏过任何项目"——
43
+ * 这是纯展示层的偏好,坏了最多回到"死目录又出现在清单里"的原状,
44
+ * 绝不能因此让 /api/workbench/projects 整个 500(它是 5s 轮询的)。
45
+ *
46
+ * @returns {Promise<string[]>} canonicalProjectPath 之后的项目 key
47
+ */
48
+ export async function readHiddenProjects() {
49
+ try {
50
+ const data = await readJson(HIDDEN_PROJECTS_FILE, { projects: [] });
51
+ const list = Array.isArray(data?.projects) ? data.projects : [];
52
+ return list.filter(k => typeof k === 'string' && k);
53
+ } catch {
54
+ return [];
55
+ }
56
+ }
57
+
58
+ /**
59
+ * 记下一个被移除的项目(幂等)。
60
+ * @returns {Promise<string[]>} 落盘后的名单
61
+ */
62
+ export async function hideProject(key) {
63
+ if (typeof key !== 'string' || !key) return readHiddenProjects();
64
+ const current = await readHiddenProjects();
65
+ const next = [key, ...current.filter(k => k !== key)].slice(0, MAX_HIDDEN_PROJECTS);
66
+ await writeJson(HIDDEN_PROJECTS_FILE, { projects: next });
67
+ return next;
68
+ }
69
+
70
+ /**
71
+ * 从项目清单里滤掉"藏起来且目录仍然不存在"的那几条。纯函数,单测覆盖。
72
+ *
73
+ * @param {Array<{key: string, exists: boolean|null}>} projects listProjects 的产物
74
+ * @param {Iterable<string>} hidden readHiddenProjects 的结果
75
+ * @returns {Array} 原样返回入参里的条目(不复制、不改字段),只调整顺序与数量
76
+ *
77
+ * `exists === null`(没探到)**不隐藏**:那是"不知道",不是"用户不要看"。
78
+ * 按 `=== false` 判,与 projectRegistry 输出的三态口径一致。
79
+ */
80
+ export function filterHiddenProjects(projects, hidden) {
81
+ const set = hidden instanceof Set ? hidden : new Set(Array.isArray(hidden) ? hidden : []);
82
+ if (set.size === 0) return Array.isArray(projects) ? projects : [];
83
+ return (Array.isArray(projects) ? projects : []).filter(
84
+ p => !(p && set.has(p.key) && p.exists === false),
85
+ );
86
+ }
87
+
@@ -0,0 +1,60 @@
1
+ // Copyright 2026 xz333221
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // 「已移除项目」名单的纯函数单测:filterHiddenProjects。
16
+ // 落盘那半(readHiddenProjects / hideProject)要真写 DATA_DIR,不在单测里跑。
17
+
18
+ import { test } from 'node:test';
19
+ import assert from 'node:assert/strict';
20
+
21
+ import { filterHiddenProjects } from './hiddenProjects.js';
22
+
23
+ const P = (key, exists) => ({ key, path: key, name: key.split(/[\\/]/).pop(), exists });
24
+
25
+ test('filterHiddenProjects: 藏起来且目录不存在 → 滤掉', () => {
26
+ const list = [P('a', true), P('b', false)];
27
+ assert.deepEqual(filterHiddenProjects(list, ['b']).map(p => p.key), ['a']);
28
+ });
29
+
30
+ test('filterHiddenProjects: 没在隐藏名单里的一律留着', () => {
31
+ const list = [P('a', false), P('b', false)];
32
+ assert.equal(filterHiddenProjects(list, ['c']).length, 2);
33
+ assert.equal(filterHiddenProjects(list, []).length, 2);
34
+ });
35
+
36
+ test('filterHiddenProjects: 目录回来了就自己现身(自愈,不做永久隐藏)', () => {
37
+ // 这条是"不硬改任务 projectPath"那套设计的兜底:用户清掉的只是死条目,
38
+ // 不是永久放弃这个项目。目录哪天被重新克隆回来,它必须自己回到清单里。
39
+ assert.equal(filterHiddenProjects([P('b', true)], ['b']).length, 1);
40
+ });
41
+
42
+ test('filterHiddenProjects: exists === null(没探到)不隐藏 —— 不知道 ≠ 用户不要看', () => {
43
+ // 与 projectRegistry 的三态口径一致:只有明确探到 false 才算"目录没了"。
44
+ // 把 null 当 false 会让一次探测超时的项目从清单里凭空消失,比多显示一行更糟。
45
+ assert.equal(filterHiddenProjects([P('b', null)], ['b']).length, 1);
46
+ });
47
+
48
+ test('filterHiddenProjects: 空名单直接原样返回,不复制数组', () => {
49
+ const list = [P('a', true)];
50
+ assert.equal(filterHiddenProjects(list, new Set()), list);
51
+ // 名单为空时没有逐条过滤的必要,也就不该因为过滤而产生新数组引用
52
+ // (board 那边拿它跟 selectedKey 比对,少一次引用变更少一次无谓的 watch 触发)
53
+ assert.equal(filterHiddenProjects(list, null), list);
54
+ });
55
+
56
+ test('filterHiddenProjects: 入参不是数组时返回空数组,不抛', () => {
57
+ assert.deepEqual(filterHiddenProjects(undefined, ['a']), []);
58
+ assert.deepEqual(filterHiddenProjects(null, ['a']), []);
59
+ assert.deepEqual(filterHiddenProjects([P('a', true)], undefined).length, 1);
60
+ });
@@ -115,6 +115,7 @@ import {
115
115
  canonicalProjectPath,
116
116
  } from './projectRegistry.js';
117
117
  import { buildEnvContextBlock } from './envContext.js';
118
+ import { readHiddenProjects, hideProject, filterHiddenProjects } from './hiddenProjects.js';
118
119
  import { ensureMemoryStore } from '../../../../memory/store.js';
119
120
  import { createJobSettledRefresher } from '../aiContext/jobRefresh.js';
120
121
  import {
@@ -1251,6 +1252,7 @@ ${subSummaries.map((s, i) => `\n### [${i + 1}] ${s.name} (${s.root})\n${s.summar
1251
1252
  // ════════════════════════════════════════════════════════════════════════
1252
1253
  // §16. 多项目编排台(L1 看板 + 主 Agent 控制台)
1253
1254
  // GET /api/workbench/projects 项目清单 + 看板任务
1255
+ // POST /api/workbench/projects/remove 从清单里移除一个项目条目
1254
1256
  // GET /api/workbench/orchestrator 调度开关 + 指令存档 + 活动流 + 默认提示词
1255
1257
  // POST /api/workbench/orchestrator/state 暂停 / 恢复调度
1256
1258
  // POST /api/workbench/orchestrator/dispatch 派发一条人类干预指令
@@ -1292,7 +1294,13 @@ ${subSummaries.map((s, i) => `\n### [${i + 1}] ${s.name} (${s.root})\n${s.summar
1292
1294
  const currentProjectPath = typeof getCurrentProjectPath === 'function' ? getCurrentProjectPath() : '';
1293
1295
  // Git 探测自带 15s TTL 缓存(directoryGitState.js),看板 5s 轮询里有 2/3 是命中缓存,
1294
1296
  // 实际 spawn 频率被自然限制在 15s 一次,不需要再给这个接口加"跳过一次探测"的开关。
1295
- const projects = await listProjects({ recentDirs, tasks, jobs: jobsSnap, currentProjectPath });
1297
+ const allProjects = await listProjects({ recentDirs, tasks, jobs: jobsSnap, currentProjectPath });
1298
+ // 滤掉用户手动移除、且目录确实已经不在了的那些条目(语义见 hiddenProjects.js)。
1299
+ // 放在 listProjects 之后而不是之前:Git 探测得照常跑完 —— 目录有没有回来,
1300
+ // 正是"该不该继续藏着"这条判据的依据。
1301
+ let projects = allProjects;
1302
+ const hidden = await readHiddenProjects();
1303
+ if (hidden.length) projects = filterHiddenProjects(allProjects, hidden);
1296
1304
  const jobsByTask = groupJobsByTask(jobsSnap);
1297
1305
  const boardTasks = tasks.map(t => decorateTaskForBoard(t, jobsByTask.get(t.id) || []));
1298
1306
  return { projects, tasks: boardTasks, currentProjectPath };
@@ -1387,6 +1395,66 @@ ${subSummaries.map((s, i) => `\n### [${i + 1}] ${s.name} (${s.root})\n${s.summar
1387
1395
  res.json({ success: true, ...payload });
1388
1396
  }));
1389
1397
 
1398
+ /**
1399
+ * 把一个项目条目从工作台清单里移除(左栏「目录不存在」那一行的删除按钮)。
1400
+ *
1401
+ * 做两件事,缺一不可:
1402
+ * ① 从常用目录(config.json 的 recentDirectories)里摘掉;
1403
+ * ② 记进 hidden-projects.json —— 只删 ① 的话,只要还有任务记着这个路径,
1404
+ * 它会立刻从「任务」那半边被重新生成,用户看到的是"点了没反应"。
1405
+ * 语义、为什么不去改任务的 projectPath,见 hiddenProjects.js 的文件头注释。
1406
+ *
1407
+ * **不删任何任务/job/历史**:只是让这一行不再出现在清单里。
1408
+ * 它在目录仍然不存在期间一直藏着;哪天目录被重新克隆回来,它会自己回来。
1409
+ *
1410
+ * 路径只认"当前清单里确实有"的那一个(用与 buildProjectEntries 同一份归一口径比对),
1411
+ * 不接受任意路径 —— 与 GET /projects 的安全边界一致,这个端点不是任意路径写配置的入口。
1412
+ */
1413
+ app.post('/api/workbench/projects/remove', asyncRoute(async (req, res) => {
1414
+ const rawPath = typeof req.body?.path === 'string' ? req.body.path.trim() : '';
1415
+ const key = canonicalProjectPath(rawPath);
1416
+ if (!key) throw new HttpError(400, '缺少项目路径');
1417
+ if (!configManager || typeof configManager.removeRecentDirectory !== 'function') {
1418
+ throw new HttpError(500, '配置管理不可用,无法移除项目');
1419
+ }
1420
+
1421
+ const tasksData = await readJson(TASKS_FILE, { tasks: [] });
1422
+ const tasks = tasksData.tasks || [];
1423
+ let recentDirs = [];
1424
+ try {
1425
+ recentDirs = (await configManager.getRecentDirectories()) || [];
1426
+ } catch (err) {
1427
+ logger.warn('[workbench] 移除项目时读取最近目录失败:', err?.message || err);
1428
+ }
1429
+
1430
+ // 只承认清单里真实存在的条目:key 归一后必须命中 buildProjectEntries 的某一条,
1431
+ // 否则 404 —— 否则这个端点就成了"往 config.json 删任意字符串"的通道。
1432
+ const entry = buildProjectEntries({ recentDirs, tasks }).find(e => e.key === key);
1433
+ if (!entry) throw new HttpError(404, '项目不在清单里,可能已经被移除');
1434
+
1435
+ // ① 常用目录。同一目录可能有多种写法(大小写 / 斜杠),逐条摘干净 ——
1436
+ // 漏掉任何一种,那一条就会在下一轮轮询里把项目重新"复活"成 source:'recent'。
1437
+ let removedFromRecent = 0;
1438
+ for (const dir of recentDirs) {
1439
+ if (canonicalProjectPath(dir) !== key) continue;
1440
+ await configManager.removeRecentDirectory(dir);
1441
+ removedFromRecent += 1;
1442
+ }
1443
+
1444
+ // ② 隐藏名单。idempotent:重复点第二次不会把它顶到最前面之外的地方。
1445
+ await hideProject(key);
1446
+
1447
+ // 通知别的 g ui 实例:它们的看板是 5s 轮询,晚一轮刷新而已,不值得推 SSE。
1448
+ publish('projects:removed', { key, path: entry.path, removedFromRecent });
1449
+
1450
+ res.json({
1451
+ success: true,
1452
+ removedFromRecent,
1453
+ // 回给前端说清楚"任务一条都没删",好让它把这句话原样讲给用户听
1454
+ keptTasks: tasks.filter(t => canonicalProjectPath(t.projectPath) === key).length,
1455
+ });
1456
+ }));
1457
+
1390
1458
  /**
1391
1459
  * 单个任务的详情 —— 点看板卡片弹出的那个框用它。
1392
1460
  *
@@ -65,6 +65,14 @@ export const MAX_ORCHESTRATOR_INSTRUCTIONS = 200;
65
65
  export const ORCHESTRATOR_REPORTS_FILE = path.join(DATA_DIR, 'orchestrator-reports.json');
66
66
  export const MAX_PROGRESS_REPORTS = 20;
67
67
 
68
+ /**
69
+ * 用户从工作台项目清单里手动移除掉的那些条目(存的是归一化后的项目 key)。
70
+ * 独立成文件而不是塞进 config.json:它一份最多几百字节,却会被 5s 轮询的项目清单
71
+ * 接口读到 —— 走 config.json 就得让每次读项目列表都解析一遍主配置。
72
+ * 语义与"为什么不能靠改任务达成"见 hiddenProjects.js 的文件头注释。
73
+ */
74
+ export const HIDDEN_PROJECTS_FILE = path.join(DATA_DIR, 'hidden-projects.json');
75
+
68
76
  /**
69
77
  * 自动进度报告允许的间隔(毫秒),0 = 关闭。
70
78
  *