diffstalker 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +5 -3
- package/bun.lock +618 -0
- package/dist/App.js +541 -1
- package/dist/components/BaseBranchPicker.js +60 -1
- package/dist/components/BottomPane.js +101 -1
- package/dist/components/CommitPanel.js +58 -1
- package/dist/components/CompareListView.js +110 -1
- package/dist/components/ExplorerContentView.js +80 -3
- package/dist/components/ExplorerView.js +37 -1
- package/dist/components/FileList.js +131 -1
- package/dist/components/Footer.js +6 -1
- package/dist/components/Header.js +107 -1
- package/dist/components/HistoryView.js +21 -1
- package/dist/components/HotkeysModal.js +108 -1
- package/dist/components/Modal.js +19 -1
- package/dist/components/ScrollableList.js +125 -1
- package/dist/components/ThemePicker.js +42 -1
- package/dist/components/TopPane.js +14 -1
- package/dist/components/UnifiedDiffView.js +115 -1
- package/dist/config.js +83 -2
- package/dist/core/GitOperationQueue.js +109 -1
- package/dist/core/GitStateManager.js +466 -1
- package/dist/git/diff.js +471 -10
- package/dist/git/status.js +269 -5
- package/dist/hooks/useCommitFlow.js +66 -1
- package/dist/hooks/useCompareState.js +123 -1
- package/dist/hooks/useExplorerState.js +248 -9
- package/dist/hooks/useGit.js +156 -1
- package/dist/hooks/useHistoryState.js +62 -1
- package/dist/hooks/useKeymap.js +167 -1
- package/dist/hooks/useLayout.js +154 -1
- package/dist/hooks/useMouse.js +87 -1
- package/dist/hooks/useTerminalSize.js +20 -1
- package/dist/hooks/useWatcher.js +137 -11
- package/dist/index.js +43 -3
- package/dist/services/commitService.js +22 -1
- package/dist/themes.js +127 -1
- package/dist/utils/ansiTruncate.js +108 -0
- package/dist/utils/baseBranchCache.js +44 -2
- package/dist/utils/commitFormat.js +38 -1
- package/dist/utils/diffFilters.js +21 -1
- package/dist/utils/diffRowCalculations.js +113 -1
- package/dist/utils/displayRows.js +172 -2
- package/dist/utils/explorerDisplayRows.js +169 -0
- package/dist/utils/fileCategories.js +26 -1
- package/dist/utils/formatDate.js +39 -1
- package/dist/utils/formatPath.js +58 -1
- package/dist/utils/languageDetection.js +180 -0
- package/dist/utils/layoutCalculations.js +98 -1
- package/dist/utils/lineBreaking.js +88 -5
- package/dist/utils/mouseCoordinates.js +165 -1
- package/dist/utils/rowCalculations.js +209 -4
- package/package.json +7 -10
package/dist/index.js
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{jsx as
|
|
2
|
+
import{jsx as xJ}from"react/jsx-runtime";import{render as pJ}from"ink";import{jsx as X0,jsxs as IX}from"react/jsx-runtime";import{useState as Q0,useCallback as k0,useMemo as VY,useEffect as v8,useRef as FX}from"react";import{Box as j8,Text as n8,useApp as wJ,useInput as gJ}from"ink";import{jsx as t,Fragment as cY,jsxs as m}from"react/jsx-runtime";import{Box as B0,Text as u}from"ink";import*as q0 from"node:fs";import*as B8 from"node:path";import*as $X from"node:os";var W$={targetFile:B8.join($X.homedir(),".cache","diffstalker","target"),watcherEnabled:!1,debug:!1,theme:"dark"},A8=B8.join($X.homedir(),".config","diffstalker","config.json"),H$=["dark","light","dark-colorblind","light-colorblind","dark-ansi","light-ansi"];function I$(X){return typeof X==="string"&&H$.includes(X)}function mY(){let X={...W$};if(process.env.DIFFSTALKER_PAGER)X.pager=process.env.DIFFSTALKER_PAGER;if(q0.existsSync(A8))try{let Y=JSON.parse(q0.readFileSync(A8,"utf-8"));if(Y.pager)X.pager=Y.pager;if(Y.targetFile)X.targetFile=Y.targetFile;if(I$(Y.theme))X.theme=Y.theme;if(typeof Y.splitRatio==="number"&&Y.splitRatio>=0.15&&Y.splitRatio<=0.85)X.splitRatio=Y.splitRatio}catch{}return X}function pX(X){let Y=B8.dirname(A8);if(!q0.existsSync(Y))q0.mkdirSync(Y,{recursive:!0});let $={};if(q0.existsSync(A8))try{$=JSON.parse(q0.readFileSync(A8,"utf-8"))}catch{}Object.assign($,X),q0.writeFileSync(A8,JSON.stringify($,null,2)+`
|
|
3
|
+
`)}function fY(X){let Y=B8.dirname(X);if(!q0.existsSync(Y))q0.mkdirSync(Y,{recursive:!0})}function a0(X){let Y=$X.homedir();if(X.startsWith(Y))return"~"+X.slice(Y.length);return X}function lY(X,Y,$,J,Z=null,K=!1){if(!X)return 1;let Q=a0(X),V=Z==="Not a git repository",G=0;if(Y){if(G=Y.current.length,Y.tracking)G+=3+Y.tracking.length;if(Y.ahead>0)G+=3+String(Y.ahead).length;if(Y.behind>0)G+=3+String(Y.behind).length}let q=Q.length;if(K)q+=2;if(V)q+=24;if(Z&&!V)q+=Z.length+3;if($?.enabled&&$.sourceFile){let N=` (follow: ${a0($.sourceFile)})`,A=J-q-G-4;if(N.length>A){let E=J-q-2;if(N.length<=E)return 2}}return 1}function dY({branch:X}){return m(B0,{children:[t(u,{color:"green",bold:!0,children:X.current}),X.tracking&&m(cY,{children:[t(u,{dimColor:!0,children:" → "}),t(u,{color:"blue",children:X.tracking})]}),(X.ahead>0||X.behind>0)&&m(u,{children:[X.ahead>0&&m(u,{color:"green",children:[" ↑",X.ahead]}),X.behind>0&&m(u,{color:"red",children:[" ↓",X.behind]})]})]})}function nY({repoPath:X,branch:Y,isLoading:$,error:J,debug:Z,watcherState:K,width:Q=80}){if(!X)return m(B0,{flexDirection:"column",children:[m(B0,{children:[t(u,{dimColor:!0,children:"Waiting for target path..."}),t(u,{dimColor:!0,children:" (write path to ~/.cache/diffstalker/target)"})]}),Z&&K&&K.enabled&&K.sourceFile&&m(B0,{children:[m(u,{dimColor:!0,children:["[debug] source: ",a0(K.sourceFile)]}),K.rawContent&&m(u,{dimColor:!0,children:[' | raw: "',K.rawContent,'"']})]})]});let V=a0(X),G=J==="Not a git repository",q=(U)=>{if(!U)return"";return U.toLocaleTimeString()},z=0;if(Y){if(z=Y.current.length,Y.tracking)z+=3+Y.tracking.length;if(Y.ahead>0)z+=3+String(Y.ahead).length;if(Y.behind>0)z+=3+String(Y.behind).length}let N=V.length;if($)N+=2;if(G)N+=24;if(J&&!G)N+=J.length+3;let A=null,E=!1;if(K?.enabled&&K.sourceFile){let M=` (follow: ${a0(K.sourceFile)})`,O=Q-N-z-4;if(M.length<=O)A=M;else{let F=Q-N-2;if(M.length<=F)A=M,E=!0}}return m(B0,{flexDirection:"column",width:Q,children:[E?m(cY,{children:[t(B0,{justifyContent:"space-between",children:m(B0,{children:[t(u,{bold:!0,color:"cyan",children:V}),$&&t(u,{color:"yellow",children:" ⟳"}),G&&t(u,{color:"yellow",children:" (not a git repository)"}),J&&!G&&m(u,{color:"red",children:[" (",J,")"]}),A&&t(u,{dimColor:!0,children:A})]})}),t(B0,{justifyContent:"flex-end",children:Y&&t(dY,{branch:Y})})]}):m(B0,{justifyContent:"space-between",children:[m(B0,{children:[t(u,{bold:!0,color:"cyan",children:V}),$&&t(u,{color:"yellow",children:" ⟳"}),G&&t(u,{color:"yellow",children:" (not a git repository)"}),J&&!G&&m(u,{color:"red",children:[" (",J,")"]}),A&&t(u,{dimColor:!0,children:A})]}),Y&&t(dY,{branch:Y})]}),Z&&K&&K.enabled&&K.sourceFile&&m(B0,{children:[m(u,{dimColor:!0,children:["[debug] source: ",a0(K.sourceFile)]}),m(u,{dimColor:!0,children:[' | raw: "',K.rawContent,'"']}),K.lastUpdate&&m(u,{dimColor:!0,children:[" | updated: ",q(K.lastUpdate)]})]})]})}import{jsx as I0,jsxs as i0}from"react/jsx-runtime";import{Box as uX,Text as z0}from"ink";function v0(X,Y){if(X.length<=Y)return X;let J=Math.max(Y,20);if(X.length<=J)return X;let Z=X.split("/");if(Z.length===1){let N=Math.floor((J-1)/2);return X.slice(0,N)+"…"+X.slice(-(J-N-1))}let K=Z[Z.length-1],Q=Z[0],V="/…/";if(Q.length+V.length+K.length>J){let N=J-2;if(K.length>N){let A=Math.floor((N-1)/2);return"…/"+K.slice(0,A)+"…"+K.slice(-(N-A-1))}return"…/"+K}let q=Q,z=1;while(z<Z.length-1){let N=Z[z],A=q+"/"+N;if(A.length+V.length+K.length<=J)q=A,z++;else break}if(z===Z.length-1)return X;return q+V+K}function g0(X){let Y=X.filter((Z)=>!Z.staged&&Z.status!=="untracked"),$=X.filter((Z)=>!Z.staged&&Z.status==="untracked"),J=X.filter((Z)=>Z.staged);return{modified:Y,untracked:$,staged:J,ordered:[...Y,...$,...J]}}function b8(X){let{modified:Y,untracked:$,staged:J}=g0(X);return{modifiedCount:Y.length,untrackedCount:$.length,stagedCount:J.length}}function F$(X){switch(X){case"modified":return"M";case"added":return"A";case"deleted":return"D";case"untracked":return"?";case"renamed":return"R";case"copied":return"C";default:return" "}}function R$(X){switch(X){case"modified":return"yellow";case"added":return"green";case"deleted":return"red";case"untracked":return"gray";case"renamed":return"blue";case"copied":return"cyan";default:return"white"}}function D$(X,Y){if(X===void 0&&Y===void 0)return null;let $=X??0,J=Y??0;if($===0&&J===0)return null;let Z=[];if($>0)Z.push(`+${$}`);if(J>0)Z.push(`-${J}`);return Z.join(" ")}function _$({file:X,isSelected:Y,isFocused:$,maxPathLength:J}){let Z=F$(X.status),K=R$(X.status),Q=X.staged?"[-]":"[+]",V=X.staged?"red":"green",G=D$(X.insertions,X.deletions),q=Y&&$,z=G?G.length+1:0,N=J-z,A=v0(X.path,N);return i0(uX,{children:[I0(z0,{color:q?"cyan":void 0,bold:q,children:q?"▸ ":" "}),i0(z0,{color:V,children:[Q," "]}),i0(z0,{color:K,children:[Z," "]}),I0(z0,{color:q?"cyan":void 0,inverse:q,children:A}),X.originalPath&&i0(z0,{dimColor:!0,children:[" ← ",v0(X.originalPath,30)]}),G&&i0(z0,{children:[I0(z0,{dimColor:!0,children:" "}),X.insertions!==void 0&&X.insertions>0&&i0(z0,{color:"green",children:["+",X.insertions]}),X.insertions!==void 0&&X.insertions>0&&X.deletions!==void 0&&X.deletions>0&&I0(z0,{dimColor:!0,children:" "}),X.deletions!==void 0&&X.deletions>0&&i0(z0,{color:"red",children:["-",X.deletions]})]})]})}function rY({files:X,selectedIndex:Y,isFocused:$,scrollOffset:J=0,maxHeight:Z,width:K=80}){let Q=K-10,{modified:V,untracked:G,staged:q}=g0(X);if(X.length===0)return I0(uX,{flexDirection:"column",children:I0(z0,{dimColor:!0,children:" No changes"})});let z=[],N=0;if(V.length>0)z.push({type:"header",content:"Modified:",headerColor:"yellow"}),V.forEach((E)=>{z.push({type:"file",file:E,fileIndex:N++})});if(G.length>0){if(V.length>0)z.push({type:"spacer"});z.push({type:"header",content:"Untracked:",headerColor:"gray"}),G.forEach((E)=>{z.push({type:"file",file:E,fileIndex:N++})})}if(q.length>0){if(V.length>0||G.length>0)z.push({type:"spacer"});z.push({type:"header",content:"Staged:",headerColor:"green"}),q.forEach((E)=>{z.push({type:"file",file:E,fileIndex:N++})})}let A=Z?z.slice(J,J+Z):z.slice(J);return I0(uX,{flexDirection:"column",children:A.map((E,U)=>{let M=`row-${J+U}`;if(E.type==="header")return I0(z0,{bold:!0,color:E.headerColor,children:E.content},M);if(E.type==="spacer")return I0(z0,{children:" "},M);if(E.type==="file"&&E.file!==void 0&&E.fileIndex!==void 0)return I0(_$,{file:E.file,isSelected:E.fileIndex===Y,isFocused:$,maxPathLength:Q},M);return null})})}function hX(X,Y){let{ordered:$}=g0(X);return $[Y]??null}function oY(X){return X.length}import{jsx as j0,jsxs as nX,Fragment as iY}from"react/jsx-runtime";import{Box as C$,Text as p0}from"ink";import{jsxs as mX,jsx as fX}from"react/jsx-runtime";import{useMemo as v$}from"react";import{Box as sY,Text as JX}from"ink";function x0({items:X,renderItem:Y,maxHeight:$,scrollOffset:J,getKey:Z,header:K,showIndicators:Q=!0,getItemHeight:V}){let G=!!V,{itemRowStarts:q,totalRows:z}=v$(()=>{if(!G)return{itemRowStarts:[],totalRows:X.length};let H=[],B=0;for(let L=0;L<X.length;L++)H.push(B),B+=V(X[L],L);return{itemRowStarts:H,totalRows:B}},[X,V,G]),N=$;if(K)N--;let A=J>0,U=(G?z:X.length)>$;if(Q&&U)N-=2;N=Math.max(1,N);let M=[],O=0,F=0,k=0;if(G){let H=0;for(let B=0;B<X.length;B++){let L=V(X[B],B);if(q[B]+L>J){H=B;break}}for(let B=H;B<X.length&&O<N;B++){let L=V(X[B],B);M.push({item:X[B],index:B}),O+=L}F=J,k=Math.max(0,z-J-O)}else{let H=Math.min(J+N,X.length);for(let B=J;B<H;B++)M.push({item:X[B],index:B}),O++;F=J,k=Math.max(0,X.length-J-O)}return mX(sY,{flexDirection:"column",overflowX:"hidden",height:$,overflow:"hidden",children:[K,Q&&U&&(A?mX(JX,{dimColor:!0,children:["↑ ",F," more above"]}):fX(JX,{children:" "})),M.map(({item:H,index:B})=>fX(sY,{children:Y(H,B)},`${J}-${B}-${Z(H,B)}`)),Q&&U&&(k>0?mX(JX,{dimColor:!0,children:["↓ ",k," more below"]}):fX(JX,{children:" "}))]})}function E8(X,Y,$=!1,J=!0){let Z=Y;if($)Z--;if(J&&X>Z)Z-=2;return Z=Math.max(1,Z),Math.max(0,X-Z)}function ZX(X){let $=new Date().getTime()-X.getTime(),J=Math.floor($/3600000),Z=Math.floor($/86400000);if(J<1)return`${Math.floor($/60000)}m ago`;else if(J<48)return`${J}h ago`;else if(Z<=14)return`${Z}d ago`;else return X.toLocaleDateString("en-US",{month:"short",day:"numeric"})}function dX(X){return X.toLocaleString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function j$(X,Y){if(X.length<=Y)return X;if(Y<=3)return X.slice(0,Y);return X.slice(0,Y-3)+"..."}function KX(X,Y,$,J=20){let Z=Y||"",K=Math.max(0,$-J-1),Q=Z;if(Q.length>K&&K>3)Q=Q.slice(0,K-3)+"...";else if(Q.length>K)Q="";let V=Q?Q.length+1:0,G=Math.max(J,$-V);return{displayMessage:j$(X,G),displayRefs:Q}}function y$(X){return!(X.startsWith("index ")||X.startsWith("--- ")||X.startsWith("+++ ")||X.startsWith("similarity index"))}function w8(X){if(X.type!=="header")return!0;return y$(X.content)}function QX(X,Y,$=!0){if(Y<=0)return[{text:X,isContinuation:!1}];if(X.length<=Y)return[{text:X,isContinuation:!1}];let J=[],Z=X,K=!0;while(Z.length>0){if(Z.length<=Y){J.push({text:Z,isContinuation:!K});break}J.push({text:Z.slice(0,Y),isContinuation:!K}),Z=Z.slice(Y),K=!1}if($)P$(X,Y,J);return J}function P$(X,Y,$){let J=$.map((Z)=>Z.text).join("");if(J!==X)throw Error(`[LineBreaking] Content was lost during breaking!
|
|
4
|
+
Original (${X.length} chars): "${X.slice(0,50)}${X.length>50?"...":""}"
|
|
5
|
+
Joined (${J.length} chars): "${J.slice(0,50)}${J.length>50?"...":""}"`);for(let Z=0;Z<$.length;Z++){let K=$[Z];if(K.text.length>Y&&Y>=1)throw Error(`[LineBreaking] Segment ${Z} exceeds maxWidth!
|
|
6
|
+
Segment length: ${K.text.length}, maxWidth: ${Y}
|
|
7
|
+
Segment: "${K.text.slice(0,50)}${K.text.length>50?"...":""}"`)}if($.length>0&&$[0].isContinuation)throw Error("[LineBreaking] First segment incorrectly marked as continuation!");for(let Z=1;Z<$.length;Z++)if(!$[Z].isContinuation)throw Error(`[LineBreaking] Segment ${Z} should be marked as continuation but isn't!`)}function g8(X,Y){if(Y<=0)return 1;if(X.length<=Y)return 1;return Math.ceil(X.length/Y)}function cX(X,Y,$,J=0){let Z=X+J;if(Z<0||Z>=Y.length)return-1;return Z}function T$(X){if(!X||X.files.length===0)return{raw:"",lines:[]};let Y=[],$=[];for(let J of X.files){for(let Z of J.diff.lines)Y.push(Z);$.push(J.diff.raw)}return{raw:$.join(`
|
|
8
|
+
`),lines:Y}}function aY(X,Y){if(!X||Y<0||Y>=X.files.length)return 0;let $=T$(X),J=0,Z=0;for(let K of $.lines){if(K.type==="header"&&K.content.startsWith("diff --git")){if(Z===Y)return J;Z++}if(!w8(K))continue;J++}return 0}function lX(X,Y,$,J=!0,Z=!0){let K=0;if(Y>0){if(X===K)return-1;if(K++,J){if(X<K+Y)return X-K;K+=Y}}if($>0){if(Y>0){if(X===K)return-1;K++}if(X===K)return-1;if(K++,Z){if(X<K+$)return Y+(X-K)}}return-1}function tY({commits:X,selectedIndex:Y,scrollOffset:$,maxHeight:J,isActive:Z,width:K,onSelectCommit:Q}){if(X.length===0)return j0(C$,{children:j0(p0,{dimColor:!0,children:"No commits yet"})});return j0(x0,{items:X,maxHeight:J,scrollOffset:$,getKey:(V)=>V.hash,renderItem:(V,G)=>{let q=G===Y&&Z,z=ZX(V.date),N=11+z.length+2,A=K-N,{displayMessage:E,displayRefs:U}=KX(V.message,V.refs,A);return nX(iY,{children:[j0(p0,{color:"yellow",children:V.shortHash}),j0(p0,{children:" "}),j0(p0,{color:q?"cyan":void 0,bold:q,inverse:q,children:E}),j0(p0,{children:" "}),nX(p0,{dimColor:!0,children:["(",z,")"]}),U&&nX(iY,{children:[j0(p0,{children:" "}),j0(p0,{color:"green",children:U})]})]})}})}import{jsx as y0,Fragment as S$,jsxs as qX}from"react/jsx-runtime";import{Box as b$,Text as E0}from"ink";function eY({activeTab:X,mouseEnabled:Y=!0,autoTabEnabled:$=!1,wrapMode:J=!1,showMiddleDots:Z=!1}){return qX(b$,{justifyContent:"space-between",children:[qX(E0,{children:[y0(E0,{dimColor:!0,children:"?"})," ",y0(E0,{color:"yellow",children:Y?"[scroll]":"m:[select]"})," ",y0(E0,{color:$?"blue":void 0,dimColor:!$,children:"[auto]"})," ",y0(E0,{color:J?"blue":void 0,dimColor:!J,children:"[wrap]"}),X==="explorer"&&qX(S$,{children:[" ",y0(E0,{color:Z?"blue":void 0,dimColor:!Z,children:"[dots]"})]})]}),qX(E0,{children:[y0(E0,{color:X==="diff"?"cyan":void 0,bold:X==="diff",children:"[1]Diff"})," ",y0(E0,{color:X==="commit"?"cyan":void 0,bold:X==="commit",children:"[2]Commit"})," ",y0(E0,{color:X==="history"?"cyan":void 0,bold:X==="history",children:"[3]History"})," ",y0(E0,{color:X==="compare"?"cyan":void 0,bold:X==="compare",children:"[4]Compare"})]})]})}import{jsx as o,jsxs as e,Fragment as p8}from"react/jsx-runtime";import h$ from"react";import{Box as t0,Text as s}from"ink";import{jsx as n,jsxs as M0,Fragment as w$}from"react/jsx-runtime";import{useMemo as g$}from"react";import{Box as x8,Text as h}from"ink";function x$({commit:X,isSelected:Y,isActive:$,width:J}){let Z=ZX(X.date),K=13+Z.length+2,Q=J-K,{displayMessage:V,displayRefs:G}=KX(X.message,X.refs,Q);return M0(x8,{children:[n(h,{children:" "}),n(h,{color:"yellow",children:X.shortHash}),n(h,{children:" "}),n(h,{color:Y&&$?"cyan":void 0,bold:Y&&$,inverse:Y&&$,children:V}),n(h,{children:" "}),M0(h,{dimColor:!0,children:["(",Z,")"]}),G&&M0(w$,{children:[n(h,{children:" "}),n(h,{color:"green",children:G})]})]})}function p$({file:X,isSelected:Y,isActive:$,maxPathLength:J}){let Z={added:"green",modified:"yellow",deleted:"red",renamed:"blue"},K={added:"A",modified:"M",deleted:"D",renamed:"R"},Q=X.isUncommitted??!1,V=5+String(X.additions).length+String(X.deletions).length,G=Q?14:0,q=J-V-G;return M0(x8,{children:[n(h,{children:" "}),Q&&n(h,{color:"magenta",bold:!0,children:"*"}),n(h,{color:Q?"magenta":Z[X.status],bold:!0,children:K[X.status]}),M0(h,{bold:Y&&$,color:Y&&$?"cyan":Q?"magenta":void 0,inverse:Y&&$,children:[" ",v0(X.path,q)]}),n(h,{dimColor:!0,children:" ("}),M0(h,{color:"green",children:["+",X.additions]}),n(h,{dimColor:!0,children:" "}),M0(h,{color:"red",children:["-",X.deletions]}),n(h,{dimColor:!0,children:")"}),Q&&M0(h,{color:"magenta",dimColor:!0,children:[" ","[uncommitted]"]})]})}function X4({commits:X,files:Y,selectedItem:$,scrollOffset:J,maxHeight:Z,isActive:K,width:Q}){let z=g$(()=>{let N=[];if(X.length>0)N.push({type:"section-header",sectionType:"commits"}),X.forEach((A,E)=>{N.push({type:"commit",commitIndex:E,commit:A})});if(Y.length>0){if(X.length>0)N.push({type:"spacer"});N.push({type:"section-header",sectionType:"files"}),Y.forEach((A,E)=>{N.push({type:"file",fileIndex:E,file:A})})}return N},[X,Y,!0,!0]).slice(J,J+Z);if(X.length===0&&Y.length===0)return n(x8,{flexDirection:"column",children:n(h,{dimColor:!0,children:"No changes compared to base branch"})});return n(x8,{flexDirection:"column",children:z.map((N,A)=>{let E=`row-${J+A}`;if(N.type==="section-header"){let U=N.sectionType==="commits",M=U?!0:!0,O=U?X.length:Y.length;return M0(x8,{children:[M0(h,{bold:!0,color:"cyan",children:[M?"▼":"▶"," ",U?"Commits":"Files"]}),M0(h,{dimColor:!0,children:[" (",O,")"]})]},E)}if(N.type==="spacer")return n(h,{children:" "},E);if(N.type==="commit"&&N.commit!==void 0&&N.commitIndex!==void 0){let U=$?.type==="commit"&&$.index===N.commitIndex;return n(x$,{commit:N.commit,isSelected:U,isActive:K,width:Q},E)}if(N.type==="file"&&N.file!==void 0&&N.fileIndex!==void 0){let U=$?.type==="file"&&$.index===N.fileIndex;return n(p$,{file:N.file,isSelected:U,isActive:K,maxPathLength:Q-5},E)}return null})})}import{jsxs as u$,jsx as F0}from"react/jsx-runtime";import{Box as zX,Text as M8}from"ink";function Y4({currentPath:X,items:Y,selectedIndex:$,scrollOffset:J,maxHeight:Z,isActive:K,width:Q,isLoading:V=!1,error:G=null}){if(G)return F0(zX,{flexDirection:"column",children:u$(M8,{color:"red",children:["Error: ",G]})});if(V)return F0(zX,{flexDirection:"column",children:F0(M8,{dimColor:!0,children:"Loading..."})});if(Y.length===0)return F0(zX,{flexDirection:"column",children:F0(M8,{dimColor:!0,children:"(empty directory)"})});let q=Math.min(Math.max(...Y.map((z)=>z.name.length+(z.isDirectory?1:0))),Q-10);return F0(x0,{items:Y,maxHeight:Z,scrollOffset:J,getKey:(z)=>z.path||z.name,renderItem:(z,N)=>{let A=N===$&&K,U=(z.isDirectory?`${z.name}/`:z.name).padEnd(q+1);return F0(zX,{children:F0(M8,{color:A?"cyan":void 0,bold:A,inverse:A,children:z.isDirectory?F0(M8,{color:A?"cyan":"blue",children:U}):F0(M8,{color:A?"cyan":void 0,children:U})})})}})}function $4(X){if(!X)return[];return X.split("/").filter(Boolean)}function J4({bottomTab:X,currentPane:Y,terminalWidth:$,topPaneHeight:J,files:Z,selectedIndex:K,fileListScrollOffset:Q,stagedCount:V,onStage:G,onUnstage:q,commits:z,historySelectedIndex:N,historyScrollOffset:A,onSelectHistoryCommit:E,compareDiff:U,compareListSelection:M,compareScrollOffset:O,includeUncommitted:F,explorerCurrentPath:k="",explorerItems:H=[],explorerSelectedIndex:B=0,explorerScrollOffset:L=0,explorerIsLoading:R=!1,explorerError:S=null,hideHiddenFiles:P=!0,hideGitignored:W=!0}){let{modified:C,untracked:b}=g0(Z),w=C.length,g=b.length;return e(t0,{flexDirection:"column",height:J,width:$,overflowX:"hidden",overflowY:"hidden",children:[(X==="diff"||X==="commit")&&e(p8,{children:[e(t0,{children:[o(s,{bold:!0,color:Y==="files"?"cyan":void 0,children:"STAGING AREA"}),e(s,{dimColor:!0,children:[" ","(",w," modified, ",g," untracked, ",V," staged)"]})]}),o(rY,{files:Z,selectedIndex:K,isFocused:Y==="files",scrollOffset:Q,maxHeight:J-1,width:$,onStage:G,onUnstage:q})]}),X==="history"&&e(p8,{children:[e(t0,{children:[o(s,{bold:!0,color:Y==="history"?"cyan":void 0,children:"COMMITS"}),e(s,{dimColor:!0,children:[" (",z.length," commits)"]})]}),o(tY,{commits:z,selectedIndex:N,scrollOffset:A,maxHeight:J-1,isActive:Y==="history",width:$,onSelectCommit:E})]}),X==="compare"&&e(p8,{children:[e(t0,{children:[o(s,{bold:!0,color:Y==="compare"?"cyan":void 0,children:"COMPARE"}),o(s,{dimColor:!0,children:" (vs "}),o(s,{color:"cyan",children:U?.baseBranch??"..."}),e(s,{dimColor:!0,children:[": ",U?.commits.length??0," commits, ",U?.files.length??0," files) (b)"]}),U&&U.uncommittedCount>0&&e(p8,{children:[o(s,{dimColor:!0,children:" | "}),e(s,{color:F?"magenta":"yellow",children:["[",F?"x":" ","] uncommitted"]}),o(s,{dimColor:!0,children:" (u)"})]})]}),o(X4,{commits:U?.commits??[],files:U?.files??[],selectedItem:M,scrollOffset:O,maxHeight:J-1,isActive:Y==="compare",width:$})]}),X==="explorer"&&e(p8,{children:[e(t0,{justifyContent:"space-between",width:$,children:[e(t0,{children:[o(s,{bold:!0,color:Y==="explorer"?"cyan":void 0,children:"EXPLORER"}),o(s,{dimColor:!0,children:" "}),$4(k).map((p,f,_)=>e(h$.Fragment,{children:[o(s,{color:"blue",children:p}),f<_.length-1&&o(s,{dimColor:!0,children:" / "})]},f)),k&&o(s,{dimColor:!0,children:" /"}),!k&&o(s,{dimColor:!0,children:"(root)"})]}),o(t0,{children:(P||W)&&e(s,{dimColor:!0,children:[P&&"H",W&&"G"]})})]}),o(Y4,{currentPath:k,items:H,selectedIndex:B,scrollOffset:L,maxHeight:J-1,isActive:Y==="explorer",width:$,isLoading:R,error:S})]})]})}import{jsx as U0,jsxs as Y8}from"react/jsx-runtime";import{useMemo as R4}from"react";import{Box as I8,Text as O0}from"ink";import{jsxs as O8,jsx as c}from"react/jsx-runtime";import{useMemo as K4}from"react";import{Box as k8,Text as a}from"ink";var m$={name:"dark",displayName:"Dark",colors:{addBg:"#022800",delBg:"#3D0100",addHighlight:"#044700",delHighlight:"#5C0200",text:"white",addLineNum:"#368F35",delLineNum:"#A14040",contextLineNum:"gray",addSymbol:"greenBright",delSymbol:"redBright"}},f$={name:"light",displayName:"Light",colors:{addBg:"#69db7c",delBg:"#ffa8b4",addHighlight:"#2f9d44",delHighlight:"#d1454b",text:"black",addLineNum:"#2f9d44",delLineNum:"#d1454b",contextLineNum:"#6c757d",addSymbol:"green",delSymbol:"red"}},d$={name:"dark-colorblind",displayName:"Dark (colorblind)",colors:{addBg:"#004466",delBg:"#660000",addHighlight:"#0077b3",delHighlight:"#b30000",text:"white",addLineNum:"#0077b3",delLineNum:"#b30000",contextLineNum:"gray",addSymbol:"cyanBright",delSymbol:"redBright"}},c$={name:"light-colorblind",displayName:"Light (colorblind)",colors:{addBg:"#99ccff",delBg:"#ffcccc",addHighlight:"#3366cc",delHighlight:"#993333",text:"black",addLineNum:"#3366cc",delLineNum:"#993333",contextLineNum:"#6c757d",addSymbol:"blue",delSymbol:"red"}},l$={name:"dark-ansi",displayName:"Dark (ANSI)",colors:{addBg:"green",delBg:"red",addHighlight:"greenBright",delHighlight:"redBright",text:"white",addLineNum:"greenBright",delLineNum:"redBright",contextLineNum:"gray",addSymbol:"greenBright",delSymbol:"redBright"}},n$={name:"light-ansi",displayName:"Light (ANSI)",colors:{addBg:"green",delBg:"red",addHighlight:"greenBright",delHighlight:"redBright",text:"black",addLineNum:"green",delLineNum:"red",contextLineNum:"gray",addSymbol:"green",delSymbol:"red"}},VX={dark:m$,light:f$,"dark-colorblind":d$,"light-colorblind":c$,"dark-ansi":l$,"light-ansi":n$},e0=["dark","light","dark-colorblind","light-colorblind","dark-ansi","light-ansi"];function UX(X){return VX[X]??VX.dark}function rX(X){if(X.type==="addition"||X.type==="deletion")return X.content.slice(1);if(X.type==="context")return X.content.startsWith(" ")?X.content.slice(1):X.content;return X.content}function r$(X){switch(X.type){case"header":return{type:"diff-header",content:X.content};case"hunk":return{type:"diff-hunk",content:X.content};case"addition":return{type:"diff-add",lineNum:X.newLineNum,content:rX(X)};case"deletion":return{type:"diff-del",lineNum:X.oldLineNum,content:rX(X)};case"context":return{type:"diff-context",lineNum:X.oldLineNum??X.newLineNum,content:rX(X)}}}function X8(X){if(!X)return[];return X.lines.filter(w8).map(r$)}function GX(X,Y){let $=[];if(X){$.push({type:"commit-header",content:`commit ${X.hash}`}),$.push({type:"commit-header",content:`Author: ${X.author}`}),$.push({type:"commit-header",content:`Date: ${dX(X.date)}`}),$.push({type:"spacer"});for(let J of X.message.split(`
|
|
9
|
+
`))$.push({type:"commit-message",content:` ${J}`});$.push({type:"spacer"})}return $.push(...X8(Y)),$}function NX(X){if(!X||X.files.length===0)return[];let Y=[];for(let $ of X.files)Y.push(...X8($.diff));return Y}function R0(X){let Y=0;for(let $ of X)if("lineNum"in $&&$.lineNum!==void 0)Y=Math.max(Y,$.lineNum);return Math.max(3,String(Y).length)}function Z4(X,Y,$){if(!$)return X;let Z=Math.max(10,Y),K=[];for(let Q of X)if(Q.type==="diff-add"||Q.type==="diff-del"||Q.type==="diff-context"){let V=Q.content;if(!V||V.length<=Z){K.push(Q);continue}let G=QX(V,Z);for(let q=0;q<G.length;q++){let z=G[q];K.push({...Q,content:z.text,lineNum:z.isContinuation?void 0:Q.lineNum,isContinuation:z.isContinuation})}}else K.push(Q);return K}function L8(X,Y,$){if(!$)return X.length;let Z=Math.max(10,Y),K=0;for(let Q of X)if(Q.type==="diff-add"||Q.type==="diff-del"||Q.type==="diff-context"){let V=Q.content;if(!V||V.length<=Z)K+=1;else K+=g8(V,Z)}else K+=1;return K}function P0(X,Y){if(Y<=0||X.length<=Y)return X;if(Y<=1)return"…";return X.slice(0,Y-1)+"…"}function oX(X,Y){if(X===void 0)return" ".repeat(Y);return String(X).padStart(Y," ")}function o$({row:X,lineNumWidth:Y,width:$,theme:J,wrapMode:Z}){let{colors:K}=J,Q=$-Y-5,V=$-2;switch(X.type){case"diff-header":{let G=X.content;if(G.startsWith("diff --git")){let q=G.match(/diff --git a\/.+ b\/(.+)$/);if(q){let z=V-6,N=P0(q[1],z);return O8(a,{color:"cyan",bold:!0,children:["── ",N," ──"]})}}return c(a,{dimColor:!0,children:P0(G,V)})}case"diff-hunk":{let G=X.content.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)$/);if(G){let q=parseInt(G[1],10),z=G[2]?parseInt(G[2],10):1,N=parseInt(G[3],10),A=G[4]?parseInt(G[4],10):1,E=G[5].trim(),U=q+z-1,M=N+A-1,O=z===1?`${q}`:`${q}-${U}`,F=A===1?`${N}`:`${N}-${M}`,k=`Lines ${O} → ${F}`,H=V-k.length-1,B=E&&H>3?" "+P0(E,H):"";return O8(k8,{children:[c(a,{color:"cyan",dimColor:!0,children:k}),B&&c(a,{color:"gray",children:B})]})}return c(a,{color:"cyan",dimColor:!0,children:P0(X.content,V)})}case"diff-add":{let G=X.isContinuation,q=G?"»":"+",N=" "+(Z?X.content||"":P0(X.content,Q)||"")||" ";return O8(k8,{children:[c(a,{backgroundColor:K.addBg,color:K.addLineNum,children:oX(X.lineNum,Y)+" "}),c(a,{backgroundColor:K.addBg,color:G?K.addLineNum:K.addSymbol,bold:!G,children:q}),c(a,{backgroundColor:K.addBg,color:K.text,children:N})]})}case"diff-del":{let G=X.isContinuation,q=G?"»":"-",N=" "+(Z?X.content||"":P0(X.content,Q)||"")||" ";return O8(k8,{children:[c(a,{backgroundColor:K.delBg,color:K.delLineNum,children:oX(X.lineNum,Y)+" "}),c(a,{backgroundColor:K.delBg,color:G?K.delLineNum:K.delSymbol,bold:!G,children:q}),c(a,{backgroundColor:K.delBg,color:K.text,children:N})]})}case"diff-context":{let q=X.isContinuation?"» ":" ",z=Z?X.content:P0(X.content,Q);return O8(k8,{children:[O8(a,{color:K.contextLineNum,children:[oX(X.lineNum,Y)," "]}),c(a,{dimColor:!0,children:q}),c(a,{children:z})]})}case"commit-header":return c(a,{color:"yellow",children:P0(X.content,V)});case"commit-message":return c(a,{children:P0(X.content,V)});case"spacer":return c(a,{children:" "})}}function Q4({rows:X,maxHeight:Y,scrollOffset:$,theme:J,width:Z,wrapMode:K=!1}){let Q=K4(()=>UX(J),[J]),V=K4(()=>R0(X),[X]);if(X.length===0)return c(k8,{paddingX:1,children:c(a,{dimColor:!0,children:"No diff to display"})});return c(k8,{flexDirection:"column",paddingX:1,width:Z,children:c(x0,{items:X,maxHeight:Y,scrollOffset:$,getKey:(G,q)=>`row-${q}`,renderItem:(G)=>c(o$,{row:G,lineNumWidth:V,width:Z,theme:Q,wrapMode:K})})})}import{jsx as V0,jsxs as h8}from"react/jsx-runtime";import{useEffect as a$}from"react";import{Box as u0,Text as T0,useInput as i$}from"ink";import t$ from"ink-text-input";import{useState as u8,useCallback as sX,useEffect as s$}from"react";function q4(X,Y,$){if(!X.trim())return{valid:!1,error:"Commit message cannot be empty"};if(Y===0&&!$)return{valid:!1,error:"No changes staged for commit"};return{valid:!0,error:null}}function z4(X){return X.trim()}function V4(X){let{stagedCount:Y,onCommit:$,onSuccess:J,getHeadMessage:Z}=X,[K,Q]=u8(""),[V,G]=u8(!1),[q,z]=u8(!1),[N,A]=u8(null),[E,U]=u8(!1);s$(()=>{if(V)Z().then((k)=>{if(k&&!K)Q(k)})},[V,Z]);let M=sX(()=>{G((k)=>!k)},[]),O=sX(async()=>{let k=q4(K,Y,V);if(!k.valid){A(k.error);return}z(!0),A(null);try{await $(z4(K),V),Q(""),G(!1),J()}catch(H){A(H instanceof Error?H.message:"Commit failed")}finally{z(!1)}},[K,Y,V,$,J]),F=sX(()=>{Q(""),G(!1),A(null),U(!1)},[]);return{message:K,amend:V,isCommitting:q,error:N,inputFocused:E,setMessage:Q,toggleAmend:M,setInputFocused:U,handleSubmit:O,reset:F}}function U4({isActive:X,stagedCount:Y,onCommit:$,onCancel:J,getHeadMessage:Z,onInputFocusChange:K}){let{message:Q,amend:V,isCommitting:G,error:q,inputFocused:z,setMessage:N,toggleAmend:A,setInputFocused:E,handleSubmit:U}=V4({stagedCount:Y,onCommit:$,onSuccess:J,getHeadMessage:Z});if(a$(()=>{K?.(z)},[z,K]),i$((M,O)=>{if(!X)return;if(O.escape){if(z)E(!1);else J();return}if(!z){if(M==="i"||O.return){E(!0);return}if(M==="a"){A();return}return}if(M==="a"&&!Q){A();return}},{isActive:X}),!X)return V0(u0,{paddingX:1,children:V0(T0,{dimColor:!0,children:"Press '2' or 'c' to open commit panel"})});return h8(u0,{flexDirection:"column",paddingX:1,children:[h8(u0,{marginBottom:1,children:[V0(T0,{bold:!0,children:"Commit Message"}),V&&V0(T0,{color:"yellow",children:" (amending)"})]}),V0(u0,{borderStyle:"round",borderColor:z?"cyan":void 0,paddingX:1,children:z?V0(t$,{value:Q,onChange:N,onSubmit:U,placeholder:"Enter commit message..."}):V0(T0,{dimColor:!Q,children:Q||"Press i or Enter to edit..."})}),h8(u0,{marginTop:1,gap:2,children:[h8(T0,{color:V?"green":"gray",children:["[",V?"x":" ","] Amend"]}),V0(T0,{dimColor:!0,children:"(a)"})]}),q&&V0(u0,{marginTop:1,children:V0(T0,{color:"red",children:q})}),G&&V0(u0,{marginTop:1,children:V0(T0,{color:"yellow",children:"Committing..."})}),V0(u0,{marginTop:1,children:h8(T0,{dimColor:!0,children:["Staged: ",Y," file(s) |"," ",z?"Enter: commit | Esc: unfocus":"i/Enter: edit | Esc: cancel | 1/3: switch tab"]})})]})}import{jsx as L0,jsxs as H4}from"react/jsx-runtime";import{useMemo as eX}from"react";import{Box as W8,Text as H8}from"ink";import{createEmphasize as e$}from"emphasize";import{common as XJ}from"lowlight";var A4=e$(XJ),YJ={ts:"typescript",tsx:"typescript",js:"javascript",jsx:"javascript",mjs:"javascript",cjs:"javascript",html:"xml",htm:"xml",xml:"xml",svg:"xml",css:"css",scss:"scss",sass:"scss",less:"less",json:"json",yaml:"yaml",yml:"yaml",toml:"ini",sh:"bash",bash:"bash",zsh:"bash",fish:"bash",ps1:"powershell",bat:"dos",cmd:"dos",c:"c",h:"c",cpp:"cpp",hpp:"cpp",cc:"cpp",cxx:"cpp",rs:"rust",go:"go",zig:"zig",java:"java",kt:"kotlin",kts:"kotlin",scala:"scala",groovy:"groovy",gradle:"groovy",py:"python",rb:"ruby",pl:"perl",lua:"lua",php:"php",r:"r",hs:"haskell",ml:"ocaml",fs:"fsharp",fsx:"fsharp",ex:"elixir",exs:"elixir",erl:"erlang",clj:"clojure",cljs:"clojure",cs:"csharp",vb:"vbnet",md:"markdown",markdown:"markdown",rst:"plaintext",txt:"plaintext",Makefile:"makefile",Dockerfile:"dockerfile",cmake:"cmake",ini:"ini",conf:"ini",cfg:"ini",sql:"sql",vim:"vim",diff:"diff",patch:"diff"},G4={Makefile:"makefile",makefile:"makefile",GNUmakefile:"makefile",Dockerfile:"dockerfile",dockerfile:"dockerfile",Jenkinsfile:"groovy",Vagrantfile:"ruby",Gemfile:"ruby",Rakefile:"ruby",".gitignore":"plaintext",".gitattributes":"plaintext",".editorconfig":"ini",".prettierrc":"json",".eslintrc":"json","tsconfig.json":"json","package.json":"json","package-lock.json":"json","bun.lockb":"plaintext","yarn.lock":"yaml","pnpm-lock.yaml":"yaml","Cargo.toml":"ini","Cargo.lock":"ini","go.mod":"go","go.sum":"plaintext"},aX=null;function N4(){if(!aX)aX=new Set(A4.listLanguages());return aX}function B4(X){if(!X)return null;let Y=X.split("/").pop()??"";if(G4[Y]){let Z=G4[Y];return N4().has(Z)?Z:null}let $=Y.includes(".")?Y.split(".").pop()?.toLowerCase():null;if(!$)return null;let J=YJ[$];if(!J)return null;return N4().has(J)?J:null}function E4(X,Y){if(!X||!Y)return X;try{return A4.highlight(Y,X).value}catch{return X}}function iX(X,Y,$){if(!X)return[];let J=[],Z=X.split(`
|
|
10
|
+
`),K=Y?B4(Y):null;for(let Q=0;Q<Z.length;Q++){let V=Z[Q],G=K?E4(V,K):void 0;J.push({type:"code",lineNum:Q+1,content:V,highlighted:G})}if($)J.push({type:"truncation",content:"(file truncated)"});return J}function M4(X,Y,$){if(!$)return X;let Z=Math.max(10,Y),K=[];for(let Q of X)if(Q.type==="code"){let V=Q.content;if(!V||V.length<=Z){K.push(Q);continue}let G=QX(V,Z);for(let q=0;q<G.length;q++){let z=G[q];K.push({type:"code",lineNum:z.isContinuation?0:Q.lineNum,content:z.text,highlighted:void 0,isContinuation:z.isContinuation})}}else K.push(Q);return K}function L4(X,Y,$){if(!$)return X.length;let Z=Math.max(10,Y),K=0;for(let Q of X)if(Q.type==="code"){let V=Q.content;if(!V||V.length<=Z)K+=1;else K+=g8(V,Z)}else K+=1;return K}function tX(X){let Y=0;for(let $ of X)if($.type==="code"&&$.lineNum>Y)Y=$.lineNum;return Math.max(3,String(Y).length)}function O4(X,Y){if(!Y||!X)return X;let $=0;for(let K of X)if(K===" ")$++;else if(K==="\t")$+=2;else break;if($===0)return X;let J="·".repeat($),Z=X.slice($);return J+Z}var k4=/\x1b\[[0-9;]*m/g;function W4(X,Y,$="…"){if(Y<=0)return $;if(!X.includes("\x1B")&&X.length<=Y)return X;if(!X.includes("\x1B")){if(X.length<=Y)return X;return X.slice(0,Y-$.length)+$}let J=[],Z=0;k4.lastIndex=0;let K;while((K=k4.exec(X))!==null){if(K.index>Z)J.push({type:"text",content:X.slice(Z,K.index)});J.push({type:"ansi",content:K[0]}),Z=K.index+K[0].length}if(Z<X.length)J.push({type:"text",content:X.slice(Z)});let Q="",V=0,G=Y-$.length,q=!1,z=!1;for(let N of J)if(N.type==="ansi")Q+=N.content,q=!0;else{let A=G-V;if(A<=0){z=!0;break}if(N.content.length<=A)Q+=N.content,V+=N.content.length;else{Q+=N.content.slice(0,A),V+=A,z=!0;break}}if(z){if(q)Q+="\x1B[0m";Q+=$}return Q}function I4({filePath:X,content:Y,maxHeight:$,scrollOffset:J,truncated:Z=!1,wrapMode:K=!1,width:Q,showMiddleDots:V=!1}){let G=eX(()=>iX(Y,X,Z),[Y,X,Z]),q=eX(()=>tX(G),[G]),z=Q-q-3,N=eX(()=>M4(G,z,K),[G,z,K]);if(!X)return L0(W8,{paddingX:1,children:L0(H8,{dimColor:!0,children:"Select a file to view its contents"})});if(!Y)return L0(W8,{paddingX:1,children:L0(H8,{dimColor:!0,children:"Loading..."})});if(N.length===0)return L0(W8,{paddingX:1,children:L0(H8,{dimColor:!0,children:"(empty file)"})});return L0(W8,{flexDirection:"column",paddingX:1,children:L0(x0,{items:N,maxHeight:$,scrollOffset:J,getKey:(A,E)=>`${E}`,renderItem:(A)=>{if(A.type==="truncation")return L0(W8,{children:L0(H8,{color:"yellow",dimColor:!0,children:A.content})});let E=A.isContinuation??!1,U;if(E)U=">>".padStart(q," ");else U=String(A.lineNum).padStart(q," ");let M=A.content,O=!K&&M.length>z,F=A.highlighted&&!E&&!V,k;if(F&&A.highlighted)k=O?W4(A.highlighted,z):A.highlighted;else{let H=M;if(V&&!E)H=O4(H,!0);if(O)H=H.slice(0,Math.max(0,z-1))+"…";k=H}return H4(W8,{children:[H4(H8,{dimColor:!E,color:E?"cyan":void 0,children:[U," "]}),L0(H8,{dimColor:V&&!F,children:k||" "})]})}})})}function F4(X,Y,$,J,Z){if(!X)return 0;let K=iX(X,Y,$),Q=tX(K),V=J-Q-3;return L4(K,V,Z)}function D4({bottomTab:X,currentPane:Y,terminalWidth:$,bottomPaneHeight:J,diffScrollOffset:Z,currentTheme:K,diff:Q,selectedFile:V,stagedCount:G,onCommit:q,onCommitCancel:z,getHeadCommitMessage:N,onCommitInputFocusChange:A,historySelectedCommit:E,historyCommitDiff:U,compareDiff:M,compareLoading:O,compareError:F,compareListSelection:k,compareSelectionDiff:H,wrapMode:B,explorerSelectedFile:L=null,explorerFileScrollOffset:R=0,showMiddleDots:S=!1}){let P=Y!=="files"&&Y!=="history"&&Y!=="compare"&&Y!=="explorer",W=R4(()=>{if(X==="diff")return X8(Q);if(X==="history")return GX(E,U);if(X==="compare"){if(k?.type==="commit"&&H)return X8(H);return NX(M)}return[]},[X,Q,E,U,k,H,M]),C=R4(()=>{if(!B||W.length===0)return W;let g=R0(W),p=$-g-5;return Z4(W,p,B)},[W,$,B]),b=()=>{if(V&&X==="diff")return U0(O0,{dimColor:!0,children:v0(V.path,$-10)});if(X==="history"&&E)return Y8(O0,{dimColor:!0,children:[E.shortHash," - ",E.message.slice(0,50)]});if(X==="compare"&&k)if(k.type==="commit"){let g=M?.commits[k.index];return Y8(O0,{dimColor:!0,children:[g?.shortHash??""," - ",g?.message.slice(0,40)??""]})}else{let g=M?.files[k.index]?.path??"";return U0(O0,{dimColor:!0,children:v0(g,$-10)})}if(X==="explorer"&&L)return U0(O0,{dimColor:!0,children:v0(L.path,$-10)});return null},w=()=>{if(X==="commit")return U0(U4,{isActive:Y==="commit",stagedCount:G,onCommit:q,onCancel:z,getHeadMessage:N,onInputFocusChange:A});if(X==="compare"){if(O)return U0(O0,{dimColor:!0,children:"Loading compare diff..."});if(F)return U0(O0,{color:"red",children:F});if(!M)return U0(O0,{dimColor:!0,children:"No base branch found (no origin/main or origin/master)"});if(M.files.length===0)return Y8(O0,{dimColor:!0,children:["No changes compared to ",M.baseBranch]})}return U0(Q4,{rows:C,maxHeight:J-1,scrollOffset:Z,theme:K,width:$,wrapMode:B})};if(X==="explorer")return Y8(I8,{flexDirection:"column",height:J,width:$,overflowY:"hidden",children:[Y8(I8,{width:$,children:[U0(O0,{bold:!0,color:P?"cyan":void 0,children:"FILE"}),U0(I8,{flexGrow:1,justifyContent:"flex-end",children:b()})]}),U0(I4,{filePath:L?.path??null,content:L?.content??null,maxHeight:J-1,scrollOffset:R,truncated:L?.truncated,wrapMode:B,width:$,showMiddleDots:S})]});return Y8(I8,{flexDirection:"column",height:J,width:$,overflowX:"hidden",overflowY:"hidden",children:[Y8(I8,{width:$,children:[U0(O0,{bold:!0,color:P?"cyan":void 0,children:X==="commit"?"COMMIT":"DIFF"}),U0(I8,{flexGrow:1,justifyContent:"flex-end",children:b()})]}),w()]})}import{useState as _4,useEffect as v4,useRef as j4}from"react";import*as $8 from"node:fs";import*as h0 from"node:path";import*as XY from"node:os";import{watch as $J}from"chokidar";function y4(X){if(X.startsWith("~/"))return h0.join(XY.homedir(),X.slice(2));if(X==="~")return XY.homedir();return X}function P4(X){let Y=X.split(`
|
|
11
|
+
`);for(let $=Y.length-1;$>=0;$--){let J=Y[$].trim();if(J)return J}return""}function T4(X,Y,$=!1){let[J,Z]=_4(X),[K,Q]=_4({path:null,lastUpdate:null,rawContent:null,sourceFile:X?Y:null,enabled:X}),V=j4(null),G=j4(null);return v4(()=>{Q((q)=>({...q,enabled:J,sourceFile:J?Y:null}))},[J,Y]),v4(()=>{if(!J)return;if(fY(Y),!$8.existsSync(Y))$8.writeFileSync(Y,"");let q=()=>{if(V.current)clearTimeout(V.current);V.current=setTimeout(()=>{try{let N=$8.readFileSync(Y,"utf-8"),A=P4(N);if(A&&A!==G.current){let E=y4(A),U=h0.isAbsolute(E)?E:h0.resolve(E),M=new Date;if($)process.stderr.write(`[diffstalker ${M.toISOString()}] Path change detected
|
|
12
|
+
`),process.stderr.write(` Source file: ${Y}
|
|
13
|
+
`),process.stderr.write(` Raw content: "${A}"
|
|
14
|
+
`),process.stderr.write(` Previous: "${G.current??"(none)"}"
|
|
15
|
+
`),process.stderr.write(` Resolved: "${U}"
|
|
16
|
+
`);G.current=U,Q({path:U,lastUpdate:M,rawContent:A,sourceFile:Y,enabled:!0})}}catch{}},100)};try{let N=$8.readFileSync(Y,"utf-8"),A=P4(N);if(A){let E=y4(A),U=h0.isAbsolute(E)?E:h0.resolve(E),M=new Date;if($)process.stderr.write(`[diffstalker ${M.toISOString()}] Initial path read
|
|
17
|
+
`),process.stderr.write(` Source file: ${Y}
|
|
18
|
+
`),process.stderr.write(` Raw content: "${A}"
|
|
19
|
+
`),process.stderr.write(` Resolved: "${U}"
|
|
20
|
+
`);G.current=U,Q({path:U,lastUpdate:M,rawContent:A,sourceFile:Y,enabled:!0})}}catch{}let z=$J(Y,{persistent:!0,ignoreInitial:!0});return z.on("change",q),z.on("add",q),()=>{if(V.current)clearTimeout(V.current);z.close()}},[J,Y,$]),{state:K,setEnabled:Z}}import{useState as MX,useEffect as UJ,useCallback as $0,useRef as GJ}from"react";import*as d8 from"node:path";import*as Y9 from"node:fs";import{watch as X9}from"chokidar";import{EventEmitter as VJ}from"node:events";class C4{queue=[];isProcessing=!1;pendingMutations=0;refreshScheduled=!1;enqueue(X){return new Promise((Y,$)=>{this.queue.push({execute:X,resolve:Y,reject:$}),this.processNext()})}enqueueMutation(X){return this.pendingMutations++,this.enqueue(X).finally(()=>{this.pendingMutations--})}hasPendingMutations(){return this.pendingMutations>0}scheduleRefresh(X){if(this.pendingMutations>0)return;if(this.refreshScheduled)return;this.refreshScheduled=!0,this.enqueue(async()=>{this.refreshScheduled=!1,await X()}).catch(()=>{this.refreshScheduled=!1})}isBusy(){return this.isProcessing||this.queue.length>0}async processNext(){if(this.isProcessing||this.queue.length===0)return;this.isProcessing=!0;let X=this.queue.shift();try{let Y=await X.execute();X.resolve(Y)}catch(Y){X.reject(Y instanceof Error?Y:Error(String(Y)))}finally{this.isProcessing=!1,this.processNext()}}}var YY=new Map;function S4(X){let Y=YY.get(X);if(!Y)Y=new C4,YY.set(X,Y);return Y}function b4(X){YY.delete(X)}import{simpleGit as C0}from"simple-git";import*as x4 from"node:fs";import*as p4 from"node:path";function w4(X){let Y=new Map;for(let $ of X.trim().split(`
|
|
21
|
+
`)){if(!$)continue;let J=$.split("\t");if(J.length>=3){let Z=J[0]==="-"?0:parseInt(J[0],10),K=J[1]==="-"?0:parseInt(J[1],10),Q=J.slice(2).join("\t");Y.set(Q,{insertions:Z,deletions:K})}}return Y}async function JJ(X,Y){try{let $=p4.join(X,Y);return(await x4.promises.readFile($,"utf-8")).split(`
|
|
22
|
+
`).filter((Z)=>Z.length>0).length}catch{return 0}}async function ZJ(X,Y){if(Y.length===0)return new Set;try{let $=new Set,J=100;for(let Z=0;Z<Y.length;Z+=100){let K=Y.slice(Z,Z+100);try{let V=(await X.raw(["check-ignore",...K])).trim().split(`
|
|
23
|
+
`).filter((G)=>G.length>0);for(let G of V)$.add(G)}catch{}}return $}catch{return new Set}}function g4(X){switch(X){case"M":return"modified";case"A":return"added";case"D":return"deleted";case"?":return"untracked";case"R":return"renamed";case"C":return"copied";default:return"modified"}}async function u4(X){let Y=C0(X);try{if(!await Y.checkIsRepo())return{files:[],branch:{current:"",ahead:0,behind:0},isRepo:!1};let J=await Y.status(),Z=[];for(let U of J.staged)Z.push({path:U,status:"added",staged:!0});for(let U of J.modified)if(!Z.find((O)=>O.path===U&&O.staged))Z.push({path:U,status:"modified",staged:!1});for(let U of J.deleted)Z.push({path:U,status:"deleted",staged:!1});for(let U of J.not_added)Z.push({path:U,status:"untracked",staged:!1});for(let U of J.renamed)Z.push({path:U.to,originalPath:U.from,status:"renamed",staged:!0});let K=[],Q=new Set,V=J.files.filter((U)=>U.working_dir==="?").map((U)=>U.path),G=await ZJ(Y,V);for(let U of J.files){if(U.index==="!"||U.working_dir==="!"||G.has(U.path))continue;let M=`${U.path}-${U.index!==" "&&U.index!=="?"}`;if(Q.has(M))continue;if(Q.add(M),U.index&&U.index!==" "&&U.index!=="?")K.push({path:U.path,status:g4(U.index),staged:!0});if(U.working_dir&&U.working_dir!==" ")K.push({path:U.path,status:U.working_dir==="?"?"untracked":g4(U.working_dir),staged:!1})}let[q,z]=await Promise.all([Y.diff(["--cached","--numstat"]).catch(()=>""),Y.diff(["--numstat"]).catch(()=>"")]),N=w4(q),A=w4(z);for(let U of K){let M=U.staged?N.get(U.path):A.get(U.path);if(M)U.insertions=M.insertions,U.deletions=M.deletions}let E=K.filter((U)=>U.status==="untracked");if(E.length>0){let U=await Promise.all(E.map((M)=>JJ(X,M.path)));for(let M=0;M<E.length;M++)E[M].insertions=U[M],E[M].deletions=0}return{files:K,branch:{current:J.current||"HEAD",tracking:J.tracking||void 0,ahead:J.ahead,behind:J.behind},isRepo:!0}}catch{return{files:[],branch:{current:"",ahead:0,behind:0},isRepo:!1}}}async function h4(X,Y){await C0(X).add(Y)}async function m4(X,Y){await C0(X).reset(["HEAD","--",Y])}async function f4(X){await C0(X).add("-A")}async function d4(X){await C0(X).reset(["HEAD"])}async function c4(X,Y){await C0(X).checkout(["--",Y])}async function l4(X,Y,$=!1){await C0(X).commit(Y,void 0,$?{"--amend":null}:void 0)}async function n4(X){let Y=C0(X);try{return(await Y.log({n:1})).latest?.message||""}catch{return""}}async function r4(X,Y=50){let $=C0(X);try{return(await $.log({n:Y})).all.map((Z)=>({hash:Z.hash,shortHash:Z.hash.slice(0,7),message:Z.message.split(`
|
|
24
|
+
`)[0],author:Z.author_name,date:new Date(Z.date),refs:Z.refs||""}))}catch{return[]}}import{execSync as KJ}from"node:child_process";import{simpleGit as m8}from"simple-git";function QJ(X){let Y=X.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);if(Y)return{oldStart:parseInt(Y[1],10),newStart:parseInt(Y[2],10)};return null}function AX(X){let Y=X.split(`
|
|
25
|
+
`),$=[],J=0,Z=0;for(let K of Y)if(K.startsWith("diff --git")||K.startsWith("index ")||K.startsWith("---")||K.startsWith("+++")||K.startsWith("new file")||K.startsWith("deleted file")||K.startsWith("Binary files")||K.startsWith("similarity index")||K.startsWith("rename from")||K.startsWith("rename to"))$.push({type:"header",content:K});else if(K.startsWith("@@")){let Q=QJ(K);if(Q)J=Q.oldStart,Z=Q.newStart;$.push({type:"hunk",content:K})}else if(K.startsWith("+"))$.push({type:"addition",content:K,newLineNum:Z++});else if(K.startsWith("-"))$.push({type:"deletion",content:K,oldLineNum:J++});else $.push({type:"context",content:K,oldLineNum:J++,newLineNum:Z++});return $}async function f8(X,Y,$=!1){let J=m8(X);try{let Z=[];if($)Z.push("--cached");if(Y)Z.push("--",Y);let K=await J.diff(Z),Q=AX(K);return{raw:K,lines:Q}}catch{return{raw:"",lines:[]}}}async function $Y(X,Y){try{let $=KJ(`cat "${Y}"`,{cwd:X,encoding:"utf-8"}),J=[{type:"header",content:`diff --git a/${Y} b/${Y}`},{type:"header",content:"new file mode 100644"},{type:"header",content:"--- /dev/null"},{type:"header",content:`+++ b/${Y}`}],Z=$.split(`
|
|
26
|
+
`);J.push({type:"hunk",content:`@@ -0,0 +1,${Z.length} @@`});let K=1;for(let V of Z)J.push({type:"addition",content:"+"+V,newLineNum:K++});return{raw:J.map((V)=>V.content).join(`
|
|
27
|
+
`),lines:J}}catch{return{raw:"",lines:[]}}}async function JY(X){return f8(X,void 0,!0)}async function ZY(X){let Y=m8(X),$=new Set,J=[];try{let Z=await Y.raw(["log","--oneline","--decorate=short","--all","-n","200"]),K=/\(([^)]+)\)/g;for(let Q of Z.split(`
|
|
28
|
+
`)){let V=K.exec(Q);if(V){let G=V[1].split(",").map((q)=>q.trim());for(let q of G){if(q.startsWith("HEAD")||q.startsWith("tag:")||!q.includes("/"))continue;let z=q.replace(/^.*-> /,"");if(z.includes("/")&&!$.has(z))$.add(z),J.push(z)}}K.lastIndex=0}if(J.length>0)J.sort((Q,V)=>{let G=Q.split("/").slice(1).join("/"),q=V.split("/").slice(1).join("/"),z=G==="main"||G==="master",N=q==="main"||q==="master";if(z&&!N)return-1;if(!z&&N)return 1;if(z&&N){let A=Q.startsWith("origin/"),E=V.startsWith("origin/");if(A&&!E)return 1;if(!A&&E)return-1}return 0})}catch{}return[...new Set(J)]}async function o4(X){return(await ZY(X))[0]??null}async function KY(X,Y){let $=m8(X),Z=(await $.raw(["merge-base",Y,"HEAD"])).trim(),K=await $.raw(["diff","--numstat",`${Z}...HEAD`]),Q=await $.raw(["diff","--name-status",`${Z}...HEAD`]),V=await $.raw(["diff",`${Z}...HEAD`]),G=K.trim().split(`
|
|
29
|
+
`).filter((B)=>B),q=new Map;for(let B of G){let L=B.split("\t");if(L.length>=3){let R=L[0]==="-"?0:parseInt(L[0],10),S=L[1]==="-"?0:parseInt(L[1],10),P=L.slice(2).join("\t");q.set(P,{additions:R,deletions:S})}}let z=Q.trim().split(`
|
|
30
|
+
`).filter((B)=>B),N=new Map;for(let B of z){let L=B.split("\t");if(L.length>=2){let R=L[0][0],S=L[L.length-1],P;switch(R){case"A":P="added";break;case"D":P="deleted";break;case"R":P="renamed";break;default:P="modified"}N.set(S,P)}}let A=[],E=V.split(/(?=^diff --git )/m).filter((B)=>B.trim());for(let B of E){let L=B.match(/^diff --git a\/.+ b\/(.+)$/m);if(!L)continue;let R=L[1],S=AX(B),P=q.get(R)||{additions:0,deletions:0},W=N.get(R)||"modified";A.push({path:R,status:W,additions:P.additions,deletions:P.deletions,diff:{raw:B,lines:S}})}let U=0,M=0;for(let B of A)U+=B.additions,M+=B.deletions;let F=(await $.status()).files.length,H=(await $.log({from:Z,to:"HEAD"})).all.map((B)=>({hash:B.hash,shortHash:B.hash.slice(0,7),message:B.message.split(`
|
|
31
|
+
`)[0],author:B.author_name,date:new Date(B.date),refs:B.refs||""}));return{baseBranch:Y,stats:{filesChanged:A.length,additions:U,deletions:M},files:A,commits:H,uncommittedCount:F}}async function QY(X,Y){let $=m8(X);try{let J=await $.raw(["show",Y,"--format="]),Z=AX(J);return{raw:J,lines:Z}}catch{return{raw:"",lines:[]}}}async function s4(X,Y){let $=m8(X),J=await KY(X,Y),Z=await $.diff(["--cached","--numstat"]),K=await $.diff(["--numstat"]),Q=await $.diff(["--cached"]),V=await $.diff([]),G=new Map;for(let B of Z.trim().split(`
|
|
32
|
+
`).filter((L)=>L)){let L=B.split("\t");if(L.length>=3){let R=L[0]==="-"?0:parseInt(L[0],10),S=L[1]==="-"?0:parseInt(L[1],10),P=L.slice(2).join("\t");G.set(P,{additions:R,deletions:S,staged:!0,unstaged:!1})}}for(let B of K.trim().split(`
|
|
33
|
+
`).filter((L)=>L)){let L=B.split("\t");if(L.length>=3){let R=L[0]==="-"?0:parseInt(L[0],10),S=L[1]==="-"?0:parseInt(L[1],10),P=L.slice(2).join("\t"),W=G.get(P);if(W)W.additions+=R,W.deletions+=S,W.unstaged=!0;else G.set(P,{additions:R,deletions:S,staged:!1,unstaged:!0})}}let q=await $.status(),z=new Map;for(let B of q.files)if(B.index==="A"||B.working_dir==="?")z.set(B.path,"added");else if(B.index==="D"||B.working_dir==="D")z.set(B.path,"deleted");else if(B.index==="R")z.set(B.path,"renamed");else z.set(B.path,"modified");let N=[],E=(Q+V).split(/(?=^diff --git )/m).filter((B)=>B.trim()),U=new Set;for(let B of E){let L=B.match(/^diff --git a\/.+ b\/(.+)$/m);if(!L)continue;let R=L[1];if(U.has(R))continue;U.add(R);let S=AX(B),P=G.get(R)||{additions:0,deletions:0},W=z.get(R)||"modified";N.push({path:R,status:W,additions:P.additions,deletions:P.deletions,diff:{raw:B,lines:S},isUncommitted:!0})}let M=new Set(J.files.map((B)=>B.path)),O=[];for(let B of J.files){let L=N.find((R)=>R.path===B.path);if(L)O.push(B),O.push(L);else O.push(B)}for(let B of N)if(!M.has(B.path))O.push(B);let F=0,k=0,H=new Set;for(let B of O){if(!H.has(B.path))H.add(B.path);F+=B.additions,k+=B.deletions}return{baseBranch:J.baseBranch,stats:{filesChanged:H.size,additions:F,deletions:k},files:O,commits:J.commits,uncommittedCount:J.uncommittedCount}}import*as S0 from"node:fs";import*as J8 from"node:path";import*as a4 from"node:os";var BX=J8.join(a4.homedir(),".cache","diffstalker","base-branches.json");function qJ(){let X=J8.dirname(BX);if(!S0.existsSync(X))S0.mkdirSync(X,{recursive:!0})}function i4(){try{if(S0.existsSync(BX))return JSON.parse(S0.readFileSync(BX,"utf-8"))}catch{}return{}}function zJ(X){qJ(),S0.writeFileSync(BX,JSON.stringify(X,null,2)+`
|
|
34
|
+
`)}function t4(X){let Y=i4(),$=J8.resolve(X);return Y[$]}function e4(X,Y){let $=i4(),J=J8.resolve(X);$[J]=Y,zJ($)}class $9 extends VJ{repoPath;queue;gitWatcher=null;workingDirWatcher=null;_state={status:null,diff:null,stagedDiff:"",selectedFile:null,isLoading:!1,error:null};_compareState={compareDiff:null,compareBaseBranch:null,compareLoading:!1,compareError:null};_historyState={selectedCommit:null,commitDiff:null};_compareSelectionState={type:null,index:0,diff:null};constructor(X){super();this.repoPath=X,this.queue=S4(X)}get state(){return this._state}get compareState(){return this._compareState}get historyState(){return this._historyState}get compareSelectionState(){return this._compareSelectionState}updateState(X){this._state={...this._state,...X},this.emit("state-change",this._state)}updateCompareState(X){this._compareState={...this._compareState,...X},this.emit("compare-state-change",this._compareState)}updateHistoryState(X){this._historyState={...this._historyState,...X},this.emit("history-state-change",this._historyState)}updateCompareSelectionState(X){this._compareSelectionState={...this._compareSelectionState,...X},this.emit("compare-selection-change",this._compareSelectionState)}startWatching(){let X=d8.join(this.repoPath,".git");if(!Y9.existsSync(X))return;let Y=d8.join(X,"index"),$=d8.join(X,"HEAD"),J=d8.join(X,"refs");this.gitWatcher=X9([Y,$,J],{persistent:!0,ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}),this.workingDirWatcher=X9(this.repoPath,{persistent:!0,ignoreInitial:!0,ignored:["**/node_modules/**","**/.git/**","**/dist/**","**/build/**","**/*.log","**/.DS_Store"],awaitWriteFinish:{stabilityThreshold:100,pollInterval:50},depth:10});let Z=()=>this.scheduleRefresh();this.gitWatcher.on("change",Z),this.gitWatcher.on("add",Z),this.gitWatcher.on("unlink",Z),this.workingDirWatcher.on("change",Z),this.workingDirWatcher.on("add",Z),this.workingDirWatcher.on("unlink",Z)}dispose(){this.gitWatcher?.close(),this.workingDirWatcher?.close(),b4(this.repoPath)}scheduleRefresh(){this.queue.scheduleRefresh(()=>this.doRefresh())}async refresh(){await this.queue.enqueue(()=>this.doRefresh())}async doRefresh(){this.updateState({isLoading:!0,error:null});try{let X=await u4(this.repoPath);if(!X.isRepo){this.updateState({status:X,diff:null,stagedDiff:"",isLoading:!1,error:"Not a git repository"});return}let[Y,$]=await Promise.all([JY(this.repoPath),f8(this.repoPath,void 0,!1)]),J,Z=this._state.selectedFile;if(Z){let K=X.files.find((Q)=>Q.path===Z.path&&Q.staged===Z.staged);if(K)if(K.status==="untracked")J=await $Y(this.repoPath,K.path);else J=await f8(this.repoPath,K.path,K.staged);else J=$.raw?$:Y,this.updateState({selectedFile:null})}else if($.raw)J=$;else if(Y.raw)J=Y;else J={raw:"",lines:[]};this.updateState({status:X,diff:J,stagedDiff:Y.raw,isLoading:!1})}catch(X){this.updateState({isLoading:!1,error:X instanceof Error?X.message:"Unknown error"})}}async selectFile(X){if(this.updateState({selectedFile:X}),!this._state.status?.isRepo)return;await this.queue.enqueue(async()=>{if(X){let Y;if(X.status==="untracked")Y=await $Y(this.repoPath,X.path);else Y=await f8(this.repoPath,X.path,X.staged);this.updateState({diff:Y})}else{let Y=await JY(this.repoPath);this.updateState({diff:Y})}})}async stage(X){let Y=this._state.status;if(Y)this.updateState({status:{...Y,files:Y.files.map(($)=>$.path===X.path&&!$.staged?{...$,staged:!0}:$)}});try{await this.queue.enqueueMutation(()=>h4(this.repoPath,X.path)),this.scheduleRefresh()}catch($){await this.refresh(),this.updateState({error:`Failed to stage ${X.path}: ${$ instanceof Error?$.message:String($)}`})}}async unstage(X){let Y=this._state.status;if(Y)this.updateState({status:{...Y,files:Y.files.map(($)=>$.path===X.path&&$.staged?{...$,staged:!1}:$)}});try{await this.queue.enqueueMutation(()=>m4(this.repoPath,X.path)),this.scheduleRefresh()}catch($){await this.refresh(),this.updateState({error:`Failed to unstage ${X.path}: ${$ instanceof Error?$.message:String($)}`})}}async discard(X){if(X.staged||X.status==="untracked")return;try{await this.queue.enqueueMutation(()=>c4(this.repoPath,X.path)),await this.refresh()}catch(Y){this.updateState({error:`Failed to discard ${X.path}: ${Y instanceof Error?Y.message:String(Y)}`})}}async stageAll(){try{await this.queue.enqueueMutation(()=>f4(this.repoPath)),await this.refresh()}catch(X){this.updateState({error:`Failed to stage all: ${X instanceof Error?X.message:String(X)}`})}}async unstageAll(){try{await this.queue.enqueueMutation(()=>d4(this.repoPath)),await this.refresh()}catch(X){this.updateState({error:`Failed to unstage all: ${X instanceof Error?X.message:String(X)}`})}}async commit(X,Y=!1){try{await this.queue.enqueue(()=>l4(this.repoPath,X,Y)),await this.refresh()}catch($){this.updateState({error:`Failed to commit: ${$ instanceof Error?$.message:String($)}`})}}async getHeadCommitMessage(){return this.queue.enqueue(()=>n4(this.repoPath))}async refreshCompareDiff(X=!1){this.updateCompareState({compareLoading:!0,compareError:null});try{await this.queue.enqueue(async()=>{let Y=this._compareState.compareBaseBranch;if(!Y)Y=t4(this.repoPath)??await o4(this.repoPath),this.updateCompareState({compareBaseBranch:Y});if(Y){let $=X?await s4(this.repoPath,Y):await KY(this.repoPath,Y);this.updateCompareState({compareDiff:$,compareLoading:!1})}else this.updateCompareState({compareDiff:null,compareLoading:!1,compareError:"No base branch found"})})}catch(Y){this.updateCompareState({compareLoading:!1,compareError:`Failed to load compare diff: ${Y instanceof Error?Y.message:String(Y)}`})}}async getCandidateBaseBranches(){return ZY(this.repoPath)}async setCompareBaseBranch(X,Y=!1){this.updateCompareState({compareBaseBranch:X}),e4(this.repoPath,X),await this.refreshCompareDiff(Y)}async selectHistoryCommit(X){if(this.updateHistoryState({selectedCommit:X,commitDiff:null}),!X)return;try{await this.queue.enqueue(async()=>{let Y=await QY(this.repoPath,X.hash);this.updateHistoryState({commitDiff:Y})})}catch(Y){this.updateState({error:`Failed to load commit diff: ${Y instanceof Error?Y.message:String(Y)}`})}}async selectCompareCommit(X){let Y=this._compareState.compareDiff;if(!Y||X<0||X>=Y.commits.length){this.updateCompareSelectionState({type:null,index:0,diff:null});return}let $=Y.commits[X];this.updateCompareSelectionState({type:"commit",index:X,diff:null});try{await this.queue.enqueue(async()=>{let J=await QY(this.repoPath,$.hash);this.updateCompareSelectionState({diff:J})})}catch(J){this.updateState({error:`Failed to load commit diff: ${J instanceof Error?J.message:String(J)}`})}}selectCompareFile(X){let Y=this._compareState.compareDiff;if(!Y||X<0||X>=Y.files.length){this.updateCompareSelectionState({type:null,index:0,diff:null});return}let $=Y.files[X];this.updateCompareSelectionState({type:"file",index:X,diff:$.diff})}}var EX=new Map;function J9(X){let Y=EX.get(X);if(!Y)Y=new $9(X),EX.set(X,Y);return Y}function Z9(X){let Y=EX.get(X);if(Y)Y.dispose(),EX.delete(X)}function K9(X){let[Y,$]=MX({status:null,diff:null,stagedDiff:"",selectedFile:null,isLoading:!1,error:null}),[J,Z]=MX({compareDiff:null,compareBaseBranch:null,compareLoading:!1,compareError:null}),[K,Q]=MX({selectedCommit:null,commitDiff:null}),[V,G]=MX({type:null,index:0,diff:null}),q=GJ(null);UJ(()=>{if(!X){$({status:null,diff:null,stagedDiff:"",selectedFile:null,isLoading:!1,error:null});return}let W=J9(X);q.current=W;let C=(p)=>{$(p)},b=(p)=>{Z(p)},w=(p)=>{Q(p)},g=(p)=>{G(p)};return W.on("state-change",C),W.on("compare-state-change",b),W.on("history-state-change",w),W.on("compare-selection-change",g),W.startWatching(),W.refresh(),()=>{W.off("state-change",C),W.off("compare-state-change",b),W.off("history-state-change",w),W.off("compare-selection-change",g),Z9(X),q.current=null}},[X]);let z=$0((W)=>{q.current?.selectFile(W)},[]),N=$0(async(W)=>{await q.current?.stage(W)},[]),A=$0(async(W)=>{await q.current?.unstage(W)},[]),E=$0(async(W)=>{await q.current?.discard(W)},[]),U=$0(async()=>{await q.current?.stageAll()},[]),M=$0(async()=>{await q.current?.unstageAll()},[]),O=$0(async(W,C=!1)=>{await q.current?.commit(W,C)},[]),F=$0(async()=>{await q.current?.refresh()},[]),k=$0(async()=>{return q.current?.getHeadCommitMessage()??""},[]),H=$0(async(W=!1)=>{await q.current?.refreshCompareDiff(W)},[]),B=$0(async()=>{return q.current?.getCandidateBaseBranches()??[]},[]),L=$0(async(W,C=!1)=>{await q.current?.setCompareBaseBranch(W,C)},[]),R=$0(async(W)=>{await q.current?.selectHistoryCommit(W)},[]),S=$0(async(W)=>{await q.current?.selectCompareCommit(W)},[]),P=$0((W)=>{q.current?.selectCompareFile(W)},[]);return{status:Y.status,diff:Y.diff,stagedDiff:Y.stagedDiff,selectedFile:Y.selectedFile,isLoading:Y.isLoading,error:Y.error,selectFile:z,stage:N,unstage:A,discard:E,stageAll:U,unstageAll:M,commit:O,refresh:F,getHeadCommitMessage:k,compareDiff:J.compareDiff,compareBaseBranch:J.compareBaseBranch,compareLoading:J.compareLoading,compareError:J.compareError,refreshCompareDiff:H,getCandidateBaseBranches:B,setCompareBaseBranch:L,historySelectedCommit:K.selectedCommit,historyCommitDiff:K.commitDiff,selectHistoryCommit:R,compareSelectionType:V.type,compareSelectionIndex:V.index,compareSelectionDiff:V.diff,selectCompareCommit:S,selectCompareFile:P}}import{useInput as NJ}from"ink";function Q9(X,Y,$){NJ((J,Z)=>{if($)return;if(Z.ctrl&&J==="c"){X.onQuit();return}if(J==="q"){X.onQuit();return}if(J==="j"||Z.downArrow){X.onNavigateDown();return}if(J==="k"||Z.upArrow){X.onNavigateUp();return}if(Z.tab){X.onTogglePane();return}if(J==="1"){X.onSwitchTab("diff");return}if(J==="2"){X.onSwitchTab("commit");return}if(J==="3"){X.onSwitchTab("history");return}if(J==="4"){X.onSwitchTab("compare");return}if(J==="5"){X.onSwitchTab("explorer");return}if(J==="u"&&X.onToggleIncludeUncommitted){X.onToggleIncludeUncommitted();return}if(J==="b"&&X.onCycleBaseBranch){X.onCycleBaseBranch();return}if(J==="t"&&X.onOpenThemePicker){X.onOpenThemePicker();return}if(J==="?"&&X.onOpenHotkeysModal){X.onOpenHotkeysModal();return}if(J==="["&&X.onShrinkTopPane){X.onShrinkTopPane();return}if(J==="]"&&X.onGrowTopPane){X.onGrowTopPane();return}if(J==="m"&&X.onToggleMouse){X.onToggleMouse();return}if(J==="f"&&X.onToggleFollow){X.onToggleFollow();return}if(J==="a"&&X.onToggleAutoTab){X.onToggleAutoTab();return}if(J==="w"&&X.onToggleWrap){X.onToggleWrap();return}if(J==="."&&X.onToggleMiddleDots){X.onToggleMiddleDots();return}if(Z.ctrl&&J==="h"&&X.onToggleHideHiddenFiles){X.onToggleHideHiddenFiles();return}if(Z.ctrl&&J==="g"&&X.onToggleHideGitignored){X.onToggleHideGitignored();return}if(Z.ctrl&&J==="s"){X.onStage();return}if(Z.ctrl&&J==="u"){X.onUnstage();return}if(Z.ctrl&&J==="a"){X.onStageAll();return}if(Z.ctrl&&J==="z"){X.onUnstageAll();return}if(J==="c"){X.onCommit();return}if(Z.ctrl&&J==="r"){X.onRefresh();return}if(J==="r"){X.onRefresh();return}if(X.onExplorerEnter&&Z.return){X.onExplorerEnter();return}if(X.onExplorerBack&&(Z.backspace||Z.delete||J==="h")){X.onExplorerBack();return}if(Z.return||J===" "){X.onSelect();return}})}import{useEffect as LX,useState as AJ,useCallback as BJ,useRef as q9}from"react";import{useStdin as EJ}from"ink";function z9(X,Y=!1){let{stdin:$,setRawMode:J}=EJ(),[Z,K]=AJ(!0),Q=q9(X);LX(()=>{Q.current=X});let V=BJ(()=>{K((q)=>!q)},[]),G=q9(Z);return LX(()=>{G.current=Z},[Z]),LX(()=>{if(!Y&&Z)process.stdout.write("\x1B[?1000h"),process.stdout.write("\x1B[?1006h");else process.stdout.write("\x1B[?1006l"),process.stdout.write("\x1B[?1000l")},[Y,Z]),LX(()=>{if(!$||!J)return;let q=(z)=>{let N=z.toString(),A=N.match(/\x1b\[<(\d+);(\d+);(\d+)([Mm])/);if(A){let U=parseInt(A[1],10),M=parseInt(A[2],10),O=parseInt(A[3],10),F=A[4]==="m";if(U>=64&&U<96){if(G.current){let k=U===64?"scroll-up":"scroll-down";Q.current({x:M,y:O,type:k,button:"none"})}}else if(F&&U>=0&&U<3){let k=U===0?"left":U===1?"middle":"right";Q.current({x:M,y:O,type:"click",button:k})}return}let E=N.match(/\x1b\[M(.)(.)(.)/);if(E){let U=E[1].charCodeAt(0)-32,M=E[2].charCodeAt(0)-32,O=E[3].charCodeAt(0)-32;if(U>=64){if(G.current){let F=U===64?"scroll-up":"scroll-down";Q.current({x:M,y:O,type:F,button:"none"})}}else if(U>=0&&U<3){let F=U===0?"left":U===1?"middle":"right";Q.current({x:M,y:O,type:"click",button:F})}}};return $.on("data",q),()=>{$.off("data",q),process.stdout.write("\x1B[?1006l"),process.stdout.write("\x1B[?1000l")}},[$,J]),{mouseEnabled:Z,toggleMouse:V}}import{useState as MJ,useEffect as LJ}from"react";function V9(){let[X,Y]=MJ({rows:process.stdout.rows??24,columns:process.stdout.columns??80});return LJ(()=>{let $=()=>{Y({rows:process.stdout.rows??24,columns:process.stdout.columns??80})};return process.stdout.on("resize",$),()=>{process.stdout.off("resize",$)}},[]),X}import{useState as c8,useEffect as qY,useMemo as O9,useCallback as F8}from"react";function U9(X){let{modifiedCount:Y,untrackedCount:$,stagedCount:J}=b8(X),Z=0;if(Y>0)Z+=1+Y;if($>0){if(Y>0)Z+=1;Z+=1+$}if(J>0){if(Y>0||$>0)Z+=1;Z+=1+J}return Z}function G9(X,Y,$,J){let Z=0;if(X<Y)return 1+X;if(Y>0)Z+=1+Y;let K=Y;if(X<K+$){let G=X-K;if(Y>0)Z+=1;return Z+1+G}if($>0){if(Y>0)Z+=1;Z+=1+$}let Q=Y+$,V=X-Q;if(Y>0||$>0)Z+=1;return Z+1+V}function N9(X,Y,$){if(X<Y)return Math.max(0,X-1);else if(X>=Y+$)return X-$+1;return Y}function A9(X,Y,$,J=1){let Z=J+2,K=J+1+X,Q=K+1,V=K+2,G=V+Y-1;return{stagingPaneStart:Z,fileListEnd:K,separatorRow:Q,diffPaneStart:V,diffPaneEnd:G,footerRow:$}}function B9(X,Y,$,J,Z){if(X<J+1||X>Z)return-1;let K=X-(J+1)+Y,{modified:Q,untracked:V,staged:G}=g0($),q=0,z=0;if(Q.length>0){q++;for(let N=0;N<Q.length;N++){if(K===q)return z;q++,z++}}if(V.length>0){if(Q.length>0)q++;q++;for(let N=0;N<V.length;N++){if(K===q)return z;q++,z++}}if(G.length>0){if(Q.length>0||V.length>0)q++;q++;for(let N=0;N<G.length;N++){if(K===q)return z;q++,z++}}return-1}function OJ(X){let Y=X-51;return{diffStart:Y,diffEnd:Y+6,commitStart:Y+8,commitEnd:Y+16,historyStart:Y+18,historyEnd:Y+27,compareStart:Y+29,compareEnd:Y+38,explorerStart:Y+40,explorerEnd:Y+50}}function E9(X,Y){let $=OJ(Y);if(X>=$.diffStart&&X<=$.diffEnd)return"diff";else if(X>=$.commitStart&&X<=$.commitEnd)return"commit";else if(X>=$.historyStart&&X<=$.historyEnd)return"history";else if(X>=$.compareStart&&X<=$.compareEnd)return"compare";else if(X>=$.explorerStart&&X<=$.explorerEnd)return"explorer";return null}function M9(X){return X<=6}function OX(X,Y,$){return X>=Y&&X<=$}function L9(X){if(X===1)return"hotkeys";if(X>=3&&X<=10)return"mouse-mode";if(X>=12&&X<=17)return"auto-tab";if(X>=19&&X<=24)return"wrap";return null}var kJ=5,k9={diff:0.4,commit:0.4,history:0.5,compare:0.5,explorer:0.4},zY=0.05;function W9(X,Y,$,J,Z,K="diff",Q,V,G=0){let q=X-kJ-G,[z,N]=c8(V??null),A=z??k9[K],{topPaneHeight:E,bottomPaneHeight:U}=O9(()=>{let v=q-5,y=Math.floor(q*A),T=Math.max(5,Math.min(y,v)),j=q-T;return{topPaneHeight:T,bottomPaneHeight:j}},[q,A]),M=F8((_)=>{let v=Math.max(0.15,Math.min(0.85,_));N(v)},[]),O=F8((_)=>{let v=z??k9[K],y=Math.max(0.15,Math.min(0.85,v+_));N(y)},[z,K]),F=A,k=G+1,H=O9(()=>A9(E,U,X,k),[E,U,X,k]),[B,L]=c8(0),[R,S]=c8(0),[P,W]=c8(0),[C,b]=c8(0);qY(()=>{L(0)},[$.length]),qY(()=>{S(0)},[Z]),qY(()=>{let{modifiedCount:_,untrackedCount:v,stagedCount:y}=b8($),T=G9(J,_,v,y),j=E-1;L((x)=>{return N9(T,x,j)})},[J,$,E]);let w=F8((_,v=3,y=0)=>{let T=U-1,x=y>T?T-2:T,D=Math.max(0,y-x);S((y8)=>{if(_==="up")return Math.max(0,y8-v);else return Math.min(D,y8+v)})},[U]),g=F8((_,v=3)=>{let y=U9($),T=E-1,j=Math.max(0,y-T);L((x)=>{if(_==="up")return Math.max(0,x-v);else return Math.min(j,x+v)})},[$,E]),p=F8((_,v=0,y=3)=>{let T=E8(v,E-1);W((j)=>{if(_==="up")return Math.max(0,j-y);else return Math.min(T,j+y)})},[E]),f=F8((_,v,y=3)=>{let T=E8(v,E-1);b((j)=>{if(_==="up")return Math.max(0,j-y);else return Math.min(T,j+y)})},[E]);return{topPaneHeight:E,bottomPaneHeight:U,contentHeight:q,paneBoundaries:H,splitRatio:F,setSplitRatio:M,adjustSplitRatio:O,fileListScrollOffset:B,diffScrollOffset:R,historyScrollOffset:P,compareScrollOffset:C,setFileListScrollOffset:L,setDiffScrollOffset:S,setHistoryScrollOffset:W,setCompareScrollOffset:b,scrollDiff:w,scrollFileList:g,scrollHistory:p,scrollCompare:f}}import{useState as H9,useEffect as I9,useCallback as F9,useMemo as R9}from"react";function D9({repoPath:X,isActive:Y,selectHistoryCommit:$,historyCommitDiff:J,historySelectedCommit:Z,topPaneHeight:K,historyScrollOffset:Q,setHistoryScrollOffset:V,setDiffScrollOffset:G,status:q,wrapMode:z,terminalWidth:N}){let[A,E]=H9([]),[U,M]=H9(0);I9(()=>{if(X&&Y)r4(X,100).then(E)},[X,Y,q]),I9(()=>{if(Y&&A.length>0){let B=A[U];if(B)$(B),G(0)}},[Y,A,U,$,G]);let O=R9(()=>{let B=GX(Z,J);if(!z)return B.length;let L=R0(B),R=N-L-5;return L8(B,R,!0)},[Z,J,z,N]),F=R9(()=>A.length,[A]),k=F9(()=>{M((B)=>{let L=Math.max(0,B-1);if(L<Q)V(L);return L})},[Q,V]),H=F9(()=>{M((B)=>{let L=Math.min(A.length-1,B+1),R=Q+K-2;if(L>=R)V(Q+1);return L})},[A.length,Q,K,V]);return{commits:A,historySelectedIndex:U,setHistorySelectedIndex:M,historyDiffTotalRows:O,navigateHistoryUp:k,navigateHistoryDown:H,historyTotalRows:F}}import{useState as l8,useEffect as kX,useCallback as m0,useMemo as _9,useRef as WJ}from"react";function v9({repoPath:X,isActive:Y,compareDiff:$,refreshCompareDiff:J,getCandidateBaseBranches:Z,setCompareBaseBranch:K,selectCompareCommit:Q,topPaneHeight:V,compareScrollOffset:G,setCompareScrollOffset:q,setDiffScrollOffset:z,status:N,wrapMode:A,terminalWidth:E}){let[U,M]=l8(!0),[O,F]=l8(null),[k,H]=l8(0),B=WJ(!1),[L,R]=l8([]),[S,P]=l8(!1);kX(()=>{if(X&&Y)J(U)},[X,Y,N,J,U]),kX(()=>{if(X&&Y)Z().then(R)},[X,Y,Z]),kX(()=>{if(Y)B.current=!1,F(null),z(0)},[Y,z]),kX(()=>{if(Y&&$&&B.current){let T=$.commits.length,j=$.files.length;if(k<T)F({type:"commit",index:k}),Q(k),z(0);else if(k<T+j){let x=k-T;F({type:"file",index:x});let D=aY($,x);z(D)}}},[Y,$,k,Q,z]);let W=_9(()=>{if(!$)return 0;return $.commits.length+$.files.length},[$]),C=_9(()=>{let T=NX($);if(!A)return T.length;let j=R0(T),x=E-j-5;return L8(T,x,!0)},[$,A,E]),b=m0(()=>{M((T)=>!T)},[]),w=m0(()=>{P(!0)},[]),g=m0(()=>{P(!1)},[]),p=m0((T)=>{P(!1),K(T,U)},[K,U]),f=m0(()=>{B.current=!0},[]),_=m0(()=>{B.current=!0,H((T)=>{let j=Math.max(0,T-1);if(j<G)q(j);return j})},[G,q]),v=m0(()=>{B.current=!0,H((T)=>{let j=Math.min(W-1,T+1),x=G+V-2;if(j>=x)q(G+1);return j})},[W,G,V,q]),y=m0((T)=>{if(!$)return-1;return lX(T,$.commits.length,$.files.length)},[$]);return{includeUncommitted:U,compareListSelection:O,compareSelectedIndex:k,baseBranchCandidates:L,showBaseBranchPicker:S,compareTotalItems:W,compareDiffTotalRows:C,setCompareSelectedIndex:H,toggleIncludeUncommitted:b,openBaseBranchPicker:w,closeBaseBranchPicker:g,selectBaseBranch:p,navigateCompareUp:_,navigateCompareDown:v,markSelectionInitialized:f,getItemIndexFromRow:y}}import{useState as R8,useEffect as j9,useCallback as WX,useMemo as HJ}from"react";import*as HX from"node:fs";import*as D0 from"node:path";import{simpleGit as IJ}from"simple-git";var FJ=1048576,RJ=102400;function DJ(X){let Y=Math.min(X.length,8192);for(let $=0;$<Y;$++)if(X[$]===0)return!0;return!1}async function _J(X,Y){if(Y.length===0)return new Set;let $=IJ(X),J=new Set,Z=100;for(let K=0;K<Y.length;K+=Z){let Q=Y.slice(K,K+Z);try{let G=(await $.raw(["check-ignore",...Q])).trim().split(`
|
|
35
|
+
`).filter((q)=>q.length>0);for(let q of G)J.add(q)}catch{}}return J}function y9({repoPath:X,isActive:Y,topPaneHeight:$,explorerScrollOffset:J,setExplorerScrollOffset:Z,fileScrollOffset:K,setFileScrollOffset:Q,hideHiddenFiles:V,hideGitignored:G}){let[q,z]=R8(""),[N,A]=R8([]),[E,U]=R8(0),[M,O]=R8(null),[F,k]=R8(!1),[H,B]=R8(null);j9(()=>{if(!Y||!X)return;(async()=>{k(!0),B(null);try{let b=D0.join(X,q),w=await HX.promises.readdir(b,{withFileTypes:!0}),g=w.map((_)=>q?D0.join(q,_.name):_.name),p=G?await _J(X,g):new Set,f=w.filter((_)=>{if(V&&_.name.startsWith("."))return!1;if(G){let v=q?D0.join(q,_.name):_.name;if(p.has(v))return!1}return!0}).map((_)=>({name:_.name,path:q?D0.join(q,_.name):_.name,isDirectory:_.isDirectory()}));if(f.sort((_,v)=>{if(_.isDirectory&&!v.isDirectory)return-1;if(!_.isDirectory&&v.isDirectory)return 1;return _.name.localeCompare(v.name)}),q)f.unshift({name:"..",path:D0.dirname(q)||"",isDirectory:!0});A(f),U(0),Z(0)}catch(b){B(b instanceof Error?b.message:"Failed to read directory"),A([])}finally{k(!1)}})()},[X,q,Y,Z,V,G]),j9(()=>{if(!Y||!X||N.length===0){O(null);return}let C=N[E];if(!C||C.isDirectory){O(null);return}(async()=>{try{let w=D0.join(X,C.path),g=await HX.promises.stat(w);if(g.size>FJ){O({path:C.path,content:`File too large to display (${(g.size/1024/1024).toFixed(2)} MB).
|
|
36
|
+
Maximum size: 1 MB`,truncated:!0});return}let p=await HX.promises.readFile(w);if(DJ(p)){O({path:C.path,content:"Binary file - cannot display"});return}let f=p.toString("utf-8"),_=!1;if(g.size>RJ)f=`⚠ Large file (${(g.size/1024).toFixed(1)} KB)
|
|
37
|
+
|
|
38
|
+
`+f;let v=5000,y=f.split(`
|
|
39
|
+
`);if(y.length>v)f=y.slice(0,v).join(`
|
|
40
|
+
`)+`
|
|
41
|
+
|
|
42
|
+
... (truncated, ${y.length-v} more lines)`,_=!0;O({path:C.path,content:f,truncated:_}),Q(0)}catch(w){O({path:C.path,content:w instanceof Error?`Error: ${w.message}`:"Failed to read file"})}})()},[X,N,E,Y,Q]);let L=HJ(()=>N.length,[N]),R=WX(()=>{U((C)=>{let b=Math.max(0,C-1);if(b<J)Z(b);return b})},[J,Z]),S=WX(()=>{U((C)=>{let b=Math.min(N.length-1,C+1),w=$-1,p=N.length>w?w-2:w,f=J+p;if(b>=f)Z(J+1);return b})},[N.length,J,$,Z]),P=WX(()=>{let C=N[E];if(!C)return;if(C.isDirectory)if(C.name==="..")z(D0.dirname(q)||"");else z(C.path)},[N,E,q]),W=WX(()=>{if(q)z(D0.dirname(q)||"")},[q]);return{currentPath:q,items:N,selectedIndex:E,setSelectedIndex:U,selectedFile:M,fileScrollOffset:K,setFileScrollOffset:Q,navigateUp:R,navigateDown:S,enterDirectory:P,goUp:W,isLoading:F,error:H,explorerTotalRows:L}}import{jsx as r,jsxs as K8}from"react/jsx-runtime";import{useState as yJ}from"react";import{Box as f0,Text as Y0,useInput as PJ}from"ink";import{jsx as P9,jsxs as vJ}from"react/jsx-runtime";import{Box as T9,Text as jJ}from"ink";function Z8({x:X,y:Y,width:$,height:J,children:Z}){let K=" ".repeat($);return vJ(T9,{position:"absolute",marginLeft:X,marginTop:Y,flexDirection:"column",children:[Array.from({length:J}).map((Q,V)=>P9(jJ,{children:K},`blank-${V}`)),P9(T9,{position:"absolute",flexDirection:"column",children:Z})]})}function D8(X,Y,$,J){return{x:Math.floor(($-X)/2),y:Math.floor((J-Y)/2)}}function TJ({theme:X}){let{colors:Y}=X;return K8(f0,{flexDirection:"column",marginLeft:2,children:[K8(f0,{children:[r(Y0,{backgroundColor:Y.delBg,color:Y.delLineNum,children:" 5 "}),r(Y0,{backgroundColor:Y.delBg,color:Y.delSymbol,bold:!0,children:"- "}),r(Y0,{backgroundColor:Y.delBg,color:Y.text,children:"const "}),r(Y0,{backgroundColor:Y.delHighlight,color:Y.text,children:"old"}),r(Y0,{backgroundColor:Y.delBg,color:Y.text,children:" = value;"})]}),K8(f0,{children:[r(Y0,{backgroundColor:Y.addBg,color:Y.addLineNum,children:" 5 "}),r(Y0,{backgroundColor:Y.addBg,color:Y.addSymbol,bold:!0,children:"+ "}),r(Y0,{backgroundColor:Y.addBg,color:Y.text,children:"const "}),r(Y0,{backgroundColor:Y.addHighlight,color:Y.text,children:"new"}),r(Y0,{backgroundColor:Y.addBg,color:Y.text,children:" = value;"})]})]})}function C9({currentTheme:X,onSelect:Y,onCancel:$,width:J,height:Z}){let[K,Q]=yJ(()=>{let A=e0.indexOf(X);return A>=0?A:0}),V=UX(e0[K]);PJ((A,E)=>{if(E.escape)$();else if(E.return)Y(e0[K]);else if(E.upArrow||A==="k")Q((U)=>Math.max(0,U-1));else if(E.downArrow||A==="j")Q((U)=>Math.min(e0.length-1,U+1))});let G=Math.min(50,J-4),q=Math.min(e0.length+10,Z-4),{x:z,y:N}=D8(G,q,J,Z);return r(Z8,{x:z,y:N,width:G,height:q,children:K8(f0,{borderStyle:"round",borderColor:"cyan",flexDirection:"column",width:G,children:[r(f0,{justifyContent:"center",marginBottom:1,children:K8(Y0,{bold:!0,color:"cyan",children:[" ","Select Theme"," "]})}),e0.map((A,E)=>{let U=VX[A],M=E===K,O=A===X;return K8(f0,{children:[r(Y0,{color:M?"cyan":void 0,children:M?"▸ ":" "}),r(Y0,{bold:M,color:M?"cyan":void 0,children:U.displayName}),O&&r(Y0,{dimColor:!0,children:" (current)"})]},A)}),K8(f0,{marginTop:1,flexDirection:"column",children:[r(Y0,{dimColor:!0,children:"Preview:"}),r(TJ,{theme:V})]}),r(f0,{marginTop:1,justifyContent:"center",children:r(Y0,{dimColor:!0,children:"↑↓ navigate • Enter select • Esc cancel"})})]})})}import{jsx as J0,jsxs as Q8}from"react/jsx-runtime";import{Box as G0,Text as q8,useInput as CJ}from"ink";var d0=[{title:"Navigation",entries:[{key:"↑/k",description:"Move up"},{key:"↓/j",description:"Move down"},{key:"Tab",description:"Toggle pane focus"}]},{title:"Staging",entries:[{key:"^S",description:"Stage file"},{key:"^U",description:"Unstage file"},{key:"^A",description:"Stage all"},{key:"^Z",description:"Unstage all"},{key:"Space/Enter",description:"Toggle stage"}]},{title:"Actions",entries:[{key:"c",description:"Open commit panel"},{key:"r",description:"Refresh"},{key:"q",description:"Quit"}]},{title:"Pane Resize",entries:[{key:"[",description:"Shrink top pane"},{key:"]",description:"Grow top pane"}]},{title:"Tabs",entries:[{key:"1",description:"Diff view"},{key:"2",description:"Commit panel"},{key:"3",description:"History view"},{key:"4",description:"Compare view"},{key:"5",description:"Explorer view"},{key:"a",description:"Toggle auto-tab mode"}]},{title:"Explorer",entries:[{key:".",description:"Toggle middle-dots"},{key:"^H",description:"Toggle hidden files"},{key:"^G",description:"Toggle gitignored"},{key:"Enter",description:"Enter directory"},{key:"Backspace/h",description:"Go up"}]},{title:"Other",entries:[{key:"m",description:"Toggle scroll/select"},{key:"f",description:"Toggle follow mode"},{key:"w",description:"Toggle wrap mode"},{key:"t",description:"Theme picker"},{key:"b",description:"Base branch picker"},{key:"u",description:"Toggle uncommitted"},{key:"?",description:"This help"}]}];function S9({onClose:X,width:Y,height:$}){CJ((z,N)=>{if(N.escape||N.return||z==="?")X()});let J=Y>=90,Z=J?38:30,K=J?Math.min(82,Y-4):Math.min(40,Y-4),Q;if(J){let z=Math.ceil(d0.length/2),N=d0.slice(0,z),A=d0.slice(z),E=N.reduce((M,O)=>M+O.entries.length+2,0),U=A.reduce((M,O)=>M+O.entries.length+2,0);Q=Math.min(Math.max(E,U)+5,$-4)}else{let z=d0.reduce((N,A)=>N+A.entries.length+2,0)+4;Q=Math.min(z,$-4)}let{x:V,y:G}=D8(K,Q,Y,$),q=(z,N)=>Q8(G0,{flexDirection:"column",marginBottom:1,children:[J0(q8,{bold:!0,dimColor:!0,children:z.title}),z.entries.map((A)=>Q8(G0,{children:[J0(G0,{width:13,children:J0(q8,{color:"cyan",children:A.key})}),J0(G0,{width:N-13,children:J0(q8,{children:A.description})})]},A.key))]},z.title);if(J){let z=Math.ceil(d0.length/2),N=d0.slice(0,z),A=d0.slice(z);return J0(Z8,{x:V,y:G,width:K,height:Q,children:Q8(G0,{borderStyle:"round",borderColor:"cyan",flexDirection:"column",width:K,children:[J0(G0,{justifyContent:"center",marginBottom:1,children:Q8(q8,{bold:!0,color:"cyan",children:[" ","Keyboard Shortcuts"," "]})}),Q8(G0,{children:[J0(G0,{flexDirection:"column",width:Z,marginRight:2,children:N.map((E)=>q(E,Z))}),J0(G0,{flexDirection:"column",width:Z,children:A.map((E)=>q(E,Z))})]}),J0(G0,{marginTop:1,justifyContent:"center",children:J0(q8,{dimColor:!0,children:"Press Esc, Enter, or ? to close"})})]})})}return J0(Z8,{x:V,y:G,width:K,height:Q,children:Q8(G0,{borderStyle:"round",borderColor:"cyan",flexDirection:"column",width:K,children:[J0(G0,{justifyContent:"center",marginBottom:1,children:Q8(q8,{bold:!0,color:"cyan",children:[" ","Keyboard Shortcuts"," "]})}),d0.map((z)=>q(z,Z)),J0(G0,{marginTop:1,justifyContent:"center",children:J0(q8,{dimColor:!0,children:"Press Esc, Enter, or ? to close"})})]})})}import{jsxs as _8,jsx as Z0}from"react/jsx-runtime";import{useState as b9,useMemo as SJ}from"react";import{Box as c0,Text as N0,useInput as bJ}from"ink";function w9({candidates:X,currentBranch:Y,onSelect:$,onCancel:J,width:Z,height:K}){let[Q,V]=b9(""),[G,q]=b9(0),z=SJ(()=>{if(!Q)return X;let H=Q.toLowerCase();return X.filter((B)=>B.toLowerCase().includes(H))},[X,Q]),N=Math.min(G,Math.max(0,z.length-1));bJ((H,B)=>{if(B.escape)J();else if(B.return){if(z.length===0&&Q)$(Q);else if(z.length>0)$(z[N])}else if(B.upArrow)q((L)=>Math.max(0,L-1));else if(B.downArrow)q((L)=>Math.min(z.length-1,L+1));else if(B.backspace||B.delete)V((L)=>L.slice(0,-1)),q(0);else if(H&&!B.ctrl&&!B.meta)V((L)=>L+H),q(0)});let A=Math.min(60,Z-4),E=Math.min(10,K-10),U=Math.min(E+9,K-4),{x:M,y:O}=D8(A,U,Z,K),F=Math.max(0,N-E+1),k=z.slice(F,F+E);return Z0(Z8,{x:M,y:O,width:A,height:U,children:_8(c0,{borderStyle:"round",borderColor:"cyan",flexDirection:"column",width:A,children:[Z0(c0,{justifyContent:"center",marginBottom:1,children:_8(N0,{bold:!0,color:"cyan",children:[" ","Select Base Branch"," "]})}),_8(c0,{marginBottom:1,children:[Z0(N0,{dimColor:!0,children:"Filter: "}),Z0(N0,{color:"cyan",children:Q}),Z0(N0,{color:"cyan",children:"▌"})]}),Z0(c0,{flexDirection:"column",height:E,children:k.length>0?k.map((H,B)=>{let R=F+B===N,S=H===Y;return _8(c0,{children:[Z0(N0,{color:R?"cyan":void 0,children:R?"▸ ":" "}),Z0(N0,{bold:R,color:R?"cyan":void 0,children:H}),S&&Z0(N0,{dimColor:!0,children:" (current)"})]},H)}):Q?_8(c0,{children:[Z0(N0,{dimColor:!0,children:" No matches. Press Enter to use: "}),Z0(N0,{color:"yellow",children:Q})]}):Z0(N0,{dimColor:!0,children:" No candidates found"})}),z.length>E&&Z0(c0,{children:_8(N0,{dimColor:!0,children:[F>0?"↑ ":" ",F+E<z.length?"↓ more":""]})}),Z0(c0,{marginTop:1,justifyContent:"center",children:Z0(N0,{dimColor:!0,children:"↑↓ navigate • Enter select • Esc cancel"})})]})})}function g9({config:X,initialPath:Y}){let{exit:$}=wJ(),{rows:J,columns:Z}=V9(),{state:K,setEnabled:Q}=T4(X.watcherEnabled,X.targetFile,X.debug),V=Y??K.path??process.cwd(),{status:G,diff:q,selectedFile:z,isLoading:N,error:A,selectFile:E,stage:U,unstage:M,discard:O,stageAll:F,unstageAll:k,commit:H,refresh:B,getHeadCommitMessage:L,compareDiff:R,compareLoading:S,compareError:P,refreshCompareDiff:W,getCandidateBaseBranches:C,setCompareBaseBranch:b,historySelectedCommit:w,historyCommitDiff:g,selectHistoryCommit:p,compareSelectionDiff:f,selectCompareCommit:_}=K9(V),v=G?.files??[],y=oY(v),T=v.filter((I)=>I.staged).length,[j,x]=Q0("files"),[D,y8]=Q0("diff"),[l0,s8]=Q0(0),[P8,DX]=Q0(null),[UY,x9]=Q0(!1),[GY,p9]=Q0(X.theme),[T8,n0]=Q0(null),[_X,NY]=Q0(!1),[_0,AY]=Q0(!1),[a8,vX]=Q0(0),[BY,jX]=Q0(0),[EY,u9]=Q0(!1),[MY,h9]=Q0(!0),[LY,m9]=Q0(!0),OY=lY(V,G?.branch??null,K,Z,A,N),f9=OY-1,{topPaneHeight:r0,bottomPaneHeight:yX,paneBoundaries:kY,splitRatio:PX,adjustSplitRatio:WY,fileListScrollOffset:TX,diffScrollOffset:d9,historyScrollOffset:i8,compareScrollOffset:t8,setDiffScrollOffset:b0,setHistoryScrollOffset:CX,setCompareScrollOffset:c9,scrollDiff:z8,scrollFileList:HY,scrollHistory:IY,scrollCompare:FY}=W9(J,Z,v,l0,q,D,void 0,X.splitRatio,f9),V8=VY(()=>{let I=X8(q);if(!_0)return I.length;let W0=R0(I),H0=Z-W0-5;return L8(I,H0,!0)},[q,_0,Z]),{commits:C8,historySelectedIndex:l9,setHistorySelectedIndex:S8,historyDiffTotalRows:RY,navigateHistoryUp:DY,navigateHistoryDown:_Y,historyTotalRows:vY}=D9({repoPath:V,isActive:D==="history",selectHistoryCommit:p,historyCommitDiff:g,historySelectedCommit:w,topPaneHeight:r0,historyScrollOffset:i8,setHistoryScrollOffset:CX,setDiffScrollOffset:b0,status:G,wrapMode:_0,terminalWidth:Z}),{includeUncommitted:n9,compareListSelection:w0,baseBranchCandidates:r9,showBaseBranchPicker:SX,compareTotalItems:e8,compareDiffTotalRows:U8,setCompareSelectedIndex:jY,toggleIncludeUncommitted:o9,openBaseBranchPicker:s9,closeBaseBranchPicker:a9,selectBaseBranch:i9,navigateCompareUp:yY,navigateCompareDown:PY,markSelectionInitialized:TY,getItemIndexFromRow:CY}=v9({repoPath:V,isActive:D==="compare",compareDiff:R,refreshCompareDiff:W,getCandidateBaseBranches:C,setCompareBaseBranch:b,selectCompareCommit:_,topPaneHeight:r0,compareScrollOffset:t8,setCompareScrollOffset:c9,setDiffScrollOffset:b0,status:G,wrapMode:_0,terminalWidth:Z}),{currentPath:t9,items:XX,selectedIndex:e9,setSelectedIndex:SY,selectedFile:G8,navigateUp:bY,navigateDown:wY,enterDirectory:X$,goUp:Y$,isLoading:$$,error:J$,explorerTotalRows:gY}=y9({repoPath:V,isActive:D==="explorer",topPaneHeight:r0,explorerScrollOffset:a8,setExplorerScrollOffset:vX,fileScrollOffset:BY,setFileScrollOffset:jX,hideHiddenFiles:MY,hideGitignored:LY}),xY=VY(()=>{if(!G8)return 0;return F4(G8.content,G8.path,G8.truncated??!1,Z,_0)},[G8,Z,_0]),pY=FX(kY);pY.current=kY;let Z$=FX(X.splitRatio);v8(()=>{if(PX!==Z$.current){let I=setTimeout(()=>pX({splitRatio:PX}),500);return()=>clearTimeout(I)}},[PX]);let K0=VY(()=>hX(v,l0),[v,l0]);v8(()=>{if(y>0&&l0>=y)s8(Math.max(0,y-1))},[y,l0]),v8(()=>{E(K0)},[K0,E]),v8(()=>{if(D==="diff"||D==="commit")b0(0)},[l0,D,b0]),v8(()=>{b0(0)},[_0,b0]);let o0=k0((I)=>{y8(I),x({diff:"files",commit:"commit",history:"history",compare:"compare",explorer:"explorer"}[I])},[]),uY=FX(()=>{}),K$=k0((I)=>{let{x:W0,y:H0,type:YX,button:gX}=I,{stagingPaneStart:N8,fileListEnd:xX,diffPaneStart:L$,diffPaneEnd:O$,footerRow:k$}=pY.current;if(YX==="click"){if(T8!==null){n0(null);return}if(H0===k$&&gX==="left"){let A0=E9(W0,Z);if(A0){o0(A0);return}let i=L9(W0);if(i==="hotkeys"){n0("hotkeys");return}else if(i==="mouse-mode"){uY.current();return}else if(i==="auto-tab"){NY((d)=>!d);return}else if(i==="wrap"){AY((d)=>!d);return}}if(OX(H0,N8+1,xX)){let A0=r0-1,i=(d)=>d>A0?1:0;if(D==="diff"||D==="commit"){let d=B9(H0,TX,v,N8,xX);if(d>=0&&d<y){s8(d),x("files");let l=hX(v,d);if(l){if(gX==="right"&&!l.staged&&l.status!=="untracked")DX(l);else if(gX==="left"&&M9(W0))if(l.staged)M(l);else U(l)}return}}else if(D==="history"){let d=i(C8.length),l=H0-N8-1-d,s0=cX(l,C8,Z,i8);if(s0>=0&&s0<C8.length){S8(s0),x("history"),b0(0);return}}else if(D==="compare"&&R){let d=i(e8),l=H0-N8-1-d+t8,s0=CY(l);if(s0>=0&&s0<e8){TY(),jY(s0),x("compare");return}}else if(D==="explorer"){let d=i(XX.length),l=H0-N8-1-d+a8;if(l>=0&&l<XX.length){SY(l),x("explorer");return}}}if(OX(H0,L$,O$))x(D)}else if(YX==="scroll-up"||YX==="scroll-down"){let A0=YX==="scroll-up"?"up":"down";if(OX(H0,N8,xX)){if(D==="diff"||D==="commit")HY(A0);else if(D==="history")IY(A0,vY);else if(D==="compare")FY(A0,e8);else if(D==="explorer"){let i=A0==="up"?-3:3,d=E8(gY,r0-1);vX((l)=>Math.max(0,Math.min(l+i,d)))}}else if(D==="explorer"){let i=A0==="up"?-3:3,d=E8(xY,yX-1);jX((l)=>Math.max(0,Math.min(l+i,d)))}else{let i;if(D==="compare"&&w0?.type!=="commit")i=U8;else if(D==="history")i=RY;else if(D==="diff")i=V8;z8(A0,3,i)}}},[Z,TX,v,y,D,C8,R,e8,U,M,z8,HY,IY,FY,i8,t8,b0,S8,jY,TY,CY,w0?.type,U8,V8,RY,vY,T8,XX,a8,gY,xY,r0,yX,SY,vX,jX]),Q$=UY||SX,{mouseEnabled:q$,toggleMouse:hY}=z9(K$,Q$);uY.current=hY;let bX=FX(y);v8(()=>{if(!_X){bX.current=y;return}let I=bX.current;if(I===0&&y>0)o0("diff");else if(I>0&&y===0)S8(0),CX(0),o0("history");bX.current=y},[y,_X,o0,S8,CX]);let z$=k0(()=>{if(j==="files")s8((I)=>Math.max(0,I-1));else if(j==="diff"){let I;if(D==="compare"&&w0?.type!=="commit")I=U8;else if(D==="diff")I=V8;z8("up",3,I)}else if(j==="history")DY();else if(j==="compare")yY();else if(j==="explorer")bY()},[j,D,w0?.type,U8,V8,z8,DY,yY,bY]),V$=k0(()=>{if(j==="files")s8((I)=>Math.min(y-1,I+1));else if(j==="diff"){let I;if(D==="compare"&&w0?.type!=="commit")I=U8;else if(D==="diff")I=V8;z8("down",3,I)}else if(j==="history")_Y();else if(j==="compare")PY();else if(j==="explorer")wY()},[j,D,w0?.type,U8,V8,y,z8,_Y,PY,wY]),U$=k0(()=>{if(D==="diff"||D==="commit")x((I)=>I==="files"?"diff":"files");else if(D==="history")x((I)=>I==="history"?"diff":"history");else if(D==="compare")x((I)=>I==="compare"?"diff":"compare");else if(D==="explorer")x((I)=>I==="explorer"?"diff":"explorer")},[D]),G$=k0(async()=>{if(K0&&!K0.staged)await U(K0)},[K0,U]),N$=k0(async()=>{if(K0?.staged)await M(K0)},[K0,M]),A$=k0(async()=>{if(!K0)return;if(K0.staged)await M(K0);else await U(K0)},[K0,U,M]),B$=k0(()=>o0("commit"),[o0]),E$=k0(()=>{y8("diff"),x("files")},[]),M$=k0((I)=>{p9(I),n0(null),pX({theme:I})},[]);Q9({onStage:G$,onUnstage:N$,onStageAll:F,onUnstageAll:k,onCommit:B$,onQuit:$,onRefresh:B,onNavigateUp:z$,onNavigateDown:V$,onTogglePane:U$,onSwitchTab:o0,onSelect:A$,onToggleIncludeUncommitted:o9,onCycleBaseBranch:s9,onOpenThemePicker:()=>n0("theme"),onShrinkTopPane:()=>WY(-zY),onGrowTopPane:()=>WY(zY),onOpenHotkeysModal:()=>n0("hotkeys"),onToggleMouse:hY,onToggleFollow:()=>Q((I)=>!I),onToggleAutoTab:()=>NY((I)=>!I),onToggleWrap:()=>AY((I)=>!I),onToggleMiddleDots:D==="explorer"?()=>u9((I)=>!I):void 0,onToggleHideHiddenFiles:D==="explorer"?()=>h9((I)=>!I):void 0,onToggleHideGitignored:D==="explorer"?()=>m9((I)=>!I):void 0,onExplorerEnter:D==="explorer"?X$:void 0,onExplorerBack:D==="explorer"?Y$:void 0},j,UY||T8!==null||SX),gJ((I,W0)=>{if(!P8)return;if(I==="y"||I==="Y")O(P8),DX(null);else if(I==="n"||I==="N"||W0.escape)DX(null)},{isActive:!!P8});let wX=()=>X0(n8,{dimColor:!0,children:"─".repeat(Z)});return IX(j8,{flexDirection:"column",height:J,width:Z,overflowX:"hidden",children:[X0(j8,{height:OY,width:Z,children:X0(nY,{repoPath:V,branch:G?.branch??null,isLoading:N,error:A,debug:X.debug,watcherState:K,width:Z})}),X0(wX,{}),X0(J4,{bottomTab:D,currentPane:j,terminalWidth:Z,topPaneHeight:r0,files:v,selectedIndex:l0,fileListScrollOffset:TX,stagedCount:T,onStage:U,onUnstage:M,commits:C8,historySelectedIndex:l9,historyScrollOffset:i8,onSelectHistoryCommit:(I,W0)=>S8(W0),compareDiff:R,compareListSelection:w0,compareScrollOffset:t8,includeUncommitted:n9,explorerCurrentPath:t9,explorerItems:XX,explorerSelectedIndex:e9,explorerScrollOffset:a8,explorerIsLoading:$$,explorerError:J$,hideHiddenFiles:MY,hideGitignored:LY}),X0(wX,{}),X0(D4,{bottomTab:D,currentPane:j,terminalWidth:Z,bottomPaneHeight:yX,diffScrollOffset:d9,currentTheme:GY,diff:q,selectedFile:z,stagedCount:T,onCommit:H,onCommitCancel:E$,getHeadCommitMessage:L,onCommitInputFocusChange:x9,historySelectedCommit:w,historyCommitDiff:g,compareDiff:R,compareLoading:S,compareError:P,compareListSelection:w0,compareSelectionDiff:f,wrapMode:_0,explorerSelectedFile:G8,explorerFileScrollOffset:BY,showMiddleDots:EY}),X0(wX,{}),P8?IX(j8,{children:[IX(n8,{color:"yellow",bold:!0,children:["Discard changes to"," "]}),X0(n8,{color:"cyan",children:P8.path}),IX(n8,{color:"yellow",bold:!0,children:["?"," "]}),X0(n8,{dimColor:!0,children:"(y/n)"})]}):X0(eY,{activeTab:D,mouseEnabled:q$,autoTabEnabled:_X,wrapMode:_0,showMiddleDots:EY}),T8==="theme"&&X0(j8,{position:"absolute",marginTop:0,marginLeft:0,children:X0(C9,{currentTheme:GY,onSelect:M$,onCancel:()=>n0(null),width:Z,height:J})}),T8==="hotkeys"&&X0(j8,{position:"absolute",marginTop:0,marginLeft:0,children:X0(S9,{onClose:()=>n0(null),width:Z,height:J})}),SX&&X0(j8,{position:"absolute",marginTop:0,marginLeft:0,children:X0(w9,{candidates:r9,currentBranch:R?.baseBranch??null,onSelect:i9,onCancel:a9,width:Z,height:J})})]})}function o8(){process.stdout.write("\x1B[?1006l"),process.stdout.write("\x1B[?1002l"),process.stdout.write("\x1B[?1000l"),process.stdout.write("\x1B[?25h")}process.on("exit",o8);process.on("SIGINT",()=>{o8(),process.exit(0)});process.on("SIGTERM",()=>{o8(),process.exit(0)});process.on("uncaughtException",(X)=>{o8(),console.error("Uncaught exception:",X),process.exit(1)});process.on("unhandledRejection",(X)=>{o8(),console.error("Unhandled rejection:",X),process.exit(1)});function uJ(X){let Y={};for(let $=0;$<X.length;$++){let J=X[$];if(J==="--follow"||J==="-f"){if(Y.follow=!0,X[$+1]&&!X[$+1].startsWith("-"))Y.followFile=X[++$]}else if(J==="--once")Y.once=!0;else if(J==="--debug"||J==="-d")Y.debug=!0;else if(J==="--help"||J==="-h")console.log(`
|
|
3
43
|
diffstalker - Terminal git diff/status viewer
|
|
4
44
|
|
|
5
45
|
Usage: diffstalker [options] [path]
|
|
@@ -24,7 +64,7 @@ Environment:
|
|
|
24
64
|
DIFFSTALKER_PAGER External pager for diff display
|
|
25
65
|
|
|
26
66
|
Keyboard:
|
|
27
|
-
j/k,
|
|
67
|
+
j/k, ↑/↓ Navigate files / scroll diff
|
|
28
68
|
Ctrl+S Stage selected file
|
|
29
69
|
Ctrl+U Unstage selected file
|
|
30
70
|
Ctrl+A Stage all files
|
|
@@ -40,4 +80,4 @@ Mouse:
|
|
|
40
80
|
Click Select file / focus pane
|
|
41
81
|
Click [+/-] Stage/unstage file
|
|
42
82
|
Scroll Navigate files / scroll diff
|
|
43
|
-
`),process.exit(0)
|
|
83
|
+
`),process.exit(0);else if(!J.startsWith("-"))Y.initialPath=J}return Y}var r8=uJ(process.argv.slice(2)),RX=mY();if(r8.follow){if(RX.watcherEnabled=!0,r8.followFile)RX.targetFile=r8.followFile}if(r8.debug)RX.debug=!0;var{waitUntilExit:hJ}=pJ(xJ(g9,{config:RX,initialPath:r8.initialPath}));hJ().then(()=>{process.exit(0)});
|
|
@@ -1 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Commit validation and logic service.
|
|
3
|
+
* Contains pure functions for commit-related operations.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Validate commit message and staged state.
|
|
7
|
+
*/
|
|
8
|
+
export function validateCommit(message, stagedCount, amend) {
|
|
9
|
+
if (!message.trim()) {
|
|
10
|
+
return { valid: false, error: 'Commit message cannot be empty' };
|
|
11
|
+
}
|
|
12
|
+
if (stagedCount === 0 && !amend) {
|
|
13
|
+
return { valid: false, error: 'No changes staged for commit' };
|
|
14
|
+
}
|
|
15
|
+
return { valid: true, error: null };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Format a commit message (trim whitespace, etc).
|
|
19
|
+
*/
|
|
20
|
+
export function formatCommitMessage(message) {
|
|
21
|
+
return message.trim();
|
|
22
|
+
}
|
package/dist/themes.js
CHANGED
|
@@ -1 +1,127 @@
|
|
|
1
|
-
|
|
1
|
+
// Theme definitions for diffstalker
|
|
2
|
+
// Dark theme - sampled from Claude Code's dark mode
|
|
3
|
+
const darkTheme = {
|
|
4
|
+
name: 'dark',
|
|
5
|
+
displayName: 'Dark',
|
|
6
|
+
colors: {
|
|
7
|
+
addBg: '#022800', // sampled: rgb(2,40,0)
|
|
8
|
+
delBg: '#3D0100', // sampled: rgb(61,1,0)
|
|
9
|
+
addHighlight: '#044700', // sampled: rgb(4,71,0)
|
|
10
|
+
delHighlight: '#5C0200', // sampled: rgb(92,2,0)
|
|
11
|
+
text: 'white',
|
|
12
|
+
addLineNum: '#368F35', // sampled: rgb(54,143,53)
|
|
13
|
+
delLineNum: '#A14040', // sampled: rgb(161,64,64)
|
|
14
|
+
contextLineNum: 'gray',
|
|
15
|
+
addSymbol: 'greenBright',
|
|
16
|
+
delSymbol: 'redBright',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
// Light theme - matches Claude Code's light mode colors
|
|
20
|
+
const lightTheme = {
|
|
21
|
+
name: 'light',
|
|
22
|
+
displayName: 'Light',
|
|
23
|
+
colors: {
|
|
24
|
+
addBg: '#69db7c', // rgb(105,219,124)
|
|
25
|
+
delBg: '#ffa8b4', // rgb(255,168,180)
|
|
26
|
+
addHighlight: '#2f9d44', // rgb(47,157,68)
|
|
27
|
+
delHighlight: '#d1454b', // rgb(209,69,75)
|
|
28
|
+
text: 'black',
|
|
29
|
+
addLineNum: '#2f9d44',
|
|
30
|
+
delLineNum: '#d1454b',
|
|
31
|
+
contextLineNum: '#6c757d',
|
|
32
|
+
addSymbol: 'green',
|
|
33
|
+
delSymbol: 'red',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
// Dark colorblind theme - matches Claude Code's dark-daltonized colors
|
|
37
|
+
const darkColorblindTheme = {
|
|
38
|
+
name: 'dark-colorblind',
|
|
39
|
+
displayName: 'Dark (colorblind)',
|
|
40
|
+
colors: {
|
|
41
|
+
addBg: '#004466', // rgb(0,68,102)
|
|
42
|
+
delBg: '#660000', // rgb(102,0,0)
|
|
43
|
+
addHighlight: '#0077b3', // rgb(0,119,179)
|
|
44
|
+
delHighlight: '#b30000', // rgb(179,0,0)
|
|
45
|
+
text: 'white',
|
|
46
|
+
addLineNum: '#0077b3',
|
|
47
|
+
delLineNum: '#b30000',
|
|
48
|
+
contextLineNum: 'gray',
|
|
49
|
+
addSymbol: 'cyanBright',
|
|
50
|
+
delSymbol: 'redBright',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
// Light colorblind theme - matches Claude Code's light-daltonized colors
|
|
54
|
+
const lightColorblindTheme = {
|
|
55
|
+
name: 'light-colorblind',
|
|
56
|
+
displayName: 'Light (colorblind)',
|
|
57
|
+
colors: {
|
|
58
|
+
addBg: '#99ccff', // rgb(153,204,255)
|
|
59
|
+
delBg: '#ffcccc', // rgb(255,204,204)
|
|
60
|
+
addHighlight: '#3366cc', // rgb(51,102,204)
|
|
61
|
+
delHighlight: '#993333', // rgb(153,51,51)
|
|
62
|
+
text: 'black',
|
|
63
|
+
addLineNum: '#3366cc',
|
|
64
|
+
delLineNum: '#993333',
|
|
65
|
+
contextLineNum: '#6c757d',
|
|
66
|
+
addSymbol: 'blue',
|
|
67
|
+
delSymbol: 'red',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
// Dark ANSI theme - uses terminal's native 16 ANSI colors
|
|
71
|
+
const darkAnsiTheme = {
|
|
72
|
+
name: 'dark-ansi',
|
|
73
|
+
displayName: 'Dark (ANSI)',
|
|
74
|
+
colors: {
|
|
75
|
+
addBg: 'green',
|
|
76
|
+
delBg: 'red',
|
|
77
|
+
addHighlight: 'greenBright',
|
|
78
|
+
delHighlight: 'redBright',
|
|
79
|
+
text: 'white',
|
|
80
|
+
addLineNum: 'greenBright',
|
|
81
|
+
delLineNum: 'redBright',
|
|
82
|
+
contextLineNum: 'gray',
|
|
83
|
+
addSymbol: 'greenBright',
|
|
84
|
+
delSymbol: 'redBright',
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
// Light ANSI theme - uses terminal's native 16 ANSI colors
|
|
88
|
+
const lightAnsiTheme = {
|
|
89
|
+
name: 'light-ansi',
|
|
90
|
+
displayName: 'Light (ANSI)',
|
|
91
|
+
colors: {
|
|
92
|
+
addBg: 'green',
|
|
93
|
+
delBg: 'red',
|
|
94
|
+
addHighlight: 'greenBright',
|
|
95
|
+
delHighlight: 'redBright',
|
|
96
|
+
text: 'black',
|
|
97
|
+
addLineNum: 'green',
|
|
98
|
+
delLineNum: 'red',
|
|
99
|
+
contextLineNum: 'gray',
|
|
100
|
+
addSymbol: 'green',
|
|
101
|
+
delSymbol: 'red',
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
export const themes = {
|
|
105
|
+
dark: darkTheme,
|
|
106
|
+
light: lightTheme,
|
|
107
|
+
'dark-colorblind': darkColorblindTheme,
|
|
108
|
+
'light-colorblind': lightColorblindTheme,
|
|
109
|
+
'dark-ansi': darkAnsiTheme,
|
|
110
|
+
'light-ansi': lightAnsiTheme,
|
|
111
|
+
};
|
|
112
|
+
export const themeOrder = [
|
|
113
|
+
'dark',
|
|
114
|
+
'light',
|
|
115
|
+
'dark-colorblind',
|
|
116
|
+
'light-colorblind',
|
|
117
|
+
'dark-ansi',
|
|
118
|
+
'light-ansi',
|
|
119
|
+
];
|
|
120
|
+
export function getTheme(name) {
|
|
121
|
+
return themes[name] ?? themes['dark'];
|
|
122
|
+
}
|
|
123
|
+
export function getNextTheme(current) {
|
|
124
|
+
const currentIndex = themeOrder.indexOf(current);
|
|
125
|
+
const nextIndex = (currentIndex + 1) % themeOrder.length;
|
|
126
|
+
return themeOrder[nextIndex];
|
|
127
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ANSI-aware string truncation utility.
|
|
3
|
+
*
|
|
4
|
+
* Truncates strings containing ANSI escape codes at a visual character limit
|
|
5
|
+
* while preserving formatting up to the truncation point.
|
|
6
|
+
*/
|
|
7
|
+
// ANSI escape sequence pattern: ESC [ ... m
|
|
8
|
+
// Matches sequences like \x1b[32m, \x1b[0m, \x1b[1;34m, etc.
|
|
9
|
+
const ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
|
|
10
|
+
// ANSI reset sequence to clear all formatting
|
|
11
|
+
const ANSI_RESET = '\x1b[0m';
|
|
12
|
+
/**
|
|
13
|
+
* Calculate the visual length of a string (excluding ANSI codes).
|
|
14
|
+
*/
|
|
15
|
+
export function visualLength(str) {
|
|
16
|
+
return str.replace(ANSI_PATTERN, '').length;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Truncate a string with ANSI codes at a visual character limit.
|
|
20
|
+
*
|
|
21
|
+
* @param str - String potentially containing ANSI escape codes
|
|
22
|
+
* @param maxVisualLength - Maximum visual characters (not counting ANSI codes)
|
|
23
|
+
* @param suffix - Suffix to append when truncated (default: '…')
|
|
24
|
+
* @returns Truncated string with ANSI reset if needed
|
|
25
|
+
*/
|
|
26
|
+
export function truncateAnsi(str, maxVisualLength, suffix = '…') {
|
|
27
|
+
if (maxVisualLength <= 0) {
|
|
28
|
+
return suffix;
|
|
29
|
+
}
|
|
30
|
+
// Quick check: if no ANSI codes and short enough, return as-is
|
|
31
|
+
if (!str.includes('\x1b') && str.length <= maxVisualLength) {
|
|
32
|
+
return str;
|
|
33
|
+
}
|
|
34
|
+
// If no ANSI codes, simple truncation
|
|
35
|
+
if (!str.includes('\x1b')) {
|
|
36
|
+
if (str.length <= maxVisualLength) {
|
|
37
|
+
return str;
|
|
38
|
+
}
|
|
39
|
+
return str.slice(0, maxVisualLength - suffix.length) + suffix;
|
|
40
|
+
}
|
|
41
|
+
// Parse string into segments: either ANSI codes or visible text
|
|
42
|
+
const segments = [];
|
|
43
|
+
let lastIndex = 0;
|
|
44
|
+
// Reset the regex state
|
|
45
|
+
ANSI_PATTERN.lastIndex = 0;
|
|
46
|
+
let match;
|
|
47
|
+
while ((match = ANSI_PATTERN.exec(str)) !== null) {
|
|
48
|
+
// Add text before this ANSI code
|
|
49
|
+
if (match.index > lastIndex) {
|
|
50
|
+
segments.push({ type: 'text', content: str.slice(lastIndex, match.index) });
|
|
51
|
+
}
|
|
52
|
+
// Add the ANSI code
|
|
53
|
+
segments.push({ type: 'ansi', content: match[0] });
|
|
54
|
+
lastIndex = match.index + match[0].length;
|
|
55
|
+
}
|
|
56
|
+
// Add remaining text after last ANSI code
|
|
57
|
+
if (lastIndex < str.length) {
|
|
58
|
+
segments.push({ type: 'text', content: str.slice(lastIndex) });
|
|
59
|
+
}
|
|
60
|
+
// Build result, tracking visual length
|
|
61
|
+
let result = '';
|
|
62
|
+
let currentVisualLength = 0;
|
|
63
|
+
const targetLength = maxVisualLength - suffix.length;
|
|
64
|
+
let hasAnsiCodes = false;
|
|
65
|
+
let truncated = false;
|
|
66
|
+
for (const segment of segments) {
|
|
67
|
+
if (segment.type === 'ansi') {
|
|
68
|
+
// Always include ANSI codes (they don't take visual space)
|
|
69
|
+
result += segment.content;
|
|
70
|
+
hasAnsiCodes = true;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Text segment - check if it fits
|
|
74
|
+
const remainingSpace = targetLength - currentVisualLength;
|
|
75
|
+
if (remainingSpace <= 0) {
|
|
76
|
+
// No more space
|
|
77
|
+
truncated = true;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
if (segment.content.length <= remainingSpace) {
|
|
81
|
+
// Entire segment fits
|
|
82
|
+
result += segment.content;
|
|
83
|
+
currentVisualLength += segment.content.length;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Partial fit - truncate this segment
|
|
87
|
+
result += segment.content.slice(0, remainingSpace);
|
|
88
|
+
currentVisualLength += remainingSpace;
|
|
89
|
+
truncated = true;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (truncated) {
|
|
95
|
+
// Reset formatting before suffix to ensure clean state
|
|
96
|
+
if (hasAnsiCodes) {
|
|
97
|
+
result += ANSI_RESET;
|
|
98
|
+
}
|
|
99
|
+
result += suffix;
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Check if a string needs truncation at the given visual length.
|
|
105
|
+
*/
|
|
106
|
+
export function needsTruncation(str, maxVisualLength) {
|
|
107
|
+
return visualLength(str) > maxVisualLength;
|
|
108
|
+
}
|
|
@@ -1,2 +1,44 @@
|
|
|
1
|
-
import*as
|
|
2
|
-
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
const CACHE_PATH = path.join(os.homedir(), '.cache', 'diffstalker', 'base-branches.json');
|
|
5
|
+
function ensureCacheDir() {
|
|
6
|
+
const dir = path.dirname(CACHE_PATH);
|
|
7
|
+
if (!fs.existsSync(dir)) {
|
|
8
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function loadCache() {
|
|
12
|
+
try {
|
|
13
|
+
if (fs.existsSync(CACHE_PATH)) {
|
|
14
|
+
return JSON.parse(fs.readFileSync(CACHE_PATH, 'utf-8'));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Ignore read errors, return empty cache
|
|
19
|
+
}
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
function saveCache(cache) {
|
|
23
|
+
ensureCacheDir();
|
|
24
|
+
fs.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2) + '\n');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get the cached base branch for a repository.
|
|
28
|
+
* Returns undefined if no cached value exists.
|
|
29
|
+
*/
|
|
30
|
+
export function getCachedBaseBranch(repoPath) {
|
|
31
|
+
const cache = loadCache();
|
|
32
|
+
// Normalize path for consistent lookup
|
|
33
|
+
const normalizedPath = path.resolve(repoPath);
|
|
34
|
+
return cache[normalizedPath];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Save the selected base branch for a repository to the cache.
|
|
38
|
+
*/
|
|
39
|
+
export function setCachedBaseBranch(repoPath, baseBranch) {
|
|
40
|
+
const cache = loadCache();
|
|
41
|
+
const normalizedPath = path.resolve(repoPath);
|
|
42
|
+
cache[normalizedPath] = baseBranch;
|
|
43
|
+
saveCache(cache);
|
|
44
|
+
}
|