Merge tag 'v0.3.0' into chore/sync-gemini-cli-v0.3.0

This commit is contained in:
mingholy.lmh
2025-09-10 21:01:40 +08:00
583 changed files with 30160 additions and 10770 deletions

View File

@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import React, { useEffect, useState, useRef } from 'react';
import type React from 'react';
import { useEffect, useState, useRef } from 'react';
import { Text, Box } from 'ink';
import { Colors } from '../../colors.js';
import { useKeypress } from '../../hooks/useKeypress.js';
@@ -64,7 +65,6 @@ export function RadioButtonSelect<T>({
const [scrollOffset, setScrollOffset] = useState(0);
const [numberInput, setNumberInput] = useState('');
const numberInputTimer = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
const newScrollOffset = Math.max(
0,
@@ -194,7 +194,10 @@ export function RadioButtonSelect<T>({
return (
<Box key={item.label} alignItems="center">
<Box minWidth={2} flexShrink={0}>
<Text color={isSelected ? Colors.AccentGreen : Colors.Foreground}>
<Text
color={isSelected ? Colors.AccentGreen : Colors.Foreground}
aria-hidden
>
{isSelected ? '●' : ' '}
</Text>
</Box>
@@ -202,6 +205,7 @@ export function RadioButtonSelect<T>({
marginRight={1}
flexShrink={0}
minWidth={itemNumberText.length}
aria-state={{ checked: isSelected }}
>
<Text color={numberColor}>{itemNumberText}</Text>
</Box>

View File

@@ -7,15 +7,17 @@
import { describe, it, expect, beforeEach } from 'vitest';
import stripAnsi from 'strip-ansi';
import { renderHook, act } from '@testing-library/react';
import {
useTextBuffer,
import type {
Viewport,
TextBuffer,
TextBufferState,
TextBufferAction,
} from './text-buffer.js';
import {
useTextBuffer,
offsetToLogicalPos,
logicalPosToOffset,
textBufferReducer,
TextBufferState,
TextBufferAction,
findWordEndInLine,
findNextWordStartInLine,
isWordCharStrict,
@@ -893,7 +895,7 @@ describe('useTextBuffer', () => {
expect(getBufferState(result).cursor).toEqual([0, 2]);
});
it('should handle inserts that contain delete characters ', () => {
it('should handle inserts that contain delete characters', () => {
const { result } = renderHook(() =>
useTextBuffer({
initialText: 'abcde',
@@ -911,7 +913,7 @@ describe('useTextBuffer', () => {
expect(getBufferState(result).cursor).toEqual([0, 2]);
});
it('should handle inserts with a mix of regular and delete characters ', () => {
it('should handle inserts with a mix of regular and delete characters', () => {
const { result } = renderHook(() =>
useTextBuffer({
initialText: 'abcde',

View File

@@ -4,17 +4,21 @@
* SPDX-License-Identifier: Apache-2.0
*/
import stripAnsi from 'strip-ansi';
import { stripVTControlCharacters } from 'util';
import { spawnSync } from 'child_process';
import fs from 'fs';
import os from 'os';
import pathMod from 'path';
import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import pathMod from 'node:path';
import { useState, useCallback, useEffect, useMemo, useReducer } from 'react';
import stringWidth from 'string-width';
import { unescapePath } from '@qwen-code/qwen-code-core';
import { toCodePoints, cpLen, cpSlice } from '../../utils/textUtils.js';
import { handleVimAction, VimAction } from './vim-buffer-actions.js';
import {
toCodePoints,
cpLen,
cpSlice,
stripUnsafeCharacters,
} from '../../utils/textUtils.js';
import type { VimAction } from './vim-buffer-actions.js';
import { handleVimAction } from './vim-buffer-actions.js';
export type Direction =
| 'left'
@@ -494,51 +498,6 @@ export const replaceRangeInternal = (
};
};
/**
* Strip characters that can break terminal rendering.
*
* Uses Node.js built-in stripVTControlCharacters to handle VT sequences,
* then filters remaining control characters that can disrupt display.
*
* Characters stripped:
* - ANSI escape sequences (via strip-ansi)
* - VT control sequences (via Node.js util.stripVTControlCharacters)
* - C0 control chars (0x00-0x1F) except CR/LF which are handled elsewhere
* - C1 control chars (0x80-0x9F) that can cause display issues
*
* Characters preserved:
* - All printable Unicode including emojis
* - DEL (0x7F) - handled functionally by applyOperations, not a display issue
* - CR/LF (0x0D/0x0A) - needed for line breaks
*/
function stripUnsafeCharacters(str: string): string {
const strippedAnsi = stripAnsi(str);
const strippedVT = stripVTControlCharacters(strippedAnsi);
return toCodePoints(strippedVT)
.filter((char) => {
const code = char.codePointAt(0);
if (code === undefined) return false;
// Preserve CR/LF for line handling
if (code === 0x0a || code === 0x0d) return true;
// Remove C0 control chars (except CR/LF) that can break display
// Examples: BELL(0x07) makes noise, BS(0x08) moves cursor, VT(0x0B), FF(0x0C)
if (code >= 0x00 && code <= 0x1f) return false;
// Remove C1 control chars (0x80-0x9F) - legacy 8-bit control codes
if (code >= 0x80 && code <= 0x9f) return false;
// Preserve DEL (0x7F) - it's handled functionally by applyOperations as backspace
// and doesn't cause rendering issues when displayed
// Preserve all other characters including Unicode/emojis
return true;
})
.join('');
}
export interface Viewport {
height: number;
width: number;

View File

@@ -4,9 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type { TextBufferState, TextBufferAction } from './text-buffer.js';
import {
TextBufferState,
TextBufferAction,
getLineRangeOffsets,
getPositionFromOffsets,
replaceRangeInternal,