This commit is contained in:
@@ -118,7 +118,6 @@ export default class JarvisExtension extends Extension {
|
||||
view.onSuggestion = (id, payload) => this._call('Ask', '(s)', [payload?.text || payload?.prompt || id]);
|
||||
view.onConfirm = (jobId, toolCallId, decision) => this._call('Confirm', '(sss)', [jobId, toolCallId, decision]);
|
||||
view.onConfirmShown = () => this._openPopup();
|
||||
view.onExpand = () => this.session.show(true);
|
||||
view.onSettings = () => this._openSettings();
|
||||
view.onMode = (mode) => this._call('SetMode', '(s)', [mode]);
|
||||
}
|
||||
@@ -134,18 +133,6 @@ export default class JarvisExtension extends Extension {
|
||||
menu.box.add_child(this.popup.root);
|
||||
}
|
||||
menu.actor?.add_style_class_name?.('jarvis-menu');
|
||||
try {
|
||||
menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
const grantItem = new PopupMenu.PopupMenuItem('Grant desktop');
|
||||
grantItem.connect('activate', () => this._call('ComputerGrant', '(b)', [true]));
|
||||
menu.addMenuItem(grantItem);
|
||||
const revokeItem = new PopupMenu.PopupMenuItem('Revoke desktop');
|
||||
revokeItem.connect('activate', () => this._call('ComputerRevoke'));
|
||||
menu.addMenuItem(revokeItem);
|
||||
const settingsItem = new PopupMenu.PopupMenuItem('Settings');
|
||||
settingsItem.connect('activate', () => this._openSettings());
|
||||
menu.addMenuItem(settingsItem);
|
||||
} catch {}
|
||||
this._menuState = menu.connect('open-state-changed', (_menu, open) => {
|
||||
if (!open) this.popup.endTalk();
|
||||
else this.popup.followActive();
|
||||
|
||||
@@ -133,8 +133,8 @@ function grantRow() {
|
||||
});
|
||||
};
|
||||
allow.connect('clicked', () => {
|
||||
callDaemon('ComputerGrant', '(b)', [true], (error) => {
|
||||
row.subtitle = error ? `Could not grant: ${error.message}` : 'Grant requested. A portal prompt may appear.';
|
||||
callDaemon('ComputerGrant', '(b)', [false], (error) => {
|
||||
row.subtitle = error ? `Could not grant: ${error.message}` : 'Grant requested. Choose a screen in the GNOME prompt.';
|
||||
refresh();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ import Shell from 'gi://Shell';
|
||||
const BUS = 'io.qvac.Jarvis.Shell';
|
||||
const PATH = '/io/qvac/Jarvis/Shell';
|
||||
const XML = `<node><interface name="io.qvac.Jarvis.Shell"><method name="ListWindows"><arg type="s" direction="out"/></method><method name="FocusedWindow"><arg type="s" direction="out"/></method><method name="FocusWindow"><arg type="t" direction="in"/></method><method name="Screenshot"><arg type="s" direction="in"/><arg type="s" direction="out"/></method></interface></node>`;
|
||||
const describe = (window) => ({ id: Number(window.get_id?.() || 0), title: window.get_title?.() || '', wm_class: window.get_wm_class?.() || '', pid: Number(window.get_pid?.() || 0), rect: (() => { const r = window.get_frame_rect?.(); return r ? [r.x, r.y, r.width, r.height] : null; })(), focused: window === global.display.get_focus_window?.() });
|
||||
const describe = (window) => ({ id: Number(window.get_id?.() || 0), title: window.get_title?.() || '', wm_class: window.get_wm_class?.() || '', pid: Number(window.get_pid?.() || 0), rect: (() => { const r = window.get_frame_rect?.(); return r ? [r.x, r.y, r.width, r.height] : null; })(), buffer_rect: (() => { const r = window.get_buffer_rect?.(); return r ? [r.x, r.y, r.width, r.height] : null; })(), focused: window === global.display.get_focus_window?.() });
|
||||
|
||||
export function installShellService() {
|
||||
const implementation = {
|
||||
|
||||
@@ -22,18 +22,18 @@
|
||||
.jarvis-tab { padding: 4px 10px; border-radius: 999px; background-color: transparent; color: #D7DDE8; font-size: 12px; }
|
||||
.jarvis-tab:hover, .jarvis-tab:focus { color: #F6F7FB; background-color: rgba(255, 255, 255, .10); }
|
||||
.jarvis-tab-active { background-color: #F4B942; color: #16191F; font-weight: bold; }
|
||||
.jarvis-thinking { color: #D7DDE8; font-size: 12px; padding: 8px 10px; border-left: 2px solid #4FD2FF; background-color: rgba(79, 210, 255, .08); border-radius: 8px; min-height: 40px; }
|
||||
.jarvis-thinking { color: #D7DDE8; font-size: 12px; padding: 8px 10px; border-left: 2px solid #4FD2FF; background-color: rgba(79, 210, 255, .08); border-radius: 8px; }
|
||||
.jarvis-notice { color: #F4B942; font-size: 12px; }
|
||||
.jarvis-confirm { spacing: 8px; padding: 4px 0; }
|
||||
.jarvis-confirm-actions { spacing: 6px; }
|
||||
.jarvis-confirm-label { color: #F6F7FB; font-size: 13px; }
|
||||
.jarvis-popup-scroll { height: 180px; }
|
||||
.jarvis-thinking-pane { height: 160px; max-height: 160px; }
|
||||
.jarvis-thinking-scroll { height: 160px; max-height: 160px; }
|
||||
.jarvis-thinking-pane { height: 228px; }
|
||||
.jarvis-thinking-scroll { height: 228px; }
|
||||
.jarvis-thinking-box { spacing: 0; }
|
||||
.jarvis-session-scroll { height: 220px; }
|
||||
.jarvis-session-thinking-pane { height: 200px; max-height: 200px; }
|
||||
.jarvis-session-thinking { height: 200px; max-height: 200px; }
|
||||
.jarvis-session-thinking-pane { height: 268px; }
|
||||
.jarvis-session-thinking { height: 268px; }
|
||||
.jarvis-thinking-scroll StScrollBar, .jarvis-session-thinking StScrollBar { min-width: 10px; padding: 0 1px; }
|
||||
.jarvis-transcript { spacing: 6px; }
|
||||
.jarvis-row { padding: 6px 10px; border-radius: 8px; font-size: 13px; color: #F6F7FB; }
|
||||
|
||||
@@ -66,6 +66,7 @@ export const prettyToolName = (value) => safeText(value || 'tool').replace(/_/g,
|
||||
|
||||
function wrapLabel(label) {
|
||||
if (label.clutter_text) {
|
||||
try { label.clutter_text.single_line_mode = false; } catch {}
|
||||
label.clutter_text.line_wrap = true;
|
||||
label.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
@@ -73,6 +74,22 @@ function wrapLabel(label) {
|
||||
return label;
|
||||
}
|
||||
|
||||
function actorWidth(actor) {
|
||||
if (!actor) return 0;
|
||||
const box = actor.get_allocation_box?.() || actor.allocation;
|
||||
if (box) {
|
||||
if (typeof box.get_width === 'function') {
|
||||
const width = Number(box.get_width()) || 0;
|
||||
if (width) return width;
|
||||
}
|
||||
const x1 = Number(box.x1) || 0;
|
||||
const x2 = Number(box.x2) || 0;
|
||||
if (x2 > x1) return x2 - x1;
|
||||
if (Number(box.width)) return Number(box.width);
|
||||
}
|
||||
return Number(actor.width) || 0;
|
||||
}
|
||||
|
||||
export class ConversationView {
|
||||
constructor({ compact = true, maxRows = compact ? POPUP_ROWS : 40, brandDir = '' } = {}) {
|
||||
this.compact = compact;
|
||||
@@ -122,19 +139,19 @@ export class ConversationView {
|
||||
this.transcript = new St.BoxLayout({ style_class: 'jarvis-transcript', vertical: true, x_expand: true });
|
||||
this.transcript.accessible_name = 'Conversation transcript';
|
||||
if (typeof this.scroll.set_child === 'function') this.scroll.set_child(this.transcript); else this.scroll.add_child(this.transcript);
|
||||
const thinkHeight = compact ? 160 : 200;
|
||||
const thinkHeight = compact ? 228 : 268;
|
||||
this.thinkingPane = new St.BoxLayout({
|
||||
style_class: compact ? 'jarvis-thinking-pane' : 'jarvis-session-thinking-pane',
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
y_expand: false,
|
||||
y_expand: true,
|
||||
visible: false,
|
||||
reactive: true,
|
||||
clip_to_allocation: true,
|
||||
height: thinkHeight,
|
||||
});
|
||||
try { this.thinkingPane.set_height(thinkHeight); } catch {}
|
||||
try { this.thinkingPane.set_style?.(`height: ${thinkHeight}px; max-height: ${thinkHeight}px;`); } catch {}
|
||||
try { this.thinkingPane.set_style?.(`height: ${thinkHeight}px;`); } catch {}
|
||||
try { this.thinkingPane.clip_to_allocation = true; } catch {}
|
||||
this.thinkingScroll = new St.ScrollView({
|
||||
style_class: compact ? 'jarvis-thinking-scroll' : 'jarvis-session-thinking',
|
||||
@@ -146,15 +163,25 @@ export class ConversationView {
|
||||
enable_mouse_scrolling: true,
|
||||
clip_to_allocation: true,
|
||||
});
|
||||
try { this.thinkingScroll.hscrollbar_policy = St.PolicyType.NEVER; this.thinkingScroll.vscrollbar_policy = St.PolicyType.ALWAYS; } catch {}
|
||||
try { this.thinkingScroll.hscrollbar_policy = St.PolicyType.NEVER; this.thinkingScroll.vscrollbar_policy = St.PolicyType.AUTOMATIC; } catch {}
|
||||
try { this.thinkingScroll.overlay_scrollbars = false; } catch {}
|
||||
try { this.thinkingScroll.set_height(thinkHeight); } catch {}
|
||||
try { this.thinkingScroll.clip_to_allocation = true; } catch {}
|
||||
this.thinkingBox = new St.BoxLayout({ style_class: 'jarvis-thinking-box', vertical: true, x_expand: true, y_expand: false });
|
||||
this.thinking = wrapLabel(new St.Label({ text: '', style_class: 'jarvis-thinking', x_expand: true, y_align: Clutter.ActorAlign.START, reactive: true, can_focus: true }));
|
||||
this.thinking = wrapLabel(new St.Label({ text: '', style_class: 'jarvis-thinking', x_expand: true, y_expand: false, y_align: Clutter.ActorAlign.START, reactive: true, can_focus: true }));
|
||||
this.thinking.accessible_name = 'Jarvis thinking';
|
||||
this.thinkingBox.add_child(this.thinking);
|
||||
if (typeof this.thinkingScroll.set_child === 'function') this.thinkingScroll.set_child(this.thinkingBox); else this.thinkingScroll.add_child(this.thinkingBox);
|
||||
this.thinkingPane.add_child(this.thinkingScroll);
|
||||
try {
|
||||
if (Clutter.BindConstraint && Clutter.BindCoordinate) {
|
||||
this.thinkingBox.add_constraint(new Clutter.BindConstraint({
|
||||
source: this.thinkingPane,
|
||||
coordinate: Clutter.BindCoordinate.WIDTH,
|
||||
offset: -8,
|
||||
}));
|
||||
}
|
||||
} catch {}
|
||||
this.chipScroll = new St.ScrollView({ style_class: 'jarvis-chip-scroll', overlay_scrollbars: true, x_expand: true, visible: false });
|
||||
try { this.chipScroll.vscrollbar_policy = St.PolicyType.NEVER; this.chipScroll.hscrollbar_policy = St.PolicyType.AUTOMATIC; } catch {}
|
||||
this.chips = new St.BoxLayout({ style_class: 'jarvis-chips' });
|
||||
@@ -177,12 +204,7 @@ export class ConversationView {
|
||||
this._bindChip(this.reset, () => this.onReset?.());
|
||||
this.controls.add_child(this.stop);
|
||||
this.controls.add_child(this.reset);
|
||||
if (compact) {
|
||||
this.expand = new St.Button({ label: 'Open', style_class: 'jarvis-chip jarvis-chip-quiet', reactive: true, can_focus: true });
|
||||
this.expand.accessible_name = 'Open conversation';
|
||||
this._bindChip(this.expand, () => this.onExpand?.());
|
||||
this.controls.add_child(this.expand);
|
||||
} else {
|
||||
if (!compact) {
|
||||
this.close = new St.Button({ label: 'Close', style_class: 'jarvis-chip jarvis-chip-quiet', reactive: true, can_focus: true });
|
||||
this._bindChip(this.close, () => this.hide());
|
||||
this.controls.add_child(this.close);
|
||||
@@ -274,30 +296,40 @@ export class ConversationView {
|
||||
try { child?.get_first_child?.()?.queue_relayout?.(); } catch {}
|
||||
}
|
||||
_viewportWidth(scroll) {
|
||||
const read = (box) => {
|
||||
if (!box) return 0;
|
||||
if (typeof box.get_width === 'function') return Number(box.get_width()) || 0;
|
||||
const x1 = Number(box.x1) || 0;
|
||||
const x2 = Number(box.x2) || 0;
|
||||
return x2 > x1 ? x2 - x1 : Number(box.width) || 0;
|
||||
};
|
||||
try {
|
||||
const width = read(scroll?.get_allocation_box?.() || scroll?.allocation);
|
||||
if (width >= 200) return width;
|
||||
} catch {}
|
||||
return Number(scroll?.width) || Number(this.root?.width) || (this.compact ? 292 : 388);
|
||||
const fallback = this.compact ? 292 : 388;
|
||||
const widths = [
|
||||
actorWidth(scroll),
|
||||
actorWidth(this.thinkingPane),
|
||||
actorWidth(this.root),
|
||||
];
|
||||
return widths.find((width) => width >= 160) || fallback;
|
||||
}
|
||||
_fitThinking() {
|
||||
if (this._destroyed) return;
|
||||
const label = this.thinking;
|
||||
const text = label?.clutter_text;
|
||||
if (!label || !text) return;
|
||||
const width = Math.max(200, this._viewportWidth(this.thinkingScroll) - 28);
|
||||
const width = Math.max(this.compact ? 260 : 360, this._viewportWidth(this.thinkingScroll) - 18);
|
||||
try { text.single_line_mode = false; } catch {}
|
||||
try { text.set_single_line_mode?.(false); } catch {}
|
||||
try { text.line_wrap = true; } catch {}
|
||||
try { text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; } catch {}
|
||||
try { text.ellipsize = Pango.EllipsizeMode.NONE; } catch {}
|
||||
try { text.width = width; } catch {}
|
||||
try { text.set_line_wrap?.(true); } catch {}
|
||||
try { text.set_line_wrap_mode?.(Pango.WrapMode.WORD_CHAR); } catch {}
|
||||
try { text.set_ellipsize?.(Pango.EllipsizeMode.NONE); } catch {}
|
||||
for (const actor of [this.thinkingBox, label, text]) {
|
||||
try { actor.set_width?.(width); } catch {}
|
||||
try { actor.width = width; } catch {}
|
||||
}
|
||||
try {
|
||||
const layout = text.get_layout?.();
|
||||
if (layout?.set_width) {
|
||||
layout.set_width(width * (Pango.SCALE || 1024));
|
||||
layout.set_wrap?.(Pango.WrapMode.WORD_CHAR);
|
||||
layout.set_ellipsize?.(Pango.EllipsizeMode.NONE);
|
||||
}
|
||||
} catch {}
|
||||
try { label.queue_relayout?.(); } catch {}
|
||||
try { this.thinkingBox?.queue_relayout?.(); } catch {}
|
||||
}
|
||||
@@ -703,10 +735,11 @@ export class SessionPanel {
|
||||
const width = Math.min(SESSION_WIDTH, monitor.width - 48);
|
||||
this.root.set_width(width);
|
||||
this.root.set_position(monitor.x + Math.max(24, monitor.width - width - 24), monitor.y + 40);
|
||||
this.view.scroll.set_height(Math.max(120, Math.min(280, monitor.height - 360)));
|
||||
const thinkHeight = Math.max(120, Math.min(240, monitor.height - 400));
|
||||
const chatHeight = Math.max(120, Math.min(280, monitor.height - 360));
|
||||
this.view.scroll.set_height(chatHeight);
|
||||
const thinkHeight = chatHeight + 48;
|
||||
this.view.thinkingPane?.set_height?.(thinkHeight);
|
||||
this.view.thinkingPane?.set_style?.(`height: ${thinkHeight}px; max-height: ${thinkHeight}px;`);
|
||||
this.view.thinkingPane?.set_style?.(`height: ${thinkHeight}px;`);
|
||||
this.view.thinkingScroll.set_height(thinkHeight);
|
||||
}
|
||||
this.root.visible = true;
|
||||
|
||||
Reference in New Issue
Block a user