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
2 changes: 1 addition & 1 deletion .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Only update the `Status` field — do not modify any other frontmatter or prompt
<!-- BEGIN:REPO:current-state -->
## Current State
Branch: `main`
In-progress: PR #131 (fix #107, empty piped stdin hang) open, auto-merge enabled.
In-progress: Nothing. PR #136 (fix #133, sticky zone ANSI width math) open, auto-merge enabled.
<!-- END:REPO:current-state -->

<!-- BEGIN:REPO:architecture -->
Expand Down
8 changes: 8 additions & 0 deletions .claude/sessions/2026-03-27.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@
- Decisions: None.
- Next: PR #131 pending CI and auto-merge.
- Violations: None

### 18:25 - release/1.0.0-alpha.71

- Did: Added `verify-commit-signatures` workflow (copied from mcp-exec repo), consolidated duplicate `### Fixed` sections in CHANGELOG, added missing #122 fix entry, bumped version to 1.0.0-alpha.71, created PR #132 (auto-merge), published release after merge, confirmed 1.0.0-alpha.71 live on npm.
- Files: `.github/workflows/verify-commit-signatures.yml`, `CHANGELOG.md`, `package.json`
- Decisions: Used `feature/version-1.0.0-alpha.71` branch per shellicar-oss convention. Prompt status update to "in-progress" timed out (fleet repo outside working dir) but was resolved by the system. Release is a pre-release (contains hyphen) so `--prerelease` flag was used.
- Next: Nothing. Release complete.
- Violations: None
9 changes: 9 additions & 0 deletions .claude/sessions/2026-03-28.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Session Log: 2026-03-28

### 20:23 - fix/sticky-zone-width-math (#133) Stage 2

- Did: Staged and committed code changes from Stage 1 (src/ansi.ts, src/renderer.ts, src/terminal.ts plus leftover session-end files from March 27 release session), pushed branch, created PR #136 with Closes #133, auto-merge enabled.
- Files: `src/ansi.ts`, `src/renderer.ts`, `src/terminal.ts`, `.claude/CLAUDE.md`, `.claude/sessions/2026-03-27.md`
- 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
11 changes: 11 additions & 0 deletions src/ansi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// biome-ignore lint/suspicious/noControlCharactersInRegex: matching terminal escape sequences requires \x1b
const ANSI_PATTERN = /\x1b\[[0-9;]*m/g;

/**
* Strips ANSI SGR escape codes and returns the visible character length.
* Handles colour, bold, inverse, and reset sequences only.
* Does not account for multi-width characters (emoji, CJK).
*/
export function stripAnsiLength(str: string): number {
return str.replace(ANSI_PATTERN, '').length;
}
6 changes: 4 additions & 2 deletions src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Computes what to display without writing to stdout.
*/

import { stripAnsiLength } from './ansi.js';
import type { EditorState } from './editor.js';

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

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

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

return { lines, cursorRow, cursorCol };
}
5 changes: 3 additions & 2 deletions src/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { inspect } from 'node:util';
import { DateTimeFormatter, LocalTime } from '@js-joda/core';
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 @@ -312,7 +313,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(this.editorContent.lines[i].length / columns));
editorScreenLines += Math.max(1, Math.ceil(stripAnsiLength(this.editorContent.lines[i]) / columns));
}

// Clear any leftover lines from previous render
Expand All @@ -321,7 +322,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(this.editorContent.lines[i].length / columns));
this.cursorLinesFromBottom += Math.max(1, Math.ceil(stripAnsiLength(this.editorContent.lines[i]) / columns));
}
if (this.cursorLinesFromBottom > 0) {
output += cursorUp(this.cursorLinesFromBottom);
Expand Down
Loading