mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 23:09:26 -05:00
Sync my_config with end-4
This commit is contained in:
@@ -0,0 +1 @@
|
||||
openai-symbolic.svg
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg9"
|
||||
sodipodi:docname="oxygen.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs9" />
|
||||
<sodipodi:namedview
|
||||
id="namedview9"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="7.6782609"
|
||||
inkscape:cx="-11.916761"
|
||||
inkscape:cy="11.786523"
|
||||
inkscape:window-width="1627"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg9" />
|
||||
<g
|
||||
id="g9"
|
||||
transform="translate(0.7158741,-0.307456)">
|
||||
<path
|
||||
d="m 12.821126,8.892686 c 0,2.99523 -2.42813,5.42337 -5.4233602,5.42337 -2.99523,0 -5.42334,-2.42814 -5.42334,-5.42337 0,-2.99523 2.42811,-5.42334 5.42334,-5.42334 2.9952302,0 5.4233602,2.42811 5.4233602,5.42334 z"
|
||||
fill="white"
|
||||
id="path7"
|
||||
style="fill:#000000" />
|
||||
<path
|
||||
d="m 16.593826,4.412536 c 0,1.04182 -0.8445,1.88638 -1.8863,1.88638 -1.0419,0 -1.8864,-0.84456 -1.8864,-1.88638 0,-1.041819 0.8445,-1.88638 1.8864,-1.88638 1.0418,0 1.8863,0.844561 1.8863,1.88638 z"
|
||||
fill="white"
|
||||
id="path8"
|
||||
style="fill:#000000" />
|
||||
<path
|
||||
d="m 16.593826,15.495056 c 0,1.4325 -1.1612,2.5937 -2.5937,2.5937 -1.4325,0 -2.5938,-1.1612 -2.5938,-2.5937 0,-1.4325 1.1613,-2.5938 2.5938,-2.5938 1.4325,0 2.5937,1.1613 2.5937,2.5938 z"
|
||||
fill="white"
|
||||
id="path9"
|
||||
style="fill:#000000" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 306 KiB |
@@ -17,6 +17,7 @@ import Session from './modules/session/main.js';
|
||||
import SideLeft from './modules/sideleft/main.js';
|
||||
import SideRight from './modules/sideright/main.js';
|
||||
|
||||
const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated`
|
||||
const range = (length, start = 1) => Array.from({ length }, (_, i) => i + start);
|
||||
function forMonitors(widget) {
|
||||
const n = Gdk.Display.get_default()?.get_n_monitors() || 1;
|
||||
@@ -27,9 +28,8 @@ function forMonitors(widget) {
|
||||
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
|
||||
async function applyStyle() {
|
||||
const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated`
|
||||
Utils.exec(`mkdir -p ${COMPILED_STYLE_DIR}`);
|
||||
Utils.exec(`sassc ${App.configDir}/scss/main.scss ${COMPILED_STYLE_DIR}/style.css`);
|
||||
Utils.exec(`sass ${App.configDir}/scss/main.scss ${COMPILED_STYLE_DIR}/style.css`);
|
||||
App.resetCss();
|
||||
App.applyCss(`${COMPILED_STYLE_DIR}/style.css`);
|
||||
console.log('[LOG] Styles loaded')
|
||||
@@ -56,7 +56,7 @@ const Windows = () => [
|
||||
];
|
||||
const CLOSE_ANIM_TIME = 210; // Longer than actual anim time to make sure widgets animate fully
|
||||
export default {
|
||||
css: `${App.configDir}/style.css`,
|
||||
css: `${COMPILED_STYLE_DIR}/style.css`,
|
||||
stackTraceOnError: true,
|
||||
closeWindowDelay: { // For animations
|
||||
'sideright': CLOSE_ANIM_TIME,
|
||||
|
||||
@@ -83,4 +83,7 @@ console.log('uwu');
|
||||
\`\`\`
|
||||
- Random instruction thing
|
||||
- To update arch lincox, run \`sudo pacman -Syu\`
|
||||
\`\`\`tex
|
||||
\\frac{d}{dx} \\left( \\frac{x-438}{x^2+23x-7} \\right) = \\frac{-x^2 + 869}{(x^2+23x-7)^2} hmmmmmm \\frac{d}{dx} \\left( \\frac{x-438}{x^2+23x-7} \\right) = \\frac{-x^2 + 869}{(x^2+23x-7)^2}
|
||||
\`\`\`
|
||||
`;
|
||||
@@ -1,12 +1,21 @@
|
||||
const { GLib } = imports.gi;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
|
||||
const { Box, Label, Overlay, Revealer } = Widget;
|
||||
const { Box, Button, EventBox, Label, Overlay, Revealer, Scrollable } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import { AnimatedCircProg } from "../.commonwidgets/cairo_circularprogress.js";
|
||||
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
|
||||
import { showMusicControls } from '../../variables.js';
|
||||
|
||||
const CUSTOM_MODULE_CONTENT_INTERVAL_FILE = `${GLib.get_home_dir()}/.cache/ags/user/scripts/custom-module-interval.txt`;
|
||||
const CUSTOM_MODULE_CONTENT_SCRIPT = `${GLib.get_home_dir()}/.cache/ags/user/scripts/custom-module-poll.sh`;
|
||||
const CUSTOM_MODULE_LEFTCLICK_SCRIPT = `${GLib.get_home_dir()}/.cache/ags/user/scripts/custom-module-leftclick.sh`;
|
||||
const CUSTOM_MODULE_RIGHTCLICK_SCRIPT = `${GLib.get_home_dir()}/.cache/ags/user/scripts/custom-module-rightclick.sh`;
|
||||
const CUSTOM_MODULE_MIDDLECLICK_SCRIPT = `${GLib.get_home_dir()}/.cache/ags/user/scripts/custom-module-middleclick.sh`;
|
||||
const CUSTOM_MODULE_SCROLLUP_SCRIPT = `${GLib.get_home_dir()}/.cache/ags/user/scripts/custom-module-scrollup.sh`;
|
||||
const CUSTOM_MODULE_SCROLLDOWN_SCRIPT = `${GLib.get_home_dir()}/.cache/ags/user/scripts/custom-module-scrolldown.sh`;
|
||||
|
||||
function trimTrackTitle(title) {
|
||||
if (!title) return '';
|
||||
const cleanPatterns = [
|
||||
@@ -17,10 +26,10 @@ function trimTrackTitle(title) {
|
||||
return title;
|
||||
}
|
||||
|
||||
const BarGroup = ({ child }) => Widget.Box({
|
||||
const BarGroup = ({ child }) => Box({
|
||||
className: 'bar-group-margin bar-sides',
|
||||
children: [
|
||||
Widget.Box({
|
||||
Box({
|
||||
className: 'bar-group bar-group-standalone bar-group-pad-system',
|
||||
children: [child],
|
||||
}),
|
||||
@@ -34,7 +43,7 @@ const BarResource = (name, icon, command) => {
|
||||
hpack: 'center',
|
||||
});
|
||||
const resourceProgress = Overlay({
|
||||
child: Widget.Box({
|
||||
child: Box({
|
||||
vpack: 'center',
|
||||
className: 'bar-batt',
|
||||
homogeneous: true,
|
||||
@@ -93,14 +102,14 @@ const switchToRelativeWorkspace = async (self, num) => {
|
||||
|
||||
export default () => {
|
||||
// TODO: use cairo to make button bounce smaller on click, if that's possible
|
||||
const playingState = Widget.Box({ // Wrap a box cuz overlay can't have margins itself
|
||||
const playingState = Box({ // Wrap a box cuz overlay can't have margins itself
|
||||
homogeneous: true,
|
||||
children: [Widget.Overlay({
|
||||
child: Widget.Box({
|
||||
children: [Overlay({
|
||||
child: Box({
|
||||
vpack: 'center',
|
||||
className: 'bar-music-playstate',
|
||||
homogeneous: true,
|
||||
children: [Widget.Label({
|
||||
children: [Label({
|
||||
vpack: 'center',
|
||||
className: 'bar-music-playstate-txt',
|
||||
justification: 'center',
|
||||
@@ -121,9 +130,9 @@ export default () => {
|
||||
]
|
||||
})]
|
||||
});
|
||||
const trackTitle = Widget.Scrollable({
|
||||
const trackTitle = Scrollable({
|
||||
hexpand: true,
|
||||
child: Widget.Label({
|
||||
child: Label({
|
||||
className: 'txt-smallie txt-onSurfaceVariant',
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
@@ -142,30 +151,57 @@ export default () => {
|
||||
trackTitle,
|
||||
]
|
||||
})
|
||||
const systemResources = BarGroup({
|
||||
child: Box({
|
||||
children: [
|
||||
BarResource('RAM Usage', 'memory', `LANG=C free | awk '/^Mem/ {printf("%.2f\\n", ($3/$2) * 100)}'`),
|
||||
Revealer({
|
||||
revealChild: true,
|
||||
transition: 'slide_left',
|
||||
transitionDuration: 200,
|
||||
child: Box({
|
||||
className: 'spacing-h-10 margin-left-10',
|
||||
children: [
|
||||
BarResource('Swap Usage', 'swap_horiz', `LANG=C free | awk '/^Swap/ {if ($2 > 0) printf("%.2f\\n", ($3/$2) * 100); else print "0";}'`),
|
||||
BarResource('CPU Usage', 'settings_motion_mode', `LANG=C top -bn1 | grep Cpu | sed 's/\\,/\\./g' | awk '{print $2}'`),
|
||||
]
|
||||
}),
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
self.revealChild = (!mpris);
|
||||
const SystemResourcesOrCustomModule = () => {
|
||||
// Check if ~/.cache/ags/user/scripts/custom-module-poll.sh exists
|
||||
if (GLib.file_test(CUSTOM_MODULE_CONTENT_SCRIPT, GLib.FileTest.EXISTS)) {
|
||||
const interval = Number(Utils.readFile(CUSTOM_MODULE_CONTENT_INTERVAL_FILE)) || 5000;
|
||||
return BarGroup({
|
||||
child: Button({
|
||||
child: Label({
|
||||
className: 'txt-smallie txt-onSurfaceVariant',
|
||||
useMarkup: true,
|
||||
setup: (self) => Utils.timeout(1, () => {
|
||||
self.label = exec(CUSTOM_MODULE_CONTENT_SCRIPT);
|
||||
self.poll(interval, (self) => {
|
||||
const content = exec(CUSTOM_MODULE_CONTENT_SCRIPT);
|
||||
self.label = content;
|
||||
})
|
||||
})
|
||||
}),
|
||||
onPrimaryClickRelease: () => execAsync(CUSTOM_MODULE_LEFTCLICK_SCRIPT).catch(print),
|
||||
onSecondaryClickRelease: () => execAsync(CUSTOM_MODULE_RIGHTCLICK_SCRIPT).catch(print),
|
||||
onMiddleClickRelease: () => execAsync(CUSTOM_MODULE_MIDDLECLICK_SCRIPT).catch(print),
|
||||
onScrollUp: () => execAsync(CUSTOM_MODULE_SCROLLUP_SCRIPT).catch(print),
|
||||
onScrollDown: () => execAsync(CUSTOM_MODULE_SCROLLDOWN_SCRIPT).catch(print),
|
||||
})
|
||||
],
|
||||
})
|
||||
});
|
||||
return Widget.EventBox({
|
||||
});
|
||||
} else {
|
||||
return BarGroup({
|
||||
child: Box({
|
||||
children: [
|
||||
BarResource('RAM Usage', 'memory', `LANG=C free | awk '/^Mem/ {printf("%.2f\\n", ($3/$2) * 100)}'`),
|
||||
Revealer({
|
||||
revealChild: true,
|
||||
transition: 'slide_left',
|
||||
transitionDuration: 200,
|
||||
child: Box({
|
||||
className: 'spacing-h-10 margin-left-10',
|
||||
children: [
|
||||
BarResource('Swap Usage', 'swap_horiz', `LANG=C free | awk '/^Swap/ {if ($2 > 0) printf("%.2f\\n", ($3/$2) * 100); else print "0";}'`),
|
||||
BarResource('CPU Usage', 'settings_motion_mode', `LANG=C top -bn1 | grep Cpu | sed 's/\\,/\\./g' | awk '{print $2}'`),
|
||||
]
|
||||
}),
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
self.revealChild = (!mpris);
|
||||
}),
|
||||
})
|
||||
],
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
return EventBox({
|
||||
onScrollUp: (self) => switchToRelativeWorkspace(self, -1),
|
||||
onScrollDown: (self) => switchToRelativeWorkspace(self, +1),
|
||||
onPrimaryClickRelease: () => showMusicControls.setValue(!showMusicControls.value),
|
||||
@@ -175,7 +211,7 @@ export default () => {
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
BarGroup({ child: musicStuff }),
|
||||
systemResources,
|
||||
SystemResourcesOrCustomModule(),
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
@@ -75,7 +75,7 @@ const Utilities = () => Box({
|
||||
children: [
|
||||
UtilButton({
|
||||
name: 'Screen snip', icon: 'screenshot_region', onClicked: () => {
|
||||
Utils.execAsync(['bash', '-c', `grim -g "$(slurp -d -c e2e2e2BB -b 31313122 -s 00000000)" - | wl-copy &`])
|
||||
Utils.execAsync(`${App.configDir}/scripts/grimblast.sh`)
|
||||
.catch(print)
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -9,6 +9,8 @@ const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget;
|
||||
import { AnimatedCircProg } from "../.commonwidgets/cairo_circularprogress.js";
|
||||
import { showMusicControls } from '../../variables.js';
|
||||
|
||||
const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated`
|
||||
|
||||
function expandTilde(path) {
|
||||
if (path.startsWith('~')) {
|
||||
return GLib.get_home_dir() + path.slice(1);
|
||||
@@ -186,22 +188,19 @@ const CoverArt = ({ player, ...rest }) => {
|
||||
// Player closed
|
||||
// Note that cover path still remains, so we're checking title
|
||||
if (!player || player.trackTitle == "") {
|
||||
self.css = `background-image: none;`;
|
||||
App.applyCss(`${App.configDir}/style.css`);
|
||||
App.applyCss(`${COMPILED_STYLE_DIR}/style.css`);
|
||||
return;
|
||||
}
|
||||
|
||||
const coverPath = player.coverPath;
|
||||
const stylePath = `${player.coverPath}${lightDark}${COVER_COLORSCHEME_SUFFIX}`;
|
||||
if (player.coverPath == lastCoverPath) { // Since 'notify::cover-path' emits on cover download complete
|
||||
// Utils.timeout(200, () => { self.css = `background-image: url('${coverPath}');`; });
|
||||
Utils.timeout(200, () => self.attribute.showImage(self, coverPath));
|
||||
}
|
||||
lastCoverPath = player.coverPath;
|
||||
|
||||
// If a colorscheme has already been generated, skip generation
|
||||
if (fileExists(stylePath)) {
|
||||
// Utils.timeout(200, () => { self.css = `background-image: url('${coverPath}');`; });
|
||||
self.attribute.showImage(self, coverPath)
|
||||
App.applyCss(stylePath);
|
||||
return;
|
||||
@@ -213,8 +212,7 @@ const CoverArt = ({ player, ...rest }) => {
|
||||
.then(() => {
|
||||
exec(`wal -i "${player.coverPath}" -n -t -s -e -q ${lightDark}`)
|
||||
exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${App.configDir}/scss/_musicwal.scss`);
|
||||
exec(`sassc ${App.configDir}/scss/_music.scss ${stylePath}`);
|
||||
// self.css = `background-image: url('${coverPath}');`;
|
||||
exec(`sass ${App.configDir}/scss/_music.scss ${stylePath}`);
|
||||
Utils.timeout(200, () => self.attribute.showImage(self, coverPath));
|
||||
App.applyCss(`${stylePath}`);
|
||||
})
|
||||
|
||||
@@ -3,16 +3,16 @@ 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, Scrollable } = Widget;
|
||||
const { Box, Button, Label, Icon, Scrollable } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
||||
import md2pango from '../../.miscutils/md2pango.js';
|
||||
|
||||
|
||||
const LATEX_DIR = `${GLib.get_user_cache_dir()}/ags/media/latex`;
|
||||
const CUSTOM_SOURCEVIEW_SCHEME_PATH = `${App.configDir}/assets/themes/sourceviewtheme.xml`;
|
||||
const CUSTOM_SCHEME_ID = 'custom';
|
||||
const USERNAME = GLib.get_user_name();
|
||||
const CHATGPT_CURSOR = ' ...';
|
||||
const AI_MESSAGE_CURSOR = ' ...';
|
||||
|
||||
/////////////////////// Custom source view colorscheme /////////////////////////
|
||||
|
||||
@@ -34,13 +34,6 @@ loadCustomColorScheme(CUSTOM_SOURCEVIEW_SCHEME_PATH);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function copyToClipboard(text) {
|
||||
const clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
|
||||
const textVariant = new GLib.Variant('s', text);
|
||||
clipboard.set_text(textVariant, -1);
|
||||
clipboard.store();
|
||||
}
|
||||
|
||||
function substituteLang(str) {
|
||||
const subs = [
|
||||
{ from: 'javascript', to: 'js' },
|
||||
@@ -82,7 +75,69 @@ const TextBlock = (content = '') => Label({
|
||||
label: content,
|
||||
});
|
||||
|
||||
Utils.execAsync(['bash', '-c', `rm ${LATEX_DIR}/*`])
|
||||
.then(() => Utils.execAsync(['bash', '-c', `mkdir -p ${LATEX_DIR}`]))
|
||||
.catch(() => {});
|
||||
const Latex = (content = '') => {
|
||||
const latexViewArea = Box({
|
||||
// vscroll: 'never',
|
||||
// hscroll: 'automatic',
|
||||
attribute: {
|
||||
render: async (self, text) => {
|
||||
if (text.length == 0) return;
|
||||
const styleContext = self.get_style_context();
|
||||
const fontSize = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL);
|
||||
|
||||
const timeSinceEpoch = Date.now();
|
||||
const fileName = `${timeSinceEpoch}.tex`;
|
||||
const outFileName = `${timeSinceEpoch}-symbolic.svg`;
|
||||
const scriptFileName = `${timeSinceEpoch}-render.sh`;
|
||||
const filePath = `${LATEX_DIR}/${fileName}`;
|
||||
const outFilePath = `${LATEX_DIR}/${outFileName}`;
|
||||
const scriptFilePath = `${LATEX_DIR}/${scriptFileName}`;
|
||||
|
||||
Utils.writeFile(text, filePath).catch(print);
|
||||
// Since MicroTex doesn't support file path input properly, we gotta cat it
|
||||
// And escaping such a command is a fucking pain so I decided to just generate a script
|
||||
// Note: MicroTex doesn't support `&=`
|
||||
// You can add this line in the middle for debugging: echo "$text" > ${filePath}.tmp
|
||||
const renderScript = `#!/usr/bin/env bash
|
||||
text=$(cat ${filePath} | sed 's/$/ \\\\\\\\/g' | sed 's/&=/=/g')
|
||||
LaTeX -headless -input="$text" -output=${outFilePath} -textsize=${fontSize * 1.1} -padding=0 -maxwidth=${latexViewArea.get_allocated_width() * 0.85}
|
||||
`;
|
||||
Utils.writeFile(renderScript, scriptFilePath).catch(print);
|
||||
Utils.exec(`chmod a+x ${scriptFilePath}`)
|
||||
Utils.timeout(100, () => {
|
||||
Utils.exec(`bash ${scriptFilePath}`);
|
||||
Gtk.IconTheme.get_default().append_search_path(LATEX_DIR);
|
||||
self.child?.destroy();
|
||||
self.child = Gtk.Image.new_from_file(outFilePath);
|
||||
})
|
||||
}
|
||||
},
|
||||
setup: (self) => self.attribute.render(self, content).catch(print),
|
||||
});
|
||||
const wholeThing = Box({
|
||||
className: 'sidebar-chat-latex',
|
||||
homogeneous: true,
|
||||
attribute: {
|
||||
'updateText': (text) => {
|
||||
latexViewArea.attribute.render(latexViewArea, text).catch(print);
|
||||
}
|
||||
},
|
||||
children: [Scrollable({
|
||||
vscroll: 'never',
|
||||
hscroll: 'automatic',
|
||||
child: latexViewArea
|
||||
})]
|
||||
})
|
||||
return wholeThing;
|
||||
}
|
||||
|
||||
const CodeBlock = (content = '', lang = 'txt') => {
|
||||
if (lang == 'tex' || lang == 'latex') {
|
||||
return Latex(content);
|
||||
}
|
||||
const topBar = Box({
|
||||
className: 'sidebar-chat-codeblock-topbar',
|
||||
children: [
|
||||
@@ -203,7 +258,7 @@ const MessageContent = (content) => {
|
||||
const lastLabel = kids[kids.length - 1];
|
||||
let blockContent = lines.slice(lastProcessed, lines.length).join('\n');
|
||||
if (!inCode)
|
||||
lastLabel.label = `${md2pango(blockContent)}${useCursor ? CHATGPT_CURSOR : ''}`;
|
||||
lastLabel.label = `${md2pango(blockContent)}${useCursor ? AI_MESSAGE_CURSOR : ''}`;
|
||||
else
|
||||
lastLabel.attribute.updateText(blockContent);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
const { Box, Button, Icon, Label, Revealer, Scrollable } = Widget;
|
||||
import ChatGPT from '../../../services/chatgpt.js';
|
||||
import GPTService from '../../../services/gpt.js';
|
||||
import { setupCursorHover, setupCursorHoverInfo } from '../../.widgetutils/cursorhover.js';
|
||||
import { SystemMessage, ChatMessage } from "./ai_chatmessage.js";
|
||||
import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../.commonwidgets/configwidgets.js';
|
||||
import { markdownTest } from '../../.miscutils/md2pango.js';
|
||||
import { MarginRevealer } from '../../.widgethacks/advancedrevealers.js';
|
||||
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
||||
|
||||
Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets/icons`);
|
||||
|
||||
@@ -19,7 +20,95 @@ export const chatGPTTabIcon = Icon({
|
||||
icon: `openai-symbolic`,
|
||||
});
|
||||
|
||||
const ChatGPTInfo = () => {
|
||||
const ProviderSwitcher = () => {
|
||||
const ProviderChoice = (id, provider) => {
|
||||
const providerSelected = MaterialIcon('check', 'norm', {
|
||||
setup: (self) => self.hook(GPTService, (self) => {
|
||||
self.toggleClassName('invisible', GPTService.providerID !== id);
|
||||
}, 'providerChanged')
|
||||
});
|
||||
return Button({
|
||||
tooltipText: provider.description,
|
||||
onClicked: () => {
|
||||
GPTService.providerID = id;
|
||||
providerList.revealChild = false;
|
||||
indicatorChevron.label = 'expand_more';
|
||||
},
|
||||
child: Box({
|
||||
className: 'spacing-h-10 txt',
|
||||
children: [
|
||||
Icon({
|
||||
icon: provider['logo_name'],
|
||||
className: 'txt-large'
|
||||
}),
|
||||
Label({
|
||||
hexpand: true,
|
||||
xalign: 0,
|
||||
className: 'txt-small',
|
||||
label: provider.name,
|
||||
}),
|
||||
providerSelected
|
||||
],
|
||||
}),
|
||||
setup: setupCursorHover,
|
||||
});
|
||||
}
|
||||
const indicatorChevron = MaterialIcon('expand_more', 'norm');
|
||||
const indicatorButton = Button({
|
||||
tooltipText: 'Select ChatGPT-compatible API provider',
|
||||
child: Box({
|
||||
className: 'spacing-h-10 txt',
|
||||
children: [
|
||||
MaterialIcon('cloud', 'norm'),
|
||||
Label({
|
||||
hexpand: true,
|
||||
xalign: 0,
|
||||
className: 'txt-small',
|
||||
label: GPTService.providerID,
|
||||
setup: (self) => self.hook(GPTService, (self) => {
|
||||
self.label = `${GPTService.providers[GPTService.providerID]['name']}`;
|
||||
}, 'providerChanged')
|
||||
}),
|
||||
indicatorChevron,
|
||||
]
|
||||
}),
|
||||
onClicked: () => {
|
||||
providerList.revealChild = !providerList.revealChild;
|
||||
indicatorChevron.label = (providerList.revealChild ? 'expand_less' : 'expand_more');
|
||||
},
|
||||
setup: setupCursorHover,
|
||||
});
|
||||
const providerList = Revealer({
|
||||
revealChild: false,
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 180,
|
||||
child: Box({
|
||||
vertical: true, className: 'spacing-v-5 sidebar-chat-providerswitcher-list',
|
||||
children: [
|
||||
Box({ className: 'separator-line margin-top-5 margin-bottom-5' }),
|
||||
Box({
|
||||
className: 'spacing-v-5',
|
||||
vertical: true,
|
||||
setup: (self) => self.hook(GPTService, (self) => {
|
||||
self.children = Object.entries(GPTService.providers)
|
||||
.map(([id, provider]) => ProviderChoice(id, provider));
|
||||
}, 'initialized'),
|
||||
})
|
||||
]
|
||||
})
|
||||
})
|
||||
return Box({
|
||||
hpack: 'center',
|
||||
vertical: true,
|
||||
className: 'sidebar-chat-providerswitcher',
|
||||
children: [
|
||||
indicatorButton,
|
||||
providerList,
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
const GPTInfo = () => {
|
||||
const openAiLogo = Icon({
|
||||
hpack: 'center',
|
||||
className: 'sidebar-chat-welcome-logo',
|
||||
@@ -34,7 +123,7 @@ const ChatGPTInfo = () => {
|
||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
||||
wrap: true,
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'Assistant (ChatGPT 3.5)',
|
||||
label: 'Assistant (GPTs)',
|
||||
}),
|
||||
Box({
|
||||
className: 'spacing-h-5',
|
||||
@@ -49,7 +138,7 @@ const ChatGPTInfo = () => {
|
||||
Button({
|
||||
className: 'txt-subtext txt-norm icon-material',
|
||||
label: 'info',
|
||||
tooltipText: 'Uses gpt-3.5-turbo.\nNot affiliated, endorsed, or sponsored by OpenAI.',
|
||||
tooltipText: 'Uses gpt-3.5-turbo.\nNot affiliated, endorsed, or sponsored by OpenAI.\n\nPrivacy: OpenAI claims they do not use your data when you use their API.',
|
||||
setup: setupCursorHoverInfo,
|
||||
}),
|
||||
]
|
||||
@@ -58,14 +147,14 @@ const ChatGPTInfo = () => {
|
||||
});
|
||||
}
|
||||
|
||||
export const ChatGPTSettings = () => MarginRevealer({
|
||||
const GPTSettings = () => MarginRevealer({
|
||||
transition: 'slide_down',
|
||||
revealChild: true,
|
||||
extraSetup: (self) => self
|
||||
.hook(ChatGPT, (self) => Utils.timeout(200, () => {
|
||||
.hook(GPTService, (self) => Utils.timeout(200, () => {
|
||||
self.attribute.hide();
|
||||
}), 'newMsg')
|
||||
.hook(ChatGPT, (self) => Utils.timeout(200, () => {
|
||||
.hook(GPTService, (self) => Utils.timeout(200, () => {
|
||||
self.attribute.show();
|
||||
}), 'clear')
|
||||
,
|
||||
@@ -77,7 +166,7 @@ export const ChatGPTSettings = () => MarginRevealer({
|
||||
hpack: 'center',
|
||||
icon: 'casino',
|
||||
name: 'Randomness',
|
||||
desc: 'ChatGPT\'s temperature value.\n Precise = 0\n Balanced = 0.5\n Creative = 1',
|
||||
desc: 'The model\'s temperature value.\n Precise = 0\n Balanced = 0.5\n Creative = 1',
|
||||
options: [
|
||||
{ value: 0.00, name: 'Precise', },
|
||||
{ value: 0.50, name: 'Balanced', },
|
||||
@@ -85,7 +174,7 @@ export const ChatGPTSettings = () => MarginRevealer({
|
||||
],
|
||||
initIndex: 2,
|
||||
onChange: (value, name) => {
|
||||
ChatGPT.temperature = value;
|
||||
GPTService.temperature = value;
|
||||
},
|
||||
}),
|
||||
ConfigGap({ vertical: true, size: 10 }), // Note: size can only be 5, 10, or 15
|
||||
@@ -98,18 +187,18 @@ export const ChatGPTSettings = () => MarginRevealer({
|
||||
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,
|
||||
initValue: GPTService.cycleModels,
|
||||
onChange: (self, newValue) => {
|
||||
ChatGPT.cycleModels = newValue;
|
||||
GPTService.cycleModels = newValue;
|
||||
},
|
||||
}),
|
||||
ConfigToggle({
|
||||
icon: 'model_training',
|
||||
name: 'Enhancements',
|
||||
desc: 'Tells ChatGPT:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
|
||||
initValue: ChatGPT.assistantPrompt,
|
||||
desc: 'Tells the model:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
|
||||
initValue: GPTService.assistantPrompt,
|
||||
onChange: (self, newValue) => {
|
||||
ChatGPT.assistantPrompt = newValue;
|
||||
GPTService.assistantPrompt = newValue;
|
||||
},
|
||||
}),
|
||||
]
|
||||
@@ -124,8 +213,8 @@ export const OpenaiApiKeyInstructions = () => Box({
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 150,
|
||||
setup: (self) => self
|
||||
.hook(ChatGPT, (self, hasKey) => {
|
||||
self.revealChild = (ChatGPT.key.length == 0);
|
||||
.hook(GPTService, (self, hasKey) => {
|
||||
self.revealChild = (GPTService.key.length == 0);
|
||||
}, 'hasKey')
|
||||
,
|
||||
child: Button({
|
||||
@@ -134,17 +223,17 @@ export const OpenaiApiKeyInstructions = () => Box({
|
||||
wrap: true,
|
||||
className: 'txt sidebar-chat-welcome-txt',
|
||||
justify: Gtk.Justification.CENTER,
|
||||
label: 'An OpenAI API key is required\nYou can grab one <u>here</u>, then enter it below'
|
||||
label: 'An 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://platform.openai.com/api-keys &`]);
|
||||
Utils.execAsync(['bash', '-c', `xdg-open ${GPTService.getKeyUrl}`]);
|
||||
}
|
||||
})
|
||||
})]
|
||||
});
|
||||
|
||||
const chatGPTWelcome = Box({
|
||||
const GPTWelcome = () => Box({
|
||||
vexpand: true,
|
||||
homogeneous: true,
|
||||
child: Box({
|
||||
@@ -152,9 +241,9 @@ const chatGPTWelcome = Box({
|
||||
vpack: 'center',
|
||||
vertical: true,
|
||||
children: [
|
||||
ChatGPTInfo(),
|
||||
GPTInfo(),
|
||||
OpenaiApiKeyInstructions(),
|
||||
ChatGPTSettings(),
|
||||
GPTSettings(),
|
||||
]
|
||||
})
|
||||
});
|
||||
@@ -163,16 +252,16 @@ export const chatContent = Box({
|
||||
className: 'spacing-v-15',
|
||||
vertical: true,
|
||||
setup: (self) => self
|
||||
.hook(ChatGPT, (box, id) => {
|
||||
const message = ChatGPT.messages[id];
|
||||
.hook(GPTService, (box, id) => {
|
||||
const message = GPTService.messages[id];
|
||||
if (!message) return;
|
||||
box.add(ChatMessage(message, 'ChatGPT'))
|
||||
box.add(ChatMessage(message, `Model (${GPTService.providers[GPTService.providerID]['name']})`))
|
||||
}, 'newMsg')
|
||||
,
|
||||
});
|
||||
|
||||
const clearChat = () => {
|
||||
ChatGPT.clear();
|
||||
GPTService.clear();
|
||||
const children = chatContent.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
@@ -180,34 +269,6 @@ const clearChat = () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const chatGPTView = Scrollable({
|
||||
className: 'sidebar-chat-viewport',
|
||||
vexpand: true,
|
||||
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');
|
||||
// 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),
|
||||
@@ -228,16 +289,16 @@ export const chatGPTCommands = Box({
|
||||
export const sendMessage = (text) => {
|
||||
// Check if text or API key is empty
|
||||
if (text.length == 0) return;
|
||||
if (ChatGPT.key.length == 0) {
|
||||
ChatGPT.key = text;
|
||||
chatContent.add(SystemMessage(`Key saved to\n\`${ChatGPT.keyPath}\``, 'API Key', chatGPTView));
|
||||
if (GPTService.key.length == 0) {
|
||||
GPTService.key = text;
|
||||
chatContent.add(SystemMessage(`Key saved to\n\`${GPTService.keyPath}\``, 'API Key', chatGPTView));
|
||||
text = '';
|
||||
return;
|
||||
}
|
||||
// Commands
|
||||
if (text.startsWith('/')) {
|
||||
if (text.startsWith('/clear')) clearChat();
|
||||
else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${ChatGPT.modelName}\``, '/model', chatGPTView))
|
||||
else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${GPTService.modelName}\``, '/model', chatGPTView))
|
||||
else if (text.startsWith('/prompt')) {
|
||||
const firstSpaceIndex = text.indexOf(' ');
|
||||
const prompt = text.slice(firstSpaceIndex + 1);
|
||||
@@ -245,18 +306,18 @@ export const sendMessage = (text) => {
|
||||
chatContent.add(SystemMessage(`Usage: \`/prompt MESSAGE\``, '/prompt', chatGPTView))
|
||||
}
|
||||
else {
|
||||
ChatGPT.addMessage('user', prompt)
|
||||
GPTService.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 stored in:\n\`${GPTService.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', chatGPTView));
|
||||
GPTService.key = parts[1];
|
||||
chatContent.add(SystemMessage(`Updated API Key at\n\`${GPTService.keyPath}\``, '/key', chatGPTView));
|
||||
}
|
||||
}
|
||||
else if (text.startsWith('/test'))
|
||||
@@ -265,6 +326,40 @@ export const sendMessage = (text) => {
|
||||
chatContent.add(SystemMessage(`Invalid command.`, 'Error', chatGPTView))
|
||||
}
|
||||
else {
|
||||
ChatGPT.send(text);
|
||||
GPTService.send(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const chatGPTView = Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
ProviderSwitcher(),
|
||||
Scrollable({
|
||||
className: 'sidebar-chat-viewport',
|
||||
vexpand: true,
|
||||
child: Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
GPTWelcome(),
|
||||
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());
|
||||
})
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
const { Box, Button, Icon, Label, Revealer, Scrollable } = Widget;
|
||||
import Gemini from '../../../services/gemini.js';
|
||||
import GeminiService from '../../../services/gemini.js';
|
||||
import { setupCursorHover, setupCursorHoverInfo } from '../../.widgetutils/cursorhover.js';
|
||||
import { SystemMessage, ChatMessage } from "./ai_chatmessage.js";
|
||||
import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../.commonwidgets/configwidgets.js';
|
||||
@@ -50,7 +50,7 @@ const GeminiInfo = () => {
|
||||
Button({
|
||||
className: 'txt-subtext txt-norm icon-material',
|
||||
label: 'info',
|
||||
tooltipText: 'Uses gemini-pro.\nNot affiliated, endorsed, or sponsored by Google.',
|
||||
tooltipText: 'Uses gemini-pro.\nNot affiliated, endorsed, or sponsored by Google.\n\nPrivacy: Google collects data for training by default.\nIf you mind, turn off Gemini Apps Activity in your account.',
|
||||
setup: setupCursorHoverInfo,
|
||||
}),
|
||||
]
|
||||
@@ -63,10 +63,10 @@ export const GeminiSettings = () => MarginRevealer({
|
||||
transition: 'slide_down',
|
||||
revealChild: true,
|
||||
extraSetup: (self) => self
|
||||
.hook(Gemini, (self) => Utils.timeout(200, () => {
|
||||
.hook(GeminiService, (self) => Utils.timeout(200, () => {
|
||||
self.attribute.hide();
|
||||
}), 'newMsg')
|
||||
.hook(Gemini, (self) => Utils.timeout(200, () => {
|
||||
.hook(GeminiService, (self) => Utils.timeout(200, () => {
|
||||
self.attribute.show();
|
||||
}), 'clear')
|
||||
,
|
||||
@@ -86,7 +86,7 @@ export const GeminiSettings = () => MarginRevealer({
|
||||
],
|
||||
initIndex: 2,
|
||||
onChange: (value, name) => {
|
||||
Gemini.temperature = value;
|
||||
GeminiService.temperature = value;
|
||||
},
|
||||
}),
|
||||
ConfigGap({ vertical: true, size: 10 }), // Note: size can only be 5, 10, or 15
|
||||
@@ -99,9 +99,9 @@ export const GeminiSettings = () => MarginRevealer({
|
||||
icon: 'model_training',
|
||||
name: 'Enhancements',
|
||||
desc: 'Tells Gemini:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
|
||||
initValue: Gemini.assistantPrompt,
|
||||
initValue: GeminiService.assistantPrompt,
|
||||
onChange: (self, newValue) => {
|
||||
Gemini.assistantPrompt = newValue;
|
||||
GeminiService.assistantPrompt = newValue;
|
||||
},
|
||||
}),
|
||||
]
|
||||
@@ -116,8 +116,8 @@ export const GoogleAiInstructions = () => Box({
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 150,
|
||||
setup: (self) => self
|
||||
.hook(Gemini, (self, hasKey) => {
|
||||
self.revealChild = (Gemini.key.length == 0);
|
||||
.hook(GeminiService, (self, hasKey) => {
|
||||
self.revealChild = (GeminiService.key.length == 0);
|
||||
}, 'hasKey')
|
||||
,
|
||||
child: Button({
|
||||
@@ -155,8 +155,8 @@ export const chatContent = Box({
|
||||
className: 'spacing-v-15',
|
||||
vertical: true,
|
||||
setup: (self) => self
|
||||
.hook(Gemini, (box, id) => {
|
||||
const message = Gemini.messages[id];
|
||||
.hook(GeminiService, (box, id) => {
|
||||
const message = GeminiService.messages[id];
|
||||
if (!message) return;
|
||||
box.add(ChatMessage(message, MODEL_NAME))
|
||||
}, 'newMsg')
|
||||
@@ -164,7 +164,7 @@ export const chatContent = Box({
|
||||
});
|
||||
|
||||
const clearChat = () => {
|
||||
Gemini.clear();
|
||||
GeminiService.clear();
|
||||
const children = chatContent.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
@@ -172,34 +172,6 @@ const clearChat = () => {
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
@@ -220,16 +192,16 @@ export const geminiCommands = Box({
|
||||
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));
|
||||
if (GeminiService.key.length == 0) {
|
||||
GeminiService.key = text;
|
||||
chatContent.add(SystemMessage(`Key saved to\n\`${GeminiService.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('/model')) chatContent.add(SystemMessage(`Currently using \`${GeminiService.modelName}\``, '/model', geminiView))
|
||||
else if (text.startsWith('/prompt')) {
|
||||
const firstSpaceIndex = text.indexOf(' ');
|
||||
const prompt = text.slice(firstSpaceIndex + 1);
|
||||
@@ -237,18 +209,18 @@ export const sendMessage = (text) => {
|
||||
chatContent.add(SystemMessage(`Usage: \`/prompt MESSAGE\``, '/prompt', geminiView))
|
||||
}
|
||||
else {
|
||||
Gemini.addMessage('user', prompt)
|
||||
GeminiService.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 stored in:\n\`${GeminiService.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));
|
||||
GeminiService.key = parts[1];
|
||||
chatContent.add(SystemMessage(`Updated API Key at\n\`${GeminiService.keyPath}\``, '/key', geminiView));
|
||||
}
|
||||
}
|
||||
else if (text.startsWith('/test'))
|
||||
@@ -257,6 +229,37 @@ export const sendMessage = (text) => {
|
||||
chatContent.add(SystemMessage(`Invalid command.`, 'Error', geminiView))
|
||||
}
|
||||
else {
|
||||
Gemini.send(text);
|
||||
GeminiService.send(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const geminiView = Box({
|
||||
homogeneous: true,
|
||||
children: [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());
|
||||
})
|
||||
}
|
||||
})]
|
||||
});
|
||||
@@ -6,7 +6,7 @@ const { execAsync, exec } = Utils;
|
||||
import { setupCursorHover, setupCursorHoverInfo } from '../.widgetutils/cursorhover.js';
|
||||
import { contentStack } from './sideleft.js';
|
||||
// APIs
|
||||
import ChatGPT from '../../services/chatgpt.js';
|
||||
import GPTService from '../../services/gpt.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';
|
||||
@@ -26,12 +26,12 @@ const APIS = [
|
||||
placeholderText: 'Message Gemini...',
|
||||
},
|
||||
{
|
||||
name: 'Assistant (ChatGPT 3.5)',
|
||||
name: 'Assistant (GPTs)',
|
||||
sendCommand: chatGPTSendMessage,
|
||||
contentWidget: chatGPTView,
|
||||
commandBar: chatGPTCommands,
|
||||
tabIcon: chatGPTTabIcon,
|
||||
placeholderText: 'Message ChatGPT...',
|
||||
placeholderText: 'Message the model...',
|
||||
},
|
||||
{
|
||||
name: 'Waifus',
|
||||
@@ -65,9 +65,9 @@ export const chatEntry = TextView({
|
||||
acceptsTab: false,
|
||||
className: 'sidebar-chat-entry txt txt-smallie',
|
||||
setup: (self) => self
|
||||
.hook(ChatGPT, (self) => {
|
||||
if (APIS[currentApiId].name != 'Assistant (ChatGPT 3.5)') return;
|
||||
self.placeholderText = (ChatGPT.key.length > 0 ? 'Message ChatGPT...' : 'Enter OpenAI API Key...');
|
||||
.hook(GPTService, (self) => {
|
||||
if (APIS[currentApiId].name != 'Assistant (GPTs)') return;
|
||||
self.placeholderText = (GPTService.key.length > 0 ? 'Message the model...' : 'Enter API Key...');
|
||||
}, 'hasKey')
|
||||
.hook(Gemini, (self) => {
|
||||
if (APIS[currentApiId].name != 'Assistant (Gemini Pro)') return;
|
||||
|
||||
@@ -66,7 +66,7 @@ apply_gtklock() {
|
||||
|
||||
# Copy template
|
||||
mkdir -p "$HOME"/.cache/ags/user/generated/gtklock
|
||||
sassc "scripts/templates/gtklock/main.scss" "$HOME"/.cache/ags/user/generated/gtklock/style.css
|
||||
sass "scripts/templates/gtklock/main.scss" "$HOME"/.cache/ags/user/generated/gtklock/style.css
|
||||
cp "$HOME"/.cache/ags/user/generated/gtklock/style.css "$HOME"/.config/gtklock/style.css
|
||||
}
|
||||
|
||||
@@ -195,9 +195,9 @@ apply_gtk() { # Using gradience-cli
|
||||
}
|
||||
|
||||
apply_ags() {
|
||||
sassc "$HOME"/.config/ags/scss/main.scss "$HOME"/.config/ags/style.css
|
||||
sass "$HOME"/.config/ags/scss/main.scss "$HOME"/.cache/ags/user/generated/style.css
|
||||
ags run-js 'openColorScheme.value = true; Utils.timeout(2000, () => openColorScheme.value = false);'
|
||||
ags run-js "App.resetCss(); App.applyCss('${HOME}/.config/ags/style.css');"
|
||||
ags run-js "App.resetCss(); App.applyCss('${HOME}/.cache/ags/user/generated/style.css');"
|
||||
}
|
||||
|
||||
apply_ags &
|
||||
|
||||
@@ -42,7 +42,7 @@ elif [ "$backend" = "pywal" ]; then
|
||||
|
||||
cat color_generation/pywal_to_material.scss >> "$HOME"/.cache/ags/user/generated/material_colors.scss
|
||||
if [ "$2" = "--apply" ]; then
|
||||
sassc "$HOME"/.cache/ags/user/generated/material_colors.scss "$HOME"/.cache/ags/user/generated/colors_classes.scss --style compact
|
||||
sass "$HOME"/.cache/ags/user/generated/material_colors.scss "$HOME"/.cache/ags/user/generated/colors_classes.scss --style compact
|
||||
sed -i "s/ { color//g" "$HOME"/.cache/ags/user/generated/colors_classes.scss
|
||||
sed -i "s/\./$/g" "$HOME"/.cache/ags/user/generated/colors_classes.scss
|
||||
sed -i "s/}//g" "$HOME"/.cache/ags/user/generated/colors_classes.scss
|
||||
|
||||
Executable
+278
@@ -0,0 +1,278 @@
|
||||
#!/usr/bin/env bash
|
||||
## Grimblast: a helper for screenshots within hyprland
|
||||
## Requirements:
|
||||
## - `grim`: screenshot utility for wayland
|
||||
## - `slurp`: to select an area
|
||||
## - `hyprctl`: to read properties of current window (provided by Hyprland)
|
||||
## - `hyprpicker`: to freeze the screen when selecting area
|
||||
## - `wl-copy`: clipboard utility (provided by wl-clipboard)
|
||||
## - `jq`: json utility to parse hyprctl output
|
||||
## - `notify-send`: to show notifications (provided by libnotify)
|
||||
## Those are needed to be installed, if unsure, run `grimblast check`
|
||||
##
|
||||
## See `man 1 grimblast` or `grimblast usage` for further details.
|
||||
|
||||
## Author: Misterio (https://github.com/misterio77)
|
||||
|
||||
## This tool is based on grimshot, with swaymsg commands replaced by their
|
||||
## hyprctl equivalents.
|
||||
## https://github.com/swaywm/sway/blob/master/contrib/grimshot
|
||||
jq=gojq
|
||||
getTargetDirectory() {
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" &&
|
||||
. "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs"
|
||||
|
||||
echo "${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}"
|
||||
}
|
||||
|
||||
tmp_editor_directory() {
|
||||
echo "/tmp"
|
||||
}
|
||||
|
||||
#Detect if $GRIMBLAST_EDITOR env exist
|
||||
env_editor_confirm() {
|
||||
if [ -n "$GRIMBLAST_EDITOR" ]; then
|
||||
echo "GRIMBLAST_EDITOR is set. Continuing..."
|
||||
else
|
||||
echo "GRIMBLAST_EDITOR is not set. Defaulting to gimp"
|
||||
GRIMBLAST_EDITOR=gimp
|
||||
fi
|
||||
}
|
||||
|
||||
NOTIFY=no
|
||||
CURSOR=
|
||||
FREEZE=
|
||||
WAIT=no
|
||||
SCALE=
|
||||
HYPRPICKER_PID=-1
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
key="$1"
|
||||
|
||||
case $key in
|
||||
-n | --notify)
|
||||
NOTIFY=yes
|
||||
shift # past argument
|
||||
;;
|
||||
-c | --cursor)
|
||||
CURSOR=yes
|
||||
shift # past argument
|
||||
;;
|
||||
-f | --freeze)
|
||||
FREEZE=yes
|
||||
shift # past argument
|
||||
;;
|
||||
-w | --wait)
|
||||
shift
|
||||
WAIT=$1
|
||||
if echo "$WAIT" | grep "[^0-9]" -q; then
|
||||
echo "Invalid value for wait '$WAIT'" >&2
|
||||
exit 3
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
-s | --scale)
|
||||
shift # past argument
|
||||
if [ $# -gt 0 ]; then
|
||||
SCALE="$1" # assign the next argument to SCALE
|
||||
shift # past argument
|
||||
else
|
||||
echo "Error: Missing argument for --scale option."
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*) # unknown option
|
||||
break # done with parsing --flags
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
ACTION=${1:-usage}
|
||||
SUBJECT=${2:-screen}
|
||||
FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
|
||||
FILE_EDITOR=${3:-$(tmp_editor_directory)/$(date -Ins).png}
|
||||
|
||||
if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "edit" ] && [ "$ACTION" != "copysave" ] && [ "$ACTION" != "check" ]; then
|
||||
echo "Usage:"
|
||||
echo " grimblast [--notify] [--cursor] [--freeze] [--wait N] [--scale <scale>] (copy|save|copysave|edit) [active|screen|output|area] [FILE|-]"
|
||||
echo " grimblast check"
|
||||
echo " grimblast usage"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " copy: Copy the screenshot data into the clipboard."
|
||||
echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT."
|
||||
echo " copysave: Combine the previous 2 options."
|
||||
echo " edit: Open screenshot in the image editor of your choice (default is gimp). See man page for info."
|
||||
echo " check: Verify if required tools are installed and exit."
|
||||
echo " usage: Show this message and exit."
|
||||
echo ""
|
||||
echo "Targets:"
|
||||
echo " active: Currently active window."
|
||||
echo " screen: All visible outputs."
|
||||
echo " output: Currently active output."
|
||||
echo " area: Manually select a region or window."
|
||||
exit
|
||||
fi
|
||||
|
||||
notify() {
|
||||
notify-send -t 3000 -a grimblast "$@"
|
||||
}
|
||||
|
||||
notifyOk() {
|
||||
[ "$NOTIFY" = "no" ] && return
|
||||
|
||||
notify "$@"
|
||||
}
|
||||
|
||||
notifyError() {
|
||||
if [ $NOTIFY = "yes" ]; then
|
||||
TITLE=${2:-"Screenshot"}
|
||||
MESSAGE=${1:-"Error taking screenshot with grim"}
|
||||
notify -u critical "$TITLE" "$MESSAGE"
|
||||
else
|
||||
echo "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
resetFade() {
|
||||
if [[ -n $FADE && -n $FADEOUT ]]; then
|
||||
hyprctl keyword animation "$FADE" >/dev/null
|
||||
hyprctl keyword animation "$FADEOUT" >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
killHyprpicker() {
|
||||
if [ ! $HYPRPICKER_PID -eq -1 ]; then
|
||||
kill $HYPRPICKER_PID
|
||||
fi
|
||||
}
|
||||
|
||||
die() {
|
||||
killHyprpicker
|
||||
MSG=${1:-Bye}
|
||||
notifyError "Error: $MSG"
|
||||
exit 2
|
||||
}
|
||||
|
||||
check() {
|
||||
COMMAND=$1
|
||||
if command -v "$COMMAND" >/dev/null 2>&1; then
|
||||
RESULT="OK"
|
||||
else
|
||||
RESULT="NOT FOUND"
|
||||
fi
|
||||
echo " $COMMAND: $RESULT"
|
||||
}
|
||||
|
||||
takeScreenshot() {
|
||||
FILE=$1
|
||||
GEOM=$2
|
||||
OUTPUT=$3
|
||||
if [ -n "$OUTPUT" ]; then
|
||||
grim ${CURSOR:+-c} ${SCALE:+-s "$SCALE"} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim"
|
||||
elif [ -z "$GEOM" ]; then
|
||||
grim ${CURSOR:+-c} ${SCALE:+-s "$SCALE"} "$FILE" || die "Unable to invoke grim"
|
||||
else
|
||||
grim ${CURSOR:+-c} ${SCALE:+-s "$SCALE"} -g "$GEOM" "$FILE" || die "Unable to invoke grim"
|
||||
resetFade
|
||||
fi
|
||||
}
|
||||
|
||||
wait() {
|
||||
if [ "$WAIT" != "no" ]; then
|
||||
sleep "$WAIT"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$ACTION" = "check" ]; then
|
||||
echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..."
|
||||
check grim
|
||||
check slurp
|
||||
check hyprctl
|
||||
check hyprpicker
|
||||
check wl-copy
|
||||
check $jq
|
||||
check notify-send
|
||||
exit
|
||||
elif [ "$SUBJECT" = "active" ]; then
|
||||
wait
|
||||
FOCUSED=$(hyprctl activewindow -j)
|
||||
GEOM=$(echo "$FOCUSED" | $jq -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"')
|
||||
APP_ID=$(echo "$FOCUSED" | $jq -r '.class')
|
||||
WHAT="$APP_ID window"
|
||||
elif [ "$SUBJECT" = "screen" ]; then
|
||||
wait
|
||||
GEOM=""
|
||||
WHAT="Screen"
|
||||
elif [ "$SUBJECT" = "output" ]; then
|
||||
wait
|
||||
GEOM=""
|
||||
OUTPUT=$(hyprctl monitors -j | $jq -r '.[] | select(.focused == true)' | $jq -r '.name')
|
||||
WHAT="$OUTPUT"
|
||||
elif [ "$SUBJECT" = "area" ]; then
|
||||
if [ "$FREEZE" = "yes" ] && [ "$(command -v "hyprpicker")" ] >/dev/null 2>&1; then
|
||||
hyprpicker -r -z &
|
||||
sleep 0.2
|
||||
HYPRPICKER_PID=$!
|
||||
fi
|
||||
|
||||
# get fade & fadeOut animation and unset it
|
||||
# this removes the black border seen around screenshots
|
||||
FADE="$(hyprctl -j animations | $jq -jr '.[0][] | select(.name == "fade") | .name, ",", (if .enabled == true then "1" else "0" end), ",", (.speed|floor), ",", .bezier')"
|
||||
FADEOUT="$(hyprctl -j animations | $jq -jr '.[0][] | select(.name == "fadeOut") | .name, ",", (if .enabled == true then "1" else "0" end), ",", (.speed|floor), ",", .bezier')"
|
||||
hyprctl keyword animation 'fade,0,1,default' >/dev/null
|
||||
hyprctl keyword animation 'fadeOut,0,1,default' >/dev/null
|
||||
|
||||
WORKSPACES="$(hyprctl monitors -j | $jq -r 'map(.activeWorkspace.id)')"
|
||||
WINDOWS="$(hyprctl clients -j | $jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))')"
|
||||
# shellcheck disable=2086 # if we don't split, spaces mess up slurp
|
||||
GEOM=$(echo "$WINDOWS" | $jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp $SLURP_ARGS)
|
||||
|
||||
# Check if user exited slurp without selecting the area
|
||||
if [ -z "$GEOM" ]; then
|
||||
killHyprpicker
|
||||
resetFade
|
||||
exit 1
|
||||
fi
|
||||
WHAT="Area"
|
||||
wait
|
||||
elif [ "$SUBJECT" = "window" ]; then
|
||||
die "Subject 'window' is now included in 'area'"
|
||||
else
|
||||
die "Unknown subject to take a screen shot from" "$SUBJECT"
|
||||
fi
|
||||
|
||||
if [ "$ACTION" = "copy" ]; then
|
||||
takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error"
|
||||
notifyOk "$WHAT copied to buffer"
|
||||
elif [ "$ACTION" = "save" ]; then
|
||||
if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then
|
||||
TITLE="Screenshot of $SUBJECT"
|
||||
MESSAGE=$(basename "$FILE")
|
||||
notifyOk "$TITLE" "$MESSAGE" -i "$FILE"
|
||||
echo "$FILE"
|
||||
else
|
||||
notifyError "Error taking screenshot with grim"
|
||||
fi
|
||||
elif [ "$ACTION" = "edit" ]; then
|
||||
env_editor_confirm
|
||||
if takeScreenshot "$FILE_EDITOR" "$GEOM" "$OUTPUT"; then
|
||||
TITLE="Screenshot of $SUBJECT"
|
||||
MESSAGE="Open screenshot in image editor"
|
||||
notifyOk "$TITLE" "$MESSAGE" -i "$FILE_EDITOR"
|
||||
$GRIMBLAST_EDITOR "$FILE_EDITOR"
|
||||
echo "$FILE_EDITOR"
|
||||
else
|
||||
notifyError "Error taking screenshot"
|
||||
fi
|
||||
else
|
||||
if [ "$ACTION" = "copysave" ]; then
|
||||
takeScreenshot - "$GEOM" "$OUTPUT" | tee "$FILE" | wl-copy --type image/png || die "Clipboard error"
|
||||
notifyOk "$WHAT copied to buffer and saved to $FILE" -i "$FILE"
|
||||
echo "$FILE"
|
||||
else
|
||||
notifyError "Error taking screenshot with grim"
|
||||
fi
|
||||
fi
|
||||
|
||||
killHyprpicker
|
||||
@@ -1,4 +1,4 @@
|
||||
$SLURP_COMMAND="$(slurp -d -c {{ $onSecondaryContainer }}BB -b {{ $secondaryContainer }}44 -s 00000000)"
|
||||
# exec = export SLURP_ARGS='-d -c {{ $onSecondaryContainer }}BB -b {{ $secondaryContainer }}44 -s 00000000'
|
||||
|
||||
general {
|
||||
col.active_border = rgba({{ $onSurface }}39)
|
||||
|
||||
@@ -40,7 +40,7 @@ label { # Clock
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
label {
|
||||
label { # Greeting
|
||||
monitor =
|
||||
text = hi $USER !!!
|
||||
color = $text_color
|
||||
@@ -51,7 +51,7 @@ label {
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
label {
|
||||
label { # lock icon
|
||||
monitor =
|
||||
text = lock
|
||||
color = $text_color
|
||||
@@ -62,7 +62,7 @@ label {
|
||||
halign = center
|
||||
valign = bottom
|
||||
}
|
||||
label {
|
||||
label { # "locked" text
|
||||
monitor =
|
||||
text = locked
|
||||
color = $text_color
|
||||
@@ -72,4 +72,16 @@ label {
|
||||
position = 0, 50
|
||||
halign = center
|
||||
valign = bottom
|
||||
}
|
||||
|
||||
label { # Status
|
||||
monitor =
|
||||
text = cmd[update:5000] ~/.config/hypr/hyprlock/status.sh
|
||||
color = $text_color
|
||||
font_size = 14
|
||||
font_family = $font_family
|
||||
|
||||
position = 30, -30
|
||||
halign = left
|
||||
valign = top
|
||||
}
|
||||
@@ -167,7 +167,7 @@
|
||||
}
|
||||
|
||||
.separator-line {
|
||||
background-color: $outline;
|
||||
background-color: mix($subtext, $surface, 50%);
|
||||
min-width: 0.068rem;
|
||||
min-height: 0.068rem;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,8 @@ $osk_key_fontsize: 1.091rem;
|
||||
|
||||
.osk-key-fn {
|
||||
min-width: $osk_key_width * 1.005;
|
||||
min-height: $osk_key_height / 2;
|
||||
min-height: calc($osk_key_height / 2); // dart-sass
|
||||
// min-height: $osk_key_height / 2; // sassc
|
||||
}
|
||||
|
||||
.osk-key-tab {
|
||||
|
||||
@@ -557,6 +557,17 @@ $colorpicker_rounding: 0.341rem;
|
||||
color: $onSecondaryContainer;
|
||||
}
|
||||
|
||||
.sidebar-chat-providerswitcher {
|
||||
@include small-rounding;
|
||||
padding: 0.477rem 0.682rem;
|
||||
background-color: $textboxColor;
|
||||
color: $onSurfaceVariant;
|
||||
}
|
||||
|
||||
// .sidebar-chat-providerswitcher-list {
|
||||
// margin: 0.341rem 0rem;
|
||||
// }
|
||||
|
||||
.sidebar-chat-viewport {
|
||||
@include element_decel;
|
||||
// margin: 0.682rem 0rem;
|
||||
@@ -655,6 +666,17 @@ $colorpicker_rounding: 0.341rem;
|
||||
@include readingfont;
|
||||
}
|
||||
|
||||
.sidebar-chat-latex {
|
||||
@include small-rounding;
|
||||
margin: 0rem 0.682rem;
|
||||
padding: 0.682rem;
|
||||
@if $darkmode ==true {
|
||||
background-color: white;
|
||||
}
|
||||
color: $onBackground;
|
||||
// background-color: $termbg;
|
||||
}
|
||||
|
||||
.sidebar-chat-codeblock {
|
||||
@include normal-rounding;
|
||||
// @include elevation2;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { fileExists } from './messages.js';
|
||||
|
||||
const initMessages =
|
||||
[
|
||||
{ role: "user", parts: [{ text: "You are an assistant on a sidebar of a Wayland Linux desktop. Please always use a casual tone when answering your questions, unless requested otherwise or making writing suggestions. These are the steps you should take to respond to the user's queries:\n1. If it's a writing- or grammar-related question or a sentence in quotation marks, Please point out errors and correct when necessary using underlines, and make the writing more natural where appropriate without making too major changes. If you're given a sentence in quotes but is grammatically correct, explain briefly concepts that are uncommon.\n2. If it's a question about system tasks, give a bash command in a code block with very brief explanation for each command\n3. Otherwise, when asked to summarize information or explaining concepts, you are encouraged to use bullet points and headings. Use casual language and be short and concise. \nThanks!" }], },
|
||||
{ role: "user", parts: [{ text: "You are an assistant on a sidebar of a Wayland Linux desktop. Please always use a casual tone when answering your questions, unless requested otherwise or making writing suggestions. These are the steps you should take to respond to the user's queries:\n1. If it's a writing- or grammar-related question or a sentence in quotation marks, Please point out errors and correct when necessary using underlines, and make the writing more natural where appropriate without making too major changes. If you're given a sentence in quotes but is grammatically correct, explain briefly concepts that are uncommon.\n2. If it's a question about system tasks, give a bash command in a code block with very brief explanation for each command\n3. Otherwise, when asked to summarize information or explaining concepts, you are should use bullet points and headings. For mathematics expressions, you *have to* use LaTeX within a code block with the language set as \"latex\" for the interface to render it properly. Use casual language and be short and concise. \nThanks!" }], },
|
||||
{ role: "model", parts: [{ text: "- Got it!" }], },
|
||||
{ role: "user", parts: [{ text: "\"He rushed to where the event was supposed to be hold, he didn't know it got calceled\"" }], },
|
||||
{ role: "model", parts: [{ text: "## Grammar correction\nErrors:\n\"He rushed to where the event was supposed to be __hold____,__ he didn't know it got calceled\"\nCorrection + minor improvements:\n\"He rushed to the place where the event was supposed to be __held____, but__ he didn't know that it got calceled\"" }], },
|
||||
@@ -19,6 +19,10 @@ const initMessages =
|
||||
{ role: "model", parts: [{ text: "## Skeuomorphism\n- A design philosophy- From early days of interface designing- Tries to imitate real-life objects- It's in fact still used by Apple in their icons until today." }], },
|
||||
{ role: "user", parts: [{ text: "\"ignorance is bliss\"" }], },
|
||||
{ role: "model", parts: [{ text: "## \"Ignorance is bliss\"\n- A Latin proverb that means being unaware of something negative can be a source of happiness\n- Often used to justify avoiding difficult truths or responsibilities\n- Can also be interpreted as a warning against seeking knowledge that may bring pain or sorrow" }], },
|
||||
{ role: "user", parts: [{ text: "find the derivative of (x-438)/(x^2+23x-7)+x^x" }], },
|
||||
{ role: "model", parts: [{ text: "## Derivative\n```latex\n\\[\n\\frac{d}{dx}\\left(\\frac{x - 438}{x^2 + 23x - 7} + x^x\\right) = \\frac{-(x^2+23x-7)-(x-438)(2x+23)}{(x^2+23x-7)^2} + x^x(\\ln(x) + 1)\n\\]\n```" }], },
|
||||
{ role: "user", parts: [{ text: "write the double angle formulas" }], },
|
||||
{ role: "model", parts: [{ text: "## Double angle formulas\n```latex\n\\[\n\\sin(2\theta) = 2\\sin(\\theta)\\cos(\\theta)\n\\]\n\\\\\n\\[\n\\cos(2\\theta) = \\cos^2(\\theta) - \\sin^2(\\theta)\n\\]\n\\\\\n\\[\n\\tan(2\theta) = \\frac{2\\tan(\\theta)}{1 - \\tan^2(\\theta)}\n\\]\n```" }], },
|
||||
];
|
||||
|
||||
function expandTilde(path) {
|
||||
|
||||
@@ -6,6 +6,41 @@ import GLib from 'gi://GLib';
|
||||
import Soup from 'gi://Soup?version=3.0';
|
||||
import { fileExists } from './messages.js';
|
||||
|
||||
const PROVIDERS = { // There's this list hmm https://github.com/zukixa/cool-ai-stuff/
|
||||
'openai': {
|
||||
'name': 'OpenAI',
|
||||
'logo_name': 'openai-symbolic',
|
||||
'description': 'Official OpenAI API.\nPricing: Free for the first $5 or 3 months, whichever is less.',
|
||||
'base_url': 'https://api.openai.com/v1/chat/completions',
|
||||
'key_get_url': 'https://platform.openai.com/api-keys',
|
||||
'key_file': 'openai_key.txt',
|
||||
},
|
||||
'oxygen': {
|
||||
'name': 'Oxygen',
|
||||
'logo_name': 'ai-oxygen-symbolic',
|
||||
'description': 'An API from Tornado Softwares\nPricing: Free: 100/day\nRequires you to join their Discord for a key',
|
||||
'base_url': 'https://app.oxyapi.uk/v1/chat/completions',
|
||||
'key_get_url': 'https://discord.com/invite/kM6MaCqGKA',
|
||||
'key_file': 'oxygen_key.txt',
|
||||
},
|
||||
'zukijourney': {
|
||||
'name': 'zukijourney',
|
||||
'logo_name': 'ai-zukijourney',
|
||||
'description': 'An API from @zukixa on GitHub.\nNote: Keys are IP-locked so it\'s buggy sometimes\nPricing: Free: 10/min, 800/day.\nRequires you to join their Discord for a key',
|
||||
'base_url': 'https://zukijourney.xyzbot.net/v1/chat/completions',
|
||||
'key_get_url': 'https://discord.com/invite/Y4J6XXnmQ6',
|
||||
'key_file': 'zuki_key.txt',
|
||||
},
|
||||
'zukijourney_roleplay': {
|
||||
'name': 'zukijourney (roleplay)',
|
||||
'logo_name': 'ai-zukijourney',
|
||||
'description': 'An API from @zukixa on GitHub.\nNote: Keys are IP-locked so it\'s buggy sometimes\nPricing: Free: 10/min, 800/day.\nRequires you to join their Discord for a key',
|
||||
'base_url': 'https://zukijourney.xyzbot.net/unf/chat/completions',
|
||||
'key_get_url': 'https://discord.com/invite/Y4J6XXnmQ6',
|
||||
'key_file': 'zuki_key.txt',
|
||||
},
|
||||
}
|
||||
|
||||
// Custom prompt
|
||||
const initMessages =
|
||||
[
|
||||
@@ -21,28 +56,9 @@ const initMessages =
|
||||
{ role: "assistant", content: "## Skeuomorphism\n- A design philosophy- From early days of interface designing- Tries to imitate real-life objects- It's in fact still used by Apple in their icons until today.", },
|
||||
];
|
||||
|
||||
function expandTilde(path) {
|
||||
if (path.startsWith('~')) {
|
||||
return GLib.get_home_dir() + path.slice(1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
// We're using many models to not be restricted to 3 messages per minute.
|
||||
// The whole chat will be sent every request anyway.
|
||||
Utils.exec(`mkdir -p ${GLib.get_user_cache_dir()}/ags/user/ai`);
|
||||
const KEY_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/ai/openai_key.txt`;
|
||||
const APIDOM_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/openai_api_dom.txt`;
|
||||
function replaceapidom(URL) {
|
||||
//Utils.writeFile(URL, "/tmp/openai-url-old.log"); // For debugging
|
||||
if (fileExists(expandTilde(APIDOM_FILE_LOCATION))) {
|
||||
var contents = Utils.readFile(expandTilde(APIDOM_FILE_LOCATION)).trim();
|
||||
var URL = URL.toString().replace("api.openai.com", contents);
|
||||
}
|
||||
//Utils.writeFile(URL, "/tmp/openai-url.log"); // For debugging
|
||||
return URL;
|
||||
}
|
||||
const CHAT_MODELS = ["gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0613"]
|
||||
const ONE_CYCLE_COUNT = 3;
|
||||
|
||||
@@ -113,25 +129,33 @@ class ChatGPTService extends Service {
|
||||
'clear': [],
|
||||
'newMsg': ['int'],
|
||||
'hasKey': ['boolean'],
|
||||
'providerChanged': [],
|
||||
});
|
||||
}
|
||||
|
||||
_assistantPrompt = true;
|
||||
_messages = [];
|
||||
_cycleModels = true;
|
||||
_currentProvider = 'openai';
|
||||
_cycleModels = false;
|
||||
_requestCount = 0;
|
||||
_temperature = 0.9;
|
||||
_messages = [];
|
||||
_modelIndex = 0;
|
||||
_key = '';
|
||||
_key_file_location = `${GLib.get_user_cache_dir()}/ags/user/ai/${PROVIDERS[this._currentProvider]['key_file']}`;
|
||||
_url = GLib.Uri.parse(PROVIDERS[this._currentProvider]['base_url'], GLib.UriFlags.NONE);
|
||||
|
||||
_decoder = new TextDecoder();
|
||||
|
||||
url = GLib.Uri.parse(replaceapidom('https://api.openai.com/v1/chat/completions'), GLib.UriFlags.NONE);
|
||||
_initChecks() {
|
||||
this._key_file_location = `${GLib.get_user_cache_dir()}/ags/user/ai/${PROVIDERS[this._currentProvider]['key_file']}`;
|
||||
if (fileExists(this._key_file_location)) this._key = Utils.readFile(this._key_file_location).trim();
|
||||
else this.emit('hasKey', false);
|
||||
this._url = GLib.Uri.parse(PROVIDERS[this._currentProvider]['base_url'], GLib.UriFlags.NONE);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (fileExists(expandTilde(KEY_FILE_LOCATION))) this._key = Utils.readFile(expandTilde(KEY_FILE_LOCATION)).trim();
|
||||
else this.emit('hasKey', false);
|
||||
this._initChecks();
|
||||
|
||||
if (this._assistantPrompt) this._messages = [...initMessages];
|
||||
else this._messages = [];
|
||||
@@ -140,12 +164,20 @@ class ChatGPTService extends Service {
|
||||
}
|
||||
|
||||
get modelName() { return CHAT_MODELS[this._modelIndex] }
|
||||
get getKeyUrl() { return PROVIDERS[this._currentProvider]['key_get_url'] }
|
||||
get providerID() { return this._currentProvider }
|
||||
set providerID(value) {
|
||||
this._currentProvider = value;
|
||||
this.emit('providerChanged');
|
||||
this._initChecks();
|
||||
}
|
||||
get providers() { return PROVIDERS }
|
||||
|
||||
get keyPath() { return KEY_FILE_LOCATION }
|
||||
get keyPath() { return this._key_file_location }
|
||||
get key() { return this._key }
|
||||
set key(keyValue) {
|
||||
this._key = keyValue;
|
||||
Utils.writeFile(this._key, expandTilde(KEY_FILE_LOCATION))
|
||||
Utils.writeFile(this._key, this._key_file_location)
|
||||
.then(this.emit('hasKey', true))
|
||||
.catch(err => print(err));
|
||||
}
|
||||
@@ -197,6 +229,7 @@ class ChatGPTService extends Service {
|
||||
return;
|
||||
}
|
||||
aiResponse.addDelta(result.choices[0].delta.content);
|
||||
// print(result.choices[0])
|
||||
}
|
||||
catch {
|
||||
aiResponse.addDelta(line + '\n');
|
||||
@@ -229,7 +262,7 @@ class ChatGPTService extends Service {
|
||||
const session = new Soup.Session();
|
||||
const message = new Soup.Message({
|
||||
method: 'POST',
|
||||
uri: this.url,
|
||||
uri: this._url,
|
||||
});
|
||||
message.request_headers.append('Authorization', `Bearer ${this._key}`);
|
||||
message.set_request_body_from_bytes('application/json', new GLib.Bytes(JSON.stringify(body)));
|
||||
@@ -3,19 +3,21 @@ $suspend_cmd = systemctl suspend
|
||||
|
||||
general {
|
||||
lock_cmd = $lock_cmd
|
||||
# unlock_cmd
|
||||
before_sleep_cmd = $lock_cmd
|
||||
# after_sleep_cmd
|
||||
}
|
||||
|
||||
listener {
|
||||
timeout = 300
|
||||
timeout = 180 # 3mins
|
||||
on-timeout = $lock_cmd
|
||||
# on-resume
|
||||
}
|
||||
|
||||
listener {
|
||||
timeout = 450
|
||||
on-timeout = $suspend_cmd
|
||||
# on-resume
|
||||
timeout = 240 # 4mins
|
||||
on-timeout = hyprctl dispatch dpms off
|
||||
on-resume = hyprctl dispatch dpms on
|
||||
}
|
||||
|
||||
listener {
|
||||
timeout = 540 # 9mins
|
||||
on-timeout = $suspend_cmd
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ bindle=, XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@
|
||||
bindle=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
||||
|
||||
# Brightness
|
||||
# Uncomment these if you can't get AGS to work
|
||||
#bindle=, XF86MonBrightnessUp, exec, brightnessctl set '12.75+'
|
||||
#bindle=, XF86MonBrightnessDown, exec, brightnessctl set '12.75-'
|
||||
bindle=, XF86MonBrightnessUp, exec, ags run-js 'brightness.screen_value += 0.05;'
|
||||
bindle=, XF86MonBrightnessDown, exec, ags run-js 'brightness.screen_value -= 0.05;'
|
||||
|
||||
#################################### Applications ###################################
|
||||
# Apps: just normal apps
|
||||
@@ -38,9 +37,9 @@ bind = Control+Shift+Alt, Delete, exec, pkill wlogout || wlogout -p layer-shell
|
||||
bind = Control+Shift+Alt+Super, Delete, exec, systemctl poweroff
|
||||
|
||||
# Screenshot, Record, OCR, Color picker, Clipboard history
|
||||
bind = Super+Shift+Alt, S, exec, grim -g $SLURP_COMMAND - | swappy -f -
|
||||
bind = Super+Shift+Alt, S, exec, grim -g $(slurp $SLURP_ARGS) - | swappy -f -
|
||||
bindl=,Print,exec,grim - | wl-copy
|
||||
bind = Super+Shift, S, exec, grim -g $SLURP_COMMAND - | wl-copy
|
||||
bind = Super+Shift, S, exec, ~/.config/ags/scripts/grimblast.sh --freeze copy area
|
||||
bind = Super+Alt, R, exec, ~/.config/ags/scripts/record-script.sh
|
||||
bind = Control+Alt, R, exec, ~/.config/ags/scripts/record-script.sh --fullscreen
|
||||
bind = Super+Shift+Alt, R, exec, ~/.config/ags/scripts/record-script.sh --fullscreen-sound
|
||||
@@ -49,11 +48,11 @@ bind = Super, V, exec, pkill fuzzel || cliphist list | fuzzel --no-fuzzy --dmenu
|
||||
|
||||
# Text-to-image
|
||||
# Normal
|
||||
bind = Control+Super+Shift,S,exec,grim -g $SLURP_COMMAND "tmp.png" && tesseract "tmp.png" - | wl-copy && rm "tmp.png"
|
||||
bind = Control+Super+Shift,S,exec,grim -g $(slurp $SLURP_ARGS) "tmp.png" && tesseract "tmp.png" - | wl-copy && rm "tmp.png"
|
||||
# English
|
||||
bind = Super+Shift,T,exec,grim -g $SLURP_COMMAND "tmp.png" && tesseract -l eng "tmp.png" - | wl-copy && rm "tmp.png"
|
||||
bind = Super+Shift,T,exec,grim -g $(slurp $SLURP_ARGS) "tmp.png" && tesseract -l eng "tmp.png" - | wl-copy && rm "tmp.png"
|
||||
# Japanese
|
||||
bind = Super+Shift,J,exec,grim -g $SLURP_COMMAND "tmp.png" && tesseract -l jpn "tmp.png" - | wl-copy && rm "tmp.png"
|
||||
bind = Super+Shift,J,exec,grim -g $(slurp $SLURP_ARGS) "tmp.png" && tesseract -l jpn "tmp.png" - | wl-copy && rm "tmp.png"
|
||||
|
||||
# Media
|
||||
bindl= Super+Shift, N, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"`
|
||||
@@ -87,10 +86,10 @@ bind = Super, K, exec, ags -t 'osk'
|
||||
bind = Control+Alt, Delete, exec, ags -t 'session'
|
||||
bindle = , XF86AudioRaiseVolume, exec, ags run-js 'indicator.popup(1);'
|
||||
bindle = , XF86AudioLowerVolume, exec, ags run-js 'indicator.popup(1);'
|
||||
bindle=, XF86MonBrightnessUp, exec, ags run-js 'brightness.screen_value += 0.05; indicator.popup(1);'
|
||||
bindle=, XF86MonBrightnessDown, exec, ags run-js 'brightness.screen_value -= 0.05; indicator.popup(1);'
|
||||
bindl = , XF86AudioMute, exec, ags run-js 'indicator.popup(1);'
|
||||
bindl = Super+Shift,M, exec, ags run-js 'indicator.popup(1);'
|
||||
bindle = , XF86MonBrightnessUp, exec, ags run-js 'indicator.popup(1);'
|
||||
bindle = , XF86MonBrightnessDown, exec, ags run-js 'indicator.popup(1);'
|
||||
|
||||
###################################### Plugins #########################################
|
||||
bind = Control+Super, P, exec, hyprctl plugin load "~/.config/hypr/plugins/droidbars.so"
|
||||
@@ -100,7 +99,8 @@ bind = Control+Super, O, exec, hyprctl plugin unload "~/.config/hypr/plugins/dro
|
||||
# bind = SuperAlt, f12, exec, notify-send "Hyprland version: $(hyprctl version | head -2 | tail -1 | cut -f2 -d ' ')" "owo" -a 'Hyprland keybind'
|
||||
# bind = Super+Alt, f12, exec, notify-send "Millis since epoch" "$(date +%s%N | cut -b1-13)" -a 'Hyprland keybind'
|
||||
bind = Super+Alt, f12, exec, notify-send 'Test notification' "Here's a really long message to test truncation and wrapping\nYou can middle click or flick this notification to dismiss it!" -a 'Shell' -A "Test1=I got it!" -A "Test2=Another action"
|
||||
bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind'
|
||||
# bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind'
|
||||
bind = Super+Alt, Equal, exec, notify-send 'hmm' ${SLURP_ARGS}
|
||||
|
||||
############################ Keybinds for Hyprland ############################
|
||||
# Swap windows
|
||||
|
||||
@@ -4,7 +4,8 @@ windowrule = noblur,.* # Disables blur for windows. Substantially improves perf
|
||||
# windowrule = opacity 0.89 override 0.89 override, .* # Applies transparency to EVERY WINDOW
|
||||
windowrule = float, ^(steam)$
|
||||
windowrule = float, ^(guifetch)$ # FlafyDev/guifetch
|
||||
windowrulev2 = tile,class:(wpsoffice)
|
||||
windowrulev2 = tile,class:(wps)
|
||||
windowrulev2 = tile,class:(dev.warp.Warp)
|
||||
|
||||
|
||||
# Dialogs
|
||||
@@ -21,6 +22,9 @@ layerrule = xray 1, .*
|
||||
layerrule = noanim, selection
|
||||
layerrule = noanim, overview
|
||||
layerrule = noanim, anyrun
|
||||
layerrule = noanim, sideleft
|
||||
layerrule = noanim, sideright
|
||||
layerrule = noanim, osk
|
||||
|
||||
layerrule = blur, eww
|
||||
layerrule = ignorealpha 0.8, eww
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
$text_color = rgba(eae0e4FF)
|
||||
$entry_background_color = rgba(120F1111)
|
||||
$entry_border_color = rgba(9a8d9555)
|
||||
$entry_color = rgba(d1c2cbFF)
|
||||
$text_color = rgba(ede0deFF)
|
||||
$entry_background_color = rgba(130F0F11)
|
||||
$entry_border_color = rgba(a08c8955)
|
||||
$entry_color = rgba(d8c2bfFF)
|
||||
$font_family = Gabarito
|
||||
$font_family_clock = Gabarito
|
||||
$font_material_symbols = Material Symbols Rounded
|
||||
|
||||
background {
|
||||
color = rgba(120F1177)
|
||||
color = rgba(130F0F77)
|
||||
# path = {{ SWWW_WALL }}
|
||||
path = screenshot
|
||||
blur_size = 5
|
||||
@@ -40,7 +40,7 @@ label { # Clock
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
label {
|
||||
label { # Greeting
|
||||
monitor =
|
||||
text = hi $USER !!!
|
||||
color = $text_color
|
||||
@@ -51,7 +51,7 @@ label {
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
label {
|
||||
label { # lock icon
|
||||
monitor =
|
||||
text = lock
|
||||
color = $text_color
|
||||
@@ -62,7 +62,7 @@ label {
|
||||
halign = center
|
||||
valign = bottom
|
||||
}
|
||||
label {
|
||||
label { # "locked" text
|
||||
monitor =
|
||||
text = locked
|
||||
color = $text_color
|
||||
@@ -72,4 +72,16 @@ label {
|
||||
position = 0, 50
|
||||
halign = center
|
||||
valign = bottom
|
||||
}
|
||||
|
||||
label { # Status
|
||||
monitor =
|
||||
text = cmd[update:5000] ~/.config/hypr/hyprlock/status.sh
|
||||
color = $text_color
|
||||
font_size = 14
|
||||
font_family = $font_family
|
||||
|
||||
position = 30, -30
|
||||
halign = left
|
||||
valign = top
|
||||
}
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
############ Variables ############
|
||||
enable_battery=false
|
||||
|
||||
####### Check availability ########
|
||||
for battery in /sys/class/power_supply/*BAT*; do
|
||||
if [[ -f "$battery/uevent" ]]; then
|
||||
enable_battery=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
############# Output #############
|
||||
if [[ $enable_battery == true ]]; then
|
||||
if [[ $(cat /sys/class/power_supply/*/status | head -1) == "Charging" ]]; then
|
||||
echo -n "(+) "
|
||||
fi
|
||||
echo -n "$(cat /sys/class/power_supply/*/capacity | head -1)"% remaining
|
||||
fi
|
||||
|
||||
echo ''
|
||||
@@ -29,12 +29,19 @@
|
||||
bash <(curl -s "https://end-4.github.io/dots-hyprland-wiki/setup.sh")
|
||||
```
|
||||
- Manual installation, other distros and more:
|
||||
- See the [Wiki](https://end-4.github.io/dots-hyprland-wiki/en/i-i/01setup/).
|
||||
- If you'd like to suggest fixes or maybe a new widget, feel free to [open an issue](https://github.com/end-4/dots-hyprland/issues/new/choose)!
|
||||
- See the [Wiki](https://end-4.github.io/dots-hyprland-wiki/en/i-i/01setup/)
|
||||
- (_Available in: English, Vietnamese, and Simplified Chinese. Translations are welcome._)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Help improve these dotfiles!</summary>
|
||||
|
||||
- Join the [discussions](https://github.com/end-4/dots-hyprland/discussions)
|
||||
- If you'd like to suggest fixes or a new widget, feel free to [open an issue](https://github.com/end-4/dots-hyprland/issues/new/choose)
|
||||
</details>
|
||||
|
||||
### [illogical_impulse](https://github.com/end-4/dots-hyprland/tree/illogical-impulse)
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
+19
-2
@@ -119,11 +119,28 @@ if $(test -d /usr/local/share/icons/OneUI); then
|
||||
else ask_OneUI=true
|
||||
fi
|
||||
if $ask_OneUI;then showfun install-OneUI;v install-OneUI;fi
|
||||
|
||||
if $(test -d /usr/local/share/icons/Bibata-Modern-Classic); then
|
||||
echo -e "\e[33m[$0]: Cursor theme \"Bibata-Modern-Classic\" already exists, no need to install.\e[0m"
|
||||
echo -e "\e[34mYou can reinstall it in order to update to the latest version anyway.\e[0m"
|
||||
ask_bibata=$ask
|
||||
else ask_bibata=true
|
||||
fi
|
||||
if $ask_bibata;then showfun install-bibata;v install-bibata;fi
|
||||
|
||||
if command -v LaTeX >/dev/null 2>&1;then
|
||||
echo -e "\e[33m[$0]: Program \"MicroTeX\" already exists, no need to install.\e[0m"
|
||||
echo -e "\e[34mYou can reinstall it in order to update to the latest version anyway.\e[0m"
|
||||
ask_MicroTeX=$ask
|
||||
else ask_MicroTeX=true
|
||||
fi
|
||||
if $ask_MicroTeX;then showfun install-MicroTeX;v install-MicroTeX;fi
|
||||
|
||||
#####################################################################################
|
||||
printf "\e[36m[$0]: 3. Copying\e[97m\n"
|
||||
|
||||
# In case ~/.local/bin does not exists
|
||||
v mkdir -p "$HOME/.local/bin" "$HOME/.local/share"
|
||||
# In case some folders does not exists
|
||||
v mkdir -p "$HOME"/.{config,cache,local/{bin,share}}
|
||||
|
||||
# `--delete' for rsync to make sure that
|
||||
# original dotfiles and new ones in the SAME DIRECTORY
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
### PKGs on same line will be send to `yay` together, unless `-f` is specified.
|
||||
|
||||
### Basic
|
||||
coreutils cliphist curl fuzzel rsync wget ripgrep gojq npm meson typescript gjs sassc
|
||||
coreutils cliphist cmake curl fuzzel rsync wget ripgrep gojq npm meson typescript gjs dart-sass axel
|
||||
|
||||
# Make deps of MicroTeX
|
||||
tinyxml2 gtkmm3 gtksourceviewmm cairomm
|
||||
|
||||
### Python
|
||||
# Add `python-setuptools-scm` and `python-wheel` explicitly to fix #197
|
||||
|
||||
@@ -73,3 +73,31 @@ install-OneUI (){
|
||||
x sudo cp -r OneUI-light /usr/local/share/icons
|
||||
x cd $base
|
||||
}
|
||||
|
||||
install-bibata (){
|
||||
x mkdir -p $base/cache/bibata-cursor
|
||||
x cd $base/cache/bibata-cursor
|
||||
name="Bibata-Modern-Classic"
|
||||
file="$name.tar.xz"
|
||||
# Use axel because `curl -O` always downloads a file with 0 byte size, idk why
|
||||
x axel https://github.com/ful1e5/Bibata_Cursor/releases/latest/download/$file
|
||||
tar -xf $file
|
||||
x sudo mkdir -p /usr/local/share/icons
|
||||
x sudo cp -r $name /usr/local/share/icons
|
||||
x cd $base
|
||||
}
|
||||
|
||||
install-MicroTeX (){
|
||||
x mkdir -p $base/cache/MicroTeX
|
||||
x cd $base/cache/MicroTeX
|
||||
try git init -b master
|
||||
try git remote add origin https://github.com/NanoMichael/MicroTeX.git
|
||||
x git pull origin master && git submodule update --init --recursive
|
||||
x mkdir -p build
|
||||
x cd build
|
||||
x cmake ..
|
||||
x make -j32
|
||||
x sudo mkdir -p /usr/local/bin
|
||||
x sudo cp ./LaTeX /usr/local/bin/
|
||||
x cd $base
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user