From 42025b3f4d086e8ada0a33fccb89ed7656ea5c72 Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Mon, 31 Aug 2026 11:05:01 -0400 Subject: [PATCH] feat(config): enhance kitty tab bar icons and multi-process detection --- .../kitty/__pycache__/tab_bar.cpython-314.pyc | Bin 8952 -> 0 bytes dot_config/kitty/tab_bar.py | 48 +++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) delete mode 100644 dot_config/kitty/__pycache__/tab_bar.cpython-314.pyc diff --git a/dot_config/kitty/__pycache__/tab_bar.cpython-314.pyc b/dot_config/kitty/__pycache__/tab_bar.cpython-314.pyc deleted file mode 100644 index 12d59478d3853d0eb864b3ffbbb584f982c42d0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8952 zcmb_heQ;A(cE3;3lWa*y{=&v!!^1W<0^17M$$||Ag3Xt~n1>^ijg5Ggp5=#SNqO(d zHf}FhsQ5z4TW zzk1GndXk~Aq_g|RKKGpadG5XE{?55qTV1Q{1itAX{pjq`^@RKi1>>{m40D?^5h9QT zd5ox}g`6flbC@|~QcW9>Hyt*sW=PG4Er(c@-9UJYYUNqg<|Vv!+&01RHcHDVEvM8@ zse^Yy=_=j@)Xi4_t>mkKR`WGLSM#+%>-aT5>-n`n*HPL)X(OfU`3(~cjKlF8p}dLG zW=dNq-Nd&-ejC3TsE6MIbSvKuw1d)4O1=DJkh6`_?UZ)$-H`J*rBCqR1AYg;bAs8A zBlYmTAn&4dH>G|29?02C={`!IZZM&#NPOk zI>J9yN5U0q1@8lXl=7AQDd3-`d=>wF;GdyO{-qcJy z;;&IT52HDRNF=HXsuYbVLm->?Mb5>|Q1+V_{)whvr#{pyky&X*GXwe7hHS^Ps%Dy+ z*VrjBEX`}yKv;+e#Wsn`*d0@IpVF_z}Vk2G6gN?e$U7J5&-4jP_Qr=ohxuaiI1Y`WZ~*(9BV z%`%qy>&q$4JQ0s*mY^7k!ZHhTC|a1(6^(^GPy?D}Mwl+_%!_}bSU00i zD+$d8f~1OFuxjzZbZ|m5!xHJLCw{6~!lI(UFak6Sg`;z@rcywCMp5N<4#>F19^QX^ z=%mK_Wib{F;GrlS41)FoI|7geOoI5xY159E2?;`813AG=F+9VYcr$PDqRFzq24EvF z7eN(P4gv-{4vQ|TaXA8ur^+9oU0Wxk@{FJ=I98L^>z#_uh~Ahi1ihW%Xg~;il_^0M zy@loV2BWG!5RFVqA@8)Ls^`3_FyWsNzcTozvyH$!aJD>Y$36;Qj2Sc5g^Zz{(hq7eD{C2`;$wy_ zMx9dHjVlCS%mnRBE8E4)KGT?0FC}CrFhL*P&Nh=$t6{fMr<8(@6^2xt3k!3tGIC344BS@C=Cc-c zo1(f|$7+l^!?gxiN{8!9yutBSL&qJb4C~{q$M!y=r*%gAQaaXX@V;W7y^Nt@2xieh zHV~h67}oLrkGC0G7-`HlLRWf(y?0GVY%BVNU7&qFWcTz{_mlzZ2ostWty&6dtP}yj z)mT+h!=lD;8qt{Z8sqg;Xl4GG91Y3B3~+&&U|5QXnq3H}(yZtg=S9sn(0_R3IDb;J z`_U8lC#A3`KLL}`tbT9?K@q@MR5j~?<45}kc&)t9bS|iwq6z?upiXIaaXuzQf^h{< zvTQ<7#E38hqfmhG%h4z_m*E^sF;AJ?16|Aev42}ZF?oQFCI$hMf|^BwW2>2N;k zh+zefSu3Z1@*b>e56Z%vpUzeGLk2y)cY#TeoZXd-|8Q$!|4mozrTJyc@~OXcW$L_F ztN&sB->%QN_9hPIoE1xTKRlKg$k|+%+HTnDZ&t2L^)J`GeJoSCC29TAQJJe`PbgNjvx4bW~j2xwvy_^zvlJu_@7?D|cROTx?7{ znRB`l17F(8b9MEX`FBR&9L+X*uQhtFnlp{vH|n}yGk@Cj&u#zMmhCxmt>?&p^rU^K z{=F|dIGu*SaesDv^4j=hI&?NOKAq_aC)u2tMBJq*pp)&bQBES>Cm}$5Oh^4B>z~ul~nne!ltbj-Ja1roIwTGQ0-r$Up znn@>VCL9@a86{Q{gc0$$c9OWM5lSV}Yi=YG<1sHB0_Qx-MJG9RO61_2wke!`cDOk> zti=1xZFicU=G`LH^6qoVT!S)fcYXADqp)ny=7Ik9rlaP62*Xz8+?B7E z=gq)EX#bX&`eeAHKly7h)?_1ObpaG62J6wz+jJj|kvYcj&$l&z7 z@e}lK^bmqQZ6Jd$!EB0AhL1JojGC0<48*b!#0(hM80~`|U4yY=+<~=%aOG|w5OtVG zOe;JvL@d+;v!!@})}}g)umC8qD}(us`~UCQ2(zL;swX@I6fdm5YZr|*gp**4qiGH6(E^7qoD&eZIAtmt4+psk zkwf1sO-dpN=Q{27Q6W67P<$(#9xf&*DksMy5m5%X16-R73n8V4JA-!^?w5(b^6FR1 zGn}MwI@=EQ?D5ESBsv%2lmJ8tDmN{PF@=*<1^ZH^84G1a|8~c4#KQ}5P@xh zJCNo)JTx-E5Ay>+Pijv7siC0(-amSr?>|XnIzYP!_87$;ZH2Xk`{1+`i$UyV2A2ib zP1tz_fvA;+T{`LyVlyNZ;=TbULGl&ET?rADt2%M;7w%dBdxz^{-(p{?IqO(|-Ld`) zXU$D_<;A(hxvYD`HTQ;PbH?40b#J-m-jZ>*CoMUL`{M4!-Rb(itYgo0$DUku-5b5H z^`?~NeVOXkQgNl_XgVN(ykkh4euYkYWdW842+8e7f`(R_7NI3dpc(PM|7KZegsO`#%&g1fFEQJdNU0Z7* zE|~*fMnV_td%z^fS2o9m_{-1d+|^0PmzA{;Q-jIvS=^I)e7WoG-mIhThNCT4z2?=U zw^&kDm$wpUMPjhDn!ndJkE1@{eVhLtsu#D}h1(2dsVk33s*6e`#h@uWKgfkVqQwJ310Mu4hO@ulfPyf7131PQ zb8O2adVW9{;WHWi#muAI>9rqhqk$Z=jF`b$gBkgllBxR$#%MkUjMF?s0U8h)UK>3& z8SO?sfuxAPnpK>KC>k!iXQL84B?cuKArj(c++xjw7hDR|@RkS8C8FLJFvA-Jk&)0X zo(CpDj4LGngWYg{v^idU4#HgkH%eSp1=zfJe)0U}iFZP8hTaLk8NSkewKlVUN7lXb zx_c)8+ma<$vHIrfx;I8&8-2t7ntypDvwBPNK+e7T!g(mkRoB1TpQ~AOflW>&qGea0Wdm6XJn@fkP77{iwVd~Kx< zWy}L@zi1!#0agRr0ndyT@0cY^^1!=hj2Y}81Vc7>w6YoFmQvpIu<-SF>kGX-0AwE! z#(U{0wfGnc+nZ5(h$asWq#vb;u_D1nIu<<7;aDMfY@=gAP<#JptUGUA@xW$l)=yOV z!oQBdjThgM^dK@CSMeGwKokW}JP<_b*Gi5);IwmXZF&qPsTdPt6)6(pAfgc=!aM{) z8@xwDfQ9iQeb0r6tZ5ha-p4)1?TX4FFTO+fHF4e*`9tuO)kGsceALo|ID1f>6yjl( zYuco89h_dJ)M)cAIM{t2Eb$#a*3@YqS}?;18sk|oahv5Mun00*3B?Pz-`ff`bp}SE z3=ZdEHI_bi$#JN!nZsfPGB8+^Rmkv|WjsOfn}awHFJnr4a#EUC&>SG%(xa{8AjnAQ z+4&F{h_;->S$VN{u@@oZ7gb$wmDb~@RBhJ5-EeRaKBZijyHc}1?oG9)tJ_}f2d`G$ zO}*O1p2eOeF?Ay2*s#oe?r6$7HeYjW&N#ME^m*m^7oJ}_nYGnlx7DYD%fZWG@R%1D z78g=2%S^`Iw7l(e_ol3S>oxb*jJqRg`EQ4NsVmiTsdu^M{Vnfq`HS|etv8x>eBrMA zL!!)ST!Zh9GFhmuz=aN0>MmiJqj2>`y40MfX{uS5|1Z*5pbacOa~u5w_|PKGl-`(1 z7qT7je5qfpF|@%a|5Dj$rhUbW5`z!@(4+BTAT&gOWPw`{gfthRK(ig&cglZs0Dh;F zak}(ztyMRHVUT4cbYgJuLARSVbt(4sBk#4{sPQBpPg@4B#IKHjJoj1U&;BUw-T#^R zxpxpU=@k2@sp8wIj6s5Kxp;(oFGL*@J>vjLd50NNip+$7g{oK%oWR za;M}iSZoPK1@M5A5@etvga z_+d(oM#JBlIyyXc+B!e}I)i6D8WFP0;bm=aBpv66)yb>oLA%(W?!O9;v6M zXOFHv?96*mPIqQ6-5ITH*Gx1R2mF!$8cNVylvYslxA0cQFn2c-ra4WTenravlho$R zh`lOd&6QUsta(dsz3D59J$WF{0td&EwI`p*+mJ6KtSdR2FGt=^*z#m+-hsT6u+F3} zzY6&Z!mdh&^OeX~VcT8#YUFDO>s~6$uSUKW>z~TkA-_iF>yclpw_S&P1LlwA8qs8T_afh?^Lvor%jk{wF(^F=5)7T+Po;j4u+adO z4iuz=R5}C_PUJ9^j(~(qGDxMPAfeL7s5Dfld7Mh%CUG&oIYFfnkZ?**QOT#5 zsvtd0rSF4;x;#TAfDbzQIF(Kp%KTJ10}|>YP-&td1*jA()D)>SS&%|hnkq;VmCk~M zn>J0QFi5E943#1v;oPHCiWS;DOC`Bbrcg=M%WlO{O0bFZw@EK!y7e4G95vT#c4zE; z3EM3@si^;knAe(a6LW<*;k@O7jO+Ei8TYP)^DDbEVFPQeUx&>zWbGX*{91a4gE#*> cYw+#!&J%D$y;B4Cmpe@m>fLQcJf`6EzgIW2s{jB1 diff --git a/dot_config/kitty/tab_bar.py b/dot_config/kitty/tab_bar.py index 22efeaf..20a069f 100644 --- a/dot_config/kitty/tab_bar.py +++ b/dot_config/kitty/tab_bar.py @@ -36,7 +36,7 @@ ICONS: dict[str, str] = { 'helix': EDITOR, # agents / harnesses 'claude': '\U000f06a9', # nf-md-robot - 'omp': '\U000f09da', # nf-md-brain + 'omp': '\uf2db', # nf-fa-microchip 'opencode': '\uf121', # nf-fa-code 'crush': '\uf0e7', # nf-fa-bolt 'codex': '\uf120', # nf-fa-terminal @@ -53,7 +53,7 @@ ICONS: dict[str, str] = { 'docker': '\uf308', 'docker-compose': '\uf308', 'podman': '\uf308', - 'kubectl': '\ue7b2', # nf-dev-kubernetes + 'kubectl': '\U000f10fe', # nf-md-kubernetes # multiplexers / terminals-in-terminals 'herdr': '\uf009', # nf-fa-th_large — panes 'tmux': '\uf00a', # nf-fa-th @@ -113,7 +113,7 @@ _repo: dict[str, str] = {} def draw_title(data: dict[str, Any]) -> str: tab = data['tab'] - program = _program(_cmdline(data['tab_id']), tab.active_exe) + program = _program(_cmdlines(data['tab_id']), tab.active_exe) index = data['index'] if program in EDITORS: edited = _nvim_file(data['title']) @@ -127,25 +127,47 @@ def draw_title(data: dict[str, Any]) -> str: return f'{index} {icon} {_elide(label or data["title"])}' -def _cmdline(tab_id: int) -> list[str]: - """argv of the tab's active window's foreground process, [] if unavailable.""" +def _cmdlines(tab_id: int) -> list[list[str]]: + """argv of every foreground process of the tab's active window. + + All of them, not just `foreground_cmdline`: an editor commonly has LSP/agent + children in the same process group, and kitty's single "process for cwd" can + be either one of them. + """ from kitty.boss import get_boss try: tab = get_boss().tab_for_id(tab_id) window = tab.active_window if tab is not None else None - return list(window.child.foreground_cmdline) if window is not None else [] + if window is None: + return [] + return [list(p['cmdline'] or ()) for p in window.child.foreground_processes] except Exception: return [] -def _program(cmdline: list[str], fallback_exe: str) -> str: - """Program name a tab should be iconified by. +def _program(cmdlines: list[list[str]], fallback_exe: str) -> str: + """Program a tab should be iconified by, across its foreground processes. - Walks the cmdline past runners and flags: `node …/claude` is claude, an - unknown script keeps its runtime's icon (`uv run python train.py` is - python, `bun run dev` is bun), and a bare `python` stays python. Falls back - to kitty's own exe for the active window when the cmdline is unreadable. + An editor wins over anything else (nvim with a node LSP child is nvim), then + the first process with an icon, then whatever the first process is. + """ + names = [name for cmdline in cmdlines if (name := _resolve(cmdline))] + for name in names: + if name in EDITORS: + return name + for name in names: + if name in ICONS: + return name + return names[0] if names else _canonical(os.path.basename(fallback_exe or '').lower()) + + +def _resolve(cmdline: list[str]) -> str: + """Program name of one cmdline. + + Walks past runners and flags: `node …/claude` is claude, an unknown script + keeps its runtime's icon (`uv run python train.py` is python, `bun run dev` + is bun), and a bare `python` stays python. """ runtime = '' skipped = False @@ -163,7 +185,7 @@ def _program(cmdline: list[str], fallback_exe: str) -> str: runtime = runtime or (name if name in ICONS else '') continue return name if name in ICONS or not runtime else runtime - return runtime or _canonical(os.path.basename(fallback_exe or '').lower()) + return runtime def _canonical(name: str) -> str: