sidebar: ai: loading skeleton

This commit is contained in:
end-4
2024-04-14 21:00:52 +07:00
parent 728d0c6361
commit 92a7c9ed26
6 changed files with 90 additions and 40 deletions
@@ -10,7 +10,7 @@ let configOptions = {
'enhancements': true,
'useHistory': true,
'writingCursor': " ...", // Warning: Using weird characters can mess up Markdown rendering
'proxyUrl': '', // Can be "socks5://127.0.0.1:9050" or "http://127.0.0.1:8080" for example. Leave it blank if you don't need it.
'proxyUrl': null, // Can be "socks5://127.0.0.1:9050" or "http://127.0.0.1:8080" for example. Leave it blank if you don't need it.
},
'animations': {
'choreographyDelay': 35,
@@ -3,7 +3,7 @@ import GtkSource from "gi://GtkSource?version=3.0";
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, Label, Icon, Scrollable } = Widget;
const { Box, Button, Label, Icon, Scrollable, Stack } = Widget;
const { execAsync, exec } = Utils;
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
import md2pango from '../../.miscutils/md2pango.js';
@@ -77,7 +77,7 @@ const TextBlock = (content = '') => Label({
Utils.execAsync(['bash', '-c', `rm ${LATEX_DIR}/*`])
.then(() => Utils.execAsync(['bash', '-c', `mkdir -p ${LATEX_DIR}`]))
.catch(() => { });
.catch(() => { });
const Latex = (content = '') => {
const latexViewArea = Box({
// vscroll: 'never',
@@ -286,7 +286,25 @@ const MessageContent = (content) => {
}
export const ChatMessage = (message, modelName = 'Model') => {
const TextSkeleton = (extraClassName = '') => Box({
className: `sidebar-chat-message-skeletonline ${extraClassName}`,
})
const messageContentBox = MessageContent(message.content);
const messageLoadingSkeleton = Box({
vertical: true,
className: 'spacing-v-5',
children: Array.from({ length: 3 }, (_, id) => TextSkeleton(`sidebar-chat-message-skeletonline-offset${id}`)),
})
const messageArea = Stack({
homogeneous: message.role !== 'user',
transition: 'crossfade',
transitionDuration: userOptions.animations.durationLarge,
children: {
'thinking': messageLoadingSkeleton,
'message': messageContentBox,
},
shown: message.thinking ? 'thinking' : 'message',
});
const thisMessage = Box({
className: 'sidebar-chat-message',
homogeneous: true,
@@ -302,11 +320,15 @@ export const ChatMessage = (message, modelName = 'Model') => {
useMarkup: true,
label: (message.role == 'user' ? USERNAME : modelName),
}),
messageContentBox,
Box({
homogeneous: true,
className: 'sidebar-chat-messagearea',
children: [messageArea]
})
],
setup: (self) => self
.hook(message, (self, isThinking) => {
messageContentBox.toggleClassName('thinking', message.thinking);
messageArea.shown = message.thinking ? 'thinking' : 'message';
}, 'notify::thinking')
.hook(message, (self) => { // Message update
messageContentBox.attribute.fullUpdate(messageContentBox, message.content, message.role != 'user');
+3 -22
View File
@@ -98,28 +98,6 @@ $elevation_margin: 0.476rem;
margin: $elevation_margin;
}
@keyframes flyin-top {
from {
margin-top: -2.795rem;
}
to {
margin-top: 0rem;
}
}
@keyframes flyin-bottom {
from {
margin-top: 4.841rem;
margin-bottom: -4.841rem;
}
to {
margin-bottom: 0rem;
margin-top: 0rem;
}
}
@mixin menu_decel {
transition: 300ms cubic-bezier(0.1, 1, 0, 1);
}
@@ -163,12 +141,15 @@ $elevation_margin: 0.476rem;
@mixin element_decel {
transition: 300ms cubic-bezier(0, 0.55, 0.45, 1);
}
@mixin element_bounceOut {
transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
@mixin element_accel {
transition: 300ms cubic-bezier(0.55, 0, 1, 0.45);
}
@mixin element_easeInOut {
transition: 300ms cubic-bezier(0.85, 0, 0.15, 1);
}
+47 -1
View File
@@ -539,13 +539,59 @@ $colorpicker_rounding: 0.341rem;
background-color: mix($primary, $hovercolor, 40%);
}
.sidebar-chat-messagearea {
margin: 0.341rem;
}
.sidebar-chat-message {
margin: 0.682rem;
@include normal-rounding;
@include group-padding;
background-color: $layer1;
}
$skeleton-accent: mix($secondary, $onSecondary, 50%);
@keyframes sidebar-chat-message-skeletonline-anim {
0% {
background-position: 175% 0%;
}
100% {
background-position: 50% 0%;
}
}
.sidebar-chat-message-skeletonline {
border-radius: $rounding_verysmall;
min-height: 1.364rem;
background-color: $layer2;
}
.sidebar-chat-message-skeletonline-offset0 {
background-repeat: no-repeat;
background: linear-gradient(to right, $layer3 0%, $skeleton-accent 25%, $layer3 50%, $layer3 100%);
background-size: 500% 500%;
animation: sidebar-chat-message-skeletonline-anim 3s linear;
animation-iteration-count: infinite;
}
.sidebar-chat-message-skeletonline-offset1 {
background-repeat: no-repeat;
background: linear-gradient(to right, $layer3 0%, $layer3 50%, $skeleton-accent 75%, $layer3 100%);
background-size: 500% 500%;
animation: sidebar-chat-message-skeletonline-anim 3s linear;
animation-iteration-count: infinite;
}
.sidebar-chat-message-skeletonline-offset2 {
margin-right: 5.795rem;
background-repeat: no-repeat;
background: linear-gradient(to right, $layer3 0%, $layer3 25%, $skeleton-accent 50%, $layer3 75%, $layer3 100%);
background-size: 500% 500%;
animation: sidebar-chat-message-skeletonline-anim 3s linear;
animation-iteration-count: infinite;
}
.sidebar-chat-indicator {
@include element_decel;
@include full-rounding;
+7 -7
View File
@@ -62,11 +62,11 @@ class GeminiMessage extends Service {
_role = '';
_parts = [{ text: '' }];
_thinking = false;
_thinking;
_done = false;
_rawData = '';
constructor(role, content, thinking = false, done = false) {
constructor(role, content, thinking = true, done = false) {
super();
this._role = role;
this._parts = [{ text: content }];
@@ -97,8 +97,8 @@ class GeminiMessage extends Service {
get label() { return this._parserState.parsed + this._parserState.stack.join('') }
get thinking() { return this._thinking }
set thinking(thinking) {
this._thinking = thinking;
set thinking(value) {
this._thinking = value;
this.notify('thinking')
this.emit('changed')
}
@@ -116,7 +116,7 @@ class GeminiMessage extends Service {
parseSection() {
if (this._thinking) {
this._thinking = false;
this.thinking = false;
this._parts[0].text = '';
}
const parsedData = JSON.parse(this._rawData);
@@ -273,12 +273,12 @@ class GeminiService extends Service {
}
addMessage(role, message) {
this._messages.push(new GeminiMessage(role, message));
this._messages.push(new GeminiMessage(role, message, false));
this.emit('newMsg', this._messages.length - 1);
}
send(msg) {
this._messages.push(new GeminiMessage('user', msg));
this._messages.push(new GeminiMessage('user', msg, false));
this.emit('newMsg', this._messages.length - 1);
const aiResponse = new GeminiMessage('model', 'thinking...', true, false)
+6 -5
View File
@@ -76,10 +76,10 @@ class GPTMessage extends Service {
_role = '';
_content = '';
_thinking = false;
_thinking;
_done = false;
constructor(role, content, thinking = false, done = false) {
constructor(role, content, thinking = true, done = false) {
super();
this._role = role;
this._content = content;
@@ -103,8 +103,8 @@ class GPTMessage extends Service {
get label() { return this._parserState.parsed + this._parserState.stack.join('') }
get thinking() { return this._thinking }
set thinking(thinking) {
this._thinking = thinking;
set thinking(value) {
this._thinking = value;
this.notify('thinking')
this.emit('changed')
}
@@ -202,6 +202,7 @@ class GPTService extends Service {
}
readResponse(stream, aiResponse) {
aiResponse.thinking = false;
stream.read_line_async(
0, null,
(stream, res) => {
@@ -234,7 +235,7 @@ class GPTService extends Service {
}
send(msg) {
this._messages.push(new GPTMessage('user', msg));
this._messages.push(new GPTMessage('user', msg, false, true));
this.emit('newMsg', this._messages.length - 1);
const aiResponse = new GPTMessage('assistant', 'thinking...', true, false)