Merge pull request #6391 from basecamp/bar-startup-churn

Load each bar widget's component once
This commit is contained in:
David Heinemeier Hansson
2026-07-26 22:32:26 -07:00
committed by GitHub
2 changed files with 41 additions and 4 deletions
+22 -4
View File
@@ -700,6 +700,12 @@ ShellRoot {
source: "plugin"
}
// A load already in flight for this URL registers itself when it
// finishes. Starting a second one produces a second Component for the
// same widget, and swapping a slot's component rebuilds its item —
// briefly running two of the widget, each registering its IPC handler.
if (existing && existing.url === url && !existing.component) continue
// If the component URL is unchanged, just refresh the metadata in
// place. We can't skip this even when the URL matches: manifests can
// change schema, defaults, or sourceDir between rescans, and the
@@ -769,17 +775,29 @@ ShellRoot {
}
}
function setPluginWidgetComponent(registryKey, entry) {
var next = ({})
for (var k in pluginWidgetComponents) if (k !== registryKey) next[k] = pluginWidgetComponents[k]
if (entry) next[registryKey] = entry
pluginWidgetComponents = next
}
function loadPluginWidget(registryKey, url, meta) {
// Claim the key before the component exists. Qt.createComponent is
// asynchronous and syncPluginWidgets runs several times while the shell
// starts, so without a marker the later passes cannot tell a load in
// flight from one that never happened.
setPluginWidgetComponent(registryKey, { url: url, component: null })
var comp = Qt.createComponent(url, Component.Asynchronous)
function finalize() {
if (comp.status === Component.Ready) {
shell.barWidgetRegistry.register(registryKey, comp, meta)
var next = ({})
for (var k in pluginWidgetComponents) next[k] = pluginWidgetComponents[k]
next[registryKey] = { url: url, component: comp }
pluginWidgetComponents = next
shell.setPluginWidgetComponent(registryKey, { url: url, component: comp })
} else if (comp.status === Component.Error) {
console.warn("Plugin widget " + registryKey + " failed: " + comp.errorString())
// Drop the claim so a later rescan can retry.
shell.setPluginWidgetComponent(registryKey, null)
shell.pluginRegistry.pluginLoadFailed(registryKey, comp.errorString())
}
}
+19
View File
@@ -230,6 +230,24 @@ for panel_id in omarchy.audio omarchy.bluetooth omarchy.monitor omarchy.network
done
pass "direct panel IPC opens and closes default panels"
# Each widget registers its IPC handler once per bar, and the bar is
# instantiated once per screen, so Quickshell reports one collision per screen
# past the first. Anything beyond that is two instances on the same screen —
# the shape duplicate component loads produced, where a sync pass that ran
# while a widget's asynchronous load was still in flight started a second one.
# Checked before the reload below, which rebuilds widgets by design.
screens=$(hyprctl -j monitors 2>/dev/null | jq 'length' 2>/dev/null || true)
[[ $screens =~ ^[0-9]+$ ]] && (( screens > 0 )) || screens=1
# No matches is the good case, and pipefail would otherwise abort the run.
worst=$(grep -oE "another handler is registered for target [a-z.-]+" "$log" |
sort | uniq -c | sort -rn | head -1 | awk '{print $1}' || true)
worst=${worst:-0}
if (( worst > screens - 1 )); then
grep "another handler is registered for target" "$log" | sed 's/^/ /' | head -20 >&2
fail_with_log "each widget registers its IPC handler once per screen (saw $worst for $screens screen(s))"
fi
pass "each widget registers its IPC handler once per screen"
HOME="$test_home" OMARCHY_PATH="$test_root" PATH="$ROOT/bin:$PATH" "$ROOT/bin/omarchy-bar-plugin" remove omarchy.audio
for _ in {1..80}; do
@@ -257,3 +275,4 @@ jq -e 'all(.[]; .id != "omarchy.audio")' <<<"$geometry" >/dev/null || {
}
pass "bar remove reloads shell config and updates bar layout"