ags: sync

This commit is contained in:
end-4
2023-12-31 01:12:29 +07:00
parent 7bede724a5
commit 5e43761875
62 changed files with 2688 additions and 1468 deletions
+101 -57
View File
@@ -6,22 +6,20 @@ 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 { ConfigToggle } from '../../../lib/configwidgets.js';
import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../../lib/configwidgets.js';
import { markdownTest } from '../../../lib/md2pango.js';
import { MarginRevealer } from '../../../lib/advancedrevealers.js';
const chatGPTTabIcon = Icon({
export const chatGPTTabIcon = Box({
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
})
className: 'sidebar-chat-apiswitcher-icon',
homogeneous: true,
children: [
MaterialIcon('forum', 'norm'),
],
});
export const chatGPTInfo = Box({
const chatGPTInfo = Box({
vertical: true,
className: 'spacing-v-15',
children: [
@@ -63,41 +61,62 @@ export const chatGPTInfo = Box({
]
})
export const chatGPTSettings = Revealer({
export const chatGPTSettings = MarginRevealer({
transition: 'slide_down',
transitionDuration: 150,
revealChild: true,
connections: [
[ChatGPT, (self) => {
self.revealChild = false;
}, 'newMsg'],
[ChatGPT, (self) => {
self.revealChild = true;
}, 'clear'],
[ChatGPT, (self) => Utils.timeout(200, () => {
self._hide(self);
}), 'newMsg'],
[ChatGPT, (self) => Utils.timeout(200, () => {
self._show(self);
}), 'clear'],
],
child: Box({
vertical: true,
hpack: 'fill',
className: 'sidebar-chat-settings',
children: [
ConfigToggle({
icon: 'cycle',
name: 'Cycle models',
desc: 'Helps avoid exceeding the API rate of 3 messages per minute.\nTurn this on if you message rapidly.',
initValue: ChatGPT.cycleModels,
onChange: (self, newValue) => {
ChatGPT.cycleModels = newValue;
},
}),
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.',
initValue: ChatGPT.assistantPrompt,
onChange: (self, newValue) => {
ChatGPT.assistantPrompt = newValue;
ConfigSegmentedSelection({
hpack: 'center',
icon: 'casino',
name: 'Randomness',
desc: 'ChatGPT\'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: 1,
onChange: (value, name) => {
ChatGPT.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: 'cycle',
name: 'Cycle models',
desc: 'Helps avoid exceeding the API rate of 3 messages per minute.\nTurn this on if you message rapidly.',
initValue: ChatGPT.cycleModels,
onChange: (self, newValue) => {
ChatGPT.cycleModels = newValue;
},
}),
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.',
initValue: ChatGPT.assistantPrompt,
onChange: (self, newValue) => {
ChatGPT.assistantPrompt = newValue;
},
}),
]
})
]
})
});
@@ -136,7 +155,7 @@ export const chatGPTWelcome = Box({
children: [
chatGPTInfo,
openaiApiKeyInstructions,
chatGPTSettings,
chatGPTSettings, ``
]
})
});
@@ -148,27 +167,37 @@ export const chatContent = Box({
[ChatGPT, (box, id) => {
const message = ChatGPT.messages[id];
if (!message) return;
box.add(ChatMessage(message))
box.add(ChatMessage(message, chatGPTView))
}, 'newMsg'],
[ChatGPT, (box) => {
box.children = [chatGPTWelcome];
}, 'clear'],
[ChatGPT, (box) => {
box.children = [chatGPTWelcome];
}, 'initialized'],
]
});
const clearChat = () => {
ChatGPT.clear();
const children = chatContent.get_children();
for (let i = 0; i < children.length; i++) {
const child = children[i];
child.destroy();
}
}
export const chatGPTView = Scrollable({
className: 'sidebar-chat-viewport',
vexpand: true,
child: chatContent,
child: Box({
vertical: true,
children: [
chatGPTWelcome,
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');
Utils.timeout(1, () => { // Fix click-to-scroll-widget-to-view behavior
// Avoid click-to-scroll-widget-to-view behavior
Utils.timeout(1, () => {
const viewport = scrolledWindow.child;
viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined));
})
@@ -183,7 +212,8 @@ export const chatGPTCommands = Box({
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
onClicked: () => chatContent.add(SystemMessage(
`Key stored in:\n\`${ChatGPT.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``,
'/key')),
'/key',
chatGPTView)),
setup: setupCursorHover,
label: '/key',
}),
@@ -191,14 +221,15 @@ export const chatGPTCommands = Box({
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
onClicked: () => chatContent.add(SystemMessage(
`Currently using \`${ChatGPT.modelName}\``,
'/model'
'/model',
chatGPTView
)),
setup: setupCursorHover,
label: '/model',
}),
Button({
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
onClicked: () => ChatGPT.clear(),
onClicked: () => clearChat(),
setup: setupCursorHover,
label: '/clear',
}),
@@ -210,26 +241,39 @@ export const chatGPTSendMessage = (text) => {
if (text.length == 0) return;
if (ChatGPT.key.length == 0) {
ChatGPT.key = text;
chatContent.add(SystemMessage(`Key saved to\n\`${ChatGPT.keyPath}\``, 'API Key'));
chatContent.add(SystemMessage(`Key saved to\n\`${ChatGPT.keyPath}\``, 'API Key', chatGPTView));
text = '';
return;
}
// Commands
if (text.startsWith('/')) {
if (text.startsWith('/clear')) ChatGPT.clear();
else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${ChatGPT.modelName}\``, '/model'))
if (text.startsWith('/clear')) clearChat();
else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${ChatGPT.modelName}\``, '/model', chatGPTView))
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', chatGPTView))
}
else {
ChatGPT.addMessage('user', prompt)
}
}
else if (text.startsWith('/key')) {
const parts = text.split(' ');
if (parts.length == 1) chatContent.add(SystemMessage(`Key stored in:\n\`${ChatGPT.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``, '/key'));
if (parts.length == 1) chatContent.add(SystemMessage(
`Key stored in:\n\`${ChatGPT.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``,
'/key',
chatGPTView));
else {
ChatGPT.key = parts[1];
chatContent.add(SystemMessage(`Updated API Key at\n\`${ChatGPT.keyPath}\``, '/key'));
chatContent.add(SystemMessage(`Updated API Key at\n\`${ChatGPT.keyPath}\``, '/key', chatGPTView));
}
}
else if (text.startsWith('/test'))
chatContent.add(SystemMessage(markdownTest, `Markdown test`));
chatContent.add(SystemMessage(markdownTest, `Markdown test`, chatGPTView));
else
chatContent.add(SystemMessage(`Invalid command.`, 'Error'))
chatContent.add(SystemMessage(`Invalid command.`, 'Error', chatGPTView))
}
else {
ChatGPT.send(text);
@@ -9,7 +9,7 @@ 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 = ' >> ';
const CHATGPT_CURSOR = ' (o) ';
const MESSAGE_SCROLL_DELAY = 13; // In milliseconds, the time before an updated message scrolls to bottom
/////////////////////// Custom source view colorscheme /////////////////////////
@@ -92,10 +92,6 @@ const CodeBlock = (content = '', lang = 'txt') => {
}),
Button({
className: 'sidebar-chat-codeblock-topbar-btn',
onClicked: (self) => {
// execAsync(['bash', '-c', `wl-copy '${content}'`, `&`]).catch(print);
execAsync([`wl-copy`, `${sourceView.label}`]).catch(print);
},
child: Box({
className: 'spacing-h-5',
children: [
@@ -104,8 +100,13 @@ const CodeBlock = (content = '', lang = 'txt') => {
label: 'Copy',
})
]
})
})
}),
onClicked: (self) => {
const copyContent = sourceView.get_buffer().get_text(0, 0, 0); // TODO: fix this
console.log(copyContent);
execAsync([`wl-copy`, `${copyContent}`]).catch(print);
},
}),
]
})
// Source view
@@ -148,7 +149,11 @@ const MessageContent = (content) => {
properties: [
['fullUpdate', (self, content, useCursor = false) => {
// Clear and add first text widget
contentBox.get_children().forEach(ch => ch.destroy());
const children = contentBox.get_children();
for (let i = 0; i < children.length; i++) {
const child = children[i];
child.destroy();
}
contentBox.add(TextBlock())
// Loop lines. Put normal text in markdown parser
// and put code into code highlighter (TODO)
@@ -214,7 +219,7 @@ const MessageContent = (content) => {
return contentBox;
}
export const ChatMessage = (message) => {
export const ChatMessage = (message, scrolledWindow) => {
const messageContentBox = MessageContent(message.content);
const thisMessage = Box({
className: 'sidebar-chat-message',
@@ -243,7 +248,7 @@ export const ChatMessage = (message) => {
[message, (self) => { // Message update
messageContentBox._fullUpdate(messageContentBox, message.content, message.role != 'user');
Utils.timeout(MESSAGE_SCROLL_DELAY, () => {
const scrolledWindow = thisMessage.get_parent().get_parent();
if (!scrolledWindow) return;
var adjustment = scrolledWindow.get_vadjustment();
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
});
@@ -258,7 +263,7 @@ export const ChatMessage = (message) => {
return thisMessage;
}
export const SystemMessage = (content, commandName) => {
export const SystemMessage = (content, commandName, scrolledWindow) => {
const messageContentBox = MessageContent(content);
const thisMessage = Box({
className: 'sidebar-chat-message',
@@ -282,7 +287,7 @@ export const SystemMessage = (content, commandName) => {
})
],
setup: (self) => Utils.timeout(MESSAGE_SCROLL_DELAY, () => {
const scrolledWindow = thisMessage.get_parent().get_parent();
if (!scrolledWindow) return;
var adjustment = scrolledWindow.get_vadjustment();
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
})
@@ -0,0 +1,72 @@
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";
import WaifuService from '../../../services/waifus.js';
export const waifuTabIcon = Box({
hpack: 'center',
className: 'sidebar-chat-apiswitcher-icon',
homogeneous: true,
children: [
MaterialIcon('photo_library', 'norm'),
]
});
const waifuContent = Box({
className: 'spacing-v-15',
vertical: true,
connections: [
[WaifuService, (box, id) => {
const message = WaifuService.responses[id];
if (!message) return;
box.add(Label({
label: message,
}))
}, 'newResponse'],
]
});
export const waifuView = Scrollable({
className: 'sidebar-chat-viewport',
vexpand: true,
child: Box({
vertical: true,
children: [
waifuContent,
]
}),
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: '/call',
}),
]
});
export const waifuCallAPI = (text) => {
// Do something on send
WaifuService.fetch(text);
}