forked from Shinonome/dots-hyprland
fancy expanding api input box
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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) &&
|
||||||
|
|||||||
Reference in New Issue
Block a user