diff --git a/default/quickshell/omarchy-shell/Commons/Color.qml b/default/quickshell/omarchy-shell/Commons/Color.qml index 47cb91b2..56f0949b 100644 --- a/default/quickshell/omarchy-shell/Commons/Color.qml +++ b/default/quickshell/omarchy-shell/Commons/Color.qml @@ -58,76 +58,11 @@ QtObject { property color unselectedBorder: root.pick("image-picker.unselected-border", root.foreground) } - // Noctalia palette tokens used by the compat widgets. Mapped onto the - // foundational palette; not exposed in shell.toml. - readonly property color mPrimary: accent - readonly property color mSecondary: Qt.darker(accent, 1.2) - readonly property color mTertiary: Qt.lighter(accent, 1.3) - readonly property color mSurface: background - readonly property color mSurfaceVariant: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.06) - readonly property color mOnSurface: foreground - readonly property color mOnSurfaceVariant: Qt.darker(foreground, 1.4) - readonly property color mOutline: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.18) - readonly property color mHover: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.14) - readonly property color mOnHover: foreground - readonly property color mError: urgent - readonly property color mOnError: background - - // Plugins read this to know whether to skip mid-flight transitions. We - // don't ship theme transitions ourselves, so it's always false. - readonly property bool isTransitioning: false - function alpha(c, opacity) { if (!c) return Qt.rgba(0, 0, 0, opacity) return Qt.rgba(c.r, c.g, c.b, opacity) } - // Noctalia's smartAlpha picks an alpha based on the host theme's perceived - // contrast. A flat 0.6 reads acceptably across our themes; cheap, no math. - function smartAlpha(c) { - return alpha(c, 0.6) - } - - // adaptiveOpacity takes a 0..1 ratio and clamps it. Plugins use it for - // fade animations relative to a "full" opacity value. - function adaptiveOpacity(value) { - if (value === undefined || value === null) return 1.0 - return Math.max(0, Math.min(1, Number(value))) - } - - // Plugins occasionally pass a color key like "accent" or "primary" via - // their own settings. resolveColorKey returns a color; resolveColorKeyOptional - // returns null/undefined-ish for "none". - function resolveColorKey(key) { - var resolved = resolveColorKeyOptional(key) - return resolved === null ? foreground : resolved - } - - function resolveColorKeyOptional(key) { - var k = String(key || "").toLowerCase() - if (!k || k === "none") return null - switch (k) { - case "primary": return mPrimary - case "secondary": return mSecondary - case "tertiary": return mTertiary - case "surface": return mSurface - case "surfacevariant": return mSurfaceVariant - case "onsurface": return mOnSurface - case "onsurfacevariant": return mOnSurfaceVariant - case "outline": return mOutline - case "hover": return mHover - case "onhover": return mOnHover - case "error": return mError - case "onerror": return mOnError - case "accent": return accent - case "foreground": return foreground - case "background": return background - case "urgent": - case "red": return urgent - } - return Qt.color(k) - } - function loadColors(raw) { var lines = String(raw || "").split("\n") var foundAccent = false diff --git a/default/quickshell/omarchy-shell/Commons/I18n.qml b/default/quickshell/omarchy-shell/Commons/I18n.qml deleted file mode 100644 index 732091b8..00000000 --- a/default/quickshell/omarchy-shell/Commons/I18n.qml +++ /dev/null @@ -1,15 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. Their plugins normally route translations through -// pluginApi.tr(), not this singleton, so the stub here mostly exists to -// satisfy `import qs.Commons` style usage. -QtObject { - readonly property string langCode: "en" - - function tr(key, interp) { return String(key === undefined ? "" : key) } - function trp(key, count, interp) { return String(key === undefined ? "" : key) } - function hasTranslation(key) { return false } - - signal translationsLoaded() -} diff --git a/default/quickshell/omarchy-shell/Commons/Icons.qml b/default/quickshell/omarchy-shell/Commons/Icons.qml deleted file mode 100644 index eae19991..00000000 --- a/default/quickshell/omarchy-shell/Commons/Icons.qml +++ /dev/null @@ -1,88 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. Their plugins resolve Tabler icon names to glyphs via -// this singleton. The original ships ~3000 mappings; for v1 we cover the -// names most commonly used by the plugins in their repo and fall back to -// "?" for unknowns so missing glyphs are visually obvious. -QtObject { - // Tabler icon names → Nerd Font glyphs. Most entries are mdi-* substitutes - // since Omarchy installs JetBrains Mono Nerd Font which includes Material - // Design Icons. Glyph codepoints embedded as actual chars (Python helper - // when the write tool strips multi-byte codepoints in some positions). - readonly property var glyphs: ({ - "bell": "\udb80\udc9a", // mdi-bell - "bell-off": "\udb80\udc9b", // mdi-bell_off - "bell-ring": "\udb80\udd6b", // mdi-bell_ring - "clock": "\udb80\udc50", // mdi-clock - "clock-outline": "\udb80\udc51", // mdi-clock_outline - "wifi": "\udb81\udda9", // mdi-wifi - "wifi-off": "\udb81\uddaa", // mdi-wifi_off - "ethernet": "\udb80\udc02", // mdi-ethernet - "bluetooth": "\udb80\udcaf", // mdi-bluetooth - "bluetooth-off": "\udb80\udcb2", // mdi-bluetooth_off - "volume": "\udb81\udd7e", // mdi-volume_high - "volume-up": "\udb81\udd7e", - "volume-down": "\udb81\udd7f", - "volume-off": "\udb83\udc08", // mdi-volume_variant_off - "volume-mute": "\udb83\udc08", - "headphones": "\udb80\udecb", // mdi-headphones - "microphone": "\udb80\udf6c", // mdi-microphone - "microphone-off": "\udb80\udf6d", // mdi-microphone_off - "play": "\udb81\udc0a", // mdi-play - "pause": "\udb80\udfe4", // mdi-pause - "skip-back": "\udb81\udcae", // mdi-skip_previous - "skip-forward": "\udb81\udcad", // mdi-skip_next - "music": "\udb81\udd5a", // mdi-music - "weather": "\udb81\udd99", // mdi-weather_sunny - "weather-sun": "\udb81\udd99", - "weather-moon": "\udb81\udd94", // mdi-weather_night - "weather-cloud": "\udb81\udd90", // mdi-weather_cloudy - "weather-rain": "\udb81\udd96", // mdi-weather_pouring - "battery": "\udb80\udc83", // mdi-battery - "battery-charging": "\udb80\udc84", - "cpu": "\udb83\udee0", // mdi-cpu_64_bit - "memory": "\udb80\udd5b", // mdi-memory - "tools": "\udb80\udd64", // mdi-tools - "settings": "\udb80\udc93", // mdi-cog - "settings-outline":"\udb80\udcbb", - "power": "\udb80\udc25", // mdi-power - "lock": "\udb80\udd3e", // mdi-lock - "unlock": "\udb80\udd3f", - "refresh": "\udb81\udc50", // mdi-refresh - "ai": "󰚩", // mdi-robot - "robot": "󰚩", - "x": "\udb80\udd56", // mdi-close - "check": "\udb80\udc12", // mdi-check - "chevron-down": "\udb80\udd40", - "chevron-up": "\udb80\udd43", - "chevron-left": "\udb80\udd41", - "chevron-right": "\udb80\udd42", - "plus": "\udb80\udc15", - "minus": "\udb80\udc16", - "leaf": "\udb80\udf2a", - "rocket": "\udb81\udc63", - "balance": "\udb81\uddd1", - "moon": "\udb81\udd94", - "sun": "\udb81\udd99", - "trash": "\udb81\udcd7", // mdi-delete - "edit": "\udb80\udd66", - "search": "\udb80\udd6f", - "menu": "\udb80\udd6c", - "home": "\udb80\udf0c", - "folder": "\udb80\udd99", - "file": "\udb80\udd97", - "calendar": "\udb80\udcf7", // mdi-calendar - "circle": "\udb80\udd2f" - }) - - function get(name) { - var key = String(name || "").toLowerCase() - if (glyphs[key]) return glyphs[key] - return "?" - } - - function has(name) { - return !!glyphs[String(name || "").toLowerCase()] - } -} diff --git a/default/quickshell/omarchy-shell/Commons/Logger.qml b/default/quickshell/omarchy-shell/Commons/Logger.qml deleted file mode 100644 index bac5f9c7..00000000 --- a/default/quickshell/omarchy-shell/Commons/Logger.qml +++ /dev/null @@ -1,19 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. Plugins call Logger.d/i/w/e routinely. -QtObject { - function format(args) { - var out = [] - for (var i = 0; i < args.length; i++) { - var a = args[i] - out.push(a === undefined ? "undefined" : (a === null ? "null" : String(a))) - } - return out.join(" ") - } - - function d() { console.debug(format(arguments)) } - function i() { console.info(format(arguments)) } - function w() { console.warn(format(arguments)) } - function e() { console.error(format(arguments)) } -} diff --git a/default/quickshell/omarchy-shell/Commons/Settings.qml b/default/quickshell/omarchy-shell/Commons/Settings.qml deleted file mode 100644 index 015ffd4f..00000000 --- a/default/quickshell/omarchy-shell/Commons/Settings.qml +++ /dev/null @@ -1,62 +0,0 @@ -pragma Singleton -import QtQuick -import Quickshell -import Quickshell.Io - -// Noctalia compat shim. Their plugins read Settings.data.* and call -// Settings.getBarPositionForScreen / getBarWidgetsForScreen. We expose a -// read-only view of our shell.json so reads succeed; writes are best routed -// through pluginApi.saveSettings() instead. -QtObject { - id: root - - property var data: ({ - bar: { - position: "top", - widgets: { left: [], center: [], right: [] } - }, - general: {}, - colorSchemes: { darkMode: true } - }) - - // Plugins use this gate around verbose debug logging. Defaulting to false - // keeps their Logger.d calls silent unless we explicitly flip it. - property bool isDebug: false - - // shellConfig is wired by shell.qml at startup; reading it keeps Settings.data - // in lockstep with the live shell config. - property var shellConfig: null - onShellConfigChanged: rebuildData() - - function rebuildData() { - var sc = shellConfig || {} - var bar = (sc && sc.bar) ? sc.bar : {} - var layout = (bar && bar.layout) ? bar.layout : {} - data = { - bar: { - position: bar.position || "top", - centerAnchor: bar.centerAnchor || "", - fontFamily: bar.fontFamily || "JetBrainsMono Nerd Font", - widgets: { - left: Array.isArray(layout.left) ? layout.left : [], - center: Array.isArray(layout.center) ? layout.center : [], - right: Array.isArray(layout.right) ? layout.right : [] - } - }, - general: {}, - colorSchemes: { darkMode: true } - } - } - - function getBarPositionForScreen(name) { - return data && data.bar ? data.bar.position : "top" - } - - function getBarWidgetsForScreen(name) { - return data && data.bar ? data.bar.widgets : { left: [], center: [], right: [] } - } - - function getScreenOverrideEntry(name) { return null } - function hasScreenOverride(name, key) { return false } - function setScreenOverride(name, key, value) { /* not supported */ } -} diff --git a/default/quickshell/omarchy-shell/Commons/ShellState.qml b/default/quickshell/omarchy-shell/Commons/ShellState.qml deleted file mode 100644 index a853e792..00000000 --- a/default/quickshell/omarchy-shell/Commons/ShellState.qml +++ /dev/null @@ -1,12 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. Their plugins occasionally introspect global UI -// state through this singleton. Returning safe defaults lets reads succeed -// without us tracking the state ourselves. -QtObject { - readonly property bool isLoaded: true - property var activeScreen: null - property bool launcherOpen: false - property bool settingsOpen: false -} diff --git a/default/quickshell/omarchy-shell/Commons/Style.qml b/default/quickshell/omarchy-shell/Commons/Style.qml deleted file mode 100644 index 2e6b268c..00000000 --- a/default/quickshell/omarchy-shell/Commons/Style.qml +++ /dev/null @@ -1,104 +0,0 @@ -pragma Singleton -import QtQuick -import "." as Commons - -// Noctalia compat shim. Sizing/spacing/typography tokens that plugins -// reference unconditionally. Real values picked to roughly match what -// Omarchy renders today — close enough that plugin layouts don't look -// jarring next to native widgets. -QtObject { - id: root - - // Radii. Omarchy uses sharp corners; all radius tokens collapse to 0 so - // Noctalia plugins inherit the same look as native widgets. - readonly property real radiusXXS: 0 - readonly property real radiusXS: 0 - readonly property real radiusS: 0 - readonly property real radiusM: 0 - readonly property real radiusL: 0 - readonly property real radiusXL: 0 - readonly property real iRadiusXS: 0 - readonly property real iRadiusS: 0 - readonly property real iRadiusM: 0 - readonly property real iRadiusL: 0 - - // Margins / paddings. Noctalia uses two parallel scales for margins (one - // tighter, one looser); we map them to the same values. - readonly property real marginXXS: 1 - readonly property real marginXS: 2 - readonly property real marginS: 4 - readonly property real marginM: 6 - readonly property real marginL: 10 - readonly property real marginXL: 14 - readonly property real margin2XXS: 1 - readonly property real margin2XS: 2 - readonly property real margin2S: 3 - readonly property real margin2M: 5 - readonly property real margin2L: 8 - readonly property real margin2XL: 12 - - // Border widths. - readonly property real borderS: 1 - readonly property real borderM: 1 - readonly property real borderL: 2 - - // Font sizes. - readonly property real fontSizeXXS: 9 - readonly property real fontSizeXS: 10 - readonly property real fontSizeS: 11 - readonly property real fontSizeM: 12 - readonly property real fontSizeL: 14 - readonly property real fontSizeXL: 16 - readonly property real fontSizeXXL: 20 - readonly property real fontSizeXXXL: 24 - - // Font weights. - readonly property int fontWeightLight: 300 - readonly property int fontWeightRegular: 400 - readonly property int fontWeightMedium: 500 - readonly property int fontWeightSemiBold: 600 - readonly property int fontWeightBold: 700 - - // Opacities. - readonly property real opacityLight: 0.4 - readonly property real opacityMedium: 0.6 - readonly property real opacityHeavy: 0.8 - readonly property real opacityFull: 1.0 - - // Animation durations (ms). - readonly property int animationFast: 100 - readonly property int animationNormal: 160 - readonly property int animationSlow: 250 - readonly property int animationSlower: 400 - readonly property int animationSlowest: 600 - - // Tooltip delay (ms). - readonly property int tooltipDelay: 400 - - // Capsule (bar pill) tokens. The Omarchy bar is flat, no capsule background, - // so we route these to transparent + the foreground outline for visual hint. - readonly property color capsuleColor: Qt.rgba(0, 0, 0, 0) - readonly property color capsuleBorderColor: Qt.rgba(0, 0, 0, 0) - readonly property real capsuleBorderWidth: 0 - readonly property real baseWidgetSize: 22 - - // UI scale ratio. Noctalia plugins multiply lengths by this when - // applyUiScale is true; we leave it at 1. - readonly property real uiScaleRatio: 1.0 - - // Helpers. Plugins pass screen names but in practice we ignore them and - // return our shell-wide values. - function getBarHeightForScreen(name) { return 28 } - function getCapsuleHeightForScreen(name) { return 22 } - function getBarFontSizeForScreen(name) { return fontSizeM } - function getBarRadiusForScreen(name) { return radiusM } - - function pixelAlignCenter(parentSize, childSize) { - return Math.round((Number(parentSize) - Number(childSize)) / 2) - } - - function toOdd(n) { - var i = Math.floor(Number(n)) - return i | 1 - } -} diff --git a/default/quickshell/omarchy-shell/Commons/ThemeIcons.qml b/default/quickshell/omarchy-shell/Commons/ThemeIcons.qml deleted file mode 100644 index 59d974bc..00000000 --- a/default/quickshell/omarchy-shell/Commons/ThemeIcons.qml +++ /dev/null @@ -1,10 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. Their plugins resolve app icons through XDG icon -// themes; we don't ship that machinery, so iconForAppId returns empty and -// callers fall back to their default glyph. -QtObject { - function iconFromName(name) { return "" } - function iconForAppId(appId) { return "" } -} diff --git a/default/quickshell/omarchy-shell/Commons/Time.qml b/default/quickshell/omarchy-shell/Commons/Time.qml deleted file mode 100644 index 9c549ff2..00000000 --- a/default/quickshell/omarchy-shell/Commons/Time.qml +++ /dev/null @@ -1,18 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. -QtObject { - function getFormattedTimestamp() { - return new Date().toISOString() - } - - function nowMs() { - return Date.now() - } - - function formatDate(date, fmt) { - if (!date) date = new Date() - return Qt.formatDateTime(date, fmt || "yyyy-MM-dd HH:mm:ss") - } -} diff --git a/default/quickshell/omarchy-shell/Commons/qmldir b/default/quickshell/omarchy-shell/Commons/qmldir index f34bcbe4..2f66f1fb 100644 --- a/default/quickshell/omarchy-shell/Commons/qmldir +++ b/default/quickshell/omarchy-shell/Commons/qmldir @@ -1,10 +1,2 @@ module qs.Commons singleton Color 1.0 Color.qml -singleton Style 1.0 Style.qml -singleton Logger 1.0 Logger.qml -singleton Settings 1.0 Settings.qml -singleton I18n 1.0 I18n.qml -singleton Time 1.0 Time.qml -singleton Icons 1.0 Icons.qml -singleton ThemeIcons 1.0 ThemeIcons.qml -singleton ShellState 1.0 ShellState.qml diff --git a/default/quickshell/omarchy-shell/Services/Power/PowerProfileService.qml b/default/quickshell/omarchy-shell/Services/Power/PowerProfileService.qml deleted file mode 100644 index cca40490..00000000 --- a/default/quickshell/omarchy-shell/Services/Power/PowerProfileService.qml +++ /dev/null @@ -1,9 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. The "noctaliaPerformanceMode" flag was a v3 internal -// state Noctalia plugins occasionally check. We hardcode false; plugins that -// branch on it will fall into the non-performance code path. -QtObject { - readonly property bool noctaliaPerformanceMode: false -} diff --git a/default/quickshell/omarchy-shell/Services/Power/qmldir b/default/quickshell/omarchy-shell/Services/Power/qmldir deleted file mode 100644 index f9e714fc..00000000 --- a/default/quickshell/omarchy-shell/Services/Power/qmldir +++ /dev/null @@ -1,2 +0,0 @@ -module qs.Services.Power -singleton PowerProfileService 1.0 PowerProfileService.qml diff --git a/default/quickshell/omarchy-shell/Services/System/HostService.qml b/default/quickshell/omarchy-shell/Services/System/HostService.qml deleted file mode 100644 index 835a807f..00000000 --- a/default/quickshell/omarchy-shell/Services/System/HostService.qml +++ /dev/null @@ -1,36 +0,0 @@ -pragma Singleton -import QtQuick -import Quickshell -import Quickshell.Io - -// Noctalia compat shim. activate-linux and a few other plugins display -// distro/host info. We parse /etc/os-release lazily. -QtObject { - id: root - - property string osPretty: "" - property string osName: "" - property string osVersion: "" - property string hostname: "" - - property Process osReleaseProc: Process { - command: ["bash", "-c", - "( . /etc/os-release && printf '%s\\t%s\\t%s\\n' \"$PRETTY_NAME\" \"$NAME\" \"$VERSION_ID\" ); hostname"] - onExited: { - var text = String(osReleaseStdout.text || "").trim().split("\n") - if (text.length >= 1) { - var fields = text[0].split("\t") - root.osPretty = fields[0] || "" - root.osName = fields[1] || "" - root.osVersion = fields[2] || "" - } - if (text.length >= 2) root.hostname = text[1].trim() - } - stdout: StdioCollector { - id: osReleaseStdout - waitForEnd: true - } - } - - Component.onCompleted: osReleaseProc.running = true -} diff --git a/default/quickshell/omarchy-shell/Services/System/qmldir b/default/quickshell/omarchy-shell/Services/System/qmldir deleted file mode 100644 index 2d3fbbc6..00000000 --- a/default/quickshell/omarchy-shell/Services/System/qmldir +++ /dev/null @@ -1,2 +0,0 @@ -module qs.Services.System -singleton HostService 1.0 HostService.qml diff --git a/default/quickshell/omarchy-shell/Services/UI/BarService.qml b/default/quickshell/omarchy-shell/Services/UI/BarService.qml deleted file mode 100644 index 5d12a0f9..00000000 --- a/default/quickshell/omarchy-shell/Services/UI/BarService.qml +++ /dev/null @@ -1,80 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. Plugin bar widgets call into BarService for -// registration bookkeeping and to ask the host where their tooltip should -// pop. We track the bar reference (set by the host at startup) and expose -// the helpers plugins actually use. -QtObject { - id: root - - // Wired by omarchy-shell's Bar.qml on construction so we can reach back - // for things like position and the tooltip popup. - property var bar: null - - // Plugins read this when computing their own layout-revision markers. - property int widgetsRevision: 0 - - // Bookkeeping for plugin widgets. Not consulted by Omarchy itself; some - // plugins call lookupWidget() to coordinate across instances. - property var registered: ({}) - - function registerWidget(screen, section, widgetId, index, item) { - var key = (screen && screen.name ? screen.name : "_") + ":" + section + ":" + widgetId + ":" + index - var next = {} - for (var k in registered) next[k] = registered[k] - next[key] = { screen: screen, section: section, widgetId: widgetId, index: index, item: item } - registered = next - widgetsRevision++ - } - - function unregisterWidget(screen, section, widgetId, index) { - var key = (screen && screen.name ? screen.name : "_") + ":" + section + ":" + widgetId + ":" + index - if (!registered[key]) return - var next = {} - for (var k in registered) if (k !== key) next[k] = registered[k] - registered = next - widgetsRevision++ - } - - // Plugins occasionally search for a sibling widget by id. We do a linear - // scan since the registry is small in practice. - function lookupWidget(widgetId) { - for (var k in registered) { - if (registered[k].widgetId === widgetId) return registered[k] - } - return undefined - } - - // Translate the bar's edge to a tooltip direction string. Noctalia expects - // strings; the Omarchy bar drives its own tooltip popup off bar.position. - function getTooltipDirection(screenName) { - var pos = bar ? bar.position : "top" - if (pos === "top") return "down" - if (pos === "bottom") return "up" - if (pos === "left") return "right" - if (pos === "right") return "left" - return "down" - } - - function getPillDirection(item) { - var pos = bar ? bar.position : "top" - return pos === "left" || pos === "right" ? "horizontal" : "vertical" - } - - // Forward into the omarchy-shell host. Most Noctalia widgets call this - // from a right-click handler. - function openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings) { - if (bar && bar.shell && typeof bar.shell.summon === "function") { - bar.shell.summon("omarchy.settings", - JSON.stringify({ focusWidgetId: widgetId, section: section, index: sectionWidgetIndex })) - } - } - - function openPluginSettings(screen, manifest) { - if (bar && bar.shell && typeof bar.shell.summon === "function") { - bar.shell.summon("omarchy.settings", - JSON.stringify({ focusPluginId: manifest ? manifest.id : "" })) - } - } -} diff --git a/default/quickshell/omarchy-shell/Services/UI/PanelService.qml b/default/quickshell/omarchy-shell/Services/UI/PanelService.qml deleted file mode 100644 index 6892682f..00000000 --- a/default/quickshell/omarchy-shell/Services/UI/PanelService.qml +++ /dev/null @@ -1,34 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. Plugins use this for context menus and to reach -// the host's panels. We forward into the omarchy-shell host where there's -// a matching surface, and log a warning for anything we don't ship in v1. -QtObject { - id: root - - property var bar: null - property var shell: null - - // Plugins call this on right-click with a Menu component. The Noctalia - // implementation parents the menu to the screen's overlay; we simply - // call `open()` on whatever the plugin passes — most plugin context menus - // are plain QtQuick `Menu` items that know how to show themselves. - function showContextMenu(menu, item, screen) { - if (!menu) return - if (typeof menu.popup === "function") menu.popup() - else if (typeof menu.open === "function") menu.open() - } - - function closeContextMenu(screen) { - // No-op; the Menu / PopupWindow closes itself on outside click. - } - - function openLauncherWithSearch(screen, prefix) { - console.warn("PanelService.openLauncherWithSearch is not supported in the Omarchy compat layer") - } - - function getPanel(name, screen) { - return null - } -} diff --git a/default/quickshell/omarchy-shell/Services/UI/TooltipService.qml b/default/quickshell/omarchy-shell/Services/UI/TooltipService.qml deleted file mode 100644 index 6d9ba26d..00000000 --- a/default/quickshell/omarchy-shell/Services/UI/TooltipService.qml +++ /dev/null @@ -1,30 +0,0 @@ -pragma Singleton -import QtQuick - -// Noctalia compat shim. Their plugins call TooltipService.show(item, text) -// on hover; we route that into the Omarchy bar's shared tooltip popup. -QtObject { - id: root - - // Wired by Bar.qml on construction. - property var bar: null - - function show(item, text, direction) { - if (!bar || !item) return - if (typeof bar.showTooltip === "function") { - bar.showTooltip(item, text || "") - } - } - - function hide(item) { - if (!bar) return - if (item && typeof bar.hideTooltip === "function") { - bar.hideTooltip(item) - } else if (bar.tooltipTarget) { - // Noctalia plugins often call TooltipService.hide() without the source - // item on click. Native bar.hideTooltip(target) ignores null targets, so - // clear the currently visible tooltip explicitly. - bar.hideTooltip(bar.tooltipTarget) - } - } -} diff --git a/default/quickshell/omarchy-shell/Services/UI/qmldir b/default/quickshell/omarchy-shell/Services/UI/qmldir deleted file mode 100644 index d36460cb..00000000 --- a/default/quickshell/omarchy-shell/Services/UI/qmldir +++ /dev/null @@ -1,4 +0,0 @@ -module qs.Services.UI -singleton BarService 1.0 BarService.qml -singleton TooltipService 1.0 TooltipService.qml -singleton PanelService 1.0 PanelService.qml diff --git a/default/quickshell/omarchy-shell/Widgets/NBox.qml b/default/quickshell/omarchy-shell/Widgets/NBox.qml deleted file mode 100644 index b3bb2b7f..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NBox.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQuick -import qs.Commons - -Rectangle { - color: Color.mSurfaceVariant - border.color: Color.mOutline - border.width: Style.borderS - radius: Style.radiusM -} diff --git a/default/quickshell/omarchy-shell/Widgets/NButton.qml b/default/quickshell/omarchy-shell/Widgets/NButton.qml deleted file mode 100644 index db709429..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NButton.qml +++ /dev/null @@ -1,26 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Commons - -Button { - id: root - property string label: "" - property color colorBg: Color.mSurfaceVariant - property color colorFg: Color.mOnSurface - - text: label || "" - background: Rectangle { - color: root.hovered ? Color.mHover : root.colorBg - radius: Style.radiusM - border.color: Color.mOutline - border.width: Style.borderS - } - contentItem: Text { - text: root.text - color: root.colorFg - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: Style.fontSizeM - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/NCheckbox.qml b/default/quickshell/omarchy-shell/Widgets/NCheckbox.qml deleted file mode 100644 index 9b0b8633..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NCheckbox.qml +++ /dev/null @@ -1,11 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Commons - -CheckBox { - id: root - property string label: "" - text: label || "" - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: Style.fontSizeS -} diff --git a/default/quickshell/omarchy-shell/Widgets/NComboBox.qml b/default/quickshell/omarchy-shell/Widgets/NComboBox.qml deleted file mode 100644 index 821cdac3..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NComboBox.qml +++ /dev/null @@ -1,138 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Commons - -ComboBox { - id: root - - property string label: "" - // Noctalia's NComboBox API commonly uses a model of { key, name } objects, - // a currentKey property, and an onSelected(key, item) handler. Qt's ComboBox - // only knows currentIndex/currentText, so bridge the small API surface here. - property string currentKey: "" - - signal selected(var key, var item) - - textRole: "name" - valueRole: "key" - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: Style.fontSizeS - implicitHeight: 32 - leftPadding: 10 - rightPadding: 28 - topPadding: 4 - bottomPadding: 4 - - function itemAt(index) { - if (index < 0) return null - if (Array.isArray(root.model)) return root.model[index] || null - return null - } - - function keyAt(index) { - var item = itemAt(index) - if (!item) return "" - if (item.key !== undefined && item.key !== null) return String(item.key) - if (item.value !== undefined && item.value !== null) return String(item.value) - if (item.name !== undefined && item.name !== null) return String(item.name) - return String(item) - } - - function syncCurrentIndex() { - if (!Array.isArray(root.model)) return - for (var i = 0; i < root.model.length; i++) { - if (keyAt(i) === String(root.currentKey || "")) { - if (root.currentIndex !== i) root.currentIndex = i - return - } - } - if (root.model.length > 0 && root.currentIndex < 0) root.currentIndex = 0 - } - - Component.onCompleted: syncCurrentIndex() - onCurrentKeyChanged: syncCurrentIndex() - onModelChanged: syncCurrentIndex() - - onActivated: function(index) { - var item = itemAt(index) - var key = keyAt(index) - root.currentKey = key - root.selected(key, item) - } - - contentItem: Text { - text: root.displayText - color: Color.mOnSurface - font: root.font - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - background: Rectangle { - color: Color.mSurfaceVariant - border.color: root.activeFocus ? Color.mPrimary : Color.mOutline - border.width: Style.borderS - radius: 0 - } - - indicator: Text { - x: root.width - width - 10 - y: (root.height - height) / 2 - text: "▾" - color: Color.mOnSurfaceVariant - font.family: root.font.family - font.pixelSize: 11 - } - - delegate: ItemDelegate { - id: del - required property int index - required property var modelData - width: ListView.view ? ListView.view.width : root.width - height: 28 - highlighted: root.highlightedIndex === del.index - - contentItem: Text { - text: { - var m = del.modelData - if (m === null || m === undefined) return "" - if (root.textRole && m[root.textRole] !== undefined) return String(m[root.textRole]) - return String(m) - } - color: Color.mOnSurface - font: root.font - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - background: Rectangle { - color: del.highlighted || del.hovered - ? Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.14) - : "transparent" - radius: 0 - } - } - - popup: Popup { - y: root.height - width: root.width - implicitHeight: Math.min(contentItem.implicitHeight + 2, 280) - padding: 1 - - background: Rectangle { - color: Color.mSurface - border.color: Color.mOutline - border.width: Style.borderS - radius: 0 - } - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: root.popup.visible ? root.delegateModel : null - currentIndex: root.highlightedIndex - boundsBehavior: Flickable.StopAtBounds - ScrollIndicator.vertical: ScrollIndicator { } - } - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/NDivider.qml b/default/quickshell/omarchy-shell/Widgets/NDivider.qml deleted file mode 100644 index 7bbe406f..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NDivider.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick -import qs.Commons - -Rectangle { - implicitHeight: 1 - color: Color.mOutline - opacity: 0.4 -} diff --git a/default/quickshell/omarchy-shell/Widgets/NIcon.qml b/default/quickshell/omarchy-shell/Widgets/NIcon.qml deleted file mode 100644 index 0f0710e2..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NIcon.qml +++ /dev/null @@ -1,18 +0,0 @@ -import QtQuick -import qs.Commons - -// Noctalia compat shim. Renders an icon by name through the Icons map. -Text { - id: root - property string icon: "" - property real pointSize: 14 - property bool applyUiScale: true - - text: Icons.get(icon) - color: Color.mOnSurface - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: Math.round(pointSize * (applyUiScale ? Style.uiScaleRatio : 1)) - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - renderType: Text.NativeRendering -} diff --git a/default/quickshell/omarchy-shell/Widgets/NIconButton.qml b/default/quickshell/omarchy-shell/Widgets/NIconButton.qml deleted file mode 100644 index 5f7607b2..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NIconButton.qml +++ /dev/null @@ -1,67 +0,0 @@ -import QtQuick -import qs.Commons -import qs.Services.UI - -// Noctalia compat shim. The widget plugins lean on this heavily; it's the -// primary clickable bar element. We mimic Noctalia's surface area -// (icon/text/tooltip/colors/border) but skip the long tail of properties -// no observed plugin actually sets. -Rectangle { - id: root - - property string icon: "" - property string text: "" - property string tooltipText: "" - property string tooltipDirection: "" - property real baseSize: Style.baseWidgetSize - property bool applyUiScale: false - property real customRadius: -1 - property color colorBg: Style.capsuleColor - property color colorFg: Color.mOnSurface - property color colorBgHover: Color.mHover - property color colorFgHover: Color.mOnHover - property color colorBorder: Style.capsuleBorderColor - property color colorBorderHover: Style.capsuleBorderColor - property bool flat: false - - signal clicked() - signal rightClicked() - signal middleClicked() - signal wheel(int delta) - - readonly property real effectiveSize: Math.round(baseSize * (applyUiScale ? Style.uiScaleRatio : 1)) - - implicitWidth: effectiveSize - implicitHeight: effectiveSize - radius: customRadius >= 0 ? customRadius : Style.radiusM - color: mouse.containsMouse ? colorBgHover : colorBg - border.color: mouse.containsMouse ? colorBorderHover : colorBorder - border.width: Style.capsuleBorderWidth - - Behavior on color { ColorAnimation { duration: Style.animationFast } } - - Text { - anchors.centerIn: parent - text: root.icon ? Icons.get(root.icon) : root.text - color: mouse.containsMouse ? root.colorFgHover : root.colorFg - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: Math.round(Style.fontSizeM * (root.applyUiScale ? Style.uiScaleRatio : 1)) - Behavior on color { ColorAnimation { duration: Style.animationFast } } - } - - MouseArea { - id: mouse - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton - onEntered: if (root.tooltipText) TooltipService.show(root, root.tooltipText, root.tooltipDirection) - onExited: TooltipService.hide(root) - onClicked: function(m) { - if (m.button === Qt.RightButton) root.rightClicked() - else if (m.button === Qt.MiddleButton) root.middleClicked() - else root.clicked() - } - onWheel: function(w) { root.wheel(w.angleDelta.y) } - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/NPopupContextMenu.qml b/default/quickshell/omarchy-shell/Widgets/NPopupContextMenu.qml deleted file mode 100644 index 4df8a9e5..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NPopupContextMenu.qml +++ /dev/null @@ -1,27 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Commons - -// Noctalia compat shim. Plugins instantiate this with a model of action -// objects and expect `triggered(action)` when a row is picked. -Menu { - id: root - - property var model: [] - // Noctalia passes the owning ShellScreen through here. Qt Quick Controls - // Menu doesn't need it, but accepting the property keeps plugin QML from - // failing at load time. - property var screen: null - signal triggered(var action, var item) - - Instantiator { - model: root.model - delegate: MenuItem { - required property var modelData - text: modelData ? String(modelData.label || modelData.action || "") : "" - onTriggered: root.triggered(modelData ? String(modelData.action || "") : "", modelData) - } - onObjectAdded: function(index, object) { root.insertItem(index, object) } - onObjectRemoved: function(index, object) { root.removeItem(object) } - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/NScrollText.qml b/default/quickshell/omarchy-shell/Widgets/NScrollText.qml deleted file mode 100644 index 806e9aee..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NScrollText.qml +++ /dev/null @@ -1,55 +0,0 @@ -import QtQuick -import qs.Commons - -// Noctalia compat shim. Auto-scrolling label. -Item { - id: root - - property string text: "" - property real maxWidth: 200 - property real pointSize: Style.fontSizeM - property int scrollMode: NScrollText.ScrollMode.Hover - property bool forcedHover: false - property real fadeExtent: 0.1 - property real fadeCornerRadius: 0 - property bool fadeRoundLeftCorners: false - property bool fadeRoundRightCorners: false - property color textColor: Color.mOnSurface - - enum ScrollMode { Always, Hover, Never } - - implicitHeight: label.implicitHeight - implicitWidth: Math.min(maxWidth, label.implicitWidth) - clip: true - - readonly property bool overflowing: label.implicitWidth > width - readonly property bool shouldScroll: { - if (!overflowing) return false - if (scrollMode === NScrollText.ScrollMode.Always) return true - if (scrollMode === NScrollText.ScrollMode.Hover) return forcedHover - return false - } - - Text { - id: label - text: root.text - color: root.textColor - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: root.pointSize - verticalAlignment: Text.AlignVCenter - height: parent.height - anchors.verticalCenter: parent.verticalCenter - - NumberAnimation on x { - id: scrollAnim - running: root.shouldScroll - loops: Animation.Infinite - duration: Math.max(5000, label.implicitWidth * 22) - from: root.width - to: -label.implicitWidth - easing.type: Easing.Linear - } - - onShouldScrollChanged: if (!root.shouldScroll) x = 0 - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/NSlider.qml b/default/quickshell/omarchy-shell/Widgets/NSlider.qml deleted file mode 100644 index 4cc311f9..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NSlider.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Commons - -Slider { - id: root - property string label: "" -} diff --git a/default/quickshell/omarchy-shell/Widgets/NSpinBox.qml b/default/quickshell/omarchy-shell/Widgets/NSpinBox.qml deleted file mode 100644 index 7b41a4b8..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NSpinBox.qml +++ /dev/null @@ -1,75 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Commons - -SpinBox { - id: root - property string label: "" - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: Style.fontSizeS - editable: true - implicitHeight: 32 - leftPadding: 10 - rightPadding: 24 - topPadding: 4 - bottomPadding: 4 - - background: Rectangle { - color: Color.mSurfaceVariant - border.color: root.activeFocus ? Color.mPrimary : Color.mOutline - border.width: Style.borderS - radius: Style.radiusS - } - - contentItem: TextInput { - text: root.displayText - font: root.font - color: Color.mOnSurface - selectionColor: Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.35) - selectedTextColor: Color.mOnSurface - horizontalAlignment: Qt.AlignLeft - verticalAlignment: Qt.AlignVCenter - readOnly: !root.editable - validator: root.validator - inputMethodHints: Qt.ImhFormattedNumbersOnly - } - - up.indicator: Rectangle { - x: root.mirrored ? 0 : root.width - width - width: 20 - height: root.height / 2 - color: root.up.pressed - ? Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.16) - : root.up.hovered - ? Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.08) - : "transparent" - radius: Style.radiusS - Text { - anchors.centerIn: parent - text: "▲" - font.family: root.font.family - font.pixelSize: 7 - color: root.up.hovered ? Color.mOnSurface : Color.mOnSurfaceVariant - } - } - - down.indicator: Rectangle { - x: root.mirrored ? 0 : root.width - width - y: root.height / 2 - width: 20 - height: root.height / 2 - color: root.down.pressed - ? Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.16) - : root.down.hovered - ? Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.08) - : "transparent" - radius: Style.radiusS - Text { - anchors.centerIn: parent - text: "▼" - font.family: root.font.family - font.pixelSize: 7 - color: root.down.hovered ? Color.mOnSurface : Color.mOnSurfaceVariant - } - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/NTabBar.qml b/default/quickshell/omarchy-shell/Widgets/NTabBar.qml deleted file mode 100644 index 0df85cde..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NTabBar.qml +++ /dev/null @@ -1,26 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import qs.Commons - -Rectangle { - id: root - - property int currentIndex: 0 - property int margins: Style.marginXS - property bool distributeEvenly: false - - implicitHeight: 32 - radius: Style.radiusS - color: Color.mSurfaceVariant - border.color: Color.mOutline - border.width: Style.borderS - - default property alias content: row.children - - RowLayout { - id: row - anchors.fill: parent - anchors.margins: root.margins - spacing: Style.marginXS - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/NTabButton.qml b/default/quickshell/omarchy-shell/Widgets/NTabButton.qml deleted file mode 100644 index 86e094f3..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NTabButton.qml +++ /dev/null @@ -1,46 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import qs.Commons - -Rectangle { - id: root - - property string text: "" - property int tabIndex: -1 - property bool checked: false - - signal clicked() - - Layout.fillWidth: true - implicitWidth: label.implicitWidth + Style.marginL * 2 - implicitHeight: 26 - radius: Style.radiusXS - color: area.containsMouse - ? Color.mHover - : (checked ? Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.18) : "transparent") - border.color: checked ? Color.mPrimary : "transparent" - border.width: checked ? 1 : 0 - - Behavior on color { ColorAnimation { duration: Style.animationFast } } - - Text { - id: label - anchors.centerIn: parent - text: root.text - color: checked ? Color.mPrimary : Color.mOnSurface - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: Style.fontSizeS - font.bold: checked - elide: Text.ElideRight - width: Math.max(0, parent.width - Style.marginM * 2) - horizontalAlignment: Text.AlignHCenter - } - - MouseArea { - id: area - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: root.clicked() - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/NText.qml b/default/quickshell/omarchy-shell/Widgets/NText.qml deleted file mode 100644 index 84e4aebc..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NText.qml +++ /dev/null @@ -1,19 +0,0 @@ -import QtQuick -import qs.Commons - -// Noctalia compat shim. The original supports pointSize (interpreted as -// pixel size in Noctalia) plus an applyUiScale flag. We translate to -// font.pixelSize directly; uiScaleRatio comes from Style. -Text { - id: root - property real pointSize: 12 - property bool applyUiScale: true - property var customFont: null - - color: Color.mOnSurface - font.family: customFont || "JetBrainsMono Nerd Font" - font.pixelSize: Math.round(pointSize * (applyUiScale ? Style.uiScaleRatio : 1)) - font.weight: Style.fontWeightRegular - verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering -} diff --git a/default/quickshell/omarchy-shell/Widgets/NTextInput.qml b/default/quickshell/omarchy-shell/Widgets/NTextInput.qml deleted file mode 100644 index 234b332d..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NTextInput.qml +++ /dev/null @@ -1,20 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Commons - -TextField { - id: root - property string label: "" - property string description: "" - property string defaultValue: "" - - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: Style.fontSizeS - color: Color.mOnSurface - background: Rectangle { - color: Color.mSurfaceVariant - border.color: root.focus ? Color.mPrimary : Color.mOutline - border.width: Style.borderS - radius: Style.radiusS - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/NToggle.qml b/default/quickshell/omarchy-shell/Widgets/NToggle.qml deleted file mode 100644 index e1badacd..00000000 --- a/default/quickshell/omarchy-shell/Widgets/NToggle.qml +++ /dev/null @@ -1,68 +0,0 @@ -import QtQuick -import qs.Commons - -Rectangle { - id: root - - property string label: "" - property string text: "" - property bool checked: false - - signal toggled(var value) - signal clicked() - - implicitWidth: labelItem.visible ? indicator.width + 8 + labelItem.implicitWidth : indicator.width - implicitHeight: Math.max(indicator.height, labelItem.implicitHeight) - color: "transparent" - opacity: enabled ? 1 : 0.4 - - Rectangle { - id: indicator - width: 48 - height: 22 - anchors.verticalCenter: parent.verticalCenter - radius: 0 - color: root.checked - ? Color.mPrimary - : Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.10) - border.color: root.checked - ? Color.mPrimary - : Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.4) - border.width: Style.borderS - - Rectangle { - width: 16 - height: 16 - radius: 0 - anchors.verticalCenter: parent.verticalCenter - x: root.checked ? parent.width - width - 3 : 3 - color: root.checked ? Color.mSurface : Color.mOnSurface - Behavior on x { NumberAnimation { duration: Style.animationFast } } - } - } - - Text { - id: labelItem - visible: (root.label || root.text) !== "" - anchors.left: indicator.right - anchors.leftMargin: 8 - anchors.verticalCenter: parent.verticalCenter - text: root.label || root.text - color: Color.mOnSurface - font.family: "JetBrainsMono Nerd Font" - font.pixelSize: Style.fontSizeS - verticalAlignment: Text.AlignVCenter - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - enabled: root.enabled - cursorShape: Qt.PointingHandCursor - onClicked: { - root.checked = !root.checked - root.clicked() - root.toggled(root.checked) - } - } -} diff --git a/default/quickshell/omarchy-shell/Widgets/qmldir b/default/quickshell/omarchy-shell/Widgets/qmldir deleted file mode 100644 index 5366b1ce..00000000 --- a/default/quickshell/omarchy-shell/Widgets/qmldir +++ /dev/null @@ -1,17 +0,0 @@ -module qs.Widgets -NText 1.0 NText.qml -NIcon 1.0 NIcon.qml -NIconButton 1.0 NIconButton.qml -NBox 1.0 NBox.qml -NButton 1.0 NButton.qml -NPopupContextMenu 1.0 NPopupContextMenu.qml -NScrollText 1.0 NScrollText.qml -NToggle 1.0 NToggle.qml -NSpinBox 1.0 NSpinBox.qml -NSlider 1.0 NSlider.qml -NTextInput 1.0 NTextInput.qml -NComboBox 1.0 NComboBox.qml -NCheckbox 1.0 NCheckbox.qml -NDivider 1.0 NDivider.qml -NTabBar 1.0 NTabBar.qml -NTabButton 1.0 NTabButton.qml diff --git a/default/quickshell/omarchy-shell/compat/noctalia/PanelWrapper.qml b/default/quickshell/omarchy-shell/compat/noctalia/PanelWrapper.qml deleted file mode 100644 index 8c04f95e..00000000 --- a/default/quickshell/omarchy-shell/compat/noctalia/PanelWrapper.qml +++ /dev/null @@ -1,146 +0,0 @@ -import QtQuick -import Quickshell -import qs.Commons - -// Wraps Noctalia panel entry points, which are plain Item content intended to -// be hosted inside Noctalia's SmartPanel. We host them as anchored PopupWindows -// so clicking a Noctalia bar widget behaves like native Omarchy popups instead -// of opening a tiled app window. -Item { - id: root - - property string panelSource: "" - property var pluginApi: null - property var manifest: ({}) - property string omarchyPath: "" - property var shell: null - property var pluginRegistry: null - property var barWidgetRegistry: null - property bool popupOpen: false - property bool closingFromShell: false - - readonly property var anchorItem: pluginApi ? pluginApi.panelAnchorItem : null - readonly property var anchorWindow: anchorItem ? anchorItem.QsWindow.window : null - readonly property string barPosition: { - if (shell && shell.barConfig && shell.barConfig.position) return String(shell.barConfig.position) - return "top" - } - - function open(payloadJson) { - popupOpen = true - } - - function close() { - closingFromShell = true - popupOpen = false - if (pluginApi) { - pluginApi.panelOpenScreen = null - pluginApi.panelAnchorItem = null - } - closingFromShell = false - } - - function dismiss() { - if (!popupOpen) return - if (pluginApi && typeof pluginApi.closePanel === "function") pluginApi.closePanel(null) - else if (shell && manifest && manifest.id && typeof shell.hide === "function") shell.hide(manifest.id) - else close() - } - - PopupWindow { - id: popup - visible: root.popupOpen - color: "transparent" - implicitWidth: Math.max(320, panelLoader.item && panelLoader.item.contentPreferredWidth - ? panelLoader.item.contentPreferredWidth : 420) - implicitHeight: Math.max(260, panelLoader.item && panelLoader.item.contentPreferredHeight - ? panelLoader.item.contentPreferredHeight : 520) - - onVisibleChanged: { - if (!visible && root.popupOpen && !root.closingFromShell) root.dismiss() - } - - // Do not use HyprlandFocusGrab here. Noctalia panels are larger, - // interactive surfaces with tabs and nested Flickables; focus grabs made - // internal clicks (notably tab switches) look like outside clicks and also - // interfered with wheel scrolling. Native Omarchy micro-popups can keep - // focus-grab dismissal, but compat panels behave more like pinned panels. - anchor { - id: popupAnchor - window: root.anchorWindow - adjustment: PopupAdjustment.Slide - edges: Edges.Top | Edges.Left - gravity: Edges.Bottom | Edges.Right - rect.width: 1 - rect.height: 1 - - onAnchoring: { - var target = root.anchorItem - var window = root.anchorWindow - if (!target || !window) { - popupAnchor.rect.x = 0 - popupAnchor.rect.y = 0 - return - } - - var popupWidth = popup.implicitWidth - var popupHeight = popup.implicitHeight - var margin = 8 - var localX = target.width / 2 - popupWidth / 2 - var localY = target.height + margin - - if (root.barPosition === "bottom") { - localY = -popupHeight - margin - } else if (root.barPosition === "left") { - localX = target.width + margin - localY = target.height / 2 - popupHeight / 2 - } else if (root.barPosition === "right") { - localX = -popupWidth - margin - localY = target.height / 2 - popupHeight / 2 - } - - var point = window.contentItem.mapFromItem(target, localX, localY) - popupAnchor.rect.x = Math.round(point.x) - popupAnchor.rect.y = Math.round(point.y) - } - } - - Rectangle { - anchors.fill: parent - color: Color.mSurface - border.color: Color.mOutline - border.width: 1 - radius: 0 - - Loader { - id: panelLoader - anchors.fill: parent - source: root.panelSource - onLoaded: root.injectPanelProps() - onStatusChanged: { - if (status === Loader.Error) { - console.warn("noctalia panel failed for " + (root.manifest ? root.manifest.id : "") + ":", errorString()) - } - } - } - } - } - - onPluginApiChanged: injectPanelProps() - onManifestChanged: injectPanelProps() - - function injectPanelProps() { - var item = panelLoader.item - if (!item) return - if ("pluginApi" in item) item.pluginApi = root.pluginApi - if ("manifest" in item) item.manifest = root.manifest - if ("omarchyPath" in item) item.omarchyPath = root.omarchyPath - if ("shell" in item) item.shell = root.shell - if ("pluginRegistry" in item) item.pluginRegistry = root.pluginRegistry - if ("barWidgetRegistry" in item) item.barWidgetRegistry = root.barWidgetRegistry - if ("screen" in item) { - var screens = Quickshell.screens - item.screen = screens && screens.length > 0 ? screens[0] : null - } - } -} diff --git a/default/quickshell/omarchy-shell/compat/noctalia/PluginApiFactory.qml b/default/quickshell/omarchy-shell/compat/noctalia/PluginApiFactory.qml deleted file mode 100644 index dcb3d591..00000000 --- a/default/quickshell/omarchy-shell/compat/noctalia/PluginApiFactory.qml +++ /dev/null @@ -1,114 +0,0 @@ -import QtQuick -import Quickshell - -// Builds the `pluginApi` QtObject that Noctalia plugins expect. The host -// (omarchy-shell or the bar) calls create() once per plugin and caches the -// returned object so the same handle is reused across the plugin's bar -// widget, panel, settings form, etc. -Item { - // create(pluginId, manifest, settingsProvider, hostBridge) -> QtObject - // - // settingsProvider: function() that returns the plugin's current settings - // merged with defaults. Called fresh on every read so the plugin always - // sees the live shell.json entry, not a stale snapshot from when create() - // was first called. - // hostBridge: object with these methods (all optional): - // - persistSettings(pluginId, settings): write back to shell.json via - // the shell's updateEntryInline helper. - // - openPanel(pluginId, screen, buttonItem): summon the plugin's panel - // entry point through the shell's plugin host. - // - closePanel(pluginId, screen): hide a previously-summoned panel. - // - currentScreen(): return the screen the plugin should anchor to. - // togglePanel is provided by the factory itself and routes through the - // open/closePanel bridge methods. Main.qml service instances are wired - // separately by the shell and set on api.mainInstance once instantiated. - function create(pluginId, manifest, settingsProvider, hostBridge) { - var api = pluginApiFactory.createObject(null, { - pluginId: pluginId, - pluginDir: manifest && manifest.__sourceDir ? manifest.__sourceDir : "", - manifest: manifest || ({}), - _settingsProvider: settingsProvider || (function() { return {} }), - _hostBridge: hostBridge || ({}) - }) - return api - } - - Component { - id: pluginApiFactory - - QtObject { - id: api - - property string pluginId: "" - property string pluginDir: "" - property var manifest: ({}) - property var _settingsProvider - property var _hostBridge: ({}) - property var mainInstance: null - - // Writable because Noctalia Settings.qml components commonly stage edits - // with `pluginApi.pluginSettings = ...; pluginApi.saveSettings()`. - // The initial binding resolves shell.json + defaults; assignment during - // save intentionally replaces it with the staged value to persist. - property var pluginSettings: _settingsProvider ? _settingsProvider() : ({}) - - property var panelOpenScreen: null - property var panelAnchorItem: null - property var ipcHandlers: ({}) - - // Noctalia i18n surface — v1 returns the key as-is. - readonly property string currentLanguage: "en" - readonly property var pluginTranslations: ({}) - readonly property var pluginFallbackTranslations: ({}) - readonly property int translationVersion: 0 - - function tr(key, interp) { return String(key === undefined ? "" : key) } - function trp(key, count, interp) { return String(key === undefined ? "" : key) } - function hasTranslation(key) { return false } - - function saveSettings(settings) { - if (settings !== undefined) pluginSettings = settings - if (_hostBridge && typeof _hostBridge.persistSettings === "function") - _hostBridge.persistSettings(pluginId, pluginSettings) - // Re-read after persisting so mainInstance/bar widgets see the - // canonical merged settings (defaults + saved overrides) immediately. - if (_settingsProvider) pluginSettings = _settingsProvider() - if (mainInstance && typeof mainInstance.refresh === "function") mainInstance.refresh() - } - - function openPanel(screen, buttonItem) { - panelOpenScreen = screen || true - panelAnchorItem = buttonItem || null - if (_hostBridge && _hostBridge.tooltipService && typeof _hostBridge.tooltipService.hide === "function") - _hostBridge.tooltipService.hide(buttonItem) - if (_hostBridge && typeof _hostBridge.openPanel === "function") - _hostBridge.openPanel(pluginId, screen, buttonItem) - } - - function closePanel(screen) { - panelOpenScreen = null - panelAnchorItem = null - if (_hostBridge && typeof _hostBridge.closePanel === "function") - _hostBridge.closePanel(pluginId, screen) - } - - function togglePanel(screen, buttonItem) { - if (panelOpenScreen) closePanel(screen) - else openPanel(screen, buttonItem) - } - - function withCurrentScreen(cb) { - if (typeof cb !== "function") return - var s = (_hostBridge && typeof _hostBridge.currentScreen === "function") - ? _hostBridge.currentScreen() : null - cb(s) - } - - function openLauncher(screen) { - console.warn("pluginApi.openLauncher is not supported in the Omarchy compat layer (plugin=" + pluginId + ")") - } - function closeLauncher(screen) { /* no-op */ } - function toggleLauncher(screen) { openLauncher(screen) } - } - } -} diff --git a/default/quickshell/omarchy-shell/compat/noctalia/README.md b/default/quickshell/omarchy-shell/compat/noctalia/README.md deleted file mode 100644 index 11eee4a4..00000000 --- a/default/quickshell/omarchy-shell/compat/noctalia/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# Noctalia plugin compatibility (omarchy-shell) - -The omarchy-shell can load most bar widget plugins from the -[noctalia-dev/noctalia-plugins](https://github.com/noctalia-dev/noctalia-plugins) -ecosystem without any modification to the plugin code. - -## Install a Noctalia plugin - -```sh -git clone https://github.com/noctalia-dev/noctalia-plugins /tmp/noctalia-plugins -ln -s /tmp/noctalia-plugins/asus-um5606-fan-state \ - ~/.config/omarchy/plugins/asus-um5606-fan-state -omarchy-shell-ipc shell rescanPlugins -``` - -The plugin id you see inside Omarchy is prefixed: `noctalia.asus-um5606-fan-state`. -Add it via the bar customizer or by editing `~/.config/omarchy/shell.json` directly. - -## What's supported in v1 - -| Noctalia concept | Omarchy support | -|---|---| -| `entryPoints.barWidget` | yes — registered through `BarWidgetRegistry` | -| `entryPoints.panel` | yes — opened via `pluginApi.openPanel()` | -| `entryPoints.settings` | yes — embedded in the settings dialog | -| `entryPoints.main` | yes — instantiated as a hidden service, exposed via `pluginApi.mainInstance` | -| `entryPoints.desktopWidget` | **no** — skipped with a console warning | -| `entryPoints.launcherProvider` | **no** — skipped with a console warning | -| Plugin i18n (`i18n/.json`) | **no** — `tr()` returns the raw key | -| Plugin install from git URL | **no** — manual drop into `~/.config/omarchy/plugins/` | -| Hot reload on plugin change | **no** — call `omarchy-shell-ipc shell rescanPlugins` | - -## Shim surface - -We ship just enough of Noctalia's QML namespace to render typical bar widgets. - -| Module | Symbols | -|---|---| -| `qs.Commons` | `Color`, `Style`, `Logger`, `Settings` (read-only), `I18n` (stub), `Time`, `Icons` (~50 entries), `ThemeIcons` (stub), `ShellState` (stub) | -| `qs.Widgets` | `NText`, `NIcon`, `NIconButton`, `NBox`, `NButton`, `NPopupContextMenu`, `NScrollText`, `NToggle`, `NSpinBox`, `NSlider`, `NTextInput`, `NComboBox`, `NCheckbox`, `NDivider`, `NTabBar`, `NTabButton` | -| `qs.Services.UI` | `BarService`, `TooltipService`, `PanelService` | -| `qs.Services.System` | `HostService` (reads `/etc/os-release`) | -| `qs.Services.Power` | `PowerProfileService` (returns `noctaliaPerformanceMode: false`) | - -Anything outside this surface logs a `console.warn` instead of crashing the -shell, but the plugin may not render correctly. - -## `pluginApi` surface - -A Noctalia plugin's `pluginApi` is built per-plugin and injected onto the bar -widget / panel / settings entry points. The implementation lives at -`compat/noctalia/PluginApiFactory.qml`. - -| Property / method | Behaviour | -|---|---| -| `pluginId`, `pluginDir`, `manifest` | Provided as expected. | -| `pluginSettings` | Live read of the plugin's entry in `~/.config/omarchy/shell.json`, merged with the manifest's `metadata.defaultSettings`. | -| `mainInstance` | Live instance of `Main.qml` when the plugin declares one. | -| `saveSettings()` | Persists the merged settings into the plugin's entry in `shell.json`. | -| `openPanel(screen, btn)` / `closePanel(screen)` / `togglePanel(screen, btn)` | Routes through `omarchy-shell-ipc shell summon/hide` against the plugin's panel entry point. | -| `withCurrentScreen(cb)` | Calls `cb` with the bar's currently-rendering screen. | -| `tr/trp/hasTranslation` | Returns the key as-is; no i18n in v1. | -| `openLauncher(...)` family | Stub — logs a warning. | - -## Known limitations - -- Plugin labels show raw translation keys for non-English locales. -- Icon names not present in our compact `Icons` map render as `?`. -- Plugins that declare only `desktopWidget` or `launcherProvider` and no - bar/panel/main do not appear in the Omarchy catalog. -- Some plugins call `Color.mPrimary` for accent colors that don't perfectly - match Omarchy's theme palette; results are close but not pixel-identical. diff --git a/default/quickshell/omarchy-shell/plugins/bar/Bar.qml b/default/quickshell/omarchy-shell/plugins/bar/Bar.qml index b32f34d2..da8adc0b 100644 --- a/default/quickshell/omarchy-shell/plugins/bar/Bar.qml +++ b/default/quickshell/omarchy-shell/plugins/bar/Bar.qml @@ -26,11 +26,8 @@ Item { // the bar just renders whatever it's handed. The bar font follows the // OS-level fontconfig monospace binding — it is not stored in shell.json. required property var barConfig - // Injected by the host shell so the bar can detect Noctalia-compat plugins - // and look up manifests when wiring per-widget Noctalia pluginApi. - property var pluginRegistry: null - // Injected by the host shell. Used so Noctalia plugins that reach for - // shell-wide APIs (openPanel, currentScreen) can do so via pluginApi. + // Injected by the host shell. Used for shell-wide actions such as opening + // settings and persisting inline widget state. property var shell: null // Mirrors the on-disk `bar-off` flag so the user can hide the bar without // killing the entire shell. Wired to BarPanel.visible below; updated by the @@ -93,40 +90,6 @@ Item { if (activePopout === owner) activePopout = null } - // -------------------------------------------------- Noctalia compat helpers - // - // The shell owns pluginApi creation and Main.qml service instantiation now; - // bar widgets just look up their api by moduleName. Keeping section/index - // helpers here because they're needed for Noctalia bar-widget injection - // (widgetId, section, sectionWidgetIndex, sectionWidgetsCount). - - function sectionOfEntry(entry) { - var sections = ["left", "center", "right"] - for (var s = 0; s < sections.length; s++) { - var list = layoutConfig[sections[s]] || [] - for (var i = 0; i < list.length; i++) { - if (list[i] === entry) return sections[s] - } - } - return "" - } - - function indexOfEntry(entry) { - var section = sectionOfEntry(entry) - var list = section ? (layoutConfig[section] || []) : [] - for (var i = 0; i < list.length; i++) if (list[i] === entry) return i - return -1 - } - - function entriesOfSection(section) { - return Array.isArray(layoutConfig[section]) ? layoutConfig[section] : [] - } - - function noctaliaPluginApiFor(moduleName) { - if (!shell || typeof shell.noctaliaPluginApiFor !== "function") return null - return shell.noctaliaPluginApiFor(moduleName) - } - readonly property bool vertical: position === "left" || position === "right" readonly property int barSize: vertical ? 28 : 26 @@ -1269,20 +1232,6 @@ Item { if ("bar" in target) target.bar = root if ("moduleName" in target) target.moduleName = moduleName if ("settings" in target) target.settings = moduleSettings - - // Noctalia compat injection. Only kicks in for plugins whose manifest - // was translated from Noctalia shape; for our first-party widgets these - // properties don't exist on the target item so nothing happens. - var manifest = root.pluginRegistry ? root.pluginRegistry.installedPlugins[moduleName] : null - if (manifest && manifest.__noctaliaCompat) { - if ("pluginApi" in target) target.pluginApi = root.noctaliaPluginApiFor(moduleName) - if ("widgetId" in target) target.widgetId = moduleName - if ("section" in target) target.section = root.sectionOfEntry(entry) - if ("sectionWidgetIndex" in target) target.sectionWidgetIndex = root.indexOfEntry(entry) - if ("sectionWidgetsCount" in target) target.sectionWidgetsCount = root.entriesOfSection(root.sectionOfEntry(entry)).length - if ("screen" in target && root.QsWindow && root.QsWindow.window) - target.screen = root.QsWindow.window.screen - } } Component { diff --git a/default/quickshell/omarchy-shell/plugins/settings/SettingsPanel.qml b/default/quickshell/omarchy-shell/plugins/settings/SettingsPanel.qml index 32f8a127..3fe29f76 100644 --- a/default/quickshell/omarchy-shell/plugins/settings/SettingsPanel.qml +++ b/default/quickshell/omarchy-shell/plugins/settings/SettingsPanel.qml @@ -370,7 +370,7 @@ Item { displayName: meta.displayName || manifest.name || key, name: meta.displayName || manifest.name || key, description: meta.description || manifest.description || "", - category: meta.category || (manifest.__noctaliaCompat ? "Noctalia" : "Plugin"), + category: meta.category || "Plugin", allowMultiple: meta.allowMultiple === true, settingsForm: meta.settingsForm || "", schema: Array.isArray(meta.schema) ? meta.schema : [], @@ -402,17 +402,9 @@ Item { var meta = widgetMetadata(id) if (meta.settingsForm) return true if (widgetSchema(id).length > 0) return true - var manifest = root.pluginRegistry ? root.pluginRegistry.installedPlugins[id] : null - if (manifest && manifest.__noctaliaCompat && manifest.entryPoints && manifest.entryPoints.settings) - return true return false } - function widgetIsNoctaliaPlugin(id) { - var manifest = root.pluginRegistry ? root.pluginRegistry.installedPlugins[id] : null - return !!(manifest && manifest.__noctaliaCompat) - } - function widgetAllowsMultiple(id) { var meta = widgetMetadata(id) if (meta.allowMultiple === true) return true @@ -465,8 +457,7 @@ Item { var allowsMultiple = widgetAllowsMultiple(id) if (!allowsMultiple && existingInBar[id]) continue result.push({ id: id, name: widgetName(id), description: widgetDescription(id), - elsewhere: allowsMultiple && !!existingInBar[id] && !existsHere, - isNoctalia: widgetIsNoctaliaPlugin(id) }) + elsewhere: allowsMultiple && !!existingInBar[id] && !existsHere }) } return result } @@ -986,7 +977,6 @@ Item { Text { text: modelData.name - + (modelData.isNoctalia ? " (Noctalia)" : "") + (modelData.elsewhere ? " (elsewhere)" : "") color: root.foreground font.family: root.fontFamily @@ -1246,9 +1236,6 @@ Item { case "weatherSettings": return weatherSettingsComponent } } - var manifest = root.pluginRegistry ? root.pluginRegistry.installedPlugins[id] : null - if (manifest && manifest.__noctaliaCompat && manifest.entryPoints && manifest.entryPoints.settings) - return noctaliaSettingsComponent if (widgetSchema(id).length > 0) return dynamicSettingsComponent return null } @@ -1262,53 +1249,6 @@ Item { } } - Component { - id: noctaliaSettingsComponent - - Item { - id: noctaliaForm - property var entry: ({}) - property string pluginId: entry && entry.id ? String(entry.id) : "" - property var manifest: pluginId && root.pluginRegistry - ? root.pluginRegistry.installedPlugins[pluginId] : null - - function saveSettings() { - if (settingsLoader.item && typeof settingsLoader.item.saveSettings === "function") { - settingsLoader.item.saveSettings() - } else { - console.warn("Noctalia settings form has no saveSettings():", pluginId) - } - } - - implicitHeight: settingsLoader.item ? settingsLoader.item.implicitHeight : 0 - implicitWidth: settingsLoader.item ? settingsLoader.item.implicitWidth : 0 - - Loader { - id: settingsLoader - anchors.fill: parent - source: { - if (!noctaliaForm.manifest) return "" - return root.pluginRegistry.entryPointUrl(noctaliaForm.manifest, "settings") - } - asynchronous: false - onLoaded: { - if (!item) return - var api = (root.shell && typeof root.shell.noctaliaPluginApiFor === "function") - ? root.shell.noctaliaPluginApiFor(noctaliaForm.pluginId) : null - if (api && "pluginApi" in item) item.pluginApi = api - if ("screen" in item && root.QsWindow && root.QsWindow.window) - item.screen = root.QsWindow.window.screen - } - onStatusChanged: { - if (status === Loader.Error) { - console.warn("noctalia Settings.qml failed for " + noctaliaForm.pluginId + ":", - sourceComponent ? sourceComponent.errorString() : "") - } - } - } - } - } - Component { id: spacerSettingsComponent diff --git a/default/quickshell/omarchy-shell/services/PluginRegistry.qml b/default/quickshell/omarchy-shell/services/PluginRegistry.qml index 00e4f0c7..259baa95 100644 --- a/default/quickshell/omarchy-shell/services/PluginRegistry.qml +++ b/default/quickshell/omarchy-shell/services/PluginRegistry.qml @@ -44,98 +44,11 @@ QtObject { return true } - // Detect a Noctalia-shape manifest (https://github.com/noctalia-dev/noctalia-plugins). - // Noctalia manifests don't carry our schemaVersion; we recognise them by the - // presence of `minNoctaliaVersion` or `metadata.defaultSettings`, or an - // `entryPoints` map that names roles Noctalia owns (main/barWidget/panel). - function isNoctaliaShape(manifest) { - if (!isPlainObject(manifest)) return false - if (manifest.schemaVersion !== undefined) return false - if (manifest.minNoctaliaVersion !== undefined) return true - if (isPlainObject(manifest.metadata) && isPlainObject(manifest.metadata.defaultSettings)) return true - if (isPlainObject(manifest.entryPoints)) { - var ep = manifest.entryPoints - if (ep.barWidget || ep.main || ep.panel || ep.desktopWidget - || ep.launcherProvider) return true - } - return false - } - - // Translate a Noctalia manifest into the shape we validate normally. We do - // not modify the on-disk file; the in-memory copy gets a __noctaliaCompat - // marker so downstream code can branch where the semantics diverge. - function translateNoctaliaManifest(src) { - var ep = isPlainObject(src.entryPoints) ? src.entryPoints : {} - var kinds = [] - var unsupported = [] - if (ep.main) kinds.push("service") - if (ep.barWidget) kinds.push("bar-widget") - if (ep.panel) kinds.push("panel") - // Out of scope for v1 — warn loudly so we don't silently strand the plugin. - if (ep.desktopWidget) unsupported.push("desktopWidget") - if (ep.launcherProvider) unsupported.push("launcherProvider") - - if (kinds.length === 0) { - console.warn("PluginRegistry: noctalia plugin '" + src.id + "' has no supported entryPoints" - + (unsupported.length ? " (unsupported in v1: " + unsupported.join(", ") + ")" : "")) - return null - } - if (unsupported.length) { - console.warn("PluginRegistry: noctalia plugin '" + src.id - + "' uses entryPoints not supported in v1, ignoring: " + unsupported.join(", ")) - } - - var translated = { - schemaVersion: 1, - id: "noctalia." + String(src.id), - name: src.name || src.id, - version: src.version || "0.0.0", - author: src.author || "", - description: src.description || "", - kinds: kinds, - activation: kinds.indexOf("service") !== -1 ? "persistent" : "on-demand", - entryPoints: {}, - __noctaliaCompat: true, - __noctaliaOriginal: src - } - - if (ep.barWidget) translated.entryPoints.barWidget = ep.barWidget - if (ep.main) translated.entryPoints.service = ep.main - if (ep.panel) translated.entryPoints.panel = ep.panel - if (ep.settings) translated.entryPoints.settings = ep.settings - - var defaults = (isPlainObject(src.metadata) && isPlainObject(src.metadata.defaultSettings)) - ? src.metadata.defaultSettings : {} - if (translated.entryPoints.barWidget) { - translated.barWidget = { - displayName: translated.name, - description: translated.description, - category: "Noctalia", - allowMultiple: false, - defaults: defaults, - schema: [] - } - } - if (defaults && Object.keys(defaults).length > 0) translated.defaults = defaults - - return translated - } - function validateManifest(manifest, sourcePath) { if (!isPlainObject(manifest)) { console.warn("PluginRegistry: manifest is not an object at " + sourcePath) return null } - // Noctalia shape — translate before validation. The translated manifest - // goes through the standard schemaVersion=1 path below. - if (isNoctaliaShape(manifest)) { - var preservedSourceDir = manifest.__sourceDir - var preservedFirstParty = manifest.__isFirstParty - manifest = translateNoctaliaManifest(manifest) - if (!manifest) return null - manifest.__sourceDir = preservedSourceDir - manifest.__isFirstParty = preservedFirstParty - } if (manifest.schemaVersion !== 1) { console.warn("PluginRegistry: unsupported schemaVersion at " + sourcePath) return null diff --git a/default/quickshell/omarchy-shell/shell.qml b/default/quickshell/omarchy-shell/shell.qml index 8ce4f634..2b4c35bf 100644 --- a/default/quickshell/omarchy-shell/shell.qml +++ b/default/quickshell/omarchy-shell/shell.qml @@ -5,9 +5,6 @@ import Quickshell.Io import "plugins/bar" import "services" -import "compat/noctalia" as Compat -import qs.Services.UI as NoctaliaUI -import qs.Commons as NoctaliaCommons ShellRoot { id: shell @@ -161,20 +158,8 @@ ShellRoot { // case the user dir already existed at startup. pluginRegistry.rescan() shell._syncFirstPartyServices() - - // Wire Noctalia compat singletons. Plugins read state from these globals; - // we hand them references to the host so they can route into the right - // popup, tooltip, etc. - NoctaliaUI.BarService.bar = bar - NoctaliaUI.TooltipService.bar = bar - NoctaliaUI.PanelService.bar = bar - NoctaliaUI.PanelService.shell = shell - NoctaliaCommons.Settings.shellConfig = shell.shellConfig } - // Keep Noctalia.Settings.data in sync with whatever shell.json currently is. - onShellConfigChanged: NoctaliaCommons.Settings.shellConfig = shell.shellConfig - function mutateShellConfig(mutator) { var copy = JSON.parse(JSON.stringify(shellConfig || builtinShellConfig)) mutator(copy) @@ -190,181 +175,14 @@ ShellRoot { omarchyPath: shell.omarchyPath barWidgetRegistry: shell.barWidgetRegistry barConfig: shell.barConfig - pluginRegistry: shell.pluginRegistry shell: shell } - // ------------------------------------------------- Noctalia plugin API - // - // One pluginApi instance per plugin, cached for the life of the shell. The - // factory is host-owned (shell, not bar) so non-bar Noctalia plugins - // (panels, services) can call into it too. Settings lookups walk the live - // shell.json on every read so reorders/edits never leave a stale entry - // captured in the closure. - Compat.PluginApiFactory { id: noctaliaApiFactory } - - Item { - id: noctaliaServiceHost - visible: false - } - - property var _noctaliaApis: ({}) - property var _noctaliaServices: ({}) - - function findShellEntry(pluginId) { - var config = shellConfig - if (!isPlainObject(config)) return null - if (isPlainObject(config.bar) && isPlainObject(config.bar.layout)) { - var sections = ["left", "center", "right"] - for (var s = 0; s < sections.length; s++) { - var arr = config.bar.layout[sections[s]] - if (!Array.isArray(arr)) continue - for (var i = 0; i < arr.length; i++) { - if (arr[i] && arr[i].id === pluginId) return arr[i] - } - } - } - if (Array.isArray(config.plugins)) { - for (var p = 0; p < config.plugins.length; p++) { - if (config.plugins[p] && config.plugins[p].id === pluginId) return config.plugins[p] - } - } - return null - } - - function noctaliaPluginApiFor(pluginId) { - var key = String(pluginId) - if (!key) return null - var existing = _noctaliaApis[key] - if (existing) return existing - var manifest = pluginRegistry && pluginRegistry.installedPlugins - ? pluginRegistry.installedPlugins[key] : null - if (!manifest || !manifest.__noctaliaCompat) return null - - var api = noctaliaApiFactory.create( - key, - manifest, - function() { - // Compute effective settings on every read. We can't capture an entry - // reference because mutateSection rebuilds the layout objects and the - // captured reference would point at a snapshot. - var defaults = (manifest.barWidget && manifest.barWidget.defaults) || manifest.defaults || {} - var merged = {} - for (var k in defaults) merged[k] = defaults[k] - var entry = findShellEntry(key) - if (entry) { - for (var ek in entry) if (ek !== "id") merged[ek] = entry[ek] - } - return merged - }, - { - persistSettings: function(_pluginId, settings) { - if (typeof shell.updateEntryInline === "function") - shell.updateEntryInline(key, settings) - }, - openPanel: function(_pluginId, _screen, _btn) { - shell.summon(key, JSON.stringify({ source: "noctalia" })) - }, - closePanel: function(_pluginId, _screen) { shell.hide(key) }, - tooltipService: NoctaliaUI.TooltipService, - currentScreen: function() { - var screens = Quickshell.screens - return screens && screens.length > 0 ? screens[0] : null - } - } - ) - - var next = ({}) - for (var existingKey in _noctaliaApis) next[existingKey] = _noctaliaApis[existingKey] - next[key] = api - _noctaliaApis = next - return api - } - - // -------------------------------------------------- noctalia service main - // - // Noctalia plugins with `entryPoints.main` (translated to kind="service") - // are instantiated once per shell session when enabled. The Main.qml is - // typically headless — a data source the plugin's bar widget reads from. - // We rely on the shared pluginApi so the service and any bar widgets see - // the same handle. - function ensureNoctaliaService(pluginId) { - var key = String(pluginId) - if (_noctaliaServices[key]) return _noctaliaServices[key] - var manifest = pluginRegistry && pluginRegistry.installedPlugins - ? pluginRegistry.installedPlugins[key] : null - if (!manifest || !manifest.__noctaliaCompat) return null - if (!manifest.entryPoints || !manifest.entryPoints.service) return null - var url = pluginRegistry.entryPointUrl(manifest, "service") - if (!url) return null - var api = noctaliaPluginApiFor(key) - if (!api) return null - - var comp = Qt.createComponent(url, Component.PreferSynchronous) - function finalize() { - if (comp.status !== Component.Ready) { - console.warn("noctalia service load failed for " + key + ": " + comp.errorString()) - return - } - var inst = comp.createObject(noctaliaServiceHost, { pluginApi: api }) - if (!inst) { - console.warn("noctalia service createObject returned null for", key) - return - } - var snext = ({}) - for (var sk in _noctaliaServices) snext[sk] = _noctaliaServices[sk] - snext[key] = inst - _noctaliaServices = snext - api.mainInstance = inst - } - if (comp.status === Component.Loading) { - comp.statusChanged.connect(finalize) - return null - } - finalize() - return _noctaliaServices[key] || null - } - - function _syncNoctaliaServices() { - if (!pluginRegistry || !pluginRegistry.installedPlugins) return - var plugins = pluginRegistry.installedPlugins - for (var id in plugins) { - var m = plugins[id] - if (!m || !m.__noctaliaCompat) continue - if (!m.entryPoints || !m.entryPoints.service) continue - if (!pluginRegistry.isEnabled(id)) continue - if (_noctaliaServices[id]) continue - ensureNoctaliaService(id) - } - // Drop services for plugins that have been disabled or removed. - for (var existingId in _noctaliaServices) { - var stillThere = plugins[existingId] - var stillEnabled = stillThere && pluginRegistry.isEnabled(existingId) - if (stillThere && stillEnabled) continue - var inst = _noctaliaServices[existingId] - if (inst && typeof inst.destroy === "function") inst.destroy() - var next = ({}) - for (var k in _noctaliaServices) if (k !== existingId) next[k] = _noctaliaServices[k] - _noctaliaServices = next - var apis = ({}) - for (var ak in _noctaliaApis) apis[ak] = _noctaliaApis[ak] - if (apis[existingId]) apis[existingId].mainInstance = null - _noctaliaApis = apis - } - } - - Connections { - target: shell.pluginRegistry - function onPluginsChanged() { shell._syncNoctaliaServices() } - } - // ---------------------------------------------------- first-party services // // Generic loader for any first-party plugin that declares kind "service". // The notification daemon is the first user; future first-party services - // (background updaters, idle inhibitor, etc.) plug in here. Mirrors - // ensureNoctaliaService but skips the noctalia-specific pluginApi handoff - // and gates on `__isFirstParty` instead of `__noctaliaCompat`. + // (background updaters, idle inhibitor, etc.) plug in here. Item { id: firstPartyServiceHost visible: false @@ -382,7 +200,6 @@ ShellRoot { var manifest = pluginRegistry && pluginRegistry.installedPlugins ? pluginRegistry.installedPlugins[key] : null if (!manifest || !manifest.__isFirstParty) return null - if (manifest.__noctaliaCompat) return null if (!Array.isArray(manifest.kinds) || manifest.kinds.indexOf("service") === -1) return null if (!manifest.entryPoints || !manifest.entryPoints.service) return null var url = pluginRegistry.entryPointUrl(manifest, "service") @@ -421,7 +238,7 @@ ShellRoot { var plugins = pluginRegistry.installedPlugins for (var id in plugins) { var m = plugins[id] - if (!m || !m.__isFirstParty || m.__noctaliaCompat) continue + if (!m || !m.__isFirstParty) continue if (!Array.isArray(m.kinds) || m.kinds.indexOf("service") === -1) continue if (!m.entryPoints || !m.entryPoints.service) continue if (!pluginRegistry.isEnabled(id)) continue @@ -446,14 +263,11 @@ ShellRoot { function onPluginsChanged() { shell._syncFirstPartyServices() } } - // Used by Noctalia compat: pluginApi.saveSettings() ends up writing inline - // settings to the plugin's entry in shell.json. moduleName is the entry id - // (the bare manifest id, e.g. "noctalia.air-quality"); settings is the - // merged plugin state. Returns true if anything actually changed. - // Compute the proposed new shellConfig in a local clone, and only persist - // if anything actually changed. Lets Noctalia plugins call saveSettings() - // repeatedly with identical values (common for reactive QML bindings) - // without dirtying shell.json or thrashing the file watcher. + // Writes inline settings to a bar layout entry or top-level plugin entry in + // shell.json. moduleName is the entry id; settings is the merged plugin + // state. Returns true if anything actually changed. Compute the proposed + // new shellConfig in a local clone, and only persist if anything actually + // changed so reactive bindings do not dirty shell.json unnecessarily. function updateEntryInline(moduleName, settings) { var stripped = String(moduleName) var copy = JSON.parse(JSON.stringify(shellConfig || builtinShellConfig)) @@ -647,9 +461,7 @@ ShellRoot { readonly property string sourceUrl: shell.pluginRegistry.entryPointUrl(manifest, entryKind) property Loader panelLoader: Loader { - readonly property bool wrapNoctaliaPanel: !!(panelEntry.manifest && panelEntry.manifest.__noctaliaCompat && panelEntry.entryKind === "panel") - source: wrapNoctaliaPanel ? "" : panelEntry.sourceUrl - sourceComponent: wrapNoctaliaPanel ? noctaliaPanelWrapperComponent : null + source: panelEntry.sourceUrl active: panelEntry.sourceUrl !== "" && (panelEntry.keepLoaded || shell.openPanelIds[panelEntry.pluginId] === true) asynchronous: true onLoaded: { @@ -663,11 +475,6 @@ ShellRoot { // (e.g. omarchy.notifications) read shared state off `service`. We // hand them the matching service instance if one was loaded. if ("service" in item) item.service = shell.firstPartyServiceFor(panelEntry.pluginId) - // Noctalia panel/overlay/menu plugins expect a pluginApi just like - // their bar widgets do. First-party Omarchy plugins don't declare - // the property so the `in target` check skips them. - if (panelEntry.manifest && panelEntry.manifest.__noctaliaCompat && "pluginApi" in item) - item.pluginApi = shell.noctaliaPluginApiFor(panelEntry.pluginId) shell.registerPanelLoader(panelEntry.pluginId, this) } onStatusChanged: { @@ -683,12 +490,6 @@ ShellRoot { } Component.onDestruction: shell.unregisterPanelLoader(panelEntry.pluginId) } - - property Component noctaliaPanelWrapperComponent: Component { - Compat.PanelWrapper { - panelSource: panelEntry.sourceUrl - } - } } } @@ -698,8 +499,8 @@ ShellRoot { // Each enabled plugin with kind "bar-widget" gets a Component created from // its manifest entry point and registered under its plain manifest id. // First-party widget ids (calendar, weather, etc.) are short and don't - // collide with namespaced plugin ids like noctalia.air-quality, so we don't - // need a separate "plugin:" namespace anymore. + // collide with namespaced plugin ids, so we don't need a separate + // "plugin:" namespace anymore. Connections { target: shell.pluginRegistry function onPluginsChanged() { shell.syncPluginWidgets() }