236 lines
6.6 KiB
JavaScript
236 lines
6.6 KiB
JavaScript
import GObject from "gi://GObject";
|
|
import St from "gi://St";
|
|
import Gio from "gi://Gio";
|
|
import GLib from "gi://GLib";
|
|
import Clutter from "gi://Clutter";
|
|
|
|
import * as Main from "resource:///org/gnome/shell/ui/main.js";
|
|
import * as PanelMenu from "resource:///org/gnome/shell/ui/panelMenu.js";
|
|
|
|
// Try common ports - worktree (10001) first, then default (10000)
|
|
const DEFAULT_PORTS = [10001, 10000];
|
|
const DEBOUNCE_DELAY = 2200; // Wait 2.2s after workspace switch (dmcore polls every 2s)
|
|
const PERIODIC_REFRESH_SECONDS = 10; // Periodic refresh as backup to catch stale data
|
|
let DESKMETER_API_URL = null;
|
|
|
|
const TaskIndicator = GObject.registerClass(
|
|
class TaskIndicator extends PanelMenu.Button {
|
|
_init() {
|
|
super._init(0.0, "Deskmeter Task Indicator", false);
|
|
|
|
// Create label for task display
|
|
this._label = new St.Label({
|
|
text: "detecting...",
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
style_class: "deskmeter-task-label",
|
|
});
|
|
|
|
this.add_child(this._label);
|
|
|
|
this._debounceTimeout = null;
|
|
this._periodicTimeout = null;
|
|
this._workspaceManager = global.workspace_manager;
|
|
this._apiUrl = null;
|
|
|
|
// Connect to workspace switch signal
|
|
this._workspaceSwitchedId = this._workspaceManager.connect(
|
|
"workspace-switched",
|
|
this._onWorkspaceSwitched.bind(this),
|
|
);
|
|
|
|
// Detect API port, then start updates
|
|
this._detectApiPort();
|
|
}
|
|
|
|
_startPeriodicRefresh() {
|
|
// Periodic refresh as backup to catch stale data
|
|
this._periodicTimeout = GLib.timeout_add_seconds(
|
|
GLib.PRIORITY_DEFAULT,
|
|
PERIODIC_REFRESH_SECONDS,
|
|
() => {
|
|
this._updateTask();
|
|
return GLib.SOURCE_CONTINUE;
|
|
},
|
|
);
|
|
}
|
|
|
|
_detectApiPort() {
|
|
// Try each port in sequence
|
|
this._tryNextPort(0);
|
|
}
|
|
|
|
_tryNextPort(index) {
|
|
if (index >= DEFAULT_PORTS.length) {
|
|
// No ports responded, use default and let it show "offline"
|
|
this._apiUrl = `http://localhost:${DEFAULT_PORTS[DEFAULT_PORTS.length - 1]}/api/current_task`;
|
|
this._scheduleUpdate();
|
|
this._startPeriodicRefresh();
|
|
return;
|
|
}
|
|
|
|
const port = DEFAULT_PORTS[index];
|
|
const url = `http://localhost:${port}/api/current_task`;
|
|
|
|
try {
|
|
let file = Gio.File.new_for_uri(url);
|
|
file.load_contents_async(null, (source, result) => {
|
|
try {
|
|
let [success, contents] = source.load_contents_finish(result);
|
|
if (success) {
|
|
// Port responded, use it
|
|
this._apiUrl = url;
|
|
this._scheduleUpdate();
|
|
this._startPeriodicRefresh();
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
// This port failed, try next
|
|
this._tryNextPort(index + 1);
|
|
}
|
|
});
|
|
} catch (e) {
|
|
// This port failed, try next
|
|
this._tryNextPort(index + 1);
|
|
}
|
|
}
|
|
|
|
_onWorkspaceSwitched() {
|
|
// Debounce updates - dmcore takes ~2 seconds to detect and update
|
|
// We wait a bit to ensure the task has been updated in MongoDB
|
|
this._scheduleUpdate();
|
|
}
|
|
|
|
_scheduleUpdate() {
|
|
// Clear any pending update
|
|
if (this._debounceTimeout) {
|
|
GLib.source_remove(this._debounceTimeout);
|
|
}
|
|
|
|
// Schedule new update
|
|
this._debounceTimeout = GLib.timeout_add(
|
|
GLib.PRIORITY_DEFAULT,
|
|
DEBOUNCE_DELAY,
|
|
() => {
|
|
this._updateTask();
|
|
this._debounceTimeout = null;
|
|
return GLib.SOURCE_REMOVE;
|
|
},
|
|
);
|
|
}
|
|
|
|
_updateTask() {
|
|
if (!this._apiUrl) {
|
|
this._label.set_text("detecting...");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Create HTTP request
|
|
let file = Gio.File.new_for_uri(this._apiUrl);
|
|
file.load_contents_async(null, (source, result) => {
|
|
try {
|
|
let [success, contents] = source.load_contents_finish(result);
|
|
if (success) {
|
|
let decoder = new TextDecoder("utf-8");
|
|
let data = JSON.parse(decoder.decode(contents));
|
|
|
|
// Update label with task path
|
|
let displayText = data.task_path || "no task";
|
|
|
|
// Optionally truncate long paths
|
|
if (displayText.length > 40) {
|
|
let parts = displayText.split("/");
|
|
if (parts.length > 2) {
|
|
displayText = ".../" + parts.slice(-2).join("/");
|
|
} else {
|
|
displayText = displayText.substring(0, 37) + "...";
|
|
}
|
|
}
|
|
|
|
this._label.set_text(displayText);
|
|
}
|
|
} catch (e) {
|
|
this._label.set_text("error");
|
|
logError(e, "Failed to parse deskmeter response");
|
|
}
|
|
});
|
|
} catch (e) {
|
|
this._label.set_text("offline");
|
|
logError(e, "Failed to fetch deskmeter task");
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
try {
|
|
if (this._debounceTimeout) {
|
|
GLib.source_remove(this._debounceTimeout);
|
|
this._debounceTimeout = null;
|
|
}
|
|
|
|
if (this._periodicTimeout) {
|
|
GLib.source_remove(this._periodicTimeout);
|
|
this._periodicTimeout = null;
|
|
}
|
|
|
|
if (this._workspaceSwitchedId) {
|
|
this._workspaceManager.disconnect(this._workspaceSwitchedId);
|
|
this._workspaceSwitchedId = null;
|
|
}
|
|
|
|
super.destroy();
|
|
} catch (e) {
|
|
// Log error but don't crash GNOME Shell
|
|
logError(e, "Failed to destroy TaskIndicator");
|
|
}
|
|
}
|
|
},
|
|
);
|
|
|
|
export default class Extension {
|
|
constructor() {
|
|
this._indicator = null;
|
|
}
|
|
|
|
enable() {
|
|
try {
|
|
this._indicator = new TaskIndicator();
|
|
|
|
// Add to panel - position after workspace indicator
|
|
// Panel boxes: left, center, right
|
|
// We'll add it to the left panel, after other items
|
|
Main.panel.addToStatusArea(
|
|
"deskmeter-task-indicator",
|
|
this._indicator,
|
|
1,
|
|
"left",
|
|
);
|
|
} catch (e) {
|
|
// Log error but don't crash GNOME Shell
|
|
logError(e, "Failed to enable Deskmeter extension");
|
|
|
|
// Clean up if partially initialized
|
|
if (this._indicator) {
|
|
try {
|
|
this._indicator.destroy();
|
|
} catch (destroyError) {
|
|
logError(destroyError, "Failed to cleanup indicator");
|
|
}
|
|
this._indicator = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
disable() {
|
|
try {
|
|
if (this._indicator) {
|
|
this._indicator.destroy();
|
|
this._indicator = null;
|
|
}
|
|
} catch (e) {
|
|
// Log error but don't crash GNOME Shell
|
|
logError(e, "Failed to disable Deskmeter extension");
|
|
this._indicator = null;
|
|
}
|
|
}
|
|
}
|