@@ -0,0 +1,136 @@
|
||||
import Adw from 'gi://Adw';
|
||||
import Gio from 'gi://Gio';
|
||||
import GLib from 'gi://GLib';
|
||||
import Gtk from 'gi://Gtk';
|
||||
import { SettingsEditor } from './settings-editor.js';
|
||||
|
||||
const BUS = 'io.qvac.Jarvis';
|
||||
const PATH = '/io/qvac/Jarvis';
|
||||
const IFACE = 'io.qvac.Jarvis.Session';
|
||||
const OVERLAY_STYLES = ['tray', 'expanded'];
|
||||
|
||||
function entryRow(settings, title, key) {
|
||||
const row = new Adw.EntryRow({ title });
|
||||
row.set_text(settings.get_string(key));
|
||||
row.connect('changed', () => settings.set_string(key, row.get_text()));
|
||||
return row;
|
||||
}
|
||||
|
||||
function strvRow(settings, title, key) {
|
||||
const row = new Adw.EntryRow({ title });
|
||||
row.set_text(settings.get_strv(key).join(', '));
|
||||
row.connect('changed', () => {
|
||||
const values = row.get_text().split(',').map((item) => item.trim()).filter(Boolean);
|
||||
settings.set_strv(key, values);
|
||||
});
|
||||
return row;
|
||||
}
|
||||
|
||||
function comboRow(settings, title, subtitle, key, values) {
|
||||
const row = new Adw.ComboRow({ title, subtitle, model: Gtk.StringList.new(values) });
|
||||
const current = settings.get_string(key);
|
||||
row.selected = Math.max(0, values.indexOf(current));
|
||||
row.connect('notify::selected', () => {
|
||||
const value = values[row.selected];
|
||||
if (value) settings.set_string(key, value);
|
||||
});
|
||||
return row;
|
||||
}
|
||||
|
||||
function callDaemon(name, signature, values, onDone) {
|
||||
Gio.DBus.session.call(
|
||||
BUS,
|
||||
PATH,
|
||||
IFACE,
|
||||
name,
|
||||
signature ? GLib.Variant.new(signature, values) : null,
|
||||
null,
|
||||
Gio.DBusCallFlags.NONE,
|
||||
4000,
|
||||
null,
|
||||
(_source, result) => {
|
||||
try {
|
||||
const reply = Gio.DBus.session.call_finish(result);
|
||||
onDone?.(null, reply);
|
||||
} catch (error) {
|
||||
onDone?.(error);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function grantRow() {
|
||||
const row = new Adw.ActionRow({
|
||||
title: 'Desktop grant',
|
||||
subtitle: 'Jarvis needs a temporary grant before observing or controlling the desktop.',
|
||||
});
|
||||
const allow = new Gtk.Button({ label: 'Allow now', valign: Gtk.Align.CENTER });
|
||||
allow.add_css_class('suggested-action');
|
||||
const revoke = new Gtk.Button({ label: 'Revoke', valign: Gtk.Align.CENTER });
|
||||
revoke.add_css_class('destructive-action');
|
||||
const refresh = () => {
|
||||
callDaemon('ComputerStatus', null, null, (error, reply) => {
|
||||
if (error) {
|
||||
row.subtitle = 'Jarvis daemon is unavailable. Start jarvisd, then try Allow now.';
|
||||
return;
|
||||
}
|
||||
let status = {};
|
||||
try {
|
||||
const unpacked = reply.deep_unpack?.() ?? reply.unpack?.();
|
||||
const raw = Array.isArray(unpacked) ? unpacked[0] : unpacked;
|
||||
status = JSON.parse(String(raw || '{}'));
|
||||
} catch {}
|
||||
if (status.active) {
|
||||
row.subtitle = `Active. ${Number(status.steps_used) || 0} of ${Number(status.steps_max) || 20} steps used.`;
|
||||
} else {
|
||||
row.subtitle = 'Off. Press Allow now so Jarvis can observe or control the desktop for the configured grant duration.';
|
||||
}
|
||||
});
|
||||
};
|
||||
allow.connect('clicked', () => {
|
||||
callDaemon('ComputerGrant', '(b)', [true], (error) => {
|
||||
row.subtitle = error ? `Could not grant: ${error.message}` : 'Grant requested. A portal prompt may appear.';
|
||||
refresh();
|
||||
});
|
||||
});
|
||||
revoke.connect('clicked', () => {
|
||||
callDaemon('ComputerRevoke', null, null, (error) => {
|
||||
row.subtitle = error ? `Could not revoke: ${error.message}` : 'Grant revoked.';
|
||||
refresh();
|
||||
});
|
||||
});
|
||||
row.add_suffix(allow);
|
||||
row.add_suffix(revoke);
|
||||
row.activatable_widget = allow;
|
||||
refresh();
|
||||
return row;
|
||||
}
|
||||
|
||||
export function fillSettingsWindow(window, settings, directory) {
|
||||
window.set_title('Jarvis QVAC');
|
||||
window.set_default_size(840, 780);
|
||||
window.search_enabled = true;
|
||||
const editor = new SettingsEditor(directory, settings);
|
||||
const voice = editor.page(window, 'Voice', ['Speech', 'Voice design'], 'audio-speakers-symbolic', true);
|
||||
window.add(voice);
|
||||
const listening = editor.page(window, 'Listening', ['Listening', 'Wake and privacy', 'Detection tuning', 'Audio routing'], 'audio-input-microphone-symbolic');
|
||||
window.add(listening);
|
||||
|
||||
const desktop = editor.page(window, 'Desktop', ['Desktop access', 'Desktop images'], 'preferences-desktop-display-symbolic');
|
||||
const desktopGroup = new Adw.PreferencesGroup({ title: 'Overlay and shortcuts', description: 'These appearance settings take effect immediately.' });
|
||||
desktopGroup.add(strvRow(settings, 'Hotkey', 'hotkey'));
|
||||
desktopGroup.add(entryRow(settings, 'Accent color', 'accent-color'));
|
||||
desktopGroup.add(comboRow(settings, 'Desktop layout', 'Tray keeps Jarvis in the top bar; expanded opens a conversation panel', 'overlay-style', OVERLAY_STYLES));
|
||||
desktop.add(desktopGroup);
|
||||
const grantGroup = new Adw.PreferencesGroup({ title: 'Temporary desktop access' });
|
||||
grantGroup.add(grantRow()); desktop.add(grantGroup);
|
||||
window.add(desktop);
|
||||
const models = editor.page(window, 'Models', ['Chat model', 'Agent limits'], 'system-run-symbolic');
|
||||
const service = new Adw.PreferencesGroup({ title: 'Apply chat model changes' });
|
||||
const restart = new Adw.ActionRow({ title: 'Restart Jarvis', subtitle: 'Saves your changes and restarts the user service. Ends the current request and desktop grant.' });
|
||||
const restartButton = new Gtk.Button({ label: 'Save & Restart', valign: Gtk.Align.CENTER });
|
||||
restartButton.connect('clicked', async () => { restartButton.sensitive = false; try { await editor.restart(); } finally { restartButton.sensitive = true; } });
|
||||
restart.add_suffix(restartButton); service.add(restart); models.add(service);
|
||||
window.add(models);
|
||||
return { editor, pages: [voice, listening, desktop, models] };
|
||||
}
|
||||
Reference in New Issue
Block a user