import { defineWidget } from "@rezi-ui/core";
interface GitNode {
name: string;
path: string;
type: "file" | "folder";
status?: "modified" | "staged" | "untracked";
children?: GitNode[];
}
const GitTree = defineWidget((ctx) => {
const [expanded, setExpanded] = ctx.useState<readonly string[]>([]);
const getStatusIcon = (status?: GitNode["status"]) => {
switch (status) {
case "modified":
return "M";
case "staged":
return "A";
case "untracked":
return "?";
default:
return " ";
}
};
const getStatusColor = (status?: GitNode["status"]) => {
switch (status) {
case "modified":
return { r: 255, g: 200, b: 50 };
case "staged":
return { r: 100, g: 200, b: 100 };
case "untracked":
return { r: 150, g: 150, b: 150 };
default:
return undefined;
}
};
return ui.tree({
id: "git-tree",
data: gitRoot,
getKey: (node) => node.path,
getChildren: (node) => node.children,
expanded,
onToggle: (node, isExpanded) => {
if (isExpanded) {
setExpanded([...expanded, node.path]);
} else {
setExpanded(expanded.filter((key) => key !== node.path));
}
},
showLines: true,
renderNode: (node, depth, state) => {
const icon = node.type === "folder" ? (state.expanded ? "▼" : "▶") : " ";
const statusIcon = getStatusIcon(node.status);
const statusColor = getStatusColor(node.status);
return ui.row({ gap: 1 }, [
ui.text(icon),
ui.text(statusIcon, { style: { fg: statusColor } }),
ui.text(node.name, {
style: {
fg: statusColor,
bg: state.focused ? { r: 50, g: 50, b: 70 } : undefined,
},
}),
]);
},
});
});