Repository navigation
feat(nam): group models by folder in a nested picker menu - #294
Conversation
Packs ship as folders of dozens of files, and the NAM stage's pick list is a flat list, so until now every model had to sit loose in the nam folder and the picker grew with the collection. The loader now walks the nam folder recursively and keys each model by its path relative to that folder, without the extension and with `/` separators on every platform (`Pack/Clean`). A top-level file keeps its bare stem, so presets saved before this change keep resolving. The cab-from-name guess still reads the file's own name, never the folder it sits in. The picker is an `iced_aw` menu bar with a single root: each folder fans out sideways into its contents, nesting as deep as the directory tree goes, and loose files sit alongside the folders at the same level, sorted together by name. The root menu is as wide as the button so it hangs straight below it like a pick list; the bar would otherwise right-align it whenever the button sits in the right half of the window. The root button is sized through `responsive` because the bar lays its root out against an unbounded width, where a `Fill` button never gets a size. `iced_aw` is pulled in with only the `menu` feature. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d hidden folders Review findings on the folder picker: - Clicking a folder row collapsed the whole menu. `Item::with_menu` inherits the bar's close-on-item-click, and people do click a folder to open it. Folder rows now opt out; only picking a model or clicking away closes. - The recursive scan followed symlinks, so a link pointing back up the tree was walked until the OS refused, parsing every model dozens of times. The walk now checks the entry's own file type, which does not follow links, and skips symlinked directories. - Dot-folders left by sync tools and desktop trash (`.stfolder`, `.Trash-1000`) were scanned, so a trashed model came back as a picker entry. They are skipped. - A file whose path is not valid UTF-8 was dropped silently; it now warns like every other skipped file. - The tree test also pins that a folder and a loose file sharing a name both survive side by side. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces a new UI dependency and menu-widget behavior relying on non-obvious runtime workarounds (float-overflow margin, responsive sizing) and affects the plugin build, none of which can be statically verified here.
0 open findings
What changed in this PR
This PR replaces the NAM stage's flat pick_list model picker with a nested, folder-grouped menu so model packs can ship as directory trees instead of loose files. The core loader now walks the nam folder recursively and keys each model by its folder-relative path, while the shared UI builds an iced_aw menu bar whose folders fan out into submenus. This fits the existing standalone + plugin architecture, where both drive the same rustortion-ui GUI.
Changes:
- Loader walks the nam directory recursively, keying models by
/-separated relative path (extension dropped), keeping top-level stems bare for backward-compatible preset resolution, and skipping hidden/symlinked directories. - UI replaces
pick_listwith a one-rooticed_awMenuBar, grouping keys into a sortedModelNodetree with selection highlighting and pick-list-like open/close behavior. - Adds the
iced_aw = 0.14.1dependency (menu feature only) with correspondingCargo.lockupdates.
| File | Description |
|---|---|
rustortion-core/src/nam/loader.rs |
Recursive directory scan with folder-relative keys, symlink/hidden-folder guards, model_key helper, and new tests. |
rustortion-ui/src/stages/nam.rs |
Replaces pick list with a nested iced_aw menu bar, adds ModelNode tree building/sorting and menu rendering helpers plus tests. |
rustortion-ui/Cargo.toml |
Adds iced_aw = 0.14.1 with only the menu feature. |
Cargo.lock |
Records iced_aw, iced_fonts, iced_fonts_macros and the new dependency edge. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Summary
Packs ship as folders of dozens of files, and the NAM stage's pick list was a flat list, so every model had to sit loose in the nam folder. This makes the nam folder's directory structure the menu structure.
rustortion-core): walks the nam folder recursively. Each model is keyed by its path relative to that folder, without the extension and with/separators on every platform (Pack/Clean). A top-level file keeps its bare stem, so existing presets keep resolving. The cab-from-name guess still reads the file's own name, never the folder.rustortion-ui): replacespick_listwith aniced_awmenu bar with a single root. Each folder fans out sideways into its contents, nesting as deep as the tree goes; loose files sit alongside folders at the same level, sorted together by name (case-insensitive). The selected model is highlighted. The menu stays open until something is clicked, like a pick list.iced_aw = 0.14.1with only themenufeature. It depends on the sameiced_widget 0.14.2already in the lockfile, and the plugin build (iced_baseview) compiles with it.Layout notes
Two iced_aw quirks are worked around and commented in the code:
Fillroot button never gets a size. The root button is sized throughresponsive.Testing
cargo test --workspace(excluding plugin/xtask): 325 passed.Folder/Sub/Modelkeys and leaves top-level stems bare; folder names don't feed the cab-from-name guess; the picker's tree builder groups and sorts keys correctly.cargo clippy --all-targetsclean on core, ui, standalone;cargo check -p rustortion-pluginclean;cargo fmt --checkclean.🤖 Generated with Claude Code