@@ -83,7 +83,11 @@ class ArcOverlay {
|
||||
this.root.add_child(this.header); this.root.add_child(this.statusLine); this.root.add_child(this.job); this.root.add_child(this.target); this.root.add_child(this.cursor); this.root.add_child(this.wave); this.root.add_child(this.scroll); this.root.add_child(this.chips);
|
||||
this.controls = new St.BoxLayout({ style_class: 'jarvis-chips' });
|
||||
this.talk = new St.Button({ label: 'Talk', style_class: 'jarvis-chip jarvis-talk', reactive: true, can_focus: true });
|
||||
this.talk.accessible_name = 'Hold to talk';
|
||||
this.talk.accessible_name = 'Hold Space or Enter to talk';
|
||||
this.talk.connect('key-press-event', (_actor, event) => { if ([Clutter.KEY_space, Clutter.KEY_Return].includes(event.get_key_symbol())) { this.onTalk?.(true); return Clutter.EVENT_STOP; } return Clutter.EVENT_PROPAGATE; });
|
||||
this.talk.connect('key-release-event', (_actor, event) => { if ([Clutter.KEY_space, Clutter.KEY_Return].includes(event.get_key_symbol())) { this.onTalk?.(false); return Clutter.EVENT_STOP; } return Clutter.EVENT_PROPAGATE; });
|
||||
this.talk.connect('key-focus-out', () => this.onTalk?.(false));
|
||||
this.talk.connect('leave-event', () => { this.onTalk?.(false); return Clutter.EVENT_PROPAGATE; });
|
||||
this.talk.connect('button-press-event', () => { this.onTalk?.(true); return Clutter.EVENT_STOP; });
|
||||
this.talk.connect('button-release-event', () => { this.onTalk?.(false); return Clutter.EVENT_STOP; });
|
||||
this.controls.add_child(this.talk);
|
||||
@@ -97,11 +101,22 @@ class ArcOverlay {
|
||||
this.reducedMotion = false;
|
||||
}
|
||||
attach() { Main.layoutManager.addChrome(this.root, { affectsStruts: false, trackFullscreen: false }); this.halo = new St.Widget({ style_class: 'jarvis-halo', reactive: false }); Main.layoutManager.addChrome(this.halo, { affectsStruts: false, trackFullscreen: false }); this.halo.hide(); this.hide(); }
|
||||
show() { const monitor = Main.layoutManager.primaryMonitor; if (monitor) this.root.set_position(monitor.x + Math.max(0, Math.round((monitor.width - OVERLAY_WIDTH) / 2)), monitor.y + 64); this.root.visible = true; this.root.grab_key_focus(); }
|
||||
hide() { this.root.visible = false; }
|
||||
show() {
|
||||
const monitor = Main.layoutManager.primaryMonitor;
|
||||
if (monitor) {
|
||||
const width = Math.min(OVERLAY_WIDTH, monitor.width - 80);
|
||||
this.root.set_width(width);
|
||||
this.root.set_position(monitor.x + Math.max(0, Math.round((monitor.width - width) / 2)), monitor.y + 48);
|
||||
this.scroll.set_height(Math.max(100, Math.min(280, monitor.height - 360)));
|
||||
}
|
||||
const wasVisible = this.root.visible;
|
||||
this.root.visible = true;
|
||||
if (!wasVisible) this.entry.grab_key_focus();
|
||||
}
|
||||
hide() { this.onTalk?.(false); this.root.visible = false; }
|
||||
toggle() { this.root.visible ? this.hide() : this.show(); }
|
||||
clear() { this.transcript.destroy_all_children(); }
|
||||
addRow(who, text) { const body = safeText(text); const row = new St.Label({ text: `${who === 'J' ? 'J' : 'YOU'} ${body}`, style_class: `jarvis-row jarvis-row-${who === 'J' ? 'jarvis' : 'user'}`, can_focus: true }); row.accessible_name = `${who === 'J' ? 'Jarvis' : 'You'}: ${body}`; this.transcript.add_child(row); this.show(); return row; }
|
||||
addRow(who, text) { const body = safeText(text); const row = new St.Label({ text: `${who === 'J' ? 'J' : 'YOU'} ${body}`, style_class: `jarvis-row jarvis-row-${who === 'J' ? 'jarvis' : 'user'}`, can_focus: true }); row.accessible_name = `${who === 'J' ? 'Jarvis' : 'You'}: ${body}`; if (row.clutter_text) { row.clutter_text.line_wrap = true; row.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; row.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; } this.transcript.add_child(row); if (this.transcript.get_n_children() > 100) this.transcript.get_first_child().destroy(); this.show(); return row; }
|
||||
token(text) { const chunk = safeText(text); if (!chunk) return; let row = this.transcript.get_last_child?.(); if (!row || !String(row.style_class || '').includes('jarvis-row-jarvis')) { row = this.addRow('J', ''); } row.text = `${row.text}${chunk}`; row.accessible_name = `Jarvis: ${row.text}`; this.show(); }
|
||||
finalizeReply(text) {
|
||||
const spoken = safeText(text);
|
||||
@@ -149,7 +164,7 @@ export default class JarvisExtension extends Extension {
|
||||
this.overlay.onMode = (mode) => this._call('SetMode', '(s)', [mode]); this.overlay.onSuggestion = (id, payload) => this._call('Ask', '(s)', [payload?.text || payload?.prompt || id]);
|
||||
this._keyName = 'hotkey'; try { Main.wm.addKeybinding(this._keyName, this.settings, Meta.KeyBindingFlags.NONE, Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, () => this.overlay.toggle()); } catch (error) { log(`Jarvis hotkey unavailable: ${error.message}`); }
|
||||
this._settingsChanged = this.settings.connect('changed::accent-color', () => this._applyAccent()); this._applyAccent(); this._connectDaemon(); this.overlay.show();
|
||||
try { this._lockChanged = Main.screenShield.connect('locked-changed', () => { if (Main.screenShield.locked) this._call('ComputerRevoke'); }); } catch {}
|
||||
try { this._lockChanged = Main.screenShield.connect('locked-changed', () => { if (Main.screenShield.locked) { this.overlay.hide(); this._call('ComputerRevoke'); } }); } catch {}
|
||||
}
|
||||
async _connectDaemon() {
|
||||
const proxy = this.proxy;
|
||||
@@ -186,9 +201,18 @@ export default class JarvisExtension extends Extension {
|
||||
if (!this.proxy?.owned?.() || !this.overlay) return;
|
||||
try {
|
||||
const runtime = await this.proxy.call('GetRuntimeStatus');
|
||||
if (!this.overlay) return;
|
||||
const status = JSON.parse(runtime.deep_unpack()[0] || '{}');
|
||||
this.overlay.setConnectionStatus(status.voice ? 'local' : 'voice-unavailable');
|
||||
} catch { this.overlay.setConnectionStatus('local'); }
|
||||
const voice = status.voice;
|
||||
this.overlay.setConnectionStatus(voice ? 'local' : 'voice-unavailable');
|
||||
if (voice) {
|
||||
const input = voice.asr && voice.capture;
|
||||
this.overlay.status.text = `${voice.tts ? 'SPEECH ON' : 'SPEECH OFF'} · ${input ? (voice.wake ? 'WAKE ON' : 'HOLD TALK') : 'MIC UNAVAILABLE'}`;
|
||||
this.overlay.status.accessible_name = this.overlay.status.text;
|
||||
this.overlay.talk.reactive = Boolean(input); this.overlay.talk.can_focus = Boolean(input);
|
||||
this.overlay.talk.label = input ? 'Hold to talk' : 'Mic unavailable';
|
||||
}
|
||||
} catch { this.overlay?.setConnectionStatus('voice-unavailable'); }
|
||||
}
|
||||
_call(name, signature, value) { this.proxy.call(name, signature, value).catch((error) => { log(`Jarvis ${name}: ${error.message}`); this.overlay?.addRow('J', `${name} failed: ${error.message}`); }); }
|
||||
_setState(state) { const value = safeText(state); this._glyph.text = ({ ARMED: '◯', LISTENING: '◌', THINKING: '◉', SPEAKING: '◎', SLEEPING: '◐' })[value] || '◯'; this._glyph.text += ' Jarvis'; this._glyph.accessible_name = `Jarvis ${value.toLowerCase()}`; this.overlay.setState(value); }
|
||||
|
||||
Reference in New Issue
Block a user