@deepseekdev/coder 1.0.74 → 1.0.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1823,11 +1823,19 @@ export class UnifiedUIRenderer extends EventEmitter {
1823
1823
  this.inBracketedPaste = false;
1824
1824
  this.pasteBuffer = '';
1825
1825
  this.pasteBufferOverflow = false;
1826
+ this.cancelPlainPasteCapture();
1827
+ this.forceNextRender = true;
1828
+ this.renderPrompt();
1826
1829
  return;
1827
1830
  }
1828
1831
  sanitized = validation.sanitized;
1829
1832
  }
1830
- let content = sanitized.replace(/\r\n?/g, '\n');
1833
+ const content = sanitized.replace(/\r\n?/g, '\n');
1834
+ // Clear bracketed paste state BEFORE render to allow prompt redraw
1835
+ this.inBracketedPaste = false;
1836
+ this.pasteBuffer = '';
1837
+ this.pasteBufferOverflow = false;
1838
+ this.cancelPlainPasteCapture();
1831
1839
  if (content) {
1832
1840
  // Insert paste content directly into buffer (supports multi-line)
1833
1841
  // Insert at cursor position
@@ -1840,10 +1848,10 @@ export class UnifiedUIRenderer extends EventEmitter {
1840
1848
  this.renderPrompt();
1841
1849
  this.emitInputChange();
1842
1850
  }
1843
- this.inBracketedPaste = false;
1844
- this.pasteBuffer = '';
1845
- this.pasteBufferOverflow = false;
1846
- this.cancelPlainPasteCapture();
1851
+ else {
1852
+ this.forceNextRender = true;
1853
+ this.renderPrompt();
1854
+ }
1847
1855
  }
1848
1856
  handlePlainPaste(str, key) {
1849
1857
  // Fallback paste capture when bracketed paste isn't supported
@@ -2824,14 +2832,31 @@ export class UnifiedUIRenderer extends EventEmitter {
2824
2832
  return false;
2825
2833
  }
2826
2834
  looksLikeDiffOutput(content) {
2827
- const lines = content.split('\n');
2835
+ const lines = content.split('\n').map(line => this.stripAnsi(line).trim());
2836
+ let hasHeader = false;
2837
+ let hasHunk = false;
2838
+ let hasFileHeader = false;
2839
+ let hasFileNew = false;
2840
+ let hasChange = false;
2828
2841
  for (const line of lines) {
2829
- const stripped = this.stripAnsi(line);
2830
- if (this.isDiffLine(stripped)) {
2831
- return true;
2832
- }
2833
- }
2834
- return false;
2842
+ if (!line)
2843
+ continue;
2844
+ if (line.startsWith('diff --git'))
2845
+ hasHeader = true;
2846
+ if (line.startsWith('@@'))
2847
+ hasHunk = true;
2848
+ if (line.startsWith('--- '))
2849
+ hasFileHeader = true;
2850
+ if (line.startsWith('+++ '))
2851
+ hasFileNew = true;
2852
+ if (/^\d+\s*[+-]/.test(line))
2853
+ hasChange = true;
2854
+ if ((line.startsWith('+') && !line.startsWith('+++')) || (line.startsWith('-') && !line.startsWith('---'))) {
2855
+ hasChange = true;
2856
+ }
2857
+ }
2858
+ const hasFilePair = hasFileHeader && hasFileNew;
2859
+ return hasChange && (hasHeader || hasHunk || hasFilePair);
2835
2860
  }
2836
2861
  isDiffLine(line) {
2837
2862
  const trimmed = line.trim();
@@ -2846,9 +2871,6 @@ export class UnifiedUIRenderer extends EventEmitter {
2846
2871
  if (/^[+-]/.test(trimmed)) {
2847
2872
  return true;
2848
2873
  }
2849
- if (/^[+-]\s*L\d+\s*\|/.test(trimmed)) {
2850
- return true;
2851
- }
2852
2874
  return false;
2853
2875
  }
2854
2876
  /**
@@ -3179,15 +3201,18 @@ export class UnifiedUIRenderer extends EventEmitter {
3179
3201
  if (!trimmed) {
3180
3202
  return null;
3181
3203
  }
3182
- const parenMatch = trimmed.match(/^([A-Za-z0-9_.-]+)\s*\(/);
3204
+ const parenMatch = trimmed.match(/^[^\w\[]*([A-Za-z0-9_.-]+)\s*\(/);
3183
3205
  if (parenMatch) {
3184
3206
  return parenMatch[1] || null;
3185
3207
  }
3186
- const bracketMatch = trimmed.match(/^\[([A-Za-z0-9_.-]+)\]/);
3208
+ const bracketMatch = trimmed.match(/^[^\w\[]*\[([A-Za-z0-9_.-]+)\]/);
3187
3209
  if (bracketMatch) {
3188
3210
  return bracketMatch[1] || null;
3189
3211
  }
3190
- if (/^running:\s*\$/i.test(trimmed)) {
3212
+ if (/^[^\w\[]*running:\s*\$/i.test(trimmed)) {
3213
+ return 'bash';
3214
+ }
3215
+ if (/^[^\w\[]*done:\s*$/i.test(trimmed)) {
3191
3216
  return 'bash';
3192
3217
  }
3193
3218
  return null;
@@ -3370,6 +3395,10 @@ export class UnifiedUIRenderer extends EventEmitter {
3370
3395
  if (this.looksLikeEditOutput(sanitized)) {
3371
3396
  return this.formatEditResultWithDiff(sanitized);
3372
3397
  }
3398
+ const diffPreview = this.extractUnifiedDiffPreview(sanitized);
3399
+ if (diffPreview) {
3400
+ return this.formatUnifiedDiffPreview(sanitized, diffPreview);
3401
+ }
3373
3402
  // Parse common result patterns for summary
3374
3403
  const lineMatch = sanitized.match(/(\d+)\s*lines?/i);
3375
3404
  const fileMatch = sanitized.match(/(\d+)\s*(?:files?|matches?)/i);
@@ -3511,7 +3540,7 @@ export class UnifiedUIRenderer extends EventEmitter {
3511
3540
  }
3512
3541
  // Store for expansion
3513
3542
  this.collapsedToolResults.push({
3514
- toolName: 'edit',
3543
+ toolName: this.lastToolName || 'edit',
3515
3544
  content,
3516
3545
  summary: `${action} ${fileName}`,
3517
3546
  timestamp: Date.now(),
@@ -3572,7 +3601,7 @@ export class UnifiedUIRenderer extends EventEmitter {
3572
3601
  }
3573
3602
  // Store for expansion with enhanced metadata
3574
3603
  this.collapsedToolResults.push({
3575
- toolName: 'edit',
3604
+ toolName: this.lastToolName || 'edit',
3576
3605
  content,
3577
3606
  summary: `Updated ${fileName} (-${oldLen}, +${newLen})`,
3578
3607
  timestamp: Date.now(),
@@ -3590,6 +3619,110 @@ export class UnifiedUIRenderer extends EventEmitter {
3590
3619
  ` ${theme.ui.muted('(ctrl+o to expand)')}` : '';
3591
3620
  return lines.join('\n') + expandHint + '\n';
3592
3621
  }
3622
+ extractUnifiedDiffPreview(content, maxLines = 4) {
3623
+ const lines = content.split('\n');
3624
+ let startIndex = -1;
3625
+ let hasHeader = false;
3626
+ let hasHunk = false;
3627
+ let hasChange = false;
3628
+ for (let i = 0; i < lines.length; i++) {
3629
+ const line = this.stripAnsi(lines[i] ?? '');
3630
+ if (!line)
3631
+ continue;
3632
+ if (startIndex === -1 && (line.startsWith('diff --git') || line.startsWith('@@') || line.startsWith('+++') || line.startsWith('---'))) {
3633
+ startIndex = i;
3634
+ }
3635
+ if (line.startsWith('diff --git'))
3636
+ hasHeader = true;
3637
+ if (line.startsWith('@@'))
3638
+ hasHunk = true;
3639
+ if ((line.startsWith('+') && !line.startsWith('+++')) || (line.startsWith('-') && !line.startsWith('---'))) {
3640
+ hasChange = true;
3641
+ }
3642
+ }
3643
+ if (!hasChange || !(hasHeader || hasHunk)) {
3644
+ return null;
3645
+ }
3646
+ if (startIndex < 0) {
3647
+ startIndex = lines.findIndex((line) => {
3648
+ const stripped = this.stripAnsi(line);
3649
+ return stripped.startsWith('diff --git') || stripped.startsWith('@@');
3650
+ });
3651
+ if (startIndex < 0)
3652
+ startIndex = 0;
3653
+ }
3654
+ const diffLines = lines.slice(startIndex).filter((line) => this.isUnifiedDiffLine(line));
3655
+ if (diffLines.length === 0) {
3656
+ return null;
3657
+ }
3658
+ return {
3659
+ previewLines: diffLines.slice(0, maxLines),
3660
+ totalLines: diffLines.length,
3661
+ };
3662
+ }
3663
+ isUnifiedDiffLine(line) {
3664
+ const stripped = this.stripAnsi(line).trimEnd();
3665
+ if (!stripped)
3666
+ return false;
3667
+ return (stripped.startsWith('diff --git') ||
3668
+ stripped.startsWith('index ') ||
3669
+ stripped.startsWith('@@') ||
3670
+ stripped.startsWith('+++') ||
3671
+ stripped.startsWith('---') ||
3672
+ stripped.startsWith('+') ||
3673
+ stripped.startsWith('-'));
3674
+ }
3675
+ formatUnifiedDiffPreview(content, preview) {
3676
+ const indent = ' ';
3677
+ const toolLabel = this.lastToolName ? `${this.lastToolName} diff` : 'Diff output';
3678
+ const result = [];
3679
+ result.push(`${indent}${theme.ui.muted('⎿')} ${theme.success('✓')} ${theme.info(toolLabel)}`);
3680
+ for (const line of preview.previewLines) {
3681
+ const colored = this.colorUnifiedDiffLine(line);
3682
+ const trimmed = colored.trim();
3683
+ if (trimmed) {
3684
+ result.push(`${indent}${theme.ui.muted('│')} ${trimmed}`);
3685
+ }
3686
+ }
3687
+ if (preview.totalLines > preview.previewLines.length) {
3688
+ result.push(`${indent}${theme.ui.muted('│')} ${theme.ui.muted(`... +${preview.totalLines - preview.previewLines.length} more`)}`);
3689
+ }
3690
+ this.collapsedToolResults.push({
3691
+ toolName: this.lastToolName || 'tool',
3692
+ content,
3693
+ summary: toolLabel,
3694
+ timestamp: Date.now(),
3695
+ });
3696
+ if (this.collapsedToolResults.length > this.maxCollapsedResults) {
3697
+ this.collapsedToolResults.shift();
3698
+ }
3699
+ const expandHint = preview.totalLines > preview.previewLines.length
3700
+ ? ` ${theme.ui.muted('(ctrl+o to expand)')}`
3701
+ : '';
3702
+ return result.join('\n') + expandHint + '\n';
3703
+ }
3704
+ colorUnifiedDiffLine(line) {
3705
+ if (!line)
3706
+ return line;
3707
+ if (line.includes('\x1b['))
3708
+ return line;
3709
+ const leadingMatch = line.match(/^\s*/);
3710
+ const leading = leadingMatch ? leadingMatch[0] : '';
3711
+ const body = line.slice(leading.length);
3712
+ if (body.startsWith('diff --git') || body.startsWith('index ') || body.startsWith('+++') || body.startsWith('---')) {
3713
+ return leading + theme.diff.meta(body);
3714
+ }
3715
+ if (body.startsWith('@@')) {
3716
+ return leading + theme.diff.hunk(body);
3717
+ }
3718
+ if (body.startsWith('+')) {
3719
+ return leading + theme.diff.added(body);
3720
+ }
3721
+ if (body.startsWith('-')) {
3722
+ return leading + theme.diff.removed(body);
3723
+ }
3724
+ return line;
3725
+ }
3593
3726
  /**
3594
3727
  * Format a compact response with bullet on first line
3595
3728
  */