From 0ada3b91e343645c3de846794cf8708378584951 Mon Sep 17 00:00:00 2001 From: James Eversole Date: Wed, 29 Apr 2026 13:55:55 -0500 Subject: [PATCH] init --- LICENSE | 7 ++ README.md | 18 +++++ contents/ui/main.qml | 161 +++++++++++++++++++++++++++++++++++++++++++ default.nix | 25 +++++++ flake.nix | 15 ++++ metadata.json | 25 +++++++ 6 files changed, 251 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 contents/ui/main.qml create mode 100644 default.nix create mode 100644 flake.nix create mode 100644 metadata.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6a7b34a --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +ISC LICENSE + +Copyright James Eversole (james@eversole.co) + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..908a35f --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +100% slopcoded + +--- + +A KDE Plasma 6 applet (panel icon) to toggle [voxtype](https://voxtype.io) voice typing on and off. + +## Prerequisites + +[voxtype](https://voxtype.io) must be installed and its daemon running. The applet reads state from `/run/user/1000/voxtype/state` and checks for the daemon via `/run/user/1000/voxtype/pid` — the same locations used by the voxtype GNOME extension. + +## What this does + +- Shows a panel icon reflecting the current voxtype state. +- Click to toggle recording on/off (sends `voxtype record toggle` when the daemon is running). +- Polls the state file periodically to update the icon and tooltip. +- Icons change by state: idle, recording, transcribing, or not-running. + +## How to use it diff --git a/contents/ui/main.qml b/contents/ui/main.qml new file mode 100644 index 0000000..94ac3ac --- /dev/null +++ b/contents/ui/main.qml @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: James Eversole +// SPDX-License-Identifier: ISC + +import QtQuick +import QtQuick.Layouts +import org.kde.kirigami as Kirigami +import org.kde.plasma.plasmoid +import org.kde.plasma.components as PlasmaComponents +import org.kde.plasma.plasma5support as Plasma5Support + +PlasmoidItem { + id: root + + // Panel size — small icon size for system tray / panel + Layout.preferredWidth: Kirigami.Units.iconSizes.small + Layout.preferredHeight: Kirigami.Units.iconSizes.small + + // Tooltip + Plasmoid.title: "Voxtype Toggle" + toolTipSubText: statusText + + // State tracking + property string currentState: "idle" + property string statusText: "Voxtype is idle" + + // State file paths (same as voxtype GNOME extension) + readonly property string runtimeDir: "/run/user/1000" + readonly property string stateFilePath: runtimeDir + "/voxtype/state" + readonly property string pidFilePath: runtimeDir + "/voxtype/pid" + + // Data source for executing voxtype record toggle + Plasma5Support.DataSource { + id: executable + engine: "executable" + + function toggleRecording() { + if (daemonRunning) { + executable.connectSource("voxtype record toggle") + } else { + updateState("not-running") + } + } + + function checkDaemon() { + // Check if the voxtype daemon process is running + var cmd = "if [ -f " + root.pidFilePath + " ]; then kill -0 $(cat " + root.pidFilePath + ") 2>/dev/null && echo running; fi" + executable.connectSource(cmd) + } + + onNewData: function(sourceName, data) { + var exitCode = data["exit code"] + var output = data.stdout.trim() + + // Handle the toggle command + if (sourceName.includes("voxtype record toggle")) { + executable.disconnectSource(sourceName) + // Re-read state from disk after toggling + refreshState() + return + } + + // Handle daemon check + if (sourceName.includes("kill -0")) { + daemonRunning = output === "running" + executable.disconnectSource(sourceName) + } + } + } + + // Data source for reading the state file from disk + Plasma5Support.DataSource { + id: stateReader + engine: "executable" + connectedSources: [] + + onNewData: function(sourceName, data) { + stateReader.disconnectSource(sourceName) + var output = data.stdout.trim() + // If the state file doesn't exist or is empty, default to idle + updateState(output || "idle") + } + } + + // Periodic timer to refresh state from disk + Timer { + id: stateRefreshTimer + interval: 3000 + running: true + repeat: true + onTriggered: { + if (currentState !== "not-running") { + refreshState() + } + } + } + + // Re-trigger polling faster when transcribing + onCurrentStateChanged: { + if (currentState === "transcribing") { + stateRefreshTimer.interval = 1500 + } else { + stateRefreshTimer.interval = 3000 + } + } + + // Whether the voxtype daemon is currently running + property bool daemonRunning: false + + function updateState(newStatus) { + currentState = newStatus + switch (newStatus) { + case "idle": + statusText = "Voxtype is idle" + break + case "recording": + statusText = "Voxtype is recording" + break + case "transcribing": + statusText = "Voxtype is transcribing" + break + case "not-running": + statusText = "Voxtype daemon not running" + break + default: + statusText = "Voxtype is " + newStatus + } + } + + function refreshState() { + stateReader.connectSource("cat " + root.stateFilePath + " 2>/dev/null || echo idle") + } + + // Icon source based on current state + readonly property string iconSource: { + switch (currentState) { + case "recording": + return "media-record-symbolic" + case "transcribing": + return "content-loading-symbolic" + case "not-running": + return "dialog-warning-symbolic" + case "idle": + default: + return "audio-input-microphone-symbolic" + } + } + + // Main clickable button + PlasmaComponents.ToolButton { + id: toggleButton + anchors.centerIn: parent + icon.name: root.iconSource + onClicked: executable.toggleRecording() + } + + // Initial state check when the component is ready + Component.onCompleted: { + refreshState() + executable.checkDaemon() + } +} diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..2532010 --- /dev/null +++ b/default.nix @@ -0,0 +1,25 @@ +{ stdenvNoCC, lib }: + +stdenvNoCC.mkDerivation { + pname = "plasma-applet-voxtype-toggle"; + version = "1.0.1"; + + src = ./.; + + installPhase = '' + runHook preInstall + + install -D -t "$out/share/plasma/plasmoids/org.eversole.voxtype-toggle/" \ + metadata.json + install -D -t "$out/share/plasma/plasmoids/org.eversole.voxtype-toggle/contents/ui" \ + contents/ui/main.qml + + runHook postInstall + ''; + + meta = with lib; { + description = "KDE Plasma 6 applet to toggle voxtype voice typing on/off"; + license = licenses.isc; + platforms = platforms.linux; + }; +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..dc7b1c7 --- /dev/null +++ b/flake.nix @@ -0,0 +1,15 @@ +{ + description = "Voxtype Toggle - Plasma 6 applet to toggle voxtype voice typing on/off"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + outputs = { self, nixpkgs, ... }: let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + in { + packages.${system} = { + plasmaAppletVoxtypeToggle = pkgs.callPackage ./. {}; + default = self.packages.${system}.plasmaAppletVoxtypeToggle; + }; + }; +} diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..48c060c --- /dev/null +++ b/metadata.json @@ -0,0 +1,25 @@ +{ + "KPackageStructure": "Plasma/Applet", + "KPlugin": { + "Name": "Voxtype Toggle", + "Name[en]": "Voxtype Toggle", + "Description": "Toggle voxtype voice typing on/off", + "Description[en]": "Toggle voxtype voice typing on/off", + "Id": "org.eversole.voxtype-toggle", + "Icon": "accessories-voice", + "Name[en_US]": "Voxtype Toggle For plasmashell", + "Version": "1.0.0", + "License": "ISC", + "Category": "System Information", + "Authors": [ + { + "Name": "James Eversole", + "Email": "james@eversole.co" + } + ], + "Website": "https://git.eversole.co/James/voxtype-toggle-plasmashell" + }, + "X-Plasma-API-Minimum-Version": "6.0", + "X-Plasma-Required-Data-Engines": "executable", + "X-Plasma-MainFile": "main.qml" +}