Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ Only update the `Status` field — do not modify any other frontmatter or prompt

<!-- BEGIN:REPO:current-state -->
## Current State
Branch: `main`
In-progress: Nothing. PR #136 (fix #133, sticky zone ANSI width math) open, auto-merge enabled.
Branch: `refactor/adopt-string-width`
In-progress: Nothing. PR #139 (refactor #137, adopt string-width) open, auto-merge enabled.
<!-- END:REPO:current-state -->

<!-- BEGIN:REPO:architecture -->
Expand Down
8 changes: 8 additions & 0 deletions .claude/sessions/2026-03-28.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@
- Decisions: Bundled leftover staged session-end files from the March 27 release session into the code commit since they had not been committed. CI and CodeQL checks in progress; auto-merge will complete when checks pass.
- Next: Await PR #136 auto-merge.
- Violations: None

### 21:30 - refactor/adopt-string-width (#137) Stage 2

- Did: Staged and committed string-width adoption changes, pushed branch, created PR #139 with Closes #137, auto-merge enabled.
- Files: `package.json`, `pnpm-lock.yaml`, `src/StatusLineBuilder.ts`, `src/ansi.ts` (deleted), `src/renderer.ts`, `src/terminal.ts`
- Decisions: Label `enhancement` chosen as adoption of a library is an improvement, not a pure bug fix.
- Next: Await PR #139 auto-merge.
- Violations: None
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@js-joda/core": "^5.7.0",
"@shellicar/mcp-exec": "1.0.0-preview.6",
"sharp": "^0.34.5",
"string-width": "^8.2.0",
"zod": "^4.3.6"
},
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319",
Expand Down
32 changes: 32 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions src/StatusLineBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import stringWidth from 'string-width';

export class StatusLineBuilder {
public output = '';
public visibleLength = 0;

/** Append text that is visible on screen (counts toward line width). */
/** Append text that is visible on screen. */
public text(s: string): this {
this.output += s;
this.visibleLength += s.length;
return this;
}

/** Append an emoji (2 terminal columns wide). */
/** Append an emoji. */
public emoji(s: string): this {
this.output += s;
this.visibleLength += 2;
return this;
}

Expand All @@ -23,6 +22,6 @@ export class StatusLineBuilder {
}

public screenLines(columns: number): number {
return Math.max(1, Math.ceil(this.visibleLength / columns));
return Math.max(1, Math.ceil(stringWidth(this.output) / columns));
}
}
11 changes: 0 additions & 11 deletions src/ansi.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Computes what to display without writing to stdout.
*/

import { stripAnsiLength } from './ansi.js';
import stringWidth from 'string-width';
import type { EditorState } from './editor.js';

const CONTINUATION = ' ';
Expand All @@ -24,11 +24,11 @@ export function prepareEditor(editor: EditorState, prompt: string): EditorRender
}

const cursorPrefix = editor.cursor.row === 0 ? prompt : CONTINUATION;
const cursorCol = stripAnsiLength(cursorPrefix) + editor.cursor.col;
const cursorCol = stringWidth(cursorPrefix) + editor.cursor.col;

let cursorRow = 0;
for (let i = 0; i < editor.cursor.row; i++) {
cursorRow += Math.max(1, Math.ceil(stripAnsiLength(lines[i]) / columns));
cursorRow += Math.max(1, Math.ceil(stringWidth(lines[i]) / columns));
}
cursorRow += Math.floor(cursorCol / columns);

Expand Down
10 changes: 4 additions & 6 deletions src/terminal.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { inspect } from 'node:util';
import { DateTimeFormatter, LocalTime } from '@js-joda/core';
import stringWidth from 'string-width';
import type { AppState } from './AppState.js';
import type { AttachmentStore } from './AttachmentStore.js';
import { stripAnsiLength } from './ansi.js';
import type { CommandMode } from './CommandMode.js';
import type { EditorState } from './editor.js';
import { type EditorRender, prepareEditor } from './renderer.js';
Expand Down Expand Up @@ -270,9 +270,7 @@ export class Terminal {
output += '\n';
}
output += clearLine + line;
// biome-ignore lint/suspicious/noControlCharactersInRegex: matching terminal escape sequences requires \x1b
const visibleLength = line.replace(/\x1b\[[0-9;]*m/g, '').length;
questionScreenLines += Math.max(1, Math.ceil(visibleLength / columns));
questionScreenLines += Math.max(1, Math.ceil(stringWidth(line) / columns));
hasOutput = true;
}

Expand Down Expand Up @@ -313,7 +311,7 @@ export class Terminal {
for (let i = 0; i < this.editorContent.lines.length; i++) {
output += '\n';
output += clearLine + this.editorContent.lines[i];
editorScreenLines += Math.max(1, Math.ceil(stripAnsiLength(this.editorContent.lines[i]) / columns));
editorScreenLines += Math.max(1, Math.ceil(stringWidth(this.editorContent.lines[i]) / columns));
}

// Clear any leftover lines from previous render
Expand All @@ -322,7 +320,7 @@ export class Terminal {
// Position cursor within editor
this.cursorLinesFromBottom = 0;
for (let i = this.editorContent.lines.length - 1; i > this.editorContent.cursorRow; i--) {
this.cursorLinesFromBottom += Math.max(1, Math.ceil(stripAnsiLength(this.editorContent.lines[i]) / columns));
this.cursorLinesFromBottom += Math.max(1, Math.ceil(stringWidth(this.editorContent.lines[i]) / columns));
}
if (this.cursorLinesFromBottom > 0) {
output += cursorUp(this.cursorLinesFromBottom);
Expand Down
Loading