agy-statusblocks 0.1.1 → 0.1.3
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 +18 -2
- package/dist/cache.js +2 -2
- package/dist/cli.js +53 -18
- package/dist/config.d.ts +3 -1
- package/dist/config.js +28 -0
- package/dist/index.js +4 -0
- package/dist/layout.d.ts +17 -1
- package/dist/layout.js +71 -18
- package/dist/segments/context.js +6 -5
- package/dist/segments/git.js +5 -4
- package/dist/segments/usage.js +29 -7
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,14 +47,30 @@ On narrow/split-pane terminals, the layout degrades gracefully to stacked single
|
|
|
47
47
|
|
|
48
48
|
```json
|
|
49
49
|
{
|
|
50
|
-
"segments": ["context", "model", "usage", "state", "git"]
|
|
50
|
+
"segments": ["context", "model", "usage", "state", "git"],
|
|
51
|
+
"border": "rounded"
|
|
51
52
|
}
|
|
52
53
|
```
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
Border styles supported:
|
|
56
|
+
- `rounded` (default) — `╭─╮` `╰─╯` `│` `─`
|
|
57
|
+
- `square` — `┌─┐` `└─┘` `│` `─`
|
|
58
|
+
- `double` — `╔═╗` `╚═╝` `║` `═`
|
|
59
|
+
- `thick` — `┏━┓` `┗━┛` `┃` `━`
|
|
60
|
+
- `ascii` — `+-+` `+-+` `|` `-` (clean standard 7-bit ASCII)
|
|
61
|
+
- `none` — clean padded columns without frame borders
|
|
62
|
+
|
|
63
|
+
Or configure via environment variables:
|
|
55
64
|
|
|
56
65
|
```sh
|
|
57
66
|
AGY_STATUSBLOCKS_SEGMENTS=context,model,usage
|
|
67
|
+
AGY_STATUSBLOCKS_BORDER=double
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or pass via CLI flag:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
npx agy-statusblocks preview --border double
|
|
58
74
|
```
|
|
59
75
|
|
|
60
76
|
## Preview
|
package/dist/cache.js
CHANGED
|
@@ -3,9 +3,9 @@ import { join } from 'path';
|
|
|
3
3
|
import { tmpdir } from 'os';
|
|
4
4
|
import { createHash } from 'crypto';
|
|
5
5
|
function cacheDir(create) {
|
|
6
|
-
const override = process.env['AGY_STATUSBLOCKS_CACHE_DIR']
|
|
6
|
+
const override = process.env['AGY_STATUSBLOCKS_CACHE_DIR'];
|
|
7
7
|
const uid = typeof process.getuid === 'function' ? `-${process.getuid()}` : '';
|
|
8
|
-
const dir = override || join(tmpdir(), `
|
|
8
|
+
const dir = override || join(tmpdir(), `agy-statusblocks${uid}`);
|
|
9
9
|
if (create)
|
|
10
10
|
mkdirSync(dir, { recursive: true });
|
|
11
11
|
return dir;
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { join, dirname, basename, sep } from 'path';
|
|
|
4
4
|
import { homedir } from 'os';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { render } from './layout.js';
|
|
7
|
-
import { loadConfig } from './config.js';
|
|
7
|
+
import { loadConfig, normalizeBorderStyle } from './config.js';
|
|
8
8
|
import { color, c } from './colors.js';
|
|
9
9
|
import { isValidVersion, isNewerVersion } from './version.js';
|
|
10
10
|
const PACKAGE_VERSION = (() => {
|
|
@@ -134,9 +134,26 @@ function runUninstall() {
|
|
|
134
134
|
}
|
|
135
135
|
console.log(color('✓ agy-statusblocks uninstalled and reverted to built-in status line', c.green));
|
|
136
136
|
}
|
|
137
|
-
function
|
|
137
|
+
function parseBorderFlag(argv) {
|
|
138
|
+
for (let i = 0; i < argv.length; i++) {
|
|
139
|
+
const arg = argv[i];
|
|
140
|
+
if (arg === '--border' || arg === '-b') {
|
|
141
|
+
const next = argv[i + 1];
|
|
142
|
+
if (next)
|
|
143
|
+
return normalizeBorderStyle(next);
|
|
144
|
+
}
|
|
145
|
+
else if (arg && arg.startsWith('--border=')) {
|
|
146
|
+
return normalizeBorderStyle(arg.slice('--border='.length));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
function runPreview(borderOverride) {
|
|
138
152
|
process.env['AGY_STATUSBLOCKS_NO_REMOTE'] = '1';
|
|
139
153
|
const config = loadConfig();
|
|
154
|
+
if (borderOverride) {
|
|
155
|
+
config.border = borderOverride;
|
|
156
|
+
}
|
|
140
157
|
const now = Date.now() / 1000;
|
|
141
158
|
const mockData = {
|
|
142
159
|
product: 'antigravity',
|
|
@@ -186,7 +203,8 @@ function runPreview() {
|
|
|
186
203
|
level: 'medium',
|
|
187
204
|
},
|
|
188
205
|
};
|
|
189
|
-
|
|
206
|
+
const styleLabel = config.border ?? 'rounded';
|
|
207
|
+
console.log(`\n${color('agy-statusblocks', c.bold, c.gemini)} preview ${color(`[border: ${styleLabel}]`, c.dim)}\n`);
|
|
190
208
|
for (const width of [120, 80, 50]) {
|
|
191
209
|
console.log(color('─'.repeat(width), c.gray));
|
|
192
210
|
console.log(`${width} cols:`);
|
|
@@ -199,12 +217,12 @@ function showHelp() {
|
|
|
199
217
|
${color('agy-statusblocks', c.bold, c.gemini)} — block-based status line for Antigravity CLI (agy)
|
|
200
218
|
|
|
201
219
|
${color('Usage:', c.bold)}
|
|
202
|
-
agy-statusblocks init
|
|
203
|
-
agy-statusblocks update
|
|
204
|
-
agy-statusblocks preview
|
|
205
|
-
agy-statusblocks uninstall
|
|
206
|
-
agy-statusblocks --version
|
|
207
|
-
agy-statusblocks help
|
|
220
|
+
agy-statusblocks init Install into Antigravity CLI settings
|
|
221
|
+
agy-statusblocks update Update installed files (--force to downgrade)
|
|
222
|
+
agy-statusblocks preview [--border <style>] Preview with mock data at various widths
|
|
223
|
+
agy-statusblocks uninstall Revert to built-in statusline
|
|
224
|
+
agy-statusblocks --version Print the installed package version
|
|
225
|
+
agy-statusblocks help Show this help
|
|
208
226
|
|
|
209
227
|
${color('Blocks:', c.bold)}
|
|
210
228
|
context Context window fill bar, percentage, token counts
|
|
@@ -213,13 +231,22 @@ ${color('Blocks:', c.bold)}
|
|
|
213
231
|
state Agent state pill and task/subagent/artifact counters
|
|
214
232
|
git Branch, staged/modified counts, lines added/removed
|
|
215
233
|
|
|
234
|
+
${color('Borders:', c.bold)}
|
|
235
|
+
rounded (default), square, double, thick, ascii, none
|
|
236
|
+
|
|
216
237
|
${color('Customize:', c.bold)}
|
|
217
|
-
~/.agy-statusblocks.json: { "segments": ["context", "model", "usage", "state", "git"] }
|
|
238
|
+
~/.agy-statusblocks.json: { "segments": ["context", "model", "usage", "state", "git"], "border": "rounded" }
|
|
218
239
|
Env vars: AGY_STATUSBLOCKS_SEGMENTS=context,model,usage
|
|
240
|
+
AGY_STATUSBLOCKS_BORDER=rounded|square|double|thick|ascii|none
|
|
241
|
+
CLI flags: --border, -b <style>
|
|
219
242
|
`);
|
|
220
243
|
}
|
|
221
|
-
const
|
|
222
|
-
|
|
244
|
+
const borderFlag = parseBorderFlag(process.argv.slice(2));
|
|
245
|
+
if (borderFlag) {
|
|
246
|
+
process.env['AGY_STATUSBLOCKS_BORDER'] = borderFlag;
|
|
247
|
+
}
|
|
248
|
+
const firstPositional = process.argv.slice(2).find(arg => !arg.startsWith('-'));
|
|
249
|
+
switch (firstPositional) {
|
|
223
250
|
case 'init':
|
|
224
251
|
runInit();
|
|
225
252
|
break;
|
|
@@ -227,18 +254,26 @@ switch (cmd) {
|
|
|
227
254
|
runUpdate(process.argv.includes('--force'));
|
|
228
255
|
break;
|
|
229
256
|
case 'preview':
|
|
230
|
-
runPreview();
|
|
257
|
+
runPreview(borderFlag);
|
|
231
258
|
break;
|
|
232
259
|
case 'uninstall':
|
|
233
260
|
runUninstall();
|
|
234
261
|
break;
|
|
235
|
-
case
|
|
236
|
-
|
|
237
|
-
|
|
262
|
+
case undefined:
|
|
263
|
+
if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
264
|
+
console.log(PACKAGE_VERSION);
|
|
265
|
+
}
|
|
266
|
+
else if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
267
|
+
showHelp();
|
|
268
|
+
}
|
|
269
|
+
else if (process.stdin.isTTY) {
|
|
270
|
+
showHelp();
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
import('./index.js');
|
|
274
|
+
}
|
|
238
275
|
break;
|
|
239
276
|
case 'help':
|
|
240
|
-
case '--help':
|
|
241
|
-
case '-h':
|
|
242
277
|
showHelp();
|
|
243
278
|
break;
|
|
244
279
|
default:
|
package/dist/config.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import type { StatusBlocksConfig } from './types.js';
|
|
1
|
+
import type { StatusBlocksConfig, BorderStyle } from './types.js';
|
|
2
|
+
export declare const VALID_BORDER_STYLES: readonly BorderStyle[];
|
|
3
|
+
export declare function normalizeBorderStyle(val: unknown): BorderStyle | undefined;
|
|
2
4
|
export declare function loadConfig(): StatusBlocksConfig;
|
package/dist/config.js
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import { readFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
|
+
export const VALID_BORDER_STYLES = [
|
|
5
|
+
'rounded',
|
|
6
|
+
'square',
|
|
7
|
+
'double',
|
|
8
|
+
'thick',
|
|
9
|
+
'ascii',
|
|
10
|
+
'none',
|
|
11
|
+
];
|
|
12
|
+
export function normalizeBorderStyle(val) {
|
|
13
|
+
if (typeof val === 'string') {
|
|
14
|
+
const lower = val.trim().toLowerCase();
|
|
15
|
+
if (VALID_BORDER_STYLES.includes(lower)) {
|
|
16
|
+
return lower;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
4
21
|
export function loadConfig() {
|
|
5
22
|
const envSegments = process.env['AGY_STATUSBLOCKS_SEGMENTS'];
|
|
23
|
+
const envBorder = process.env['AGY_STATUSBLOCKS_BORDER'];
|
|
6
24
|
const config = {};
|
|
7
25
|
try {
|
|
8
26
|
const raw = readFileSync(join(homedir(), '.agy-statusblocks.json'), 'utf8');
|
|
@@ -14,10 +32,20 @@ export function loadConfig() {
|
|
|
14
32
|
config.remoteUsage = parsed.remoteUsage;
|
|
15
33
|
if (typeof parsed.stackWithDefault === 'boolean')
|
|
16
34
|
config.stackWithDefault = parsed.stackWithDefault;
|
|
35
|
+
if (parsed.border) {
|
|
36
|
+
const border = normalizeBorderStyle(parsed.border);
|
|
37
|
+
if (border)
|
|
38
|
+
config.border = border;
|
|
39
|
+
}
|
|
17
40
|
}
|
|
18
41
|
}
|
|
19
42
|
catch { /* no config file — use defaults */ }
|
|
20
43
|
if (envSegments)
|
|
21
44
|
config.segments = envSegments.split(',');
|
|
45
|
+
if (envBorder) {
|
|
46
|
+
const border = normalizeBorderStyle(envBorder);
|
|
47
|
+
if (border)
|
|
48
|
+
config.border = border;
|
|
49
|
+
}
|
|
22
50
|
return config;
|
|
23
51
|
}
|
package/dist/index.js
CHANGED
|
@@ -129,6 +129,10 @@ function normalizeInput(raw) {
|
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
131
|
let input = '';
|
|
132
|
+
if (process.stdin.isTTY) {
|
|
133
|
+
process.stdout.write('\n');
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
132
136
|
process.stdin.setEncoding('utf8');
|
|
133
137
|
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
134
138
|
process.stdin.on('end', () => {
|
package/dist/layout.d.ts
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
import type { AgyStatusData, StatusBlocksConfig } from './types.js';
|
|
1
|
+
import type { Block, AgyStatusData, StatusBlocksConfig, BorderStyle } from './types.js';
|
|
2
2
|
export declare const DEFAULT_ORDER: string[];
|
|
3
|
+
export interface BorderGlyphs {
|
|
4
|
+
tl: string;
|
|
5
|
+
tr: string;
|
|
6
|
+
bl: string;
|
|
7
|
+
br: string;
|
|
8
|
+
h: string;
|
|
9
|
+
v: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const BORDER_STYLES: Record<BorderStyle, BorderGlyphs>;
|
|
12
|
+
type RowGroup = {
|
|
13
|
+
blocks: Block[];
|
|
14
|
+
widths: number[];
|
|
15
|
+
};
|
|
16
|
+
export declare function boxify(lines: string[], innerW: number, style?: BorderStyle, title?: string): string[];
|
|
17
|
+
export declare function renderRow(group: RowGroup, style?: BorderStyle): string[];
|
|
3
18
|
export declare function render(data: AgyStatusData, width: number, config?: StatusBlocksConfig): string;
|
|
19
|
+
export {};
|
package/dist/layout.js
CHANGED
|
@@ -7,35 +7,62 @@ import { gitSegment } from './segments/git.js';
|
|
|
7
7
|
const ALL_SEGMENTS = [contextSegment, modelSegment, usageSegment, stateSegment, gitSegment];
|
|
8
8
|
export const DEFAULT_ORDER = ['context', 'model', 'usage', 'state', 'git'];
|
|
9
9
|
const GAP = 1;
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
export const BORDER_STYLES = {
|
|
11
|
+
rounded: { tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│' },
|
|
12
|
+
square: { tl: '┌', tr: '┐', bl: '└', br: '┘', h: '─', v: '│' },
|
|
13
|
+
double: { tl: '╔', tr: '╗', bl: '╚', br: '╝', h: '═', v: '║' },
|
|
14
|
+
thick: { tl: '┏', tr: '┓', bl: '┗', br: '┛', h: '━', v: '┃' },
|
|
15
|
+
ascii: { tl: '+', tr: '+', bl: '+', br: '+', h: '-', v: '|' },
|
|
16
|
+
none: { tl: '', tr: '', bl: '', br: '', h: '', v: '' },
|
|
17
|
+
};
|
|
18
|
+
export function boxify(lines, innerW, style = 'rounded', title) {
|
|
19
|
+
if (style === 'none') {
|
|
20
|
+
const boxed = [];
|
|
21
|
+
if (title) {
|
|
22
|
+
boxed.push(' ' + color(title, c.dim) + ' '.repeat(Math.max(0, innerW + 1 - title.length)));
|
|
23
|
+
}
|
|
24
|
+
for (const line of lines) {
|
|
25
|
+
boxed.push(' ' + padRight(line, innerW) + ' ');
|
|
26
|
+
}
|
|
27
|
+
return boxed;
|
|
28
|
+
}
|
|
29
|
+
const g = BORDER_STYLES[style] || BORDER_STYLES.rounded;
|
|
12
30
|
let top;
|
|
13
31
|
if (title) {
|
|
14
32
|
const label = ` ${title} `;
|
|
15
33
|
const remaining = Math.max(0, innerW + 2 - label.length - 1);
|
|
16
|
-
top = color(
|
|
34
|
+
top = color(g.tl + g.h, c.gray) + color(label, c.dim) + color(g.h.repeat(remaining) + g.tr, c.gray);
|
|
17
35
|
}
|
|
18
36
|
else {
|
|
19
|
-
top = color(
|
|
37
|
+
top = color(g.tl + g.h.repeat(innerW + 2) + g.tr, c.gray);
|
|
20
38
|
}
|
|
21
|
-
const bot = color(
|
|
39
|
+
const bot = color(g.bl + g.h.repeat(innerW + 2) + g.br, c.gray);
|
|
22
40
|
const boxed = [top];
|
|
23
41
|
for (const line of lines) {
|
|
24
|
-
boxed.push(color(
|
|
42
|
+
boxed.push(color(g.v, c.gray) + ' ' + padRight(line, innerW) + ' ' + color(g.v, c.gray));
|
|
25
43
|
}
|
|
26
44
|
boxed.push(bot);
|
|
27
45
|
return boxed;
|
|
28
46
|
}
|
|
29
|
-
function renderRow(group) {
|
|
47
|
+
export function renderRow(group, style = 'rounded') {
|
|
30
48
|
const { blocks, widths } = group;
|
|
31
|
-
const
|
|
32
|
-
const
|
|
49
|
+
const chrome = style === 'none' ? 2 : 4;
|
|
50
|
+
const boxes = blocks.map((b, i) => boxify(b.lines, widths[i], style, b.id));
|
|
51
|
+
const boxWidths = widths.map(w => w + chrome);
|
|
52
|
+
const g = BORDER_STYLES[style] || BORDER_STYLES.rounded;
|
|
33
53
|
const maxHeight = Math.max(...boxes.map(b => b.length));
|
|
34
54
|
for (let i = 0; i < boxes.length; i++) {
|
|
35
55
|
const box = boxes[i];
|
|
36
56
|
while (box.length < maxHeight) {
|
|
37
|
-
const blankRow =
|
|
38
|
-
|
|
57
|
+
const blankRow = style === 'none'
|
|
58
|
+
? ' '.repeat(widths[i] + 2)
|
|
59
|
+
: color(g.v, c.gray) + ' '.repeat(widths[i] + 2) + color(g.v, c.gray);
|
|
60
|
+
if (style === 'none') {
|
|
61
|
+
box.push(blankRow);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
box.splice(box.length - 1, 0, blankRow);
|
|
65
|
+
}
|
|
39
66
|
}
|
|
40
67
|
}
|
|
41
68
|
const rows = [];
|
|
@@ -63,12 +90,12 @@ function decodeAssignment(combo, blockCount, targetRows, blockRow, rowUsed) {
|
|
|
63
90
|
}
|
|
64
91
|
return true;
|
|
65
92
|
}
|
|
66
|
-
function computeRowWidths(blockRow, blockCount, targetRows, blockWidths, rowWidths) {
|
|
93
|
+
function computeRowWidths(blockRow, blockCount, targetRows, blockWidths, rowWidths, chrome) {
|
|
67
94
|
rowWidths.fill(0, 0, targetRows);
|
|
68
95
|
const rowCounts = new Array(targetRows).fill(0);
|
|
69
96
|
for (let b = 0; b < blockCount; b++) {
|
|
70
97
|
const r = blockRow[b];
|
|
71
|
-
rowWidths[r] = (rowWidths[r] ?? 0) + (blockWidths[b] ?? 0) +
|
|
98
|
+
rowWidths[r] = (rowWidths[r] ?? 0) + (blockWidths[b] ?? 0) + chrome;
|
|
72
99
|
rowCounts[r] = (rowCounts[r] ?? 0) + 1;
|
|
73
100
|
}
|
|
74
101
|
for (let r = 0; r < targetRows; r++) {
|
|
@@ -77,10 +104,34 @@ function computeRowWidths(blockRow, blockCount, targetRows, blockWidths, rowWidt
|
|
|
77
104
|
}
|
|
78
105
|
}
|
|
79
106
|
}
|
|
80
|
-
function
|
|
107
|
+
function solveGreedy(blocks, maxWidth, chrome) {
|
|
108
|
+
const rows = [];
|
|
109
|
+
for (const b of blocks) {
|
|
110
|
+
const boxW = b.width + chrome;
|
|
111
|
+
let placed = false;
|
|
112
|
+
for (const r of rows) {
|
|
113
|
+
const addedW = r.blocks.length > 0 ? boxW + GAP : boxW;
|
|
114
|
+
if (r.currentW + addedW <= maxWidth) {
|
|
115
|
+
r.blocks.push(b);
|
|
116
|
+
r.widths.push(b.width);
|
|
117
|
+
r.currentW += addedW;
|
|
118
|
+
placed = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!placed) {
|
|
123
|
+
rows.push({ blocks: [b], widths: [b.width], currentW: boxW });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
rows.sort((a, b) => a.currentW - b.currentW);
|
|
127
|
+
return rows.map(r => ({ blocks: r.blocks, widths: r.widths }));
|
|
128
|
+
}
|
|
129
|
+
function solveLayout(blocks, maxWidth, chrome) {
|
|
81
130
|
if (blocks.length === 0)
|
|
82
131
|
return [];
|
|
83
132
|
const blockCount = blocks.length;
|
|
133
|
+
if (blockCount > 6)
|
|
134
|
+
return solveGreedy(blocks, maxWidth, chrome);
|
|
84
135
|
const blockWidths = blocks.map(b => b.width);
|
|
85
136
|
for (let targetRows = 1; targetRows <= blockCount; targetRows++) {
|
|
86
137
|
const totalCombos = Math.pow(targetRows, blockCount);
|
|
@@ -92,7 +143,7 @@ function solveLayout(blocks, maxWidth) {
|
|
|
92
143
|
for (let combo = 0; combo < totalCombos; combo++) {
|
|
93
144
|
if (!decodeAssignment(combo, blockCount, targetRows, blockRow, rowUsed))
|
|
94
145
|
continue;
|
|
95
|
-
computeRowWidths(blockRow, blockCount, targetRows, blockWidths, rowWidths);
|
|
146
|
+
computeRowWidths(blockRow, blockCount, targetRows, blockWidths, rowWidths, chrome);
|
|
96
147
|
let fits = true;
|
|
97
148
|
let maxRowW = 0;
|
|
98
149
|
for (let r = 0; r < targetRows; r++) {
|
|
@@ -121,7 +172,7 @@ function solveLayout(blocks, maxWidth) {
|
|
|
121
172
|
rWidths.push(blocks[b].width);
|
|
122
173
|
}
|
|
123
174
|
}
|
|
124
|
-
const totalW = rWidths.reduce((a, w) => a + w +
|
|
175
|
+
const totalW = rWidths.reduce((a, w) => a + w + chrome, 0) + (rBlocks.length - 1) * GAP;
|
|
125
176
|
rows.push({ blocks: rBlocks, widths: rWidths, totalW });
|
|
126
177
|
}
|
|
127
178
|
rows.sort((a, b) => a.totalW - b.totalW);
|
|
@@ -132,15 +183,17 @@ function solveLayout(blocks, maxWidth) {
|
|
|
132
183
|
}
|
|
133
184
|
export function render(data, width, config = {}) {
|
|
134
185
|
const order = config.segments ?? DEFAULT_ORDER;
|
|
186
|
+
const borderStyle = config.border ?? 'rounded';
|
|
187
|
+
const chrome = borderStyle === 'none' ? 2 : 4;
|
|
135
188
|
const segmentsMap = new Map(ALL_SEGMENTS.map(s => [s.id, s]));
|
|
136
189
|
const activeSegments = order
|
|
137
190
|
.map(id => segmentsMap.get(id))
|
|
138
191
|
.filter((s) => !!s && s.enabled(data));
|
|
139
192
|
const blocks = activeSegments.map(s => s.render(data, width));
|
|
140
|
-
const groups = solveLayout(blocks, width);
|
|
193
|
+
const groups = solveLayout(blocks, width, chrome);
|
|
141
194
|
const lines = [];
|
|
142
195
|
for (const group of groups) {
|
|
143
|
-
lines.push(...renderRow(group));
|
|
196
|
+
lines.push(...renderRow(group, borderStyle));
|
|
144
197
|
}
|
|
145
198
|
return lines.join('\n');
|
|
146
199
|
}
|
package/dist/segments/context.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { color, c, visibleLength, pctColor, renderBar } from '../colors.js';
|
|
2
2
|
function formatTokens(n) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const safe = Math.max(0, Math.round(n || 0));
|
|
4
|
+
if (safe >= 1000000)
|
|
5
|
+
return `${(safe / 1000000).toFixed(1)}M`;
|
|
6
|
+
if (safe >= 1000)
|
|
7
|
+
return `${Math.round(safe / 1000)}K`;
|
|
8
|
+
return String(safe);
|
|
8
9
|
}
|
|
9
10
|
export const contextSegment = {
|
|
10
11
|
id: 'context',
|
package/dist/segments/git.js
CHANGED
|
@@ -31,12 +31,13 @@ function getGitInfo(cwd) {
|
|
|
31
31
|
}
|
|
32
32
|
let info = null;
|
|
33
33
|
try {
|
|
34
|
-
|
|
34
|
+
const execOpts = { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'], timeout: 1500 };
|
|
35
|
+
let branch = execSync('git branch --show-current', execOpts).trim();
|
|
35
36
|
if (!branch) {
|
|
36
|
-
branch = '@' + execSync('git rev-parse --short HEAD',
|
|
37
|
+
branch = '@' + execSync('git rev-parse --short HEAD', execOpts).trim();
|
|
37
38
|
}
|
|
38
|
-
const stagedOut = execSync('git diff --cached --numstat',
|
|
39
|
-
const modifiedOut = execSync('git diff --numstat',
|
|
39
|
+
const stagedOut = execSync('git diff --cached --numstat', execOpts).trim();
|
|
40
|
+
const modifiedOut = execSync('git diff --numstat', execOpts).trim();
|
|
40
41
|
const staged = stagedOut ? stagedOut.split('\n').length : 0;
|
|
41
42
|
const modified = modifiedOut ? modifiedOut.split('\n').length : 0;
|
|
42
43
|
let added = 0, removed = 0;
|
package/dist/segments/usage.js
CHANGED
|
@@ -16,7 +16,7 @@ function formatResetTime(epochSec) {
|
|
|
16
16
|
return `${d}d${h > 0 ? `${h}h` : ''}${m > 0 ? `${m}m` : ''}`;
|
|
17
17
|
if (h > 0)
|
|
18
18
|
return `${h}h${m > 0 ? `${m}m` : ''}`;
|
|
19
|
-
return `${m}m
|
|
19
|
+
return m > 0 ? `${m}m` : '<1m';
|
|
20
20
|
}
|
|
21
21
|
function clampPercent(v) {
|
|
22
22
|
return Math.min(999, Math.max(0, Math.round(v ?? 0)));
|
|
@@ -178,12 +178,34 @@ export const usageSegment = {
|
|
|
178
178
|
return { id: 'usage', priority: 25, width: 0, lines: [''] };
|
|
179
179
|
}
|
|
180
180
|
const BAR_WIDTH = 8;
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
const pctStr =
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
|
|
181
|
+
const active = limits.slice(0, 2);
|
|
182
|
+
const formatted = active.map(l => {
|
|
183
|
+
const pctStr = `${l.percent}%`;
|
|
184
|
+
const resetTime = l.resetsAt ? formatResetTime(l.resetsAt) : '';
|
|
185
|
+
const resetRaw = resetTime ? `↻ ${resetTime}` : '';
|
|
186
|
+
return {
|
|
187
|
+
limit: l,
|
|
188
|
+
pctStr,
|
|
189
|
+
resetRaw,
|
|
190
|
+
label: l.label,
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
const maxPctLen = Math.max(...formatted.map(r => r.pctStr.length));
|
|
194
|
+
const hasAnyReset = formatted.some(r => r.resetRaw.length > 0);
|
|
195
|
+
const maxResetLen = hasAnyReset ? Math.max(...formatted.map(r => r.resetRaw.length)) : 0;
|
|
196
|
+
const maxLabelLen = Math.max(...formatted.map(r => r.label.length));
|
|
197
|
+
const rows = formatted.map(r => {
|
|
198
|
+
const bar = renderBar(r.limit.percent, BAR_WIDTH);
|
|
199
|
+
const paddedPct = ' '.repeat(maxPctLen - r.pctStr.length) + r.pctStr;
|
|
200
|
+
const pctColorStr = color(paddedPct, pctColor(r.limit.percent), c.bold);
|
|
201
|
+
let resetColorStr = '';
|
|
202
|
+
if (hasAnyReset && r.resetRaw) {
|
|
203
|
+
const paddedReset = r.resetRaw + ' '.repeat(maxResetLen - r.resetRaw.length);
|
|
204
|
+
resetColorStr = color(paddedReset, c.dim);
|
|
205
|
+
}
|
|
206
|
+
const paddedLabel = ' '.repeat(maxLabelLen - r.label.length) + r.label;
|
|
207
|
+
const lbl = color(paddedLabel, c.dim);
|
|
208
|
+
const parts = [bar, pctColorStr, resetColorStr, lbl].filter(Boolean);
|
|
187
209
|
return parts.join(color(' · ', c.dim));
|
|
188
210
|
});
|
|
189
211
|
const width = Math.max(...rows.map(visibleLength));
|
package/dist/types.d.ts
CHANGED
|
@@ -76,8 +76,10 @@ export interface Segment {
|
|
|
76
76
|
render(data: AgyStatusData, allocWidth: number): Block;
|
|
77
77
|
enabled(data: AgyStatusData): boolean;
|
|
78
78
|
}
|
|
79
|
+
export type BorderStyle = 'rounded' | 'square' | 'double' | 'thick' | 'ascii' | 'none';
|
|
79
80
|
export interface StatusBlocksConfig {
|
|
80
81
|
segments?: string[];
|
|
81
82
|
stackWithDefault?: boolean;
|
|
82
83
|
remoteUsage?: boolean;
|
|
84
|
+
border?: BorderStyle;
|
|
83
85
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agy-statusblocks",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Opinionated, adaptive block-based status line for Antigravity CLI (agy) with bin-packing layout, rate limit tracking, and live agent telemetry",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|