|
112 | 112 | // Measures multiple sample dates to find the maximum width needed. |
113 | 113 | const dateColumnWidth = $derived(measureDateColumnWidth(formatDateTime)) |
114 | 114 |
|
| 115 | + /** Extracts display extension from a filename (no dot). Matches Rust sorting logic: |
| 116 | + * dotfiles without secondary dot → empty, no extension → empty, otherwise last segment. */ |
| 117 | + function getDisplayExtension(name: string, isDirectory: boolean): string { |
| 118 | + if (isDirectory) return '' |
| 119 | + if (name.startsWith('.') && !name.slice(1).includes('.')) return '' |
| 120 | + const dotPos = name.lastIndexOf('.') |
| 121 | + if (dotPos <= 0 || dotPos === name.length - 1) return '' |
| 122 | + return name.slice(dotPos + 1) |
| 123 | + } |
| 124 | +
|
115 | 125 | // Drive index state — show spinner while scanning OR aggregating (sizes aren't ready until aggregation finishes) |
116 | 126 | const indexing = $derived(isScanning() || isAggregating()) |
117 | 127 |
|
|
357 | 367 |
|
358 | 368 | <div class="full-list-container" class:is-focused={isFocused} class:is-compact={isCompact}> |
359 | 369 | <!-- Header row with sortable columns (outside scroll container for correct height calculation) --> |
360 | | - <div class="header-row" style="grid-template-columns: 16px 1fr 115px {dateColumnWidth}px;"> |
| 370 | + <div class="header-row" style="grid-template-columns: 16px 1fr 60px 115px {dateColumnWidth}px;"> |
361 | 371 | <span class="header-icon"></span> |
362 | 372 | <SortableHeader |
363 | 373 | column="name" |
|
366 | 376 | currentSortOrder={sortOrder} |
367 | 377 | onClick={onSortChange ?? (() => {})} |
368 | 378 | /> |
| 379 | + <SortableHeader |
| 380 | + column="extension" |
| 381 | + label="Ext" |
| 382 | + currentSortColumn={sortBy} |
| 383 | + currentSortOrder={sortOrder} |
| 384 | + onClick={onSortChange ?? (() => {})} |
| 385 | + /> |
369 | 386 | <SortableHeader |
370 | 387 | column="size" |
371 | 388 | label="Size" |
|
405 | 422 | class:is-under-cursor={globalIndex === cursorIndex} |
406 | 423 | class:is-selected={selectedIndices.has(globalIndex)} |
407 | 424 | data-drop-target-path={file.isDirectory && file.name !== '..' ? file.path : undefined} |
408 | | - style="height: {rowHeight}px; grid-template-columns: 16px 1fr 115px {dateColumnWidth}px;" |
| 425 | + style="height: {rowHeight}px; grid-template-columns: 16px 1fr 60px 115px {dateColumnWidth}px;" |
409 | 426 | onmousedown={(e: MouseEvent) => { |
410 | 427 | handleMouseDown(e, globalIndex) |
411 | 428 | }} |
|
438 | 455 | {:else} |
439 | 456 | <span class="col-name">{file.name}</span> |
440 | 457 | {/if} |
| 458 | + <span class="col-ext">{getDisplayExtension(file.name, file.isDirectory)}</span> |
441 | 459 | <span |
442 | 460 | class="col-size" |
443 | 461 | use:tooltip={file.isDirectory |
|
559 | 577 | white-space: nowrap; |
560 | 578 | } |
561 | 579 |
|
| 580 | + .col-ext { |
| 581 | + overflow: hidden; |
| 582 | + text-overflow: ellipsis; |
| 583 | + white-space: nowrap; |
| 584 | + font-size: var(--font-size-sm); |
| 585 | + color: var(--color-text-secondary); |
| 586 | + } |
| 587 | +
|
562 | 588 | .col-size { |
563 | 589 | text-align: right; |
564 | 590 | font-size: var(--font-size-sm); |
|
590 | 616 | color: var(--color-selection-fg); |
591 | 617 | } |
592 | 618 |
|
| 619 | + .file-entry.is-selected .col-ext { |
| 620 | + color: var(--color-selection-fg); |
| 621 | + } |
| 622 | +
|
593 | 623 | .file-entry.is-selected .col-date { |
594 | 624 | color: var(--color-selection-fg); |
595 | 625 | } |
|
624 | 654 | color: var(--color-selection-fg); |
625 | 655 | } |
626 | 656 |
|
| 657 | + .full-list-container.is-focused .file-entry.is-under-cursor.is-selected .col-ext { |
| 658 | + color: var(--color-selection-fg); |
| 659 | + } |
| 660 | +
|
627 | 661 | .full-list-container.is-focused .file-entry.is-under-cursor.is-selected .col-date { |
628 | 662 | color: var(--color-selection-fg); |
629 | 663 | } |
|
0 commit comments