waifu widget: added actions

This commit is contained in:
end-4
2024-01-01 16:09:14 +07:00
parent ff377a5826
commit cc408501df
7 changed files with 309 additions and 114 deletions
+78 -64
View File
@@ -21,12 +21,11 @@
}
.test {
background-image: linear-gradient(45deg,
#F4D609 0%, #F4D609 10%, #212121 10%, #212121 20%,
#F4D609 20%, #F4D609 30%, #212121 30%, #212121 40%,
#F4D609 40%, #F4D609 50%, #212121 50%, #212121 60%,
#F4D609 60%, #F4D609 70%, #212121 70%, #212121 80%,
#F4D609 80%, #F4D609 90%, #212121 90%, #212121 100%);
background-image: linear-gradient(
45deg, #f4d609 0%, #f4d609 10%, #212121 10%, #212121 20%, #f4d609 20%, #f4d609 30%, #212121 30%,
#212121 40%, #f4d609 40%, #f4d609 50%, #212121 50%, #212121 60%, #f4d609 60%,
#f4d609 70%, #212121 70%, #212121 80%, #f4d609 80%, #f4d609 90%, #212121 90%, #212121 100%
);
background-repeat: repeat;
}
@@ -186,183 +185,190 @@
min-height: 0.545rem;
}
.spacing-h-3>* {
.spacing-h-3 > * {
margin-right: 0.205rem;
}
.spacing-h-3>*:last-child {
.spacing-h-3 > *:last-child {
margin-right: 0rem;
}
.spacing-v-15>* {
.spacing-v-3 > * {
margin-bottom: 0.205rem;
}
.spacing-v-3 > *:last-child {
margin-bottom: 0rem;
}
.spacing-v-15 > * {
margin-bottom: 1.023rem;
}
.spacing-v-15>*:last-child {
.spacing-v-15 > *:last-child {
margin-bottom: 0rem;
}
.spacing-h-15>* {
.spacing-h-15 > * {
margin-right: 1.023rem;
}
.spacing-h-15>*:last-child {
.spacing-h-15 > *:last-child {
margin-right: 0rem;
}
.spacing-h-15>revealer>* {
.spacing-h-15 > revealer > * {
margin-right: 1.023rem;
}
.spacing-h-15>revealer:last-child>* {
.spacing-h-15 > revealer:last-child > * {
margin-right: 0rem;
}
.spacing-h-15>scrolledwindow>* {
.spacing-h-15 > scrolledwindow > * {
margin-right: 1.023rem;
}
.spacing-h-15>scrolledwindow:last-child>* {
.spacing-h-15 > scrolledwindow:last-child > * {
margin-right: 0rem;
}
.spacing-v-5>box {
.spacing-v-5 > box {
margin-bottom: 0.341rem;
}
.spacing-v-5>box:last-child {
.spacing-v-5 > box:last-child {
margin-bottom: 0rem;
}
.spacing-v-5>* {
.spacing-v-5 > * {
margin-bottom: 0.341rem;
}
.spacing-v-5>*:last-child {
.spacing-v-5 > *:last-child {
margin-bottom: 0rem;
}
.spacing-v-5-revealer>revealer>* {
.spacing-v-5-revealer > revealer > * {
margin-bottom: 0.341rem;
}
.spacing-v-5-revealer>revealer:last-child>* {
.spacing-v-5-revealer > revealer:last-child > * {
margin-bottom: 0rem;
}
.spacing-v-5-revealer>scrolledwindow>* {
.spacing-v-5-revealer > scrolledwindow > * {
margin-bottom: 0.341rem;
}
.spacing-v-5-revealer>scrolledwindow:last-child>* {
.spacing-v-5-revealer > scrolledwindow:last-child > * {
margin-bottom: 0rem;
}
.spacing-h-4>* {
.spacing-h-4 > * {
margin-right: 0.273rem;
}
.spacing-h-4>*:last-child {
.spacing-h-4 > *:last-child {
margin-right: 0rem;
}
.spacing-h-5>* {
.spacing-h-5 > * {
margin-right: 0.341rem;
}
.spacing-h-5>*:last-child {
.spacing-h-5 > *:last-child {
margin-right: 0rem;
}
.spacing-h-5>widget>* {
.spacing-h-5 > widget > * {
margin-right: 0.341rem;
}
.spacing-h-5>widget:last-child>* {
.spacing-h-5 > widget:last-child > * {
margin-right: 0rem;
}
.spacing-h-5>revealer>* {
.spacing-h-5 > revealer > * {
margin-right: 0.341rem;
}
.spacing-h-5>revealer:last-child>* {
.spacing-h-5 > revealer:last-child > * {
margin-right: 0rem;
}
.spacing-h-5>scrolledwindow>* {
.spacing-h-5 > scrolledwindow > * {
margin-right: 0.341rem;
}
.spacing-h-5>scrolledwindow:last-child>* {
.spacing-h-5 > scrolledwindow:last-child > * {
margin-right: 0rem;
}
.spacing-v-minus5>* {
.spacing-v-minus5 > * {
margin-bottom: -0.341rem;
}
.spacing-v-minus5>*:last-child {
.spacing-v-minus5 > *:last-child {
margin-bottom: 0rem;
}
.spacing-h-10>* {
.spacing-h-10 > * {
margin-right: 0.682rem;
}
.spacing-h-10>*:last-child {
.spacing-h-10 > *:last-child {
margin-right: 0rem;
}
.spacing-h-10>revealer>* {
.spacing-h-10 > revealer > * {
margin-right: 0.682rem;
}
.spacing-h-10>revealer:last-child>* {
.spacing-h-10 > revealer:last-child > * {
margin-right: 0rem;
}
.spacing-h-10>scrolledwindow>* {
.spacing-h-10 > scrolledwindow > * {
margin-right: 0.682rem;
}
.spacing-h-10>scrolledwindow:last-child>* {
.spacing-h-10 > scrolledwindow:last-child > * {
margin-right: 0rem;
}
.spacing-h-10>flowboxchild>* {
.spacing-h-10 > flowboxchild > * {
margin-right: 0.682rem;
}
.spacing-h-10>flowboxchild:last-child>* {
.spacing-h-10 > flowboxchild:last-child > * {
margin-right: 0rem;
}
.spacing-v-10>* {
.spacing-v-10 > * {
margin-bottom: 0.682rem;
}
.spacing-v-10>*:last-child {
.spacing-v-10 > *:last-child {
margin-bottom: 0rem;
}
.spacing-h-20>* {
.spacing-h-20 > * {
margin-right: 1.364rem;
}
.spacing-h-20>*:last-child {
.spacing-h-20 > *:last-child {
margin-right: 0rem;
}
.spacing-v-20>* {
.spacing-v-20 > * {
margin-bottom: 1.364rem;
}
.spacing-v-20>*:last-child {
.spacing-v-20 > *:last-child {
margin-bottom: 0rem;
}
.anim-enter {
@include anim-enter;
}
@@ -429,51 +435,51 @@
color: transparent;
}
.spacing-h--5>box {
.spacing-h--5 > box {
margin-right: -0.341rem;
}
.spacing-h--5>box:last-child {
.spacing-h--5 > box:last-child {
margin-right: 0rem;
}
.spacing-v--5>* {
.spacing-v--5 > * {
margin-bottom: -0.341rem;
}
.spacing-v--5>*:last-child {
.spacing-v--5 > *:last-child {
margin-bottom: 0rem;
}
.spacing-h--10>* {
.spacing-h--10 > * {
margin-left: -1.364rem;
}
.spacing-h--10>*:first-child {
.spacing-h--10 > *:first-child {
margin-left: 0rem;
}
.spacing-v--10>* {
.spacing-v--10 > * {
margin-bottom: -0.682rem;
}
.spacing-v--10>*:last-child {
.spacing-v--10 > *:last-child {
margin-bottom: 0rem;
}
.spacing-v--10>* {
.spacing-v--10 > * {
margin-bottom: -0.682rem;
}
.spacing-v--10>*:last-child {
.spacing-v--10 > *:last-child {
margin-bottom: 0rem;
}
.spacing-h--20>* {
.spacing-h--20 > * {
margin-left: -1.364rem;
}
.spacing-h--20>*:first-child {
.spacing-h--20 > *:first-child {
margin-left: 0rem;
}
@@ -491,4 +497,12 @@
.element-hide {
@include element_easeInOut;
}
}
.element-decel {
@include element_decel;
}
.element-accel {
@include element_accel;
}
+18 -3
View File
@@ -759,16 +759,13 @@ $onChatgpt: $onPrimary;
margin-left: -0.136rem;
padding-left: 0.818rem;
}
.sidebar-waifu-content {
margin-left: 0.682rem;
}
.sidebar-waifu-txt {
@include readingfont;
margin-left: 0.682rem;
}
.sidebar-waifu-image {
margin-left: 0.682rem;
@include normal-rounding;
@@ -776,3 +773,21 @@ $onChatgpt: $onPrimary;
background-repeat: no-repeat;
background-position: center;
}
.sidebar-waifu-image-actions {
padding: 0.313rem;
}
$waifu_image_overlay_transparency: 0.7;
.sidebar-waifu-image-action {
@include full-rounding;
min-width: 1.875rem;
min-height: 1.875rem;
background-color: rgba(0, 0, 0, $waifu_image_overlay_transparency); // Fixed cuz on image
color: rgba(255, 255, 255, $waifu_image_overlay_transparency);
}
.sidebar-waifu-image-action:hover,
.sidebar-waifu-image-action:focus {
background-color: rgba(30, 30, 30, $waifu_image_overlay_transparency);
}
.sidebar-waifu-image-action:active {
background-color: rgba(60, 60, 60, $waifu_image_overlay_transparency);
}
+22 -2
View File
@@ -5,6 +5,20 @@ import GLib from 'gi://GLib';
import Soup from 'gi://Soup?version=3.0';
import { fileExists } from './messages.js';
// Usage from my python waifu fetcher, for reference
// Usage: waifu-get.py [OPTION]... [TAG]...
// Options:
// --segs\tForce NSFW images
// --im\tUse waifu.im API. You can use many tags
// --pics\tUse waifu.pics API. Use 1 tag only.
// --nekos\tUse nekos.life (old) API. No tags.
// Tags:
// waifu.im (type):
// maid waifu marin-kitagawa mori-calliope raiden-shogun oppai selfies uniform
// waifu.im (nsfw tags):
// ecchi hentai ero ass paizuri oral milf
function paramStringFromObj(params) {
return Object.entries(params)
.map(([key, value]) => {
@@ -38,6 +52,7 @@ class WaifuService extends Service {
_queries = [];
_nsfw = false;
_minHeight = 600;
_status = 0;
static {
Service.register(this, {
@@ -81,17 +96,21 @@ class WaifuService extends Service {
'nsfw': this._nsfw,
};
const paramString = paramStringFromObj(params);
console.log(paramString);
// Fetch
const options = {
method: 'GET',
headers: this._headers[this._mode],
};
var status = 0;
Utils.fetch(`${this._baseUrl}?${paramString}`, options)
.then(result => result.text()) // Parse
.then(result => {
status = result.status;
return result.text();
})
.then((dataString) => { // Store interesting stuff and emit
const parsedData = JSON.parse(dataString);
if (!parsedData.images) this._responses.push({
status: status,
signature: -1,
url: '',
source: '',
@@ -104,6 +123,7 @@ class WaifuService extends Service {
else {
const imageData = parsedData.images[0];
this._responses.push({
status: status,
signature: imageData?.signature || -1,
url: imageData?.url || undefined,
source: imageData?.source,
+31 -1
View File
@@ -16,7 +16,7 @@
margin-top: 0rem; } }
.test {
background-image: linear-gradient(45deg, #F4D609 0%, #F4D609 10%, #212121 10%, #212121 20%, #F4D609 20%, #F4D609 30%, #212121 30%, #212121 40%, #F4D609 40%, #F4D609 50%, #212121 50%, #212121 60%, #F4D609 60%, #F4D609 70%, #212121 70%, #212121 80%, #F4D609 80%, #F4D609 90%, #212121 90%, #212121 100%);
background-image: linear-gradient(45deg, #f4d609 0%, #f4d609 10%, #212121 10%, #212121 20%, #f4d609 20%, #f4d609 30%, #212121 30%, #212121 40%, #f4d609 40%, #f4d609 50%, #212121 50%, #212121 60%, #f4d609 60%, #f4d609 70%, #212121 70%, #212121 80%, #f4d609 80%, #f4d609 90%, #212121 90%, #212121 100%);
background-repeat: repeat; }
.test-size {
@@ -144,6 +144,12 @@
.spacing-h-3 > *:last-child {
margin-right: 0rem; }
.spacing-v-3 > * {
margin-bottom: 0.205rem; }
.spacing-v-3 > *:last-child {
margin-bottom: 0rem; }
.spacing-v-15 > * {
margin-bottom: 1.023rem; }
@@ -369,6 +375,12 @@
.element-hide {
transition: 300ms cubic-bezier(0.85, 0, 0.15, 1); }
.element-decel {
transition: 300ms cubic-bezier(0, 0.55, 0.45, 1); }
.element-accel {
transition: 300ms cubic-bezier(0.55, 0, 1, 0.45); }
* {
caret-color: #ffc4f5; }
* selection {
@@ -2001,6 +2013,24 @@ tooltip {
background-repeat: no-repeat;
background-position: center; }
.sidebar-waifu-image-actions {
padding: 0.313rem; }
.sidebar-waifu-image-action {
border-radius: 9999px;
-gtk-outline-radius: 9999px;
min-width: 1.875rem;
min-height: 1.875rem;
background-color: rgba(0, 0, 0, 0.7);
color: rgba(255, 255, 255, 0.7); }
.sidebar-waifu-image-action:hover,
.sidebar-waifu-image-action:focus {
background-color: rgba(30, 30, 30, 0.7); }
.sidebar-waifu-image-action:active {
background-color: rgba(60, 60, 60, 0.7); }
.session-bg {
margin-top: -2.727rem;
background-color: rgba(16, 13, 16, 0.64); }
@@ -201,6 +201,11 @@ export const chatGPTView = Scrollable({
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());
})
}
});
@@ -10,7 +10,6 @@ const CUSTOM_SOURCEVIEW_SCHEME_PATH = `${App.configDir}/data/sourceviewtheme.xml
const CUSTOM_SCHEME_ID = 'custom';
const USERNAME = GLib.get_user_name();
const CHATGPT_CURSOR = ' (o) ';
const MESSAGE_SCROLL_DELAY = 13; // In milliseconds, the time before an updated message scrolls to bottom
/////////////////////// Custom source view colorscheme /////////////////////////
@@ -125,7 +124,11 @@ const CodeBlock = (content = '', lang = 'txt') => {
Box({
className: 'sidebar-chat-codeblock-code',
homogeneous: true,
children: [sourceView,],
children: [Scrollable({
vscroll: 'never',
hscroll: 'automatic',
child: sourceView,
})],
})
]
})
@@ -246,11 +249,6 @@ export const ChatMessage = (message, scrolledWindow) => {
}, 'notify::thinking'],
[message, (self) => { // Message update
messageContentBox._fullUpdate(messageContentBox, message.content, message.role != 'user');
Utils.timeout(MESSAGE_SCROLL_DELAY, () => {
if (!scrolledWindow) return;
var adjustment = scrolledWindow.get_vadjustment();
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
});
}, 'notify::content'],
[message, (label, isDone) => { // Remove the cursor
messageContentBox._fullUpdate(messageContentBox, message.content, false);
@@ -285,11 +283,6 @@ export const SystemMessage = (content, commandName, scrolledWindow) => {
],
})
],
setup: (self) => Utils.timeout(MESSAGE_SCROLL_DELAY, () => {
if (!scrolledWindow) return;
var adjustment = scrolledWindow.get_vadjustment();
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
})
});
return thisMessage;
}
+150 -32
View File
@@ -1,13 +1,12 @@
const { Gdk, GLib, Gtk, Pango } = imports.gi;
const { Gdk, Gio, 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 { MarginRevealer } from '../../../lib/advancedrevealers.js';
import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js";
import WaifuService from '../../../services/waifus.js';
const MESSAGE_SCROLL_DELAY = 13; // In milliseconds, the time before an updated message scrolls to bottom
// Create cache folder and clear pics from previous session
Utils.exec(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/media/waifus'`);
Utils.exec(`bash -c 'rm ${GLib.get_user_cache_dir()}/ags/media/waifus/*'`);
@@ -29,62 +28,146 @@ export const waifuTabIcon = Box({
});
const WaifuImage = (taglist) => {
var imagePath = '';
var blockImageData = {};
const ImageState = (icon, name) => Box({
className: 'spacing-h-5',
children: [
Box({ hexpand: true }),
Label({
className: 'sidebar-waifu-txt txt-smallie txt',
xalign: 0,
label: name,
}),
MaterialIcon(icon, 'norm'),
]
})
const ImageAction = ({ name, icon, action }) => Button({
className: 'sidebar-waifu-image-action txt-norm icon-material',
tooltipText: name,
label: icon,
onClicked: action,
setup: setupCursorHover,
})
const colorIndicator = Box({
className: `sidebar-chat-indicator`,
});
const downloadIndicator = Label({
className: 'sidebar-waifu-txt txt-smallie txt',
xalign: 0,
label: 'Downloading image...',
const downloadState = Stack({
homogeneous: false,
transition: 'slide_up_down',
transitionDuration: 150,
items: [
['api', ImageState('api', 'Calling API')],
['download', ImageState('downloading', 'Downloading image')],
['done', ImageState('done', 'Finished!')],
['error', ImageState('error', 'Error')],
]
});
const downloadIndicator = MarginRevealer({
vpack: 'center',
transition: 'slide_left',
revealChild: true,
child: downloadState,
});
const blockHeading = Box({
className: 'sidebar-waifu-content',
vertical: true,
hpack: 'fill',
className: 'sidebar-waifu-content spacing-h-5',
children: [
Box({
children: taglist.map((tag) => CommandButton(tag))
}),
...taglist.map((tag) => CommandButton(tag)),
Box({ hexpand: true }),
downloadIndicator,
]
});
const blockImage = Box({
hpack: 'start',
className: 'sidebar-waifu-image',
const blockImageActions = Box({
className: 'sidebar-waifu-image-actions spacing-h-3',
children: [
Box({ hexpand: true }),
ImageAction({
name: 'Go to source',
icon: 'link',
action: () => execAsync(['xdg-open', `${blockImageData.source}`]).catch(print),
}),
ImageAction({
name: 'Hoard',
icon: 'save',
action: () => execAsync(['bash', '-c', `mkdir -p ~/Pictures/waifus && cp ${imagePath} ~/Pictures/waifus`]).catch(print),
}),
ImageAction({
name: 'Open externally',
icon: 'open_in_new',
action: () => execAsync(['xdg-open', `${imagePath}`]).catch(print),
}),
]
})
const blockImage = Box({
className: 'test',
hpack: 'start',
vertical: true,
className: 'sidebar-waifu-image',
homogeneous: true,
children: [
Revealer({
transition: 'crossfade',
revealChild: false,
child: Box({
vertical: true,
children: [blockImageActions],
})
})
]
})
const blockImageRevealer = Revealer({
transition: 'slide_down',
transitionDuration: 150,
revealChild: false,
child: blockImage,
});
const thisBlock = Box({
className: 'sidebar-chat-message',
properties: [
['update', (imageData) => {
const { signature, url, source, dominant_color, is_nsfw, width, height, tags } = imageData;
const imagePath = `${GLib.get_user_cache_dir()}/ags/media/waifus/${signature}`;
blockImageData = imageData;
const { status, signature, url, source, dominant_color, is_nsfw, width, height, tags } = blockImageData;
if (status != 200) {
downloadState.shown = 'error';
return;
}
imagePath = `${GLib.get_user_cache_dir()}/ags/media/waifus/${signature}`;
downloadState.shown = 'download';
// Width allocation
const widgetWidth = Math.min(Math.floor(waifuContent.get_allocated_width() * 0.75), width);
blockImage.set_size_request(widgetWidth, Math.ceil(widgetWidth * height / width));
// Start download
Utils.execAsync(['bash', '-c', `wget -O '${imagePath}' '${url}'`])
.then(() => {
blockImage.css = `background-image:url('${imagePath}');`;
downloadIndicator.destroy();
})
const showImage = () => {
downloadState.shown = 'done';
blockImage.css = `background-image:url('${imagePath}');`;
blockImage.get_children()[0].revealChild = true;
Utils.timeout(blockImageRevealer.transitionDuration,
() => blockImageRevealer.revealChild = true
);
downloadIndicator._hide(downloadIndicator);
}
if (Gio.File.new_for_path(imagePath).query_exists(null)) showImage();
else Utils.execAsync(['bash', '-c', `wget -O '${imagePath}' '${url}'`])
.then(showImage)
.catch(print);
colorIndicator.css = `background-color: ${dominant_color};`;
// Width allocation
const widgetWidth = Math.floor(waifuContent.get_allocated_width() * 0.75); // idk tbh
blockImage.set_size_request(widgetWidth, Math.ceil(widgetWidth * height / width));
}],
],
children: [
colorIndicator,
Box({
vertical: true,
className: 'spacing-v-10',
className: 'spacing-v-5',
children: [
blockHeading,
blockImage,
Box({
vertical: true,
children: [blockImageRevealer],
})
]
})
],
setup: (self) => Utils.timeout(MESSAGE_SCROLL_DELAY, () => {
var adjustment = waifuView.get_vadjustment();
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
})
});
return thisBlock;
}
@@ -99,7 +182,6 @@ const waifuContent = Box({
connections: [
[WaifuService, (box, id) => {
if (id === undefined) return;
console.log('new', WaifuService.queries[id]);
const newImageBlock = WaifuImage(WaifuService.queries[id]);
box.add(newImageBlock);
box.show_all();
@@ -134,9 +216,29 @@ export const waifuView = Scrollable({
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 waifuTags = Box({
// className: 'spacing-h-5',
// children: [
// Box({ hexpand: true }),
// CommandButton('waifu'),
// CommandButton('maid'),
// CommandButton('uniform'),
// CommandButton('oppai'),
// CommandButton('selfies'),
// CommandButton('marin-kitagawa'),
// CommandButton('raiden-shogun'),
// CommandButton('mori-calliope'),
// ]
// });
export const waifuCommands = Box({
className: 'spacing-h-5',
children: [
@@ -156,6 +258,22 @@ export const sendMessage = (text) => {
child.destroy();
}
}
else if (text.startsWith('/test')) {
const newImage = WaifuImage(['/test']);
waifuContent.add(newImage);
Utils.timeout(13, () => newImage._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',
signature: 0,
source: 'https://picsum.photos/400/600',
dominant_color: '#9392A6',
is_nsfw: false,
width: 300,
height: 200,
tags: ['/test'],
}));
}
}
else WaifuService.fetch(text);
}