Sync my_config with end-4

This commit is contained in:
midn8hustlr
2024-02-29 18:32:10 +05:30
32 changed files with 934 additions and 244 deletions
+1
View File
@@ -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

+3 -3
View File
@@ -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}
\`\`\`
`;
+69 -33
View File
@@ -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(),
]
})
});
+1 -1
View File
@@ -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);
}
+158 -63
View File
@@ -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());
})
}
})
]
});
+53 -50
View File
@@ -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 -6
View File
@@ -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
+278
View File
@@ -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
}
+1 -1
View File
@@ -167,7 +167,7 @@
}
.separator-line {
background-color: $outline;
background-color: mix($subtext, $surface, 50%);
min-width: 0.068rem;
min-height: 0.068rem;
}
+2 -1
View File
@@ -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 {
+22
View File
@@ -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;
+5 -1
View File
@@ -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)));
+9 -7
View File
@@ -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
}
+10 -10
View File
@@ -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
+5 -1
View File
@@ -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
+20 -8
View File
@@ -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
}
+22
View File
@@ -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 ''
+10 -3
View File
@@ -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)
![image](https://github.com/end-4/dots-hyprland/assets/97237370/abd05702-a248-4f53-ac82-500219aa19f1)
![image](https://github.com/end-4/dots-hyprland/assets/97237370/90c13b64-cde3-4363-9716-718d35845d95)
![image](https://github.com/end-4/dots-hyprland/assets/97237370/9e7adedd-fae8-4cc8-9c81-d7ad489d7559)
![image](https://github.com/end-4/dots-hyprland/assets/97237370/354431f6-8939-487f-9292-0bac71cf9ca8)
![image](https://github.com/end-4/dots-hyprland/assets/97237370/98fe2c03-a128-45c0-8155-3a6080db3b84)
+19 -2
View File
@@ -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
+4 -1
View File
@@ -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
+28
View File
@@ -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
}