# 🚀 Sync Gemini CLI v0.2.1 - Major Feature Update (#483)

This commit is contained in:
tanzhenxin
2025-09-01 14:48:55 +08:00
committed by GitHub
parent 1610c1586e
commit 2572faf726
292 changed files with 19401 additions and 5941 deletions

View File

@@ -38,7 +38,7 @@ function renderHastNode(
// Handle Element Nodes: Determine color and pass it down, don't wrap
if (node.type === 'element') {
const nodeClasses: string[] =
(node.properties?.className as string[]) || [];
(node.properties?.['className'] as string[]) || [];
let elementColor: string | undefined = undefined;
// Find color defined specifically for this element's class

View File

@@ -22,10 +22,15 @@ interface RenderInlineProps {
}
const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
// Early return for plain text without markdown or URLs
if (!/[*_~`<[https?:]/.test(text)) {
return <Text>{text}</Text>;
}
const nodes: React.ReactNode[] = [];
let lastIndex = 0;
const inlineRegex =
/(\*\*.*?\*\*|\*.*?\*|_.*?_|~~.*?~~|\[.*?\]\(.*?\)|`+.+?`+|<u>.*?<\/u>)/g;
/(\*\*.*?\*\*|\*.*?\*|_.*?_|~~.*?~~|\[.*?\]\(.*?\)|`+.+?`+|<u>.*?<\/u>|https?:\/\/\S+)/g;
let match;
while ((match = inlineRegex.exec(text)) !== null) {
@@ -126,6 +131,12 @@ const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
)}
</Text>
);
} else if (fullMatch.match(/^https?:\/\//)) {
renderedNode = (
<Text key={key} color={Colors.AccentBlue}>
{fullMatch}
</Text>
);
}
} catch (e) {
console.error('Error parsing inline markdown part:', fullMatch, e);

View File

@@ -121,6 +121,10 @@ describe('computeSessionStats', () => {
totalDecisions: { accept: 0, reject: 0, modify: 0 },
byName: {},
},
files: {
totalLinesAdded: 0,
totalLinesRemoved: 0,
},
};
const result = computeSessionStats(metrics);
@@ -137,6 +141,8 @@ describe('computeSessionStats', () => {
agreementRate: 0,
totalPromptTokens: 0,
totalCachedTokens: 0,
totalLinesAdded: 0,
totalLinesRemoved: 0,
});
});
@@ -163,6 +169,10 @@ describe('computeSessionStats', () => {
totalDecisions: { accept: 0, reject: 0, modify: 0 },
byName: {},
},
files: {
totalLinesAdded: 0,
totalLinesRemoved: 0,
},
};
const result = computeSessionStats(metrics);
@@ -197,6 +207,10 @@ describe('computeSessionStats', () => {
totalDecisions: { accept: 0, reject: 0, modify: 0 },
byName: {},
},
files: {
totalLinesAdded: 0,
totalLinesRemoved: 0,
},
};
const result = computeSessionStats(metrics);
@@ -215,6 +229,10 @@ describe('computeSessionStats', () => {
totalDecisions: { accept: 6, reject: 2, modify: 2 },
byName: {},
},
files: {
totalLinesAdded: 0,
totalLinesRemoved: 0,
},
};
const result = computeSessionStats(metrics);
@@ -234,6 +252,10 @@ describe('computeSessionStats', () => {
totalDecisions: { accept: 0, reject: 0, modify: 0 },
byName: {},
},
files: {
totalLinesAdded: 0,
totalLinesRemoved: 0,
},
};
const result = computeSessionStats(metrics);
@@ -244,4 +266,27 @@ describe('computeSessionStats', () => {
expect(result.successRate).toBe(0);
expect(result.agreementRate).toBe(0);
});
it('should correctly include line counts', () => {
const metrics: SessionMetrics = {
models: {},
tools: {
totalCalls: 0,
totalSuccess: 0,
totalFail: 0,
totalDurationMs: 0,
totalDecisions: { accept: 0, reject: 0, modify: 0 },
byName: {},
},
files: {
totalLinesAdded: 42,
totalLinesRemoved: 18,
},
};
const result = computeSessionStats(metrics);
expect(result.totalLinesAdded).toBe(42);
expect(result.totalLinesRemoved).toBe(18);
});
});

View File

@@ -34,7 +34,7 @@ export function calculateCacheHitRate(metrics: ModelMetrics): number {
export const computeSessionStats = (
metrics: SessionMetrics,
): ComputedSessionStats => {
const { models, tools } = metrics;
const { models, tools, files } = metrics;
const totalApiTime = Object.values(models).reduce(
(acc, model) => acc + model.api.totalLatencyMs,
0,
@@ -80,5 +80,7 @@ export const computeSessionStats = (
agreementRate,
totalCachedTokens,
totalPromptTokens,
totalLinesAdded: files.totalLinesAdded,
totalLinesRemoved: files.totalLinesRemoved,
};
};

View File

@@ -17,6 +17,14 @@
*/
export const KITTY_CTRL_C = '[99;5u';
/**
* Kitty keyboard protocol keycodes
*/
export const KITTY_KEYCODE_ENTER = 13;
export const KITTY_KEYCODE_NUMPAD_ENTER = 57414;
export const KITTY_KEYCODE_TAB = 9;
export const KITTY_KEYCODE_BACKSPACE = 127;
/**
* Timing constants for terminal interactions
*/

View File

@@ -52,22 +52,24 @@ type SupportedTerminal = 'vscode' | 'cursor' | 'windsurf';
// Terminal detection
async function detectTerminal(): Promise<SupportedTerminal | null> {
const termProgram = process.env.TERM_PROGRAM;
const termProgram = process.env['TERM_PROGRAM'];
// Check VS Code and its forks - check forks first to avoid false positives
// Check for Cursor-specific indicators
if (
process.env.CURSOR_TRACE_ID ||
process.env.VSCODE_GIT_ASKPASS_MAIN?.toLowerCase().includes('cursor')
process.env['CURSOR_TRACE_ID'] ||
process.env['VSCODE_GIT_ASKPASS_MAIN']?.toLowerCase().includes('cursor')
) {
return 'cursor';
}
// Check for Windsurf-specific indicators
if (process.env.VSCODE_GIT_ASKPASS_MAIN?.toLowerCase().includes('windsurf')) {
if (
process.env['VSCODE_GIT_ASKPASS_MAIN']?.toLowerCase().includes('windsurf')
) {
return 'windsurf';
}
// Check VS Code last since forks may also set VSCODE env vars
if (termProgram === 'vscode' || process.env.VSCODE_GIT_IPC_HANDLE) {
if (termProgram === 'vscode' || process.env['VSCODE_GIT_IPC_HANDLE']) {
return 'vscode';
}
@@ -118,10 +120,10 @@ function getVSCodeStyleConfigDir(appName: string): string | null {
'User',
);
} else if (platform === 'win32') {
if (!process.env.APPDATA) {
if (!process.env['APPDATA']) {
return null;
}
return path.join(process.env.APPDATA, appName, 'User');
return path.join(process.env['APPDATA'], appName, 'User');
} else {
return path.join(os.homedir(), '.config', appName, 'User');
}

View File

@@ -22,7 +22,7 @@ describe('checkForUpdates', () => {
vi.useFakeTimers();
vi.resetAllMocks();
// Clear DEV environment variable before each test
delete process.env.DEV;
delete process.env['DEV'];
});
afterEach(() => {
@@ -31,7 +31,7 @@ describe('checkForUpdates', () => {
});
it('should return null when running from source (DEV=true)', async () => {
process.env.DEV = 'true';
process.env['DEV'] = 'true';
getPackageJson.mockResolvedValue({
name: 'test-package',
version: '1.0.0',

View File

@@ -41,7 +41,7 @@ function getBestAvailableUpdate(
export async function checkForUpdates(): Promise<UpdateObject | null> {
try {
// Skip update check when running from source (development mode)
if (process.env.DEV === 'true') {
if (process.env['DEV'] === 'true') {
return null;
}
const packageJson = await getPackageJson();