codex-slot 0.1.20 → 0.1.21
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/dist/status-command.js +105 -14
- package/dist/status.js +214 -13
- package/package.json +1 -1
package/dist/status-command.js
CHANGED
|
@@ -91,6 +91,84 @@ function renderSummaryLine(summary, narrowScreen, styled) {
|
|
|
91
91
|
}
|
|
92
92
|
return `available=${available} 5h_limited=${fiveHourLimited} weekly_limited=${weeklyLimited}`;
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* 移除 ANSI 控制序列,避免布局计算把颜色码当成可见字符。
|
|
96
|
+
*
|
|
97
|
+
* @param value 可能包含 ANSI 样式的文本。
|
|
98
|
+
* @returns 去除 ANSI 控制符后的文本。
|
|
99
|
+
* @throws 无显式抛出。
|
|
100
|
+
*/
|
|
101
|
+
function stripAnsi(value) {
|
|
102
|
+
return value.replace(/\x1b\[[0-9;]*m/g, "");
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 判断字符是否应按双列宽展示。
|
|
106
|
+
*
|
|
107
|
+
* @param codePoint Unicode code point;必须来自单个字符迭代结果。
|
|
108
|
+
* @returns 中文、全角符号等宽字符返回 `true`,其他字符返回 `false`。
|
|
109
|
+
* @throws 无显式抛出。
|
|
110
|
+
*/
|
|
111
|
+
function isWideCodePoint(codePoint) {
|
|
112
|
+
return (codePoint >= 0x1100 &&
|
|
113
|
+
(codePoint <= 0x115f ||
|
|
114
|
+
codePoint === 0x2329 ||
|
|
115
|
+
codePoint === 0x232a ||
|
|
116
|
+
(codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
|
|
117
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
118
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
119
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
120
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
|
|
121
|
+
(codePoint >= 0xff00 && codePoint <= 0xff60) ||
|
|
122
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6)));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 计算文本在常见等宽终端中的显示列宽。
|
|
126
|
+
*
|
|
127
|
+
* @param value 待计算的文本;允许包含 ANSI 样式。
|
|
128
|
+
* @returns 文本实际占用的显示列数。
|
|
129
|
+
* @throws 无显式抛出。
|
|
130
|
+
*/
|
|
131
|
+
function getDisplayWidth(value) {
|
|
132
|
+
let width = 0;
|
|
133
|
+
for (const char of stripAnsi(value)) {
|
|
134
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
135
|
+
width += isWideCodePoint(codePoint) ? 2 : 1;
|
|
136
|
+
}
|
|
137
|
+
return width;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* 按显示宽度补齐右侧空格。
|
|
141
|
+
*
|
|
142
|
+
* @param value 原始文本;允许包含 ANSI 样式。
|
|
143
|
+
* @param width 目标显示列宽。
|
|
144
|
+
* @returns 右侧补齐后的文本。
|
|
145
|
+
* @throws 无显式抛出。
|
|
146
|
+
*/
|
|
147
|
+
function padVisible(value, width) {
|
|
148
|
+
return `${value}${" ".repeat(Math.max(0, width - getDisplayWidth(value)))}`;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* 将左右两组文本行渲染为双栏布局。
|
|
152
|
+
*
|
|
153
|
+
* 业务含义:
|
|
154
|
+
* 1. 宽屏状态页左侧展示账号列表,右侧展示当前账号与摘要。
|
|
155
|
+
* 2. 左栏高度通常更高,右栏缺失行需要自动补空,避免右侧内容把左表挤乱。
|
|
156
|
+
*
|
|
157
|
+
* @param leftLines 左栏文本行。
|
|
158
|
+
* @param rightLines 右栏文本行。
|
|
159
|
+
* @param gap 两栏之间的空格数量。
|
|
160
|
+
* @returns 合并后的双栏文本行。
|
|
161
|
+
* @throws 无显式抛出。
|
|
162
|
+
*/
|
|
163
|
+
function renderColumns(leftLines, rightLines, gap) {
|
|
164
|
+
const leftWidth = Math.max(0, ...leftLines.map((line) => getDisplayWidth(line)));
|
|
165
|
+
const rowCount = Math.max(leftLines.length, rightLines.length);
|
|
166
|
+
const rows = [];
|
|
167
|
+
for (let index = 0; index < rowCount; index += 1) {
|
|
168
|
+
rows.push(`${padVisible(leftLines[index] ?? "", leftWidth)}${" ".repeat(gap)}${rightLines[index] ?? ""}`.trimEnd());
|
|
169
|
+
}
|
|
170
|
+
return rows;
|
|
171
|
+
}
|
|
94
172
|
/**
|
|
95
173
|
* 进入交互式全屏缓冲区,并隐藏光标,确保后续重绘始终基于固定画布。
|
|
96
174
|
*
|
|
@@ -185,7 +263,6 @@ async function handleInteractiveToggle(initialStatuses) {
|
|
|
185
263
|
let closed = false;
|
|
186
264
|
const render = () => {
|
|
187
265
|
const screenWidth = process.stdout.columns ?? 80;
|
|
188
|
-
const narrowScreen = screenWidth < 72;
|
|
189
266
|
const styled = shouldUseAnsiStyle();
|
|
190
267
|
const latestSnapshot = (0, status_service_1.getStatusSnapshot)();
|
|
191
268
|
const statusSource = changed ? latestSnapshot.statuses : (initialStatuses ?? latestSnapshot.statuses);
|
|
@@ -205,27 +282,41 @@ async function handleInteractiveToggle(initialStatuses) {
|
|
|
205
282
|
})
|
|
206
283
|
.filter((item) => item !== null);
|
|
207
284
|
const currentItem = displayStatuses.find((item) => item.id === accounts[cursor]?.id) ?? null;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
285
|
+
const wideLayout = screenWidth >= 104;
|
|
286
|
+
const leftWidth = wideLayout ? Math.max(68, Math.floor(screenWidth * 0.64)) : screenWidth;
|
|
287
|
+
const rightWidth = wideLayout ? Math.max(28, screenWidth - leftWidth - 3) : screenWidth;
|
|
288
|
+
const accountLines = [
|
|
289
|
+
renderSectionHeader("accounts", leftWidth, styled),
|
|
290
|
+
...(0, status_1.renderStatusTable)(displayStatuses, {
|
|
211
291
|
compact: true,
|
|
212
|
-
maxWidth:
|
|
292
|
+
maxWidth: leftWidth,
|
|
293
|
+
styled,
|
|
213
294
|
selectorColumn: {
|
|
214
295
|
enabledById: Object.fromEntries(accounts.map((account) => [account.id, account.enabled])),
|
|
215
296
|
cursorAccountId: accounts[cursor]?.id ?? null
|
|
216
297
|
}
|
|
217
|
-
})
|
|
298
|
+
}).split("\n")
|
|
299
|
+
];
|
|
300
|
+
const sideLines = [
|
|
301
|
+
renderSectionHeader("current", rightWidth, styled),
|
|
302
|
+
...(0, status_1.renderStatusDetails)(currentItem, { maxWidth: rightWidth, header: false }).split("\n"),
|
|
218
303
|
"",
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
(0, status_1.renderStatusDetails)(currentItem, { maxWidth: screenWidth, header: false }),
|
|
222
|
-
"",
|
|
223
|
-
renderSectionHeader("summary", screenWidth, styled),
|
|
224
|
-
renderSummaryLine(summary, narrowScreen, styled),
|
|
304
|
+
renderSectionHeader("summary", rightWidth, styled),
|
|
305
|
+
renderSummaryLine(summary, rightWidth < 42, styled),
|
|
225
306
|
`selected=${latestSnapshot.selectedName ?? "none"}`,
|
|
226
307
|
"",
|
|
227
|
-
renderSectionHeader("help",
|
|
228
|
-
(0, text_1.bi)(
|
|
308
|
+
renderSectionHeader("help", rightWidth, styled),
|
|
309
|
+
(0, text_1.bi)(rightWidth < 42 ? "Space 切换,Enter/q 退出。" : "Space 切换启用状态,Enter / q 退出。", rightWidth < 42 ? "Space toggles, Enter/q exits." : "Space toggles enabled state, Enter / q exits.")
|
|
310
|
+
];
|
|
311
|
+
if (wideLayout) {
|
|
312
|
+
renderInteractiveScreen(renderColumns(accountLines, sideLines, 3));
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
renderInteractiveScreen([
|
|
316
|
+
...accountLines,
|
|
317
|
+
"",
|
|
318
|
+
renderDivider(screenWidth, styled),
|
|
319
|
+
...sideLines
|
|
229
320
|
]);
|
|
230
321
|
};
|
|
231
322
|
const applyChanges = () => {
|
package/dist/status.js
CHANGED
|
@@ -8,6 +8,14 @@ const config_1 = require("./config");
|
|
|
8
8
|
const account_store_1 = require("./account-store");
|
|
9
9
|
const state_1 = require("./state");
|
|
10
10
|
const text_1 = require("./text");
|
|
11
|
+
const TABLE_ANSI = {
|
|
12
|
+
reset: "\x1b[0m",
|
|
13
|
+
dim: "\x1b[2m",
|
|
14
|
+
green: "\x1b[32m",
|
|
15
|
+
yellow: "\x1b[33m",
|
|
16
|
+
red: "\x1b[31m",
|
|
17
|
+
cyan: "\x1b[36m"
|
|
18
|
+
};
|
|
11
19
|
function computeLeftPercent(usedPercent) {
|
|
12
20
|
if (usedPercent === null || usedPercent === undefined || Number.isNaN(usedPercent)) {
|
|
13
21
|
return null;
|
|
@@ -30,23 +38,214 @@ function formatReset(unixSeconds) {
|
|
|
30
38
|
return (0, text_1.formatLocalDateTime)(unixSeconds);
|
|
31
39
|
}
|
|
32
40
|
/**
|
|
33
|
-
*
|
|
41
|
+
* 移除 ANSI 控制序列,获得真实可见文本。
|
|
42
|
+
*
|
|
43
|
+
* 业务含义:
|
|
44
|
+
* 1. 交互界面会对状态列做轻量着色。
|
|
45
|
+
* 2. 表格宽度计算必须忽略颜色控制符,否则列宽会被错误拉大。
|
|
46
|
+
*
|
|
47
|
+
* @param value 可能包含 ANSI 样式的文本。
|
|
48
|
+
* @returns 去除 ANSI 控制序列后的可见文本。
|
|
49
|
+
* @throws 无显式抛出。
|
|
50
|
+
*/
|
|
51
|
+
function stripAnsi(value) {
|
|
52
|
+
return value.replace(/\x1b\[[0-9;]*m/g, "");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 按需给文本添加 ANSI 样式。
|
|
34
56
|
*
|
|
35
57
|
* @param value 原始文本。
|
|
36
|
-
* @param
|
|
58
|
+
* @param color ANSI 颜色或样式控制符。
|
|
59
|
+
* @param styled 是否启用样式。
|
|
60
|
+
* @returns 启用样式时返回带 ANSI 控制符的文本,否则返回原文。
|
|
61
|
+
* @throws 无显式抛出。
|
|
62
|
+
*/
|
|
63
|
+
function styleCell(value, color, styled) {
|
|
64
|
+
if (!styled) {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
return `${color}${value}${TABLE_ANSI.reset}`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 判断字符是否应按双列宽展示。
|
|
71
|
+
*
|
|
72
|
+
* 业务含义:
|
|
73
|
+
* 1. 终端中中文、全角符号与多数 CJK 字符通常占两个显示列。
|
|
74
|
+
* 2. 表格列宽若只按字符串长度计算,会导致包含中文括号的账号名错位或过早截断。
|
|
75
|
+
*
|
|
76
|
+
* @param codePoint Unicode code point;必须来自单个字符迭代结果。
|
|
77
|
+
* @returns `true` 表示该字符应按双列宽计算;其他字符返回 `false`。
|
|
78
|
+
* @throws 无显式抛出。
|
|
79
|
+
*/
|
|
80
|
+
function isWideCodePoint(codePoint) {
|
|
81
|
+
return (codePoint >= 0x1100 &&
|
|
82
|
+
(codePoint <= 0x115f ||
|
|
83
|
+
codePoint === 0x2329 ||
|
|
84
|
+
codePoint === 0x232a ||
|
|
85
|
+
(codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
|
|
86
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
87
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
88
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
89
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
|
|
90
|
+
(codePoint >= 0xff00 && codePoint <= 0xff60) ||
|
|
91
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6)));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 计算文本在常见等宽终端中的显示列宽。
|
|
95
|
+
*
|
|
96
|
+
* 业务含义:
|
|
97
|
+
* 1. 状态表需要根据终端列数分配可用空间。
|
|
98
|
+
* 2. 账号名可能包含中文日期括号,必须按显示宽度而不是 UTF-16 长度计算。
|
|
99
|
+
*
|
|
100
|
+
* @param value 待展示文本;允许为空字符串。
|
|
101
|
+
* @returns 文本占用的终端显示列数。
|
|
102
|
+
* @throws 无显式抛出。
|
|
103
|
+
*/
|
|
104
|
+
function getDisplayWidth(value) {
|
|
105
|
+
const visibleValue = stripAnsi(value);
|
|
106
|
+
let width = 0;
|
|
107
|
+
for (const char of visibleValue) {
|
|
108
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
109
|
+
width += isWideCodePoint(codePoint) ? 2 : 1;
|
|
110
|
+
}
|
|
111
|
+
return width;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 按终端显示列宽截断文本。
|
|
115
|
+
*
|
|
116
|
+
* 业务含义:
|
|
117
|
+
* 1. 优先保证表格整体不换行。
|
|
118
|
+
* 2. 截断时保留省略号,让用户能看出内容未完全展示。
|
|
119
|
+
*
|
|
120
|
+
* @param value 原始文本。
|
|
121
|
+
* @param maxWidth 最大显示列宽;小于等于 0 时返回空字符串。
|
|
37
122
|
* @returns 截断后的文本;宽度过小时退化为最短可读形式。
|
|
123
|
+
* @throws 无显式抛出。
|
|
38
124
|
*/
|
|
39
125
|
function truncateCell(value, maxWidth) {
|
|
40
126
|
if (maxWidth <= 0) {
|
|
41
127
|
return "";
|
|
42
128
|
}
|
|
43
|
-
if (value
|
|
129
|
+
if (getDisplayWidth(value) <= maxWidth) {
|
|
44
130
|
return value;
|
|
45
131
|
}
|
|
46
132
|
if (maxWidth <= 2) {
|
|
47
|
-
|
|
133
|
+
let output = "";
|
|
134
|
+
let width = 0;
|
|
135
|
+
for (const char of value) {
|
|
136
|
+
const charWidth = getDisplayWidth(char);
|
|
137
|
+
if (width + charWidth > maxWidth) {
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
output += char;
|
|
141
|
+
width += charWidth;
|
|
142
|
+
}
|
|
143
|
+
return output;
|
|
144
|
+
}
|
|
145
|
+
const ellipsis = "…";
|
|
146
|
+
const targetWidth = maxWidth - getDisplayWidth(ellipsis);
|
|
147
|
+
let output = "";
|
|
148
|
+
let width = 0;
|
|
149
|
+
for (const char of value) {
|
|
150
|
+
const charWidth = getDisplayWidth(char);
|
|
151
|
+
if (width + charWidth > targetWidth) {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
output += char;
|
|
155
|
+
width += charWidth;
|
|
156
|
+
}
|
|
157
|
+
return `${output}${ellipsis}`;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* 按终端显示列宽补齐单元格。
|
|
161
|
+
*
|
|
162
|
+
* @param value 已完成截断的单元格文本。
|
|
163
|
+
* @param width 目标显示列宽。
|
|
164
|
+
* @returns 右侧补空格后的单元格文本。
|
|
165
|
+
* @throws 无显式抛出。
|
|
166
|
+
*/
|
|
167
|
+
function padCell(value, width) {
|
|
168
|
+
return `${value}${" ".repeat(Math.max(0, width - getDisplayWidth(value)))}`;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 根据账号状态给状态标签选择终端样式。
|
|
172
|
+
*
|
|
173
|
+
* 业务含义:
|
|
174
|
+
* 1. `available` 是调度可用状态,使用绿色强调。
|
|
175
|
+
* 2. 额度限制和短时熔断需要提醒但不一定是错误,使用黄色。
|
|
176
|
+
* 3. 工作空间损坏、账号缺失等不可用异常使用红色。
|
|
177
|
+
* 4. 禁用账号使用弱化样式,减少对可用账号的视觉干扰。
|
|
178
|
+
*
|
|
179
|
+
* @param status 已归一化的状态标签。
|
|
180
|
+
* @param item 单个账号状态。
|
|
181
|
+
* @param styled 是否启用 ANSI 样式。
|
|
182
|
+
* @returns 应用于表格状态列的文本。
|
|
183
|
+
* @throws 无显式抛出。
|
|
184
|
+
*/
|
|
185
|
+
function styleStatusCell(status, item, styled) {
|
|
186
|
+
if (!styled) {
|
|
187
|
+
return status;
|
|
188
|
+
}
|
|
189
|
+
if (item.isAvailable) {
|
|
190
|
+
return styleCell(status, TABLE_ANSI.green, styled);
|
|
191
|
+
}
|
|
192
|
+
if (!item.enabled) {
|
|
193
|
+
return styleCell(status, TABLE_ANSI.dim, styled);
|
|
194
|
+
}
|
|
195
|
+
if (item.refreshErrorCode || !item.exists) {
|
|
196
|
+
return styleCell(status, TABLE_ANSI.red, styled);
|
|
48
197
|
}
|
|
49
|
-
|
|
198
|
+
if (item.isFiveHourLimited || item.isWeeklyLimited || item.localBlockUntil) {
|
|
199
|
+
return styleCell(status, TABLE_ANSI.yellow, styled);
|
|
200
|
+
}
|
|
201
|
+
return status;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* 对当前自动选中账号的名称做轻量强调。
|
|
205
|
+
*
|
|
206
|
+
* @param name 账号展示名称。
|
|
207
|
+
* @param styled 是否启用 ANSI 样式。
|
|
208
|
+
* @returns 表格名称列展示文本。
|
|
209
|
+
* @throws 无显式抛出。
|
|
210
|
+
*/
|
|
211
|
+
function styleNameCell(name, styled) {
|
|
212
|
+
if (!styled || !name.endsWith("*")) {
|
|
213
|
+
return name;
|
|
214
|
+
}
|
|
215
|
+
return styleCell(name, TABLE_ANSI.cyan, styled);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* 计算紧凑状态表中账号名称列可使用的显示宽度。
|
|
219
|
+
*
|
|
220
|
+
* 业务含义:
|
|
221
|
+
* 1. 窄终端下保留最小可读名称。
|
|
222
|
+
* 2. 终端变宽时优先把新增空间分配给账号名称,避免固定 12 列导致名字仍被截断。
|
|
223
|
+
*
|
|
224
|
+
* @param statuses 待展示账号状态。
|
|
225
|
+
* @param hasSelector 是否展示选择/启用状态列。
|
|
226
|
+
* @param maxWidth 当前终端最大显示列宽;无穷大表示不限制。
|
|
227
|
+
* @param headerWidth 当前账号列标题宽度。
|
|
228
|
+
* @param planWidth plan 列宽。
|
|
229
|
+
* @param statusWidth 状态列宽。
|
|
230
|
+
* @returns 账号名称列的目标显示宽度。
|
|
231
|
+
* @throws 无显式抛出。
|
|
232
|
+
*/
|
|
233
|
+
function resolveCompactSlotWidth(statuses, hasSelector, maxWidth, headerWidth, planWidth, statusWidth) {
|
|
234
|
+
const longestNameWidth = Math.max(headerWidth, ...statuses.map((item) => getDisplayWidth(item.name)));
|
|
235
|
+
if (!Number.isFinite(maxWidth)) {
|
|
236
|
+
return longestNameWidth;
|
|
237
|
+
}
|
|
238
|
+
const fixedColumnWidths = [
|
|
239
|
+
...(hasSelector ? [4] : []),
|
|
240
|
+
planWidth,
|
|
241
|
+
3,
|
|
242
|
+
4,
|
|
243
|
+
statusWidth
|
|
244
|
+
];
|
|
245
|
+
const separatorWidth = 2 * fixedColumnWidths.length;
|
|
246
|
+
const availableWidth = Math.floor(maxWidth) - fixedColumnWidths.reduce((sum, width) => sum + width, 0) - separatorWidth;
|
|
247
|
+
const minWidth = maxWidth < 56 ? 8 : 12;
|
|
248
|
+
return Math.max(Math.min(longestNameWidth, availableWidth), Math.min(minWidth, Math.max(4, availableWidth)));
|
|
50
249
|
}
|
|
51
250
|
/**
|
|
52
251
|
* 生成固定标签宽度的详情行,超出终端宽度时自动截断值部分。
|
|
@@ -263,15 +462,17 @@ function renderStatusTable(statuses, options) {
|
|
|
263
462
|
const selectorColumn = options?.selectorColumn;
|
|
264
463
|
const compact = options?.compact ?? false;
|
|
265
464
|
const maxWidth = options?.maxWidth ?? Number.POSITIVE_INFINITY;
|
|
465
|
+
const styled = options?.styled ?? false;
|
|
266
466
|
const compactHeader = maxWidth < 68;
|
|
267
|
-
const compactSlotWidth = maxWidth < 56 ? 8 : 12;
|
|
268
467
|
const compactPlanWidth = maxWidth < 56 ? 4 : 6;
|
|
269
468
|
const compactStatusWidth = maxWidth < 56 ? 12 : 18;
|
|
469
|
+
const compactSlotHeader = compactHeader ? "ID" : "SLOT";
|
|
470
|
+
const compactSlotWidth = resolveCompactSlotWidth(statuses, Boolean(selectorColumn), maxWidth, getDisplayWidth(compactSlotHeader), compactPlanWidth, compactStatusWidth);
|
|
270
471
|
const rows = [
|
|
271
472
|
compact
|
|
272
473
|
? [
|
|
273
474
|
...(selectorColumn ? [" "] : []),
|
|
274
|
-
|
|
475
|
+
compactSlotHeader,
|
|
275
476
|
compactHeader ? "P" : "PLAN",
|
|
276
477
|
"5H",
|
|
277
478
|
compactHeader ? "WK" : "WEEK",
|
|
@@ -297,27 +498,27 @@ function renderStatusTable(statuses, options) {
|
|
|
297
498
|
rows.push(compact
|
|
298
499
|
? [
|
|
299
500
|
...(selectorCell ? [selectorCell] : []),
|
|
300
|
-
truncateCell(item.name, compactSlotWidth),
|
|
501
|
+
styleNameCell(truncateCell(item.name, compactSlotWidth), styled),
|
|
301
502
|
truncateCell(item.plan, compactPlanWidth),
|
|
302
503
|
formatPercent(item.fiveHourLeftPercent),
|
|
303
504
|
formatPercent(item.weeklyLeftPercent),
|
|
304
|
-
truncateCell(status, compactStatusWidth)
|
|
505
|
+
styleStatusCell(truncateCell(status, compactStatusWidth), item, styled)
|
|
305
506
|
]
|
|
306
507
|
: [
|
|
307
508
|
...(selectorCell ? [selectorCell] : []),
|
|
308
|
-
item.name,
|
|
509
|
+
styleNameCell(item.name, styled),
|
|
309
510
|
item.email ?? "-",
|
|
310
511
|
item.plan,
|
|
311
512
|
formatPercent(item.fiveHourLeftPercent),
|
|
312
513
|
formatReset(item.fiveHourResetsAt),
|
|
313
514
|
formatPercent(item.weeklyLeftPercent),
|
|
314
515
|
formatReset(item.weeklyResetsAt),
|
|
315
|
-
status
|
|
516
|
+
styleStatusCell(status, item, styled)
|
|
316
517
|
]);
|
|
317
518
|
}
|
|
318
|
-
const widths = rows[0].map((_, columnIndex) => Math.max(...rows.map((row) => row[columnIndex]
|
|
519
|
+
const widths = rows[0].map((_, columnIndex) => Math.max(...rows.map((row) => getDisplayWidth(row[columnIndex]))));
|
|
319
520
|
return rows
|
|
320
|
-
.map((row) => row.map((cell, index) => cell
|
|
521
|
+
.map((row) => row.map((cell, index) => padCell(cell, widths[index])).join(" "))
|
|
321
522
|
.join("\n");
|
|
322
523
|
}
|
|
323
524
|
/**
|