Files · Items: one icon map, file icon, file row and file grid

The crate had two maps from a file's name to its icon, in chat and in the
file tree, and they disagreed. One map now lives in primitives, over the
Lucide file-* icons, and chat, agent, the file tree and the directory
listing use it. FileIcon draws it for a file or a folder. FileItem is a
file's row, a ListItem with its size and when it changed. FileList is the
directory listing. FileGrid lays tiles in the order given, with a cursor
seeded from the owner's pick: arrows by tile and by row, Home, End, Enter
and a double press to open. The count of what fits a width has one home,
layout's columns_for, and fit builds on it.
This commit is contained in:
ZacharyZhang-NY committed 2026-09-26 19:40:10 -04:00
1 parent e75171c995
commit a846e10847
36 files changed
+1024 -77

No files matched your search

+11 -2
View File
@@ -9,7 +9,7 @@ use jiff::Timestamp;
use super::{ListItem, SelectableList};
use crate::{
navigation::{Breadcrumb, Crumb},
primitives::{Icon, IconName},
primitives::{Icon, IconName, file_icon},
theme::{ActiveTheme, IconSize, TextSize},
typography::{format, tabular},
};
@@ -46,6 +46,15 @@ impl DirEntry {
pub fn is_folder(&self) -> bool {
self.size.is_none()
}
/// A file's size in bytes; none for a folder.
pub fn size(&self) -> Option<u64> {
self.size
}
pub fn modified(&self) -> Timestamp {
self.modified
}
}
/// A column a listing sorts by.
@@ -223,7 +232,7 @@ impl RenderOnce for DirectoryListing {
Icon::new(if entry.is_folder() {
IconName::Folder
} else {
IconName::File
file_icon(&entry.name)
})
.size(IconSize::Md)
.color(colors.fg_muted),
+2 -15
View File
@@ -9,7 +9,7 @@ use super::{Tree, TreeNode};
use crate::{
data_display::Tone,
forms::{SearchInput, TextInput},
primitives::IconName,
primitives::{IconName, file_icon},
theme::ControlSize,
};
@@ -57,19 +57,6 @@ impl GitStatus {
}
}
/// The icon a file's name suggests.
fn icon_for(name: &str) -> IconName {
let kind = name.rsplit_once('.').map(|(_, kind)| kind.to_lowercase());
match kind.as_deref() {
Some("rs" | "ts" | "tsx" | "js" | "py" | "go" | "swift" | "c" | "h" | "sh") => {
IconName::Code
}
Some("png" | "jpg" | "jpeg" | "gif" | "svg" | "webp") => IconName::Image,
Some("toml" | "json" | "yaml" | "yml" | "lock") => IconName::SlidersHorizontal,
_ => IconName::FileText,
}
}
/// A folder of the tree being built: its folders and its files, each by name.
#[derive(Default)]
struct Folder {
@@ -141,7 +128,7 @@ fn nodes(
files.sort_by_key(|name| name.to_lowercase());
for name in files {
let path = format!("{prefix}{name}");
let node = TreeNode::new(path.clone(), name.clone()).icon(icon_for(name));
let node = TreeNode::new(path.clone(), name.clone()).icon(file_icon(name));
out.push(match state(&path) {
Some(state) => {
changed = true;