forked from Shinonome/dots-hyprland
ags: sync
This commit is contained in:
+3
-2
@@ -223,7 +223,7 @@ const MessageContent = (content) => {
|
||||
return contentBox;
|
||||
}
|
||||
|
||||
export const ChatMessage = (message, scrolledWindow) => {
|
||||
export const ChatMessage = (message, modelName = 'Model') => {
|
||||
const messageContentBox = MessageContent(message.content);
|
||||
const thisMessage = Box({
|
||||
className: 'sidebar-chat-message',
|
||||
@@ -241,7 +241,8 @@ export const ChatMessage = (message, scrolledWindow) => {
|
||||
xalign: 0,
|
||||
className: 'txt txt-bold sidebar-chat-name',
|
||||
wrap: true,
|
||||
label: (message.role == 'user' ? USERNAME : 'ChatGPT'),
|
||||
useMarkup: true,
|
||||
label: (message.role == 'user' ? USERNAME : modelName),
|
||||
}),
|
||||
messageContentBox,
|
||||
],
|
||||
@@ -8,63 +8,75 @@ const { execAsync, exec } = Utils;
|
||||
import ChatGPT from '../../../services/chatgpt.js';
|
||||
import { MaterialIcon } from "../../../lib/materialicon.js";
|
||||
import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js";
|
||||
import { SystemMessage, ChatMessage } from "./chatgpt_chatmessage.js";
|
||||
import { SystemMessage, ChatMessage } from "./ai_chatmessage.js";
|
||||
import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../../lib/configwidgets.js';
|
||||
import { markdownTest } from '../../../lib/md2pango.js';
|
||||
import { MarginRevealer } from '../../../lib/advancedwidgets.js';
|
||||
|
||||
export const chatGPTTabIcon = Box({
|
||||
Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets`);
|
||||
|
||||
export const chatGPTTabIcon = Icon({
|
||||
hpack: 'center',
|
||||
className: 'sidebar-chat-apiswitcher-icon',
|
||||
homogeneous: true,
|
||||
children: [
|
||||
MaterialIcon('forum', 'norm'),
|
||||
],
|
||||
icon: `openai-symbolic`,
|
||||
setup: (self) => Utils.timeout(513, () => { // stupid condition race
|
||||
const styleContext = self.get_style_context();
|
||||
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
||||
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
// console.log(Math.round(Math.max(width, height, 1)));
|
||||
self.size = Math.max(width, height, 1) * 116 / 180;
|
||||
// ↑ Why such a specific proportion? See https://openai.com/brand#logos
|
||||
})
|
||||
});
|
||||
|
||||
const chatGPTInfo = Box({
|
||||
vertical: true,
|
||||
className: 'spacing-v-15',
|
||||
children: [
|
||||
Icon({
|
||||
hpack: 'center',
|
||||
className: 'sidebar-chat-welcome-logo',
|
||||
icon: `${App.configDir}/assets/openai-logomark.svg`,
|
||||
setup: (self) => Utils.timeout(1, () => {
|
||||
const styleContext = self.get_style_context();
|
||||
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
|
||||
})
|
||||
}),
|
||||
Label({
|
||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
||||
wrap: true,
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'ChatGPT',
|
||||
}),
|
||||
Box({
|
||||
className: 'spacing-h-5',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Label({
|
||||
className: 'txt-smallie txt-subtext',
|
||||
wrap: true,
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'Powered by OpenAI',
|
||||
}),
|
||||
Button({
|
||||
className: 'txt-subtext txt-norm icon-material',
|
||||
label: 'info',
|
||||
tooltipText: 'Uses gpt-3.5-turbo.\nNot affiliated, endorsed, or sponsored by OpenAI.',
|
||||
setup: setupCursorHoverInfo,
|
||||
}),
|
||||
]
|
||||
}),
|
||||
]
|
||||
})
|
||||
const ChatGPTInfo = () => {
|
||||
const openAiLogo = Icon({
|
||||
hpack: 'center',
|
||||
className: 'sidebar-chat-welcome-logo',
|
||||
icon: `openai-symbolic`,
|
||||
setup: (self) => Utils.timeout(513, () => { // stupid condition race
|
||||
const styleContext = self.get_style_context();
|
||||
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
||||
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
// console.log(Math.round(Math.max(width, height, 1)));
|
||||
self.size = Math.max(width, height, 1) * 116 / 180;
|
||||
// ↑ Why such a specific proportion? See https://openai.com/brand#logos
|
||||
})
|
||||
});
|
||||
return Box({
|
||||
vertical: true,
|
||||
className: 'spacing-v-15',
|
||||
children: [
|
||||
openAiLogo,
|
||||
Label({
|
||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
||||
wrap: true,
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'Assistant (ChatGPT 3.5)',
|
||||
}),
|
||||
Box({
|
||||
className: 'spacing-h-5',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Label({
|
||||
className: 'txt-smallie txt-subtext',
|
||||
wrap: true,
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'Powered by OpenAI',
|
||||
}),
|
||||
Button({
|
||||
className: 'txt-subtext txt-norm icon-material',
|
||||
label: 'info',
|
||||
tooltipText: 'Uses gpt-3.5-turbo.\nNot affiliated, endorsed, or sponsored by OpenAI.',
|
||||
setup: setupCursorHoverInfo,
|
||||
}),
|
||||
]
|
||||
}),
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
export const chatGPTSettings = MarginRevealer({
|
||||
export const ChatGPTSettings = () => MarginRevealer({
|
||||
transition: 'slide_down',
|
||||
revealChild: true,
|
||||
extraSetup: (self) => self
|
||||
@@ -89,7 +101,7 @@ export const chatGPTSettings = MarginRevealer({
|
||||
{ value: 0.50, name: 'Balanced', },
|
||||
{ value: 1.00, name: 'Creative', },
|
||||
],
|
||||
initIndex: 1,
|
||||
initIndex: 2,
|
||||
onChange: (value, name) => {
|
||||
ChatGPT.temperature = value;
|
||||
},
|
||||
@@ -111,8 +123,8 @@ export const chatGPTSettings = MarginRevealer({
|
||||
}),
|
||||
ConfigToggle({
|
||||
icon: 'description',
|
||||
name: 'Assistant prompt',
|
||||
desc: 'Tells ChatGPT\n 1. It\'s a sidebar assistant on Linux\n 2. Be short and concise\n 3. Use markdown features extensively\nLeave this off for a vanilla ChatGPT experience.',
|
||||
name: 'Enhancements',
|
||||
desc: 'Tells ChatGPT:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
|
||||
initValue: ChatGPT.assistantPrompt,
|
||||
onChange: (self, newValue) => {
|
||||
ChatGPT.assistantPrompt = newValue;
|
||||
@@ -124,7 +136,7 @@ export const chatGPTSettings = MarginRevealer({
|
||||
})
|
||||
});
|
||||
|
||||
export const openaiApiKeyInstructions = Box({
|
||||
export const OpenaiApiKeyInstructions = () => Box({
|
||||
homogeneous: true,
|
||||
children: [Revealer({
|
||||
transition: 'slide_down',
|
||||
@@ -150,7 +162,7 @@ export const openaiApiKeyInstructions = Box({
|
||||
})]
|
||||
});
|
||||
|
||||
export const chatGPTWelcome = Box({
|
||||
const chatGPTWelcome = Box({
|
||||
vexpand: true,
|
||||
homogeneous: true,
|
||||
child: Box({
|
||||
@@ -158,9 +170,9 @@ export const chatGPTWelcome = Box({
|
||||
vpack: 'center',
|
||||
vertical: true,
|
||||
children: [
|
||||
chatGPTInfo,
|
||||
openaiApiKeyInstructions,
|
||||
chatGPTSettings, ``
|
||||
ChatGPTInfo(),
|
||||
OpenaiApiKeyInstructions(),
|
||||
ChatGPTSettings(),
|
||||
]
|
||||
})
|
||||
});
|
||||
@@ -172,7 +184,7 @@ export const chatContent = Box({
|
||||
.hook(ChatGPT, (box, id) => {
|
||||
const message = ChatGPT.messages[id];
|
||||
if (!message) return;
|
||||
box.add(ChatMessage(message, chatGPTView))
|
||||
box.add(ChatMessage(message, 'ChatGPT'))
|
||||
}, 'newMsg')
|
||||
,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
const { Gtk } = imports.gi;
|
||||
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';
|
||||
|
||||
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import Gemini from '../../../services/gemini.js';
|
||||
import { MaterialIcon } from "../../../lib/materialicon.js";
|
||||
import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js";
|
||||
import { SystemMessage, ChatMessage } from "./ai_chatmessage.js";
|
||||
import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../../lib/configwidgets.js';
|
||||
import { markdownTest } from '../../../lib/md2pango.js';
|
||||
import { MarginRevealer } from '../../../lib/advancedwidgets.js';
|
||||
|
||||
Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets`);
|
||||
const MODEL_NAME = `Gemini`;
|
||||
|
||||
export const geminiTabIcon = Icon({
|
||||
hpack: 'center',
|
||||
className: 'sidebar-chat-apiswitcher-icon',
|
||||
icon: `google-gemini-symbolic`,
|
||||
setup: (self) => Utils.timeout(513, () => { // stupid condition race
|
||||
const styleContext = self.get_style_context();
|
||||
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;
|
||||
})
|
||||
})
|
||||
|
||||
const GeminiInfo = () => {
|
||||
const geminiLogo = Icon({
|
||||
hpack: 'center',
|
||||
className: 'sidebar-chat-welcome-logo',
|
||||
icon: `google-gemini-symbolic`,
|
||||
setup: (self) => Utils.timeout(513, () => { // stupid condition race
|
||||
const styleContext = self.get_style_context();
|
||||
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;
|
||||
})
|
||||
});
|
||||
return Box({
|
||||
vertical: true,
|
||||
className: 'spacing-v-15',
|
||||
children: [
|
||||
geminiLogo,
|
||||
Label({
|
||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
||||
wrap: true,
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'Assistant (Gemini Pro)',
|
||||
}),
|
||||
Box({
|
||||
className: 'spacing-h-5',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Label({
|
||||
className: 'txt-smallie txt-subtext',
|
||||
wrap: true,
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'Powered by Google',
|
||||
}),
|
||||
Button({
|
||||
className: 'txt-subtext txt-norm icon-material',
|
||||
label: 'info',
|
||||
tooltipText: 'Uses gemini-pro.\nNot affiliated, endorsed, or sponsored by Google.',
|
||||
setup: setupCursorHoverInfo,
|
||||
}),
|
||||
]
|
||||
}),
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
export const GeminiSettings = () => MarginRevealer({
|
||||
transition: 'slide_down',
|
||||
revealChild: true,
|
||||
extraSetup: (self) => self
|
||||
.hook(Gemini, (self) => Utils.timeout(200, () => {
|
||||
self.attribute.hide();
|
||||
}), 'newMsg')
|
||||
.hook(Gemini, (self) => Utils.timeout(200, () => {
|
||||
self.attribute.show();
|
||||
}), 'clear')
|
||||
,
|
||||
child: Box({
|
||||
vertical: true,
|
||||
className: 'sidebar-chat-settings',
|
||||
children: [
|
||||
ConfigSegmentedSelection({
|
||||
hpack: 'center',
|
||||
icon: 'casino',
|
||||
name: 'Randomness',
|
||||
desc: 'Gemini\'s temperature value.\n Precise = 0\n Balanced = 0.5\n Creative = 1',
|
||||
options: [
|
||||
{ value: 0.00, name: 'Precise', },
|
||||
{ value: 0.50, name: 'Balanced', },
|
||||
{ value: 1.00, name: 'Creative', },
|
||||
],
|
||||
initIndex: 2,
|
||||
onChange: (value, name) => {
|
||||
Gemini.temperature = value;
|
||||
},
|
||||
}),
|
||||
ConfigGap({ vertical: true, size: 10 }), // Note: size can only be 5, 10, or 15
|
||||
Box({
|
||||
vertical: true,
|
||||
hpack: 'fill',
|
||||
className: 'sidebar-chat-settings-toggles',
|
||||
children: [
|
||||
ConfigToggle({
|
||||
icon: 'description',
|
||||
name: 'Enhancements',
|
||||
desc: 'Tells Gemini:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
|
||||
initValue: Gemini.assistantPrompt,
|
||||
onChange: (self, newValue) => {
|
||||
Gemini.assistantPrompt = newValue;
|
||||
},
|
||||
}),
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
export const GoogleAiInstructions = () => Box({
|
||||
homogeneous: true,
|
||||
children: [Revealer({
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 150,
|
||||
setup: (self) => self
|
||||
.hook(Gemini, (self, hasKey) => {
|
||||
self.revealChild = (Gemini.key.length == 0);
|
||||
}, 'hasKey')
|
||||
,
|
||||
child: Button({
|
||||
child: Label({
|
||||
useMarkup: true,
|
||||
wrap: true,
|
||||
className: 'txt sidebar-chat-welcome-txt',
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'A Google AI API key is required\nYou can grab one <u>here</u>, then enter it below'
|
||||
}),
|
||||
setup: setupCursorHover,
|
||||
onClicked: () => {
|
||||
Utils.execAsync(['bash', '-c', `xdg-open https://makersuite.google.com/app/apikey &`]);
|
||||
}
|
||||
})
|
||||
})]
|
||||
});
|
||||
|
||||
const geminiWelcome = Box({
|
||||
vexpand: true,
|
||||
homogeneous: true,
|
||||
child: Box({
|
||||
className: 'spacing-v-15',
|
||||
vpack: 'center',
|
||||
vertical: true,
|
||||
children: [
|
||||
GeminiInfo(),
|
||||
GoogleAiInstructions(),
|
||||
GeminiSettings(),
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
export const chatContent = Box({
|
||||
className: 'spacing-v-15',
|
||||
vertical: true,
|
||||
setup: (self) => self
|
||||
.hook(Gemini, (box, id) => {
|
||||
const message = Gemini.messages[id];
|
||||
if (!message) return;
|
||||
box.add(ChatMessage(message, MODEL_NAME))
|
||||
}, 'newMsg')
|
||||
,
|
||||
});
|
||||
|
||||
const clearChat = () => {
|
||||
Gemini.clear();
|
||||
const children = chatContent.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
child.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export const geminiView = Scrollable({
|
||||
className: 'sidebar-chat-viewport',
|
||||
vexpand: true,
|
||||
child: Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
geminiWelcome,
|
||||
chatContent,
|
||||
]
|
||||
}),
|
||||
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));
|
||||
})
|
||||
// Always scroll to bottom with new content
|
||||
const adjustment = scrolledWindow.get_vadjustment();
|
||||
adjustment.connect("changed", () => {
|
||||
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const CommandButton = (command) => Button({
|
||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
||||
onClicked: () => sendMessage(command),
|
||||
setup: setupCursorHover,
|
||||
label: command,
|
||||
});
|
||||
|
||||
export const geminiCommands = Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
Box({ hexpand: true }),
|
||||
CommandButton('/key'),
|
||||
CommandButton('/model'),
|
||||
CommandButton('/clear'),
|
||||
]
|
||||
});
|
||||
|
||||
export const sendMessage = (text) => {
|
||||
// Check if text or API key is empty
|
||||
if (text.length == 0) return;
|
||||
if (Gemini.key.length == 0) {
|
||||
Gemini.key = text;
|
||||
chatContent.add(SystemMessage(`Key saved to\n\`${Gemini.keyPath}\``, 'API Key', geminiView));
|
||||
text = '';
|
||||
return;
|
||||
}
|
||||
// Commands
|
||||
if (text.startsWith('/')) {
|
||||
if (text.startsWith('/clear')) clearChat();
|
||||
else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${Gemini.modelName}\``, '/model', geminiView))
|
||||
else if (text.startsWith('/prompt')) {
|
||||
const firstSpaceIndex = text.indexOf(' ');
|
||||
const prompt = text.slice(firstSpaceIndex + 1);
|
||||
if (firstSpaceIndex == -1 || prompt.length < 1) {
|
||||
chatContent.add(SystemMessage(`Usage: \`/prompt MESSAGE\``, '/prompt', geminiView))
|
||||
}
|
||||
else {
|
||||
Gemini.addMessage('user', prompt)
|
||||
}
|
||||
}
|
||||
else if (text.startsWith('/key')) {
|
||||
const parts = text.split(' ');
|
||||
if (parts.length == 1) chatContent.add(SystemMessage(
|
||||
`Key stored in:\n\`${Gemini.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``,
|
||||
'/key',
|
||||
geminiView));
|
||||
else {
|
||||
Gemini.key = parts[1];
|
||||
chatContent.add(SystemMessage(`Updated API Key at\n\`${Gemini.keyPath}\``, '/key', geminiView));
|
||||
}
|
||||
}
|
||||
else if (text.startsWith('/test'))
|
||||
chatContent.add(SystemMessage(markdownTest, `Markdown test`, geminiView));
|
||||
else
|
||||
chatContent.add(SystemMessage(`Invalid command.`, 'Error', geminiView))
|
||||
}
|
||||
else {
|
||||
Gemini.send(text);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ const { Box, Button, Label, Overlay, Revealer, Scrollable, Stack } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import { MaterialIcon } from "../../../lib/materialicon.js";
|
||||
import { MarginRevealer } from '../../../lib/advancedwidgets.js';
|
||||
import { setupCursorHover } from "../../../lib/cursorhover.js";
|
||||
import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js";
|
||||
import WaifuService from '../../../services/waifus.js';
|
||||
|
||||
async function getImageViewerApp(preferredApp) {
|
||||
@@ -162,7 +162,8 @@ const WaifuImage = (taglist) => {
|
||||
blockImage.set_size_request(widgetWidth, widgetHeight);
|
||||
const showImage = () => {
|
||||
downloadState.shown = 'done';
|
||||
const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(thisBlock.attribute.imagePath, widgetWidth, widgetHeight, false);
|
||||
const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(thisBlock.attribute.imagePath, widgetWidth, widgetHeight);
|
||||
// const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(thisBlock.attribute.imagePath, widgetWidth, widgetHeight, false);
|
||||
|
||||
blockImage.set_size_request(widgetWidth, widgetHeight);
|
||||
blockImage.connect("draw", (widget, cr) => {
|
||||
@@ -220,10 +221,62 @@ const WaifuImage = (taglist) => {
|
||||
return thisBlock;
|
||||
}
|
||||
|
||||
const WaifuInfo = () => {
|
||||
const waifuLogo = Label({
|
||||
hpack: 'center',
|
||||
className: 'sidebar-chat-welcome-logo',
|
||||
label: 'photo_library',
|
||||
})
|
||||
return Box({
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
className: 'spacing-v-15',
|
||||
children: [
|
||||
waifuLogo,
|
||||
Label({
|
||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
||||
wrap: true,
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'Waifus',
|
||||
}),
|
||||
Box({
|
||||
className: 'spacing-h-5',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Label({
|
||||
className: 'txt-smallie txt-subtext',
|
||||
wrap: true,
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'Powered by waifu.im',
|
||||
}),
|
||||
Button({
|
||||
className: 'txt-subtext txt-norm icon-material',
|
||||
label: 'info',
|
||||
tooltipText: 'A free Waifu API. An alternative to waifu.pics.',
|
||||
setup: setupCursorHoverInfo,
|
||||
}),
|
||||
]
|
||||
}),
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
const waifuWelcome = Box({
|
||||
vexpand: true,
|
||||
homogeneous: true,
|
||||
child: Box({
|
||||
className: 'spacing-v-15',
|
||||
vpack: 'center',
|
||||
vertical: true,
|
||||
children: [
|
||||
WaifuInfo(),
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
const waifuContent = Box({
|
||||
className: 'spacing-v-15',
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
attribute: {
|
||||
'map': new Map(),
|
||||
},
|
||||
@@ -251,6 +304,7 @@ export const waifuView = Scrollable({
|
||||
child: Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
waifuWelcome,
|
||||
waifuContent,
|
||||
]
|
||||
}),
|
||||
@@ -327,6 +381,21 @@ const clearChat = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const DummyTag = (width, height, url, color = '#9392A6') => {
|
||||
return { // Needs timeout or inits won't make it
|
||||
status: 200,
|
||||
url: url,
|
||||
extension: '',
|
||||
signature: 0,
|
||||
source: url,
|
||||
dominant_color: color,
|
||||
is_nsfw: false,
|
||||
width: width,
|
||||
height: height,
|
||||
tags: ['/test'],
|
||||
};
|
||||
}
|
||||
|
||||
export const sendMessage = (text) => {
|
||||
// Do something on send
|
||||
// Commands
|
||||
@@ -335,20 +404,20 @@ export const sendMessage = (text) => {
|
||||
else if (text.startsWith('/test')) {
|
||||
const newImage = WaifuImage(['/test']);
|
||||
waifuContent.add(newImage);
|
||||
Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update({ // Needs timeout or inits won't make it
|
||||
// This is an image uploaded to my github repo
|
||||
status: 200,
|
||||
url: 'https://picsum.photos/400/600',
|
||||
extension: '',
|
||||
signature: 0,
|
||||
source: 'https://picsum.photos/400/600',
|
||||
dominant_color: '#9392A6',
|
||||
is_nsfw: false,
|
||||
width: 300,
|
||||
height: 200,
|
||||
tags: ['/test'],
|
||||
}, true));
|
||||
Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update(
|
||||
DummyTag(300, 200, 'https://picsum.photos/600/400'),
|
||||
true
|
||||
));
|
||||
}
|
||||
else if (text.startsWith('/chino')) {
|
||||
const newImage = WaifuImage(['/chino']);
|
||||
waifuContent.add(newImage);
|
||||
Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update(
|
||||
DummyTag(300, 400, 'https://chino.pages.dev/chino', '#B2AEF3'),
|
||||
true
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
else WaifuService.fetch(text);
|
||||
}
|
||||
@@ -1,22 +1,32 @@
|
||||
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';
|
||||
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
const { Box, Button, CenterBox, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import { setupCursorHover, setupCursorHoverInfo } from "../../lib/cursorhover.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 APIS = [
|
||||
{
|
||||
name: 'ChatGPT',
|
||||
name: 'Assistant (ChatGPT 3.5)',
|
||||
sendCommand: chatGPTSendMessage,
|
||||
contentWidget: chatGPTView,
|
||||
commandBar: chatGPTCommands,
|
||||
tabIcon: chatGPTTabIcon,
|
||||
placeholderText: 'Message ChatGPT',
|
||||
placeholderText: 'Message ChatGPT...',
|
||||
},
|
||||
{
|
||||
name: 'Assistant (Gemini Pro)',
|
||||
sendCommand: geminiSendMessage,
|
||||
contentWidget: geminiView,
|
||||
commandBar: geminiCommands,
|
||||
tabIcon: geminiTabIcon,
|
||||
placeholderText: 'Message Gemini...',
|
||||
},
|
||||
{
|
||||
name: 'Waifus',
|
||||
@@ -35,8 +45,12 @@ export const chatEntry = Entry({
|
||||
hexpand: true,
|
||||
setup: (self) => self
|
||||
.hook(ChatGPT, (self) => {
|
||||
if (APIS[currentApiId].name != 'ChatGPT') return;
|
||||
self.placeholderText = (ChatGPT.key.length > 0 ? 'Ask a question...' : 'Enter OpenAI API Key...');
|
||||
if (APIS[currentApiId].name != 'Assistant (ChatGPT 3.5)') return;
|
||||
self.placeholderText = (ChatGPT.key.length > 0 ? 'Message ChatGPT...' : 'Enter OpenAI API Key...');
|
||||
}, 'hasKey')
|
||||
.hook(Gemini, (self) => {
|
||||
if (APIS[currentApiId].name != 'Assistant (Gemini Pro)') return;
|
||||
self.placeholderText = (Gemini.key.length > 0 ? 'Message Gemini...' : 'Enter Google AI API Key...');
|
||||
}, 'hasKey')
|
||||
,
|
||||
onChange: (entry) => {
|
||||
@@ -86,22 +100,27 @@ function switchToTab(id) {
|
||||
chatEntry.placeholderText = APIS[id].placeholderText,
|
||||
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);
|
||||
}
|
||||
})),
|
||||
}),
|
||||
]
|
||||
|
||||
const apiSwitcher = CenterBox({
|
||||
centerWidget: 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);
|
||||
}
|
||||
})),
|
||||
}),
|
||||
endWidget: Button({
|
||||
hpack: 'end',
|
||||
className: 'txt-subtext txt-norm icon-material',
|
||||
label: 'lightbulb',
|
||||
tooltipText: 'Use PageUp/PageDown to switch between API pages',
|
||||
setup: setupCursorHoverInfo,
|
||||
}),
|
||||
})
|
||||
|
||||
export default Widget.Box({
|
||||
|
||||
@@ -5,7 +5,7 @@ export default () => PopupWindow({
|
||||
focusable: true,
|
||||
anchor: ['left', 'top', 'bottom'],
|
||||
name: 'sideleft',
|
||||
// exclusivity: 'exclusive',
|
||||
layer: 'top',
|
||||
showClassName: 'sideleft-show',
|
||||
hideClassName: 'sideleft-hide',
|
||||
child: SidebarLeft(),
|
||||
|
||||
@@ -102,22 +102,14 @@ const pinButton = Button({
|
||||
self.toggleClassName('sidebar-pin-enabled', self.attribute.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.attribute.enabled);
|
||||
|
||||
if (self.attribute.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';
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ export default Scrollable({
|
||||
child: Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
QuickScripts(),
|
||||
// QuickScripts(),
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user