mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-01 20:28:16 +02:00
Enable plugin hot reloading for add / remove
This commit is contained in:
+23
-1
@@ -94,6 +94,21 @@ require_command() {
|
|||||||
command -v "$command_name" >/dev/null 2>&1 || fail "$command_name is required"
|
command -v "$command_name" >/dev/null 2>&1 || fail "$command_name is required"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugin_discovered() {
|
||||||
|
local id="$1" plugins
|
||||||
|
plugins=$(omarchy-shell shell listPlugins 2>/dev/null) || return 1
|
||||||
|
jq -e --arg id "$id" 'any(.[]; .id == $id)' <<<"$plugins" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_plugin_discovery() {
|
||||||
|
local id="$1" attempt
|
||||||
|
for (( attempt = 0; attempt < 40; attempt++ )); do
|
||||||
|
plugin_discovered "$id" && return 0
|
||||||
|
sleep 0.05
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
section_valid() {
|
section_valid() {
|
||||||
[[ $1 =~ ^(left|center|right)$ ]]
|
[[ $1 =~ ^(left|center|right)$ ]]
|
||||||
}
|
}
|
||||||
@@ -181,7 +196,14 @@ plugin_enabled() {
|
|||||||
id=$(canonical_widget_id "$id")
|
id=$(canonical_widget_id "$id")
|
||||||
|
|
||||||
omarchy-shell shell rescanPlugins >/dev/null 2>&1 || true
|
omarchy-shell shell rescanPlugins >/dev/null 2>&1 || true
|
||||||
omarchy-shell shell setPluginEnabled "$id" "$enabled" || fail "could not update plugin; is omarchy-shell running?"
|
if [[ $enabled == "true" ]]; then
|
||||||
|
require_command jq
|
||||||
|
wait_for_plugin_discovery "$id" || fail "plugin '$id' was not discovered; is omarchy-shell running?"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local result
|
||||||
|
result=$(omarchy-shell shell setPluginEnabled "$id" "$enabled") || fail "could not update plugin; is omarchy-shell running?"
|
||||||
|
[[ $result == "ok" ]] || fail "plugin '$id' is not known; run: omarchy plugin rescan"
|
||||||
|
|
||||||
if [[ $enabled == "true" && $# -gt 0 ]]; then
|
if [[ $enabled == "true" && $# -gt 0 ]]; then
|
||||||
mutate_bar move --id "$id" "$@"
|
mutate_bar move --id "$id" "$@"
|
||||||
|
|||||||
+18
-1
@@ -29,6 +29,21 @@ interactive() {
|
|||||||
[[ -t 0 && -t 1 ]]
|
[[ -t 0 && -t 1 ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugin_discovered() {
|
||||||
|
local id="$1" plugins
|
||||||
|
plugins=$(omarchy-shell shell listPlugins 2>/dev/null) || return 1
|
||||||
|
jq -e --arg id "$id" 'any(.[]; .id == $id)' <<<"$plugins" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_plugin_discovery() {
|
||||||
|
local id="$1" attempt
|
||||||
|
for (( attempt = 0; attempt < 40; attempt++ )); do
|
||||||
|
plugin_discovered "$id" && return 0
|
||||||
|
sleep 0.05
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
PLUGIN_ID=""
|
PLUGIN_ID=""
|
||||||
FROM_SOURCE=""
|
FROM_SOURCE=""
|
||||||
ENABLE_AFTER="" # "", "true", or "false"
|
ENABLE_AFTER="" # "", "true", or "false"
|
||||||
@@ -280,7 +295,9 @@ if [[ -z $ENABLE_AFTER ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $ENABLE_AFTER == true ]]; then
|
if [[ $ENABLE_AFTER == true ]]; then
|
||||||
if omarchy-shell shell setPluginEnabled "$m_id" true >/dev/null 2>&1; then
|
if ! wait_for_plugin_discovery "$m_id"; then
|
||||||
|
echo "Could not enable $m_id because omarchy-shell did not discover it yet. Enable later with: omarchy plugin enable $m_id" >&2
|
||||||
|
elif result=$(omarchy-shell shell setPluginEnabled "$m_id" true) && [[ $result == "ok" ]]; then
|
||||||
echo "Enabled $m_id."
|
echo "Enabled $m_id."
|
||||||
if [[ $m_kinds == *bar-widget* ]]; then
|
if [[ $m_kinds == *bar-widget* ]]; then
|
||||||
echo "It was added to the bar; arrange it with: omarchy plugin bar move $m_id --section <left|center|right>"
|
echo "It was added to the bar; arrange it with: omarchy plugin bar move $m_id --section <left|center|right>"
|
||||||
|
|||||||
@@ -102,6 +102,5 @@ fi
|
|||||||
omarchy-shell shell rescanPlugins >/dev/null 2>&1 || true
|
omarchy-shell shell rescanPlugins >/dev/null 2>&1 || true
|
||||||
|
|
||||||
if [[ $was_enabled == "true" ]]; then
|
if [[ $was_enabled == "true" ]]; then
|
||||||
echo "Plugin was enabled; restart the shell to fully unload it:"
|
echo "Plugin was enabled and was unloaded from omarchy-shell."
|
||||||
echo " omarchy restart shell"
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -208,8 +208,7 @@ done
|
|||||||
|
|
||||||
if ((any_enabled)); then
|
if ((any_enabled)); then
|
||||||
echo
|
echo
|
||||||
echo "Updated plugins that were enabled may need a shell restart to fully reload:"
|
echo "Updated enabled plugins were hot-reloaded by omarchy-shell."
|
||||||
echo " omarchy restart shell"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit $rc
|
exit $rc
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ cp ~/.config/hypr/bindings.conf ~/.config/hypr/bindings.conf.bak.$(date +%s)
|
|||||||
|
|
||||||
# 4. Apply changes
|
# 4. Apply changes
|
||||||
# - Hyprland: auto-reloads on save, but MUST validate with `hyprctl reload` and `hyprctl configerrors`
|
# - Hyprland: auto-reloads on save, but MUST validate with `hyprctl reload` and `hyprctl configerrors`
|
||||||
# - Omarchy shell: shell.json hot-reloads; use `omarchy-restart-shell` for plugin/widget code changes
|
# - Omarchy shell: shell.json hot-reloads; use `omarchy plugin rescan` for plugin/widget code changes
|
||||||
# - Launcher: restart with `omarchy restart shell`
|
# - Launcher: restart with `omarchy restart shell`
|
||||||
# - Terminals: MUST restart with `omarchy restart terminal`
|
# - Terminals: MUST restart with `omarchy restart terminal`
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ individual plugins (`bar`, `image-selector`, …).
|
|||||||
| `hide <id>` | close a previously-summoned |
|
| `hide <id>` | close a previously-summoned |
|
||||||
| `toggle <id> <payloadJson>` | summon if closed, hide if open |
|
| `toggle <id> <payloadJson>` | summon if closed, hide if open |
|
||||||
| `call <id> <method> <arg>` | call an already-loaded plugin |
|
| `call <id> <method> <arg>` | call an already-loaded plugin |
|
||||||
| `rescanPlugins` | re-walk plugin dirs |
|
| `rescanPlugins` | re-walk plugin dirs and hot-reload plugin code |
|
||||||
| `reloadConfig` | reload shell.json |
|
| `reloadConfig` | reload shell.json |
|
||||||
| `setPluginEnabled <id> <"true"\|…>` | flip enabled bit |
|
| `setPluginEnabled <id> <"true"\|…>` | flip enabled bit (`ok` / `unknown`) |
|
||||||
| `listPlugins` | JSON of every discovered plugin |
|
| `listPlugins` | JSON of every discovered plugin |
|
||||||
|
|
||||||
`setPluginEnabled` takes a string; only literal `"true"` enables.
|
`setPluginEnabled` takes a string; only literal `"true"` enables.
|
||||||
|
|||||||
+2
-2
@@ -172,9 +172,9 @@ running a separate Quickshell instance.
|
|||||||
| `hide <id>` | — | close a previously-summoned plugin |
|
| `hide <id>` | — | close a previously-summoned plugin |
|
||||||
| `toggle <id> <payloadJson>` | — | summon if closed, hide if open |
|
| `toggle <id> <payloadJson>` | — | summon if closed, hide if open |
|
||||||
| `call <id> <method> <arg>` | string | call a method on an already-loaded plugin |
|
| `call <id> <method> <arg>` | string | call a method on an already-loaded plugin |
|
||||||
| `rescanPlugins` | — | re-walk plugin dirs and pick up new/changed manifests |
|
| `rescanPlugins` | — | re-walk plugin dirs and hot-reload plugin code |
|
||||||
| `reloadConfig` | `ok` | reload `~/.config/omarchy/shell.json` |
|
| `reloadConfig` | `ok` | reload `~/.config/omarchy/shell.json` |
|
||||||
| `setPluginEnabled <id> <enabled>` | — | flip the persisted enabled bit (see note) |
|
| `setPluginEnabled <id> <enabled>` | `ok` / `unknown` | flip the persisted enabled bit (see note) |
|
||||||
| `listPlugins` | JSON | every discovered plugin (id, name, kinds, enabled) |
|
| `listPlugins` | JSON | every discovered plugin (id, name, kinds, enabled) |
|
||||||
|
|
||||||
Direct invocation:
|
Direct invocation:
|
||||||
|
|||||||
@@ -35,4 +35,4 @@ Renders the Tailscale mark natively as a theme-colored 3×3 dot grid, matching t
|
|||||||
|
|
||||||
## Add to the bar
|
## Add to the bar
|
||||||
|
|
||||||
This widget ships as first-party plugin `omarchy.tailscale`. Add it with `omarchy plugin bar add omarchy.tailscale`, or add an entry such as `{ "id": "omarchy.tailscale" }` to one of the `bar.layout` sections in `~/.config/omarchy/shell.json` and restart the shell.
|
This widget ships as first-party plugin `omarchy.tailscale`. Add it with `omarchy plugin bar add omarchy.tailscale`, or add an entry such as `{ "id": "omarchy.tailscale" }` to one of the `bar.layout` sections in `~/.config/omarchy/shell.json`; the shell reloads `shell.json` automatically.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ QtObject {
|
|||||||
property bool scanning: false
|
property bool scanning: false
|
||||||
|
|
||||||
signal pluginsChanged()
|
signal pluginsChanged()
|
||||||
|
signal scanFinished()
|
||||||
signal pluginLoadFailed(string id, string error)
|
signal pluginLoadFailed(string id, string error)
|
||||||
|
|
||||||
// ---------------------------------------------------------------- helpers
|
// ---------------------------------------------------------------- helpers
|
||||||
@@ -157,9 +158,13 @@ QtObject {
|
|||||||
var key = Util.canonicalWidgetId(String(id))
|
var key = Util.canonicalWidgetId(String(id))
|
||||||
if (!shellConfigMutator) {
|
if (!shellConfigMutator) {
|
||||||
console.warn("PluginRegistry.setEnabled called before shellConfigMutator wired")
|
console.warn("PluginRegistry.setEnabled called before shellConfigMutator wired")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
var manifest = installedPlugins[key]
|
var manifest = installedPlugins[key]
|
||||||
|
if (value && !manifest) {
|
||||||
|
console.warn("PluginRegistry.setEnabled: unknown plugin " + key)
|
||||||
|
return false
|
||||||
|
}
|
||||||
var isBarOption = manifest && Array.isArray(manifest.kinds) && manifest.kinds.indexOf("bar") !== -1
|
var isBarOption = manifest && Array.isArray(manifest.kinds) && manifest.kinds.indexOf("bar") !== -1
|
||||||
var isBarWidget = manifest && Array.isArray(manifest.kinds) && manifest.kinds.indexOf("bar-widget") !== -1
|
var isBarWidget = manifest && Array.isArray(manifest.kinds) && manifest.kinds.indexOf("bar-widget") !== -1
|
||||||
shellConfigMutator(function(config) {
|
shellConfigMutator(function(config) {
|
||||||
@@ -196,6 +201,7 @@ QtObject {
|
|||||||
})
|
})
|
||||||
registryRevision++
|
registryRevision++
|
||||||
pluginsChanged()
|
pluginsChanged()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- scanning
|
// ---------------------------------------------------------------- scanning
|
||||||
@@ -269,6 +275,7 @@ QtObject {
|
|||||||
registryRevision++
|
registryRevision++
|
||||||
scanning = false
|
scanning = false
|
||||||
pluginsChanged()
|
pluginsChanged()
|
||||||
|
scanFinished()
|
||||||
}
|
}
|
||||||
|
|
||||||
property Process scanProcess: Process {
|
property Process scanProcess: Process {
|
||||||
|
|||||||
+69
-8
@@ -54,6 +54,8 @@ ShellRoot {
|
|||||||
property var defaultsConfig: builtinShellConfig
|
property var defaultsConfig: builtinShellConfig
|
||||||
property var shellConfig: builtinShellConfig
|
property var shellConfig: builtinShellConfig
|
||||||
property bool suppressUserReload: false
|
property bool suppressUserReload: false
|
||||||
|
property bool pluginReloading: false
|
||||||
|
property bool pluginReloadPending: false
|
||||||
|
|
||||||
onShellConfigChanged: {
|
onShellConfigChanged: {
|
||||||
if (failedBarId !== "") failedBarId = ""
|
if (failedBarId !== "") failedBarId = ""
|
||||||
@@ -245,11 +247,11 @@ ShellRoot {
|
|||||||
Loader {
|
Loader {
|
||||||
id: pluginBarLoader
|
id: pluginBarLoader
|
||||||
|
|
||||||
active: shell.activeBarId !== shell.defaultBarId && shell.activeBarSourceUrl !== ""
|
active: !shell.pluginReloading && shell.activeBarId !== shell.defaultBarId && shell.activeBarSourceUrl !== ""
|
||||||
source: shell.activeBarId !== shell.defaultBarId ? shell.activeBarSourceUrl : ""
|
source: shell.activeBarId !== shell.defaultBarId ? shell.activeBarSourceUrl : ""
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
onLoaded: shell.configureBar(item, shell.activeBarManifest)
|
onLoaded: shell.configureBar(item, shell.activeBarManifest)
|
||||||
onActiveChanged: if (!active && shell.activeBarId === shell.defaultBarId) shell.bar = null
|
onActiveChanged: if (!active) shell.bar = null
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if (status === Loader.Error) {
|
if (status === Loader.Error) {
|
||||||
var detail = errorString && errorString() ? errorString() : ""
|
var detail = errorString && errorString() ? errorString() : ""
|
||||||
@@ -344,9 +346,17 @@ ShellRoot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unloadPluginServices() {
|
||||||
|
for (var existingId in _services) {
|
||||||
|
var inst = _services[existingId]
|
||||||
|
if (inst && typeof inst.destroy === "function") inst.destroy()
|
||||||
|
}
|
||||||
|
_services = ({})
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: shell.pluginRegistry
|
target: shell.pluginRegistry
|
||||||
function onPluginsChanged() { shell._syncServices() }
|
function onPluginsChanged() { if (!shell.pluginReloading) shell._syncServices() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes inline settings to a bar layout entry or top-level plugin entry in
|
// Writes inline settings to a bar layout entry or top-level plugin entry in
|
||||||
@@ -484,6 +494,14 @@ ShellRoot {
|
|||||||
panelLoaders = next
|
panelLoaders = next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unloadPanels() {
|
||||||
|
for (var id in panelLoaders) hide(id)
|
||||||
|
panelEntries = []
|
||||||
|
panelLoaders = ({})
|
||||||
|
pendingPayloads = ({})
|
||||||
|
openPanelIds = ({})
|
||||||
|
}
|
||||||
|
|
||||||
function deliverIfLoaded(pluginId) {
|
function deliverIfLoaded(pluginId) {
|
||||||
var loader = panelLoaders[pluginId]
|
var loader = panelLoaders[pluginId]
|
||||||
if (!loader || !loader.item) return
|
if (!loader || !loader.item) return
|
||||||
@@ -549,7 +567,7 @@ ShellRoot {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: shell.pluginRegistry
|
target: shell.pluginRegistry
|
||||||
function onPluginsChanged() { shell.panelEntries = shell.computePanelEntries() }
|
function onPluginsChanged() { if (!shell.pluginReloading) shell.panelEntries = shell.computePanelEntries() }
|
||||||
}
|
}
|
||||||
|
|
||||||
Instantiator {
|
Instantiator {
|
||||||
@@ -606,7 +624,7 @@ ShellRoot {
|
|||||||
// widgets use the same first-party manifest contract as third-party widgets.
|
// widgets use the same first-party manifest contract as third-party widgets.
|
||||||
Connections {
|
Connections {
|
||||||
target: shell.pluginRegistry
|
target: shell.pluginRegistry
|
||||||
function onPluginsChanged() { shell.syncPluginWidgets() }
|
function onPluginsChanged() { if (!shell.pluginReloading) shell.syncPluginWidgets() }
|
||||||
}
|
}
|
||||||
|
|
||||||
property var pluginWidgetComponents: ({})
|
property var pluginWidgetComponents: ({})
|
||||||
@@ -670,6 +688,49 @@ ShellRoot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unloadPluginWidgets() {
|
||||||
|
for (var id in pluginWidgetComponents) shell.barWidgetRegistry.unregister(id)
|
||||||
|
pluginWidgetComponents = ({})
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadPlugins() {
|
||||||
|
if (shell.pluginReloading || shell.pluginRegistry.scanning) {
|
||||||
|
shell.pluginReloadPending = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
shell.pluginReloading = true
|
||||||
|
shell.unloadPanels()
|
||||||
|
shell.unloadPluginServices()
|
||||||
|
shell.unloadPluginWidgets()
|
||||||
|
Qt.callLater(shell.finishPluginReload)
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishPluginReload() {
|
||||||
|
if (!shell.pluginReloading) return
|
||||||
|
if (shell.pluginRegistry.scanning) {
|
||||||
|
shell.pluginReloadPending = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof Qt.clearComponentCache === "function") Qt.clearComponentCache()
|
||||||
|
shell.pluginRegistry.rescan()
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: shell.pluginRegistry
|
||||||
|
function onScanFinished() {
|
||||||
|
if (shell.pluginReloadPending) {
|
||||||
|
shell.pluginReloadPending = false
|
||||||
|
shell.pluginReloading = false
|
||||||
|
Qt.callLater(shell.reloadPlugins)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
shell.pluginReloading = false
|
||||||
|
shell._syncServices()
|
||||||
|
shell.panelEntries = shell.computePanelEntries()
|
||||||
|
shell.syncPluginWidgets()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function loadPluginWidget(registryKey, url, meta) {
|
function loadPluginWidget(registryKey, url, meta) {
|
||||||
var comp = Qt.createComponent(url, Component.Asynchronous)
|
var comp = Qt.createComponent(url, Component.Asynchronous)
|
||||||
function finalize() {
|
function finalize() {
|
||||||
@@ -712,7 +773,7 @@ ShellRoot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function rescanPlugins(): void {
|
function rescanPlugins(): void {
|
||||||
shell.pluginRegistry.rescan()
|
shell.reloadPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadConfig(): string {
|
function reloadConfig(): string {
|
||||||
@@ -720,8 +781,8 @@ ShellRoot {
|
|||||||
return "ok"
|
return "ok"
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPluginEnabled(id: string, enabled: string): void {
|
function setPluginEnabled(id: string, enabled: string): string {
|
||||||
shell.pluginRegistry.setEnabled(id, enabled === "true")
|
return shell.pluginRegistry.setEnabled(id, enabled === "true") ? "ok" : "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
function listPlugins(): string {
|
function listPlugins(): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user