pragma Singleton pragma ComponentBehavior: Bound import QtCore import QtQuick import Quickshell import Quickshell.Io Singleton { id: root property bool dmsAvailable: false property var availablePlugins: [] property var installedPlugins: [] property bool isConnected: false property bool isConnecting: false readonly property string socketPath: Quickshell.env("DMS_SOCKET") property int nextRequestId: 1 property var pendingRequests: ({}) signal pluginsListReceived(var plugins) signal installedPluginsReceived(var plugins) signal searchResultsReceived(var plugins) signal operationSuccess(string message) signal operationError(string error) Component.onCompleted: { if (socketPath && socketPath.length > 0) { checkSocket() } } function checkSocket() { testProcess.running = true } Process { id: testProcess command: ["test", "-S", root.socketPath] onExited: exitCode => { if (exitCode === 0) { root.dmsAvailable = true connectSocket() } else { root.dmsAvailable = false } } } function connectSocket() { if (!dmsAvailable || isConnected || isConnecting) { return } isConnecting = true socket.connected = true } Socket { id: socket path: root.socketPath connected: false onConnectionStateChanged: { if (connected) { root.isConnected = true root.isConnecting = false } else { root.isConnected = false root.isConnecting = false } } parser: SplitParser { onRead: line => { if (!line || line.length === 0) { return } try { const response = JSON.parse(line) handleResponse(response) } catch (e) { console.warn("DMSService: Failed to parse response:", line, e) } } } } function sendRequest(method, params, callback) { if (!isConnected) { if (callback) { callback({ "error": "not connected to DMS socket" }) } return } const id = nextRequestId++ const request = { "id": id, "method": method } if (params) { request.params = params } if (callback) { pendingRequests[id] = callback } const json = JSON.stringify(request) + "\n" socket.write(json) } function handleResponse(response) { const callback = pendingRequests[response.id] if (callback) { delete pendingRequests[response.id] callback(response) } } function ping(callback) { sendRequest("ping", null, callback) } function listPlugins(callback) { sendRequest("plugins.list", null, response => { if (response.result) { availablePlugins = response.result pluginsListReceived(response.result) } if (callback) { callback(response) } }) } function listInstalled(callback) { sendRequest("plugins.listInstalled", null, response => { if (response.result) { installedPlugins = response.result installedPluginsReceived(response.result) } if (callback) { callback(response) } }) } function search(query, category, compositor, capability, callback) { const params = { "query": query } if (category) { params.category = category } if (compositor) { params.compositor = compositor } if (capability) { params.capability = capability } sendRequest("plugins.search", params, response => { if (response.result) { searchResultsReceived(response.result) } if (callback) { callback(response) } }) } function install(pluginName, callback) { sendRequest("plugins.install", { "name": pluginName }, response => { if (callback) { callback(response) } if (!response.error) { listInstalled() } }) } function uninstall(pluginName, callback) { sendRequest("plugins.uninstall", { "name": pluginName }, response => { if (callback) { callback(response) } if (!response.error) { listInstalled() } }) } function update(pluginName, callback) { sendRequest("plugins.update", { "name": pluginName }, response => { if (callback) { callback(response) } if (!response.error) { listInstalled() } }) } }