@owloops/claude-powerline 1.24.0 → 1.24.2

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/README.md CHANGED
@@ -609,7 +609,7 @@ Create custom themes and configure color compatibility:
609
609
  }
610
610
  ```
611
611
 
612
- By default, the TUI panel uses a built-in responsive layout. For full control over what goes where, add a `display.tui` object to your config this activates the **grid layout engine**, a CSS Grid-inspired system that lets you define rows, columns, spans, and responsive breakpoints.
612
+ By default, the TUI panel uses a built-in responsive layout. For full control over what goes where, add a `display.tui` object to your config. This activates the **grid layout engine**, a CSS Grid-inspired system that lets you define rows, columns, spans, and responsive breakpoints.
613
613
 
614
614
  #### Grid Layout Configuration
615
615
 
@@ -648,10 +648,10 @@ Add `display.tui` to your config file to enable the grid engine:
648
648
  | `padding.horizontal` | `number` | `0` | Extra horizontal padding in `fitContent` mode |
649
649
  | `separator.column` | `string` | `" "` | String placed between columns |
650
650
  | `separator.divider` | `string` | box char | Character used for `---` divider rows |
651
- | `box` | `object` | | Custom box-drawing characters (see below) |
652
- | `title` | `object` | | Title bar text configuration (see below) |
653
- | `footer` | `object` | | Footer text configuration (see below) |
654
- | `segments` | `object` | | Custom segment templates (see below) |
651
+ | `box` | `object` | -- | Custom box-drawing characters (see below) |
652
+ | `title` | `object` | -- | Title bar text configuration (see below) |
653
+ | `footer` | `object` | -- | Footer text configuration (see below) |
654
+ | `segments` | `object` | -- | Custom segment templates (see below) |
655
655
  | `breakpoints` | `array` | required | Responsive layout definitions |
656
656
 
657
657
  #### Breakpoints
@@ -700,18 +700,18 @@ Each breakpoint defines a complete layout that activates when the panel width is
700
700
  | Property | Type | Required | Description |
701
701
  |---|---|---|---|
702
702
  | `minWidth` | `number` | yes | Minimum panel width to activate this layout |
703
- | `areas` | `string[]` | yes | Grid rows each string is one row of space-separated cell names |
703
+ | `areas` | `string[]` | yes | Grid rows, each string is one row of space-separated cell names |
704
704
  | `columns` | `string[]` | yes | Column sizing: `"auto"`, `"1fr"` / `"2fr"`, or a fixed number like `"20"` |
705
705
  | `align` | `string[]` | no | Per-column alignment: `"left"`, `"center"`, or `"right"` (defaults to `"left"`) |
706
706
 
707
707
  **Column sizing:**
708
- - `"auto"` shrinks to the widest content in that column
709
- - `"1fr"`, `"2fr"` fractional units that divide remaining space proportionally
710
- - `"20"` fixed width in characters
708
+ - `"auto"` - shrinks to the widest content in that column
709
+ - `"1fr"`, `"2fr"` - fractional units that divide remaining space proportionally
710
+ - `"20"` - fixed width in characters
711
711
 
712
712
  **Special area tokens:**
713
- - `.` empty cell (renders as blank space)
714
- - `---` full-width horizontal divider row
713
+ - `.` - empty cell (renders as blank space)
714
+ - `---` - full-width horizontal divider row
715
715
 
716
716
  **Spanning:** repeat the same name in adjacent cells to span columns:
717
717
 
@@ -751,7 +751,7 @@ Use `segment.part` to place individual pieces of a segment into separate cells w
751
751
  | `dir` | `value` |
752
752
  | `env` | `prefix`, `value` |
753
753
 
754
- Example block segment with a progress bar, mirroring the context layout:
754
+ Example, block segment with a progress bar, mirroring the context layout:
755
755
 
756
756
  ```json
757
757
  "areas": [
@@ -761,7 +761,7 @@ Example — block segment with a progress bar, mirroring the context layout:
761
761
  ```
762
762
 
763
763
  > [!NOTE]
764
- > `context.bar`, `block.bar`, and `weekly.bar` are width-aware their progress bars render at exactly the resolved column width. Block bar uses `nativeUtilization` when available, or `cost / budget` for transcript mode. Weekly bar uses the 7-day `used_percentage`.
764
+ > `context.bar`, `block.bar`, and `weekly.bar` are width-aware. Their progress bars render at exactly the resolved column width. Block bar uses `nativeUtilization` when available, or `cost / budget` for transcript mode. Weekly bar uses the 7-day `used_percentage`.
765
765
 
766
766
  #### Custom Box Characters
767
767
 
@@ -780,7 +780,7 @@ Override individual box-drawing characters. Partial overrides merge with the cha
780
780
  }
781
781
  ```
782
782
 
783
- Only specify the characters you want to change the rest inherit from the active charset.
783
+ Only specify the characters you want to change. The rest inherit from the active charset.
784
784
 
785
785
  #### Title Bar
786
786
 
@@ -811,8 +811,8 @@ Same as the title bar, but on the bottom border. Defaults to no text (plain bord
811
811
 
812
812
  | Property | Type | Default | Description |
813
813
  |---|---|---|---|
814
- | `left` | `string` | | Left-side footer text (supports tokens) |
815
- | `right` | `string` | | Right-side footer text (supports tokens) |
814
+ | `left` | `string` | -- | Left-side footer text (supports tokens) |
815
+ | `right` | `string` | -- | Right-side footer text (supports tokens) |
816
816
 
817
817
  Tokens resolve any segment or subsegment reference: `{model}`, `{dir}`, `{git.head}`, `{block.value}`, `{metrics.lastResponse}`, etc.
818
818
 
@@ -840,7 +840,7 @@ The template name (e.g. `metrics.lastResponse`) can then be used as a cell name
840
840
 
841
841
  #### Automatic Culling
842
842
 
843
- Empty segments are automatically removed cells resolve to `.`, empty rows are dropped, and orphaned dividers are cleaned up. A wide layout gracefully degrades when data is unavailable.
843
+ Empty segments are automatically removed. Cells resolve to `.`, empty rows are dropped, and orphaned dividers are cleaned up. A wide layout gracefully degrades when data is unavailable.
844
844
 
845
845
  > [!NOTE]
846
846
  > Claude Code's internal progress indicators (spinner, context bar) may briefly overlap the TUI panel during tool calls. This is a limitation of the hook architecture and resolves once the tool call completes.
package/dist/index.mjs CHANGED
@@ -8,7 +8,7 @@ import e from"node:process";import{json as t}from"node:stream/consumers";import
8
8
  `)+kn}let s=Mn(r),c=s-2,l=c-2,u=jn(s),d=[];d.push(Et(e,t,c));let f=Mt(e,l,a,n,o);f&&d.push(V(t,f,c));let p={lines:d,data:e,box:t,contentWidth:l,innerWidth:c,sym:a,config:i,reset:n,colors:o};return u===`wide`?(un(p),dn(p)):u===`medium`?(fn(p),pn(p)):(mn(p),hn(p)),d.push(wt(t,c)),On+d.map(e=>An+e).join(`
9
9
  `)+kn}var Pn=class{symbols;_usageProvider;_blockProvider;_todayProvider;_contextProvider;_gitService;_tmuxService;_metricsProvider;_segmentRenderer;constructor(e){this.config=e,this.symbols=this.initializeSymbols()}get usageProvider(){return this._usageProvider||=new Be,this._usageProvider}get blockProvider(){return this._blockProvider||=new at,this._blockProvider}get todayProvider(){return this._todayProvider||=new ct,this._todayProvider}get contextProvider(){return this._contextProvider||=new Ve(this.config),this._contextProvider}get gitService(){return this._gitService||=new Ce,this._gitService}get tmuxService(){return this._tmuxService||=new Te,this._tmuxService}get metricsProvider(){return this._metricsProvider||=new He,this._metricsProvider}get segmentRenderer(){return this._segmentRenderer||=new nt(this.config,this.symbols),this._segmentRenderer}needsSegmentInfo(e){return this.config.display.lines.some(t=>t.segments[e]?.enabled)}async generateStatusline(e){if(this.config.display.style===`tui`)return this.generateTuiStatusline(e);let t=this.needsSegmentInfo(`session`)?await this.usageProvider.getUsageInfo(e.session_id,e):null,n=this.needsSegmentInfo(`block`)?await this.blockProvider.getActiveBlockInfo(e):null,r=this.needsSegmentInfo(`today`)?await this.todayProvider.getTodayInfo():null,i=this.config.display.lines.map(e=>e.segments.context).find(e=>e?.enabled)?.autocompactBuffer??33e3,a=this.needsSegmentInfo(`context`)?await this.contextProvider.getContextInfo(e,i):null,o=this.needsSegmentInfo(`metrics`)?await this.metricsProvider.getMetricsInfo(e.session_id,e):null;return this.config.display.autoWrap?this.generateAutoWrapStatusline(e,t,n,r,a,o):(await Promise.all(this.config.display.lines.map(i=>this.renderLine(i,e,t,n,r,a,o)))).filter(e=>e.length>0).join(`
10
10
  `)}async generateAutoWrapStatusline(e,t,n,r,i,a){let o=this.getThemeColors(),s=e.workspace?.current_dir||e.cwd||`/`,c=yt(),l=[];for(let u of this.config.display.lines){let d=Object.entries(u.segments).filter(([e,t])=>t?.enabled).map(([e,t])=>({type:e,config:t})),f=[];for(let c of d){let l=await this.renderSegment(c,e,t,n,r,i,a,o,s);l&&f.push({type:c.type,text:l.text,bgColor:l.bgColor,fgColor:l.fgColor})}if(f.length===0)continue;if(!c||c<=0){l.push(this.buildLineFromSegments(f,o));continue}let p=[],m=0;for(let e of f){let t=this.calculateSegmentWidth(e,p.length===0);p.length>0&&m+t>c&&(l.push(this.buildLineFromSegments(p,o)),p=[],m=0),p.push(e),m+=t}p.length>0&&l.push(this.buildLineFromSegments(p,o))}return l.join(`
11
- `)}async generateTuiStatusline(e){let t=this.getThemeColors(),n=yt(),r=e.workspace?.current_dir||e.cwd||`/`,i=(this.config.display.charset||`unicode`)===`text`?dt:ut,a=this.config.display.lines.map(e=>e.segments.context).find(e=>e?.enabled)?.autocompactBuffer??33e3,o=await Promise.allSettled([this.usageProvider.getUsageInfo(e.session_id,e),this.blockProvider.getActiveBlockInfo(e),this.todayProvider.getTodayInfo(),this.contextProvider.getContextInfo(e,a),this.metricsProvider.getMetricsInfo(e.session_id,e),this.gitService.getGitInfo(r,{showSha:!1,showWorkingTree:!0,showOperation:!1,showTag:!1,showTimeSinceCommit:!1,showStashCount:!1,showUpstream:!1,showRepoName:!1},e.workspace?.project_dir),this.tmuxService.getSessionId()]),s=e=>e.status===`fulfilled`?e.value:null,[c,l,u,d,f,p,m]=[s(o[0]),s(o[1]),s(o[2]),s(o[3]),s(o[4]),s(o[5]),s(o[6])];return Nn({hookData:e,usageInfo:c,blockInfo:l,todayInfo:u,contextInfo:d,metricsInfo:f,gitInfo:p,tmuxSessionId:m,colors:t},i,t.reset,n,this.config)}calculateSegmentWidth(e,t){let n=this.config.display.style===`capsule`,r=L(e.text),i=(this.config.display.padding??1)*2;return n?r+(2+i+(t?0:1)):r+(1+i)}buildLineFromSegments(e,t){let n=this.config.display.style===`capsule`,r=t.reset;for(let i=0;i<e.length;i++){let a=e[i];if(!a)continue;let o=i===0,s=i===e.length-1?null:e[i+1];n&&!o&&(r+=` `),r+=this.formatSegment(a.bgColor,a.fgColor,a.text,s?.bgColor,t)}return r}async renderLine(e,t,n,r,i,a,o){let s=this.getThemeColors(),c=t.workspace?.current_dir||t.cwd||`/`,l=Object.entries(e.segments).filter(([e,t])=>t?.enabled).map(([e,t])=>({type:e,config:t})),u=[];for(let e of l){let l=await this.renderSegment(e,t,n,r,i,a,o,s,c);l&&u.push({type:e.type,text:l.text,bgColor:l.bgColor,fgColor:l.fgColor})}return this.buildLineFromSegments(u,s)}async renderSegment(e,t,n,r,i,a,o,s,c){return e.type===`directory`?this.segmentRenderer.renderDirectory(t,s,e.config):e.type===`model`?this.segmentRenderer.renderModel(t,s):e.type===`git`?await this.renderGitSegment(e.config,t,s,c):e.type===`session`?this.renderSessionSegment(e.config,n,s):e.type===`sessionId`?t.session_id?this.segmentRenderer.renderSessionId(t.session_id,s,e.config):null:e.type===`tmux`?await this.renderTmuxSegment(s):e.type===`context`?this.renderContextSegment(e.config,a,s):e.type===`metrics`?this.renderMetricsSegment(e.config,o,r,s):e.type===`block`?this.renderBlockSegment(e.config,r,s):e.type===`today`?this.renderTodaySegment(e.config,i,s):e.type===`version`?this.renderVersionSegment(e.config,t,s):e.type===`env`?this.segmentRenderer.renderEnv(s,e.config):e.type===`weekly`?this.segmentRenderer.renderWeekly(t,s,e.config):null}async renderGitSegment(e,t,n,r){if(!this.needsSegmentInfo(`git`))return null;let i=await this.gitService.getGitInfo(r,{showSha:e?.showSha,showWorkingTree:e?.showWorkingTree,showOperation:e?.showOperation,showTag:e?.showTag,showTimeSinceCommit:e?.showTimeSinceCommit,showStashCount:e?.showStashCount,showUpstream:e?.showUpstream,showRepoName:e?.showRepoName},t.workspace?.project_dir);return i?this.segmentRenderer.renderGit(i,n,e):null}renderSessionSegment(e,t,n){return t?this.segmentRenderer.renderSession(t,n,e):null}async renderTmuxSegment(e){if(!this.needsSegmentInfo(`tmux`))return null;let t=await this.tmuxService.getSessionId();return this.segmentRenderer.renderTmux(t,e)}renderContextSegment(e,t,n){return this.needsSegmentInfo(`context`)?this.segmentRenderer.renderContext(t,n,e):null}renderMetricsSegment(e,t,n,r){return this.segmentRenderer.renderMetrics(t,r,e)}renderBlockSegment(e,t,n){return t?this.segmentRenderer.renderBlock(t,n,e):null}renderTodaySegment(e,t,n){if(!t)return null;let r=e?.type||`cost`;return this.segmentRenderer.renderToday(t,n,r)}renderVersionSegment(e,t,n){return this.segmentRenderer.renderVersion(t,n,e)}initializeSymbols(){let e=this.config.display.style,t=this.config.display.charset||`unicode`,n=e===`minimal`,r=e===`capsule`,i=t===`text`?ft:lt;return{right:n?``:r?i.right_rounded:i.right,left:r?i.left_rounded:``,branch:i.branch,model:i.model,git_clean:i.git_clean,git_dirty:i.git_dirty,git_conflicts:i.git_conflicts,git_ahead:i.git_ahead,git_behind:i.git_behind,git_worktree:i.git_worktree,git_tag:i.git_tag,git_sha:i.git_sha,git_upstream:i.git_upstream,git_stash:i.git_stash,git_time:i.git_time,session_cost:i.session_cost,block_cost:i.block_cost,today_cost:i.today_cost,context_time:i.context_time,metrics_response:i.metrics_response,metrics_last_response:i.metrics_last_response,metrics_duration:i.metrics_duration,metrics_messages:i.metrics_messages,metrics_lines_added:i.metrics_lines_added,metrics_lines_removed:i.metrics_lines_removed,metrics_burn:i.metrics_burn,version:i.version,bar_filled:i.bar_filled,bar_empty:i.bar_empty,env:i.env,session_id:i.session_id,weekly_cost:i.weekly_cost}}getThemeColors(){let e=this.config.theme,t,n=this.config.display.colorCompatibility||`auto`,r=n===`auto`?C():n;if(e===`custom`){if(t=this.config.colors?.custom,!t)throw Error(`Custom theme selected but no colors provided in configuration`)}else t=T(e,r),t||=(console.warn(`Built-in theme '${e}' not found, falling back to 'dark' theme`),T(`dark`,r));let i=(e,t)=>r===`none`?``:r===`ansi`?ne(e,t):r===`ansi256`?te(e,t):ee(e,t),a=T(`dark`,r),o=this.config.display.style===`tui`,s=e===`light`?`#f0f0f0`:`#1e1e1e`,c=e=>{let n=t[e]||a[e],r=n.fg;return o&&re(r,s)<60&&(r=n.bg),{bg:i(n.bg,!0),fg:i(r,!1)}},l=c(`directory`),u=c(`git`),d=c(`model`),f=c(`session`),p=c(`block`),m=c(`today`),h=c(`tmux`),g=c(`context`),_=c(`contextWarning`),v=c(`contextCritical`),y=c(`metrics`),b=c(`version`),x=c(`env`),S=c(`weekly`);return{reset:r===`none`?``:`\x1B[0m`,modeBg:l.bg,modeFg:l.fg,gitBg:u.bg,gitFg:u.fg,modelBg:d.bg,modelFg:d.fg,sessionBg:f.bg,sessionFg:f.fg,blockBg:p.bg,blockFg:p.fg,todayBg:m.bg,todayFg:m.fg,tmuxBg:h.bg,tmuxFg:h.fg,contextBg:g.bg,contextFg:g.fg,contextWarningBg:_.bg,contextWarningFg:_.fg,contextCriticalBg:v.bg,contextCriticalFg:v.fg,metricsBg:y.bg,metricsFg:y.fg,versionBg:b.bg,versionFg:b.fg,envBg:x.bg,envFg:x.fg,weeklyBg:S.bg,weeklyFg:S.fg,partFg:this.resolvePartColors(i)}}resolvePartColors(e){let t=this.config.colors?.custom;if(!t)return{};let n={};for(let r of Object.keys(t)){let i=t[r];i?.fg&&(n[r]=e(i.fg,!1))}return n}getSegmentBgColor(e,t){switch(e){case`directory`:return t.modeBg;case`git`:return t.gitBg;case`model`:return t.modelBg;case`session`:case`sessionId`:return t.sessionBg;case`block`:return t.blockBg;case`today`:return t.todayBg;case`tmux`:return t.tmuxBg;case`context`:return t.contextBg;case`metrics`:return t.metricsBg;case`version`:return t.versionBg;case`env`:return t.envBg;case`weekly`:return t.weeklyBg;default:return t.modeBg}}formatSegment(e,t,n,r,i){let a=this.config.display.style===`capsule`,o=` `.repeat(this.config.display.padding??1);if(a){let r=this.config.display.colorCompatibility||`auto`,a=S(e,(r===`auto`?C():r)===`ansi`);return`${`${a}${this.symbols.left}${i.reset}`}${`${e}${t}${o}${n}${o}${i.reset}`}${`${a}${this.symbols.right}${i.reset}`}`}let s=`${e}${t}${o}${n}${o}`,c=this.config.display.colorCompatibility||`auto`,l=(c===`auto`?C():c)===`ansi`;if(r){let t=S(e,l);s+=`${i.reset}${r}${t}${this.symbols.right}`}else s+=`${i.reset}${S(e,l)}${this.symbols.right}${i.reset}`;return s}};const Fn={theme:`dark`,display:{style:`minimal`,charset:`unicode`,colorCompatibility:`auto`,autoWrap:!0,padding:1,lines:[{segments:{directory:{enabled:!0,style:`basename`},git:{enabled:!0,showSha:!1,showWorkingTree:!1,showOperation:!1,showTag:!1,showTimeSinceCommit:!1,showStashCount:!1,showUpstream:!1,showRepoName:!1},model:{enabled:!0},session:{enabled:!0,type:`tokens`,costSource:`calculated`},today:{enabled:!0,type:`cost`},block:{enabled:!1,type:`cost`,burnType:`cost`,displayStyle:`text`},weekly:{enabled:!1,displayStyle:`text`},version:{enabled:!1},tmux:{enabled:!1},sessionId:{enabled:!1,showIdLabel:!0},context:{enabled:!0,showPercentageOnly:!1,displayStyle:`text`,autocompactBuffer:33e3},metrics:{enabled:!1,showResponseTime:!0,showLastResponseTime:!0,showDuration:!0,showMessageCount:!0,showLinesAdded:!0,showLinesRemoved:!0}}}]},budget:{session:{warningThreshold:80},today:{warningThreshold:80,amount:50},block:{warningThreshold:80,amount:15}},modelContextLimits:{default:2e5,sonnet:2e5,opus:2e5}},In=new Set([`context`,`block`,`session`,`today`,`weekly`,`git`,`dir`,`model`,`version`,`tmux`,`metrics`,`activity`,`burn`,`env`]),Ln={session:[`icon`,`cost`,`tokens`,`budget`],block:[`icon`,`value`,`time`,`budget`,`bar`],today:[`icon`,`cost`,`label`,`budget`],weekly:[`icon`,`pct`,`time`,`bar`],git:[`icon`,`branch`,`status`,`ahead`,`behind`,`working`,`head`],context:[`icon`,`bar`,`pct`,`tokens`],metrics:[`response`,`responseIcon`,`responseVal`,`lastResponse`,`lastResponseIcon`,`lastResponseVal`,`added`,`addedIcon`,`addedVal`,`removed`,`removedIcon`,`removedVal`],activity:[`duration`,`durationIcon`,`durationVal`,`messages`,`messagesIcon`,`messagesVal`],model:[`icon`,`value`],burn:[`icon`,`rate`],version:[`icon`,`value`],tmux:[`label`,`value`],dir:[`value`],env:[`prefix`,`value`]};function Rn(e){if(e===`.`||e===`---`||In.has(e))return!0;let t=e.indexOf(`.`);if(t===-1)return!1;let n=e.slice(0,t),r=e.slice(t+1);if(!n||!r)return!1;let i=Ln[n];return i?i.includes(r):!1}function zn(e){return[`light`,`dark`,`nord`,`tokyo-night`,`rose-pine`,`gruvbox`,`custom`].includes(e)}function Bn(e){return e===`minimal`||e===`powerline`||e===`capsule`||e===`tui`}function Vn(e){return e===`unicode`||e===`text`}function Q(e,t){for(let n=0;n<e.length;n++){let r=e[n];if(r===t&&n+1<e.length)return e[n+1];if(r?.startsWith(`${t}=`))return r.split(`=`)[1]}}function $(e,t){let n={...e};for(let e in t){let r=t[e];r!==void 0&&(typeof r==`object`&&r&&!Array.isArray(r)?n[e]=$(n[e]||{},r):n[e]=r)}return n}function Hn(e,t){return e?o.existsSync(e)?e:null:[...t?[l.join(t,`.claude-powerline.json`)]:[],l.join(process.cwd(),`.claude-powerline.json`),l.join(m.homedir(),`.claude`,`claude-powerline.json`),l.join(m.homedir(),`.config`,`claude-powerline`,`config.json`)].find(o.existsSync)||null}function Un(e){try{let t=o.readFileSync(e,`utf-8`);return JSON.parse(t)}catch(t){throw Error(`Failed to load config file ${e}: ${t instanceof Error?t.message:String(t)}`)}}function Wn(){let e={},t={},n=process.env.CLAUDE_POWERLINE_THEME;n&&zn(n)&&(e.theme=n);let r=process.env.CLAUDE_POWERLINE_STYLE;return r&&(Bn(r)?t.style=r:(console.warn(`Invalid display style '${r}' from environment variable, falling back to 'minimal'`),t.style=`minimal`)),Object.keys(t).length>0&&(e.display=t),e}function Gn(){return process.env.CLAUDE_POWERLINE_CONFIG}function Kn(e){let t={},n={},r=Q(e,`--theme`);r&&zn(r)&&(t.theme=r);let i=Q(e,`--style`);i&&(Bn(i)?n.style=i:(console.warn(`Invalid display style '${i}' from CLI argument, falling back to 'minimal'`),n.style=`minimal`));let a=Q(e,`--charset`);return a&&(Vn(a)?n.charset=a:(console.warn(`Invalid charset '${a}' from CLI argument, falling back to 'unicode'`),n.charset=`unicode`)),Object.keys(n).length>0&&(t.display=n),t}function qn(e){if(typeof e.box==`string`&&!I[e.box]){let t=Object.keys(I).join(`, `);return`unknown box preset "${e.box}" (valid: ${t})`}if(!e.breakpoints||!Array.isArray(e.breakpoints)||e.breakpoints.length===0)return`grid config must have at least one breakpoint`;let t=new Set;for(let n=0;n<e.breakpoints.length;n++){let r=e.breakpoints[n],i=`breakpoint[${n}]`;if(typeof r.minWidth!=`number`||r.minWidth<0)return`${i}: minWidth must be a non-negative number`;if(t.has(r.minWidth))return`${i}: duplicate minWidth ${r.minWidth} (each breakpoint must have a unique minWidth)`;if(t.add(r.minWidth),!r.areas||!Array.isArray(r.areas)||r.areas.length===0)return`${i}: areas must be a non-empty array of strings`;if(!r.columns||!Array.isArray(r.columns)||r.columns.length===0)return`${i}: columns must be a non-empty array`;let a=r.columns.length;for(let e of r.columns){if(typeof e!=`string`)return`${i}: column definition must be a string`;if(!/^(\d+fr|\d+|auto)$/.test(e))return`${i}: invalid column definition "${e}" (use "auto", "Nfr", or a fixed integer)`}if(r.align!==void 0){if(!Array.isArray(r.align))return`${i}: align must be an array`;if(r.align.length!==a)return`${i}: align length (${r.align.length}) must match columns length (${a})`;for(let e of r.align)if(e!==`left`&&e!==`center`&&e!==`right`)return`${i}: invalid align value "${e}"`}let o=new Set;for(let t=0;t<r.areas.length;t++){let n=r.areas[t];if(n.trim()===`---`)continue;let s=n.trim().split(/\s+/);if(s.length!==a)return`${i}: row "${n}" has ${s.length} cells but expected ${a} columns`;let c=e.segments?new Set(Object.keys(e.segments)):new Set,l=``,u=``;for(let e of s){if(e!==`.`){if(!Rn(e)&&!c.has(e))return`${i}: unknown segment name "${e}"`;if(e!==u&&o.has(e))return`${i}: segment "${e}" appears on multiple rows`}e!==l&&(u=e),l=e}let d=new Map;for(let e=0;e<s.length;e++){let t=s[e];if(t===`.`||t===`---`)continue;let r=d.get(t);if(r!==void 0&&r!==e-1)return`${i}: segment "${t}" has non-contiguous span in row "${n}"`;d.set(t,e)}for(let e of s)e!==`.`&&e!==`---`&&o.add(e)}}if(e.segments)for(let[t,n]of Object.entries(e.segments)){if(!n.items||!Array.isArray(n.items))return`segments["${t}"]: items must be an array`;if(n.justify!==void 0&&n.justify!==`start`&&n.justify!==`between`)return`segments["${t}"]: invalid justify value "${n.justify}" (use "start" or "between")`}return null}function Jn(e=process.argv,t){let n=JSON.parse(JSON.stringify(Fn)),r=Q(e,`--config`)||Gn(),i=Hn(r?.startsWith(`~`)?r.replace(`~`,m.homedir()):r,t);if(i)try{let e=Un(i);n=$(n,e)}catch(e){console.warn(`Warning: ${e instanceof Error?e.message:String(e)}`)}n.display?.style&&!Bn(n.display.style)&&(console.warn(`Invalid display style '${n.display.style}' in config file, falling back to 'minimal'`),n.display.style=`minimal`),n.display?.charset&&!Vn(n.display.charset)&&(console.warn(`Invalid charset '${n.display.charset}' in config file, falling back to 'unicode'`),n.display.charset=`unicode`),n.theme&&!zn(n.theme)&&(console.warn(`Invalid theme '${n.theme}' in config file, falling back to 'dark'`),n.theme=`dark`);let a=Wn();n=$(n,a);let o=Kn(e);if(n=$(n,o),n.display?.tui){let e=qn(n.display.tui);e&&(process.stderr.write(`Warning: invalid grid config: ${e}. Falling back to hardcoded layout.\n`),delete n.display.tui)}return n}const Yn=Jn;function Xn(){console.log(`
11
+ `)}async generateTuiStatusline(e){let t=this.getThemeColors(),n=yt(),r=e.workspace?.current_dir||e.cwd||`/`,i=(this.config.display.charset||`unicode`)===`text`?dt:ut,a=this.config.display.lines.map(e=>e.segments.context).find(e=>e?.enabled)?.autocompactBuffer??33e3,o=await Promise.allSettled([this.usageProvider.getUsageInfo(e.session_id,e),this.blockProvider.getActiveBlockInfo(e),this.todayProvider.getTodayInfo(),this.contextProvider.getContextInfo(e,a),this.metricsProvider.getMetricsInfo(e.session_id,e),this.gitService.getGitInfo(r,{showSha:!1,showWorkingTree:!0,showOperation:!1,showTag:!1,showTimeSinceCommit:!1,showStashCount:!1,showUpstream:!1,showRepoName:!1},e.workspace?.project_dir),this.tmuxService.getSessionId()]),s=e=>e.status===`fulfilled`?e.value:null,[c,l,u,d,f,p,m]=[s(o[0]),s(o[1]),s(o[2]),s(o[3]),s(o[4]),s(o[5]),s(o[6])];return Nn({hookData:e,usageInfo:c,blockInfo:l,todayInfo:u,contextInfo:d,metricsInfo:f,gitInfo:p,tmuxSessionId:m,colors:t},i,t.reset,n,this.config)}calculateSegmentWidth(e,t){let n=this.config.display.style===`capsule`,r=L(e.text),i=(this.config.display.padding??1)*2;return n?r+(2+i+(t?0:1)):r+(1+i)}buildLineFromSegments(e,t){let n=this.config.display.style===`capsule`,r=t.reset;for(let i=0;i<e.length;i++){let a=e[i];if(!a)continue;let o=i===0,s=i===e.length-1?null:e[i+1];n&&!o&&(r+=` `),r+=this.formatSegment(a.bgColor,a.fgColor,a.text,s?.bgColor,t)}return r}async renderLine(e,t,n,r,i,a,o){let s=this.getThemeColors(),c=t.workspace?.current_dir||t.cwd||`/`,l=Object.entries(e.segments).filter(([e,t])=>t?.enabled).map(([e,t])=>({type:e,config:t})),u=[];for(let e of l){let l=await this.renderSegment(e,t,n,r,i,a,o,s,c);l&&u.push({type:e.type,text:l.text,bgColor:l.bgColor,fgColor:l.fgColor})}return this.buildLineFromSegments(u,s)}async renderSegment(e,t,n,r,i,a,o,s,c){return e.type===`directory`?this.segmentRenderer.renderDirectory(t,s,e.config):e.type===`model`?this.segmentRenderer.renderModel(t,s):e.type===`git`?await this.renderGitSegment(e.config,t,s,c):e.type===`session`?this.renderSessionSegment(e.config,n,s):e.type===`sessionId`?t.session_id?this.segmentRenderer.renderSessionId(t.session_id,s,e.config):null:e.type===`tmux`?await this.renderTmuxSegment(s):e.type===`context`?this.renderContextSegment(e.config,a,s):e.type===`metrics`?this.renderMetricsSegment(e.config,o,r,s):e.type===`block`?this.renderBlockSegment(e.config,r,s):e.type===`today`?this.renderTodaySegment(e.config,i,s):e.type===`version`?this.renderVersionSegment(e.config,t,s):e.type===`env`?this.segmentRenderer.renderEnv(s,e.config):e.type===`weekly`?this.segmentRenderer.renderWeekly(t,s,e.config):null}async renderGitSegment(e,t,n,r){if(!this.needsSegmentInfo(`git`))return null;let i=await this.gitService.getGitInfo(r,{showSha:e?.showSha,showWorkingTree:e?.showWorkingTree,showOperation:e?.showOperation,showTag:e?.showTag,showTimeSinceCommit:e?.showTimeSinceCommit,showStashCount:e?.showStashCount,showUpstream:e?.showUpstream,showRepoName:e?.showRepoName},t.workspace?.project_dir);return i?this.segmentRenderer.renderGit(i,n,e):null}renderSessionSegment(e,t,n){return t?this.segmentRenderer.renderSession(t,n,e):null}async renderTmuxSegment(e){if(!this.needsSegmentInfo(`tmux`))return null;let t=await this.tmuxService.getSessionId();return this.segmentRenderer.renderTmux(t,e)}renderContextSegment(e,t,n){return this.needsSegmentInfo(`context`)?this.segmentRenderer.renderContext(t,n,e):null}renderMetricsSegment(e,t,n,r){return this.segmentRenderer.renderMetrics(t,r,e)}renderBlockSegment(e,t,n){return t?this.segmentRenderer.renderBlock(t,n,e):null}renderTodaySegment(e,t,n){if(!t)return null;let r=e?.type||`cost`;return this.segmentRenderer.renderToday(t,n,r)}renderVersionSegment(e,t,n){return this.segmentRenderer.renderVersion(t,n,e)}initializeSymbols(){let e=this.config.display.style,t=this.config.display.charset||`unicode`,n=e===`minimal`,r=e===`capsule`,i=t===`text`?ft:lt;return{right:n?``:r?i.right_rounded:i.right,left:r?i.left_rounded:``,branch:i.branch,model:i.model,git_clean:i.git_clean,git_dirty:i.git_dirty,git_conflicts:i.git_conflicts,git_ahead:i.git_ahead,git_behind:i.git_behind,git_worktree:i.git_worktree,git_tag:i.git_tag,git_sha:i.git_sha,git_upstream:i.git_upstream,git_stash:i.git_stash,git_time:i.git_time,session_cost:i.session_cost,block_cost:i.block_cost,today_cost:i.today_cost,context_time:i.context_time,metrics_response:i.metrics_response,metrics_last_response:i.metrics_last_response,metrics_duration:i.metrics_duration,metrics_messages:i.metrics_messages,metrics_lines_added:i.metrics_lines_added,metrics_lines_removed:i.metrics_lines_removed,metrics_burn:i.metrics_burn,version:i.version,bar_filled:i.bar_filled,bar_empty:i.bar_empty,env:i.env,session_id:i.session_id,weekly_cost:i.weekly_cost}}getThemeColors(){let e=this.config.theme,t,n=this.config.display.colorCompatibility||`auto`,r=n===`auto`?C():n;if(e===`custom`){if(t=this.config.colors?.custom,!t)throw Error(`Custom theme selected but no colors provided in configuration`)}else t=T(e,r),t||=(console.warn(`Built-in theme '${e}' not found, falling back to 'dark' theme`),T(`dark`,r));let i=(e,t)=>r===`none`?``:r===`ansi`?ne(e,t):r===`ansi256`?te(e,t):ee(e,t),a=T(`dark`,r),o=this.config.display.style===`tui`,s=e===`light`?`#f0f0f0`:`#1e1e1e`,c=e=>{let n=a[e],r=t[e],c={fg:r?.fg||n.fg,bg:r?.bg||n.bg},l=c.fg;return o&&re(l,s)<60&&(l=c.bg),{bg:i(c.bg,!0),fg:i(l,!1)}},l=c(`directory`),u=c(`git`),d=c(`model`),f=c(`session`),p=c(`block`),m=c(`today`),h=c(`tmux`),g=c(`context`),_=c(`contextWarning`),v=c(`contextCritical`),y=c(`metrics`),b=c(`version`),x=c(`env`),S=c(`weekly`);return{reset:r===`none`?``:`\x1B[0m`,modeBg:l.bg,modeFg:l.fg,gitBg:u.bg,gitFg:u.fg,modelBg:d.bg,modelFg:d.fg,sessionBg:f.bg,sessionFg:f.fg,blockBg:p.bg,blockFg:p.fg,todayBg:m.bg,todayFg:m.fg,tmuxBg:h.bg,tmuxFg:h.fg,contextBg:g.bg,contextFg:g.fg,contextWarningBg:_.bg,contextWarningFg:_.fg,contextCriticalBg:v.bg,contextCriticalFg:v.fg,metricsBg:y.bg,metricsFg:y.fg,versionBg:b.bg,versionFg:b.fg,envBg:x.bg,envFg:x.fg,weeklyBg:S.bg,weeklyFg:S.fg,partFg:this.resolvePartColors(i)}}resolvePartColors(e){let t=this.config.colors?.custom;if(!t)return{};let n={};for(let r of Object.keys(t)){let i=t[r];i?.fg&&(n[r]=e(i.fg,!1))}return n}getSegmentBgColor(e,t){switch(e){case`directory`:return t.modeBg;case`git`:return t.gitBg;case`model`:return t.modelBg;case`session`:case`sessionId`:return t.sessionBg;case`block`:return t.blockBg;case`today`:return t.todayBg;case`tmux`:return t.tmuxBg;case`context`:return t.contextBg;case`metrics`:return t.metricsBg;case`version`:return t.versionBg;case`env`:return t.envBg;case`weekly`:return t.weeklyBg;default:return t.modeBg}}formatSegment(e,t,n,r,i){let a=this.config.display.style===`capsule`,o=` `.repeat(this.config.display.padding??1);if(a){let r=this.config.display.colorCompatibility||`auto`,a=S(e,(r===`auto`?C():r)===`ansi`);return`${`${a}${this.symbols.left}${i.reset}`}${`${e}${t}${o}${n}${o}${i.reset}`}${`${a}${this.symbols.right}${i.reset}`}`}let s=`${e}${t}${o}${n}${o}`,c=this.config.display.colorCompatibility||`auto`,l=(c===`auto`?C():c)===`ansi`;if(r){let t=S(e,l);s+=`${i.reset}${r}${t}${this.symbols.right}`}else s+=`${i.reset}${S(e,l)}${this.symbols.right}${i.reset}`;return s}};const Fn={theme:`dark`,display:{style:`minimal`,charset:`unicode`,colorCompatibility:`auto`,autoWrap:!0,padding:1,lines:[{segments:{directory:{enabled:!0,style:`basename`},git:{enabled:!0,showSha:!1,showWorkingTree:!1,showOperation:!1,showTag:!1,showTimeSinceCommit:!1,showStashCount:!1,showUpstream:!1,showRepoName:!1},model:{enabled:!0},session:{enabled:!0,type:`tokens`,costSource:`calculated`},today:{enabled:!0,type:`cost`},block:{enabled:!1,type:`cost`,burnType:`cost`,displayStyle:`text`},weekly:{enabled:!1,displayStyle:`text`},version:{enabled:!1},tmux:{enabled:!1},sessionId:{enabled:!1,showIdLabel:!0},context:{enabled:!0,showPercentageOnly:!1,displayStyle:`text`,autocompactBuffer:33e3},metrics:{enabled:!1,showResponseTime:!0,showLastResponseTime:!0,showDuration:!0,showMessageCount:!0,showLinesAdded:!0,showLinesRemoved:!0}}}]},budget:{session:{warningThreshold:80},today:{warningThreshold:80,amount:50},block:{warningThreshold:80,amount:15}},modelContextLimits:{default:2e5,sonnet:2e5,opus:2e5}},In=new Set([`context`,`block`,`session`,`today`,`weekly`,`git`,`dir`,`model`,`version`,`tmux`,`metrics`,`activity`,`burn`,`env`]),Ln={session:[`icon`,`cost`,`tokens`,`budget`],block:[`icon`,`value`,`time`,`budget`,`bar`],today:[`icon`,`cost`,`label`,`budget`],weekly:[`icon`,`pct`,`time`,`bar`],git:[`icon`,`branch`,`status`,`ahead`,`behind`,`working`,`head`],context:[`icon`,`bar`,`pct`,`tokens`],metrics:[`response`,`responseIcon`,`responseVal`,`lastResponse`,`lastResponseIcon`,`lastResponseVal`,`added`,`addedIcon`,`addedVal`,`removed`,`removedIcon`,`removedVal`],activity:[`duration`,`durationIcon`,`durationVal`,`messages`,`messagesIcon`,`messagesVal`],model:[`icon`,`value`],burn:[`icon`,`rate`],version:[`icon`,`value`],tmux:[`label`,`value`],dir:[`value`],env:[`prefix`,`value`]};function Rn(e){if(e===`.`||e===`---`||In.has(e))return!0;let t=e.indexOf(`.`);if(t===-1)return!1;let n=e.slice(0,t),r=e.slice(t+1);if(!n||!r)return!1;let i=Ln[n];return i?i.includes(r):!1}function zn(e){return[`light`,`dark`,`nord`,`tokyo-night`,`rose-pine`,`gruvbox`,`custom`].includes(e)}function Bn(e){return e===`minimal`||e===`powerline`||e===`capsule`||e===`tui`}function Vn(e){return e===`unicode`||e===`text`}function Q(e,t){for(let n=0;n<e.length;n++){let r=e[n];if(r===t&&n+1<e.length)return e[n+1];if(r?.startsWith(`${t}=`))return r.split(`=`)[1]}}function $(e,t){let n={...e};for(let e in t){let r=t[e];r!==void 0&&(typeof r==`object`&&r&&!Array.isArray(r)?n[e]=$(n[e]||{},r):n[e]=r)}return n}function Hn(e,t){return e?o.existsSync(e)?e:null:[...t?[l.join(t,`.claude-powerline.json`)]:[],l.join(process.cwd(),`.claude-powerline.json`),l.join(m.homedir(),`.claude`,`claude-powerline.json`),l.join(m.homedir(),`.config`,`claude-powerline`,`config.json`)].find(o.existsSync)||null}function Un(e){try{let t=o.readFileSync(e,`utf-8`);return JSON.parse(t)}catch(t){throw Error(`Failed to load config file ${e}: ${t instanceof Error?t.message:String(t)}`)}}function Wn(){let e={},t={},n=process.env.CLAUDE_POWERLINE_THEME;n&&zn(n)&&(e.theme=n);let r=process.env.CLAUDE_POWERLINE_STYLE;return r&&(Bn(r)?t.style=r:(console.warn(`Invalid display style '${r}' from environment variable, falling back to 'minimal'`),t.style=`minimal`)),Object.keys(t).length>0&&(e.display=t),e}function Gn(){return process.env.CLAUDE_POWERLINE_CONFIG}function Kn(e){let t={},n={},r=Q(e,`--theme`);r&&zn(r)&&(t.theme=r);let i=Q(e,`--style`);i&&(Bn(i)?n.style=i:(console.warn(`Invalid display style '${i}' from CLI argument, falling back to 'minimal'`),n.style=`minimal`));let a=Q(e,`--charset`);return a&&(Vn(a)?n.charset=a:(console.warn(`Invalid charset '${a}' from CLI argument, falling back to 'unicode'`),n.charset=`unicode`)),Object.keys(n).length>0&&(t.display=n),t}function qn(e){if(typeof e.box==`string`&&!I[e.box]){let t=Object.keys(I).join(`, `);return`unknown box preset "${e.box}" (valid: ${t})`}if(!e.breakpoints||!Array.isArray(e.breakpoints)||e.breakpoints.length===0)return`grid config must have at least one breakpoint`;let t=new Set;for(let n=0;n<e.breakpoints.length;n++){let r=e.breakpoints[n],i=`breakpoint[${n}]`;if(typeof r.minWidth!=`number`||r.minWidth<0)return`${i}: minWidth must be a non-negative number`;if(t.has(r.minWidth))return`${i}: duplicate minWidth ${r.minWidth} (each breakpoint must have a unique minWidth)`;if(t.add(r.minWidth),!r.areas||!Array.isArray(r.areas)||r.areas.length===0)return`${i}: areas must be a non-empty array of strings`;if(!r.columns||!Array.isArray(r.columns)||r.columns.length===0)return`${i}: columns must be a non-empty array`;let a=r.columns.length;for(let e of r.columns){if(typeof e!=`string`)return`${i}: column definition must be a string`;if(!/^(\d+fr|\d+|auto)$/.test(e))return`${i}: invalid column definition "${e}" (use "auto", "Nfr", or a fixed integer)`}if(r.align!==void 0){if(!Array.isArray(r.align))return`${i}: align must be an array`;if(r.align.length!==a)return`${i}: align length (${r.align.length}) must match columns length (${a})`;for(let e of r.align)if(e!==`left`&&e!==`center`&&e!==`right`)return`${i}: invalid align value "${e}"`}let o=new Set;for(let t=0;t<r.areas.length;t++){let n=r.areas[t];if(n.trim()===`---`)continue;let s=n.trim().split(/\s+/);if(s.length!==a)return`${i}: row "${n}" has ${s.length} cells but expected ${a} columns`;let c=e.segments?new Set(Object.keys(e.segments)):new Set,l=``,u=``;for(let e of s){if(e!==`.`){if(!Rn(e)&&!c.has(e))return`${i}: unknown segment name "${e}"`;if(e!==u&&o.has(e))return`${i}: segment "${e}" appears on multiple rows`}e!==l&&(u=e),l=e}let d=new Map;for(let e=0;e<s.length;e++){let t=s[e];if(t===`.`||t===`---`)continue;let r=d.get(t);if(r!==void 0&&r!==e-1)return`${i}: segment "${t}" has non-contiguous span in row "${n}"`;d.set(t,e)}for(let e of s)e!==`.`&&e!==`---`&&o.add(e)}}if(e.segments)for(let[t,n]of Object.entries(e.segments)){if(!n.items||!Array.isArray(n.items))return`segments["${t}"]: items must be an array`;if(n.justify!==void 0&&n.justify!==`start`&&n.justify!==`between`)return`segments["${t}"]: invalid justify value "${n.justify}" (use "start" or "between")`}return null}function Jn(e=process.argv,t){let n=JSON.parse(JSON.stringify(Fn)),r=Q(e,`--config`)||Gn(),i=Hn(r?.startsWith(`~`)?r.replace(`~`,m.homedir()):r,t);if(i)try{let e=Un(i);n=$(n,e)}catch(e){console.warn(`Warning: ${e instanceof Error?e.message:String(e)}`)}n.display?.style&&!Bn(n.display.style)&&(console.warn(`Invalid display style '${n.display.style}' in config file, falling back to 'minimal'`),n.display.style=`minimal`),n.display?.charset&&!Vn(n.display.charset)&&(console.warn(`Invalid charset '${n.display.charset}' in config file, falling back to 'unicode'`),n.display.charset=`unicode`),n.theme&&!zn(n.theme)&&(console.warn(`Invalid theme '${n.theme}' in config file, falling back to 'dark'`),n.theme=`dark`);let a=Wn();n=$(n,a);let o=Kn(e);if(n=$(n,o),n.display?.tui){let e=qn(n.display.tui);e&&(process.stderr.write(`Warning: invalid grid config: ${e}. Falling back to hardcoded layout.\n`),delete n.display.tui)}return n}const Yn=Jn;function Xn(){console.log(`
12
12
  claude-powerline - Beautiful powerline statusline for Claude Code
13
13
 
14
14
  Usage: claude-powerline [options]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owloops/claude-powerline",
3
- "version": "1.24.0",
3
+ "version": "1.24.2",
4
4
  "description": "Beautiful vim-style powerline statusline for Claude Code with real-time usage tracking, git integration, and custom themes",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",