From 2716fdd081d986ada894f3135966057b76db3d0a Mon Sep 17 00:00:00 2001 From: gitRasheed Date: Wed, 23 Sep 2026 19:07:28 +0100 Subject: [PATCH 1/2] Type panel percentages add up to the rows shown Percentages divided by the filtered total, so with a type filter active a row could read above 100%. They now divide by the sum of the displayed rows, and emptiness is judged from those rows too. The active ext: list normalizes dots and case the way the Rust parser does, so shift-clicking .pdf while ext:.PDF is active removes it instead of adding a duplicate. --- src-tauri/src/scan.rs | 6 +----- ui/src/App.tsx | 8 +++++++- ui/src/components/TypePanel.tsx | 13 ++++++++----- ui/src/lib/api.ts | 2 -- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src-tauri/src/scan.rs b/src-tauri/src/scan.rs index 1f4f8a2..e7f3d38 100644 --- a/src-tauri/src/scan.rs +++ b/src-tauri/src/scan.rs @@ -399,8 +399,6 @@ pub struct TypeStatDto { #[serde(rename_all = "camelCase")] pub struct TypePanelData { types: Vec, - total_bytes: u64, - total_files: u64, top_files: Vec, } @@ -424,7 +422,7 @@ pub fn get_type_stats( let overlay = overlay_for(&session, tree, filter.as_deref(), hide_system); let visible = overlay.as_deref().map(|o| o.visible.as_slice()); let filtered = mathom_core::stats::type_breakdown(tree, root_id, hide_system, visible); - let (total_bytes, total_files) = (filtered.total_bytes, filtered.total_files); + let total_bytes = filtered.total_bytes; let top_files = mathom_core::stats::largest_files(tree, root_id, TOP_FILES, hide_system, visible) .into_iter() @@ -445,8 +443,6 @@ pub fn get_type_stats( files: t.files, }) .collect(), - total_bytes, - total_files, top_files, }) } diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 2039b87..1703d05 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -201,7 +201,13 @@ export default function App() { // Only a pure `ext:` filter is treated as the active type list; other queries start fresh. const { filter } = scan; const activeExts = useMemo( - () => filter?.match(/^ext:([^\s]+)$/)?.[1].split(",") ?? [], + () => + filter + ?.trim() + .match(/^ext:([^\s]+)$/)?.[1] + .split(",") + .map((ext) => ext.replace(/^\.+/, "").toLowerCase()) + .filter(Boolean) ?? [], [filter], ); diff --git a/ui/src/components/TypePanel.tsx b/ui/src/components/TypePanel.tsx index 8a948b9..86f0b83 100644 --- a/ui/src/components/TypePanel.tsx +++ b/ui/src/components/TypePanel.tsx @@ -107,7 +107,7 @@ export function TypePanel({ Appears during a scan ) : null - ) : data.totalFiles === 0 ? ( + ) : data.types.length === 0 ? (
No files here
) : ( sum + type.bytes, + 0, + ), activeExts, canFilter, onFilterType, @@ -153,7 +156,7 @@ export function TypePanel({ interface TypeRowsProps { types: TypeStat[]; - totalBytes: number; + displayedBytes: number; activeExts: string[]; canFilter: boolean; onFilterType: (ext: string, additive: boolean) => void; @@ -163,7 +166,7 @@ function TypeRow({ index, style, types, - totalBytes, + displayedBytes, activeExts, canFilter, onFilterType, @@ -202,7 +205,7 @@ function TypeRow({ {formatBytes(t.bytes)} - {formatPercent(totalBytes > 0 ? t.bytes / totalBytes : 0)} + {formatPercent(displayedBytes > 0 ? t.bytes / displayedBytes : 0)} ); diff --git a/ui/src/lib/api.ts b/ui/src/lib/api.ts index 3a7e806..dd3b149 100644 --- a/ui/src/lib/api.ts +++ b/ui/src/lib/api.ts @@ -81,8 +81,6 @@ export interface TypeStat { export interface TypePanelData { /** Every extension in the subtree, largest first. */ types: TypeStat[]; - totalBytes: number; - totalFiles: number; topFiles: Row[]; } From 9ad11f1c6af7574f839652f28c69bdedef223cdf Mon Sep 17 00:00:00 2001 From: gitRasheed <99349713+gitRasheed@users.noreply.github.com> Date: Sun, 1 Nov 2026 21:41:19 +0000 Subject: [PATCH 2/2] Sidebar clicks select without moving the treemap A click or arrow key in the sidebar selects the row, and the treemap outlines it if it is drawn. Enter or double-click moves the treemap there, as a click used to. Deleting from the sidebar no longer jumps the map around. --- ui/src/App.tsx | 9 +++------ ui/src/components/TreeView.tsx | 15 ++++++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 1703d05..cd57d37 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -103,7 +103,7 @@ export default function App() { [pathOf], ); - const handleTreeSelect = useCallback( + const handleTreeActivate = useCallback( (row: Row) => { select(row.id); if (row.isDir) { @@ -193,10 +193,7 @@ export default function App() { [generation, select], ); - const handleTreeKeySelect = useCallback( - (row: Row) => select(row.id), - [select], - ); + const handleTreeSelect = useCallback((row: Row) => select(row.id), [select]); // Only a pure `ext:` filter is treated as the active type list; other queries start fresh. const { filter } = scan; @@ -410,7 +407,7 @@ export default function App() { onRevealed={handleRevealed} onToggle={scan.toggleExpand} onSelect={handleTreeSelect} - onKeySelect={handleTreeKeySelect} + onActivate={handleTreeActivate} onHoverRow={setHoveredId} onContext={handleContextMenu} onSort={scan.changeSort} diff --git a/ui/src/components/TreeView.tsx b/ui/src/components/TreeView.tsx index 010621f..efa2837 100644 --- a/ui/src/components/TreeView.tsx +++ b/ui/src/components/TreeView.tsx @@ -128,6 +128,7 @@ interface TreeRowProps { hoveredId: number | null; onToggle: (id: number) => void; onSelect: (row: Row) => void; + onActivate: (row: Row) => void; onHoverRow: (id: number | null) => void; onContext: (id: number, x: number, y: number) => void; } @@ -142,6 +143,7 @@ function TreeRow({ hoveredId, onToggle, onSelect, + onActivate, onHoverRow, onContext, }: RowComponentProps) { @@ -162,6 +164,7 @@ function TreeRow({ onClick={() => onSelect(row)} onDoubleClick={() => { if (row.isDir && row.hasChildren) onToggle(row.id); + onActivate(row); }} onContextMenu={(e) => { e.preventDefault(); @@ -308,9 +311,10 @@ export interface TreeViewProps { revealId: number | null; onRevealed: () => void; onToggle: (id: number) => void; + /** Click and arrow keys: select only, the treemap stays where it is. */ onSelect: (row: Row) => void; - /** Arrow-key selection: select only — Enter (onSelect) moves the view. */ - onKeySelect: (row: Row) => void; + /** Enter and double-click: move the treemap to the row. */ + onActivate: (row: Row) => void; onHoverRow: (id: number | null) => void; onContext: (id: number, x: number, y: number) => void; onSort: (key: SortKey) => void; @@ -328,7 +332,7 @@ export function TreeView({ onRevealed, onToggle, onSelect, - onKeySelect, + onActivate, onHoverRow, onContext, onSort, @@ -359,7 +363,7 @@ export function TreeView({ const action = navAction(e.key, flat, selectedIndex, expanded); if (!action) return; if (action.kind === "step") { - onKeySelect(flat[action.index].row); + onSelect(flat[action.index].row); listRef.current?.scrollToRow({ index: action.index, align: "smart", @@ -368,7 +372,7 @@ export function TreeView({ } else if (action.kind === "toggle") { onToggle(action.id); } else { - onSelect(action.row); + onActivate(action.row); } }; @@ -393,6 +397,7 @@ export function TreeView({ hoveredId, onToggle, onSelect, + onActivate, onHoverRow, onContext, }}