claude-statusline 2.1.2 → 2.1.4
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 +1 -1
- package/dist/index.bundle.js +9 -10
- package/dist/index.bundle.js.map +7 -0
- package/dist/index.js +71 -44
- package/dist/index.js.map +1 -1
- package/dist/metafile.json +479 -0
- package/dist/metafile.prod.json +6 -6
- package/dist/ui/width.d.ts +5 -0
- package/dist/ui/width.js +48 -0
- package/dist/ui/width.js.map +1 -1
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { validateInput, validateDirectory } from './core/security.js';
|
|
|
9
9
|
import { Cache } from './core/cache.js';
|
|
10
10
|
import { GitOperations } from './git/status.js';
|
|
11
11
|
import { detectSymbols, getEnvironmentSymbols } from './ui/symbols.js';
|
|
12
|
-
import { getTerminalWidth, truncateText, smartTruncate, debugWidthDetection } from './ui/width.js';
|
|
12
|
+
import { getTerminalWidth, truncateText, smartTruncate, debugWidthDetection, getStringDisplayWidth } from './ui/width.js';
|
|
13
13
|
import { EnvironmentDetector, EnvironmentFormatter } from './env/context.js';
|
|
14
14
|
/**
|
|
15
15
|
* Main execution function
|
|
@@ -153,15 +153,23 @@ async function buildStatusline(params) {
|
|
|
153
153
|
}
|
|
154
154
|
/**
|
|
155
155
|
* Calculate context window usage percentage
|
|
156
|
+
*
|
|
157
|
+
* Prioritizes the pre-calculated used_percentage field (Claude Code v2.1.15+)
|
|
158
|
+
* Falls back to manual calculation from current_usage if not available
|
|
156
159
|
*/
|
|
157
160
|
function calculateContextWindowPercentage(contextWindow) {
|
|
158
161
|
try {
|
|
162
|
+
// Try pre-calculated percentage first (Claude Code v2.1.15+)
|
|
163
|
+
if (contextWindow.used_percentage !== undefined && contextWindow.used_percentage !== null) {
|
|
164
|
+
// Cap at 100% and ensure non-negative
|
|
165
|
+
return Math.max(0, Math.min(100, Math.round(contextWindow.used_percentage)));
|
|
166
|
+
}
|
|
167
|
+
// Fall back to manual calculation for older Claude Code versions
|
|
159
168
|
const { current_usage, context_window_size } = contextWindow;
|
|
160
169
|
if (!context_window_size || context_window_size === 0) {
|
|
161
170
|
return null;
|
|
162
171
|
}
|
|
163
172
|
// If current_usage is null or undefined, we cannot calculate the percentage
|
|
164
|
-
// This matches the official Claude Code documentation behavior
|
|
165
173
|
if (!current_usage) {
|
|
166
174
|
return 0;
|
|
167
175
|
}
|
|
@@ -188,27 +196,30 @@ function applySmartTruncation(params) {
|
|
|
188
196
|
// Use 15-char margin for Claude telemetry compatibility
|
|
189
197
|
const maxLen = Math.max(terminalWidth - config.rightMargin, 30);
|
|
190
198
|
const projectGit = `${projectName}${gitStatus}`;
|
|
191
|
-
// Check if everything fits
|
|
192
|
-
|
|
199
|
+
// Check if everything fits (using display width for accuracy)
|
|
200
|
+
const statuslineDisplayWidth = getStringDisplayWidth(statusline);
|
|
201
|
+
if (statuslineDisplayWidth <= maxLen) {
|
|
193
202
|
return statusline;
|
|
194
203
|
}
|
|
195
|
-
// Check if project + space fits, truncate model part only
|
|
196
|
-
|
|
204
|
+
// Check if project + space fits, truncate model part only (using display width)
|
|
205
|
+
const projectGitDisplayWidth = getStringDisplayWidth(projectGit);
|
|
206
|
+
if (projectGitDisplayWidth + 1 <= maxLen) {
|
|
197
207
|
// Smart truncation with soft-wrapping (default behavior)
|
|
198
208
|
// Allow disabling soft-wrapping with config setting
|
|
199
209
|
if (config.noSoftWrap) {
|
|
200
210
|
// Legacy behavior: simple truncation only
|
|
201
|
-
const modelMaxLen = maxLen -
|
|
211
|
+
const modelMaxLen = maxLen - projectGitDisplayWidth - 1;
|
|
202
212
|
const truncatedModel = truncateText(modelString, modelMaxLen);
|
|
203
213
|
return `${projectGit} ${truncatedModel}`;
|
|
204
214
|
}
|
|
205
215
|
else {
|
|
206
216
|
// Default: soft-wrap model part
|
|
207
|
-
const modelMaxLen = maxLen -
|
|
217
|
+
const modelMaxLen = maxLen - projectGitDisplayWidth - 1;
|
|
208
218
|
// If model string needs wrapping and it starts with model icon,
|
|
209
219
|
// prefer wrapping the entire model string to next line
|
|
210
220
|
const modelIconPattern = /^[*]/;
|
|
211
|
-
|
|
221
|
+
const modelDisplayWidth = getStringDisplayWidth(modelString);
|
|
222
|
+
if (modelIconPattern.test(modelString) && modelDisplayWidth > modelMaxLen) {
|
|
212
223
|
// Wrap entire model to next line
|
|
213
224
|
return `${projectGit}\n${modelString}`;
|
|
214
225
|
}
|
|
@@ -228,7 +239,9 @@ function applySmartTruncation(params) {
|
|
|
228
239
|
* Apply soft wrapping to text
|
|
229
240
|
*/
|
|
230
241
|
function applySoftWrap(text, maxLength) {
|
|
231
|
-
|
|
242
|
+
// Use display width for accurate measurement
|
|
243
|
+
const displayWidth = getStringDisplayWidth(text);
|
|
244
|
+
if (displayWidth <= maxLength) {
|
|
232
245
|
return text;
|
|
233
246
|
}
|
|
234
247
|
// Check if this is a model string (starts with model icon)
|
|
@@ -236,26 +249,27 @@ function applySoftWrap(text, maxLength) {
|
|
|
236
249
|
if (modelIconPattern.test(text)) {
|
|
237
250
|
return applySoftWrapToModelString(text, maxLength);
|
|
238
251
|
}
|
|
239
|
-
// Find a good break point
|
|
252
|
+
// Find a good break point using display width
|
|
240
253
|
let foundBreak = false;
|
|
241
254
|
// Work with actual Unicode characters to avoid splitting multi-byte sequences
|
|
242
255
|
const chars = Array.from(text); // This splits by actual Unicode characters
|
|
243
|
-
let
|
|
256
|
+
let currentDisplayWidth = 0;
|
|
244
257
|
let breakCharIndex = chars.length; // Default to no break
|
|
245
258
|
let lastSpaceIndex = -1;
|
|
246
|
-
// Find the best break point by
|
|
259
|
+
// Find the best break point by display width
|
|
247
260
|
for (let i = 0; i < chars.length; i++) {
|
|
248
261
|
const char = chars[i];
|
|
262
|
+
if (!char)
|
|
263
|
+
break;
|
|
249
264
|
// Track spaces for potential break points
|
|
250
265
|
if (char === ' ') {
|
|
251
266
|
lastSpaceIndex = i;
|
|
252
267
|
}
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
charCount++;
|
|
268
|
+
// Calculate display width for this character
|
|
269
|
+
const charWidth = getStringDisplayWidth(char);
|
|
270
|
+
currentDisplayWidth += charWidth;
|
|
257
271
|
// Check if we've exceeded the max length
|
|
258
|
-
if (
|
|
272
|
+
if (currentDisplayWidth > maxLength) {
|
|
259
273
|
// If we found a space before this point, use it
|
|
260
274
|
if (lastSpaceIndex >= 0) {
|
|
261
275
|
breakCharIndex = lastSpaceIndex;
|
|
@@ -276,17 +290,24 @@ function applySoftWrap(text, maxLength) {
|
|
|
276
290
|
// But only if we're not dealing with a model string that starts with an icon
|
|
277
291
|
const firstChar = chars.length > 0 ? chars[0] : '';
|
|
278
292
|
const startsWithIcon = firstChar && firstChar !== ' ' && Buffer.byteLength(firstChar, 'utf8') > 1;
|
|
279
|
-
if (!foundBreak && maxLength -
|
|
293
|
+
if (!foundBreak && maxLength - currentDisplayWidth > -3 && !startsWithIcon) {
|
|
280
294
|
return text;
|
|
281
295
|
}
|
|
282
296
|
// Special case: if we're starting with an icon and breaking very early,
|
|
283
297
|
// try to keep at least the icon and 1-2 more characters
|
|
284
298
|
if (startsWithIcon && breakCharIndex <= 2 && maxLength >= 3) {
|
|
285
|
-
// Find a better break point after at least 3 characters total
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
299
|
+
// Find a better break point after at least 3 characters total (by display width)
|
|
300
|
+
let testWidth = 0;
|
|
301
|
+
for (let i = 0; i < chars.length; i++) {
|
|
302
|
+
const char = chars[i];
|
|
303
|
+
if (!char)
|
|
304
|
+
break;
|
|
305
|
+
testWidth += getStringDisplayWidth(char);
|
|
306
|
+
if (testWidth >= 3 || testWidth >= maxLength) {
|
|
307
|
+
breakCharIndex = i;
|
|
308
|
+
foundBreak = true;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
290
311
|
}
|
|
291
312
|
}
|
|
292
313
|
// Build the strings using character indices
|
|
@@ -312,7 +333,9 @@ function applySoftWrap(text, maxLength) {
|
|
|
312
333
|
* with the model name and context usage as a unit
|
|
313
334
|
*/
|
|
314
335
|
function applySoftWrapToModelString(text, maxLength) {
|
|
315
|
-
|
|
336
|
+
// Use display width for accurate measurement
|
|
337
|
+
const displayWidth = getStringDisplayWidth(text);
|
|
338
|
+
if (displayWidth <= maxLength) {
|
|
316
339
|
return text;
|
|
317
340
|
}
|
|
318
341
|
const chars = Array.from(text);
|
|
@@ -340,9 +363,10 @@ function applySoftWrapToModelString(text, maxLength) {
|
|
|
340
363
|
// Find the space before context usage
|
|
341
364
|
const spaceBeforeContext = chars.findIndex((c, i) => i < contextIconIndex && c === ' ');
|
|
342
365
|
if (spaceBeforeContext > 0) {
|
|
343
|
-
// Check if we can fit model + icon + percentage on second line
|
|
366
|
+
// Check if we can fit model + icon + percentage on second line (using display width)
|
|
344
367
|
const contextPart = chars.slice(spaceBeforeContext + 1).join('');
|
|
345
|
-
|
|
368
|
+
const contextPartWidth = getStringDisplayWidth(contextPart);
|
|
369
|
+
if (contextPartWidth <= maxLength) {
|
|
346
370
|
// Put model name on first line, context on second line
|
|
347
371
|
const modelPart = chars.slice(0, spaceBeforeContext).join('');
|
|
348
372
|
return `${modelPart}\n${contextPart}`;
|
|
@@ -354,9 +378,10 @@ function applySoftWrapToModelString(text, maxLength) {
|
|
|
354
378
|
if (spacePositions.length > 0) {
|
|
355
379
|
const firstSpaceAfterModel = spacePositions[0];
|
|
356
380
|
if (firstSpaceAfterModel !== undefined && firstSpaceAfterModel > 1) {
|
|
357
|
-
// Check if the model part fits
|
|
381
|
+
// Check if the model part fits (using display width)
|
|
358
382
|
const modelPart = chars.slice(0, firstSpaceAfterModel).join('');
|
|
359
|
-
|
|
383
|
+
const modelPartWidth = getStringDisplayWidth(modelPart);
|
|
384
|
+
if (modelPartWidth <= maxLength) {
|
|
360
385
|
const remainingPart = chars.slice(firstSpaceAfterModel + 1).join('');
|
|
361
386
|
if (remainingPart) {
|
|
362
387
|
return `${modelPart}\n${remainingPart}`;
|
|
@@ -365,23 +390,25 @@ function applySoftWrapToModelString(text, maxLength) {
|
|
|
365
390
|
}
|
|
366
391
|
}
|
|
367
392
|
}
|
|
368
|
-
// Strategy 3:
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
393
|
+
// Strategy 3: Find break point by display width (not character count)
|
|
394
|
+
// This is the critical fix for the garbled output artifacts
|
|
395
|
+
let currentWidth = 0;
|
|
396
|
+
let breakCharIndex = chars.length;
|
|
397
|
+
for (let i = 0; i < chars.length; i++) {
|
|
398
|
+
const char = chars[i];
|
|
399
|
+
if (!char)
|
|
400
|
+
break;
|
|
401
|
+
const charWidth = getStringDisplayWidth(char);
|
|
402
|
+
if (currentWidth + charWidth > maxLength) {
|
|
403
|
+
breakCharIndex = i;
|
|
404
|
+
break;
|
|
378
405
|
}
|
|
379
|
-
|
|
406
|
+
currentWidth += charWidth;
|
|
380
407
|
}
|
|
381
|
-
//
|
|
382
|
-
if (
|
|
383
|
-
const firstPart = chars.slice(0,
|
|
384
|
-
const secondPart = chars.slice(
|
|
408
|
+
// If we found a valid break point
|
|
409
|
+
if (breakCharIndex < chars.length && breakCharIndex > 0) {
|
|
410
|
+
const firstPart = chars.slice(0, breakCharIndex).join('');
|
|
411
|
+
const secondPart = chars.slice(breakCharIndex).join('');
|
|
385
412
|
if (secondPart) {
|
|
386
413
|
return `${firstPart}\n${secondPart}`;
|
|
387
414
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\n/**\n * Claude Statusline - TypeScript v2.0\n * Main entry point\n */\n\nimport { readFileSync } from 'fs';\nimport { loadConfig, Config } from './core/config.js';\nimport { validateInput, validateDirectory } from './core/security.js';\nimport { Cache } from './core/cache.js';\nimport { GitOperations } from './git/status.js';\nimport { detectSymbols, getEnvironmentSymbols, SymbolSet } from './ui/symbols.js';\nimport { getTerminalWidth, truncateText, smartTruncate, debugWidthDetection } from './ui/width.js';\nimport { EnvironmentDetector, EnvironmentFormatter } from './env/context.js';\n\n/**\n * Claude Code input interface\n */\ninterface ClaudeInput {\n workspace: {\n current_dir: string;\n };\n model: {\n display_name: string;\n };\n context_window?: {\n total_input_tokens: number;\n total_output_tokens: number;\n context_window_size: number;\n current_usage: {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens: number;\n cache_read_input_tokens: number;\n };\n };\n}\n\n/**\n * Main execution function\n */\nexport async function main(): Promise<void> {\n try {\n // Load configuration\n const config = loadConfig();\n\n // Initialize components\n const cache = new Cache(config);\n const gitOps = new GitOperations(config, cache);\n const envDetector = new EnvironmentDetector(config, cache);\n\n // Debug width detection if enabled\n await debugWidthDetection(config);\n\n // Read and validate input from stdin\n const input = await readInput();\n if (!validateInput(JSON.stringify(input), config)) {\n console.error('[ERROR] Invalid input received');\n process.exit(1);\n }\n\n // Extract information from input\n const { fullDir, modelName, contextWindow } = extractInputInfo(input);\n if (!fullDir || !modelName) {\n console.error('[ERROR] Failed to extract required information from input');\n process.exit(1);\n }\n\n // Validate directory\n const isValidDir = await validateDirectory(fullDir);\n if (!isValidDir) {\n console.error('[ERROR] Invalid or inaccessible directory:', fullDir);\n process.exit(1);\n }\n\n // Get components (run in parallel for better performance)\n const operations: Promise<any>[] = [\n gitOps.getGitInfo(fullDir),\n envDetector.getEnvironmentInfo(),\n detectSymbols(config),\n ];\n\n // Only get terminal width if smart truncation is enabled\n let terminalWidth: number | undefined;\n if (config.truncate) {\n operations.push(getTerminalWidth(config));\n }\n\n const results = await Promise.all(operations);\n const [gitInfo, envInfo, symbols] = results;\n\n // Extract terminal width from results if it was requested\n if (config.truncate && results.length > 3) {\n terminalWidth = results[3];\n }\n\n // Build statusline\n const statusline = await buildStatusline({\n fullDir,\n modelName,\n contextWindow,\n gitInfo,\n envInfo,\n symbols,\n ...(terminalWidth && { terminalWidth }), // Only include if defined\n config,\n gitOps,\n });\n\n // Output result\n process.stdout.write(statusline);\n\n } catch (error) {\n console.error('[ERROR]', error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n}\n\n/**\n * Read JSON input from stdin\n */\nasync function readInput(): Promise<ClaudeInput> {\n try {\n const input = readFileSync(0, 'utf-8'); // Read from stdin (fd 0)\n const parsed = JSON.parse(input.trim());\n return parsed as ClaudeInput;\n } catch (error) {\n throw new Error(`Failed to read or parse input: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\n/**\n * Extract directory and model name from Claude input\n */\nfunction extractInputInfo(input: ClaudeInput): { fullDir: string; modelName: string; contextWindow?: ClaudeInput['context_window'] } {\n const fullDir = input.workspace?.current_dir || '';\n const modelName = input.model?.display_name || 'Unknown';\n const contextWindow = input.context_window;\n\n return { fullDir, modelName, contextWindow };\n}\n\n/**\n * Build the complete statusline string\n */\nasync function buildStatusline(params: {\n fullDir: string;\n modelName: string;\n contextWindow?: ClaudeInput['context_window'];\n gitInfo: any;\n envInfo: any;\n symbols: SymbolSet;\n terminalWidth?: number; // Optional - only needed for smart truncation\n config: Config;\n gitOps: GitOperations;\n}): Promise<string> {\n const { fullDir, modelName, contextWindow, gitInfo, envInfo, symbols, terminalWidth, config, gitOps } = params;\n\n // Get project name\n const projectName = fullDir.split('/').pop() || fullDir.split('\\\\').pop() || 'project';\n\n // Build git status string\n let gitStatus = '';\n if (gitInfo) {\n gitStatus = gitOps.formatGitStatus(gitInfo, symbols);\n }\n\n // Build environment context string\n let envContext = '';\n if (envInfo) {\n const envSymbols = getEnvironmentSymbols(symbols);\n const envFormatter = new EnvironmentFormatter(envSymbols);\n envContext = ` ${envFormatter.formatWithIcons(envInfo)}`;\n }\n\n // Build context window usage string\n let contextUsage = '';\n if (contextWindow && !config.noContextWindow) {\n const percentage = calculateContextWindowPercentage(contextWindow);\n if (percentage !== null) {\n contextUsage = ` ${symbols.contextWindow}${percentage}%`;\n }\n }\n\n // Build model string\n const modelString = `${symbols.model}${modelName}${envContext}${contextUsage}`;\n\n // Initial statusline\n let statusline = `${projectName}${gitStatus} ${modelString}`;\n\n // Apply smart truncation if enabled\n if (config.truncate) {\n if (!terminalWidth) {\n console.error('[ERROR] Smart truncation enabled but terminal width not available');\n process.exit(1);\n }\n statusline = applySmartTruncation({\n statusline,\n projectName,\n gitStatus,\n modelString,\n terminalWidth,\n config,\n symbols,\n });\n }\n // No basic truncation - let terminal handle overflow\n\n return statusline;\n}\n\n/**\n * Calculate context window usage percentage\n */\nfunction calculateContextWindowPercentage(contextWindow: NonNullable<ClaudeInput['context_window']>): number | null {\n try {\n const { current_usage, context_window_size } = contextWindow;\n\n if (!context_window_size || context_window_size === 0) {\n return null;\n }\n\n // If current_usage is null or undefined, we cannot calculate the percentage\n // This matches the official Claude Code documentation behavior\n if (!current_usage) {\n return 0;\n }\n\n // Calculate total tokens used (input + cache tokens from current_usage)\n // Note: Output tokens are NOT included in context window calculation\n // per Claude Code documentation\n const totalUsed = current_usage.input_tokens +\n (current_usage.cache_creation_input_tokens || 0) +\n (current_usage.cache_read_input_tokens || 0);\n\n // Calculate percentage\n const percentage = Math.round((totalUsed / context_window_size) * 100);\n\n // Cap at 100% and ensure non-negative\n return Math.max(0, Math.min(100, percentage));\n } catch {\n return 0;\n }\n}\n\n/**\n * Apply smart truncation with branch prioritization\n */\nfunction applySmartTruncation(params: {\n statusline: string;\n projectName: string;\n gitStatus: string;\n modelString: string;\n terminalWidth: number;\n config: Config;\n symbols: SymbolSet;\n}): string {\n const { statusline, projectName, gitStatus, modelString, terminalWidth, config } = params;\n\n // Use 15-char margin for Claude telemetry compatibility\n const maxLen = Math.max(terminalWidth - config.rightMargin, 30);\n const projectGit = `${projectName}${gitStatus}`;\n\n // Check if everything fits\n if (statusline.length <= maxLen) {\n return statusline;\n }\n\n // Check if project + space fits, truncate model part only\n if (projectGit.length + 1 <= maxLen) {\n // Smart truncation with soft-wrapping (default behavior)\n // Allow disabling soft-wrapping with config setting\n if (config.noSoftWrap) {\n // Legacy behavior: simple truncation only\n const modelMaxLen = maxLen - projectGit.length - 1;\n const truncatedModel = truncateText(modelString, modelMaxLen);\n return `${projectGit} ${truncatedModel}`;\n } else {\n // Default: soft-wrap model part\n const modelMaxLen = maxLen - projectGit.length - 1;\n\n // If model string needs wrapping and it starts with model icon,\n // prefer wrapping the entire model string to next line\n const modelIconPattern = /^[*]/;\n if (modelIconPattern.test(modelString) && modelString.length > modelMaxLen) {\n // Wrap entire model to next line\n return `${projectGit}\\n${modelString}`;\n }\n\n const wrappedModel = applySoftWrap(modelString, modelMaxLen);\n return `${projectGit} ${wrappedModel}`;\n }\n }\n\n // Smart truncation of project+git part\n const truncated = smartTruncate(projectName, gitStatus, maxLen, config);\n if (truncated) {\n return truncated;\n }\n\n // Basic fallback\n return truncateText(statusline, maxLen);\n}\n\n/**\n * Apply soft wrapping to text\n */\nfunction applySoftWrap(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n\n // Check if this is a model string (starts with model icon)\n const modelIconPattern = /^[*]/; // Nerd Font or ASCII model icon\n if (modelIconPattern.test(text)) {\n return applySoftWrapToModelString(text, maxLength);\n }\n\n // Find a good break point\n let foundBreak = false;\n\n // Work with actual Unicode characters to avoid splitting multi-byte sequences\n const chars = Array.from(text); // This splits by actual Unicode characters\n let charCount = 0;\n let breakCharIndex = chars.length; // Default to no break\n let lastSpaceIndex = -1;\n\n // Find the best break point by character count\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n\n // Track spaces for potential break points\n if (char === ' ') {\n lastSpaceIndex = i;\n }\n\n // Estimate display width (this is approximate)\n // Most Unicode icons count as 1 display character\n // ASCII characters count as 1\n charCount++;\n\n // Check if we've exceeded the max length\n if (charCount > maxLength) {\n // If we found a space before this point, use it\n if (lastSpaceIndex >= 0) {\n breakCharIndex = lastSpaceIndex;\n } else {\n // No space found, break before current character\n breakCharIndex = i;\n }\n foundBreak = lastSpaceIndex >= 0;\n break;\n }\n }\n\n // If everything fits, return as is\n if (breakCharIndex >= chars.length) {\n return text;\n }\n\n // If no safe break found and we're very close to max_length, just fit without wrapping\n // But only if we're not dealing with a model string that starts with an icon\n const firstChar = chars.length > 0 ? chars[0] : '';\n const startsWithIcon = firstChar && firstChar !== ' ' && Buffer.byteLength(firstChar, 'utf8') > 1;\n if (!foundBreak && maxLength - charCount > -3 && !startsWithIcon) {\n return text;\n }\n\n // Special case: if we're starting with an icon and breaking very early,\n // try to keep at least the icon and 1-2 more characters\n if (startsWithIcon && breakCharIndex <= 2 && maxLength >= 3) {\n // Find a better break point after at least 3 characters total\n for (let i = 2; i < Math.min(chars.length, maxLength); i++) {\n breakCharIndex = i;\n foundBreak = true;\n break;\n }\n }\n\n // Build the strings using character indices\n const firstChars = chars.slice(0, breakCharIndex);\n const secondChars = chars.slice(breakCharIndex);\n\n // Remove leading space from second line if we broke at space\n if (secondChars.length > 0 && secondChars[0] === ' ') {\n secondChars.shift();\n }\n\n // Join characters back into strings\n const firstLine = firstChars.join('');\n const secondLine = secondChars.join('');\n\n // Only wrap if second line has meaningful content\n if (secondLine) {\n return `${firstLine}\\n${secondLine}`;\n } else {\n return firstLine;\n }\n}\n\n/**\n * Apply soft wrapping specifically to model strings, keeping the model icon\n * with the model name and context usage as a unit\n */\nfunction applySoftWrapToModelString(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n\n const chars = Array.from(text);\n\n // Find all space positions to understand the structure\n const spacePositions: number[] = [];\n for (let i = 0; i < chars.length; i++) {\n if (chars[i] === ' ') {\n spacePositions.push(i);\n }\n }\n\n // If we have context usage (marked by context window icon like ⚡︎ or #)\n // The structure is: [icon][model] [env] [context icon][percentage]\n // We want to prefer keeping: [icon][model] [context icon][percentage] together if possible\n\n let contextIconIndex = -1;\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (char === '⚡' || char === '#') {\n contextIconIndex = i;\n break;\n }\n }\n\n // Strategy 1: If we have context usage, try to keep it with the model on the second line\n // This ensures the icon and percentage stay together\n if (contextIconIndex > 0) {\n // Find the space before context usage\n const spaceBeforeContext = chars.findIndex((c, i) => i < contextIconIndex && c === ' ');\n\n if (spaceBeforeContext > 0) {\n // Check if we can fit model + icon + percentage on second line\n const contextPart = chars.slice(spaceBeforeContext + 1).join('');\n if (contextPart.length <= maxLength) {\n // Put model name on first line, context on second line\n const modelPart = chars.slice(0, spaceBeforeContext).join('');\n return `${modelPart}\\n${contextPart}`;\n }\n }\n }\n\n // Strategy 2: Try to find a break point that keeps the model icon with model name\n // Look for first space after model name (if no context or if context doesn't fit)\n if (spacePositions.length > 0) {\n const firstSpaceAfterModel = spacePositions[0];\n if (firstSpaceAfterModel !== undefined && firstSpaceAfterModel > 1) {\n // Check if the model part fits\n const modelPart = chars.slice(0, firstSpaceAfterModel).join('');\n if (modelPart.length <= maxLength) {\n const remainingPart = chars.slice(firstSpaceAfterModel + 1).join('');\n if (remainingPart) {\n return `${modelPart}\\n${remainingPart}`;\n }\n return modelPart;\n }\n }\n }\n\n // Strategy 3: Try to break at a reasonable point that doesn't split the model icon\n // We want to keep at least the icon + first few characters together\n const minKeepLength = Math.min(5, maxLength); // Keep at least 5 chars or maxLength\n if (minKeepLength >= 3 && chars.length > minKeepLength) {\n // Find a character break point after the minimum keep length\n const breakPoint = Math.min(maxLength, chars.length - 1);\n const firstPart = chars.slice(0, breakPoint).join('');\n const secondPart = chars.slice(breakPoint).join('');\n if (secondPart) {\n return `${firstPart}\\n${secondPart}`;\n }\n return firstPart;\n }\n\n // Last resort: simple character-based truncation\n if (maxLength >= 3) {\n const firstPart = chars.slice(0, maxLength).join('');\n const secondPart = chars.slice(maxLength).join('');\n if (secondPart) {\n return `${firstPart}\\n${secondPart}`;\n }\n return firstPart;\n }\n\n return text;\n}\n\n// Run main function if this file is executed directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n main();\n}"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,UAAU,EAAU,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAa,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC1H,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AA6B7E;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,IAAI,CAAC;QACH,qBAAqB;QACrB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAE5B,wBAAwB;QACxB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAChD,MAAM,WAAW,GAAG,IAAI,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAE3D,mCAAmC;QACnC,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAElC,qCAAqC;QACrC,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;QAChC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;YAClD,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,iCAAiC;QACjC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACtE,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;YAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,qBAAqB;QACrB,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,4CAA4C,EAAE,OAAO,CAAC,CAAC;YACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,0DAA0D;QAC1D,MAAM,UAAU,GAAmB;YACjC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC;YAC1B,WAAW,CAAC,kBAAkB,EAAE;YAChC,aAAa,CAAC,MAAM,CAAC;SACtB,CAAC;QAEF,yDAAyD;QACzD,IAAI,aAAiC,CAAC;QACtC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAE5C,0DAA0D;QAC1D,IAAI,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QAED,mBAAmB;QACnB,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC;YACvC,OAAO;YACP,SAAS;YACT,aAAa;YACb,OAAO;YACP,OAAO;YACP,OAAO;YACP,GAAG,CAAC,aAAa,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,0BAA0B;YACnE,MAAM;YACN,MAAM;SACP,CAAC,CAAC;QAEH,gBAAgB;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAEnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,SAAS;IACtB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,yBAAyB;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACxC,OAAO,MAAqB,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,kCAAkC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9G,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,KAAkB;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,EAAE,WAAW,IAAI,EAAE,CAAC;IACnD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,EAAE,YAAY,IAAI,SAAS,CAAC;IACzD,MAAM,aAAa,GAAG,KAAK,CAAC,cAAc,CAAC;IAE3C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,eAAe,CAAC,MAU9B;IACC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE/G,mBAAmB;IACnB,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,SAAS,CAAC;IAEvF,0BAA0B;IAC1B,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,OAAO,EAAE,CAAC;QACZ,SAAS,GAAG,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvD,CAAC;IAED,mCAAmC;IACnC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,YAAY,GAAG,IAAI,oBAAoB,CAAC,UAAU,CAAC,CAAC;QAC1D,UAAU,GAAG,IAAI,YAAY,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;IAC3D,CAAC;IAEC,oCAAoC;IACpC,IAAI,YAAY,GAAG,EAAE,CAAC;IACtB,IAAI,aAAa,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;QAC7C,MAAM,UAAU,GAAG,gCAAgC,CAAC,aAAa,CAAC,CAAC;QACnE,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,YAAY,GAAG,IAAI,OAAO,CAAC,aAAa,GAAG,UAAU,GAAG,CAAC;QAC3D,CAAC;IACH,CAAC;IAEH,qBAAqB;IACrB,MAAM,WAAW,GAAG,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,EAAE,CAAC;IAE/E,qBAAqB;IACrB,IAAI,UAAU,GAAG,GAAG,WAAW,GAAG,SAAS,IAAI,WAAW,EAAE,CAAC;IAE7D,oCAAoC;IACpC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;YACnF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,UAAU,GAAG,oBAAoB,CAAC;YAChC,UAAU;YACV,WAAW;YACX,SAAS;YACT,WAAW;YACX,aAAa;YACb,MAAM;YACN,OAAO;SACR,CAAC,CAAC;IACL,CAAC;IACD,qDAAqD;IAErD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,SAAS,gCAAgC,CAAC,aAAyD;IACjG,IAAI,CAAC;QACH,6DAA6D;QAC7D,IAAI,aAAa,CAAC,eAAe,KAAK,SAAS,IAAI,aAAa,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;YAC1F,sCAAsC;YACtC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC/E,CAAC;QAED,iEAAiE;QACjE,MAAM,EAAE,aAAa,EAAE,mBAAmB,EAAE,GAAG,aAAa,CAAC;QAE7D,IAAI,CAAC,mBAAmB,IAAI,mBAAmB,KAAK,CAAC,EAAE,CAAC;YACtD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,4EAA4E;QAC5E,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,CAAC,CAAC;QACX,CAAC;QAED,wEAAwE;QACxE,qEAAqE;QACrE,gCAAgC;QAChC,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY;YAC3B,CAAC,aAAa,CAAC,2BAA2B,IAAI,CAAC,CAAC;YAChD,CAAC,aAAa,CAAC,uBAAuB,IAAI,CAAC,CAAC,CAAC;QAE9D,uBAAuB;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,mBAAmB,CAAC,GAAG,GAAG,CAAC,CAAC;QAEvE,sCAAsC;QACtC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,MAQ7B;IACC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE1F,wDAAwD;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,GAAG,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,GAAG,WAAW,GAAG,SAAS,EAAE,CAAC;IAEhD,8DAA8D;IAC9D,MAAM,sBAAsB,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;IACjE,IAAI,sBAAsB,IAAI,MAAM,EAAE,CAAC;QACrC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,gFAAgF;IAChF,MAAM,sBAAsB,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;IACjE,IAAI,sBAAsB,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;QACzC,yDAAyD;QACzD,oDAAoD;QACpD,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,0CAA0C;YAC1C,MAAM,WAAW,GAAG,MAAM,GAAG,sBAAsB,GAAG,CAAC,CAAC;YACxD,MAAM,cAAc,GAAG,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YAC9D,OAAO,GAAG,UAAU,IAAI,cAAc,EAAE,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,gCAAgC;YAChC,MAAM,WAAW,GAAG,MAAM,GAAG,sBAAsB,GAAG,CAAC,CAAC;YAExD,gEAAgE;YAChE,uDAAuD;YACvD,MAAM,gBAAgB,GAAG,QAAQ,CAAC;YAClC,MAAM,iBAAiB,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;YAC7D,IAAI,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,iBAAiB,GAAG,WAAW,EAAE,CAAC;gBAC1E,iCAAiC;gBACjC,OAAO,GAAG,UAAU,KAAK,WAAW,EAAE,CAAC;YACzC,CAAC;YAED,MAAM,YAAY,GAAG,aAAa,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YAC7D,OAAO,GAAG,UAAU,IAAI,YAAY,EAAE,CAAC;QACzC,CAAC;IACH,CAAC;IAED,uCAAuC;IACvC,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACxE,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,iBAAiB;IACjB,OAAO,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,IAAY,EAAE,SAAiB;IACpD,6CAA6C;IAC7C,MAAM,YAAY,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,YAAY,IAAI,SAAS,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2DAA2D;IAC3D,MAAM,gBAAgB,GAAG,QAAQ,CAAC,CAAC,gCAAgC;IACnE,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,OAAO,0BAA0B,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACrD,CAAC;IAED,8CAA8C;IAC9C,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,8EAA8E;IAC9E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,2CAA2C;IAC3E,IAAI,mBAAmB,GAAG,CAAC,CAAC;IAC5B,IAAI,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,sBAAsB;IACzD,IAAI,cAAc,GAAG,CAAC,CAAC,CAAC;IAExB,6CAA6C;IAC7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI;YAAE,MAAM;QAEjB,0CAA0C;QAC1C,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,cAAc,GAAG,CAAC,CAAC;QACrB,CAAC;QAED,6CAA6C;QAC7C,MAAM,SAAS,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC9C,mBAAmB,IAAI,SAAS,CAAC;QAEjC,yCAAyC;QACzC,IAAI,mBAAmB,GAAG,SAAS,EAAE,CAAC;YACpC,gDAAgD;YAChD,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;gBACxB,cAAc,GAAG,cAAc,CAAC;YAClC,CAAC;iBAAM,CAAC;gBACN,iDAAiD;gBACjD,cAAc,GAAG,CAAC,CAAC;YACrB,CAAC;YACD,UAAU,GAAG,cAAc,IAAI,CAAC,CAAC;YACjC,MAAM;QACR,CAAC;IACH,CAAC;IAED,mCAAmC;IACnC,IAAI,cAAc,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uFAAuF;IACvF,6EAA6E;IAC7E,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,MAAM,cAAc,GAAG,SAAS,IAAI,SAAS,KAAK,GAAG,IAAI,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAClG,IAAI,CAAC,UAAU,IAAI,SAAS,GAAG,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wEAAwE;IACxE,wDAAwD;IACxD,IAAI,cAAc,IAAI,cAAc,IAAI,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QAC5D,iFAAiF;QACjF,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,IAAI;gBAAE,MAAM;YACjB,SAAS,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,SAAS,IAAI,CAAC,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;gBAC7C,cAAc,GAAG,CAAC,CAAC;gBACnB,UAAU,GAAG,IAAI,CAAC;gBAClB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,4CAA4C;IAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAEhD,6DAA6D;IAC7D,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;QACrD,WAAW,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,oCAAoC;IACpC,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAExC,kDAAkD;IAClD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,GAAG,SAAS,KAAK,UAAU,EAAE,CAAC;IACvC,CAAC;SAAM,CAAC;QACN,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,0BAA0B,CAAC,IAAY,EAAE,SAAiB;IACjE,6CAA6C;IAC7C,MAAM,YAAY,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,YAAY,IAAI,SAAS,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE/B,uDAAuD;IACvD,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACrB,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,mEAAmE;IACnE,2FAA2F;IAE3F,IAAI,gBAAgB,GAAG,CAAC,CAAC,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjC,gBAAgB,GAAG,CAAC,CAAC;YACrB,MAAM;QACR,CAAC;IACH,CAAC;IAED,yFAAyF;IACzF,qDAAqD;IACrD,IAAI,gBAAgB,GAAG,CAAC,EAAE,CAAC;QACzB,sCAAsC;QACtC,MAAM,kBAAkB,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,gBAAgB,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QAExF,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;YAC3B,qFAAqF;YACrF,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjE,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;YAC5D,IAAI,gBAAgB,IAAI,SAAS,EAAE,CAAC;gBAClC,uDAAuD;gBACvD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC9D,OAAO,GAAG,SAAS,KAAK,WAAW,EAAE,CAAC;YACxC,CAAC;QACH,CAAC;IACH,CAAC;IAED,kFAAkF;IAClF,kFAAkF;IAClF,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,oBAAoB,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;QAC/C,IAAI,oBAAoB,KAAK,SAAS,IAAI,oBAAoB,GAAG,CAAC,EAAE,CAAC;YACnE,qDAAqD;YACrD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChE,MAAM,cAAc,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;YACxD,IAAI,cAAc,IAAI,SAAS,EAAE,CAAC;gBAChC,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACrE,IAAI,aAAa,EAAE,CAAC;oBAClB,OAAO,GAAG,SAAS,KAAK,aAAa,EAAE,CAAC;gBAC1C,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,4DAA4D;IAC5D,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC;IAElC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI;YAAE,MAAM;QACjB,MAAM,SAAS,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,YAAY,GAAG,SAAS,GAAG,SAAS,EAAE,CAAC;YACzC,cAAc,GAAG,CAAC,CAAC;YACnB,MAAM;QACR,CAAC;QACD,YAAY,IAAI,SAAS,CAAC;IAC5B,CAAC;IAED,kCAAkC;IAClC,IAAI,cAAc,GAAG,KAAK,CAAC,MAAM,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;QACxD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,GAAG,SAAS,KAAK,UAAU,EAAE,CAAC;QACvC,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,sDAAsD;AACtD,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACpD,IAAI,EAAE,CAAC;AACT,CAAC","sourcesContent":["#!/usr/bin/env node\n\n/**\n * Claude Statusline - TypeScript v2.0\n * Main entry point\n */\n\nimport { readFileSync } from 'fs';\nimport { loadConfig, Config } from './core/config.js';\nimport { validateInput, validateDirectory } from './core/security.js';\nimport { Cache } from './core/cache.js';\nimport { GitOperations } from './git/status.js';\nimport { detectSymbols, getEnvironmentSymbols, SymbolSet } from './ui/symbols.js';\nimport { getTerminalWidth, truncateText, smartTruncate, debugWidthDetection, getStringDisplayWidth } from './ui/width.js';\nimport { EnvironmentDetector, EnvironmentFormatter } from './env/context.js';\n\n/**\n * Claude Code input interface\n */\ninterface ClaudeInput {\n workspace: {\n current_dir: string;\n };\n model: {\n display_name: string;\n };\n context_window?: {\n total_input_tokens: number;\n total_output_tokens: number;\n context_window_size: number;\n // New in Claude Code v2.1.15: Pre-calculated percentages\n used_percentage?: number;\n remaining_percentage?: number;\n // Legacy: Current usage for manual calculation\n current_usage?: {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens: number;\n cache_read_input_tokens: number;\n };\n };\n}\n\n/**\n * Main execution function\n */\nexport async function main(): Promise<void> {\n try {\n // Load configuration\n const config = loadConfig();\n\n // Initialize components\n const cache = new Cache(config);\n const gitOps = new GitOperations(config, cache);\n const envDetector = new EnvironmentDetector(config, cache);\n\n // Debug width detection if enabled\n await debugWidthDetection(config);\n\n // Read and validate input from stdin\n const input = await readInput();\n if (!validateInput(JSON.stringify(input), config)) {\n console.error('[ERROR] Invalid input received');\n process.exit(1);\n }\n\n // Extract information from input\n const { fullDir, modelName, contextWindow } = extractInputInfo(input);\n if (!fullDir || !modelName) {\n console.error('[ERROR] Failed to extract required information from input');\n process.exit(1);\n }\n\n // Validate directory\n const isValidDir = await validateDirectory(fullDir);\n if (!isValidDir) {\n console.error('[ERROR] Invalid or inaccessible directory:', fullDir);\n process.exit(1);\n }\n\n // Get components (run in parallel for better performance)\n const operations: Promise<any>[] = [\n gitOps.getGitInfo(fullDir),\n envDetector.getEnvironmentInfo(),\n detectSymbols(config),\n ];\n\n // Only get terminal width if smart truncation is enabled\n let terminalWidth: number | undefined;\n if (config.truncate) {\n operations.push(getTerminalWidth(config));\n }\n\n const results = await Promise.all(operations);\n const [gitInfo, envInfo, symbols] = results;\n\n // Extract terminal width from results if it was requested\n if (config.truncate && results.length > 3) {\n terminalWidth = results[3];\n }\n\n // Build statusline\n const statusline = await buildStatusline({\n fullDir,\n modelName,\n contextWindow,\n gitInfo,\n envInfo,\n symbols,\n ...(terminalWidth && { terminalWidth }), // Only include if defined\n config,\n gitOps,\n });\n\n // Output result\n process.stdout.write(statusline);\n\n } catch (error) {\n console.error('[ERROR]', error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n}\n\n/**\n * Read JSON input from stdin\n */\nasync function readInput(): Promise<ClaudeInput> {\n try {\n const input = readFileSync(0, 'utf-8'); // Read from stdin (fd 0)\n const parsed = JSON.parse(input.trim());\n return parsed as ClaudeInput;\n } catch (error) {\n throw new Error(`Failed to read or parse input: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\n/**\n * Extract directory and model name from Claude input\n */\nfunction extractInputInfo(input: ClaudeInput): { fullDir: string; modelName: string; contextWindow?: ClaudeInput['context_window'] } {\n const fullDir = input.workspace?.current_dir || '';\n const modelName = input.model?.display_name || 'Unknown';\n const contextWindow = input.context_window;\n\n return { fullDir, modelName, contextWindow };\n}\n\n/**\n * Build the complete statusline string\n */\nasync function buildStatusline(params: {\n fullDir: string;\n modelName: string;\n contextWindow?: ClaudeInput['context_window'];\n gitInfo: any;\n envInfo: any;\n symbols: SymbolSet;\n terminalWidth?: number; // Optional - only needed for smart truncation\n config: Config;\n gitOps: GitOperations;\n}): Promise<string> {\n const { fullDir, modelName, contextWindow, gitInfo, envInfo, symbols, terminalWidth, config, gitOps } = params;\n\n // Get project name\n const projectName = fullDir.split('/').pop() || fullDir.split('\\\\').pop() || 'project';\n\n // Build git status string\n let gitStatus = '';\n if (gitInfo) {\n gitStatus = gitOps.formatGitStatus(gitInfo, symbols);\n }\n\n // Build environment context string\n let envContext = '';\n if (envInfo) {\n const envSymbols = getEnvironmentSymbols(symbols);\n const envFormatter = new EnvironmentFormatter(envSymbols);\n envContext = ` ${envFormatter.formatWithIcons(envInfo)}`;\n }\n\n // Build context window usage string\n let contextUsage = '';\n if (contextWindow && !config.noContextWindow) {\n const percentage = calculateContextWindowPercentage(contextWindow);\n if (percentage !== null) {\n contextUsage = ` ${symbols.contextWindow}${percentage}%`;\n }\n }\n\n // Build model string\n const modelString = `${symbols.model}${modelName}${envContext}${contextUsage}`;\n\n // Initial statusline\n let statusline = `${projectName}${gitStatus} ${modelString}`;\n\n // Apply smart truncation if enabled\n if (config.truncate) {\n if (!terminalWidth) {\n console.error('[ERROR] Smart truncation enabled but terminal width not available');\n process.exit(1);\n }\n statusline = applySmartTruncation({\n statusline,\n projectName,\n gitStatus,\n modelString,\n terminalWidth,\n config,\n symbols,\n });\n }\n // No basic truncation - let terminal handle overflow\n\n return statusline;\n}\n\n/**\n * Calculate context window usage percentage\n *\n * Prioritizes the pre-calculated used_percentage field (Claude Code v2.1.15+)\n * Falls back to manual calculation from current_usage if not available\n */\nfunction calculateContextWindowPercentage(contextWindow: NonNullable<ClaudeInput['context_window']>): number | null {\n try {\n // Try pre-calculated percentage first (Claude Code v2.1.15+)\n if (contextWindow.used_percentage !== undefined && contextWindow.used_percentage !== null) {\n // Cap at 100% and ensure non-negative\n return Math.max(0, Math.min(100, Math.round(contextWindow.used_percentage)));\n }\n\n // Fall back to manual calculation for older Claude Code versions\n const { current_usage, context_window_size } = contextWindow;\n\n if (!context_window_size || context_window_size === 0) {\n return null;\n }\n\n // If current_usage is null or undefined, we cannot calculate the percentage\n if (!current_usage) {\n return 0;\n }\n\n // Calculate total tokens used (input + cache tokens from current_usage)\n // Note: Output tokens are NOT included in context window calculation\n // per Claude Code documentation\n const totalUsed = current_usage.input_tokens +\n (current_usage.cache_creation_input_tokens || 0) +\n (current_usage.cache_read_input_tokens || 0);\n\n // Calculate percentage\n const percentage = Math.round((totalUsed / context_window_size) * 100);\n\n // Cap at 100% and ensure non-negative\n return Math.max(0, Math.min(100, percentage));\n } catch {\n return 0;\n }\n}\n\n/**\n * Apply smart truncation with branch prioritization\n */\nfunction applySmartTruncation(params: {\n statusline: string;\n projectName: string;\n gitStatus: string;\n modelString: string;\n terminalWidth: number;\n config: Config;\n symbols: SymbolSet;\n}): string {\n const { statusline, projectName, gitStatus, modelString, terminalWidth, config } = params;\n\n // Use 15-char margin for Claude telemetry compatibility\n const maxLen = Math.max(terminalWidth - config.rightMargin, 30);\n const projectGit = `${projectName}${gitStatus}`;\n\n // Check if everything fits (using display width for accuracy)\n const statuslineDisplayWidth = getStringDisplayWidth(statusline);\n if (statuslineDisplayWidth <= maxLen) {\n return statusline;\n }\n\n // Check if project + space fits, truncate model part only (using display width)\n const projectGitDisplayWidth = getStringDisplayWidth(projectGit);\n if (projectGitDisplayWidth + 1 <= maxLen) {\n // Smart truncation with soft-wrapping (default behavior)\n // Allow disabling soft-wrapping with config setting\n if (config.noSoftWrap) {\n // Legacy behavior: simple truncation only\n const modelMaxLen = maxLen - projectGitDisplayWidth - 1;\n const truncatedModel = truncateText(modelString, modelMaxLen);\n return `${projectGit} ${truncatedModel}`;\n } else {\n // Default: soft-wrap model part\n const modelMaxLen = maxLen - projectGitDisplayWidth - 1;\n\n // If model string needs wrapping and it starts with model icon,\n // prefer wrapping the entire model string to next line\n const modelIconPattern = /^[*]/;\n const modelDisplayWidth = getStringDisplayWidth(modelString);\n if (modelIconPattern.test(modelString) && modelDisplayWidth > modelMaxLen) {\n // Wrap entire model to next line\n return `${projectGit}\\n${modelString}`;\n }\n\n const wrappedModel = applySoftWrap(modelString, modelMaxLen);\n return `${projectGit} ${wrappedModel}`;\n }\n }\n\n // Smart truncation of project+git part\n const truncated = smartTruncate(projectName, gitStatus, maxLen, config);\n if (truncated) {\n return truncated;\n }\n\n // Basic fallback\n return truncateText(statusline, maxLen);\n}\n\n/**\n * Apply soft wrapping to text\n */\nfunction applySoftWrap(text: string, maxLength: number): string {\n // Use display width for accurate measurement\n const displayWidth = getStringDisplayWidth(text);\n if (displayWidth <= maxLength) {\n return text;\n }\n\n // Check if this is a model string (starts with model icon)\n const modelIconPattern = /^[*]/; // Nerd Font or ASCII model icon\n if (modelIconPattern.test(text)) {\n return applySoftWrapToModelString(text, maxLength);\n }\n\n // Find a good break point using display width\n let foundBreak = false;\n\n // Work with actual Unicode characters to avoid splitting multi-byte sequences\n const chars = Array.from(text); // This splits by actual Unicode characters\n let currentDisplayWidth = 0;\n let breakCharIndex = chars.length; // Default to no break\n let lastSpaceIndex = -1;\n\n // Find the best break point by display width\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (!char) break;\n\n // Track spaces for potential break points\n if (char === ' ') {\n lastSpaceIndex = i;\n }\n\n // Calculate display width for this character\n const charWidth = getStringDisplayWidth(char);\n currentDisplayWidth += charWidth;\n\n // Check if we've exceeded the max length\n if (currentDisplayWidth > maxLength) {\n // If we found a space before this point, use it\n if (lastSpaceIndex >= 0) {\n breakCharIndex = lastSpaceIndex;\n } else {\n // No space found, break before current character\n breakCharIndex = i;\n }\n foundBreak = lastSpaceIndex >= 0;\n break;\n }\n }\n\n // If everything fits, return as is\n if (breakCharIndex >= chars.length) {\n return text;\n }\n\n // If no safe break found and we're very close to max_length, just fit without wrapping\n // But only if we're not dealing with a model string that starts with an icon\n const firstChar = chars.length > 0 ? chars[0] : '';\n const startsWithIcon = firstChar && firstChar !== ' ' && Buffer.byteLength(firstChar, 'utf8') > 1;\n if (!foundBreak && maxLength - currentDisplayWidth > -3 && !startsWithIcon) {\n return text;\n }\n\n // Special case: if we're starting with an icon and breaking very early,\n // try to keep at least the icon and 1-2 more characters\n if (startsWithIcon && breakCharIndex <= 2 && maxLength >= 3) {\n // Find a better break point after at least 3 characters total (by display width)\n let testWidth = 0;\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (!char) break;\n testWidth += getStringDisplayWidth(char);\n if (testWidth >= 3 || testWidth >= maxLength) {\n breakCharIndex = i;\n foundBreak = true;\n break;\n }\n }\n }\n\n // Build the strings using character indices\n const firstChars = chars.slice(0, breakCharIndex);\n const secondChars = chars.slice(breakCharIndex);\n\n // Remove leading space from second line if we broke at space\n if (secondChars.length > 0 && secondChars[0] === ' ') {\n secondChars.shift();\n }\n\n // Join characters back into strings\n const firstLine = firstChars.join('');\n const secondLine = secondChars.join('');\n\n // Only wrap if second line has meaningful content\n if (secondLine) {\n return `${firstLine}\\n${secondLine}`;\n } else {\n return firstLine;\n }\n}\n\n/**\n * Apply soft wrapping specifically to model strings, keeping the model icon\n * with the model name and context usage as a unit\n */\nfunction applySoftWrapToModelString(text: string, maxLength: number): string {\n // Use display width for accurate measurement\n const displayWidth = getStringDisplayWidth(text);\n if (displayWidth <= maxLength) {\n return text;\n }\n\n const chars = Array.from(text);\n\n // Find all space positions to understand the structure\n const spacePositions: number[] = [];\n for (let i = 0; i < chars.length; i++) {\n if (chars[i] === ' ') {\n spacePositions.push(i);\n }\n }\n\n // If we have context usage (marked by context window icon like ⚡︎ or #)\n // The structure is: [icon][model] [env] [context icon][percentage]\n // We want to prefer keeping: [icon][model] [context icon][percentage] together if possible\n\n let contextIconIndex = -1;\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (char === '⚡' || char === '#') {\n contextIconIndex = i;\n break;\n }\n }\n\n // Strategy 1: If we have context usage, try to keep it with the model on the second line\n // This ensures the icon and percentage stay together\n if (contextIconIndex > 0) {\n // Find the space before context usage\n const spaceBeforeContext = chars.findIndex((c, i) => i < contextIconIndex && c === ' ');\n\n if (spaceBeforeContext > 0) {\n // Check if we can fit model + icon + percentage on second line (using display width)\n const contextPart = chars.slice(spaceBeforeContext + 1).join('');\n const contextPartWidth = getStringDisplayWidth(contextPart);\n if (contextPartWidth <= maxLength) {\n // Put model name on first line, context on second line\n const modelPart = chars.slice(0, spaceBeforeContext).join('');\n return `${modelPart}\\n${contextPart}`;\n }\n }\n }\n\n // Strategy 2: Try to find a break point that keeps the model icon with model name\n // Look for first space after model name (if no context or if context doesn't fit)\n if (spacePositions.length > 0) {\n const firstSpaceAfterModel = spacePositions[0];\n if (firstSpaceAfterModel !== undefined && firstSpaceAfterModel > 1) {\n // Check if the model part fits (using display width)\n const modelPart = chars.slice(0, firstSpaceAfterModel).join('');\n const modelPartWidth = getStringDisplayWidth(modelPart);\n if (modelPartWidth <= maxLength) {\n const remainingPart = chars.slice(firstSpaceAfterModel + 1).join('');\n if (remainingPart) {\n return `${modelPart}\\n${remainingPart}`;\n }\n return modelPart;\n }\n }\n }\n\n // Strategy 3: Find break point by display width (not character count)\n // This is the critical fix for the garbled output artifacts\n let currentWidth = 0;\n let breakCharIndex = chars.length;\n\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n if (!char) break;\n const charWidth = getStringDisplayWidth(char);\n if (currentWidth + charWidth > maxLength) {\n breakCharIndex = i;\n break;\n }\n currentWidth += charWidth;\n }\n\n // If we found a valid break point\n if (breakCharIndex < chars.length && breakCharIndex > 0) {\n const firstPart = chars.slice(0, breakCharIndex).join('');\n const secondPart = chars.slice(breakCharIndex).join('');\n if (secondPart) {\n return `${firstPart}\\n${secondPart}`;\n }\n return firstPart;\n }\n\n return text;\n}\n\n// Run main function if this file is executed directly\nif (import.meta.url === `file://${process.argv[1]}`) {\n main();\n}"]}
|