(width: number)
| 376 | } |
| 377 | |
| 378 | render(width: number): string[] { |
| 379 | // Calculate visible window |
| 380 | const prompt = "> "; |
| 381 | const availableWidth = width - prompt.length; |
| 382 | |
| 383 | if (availableWidth <= 0) { |
| 384 | return [prompt]; |
| 385 | } |
| 386 | |
| 387 | let visibleText = ""; |
| 388 | let cursorDisplay = this.cursor; |
| 389 | const totalWidth = visibleWidth(this.value); |
| 390 | |
| 391 | if (totalWidth < availableWidth) { |
| 392 | // Everything fits (leave room for cursor at end) |
| 393 | visibleText = this.value; |
| 394 | } else { |
| 395 | // Need horizontal scrolling |
| 396 | // Reserve one column for cursor if it's at the end |
| 397 | const scrollWidth = this.cursor === this.value.length ? availableWidth - 1 : availableWidth; |
| 398 | const cursorCol = visibleWidth(this.value.slice(0, this.cursor)); |
| 399 | |
| 400 | if (scrollWidth > 0) { |
| 401 | const halfWidth = Math.floor(scrollWidth / 2); |
| 402 | let startCol = 0; |
| 403 | |
| 404 | if (cursorCol < halfWidth) { |
| 405 | // Cursor near start |
| 406 | startCol = 0; |
| 407 | } else if (cursorCol > totalWidth - halfWidth) { |
| 408 | // Cursor near end |
| 409 | startCol = Math.max(0, totalWidth - scrollWidth); |
| 410 | } else { |
| 411 | // Cursor in middle |
| 412 | startCol = Math.max(0, cursorCol - halfWidth); |
| 413 | } |
| 414 | |
| 415 | visibleText = sliceByColumn(this.value, startCol, scrollWidth, true); |
| 416 | const beforeCursor = sliceByColumn(this.value, startCol, Math.max(0, cursorCol - startCol), true); |
| 417 | cursorDisplay = beforeCursor.length; |
| 418 | } else { |
| 419 | visibleText = ""; |
| 420 | cursorDisplay = 0; |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | // Build line with fake cursor |
| 425 | // Insert cursor character at cursor position |
| 426 | const graphemes = [...segmenter.segment(visibleText.slice(cursorDisplay))]; |
| 427 | const cursorGrapheme = graphemes[0]; |
| 428 | |
| 429 | const beforeCursor = visibleText.slice(0, cursorDisplay); |
| 430 | const atCursor = cursorGrapheme?.segment ?? " "; // Character at cursor, or space if at end |
| 431 | const afterCursor = visibleText.slice(cursorDisplay + atCursor.length); |
| 432 | |
| 433 | // Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning) |
| 434 | const marker = this.focused ? CURSOR_MARKER : ""; |
| 435 |
nothing calls this directly
no test coverage detected