fancy expanding api input box

This commit is contained in:
end-4
2024-01-27 00:19:42 +07:00
parent 962e29d406
commit d425af4cdf
5 changed files with 110 additions and 26 deletions
+8
View File
@@ -420,6 +420,14 @@
margin-bottom: 1.023rem; margin-bottom: 1.023rem;
} }
.width-10 {
min-width: 0.682rem;
}
.height-10 {
min-width: 0.682rem;
}
.invisible { .invisible {
opacity: 0; opacity: 0;
background-color: transparent; background-color: transparent;
+8
View File
@@ -523,6 +523,14 @@ $onChatgpt: $onPrimary;
min-width: 0rem; min-width: 0rem;
} }
.sidebar-chat-wrapper {
@include element_easeInOut;
}
.sidebar-chat-wrapper-extended {
min-height: 5.114rem;
}
.sidebar-chat-send { .sidebar-chat-send {
@include menu_decel; @include menu_decel;
min-width: 1.705rem; min-width: 1.705rem;
+4 -2
View File
@@ -83,12 +83,14 @@ class WaifuService extends Service {
async fetch(msg) { async fetch(msg) {
// Init // Init
const userArgs = msg.split(' '); const userArgs = msg.split(/\s+/);
let taglist = []; let taglist = [];
this._nsfw = false; this._nsfw = false;
// Construct body/headers // Construct body/headers
for (let i = 0; i < userArgs.length; i++) { for (let i = 0; i < userArgs.length; i++) {
const thisArg = userArgs[i]; const thisArg = userArgs[i].trim();
if(thisArg.length == 0) continue;
if (thisArg == '--im') this._mode = 'im'; if (thisArg == '--im') this._mode = 'im';
else if (thisArg == '--nekos') this._mode = 'nekos'; else if (thisArg == '--nekos') this._mode = 'nekos';
else if (thisArg.includes('pics')) this._mode = 'pics'; else if (thisArg.includes('pics')) this._mode = 'pics';
+82 -17
View File
@@ -1,16 +1,28 @@
const { Gtk, Gdk } = imports.gi;
import App from 'resource:///com/github/Aylur/ags/app.js'; import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.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'; 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; const { execAsync, exec } = Utils;
import { setupCursorHover, setupCursorHoverInfo } from "../../lib/cursorhover.js"; import { setupCursorHover, setupCursorHoverInfo } from "../../lib/cursorhover.js";
import { contentStack } from './sideleft.js';
// APIs // APIs
import ChatGPT from '../../services/chatgpt.js'; import ChatGPT from '../../services/chatgpt.js';
import Gemini from '../../services/gemini.js'; import Gemini from '../../services/gemini.js';
import { geminiView, geminiCommands, sendMessage as geminiSendMessage, geminiTabIcon } from './apis/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 { chatGPTView, chatGPTCommands, sendMessage as chatGPTSendMessage, chatGPTTabIcon } from './apis/chatgpt.js';
import { waifuView, waifuCommands, sendMessage as waifuSendMessage, waifuTabIcon } from './apis/waifu.js'; import { waifuView, waifuCommands, sendMessage as waifuSendMessage, waifuTabIcon } from './apis/waifu.js';
class AgsTextView extends AgsWidget(Gtk.TextView, "AgsTextView") {
static { AgsWidget.register(this, {}); }
constructor(params) {
super(params);
}
}
const TextView = Widget.createCtor(AgsTextView);
const EXPAND_INPUT_THRESHOLD = 30;
const APIS = [ const APIS = [
{ {
name: 'Assistant (ChatGPT 3.5)', name: 'Assistant (ChatGPT 3.5)',
@@ -40,9 +52,27 @@ const APIS = [
let currentApiId = 0; let currentApiId = 0;
APIS[currentApiId].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enabled', true); APIS[currentApiId].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enabled', true);
export const chatEntry = Entry({ function apiSendMessage(textView) {
className: 'sidebar-chat-entry', // 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
Utils.timeout(100, () => {
buffer.set_text("", -1);
chatEntryWrapper.toggleClassName('sidebar-chat-wrapper-extended', false);
chatEntry.set_valign(Gtk.Align.CENTER);
});
}
export const chatEntry = TextView({
hexpand: true, hexpand: true,
wrapMode: Gtk.WrapMode.WORD_CHAR,
acceptsTab: false,
className: 'sidebar-chat-entry',
setup: (self) => self setup: (self) => self
.hook(ChatGPT, (self) => { .hook(ChatGPT, (self) => {
if (APIS[currentApiId].name != 'Assistant (ChatGPT 3.5)') return; if (APIS[currentApiId].name != 'Assistant (ChatGPT 3.5)') return;
@@ -52,31 +82,66 @@ export const chatEntry = Entry({
if (APIS[currentApiId].name != 'Assistant (Gemini Pro)') return; if (APIS[currentApiId].name != 'Assistant (Gemini Pro)') return;
self.placeholderText = (Gemini.key.length > 0 ? 'Message Gemini...' : 'Enter Google AI API Key...'); self.placeholderText = (Gemini.key.length > 0 ? 'Message Gemini...' : 'Enter Google AI API Key...');
}, 'hasKey') }, '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);
}, chatEntry.get_buffer().connect("changed", (buffer) => {
onAccept: (entry) => { const bufferText = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), true);
APIS[currentApiId].sendCommand(entry.text) chatSendButton.toggleClassName('sidebar-chat-send-available', bufferText.length > 0);
entry.text = ''; if (buffer.get_line_count() > 1 || bufferText.length > EXPAND_INPUT_THRESHOLD) {
}, chatEntryWrapper.toggleClassName('sidebar-chat-wrapper-extended', true);
chatEntry.set_valign(Gtk.Align.FILL);
}
else {
chatEntryWrapper.toggleClassName('sidebar-chat-wrapper-extended', false);
chatEntry.set_valign(Gtk.Align.CENTER);
}
});
const chatEntryWrapper = Scrollable({
className: 'sidebar-chat-wrapper',
hscroll: 'never',
vscroll: 'never',
child: chatEntry,
}); });
const chatSendButton = Button({ const chatSendButton = Button({
className: 'txt-norm icon-material sidebar-chat-send', className: 'txt-norm icon-material sidebar-chat-send',
vpack: 'center', vpack: 'end',
label: 'arrow_upward', label: 'arrow_upward',
setup: setupCursorHover, setup: setupCursorHover,
onClicked: (self) => { onClicked: (self) => {
APIS[currentApiId].sendCommand(chatEntry.text); APIS[currentApiId].sendCommand(chatEntry.get_buffer().text);
chatEntry.text = ''; chatEntry.get_buffer().set_text("", -1);
}, },
}); });
const textboxArea = Box({ // Entry area const textboxArea = Box({ // Entry area
className: 'sidebar-chat-textarea spacing-h-10', className: 'sidebar-chat-textarea',
children: [ children: [
chatEntry, Overlay({
passThrough: true,
child: chatEntryWrapper,
}),
Box({ className: 'width-10' }),
chatSendButton, chatSendButton,
] ]
}); });
@@ -97,8 +162,8 @@ function switchToTab(id) {
APIS[id].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enabled', true); APIS[id].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enabled', true);
apiContentStack.shown = APIS[id].name; apiContentStack.shown = APIS[id].name;
apiCommandStack.shown = APIS[id].name; apiCommandStack.shown = APIS[id].name;
chatEntry.placeholderText = APIS[id].placeholderText, chatEntry.placeholderText = APIS[id].placeholderText;
currentApiId = id; currentApiId = id;
} }
const apiSwitcher = CenterBox({ const apiSwitcher = CenterBox({
+8 -7
View File
@@ -27,7 +27,7 @@ const contents = [
] ]
let currentTabId = 0; let currentTabId = 0;
const contentStack = Stack({ export const contentStack = Stack({
vexpand: true, vexpand: true,
transition: 'slide_left_right', transition: 'slide_left_right',
items: contents.map(item => [item.name, item.content]), items: contents.map(item => [item.name, item.content]),
@@ -117,9 +117,9 @@ const pinButton = Button({
vpack: 'start', vpack: 'start',
className: 'sidebar-pin', className: 'sidebar-pin',
child: MaterialIcon('push_pin', 'larger'), child: MaterialIcon('push_pin', 'larger'),
tooltipText: 'Pin sidebar', tooltipText: 'Pin sidebar (Ctrl+P)',
onClicked: (self) => self.attribute.toggle(self), onClicked: (self) => self.attribute.toggle(self),
// QoL: Focus Pin button on open. Hit keybind -> space/enter = toggle pin state // Focus Pin button on open. Hit keybind -> space/enter = toggle pin state
setup: (self) => self setup: (self) => self
.hook(App, (self, currentName, visible) => { .hook(App, (self, currentName, visible) => {
if (currentName === 'sideleft' && visible) if (currentName === 'sideleft' && visible)
@@ -163,7 +163,7 @@ export default () => Box({
], ],
setup: (self) => self setup: (self) => self
.on('key-press-event', (widget, event) => { // Handle keybinds .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 // Pin sidebar
if (event.get_keyval()[1] == Gdk.KEY_p) if (event.get_keyval()[1] == Gdk.KEY_p)
pinButton.attribute.toggle(pinButton); pinButton.attribute.toggle(pinButton);
@@ -176,7 +176,7 @@ export default () => Box({
switchToTab(Math.min(currentTabId + 1, contents.length - 1)); switchToTab(Math.min(currentTabId + 1, contents.length - 1));
} }
if (contentStack.shown == 'apis') { // If api tab is focused if (contentStack.shown == 'apis') { // If api tab is focused
// Automatically focus entry when typing // Focus entry when typing
if (( if ((
!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) && !(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 && event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 &&
@@ -186,8 +186,9 @@ export default () => Box({
event.get_keyval()[1] === Gdk.KEY_v) event.get_keyval()[1] === Gdk.KEY_v)
) { ) {
chatEntry.grab_focus(); chatEntry.grab_focus();
chatEntry.set_text(chatEntry.text + String.fromCharCode(event.get_keyval()[1])); const buffer = chatEntry.get_buffer();
chatEntry.set_position(-1); buffer.set_text(buffer.text + String.fromCharCode(event.get_keyval()[1]), -1);
buffer.place_cursor(buffer.get_iter_at_offset(-1));
} }
// Switch API type // Switch API type
else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) && else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&