forked from Shinonome/dots-hyprland
Compare commits
62 Commits
2025-05-13
...
ii-ags
| Author | SHA1 | Date | |
|---|---|---|---|
| 1703d9bdad | |||
| f225ec1975 | |||
| 2020e13a05 | |||
| 506fb857aa | |||
| d96abe7a4d | |||
| 181ac0561d | |||
| d00a21ac56 | |||
| 0ea31737ea | |||
| 341c6be9be | |||
| 66c95231a7 | |||
| 077f52f6b8 | |||
| 56bb53ed95 | |||
| 663c3483be | |||
| b5ac985b7d | |||
| 90ea701797 | |||
| d4ad68f8c6 | |||
| 1af166ef7c | |||
| 7593938986 | |||
| e2e6604d16 | |||
| 7e5610a9e1 | |||
| e5b920550b | |||
| ea8f06b632 | |||
| e08230cf69 | |||
| 623fd80a54 | |||
| 277162f4d4 | |||
| f61da8e09a | |||
| b1a0e3c258 | |||
| 2f0a0b88e2 | |||
| f1cee49494 | |||
| e9485f0b8a | |||
| adce55865e | |||
| d12ada5222 | |||
| c6ff825aa5 | |||
| db67398c97 | |||
| 9cce9edf17 | |||
| 5afc4bc41e | |||
| b3e339c60f | |||
| cfbf18f564 | |||
| a29103d639 | |||
| 51b285b831 | |||
| 24276cdf93 | |||
| e85822c811 | |||
| 13cb540e49 | |||
| cfe48fb0a1 | |||
| fbe6c8733b | |||
| f1b6789b15 | |||
| a65363c60f | |||
| d4603c6b8a | |||
| a149abf9fe | |||
| 0e2252995c | |||
| d8dc1c7d69 | |||
| 086451951a | |||
| 6281c3a23c | |||
| d365ede358 | |||
| 7428da2552 | |||
| 09696d9fdb | |||
| feca4c6256 | |||
| def2d6f383 | |||
| 8c62520666 | |||
| a544f09114 | |||
| cd9167344f | |||
| 1a2284234a |
@@ -39,12 +39,12 @@ function guessMessageType(summary) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processNotificationBody(body, appEntry) {
|
function processNotificationBody(body, appEntry) {
|
||||||
// Only process Chrome/Chromium notifications
|
let processedBody = body;
|
||||||
if (appEntry?.toLowerCase().includes('chrome')) {
|
if (appEntry?.toLowerCase().includes('chrome')) {
|
||||||
// Remove the first line
|
processedBody = body.split('\n\n').slice(1).join('\n\n');
|
||||||
return body.split('\n\n').slice(1).join('\n\n');
|
|
||||||
}
|
}
|
||||||
return body;
|
processedBody = processedBody.replace(/<[^>]*>/g, '');
|
||||||
|
return processedBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFriendlyNotifTimeString = (timeObject) => {
|
const getFriendlyNotifTimeString = (timeObject) => {
|
||||||
|
|||||||
@@ -199,6 +199,7 @@
|
|||||||
"firefox",
|
"firefox",
|
||||||
"org.gnome.Nautilus"
|
"org.gnome.Nautilus"
|
||||||
],
|
],
|
||||||
|
"ignoredAppsRegex": [],
|
||||||
"layer": "top",
|
"layer": "top",
|
||||||
"monitorExclusivity": true, // Dock will move to other monitor along with focus if enabled
|
"monitorExclusivity": true, // Dock will move to other monitor along with focus if enabled
|
||||||
"searchPinnedAppIcons": false, // Try to search for the correct icon if the app class isn't an icon name
|
"searchPinnedAppIcons": false, // Try to search for the correct icon if the app class isn't an icon name
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const { GLib } = imports.gi;
|
|||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||||
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
|
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
|
||||||
const { Box, Button, EventBox, Label, Overlay, Revealer, Scrollable } = Widget;
|
const { Box, Button, EventBox, Label, Overlay, Revealer } = Widget;
|
||||||
const { execAsync, exec } = Utils;
|
const { execAsync, exec } = Utils;
|
||||||
import { AnimatedCircProg } from "../../.commonwidgets/cairo_circularprogress.js";
|
import { AnimatedCircProg } from "../../.commonwidgets/cairo_circularprogress.js";
|
||||||
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
||||||
@@ -136,8 +136,8 @@ export default () => {
|
|||||||
setup: (self) => self.hook(Mpris, label => {
|
setup: (self) => self.hook(Mpris, label => {
|
||||||
const mpris = Mpris.getPlayer('');
|
const mpris = Mpris.getPlayer('');
|
||||||
if (!mpris) return;
|
if (!mpris) return;
|
||||||
label.toggleClassName('bar-music-playstate-playing', mpris !== null && mpris.playBackStatus == 'Playing');
|
label.toggleClassName('bar-music-playstate-playing', mpris.playBackStatus == 'Playing');
|
||||||
label.toggleClassName('bar-music-playstate', mpris !== null || mpris.playBackStatus == 'Paused');
|
label.toggleClassName('bar-music-playstate', mpris.playBackStatus == 'Paused');
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
overlays: [
|
overlays: [
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const periodicTable = [
|
|||||||
{ name: 'Argon', symbol: 'Ar', number: 18, weight: 39.95, type: 'noblegas' },
|
{ name: 'Argon', symbol: 'Ar', number: 18, weight: 39.95, type: 'noblegas' },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ name: 'Kalium', symbol: 'K', number: 19, weight: 39.098, type: 'metal' },
|
{ name: 'Potassium', symbol: 'K', number: 19, weight: 39.098, type: 'metal' },
|
||||||
{ name: 'Calcium', symbol: 'Ca', number: 20, weight: 40.078, type: 'metal' },
|
{ name: 'Calcium', symbol: 'Ca', number: 20, weight: 40.078, type: 'metal' },
|
||||||
{ name: 'Scandium', symbol: 'Sc', number: 21, weight: 44.956, type: 'metal' },
|
{ name: 'Scandium', symbol: 'Sc', number: 21, weight: 44.956, type: 'metal' },
|
||||||
{ name: 'Titanium', symbol: 'Ti', number: 22, weight: 47.87, type: 'metal' },
|
{ name: 'Titanium', symbol: 'Ti', number: 22, weight: 47.87, type: 'metal' },
|
||||||
|
|||||||
@@ -119,6 +119,21 @@ const Taskbar = (monitor) => Widget.Box({
|
|||||||
const client = Hyprland.clients[i];
|
const client = Hyprland.clients[i];
|
||||||
if (client["pid"] == -1) return;
|
if (client["pid"] == -1) return;
|
||||||
const appClass = substitute(client.class);
|
const appClass = substitute(client.class);
|
||||||
|
const ignoredAppsRegex = userOptions.dock.ignoredAppsRegex || [];
|
||||||
|
let isIgnored = false;
|
||||||
|
|
||||||
|
for (const regex of ignoredAppsRegex) {
|
||||||
|
try {
|
||||||
|
const pattern = new RegExp(regex);
|
||||||
|
if (pattern.test(appClass)) {
|
||||||
|
isIgnored = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIgnored) continue;
|
||||||
|
|
||||||
// for (const appName of userOptions.dock.pinnedApps) {
|
// for (const appName of userOptions.dock.pinnedApps) {
|
||||||
// if (appClass.includes(appName.toLowerCase()))
|
// if (appClass.includes(appName.toLowerCase()))
|
||||||
// return null;
|
// return null;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import Indicator from '../../services/indicator.js';
|
|||||||
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
|
import { MaterialIcon } from '../.commonwidgets/materialicon.js';
|
||||||
|
|
||||||
const OsdValue = ({
|
const OsdValue = ({
|
||||||
name, icon, nameSetup = undefined, labelSetup, progressSetup,
|
name, icon, nameSetup = undefined, labelSetup, progressSetup, iconSetup,
|
||||||
extraClassName = '', extraProgressClassName = '',
|
extraClassName = '', extraProgressClassName = '',
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
@@ -31,7 +31,7 @@ const OsdValue = ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
MaterialIcon(icon, 'hugeass', {vpack: 'center'}),
|
MaterialIcon(icon, 'hugeass', {vpack: 'center', setup: iconSetup}),
|
||||||
Box({
|
Box({
|
||||||
vertical: true,
|
vertical: true,
|
||||||
className: 'spacing-v-5',
|
className: 'spacing-v-5',
|
||||||
@@ -74,7 +74,6 @@ export default (monitor = 0) => {
|
|||||||
|
|
||||||
const volumeIndicator = OsdValue({
|
const volumeIndicator = OsdValue({
|
||||||
name: 'Volume',
|
name: 'Volume',
|
||||||
icon: 'volume_up',
|
|
||||||
extraClassName: 'osd-volume',
|
extraClassName: 'osd-volume',
|
||||||
extraProgressClassName: 'osd-volume-progress',
|
extraProgressClassName: 'osd-volume-progress',
|
||||||
attribute: { headphones: undefined , device: undefined},
|
attribute: { headphones: undefined , device: undefined},
|
||||||
@@ -93,7 +92,9 @@ export default (monitor = 0) => {
|
|||||||
}),
|
}),
|
||||||
labelSetup: (self) => self.hook(Audio, (label) => {
|
labelSetup: (self) => self.hook(Audio, (label) => {
|
||||||
const newDevice = (Audio.speaker?.name);
|
const newDevice = (Audio.speaker?.name);
|
||||||
const updateValue = Math.round(Audio.speaker?.volume * 100);
|
const updateValue = Audio.speaker?.stream?.isMuted
|
||||||
|
? 0
|
||||||
|
: Math.round(Audio.speaker?.volume * 100);
|
||||||
if (!isNaN(updateValue)) {
|
if (!isNaN(updateValue)) {
|
||||||
if (newDevice === volumeIndicator.attribute.device && updateValue != label.label) {
|
if (newDevice === volumeIndicator.attribute.device && updateValue != label.label) {
|
||||||
Indicator.popup(1);
|
Indicator.popup(1);
|
||||||
@@ -103,12 +104,20 @@ export default (monitor = 0) => {
|
|||||||
label.label = `${updateValue}`;
|
label.label = `${updateValue}`;
|
||||||
}),
|
}),
|
||||||
progressSetup: (self) => self.hook(Audio, (progress) => {
|
progressSetup: (self) => self.hook(Audio, (progress) => {
|
||||||
const updateValue = Audio.speaker?.volume;
|
const updateValue = Audio.speaker?.stream?.isMuted
|
||||||
|
? 0
|
||||||
|
: Audio.speaker?.volume;
|
||||||
if (!isNaN(updateValue)) {
|
if (!isNaN(updateValue)) {
|
||||||
if (updateValue > 1) progress.value = 1;
|
if (updateValue > 1) progress.value = 1;
|
||||||
else progress.value = updateValue;
|
else progress.value = updateValue;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
iconSetup: (self) => self.hook(Audio, (progress) => {
|
||||||
|
self.label =
|
||||||
|
Audio.speaker?.stream?.isMuted || !Audio.speaker.volume
|
||||||
|
? 'volume_off'
|
||||||
|
: 'volume_up';
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
return MarginRevealer({
|
return MarginRevealer({
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ var lastCoverPath = '';
|
|||||||
function isRealPlayer(player) {
|
function isRealPlayer(player) {
|
||||||
return (
|
return (
|
||||||
// Remove unecessary native buses from browsers if there's plasma integration
|
// Remove unecessary native buses from browsers if there's plasma integration
|
||||||
!(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
// !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
||||||
!(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
// !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
||||||
// playerctld just copies other buses and we don't need duplicates
|
// playerctld just copies other buses and we don't need duplicates
|
||||||
!player.busName.startsWith('org.mpris.MediaPlayer2.playerctld') &&
|
!player.busName.startsWith('org.mpris.MediaPlayer2.playerctld') &&
|
||||||
// Non-instance mpd bus
|
// Non-instance mpd bus
|
||||||
@@ -209,8 +209,14 @@ const CoverArt = ({ player, ...rest }) => {
|
|||||||
execAsync(['bash', '-c',
|
execAsync(['bash', '-c',
|
||||||
`${App.configDir}/scripts/color_generation/generate_colors_material.py --path '${coverPath}' --mode ${darkMode.value ? 'dark' : 'light'} > ${GLib.get_user_state_dir()}/ags/scss/_musicmaterial.scss`])
|
`${App.configDir}/scripts/color_generation/generate_colors_material.py --path '${coverPath}' --mode ${darkMode.value ? 'dark' : 'light'} > ${GLib.get_user_state_dir()}/ags/scss/_musicmaterial.scss`])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
exec(`${App.configDir}/scripts/color_generation/pywal.sh -i "${player.coverPath}" -n -t -s -e -q ${darkMode.value ? '' : '-l'}`)
|
const dominantColor = `#${Utils.exec(`sh -c "magick '${coverPath}' -scale 1x1\\! -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info: | sed 's/,/\\n/g' | xargs -L 1 printf '%02x' ; echo"`)}`
|
||||||
exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss`);
|
// exec(`${App.configDir}/scripts/color_generation/pywal.sh -i "${player.coverPath}" -n -t -s -e -q ${darkMode.value ? '' : '-l'}`)
|
||||||
|
// exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss`);
|
||||||
|
exec(`cp '${App.configDir}/scripts/templates/wal/_musicwal.scss' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`);
|
||||||
|
exec(`sed -i 's/{{dominantColor}}/${dominantColor}/g' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`)
|
||||||
|
exec(`sed -i 's/{{backgroundColor}}/${darkMode.value ? "#0E1415" : "#EEF4F4"}/g' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`)
|
||||||
|
exec(`sed -i 's/{{foregroundColor}}/${darkMode.value ? "#EEF4F4" : "#0E1415"}/g' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`)
|
||||||
|
|
||||||
exec(`sass -I "${GLib.get_user_state_dir()}/ags/scss" -I "${App.configDir}/scss/fallback" "${App.configDir}/scss/_music.scss" "${stylePath}"`);
|
exec(`sass -I "${GLib.get_user_state_dir()}/ags/scss" -I "${App.configDir}/scss/fallback" "${App.configDir}/scss/_music.scss" "${stylePath}"`);
|
||||||
Utils.timeout(200, () => {
|
Utils.timeout(200, () => {
|
||||||
// self.attribute.showImage(self, coverPath)
|
// self.attribute.showImage(self, coverPath)
|
||||||
|
|||||||
@@ -49,11 +49,6 @@ export function launchCustomCommand(command) {
|
|||||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print))
|
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print))
|
||||||
.catch(print);
|
.catch(print);
|
||||||
}
|
}
|
||||||
else if (args[0] == '>pywal') { // Use Pywal (ik it looks shit but I'm not removing)
|
|
||||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_state_dir()}/ags/user && echo "pywal" > ${GLib.get_user_state_dir()}/ags/user/colorbackend.txt`]).catch(print)
|
|
||||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print))
|
|
||||||
.catch(print);
|
|
||||||
}
|
|
||||||
else if (args[0] == '>todo') { // Todo
|
else if (args[0] == '>todo') { // Todo
|
||||||
Todo.add(args.slice(1).join(' '));
|
Todo.add(args.slice(1).join(' '));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,19 +49,16 @@ get_light_dark() {
|
|||||||
|
|
||||||
apply_fuzzel() {
|
apply_fuzzel() {
|
||||||
# Check if template exists
|
# Check if template exists
|
||||||
if [ ! -f "scripts/templates/fuzzel/fuzzel.ini" ]; then
|
if [ ! -f "scripts/templates/fuzzel/fuzzel.theme" ]; then
|
||||||
echo "Template file not found for Fuzzel. Skipping that."
|
echo "Template file not found for Fuzzel. Skipping that."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
# Copy template
|
# Copy template
|
||||||
mkdir -p "$CACHE_DIR"/user/generated/fuzzel
|
cp "scripts/templates/fuzzel/fuzzel.theme" "$XDG_CONFIG_HOME"/fuzzel/fuzzel.theme
|
||||||
cp "scripts/templates/fuzzel/fuzzel.ini" "$CACHE_DIR"/user/generated/fuzzel/fuzzel.ini
|
|
||||||
# Apply colors
|
# Apply colors
|
||||||
for i in "${!colorlist[@]}"; do
|
for i in "${!colorlist[@]}"; do
|
||||||
sed -i "s/{{ ${colorlist[$i]} }}/${colorvalues[$i]#\#}/g" "$CACHE_DIR"/user/generated/fuzzel/fuzzel.ini
|
sed -i "s/{{ ${colorlist[$i]} }}/${colorvalues[$i]#\#}/g" "$XDG_CONFIG_HOME"/fuzzel/fuzzel.theme
|
||||||
done
|
done
|
||||||
|
|
||||||
cp "$CACHE_DIR"/user/generated/fuzzel/fuzzel.ini "$XDG_CONFIG_HOME"/fuzzel/fuzzel.ini
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply_term() {
|
apply_term() {
|
||||||
|
|||||||
@@ -1,7 +1,56 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
|
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||||
|
XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
|
||||||
CONFIG_DIR="$XDG_CONFIG_HOME/ags"
|
CONFIG_DIR="$XDG_CONFIG_HOME/ags"
|
||||||
|
CACHE_DIR="$XDG_CACHE_HOME/ags"
|
||||||
|
|
||||||
|
CUSTOM_DIR="$XDG_CONFIG_HOME/hypr/custom"
|
||||||
|
RESTORE_SCRIPT_DIR="$CUSTOM_DIR/scripts"
|
||||||
|
RESTORE_SCRIPT="$RESTORE_SCRIPT_DIR/__restore_video_wallpaper.sh"
|
||||||
|
|
||||||
|
VIDEO_OPTS="no-audio loop hwdec=auto scale=bilinear interpolation=no video-sync=display-resample panscan=1.0 video-scale-x=1.0 video-scale-y=1.0 video-align-x=0.5 video-align-y=0.5"
|
||||||
|
|
||||||
|
mkdir -p "$RESTORE_SCRIPT_DIR"
|
||||||
|
|
||||||
|
is_video() {
|
||||||
|
local extension="${1##*.}"
|
||||||
|
[[ "$extension" == "mp4" || "$extension" == "mkv" || "$extension" == "webm" ]] && return 0 || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
kill_existing_mpvpaper() {
|
||||||
|
# Abort all mpvpapers' instance
|
||||||
|
pkill -f -9 mpvpaper || true
|
||||||
|
}
|
||||||
|
|
||||||
|
create_restore_script() {
|
||||||
|
local video_path=$1
|
||||||
|
|
||||||
|
cat > "$RESTORE_SCRIPT.tmp" << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
# Generated by switchwall.sh - Don't modify it by yourself.
|
||||||
|
# Time: $(date)
|
||||||
|
|
||||||
|
pkill -f -9 mpvpaper
|
||||||
|
|
||||||
|
for monitor in \$(hyprctl monitors -j | jq -r '.[] | .name'); do
|
||||||
|
mpvpaper -o "$VIDEO_OPTS" "\$monitor" "$video_path" &
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mv "$RESTORE_SCRIPT.tmp" "$RESTORE_SCRIPT"
|
||||||
|
chmod +x "$RESTORE_SCRIPT"
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_restore() {
|
||||||
|
cat > "$RESTORE_SCRIPT.tmp" << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
# The content of this script will be generated by switchwall.sh - Don't modify it by yourself.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mv "$RESTORE_SCRIPT.tmp" "$RESTORE_SCRIPT"
|
||||||
|
}
|
||||||
|
|
||||||
switch() {
|
switch() {
|
||||||
imgpath=$1
|
imgpath=$1
|
||||||
@@ -17,24 +66,73 @@ switch() {
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# agsv1 run-js "wallpaper.set('')"
|
kill_existing_mpvpaper
|
||||||
# sleep 0.1 && agsv1 run-js "wallpaper.set('${imgpath}')" &
|
|
||||||
swww img "$imgpath" --transition-step 100 --transition-fps 120 \
|
if is_video "$imgpath"; then
|
||||||
--transition-type grow --transition-angle 30 --transition-duration 1 \
|
missing_deps=()
|
||||||
--transition-pos "$cursorposx, $cursorposy_inverted"
|
if ! command -v mpvpaper &> /dev/null; then
|
||||||
|
missing_deps+=("mpvpaper")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v ffmpeg &> /dev/null; then
|
||||||
|
missing_deps+=("ffmpeg")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#missing_deps[@]} -gt 0 ]; then
|
||||||
|
echo "Missing deps: ${missing_deps[*]}"
|
||||||
|
echo "Arch: "
|
||||||
|
echo " yay -S ${missing_deps[*]}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local video_path=$1
|
||||||
|
|
||||||
|
monitors=$(hyprctl monitors -j | jq -r '.[] | .name')
|
||||||
|
|
||||||
|
for monitor in $monitors; do
|
||||||
|
mpvpaper -o "$VIDEO_OPTS" "$monitor" "$video_path" &
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
# We take the first frame of video to colorgen and swww
|
||||||
|
thumbnail="$CACHE_DIR"/user/generated/mpvpaper_thumbnail.jpg
|
||||||
|
ffmpeg -y -i "$imgpath" -vframes 1 "$thumbnail" 2>/dev/null
|
||||||
|
|
||||||
|
if [ -f "$thumbnail" ]; then
|
||||||
|
# Apply swww wallpaper using the thumbnail
|
||||||
|
swww img "$thumbnail" --transition-step 100 --transition-fps 120 \
|
||||||
|
--transition-type grow --transition-angle 30 --transition-duration 1 \
|
||||||
|
--transition-pos "$cursorposx, $cursorposy_inverted"
|
||||||
|
"$CONFIG_DIR"/scripts/color_generation/colorgen.sh "$thumbnail" --apply --smart
|
||||||
|
|
||||||
|
create_restore_script "$video_path"
|
||||||
|
else
|
||||||
|
echo "Cannot create image to colorgen"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# agsv1 run-js "wallpaper.set('')"
|
||||||
|
# sleep 0.1 && agsv1 run-js "wallpaper.set('${imgpath}')" &
|
||||||
|
swww img "$imgpath" --transition-step 100 --transition-fps 120 \
|
||||||
|
--transition-type grow --transition-angle 30 --transition-duration 1 \
|
||||||
|
--transition-pos "$cursorposx, $cursorposy_inverted"
|
||||||
|
|
||||||
|
"$CONFIG_DIR"/scripts/color_generation/colorgen.sh "$imgpath" --apply --smart
|
||||||
|
remove_restore
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ "$1" == "--noswitch" ]; then
|
if [ "$1" == "--noswitch" ]; then
|
||||||
imgpath=$(swww query | awk -F 'image: ' '{print $2}')
|
if pgrep -f mpvpaper > /dev/null; then
|
||||||
# imgpath=$(agsv1 run-js 'wallpaper.get(0)')
|
imgpath=$(ps -eo cmd | grep mpvpaper | grep -v grep | awk '{for(i=NF;i>0;i--) if($i!~/^-/) {print $i; break}}')
|
||||||
|
else
|
||||||
|
imgpath=$(swww query | awk -F 'image: ' '{print $2}')
|
||||||
|
# imgpath=$(agsv1 run-js 'wallpaper.get(0)')
|
||||||
|
fi
|
||||||
elif [[ "$1" ]]; then
|
elif [[ "$1" ]]; then
|
||||||
switch "$1"
|
switch "$1"
|
||||||
else
|
else
|
||||||
# Select and set image (hyprland)
|
# Select and set image (hyprland)
|
||||||
|
|
||||||
cd "$(xdg-user-dir PICTURES)/Wallpapers" || cd "$(xdg-user-dir PICTURES)" || return 1
|
cd "$(xdg-user-dir PICTURES)/Wallpapers" || cd "$(xdg-user-dir PICTURES)" || return 1
|
||||||
switch "$(yad --width 1200 --height 800 --file --add-preview --large-preview --title='Choose wallpaper')"
|
switch "$(yad --width 1200 --height 800 --file --add-preview --large-preview --title='Choose wallpaper')"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate colors for ags n stuff
|
|
||||||
"$CONFIG_DIR"/scripts/color_generation/colorgen.sh "${imgpath}" --apply --smart
|
|
||||||
|
|||||||
-13
@@ -1,8 +1,3 @@
|
|||||||
font=Gabarito
|
|
||||||
terminal=foot -e
|
|
||||||
prompt=">> "
|
|
||||||
layer=overlay
|
|
||||||
|
|
||||||
[colors]
|
[colors]
|
||||||
background={{ $background }}ff
|
background={{ $background }}ff
|
||||||
text={{ $onBackground }}ff
|
text={{ $onBackground }}ff
|
||||||
@@ -11,11 +6,3 @@ selection-text={{ $onSurfaceVariant }}ff
|
|||||||
border={{ $surfaceVariant }}dd
|
border={{ $surfaceVariant }}dd
|
||||||
match={{ $primary }}ff
|
match={{ $primary }}ff
|
||||||
selection-match={{ $primary }}ff
|
selection-match={{ $primary }}ff
|
||||||
|
|
||||||
|
|
||||||
[border]
|
|
||||||
radius=17
|
|
||||||
width=1
|
|
||||||
|
|
||||||
[dmenu]
|
|
||||||
exit-immediately-if-empty=yes
|
|
||||||
@@ -20,8 +20,11 @@ $secondaryContainer: transparentize(mix(mix($background, $color2, 50%), $color6,
|
|||||||
$onSecondaryContainer: mix($color7, $color2, 90%);
|
$onSecondaryContainer: mix($color7, $color2, 90%);
|
||||||
@if $darkmode == False {
|
@if $darkmode == False {
|
||||||
$onSecondaryContainer: mix($onSecondaryContainer, black, 50%);
|
$onSecondaryContainer: mix($onSecondaryContainer, black, 50%);
|
||||||
|
} @else {
|
||||||
|
$onSecondaryContainer: mix($onSecondaryContainer, white, 50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.osd-music {
|
.osd-music {
|
||||||
@include menu_decel;
|
@include menu_decel;
|
||||||
@include elevation2;
|
@include elevation2;
|
||||||
|
|||||||
@@ -90,27 +90,44 @@ class BrightnessDdcService extends BrightnessServiceBase {
|
|||||||
async function listDdcMonitorsSnBus() {
|
async function listDdcMonitorsSnBus() {
|
||||||
let ddcSnBus = {};
|
let ddcSnBus = {};
|
||||||
try {
|
try {
|
||||||
const out = await Utils.execAsync('ddcutil detect --brief');
|
// Its' better not to use --brief. This way if a serial number is not
|
||||||
|
// found we can still use the binary serial number as an alternative
|
||||||
|
const out = await Utils.execAsync('ddcutil detect');
|
||||||
const displays = out.split('\n\n');
|
const displays = out.split('\n\n');
|
||||||
displays.forEach(display => {
|
displays.forEach(display => {
|
||||||
const reg = /^Display \d+/;
|
const reg = /[Dd]isplay/;
|
||||||
if (!reg.test(display))
|
if (!reg.test(display)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
const lines = display.split('\n');
|
const lines = display.split('\n');
|
||||||
let sn, busNum;
|
let sn, busNum;
|
||||||
|
let unresponsive = false;
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
line = line.trim()
|
line = line.trim()
|
||||||
if (line.startsWith('Monitor:')) {
|
|
||||||
sn = line.split(':')[3];
|
// Sometimes ddcutils will report a DP monitor twice, one of the
|
||||||
|
// two copies of the monitor will "not support DDC/CI". Just ignore it
|
||||||
|
// See https://www.ddcutil.com/faq/#duplicate_displayport
|
||||||
|
if (line.includes('unresponsive')) {
|
||||||
|
unresponsive = true;
|
||||||
|
}
|
||||||
|
if (line.startsWith('Serial')) {
|
||||||
|
sn = line.split(':')[1].trim();
|
||||||
|
// Sometimes sn can be empty. In this cases let's relay on binary sn
|
||||||
|
} else if (line.startsWith('Binary') && !sn) {
|
||||||
|
// Make the serial number upper case except for the leading '0x' since Hyprland
|
||||||
|
// seems to use upper case for the rest of the string and ddcutil uses
|
||||||
|
// lower case for all the binary sn
|
||||||
|
sn = '0x'+line.split('(')[1].slice(2,-1).toUpperCase();
|
||||||
} else if (line.startsWith('I2C bus:')) {
|
} else if (line.startsWith('I2C bus:')) {
|
||||||
busNum = line.split('/dev/i2c-')[1];
|
busNum = line.split('/dev/i2c-')[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sn && busNum)
|
if (sn && busNum && !unresponsive){
|
||||||
ddcSnBus[sn] = busNum;
|
ddcSnBus[sn] = busNum;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
print(err);
|
|
||||||
}
|
}
|
||||||
return ddcSnBus;
|
return ddcSnBus;
|
||||||
}
|
}
|
||||||
@@ -133,10 +150,12 @@ for (let i = 0; i < service.length; i++) {
|
|||||||
service[i] = new BrightnessDdcService(ddcSnBus[monitorSn]);
|
service[i] = new BrightnessDdcService(ddcSnBus[monitorSn]);
|
||||||
break;
|
break;
|
||||||
case "auto":
|
case "auto":
|
||||||
if (monitorSn in ddcSnBus && !!exec(`bash -c 'command -v ddcutil'`))
|
if (monitorSn in ddcSnBus && !!exec(`bash -c 'command -v ddcutil'`)){
|
||||||
service[i] = new BrightnessDdcService(ddcSnBus[monitorSn]);
|
service[i] = new BrightnessDdcService(ddcSnBus[monitorSn]);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
service[i] = new BrightnessCtlService();
|
service[i] = new BrightnessCtlService();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown brightness controller ${preferredController}`);
|
throw new Error(`Unknown brightness controller ${preferredController}`);
|
||||||
|
|||||||
+13
-25
@@ -44,16 +44,6 @@ const PROVIDERS = Object.assign({
|
|||||||
"key_file": "openrouter_key.txt",
|
"key_file": "openrouter_key.txt",
|
||||||
"model": "meta-llama/llama-3-70b-instruct",
|
"model": "meta-llama/llama-3-70b-instruct",
|
||||||
},
|
},
|
||||||
"openai": {
|
|
||||||
"name": "OpenAI - GPT-3.5",
|
|
||||||
"logo_name": "openai-symbolic",
|
|
||||||
"description": getString('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",
|
|
||||||
"requires_key": true,
|
|
||||||
"key_file": "openai_key.txt",
|
|
||||||
"model": "gpt-3.5-turbo",
|
|
||||||
},
|
|
||||||
}, userOptions.ai.extraGptModels)
|
}, userOptions.ai.extraGptModels)
|
||||||
|
|
||||||
const installedOllamaModels = JSON.parse(
|
const installedOllamaModels = JSON.parse(
|
||||||
@@ -76,7 +66,7 @@ installedOllamaModels.forEach(model => {
|
|||||||
// Custom prompt
|
// Custom prompt
|
||||||
const initMessages =
|
const initMessages =
|
||||||
[
|
[
|
||||||
{ role: "user", content: getString("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 brief explanation.\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\". \nNote: Use casual language, be short, while ensuring the factual correctness of your response. If you are unsure or don’t have enough information to provide a confident answer, simply say “I don’t know” or “I’m not sure.”. \nThanks!"), },
|
{ role: "user", content: getString("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 brief explanation.\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\". \nNote: Use casual language, be short, while ensuring the factual correctness of your response. If you are unsure or don't have enough information to provide a confident answer, simply say \"I don't know\" or \"I'm not sure.\". \nThanks!") },
|
||||||
{ role: "assistant", content: "- Got it!", },
|
{ role: "assistant", content: "- Got it!", },
|
||||||
{ role: "user", content: "\"He rushed to where the event was supposed to be hold, he didn't know it got canceled\"", },
|
{ role: "user", content: "\"He rushed to where the event was supposed to be hold, he didn't know it got canceled\"", },
|
||||||
{ role: "assistant", content: "## Grammar correction\nErrors:\n\"He rushed to where the event was supposed to be __hold____,__ he didn't know it got canceled\"\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 canceled\"", },
|
{ role: "assistant", content: "## Grammar correction\nErrors:\n\"He rushed to where the event was supposed to be __hold____,__ he didn't know it got canceled\"\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 canceled\"", },
|
||||||
@@ -259,11 +249,22 @@ class GPTService extends Service {
|
|||||||
const [bytes] = stream.read_line_finish(res);
|
const [bytes] = stream.read_line_finish(res);
|
||||||
const line = this._decoder.decode(bytes);
|
const line = this._decoder.decode(bytes);
|
||||||
if (line && line != '') {
|
if (line && line != '') {
|
||||||
|
|
||||||
|
// Ignore SSE comments (lines starting with ":")
|
||||||
|
if (line.startsWith(':')) {
|
||||||
|
this.readResponse(stream, aiResponse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let data = line.substr(6);
|
let data = line.substr(6);
|
||||||
if (data == '[DONE]') return;
|
if (data == '[DONE]') return;
|
||||||
try {
|
try {
|
||||||
const result = JSON.parse(data);
|
const result = JSON.parse(data);
|
||||||
if (result.choices[0].finish_reason === 'stop') {
|
if (result.choices[0].finish_reason === 'stop') {
|
||||||
|
// If the stop payload has content, add it to the response
|
||||||
|
if (result.choices[0].delta.content) {
|
||||||
|
aiResponse.addDelta(result.choices[0].delta.content);
|
||||||
|
}
|
||||||
aiResponse.done = true;
|
aiResponse.done = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -279,7 +280,7 @@ class GPTService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (aiResponse.hasReasoningContent) {
|
if (aiResponse.hasReasoningContent && !aiResponse.parsedReasoningContent) {
|
||||||
aiResponse.parsedReasoningContent = true;
|
aiResponse.parsedReasoningContent = true;
|
||||||
aiResponse.addDelta(`\n</think>\n`);
|
aiResponse.addDelta(`\n</think>\n`);
|
||||||
}
|
}
|
||||||
@@ -334,16 +335,3 @@ class GPTService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default new GPTService();
|
export default new GPTService();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,9 @@
|
|||||||
|
include="~/.config/fuzzel/fuzzel.theme"
|
||||||
font=Gabarito
|
font=Gabarito
|
||||||
terminal=foot -e
|
terminal=foot -e
|
||||||
prompt=">> "
|
prompt=">> "
|
||||||
layer=overlay
|
layer=overlay
|
||||||
|
|
||||||
[colors]
|
|
||||||
background=1D1011ff
|
|
||||||
text=F7DCDEff
|
|
||||||
selection=574144ff
|
|
||||||
selection-text=DEBFC2ff
|
|
||||||
border=574144dd
|
|
||||||
match=FFB2BCff
|
|
||||||
selection-match=FFB2BCff
|
|
||||||
|
|
||||||
|
|
||||||
[border]
|
[border]
|
||||||
radius=17
|
radius=17
|
||||||
width=1
|
width=1
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# The content of this script will be generated by switchwall.sh - Don't modify it by yourself.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
$lock_cmd = pidof hyprlock || hyprlock
|
$lock_cmd = pidof hyprlock || hyprlock
|
||||||
$suspend_cmd = pidof steam || systemctl suspend || loginctl suspend # fuck nvidia
|
$suspend_cmd = systemctl suspend || loginctl suspend
|
||||||
|
|
||||||
general {
|
general {
|
||||||
lock_cmd = $lock_cmd
|
lock_cmd = $lock_cmd
|
||||||
|
|||||||
@@ -15,4 +15,3 @@ source=~/.config/hypr/custom/execs.conf
|
|||||||
source=~/.config/hypr/custom/general.conf
|
source=~/.config/hypr/custom/general.conf
|
||||||
source=~/.config/hypr/custom/rules.conf
|
source=~/.config/hypr/custom/rules.conf
|
||||||
source=~/.config/hypr/custom/keybinds.conf
|
source=~/.config/hypr/custom/keybinds.conf
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ env = QT_QPA_PLATFORMTHEME, qt6ct
|
|||||||
# ######## Screen tearing #########
|
# ######## Screen tearing #########
|
||||||
# env = WLR_DRM_NO_ATOMIC, 1
|
# env = WLR_DRM_NO_ATOMIC, 1
|
||||||
|
|
||||||
# ######## Virtual envrionment #########
|
# ######## Virtual environment #########
|
||||||
env = ILLOGICAL_IMPULSE_VIRTUAL_ENV, ~/.local/state/ags/.venv
|
env = ILLOGICAL_IMPULSE_VIRTUAL_ENV, ~/.local/state/ags/.venv
|
||||||
|
|
||||||
# ############ Others #############
|
# ############ Others #############
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# Bar, wallpaper
|
# Bar, wallpaper
|
||||||
exec-once = swww-daemon --format xrgb
|
exec-once = swww-daemon --format xrgb
|
||||||
|
exec-once = bash ~/.config/hypr/custom/scripts/__restore_video_wallpaper.sh
|
||||||
exec-once = /usr/lib/geoclue-2.0/demos/agent & gammastep
|
exec-once = /usr/lib/geoclue-2.0/demos/agent & gammastep
|
||||||
exec-once = agsv1 &
|
exec-once = agsv1 &
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
|
|
||||||
bindl = Alt ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
bindl = Alt ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||||
bindl = Super ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
bindl = Super ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||||
bindl = ,XF86AudioMute, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 0% # [hidden]
|
bindl = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle # [hidden]
|
||||||
bindl = Super+Shift,M, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 0% # [hidden]
|
bindl = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle # [hidden]
|
||||||
bindle=, XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+ # [hidden]
|
bindl = Super+Shift,M, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle # [hidden]
|
||||||
bindle=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- # [hidden]
|
bindle=, XF86AudioRaiseVolume, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 && wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+ # [hidden]
|
||||||
|
bindle=, XF86AudioLowerVolume, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 && wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- # [hidden]
|
||||||
|
|
||||||
# Uncomment these if you can't get AGS to work
|
# Uncomment these if you can't get AGS to work
|
||||||
#bindle=, XF86MonBrightnessUp, exec, brightnessctl set '12.75+'
|
#bindle=, XF86MonBrightnessUp, exec, brightnessctl set '12.75+'
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
[Appearance]
|
||||||
|
color_scheme_path=/home/end/.config/qt6ct/style-colors.conf
|
||||||
|
custom_palette=true
|
||||||
|
icon_theme=OneUI
|
||||||
|
standard_dialogs=default
|
||||||
|
style=kvantum
|
||||||
|
|
||||||
|
[Fonts]
|
||||||
|
fixed="JetBrainsMono Nerd Font,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular"
|
||||||
|
general="Rubik,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular"
|
||||||
|
|
||||||
|
[Interface]
|
||||||
|
activate_item_on_single_click=1
|
||||||
|
buttonbox_layout=0
|
||||||
|
cursor_flash_time=1000
|
||||||
|
dialog_buttons_have_icons=1
|
||||||
|
double_click_interval=400
|
||||||
|
gui_effects=@Invalid()
|
||||||
|
keyboard_scheme=2
|
||||||
|
menus_have_icons=true
|
||||||
|
show_shortcuts_in_context_menus=true
|
||||||
|
stylesheets=@Invalid()
|
||||||
|
toolbutton_style=4
|
||||||
|
underline_shortcut=1
|
||||||
|
wheel_scroll_lines=3
|
||||||
|
|
||||||
|
[SettingsWindow]
|
||||||
|
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\as\0\0\x4\x3\0\0\0\0\0\0\0\0\0\0\as\0\0\x4\x3\0\0\0\0\x2\0\0\0\a\x80\0\0\0\0\0\0\0\0\0\0\as\0\0\x4\x3)
|
||||||
|
|
||||||
|
[Troubleshooting]
|
||||||
|
force_raster_widgets=1
|
||||||
|
ignored_applications=@Invalid()
|
||||||
@@ -35,6 +35,12 @@
|
|||||||
```bash
|
```bash
|
||||||
bash <(curl -s "https://end-4.github.io/dots-hyprland-wiki/setup.sh")
|
bash <(curl -s "https://end-4.github.io/dots-hyprland-wiki/setup.sh")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you are using fish shell (non-posix-compliant shell) then:
|
||||||
|
```bash
|
||||||
|
bash -c "$(curl -s https://end-4.github.io/dots-hyprland-wiki/setup.sh)"
|
||||||
|
```
|
||||||
|
|
||||||
- **Manual** installation, other distros and more:
|
- **Manual** installation, other distros and more:
|
||||||
- See the [Wiki](https://end-4.github.io/dots-hyprland-wiki/en/i-i/01setup/)
|
- 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._)
|
- (_Available in: English, Vietnamese, and Simplified Chinese. Translations are welcome._)
|
||||||
@@ -66,9 +72,8 @@
|
|||||||
<details>
|
<details>
|
||||||
<summary>Help improve these dotfiles</summary>
|
<summary>Help improve these dotfiles</summary>
|
||||||
|
|
||||||
- New: Try the [Quickshell](https://quickshell.outfoxxed.me/)-powered version at [`ii-qs` branch](https://github.com/end-4/dots-hyprland/tree/ii-qs)
|
- You can give feedback/suggestions for the [`ii-qs` branch](https://github.com/end-4/dots-hyprland/tree/ii-qs) in [#1276](https://github.com/end-4/dots-hyprland/pull/1276)
|
||||||
- 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>
|
</details>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
@@ -78,6 +83,8 @@
|
|||||||
|
|
||||||
## Main branch (*illogical-impulse*)
|
## Main branch (*illogical-impulse*)
|
||||||
|
|
||||||
|
**Note**: Expect minimal maintenance for the main branch, as I'm already working on and using the new Quickshell version of illogical-impulse at the ii-qs branch. See [#1276](https://github.com/end-4/dots-hyprland/pull/1276).
|
||||||
|
|
||||||
### AI
|
### AI
|
||||||

|

|
||||||
_<sup>Sidebar offers online and offline chat. Text selection summary is offline only for privacy.</sup>_
|
_<sup>Sidebar offers online and offline chat. Text selection summary is offline only for privacy.</sup>_
|
||||||
|
|||||||
+18
-18
@@ -119,24 +119,24 @@ showfun install-python-packages
|
|||||||
v install-python-packages
|
v install-python-packages
|
||||||
|
|
||||||
## Optional dependencies
|
## Optional dependencies
|
||||||
if pacman -Qs ^plasma-browser-integration$ ;then SKIP_PLASMAINTG=true;fi
|
# if pacman -Qs ^plasma-browser-integration$ ;then SKIP_PLASMAINTG=true;fi
|
||||||
case $SKIP_PLASMAINTG in
|
# case $SKIP_PLASMAINTG in
|
||||||
true) sleep 0;;
|
# true) sleep 0;;
|
||||||
*)
|
# *)
|
||||||
if $ask;then
|
# if $ask;then
|
||||||
echo -e "\e[33m[$0]: NOTE: The size of \"plasma-browser-integration\" is about 250 MiB.\e[0m"
|
# echo -e "\e[33m[$0]: NOTE: The size of \"plasma-browser-integration\" is about 250 MiB.\e[0m"
|
||||||
echo -e "\e[33mIt is needed if you want playtime of media in Firefox to be shown on the music controls widget.\e[0m"
|
# echo -e "\e[33mIt is needed if you want playtime of media in Firefox to be shown on the music controls widget.\e[0m"
|
||||||
echo -e "\e[33mInstall it? [y/N]\e[0m"
|
# echo -e "\e[33mInstall it? [y/N]\e[0m"
|
||||||
read -p "====> " p
|
# read -p "====> " p
|
||||||
else
|
# else
|
||||||
p=y
|
# p=y
|
||||||
fi
|
# fi
|
||||||
case $p in
|
# case $p in
|
||||||
y) x sudo pacman -S --needed --noconfirm plasma-browser-integration ;;
|
# y) x sudo pacman -S --needed --noconfirm plasma-browser-integration ;;
|
||||||
*) echo "Ok, won't install"
|
# *) echo "Ok, won't install"
|
||||||
esac
|
# esac
|
||||||
;;
|
# ;;
|
||||||
esac
|
# esac
|
||||||
|
|
||||||
v sudo usermod -aG video,i2c,input "$(whoami)"
|
v sudo usermod -aG video,i2c,input "$(whoami)"
|
||||||
v bash -c "echo i2c-dev | sudo tee /etc/modules-load.d/i2c-dev.conf"
|
v bash -c "echo i2c-dev | sudo tee /etc/modules-load.d/i2c-dev.conf"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
build
|
build
|
||||||
pillow
|
pillow
|
||||||
pywal
|
|
||||||
setuptools-scm
|
setuptools-scm
|
||||||
wheel
|
wheel
|
||||||
pywayland
|
pywayland
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pycparser==2.22
|
|||||||
# via cffi
|
# via cffi
|
||||||
pyproject-hooks==1.2.0
|
pyproject-hooks==1.2.0
|
||||||
# via build
|
# via build
|
||||||
pywal==3.3.0
|
# pywal==3.3.0
|
||||||
# via -r scriptdata/requirements.in
|
# via -r scriptdata/requirements.in
|
||||||
pywayland==0.4.18
|
pywayland==0.4.18
|
||||||
# via -r scriptdata/requirements.in
|
# via -r scriptdata/requirements.in
|
||||||
|
|||||||
+1
-1
@@ -54,6 +54,6 @@ v sudo rm /etc/modules-load.d/i2c-dev.conf
|
|||||||
read -p "Do you want to uninstall packages used by the dotfiles?\nCtrl+C to exit, or press Enter to proceed"
|
read -p "Do you want to uninstall packages used by the dotfiles?\nCtrl+C to exit, or press Enter to proceed"
|
||||||
|
|
||||||
# Removing installed yay packages and dependencies
|
# Removing installed yay packages and dependencies
|
||||||
v yay -Rns illogical-impulse-{ags,audio,backlight,basic,bibata-modern-classic-bin,fonts-themes,gnome,gtk,hyprland,microtex-git,oneui4-icons-git,portal,python,screencapture,widgets} plasma-browser-integration
|
v yay -Rns illogical-impulse-{agsv1,audio,backlight,basic,bibata-modern-classic-bin,fonts-themes,gnome,gtk,hyprland,microtex-git,oneui4-icons-git,portal,python,screencapture,widgets} plasma-browser-integration
|
||||||
|
|
||||||
printf '\e[36mUninstall Complete.\n\e[97m'
|
printf '\e[36mUninstall Complete.\n\e[97m'
|
||||||
|
|||||||
@@ -0,0 +1,849 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# update.sh - Enhanced dotfiles update script
|
||||||
|
#
|
||||||
|
# Features:
|
||||||
|
# - Pull latest commits from remote
|
||||||
|
# - Rebuild packages if PKGBUILD files changed (user choice)
|
||||||
|
# - Handle config file conflicts with user choices
|
||||||
|
# - Respect .updateignore file for exclusions
|
||||||
|
#
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# === Configuration ===
|
||||||
|
FORCE_CHECK=false
|
||||||
|
CHECK_PACKAGES=false
|
||||||
|
REPO_DIR="$(cd "$(dirname $0)" &>/dev/null && pwd)"
|
||||||
|
ARCH_PACKAGES_DIR="${REPO_DIR}/arch-packages"
|
||||||
|
UPDATE_IGNORE_FILE="${REPO_DIR}/.updateignore"
|
||||||
|
HOME_UPDATE_IGNORE_FILE="${HOME}/.updateignore"
|
||||||
|
|
||||||
|
# Directories to monitor for changes
|
||||||
|
MONITOR_DIRS=(".config" ".local/bin")
|
||||||
|
|
||||||
|
# === Color Codes ===
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
PURPLE='\033[0;35m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# === Helper Functions ===
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
log_header() {
|
||||||
|
echo -e "\n${PURPLE}=== $1 ===${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
log_error "$1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to safely read input with terminal compatibility
|
||||||
|
safe_read() {
|
||||||
|
local prompt="$1"
|
||||||
|
local varname="$2"
|
||||||
|
local default="${3:-}"
|
||||||
|
|
||||||
|
# Simple approach: just use read with /dev/tty and handle errors
|
||||||
|
local input_value=""
|
||||||
|
|
||||||
|
# Display prompt and read from terminal
|
||||||
|
echo -n "$prompt"
|
||||||
|
if read input_value </dev/tty 2>/dev/null || read input_value 2>/dev/null; then
|
||||||
|
eval "$varname='$input_value'"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# If read failed and we have a default, use it
|
||||||
|
if [[ -n "$default" ]]; then
|
||||||
|
echo
|
||||||
|
log_warning "Using default: $default"
|
||||||
|
eval "$varname='$default'"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
log_error "Failed to read input"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if a file should be ignored
|
||||||
|
should_ignore() {
|
||||||
|
local file_path="$1"
|
||||||
|
local relative_path="${file_path#$HOME/}"
|
||||||
|
|
||||||
|
# Also get path relative to repo for repo-level ignores
|
||||||
|
local repo_relative=""
|
||||||
|
if [[ "$file_path" == "$REPO_DIR"* ]]; then
|
||||||
|
repo_relative="${file_path#$REPO_DIR/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check both repo and home ignore files
|
||||||
|
for ignore_file in "$UPDATE_IGNORE_FILE" "$HOME_UPDATE_IGNORE_FILE"; do
|
||||||
|
if [[ -f "$ignore_file" ]]; then
|
||||||
|
while IFS= read -r pattern || [[ -n "$pattern" ]]; do
|
||||||
|
# Skip empty lines and comments
|
||||||
|
[[ -z "$pattern" || "$pattern" =~ ^[[:space:]]*# ]] && continue
|
||||||
|
# Remove leading/trailing whitespace
|
||||||
|
pattern=$(echo "$pattern" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||||
|
[[ -z "$pattern" ]] && continue
|
||||||
|
|
||||||
|
# Handle different gitignore-style patterns
|
||||||
|
local should_skip=false
|
||||||
|
|
||||||
|
# Exact match
|
||||||
|
if [[ "$relative_path" == "$pattern" ]] || [[ "$repo_relative" == "$pattern" ]]; then
|
||||||
|
should_skip=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wildcard patterns (basic glob matching)
|
||||||
|
if [[ "$relative_path" == $pattern ]] || [[ "$repo_relative" == $pattern ]]; then
|
||||||
|
should_skip=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Directory patterns (ending with /)
|
||||||
|
if [[ "$pattern" == */ ]]; then
|
||||||
|
local dir_pattern="${pattern%/}"
|
||||||
|
if [[ "$relative_path" == "$dir_pattern"/* ]] || [[ "$repo_relative" == "$dir_pattern"/* ]]; then
|
||||||
|
should_skip=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Patterns starting with / (from root)
|
||||||
|
if [[ "$pattern" == /* ]]; then
|
||||||
|
local root_pattern="${pattern#/}"
|
||||||
|
if [[ "$relative_path" == "$root_pattern" ]] || [[ "$relative_path" == "$root_pattern"/* ]] ||
|
||||||
|
[[ "$repo_relative" == "$root_pattern" ]] || [[ "$repo_relative" == "$root_pattern"/* ]]; then
|
||||||
|
should_skip=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Patterns with wildcards
|
||||||
|
if [[ "$pattern" == *"*"* ]]; then
|
||||||
|
if [[ "$relative_path" == $pattern ]] || [[ "$repo_relative" == $pattern ]]; then
|
||||||
|
should_skip=true
|
||||||
|
fi
|
||||||
|
# Also check if any parent directory matches
|
||||||
|
local temp_path="$relative_path"
|
||||||
|
while [[ "$temp_path" == */* ]]; do
|
||||||
|
temp_path="${temp_path%/*}"
|
||||||
|
if [[ "$temp_path" == $pattern ]]; then
|
||||||
|
should_skip=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Simple substring matching (for backward compatibility)
|
||||||
|
if [[ ! "$should_skip" == true ]]; then
|
||||||
|
if [[ "$file_path" == *"$pattern"* ]] || [[ "$relative_path" == *"$pattern"* ]]; then
|
||||||
|
should_skip=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$should_skip" == true ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done <"$ignore_file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to show file diff with syntax highlighting if possible
|
||||||
|
show_diff() {
|
||||||
|
local file1="$1"
|
||||||
|
local file2="$2"
|
||||||
|
|
||||||
|
echo -e "\n${CYAN}Showing differences:${NC}"
|
||||||
|
echo -e "${CYAN}Old file: $file1${NC}"
|
||||||
|
echo -e "${CYAN}New file: $file2${NC}"
|
||||||
|
echo "----------------------------------------"
|
||||||
|
|
||||||
|
if command -v diff &>/dev/null; then
|
||||||
|
diff -u "$file1" "$file2" || true
|
||||||
|
else
|
||||||
|
echo "diff command not available"
|
||||||
|
fi
|
||||||
|
echo "----------------------------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to handle file conflicts
|
||||||
|
handle_file_conflict() {
|
||||||
|
local repo_file="$1"
|
||||||
|
local home_file="$2"
|
||||||
|
local filename=$(basename "$home_file")
|
||||||
|
local dirname=$(dirname "$home_file")
|
||||||
|
|
||||||
|
echo -e "\n${YELLOW}Conflict detected:${NC} $home_file"
|
||||||
|
echo "Repository version differs from your local version."
|
||||||
|
echo
|
||||||
|
echo "Choose an action:"
|
||||||
|
echo "1) Replace local file with repository version"
|
||||||
|
echo "2) Keep local file unchanged"
|
||||||
|
echo "3) Backup local file as ${filename}.old, use repository version"
|
||||||
|
echo "4) Save repository version as ${filename}.new, keep local file"
|
||||||
|
echo "5) Show diff and decide"
|
||||||
|
echo "6) Skip this file"
|
||||||
|
echo
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if ! safe_read "Enter your choice (1-6): " choice "6"; then
|
||||||
|
echo
|
||||||
|
log_warning "Failed to read input. Skipping file."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1)
|
||||||
|
cp -p "$repo_file" "$home_file"
|
||||||
|
log_success "Replaced $home_file with repository version"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
log_info "Keeping local version of $home_file"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
mv "$home_file" "${dirname}/${filename}.old"
|
||||||
|
cp -p "$repo_file" "$home_file"
|
||||||
|
log_success "Backed up local file to ${filename}.old and updated with repository version"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
cp -p "$repo_file" "${dirname}/${filename}.new"
|
||||||
|
log_success "Saved repository version as ${filename}.new, kept local file"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
show_diff "$home_file" "$repo_file"
|
||||||
|
echo
|
||||||
|
echo "After reviewing the diff, choose:"
|
||||||
|
echo "r) Replace with repository version"
|
||||||
|
echo "k) Keep local version"
|
||||||
|
echo "b) Backup local and use repository version"
|
||||||
|
echo "n) Save repository version as .new"
|
||||||
|
echo "s) Skip this file"
|
||||||
|
|
||||||
|
if ! safe_read "Enter your choice (r/k/b/n/s): " subchoice "s"; then
|
||||||
|
echo
|
||||||
|
log_warning "Failed to read input. Skipping file."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $subchoice in
|
||||||
|
r)
|
||||||
|
cp -p "$repo_file" "$home_file"
|
||||||
|
log_success "Replaced $home_file with repository version"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
k)
|
||||||
|
log_info "Keeping local version of $home_file"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
b)
|
||||||
|
mv "$home_file" "${dirname}/${filename}.old"
|
||||||
|
cp -p "$repo_file" "$home_file"
|
||||||
|
log_success "Backed up local file to ${filename}.old and updated"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
n)
|
||||||
|
cp -p "$repo_file" "${dirname}/${filename}.new"
|
||||||
|
log_success "Saved repository version as ${filename}.new"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
s)
|
||||||
|
log_info "Skipping $home_file"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Invalid choice. Please try again."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
6)
|
||||||
|
log_info "Skipping $home_file"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Invalid choice. Please enter 1-6."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if PKGBUILD has changed
|
||||||
|
check_pkgbuild_changed() {
|
||||||
|
local pkg_dir="$1"
|
||||||
|
local pkgbuild_path="${pkg_dir}/PKGBUILD"
|
||||||
|
|
||||||
|
[[ ! -f "$pkgbuild_path" ]] && return 1
|
||||||
|
|
||||||
|
# Get the path relative to repo
|
||||||
|
local relative_path="${pkgbuild_path#$REPO_DIR/}"
|
||||||
|
|
||||||
|
# If force check is enabled, always return true
|
||||||
|
if [[ "$FORCE_CHECK" == true ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if file changed in the last pull
|
||||||
|
if git diff --name-only HEAD@{1} HEAD 2>/dev/null | grep -q "^${relative_path}$"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to list available packages
|
||||||
|
list_packages() {
|
||||||
|
local available_packages=()
|
||||||
|
local changed_packages=()
|
||||||
|
|
||||||
|
if [[ ! -d "$ARCH_PACKAGES_DIR" ]]; then
|
||||||
|
log_warning "No arch-packages directory found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do
|
||||||
|
if [[ -f "${pkg_dir}/PKGBUILD" ]]; then
|
||||||
|
local pkg_name=$(basename "$pkg_dir")
|
||||||
|
available_packages+=("$pkg_name")
|
||||||
|
|
||||||
|
if check_pkgbuild_changed "$pkg_dir"; then
|
||||||
|
changed_packages+=("$pkg_name")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#available_packages[@]} -eq 0 ]]; then
|
||||||
|
log_info "No packages found in arch-packages directory"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\n${CYAN}Available packages:${NC}"
|
||||||
|
for pkg in "${available_packages[@]}"; do
|
||||||
|
if [[ " ${changed_packages[*]} " =~ " ${pkg} " ]]; then
|
||||||
|
echo -e " ${GREEN}● ${pkg}${NC} (PKGBUILD changed)"
|
||||||
|
else
|
||||||
|
echo -e " ○ ${pkg}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#changed_packages[@]} -gt 0 ]]; then
|
||||||
|
echo -e "\n${YELLOW}Packages with changed PKGBUILDs: ${changed_packages[*]}${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to build selected packages
|
||||||
|
build_packages() {
|
||||||
|
local build_mode="$1" # "changed", "all", or "select"
|
||||||
|
local packages_to_build=()
|
||||||
|
local rebuilt_packages=0
|
||||||
|
|
||||||
|
case "$build_mode" in
|
||||||
|
"changed")
|
||||||
|
for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do
|
||||||
|
if [[ -f "${pkg_dir}/PKGBUILD" ]]; then
|
||||||
|
local pkg_name=$(basename "$pkg_dir")
|
||||||
|
if check_pkgbuild_changed "$pkg_dir"; then
|
||||||
|
packages_to_build+=("$pkg_name")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
"all")
|
||||||
|
for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do
|
||||||
|
if [[ -f "${pkg_dir}/PKGBUILD" ]]; then
|
||||||
|
local pkg_name=$(basename "$pkg_dir")
|
||||||
|
packages_to_build+=("$pkg_name")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
"select")
|
||||||
|
echo -e "\nEnter package names separated by spaces (or 'all' for all packages):"
|
||||||
|
if ! safe_read "Packages to build: " user_selection ""; then
|
||||||
|
log_warning "Failed to read input. Skipping package builds."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$user_selection" == "all" ]]; then
|
||||||
|
for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do
|
||||||
|
if [[ -f "${pkg_dir}/PKGBUILD" ]]; then
|
||||||
|
local pkg_name=$(basename "$pkg_dir")
|
||||||
|
packages_to_build+=("$pkg_name")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
read -ra packages_to_build <<<"$user_selection"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ ${#packages_to_build[@]} -eq 0 ]]; then
|
||||||
|
log_info "No packages selected for building"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\n${CYAN}Packages to build: ${packages_to_build[*]}${NC}"
|
||||||
|
|
||||||
|
if ! safe_read "Proceed with building these packages? (Y/n): " confirm "Y"; then
|
||||||
|
log_warning "Failed to read input. Skipping package builds."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$confirm" =~ ^[Nn]$ ]]; then
|
||||||
|
log_info "Package building cancelled by user"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pkg_name in "${packages_to_build[@]}"; do
|
||||||
|
local pkg_dir="${ARCH_PACKAGES_DIR}/${pkg_name}"
|
||||||
|
|
||||||
|
if [[ ! -d "$pkg_dir" || ! -f "${pkg_dir}/PKGBUILD" ]]; then
|
||||||
|
log_error "Package not found or missing PKGBUILD: $pkg_name"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Building package: $pkg_name"
|
||||||
|
cd "$pkg_dir" || continue
|
||||||
|
|
||||||
|
if makepkg -si --noconfirm; then
|
||||||
|
log_success "Successfully built and installed $pkg_name"
|
||||||
|
((rebuilt_packages++))
|
||||||
|
else
|
||||||
|
log_error "Failed to build package $pkg_name"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$REPO_DIR" || die "Failed to return to repository directory"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $rebuilt_packages -eq 0 ]]; then
|
||||||
|
log_warning "No packages were successfully built"
|
||||||
|
else
|
||||||
|
log_success "Successfully rebuilt $rebuilt_packages package(s)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to get list of changed files since last pull or all files if force check
|
||||||
|
get_changed_files() {
|
||||||
|
local dir_path="$1"
|
||||||
|
|
||||||
|
if [[ "$FORCE_CHECK" == true ]]; then
|
||||||
|
# Return all files in the directory
|
||||||
|
find "$dir_path" -type f -print0 2>/dev/null
|
||||||
|
else
|
||||||
|
# Get files that changed in the last pull
|
||||||
|
local changed_files=()
|
||||||
|
while IFS= read -r file; do
|
||||||
|
local full_path="${REPO_DIR}/${file}"
|
||||||
|
# Check if file is in the directory we're processing
|
||||||
|
if [[ "$full_path" == "$dir_path"/* ]] && [[ -f "$full_path" ]]; then
|
||||||
|
printf '%s\0' "$full_path"
|
||||||
|
fi
|
||||||
|
done < <(git diff --name-only HEAD@{1} HEAD 2>/dev/null || true)
|
||||||
|
|
||||||
|
# If no files changed via git, but force_check is false, still check all files
|
||||||
|
# This handles the case where there were no new commits but files might differ
|
||||||
|
if ! git diff --quiet HEAD@{1} HEAD 2>/dev/null; then
|
||||||
|
: # Files were found via git diff
|
||||||
|
else
|
||||||
|
# No git changes detected, check all files anyway for local differences
|
||||||
|
find "$dir_path" -type f -print0 2>/dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if we have new commits
|
||||||
|
has_new_commits() {
|
||||||
|
# Check if HEAD@{1} exists (meaning there was a previous commit)
|
||||||
|
if git rev-parse --verify HEAD@{1} &>/dev/null; then
|
||||||
|
# Check if HEAD and HEAD@{1} are different
|
||||||
|
[[ "$(git rev-parse HEAD)" != "$(git rev-parse HEAD@{1})" ]]
|
||||||
|
else
|
||||||
|
# No previous commit reference, assume we have commits
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script starts here
|
||||||
|
log_header "Dotfiles Update Script"
|
||||||
|
|
||||||
|
check=true
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-f | --force)
|
||||||
|
FORCE_CHECK=true
|
||||||
|
log_info "Force check mode enabled - will check all files regardless of git changes"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-p | --packages)
|
||||||
|
CHECK_PACKAGES=true
|
||||||
|
log_info "Package checking enabled"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h | --help)
|
||||||
|
echo "Usage: $0 [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -f, --force Force check all files even if no new commits"
|
||||||
|
echo " -p, --packages Enable package checking and building"
|
||||||
|
echo " -h, --help Show this help message"
|
||||||
|
echo ""
|
||||||
|
echo "This script updates your dotfiles by:"
|
||||||
|
echo " 1. Pulling latest changes from git remote"
|
||||||
|
echo " 2. Optionally rebuilding packages (if -p flag is used)"
|
||||||
|
echo " 3. Syncing configuration files"
|
||||||
|
echo " 4. Updating script permissions"
|
||||||
|
echo ""
|
||||||
|
echo "Package modes (when -p is used):"
|
||||||
|
echo " - If no PKGBUILDs changed: asks if you want to check packages anyway"
|
||||||
|
echo " - If PKGBUILDs changed: offers to build changed packages"
|
||||||
|
echo " - Interactive selection of packages to build"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--skip-notice)
|
||||||
|
log_warning "Skipping notice about script being untested"
|
||||||
|
check=false
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Unknown option: $1"
|
||||||
|
echo "Use --help for usage information"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$check" == true ]]; then
|
||||||
|
log_warning "THIS SCRIPT IS NOT FULLY TESTED AND MAY CAUSE ISSUES!"
|
||||||
|
safe_read "BY CONTINUE YOU WILL USE IT AT YOUR OWN RISK (y/N): " response "N"
|
||||||
|
|
||||||
|
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||||
|
log_error "Update aborted by user"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if we're in a git repository
|
||||||
|
cd "$REPO_DIR" || die "Failed to change to repository directory"
|
||||||
|
|
||||||
|
if git rev-parse --is-inside-work-tree &>/dev/null; then
|
||||||
|
log_info "Running in git repository: $(git rev-parse --show-toplevel)"
|
||||||
|
else
|
||||||
|
log_error "Not in a git repository. Please run this script from your dotfiles repository."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 1: Pull latest commits
|
||||||
|
log_header "Pulling Latest Changes"
|
||||||
|
|
||||||
|
# Check current branch
|
||||||
|
current_branch=$(git branch --show-current)
|
||||||
|
if [[ -z "$current_branch" ]]; then
|
||||||
|
log_warning "In detached HEAD state. Checking out main/master branch..."
|
||||||
|
if git show-ref --verify --quiet refs/heads/main; then
|
||||||
|
git checkout main
|
||||||
|
current_branch="main"
|
||||||
|
elif git show-ref --verify --quiet refs/heads/master; then
|
||||||
|
git checkout master
|
||||||
|
current_branch="master"
|
||||||
|
else
|
||||||
|
die "Could not find main or master branch"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Current branch: $current_branch"
|
||||||
|
|
||||||
|
# Check for uncommitted changes
|
||||||
|
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||||
|
log_warning "You have uncommitted changes:"
|
||||||
|
git status --short
|
||||||
|
echo
|
||||||
|
|
||||||
|
if ! safe_read "Do you want to continue? This will stash your changes. (y/N): " response "N"; then
|
||||||
|
echo
|
||||||
|
log_error "Failed to read input. Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||||
|
die "Aborted by user"
|
||||||
|
fi
|
||||||
|
git stash push -m "Auto-stash before update $(date)"
|
||||||
|
log_info "Changes stashed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if remote exists
|
||||||
|
if git remote get-url origin &>/dev/null; then
|
||||||
|
# Pull changes
|
||||||
|
log_info "Pulling changes from origin/$current_branch..."
|
||||||
|
if git pull; then
|
||||||
|
log_success "Successfully pulled latest changes"
|
||||||
|
else
|
||||||
|
log_warning "Failed to pull changes from remote. Continuing with local repository..."
|
||||||
|
log_info "You may need to resolve conflicts manually later."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warning "No remote 'origin' configured. Skipping pull operation."
|
||||||
|
log_info "This appears to be a local-only repository."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 2: Handle package building (only if requested)
|
||||||
|
rebuilt_packages=0
|
||||||
|
|
||||||
|
if [[ "$CHECK_PACKAGES" == true ]]; then
|
||||||
|
log_header "Package Management"
|
||||||
|
|
||||||
|
if [[ ! -d "$ARCH_PACKAGES_DIR" ]]; then
|
||||||
|
log_warning "No arch-packages directory found. Skipping package management."
|
||||||
|
else
|
||||||
|
# Check if any PKGBUILDs have changed
|
||||||
|
changed_pkgbuilds=()
|
||||||
|
for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do
|
||||||
|
if [[ -f "${pkg_dir}/PKGBUILD" ]]; then
|
||||||
|
local pkg_name=$(basename "$pkg_dir")
|
||||||
|
if check_pkgbuild_changed "$pkg_dir"; then
|
||||||
|
changed_pkgbuilds+=("$pkg_name")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#changed_pkgbuilds[@]} -gt 0 ]]; then
|
||||||
|
log_info "Found ${#changed_pkgbuilds[@]} package(s) with changed PKGBUILDs: ${changed_pkgbuilds[*]}"
|
||||||
|
echo
|
||||||
|
echo "Package build options:"
|
||||||
|
echo "1) Build only packages with changed PKGBUILDs"
|
||||||
|
echo "2) List all packages and select which to build"
|
||||||
|
echo "3) Build all packages"
|
||||||
|
echo "4) Skip package building"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if safe_read "Choose an option (1-4): " pkg_choice "1"; then
|
||||||
|
case $pkg_choice in
|
||||||
|
1)
|
||||||
|
build_packages "changed"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
if list_packages; then
|
||||||
|
build_packages "select"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
build_packages "all"
|
||||||
|
;;
|
||||||
|
4 | *)
|
||||||
|
log_info "Skipping package building"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
log_warning "Failed to read input. Skipping package building."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "No PKGBUILDs have changed since last update."
|
||||||
|
echo
|
||||||
|
if safe_read "Do you want to check and build packages anyway? (y/N): " check_anyway "N"; then
|
||||||
|
if [[ "$check_anyway" =~ ^[Yy]$ ]]; then
|
||||||
|
if list_packages; then
|
||||||
|
echo
|
||||||
|
echo "Package build options:"
|
||||||
|
echo "1) Select specific packages to build"
|
||||||
|
echo "2) Build all packages"
|
||||||
|
echo "3) Skip package building"
|
||||||
|
|
||||||
|
if safe_read "Choose an option (1-3): " build_choice "3"; then
|
||||||
|
case $build_choice in
|
||||||
|
1)
|
||||||
|
build_packages "select"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
build_packages "all"
|
||||||
|
;;
|
||||||
|
3 | *)
|
||||||
|
log_info "Skipping package building"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
log_info "Skipping package building"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Skipping package management"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Skipping package management"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_header "Package Management"
|
||||||
|
log_info "Package checking disabled. Use -p or --packages flag to enable package management."
|
||||||
|
|
||||||
|
# Still show a hint if there are changed PKGBUILDs
|
||||||
|
if [[ -d "$ARCH_PACKAGES_DIR" ]]; then
|
||||||
|
changed_count=0
|
||||||
|
for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do
|
||||||
|
if [[ -f "${pkg_dir}/PKGBUILD" ]] && check_pkgbuild_changed "$pkg_dir"; then
|
||||||
|
((changed_count++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $changed_count -gt 0 ]]; then
|
||||||
|
log_warning "Note: $changed_count package(s) have changed PKGBUILDs. Use -p flag to manage packages."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 3: Update configuration files
|
||||||
|
log_header "Updating Configuration Files"
|
||||||
|
|
||||||
|
# Check if we should process files
|
||||||
|
process_files=false
|
||||||
|
if [[ "$FORCE_CHECK" == true ]]; then
|
||||||
|
process_files=true
|
||||||
|
log_info "Force mode: checking all configuration files"
|
||||||
|
elif has_new_commits; then
|
||||||
|
process_files=true
|
||||||
|
log_info "New commits detected: checking changed configuration files"
|
||||||
|
else
|
||||||
|
log_info "No new commits found: checking for local file differences"
|
||||||
|
process_files=true # Always check for differences even without commits
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$process_files" == true ]]; then
|
||||||
|
files_processed=0
|
||||||
|
files_updated=0
|
||||||
|
files_created=0
|
||||||
|
|
||||||
|
for dir_name in "${MONITOR_DIRS[@]}"; do
|
||||||
|
repo_dir_path="${REPO_DIR}/${dir_name}"
|
||||||
|
home_dir_path="${HOME}/${dir_name}"
|
||||||
|
|
||||||
|
if [[ ! -d "$repo_dir_path" ]]; then
|
||||||
|
log_warning "Repository directory not found: $repo_dir_path"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Processing directory: $dir_name"
|
||||||
|
|
||||||
|
# Create home directory if it doesn't exist
|
||||||
|
mkdir -p "$home_dir_path"
|
||||||
|
|
||||||
|
# Get files to process (changed files or all files based on mode)
|
||||||
|
while IFS= read -r -d '' repo_file; do
|
||||||
|
# Calculate relative path and corresponding home file path
|
||||||
|
rel_path="${repo_file#$repo_dir_path/}"
|
||||||
|
home_file="${home_dir_path}/${rel_path}"
|
||||||
|
|
||||||
|
# Check if file should be ignored
|
||||||
|
if should_ignore "$home_file"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
((files_processed++))
|
||||||
|
|
||||||
|
# Create directory structure if needed
|
||||||
|
mkdir -p "$(dirname "$home_file")"
|
||||||
|
|
||||||
|
if [[ -f "$home_file" ]]; then
|
||||||
|
# File exists, check if different
|
||||||
|
if ! cmp -s "$repo_file" "$home_file"; then
|
||||||
|
log_info "Found difference in: $rel_path"
|
||||||
|
handle_file_conflict "$repo_file" "$home_file"
|
||||||
|
((files_updated++))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# New file, copy it
|
||||||
|
cp -p "$repo_file" "$home_file"
|
||||||
|
log_success "Created new file: $home_file"
|
||||||
|
((files_created++))
|
||||||
|
fi
|
||||||
|
done < <(get_changed_files "$repo_dir_path")
|
||||||
|
done
|
||||||
|
|
||||||
|
# Show processing summary
|
||||||
|
echo
|
||||||
|
log_info "File processing summary:"
|
||||||
|
log_info "- Files processed: $files_processed"
|
||||||
|
log_info "- Files with conflicts: $files_updated"
|
||||||
|
log_info "- New files created: $files_created"
|
||||||
|
else
|
||||||
|
log_info "Skipping file updates (no changes detected and not in force mode)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 4: Update script permissions
|
||||||
|
log_header "Updating Script Permissions"
|
||||||
|
|
||||||
|
if [[ -d "${REPO_DIR}/scriptdata" ]]; then
|
||||||
|
find "${REPO_DIR}/scriptdata" -type f -name "*.sh" -exec chmod +x {} \;
|
||||||
|
find "${REPO_DIR}/scriptdata" -type f -executable -exec chmod +x {} \;
|
||||||
|
log_success "Updated script permissions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure local bin scripts are executable
|
||||||
|
if [[ -d "${HOME}/.local/bin" ]]; then
|
||||||
|
find "${HOME}/.local/bin" -type f -exec chmod +x {} \; 2>/dev/null || true
|
||||||
|
log_success "Updated ~/.local/bin script permissions"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_header "Update Complete"
|
||||||
|
log_success "Dotfiles update completed successfully!"
|
||||||
|
|
||||||
|
# Show summary
|
||||||
|
echo
|
||||||
|
echo -e "${CYAN}Summary:${NC}"
|
||||||
|
echo "- Repository: $(git log -1 --pretty=format:'%h - %s (%cr)')"
|
||||||
|
echo "- Branch: $current_branch"
|
||||||
|
echo "- Mode: $([ "$FORCE_CHECK" == true ] && echo "Force check" || echo "Normal")"
|
||||||
|
echo "- Package checking: $([ "$CHECK_PACKAGES" == true ] && echo "Enabled" || echo "Disabled")"
|
||||||
|
|
||||||
|
if [[ $rebuilt_packages -gt 0 ]]; then
|
||||||
|
echo "- Packages rebuilt: $rebuilt_packages"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$process_files" == true ]]; then
|
||||||
|
echo "- Files processed: $files_processed"
|
||||||
|
echo "- Files updated/conflicted: $files_updated"
|
||||||
|
echo "- New files created: $files_created"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "- Configuration directories: ${MONITOR_DIRS[*]}"
|
||||||
|
|
||||||
|
# Remind about ignore files and show examples
|
||||||
|
if [[ ! -f "$HOME_UPDATE_IGNORE_FILE" && ! -f "$UPDATE_IGNORE_FILE" ]]; then
|
||||||
|
echo
|
||||||
|
log_info "Tip: Create ignore files to exclude files from updates:"
|
||||||
|
echo " - Repository ignore: ${REPO_DIR}/.updateignore"
|
||||||
|
echo " - User ignore: ~/.updateignore"
|
||||||
|
echo
|
||||||
|
echo "Example patterns:"
|
||||||
|
echo " *.log # Ignore all .log files"
|
||||||
|
echo " .config/personal/ # Ignore entire directory"
|
||||||
|
echo " secret-config.conf # Ignore specific file"
|
||||||
|
echo " /temp-file # Ignore from root only"
|
||||||
|
echo " *secret* # Ignore files containing 'secret'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
Reference in New Issue
Block a user