Build the Panama Hyprland desktop

This commit is contained in:
Gabriel Brown
2026-08-17 10:32:55 -04:00
parent 67033f2a31
commit 5248883e4b
190 changed files with 18554 additions and 34 deletions
@@ -0,0 +1,209 @@
pragma Singleton
// Local screen recognition. Capture owns geometry; this service owns the
// Tesseract/ZBar process boundary, result state, explicit follow-up actions,
// and cleanup of temporary screen images.
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
readonly property string helperPath: Quickshell.shellDir + "/scripts/screen-intelligence"
// "idle" | "capturing" | "reading" | "ready" | "error"
property string phase: "idle"
// Do not map the result surface until grim has finished, or the overlay
// can appear in the very pixels it is trying to analyze.
readonly property bool visible: phase !== "idle" && phase !== "capturing"
readonly property bool busy: phase === "capturing" || phase === "reading"
property bool ocrReady: false
property bool codeReady: false
property bool englishReady: false
property string text: ""
property string codeType: ""
property string codeValue: ""
property string error: ""
property string imagePath: ""
property bool ownsImage: false
readonly property string primaryUrl: root._firstWebUrl(root.codeValue) || root._firstWebUrl(root.text)
function refresh(): void {
probeProc.running = false;
probeProc.running = true;
}
function analyzeRegion(geometry: string, outputName: string): void {
root._prepare();
root.imagePath = Quickshell.cachePath("screen-intelligence-" + Date.now() + ".png");
root.ownsImage = true;
root.phase = "capturing";
const command = ["grim"];
if (geometry !== "")
command.push("-g", geometry);
else if (outputName !== "")
command.push("-o", outputName);
command.push(root.imagePath);
captureProc.command = command;
captureProc.running = false;
captureProc.running = true;
}
function analyzeFile(path: string): void {
root._prepare();
root.imagePath = path;
root.ownsImage = false;
root._startRecognition();
}
function copyText(): void {
if (root.text !== "")
Quickshell.execDetached(["wl-copy", root.text]);
}
function copyCode(): void {
if (root.codeValue !== "")
Quickshell.execDetached(["wl-copy", root.codeValue]);
}
function search(): void {
if (root.text === "")
return;
Quickshell.execDetached(["xdg-open", "https://www.google.com/search?q=" + encodeURIComponent(root.text.slice(0, 4000))]);
}
function translate(): void {
if (root.text === "")
return;
Quickshell.execDetached(["xdg-open", "https://translate.google.com/?sl=auto&tl=en&text=" + encodeURIComponent(root.text.slice(0, 4000))]);
}
function openDetected(): void {
if (root.primaryUrl !== "")
Quickshell.execDetached(["xdg-open", root.primaryUrl]);
}
function close(): void {
captureProc.running = false;
recognizeProc.running = false;
root._dropOwnedImage();
root.phase = "idle";
root.text = "";
root.codeType = "";
root.codeValue = "";
root.error = "";
root.imagePath = "";
root.ownsImage = false;
}
function _prepare(): void {
captureProc.running = false;
recognizeProc.running = false;
root._dropOwnedImage();
root.text = "";
root.codeType = "";
root.codeValue = "";
root.error = "";
root.imagePath = "";
root.ownsImage = false;
}
function _startRecognition(): void {
root.phase = "reading";
recognizeProc.sawOutput = false;
recognizeProc.command = [root.helperPath, "analyze-file", root.imagePath];
recognizeProc.running = false;
recognizeProc.running = true;
}
function _consume(raw: string): void {
try {
const result = JSON.parse(raw);
if (!result.ok) {
root.error = result.error || "Panama could not read that capture.";
root.phase = "error";
return;
}
root.text = (result.text || "").trim();
root.codeType = result.codeType || "";
root.codeValue = result.codeValue || "";
root.error = "";
root.phase = "ready";
} catch (e) {
root.error = "The local recognition engine returned an unreadable result.";
root.phase = "error";
}
}
function _dropOwnedImage(): void {
if (root.ownsImage && root.imagePath !== "")
Quickshell.execDetached(["rm", "-f", root.imagePath]);
}
function _firstWebUrl(value: string): string {
if (!value)
return "";
const match = value.match(/https?:\/\/[^\s<>'\"]+/i);
if (!match)
return "";
return match[0].replace(/[),.;!?]+$/, "");
}
Process {
id: probeProc
command: [root.helperPath, "probe"]
stdout: StdioCollector {
onStreamFinished: {
try {
const result = JSON.parse(this.text);
root.ocrReady = result.tesseract === true;
root.codeReady = result.zbar === true;
root.englishReady = result.english === true;
} catch (e) {
root.ocrReady = false;
root.codeReady = false;
root.englishReady = false;
}
}
}
}
Process {
id: captureProc
onExited: (code, status) => {
if (root.phase !== "capturing")
return;
if (code !== 0) {
root.error = "Panama could not capture the selected area.";
root.phase = "error";
return;
}
root._startRecognition();
}
}
Process {
id: recognizeProc
property bool sawOutput: false
stdout: StdioCollector {
onStreamFinished: {
recognizeProc.sawOutput = this.text !== "";
root._consume(this.text);
}
}
onExited: (code, status) => {
if (!recognizeProc.sawOutput && root.phase === "reading") {
root.error = "The local recognition engine did not return a result.";
root.phase = "error";
}
}
}
Component.onCompleted: root.refresh()
}