getting ready for more apis...

This commit is contained in:
end-4 2023-12-28 01:31:09 +07:00
parent 5454b3cad3
commit b0eb943ebc
8 changed files with 260 additions and 102 deletions

View file

@ -1,7 +1,6 @@
"strict mode"; "strict mode";
// Import // Import
import { App, Utils } from './imports.js'; import { App, Utils } from './imports.js';
import { firstRunWelcome } from './services/messages.js';
// Widgets // Widgets
import Bar from './widgets/bar/main.js'; import Bar from './widgets/bar/main.js';
import Cheatsheet from './widgets/cheatsheet/main.js'; import Cheatsheet from './widgets/cheatsheet/main.js';
@ -15,18 +14,20 @@ import Session from './widgets/session/main.js';
import SideLeft from './widgets/sideleft/main.js'; import SideLeft from './widgets/sideleft/main.js';
import SideRight from './widgets/sideright/main.js'; import SideRight from './widgets/sideright/main.js';
// Longer than actual anim time (see styles) to make sure widgets animate fully const CLOSE_ANIM_TIME = 210; // Longer than actual anim time (see styles) to make sure widgets animate fully
const CLOSE_ANIM_TIME = 210;
// Init cache and check first run // Init cache and check first run
Utils.exec(`bash -c 'mkdir -p ~/.cache/ags/user/colorschemes'`); Utils.exec(`bash -c 'mkdir -p ~/.cache/ags/user/colorschemes'`);
// SCSS compilation // SCSS compilation
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicmaterial.scss'`); // reset music styles Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicmaterial.scss'`); // reset music styles
Utils.exec(`sassc ${App.configDir}/scss/main.scss ${App.configDir}/style.css`); function applyStyle() {
App.resetCss(); Utils.exec(`sassc ${App.configDir}/scss/main.scss ${App.configDir}/style.css`);
App.applyCss(`${App.configDir}/style.css`); App.resetCss();
App.applyCss(`${App.configDir}/style.css`);
console.log('[LOG] Styles loaded')
}
applyStyle();
// Config object // Config object
export default { export default {

View file

@ -28,7 +28,7 @@ const m2p_styles = [
{ name: EMPH, re: /\*(\S.*?\S)\*/g, sub: "<i>$1</i>" }, { name: EMPH, re: /\*(\S.*?\S)\*/g, sub: "<i>$1</i>" },
// { name: EMPH, re: /_(\S.*?\S)_/g, sub: "<i>$1</i>" }, // { name: EMPH, re: /_(\S.*?\S)_/g, sub: "<i>$1</i>" },
{ name: HEXCOLOR, re: /#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/g, sub: `<span bgcolor='#$1' fgcolor='#000000' font_family='${monospaceFonts}'> #$1 </span>` }, { name: HEXCOLOR, re: /#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/g, sub: `<span bgcolor='#$1' fgcolor='#000000' font_family='${monospaceFonts}'> #$1 </span>` },
{ name: INLCODE, re: /(`)([^`]*)(`)/g, sub: `<span font_weight='bold' font_family='${monospaceFonts}' bgcolor='#000000' fgcolor='#ffffff'> $2 </span>` }, { name: INLCODE, re: /(`)([^`]*)(`)/g, sub: `<span font_weight='bold' font_family='${monospaceFonts}'> $2 </span>` },
// { name: UND, re: /(__|\*\*)(\S[\s\S]*?\S)(__|\*\*)/g, sub: "<u>$2</u>" }, // { name: UND, re: /(__|\*\*)(\S[\s\S]*?\S)(__|\*\*)/g, sub: "<u>$2</u>" },
] ]

View file

@ -500,9 +500,26 @@ $onChatgpt: $onPrimary;
min-height: 1.705rem; min-height: 1.705rem;
} }
.sidebar-chat-apiswitcher {
@include full-rounding;
@include group-padding;
background-color: $surface;
}
.sidebar-chat-apiswitcher-icon {
@include full-rounding;
min-width: 2.182rem;
min-height: 2.182rem;
color: $onSurface;
}
.sidebar-chat-apiswitcher-icon-enabled {
color: $primary;
}
.sidebar-chat-viewport { .sidebar-chat-viewport {
@include menu_decel; @include menu_decel;
margin: 0.682rem 0rem; // margin: 0.682rem 0rem;
padding: 0.682rem 0rem; padding: 0.682rem 0rem;
} }
@ -721,3 +738,4 @@ $onChatgpt: $onPrimary;
.sidebar-pin-enabled:active { .sidebar-pin-enabled:active {
background-color: mix($primary, $onPrimary, 80%); background-color: mix($primary, $onPrimary, 80%);
} }

View file

@ -1769,9 +1769,24 @@ tooltip {
min-width: 1.705rem; min-width: 1.705rem;
min-height: 1.705rem; } min-height: 1.705rem; }
.sidebar-chat-apiswitcher {
border-radius: 9999px;
-gtk-outline-radius: 9999px;
padding: 0.341rem;
background-color: #1e1c20; }
.sidebar-chat-apiswitcher-icon {
border-radius: 9999px;
-gtk-outline-radius: 9999px;
min-width: 2.182rem;
min-height: 2.182rem;
color: #e7e1e6; }
.sidebar-chat-apiswitcher-icon-enabled {
color: #d6baff; }
.sidebar-chat-viewport { .sidebar-chat-viewport {
transition: 300ms cubic-bezier(0.1, 1, 0, 1); transition: 300ms cubic-bezier(0.1, 1, 0, 1);
margin: 0.682rem 0rem;
padding: 0.682rem 0rem; } padding: 0.682rem 0rem; }
.sidebar-chat-textarea { .sidebar-chat-textarea {

View file

@ -9,19 +9,16 @@ import { SystemMessage, ChatMessage } from "./chatgpt_chatmessage.js";
import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../../lib/configwidgets.js'; import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../../lib/configwidgets.js';
import { markdownTest } from '../../../lib/md2pango.js'; import { markdownTest } from '../../../lib/md2pango.js';
const chatGPTTabIcon = Icon({ export const chatGPTTabIcon = Box({
hpack: 'center', hpack: 'center',
className: 'sidebar-chat-welcome-logo', className: 'sidebar-chat-apiswitcher-icon',
icon: `${App.configDir}/assets/openai-logomark.svg`, homogeneous: true,
setup: (self) => Utils.timeout(1, () => { children: [
const styleContext = self.get_style_context(); MaterialIcon('forum', 'norm'),
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL); ],
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
self.size = Math.max(width, height, 1) * 116 / 180; // Why such a specific proportion? See https://openai.com/brand#logos
})
}); });
export const chatGPTInfo = Box({ const chatGPTInfo = Box({
vertical: true, vertical: true,
className: 'spacing-v-15', className: 'spacing-v-15',
children: [ children: [
@ -195,11 +192,12 @@ export const chatGPTView = Scrollable({
] ]
}), }),
setup: (scrolledWindow) => { setup: (scrolledWindow) => {
// Show scrollbar
scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
const vScrollbar = scrolledWindow.get_vscrollbar(); const vScrollbar = scrolledWindow.get_vscrollbar();
vScrollbar.get_style_context().add_class('sidebar-scrollbar'); vScrollbar.get_style_context().add_class('sidebar-scrollbar');
// Avoid click-to-scroll-widget-to-view behavior
Utils.timeout(1, () => { // Fix click-to-scroll-widget-to-view behavior Utils.timeout(1, () => {
const viewport = scrolledWindow.child; const viewport = scrolledWindow.child;
viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined)); viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined));
}) })

View file

@ -0,0 +1,55 @@
const { Gdk, GLib, Gtk, Pango } = imports.gi;
import { App, Utils, Widget } from '../../../imports.js';
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
const { execAsync, exec } = Utils;
import { MaterialIcon } from "../../../lib/materialicon.js";
import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js";
export const waifuTabIcon = Box({
hpack: 'center',
className: 'sidebar-chat-apiswitcher-icon',
homogeneous: true,
children: [
MaterialIcon('photo_library', 'norm'),
]
});
export const waifuView = Scrollable({
className: 'sidebar-chat-viewport',
vexpand: true,
child: Box({
vertical: true,
children: [
]
}),
setup: (scrolledWindow) => {
// Show scrollbar
scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
const vScrollbar = scrolledWindow.get_vscrollbar();
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
// Avoid click-to-scroll-widget-to-view behavior
Utils.timeout(1, () => {
const viewport = scrolledWindow.child;
viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined));
})
}
});
export const waifuCommands = Box({
className: 'spacing-h-5',
children: [
Box({ hexpand: true }),
Button({
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
onClicked: () => {
// command do something
},
setup: setupCursorHover,
label: '/A command button',
}),
]
});
export const waifuCallAPI = (text) => {
// Do something on send
}

View file

@ -1,10 +1,12 @@
const { Gtk, Gdk } = imports.gi;
import { App, Utils, Widget } from '../../imports.js'; import { App, Utils, Widget } from '../../imports.js';
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget; const { Box, Button, Entry, EventBox, Icon, Label, 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";
// APIs // APIs
import ChatGPT from '../../services/chatgpt.js'; import ChatGPT from '../../services/chatgpt.js';
import { chatGPTView, chatGPTCommands, chatGPTSendMessage } from './apis/chatgpt.js'; import { chatGPTView, chatGPTCommands, chatGPTSendMessage, chatGPTTabIcon } from './apis/chatgpt.js';
import { waifuView, waifuCommands, waifuCallAPI, waifuTabIcon } from './apis/waifu.js';
const APIS = [ const APIS = [
{ {
@ -12,20 +14,18 @@ const APIS = [
sendCommand: chatGPTSendMessage, sendCommand: chatGPTSendMessage,
contentWidget: chatGPTView, contentWidget: chatGPTView,
commandBar: chatGPTCommands, commandBar: chatGPTCommands,
tabIcon: Box({}), tabIcon: chatGPTTabIcon,
} },
{
name: 'Waifus',
sendCommand: waifuCallAPI,
contentWidget: waifuView,
commandBar: waifuCommands,
tabIcon: waifuTabIcon,
},
]; ];
let currentApiId = 0; let currentApiId = 0;
APIS[currentApiId].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enabled', true);
const apiSwitcher = Box({
vertical: true,
children: [
Box({
homogeneous: true,
children: APIS.map(api => api.tabIcon),
}),
]
})
export const chatEntry = Entry({ export const chatEntry = Entry({
className: 'sidebar-chat-entry', className: 'sidebar-chat-entry',
@ -75,7 +75,36 @@ const apiCommandStack = Stack({
items: APIS.map(api => [api.name, api.commandBar]), items: APIS.map(api => [api.name, api.commandBar]),
}) })
function switchToTab(id) {
APIS[currentApiId].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enabled', false);
APIS[id].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enabled', true);
apiContentStack.shown = APIS[id].name;
apiCommandStack.shown = APIS[id].name;
currentApiId = id;
}
const apiSwitcher = Box({
homogeneous: true,
children: [
Box({
className: 'sidebar-chat-apiswitcher spacing-h-5',
hpack: 'center',
children: APIS.map((api, id) => Button({
child: api.tabIcon,
tooltipText: api.name,
setup: setupCursorHover,
onClicked: () => {
switchToTab(id);
}
})),
}),
]
})
export default Widget.Box({ export default Widget.Box({
properties: [
['nextTab', () => switchToTab(Math.min(currentApiId + 1, APIS.length - 1))],
['prevTab', () => switchToTab(Math.max(0, currentApiId-1))],
],
vertical: true, vertical: true,
className: 'spacing-v-10', className: 'spacing-v-10',
homogeneous: false, homogeneous: false,
@ -84,5 +113,5 @@ export default Widget.Box({
apiContentStack, apiContentStack,
apiCommandStack, apiCommandStack,
textboxArea, textboxArea,
] ],
}); });

View file

@ -7,105 +7,124 @@ import { setupCursorHover } from "../../lib/cursorhover.js";
import { NavigationIndicator } from "../../lib/navigationindicator.js"; import { NavigationIndicator } from "../../lib/navigationindicator.js";
import toolBox from './toolbox.js'; import toolBox from './toolbox.js';
import apiWidgets from './apiwidgets.js'; import apiWidgets from './apiwidgets.js';
import { chatEntry } from './apiwidgets.js'; import apiwidgets, { chatEntry } from './apiwidgets.js';
const SidebarTabButton = (stack, stackItem, navIndicator, navIndex, icon, label) => Widget.Button({ const contents = [
{
name: 'apis',
content: apiWidgets,
materialIcon: 'api',
friendlyName: 'APIs',
},
{
name: 'tools',
content: toolBox,
materialIcon: 'home_repair_service',
friendlyName: 'Tools',
},
]
let currentTabId = 0;
const contentStack = Stack({
vexpand: true,
transition: 'slide_left_right',
items: contents.map(item => [item.name, item.content]),
})
function switchToTab(id) {
const allTabs = navTabs.get_children();
const tabButton = allTabs[id];
allTabs[currentTabId].toggleClassName('sidebar-selector-tab-active', false);
allTabs[id].toggleClassName('sidebar-selector-tab-active', true);
contentStack.shown = contents[id].name;
if (tabButton) {
// Fancy highlighter line width
const buttonWidth = tabButton.get_allocated_width();
const highlightWidth = tabButton.get_children()[0].get_allocated_width();
navIndicator.css = `
font-size: ${id}px;
padding: 0px ${(buttonWidth - highlightWidth) / 2}px;
`;
}
currentTabId = id;
}
const SidebarTabButton = (navIndex) => Widget.Button({
// hexpand: true, // hexpand: true,
className: 'sidebar-selector-tab', className: 'sidebar-selector-tab',
onClicked: (self) => { onClicked: (self) => {
stack.shown = stackItem; switchToTab(navIndex);
// Add active class to self and remove for others
const allTabs = self.get_parent().get_children();
for (let i = 0; i < allTabs.length; i++) {
if (allTabs[i] != self) allTabs[i].toggleClassName('sidebar-selector-tab-active', false);
else self.toggleClassName('sidebar-selector-tab-active', true);
}
// Fancy highlighter line width
const buttonWidth = self.get_allocated_width();
const highlightWidth = self.get_children()[0].get_allocated_width();
navIndicator.css = `
font-size: ${navIndex}px;
padding: 0px ${(buttonWidth - highlightWidth) / 2}px;
`;
}, },
child: Box({ child: Box({
hpack: 'center', hpack: 'center',
className: 'spacing-h-5', className: 'spacing-h-5',
children: [ children: [
MaterialIcon(icon, 'larger'), MaterialIcon(contents[navIndex].materialIcon, 'larger'),
Label({ Label({
className: 'txt txt-smallie', className: 'txt txt-smallie',
label: label, label: `${contents[navIndex].friendlyName}`,
}) })
] ]
}), }),
setup: (button) => Utils.timeout(1, () => { setup: (button) => Utils.timeout(1, () => {
setupCursorHover(button); setupCursorHover(button);
button.toggleClassName('sidebar-selector-tab-active', defaultTab === stackItem); button.toggleClassName('sidebar-selector-tab-active', currentTabId == navIndex);
}), }),
}); });
const defaultTab = 'apis'; const navTabs = Box({
const contentStack = Stack({ homogeneous: true,
vexpand: true, children: contents.map((item, id) =>
transition: 'slide_left_right', SidebarTabButton(id, item.materialIcon, item.friendlyName)
items: [ ),
['apis', apiWidgets], });
['tools', toolBox],
],
})
const navIndicator = NavigationIndicator(2, false, { // The line thing const navIndicator = NavigationIndicator(2, false, { // The line thing
className: 'sidebar-selector-highlight', className: 'sidebar-selector-highlight',
css: 'font-size: 0px; padding: 0rem 4.160rem;', // Shushhhh css: 'font-size: 0px; padding: 0rem 4.160rem;', // Shushhhh
}) });
const navBar = Box({ const navBar = Box({
vertical: true, vertical: true,
hexpand: true, hexpand: true,
children: [ children: [
Box({ navTabs,
homogeneous: true,
children: [
SidebarTabButton(contentStack, 'apis', navIndicator, 0, 'api', 'APIs'),
SidebarTabButton(contentStack, 'tools', navIndicator, 1, 'home_repair_service', 'Tools'),
]
}),
navIndicator, navIndicator,
] ]
}) });
const pinButton = Button({ const pinButton = Button({
properties: [ properties: [
['enabled', false], ['enabled', false],
['toggle', (self) => {
self._enabled = !self._enabled;
self.toggleClassName('sidebar-pin-enabled', self._enabled);
const sideleftWindow = App.getWindow('sideleft');
const barWindow = App.getWindow('bar');
const cornerTopLeftWindow = App.getWindow('cornertl');
const sideleftContent = sideleftWindow.get_children()[0].get_children()[0].get_children()[1];
sideleftContent.toggleClassName('sidebar-pinned', self._enabled);
if (self._enabled) {
sideleftWindow.layer = 'bottom';
barWindow.layer = 'bottom';
cornerTopLeftWindow.layer = 'bottom';
sideleftWindow.exclusivity = 'exclusive';
}
else {
sideleftWindow.layer = 'top';
barWindow.layer = 'top';
cornerTopLeftWindow.layer = 'top';
sideleftWindow.exclusivity = 'normal';
}
}],
], ],
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',
onClicked: (self) => { onClicked: (self) => self._toggle(self),
self._enabled = !self._enabled;
self.toggleClassName('sidebar-pin-enabled', self._enabled);
const sideleftWindow = App.getWindow('sideleft');
const barWindow = App.getWindow('bar');
const cornerTopLeftWindow = App.getWindow('cornertl');
const sideleftContent = sideleftWindow.get_children()[0].get_children()[0].get_children()[1];
sideleftWindow.exclusivity = (self._enabled ? 'exclusive' : 'normal');
sideleftContent.toggleClassName('sidebar-pinned', self._enabled);
if(self._enabled) {
sideleftWindow.layer = 'bottom';
barWindow.layer = 'bottom';
cornerTopLeftWindow.layer = 'bottom';
}
else {
sideleftWindow.layer = 'top';
barWindow.layer = 'top';
cornerTopLeftWindow.layer = 'top';
}
},
// QoL: Focus Pin button on open. Hit keybind -> space/enter = toggle pin state // QoL: Focus Pin button on open. Hit keybind -> space/enter = toggle pin state
connections: [[App, (self, currentName, visible) => { connections: [[App, (self, currentName, visible) => {
if (currentName === 'sideleft' && visible) { if (currentName === 'sideleft' && visible) {
@ -128,7 +147,7 @@ export default () => Box({
Box({ Box({
vertical: true, vertical: true,
vexpand: true, vexpand: true,
className: 'sidebar-left', className: 'sidebar-left spacing-v-10',
children: [ children: [
Box({ Box({
className: 'spacing-h-10', className: 'spacing-h-10',
@ -147,15 +166,38 @@ export default () => Box({
}), }),
], ],
connections: [ connections: [
['key-press-event', (widget, event) => { // Typing ['key-press-event', (widget, event) => { // Handle keybinds
if (event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 && if (event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) {
widget != chatEntry && event.get_keyval()[1] != Gdk.KEY_space) { // Pin sidebar
if (contentStack.shown == 'apis') { if (event.get_keyval()[1] == Gdk.KEY_p)
pinButton._toggle(pinButton);
// Switch sidebar tab
else if (event.get_keyval()[1] === Gdk.KEY_Page_Up)
switchToTab(Math.max(currentTabId - 1), 0);
else if (event.get_keyval()[1] === Gdk.KEY_Page_Down)
switchToTab(Math.min(currentTabId + 1), contents.length);
}
if (contentStack.shown == 'apis') { // If api tab is focused
// Automatically focus entry when typing
if (event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 &&
widget != chatEntry && event.get_keyval()[1] != Gdk.KEY_space) {
chatEntry.grab_focus(); chatEntry.grab_focus();
chatEntry.set_text(chatEntry.text + String.fromCharCode(event.get_keyval()[1])); chatEntry.set_text(chatEntry.text + String.fromCharCode(event.get_keyval()[1]));
chatEntry.set_position(-1); chatEntry.set_position(-1);
} }
// Switch API type
else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
event.get_keyval()[1] === Gdk.KEY_Page_Down) {
const toSwitchTab = contentStack.get_visible_child();
toSwitchTab._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._prevTab();
}
} }
}], }],
], ],
}); });