Skip to content

Keep a panel plugin enabled when its bar icon is removed - #6510

Open
28allday wants to merge 1 commit into
basecamp:quattrofrom
28allday:plugin-enable-panel-ref
Open

Keep a panel plugin enabled when its bar icon is removed#6510
28allday wants to merge 1 commit into
basecamp:quattrofrom
28allday:plugin-enable-panel-ref

Conversation

@28allday

@28allday 28allday commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

A plugin whose kinds include both bar-widget and a panel, overlay or menu kind is enabled by its bar entry alone. setEnabled picks the bar branch of an if/else chain, so the config.plugins.push(entry) below it is unreachable for that shape:

if (!location.found && isBarWidget) {
  ...                                    // bar entry written here
} else if (!location.found && !isFirstParty) {
  config.plugins.push(entry)             // never reached for a panel widget
}

isEnabled() then falls through to findEntryLocation(), which only finds the bar entry. Take such a plugin's icon out of the bar and it counts as disabled: the panel delegate is never instantiated and omarchy-shell shell toggle <id> becomes a silent no-op — exit 0, nothing on screen, and the reason only in /run/user/$UID/quickshell/by-pid/*/log.log:

WARN qml: summon: plugin not enabled, not summoning: <id>

Users hit this by binding a key to a panel plugin and then not wanting a second icon in their bar, or by running with the bar hidden. It reads as a broken keybinding, which is a long way from where the cause is.

Changes

  • setEnabled writes both references on enable: the bar entry for the widget, and a plugins[] entry for the kind that outlives it. Widget-only plugins are unaffected — they still get the bar entry alone, and first-party plugins still get neither.
  • Disable clears every reference in one call rather than the first one found, so off still means off. This also means omarchy plugin remove's single setPluginEnabled <id> false can no longer leave an orphan entry pointing at a directory that has been deleted.
  • restoreCloneSource drops a clone's plugins[] entry whichever way it was referenced — without this the enable change would orphan one on every hybrid clone.
  • listPlugins reports a widget-only plugin by its place in the bar as before; anything with another kind reports whether it is actually enabled.
  • New findPluginLocation, plus a note on findEntryLocation that it returns the first reference now that two can coexist.

Testing

plugin-registry-contract-test.sh runs real headless quickshell against the registry, so these are behavioural checks rather than source assertions. Added a third.hybrid ["panel","bar-widget"] fixture covering:

  • both references written on enable
  • still enabled after the bar entry is removed
  • one-call disable clearing both
  • re-enabling twice adding one of each
  • no orphan left behind when a clone's source is restored

Reverting only PluginRegistry.qml fails on exactly the new assertions (expected=[{"id":"third.hybrid"}] actual=[]), so the test pins the fix rather than merely passing alongside it. ./test/shell is otherwise green — 603 passing.

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings August 2, 2026 10:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates plugin enablement so hybrid panel/bar plugins remain active without their bar icon.

Changes:

  • Stores independent bar and plugin references for hybrid plugins.
  • Removes references when disabling or restoring clones.
  • Adds behavioral coverage for hybrid plugin lifecycle.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
shell/services/PluginRegistry.qml Manages hybrid plugin references and clone restoration.
shell/shell.qml Reports hybrid plugin enablement state.
test/shell.d/fixtures/plugin-registry/shell.qml Adds hybrid lifecycle contract tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +518 to +521
var barEntry = findBarLocation(config, key, "")
if (barEntry.found) config.bar.layout[barEntry.section].splice(barEntry.index, 1)
var pluginEntry = findPluginLocation(config, key)
if (pluginEntry.found) config.plugins.splice(pluginEntry.index, 1)
Comment thread shell/shell.qml
Comment on lines 961 to +962
enabled: isBarOption ? active
: (isBarWidget ? shell.pluginRegistry.inBar(id) : shell.pluginRegistry.isEnabled(id)),
: (isWidgetOnly ? shell.pluginRegistry.inBar(id) : shell.pluginRegistry.isEnabled(id)),
A plugin whose kinds include both a bar-widget and a panel, overlay or
menu was enabled by its bar entry alone: setEnabled picked the bar branch
of an if/else chain, so the plugins[] push below it was unreachable. Take
such a plugin's icon out of the bar and isEnabled() went false, the panel
delegate was never instantiated, and `omarchy-shell shell toggle <id>`
became a silent no-op — exit 0, nothing on screen, and the reason only in
the shell log. Users hit this by binding a key to a panel and then not
wanting a second icon in their bar.

Enable now writes both references: the bar entry for the widget, and a
plugins[] entry for the kind that outlives it. Disable clears every
reference in one call rather than the first one found, so off still means
off, and restoring a clone's source drops the clone's plugins[] entry
whichever way it was referenced.

listPlugins reports a plugin that is only a widget by its place in the
bar as before, and anything with another kind by whether it is enabled.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Copilot AI review requested due to automatic review settings August 13, 2026 19:44
@28allday
28allday force-pushed the plugin-enable-panel-ref branch from 68e7be8 to 2988588 Compare August 13, 2026 19:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (3)

shell/services/PluginRegistry.qml:554

  • This still removes only the first matching entry of each kind, despite the new guarantee that disable clears every reference. Configs may validly contain repeated bar entries when allowMultiple is enabled, and duplicate plugins[] entries can also exist; omarchy plugin remove would then leave references to the deleted plugin. Iterate backward through every bar section and plugins[] to remove all matching IDs.
        var barEntry = findBarLocation(config, key, "")
        if (barEntry.found) config.bar.layout[barEntry.section].splice(barEntry.index, 1)
        var pluginEntry = findPluginLocation(config, key)
        if (pluginEntry.found) config.plugins.splice(pluginEntry.index, 1)

shell/services/PluginRegistry.qml:533

  • Existing installations are not upgraded by this branch. A hybrid plugin enabled before this change has only its bar-layout entry, so setEnabled(..., true) is never called to add the durable plugins[] reference; removing that existing icon after upgrade still makes isEnabled() false and reproduces the reported no-op. Backfill bar-referenced third-party hybrids when loading/scanning the config (or add a migration), and cover a pre-existing bar-only hybrid in the contract test.
        if (hasNonWidgetKind && !isFirstParty && !findPluginLocation(config, key).found)
          config.plugins.push({ id: key })

shell/services/PluginRegistry.qml:516

  • This introduces two persisted entries for one hybrid plugin, but the public shell configuration contract still says “Every plugin instance is one entry” and chooses either bar.layout or plugins[] (docs/omarchy-shell.md:144-145 and shell/README.md:259-261). Update those storage rules so users and plugin authors do not remove the apparently redundant entry and reintroduce the failure.
        // A widget is enabled by its place in the bar. Anything with another
        // kind is enabled by a plugins[] entry, and a plugin that is both
        // needs both: the panel behind a bar icon must not be unloaded just
        // because the icon was taken out of the bar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants