Skip to content

Feature: Show special icon for deleted files in PR diff view #66

@areibman

Description

@areibman

Feature Request

Display a special icon/indicator for deleted files in the PR diff view, similar to how newly added files are shown with a special icon.

Current Behavior

  • New/added files may have a special icon or indicator
  • Deleted files don't have a clear visual distinction
  • Users need to read the diff header or stats to know a file was deleted

Expected Behavior

  • Deleted files should have a distinctive icon (e.g., 🗑️, ❌, or a red minus icon)
  • Visual consistency with how added files are displayed
  • Clear visual hierarchy: Added (green/+), Modified (yellow/~), Deleted (red/-)

Implementation

File Status Icons

interface FileStatus {
  type: 'added' | 'modified' | 'deleted' | 'renamed' | 'copied';
  icon: string;
  className: string;
  label: string;
}

const FILE_STATUS_CONFIG: Record<string, FileStatus> = {
  added: {
    type: 'added',
    icon: '✨', // or '➕' or custom SVG
    className: 'file-added',
    label: 'New file'
  },
  modified: {
    type: 'modified',
    icon: '📝', // or '✏️' or custom SVG
    className: 'file-modified',
    label: 'Modified'
  },
  deleted: {
    type: 'deleted',
    icon: '🗑️', // or '❌' or '➖' or custom SVG
    className: 'file-deleted',
    label: 'Deleted'
  },
  renamed: {
    type: 'renamed',
    icon: '📋', // or '➡️' or custom SVG
    className: 'file-renamed',
    label: 'Renamed'
  },
  copied: {
    type: 'copied',
    icon: '📄', // or custom SVG
    className: 'file-copied',
    label: 'Copied'
  }
};

Component Implementation

function FileListItem({ file }) {
  const status = getFileStatus(file);
  
  return (
    <div className={`file-item ${status.className}`}>
      <span className="file-status-icon" title={status.label}>
        {status.icon}
      </span>
      
      <span className="file-path">
        {file.status === 'renamed' ? (
          <>
            <span className="old-name">{file.previous_filename}</span>
            <span className="rename-arrow"></span>
            <span className="new-name">{file.filename}</span>
          </>
        ) : (
          <span className={file.status === 'deleted' ? 'strikethrough' : ''}>
            {file.filename}
          </span>
        )}
      </span>
      
      <span className="file-stats">
        {file.status !== 'deleted' && (
          <span className="additions">+{file.additions}</span>
        )}
        {file.status !== 'added' && (
          <span className="deletions">-{file.deletions}</span>
        )}
      </span>
    </div>
  );
}

function getFileStatus(file): FileStatus {
  if (file.status === 'removed') {
    return FILE_STATUS_CONFIG.deleted;
  }
  if (file.status === 'added') {
    return FILE_STATUS_CONFIG.added;
  }
  if (file.status === 'renamed') {
    return FILE_STATUS_CONFIG.renamed;
  }
  if (file.status === 'copied') {
    return FILE_STATUS_CONFIG.copied;
  }
  return FILE_STATUS_CONFIG.modified;
}

Styling

.file-item {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  border-radius: 4px;
  transition: background-color 0.2s;
}

.file-item:hover {
  background-color: var(--hover-bg);
}

.file-status-icon {
  margin-right: 8px;
  font-size: 16px;
  flex-shrink: 0;
}

/* File status specific styles */
.file-added {
  border-left: 3px solid var(--color-success);
}

.file-added .file-status-icon {
  color: var(--color-success);
}

.file-modified {
  border-left: 3px solid var(--color-warning);
}

.file-modified .file-status-icon {
  color: var(--color-warning);
}

.file-deleted {
  border-left: 3px solid var(--color-danger);
  opacity: 0.8;
}

.file-deleted .file-status-icon {
  color: var(--color-danger);
}

.file-deleted .file-path {
  text-decoration: line-through;
  opacity: 0.7;
}

.file-renamed {
  border-left: 3px solid var(--color-info);
}

.file-renamed .file-status-icon {
  color: var(--color-info);
}

.rename-arrow {
  margin: 0 8px;
  color: var(--text-secondary);
}

/* Alternative: Use background colors */
.file-added-alt {
  background-color: rgba(46, 160, 67, 0.1);
}

.file-deleted-alt {
  background-color: rgba(248, 81, 73, 0.1);
}

.file-modified-alt {
  background-color: rgba(250, 179, 0, 0.1);
}

Alternative Icon Approaches

1. SVG Icons

const FileStatusIcons = {
  added: (
    <svg className="file-icon added" viewBox="0 0 16 16">
      <path d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z"/>
    </svg>
  ),
  deleted: (
    <svg className="file-icon deleted" viewBox="0 0 16 16">
      <path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
      <path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1z"/>
    </svg>
  ),
  modified: (
    <svg className="file-icon modified" viewBox="0 0 16 16">
      <path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
    </svg>
  )
};

2. Text Indicators

const FileStatusBadges = {
  added: <span className="badge badge-success">NEW</span>,
  deleted: <span className="badge badge-danger">DELETED</span>,
  modified: <span className="badge badge-warning">MODIFIED</span>,
  renamed: <span className="badge badge-info">RENAMED</span>
};

Diff Header Enhancement

function DiffFileHeader({ file }) {
  const status = getFileStatus(file);
  
  return (
    <div className={`diff-header ${status.className}`}>
      <div className="diff-header-content">
        <span className="file-status-indicator">
          {status.icon}
        </span>
        
        <span className="file-name">
          {file.status === 'deleted' ? (
            <del>{file.filename}</del>
          ) : (
            file.filename
          )}
        </span>
        
        {file.status === 'deleted' && (
          <span className="deletion-notice">
            This file was deleted
          </span>
        )}
      </div>
      
      {file.status === 'deleted' && (
        <div className="deleted-file-content">
          <p className="deletion-info">
            {file.deletions} lines removed
          </p>
          <button onClick={() => viewDeletedContent(file)}>
            View deleted content
          </button>
        </div>
      )}
    </div>
  );
}

Accessibility Considerations

function AccessibleFileStatus({ file }) {
  const status = getFileStatus(file);
  
  return (
    <>
      <span 
        className="file-status-icon"
        role="img"
        aria-label={status.label}
        title={status.label}
      >
        {status.icon}
      </span>
      <span className="sr-only">
        {status.label}: {file.filename}
      </span>
    </>
  );
}

Visual Examples

File List View:
┌─────────────────────────────────────────┐
│ ✨ src/newFeature.js        +150    -0  │ ← Added (green)
│ 📝 src/existing.js           +20   -15  │ ← Modified (yellow)
│ 🗑️ src/deprecated.js          -0  -200  │ ← Deleted (red)
│ 📋 src/old.js → src/new.js   +10    -5  │ ← Renamed (blue)
└─────────────────────────────────────────┘

Compact View:
[✨] newFile.js
[📝] modified.js
[🗑️] deleted.js (strikethrough)
[📋] renamed.js

Benefits

  • Visual Clarity: Instantly see which files were deleted
  • Consistency: Similar UX pattern to added files
  • Improved Scanning: Easier to spot deleted files in long PR lists
  • Accessibility: Clear indicators for all users
  • Reduced Cognitive Load: No need to parse diff stats

Acceptance Criteria

  • Deleted files show a distinctive icon
  • Icon is visually consistent with added file icon style
  • Deleted file names may have strikethrough styling
  • Icon has proper accessibility labels
  • Works in all file list views (tree view, flat view)
  • Shows in PR file list
  • Shows in diff headers
  • Color scheme works in both light and dark themes
  • Tooltip shows "Deleted" on hover
  • Visual hierarchy is clear (added vs modified vs deleted)

Future Enhancements

  • Animated transitions when files change status
  • Filter to show only deleted files
  • Bulk restore deleted files option
  • Show deletion reason if available from commit message

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions