Fix collapsing submenus on refresh (Issues #143, #29, #40) + optional reopen/minwidth support
Summary
This PR addresses a long-standing usability issue in Argos: collapsing submenus after each refresh.
Currently, whenever the menu rebuilds (e.g., due to updateInterval), all open submenus are lost and collapse.
This is tracked in multiple open issues: #143, #29, #40.
The result is a frustrating user experience whenever scripts display structured information across submenus โ users constantly need to reopen the submenu they were working with.
This PR introduces backward-compatible, optional extensions to improve daily usability while leaving existing BitBar/Argos scripts untouched.
Changes
1. Preserve Submenu State
- Problem: Open submenus collapse on every refresh (see #143 / #29 / #40).
- Solution:
- Submenu state is captured before
removeAll()and restored afterwards. - Requires scripts to assign a stable
idto submenu headers:-- Connections | id=net ---- Item A ---- Item B - If
idis not set, the submenu behaves as before (collapses on refresh).
- Submenu state is captured before
- Implementation: A new helper class
submenu_state.jsmanages capture/restore. UsesGLib.idle_addto avoid race conditions during menu rebuild.
2. Reopen Menu After Action
- Problem: After executing a menu command, the dropdown always closes, even when the user wants to toggle multiple options quickly.
- Solution:
- Scripts can add
reopen=trueto an item. - After the action finishes, the menu automatically reopens, preserving state.
- Scripts can add
- Implementation: Handled in
submenu_state.jswithrequestReopen()andfinalizeUpdate().
3. Minimum Width for Submenus
- Problem: When submenu content changes length between refreshes, the menu width jumps, causing visual flicker.
- Solution:
- Submenu headers may specify
minwidthin pixels:-- Status | id=status minwidth=360 ---- Connected ---- Last handshake: 2m ago - Applied as a
min-widthstyle to the correspondingPopupSubMenuMenuItem.
- Submenu headers may specify
- Implementation: Applied directly in
button.jswhen building submenus.
Backward Compatibility
- Scripts without
id,reopen, orminwidthproperties behave exactly as before. - All new features are strictly optional and ignored unless specified in the script output.
- BitBar/xbar scripts continue to run without modification.
Impact
- Directly addresses the top UX complaint in Argos (collapsing submenus, see #143 / #29 / #40).
- Improves workflow for scripts that act as dashboards or toggles (e.g., VPN clients, system controls).
- Minimal code changes, isolated to new utility class and property checks.
Screenshots / Demo
(optional: add screenshots or a short screencast here to illustrate submenu state being preserved and menu reopening)
Conclusion
This PR makes Argos more robust and convenient in daily use without breaking compatibility.
Users frustrated by collapsing submenus (#143 / #29 / #40) finally get relief, while all existing scripts continue to function as before.
Example
The new features can be seen in the attached demo script:
#!/usr/bin/env bash
# example.5s.sh
# Demonstrates submenu state restore (id=...), reopen=true, and minwidth.
echo "๐งช Demo"
echo "---"
# Submenu with stable id and fixed minimum width
echo "โ๏ธ Settings | id=demo-settings minwidth=360"
FLAG="/tmp/argos-demo-flag"
if [[ -f "$FLAG" ]]; then
echo "--โ
Enabled | bash='rm -f \"$FLAG\"' terminal=false refresh=true reopen=true"
else
echo "--โ Disabled | bash='touch \"$FLAG\"' terminal=false refresh=true reopen=true"
fi
# Another submenu โ open state will be preserved
echo "---"
echo "๐ Status | id=demo-status minwidth=360"
NOW="$(date '+%H:%M:%S')"
echo "--Time: $NOW"
echo "--Flag file present: $( [[ -f \"$FLAG\" ]] && echo yes || echo no )"
# Top-level refresh action
echo "---"
echo "โป๏ธ Force Refresh | refresh=true"
@vaab, could you give this a try, as you've had similar issues in #173? If your testing is successful, I'll merge this.
Awesome, a few notes:
-
There is a visible flicker because the menu is actually closing, then argos re-launch the shell script, and then a redisplay of the menu is done with the correct submenu opened.
The flicker time will be bigger depending on the execution time of the shell script. But it is quite noticeable already with the example script. With my case, I'm already using extensive caching, so it stays usable
-
The reopen=True is plagued by the same flickering.
And this one, with my case, it is unusable : it stays more than 1 to 2 seconds of screen and come back to the screen.
-
your demo script vanishes at some point (a few minutes after letting it alone):
it hits an exception (i'm on gnome 46):
Oct 03 05:22:10 wen gnome-shell[42575]: Source ID 157777 was not found when attempting to remove it
Oct 03 05:22:10 wen gnome-shell[42575]: JS ERROR: TypeError: appIndicator is undefined
#handleAppIndicatorItem@file:///home/vaab/.local/share/gnome-shell/extensions/[email protected]/extensionModules/BoxOrderManager.js:80:27
addNewItemSettingsIdsToBoxOrder@file:///home/vaab/.local/share/gnome-shell/extensions/[email protected]/extensionModules/BoxOrderManager.js:295:70
saveNewTopBarItems@file:///home/vaab/.local/share/gnome-shell/extensions/[email protected]/extensionModules/BoxOrderManager.js:333:40
#handleNewItemsAndOrderTopBar@file:///home/vaab/.local/share/gnome-shell/extensions/[email protected]/extension.js:158:31
handleNewItemsAndOrderTopBar@file:///home/vaab/.local/share/gnome-shell/extensions/[email protected]/extension.js:68:47
#overwritePanelAddToPanelBox/Panel.Panel.prototype._addToPanelBox@file:///home/vaab/.local/share/gnome-shell/extensions/[email protected]/extension.js:76:13
addButtons@file:///home/vaab/.local/share/gnome-shell/extensions/[email protected]/extension.js:105:16
enable/this.directoryChangedId</this.debounceTimeout<@file:///home/vaab/.local/share/gnome-shell/extensions/[email protected]/extension.js:64:14
I didn't really check the code, but wouldn't it be possible to load the old menu while the command is being run ? That would reduce flicker. Or best, avoid closing it in the first place ;-)
Well, I feel this is incrementally better, but a little quirky feeling. I'm unsure of what to think about if this is better than just avoiding refreshes while a submenu is open for people only minding about the submenu closing upon refresh.
Screencast from 2025-10-03 05-36-25.webm
Also, this PR is not ready for merge as-is strictly speaking : there is no doc, and it contains other unrelated stuff.