@@ -502,51 +502,50 @@ export function activate(context: vscode.ExtensionContext): void {
502502 vscode . window . showWarningMessage ( 'No Claude Code sessions found for the current workspace.' ) ;
503503 return ;
504504 }
505+
506+ // Pick from current workspace sessions sorted by size
505507 const trimJsonls = fs . readdirSync ( claudeProjectDir )
506508 . filter ( ( f ) => f . endsWith ( '.jsonl' ) )
507509 . map ( ( f ) => path . join ( claudeProjectDir , f ) ) ;
508510
509511 if ( trimJsonls . length === 0 ) {
510- vscode . window . showInformationMessage ( 'No sessions found.' ) ;
512+ vscode . window . showInformationMessage ( '未找到会话文件 / No sessions found.' ) ;
511513 return ;
512514 }
513515
514- // Sort by size descending, let user pick
515516 const sizedFiles = trimJsonls
516517 . map ( ( f ) => ( { f, sizeMb : fs . statSync ( f ) . size / 1024 / 1024 } ) )
517518 . sort ( ( a , b ) => b . sizeMb - a . sizeMb ) ;
518519
519520 const picks = sizedFiles . map ( ( { f, sizeMb } ) => ( {
520- label : `$(file) ${ path . basename ( f , '.jsonl' ) } ` ,
521- description : `${ sizeMb . toFixed ( 1 ) } MB` ,
521+ label : `$(file) ${ getSessionDisplayTitle ( f ) } ` ,
522+ description : `${ sizeMb . toFixed ( 1 ) } MB [ ${ path . basename ( f , '.jsonl' ) . slice ( 0 , 8 ) } ] ` ,
522523 value : f ,
523524 large : sizeMb > 10 ,
524525 } ) ) ;
525526
526527 const picked = await vscode . window . showQuickPick ( picks , {
527- placeHolder : 'Select session to trim (original will be backed up)' ,
528- title : 'Trim Large Session' ,
528+ placeHolder : '选择要裁剪的会话(原文件将自动备份)/ Select session to trim (original will be backed up)' ,
529+ title : '裁剪大型会话 / Trim Large Session' ,
529530 } ) ;
530531 if ( ! picked ) return ;
531532
532533 const keepInput = await vscode . window . showInputBox ( {
533- prompt : 'How many recent messages to keep? (each user/assistant turn counts as 1)' ,
534+ prompt : '保留最近多少条消息?每条用户/助手发言计为1条 / How many recent messages to keep? (each user/assistant turn counts as 1)' ,
534535 value : '400' ,
535- validateInput : ( v ) => ( isNaN ( Number ( v ) ) || Number ( v ) < 10 ) ? 'Enter a number ≥ 10' : undefined ,
536+ validateInput : ( v ) => ( isNaN ( Number ( v ) ) || Number ( v ) < 10 ) ? '请输入 ≥ 10 的数字 / Enter a number ≥ 10' : undefined ,
536537 } ) ;
537538 if ( ! keepInput ) return ;
538539
539540 try {
540541 const r = trimSession ( picked . value , Number ( keepInput ) ) ;
541542 vscode . window . showInformationMessage (
542- `✓ Trimmed session from ${ r . originalLines } → ${ r . keptLines } lines ` +
543- `(${ r . sizeMb . toFixed ( 1 ) } MB → ${ r . trimmedSizeMb . toFixed ( 1 ) } MB).\n\n` +
544- `Original backed up to ${ path . basename ( picked . value ) } .bak-*\n\n` +
545- `Close and reopen your CC chat to reload the trimmed session.` ,
543+ `✓ 裁剪完成 / Trimmed: ${ r . originalLines } → ${ r . keptLines } 行 (${ r . sizeMb . toFixed ( 1 ) } MB → ${ r . trimmedSizeMb . toFixed ( 1 ) } MB)\n\n` +
544+ `备份位置 / Backup: ${ r . backupPath } \n\n关闭并重新打开 CC 对话面板以生效 / Close and reopen CC chat to reload.` ,
546545 { modal : true }
547546 ) ;
548547 } catch ( err ) {
549- vscode . window . showErrorMessage ( `Trim failed: ${ err } ` ) ;
548+ vscode . window . showErrorMessage ( `裁剪失败 / Trim failed: ${ err } ` ) ;
550549 }
551550 return ;
552551 }
@@ -574,49 +573,50 @@ export function activate(context: vscode.ExtensionContext): void {
574573 vscode . commands . registerCommand ( 'claudeCodeExporter.trimSession' , async ( item ?: SessionItem ) => {
575574 let jsonlPath : string | undefined ;
576575
577- if ( item ?. resourceUri ?. fsPath ?. endsWith ( '.jsonl' ) ) {
578- jsonlPath = item . resourceUri . fsPath ;
576+ // If invoked from a tree item, use its path directly — no picker needed
577+ if ( item ?. sessionFilePath ?. endsWith ( '.jsonl' ) ) {
578+ jsonlPath = item . sessionFilePath ;
579579 } else if ( claudeProjectDir ) {
580- // Pick from current workspace sessions sorted by size
580+ // Invoked from command palette: let user pick
581581 const jsonls = fs . readdirSync ( claudeProjectDir )
582582 . filter ( ( f ) => f . endsWith ( '.jsonl' ) )
583583 . map ( ( f ) => ( { f : path . join ( claudeProjectDir , f ) , sizeMb : fs . statSync ( path . join ( claudeProjectDir , f ) ) . size / 1024 / 1024 } ) )
584584 . sort ( ( a , b ) => b . sizeMb - a . sizeMb ) ;
585585
586586 const picked = await vscode . window . showQuickPick (
587587 jsonls . map ( ( { f, sizeMb } ) => ( {
588- label : `$(file) ${ path . basename ( f , '.jsonl' ) } ` ,
589- description : `${ sizeMb . toFixed ( 1 ) } MB` ,
588+ label : `$(file) ${ getSessionDisplayTitle ( f ) } ` ,
589+ description : `${ sizeMb . toFixed ( 1 ) } MB [ ${ path . basename ( f , '.jsonl' ) . slice ( 0 , 8 ) } ] ` ,
590590 value : f ,
591591 } ) ) ,
592- { placeHolder : 'Select session to trim' , title : 'Trim Large Session' }
592+ { placeHolder : '选择要裁剪的会话 / Select session to trim' , title : '裁剪大型会话 / Trim Large Session' }
593593 ) ;
594594 if ( ! picked ) return ;
595595 jsonlPath = picked . value ;
596596 }
597597
598598 if ( ! jsonlPath ) {
599- vscode . window . showWarningMessage ( 'No session selected.' ) ;
599+ vscode . window . showWarningMessage ( '未选择会话 / No session selected.' ) ;
600600 return ;
601601 }
602602
603603 const sizeMb = fs . statSync ( jsonlPath ) . size / 1024 / 1024 ;
604604 const keepInput = await vscode . window . showInputBox ( {
605- prompt : `Session is ${ sizeMb . toFixed ( 1 ) } MB. How many recent messages to keep?` ,
605+ prompt : `当前会话 ${ sizeMb . toFixed ( 1 ) } MB,保留最近多少条消息? / Session is ${ sizeMb . toFixed ( 1 ) } MB — how many recent messages to keep?` ,
606606 value : '400' ,
607- validateInput : ( v ) => ( isNaN ( Number ( v ) ) || Number ( v ) < 10 ) ? 'Enter a number ≥ 10' : undefined ,
607+ validateInput : ( v ) => ( isNaN ( Number ( v ) ) || Number ( v ) < 10 ) ? '请输入 ≥ 10 的数字 / Enter a number ≥ 10' : undefined ,
608608 } ) ;
609609 if ( ! keepInput ) return ;
610610
611611 try {
612612 const r = trimSession ( jsonlPath , Number ( keepInput ) ) ;
613613 vscode . window . showInformationMessage (
614- `✓ Trimmed: ${ r . originalLines } → ${ r . keptLines } lines (${ r . sizeMb . toFixed ( 1 ) } MB → ${ r . trimmedSizeMb . toFixed ( 1 ) } MB). \n\n` +
615- `Original backed up. Close and reopen your CC chat to reload.` ,
614+ `✓ 裁剪完成 / Trimmed: ${ r . originalLines } → ${ r . keptLines } 行 / lines (${ r . sizeMb . toFixed ( 1 ) } MB → ${ r . trimmedSizeMb . toFixed ( 1 ) } MB)\n\n` +
615+ `备份位置 / Backup: ${ r . backupPath } \n\n关闭并重新打开 CC 对话面板以生效 / Close and reopen CC chat panel to reload.` ,
616616 { modal : true }
617617 ) ;
618618 } catch ( err ) {
619- vscode . window . showErrorMessage ( `Trim failed: ${ err } ` ) ;
619+ vscode . window . showErrorMessage ( `裁剪失败 / Trim failed: ${ err } ` ) ;
620620 }
621621 } ) ,
622622
@@ -1304,10 +1304,37 @@ function repairThinkingSignatures(jsonlPath: string): number {
13041304// Backs up original to .jsonl.bak-TIMESTAMP, then overwrites in-place so the
13051305// session ID and CC entry point are unchanged.
13061306// Returns { keptLines, originalLines, sizeMb, trimmedSizeMb }
1307+
1308+ function getSessionDisplayTitle ( jsonlPath : string ) : string {
1309+ try {
1310+ const content = fs . readFileSync ( jsonlPath , 'utf8' ) ;
1311+ const lines = content . split ( '\n' ) . filter ( ( l ) => l . trim ( ) ) ;
1312+ for ( const line of lines ) {
1313+ try {
1314+ const obj = JSON . parse ( line ) as Record < string , unknown > ;
1315+ if ( obj . customTitle && typeof obj . customTitle === 'string' ) return obj . customTitle ;
1316+ const msg = obj . message as Record < string , unknown > | undefined ;
1317+ if ( msg ?. role === 'user' ) {
1318+ const blocks = msg . content ;
1319+ if ( Array . isArray ( blocks ) ) {
1320+ for ( const b of blocks ) {
1321+ if ( ( b as Record < string , unknown > ) . type === 'text' ) {
1322+ const t = ( ( b as Record < string , unknown > ) . text as string || '' ) . trim ( ) ;
1323+ if ( t ) return t . slice ( 0 , 50 ) ;
1324+ }
1325+ }
1326+ }
1327+ if ( typeof blocks === 'string' && blocks . trim ( ) ) return blocks . trim ( ) . slice ( 0 , 50 ) ;
1328+ }
1329+ } catch { /* skip bad line */ }
1330+ }
1331+ } catch { /* unreadable */ }
1332+ return path . basename ( jsonlPath , '.jsonl' ) . slice ( 0 , 8 ) ;
1333+ }
13071334function trimSession (
13081335 jsonlPath : string ,
13091336 keepRecentMessages : number = 400
1310- ) : { keptLines : number ; originalLines : number ; sizeMb : number ; trimmedSizeMb : number } {
1337+ ) : { keptLines : number ; originalLines : number ; sizeMb : number ; trimmedSizeMb : number ; backupPath : string } {
13111338 const raw = fs . readFileSync ( jsonlPath , 'utf8' ) ;
13121339 const lines = raw . split ( '\n' ) ;
13131340 const originalLines = lines . filter ( ( l ) => l . trim ( ) ) . length ;
@@ -1359,6 +1386,6 @@ function trimSession(
13591386 fs . writeFileSync ( jsonlPath , outLines . join ( '\n' ) + '\n' , 'utf8' ) ;
13601387 const trimmedSizeMb = fs . statSync ( jsonlPath ) . size / 1024 / 1024 ;
13611388
1362- return { keptLines : outLines . length , originalLines, sizeMb, trimmedSizeMb } ;
1389+ return { keptLines : outLines . length , originalLines, sizeMb, trimmedSizeMb, backupPath } ;
13631390}
13641391
0 commit comments