ags: sync

This commit is contained in:
end-4
2024-02-05 13:20:00 +07:00
parent 14368314e5
commit 73c8454c1c
61 changed files with 1327 additions and 1220 deletions
@@ -1,4 +1,5 @@
const { Gdk, Gio, GLib, Gtk } = imports.gi;
import GtkSource from "gi://GtkSource?version=3.0";
import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
@@ -6,12 +7,12 @@ const { Box, Button, Label, Scrollable } = Widget;
const { execAsync, exec } = Utils;
import { MaterialIcon } from "../../../lib/materialicon.js";
import md2pango from "../../../lib/md2pango.js";
import GtkSource from "gi://GtkSource?version=3.0";
const CUSTOM_SOURCEVIEW_SCHEME_PATH = `${App.configDir}/data/sourceviewtheme.xml`;
const CUSTOM_SCHEME_ID = 'custom';
const USERNAME = GLib.get_user_name();
const CHATGPT_CURSOR = ' (o) ';
const CHATGPT_CURSOR = ' ...';
/////////////////////// Custom source view colorscheme /////////////////////////
+1 -1
View File
@@ -122,7 +122,7 @@ export const ChatGPTSettings = () => MarginRevealer({
},
}),
ConfigToggle({
icon: 'description',
icon: 'model_training',
name: 'Enhancements',
desc: 'Tells ChatGPT:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
initValue: ChatGPT.assistantPrompt,
+1 -1
View File
@@ -110,7 +110,7 @@ export const GeminiSettings = () => MarginRevealer({
className: 'sidebar-chat-settings-toggles',
children: [
ConfigToggle({
icon: 'description',
icon: 'model_training',
name: 'Enhancements',
desc: 'Tells Gemini:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
initValue: Gemini.assistantPrompt,
+6 -6
View File
@@ -72,12 +72,12 @@ const WaifuImage = (taglist) => {
homogeneous: false,
transition: 'slide_up_down',
transitionDuration: 150,
items: [
['api', ImageState('api', 'Calling API')],
['download', ImageState('downloading', 'Downloading image')],
['done', ImageState('done', 'Finished!')],
['error', ImageState('error', 'Error')],
]
children: {
'api': ImageState('api', 'Calling API'),
'download': ImageState('downloading', 'Downloading image'),
'done': ImageState('done', 'Finished!'),
'error': ImageState('error', 'Error'),
},
});
const downloadIndicator = MarginRevealer({
vpack: 'center',
+92 -17
View File
@@ -1,16 +1,22 @@
const { Gtk, Gdk } = imports.gi;
import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import AgsWidget from "resource:///com/github/Aylur/ags/widgets/widget.js";
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
const { Box, Button, CenterBox, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
const { Box, Button, CenterBox, Entry, EventBox, Icon, Label, Overlay, Revealer, Scrollable, Stack } = Widget;
const { execAsync, exec } = Utils;
import { setupCursorHover, setupCursorHoverInfo } from "../../lib/cursorhover.js";
import { contentStack } from './sideleft.js';
// APIs
import ChatGPT from '../../services/chatgpt.js';
import Gemini from '../../services/gemini.js';
import { geminiView, geminiCommands, sendMessage as geminiSendMessage, geminiTabIcon } from './apis/gemini.js';
import { chatGPTView, chatGPTCommands, sendMessage as chatGPTSendMessage, chatGPTTabIcon } from './apis/chatgpt.js';
import { waifuView, waifuCommands, sendMessage as waifuSendMessage, waifuTabIcon } from './apis/waifu.js';
const TextView = Widget.subclass(Gtk.TextView, "AgsTextView");
const EXPAND_INPUT_THRESHOLD = 30;
const APIS = [
{
name: 'Assistant (ChatGPT 3.5)',
@@ -40,9 +46,25 @@ const APIS = [
let currentApiId = 0;
APIS[currentApiId].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enabled', true);
export const chatEntry = Entry({
className: 'sidebar-chat-entry',
function apiSendMessage(textView) {
// Get text
const buffer = textView.get_buffer();
const [start, end] = buffer.get_bounds();
const text = buffer.get_text(start, end, true).trimStart();
if (!text || text.length == 0) return;
// Send
APIS[currentApiId].sendCommand(text)
// Reset
buffer.set_text("", -1);
chatEntryWrapper.toggleClassName('sidebar-chat-wrapper-extended', false);
chatEntry.set_valign(Gtk.Align.CENTER);
}
export const chatEntry = TextView({
hexpand: true,
wrapMode: Gtk.WrapMode.WORD_CHAR,
acceptsTab: false,
className: 'sidebar-chat-entry txt txt-smallie',
setup: (self) => self
.hook(ChatGPT, (self) => {
if (APIS[currentApiId].name != 'Assistant (ChatGPT 3.5)') return;
@@ -52,31 +74,84 @@ export const chatEntry = Entry({
if (APIS[currentApiId].name != 'Assistant (Gemini Pro)') return;
self.placeholderText = (Gemini.key.length > 0 ? 'Message Gemini...' : 'Enter Google AI API Key...');
}, 'hasKey')
.on("key-press-event", (widget, event) => {
const keyval = event.get_keyval()[1];
if (event.get_keyval()[1] === Gdk.KEY_Return && event.get_state()[1] == Gdk.ModifierType.MOD2_MASK) {
apiSendMessage(widget);
return true;
}
// Global keybinds
if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
event.get_keyval()[1] === Gdk.KEY_Page_Down) {
const toSwitchTab = contentStack.get_visible_child();
toSwitchTab.attribute.nextTab();
}
else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
event.get_keyval()[1] === Gdk.KEY_Page_Up) {
const toSwitchTab = contentStack.get_visible_child();
toSwitchTab.attribute.prevTab();
}
})
,
onChange: (entry) => {
chatSendButton.toggleClassName('sidebar-chat-send-available', entry.text.length > 0);
},
onAccept: (entry) => {
APIS[currentApiId].sendCommand(entry.text)
entry.text = '';
},
});
chatEntry.get_buffer().connect("changed", (buffer) => {
const bufferText = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), true);
chatSendButton.toggleClassName('sidebar-chat-send-available', bufferText.length > 0);
chatPlaceholderRevealer.revealChild = (bufferText.length == 0);
if (buffer.get_line_count() > 1 || bufferText.length > EXPAND_INPUT_THRESHOLD) {
chatEntryWrapper.toggleClassName('sidebar-chat-wrapper-extended', true);
chatEntry.set_valign(Gtk.Align.FILL);
chatPlaceholder.set_valign(Gtk.Align.FILL);
}
else {
chatEntryWrapper.toggleClassName('sidebar-chat-wrapper-extended', false);
chatEntry.set_valign(Gtk.Align.CENTER);
chatPlaceholder.set_valign(Gtk.Align.CENTER);
}
});
const chatEntryWrapper = Scrollable({
className: 'sidebar-chat-wrapper',
hscroll: 'never',
vscroll: 'always',
child: chatEntry,
});
const chatSendButton = Button({
className: 'txt-norm icon-material sidebar-chat-send',
vpack: 'center',
vpack: 'end',
label: 'arrow_upward',
setup: setupCursorHover,
onClicked: (self) => {
APIS[currentApiId].sendCommand(chatEntry.text);
chatEntry.text = '';
APIS[currentApiId].sendCommand(chatEntry.get_buffer().text);
chatEntry.get_buffer().set_text("", -1);
},
});
const chatPlaceholder = Label({
className: 'txt-subtext txt-smallie margin-left-5',
hpack: 'start',
vpack: 'center',
label: APIS[currentApiId].placeholderText,
});
const chatPlaceholderRevealer = Revealer({
revealChild: true,
transition: 'crossfade',
transitionDuration: 200,
child: chatPlaceholder,
});
const textboxArea = Box({ // Entry area
className: 'sidebar-chat-textarea spacing-h-10',
className: 'sidebar-chat-textarea',
children: [
chatEntry,
Overlay({
passThrough: true,
child: chatEntryWrapper,
overlays: [chatPlaceholderRevealer],
}),
Box({ className: 'width-10' }),
chatSendButton,
]
});
@@ -97,8 +172,8 @@ function switchToTab(id) {
APIS[id].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enabled', true);
apiContentStack.shown = APIS[id].name;
apiCommandStack.shown = APIS[id].name;
chatEntry.placeholderText = APIS[id].placeholderText,
currentApiId = id;
chatPlaceholder.label = APIS[id].placeholderText;
currentApiId = id;
}
const apiSwitcher = CenterBox({
+1 -1
View File
@@ -2,7 +2,7 @@ import PopupWindow from '../../lib/popupwindow.js';
import SidebarLeft from "./sideleft.js";
export default () => PopupWindow({
focusable: true,
keymode: 'exclusive',
anchor: ['left', 'top', 'bottom'],
name: 'sideleft',
layer: 'top',
+12 -12
View File
@@ -27,7 +27,7 @@ const contents = [
]
let currentTabId = 0;
const contentStack = Stack({
export const contentStack = Stack({
vexpand: true,
transition: 'slide_left_right',
items: contents.map(item => [item.name, item.content]),
@@ -117,15 +117,14 @@ const pinButton = Button({
vpack: 'start',
className: 'sidebar-pin',
child: MaterialIcon('push_pin', 'larger'),
tooltipText: 'Pin sidebar',
tooltipText: 'Pin sidebar (Ctrl+P)',
onClicked: (self) => self.attribute.toggle(self),
// QoL: Focus Pin button on open. Hit keybind -> space/enter = toggle pin state
setup: (self) => self
.hook(App, (self, currentName, visible) => {
if (currentName === 'sideleft' && visible)
self.grab_focus();
setup: (self) => {
setupCursorHover(self);
self.hook(App, (self, currentName, visible) => {
if (currentName === 'sideleft' && visible) self.grab_focus();
})
,
},
})
export default () => Box({
@@ -163,7 +162,7 @@ export default () => Box({
],
setup: (self) => self
.on('key-press-event', (widget, event) => { // Handle keybinds
if (event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) {
if (event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) { // Ctrl held
// Pin sidebar
if (event.get_keyval()[1] == Gdk.KEY_p)
pinButton.attribute.toggle(pinButton);
@@ -176,7 +175,7 @@ export default () => Box({
switchToTab(Math.min(currentTabId + 1, contents.length - 1));
}
if (contentStack.shown == 'apis') { // If api tab is focused
// Automatically focus entry when typing
// Focus entry when typing
if ((
!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 &&
@@ -186,8 +185,9 @@ export default () => Box({
event.get_keyval()[1] === Gdk.KEY_v)
) {
chatEntry.grab_focus();
chatEntry.set_text(chatEntry.text + String.fromCharCode(event.get_keyval()[1]));
chatEntry.set_position(-1);
const buffer = chatEntry.get_buffer();
buffer.set_text(buffer.text + String.fromCharCode(event.get_keyval()[1]), -1);
buffer.place_cursor(buffer.get_iter_at_offset(-1));
}
// Switch API type
else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&