pragma ComponentBehavior: Bound import QtQuick import Quickshell import Quickshell.Io import Quickshell.Services.Notifications import Quickshell.Wayland import "../.." as N Scope { id: root // Walk visual QObject children, not production-only testing hooks. function tree(item: var): var { let items = [item]; const children = item.children || []; for (let i = 0; i < children.length; i++) items = items.concat(tree(children[i])); return items; } function find(item: var, predicate: var): var { return tree(item).find(predicate) || null; } function rect(item: var, window: var): var { if (!item) return null; const point = item.mapToItem(window.contentItem, 0, 0); return { x: point.x, y: point.y, width: item.width, height: item.height }; } function textRect(item: var, window: var, text: string): var { return rect(find(item, function (child) { return child.visible && child.text === text; }), window); } function visibleText(item: var, window: var): var { return tree(item).filter(function (child) { // Never serialize editable text, including when reveal is enabled. return child.visible && child.font !== undefined && child.text !== undefined && child.echoMode === undefined && String(child.text).length > 0; }).map(function (child) { return { text: String(child.text), rect: rect(child, window) }; }); } N.Theme { id: palette } N.Time { id: clock } N.CalDav { id: calendar } N.Resources { id: resources upower: null powerProfiles: null brightnessService: null } N.Privilege { id: privilege } N.Noko { id: noko } N.Niri { id: niri } N.Audio { id: audio } N.Network { id: network } N.Vault { id: vault clipboardService: null } N.Diagnostics { id: diagnosticsService surfaces: surfaces screens: Quickshell.screens niriService: niri audioService: audio networkService: network vaultService: vault nokoService: noko } N.SurfaceManager { id: surfaces screenWidth: desktop.width screenHeight: desktop.height barHeight: 48 } NotificationServer {} PanelWindow { id: desktop anchors { top: true left: true right: true bottom: true } exclusionMode: ExclusionMode.Ignore WlrLayershell.layer: WlrLayer.Bottom WlrLayershell.keyboardFocus: WlrKeyboardFocus.None color: palette.content.backdrop Repeater { model: ["clock", "system"] N.PopupButton { required property int index required property string modelData theme: palette text: modelData x: surfaces.vertical ? 2 : 8 + index * 110 y: surfaces.vertical ? 8 + index * 48 : 4 width: surfaces.vertical ? 44 : 100 height: 38 onClicked: { if (surfaces.popoutOpen && surfaces.popoutName === modelData) surfaces.closePopout("bar-toggle"); else surfaces.openPopout(modelData, "0,0,48,48", ""); } } } } N.PopoutHost { id: host theme: palette surfaces: surfaces barWindow: desktop timeService: clock caldavService: calendar resourceService: resources privilegeService: privilege niriService: null notificationService: null trayService: null mediaService: null clipboardService: null vaultService: null launcherService: null audioService: null brightnessService: null networkService: null bluetoothService: null powerService: null lockService: null nokoService: null } PanelWindow { id: authWindow visible: privilege.active || privilege.confirming || privilege.error.length > 0 anchors { top: true left: true right: true bottom: true } exclusionMode: ExclusionMode.Ignore WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive color: "transparent" onVisibleChanged: if (visible) Qt.callLater(auth.focusSecret) N.PrivilegeSurface { id: auth anchors.fill: parent theme: palette privilegeService: privilege } } PanelWindow { id: nokoWindow visible: noko.overlayState.length > 0 anchors { top: true left: true right: true bottom: true } exclusionMode: ExclusionMode.Ignore WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.keyboardFocus: WlrKeyboardFocus.None color: "transparent" mask: Region {} N.NokoFocusOverlay { id: overlay anchors.fill: parent theme: palette nokoService: noko niriService: null barHeight: surfaces.barHeight orientation: surfaces.orientation } } // var parameters keep synthetic-only state out of production type contracts. // Only external service/layout events; all user actions use real input. function externalEvent(event: string, calendar: var, resources: var, privilege: var, noko: var): void { switch (event) { case "horizontal": case "vertical": surfaces.orientation = event; break; case "events": calendar.updateEvents(); break; case "empty-resources": resources.clear(); break; case "resource-incidents": resources.showIncidents(); break; case "resource-more-crashes": resources.addCrash(); break; case "resource-history-unavailable": resources.incidentStatus = "unavailable"; break; case "agent-success": resources.agentResult(true); break; case "agent-failure": resources.agentResult(false); break; case "privilege": privilege.begin(); break; case "capturing": case "transcribing": case "finished": noko.overlayState = event; break; case "idle": noko.overlayState = ""; break; case "thicker-bar": surfaces.barHeight = 64; break; } } function state(calendar: var, resources: var, privilege: var): string { const panel = root.find(host.contentItem, function (item) { return item.setupMode !== undefined || item.issueList !== undefined; }); const card = root.find(host.contentItem, function (item) { return item.padding === 0 && item.radius === 14; }); const url = root.find(host.contentItem, function (item) { return item.placeholder === "https://host/remote.php/dav"; }); const user = root.find(host.contentItem, function (item) { return item.placeholder === "benutzername"; }); const input = root.find(auth, function (item) { return item.echoMode !== undefined; }); const shake = root.find(auth, function (item) { return item.objectName === "failureShakeSlot"; }); const shakeContent = root.find(shake, function (item) { return item.spacing === 7; }); const authCard = auth.children[0]; const reveal = root.find(auth, item => item.clicked !== undefined && item.accessibleName === (auth.secretVisible ? "hide password" : "show password")); const cancel = root.find(auth, item => item.clicked !== undefined && item.accessibleName === "cancel"); const authorize = root.find(auth, item => item.clicked !== undefined && item.accessibleName === "authorize"); return JSON.stringify({ width: desktop.width, height: desktop.height, orientation: surfaces.orientation, popup: JSON.parse(surfaces.popoutState()), catalog: JSON.parse(surfaces.popoutContentCatalog()), card: root.rect(card, host), panel: root.rect(panel, host), text: host.visible ? root.visibleText(host.contentItem, host) : [], buttons: { clock: root.textRect(desktop.contentItem, desktop, "clock"), system: root.textRect(desktop.contentItem, desktop, "system") }, calendar: { refreshes: calendar.refreshes, saves: calendar.saves, savedDrafts: calendar.savedDrafts, draftsEdited: calendar.setupUrlDraft === "https://fixture.invalid/dav" && calendar.setupUsernameDraft === "fixture-user", url: root.rect(url, host), user: root.rect(user, host), save: root.textRect(host.contentItem, host, "speichern"), todayItems: panel && panel.todayItems ? panel.todayItems.length : 0 }, resources: { open: resources.popoutOpen, opens: resources.opens, closes: resources.closes, actions: resources.actions, lastAction: resources.lastAction, agentActions: resources.agentActions, agentPending: resources.agentPending, agentIssueId: resources.agentIssueId, agentError: resources.agentError, issueRows: root.tree(host.contentItem).filter(item => item.issue !== undefined && item.historical !== undefined).map(item => ({ id: item.issue.id, historical: item.historical, attention: item.attention, destructive: item.destructive, highlighted: item.highlighted, clock: !!root.find(item, child => child.visible && child.text === "") })), agent: root.rect(root.find(host.contentItem, item => item.visible && item.clicked !== undefined && item.accessibleName === "fix with agent: fixture service failed"), host), agentFocused: !!root.find(host.contentItem, item => item.controlFocused && item.accessibleName === "fix with agent: fixture service failed"), crashGroupId: resources.incidents.length ? resources.incidents[0].id : "", crashAgent: root.rect(root.find(host.contentItem, item => item.visible && item.clicked !== undefined && item.accessibleName === "assess crashes: fixture editor"), host), crashOpen: root.rect(root.find(host.contentItem, item => item.visible && item.clicked !== undefined && item.accessibleName === "open latest crash for fixture editor"), host), batteryAgent: root.rect(root.find(host.contentItem, item => item.visible && item.clicked !== undefined && item.accessibleName === "fix with agent: battery wear"), host), updatesAgent: root.rect(root.find(host.contentItem, item => item.visible && item.clicked !== undefined && item.accessibleName === "fix with agent: 3 updates available"), host), updatesOpen: root.rect(root.find(host.contentItem, item => item.visible && item.clicked !== undefined && item.accessibleName === "open 3 updates available"), host), updatesFocused: !!root.find(host.contentItem, item => item.controlFocused && item.accessibleName === "open 3 updates available"), action: root.rect(root.find(host.contentItem, item => item.visible && item.clicked !== undefined && item.accessibleName === "open fixture service failed"), host) }, auth: { visible: authWindow.visible, active: privilege.active, confirming: privilege.confirming, error: privilege.error, submissions: privilege.submissions, cancellations: privilege.cancellations, receivedLiteral: privilege.receivedLiteral, revealed: auth.secretVisible, inputFocused: input ? input.activeFocus : false, hasInput: input ? input.text.length > 0 : false, inputMasked: input ? input.echoMode === TextInput.Password : false, input: root.rect(input, authWindow), card: root.rect(authCard, authWindow), shake: root.rect(shake, authWindow), shakeContentHeight: shakeContent ? shakeContent.implicitHeight : 0, reveal: root.rect(reveal, authWindow), cancel: root.rect(cancel, authWindow), revealFocused: !!reveal && reveal.controlFocused, cancelFocused: !!cancel && cancel.controlFocused, authorizeFocused: !!authorize && authorize.controlFocused, text: authWindow.visible ? root.visibleText(auth, authWindow) : [] }, noko: { visible: nokoWindow.visible, phase: overlay.phase, tick: overlay.tick, tab: root.rect(overlay.children[0], nokoWindow), text: nokoWindow.visible ? root.visibleText(overlay, nokoWindow) : [] } }); } IpcHandler { target: "nuguland" function diagnostics(): string { return diagnosticsService.snapshot(); } } IpcHandler { target: "uiTest" function external(event: string): void { root.externalEvent(event, calendar, resources, privilege, noko); } function snapshot(): string { return root.state(calendar, resources, privilege); } } }