• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

agentic-dev-library / thumbcode / 22032840233

15 Feb 2026 08:54AM UTC coverage: 51.666% (+0.09%) from 51.576%
22032840233

Pull #139

github

web-flow
Merge c7b24c305 into 7abe8b2f3
Pull Request #139: ⚡ Optimize FileTree rendering performance using Zustand store

1334 of 2951 branches covered (45.21%)

Branch coverage included in aggregate %.

17 of 26 new or added lines in 3 files covered. (65.38%)

13 existing lines in 2 files now uncovered.

2201 of 3891 relevant lines covered (56.57%)

9.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.18
/src/components/code/TreeNode.tsx
1
/**
2
 * TreeNode Component
3
 *
4
 * Renders a single node (file or folder) in the FileTree,
5
 * including the icon, label, status indicator, and recursive children.
6
 */
7

8
import React, { memo, useContext } from 'react';
9
import { useStore } from 'zustand';
10
import {
11
  ChevronDownIcon,
12
  FileCodeIcon,
13
  FileConfigIcon,
14
  FileDataIcon,
15
  FileDocIcon,
16
  FileIcon,
17
  FileMediaIcon,
18
  FileStyleIcon,
19
  FileWebIcon,
20
  FolderIcon,
21
  FolderOpenIcon,
22
  type IconColor,
23
} from '@/components/icons';
24
import { Text } from '@/components/ui';
25
import type { FileNode } from './FileTree';
26
import { FileTreeContext } from './FileTreeContext';
27

28
type FileIconComponent = React.FC<{ size?: number; color?: IconColor; turbulence?: number }>;
29

30
interface FileIconInfo {
31
  Icon: FileIconComponent;
32
  color: IconColor;
33
}
34

35
function getFileIconInfo(name: string): FileIconInfo {
36
  const ext = name.split('.').pop()?.toLowerCase();
22✔
37
  const iconMap: Record<string, FileIconInfo> = {
22✔
38
    ts: { Icon: FileCodeIcon, color: 'teal' },
39
    tsx: { Icon: FileCodeIcon, color: 'teal' },
40
    js: { Icon: FileCodeIcon, color: 'gold' },
41
    jsx: { Icon: FileCodeIcon, color: 'gold' },
42
    json: { Icon: FileDataIcon, color: 'gold' },
43
    md: { Icon: FileDocIcon, color: 'warmGray' },
44
    css: { Icon: FileStyleIcon, color: 'coral' },
45
    scss: { Icon: FileStyleIcon, color: 'coral' },
46
    html: { Icon: FileWebIcon, color: 'teal' },
47
    png: { Icon: FileMediaIcon, color: 'coral' },
48
    jpg: { Icon: FileMediaIcon, color: 'coral' },
49
    jpeg: { Icon: FileMediaIcon, color: 'coral' },
50
    svg: { Icon: FileMediaIcon, color: 'coral' },
51
    gif: { Icon: FileMediaIcon, color: 'coral' },
52
    git: { Icon: FileConfigIcon, color: 'warmGray' },
53
    env: { Icon: FileConfigIcon, color: 'warmGray' },
54
    lock: { Icon: FileConfigIcon, color: 'warmGray' },
55
    yaml: { Icon: FileConfigIcon, color: 'warmGray' },
56
    yml: { Icon: FileConfigIcon, color: 'warmGray' },
57
  };
58
  return iconMap[ext || ''] || { Icon: FileIcon, color: 'warmGray' };
22!
59
}
60

61
function getStatusColor(node: FileNode): string {
62
  if (node.added) return 'text-teal-400';
30✔
63
  if (node.modified) return 'text-gold-400';
22✔
64
  if (node.deleted) return 'text-coral-400';
19!
65
  return '';
19✔
66
}
67

68
function getStatusText(node: FileNode): string {
69
  if (node.added) return 'added';
30✔
70
  if (node.modified) return 'modified';
22✔
71
  if (node.deleted) return 'deleted';
19!
72
  return '';
19✔
73
}
74

75
function getStatusLabel(node: FileNode): string {
76
  if (node.added) return 'A';
11✔
77
  if (node.modified) return 'M';
3!
UNCOV
78
  if (node.deleted) return 'D';
×
79
  return '';
×
80
}
81

82
function getAccessibilityHint(
83
  isFolder: boolean,
84
  hasChildren: boolean,
85
  isExpanded: boolean
86
): string {
87
  if (!isFolder) return 'Open file';
30✔
88
  if (!hasChildren) return 'Empty folder';
8!
89
  return isExpanded ? 'Collapse folder' : 'Expand folder';
8✔
90
}
91

92
function FileTreeNodeRow({
93
  node,
94
  depth,
95
  isSelected,
96
  isExpanded,
97
  isFolder,
98
  hasChildren,
99
  statusColor,
100
  onPress,
101
  accessibilityLabel,
102
  accessibilityHint,
103
}: {
104
  node: FileNode;
105
  depth: number;
106
  isSelected: boolean;
107
  isExpanded: boolean;
108
  isFolder: boolean;
109
  hasChildren: boolean;
110
  statusColor: string;
111
  onPress: () => void;
112
  accessibilityLabel: string;
113
  accessibilityHint: string;
114
}) {
115
  let iconInfo: FileIconInfo;
116
  if (isFolder) {
30✔
117
    const Icon = isExpanded ? FolderOpenIcon : FolderIcon;
8✔
118
    iconInfo = { Icon, color: 'gold' };
8✔
119
  } else {
120
    iconInfo = getFileIconInfo(node.name);
22✔
121
  }
122

123
  const rowClass = isSelected ? 'bg-teal-600/20' : 'active:bg-neutral-700';
30!
124
  const textClass = isSelected ? 'text-teal-300' : 'text-neutral-200';
30!
125

126
  return (
30✔
127
    <button
128
      type="button"
129
      onClick={onPress}
130
      aria-label={accessibilityLabel}
131
      aria-description={accessibilityHint}
132
      className={`flex-row items-center py-1.5 px-2 ${rowClass}`}
133
      style={{ paddingLeft: 8 + depth * 16 }}
134
    >
135
      <div className="w-4 mr-1 items-center justify-center">
136
        {isFolder && hasChildren ? (
68✔
137
          <div style={{ transform: `rotate(${isExpanded ? '0deg' : '-90deg'})` }}>
8✔
138
            <ChevronDownIcon size={12} color="warmGray" turbulence={0.12} />
139
          </div>
140
        ) : null}
141
      </div>
142
      <div className="mr-2">
143
        <iconInfo.Icon size={16} color={iconInfo.color} turbulence={0.15} />
144
      </div>
145
      <Text className={`font-mono text-sm flex-1 ${textClass} ${statusColor}`} numberOfLines={1}>
146
        {node.name}
147
      </Text>
148
    </button>
149
  );
150
}
151

152
export interface TreeNodeProps {
153
  node: FileNode;
154
  depth: number;
155
}
156

157
export const TreeNode = memo(function TreeNode({
1✔
158
  node,
159
  depth,
160
}: Readonly<TreeNodeProps>) {
161
  const context = useContext(FileTreeContext);
30✔
162
  if (!context) {
30!
NEW
163
    throw new Error('TreeNode must be used within a FileTreeContext.Provider');
×
164
  }
165
  const { store, onSelectFile, showStatus } = context;
30✔
166

167
  const isExpanded = useStore(store, (s) => s.expandedPaths.has(node.path));
120✔
168
  const isSelected = useStore(store, (s) => s.selectedPath === node.path);
120✔
169
  const isFolder = node.type === 'folder';
30✔
170
  const hasChildren = Boolean(node.children?.length);
30✔
171
  const statusColor = getStatusColor(node);
30✔
172

173
  const handlePress = () => {
30✔
174
    if (isFolder && hasChildren) {
1!
NEW
UNCOV
175
      store.getState().toggleExpanded(node.path);
×
176
    } else if (!isFolder) {
1!
177
      onSelectFile?.(node.path);
1✔
178
    }
179
  };
180

181
  const shouldShowStatus = showStatus && Boolean(node.added || node.modified || node.deleted);
30✔
182

183
  const accessibilityLabel = [node.name, isFolder ? 'folder' : 'file', getStatusText(node)]
30✔
184
    .filter(Boolean)
185
    .join(', ');
186

187
  return (
30✔
188
    <div>
189
      <div className="flex-row items-center">
190
        <div className="flex-1">
191
          <FileTreeNodeRow
192
            node={node}
193
            depth={depth}
194
            isSelected={isSelected}
195
            isExpanded={isExpanded}
196
            isFolder={isFolder}
197
            hasChildren={hasChildren}
198
            statusColor={statusColor}
199
            onPress={handlePress}
200
            accessibilityLabel={accessibilityLabel}
201
            accessibilityHint={getAccessibilityHint(isFolder, hasChildren, isExpanded)}
202
          />
203
        </div>
204
        {shouldShowStatus && (
41✔
205
          <Text className={`text-xs ${statusColor} mr-2`}>{getStatusLabel(node)}</Text>
206
        )}
207
      </div>
208

209
      {isFolder && isExpanded && hasChildren && (
44✔
210
        <div>
211
          {node.children?.map((child) => (
212
            <TreeNode
6✔
213
              key={child.path}
214
              node={child}
215
              depth={depth + 1}
216
            />
217
          ))}
218
        </div>
219
      )}
220
    </div>
221
  );
222
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc