shine-code-submit 1.0.6 → 1.0.8

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.
@@ -118,10 +118,18 @@ export interface CommitsResponse {
118
118
  // ---- GET /api/report:数据上报页用的跨项目聚合 ----
119
119
 
120
120
  /** 报告里单个会话的 token 明细。tokenTotal 读不到 transcript 为 null。 */
121
+ /** 代码变更行数(分开统计,三者不重复:added 纯增 / deleted 纯删 / modified 一删一加配对)。 */
122
+ export interface LinesStat {
123
+ added: number;
124
+ deleted: number;
125
+ modified: number;
126
+ }
127
+
121
128
  export interface ReportSession {
122
129
  sessionId: string;
123
130
  lastActive: number;
124
131
  tokenTotal: TokenUsage | null;
132
+ linesTotal: LinesStat | null;
125
133
  }
126
134
 
127
135
  /** 报告里单个项目(=cwd)的聚合行。 */
@@ -133,6 +141,7 @@ export interface ReportProject {
133
141
  sessionCount: number;
134
142
  sessions: ReportSession[]; // 每会话 token 明细
135
143
  totalTokens: TokenUsage; // 该项目 token 合计
144
+ totalLines: LinesStat; // 该项目代码变更行数合计
136
145
  gitError?: string;
137
146
  }
138
147
 
@@ -141,6 +150,7 @@ export interface ReportTotals {
141
150
  projects: number;
142
151
  sessions: number;
143
152
  tokens: TokenUsage;
153
+ lines: LinesStat;
144
154
  }
145
155
 
146
156
  /** GET /api/report 响应。 */
package/ui/.build/app.js CHANGED
@@ -73,4 +73,4 @@ Please report this to https://github.com/markedjs/marked.`,Y){let U="<p>An error
73
73
  `),U=String(G||"").split(`
74
74
  `),Z=X.length,A=U.length,z=Array.from({length:Z+1},()=>Array(A+1).fill(0));for(let q=Z-1;q>=0;q--)for(let Q=A-1;Q>=0;Q--)z[q][Q]=X[q]===U[Q]?z[q+1][Q+1]+1:Math.max(z[q+1][Q],z[q][Q+1]);let J=[],H=0,W=0;while(H<Z&&W<A)if(X[H]===U[W])J.push({op:"ctx",text:X[H]}),H++,W++;else if(z[H+1][W]>=z[H][W+1])J.push({op:"del",text:X[H]}),H++;else J.push({op:"add",text:U[W]}),W++;while(H<Z)J.push({op:"del",text:X[H]}),H++;while(W<A)J.push({op:"add",text:U[W]}),W++;return J}function iW(Y,G){let X=(U)=>{if(G&&typeof G==="object"){let Z=G[U];return Z!=null?String(Z):""}return""};switch(Y){case"Bash":return X("command");case"Read":case"Edit":case"Write":return X("file_path");case"Grep":case"Glob":return X("pattern");case"WebSearch":return X("query");case"WebFetch":return X("url");case"TaskCreate":return X("subject")||X("description");case"TaskUpdate":return"#"+X("taskId")+" → "+X("status");case"TaskGet":return"#"+X("taskId");case"TaskList":return"";case"Agent":return X("description");default:return FU(G).slice(0,80)}}var qM={Bash:"▶",Read:"\uD83D\uDCC4",Edit:"✎",Write:"✎",Grep:"\uD83D\uDD0E",Glob:"\uD83D\uDDC2",WebSearch:"\uD83C\uDF10",WebFetch:"\uD83C\uDF10",TaskCreate:"\uD83D\uDCCC",TaskUpdate:"\uD83D\uDCCC",TaskGet:"\uD83D\uDCCC",TaskList:"\uD83D\uDCCC",Agent:"\uD83E\uDD16"};function pW(Y){return qM[Y]??"\uD83D\uDEE0"}function rW(Y){let G=String(Y||"").split(`
75
75
  `),X=G.slice(0,50),U=X.map((Z)=>`<div class="diff-line ctx"><span class="diff-text">${OW(Z)}</span></div>`);if(G.length>X.length)U.push(`<div class="diff-folds">⋯ 共 ${G.length} 行,前 ${X.length} 行</div>`);return`<div class="diff-block">${U.join("")}</div>`}function oW({diffs:Y}){let G=[],X=[],U=0,Z=()=>{if(!X.length)return;if(X.length<=3)for(let A of X)G.push(B("div",{className:"diff-line ctx",children:B("span",{className:"diff-text",children:A.text})},U++));else{let A=X[0],z=X[X.length-1];G.push(B("div",{className:"diff-line ctx",children:B("span",{className:"diff-text",children:A.text})},U++)),G.push(O("div",{className:"diff-folds",children:["⋯ ",X.length-2," 行未改"]},U++)),G.push(B("div",{className:"diff-line ctx",children:B("span",{className:"diff-text",children:z.text})},U++))}X=[]};for(let A of Y){if(A.op==="ctx"){X.push(A);continue}if(Z(),A.op==="add")G.push(O("div",{className:"diff-line add",children:[B("span",{className:"diff-sign",children:"+"}),B("span",{className:"diff-text",children:A.text})]},U++));else G.push(O("div",{className:"diff-line del",children:[B("span",{className:"diff-sign",children:"-"}),B("span",{className:"diff-text",children:A.text})]},U++))}return Z(),B("div",{className:"diff-block",children:G})}function sW({t:Y}){let G=Y.name||"?",X=Y.input&&typeof Y.input==="object"?Y.input:{},U=(J)=>X[J]!=null?String(X[J]):"",Z=iW(G,X).slice(0,100),{body:A,stat:z}=f0(()=>{if(G==="Edit"){let J=lW(U("old_string"),U("new_string")),H=0,W=0;for(let q of J)if(q.op==="add")H++;else if(q.op==="del")W++;return{body:B(oW,{diffs:J}),stat:{add:H,del:W}}}if(G==="Write")return{body:B("div",{dangerouslySetInnerHTML:{__html:rW(U("content"))}}),stat:null};return{body:B("pre",{className:"card-output",children:FU(X)}),stat:null}},[G,X]);return O("details",{className:"tool-card",children:[O("summary",{className:"card-trigger",children:[B("span",{className:"card-chev",children:B(G0,{name:"chevron",size:12})}),B("span",{className:"tool-icon",children:pW(G)}),B("span",{className:"tool-title",children:G}),B("span",{className:"tool-sub",children:Z}),z&&O("span",{className:"tool-stat",children:[O("span",{className:"s-add",children:["+",z.add]})," ",O("span",{className:"s-del",children:["-",z.del]})]})]}),B("div",{className:"tool-content",children:A})]})}function aW({m:Y}){if(Y.role==="user")return B("div",{className:"msg-row user-row",children:B("div",{className:"bubble",children:Y.text})});if(Y.role==="tool")return B("div",{className:"msg-row",children:O("details",{className:`result-card${Y.isError?" is-error":""}`,children:[O("summary",{className:"card-trigger",children:[B("span",{className:"card-chev",children:B(G0,{name:"chevron",size:12})}),B("span",{className:"result-label",children:Y.toolName||"结果"})]}),B("pre",{className:"card-output",children:Y.text})]})});return O("div",{className:"msg-row assistant-row",children:[O("div",{className:"role-head",children:[B("span",{className:"role-mark",children:B(G0,{name:"diamond",size:10})}),B("span",{className:"role-name",children:"Claude"}),Y.usage&&B("span",{className:"role-tokens",title:m0(Y.usage),children:jG(Y.usage)}),Y.ts!=null&&B("span",{className:"role-ts",children:DW(Y.ts)})]}),Y.thinking&&O("details",{className:"think-card",children:[O("summary",{className:"card-trigger",children:[B("span",{className:"card-chev",children:B(G0,{name:"chevron",size:12})}),B("span",{className:"think-label",children:"Thinking"})]}),B("div",{className:"think-body",children:B(xz,{src:Y.thinking})})]}),Y.text&&B("div",{className:"assistant-text msg-text",children:B(xz,{src:Y.text})}),Y.tools.map((G,X)=>B(sW,{t:G},X))]})}function QM(Y,G){if(!G)return!0;if((Y.text||"").toLowerCase().includes(G))return!0;if((Y.thinking||"").toLowerCase().includes(G))return!0;if((Y.toolName||"").toLowerCase().includes(G))return!0;return(Y.tools||[]).some((X)=>(X.name||"").toLowerCase().includes(G)||JSON.stringify(X.input).toLowerCase().includes(G))}function MM(){return O(n,{children:[B("div",{className:"skeleton skel-bubble"}),B("div",{className:"skeleton skel-block"}),B("div",{className:"skeleton skel-bubble"}),B("div",{className:"skeleton skel-block",style:{width:"68%"}})]})}function nW({messages:Y,loading:G,error:X,search:U}){let Z=U.trim().toLowerCase(),A=f0(()=>Z?Y.filter((H)=>QM(H,Z)):Y,[Y,Z]),z=f0(()=>FW(Y),[Y]),J;if(G&&Y.length===0)J=B(MM,{});else if(X)J=O("div",{className:"empty-state",children:[B(G0,{name:"warning",size:28}),B("span",{className:"es-hint",children:"加载失败"}),B("span",{className:"es-sub",children:X})]});else if(!Y.length)J=O("div",{className:"empty-state",children:[B(G0,{name:"chat",size:30}),B("span",{className:"es-hint",children:"暂无对话"}),B("span",{className:"es-sub",children:"该会话无 transcript 或尚未产生对话"})]});else if(!A.length)J=O("div",{className:"empty-state",children:[B("span",{className:"es-hint",children:"无匹配消息"}),O("span",{className:"es-sub",children:["“",U,"” 未命中(0/",Y.length,")"]})]});else J=O(n,{children:[Z&&B("div",{className:"search-count",children:`${A.length}/${Y.length} 条匹配`}),A.map((H,W)=>B(aW,{m:H},W))]});return O("div",{id:"conversation",className:"conversation",children:[!G&&!X&&Y.length>0&&(z.input>0||z.output>0)&&O("div",{className:"conv-token-bar",title:m0(z),children:["本会话累计 ",B("b",{children:jG(z)}),O("span",{className:"conv-token-cache",children:[" ","· 缓存读 ",M0(z.cacheRead)," / 写 ",M0(z.cacheCreation)]})]}),J]})}function tW({sessionId:Y}){let{token:G,sessions:X}=t(),U=MY(G),[Z,A]=j(""),{messages:z,loading:J,error:H}=TW(U,Y,!0),W=X.find((Q)=>Q.sessionId===Y),q=W?.tokenTotal??null;return O("div",{className:"session-detail",children:[O("div",{className:"detail-head",children:[B("span",{className:"detail-sid",title:Y,children:Y.slice(0,8)}),W?.cwd&&B("span",{className:"detail-cwd",title:W.cwd,children:PG(W.cwd)}),q&&(F0(q)>0||q.output>0)&&B("span",{className:"detail-token",title:m0(q),children:VU(q)})]}),B("div",{className:"toolbar",children:B("input",{className:"search-input",type:"search",placeholder:"搜索对话…",value:Z,onChange:(Q)=>A(Q.target.value),autoComplete:"off"})}),B("div",{className:"detail-body",children:B(nW,{messages:z,loading:J,error:H,search:Z})})]})}X0();function eW(Y,G){let X=LY(null),U=M7((Z)=>{Z.preventDefault();let A=X.current;if(!A)return;A.classList.add("dragging"),document.body.style.cursor=Y==="v"?"col-resize":"row-resize",document.body.style.userSelect="none";let{clientX:z,clientY:J}=Z,H=document.documentElement,W=Y==="v"?G??"--nav-w":"--footer-h",q=parseFloat(getComputedStyle(H).getPropertyValue(W))||200,Q=parseFloat(getComputedStyle(H).getPropertyValue("--footer-h"))||170,M=(V)=>{if(Y==="v")H.style.setProperty(W,Math.max(140,q+(V.clientX-z))+"px");else H.style.setProperty("--footer-h",Math.max(60,Q-(V.clientY-J))+"px")},L=()=>{A.classList.remove("dragging"),document.body.style.cursor="",document.body.style.userSelect="",document.removeEventListener("mousemove",M),document.removeEventListener("mouseup",L)};document.addEventListener("mousemove",M),document.addEventListener("mouseup",L)},[Y,G]);return{ref:X,onMouseDown:U}}function $U({orient:Y,hidden:G,varName:X}){let{ref:U,onMouseDown:Z}=eW(Y,X),A=`splitter splitter-${Y}${G?" hidden":""}`;return B("div",{ref:U,className:A,"data-orient":Y,onMouseDown:Z,title:Y==="v"?"拖拽调整宽度":"拖拽调整高度"})}function Y4(){let{selectedSessionId:Y,setSelectedSessionId:G,sessions:X}=t();return D0(()=>{let U=X[0];if(!Y&&U)G(U.sessionId)},[Y,X,G]),O("div",{className:"sessions-with-tree",children:[B("aside",{className:"sessions-tree-panel panel",children:B(NW,{})}),B($U,{orient:"v",varName:"--tree-w"}),B("div",{className:"sessions-main",children:Y?B(tW,{sessionId:Y}):O("div",{className:"empty-state",children:[B(G0,{name:"chat",size:30}),B("span",{className:"es-hint",children:"暂无会话"}),B("span",{className:"es-sub",children:"启动 Claude Code 后会出现"})]})})]})}X0();X0();function G4(Y,G,X){let[U,Z]=j([]),[A,z]=j(!1),[J,H]=j(null),W=LY(void 0);return D0(()=>{if(!X)return;if(W.current!==G)Z([]),W.current=G;let q=!0;return z(!0),H(null),(async()=>{try{let Q=G?"?sessionId="+encodeURIComponent(G)+"&limit=200":"?limit=200",M=await Y("/api/events"+Q);if(q)Z(M.events),H(null)}catch(Q){if(q)H(Q instanceof Error?Q.message:String(Q))}finally{if(q)z(!1)}})(),()=>{q=!1}},[Y,G,X]),{events:U,loading:A,error:J}}X0();function X4(Y,G,X){let[U,Z]=j([]),[A,z]=j(!1),J=LY("");return D0(()=>{if(!X)return;let H=Array.from(new Set(G.map((Q)=>Q.cwd).filter(Boolean))),W=H.join(`
76
- `);if(J.current===W)return;if(J.current=W,H.length===0){Z([]);return}let q=!0;return z(!0),(async()=>{try{let Q=await Promise.all(H.map(async(L)=>{try{let V=await Y(`/api/commits?cwd=${encodeURIComponent(L)}&limit=200`);return{cwd:L,commits:V.commits??[]}}catch{return{cwd:L,commits:[]}}}));if(!q)return;let M=Q.flatMap((L)=>L.commits.map((V)=>({...V,cwd:L.cwd})));M.sort((L,V)=>V.time-L.time),Z(M)}finally{if(q)z(!1)}})(),()=>{q=!1}},[Y,G,X]),{commits:U,loading:A}}function hz(){let{token:Y,sessions:G,stats:X}=t(),U=MY(Y),{events:Z}=G4(U,null,!0),{commits:A}=X4(U,G,!0),z=f0(()=>fU(G.map((H)=>H.tokenTotal)),[G]),J=f0(()=>{let H=Z.slice(0,20).map((q)=>({ts:q.timestamp,kind:"event",text:q.type,cwd:q.cwd})),W=A.slice(0,20).map((q)=>({ts:q.time,kind:"commit",text:q.subject||"(无说明)",cwd:q.cwd}));return[...H,...W].sort((q,Q)=>Q.ts-q.ts).slice(0,20)},[Z,A]);return O("div",{className:"overview-view",children:[O("div",{className:"kpi-grid",children:[O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"Token 输入"}),B("b",{className:"kpi-value",children:M0(F0(z.total))})]}),O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"Token 输出"}),B("b",{className:"kpi-value",children:M0(z.total.output)})]}),O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"会话数"}),B("b",{className:"kpi-value",children:G.length})]}),O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"事件总数"}),B("b",{className:"kpi-value",children:X?.totalEvents??0})]}),O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"提交数"}),B("b",{className:"kpi-value",children:A.length})]})]}),O("section",{className:"sum-section",children:[B("div",{className:"sum-head",children:B("h3",{children:"近期活动"})}),J.length===0?B("div",{className:"sum-empty",children:"暂无活动"}):B("ul",{className:"sum-list",children:J.map((H,W)=>O("li",{children:[B("span",{className:"sum-ts",children:SG(H.ts)}),B("span",{className:`tl-kind ${H.kind}`,children:H.kind==="event"?"事件":"提交"}),B("span",{className:"sum-cwd",title:H.cwd,children:PG(H.cwd)||"?"}),B("span",{className:"sum-subject",children:H.text})]},W))})]})]})}X0();var SU=20;function U4(){let{token:Y}=t(),G=MY(Y),[X,U]=j(null),[Z,A]=j(null),[z,J]=j(null),[H,W]=j(!1),[q,Q]=j(null);D0(()=>{let L=!1;return A(null),G("/api/report?since=0").then((V)=>!L&&U(V)).catch((V)=>!L&&A(String(V))),()=>{L=!0}},[G]),D0(()=>{if(X&&!z&&X.projects[0])J(X.projects[0].cwd)},[X,z]);let M=X?.projects.find((L)=>L.cwd===z)??null;return O("div",{className:"stats-view",children:[B("div",{className:"panel-header",style:{display:"flex",gap:"1.1rem",alignItems:"baseline",flexWrap:"wrap"},children:X?O(n,{children:[B("b",{children:"报表"}),O("span",{title:"软件版本",children:["v",X.version]}),O("span",{title:"git 用户",children:["\uD83D\uDC64 ",X.gitUser??"—"]}),O("span",{children:[X.totals.projects," 项目 · ",X.totals.sessions," 会话"]}),O("button",{type:"button",className:"tab",disabled:H,title:"手动上报报表到服务器(需先在「设置」配置上报地址)",style:{marginLeft:"auto"},onClick:async()=>{W(!0),Q(null);try{let L=await fetch(`${location.origin}/api/report/upload`,{method:"POST",headers:{Authorization:`Bearer ${Y}`}}),V=await L.json().catch(()=>({}));if(L.ok&&V.status==="ok")Q({ok:!0,text:"上报成功"});else if(L.ok&&V.status==="skipped")Q({ok:!1,text:`已跳过:${V.reason??"无 git 身份"}`});else Q({ok:!1,text:V.error??`HTTP ${L.status}`})}catch(L){Q({ok:!1,text:L instanceof Error?L.message:String(L)})}finally{W(!1),setTimeout(()=>Q(null),3000)}},children:["☁ ",H?"上报中…":"上报"]}),q&&B("span",{className:q.ok?"field-ok":"field-err",children:q.text})]}):B("span",{children:Z?`加载失败:${Z}`:"加载中…"})}),O("div",{className:"sessions-with-tree",children:[O("aside",{className:"sessions-tree-panel panel",children:[B("div",{className:"panel-header",children:O("h2",{children:["项目 · ",X?.projects.length??0]})}),!X||X.projects.length===0?O("div",{className:"empty-state",style:{padding:"2rem 1rem"},children:[B("span",{className:"es-hint",children:"暂无项目"}),B("span",{className:"es-sub",children:"启动 Claude Code 后会出现"})]}):B("ul",{className:"report-nav",children:X.projects.map((L)=>O("li",{className:L.cwd===z?"active":void 0,title:L.cwd,onClick:()=>J(L.cwd),style:{display:"flex",justifyContent:"space-between",alignItems:"baseline",gap:"0.5rem"},children:[B("span",{children:PG(L.cwd)||L.cwd}),B("span",{className:"nav-tokens",title:m0(L.totalTokens),children:M0(F0(L.totalTokens)+L.totalTokens.output)})]},L.cwd))})]}),B($U,{orient:"v",varName:"--tree-w"}),B("div",{className:"sessions-main",children:M?B(_M,{p:M},M.cwd):O("div",{className:"empty-state",children:[B(G0,{name:"log",size:30}),B("span",{className:"es-hint",children:"选左侧项目查看详情"})]})})]})]})}function _M({p:Y}){let[G,X]=j(1),U=Y.sessions.filter((J)=>J.tokenTotal&&F0(J.tokenTotal)+J.tokenTotal.output>0),Z=Math.max(1,Math.ceil(U.length/SU)),A=Math.min(G,Z),z=U.slice((A-1)*SU,A*SU);return O(n,{children:[O("div",{className:"report-title",children:[Y.gitRemote&&O("span",{className:"rt-sum",title:Y.gitRemote,style:{flex:"0 0 60ch",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},children:["\uD83D\uDD17 ",Y.gitRemote]}),B("span",{className:"rt-sum",title:m0(Y.totalTokens),style:{marginLeft:"auto",color:"#9dbefe"},children:VU(Y.totalTokens)})]}),O("div",{style:{overflow:"auto",flex:"1 1 0",minHeight:0},children:[O("table",{className:"report-table",children:[B("thead",{children:O("tr",{children:[B("th",{className:"rt-idx",children:"#"}),B("th",{children:"Session"}),B("th",{children:"时间"}),B("th",{className:"rt-num",children:"输入 token"}),B("th",{className:"rt-num",children:"输出 token"}),B("th",{className:"rt-num",children:"总数"})]})}),B("tbody",{children:z.map((J,H)=>O("tr",{children:[B("td",{className:"rt-idx",children:(A-1)*SU+H+1}),B("td",{className:"rt-sid",title:J.sessionId,children:J.sessionId.slice(0,8)}),B("td",{children:SG(J.lastActive)}),B("td",{className:"rt-num",children:M0(F0(J.tokenTotal))}),B("td",{className:"rt-num",children:M0(J.tokenTotal.output)}),B("td",{className:"rt-num",children:M0(F0(J.tokenTotal)+J.tokenTotal.output)})]},J.sessionId))})]}),U.length===0&&B("div",{className:"sum-empty",children:"无有效会话(均已隐藏 0 token)"})]}),O("div",{className:"report-pager",children:[B("button",{type:"button",disabled:A<=1,onClick:()=>X(A-1),children:"‹ 上一页"}),O("span",{children:["第 ",A," / ",Z," 页"]}),B("button",{type:"button",disabled:A>=Z,onClick:()=>X(A+1),children:"下一页 ›"}),O("span",{style:{marginLeft:"auto"},children:["共 ",U.length," 个会话(已隐藏 0 token)"]})]})]})}X0();function Z4(){let{token:Y}=t(),G=MY(Y),X=location.origin,[U,Z]=j(""),[A,z]=j(""),[J,H]=j(!0),[W,q]=j(""),[Q,M]=j(""),[L,V]=j(null),[K,E]=j(!0),[w,_]=j(!1),[R,D]=j(null);D0(()=>{Promise.all([G("/api/settings"),fetch(X+"/api/health").then((N)=>N.json())]).then(([N,f])=>{Z(N.reportUrl??""),z(N.reportIntervalMin!=null?String(N.reportIntervalMin):""),H(N.autoUpdate!==!1),q(N.autoUpdateIntervalMin!=null?String(N.autoUpdateIntervalMin):""),V(N.latestVersion??null),M(f.version??""),E(!1)}).catch(()=>{D({kind:"err",text:"读取设置失败"}),E(!1)})},[G,X]);let I=async()=>{_(!0),D(null);let N=parseInt(A,10),f=parseInt(W,10),b={reportUrl:U.trim()||null,reportIntervalMin:Number.isFinite(N)&&N>0?N:null,autoUpdate:J,autoUpdateIntervalMin:Number.isFinite(f)&&f>0?f:null};try{let $=await fetch(X+"/api/settings",{method:"PUT",headers:{Authorization:"Bearer "+Y,"content-type":"application/json"},body:JSON.stringify(b)});if(!$.ok)throw Error(String($.status));let S=await $.json();Z(S.reportUrl??""),z(S.reportIntervalMin!=null?String(S.reportIntervalMin):""),H(S.autoUpdate!==!1),q(S.autoUpdateIntervalMin!=null?String(S.autoUpdateIntervalMin):""),D({kind:"ok",text:"已保存"}),setTimeout(()=>D(null),2000)}catch{D({kind:"err",text:"保存失败,请重试"})}finally{_(!1)}};return O("div",{className:"stats-view",children:[B("div",{className:"panel-header",children:B("h2",{children:"设置"})}),B("div",{className:"stats-body",children:K?B("div",{className:"sum-empty",children:"加载中…"}):O(n,{children:[O("section",{className:"sum-section",children:[B("div",{className:"sum-head",children:B("h3",{children:"上报"})}),O("div",{className:"field-row",children:[B("label",{children:"上报地址"}),B("input",{className:"field-input",type:"url",placeholder:"https://your-server/api/report",value:U,onChange:(N)=>Z(N.target.value),spellCheck:!1})]}),O("div",{className:"field-row",children:[B("label",{children:"上报间隔"}),B("input",{className:"field-input",type:"number",min:1,placeholder:"0 = 不自动上报",value:A,onChange:(N)=>z(N.target.value),spellCheck:!1,style:{flex:"0 0 120px"}}),B("span",{className:"field-hint",style:{padding:0},children:"分钟(填了地址且间隔大于 0 才会自动上报)"})]}),B("div",{className:"field-hint",children:"daemon 每分钟检查一次:地址 + 间隔都配了,就把「报表」数据 POST 到该地址;留空 / 间隔 0 = 不上报。改完无需重启。"})]}),O("section",{className:"sum-section",children:[B("div",{className:"sum-head",children:B("h3",{children:"自动更新"})}),O("div",{className:"field-row",children:[B("label",{children:"版本"}),O("span",{className:"field-hint",style:{padding:0},children:["当前 v",Q||"?",L&&L!==Q?`(npm 最新 v${L})`:"(已是最新)"]})]}),O("div",{className:"field-row",children:[B("label",{children:"自动更新"}),B("input",{type:"checkbox",checked:J,onChange:(N)=>H(N.target.checked)}),B("span",{className:"field-hint",style:{padding:0},children:"开启后 daemon 启动时 + 定时检测 npm 新版本,有新版自动后台升级"})]}),O("div",{className:"field-row",children:[B("label",{children:"检测间隔"}),B("input",{className:"field-input",type:"number",min:1,placeholder:"60",value:W,onChange:(N)=>q(N.target.value),style:{flex:"0 0 120px"}}),B("span",{className:"field-hint",style:{padding:0},children:"分钟(daemon 每分钟 tick,按此间隔节流)"})]}),O("div",{className:"field-row",children:[B("label",{})," ",B("button",{type:"button",className:"tab",onClick:I,disabled:w,title:"保存设置",children:w?"保存中…":"保存"}),R&&B("span",{className:R.kind==="ok"?"field-ok":"field-err",children:R.text})]}),O("div",{className:"field-hint",children:["升级后 daemon 自动重启到新版(版本感知);plugin 需重启 Claude Code 生效。也可命令行手动 ",B("code",{children:"shine-code-submit update"}),"。"]})]})]})})]})}function A4(){let{stats:Y}=t();if(!Y)return B("div",{className:"empty-state",children:B("span",{className:"es-hint",children:"连接中…"})});let G=Math.floor(Y.uptime/1000);return B("div",{className:"system-view",children:O("section",{className:"sum-section",children:[B("div",{className:"sum-head",children:B("h3",{children:"Daemon"})}),O("div",{className:"sys-grid",children:[O("div",{className:"sys-row",children:[B("span",{children:"服务"}),B("b",{children:Y.service})]}),O("div",{className:"sys-row",children:[B("span",{children:"版本"}),O("b",{children:["v",Y.version]})]}),O("div",{className:"sys-row",children:[B("span",{children:"pid"}),B("b",{children:Y.pid})]}),O("div",{className:"sys-row",children:[B("span",{children:"uptime"}),O("b",{children:[G,"s"]})]}),O("div",{className:"sys-row",children:[B("span",{children:"事件总数"}),B("b",{children:Y.totalEvents})]}),O("div",{className:"sys-row",children:[B("span",{children:"事件速率"}),O("b",{children:[Y.eventsPerSec.toFixed(1)," evt/s"]})]}),O("div",{className:"sys-row",children:[B("span",{children:"spool 积压"}),B("b",{children:Y.spoolBacklog})]}),Y.lastError&&O("div",{className:"sys-row err",children:[B("span",{children:"最近错误"}),B("b",{children:Y.lastError.message})]})]})]})})}function LM(){let{activeModule:Y}=t();switch(Y){case"overview":return B(hz,{});case"sessions":return B(Y4,{});case"report":return B(U4,{});case"settings":return B(Z4,{});case"system":return B(A4,{});default:return B(hz,{})}}function wM(){let{navCollapsed:Y}=t();return O(n,{children:[B(fW,{}),O("div",{className:"body-middle",children:[B("aside",{id:"nav-panel",className:`panel${Y?" collapsed":""}`,children:B(KW,{})}),B("section",{id:"events-panel",className:"panel",children:B(LM,{})})]})]})}function z4({token:Y}){return B(RW,{token:Y,children:B(wM,{})})}var J4="shine_code_submit_token";function RM(){let Y=new URLSearchParams(location.search).get("t");if(Y)return sessionStorage.setItem(J4,Y),history.replaceState(null,"",location.pathname),Y;return sessionStorage.getItem(J4)}var H4=RM(),W4=document.getElementById("root");if(!H4)W4.innerHTML='<div style="padding:2rem;font-family:sans-serif;color:#ccc;background:#0f1115;min-height:100vh">'+"缺少 token。请通过 <code>shine-code-submit ui</code> 命令打开。</div>";else B4.createRoot(W4).render(B(PU,{children:B(z4,{token:H4})}));
76
+ `);if(J.current===W)return;if(J.current=W,H.length===0){Z([]);return}let q=!0;return z(!0),(async()=>{try{let Q=await Promise.all(H.map(async(L)=>{try{let V=await Y(`/api/commits?cwd=${encodeURIComponent(L)}&limit=200`);return{cwd:L,commits:V.commits??[]}}catch{return{cwd:L,commits:[]}}}));if(!q)return;let M=Q.flatMap((L)=>L.commits.map((V)=>({...V,cwd:L.cwd})));M.sort((L,V)=>V.time-L.time),Z(M)}finally{if(q)z(!1)}})(),()=>{q=!1}},[Y,G,X]),{commits:U,loading:A}}function hz(){let{token:Y,sessions:G,stats:X}=t(),U=MY(Y),{events:Z}=G4(U,null,!0),{commits:A}=X4(U,G,!0),z=f0(()=>fU(G.map((H)=>H.tokenTotal)),[G]),J=f0(()=>{let H=Z.slice(0,20).map((q)=>({ts:q.timestamp,kind:"event",text:q.type,cwd:q.cwd})),W=A.slice(0,20).map((q)=>({ts:q.time,kind:"commit",text:q.subject||"(无说明)",cwd:q.cwd}));return[...H,...W].sort((q,Q)=>Q.ts-q.ts).slice(0,20)},[Z,A]);return O("div",{className:"overview-view",children:[O("div",{className:"kpi-grid",children:[O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"Token 输入"}),B("b",{className:"kpi-value",children:M0(F0(z.total))})]}),O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"Token 输出"}),B("b",{className:"kpi-value",children:M0(z.total.output)})]}),O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"会话数"}),B("b",{className:"kpi-value",children:G.length})]}),O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"事件总数"}),B("b",{className:"kpi-value",children:X?.totalEvents??0})]}),O("div",{className:"kpi-card",children:[B("span",{className:"kpi-label",children:"提交数"}),B("b",{className:"kpi-value",children:A.length})]})]}),O("section",{className:"sum-section",children:[B("div",{className:"sum-head",children:B("h3",{children:"近期活动"})}),J.length===0?B("div",{className:"sum-empty",children:"暂无活动"}):B("ul",{className:"sum-list",children:J.map((H,W)=>O("li",{children:[B("span",{className:"sum-ts",children:SG(H.ts)}),B("span",{className:`tl-kind ${H.kind}`,children:H.kind==="event"?"事件":"提交"}),B("span",{className:"sum-cwd",title:H.cwd,children:PG(H.cwd)||"?"}),B("span",{className:"sum-subject",children:H.text})]},W))})]})]})}X0();var SU=20;function U4(){let{token:Y}=t(),G=MY(Y),[X,U]=j(null),[Z,A]=j(null),[z,J]=j(null),[H,W]=j(!1),[q,Q]=j(null);D0(()=>{let L=!1;return A(null),G("/api/report?since=0").then((V)=>!L&&U(V)).catch((V)=>!L&&A(String(V))),()=>{L=!0}},[G]),D0(()=>{if(X&&!z&&X.projects[0])J(X.projects[0].cwd)},[X,z]);let M=X?.projects.find((L)=>L.cwd===z)??null;return O("div",{className:"stats-view",children:[B("div",{className:"panel-header",style:{display:"flex",gap:"1.1rem",alignItems:"baseline",flexWrap:"wrap"},children:X?O(n,{children:[B("b",{children:"报表"}),O("span",{title:"软件版本",children:["v",X.version]}),O("span",{title:"git 用户",children:["\uD83D\uDC64 ",X.gitUser??"—"]}),O("span",{children:[X.totals.projects," 项目 · ",X.totals.sessions," 会话"]}),O("button",{type:"button",className:"tab",disabled:H,title:"手动上报报表到服务器(需先在「设置」配置上报地址)",style:{marginLeft:"auto"},onClick:async()=>{W(!0),Q(null);try{let L=await fetch(`${location.origin}/api/report/upload`,{method:"POST",headers:{Authorization:`Bearer ${Y}`}}),V=await L.json().catch(()=>({}));if(L.ok&&V.status==="ok")Q({ok:!0,text:"上报成功"});else if(L.ok&&V.status==="skipped")Q({ok:!1,text:`已跳过:${V.reason??"无 git 身份"}`});else Q({ok:!1,text:V.error??`HTTP ${L.status}`})}catch(L){Q({ok:!1,text:L instanceof Error?L.message:String(L)})}finally{W(!1),setTimeout(()=>Q(null),3000)}},children:["☁ ",H?"上报中…":"上报"]}),q&&B("span",{className:q.ok?"field-ok":"field-err",children:q.text})]}):B("span",{children:Z?`加载失败:${Z}`:"加载中…"})}),O("div",{className:"sessions-with-tree",children:[O("aside",{className:"sessions-tree-panel panel",children:[B("div",{className:"panel-header",children:O("h2",{children:["项目 · ",X?.projects.length??0]})}),!X||X.projects.length===0?O("div",{className:"empty-state",style:{padding:"2rem 1rem"},children:[B("span",{className:"es-hint",children:"暂无项目"}),B("span",{className:"es-sub",children:"启动 Claude Code 后会出现"})]}):B("ul",{className:"report-nav",children:X.projects.map((L)=>O("li",{className:L.cwd===z?"active":void 0,title:L.cwd,onClick:()=>J(L.cwd),style:{display:"flex",justifyContent:"space-between",alignItems:"baseline",gap:"0.5rem"},children:[B("span",{children:PG(L.cwd)||L.cwd}),B("span",{className:"nav-tokens",title:m0(L.totalTokens),children:M0(F0(L.totalTokens)+L.totalTokens.output)})]},L.cwd))})]}),B($U,{orient:"v",varName:"--tree-w"}),B("div",{className:"sessions-main",children:M?B(_M,{p:M},M.cwd):O("div",{className:"empty-state",children:[B(G0,{name:"log",size:30}),B("span",{className:"es-hint",children:"选左侧项目查看详情"})]})})]})]})}function _M({p:Y}){let[G,X]=j(1),U=Y.sessions.filter((J)=>J.tokenTotal&&F0(J.tokenTotal)+J.tokenTotal.output>0),Z=Math.max(1,Math.ceil(U.length/SU)),A=Math.min(G,Z),z=U.slice((A-1)*SU,A*SU);return O(n,{children:[O("div",{className:"report-title",children:[Y.gitRemote&&O("span",{className:"rt-sum",title:Y.gitRemote,style:{flex:"0 0 60ch",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},children:["\uD83D\uDD17 ",Y.gitRemote]}),B("span",{className:"rt-sum",title:m0(Y.totalTokens),style:{marginLeft:"auto",color:"#9dbefe"},children:VU(Y.totalTokens)}),O("span",{className:"rt-sum",title:`代码变更 +${Y.totalLines.added} -${Y.totalLines.deleted} M${Y.totalLines.modified}`,style:{color:"#9aa"},children:["+",Y.totalLines.added," -",Y.totalLines.deleted," M",Y.totalLines.modified]})]}),O("div",{style:{overflow:"auto",flex:"1 1 0",minHeight:0},children:[O("table",{className:"report-table",children:[B("thead",{children:O("tr",{children:[B("th",{className:"rt-idx",children:"#"}),B("th",{children:"Session"}),B("th",{children:"时间"}),B("th",{className:"rt-num",children:"输入 token"}),B("th",{className:"rt-num",children:"输出 token"}),B("th",{className:"rt-num",children:"总数"}),B("th",{className:"rt-num",children:"代码变更"})]})}),B("tbody",{children:z.map((J,H)=>O("tr",{children:[B("td",{className:"rt-idx",children:(A-1)*SU+H+1}),B("td",{className:"rt-sid",title:J.sessionId,children:J.sessionId.slice(0,8)}),B("td",{children:SG(J.lastActive)}),B("td",{className:"rt-num",children:M0(F0(J.tokenTotal))}),B("td",{className:"rt-num",children:M0(J.tokenTotal.output)}),B("td",{className:"rt-num",children:M0(F0(J.tokenTotal)+J.tokenTotal.output)}),B("td",{className:"rt-num",title:J.linesTotal?`+${J.linesTotal.added} -${J.linesTotal.deleted} M${J.linesTotal.modified}`:"",children:J.linesTotal?`+${J.linesTotal.added} -${J.linesTotal.deleted} M${J.linesTotal.modified}`:"-"})]},J.sessionId))})]}),U.length===0&&B("div",{className:"sum-empty",children:"无有效会话(均已隐藏 0 token)"})]}),O("div",{className:"report-pager",children:[B("button",{type:"button",disabled:A<=1,onClick:()=>X(A-1),children:"‹ 上一页"}),O("span",{children:["第 ",A," / ",Z," 页"]}),B("button",{type:"button",disabled:A>=Z,onClick:()=>X(A+1),children:"下一页 ›"}),O("span",{style:{marginLeft:"auto"},children:["共 ",U.length," 个会话(已隐藏 0 token)"]})]})]})}X0();function Z4(){let{token:Y}=t(),G=MY(Y),X=location.origin,[U,Z]=j(""),[A,z]=j(""),[J,H]=j(!0),[W,q]=j(""),[Q,M]=j(""),[L,V]=j(null),[K,E]=j(!0),[w,_]=j(!1),[R,D]=j(null);D0(()=>{Promise.all([G("/api/settings"),fetch(X+"/api/health").then((N)=>N.json())]).then(([N,f])=>{Z(N.reportUrl??""),z(N.reportIntervalMin!=null?String(N.reportIntervalMin):""),H(N.autoUpdate!==!1),q(N.autoUpdateIntervalMin!=null?String(N.autoUpdateIntervalMin):""),V(N.latestVersion??null),M(f.version??""),E(!1)}).catch(()=>{D({kind:"err",text:"读取设置失败"}),E(!1)})},[G,X]);let I=async()=>{_(!0),D(null);let N=parseInt(A,10),f=parseInt(W,10),b={reportUrl:U.trim()||null,reportIntervalMin:Number.isFinite(N)&&N>0?N:null,autoUpdate:J,autoUpdateIntervalMin:Number.isFinite(f)&&f>0?f:null};try{let $=await fetch(X+"/api/settings",{method:"PUT",headers:{Authorization:"Bearer "+Y,"content-type":"application/json"},body:JSON.stringify(b)});if(!$.ok)throw Error(String($.status));let S=await $.json();Z(S.reportUrl??""),z(S.reportIntervalMin!=null?String(S.reportIntervalMin):""),H(S.autoUpdate!==!1),q(S.autoUpdateIntervalMin!=null?String(S.autoUpdateIntervalMin):""),D({kind:"ok",text:"已保存"}),setTimeout(()=>D(null),2000)}catch{D({kind:"err",text:"保存失败,请重试"})}finally{_(!1)}};return O("div",{className:"stats-view",children:[B("div",{className:"panel-header",children:B("h2",{children:"设置"})}),B("div",{className:"stats-body",children:K?B("div",{className:"sum-empty",children:"加载中…"}):O(n,{children:[O("section",{className:"sum-section",children:[B("div",{className:"sum-head",children:B("h3",{children:"上报"})}),O("div",{className:"field-row",children:[B("label",{children:"上报地址"}),B("input",{className:"field-input",type:"url",placeholder:"https://your-server/api/report",value:U,onChange:(N)=>Z(N.target.value),spellCheck:!1})]}),O("div",{className:"field-row",children:[B("label",{children:"上报间隔"}),B("input",{className:"field-input",type:"number",min:1,placeholder:"0 = 不自动上报",value:A,onChange:(N)=>z(N.target.value),spellCheck:!1,style:{flex:"0 0 120px"}}),B("span",{className:"field-hint",style:{padding:0},children:"分钟(填了地址且间隔大于 0 才会自动上报)"})]}),B("div",{className:"field-hint",children:"daemon 每分钟检查一次:地址 + 间隔都配了,就把「报表」数据 POST 到该地址;留空 / 间隔 0 = 不上报。改完无需重启。"})]}),O("section",{className:"sum-section",children:[B("div",{className:"sum-head",children:B("h3",{children:"自动更新"})}),O("div",{className:"field-row",children:[B("label",{children:"版本"}),O("span",{className:"field-hint",style:{padding:0},children:["当前 v",Q||"?",L&&L!==Q?`(npm 最新 v${L})`:"(已是最新)"]})]}),O("div",{className:"field-row",children:[B("label",{children:"自动更新"}),B("input",{type:"checkbox",checked:J,onChange:(N)=>H(N.target.checked)}),B("span",{className:"field-hint",style:{padding:0},children:"开启后 daemon 启动时 + 定时检测 npm 新版本,有新版自动后台升级"})]}),O("div",{className:"field-row",children:[B("label",{children:"检测间隔"}),B("input",{className:"field-input",type:"number",min:1,placeholder:"60",value:W,onChange:(N)=>q(N.target.value),style:{flex:"0 0 120px"}}),B("span",{className:"field-hint",style:{padding:0},children:"分钟(daemon 每分钟 tick,按此间隔节流)"})]}),O("div",{className:"field-row",children:[B("label",{})," ",B("button",{type:"button",className:"tab",onClick:I,disabled:w,title:"保存设置",children:w?"保存中…":"保存"}),R&&B("span",{className:R.kind==="ok"?"field-ok":"field-err",children:R.text})]}),O("div",{className:"field-hint",children:["升级后 daemon 自动重启到新版(版本感知);plugin 需重启 Claude Code 生效。也可命令行手动 ",B("code",{children:"shine-code-submit update"}),"。"]})]})]})})]})}function A4(){let{stats:Y}=t();if(!Y)return B("div",{className:"empty-state",children:B("span",{className:"es-hint",children:"连接中…"})});let G=Math.floor(Y.uptime/1000);return B("div",{className:"system-view",children:O("section",{className:"sum-section",children:[B("div",{className:"sum-head",children:B("h3",{children:"Daemon"})}),O("div",{className:"sys-grid",children:[O("div",{className:"sys-row",children:[B("span",{children:"服务"}),B("b",{children:Y.service})]}),O("div",{className:"sys-row",children:[B("span",{children:"版本"}),O("b",{children:["v",Y.version]})]}),O("div",{className:"sys-row",children:[B("span",{children:"pid"}),B("b",{children:Y.pid})]}),O("div",{className:"sys-row",children:[B("span",{children:"uptime"}),O("b",{children:[G,"s"]})]}),O("div",{className:"sys-row",children:[B("span",{children:"事件总数"}),B("b",{children:Y.totalEvents})]}),O("div",{className:"sys-row",children:[B("span",{children:"事件速率"}),O("b",{children:[Y.eventsPerSec.toFixed(1)," evt/s"]})]}),O("div",{className:"sys-row",children:[B("span",{children:"spool 积压"}),B("b",{children:Y.spoolBacklog})]}),Y.lastError&&O("div",{className:"sys-row err",children:[B("span",{children:"最近错误"}),B("b",{children:Y.lastError.message})]})]})]})})}function LM(){let{activeModule:Y}=t();switch(Y){case"overview":return B(hz,{});case"sessions":return B(Y4,{});case"report":return B(U4,{});case"settings":return B(Z4,{});case"system":return B(A4,{});default:return B(hz,{})}}function wM(){let{navCollapsed:Y}=t();return O(n,{children:[B(fW,{}),O("div",{className:"body-middle",children:[B("aside",{id:"nav-panel",className:`panel${Y?" collapsed":""}`,children:B(KW,{})}),B("section",{id:"events-panel",className:"panel",children:B(LM,{})})]})]})}function z4({token:Y}){return B(RW,{token:Y,children:B(wM,{})})}var J4="shine_code_submit_token";function RM(){let Y=new URLSearchParams(location.search).get("t");if(Y)return sessionStorage.setItem(J4,Y),history.replaceState(null,"",location.pathname),Y;return sessionStorage.getItem(J4)}var H4=RM(),W4=document.getElementById("root");if(!H4)W4.innerHTML='<div style="padding:2rem;font-family:sans-serif;color:#ccc;background:#0f1115;min-height:100vh">'+"缺少 token。请通过 <code>shine-code-submit ui</code> 命令打开。</div>";else B4.createRoot(W4).render(B(PU,{children:B(z4,{token:H4})}));
@@ -172,6 +172,9 @@ function ProjectDetail({ p }: { p: ReportProject }) {
172
172
  <span className="rt-sum" title={fmtUsageFull(p.totalTokens)} style={{ marginLeft: "auto", color: "#9dbefe" }}>
173
173
  {fmtUsageLabeled(p.totalTokens)}
174
174
  </span>
175
+ <span className="rt-sum" title={`代码变更 +${p.totalLines.added} -${p.totalLines.deleted} M${p.totalLines.modified}`} style={{ color: "#9aa" }}>
176
+ +{p.totalLines.added} -{p.totalLines.deleted} M{p.totalLines.modified}
177
+ </span>
175
178
  </div>
176
179
 
177
180
  <div style={{ overflow: "auto", flex: "1 1 0", minHeight: 0 }}>
@@ -184,6 +187,7 @@ function ProjectDetail({ p }: { p: ReportProject }) {
184
187
  <th className="rt-num">输入 token</th>
185
188
  <th className="rt-num">输出 token</th>
186
189
  <th className="rt-num">总数</th>
190
+ <th className="rt-num">代码变更</th>
187
191
  </tr>
188
192
  </thead>
189
193
  <tbody>
@@ -197,6 +201,9 @@ function ProjectDetail({ p }: { p: ReportProject }) {
197
201
  <td className="rt-num">{fmtTokens(realInput(s.tokenTotal!))}</td>
198
202
  <td className="rt-num">{fmtTokens(s.tokenTotal!.output)}</td>
199
203
  <td className="rt-num">{fmtTokens(realInput(s.tokenTotal!) + s.tokenTotal!.output)}</td>
204
+ <td className="rt-num" title={s.linesTotal ? `+${s.linesTotal.added} -${s.linesTotal.deleted} M${s.linesTotal.modified}` : ""}>
205
+ {s.linesTotal ? `+${s.linesTotal.added} -${s.linesTotal.deleted} M${s.linesTotal.modified}` : "-"}
206
+ </td>
200
207
  </tr>
201
208
  ))}
202
209
  </tbody>