forked from Shinonome/dots-hyprland
Merge branch 'main' into main
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "dots/.config/quickshell/ii/modules/common/widgets/shapes"]
|
||||||
|
path = dots/.config/quickshell/ii/modules/common/widgets/shapes
|
||||||
|
url = https://github.com/end-4/rounded-polygon-qmljs.git
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<details>
|
<details>
|
||||||
<summary>Installation (illogical-impulse Quickshell)</summary>
|
<summary>Installation (illogical-impulse Quickshell)</summary>
|
||||||
|
|
||||||
- Just run `bash <(curl -s https://ii.clsty.link/setup)`
|
- Just run `bash <(curl -s https://ii.clsty.link/get)`
|
||||||
- Or, clone this repo and run `./setup install`
|
- Or, clone this repo and run `./setup install`
|
||||||
- See [document](https://ii.clsty.link/en/ii-qs/01setup/) for details.
|
- See [document](https://ii.clsty.link/en/ii-qs/01setup/) for details.
|
||||||
- **Default keybinds**: Should be somewhat familiar to Windows or GNOME users. Important ones:
|
- **Default keybinds**: Should be somewhat familiar to Windows or GNOME users. Important ones:
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
|
||||||
|
<fontconfig>
|
||||||
|
<match target="font">
|
||||||
|
<edit name="rgba" mode="assign">
|
||||||
|
<const>none</const>
|
||||||
|
</edit>
|
||||||
|
</match>
|
||||||
|
|
||||||
|
<!-- Fix for: arabic fonts rendering in Noto Nastaliq Urdu | affects Chromium, Discord (maybe all chromium based apps, but not Spotify somehow) -->
|
||||||
|
<match target="pattern">
|
||||||
|
<test compare="eq" name="family">
|
||||||
|
<string>sans-serif</string>
|
||||||
|
</test>
|
||||||
|
<edit name="family" mode="prepend" binding="strong">
|
||||||
|
<string>Noto Sans Arabic</string>
|
||||||
|
</edit>
|
||||||
|
</match>
|
||||||
|
</fontconfig>
|
||||||
@@ -70,14 +70,18 @@ bind = Super+Shift, T,exec, qs -c $qsConfig ipc call TEST_ALIVE || pidof slurp |
|
|||||||
# Color picker
|
# Color picker
|
||||||
bindd = Super+Shift, C, Color picker, exec, hyprpicker -a # Pick color (Hex) >> clipboard
|
bindd = Super+Shift, C, Color picker, exec, hyprpicker -a # Pick color (Hex) >> clipboard
|
||||||
# Fullscreen screenshot
|
# Fullscreen screenshot
|
||||||
bindld = ,Print, Screenshot >> clipboard ,exec,grim - | wl-copy # Screenshot >> clipboard
|
bindl = ,Print,exec,grim - | wl-copy # Screenshot >> clipboard
|
||||||
bindld = Ctrl,Print, Screenshot >> clipboard & save, exec, mkdir -p $(xdg-user-dir PICTURES)/Screenshots && grim $(xdg-user-dir PICTURES)/Screenshots/Screenshot_"$(date '+%Y-%m-%d_%H.%M.%S')".png # Screenshot >> clipboard & file
|
bindln = Ctrl,Print, exec, mkdir -p $(xdg-user-dir PICTURES)/Screenshots && grim $(xdg-user-dir PICTURES)/Screenshots/Screenshot_"$(date '+%Y-%m-%d_%H.%M.%S')".png # Screenshot >> clipboard & file (file)
|
||||||
|
bindln = Ctrl,Print,exec,grim - | wl-copy # [hidden] Screenshot >> clipboard & file (clipboard)
|
||||||
# Recording stuff
|
# Recording stuff
|
||||||
bindl = Super+Alt, R, exec, ~/.config/hypr/hyprland/scripts/record.sh # Record region (no sound)
|
bindl = Super+Shift, R, global, quickshell:regionRecord # Record region (no sound)
|
||||||
bindl = Ctrl+Alt, R, exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen # [hidden] Record screen (no sound)
|
bindl = Super+Shift, R, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/quickshell/$qsConfig/scripts/videos/record.sh # [hidden] Record region (no sound) (fallback)
|
||||||
bindl = Super+Shift+Alt, R, exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen-sound # Record screen (with sound)
|
bindl = Super+Alt, R, global, quickshell:regionRecord # [hidden] Record region (no sound)
|
||||||
|
bindl = Super+Alt, R, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/quickshell/$qsConfig/scripts/videos/record.sh # [hidden] Record region (no sound) (fallback)
|
||||||
|
bindl = Ctrl+Alt, R, exec, ~/.config/quickshell/$qsConfig/scripts/videos/record.sh --fullscreen # [hidden] Record screen (no sound)
|
||||||
|
bindl = Super+Shift+Alt, R, exec, ~/.config/quickshell/$qsConfig/scripts/videos/record.sh --fullscreen --sound # Record screen (with sound)
|
||||||
# AI
|
# AI
|
||||||
bindd = Super+Shift+Alt, mouse:273, Generate AI summary for selected text, exec, ~/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh # AI summary for selected text
|
bindd = Super+Shift+Alt, mouse:273, Generate AI summary for selected text, exec, ~/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh # [hidden] AI summary for selected text
|
||||||
|
|
||||||
#!
|
#!
|
||||||
##! Window
|
##! Window
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
getdate() {
|
|
||||||
date '+%Y-%m-%d_%H.%M.%S'
|
|
||||||
}
|
|
||||||
getaudiooutput() {
|
|
||||||
pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2
|
|
||||||
}
|
|
||||||
getactivemonitor() {
|
|
||||||
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
|
||||||
}
|
|
||||||
|
|
||||||
xdgvideo="$(xdg-user-dir VIDEOS)"
|
|
||||||
if [[ $xdgvideo = "$HOME" ]]; then
|
|
||||||
unset xdgvideo
|
|
||||||
fi
|
|
||||||
mkdir -p "${xdgvideo:-$HOME/Videos}"
|
|
||||||
cd "${xdgvideo:-$HOME/Videos}" || exit
|
|
||||||
|
|
||||||
if pgrep wf-recorder > /dev/null; then
|
|
||||||
notify-send "Recording Stopped" "Stopped" -a 'Recorder' &
|
|
||||||
pkill wf-recorder &
|
|
||||||
else
|
|
||||||
if [[ "$1" == "--fullscreen-sound" ]]; then
|
|
||||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
|
||||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)"
|
|
||||||
elif [[ "$1" == "--fullscreen" ]]; then
|
|
||||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
|
||||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t
|
|
||||||
else
|
|
||||||
if ! region="$(slurp 2>&1)"; then
|
|
||||||
notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
|
||||||
if [[ "$1" == "--sound" ]]; then
|
|
||||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)"
|
|
||||||
else
|
|
||||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
@@ -96,7 +96,10 @@ Variants {
|
|||||||
left: true
|
left: true
|
||||||
right: true
|
right: true
|
||||||
}
|
}
|
||||||
color: CF.ColorUtils.transparentize(CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75), (bgRoot.wallpaperIsVideo ? 1 : 0))
|
color: {
|
||||||
|
if (!bgRoot.wallpaperSafetyTriggered || bgRoot.wallpaperIsVideo) return "transparent";
|
||||||
|
return CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75)
|
||||||
|
}
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
}
|
}
|
||||||
@@ -442,7 +445,7 @@ Variants {
|
|||||||
color: bgRoot.colText
|
color: bgRoot.colText
|
||||||
style: Text.Raised
|
style: Text.Raised
|
||||||
styleColor: Appearance.colors.colShadow
|
styleColor: Appearance.colors.colShadow
|
||||||
animateChange: true
|
animateChange: Config.options.background.clock.digital.animateChange
|
||||||
}
|
}
|
||||||
component ClockStatusText: Row {
|
component ClockStatusText: Row {
|
||||||
id: statusTextRow
|
id: statusTextRow
|
||||||
|
|||||||
+4
-3
@@ -13,13 +13,14 @@ Item {
|
|||||||
|
|
||||||
text: Qt.locale().toString(DateTime.clock.date, root.isMonth ? "MM" : "d")
|
text: Qt.locale().toString(DateTime.clock.date, root.isMonth ? "MM" : "d")
|
||||||
|
|
||||||
MaterialCookie {
|
MaterialShape {
|
||||||
|
id: bubble
|
||||||
z: 5
|
z: 5
|
||||||
sides: root.isMonth ? 1 : 4
|
// sides: root.isMonth ? 1 : 4
|
||||||
|
shape: root.isMonth ? MaterialShape.Shape.Pill : MaterialShape.Shape.Pentagon
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
color: root.isMonth ? Appearance.colors.colPrimaryContainer : Appearance.colors.colTertiaryContainer
|
color: root.isMonth ? Appearance.colors.colPrimaryContainer : Appearance.colors.colTertiaryContainer
|
||||||
implicitSize: targetSize
|
implicitSize: targetSize
|
||||||
constantlyRotate: Config.options.background.clock.cookie.constantlyRotate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ Item { // Bar content region
|
|||||||
|
|
||||||
// Background shadow
|
// Background shadow
|
||||||
Loader {
|
Loader {
|
||||||
active: Config.options.bar.showBackground && Config.options.bar.cornerStyle === 1
|
active: Config.options.bar.showBackground && Config.options.bar.cornerStyle === 1 && Config.options.bar.floatStyleShadow
|
||||||
anchors.fill: barBackground
|
anchors.fill: barBackground
|
||||||
sourceComponent: StyledRectangularShadow {
|
sourceComponent: StyledRectangularShadow {
|
||||||
anchors.fill: undefined // The loader's anchors act on this, and this should not have any anchor
|
anchors.fill: undefined // The loader's anchors act on this, and this should not have any anchor
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ Item {
|
|||||||
visible: Config.options.bar.utilButtons.showScreenRecord
|
visible: Config.options.bar.utilButtons.showScreenRecord
|
||||||
sourceComponent: CircleUtilButton {
|
sourceComponent: CircleUtilButton {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
onClicked: Quickshell.execDetached(["bash", "-c", "~/.config/hypr/hyprland/scripts/record.sh"])
|
onClicked: Quickshell.execDetached([Directories.recordScriptPath])
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
fill: 1
|
fill: 1
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Qt.labs.synchronizer
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
@@ -22,7 +23,6 @@ Scope { // Scope
|
|||||||
"name": Translation.tr("Elements")
|
"name": Translation.tr("Elements")
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
property int selectedTab: 0
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: cheatsheetLoader
|
id: cheatsheetLoader
|
||||||
@@ -31,6 +31,7 @@ Scope { // Scope
|
|||||||
sourceComponent: PanelWindow { // Window
|
sourceComponent: PanelWindow { // Window
|
||||||
id: cheatsheetRoot
|
id: cheatsheetRoot
|
||||||
visible: cheatsheetLoader.active
|
visible: cheatsheetLoader.active
|
||||||
|
property int selectedTab: 0
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
@@ -85,16 +86,16 @@ Scope { // Scope
|
|||||||
}
|
}
|
||||||
if (event.modifiers === Qt.ControlModifier) {
|
if (event.modifiers === Qt.ControlModifier) {
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1);
|
cheatsheetRoot.selectedTab = Math.min(cheatsheetRoot.selectedTab + 1, root.tabButtonList.length - 1);
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
} else if (event.key === Qt.Key_PageUp) {
|
||||||
root.selectedTab = Math.max(root.selectedTab - 1, 0);
|
cheatsheetRoot.selectedTab = Math.max(cheatsheetRoot.selectedTab - 1, 0);
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
} else if (event.key === Qt.Key_Tab) {
|
} else if (event.key === Qt.Key_Tab) {
|
||||||
root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length;
|
cheatsheetRoot.selectedTab = (cheatsheetRoot.selectedTab + 1) % root.tabButtonList.length;
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
} else if (event.key === Qt.Key_Backtab) {
|
} else if (event.key === Qt.Key_Backtab) {
|
||||||
root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length;
|
cheatsheetRoot.selectedTab = (cheatsheetRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length;
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,9 +141,8 @@ Scope { // Scope
|
|||||||
PrimaryTabBar { // Tab strip
|
PrimaryTabBar { // Tab strip
|
||||||
id: tabBar
|
id: tabBar
|
||||||
tabButtonList: root.tabButtonList
|
tabButtonList: root.tabButtonList
|
||||||
externalTrackedTab: root.selectedTab
|
Synchronizer on currentIndex {
|
||||||
function onCurrentIndexChanged(currentIndex) {
|
property alias source: cheatsheetRoot.selectedTab
|
||||||
root.selectedTab = currentIndex;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,12 +164,12 @@ Scope { // Scope
|
|||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIndex: tabBar.externalTrackedTab
|
currentIndex: cheatsheetRoot.selectedTab
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
contentWidthBehavior.enabled = true;
|
contentWidthBehavior.enabled = true;
|
||||||
contentHeightBehavior.enabled = true;
|
contentHeightBehavior.enabled = true;
|
||||||
tabBar.enableIndicatorAnimation = true;
|
tabBar.enableIndicatorAnimation = true;
|
||||||
root.selectedTab = currentIndex;
|
cheatsheetRoot.selectedTab = currentIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|||||||
@@ -159,6 +159,8 @@ Singleton {
|
|||||||
property color colTertiaryContainer: m3colors.m3tertiaryContainer
|
property color colTertiaryContainer: m3colors.m3tertiaryContainer
|
||||||
property color colTertiaryContainerHover: ColorUtils.mix(m3colors.m3tertiaryContainer, m3colors.m3onTertiaryContainer, 0.90)
|
property color colTertiaryContainerHover: ColorUtils.mix(m3colors.m3tertiaryContainer, m3colors.m3onTertiaryContainer, 0.90)
|
||||||
property color colTertiaryContainerActive: ColorUtils.mix(m3colors.m3tertiaryContainer, colLayer1Active, 0.54)
|
property color colTertiaryContainerActive: ColorUtils.mix(m3colors.m3tertiaryContainer, colLayer1Active, 0.54)
|
||||||
|
property color colOnTertiary: m3colors.m3onTertiary
|
||||||
|
property color colOnTertiaryContainer: m3colors.m3onTertiaryContainer
|
||||||
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
|
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
|
||||||
property color colSurfaceContainerLow: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency)
|
property color colSurfaceContainerLow: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency)
|
||||||
property color colSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency)
|
property color colSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency)
|
||||||
@@ -315,9 +317,9 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property QtObject clickBounce: QtObject {
|
property QtObject clickBounce: QtObject {
|
||||||
property int duration: 200
|
property int duration: 400
|
||||||
property int type: Easing.BezierSpline
|
property int type: Easing.BezierSpline
|
||||||
property list<real> bezierCurve: animationCurves.expressiveFastSpatial
|
property list<real> bezierCurve: animationCurves.expressiveDefaultSpatial
|
||||||
property int velocity: 850
|
property int velocity: 850
|
||||||
property Component numberAnimation: Component { NumberAnimation {
|
property Component numberAnimation: Component { NumberAnimation {
|
||||||
duration: root.animation.clickBounce.duration
|
duration: root.animation.clickBounce.duration
|
||||||
|
|||||||
@@ -165,6 +165,9 @@ Singleton {
|
|||||||
property bool dateInClock: true
|
property bool dateInClock: true
|
||||||
property bool constantlyRotate: false
|
property bool constantlyRotate: false
|
||||||
}
|
}
|
||||||
|
property JsonObject digital: JsonObject {
|
||||||
|
property bool animateChange: true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
property string wallpaperPath: ""
|
property string wallpaperPath: ""
|
||||||
@@ -194,6 +197,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
property bool bottom: false // Instead of top
|
property bool bottom: false // Instead of top
|
||||||
property int cornerStyle: 0 // 0: Hug | 1: Float | 2: Plain rectangle
|
property int cornerStyle: 0 // 0: Hug | 1: Float | 2: Plain rectangle
|
||||||
|
property bool floatStyleShadow: true // Show shadow behind bar when cornerStyle == 1 (Float)
|
||||||
property bool borderless: false // true for no grouping of items
|
property bool borderless: false // true for no grouping of items
|
||||||
property string topLeftIcon: "spark" // Options: "distro" or any icon name in ~/.config/quickshell/ii/assets/icons
|
property string topLeftIcon: "spark" // Options: "distro" or any icon name in ~/.config/quickshell/ii/assets/icons
|
||||||
property bool showBackground: true
|
property bool showBackground: true
|
||||||
@@ -376,6 +380,11 @@ Singleton {
|
|||||||
property int updateInterval: 3000
|
property int updateInterval: 3000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property JsonObject musicRecognition: JsonObject {
|
||||||
|
property int timeout: 16
|
||||||
|
property int interval: 4
|
||||||
|
}
|
||||||
|
|
||||||
property JsonObject search: JsonObject {
|
property JsonObject search: JsonObject {
|
||||||
property int nonAppResultDelay: 30 // This prevents lagging when typing
|
property int nonAppResultDelay: 30 // This prevents lagging when typing
|
||||||
property string engineBaseUrl: "https://www.google.com/search?q="
|
property string engineBaseUrl: "https://www.google.com/search?q="
|
||||||
@@ -419,10 +428,11 @@ Singleton {
|
|||||||
property bool bottom: false
|
property bool bottom: false
|
||||||
property bool valueScroll: true
|
property bool valueScroll: true
|
||||||
property bool clickless: false
|
property bool clickless: false
|
||||||
property real cornerRegionWidth: 250
|
property int cornerRegionWidth: 250
|
||||||
property real cornerRegionHeight: 2
|
property int cornerRegionHeight: 5
|
||||||
property bool visualize: false
|
property bool visualize: false
|
||||||
property bool clicklessCornerEnd: true
|
property bool clicklessCornerEnd: true
|
||||||
|
property int clicklessCornerVerticalOffset: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject quickToggles: JsonObject {
|
property JsonObject quickToggles: JsonObject {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ Singleton {
|
|||||||
property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`)
|
property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`)
|
||||||
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
|
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
|
||||||
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
|
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
|
||||||
|
property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`)
|
||||||
// Cleanup on init
|
// Cleanup on init
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`])
|
Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`])
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ Rectangle {
|
|||||||
property alias uniformCellSizes: rowLayout.uniformCellSizes
|
property alias uniformCellSizes: rowLayout.uniformCellSizes
|
||||||
property real spacing: 5
|
property real spacing: 5
|
||||||
property real padding: 0
|
property real padding: 0
|
||||||
property int clickIndex: rowLayout.clickIndex
|
property alias clickIndex: rowLayout.clickIndex
|
||||||
property int childrenCount: rowLayout.children.length
|
property alias childrenCount: rowLayout.childrenCount
|
||||||
|
|
||||||
property real contentWidth: {
|
property real contentWidth: {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
@@ -44,5 +44,6 @@ Rectangle {
|
|||||||
anchors.margins: root.padding
|
anchors.margins: root.padding
|
||||||
spacing: root.spacing
|
spacing: root.spacing
|
||||||
property int clickIndex: -1
|
property int clickIndex: -1
|
||||||
|
property int childrenCount: children.length
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import QtQuick.Shapes
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool loading: true
|
||||||
|
property double pullProgress: 0
|
||||||
|
|
||||||
|
// Size, color
|
||||||
|
property double implicitSize: 48
|
||||||
|
implicitWidth: implicitSize
|
||||||
|
implicitHeight: implicitSize
|
||||||
|
radius: Math.min(width, height) / 2
|
||||||
|
color: Appearance.colors.colPrimaryContainer
|
||||||
|
property double baseShapeSize: root.implicitSize * 0.7
|
||||||
|
property double leapZoomSize: root.baseShapeSize * 1.2
|
||||||
|
property double leapZoomProgress: 0
|
||||||
|
|
||||||
|
// Shape
|
||||||
|
property list<var> shapes: [
|
||||||
|
MaterialShape.Shape.SoftBurst,
|
||||||
|
MaterialShape.Shape.Cookie9Sided,
|
||||||
|
MaterialShape.Shape.Pentagon,
|
||||||
|
MaterialShape.Shape.Pill,
|
||||||
|
MaterialShape.Shape.Sunny,
|
||||||
|
MaterialShape.Shape.Cookie4Sided,
|
||||||
|
MaterialShape.Shape.Oval,
|
||||||
|
]
|
||||||
|
property int shapeIndex: 0
|
||||||
|
property double pullRotation: root.loading ? 0 : -(root.pullProgress * 360)
|
||||||
|
property double continuousRotation: 0
|
||||||
|
property double leapRotation: 0
|
||||||
|
rotation: pullRotation + continuousRotation + leapRotation
|
||||||
|
|
||||||
|
RotationAnimation on continuousRotation {
|
||||||
|
running: root.loading
|
||||||
|
duration: 12000
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
loops: Animation.Infinite
|
||||||
|
from: 0
|
||||||
|
to: 360
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
interval: 800
|
||||||
|
running: root.loading
|
||||||
|
repeat: true
|
||||||
|
onTriggered: leapAnimation.start()
|
||||||
|
}
|
||||||
|
ParallelAnimation {
|
||||||
|
id: leapAnimation
|
||||||
|
PropertyAction { target: root; property: "shapeIndex"; value: (root.shapeIndex + 1) % root.shapes.length }
|
||||||
|
RotationAnimation {
|
||||||
|
target: root
|
||||||
|
direction: RotationAnimation.Shortest
|
||||||
|
property: "leapRotation"
|
||||||
|
to: (root.leapRotation + 90) % 360
|
||||||
|
duration: 350
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: root
|
||||||
|
property: "leapZoomProgress"
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: 750
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialShape {
|
||||||
|
id: shape
|
||||||
|
anchors.centerIn: parent
|
||||||
|
shape: root.shapes[root.shapeIndex]
|
||||||
|
implicitSize: {
|
||||||
|
const leapZoomDiff = root.leapZoomSize - root.baseShapeSize
|
||||||
|
const progressFirstHalf = Math.min(root.leapZoomProgress, 0.5) * 2;
|
||||||
|
const progressSecondHalf = Math.max(root.leapZoomProgress - 0.5, 0) * 2;
|
||||||
|
return root.baseShapeSize + leapZoomDiff * progressFirstHalf - leapZoomDiff * progressSecondHalf;
|
||||||
|
}
|
||||||
|
color: Appearance.colors.colOnPrimaryContainer
|
||||||
|
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import qs.modules.common.widgets.shapes
|
||||||
|
import "shapes/material-shapes.js" as MaterialShapes
|
||||||
|
|
||||||
|
ShapeCanvas {
|
||||||
|
id: root
|
||||||
|
enum Shape {
|
||||||
|
Circle,
|
||||||
|
Square,
|
||||||
|
Slanted,
|
||||||
|
Arch,
|
||||||
|
Fan,
|
||||||
|
Arrow,
|
||||||
|
SemiCircle,
|
||||||
|
Oval,
|
||||||
|
Pill,
|
||||||
|
Triangle,
|
||||||
|
Diamond,
|
||||||
|
ClamShell,
|
||||||
|
Pentagon,
|
||||||
|
Gem,
|
||||||
|
Sunny,
|
||||||
|
VerySunny,
|
||||||
|
Cookie4Sided,
|
||||||
|
Cookie6Sided,
|
||||||
|
Cookie7Sided,
|
||||||
|
Cookie9Sided,
|
||||||
|
Cookie12Sided,
|
||||||
|
Ghostish,
|
||||||
|
Clover4Leaf,
|
||||||
|
Clover8Leaf,
|
||||||
|
Burst,
|
||||||
|
SoftBurst,
|
||||||
|
Boom,
|
||||||
|
SoftBoom,
|
||||||
|
Flower,
|
||||||
|
Puffy,
|
||||||
|
PuffyDiamond,
|
||||||
|
PixelCircle,
|
||||||
|
PixelTriangle,
|
||||||
|
Bun,
|
||||||
|
Heart
|
||||||
|
}
|
||||||
|
required property var shape
|
||||||
|
property double implicitSize
|
||||||
|
implicitHeight: implicitSize
|
||||||
|
implicitWidth: implicitSize
|
||||||
|
roundedPolygon: {
|
||||||
|
switch (root.shape) {
|
||||||
|
case MaterialShape.Shape.Circle: return MaterialShapes.getCircle();
|
||||||
|
case MaterialShape.Shape.Square: return MaterialShapes.getSquare();
|
||||||
|
case MaterialShape.Shape.Slanted: return MaterialShapes.getSlanted();
|
||||||
|
case MaterialShape.Shape.Arch: return MaterialShapes.getArch();
|
||||||
|
case MaterialShape.Shape.Fan: return MaterialShapes.getFan();
|
||||||
|
case MaterialShape.Shape.Arrow: return MaterialShapes.getArrow();
|
||||||
|
case MaterialShape.Shape.SemiCircle: return MaterialShapes.getSemiCircle();
|
||||||
|
case MaterialShape.Shape.Oval: return MaterialShapes.getOval();
|
||||||
|
case MaterialShape.Shape.Pill: return MaterialShapes.getPill();
|
||||||
|
case MaterialShape.Shape.Triangle: return MaterialShapes.getTriangle();
|
||||||
|
case MaterialShape.Shape.Diamond: return MaterialShapes.getDiamond();
|
||||||
|
case MaterialShape.Shape.ClamShell: return MaterialShapes.getClamShell();
|
||||||
|
case MaterialShape.Shape.Pentagon: return MaterialShapes.getPentagon();
|
||||||
|
case MaterialShape.Shape.Gem: return MaterialShapes.getGem();
|
||||||
|
case MaterialShape.Shape.Sunny: return MaterialShapes.getSunny();
|
||||||
|
case MaterialShape.Shape.VerySunny: return MaterialShapes.getVerySunny();
|
||||||
|
case MaterialShape.Shape.Cookie4Sided: return MaterialShapes.getCookie4Sided();
|
||||||
|
case MaterialShape.Shape.Cookie6Sided: return MaterialShapes.getCookie6Sided();
|
||||||
|
case MaterialShape.Shape.Cookie7Sided: return MaterialShapes.getCookie7Sided();
|
||||||
|
case MaterialShape.Shape.Cookie9Sided: return MaterialShapes.getCookie9Sided();
|
||||||
|
case MaterialShape.Shape.Cookie12Sided: return MaterialShapes.getCookie12Sided();
|
||||||
|
case MaterialShape.Shape.Ghostish: return MaterialShapes.getGhostish();
|
||||||
|
case MaterialShape.Shape.Clover4Leaf: return MaterialShapes.getClover4Leaf();
|
||||||
|
case MaterialShape.Shape.Clover8Leaf: return MaterialShapes.getClover8Leaf();
|
||||||
|
case MaterialShape.Shape.Burst: return MaterialShapes.getBurst();
|
||||||
|
case MaterialShape.Shape.SoftBurst: return MaterialShapes.getSoftBurst();
|
||||||
|
case MaterialShape.Shape.Boom: return MaterialShapes.getBoom();
|
||||||
|
case MaterialShape.Shape.SoftBoom: return MaterialShapes.getSoftBoom();
|
||||||
|
case MaterialShape.Shape.Flower: return MaterialShapes.getFlower();
|
||||||
|
case MaterialShape.Shape.Puffy: return MaterialShapes.getPuffy();
|
||||||
|
case MaterialShape.Shape.PuffyDiamond: return MaterialShapes.getPuffyDiamond();
|
||||||
|
case MaterialShape.Shape.PixelCircle: return MaterialShapes.getPixelCircle();
|
||||||
|
case MaterialShape.Shape.PixelTriangle: return MaterialShapes.getPixelTriangle();
|
||||||
|
case MaterialShape.Shape.Bun: return MaterialShapes.getBun();
|
||||||
|
case MaterialShape.Shape.Heart: return MaterialShapes.getHeart();
|
||||||
|
default: return MaterialShapes.getCircle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -2,7 +2,7 @@ import QtQuick
|
|||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
|
|
||||||
MaterialCookie {
|
MaterialShape {
|
||||||
id: root
|
id: root
|
||||||
property alias text: symbol.text
|
property alias text: symbol.text
|
||||||
property alias iconSize: symbol.iconSize
|
property alias iconSize: symbol.iconSize
|
||||||
@@ -13,7 +13,7 @@ MaterialCookie {
|
|||||||
color: Appearance.colors.colSecondaryContainer
|
color: Appearance.colors.colSecondaryContainer
|
||||||
colSymbol: Appearance.colors.colOnSecondaryContainer
|
colSymbol: Appearance.colors.colOnSecondaryContainer
|
||||||
|
|
||||||
sides: 5
|
shape: MaterialShape.Shape.Clover4Leaf
|
||||||
|
|
||||||
implicitSize: Math.max(symbol.implicitWidth, symbol.implicitHeight) + padding * 2
|
implicitSize: Math.max(symbol.implicitWidth, symbol.implicitHeight) + padding * 2
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ Rectangle {
|
|||||||
id: root
|
id: root
|
||||||
property alias materialIcon: icon.text
|
property alias materialIcon: icon.text
|
||||||
property alias text: noticeText.text
|
property alias text: noticeText.text
|
||||||
|
default property alias data: buttonRow.data
|
||||||
|
|
||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
color: Appearance.colors.colPrimaryContainer
|
color: Appearance.colors.colPrimaryContainer
|
||||||
@@ -28,13 +29,23 @@ Rectangle {
|
|||||||
color: Appearance.colors.colOnPrimaryContainer
|
color: Appearance.colors.colOnPrimaryContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
ColumnLayout {
|
||||||
id: noticeText
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignVCenter
|
spacing: 4
|
||||||
text: "Notice message"
|
|
||||||
color: Appearance.colors.colOnPrimaryContainer
|
StyledText {
|
||||||
wrapMode: Text.WordWrap
|
id: noticeText
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: "Notice message"
|
||||||
|
color: Appearance.colors.colOnPrimaryContainer
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: buttonRow
|
||||||
|
visible: children.length > 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Quickshell
|
|||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import Quickshell.Services.Notifications
|
import Quickshell.Services.Notifications
|
||||||
|
|
||||||
MaterialCookie { // App icon
|
MaterialShape { // App icon
|
||||||
id: root
|
id: root
|
||||||
property var appIcon: ""
|
property var appIcon: ""
|
||||||
property var summary: ""
|
property var summary: ""
|
||||||
@@ -21,8 +21,11 @@ MaterialCookie { // App icon
|
|||||||
property real smallAppIconSize: implicitSize * smallAppIconScale
|
property real smallAppIconSize: implicitSize * smallAppIconScale
|
||||||
|
|
||||||
implicitSize: 38 * scale
|
implicitSize: 38 * scale
|
||||||
sides: isUrgent ? 10 : 0
|
property list<var> urgentShapes: [
|
||||||
amplitude: implicitSize / 24
|
MaterialShape.Shape.VerySunny,
|
||||||
|
MaterialShape.Shape.SoftBurst,
|
||||||
|
]
|
||||||
|
shape: isUrgent ? urgentShapes[Math.floor(Math.random() * urgentShapes.length)] : MaterialShape.Shape.Circle
|
||||||
|
|
||||||
color: isUrgent ? Appearance.colors.colPrimary : Appearance.colors.colSecondaryContainer
|
color: isUrgent ? Appearance.colors.colPrimary : Appearance.colors.colSecondaryContainer
|
||||||
Loader {
|
Loader {
|
||||||
|
|||||||
+10
-5
@@ -7,9 +7,11 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool shown: true
|
property bool shown: true
|
||||||
property alias icon: cookieWrappedMaterialSymbol.text
|
property alias icon: shapeWidget.text
|
||||||
property alias title: widgetNameText.text
|
property alias title: widgetNameText.text
|
||||||
property alias description: widgetDescriptionText.text
|
property alias description: widgetDescriptionText.text
|
||||||
|
property alias shape: shapeWidget.shape
|
||||||
|
property alias descriptionHorizontalAlignment: widgetDescriptionText.horizontalAlignment
|
||||||
|
|
||||||
opacity: shown ? 1 : 0
|
opacity: shown ? 1 : 0
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
@@ -27,14 +29,16 @@ Item {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
spacing: 5
|
spacing: 5
|
||||||
|
|
||||||
CookieWrappedMaterialSymbol {
|
MaterialShapeWrappedMaterialSymbol {
|
||||||
id: cookieWrappedMaterialSymbol
|
id: shapeWidget
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
iconSize: 60
|
padding: 12
|
||||||
rotation: -60 * (1 - root.opacity)
|
iconSize: 56
|
||||||
|
rotation: -30 * (1 - root.opacity)
|
||||||
}
|
}
|
||||||
StyledText {
|
StyledText {
|
||||||
id: widgetNameText
|
id: widgetNameText
|
||||||
|
visible: title !== ""
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
font.pixelSize: Appearance.font.pixelSize.larger
|
font.pixelSize: Appearance.font.pixelSize.larger
|
||||||
font.family: Appearance.font.family.title
|
font.family: Appearance.font.family.title
|
||||||
@@ -43,6 +47,7 @@ Item {
|
|||||||
}
|
}
|
||||||
StyledText {
|
StyledText {
|
||||||
id: widgetDescriptionText
|
id: widgetDescriptionText
|
||||||
|
visible: description !== ""
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
font.pixelSize: Appearance.font.pixelSize.small
|
||||||
color: Appearance.m3colors.m3outline
|
color: Appearance.m3colors.m3outline
|
||||||
@@ -3,16 +3,20 @@ import qs.services
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import Qt.labs.synchronizer
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
spacing: 0
|
spacing: 0
|
||||||
required property var tabButtonList // Something like [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}]
|
required property var tabButtonList // Something like [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}]
|
||||||
required property var externalTrackedTab
|
property int currentIndex
|
||||||
property bool enableIndicatorAnimation: false
|
property bool enableIndicatorAnimation: false
|
||||||
property color colIndicator: Appearance?.colors.colPrimary ?? "#65558F"
|
property color colIndicator: Appearance?.colors.colPrimary ?? "#65558F"
|
||||||
property color colBorder: Appearance?.m3colors.m3outlineVariant ?? "#C6C6D0"
|
property color colBorder: Appearance?.m3colors.m3outlineVariant ?? "#C6C6D0"
|
||||||
signal currentIndexChanged(int index)
|
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
enableIndicatorAnimation = true
|
||||||
|
}
|
||||||
|
|
||||||
property bool centerTabBar: parent.width > 500
|
property bool centerTabBar: parent.width > 500
|
||||||
Layout.fillWidth: !centerTabBar
|
Layout.fillWidth: !centerTabBar
|
||||||
@@ -22,9 +26,8 @@ ColumnLayout {
|
|||||||
TabBar {
|
TabBar {
|
||||||
id: tabBar
|
id: tabBar
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
currentIndex: root.externalTrackedTab
|
Synchronizer on currentIndex {
|
||||||
onCurrentIndexChanged: {
|
property alias source: root.currentIndex
|
||||||
root.onCurrentIndexChanged(currentIndex)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Item {
|
background: Item {
|
||||||
@@ -42,10 +45,11 @@ ColumnLayout {
|
|||||||
Repeater {
|
Repeater {
|
||||||
model: root.tabButtonList
|
model: root.tabButtonList
|
||||||
delegate: PrimaryTabButton {
|
delegate: PrimaryTabButton {
|
||||||
selected: (index == root.externalTrackedTab)
|
selected: (index == root.currentIndex)
|
||||||
buttonText: modelData.name
|
buttonText: modelData.name
|
||||||
buttonIcon: modelData.icon
|
buttonIcon: modelData.icon
|
||||||
minimumWidth: 160
|
minimumWidth: 160
|
||||||
|
onClicked: root.currentIndex = index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,12 +58,6 @@ ColumnLayout {
|
|||||||
id: tabIndicator
|
id: tabIndicator
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: 3
|
height: 3
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onExternalTrackedTabChanged() {
|
|
||||||
root.enableIndicatorAnimation = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: indicator
|
id: indicator
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ TabButton {
|
|||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: buttonBackground
|
id: buttonBackground
|
||||||
radius: Appearance?.rounding.small ?? 7
|
radius: Appearance?.rounding.normal
|
||||||
implicitHeight: 37
|
implicitHeight: 37
|
||||||
color: (root.hovered ? root.colBackgroundHover : root.colBackground)
|
color: (root.hovered ? root.colBackgroundHover : root.colBackground)
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
|
|||||||
@@ -45,13 +45,25 @@ Switch {
|
|||||||
anchors.leftMargin: root.checked ? ((root.pressed || root.down) ? (22 * root.scale) : 24 * root.scale) : ((root.pressed || root.down) ? (2 * root.scale) : 8 * root.scale)
|
anchors.leftMargin: root.checked ? ((root.pressed || root.down) ? (22 * root.scale) : 24 * root.scale) : ((root.pressed || root.down) ? (2 * root.scale) : 8 * root.scale)
|
||||||
|
|
||||||
Behavior on anchors.leftMargin {
|
Behavior on anchors.leftMargin {
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
NumberAnimation {
|
||||||
|
duration: Appearance.animationCurves.expressiveFastSpatialDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
NumberAnimation {
|
||||||
|
duration: Appearance.animationCurves.expressiveFastSpatialDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
NumberAnimation {
|
||||||
|
duration: Appearance.animationCurves.expressiveFastSpatialDuration
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
|||||||
@@ -17,13 +17,17 @@ function findSuitableMaterialSymbol(summary = "") {
|
|||||||
'time': 'scheduleb',
|
'time': 'scheduleb',
|
||||||
'installed': 'download',
|
'installed': 'download',
|
||||||
'configuration reloaded': 'reset_wrench',
|
'configuration reloaded': 'reset_wrench',
|
||||||
|
'unable': 'question_mark',
|
||||||
|
"couldn't": 'question_mark',
|
||||||
'config': 'reset_wrench',
|
'config': 'reset_wrench',
|
||||||
'update': 'update',
|
'update': 'update',
|
||||||
'ai response': 'neurology',
|
'ai response': 'neurology',
|
||||||
'control': 'settings',
|
'control': 'settings',
|
||||||
'upsca': 'compare',
|
'upsca': 'compare',
|
||||||
|
'music': 'queue_music',
|
||||||
'install': 'deployed_code_update',
|
'install': 'deployed_code_update',
|
||||||
'startswith:file': 'folder_copy', // Declarative startsWith check
|
'startswith:file': 'folder_copy', // Declarative startsWith check
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const lowerSummary = summary.toLowerCase();
|
const lowerSummary = summary.toLowerCase();
|
||||||
|
|||||||
Submodule dots/.config/quickshell/ii/modules/common/widgets/shapes added at 8369a081ba
@@ -28,7 +28,10 @@ Scope {
|
|||||||
Connections {
|
Connections {
|
||||||
target: GlobalStates
|
target: GlobalStates
|
||||||
function onScreenLockedChanged() {
|
function onScreenLockedChanged() {
|
||||||
if (GlobalStates.screenLocked) lockContext.reset();
|
if (GlobalStates.screenLocked) {
|
||||||
|
lockContext.reset();
|
||||||
|
lockContext.tryFingerUnlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +116,7 @@ Scope {
|
|||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
if (Config.options.lock.useHyprlock) {
|
if (Config.options.lock.useHyprlock) {
|
||||||
Quickshell.execDetached(["hyprlock"])
|
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GlobalStates.screenLocked = true;
|
GlobalStates.screenLocked = true;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import qs
|
|||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
import Quickshell.Services.Pam
|
import Quickshell.Services.Pam
|
||||||
|
|
||||||
Scope {
|
Scope {
|
||||||
@@ -18,6 +19,7 @@ Scope {
|
|||||||
property string currentText: ""
|
property string currentText: ""
|
||||||
property bool unlockInProgress: false
|
property bool unlockInProgress: false
|
||||||
property bool showFailure: false
|
property bool showFailure: false
|
||||||
|
property bool fingerprintsConfigured: false
|
||||||
property var targetAction: LockContext.ActionEnum.Unlock
|
property var targetAction: LockContext.ActionEnum.Unlock
|
||||||
|
|
||||||
function resetTargetAction() {
|
function resetTargetAction() {
|
||||||
@@ -60,6 +62,34 @@ Scope {
|
|||||||
pam.start();
|
pam.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryFingerUnlock() {
|
||||||
|
if (root.fingerprintsConfigured) {
|
||||||
|
fingerPam.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopFingerPam() {
|
||||||
|
fingerPam.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: fingerprintCheckProc
|
||||||
|
running: true
|
||||||
|
command: ["bash", "-c", "fprintd-list $(whoami)"]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
id: fingerprintOutputCollector
|
||||||
|
onStreamFinished: {
|
||||||
|
root.fingerprintsConfigured = fingerprintOutputCollector.text.includes("Fingerprints for user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: (exitCode, exitStatus) => {
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
console.warn("fprintd-list command exited with error:", exitCode, exitStatus);
|
||||||
|
root.fingerprintsConfigured = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PamContext {
|
PamContext {
|
||||||
id: pam
|
id: pam
|
||||||
|
|
||||||
@@ -74,6 +104,7 @@ Scope {
|
|||||||
onCompleted: result => {
|
onCompleted: result => {
|
||||||
if (result == PamResult.Success) {
|
if (result == PamResult.Success) {
|
||||||
root.unlocked(root.targetAction);
|
root.unlocked(root.targetAction);
|
||||||
|
stopFingerPam();
|
||||||
} else {
|
} else {
|
||||||
root.clearText();
|
root.clearText();
|
||||||
root.unlockInProgress = false;
|
root.unlockInProgress = false;
|
||||||
@@ -83,4 +114,19 @@ Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PamContext {
|
||||||
|
id: fingerPam
|
||||||
|
|
||||||
|
configDirectory: "pam"
|
||||||
|
config: "fprintd.conf"
|
||||||
|
|
||||||
|
onCompleted: result => {
|
||||||
|
if (result == PamResult.Success) {
|
||||||
|
root.unlocked(root.targetAction);
|
||||||
|
stopFingerPam();
|
||||||
|
} else if (result == PamResult.Error){ // if timeout or etc..
|
||||||
|
tryFingerUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
import Quickshell.Services.UPower
|
import Quickshell.Services.UPower
|
||||||
import qs
|
import qs
|
||||||
import qs.services
|
import qs.services
|
||||||
@@ -7,6 +8,7 @@ import qs.modules.common
|
|||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import qs.modules.common.functions
|
import qs.modules.common.functions
|
||||||
import qs.modules.bar as Bar
|
import qs.modules.bar as Bar
|
||||||
|
import Quickshell
|
||||||
import Quickshell.Services.SystemTray
|
import Quickshell.Services.SystemTray
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -98,6 +100,23 @@ MouseArea {
|
|||||||
scale: root.toolbarScale
|
scale: root.toolbarScale
|
||||||
opacity: root.toolbarOpacity
|
opacity: root.toolbarOpacity
|
||||||
|
|
||||||
|
// Fingerprint
|
||||||
|
Loader {
|
||||||
|
Layout.leftMargin: 10
|
||||||
|
Layout.rightMargin: 6
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
active: root.context.fingerprintsConfigured
|
||||||
|
visible: active
|
||||||
|
|
||||||
|
sourceComponent: MaterialSymbol {
|
||||||
|
id: fingerprintIcon
|
||||||
|
fill: 1
|
||||||
|
text: "fingerprint"
|
||||||
|
iconSize: Appearance.font.pixelSize.hugeass
|
||||||
|
color: Appearance.colors.colOnSurfaceVariant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ToolbarTextField {
|
ToolbarTextField {
|
||||||
id: passwordBox
|
id: passwordBox
|
||||||
placeholderText: GlobalStates.screenUnlockFailed ? Translation.tr("Incorrect password") : Translation.tr("Enter password")
|
placeholderText: GlobalStates.screenUnlockFailed ? Translation.tr("Incorrect password") : Translation.tr("Enter password")
|
||||||
@@ -124,7 +143,17 @@ MouseArea {
|
|||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
root.context.resetClearTimer();
|
root.context.resetClearTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: passwordBox.width - 8
|
||||||
|
height: passwordBox.height
|
||||||
|
radius: height / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shake when wrong password
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
id: wrongPasswordShakeAnim
|
id: wrongPasswordShakeAnim
|
||||||
NumberAnimation { target: passwordBox; property: "x"; to: -30; duration: 50 }
|
NumberAnimation { target: passwordBox; property: "x"; to: -30; duration: 50 }
|
||||||
@@ -139,6 +168,17 @@ MouseArea {
|
|||||||
if (GlobalStates.screenUnlockFailed) wrongPasswordShakeAnim.restart();
|
if (GlobalStates.screenUnlockFailed) wrongPasswordShakeAnim.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We're drawing dots manually
|
||||||
|
color: ColorUtils.transparentize(Appearance.colors.colOnLayer1)
|
||||||
|
PasswordChars {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: passwordBox.padding
|
||||||
|
rightMargin: passwordBox.padding
|
||||||
|
}
|
||||||
|
length: root.context.currentText.length
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolbarButton {
|
ToolbarButton {
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.common.functions
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
StyledFlickable {
|
||||||
|
id: root
|
||||||
|
required property int length
|
||||||
|
contentWidth: dotsRow.implicitWidth
|
||||||
|
contentX: (Math.max(contentWidth - width, 0))
|
||||||
|
Behavior on contentX {
|
||||||
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
rightMargin: 14
|
||||||
|
Row {
|
||||||
|
id: dotsRow
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
leftMargin: 4
|
||||||
|
}
|
||||||
|
spacing: 10
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: Array(root.length)
|
||||||
|
}
|
||||||
|
delegate: Item {
|
||||||
|
id: charItem
|
||||||
|
required property int index
|
||||||
|
implicitWidth: 10
|
||||||
|
implicitHeight: 10
|
||||||
|
MaterialShape {
|
||||||
|
id: materialShape
|
||||||
|
anchors.centerIn: parent
|
||||||
|
property list<var> charShapes: [
|
||||||
|
MaterialShape.Shape.Clover4Leaf,
|
||||||
|
MaterialShape.Shape.Arrow,
|
||||||
|
MaterialShape.Shape.Pill,
|
||||||
|
MaterialShape.Shape.SoftBurst,
|
||||||
|
MaterialShape.Shape.Diamond,
|
||||||
|
MaterialShape.Shape.ClamShell,
|
||||||
|
MaterialShape.Shape.Pentagon,
|
||||||
|
]
|
||||||
|
shape: charShapes[charItem.index % charShapes.length]
|
||||||
|
// Animate on appearance
|
||||||
|
color: Appearance.colors.colPrimary
|
||||||
|
implicitSize: 0
|
||||||
|
opacity: 0
|
||||||
|
scale: 0.5
|
||||||
|
Component.onCompleted: {
|
||||||
|
appearAnim.start();
|
||||||
|
}
|
||||||
|
ParallelAnimation {
|
||||||
|
id: appearAnim
|
||||||
|
NumberAnimation {
|
||||||
|
target: materialShape
|
||||||
|
properties: "opacity"
|
||||||
|
to: 1
|
||||||
|
duration: 50
|
||||||
|
easing.type: Appearance.animation.elementMoveFast.type
|
||||||
|
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: materialShape
|
||||||
|
properties: "scale"
|
||||||
|
to: 1
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: materialShape
|
||||||
|
properties: "implicitSize"
|
||||||
|
to: 18
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||||
|
}
|
||||||
|
ColorAnimation {
|
||||||
|
target: materialShape
|
||||||
|
properties: "color"
|
||||||
|
from: Appearance.colors.colPrimary
|
||||||
|
to: Appearance.colors.colOnLayer1
|
||||||
|
duration: 1000
|
||||||
|
easing.type: Appearance.animation.elementMoveFast.type
|
||||||
|
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
auth sufficient pam_fprintd.so
|
||||||
@@ -21,7 +21,7 @@ Scope {
|
|||||||
readonly property real osdWidth: Appearance.sizes.osdWidth
|
readonly property real osdWidth: Appearance.sizes.osdWidth
|
||||||
readonly property real widgetWidth: Appearance.sizes.mediaControlsWidth
|
readonly property real widgetWidth: Appearance.sizes.mediaControlsWidth
|
||||||
readonly property real widgetHeight: Appearance.sizes.mediaControlsHeight
|
readonly property real widgetHeight: Appearance.sizes.mediaControlsHeight
|
||||||
property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
|
||||||
property list<real> visualizerPoints: []
|
property list<real> visualizerPoints: []
|
||||||
|
|
||||||
property bool hasPlasmaIntegration: false
|
property bool hasPlasmaIntegration: false
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ Toolbar {
|
|||||||
return "image_search";
|
return "image_search";
|
||||||
case RegionSelection.SnipAction.CharRecognition:
|
case RegionSelection.SnipAction.CharRecognition:
|
||||||
return "document_scanner";
|
return "document_scanner";
|
||||||
|
case RegionSelection.SnipAction.Record:
|
||||||
|
case RegionSelection.SnipAction.RecordWithSound:
|
||||||
|
return "videocam";
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,16 @@ import qs.modules.common.widgets
|
|||||||
import qs.services
|
import qs.services
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import Qt.labs.synchronizer
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import Qt.labs.synchronizer
|
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: root
|
id: root
|
||||||
visible: false
|
visible: false
|
||||||
|
color: "transparent"
|
||||||
WlrLayershell.namespace: "quickshell:regionSelector"
|
WlrLayershell.namespace: "quickshell:regionSelector"
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
@@ -26,7 +27,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Ask: sidebar AI; Ocr: tesseract
|
// TODO: Ask: sidebar AI; Ocr: tesseract
|
||||||
enum SnipAction { Copy, Edit, Search, CharRecognition }
|
enum SnipAction { Copy, Edit, Search, CharRecognition, Record, RecordWithSound }
|
||||||
enum SelectionMode { RectCorners, Circle }
|
enum SelectionMode { RectCorners, Circle }
|
||||||
property var action: RegionSelection.SnipAction.Copy
|
property var action: RegionSelection.SnipAction.Copy
|
||||||
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
||||||
@@ -175,14 +176,35 @@ PanelWindow {
|
|||||||
property real regionY: Math.min(dragStartY, draggingY)
|
property real regionY: Math.min(dragStartY, draggingY)
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: screenshotProcess
|
id: screenshotProc
|
||||||
running: true
|
running: true
|
||||||
command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(root.screen.name)}' '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`]
|
command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(root.screen.name)}' '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`]
|
||||||
onExited: (exitCode, exitStatus) => {
|
onExited: (exitCode, exitStatus) => {
|
||||||
root.visible = true;
|
|
||||||
if (root.enableContentRegions) imageDetectionProcess.running = true;
|
if (root.enableContentRegions) imageDetectionProcess.running = true;
|
||||||
|
root.preparationDone = !checkRecordingProc.running;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
property bool isRecording: root.action === RegionSelection.SnipAction.Record || root.action === RegionSelection.SnipAction.RecordWithSound
|
||||||
|
property bool recordingShouldStop: false
|
||||||
|
Process {
|
||||||
|
id: checkRecordingProc
|
||||||
|
running: isRecording
|
||||||
|
command: ["pidof", "wf-recorder"]
|
||||||
|
onExited: (exitCode, exitStatus) => {
|
||||||
|
root.preparationDone = !screenshotProc.running
|
||||||
|
root.recordingShouldStop = (exitCode === 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
property bool preparationDone: false
|
||||||
|
onPreparationDoneChanged: {
|
||||||
|
if (!preparationDone) return;
|
||||||
|
if (root.isRecording && root.recordingShouldStop) {
|
||||||
|
Quickshell.execDetached([Directories.recordScriptPath]);
|
||||||
|
root.dismiss();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: imageDetectionProcess
|
id: imageDetectionProcess
|
||||||
@@ -221,11 +243,16 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set command for action
|
// Set command for action
|
||||||
|
const rx = Math.round(root.regionX * root.monitorScale);
|
||||||
|
const ry = Math.round(root.regionY * root.monitorScale);
|
||||||
|
const rw = Math.round(root.regionWidth * root.monitorScale);
|
||||||
|
const rh = Math.round(root.regionHeight * root.monitorScale);
|
||||||
const cropBase = `magick ${StringUtils.shellSingleQuoteEscape(root.screenshotPath)} `
|
const cropBase = `magick ${StringUtils.shellSingleQuoteEscape(root.screenshotPath)} `
|
||||||
+ `-crop ${root.regionWidth * root.monitorScale}x${root.regionHeight * root.monitorScale}+${root.regionX * root.monitorScale}+${root.regionY * root.monitorScale}`
|
+ `-crop ${rw}x${rh}+${rx}+${ry}`
|
||||||
const cropToStdout = `${cropBase} -`
|
const cropToStdout = `${cropBase} -`
|
||||||
const cropInPlace = `${cropBase} '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`
|
const cropInPlace = `${cropBase} '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`
|
||||||
const cleanup = `rm '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`
|
const cleanup = `rm '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`
|
||||||
|
const slurpRegion = `${rx},${ry} ${rw}x${rh}`
|
||||||
const uploadAndGetUrl = (filePath) => {
|
const uploadAndGetUrl = (filePath) => {
|
||||||
return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'`
|
return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'`
|
||||||
}
|
}
|
||||||
@@ -242,6 +269,12 @@ PanelWindow {
|
|||||||
case RegionSelection.SnipAction.CharRecognition:
|
case RegionSelection.SnipAction.CharRecognition:
|
||||||
snipProc.command = ["bash", "-c", `${cropInPlace} && tesseract '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}' stdout -l $(tesseract --list-langs | awk 'NR>1{print $1}' | tr '\\n' '+' | sed 's/\\+$/\\n/') | wl-copy && ${cleanup}`]
|
snipProc.command = ["bash", "-c", `${cropInPlace} && tesseract '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}' stdout -l $(tesseract --list-langs | awk 'NR>1{print $1}' | tr '\\n' '+' | sed 's/\\+$/\\n/') | wl-copy && ${cleanup}`]
|
||||||
break;
|
break;
|
||||||
|
case RegionSelection.SnipAction.Record:
|
||||||
|
snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}'`]
|
||||||
|
break;
|
||||||
|
case RegionSelection.SnipAction.RecordWithSound:
|
||||||
|
snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}' --sound`]
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn("[Region Selector] Unknown snip action, skipping snip.");
|
console.warn("[Region Selector] Unknown snip action, skipping snip.");
|
||||||
root.dismiss();
|
root.dismiss();
|
||||||
|
|||||||
@@ -62,6 +62,18 @@ Scope {
|
|||||||
GlobalStates.regionSelectorOpen = true
|
GlobalStates.regionSelectorOpen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function record() {
|
||||||
|
root.action = RegionSelection.SnipAction.Record
|
||||||
|
root.selectionMode = RegionSelection.SelectionMode.RectCorners
|
||||||
|
GlobalStates.regionSelectorOpen = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordWithSound() {
|
||||||
|
root.action = RegionSelection.SnipAction.RecordWithSound
|
||||||
|
root.selectionMode = RegionSelection.SelectionMode.RectCorners
|
||||||
|
GlobalStates.regionSelectorOpen = true
|
||||||
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
target: "region"
|
target: "region"
|
||||||
|
|
||||||
@@ -71,10 +83,15 @@ Scope {
|
|||||||
function search() {
|
function search() {
|
||||||
root.search()
|
root.search()
|
||||||
}
|
}
|
||||||
|
|
||||||
function ocr() {
|
function ocr() {
|
||||||
root.ocr()
|
root.ocr()
|
||||||
}
|
}
|
||||||
|
function record() {
|
||||||
|
root.record()
|
||||||
|
}
|
||||||
|
function recordWithSound() {
|
||||||
|
root.recordWithSound()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
@@ -92,4 +109,14 @@ Scope {
|
|||||||
description: "Recognizes text in the selected region"
|
description: "Recognizes text in the selected region"
|
||||||
onPressed: root.ocr()
|
onPressed: root.ocr()
|
||||||
}
|
}
|
||||||
|
GlobalShortcut {
|
||||||
|
name: "regionRecord"
|
||||||
|
description: "Records the selected region"
|
||||||
|
onPressed: root.record()
|
||||||
|
}
|
||||||
|
GlobalShortcut {
|
||||||
|
name: "regionRecordWithSound"
|
||||||
|
description: "Records the selected region with sound"
|
||||||
|
onPressed: root.recordWithSound()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,10 +79,12 @@ Scope {
|
|||||||
implicitWidth: Config.options.sidebar.cornerOpen.cornerRegionWidth
|
implicitWidth: Config.options.sidebar.cornerOpen.cornerRegionWidth
|
||||||
implicitHeight: Config.options.sidebar.cornerOpen.cornerRegionHeight
|
implicitHeight: Config.options.sidebar.cornerOpen.cornerRegionHeight
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onMouseXChanged: {
|
onPositionChanged: {
|
||||||
if (!Config.options.sidebar.cornerOpen.clicklessCornerEnd) return;
|
if (!Config.options.sidebar.cornerOpen.clicklessCornerEnd) return;
|
||||||
if ((cornerWidget.isRight && mouseArea.mouseX >= mouseArea.width - 2)
|
const verticalOffset = Config.options.sidebar.cornerOpen.clicklessCornerVerticalOffset;
|
||||||
|| (cornerWidget.isLeft && mouseArea.mouseX <= 2))
|
const correctX = (cornerWidget.isRight && mouseArea.mouseX >= mouseArea.width - 2) || (cornerWidget.isLeft && mouseArea.mouseX <= 2);
|
||||||
|
const correctY = (cornerWidget.isTop && mouseArea.mouseY > verticalOffset || cornerWidget.isBottom && mouseArea.mouseY < mouseArea.height - verticalOffset);
|
||||||
|
if (correctX && correctY)
|
||||||
screenCorners.actionForCorner[cornerPanelWindow.corner]();
|
screenCorners.actionForCorner[cornerPanelWindow.corner]();
|
||||||
}
|
}
|
||||||
onEntered: {
|
onEntered: {
|
||||||
|
|||||||
@@ -55,6 +55,20 @@ ContentPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContentSubsection {
|
||||||
|
visible: Config.options.background.clock.style === "digital"
|
||||||
|
title: Translation.tr("Digital clock settings")
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "animation"
|
||||||
|
text: Translation.tr("Animate time change")
|
||||||
|
checked: Config.options.background.clock.digital.animateChange
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.background.clock.digital.animateChange = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ContentSubsection {
|
ContentSubsection {
|
||||||
visible: Config.options.background.clock.style === "cookie"
|
visible: Config.options.background.clock.style === "cookie"
|
||||||
title: Translation.tr("Cookie clock settings")
|
title: Translation.tr("Cookie clock settings")
|
||||||
@@ -787,22 +801,22 @@ ContentPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ConfigSwitch {
|
||||||
|
buttonIcon: "highlight_mouse_cursor"
|
||||||
|
text: Translation.tr("Hover to trigger")
|
||||||
|
checked: Config.options.sidebar.cornerOpen.clickless
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.sidebar.cornerOpen.clickless = checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledToolTip {
|
||||||
|
text: Translation.tr("When this is off you'll have to click")
|
||||||
|
}
|
||||||
|
}
|
||||||
Row {
|
Row {
|
||||||
ConfigSwitch {
|
|
||||||
buttonIcon: "highlight_mouse_cursor"
|
|
||||||
text: Translation.tr("Hover to trigger")
|
|
||||||
checked: Config.options.sidebar.cornerOpen.clickless
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.sidebar.cornerOpen.clickless = checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledToolTip {
|
|
||||||
text: Translation.tr("When this is off you'll have to click")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
ConfigSwitch {
|
||||||
enabled: !Config.options.sidebar.cornerOpen.clickless
|
enabled: !Config.options.sidebar.cornerOpen.clickless
|
||||||
text: Translation.tr("but force at absolute corner")
|
text: Translation.tr("Force hover open at absolute corner")
|
||||||
checked: Config.options.sidebar.cornerOpen.clicklessCornerEnd
|
checked: Config.options.sidebar.cornerOpen.clicklessCornerEnd
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
Config.options.sidebar.cornerOpen.clicklessCornerEnd = checked;
|
Config.options.sidebar.cornerOpen.clicklessCornerEnd = checked;
|
||||||
@@ -812,7 +826,29 @@ ContentPage {
|
|||||||
text: Translation.tr("When the previous option is off and this is on,\nyou can still hover the corner's end to open sidebar,\nand the remaining area can be used for volume/brightness scroll")
|
text: Translation.tr("When the previous option is off and this is on,\nyou can still hover the corner's end to open sidebar,\nand the remaining area can be used for volume/brightness scroll")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ConfigSpinBox {
|
||||||
|
icon: "arrow_cool_down"
|
||||||
|
text: Translation.tr("with vertical offset")
|
||||||
|
value: Config.options.sidebar.cornerOpen.clicklessCornerVerticalOffset
|
||||||
|
from: 0
|
||||||
|
to: 20
|
||||||
|
stepSize: 1
|
||||||
|
onValueChanged: {
|
||||||
|
Config.options.sidebar.cornerOpen.clicklessCornerVerticalOffset = value;
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
StyledToolTip {
|
||||||
|
extraVisibleCondition: mouseArea.containsMouse
|
||||||
|
text: Translation.tr("Why this is cool:\nFor non-0 values, it won't trigger when you reach the\nscreen corner along the horizontal edge, but it will when\nyou do along the vertical edge")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigRow {
|
ConfigRow {
|
||||||
uniform: true
|
uniform: true
|
||||||
ConfigSwitch {
|
ConfigSwitch {
|
||||||
|
|||||||
@@ -332,5 +332,33 @@ ContentPage {
|
|||||||
NoticeBox {
|
NoticeBox {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: Translation.tr('Not all options are available in this app. You should also check the config file by hitting the "Config file" button on the topleft corner or opening %1 manually.').arg(Directories.shellConfigPath)
|
text: Translation.tr('Not all options are available in this app. You should also check the config file by hitting the "Config file" button on the topleft corner or opening %1 manually.').arg(Directories.shellConfigPath)
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
RippleButtonWithIcon {
|
||||||
|
id: copyPathButton
|
||||||
|
property bool justCopied: false
|
||||||
|
Layout.fillWidth: false
|
||||||
|
buttonRadius: Appearance.rounding.small
|
||||||
|
materialIcon: justCopied ? "check" : "content_copy"
|
||||||
|
mainText: justCopied ? Translation.tr("Path copied") : Translation.tr("Copy path")
|
||||||
|
onClicked: {
|
||||||
|
copyPathButton.justCopied = true
|
||||||
|
Quickshell.clipboardText = FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse/config.json`);
|
||||||
|
revertTextTimer.restart();
|
||||||
|
}
|
||||||
|
colBackground: ColorUtils.transparentize(Appearance.colors.colPrimaryContainer)
|
||||||
|
colBackgroundHover: Appearance.colors.colPrimaryContainerHover
|
||||||
|
colRipple: Appearance.colors.colPrimaryContainerActive
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: revertTextTimer
|
||||||
|
interval: 1500
|
||||||
|
onTriggered: {
|
||||||
|
copyPathButton.justCopied = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,34 @@ ContentPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContentSection {
|
||||||
|
icon: "music_cast"
|
||||||
|
title: Translation.tr("Music Recognition")
|
||||||
|
|
||||||
|
ConfigSpinBox {
|
||||||
|
icon: "timer_off"
|
||||||
|
text: Translation.tr("Total duration timeout (s)")
|
||||||
|
value: Config.options.musicRecognition.timeout
|
||||||
|
from: 10
|
||||||
|
to: 100
|
||||||
|
stepSize: 2
|
||||||
|
onValueChanged: {
|
||||||
|
Config.options.musicRecognition.timeout = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigSpinBox {
|
||||||
|
icon: "av_timer"
|
||||||
|
text: Translation.tr("Polling interval (s)")
|
||||||
|
value: Config.options.musicRecognition.interval
|
||||||
|
from: 2
|
||||||
|
to: 10
|
||||||
|
stepSize: 1
|
||||||
|
onValueChanged: {
|
||||||
|
Config.options.musicRecognition.interval = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ContentSection {
|
ContentSection {
|
||||||
icon: "cell_tower"
|
icon: "cell_tower"
|
||||||
title: Translation.tr("Networking")
|
title: Translation.tr("Networking")
|
||||||
@@ -54,6 +82,7 @@ ContentPage {
|
|||||||
Config.options.resources.updateInterval = value;
|
Config.options.resources.updateInterval = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentSection {
|
ContentSection {
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
|||||||
icon: "neurology"
|
icon: "neurology"
|
||||||
title: Translation.tr("Large language models")
|
title: Translation.tr("Large language models")
|
||||||
description: Translation.tr("Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window")
|
description: Translation.tr("Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window")
|
||||||
|
shape: MaterialShape.Shape.PixelCircle
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollToBottomButton {
|
ScrollToBottomButton {
|
||||||
|
|||||||
@@ -23,12 +23,21 @@ Item {
|
|||||||
property var suggestionQuery: ""
|
property var suggestionQuery: ""
|
||||||
property var suggestionList: []
|
property var suggestionList: []
|
||||||
|
|
||||||
|
property bool pullLoading: false
|
||||||
|
property int pullLoadingGap: 80
|
||||||
|
property real normalizedPullDistance: Math.max(0, (1 - Math.exp(-booruResponseListView.verticalOvershoot / 50)) * booruResponseListView.dragging)
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Booru
|
target: Booru
|
||||||
function onTagSuggestion(query, suggestions) {
|
function onTagSuggestion(query, suggestions) {
|
||||||
root.suggestionQuery = query;
|
root.suggestionQuery = query;
|
||||||
root.suggestionList = suggestions;
|
root.suggestionList = suggestions;
|
||||||
}
|
}
|
||||||
|
function onRunningRequestsChanged() {
|
||||||
|
if (Booru.runningRequests === 0) {
|
||||||
|
root.pullLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var allCommands: [
|
property var allCommands: [
|
||||||
@@ -53,6 +62,8 @@ Item {
|
|||||||
if (root.responses.length > 0) {
|
if (root.responses.length > 0) {
|
||||||
const lastResponse = root.responses[root.responses.length - 1];
|
const lastResponse = root.responses[root.responses.length - 1];
|
||||||
root.handleInput(`${lastResponse.tags.join(" ")} ${parseInt(lastResponse.page) + 1}`);
|
root.handleInput(`${lastResponse.tags.join(" ")} ${parseInt(lastResponse.page) + 1}`);
|
||||||
|
} else {
|
||||||
|
root.handleInput("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -85,10 +96,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (inputText.trim() == "+") {
|
else if (inputText.trim() == "+") {
|
||||||
if (root.responses.length > 0) {
|
root.handleInput(`${root.commandPrefix}next`);
|
||||||
const lastResponse = root.responses[root.responses.length - 1]
|
|
||||||
root.handleInput(lastResponse.tags.join(" ") + ` ${parseInt(lastResponse.page) + 1}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Create tag list
|
// Create tag list
|
||||||
@@ -111,17 +119,23 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property real pageKeyScrollAmount: booruResponseListView.height / 2
|
||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
tagInputField.forceActiveFocus()
|
tagInputField.forceActiveFocus()
|
||||||
if (event.modifiers === Qt.NoModifier) {
|
if (event.modifiers === Qt.NoModifier) {
|
||||||
if (event.key === Qt.Key_PageUp) {
|
if (event.key === Qt.Key_PageUp) {
|
||||||
booruResponseListView.contentY = Math.max(0, booruResponseListView.contentY - booruResponseListView.height / 2)
|
if (booruResponseListView.atYBeginning) return;
|
||||||
|
booruResponseListView.contentY = Math.max(0, booruResponseListView.contentY - root.pageKeyScrollAmount)
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
} else if (event.key === Qt.Key_PageDown) {
|
} else if (event.key === Qt.Key_PageDown) {
|
||||||
booruResponseListView.contentY = Math.min(booruResponseListView.contentHeight - booruResponseListView.height / 2, booruResponseListView.contentY + booruResponseListView.height / 2)
|
if (booruResponseListView.atYEnd) return;
|
||||||
|
booruResponseListView.contentY = Math.min(booruResponseListView.contentHeight, booruResponseListView.contentY + root.pageKeyScrollAmount)
|
||||||
event.accepted = true
|
event.accepted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier) && event.key === Qt.Key_O) {
|
||||||
|
Booru.clearResponses()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -158,17 +172,20 @@ Item {
|
|||||||
mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4
|
mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4
|
||||||
|
|
||||||
property int lastResponseLength: 0
|
property int lastResponseLength: 0
|
||||||
|
Connections {
|
||||||
model: ScriptModel {
|
target: root
|
||||||
values: {
|
function onResponsesChanged() {
|
||||||
if(root.responses.length > booruResponseListView.lastResponseLength) {
|
if (root.responses.length > booruResponseListView.lastResponseLength) {
|
||||||
if (booruResponseListView.lastResponseLength > 0 && root.responses[booruResponseListView.lastResponseLength].provider != "system")
|
if (booruResponseListView.lastResponseLength > 0 && root.responses[booruResponseListView.lastResponseLength].provider != "system")
|
||||||
booruResponseListView.contentY = booruResponseListView.contentY + root.scrollOnNewResponse
|
booruResponseListView.contentY = booruResponseListView.contentY + root.scrollOnNewResponse
|
||||||
booruResponseListView.lastResponseLength = root.responses.length
|
booruResponseListView.lastResponseLength = root.responses.length
|
||||||
}
|
}
|
||||||
return root.responses
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: root.responses
|
||||||
|
}
|
||||||
delegate: BooruResponse {
|
delegate: BooruResponse {
|
||||||
responseData: modelData
|
responseData: modelData
|
||||||
tagInputField: root.inputField
|
tagInputField: root.inputField
|
||||||
@@ -176,6 +193,14 @@ Item {
|
|||||||
downloadPath: root.downloadPath
|
downloadPath: root.downloadPath
|
||||||
nsfwPath: root.nsfwPath
|
nsfwPath: root.nsfwPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDragEnded: { // Pull to load more
|
||||||
|
const gap = booruResponseListView.verticalOvershoot
|
||||||
|
if (gap > root.pullLoadingGap) {
|
||||||
|
root.pullLoading = true
|
||||||
|
root.handleInput(`${root.commandPrefix}next`)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PagePlaceholder {
|
PagePlaceholder {
|
||||||
@@ -185,6 +210,7 @@ Item {
|
|||||||
icon: "bookmark_heart"
|
icon: "bookmark_heart"
|
||||||
title: Translation.tr("Anime boorus")
|
title: Translation.tr("Anime boorus")
|
||||||
description: ""
|
description: ""
|
||||||
|
shape: MaterialShape.Shape.Bun
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollToBottomButton {
|
ScrollToBottomButton {
|
||||||
@@ -192,42 +218,24 @@ Item {
|
|||||||
target: booruResponseListView
|
target: booruResponseListView
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { // Queries awaiting response
|
MaterialLoadingIndicator {
|
||||||
|
id: loadingIndicator
|
||||||
z: 4
|
z: 4
|
||||||
anchors.left: parent.left
|
anchors {
|
||||||
anchors.right: parent.right
|
horizontalCenter: parent.horizontalCenter
|
||||||
anchors.bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
anchors.margins: 10
|
bottomMargin: 20 + (root.pullLoading ? 0 : Math.max(0, (root.normalizedPullDistance - 0.5) * 50))
|
||||||
implicitHeight: pendingBackground.implicitHeight
|
Behavior on bottomMargin {
|
||||||
opacity: Booru.runningRequests > 0 ? 1 : 0
|
NumberAnimation {
|
||||||
visible: opacity > 0
|
duration: 200
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
Behavior on opacity {
|
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
|
||||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: pendingBackground
|
|
||||||
color: Appearance.m3colors.m3inverseSurface
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
implicitHeight: pendingText.implicitHeight + 12 * 2
|
|
||||||
radius: Appearance.rounding.verysmall
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: pendingText
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.leftMargin: 12
|
|
||||||
anchors.rightMargin: 12
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
|
||||||
color: Appearance.m3colors.m3inverseOnSurface
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
text: Translation.tr("%1 queries pending").arg(Booru.runningRequests)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
loading: root.pullLoading || Booru.runningRequests > 0
|
||||||
|
pullProgress: Math.min(1, booruResponseListView.verticalOvershoot / root.pullLoadingGap * booruResponseListView.dragging)
|
||||||
|
scale: root.pullLoading ? 1 : Math.min(1, root.normalizedPullDistance * 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Qt.labs.synchronizer
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -58,9 +59,8 @@ Item {
|
|||||||
id: tabBar
|
id: tabBar
|
||||||
visible: root.tabButtonList.length > 1
|
visible: root.tabButtonList.length > 1
|
||||||
tabButtonList: root.tabButtonList
|
tabButtonList: root.tabButtonList
|
||||||
externalTrackedTab: root.selectedTab
|
Synchronizer on currentIndex {
|
||||||
function onCurrentIndexChanged(currentIndex) {
|
property alias source: root.selectedTab
|
||||||
root.selectedTab = currentIndex
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ Item {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
currentIndex: tabBar.externalTrackedTab
|
currentIndex: root.selectedTab
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
tabBar.enableIndicatorAnimation = true
|
tabBar.enableIndicatorAnimation = true
|
||||||
root.selectedTab = currentIndex
|
root.selectedTab = currentIndex
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ Rectangle {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.rightMargin: 10
|
anchors.rightMargin: 10
|
||||||
spacing: 7
|
spacing: 12
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
@@ -177,6 +177,20 @@ Rectangle {
|
|||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
spacing: 5
|
spacing: 5
|
||||||
|
|
||||||
|
AiMessageControlButton {
|
||||||
|
id: regenButton
|
||||||
|
buttonIcon: "refresh"
|
||||||
|
visible: messageData?.role === 'assistant'
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Ai.regenerate(root.messageIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledToolTip {
|
||||||
|
text: Translation.tr("Regenerate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AiMessageControlButton {
|
AiMessageControlButton {
|
||||||
id: copyButton
|
id: copyButton
|
||||||
buttonIcon: activated ? "inventory" : "content_copy"
|
buttonIcon: activated ? "inventory" : "content_copy"
|
||||||
@@ -254,28 +268,50 @@ Rectangle {
|
|||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.messageBlocks.length
|
model: ScriptModel {
|
||||||
delegate: Loader {
|
values: Array.from({ length: root.messageBlocks.length }, (msg, i) => {
|
||||||
required property int index
|
return ({
|
||||||
property var thisBlock: root.messageBlocks[index]
|
type: root.messageBlocks[i].type
|
||||||
Layout.fillWidth: true
|
})
|
||||||
// property var segment: thisBlock
|
});
|
||||||
property var segmentContent: thisBlock.content
|
}
|
||||||
property var segmentLang: thisBlock.lang
|
|
||||||
property var messageData: root.messageData
|
|
||||||
property var editing: root.editing
|
|
||||||
property var renderMarkdown: root.renderMarkdown
|
|
||||||
property var enableMouseSelection: root.enableMouseSelection
|
|
||||||
property bool thinking: root.messageData?.thinking ?? true
|
|
||||||
property bool done: root.messageData?.done ?? false
|
|
||||||
property bool completed: thisBlock.completed ?? false
|
|
||||||
|
|
||||||
property bool forceDisableChunkSplitting: root.messageData.content.includes("```")
|
delegate: DelegateChooser {
|
||||||
|
id: messageDelegate
|
||||||
source: thisBlock.type === "code" ? "MessageCodeBlock.qml" :
|
role: "type"
|
||||||
thisBlock.type === "think" ? "MessageThinkBlock.qml" :
|
|
||||||
"MessageTextBlock.qml"
|
|
||||||
|
|
||||||
|
DelegateChoice { roleValue: "code"; MessageCodeBlock {
|
||||||
|
required property int index
|
||||||
|
property var thisBlock: root.messageBlocks[index]
|
||||||
|
editing: root.editing
|
||||||
|
renderMarkdown: root.renderMarkdown
|
||||||
|
enableMouseSelection: root.enableMouseSelection
|
||||||
|
segmentContent: thisBlock.content
|
||||||
|
segmentLang: thisBlock.lang
|
||||||
|
messageData: root.messageData
|
||||||
|
} }
|
||||||
|
DelegateChoice { roleValue: "think"; MessageThinkBlock {
|
||||||
|
required property int index
|
||||||
|
property var thisBlock: root.messageBlocks[index]
|
||||||
|
editing: root.editing
|
||||||
|
renderMarkdown: root.renderMarkdown
|
||||||
|
enableMouseSelection: root.enableMouseSelection
|
||||||
|
segmentContent: thisBlock.content
|
||||||
|
messageData: root.messageData
|
||||||
|
done: root.messageData?.done ?? false
|
||||||
|
completed: thisBlock.completed ?? false
|
||||||
|
} }
|
||||||
|
DelegateChoice { roleValue: "text"; MessageTextBlock {
|
||||||
|
required property int index
|
||||||
|
property var thisBlock: root.messageBlocks[index]
|
||||||
|
editing: root.editing
|
||||||
|
renderMarkdown: root.renderMarkdown
|
||||||
|
enableMouseSelection: root.enableMouseSelection
|
||||||
|
segmentContent: thisBlock.content
|
||||||
|
messageData: root.messageData
|
||||||
|
done: root.messageData?.done ?? false
|
||||||
|
forceDisableChunkSplitting: root.messageData.content.includes("```")
|
||||||
|
} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,22 +13,20 @@ import org.kde.syntaxhighlighting
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
// These are needed on the parent loader
|
// These are needed on the parent loader
|
||||||
property bool editing: parent?.editing ?? false
|
property bool editing: false
|
||||||
property bool renderMarkdown: parent?.renderMarkdown ?? true
|
property bool renderMarkdown: true
|
||||||
property bool enableMouseSelection: parent?.enableMouseSelection ?? false
|
property bool enableMouseSelection: false
|
||||||
property var segmentContent: parent?.segmentContent ?? ({})
|
property var segmentContent: ({})
|
||||||
property var segmentLang: parent?.segmentLang ?? "txt"
|
property var segmentLang: "txt"
|
||||||
|
property var messageData: {}
|
||||||
property bool isCommandRequest: segmentLang === "command"
|
property bool isCommandRequest: segmentLang === "command"
|
||||||
property var displayLang: (isCommandRequest ? "bash" : segmentLang)
|
property var displayLang: (isCommandRequest ? "bash" : segmentLang)
|
||||||
property var messageData: parent?.messageData ?? {}
|
|
||||||
|
|
||||||
property real codeBlockBackgroundRounding: Appearance.rounding.small
|
property real codeBlockBackgroundRounding: Appearance.rounding.small
|
||||||
property real codeBlockHeaderPadding: 3
|
property real codeBlockHeaderPadding: 3
|
||||||
property real codeBlockComponentSpacing: 2
|
property real codeBlockComponentSpacing: 2
|
||||||
|
|
||||||
spacing: codeBlockComponentSpacing
|
spacing: codeBlockComponentSpacing
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
Rectangle { // Code background
|
Rectangle { // Code background
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -14,17 +14,17 @@ import Quickshell.Hyprland
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
// These are needed on the parent loader
|
// These are needed on the parent loader
|
||||||
property bool editing: parent?.editing ?? false
|
property bool editing: false
|
||||||
property bool renderMarkdown: parent?.renderMarkdown ?? true
|
property bool renderMarkdown: true
|
||||||
property bool enableMouseSelection: parent?.enableMouseSelection ?? false
|
property bool enableMouseSelection: false
|
||||||
property string segmentContent: parent?.segmentContent ?? ({})
|
property var segmentContent: ({})
|
||||||
property var messageData: parent?.messageData ?? {}
|
property var messageData: {}
|
||||||
property bool done: parent?.done ?? true
|
property bool done: true
|
||||||
property list<string> renderedLatexHashes: []
|
property bool forceDisableChunkSplitting: false
|
||||||
|
|
||||||
|
property list<string> renderedLatexHashes: []
|
||||||
property string renderedSegmentContent: ""
|
property string renderedSegmentContent: ""
|
||||||
property string shownText: ""
|
property string shownText: ""
|
||||||
property bool forceDisableChunkSplitting: parent?.forceDisableChunkSplitting ?? false
|
|
||||||
property bool fadeChunkSplitting: !forceDisableChunkSplitting && !editing && !/\n\|/.test(shownText) && Config.options.sidebar.ai.textFadeIn
|
property bool fadeChunkSplitting: !forceDisableChunkSplitting && !editing && !/\n\|/.test(shownText) && Config.options.sidebar.ai.textFadeIn
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import Qt5Compat.GraphicalEffects
|
|||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
// These are needed on the parent loader
|
// These are needed on the parent loader
|
||||||
property bool editing: parent?.editing ?? false
|
property bool editing: false
|
||||||
property bool renderMarkdown: parent?.renderMarkdown ?? true
|
property bool renderMarkdown: true
|
||||||
property bool enableMouseSelection: parent?.enableMouseSelection ?? false
|
property bool enableMouseSelection: false
|
||||||
property string segmentContent: parent?.segmentContent ?? ({})
|
property var segmentContent: ({})
|
||||||
property var messageData: parent?.messageData ?? {}
|
property var messageData: {}
|
||||||
property bool done: parent?.done ?? true
|
property bool done: true
|
||||||
property bool completed: parent?.completed ?? false
|
property bool completed: false
|
||||||
|
|
||||||
property real thinkBlockBackgroundRounding: Appearance.rounding.small
|
property real thinkBlockBackgroundRounding: Appearance.rounding.small
|
||||||
property real thinkBlockHeaderPaddingVertical: 3
|
property real thinkBlockHeaderPaddingVertical: 3
|
||||||
|
|||||||
@@ -100,9 +100,7 @@ Rectangle {
|
|||||||
id: tagsFlickable
|
id: tagsFlickable
|
||||||
visible: root.responseData.tags.length > 0
|
visible: root.responseData.tags.length > 0
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.fillWidth: {
|
Layout.fillWidth: true
|
||||||
return true
|
|
||||||
}
|
|
||||||
implicitHeight: tagRowLayout.implicitHeight
|
implicitHeight: tagRowLayout.implicitHeight
|
||||||
contentWidth: tagRowLayout.implicitWidth
|
contentWidth: tagRowLayout.implicitWidth
|
||||||
|
|
||||||
|
|||||||
@@ -31,37 +31,12 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder when list is empty
|
// Placeholder when list is empty
|
||||||
Item {
|
PagePlaceholder {
|
||||||
anchors.fill: listview
|
shown: Notifications.list.length === 0
|
||||||
|
icon: "notifications_active"
|
||||||
visible: opacity > 0
|
description: Translation.tr("Nothing")
|
||||||
opacity: (Notifications.list.length === 0) ? 1 : 0
|
shape: MaterialShape.Shape.Ghostish
|
||||||
|
descriptionHorizontalAlignment: Text.AlignHCenter
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.menuDecel.duration
|
|
||||||
easing.type: Appearance.animation.menuDecel.type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
iconSize: 55
|
|
||||||
color: Appearance.m3colors.m3outline
|
|
||||||
text: "notifications_active"
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
color: Appearance.m3colors.m3outline
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: Translation.tr("No notifications")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ AbstractQuickPanel {
|
|||||||
readonly property real baseCellHeight: 56
|
readonly property real baseCellHeight: 56
|
||||||
|
|
||||||
// Toggles
|
// Toggles
|
||||||
readonly property list<string> availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile"]
|
readonly property list<string> availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile","musicRecognition"]
|
||||||
readonly property int columns: Config.options.sidebar.quickToggles.android.columns
|
readonly property int columns: Config.options.sidebar.quickToggles.android.columns
|
||||||
readonly property list<var> toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : []
|
readonly property list<var> toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : []
|
||||||
readonly property list<var> toggleRows: toggleRowsForList(toggles)
|
readonly property list<var> toggleRows: toggleRowsForList(toggles)
|
||||||
|
|||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
import qs
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.services
|
||||||
|
|
||||||
|
|
||||||
|
AndroidQuickToggleButton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int timeoutInterval: Config.options.musicRecognition.interval
|
||||||
|
property int timeoutDuration: Config.options.musicRecognition.timeout
|
||||||
|
|
||||||
|
|
||||||
|
property string monitorSource: "monitor" // "monitor" (system sound) , "input" (microphone)
|
||||||
|
|
||||||
|
name: Translation.tr("Identify Music")
|
||||||
|
statusText: toggled ? Translation.tr("Listening...") : monitorSource === "monitor" ? Translation.tr("System sound") : Translation.tr("Microphone")
|
||||||
|
toggled: false
|
||||||
|
buttonIcon: toggled ? "music_cast" : (monitorSource === "monitor" ? "music_note" : "frame_person_mic")
|
||||||
|
|
||||||
|
property var recognizedTrack: ({ title:"", subtitle:"", url:""})
|
||||||
|
|
||||||
|
function handleRecognition(jsonText) {
|
||||||
|
try {
|
||||||
|
var obj = JSON.parse(jsonText)
|
||||||
|
root.recognizedTrack = {
|
||||||
|
title: obj.track.title,
|
||||||
|
subtitle: obj.track.subtitle,
|
||||||
|
url: obj.track.url
|
||||||
|
}
|
||||||
|
musicReconizedProc.running = true
|
||||||
|
} catch(e) {
|
||||||
|
Quickshell.execDetached(["notify-send", Translation.tr("Couldn't recognize music"), Translation.tr("Perhaps what you're listening to is too niche"), "-a", "Shell"])
|
||||||
|
} finally {
|
||||||
|
root.toggled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StyledToolTip {
|
||||||
|
text: Translation.tr("Recognize music | Right-click to toggle source")
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.toggled = !root.toggled
|
||||||
|
recognizeMusicProc.running = root.toggled
|
||||||
|
musicReconizedProc.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
altAction: () => {
|
||||||
|
if (root.monitorSource === "monitor"){
|
||||||
|
root.monitorSource = "input"
|
||||||
|
return
|
||||||
|
}else {
|
||||||
|
root.monitorSource = "monitor"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: recognizeMusicProc
|
||||||
|
running: false
|
||||||
|
command: [`${Directories.scriptPath}/musicRecognition/recognize-music.sh`, "-i", root.timeoutInterval, "-t", root.timeoutDuration, "-s", root.monitorSource]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
handleRecognition(this.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: (exitCode, exitStatus) => {
|
||||||
|
if (exitCode === 1) {
|
||||||
|
Quickshell.execDetached(["notify-send", Translation.tr("Couldn't recognize music"), Translation.tr("Make sure you have songrec installed"), "-a", "Shell"])
|
||||||
|
root.toggled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: musicReconizedProc
|
||||||
|
running: false
|
||||||
|
command: [
|
||||||
|
"notify-send",
|
||||||
|
Translation.tr("Music Recognized"),
|
||||||
|
root.recognizedTrack.title + " - " + root.recognizedTrack.subtitle,
|
||||||
|
"-A", "Shazam",
|
||||||
|
"-A", "YouTube",
|
||||||
|
"-a", "Shell"
|
||||||
|
]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
if (this.text === "") return
|
||||||
|
if (this.text == 0){
|
||||||
|
Qt.openUrlExternally(root.recognizedTrack.url);
|
||||||
|
} else {
|
||||||
|
Qt.openUrlExternally("https://www.youtube.com/results?search_query=" + root.recognizedTrack.title + " - " + root.recognizedTrack.subtitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -232,4 +232,17 @@ DelegateChooser {
|
|||||||
cellSize: modelData.size
|
cellSize: modelData.size
|
||||||
} }
|
} }
|
||||||
|
|
||||||
|
DelegateChoice { roleValue: "musicRecognition"; AndroidMusicRecognition {
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
buttonIndex: root.startingIndex + index
|
||||||
|
buttonData: modelData
|
||||||
|
editMode: root.editMode
|
||||||
|
expandedSize: modelData.size > 1
|
||||||
|
baseCellWidth: root.baseCellWidth
|
||||||
|
baseCellHeight: root.baseCellHeight
|
||||||
|
cellSpacing: root.spacing
|
||||||
|
cellSize: modelData.size
|
||||||
|
} }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
COLOR_FILE_PATH="${XDG_STATE_HOME:-$HOME/.local/state}/quickshell/user/generated/color.txt"
|
||||||
|
|
||||||
|
# Define an array of possible VSCode settings file paths for various forks
|
||||||
|
settings_paths=(
|
||||||
|
"${XDG_CONFIG_HOME:-$HOME/.config}/Code/User/settings.json"
|
||||||
|
"${XDG_CONFIG_HOME:-$HOME/.config}/VSCodium/User/settings.json"
|
||||||
|
"${XDG_CONFIG_HOME:-$HOME/.config}/Code - OSS/User/settings.json"
|
||||||
|
"${XDG_CONFIG_HOME:-$HOME/.config}/Code - Insiders/User/settings.json"
|
||||||
|
"${XDG_CONFIG_HOME:-$HOME/.config}/Cursor/User/settings.json"
|
||||||
|
# Add more paths as needed for other forks
|
||||||
|
)
|
||||||
|
|
||||||
|
new_color=$(cat "$COLOR_FILE_PATH")
|
||||||
|
|
||||||
|
# Loop through each settings file path
|
||||||
|
for CODE_SETTINGS_PATH in "${settings_paths[@]}"; do
|
||||||
|
if [[ -f "$CODE_SETTINGS_PATH" ]]; then
|
||||||
|
# Try to update the key if it exists
|
||||||
|
if grep -q '"material-code.primaryColor"' "$CODE_SETTINGS_PATH"; then
|
||||||
|
sed -i -E \
|
||||||
|
"s/(\"material-code.primaryColor\"\s*:\s*\")[^\"]*(\")/\1${new_color}\2/" \
|
||||||
|
"$CODE_SETTINGS_PATH"
|
||||||
|
else # If the key is not already there, add it
|
||||||
|
sed -i '$ s/}/,\n "material-code.primaryColor": "'${new_color}'"\n}/' "$CODE_SETTINGS_PATH"
|
||||||
|
sed -i '$ s/,\n,/,/' "$CODE_SETTINGS_PATH"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
@@ -55,18 +55,8 @@ post_process() {
|
|||||||
local screen_height="$2"
|
local screen_height="$2"
|
||||||
local wallpaper_path="$3"
|
local wallpaper_path="$3"
|
||||||
|
|
||||||
|
|
||||||
handle_kde_material_you_colors &
|
handle_kde_material_you_colors &
|
||||||
|
"$SCRIPT_DIR/code/material-code-set-color.sh" &
|
||||||
# Determine the largest region on the wallpaper that's sufficiently un-busy to put widgets in
|
|
||||||
# if [ ! -f "$MATUGEN_DIR/scripts/least_busy_region.py" ]; then
|
|
||||||
# echo "Error: least_busy_region.py script not found in $MATUGEN_DIR/scripts/"
|
|
||||||
# else
|
|
||||||
# "$MATUGEN_DIR/scripts/least_busy_region.py" \
|
|
||||||
# --screen-width "$screen_width" --screen-height "$screen_height" \
|
|
||||||
# --width 300 --height 200 \
|
|
||||||
# "$wallpaper_path" > "$STATE_DIR"/user/generated/wallpaper/least_busy_region.json
|
|
||||||
# fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
check_and_prompt_upscale() {
|
check_and_prompt_upscale() {
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
INTERVAL=2
|
||||||
|
TOTAL_DURATION=30
|
||||||
|
MIN_VALID_RESULT_LENGTH=300
|
||||||
|
SOURCE_TYPE="monitor" # monitor | input
|
||||||
|
TMP_PATH="/tmp/quickshell/media/songrec"
|
||||||
|
TMP_RAW="$TMP_PATH/recording.raw"
|
||||||
|
TMP_MP3="$TMP_PATH/recording.mp3"
|
||||||
|
|
||||||
|
while getopts "i:t:s:" opt; do
|
||||||
|
case $opt in
|
||||||
|
i) INTERVAL=$OPTARG ;;
|
||||||
|
t) TOTAL_DURATION=$OPTARG ;;
|
||||||
|
s) SOURCE_TYPE=$OPTARG ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [ "$SOURCE_TYPE" = "monitor" ]; then
|
||||||
|
MONITOR_SOURCE=$(pactl list short sources 2>/dev/null | grep -m1 monitor | awk '{print $2}' || true)
|
||||||
|
elif [ "$SOURCE_TYPE" = "input" ]; then
|
||||||
|
MONITOR_SOURCE=$(pactl info | grep "Default Source:" | awk '{print $3}' || true)
|
||||||
|
else
|
||||||
|
echo "Invalid source type"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$MONITOR_SOURCE" ] || ! command -v songrec >/dev/null 2>&1; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -f "$TMP_RAW" "$TMP_MP3"
|
||||||
|
pkill -P $$ parec >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
mkdir -p "$TMP_PATH"
|
||||||
|
parec --device="$MONITOR_SOURCE" --format=s16le --rate=44100 --channels=2 > "$TMP_RAW" &
|
||||||
|
START_TIME=$(date +%s)
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
sleep "$INTERVAL"
|
||||||
|
CURRENT_TIME=$(date +%s)
|
||||||
|
ELAPSED=$((CURRENT_TIME - START_TIME))
|
||||||
|
|
||||||
|
if (( ELAPSED >= TOTAL_DURATION )); then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ffmpeg -f s16le -ar 44100 -ac 2 -i "$TMP_RAW" -acodec libmp3lame -y -hide_banner -loglevel error "$TMP_MP3" 2>/dev/null
|
||||||
|
RESULT=$(songrec audio-file-to-recognized-song "$TMP_MP3" 2>/dev/null || true)
|
||||||
|
|
||||||
|
if echo "$RESULT" | grep -q '"matches": \[' && [ ${#RESULT} -gt $MIN_VALID_RESULT_LENGTH ]; then
|
||||||
|
echo "$RESULT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
getdate() {
|
||||||
|
date '+%Y-%m-%d_%H.%M.%S'
|
||||||
|
}
|
||||||
|
getaudiooutput() {
|
||||||
|
pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2
|
||||||
|
}
|
||||||
|
getactivemonitor() {
|
||||||
|
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
||||||
|
}
|
||||||
|
|
||||||
|
xdgvideo="$(xdg-user-dir VIDEOS)"
|
||||||
|
if [[ $xdgvideo = "$HOME" ]]; then
|
||||||
|
unset xdgvideo
|
||||||
|
fi
|
||||||
|
mkdir -p "${xdgvideo:-$HOME/Videos}"
|
||||||
|
cd "${xdgvideo:-$HOME/Videos}" || exit
|
||||||
|
|
||||||
|
# parse --region <value> without modifying $@ so other flags like --fullscreen still work
|
||||||
|
ARGS=("$@")
|
||||||
|
MANUAL_REGION=""
|
||||||
|
SOUND_FLAG=0
|
||||||
|
FULLSCREEN_FLAG=0
|
||||||
|
for ((i=0;i<${#ARGS[@]};i++)); do
|
||||||
|
if [[ "${ARGS[i]}" == "--region" ]]; then
|
||||||
|
if (( i+1 < ${#ARGS[@]} )); then
|
||||||
|
MANUAL_REGION="${ARGS[i+1]}"
|
||||||
|
else
|
||||||
|
notify-send "Recording cancelled" "No region specified for --region" -a 'Recorder' & disown
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
elif [[ "${ARGS[i]}" == "--sound" ]]; then
|
||||||
|
SOUND_FLAG=1
|
||||||
|
elif [[ "${ARGS[i]}" == "--fullscreen" ]]; then
|
||||||
|
FULLSCREEN_FLAG=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if pgrep wf-recorder > /dev/null; then
|
||||||
|
notify-send "Recording Stopped" "Stopped" -a 'Recorder' &
|
||||||
|
pkill wf-recorder &
|
||||||
|
else
|
||||||
|
if [[ $FULLSCREEN_FLAG -eq 1 ]]; then
|
||||||
|
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||||
|
if [[ $SOUND_FLAG -eq 1 ]]; then
|
||||||
|
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)"
|
||||||
|
else
|
||||||
|
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# If a manual region was provided via --region, use it; otherwise run slurp as before.
|
||||||
|
if [[ -n "$MANUAL_REGION" ]]; then
|
||||||
|
region="$MANUAL_REGION"
|
||||||
|
else
|
||||||
|
if ! region="$(slurp 2>&1)"; then
|
||||||
|
notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
||||||
|
if [[ $SOUND_FLAG -eq 1 ]]; then
|
||||||
|
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)"
|
||||||
|
else
|
||||||
|
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
@@ -769,6 +769,18 @@ Singleton {
|
|||||||
root.pendingFilePath = CF.FileUtils.trimFileProtocol(filePath);
|
root.pendingFilePath = CF.FileUtils.trimFileProtocol(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function regenerate(messageIndex) {
|
||||||
|
if (messageIndex < 0 || messageIndex >= messageIDs.length) return;
|
||||||
|
const id = root.messageIDs[messageIndex];
|
||||||
|
const message = root.messageByID[id];
|
||||||
|
if (message.role !== "assistant") return;
|
||||||
|
// Remove all messages after this one
|
||||||
|
for (let i = root.messageIDs.length - 1; i >= messageIndex; i--) {
|
||||||
|
root.removeMessage(i);
|
||||||
|
}
|
||||||
|
requester.makeRequest();
|
||||||
|
}
|
||||||
|
|
||||||
function createFunctionOutputMessage(name, output, includeOutputInChat = true) {
|
function createFunctionOutputMessage(name, output, includeOutputInChat = true) {
|
||||||
return aiMessageComponent.createObject(root, {
|
return aiMessageComponent.createObject(root, {
|
||||||
"role": "user",
|
"role": "user",
|
||||||
|
|||||||
@@ -29,12 +29,18 @@ Singleton {
|
|||||||
property real lastVolume: 0
|
property real lastVolume: 0
|
||||||
function onVolumeChanged() {
|
function onVolumeChanged() {
|
||||||
if (!Config.options.audio.protection.enable) return;
|
if (!Config.options.audio.protection.enable) return;
|
||||||
|
const newVolume = sink.audio.volume;
|
||||||
|
// when resuming from suspend, we should not write volume to avoid pipewire volume reset issues
|
||||||
|
if (isNaN(newVolume) || newVolume === undefined || newVolume === null) {
|
||||||
|
lastReady = false;
|
||||||
|
lastVolume = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!lastReady) {
|
if (!lastReady) {
|
||||||
lastVolume = sink.audio.volume;
|
lastVolume = newVolume;
|
||||||
lastReady = true;
|
lastReady = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newVolume = sink.audio.volume;
|
|
||||||
const maxAllowedIncrease = Config.options.audio.protection.maxAllowedIncrease / 100;
|
const maxAllowedIncrease = Config.options.audio.protection.maxAllowedIncrease / 100;
|
||||||
const maxAllowed = Config.options.audio.protection.maxAllowed / 100;
|
const maxAllowed = Config.options.audio.protection.maxAllowed / 100;
|
||||||
|
|
||||||
@@ -45,9 +51,6 @@ Singleton {
|
|||||||
root.sinkProtectionTriggered(Translation.tr("Exceeded max allowed"));
|
root.sinkProtectionTriggered(Translation.tr("Exceeded max allowed"));
|
||||||
sink.audio.volume = Math.min(lastVolume, maxAllowed);
|
sink.audio.volume = Math.min(lastVolume, maxAllowed);
|
||||||
}
|
}
|
||||||
if (sink.ready && (isNaN(sink.audio.volume) || sink.audio.volume === undefined || sink.audio.volume === null)) {
|
|
||||||
sink.audio.volume = 0;
|
|
||||||
}
|
|
||||||
lastVolume = sink.audio.volume;
|
lastVolume = sink.audio.volume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import QtQuick
|
|||||||
*/
|
*/
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
property real minimumBrightnessAllowed: 0.00001 // Setting to 0 would kind of turn off the screen. We don't want that.
|
|
||||||
|
|
||||||
signal brightnessChanged()
|
signal brightnessChanged()
|
||||||
|
|
||||||
property var ddcMonitors: []
|
property var ddcMonitors: []
|
||||||
@@ -137,14 +135,14 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function syncBrightness() {
|
function syncBrightness() {
|
||||||
const brightnessValue = Math.max(monitor.multipliedBrightness, root.minimumBrightnessAllowed)
|
const brightnessValue = Math.max(monitor.multipliedBrightness, 0)
|
||||||
const rounded = Math.round(brightnessValue * monitor.rawMaxBrightness);
|
const rawValueRounded = Math.max(Math.floor(brightnessValue * monitor.rawMaxBrightness), 1);
|
||||||
setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rounded] : ["brightnessctl", "--class", "backlight", "s", rounded, "--quiet"];
|
setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rawValueRounded] : ["brightnessctl", "--class", "backlight", "s", rawValueRounded, "--quiet"];
|
||||||
setProc.startDetached();
|
setProc.startDetached();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setBrightness(value: real): void {
|
function setBrightness(value: real): void {
|
||||||
value = Math.max(root.minimumBrightnessAllowed, Math.min(1, value));
|
value = Math.max(0, Math.min(1, value));
|
||||||
monitor.brightness = value;
|
monitor.brightness = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
|
import Quickshell
|
||||||
import qs.services
|
import qs.services
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
@@ -169,15 +170,29 @@ ApplicationWindow {
|
|||||||
|
|
||||||
FloatingActionButton {
|
FloatingActionButton {
|
||||||
id: fab
|
id: fab
|
||||||
iconText: "edit"
|
property bool justCopied: false
|
||||||
buttonText: Translation.tr("Config file")
|
iconText: justCopied ? "check" : "edit"
|
||||||
|
buttonText: justCopied ? Translation.tr("Path copied") : Translation.tr("Config file")
|
||||||
expanded: navRail.expanded
|
expanded: navRail.expanded
|
||||||
downAction: () => {
|
downAction: () => {
|
||||||
Qt.openUrlExternally(`${Directories.config}/illogical-impulse/config.json`);
|
Qt.openUrlExternally(`${Directories.config}/illogical-impulse/config.json`);
|
||||||
}
|
}
|
||||||
|
altAction: () => {
|
||||||
|
Quickshell.clipboardText = CF.FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse/config.json`);
|
||||||
|
fab.justCopied = true;
|
||||||
|
revertTextTimer.restart()
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: revertTextTimer
|
||||||
|
interval: 1500
|
||||||
|
onTriggered: {
|
||||||
|
fab.justCopied = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
text: Translation.tr("Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json")
|
text: Translation.tr("Open the shell config file\nAlternatively right-click to copy path")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
pkgname=illogical-impulse-basic
|
pkgname=illogical-impulse-basic
|
||||||
pkgver=1.0
|
pkgver=1.0
|
||||||
pkgrel=1
|
pkgrel=2
|
||||||
pkgdesc='Illogical Impulse Basic Dependencies'
|
pkgdesc='Illogical Impulse Basic Dependencies'
|
||||||
arch=(any)
|
arch=(any)
|
||||||
license=(None)
|
license=(None)
|
||||||
depends=(
|
depends=(
|
||||||
axel
|
axel
|
||||||
bc
|
bc
|
||||||
coreutils
|
coreutils
|
||||||
cliphist
|
cliphist
|
||||||
cmake
|
cmake
|
||||||
curl
|
curl
|
||||||
rsync
|
wget
|
||||||
wget
|
ripgrep
|
||||||
ripgrep
|
jq
|
||||||
jq
|
meson
|
||||||
meson
|
xdg-user-dirs
|
||||||
xdg-user-dirs
|
# Used in install script
|
||||||
|
rsync
|
||||||
|
go-yq # https://github.com/mikefarah/yq
|
||||||
)
|
)
|
||||||
|
|||||||
+1
@@ -25,4 +25,5 @@ RDEPEND="
|
|||||||
dev-python/jq
|
dev-python/jq
|
||||||
dev-build/meson
|
dev-build/meson
|
||||||
x11-misc/xdg-user-dirs
|
x11-misc/xdg-user-dirs
|
||||||
|
app-misc/yq-go
|
||||||
"
|
"
|
||||||
@@ -24,6 +24,7 @@ printf "${STY_RST}"
|
|||||||
pause
|
pause
|
||||||
|
|
||||||
x sudo emerge --noreplace --quiet app-eselect/eselect-repository
|
x sudo emerge --noreplace --quiet app-eselect/eselect-repository
|
||||||
|
x sudo emerge --noreplace --quiet app-portage/smart-live-rebuild
|
||||||
|
|
||||||
if [[ -z $(eselect repository list | grep localrepo) ]]; then
|
if [[ -z $(eselect repository list | grep localrepo) ]]; then
|
||||||
v sudo eselect repository create localrepo
|
v sudo eselect repository create localrepo
|
||||||
@@ -53,6 +54,7 @@ v sudo sh -c 'cat ./sdata/dist-gentoo/additional-useflags >> /etc/portage/packag
|
|||||||
# Update system
|
# Update system
|
||||||
v sudo emerge --sync
|
v sudo emerge --sync
|
||||||
v sudo emerge --quiet --newuse --update --deep @world
|
v sudo emerge --quiet --newuse --update --deep @world
|
||||||
|
v sudo emerge --quiet @smart-live-rebuild
|
||||||
v sudo emerge --depclean
|
v sudo emerge --depclean
|
||||||
|
|
||||||
# Remove old ebuilds (if this isn't done the wildcard will fuck upon a version change)
|
# Remove old ebuilds (if this isn't done the wildcard will fuck upon a version change)
|
||||||
@@ -80,7 +82,6 @@ v sudo ebuild ${ebuild_dir}/dev-libs/hyprlang/hyprlang*9999.ebuild digest
|
|||||||
v sudo ebuild ${ebuild_dir}/dev-util/hyprwayland-scanner/hyprwayland-scanner*9999.ebuild digest
|
v sudo ebuild ${ebuild_dir}/dev-util/hyprwayland-scanner/hyprwayland-scanner*9999.ebuild digest
|
||||||
###### LIVE EBUILDS END
|
###### LIVE EBUILDS END
|
||||||
|
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
for i in "${metapkgs[@]}"; do
|
for i in "${metapkgs[@]}"; do
|
||||||
x sudo mkdir -p ${ebuild_dir}/app-misc/${i}
|
x sudo mkdir -p ${ebuild_dir}/app-misc/${i}
|
||||||
|
|||||||
@@ -2,3 +2,130 @@
|
|||||||
# It's not for directly running.
|
# It's not for directly running.
|
||||||
|
|
||||||
# This file is currently WIP.
|
# This file is currently WIP.
|
||||||
|
|
||||||
|
function install_home-manager(){
|
||||||
|
# https://nix-community.github.io/home-manager/index.xhtml#sec-install-standalone
|
||||||
|
local cmd=home-manager
|
||||||
|
# Maybe installed already, just not sourced yet
|
||||||
|
try source $HOME/.nix-profile/etc/profile.d/hm-session-vars.sh
|
||||||
|
command -v $cmd && return
|
||||||
|
|
||||||
|
x nix-channel --add https://nixos.org/channels/nixos-25.05 nixpkgs-home
|
||||||
|
x nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.05.tar.gz home-manager
|
||||||
|
x nix-channel --update
|
||||||
|
x env NIX_PATH="nixpkgs=$HOME/.nix-defexpr/channels/nixpkgs-home" nix-shell '<home-manager>' -A install
|
||||||
|
|
||||||
|
command -v $cmd && return
|
||||||
|
echo "Failed in installing $cmd."
|
||||||
|
echo "Please install it by yourself and then retry."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
function install_nix(){
|
||||||
|
# https://github.com/NixOS/experimental-nix-installer
|
||||||
|
local cmd=nix
|
||||||
|
|
||||||
|
x mkdir -p ${REPO_ROOT}/cache
|
||||||
|
x curl -JLo ${REPO_ROOT}/cache/nix-installer https://artifacts.nixos.org/experimental-installer
|
||||||
|
x sh ${REPO_ROOT}/cache/nix-installer install
|
||||||
|
try source '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
|
||||||
|
|
||||||
|
command -v $cmd && return
|
||||||
|
echo "Failed in installing $cmd."
|
||||||
|
echo "Please install it by yourself and then retry."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
function install_curl(){
|
||||||
|
local cmd=curl
|
||||||
|
|
||||||
|
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||||
|
x sudo pacman -Syu
|
||||||
|
x sudo pacman -S --noconfirm $cmd
|
||||||
|
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||||
|
x sudo apt update
|
||||||
|
x sudo apt install $cmd
|
||||||
|
fi
|
||||||
|
|
||||||
|
command -v $cmd && return
|
||||||
|
echo "Failed in installing $cmd."
|
||||||
|
echo "Please install it by yourself and then retry."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
function install_zsh(){
|
||||||
|
local cmd=zsh
|
||||||
|
|
||||||
|
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||||
|
x sudo pacman -Syu
|
||||||
|
x sudo pacman -S --noconfirm $cmd
|
||||||
|
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||||
|
x sudo apt update
|
||||||
|
x sudo apt install $cmd
|
||||||
|
fi
|
||||||
|
|
||||||
|
command -v $cmd && return
|
||||||
|
echo "Failed in installing $cmd."
|
||||||
|
echo "Please install it by yourself and then retry."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
function install_swaylock(){
|
||||||
|
local cmd=swaylock
|
||||||
|
echo "Detecting command \"$cmd\"..."
|
||||||
|
command -v $cmd && return
|
||||||
|
echo "Command \"$cmd\" not found, try to install..."
|
||||||
|
|
||||||
|
if [[ "$OS_DISTRO_ID" == "arch" || "$OS_DISTRO_ID_LIKE" == "arch" || "$OS_DISTRO_ID" == "cachyos" ]]; then
|
||||||
|
x sudo pacman -Syu
|
||||||
|
x sudo pacman -S --noconfirm $cmd
|
||||||
|
elif [[ "$OS_DISTRO_ID" == "debian" || "$OS_DISTRO_ID_LIKE" == "debian" ]]; then
|
||||||
|
x sudo apt update
|
||||||
|
x sudo apt install $cmd
|
||||||
|
fi
|
||||||
|
|
||||||
|
command -v $cmd && return
|
||||||
|
echo "Failed in installing $cmd."
|
||||||
|
echo "Please install it by yourself and then retry."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function hm_deps(){
|
||||||
|
SETUP_HM_DIR="${REPO_ROOT}/sdata/dist-nix/home-manager"
|
||||||
|
SETUP_USERNAME_NIXFILE="${SETUP_HM_DIR}/username.nix"
|
||||||
|
echo "\"$(whoami)\"" > "${SETUP_USERNAME_NIXFILE}"
|
||||||
|
x git add "${SETUP_USERNAME_NIXFILE}"
|
||||||
|
cd $SETUP_HM_DIR
|
||||||
|
x home-manager switch --flake .#illogical_impulse \
|
||||||
|
--extra-experimental-features nix-command \
|
||||||
|
--extra-experimental-features flakes
|
||||||
|
cd $REPO_ROOT
|
||||||
|
x git reset "${SETUP_USERNAME_NIXFILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
##################################################
|
||||||
|
if ! command -v curl >/dev/null 2>&1;then
|
||||||
|
echo -e "${STY_YELLOW}[$0]: \"curl\" not found.${STY_RST}"
|
||||||
|
showfun install_curl
|
||||||
|
v install_curl
|
||||||
|
fi
|
||||||
|
if ! command -v zsh >/dev/null 2>&1;then
|
||||||
|
echo -e "${STY_YELLOW}[$0]: \"zsh\" not found.${STY_RST}"
|
||||||
|
showfun install_zsh
|
||||||
|
v install_zsh
|
||||||
|
fi
|
||||||
|
if ! command -v swaylock >/dev/null 2>&1;then
|
||||||
|
echo -e "${STY_YELLOW}[$0]: \"swaylock\" not found.${STY_RST}"
|
||||||
|
showfun install_swaylock
|
||||||
|
v install_swaylock
|
||||||
|
fi
|
||||||
|
if ! command -v nix >/dev/null 2>&1;then
|
||||||
|
echo -e "${STY_YELLOW}[$0]: \"nix\" not found.${STY_RST}"
|
||||||
|
showfun install_nix
|
||||||
|
v install_nix
|
||||||
|
fi
|
||||||
|
if ! command -v home-manager >/dev/null 2>&1;then
|
||||||
|
echo -e "${STY_YELLOW}[$0]: \"home-manager\" not found.${STY_RST}"
|
||||||
|
showfun install_home-manager
|
||||||
|
v install_home-manager
|
||||||
|
fi
|
||||||
|
|
||||||
|
showfun hm_deps
|
||||||
|
v hm_deps
|
||||||
+73
-1
@@ -69,7 +69,11 @@ function pause(){
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
function remove_bashcomments_emptylines(){
|
function remove_bashcomments_emptylines(){
|
||||||
mkdir -p "$(dirname "$2")" && cat "$1" | sed -e 's/#.*//' -e '/^[[:space:]]*$/d' > "$2"
|
echo "pwd=$(pwd)"
|
||||||
|
echo "input=$1"
|
||||||
|
echo "output=$2"
|
||||||
|
mkdir -p "$(dirname "$2")"
|
||||||
|
cat "$1" | sed -e 's/#.*//' -e '/^[[:space:]]*$/d' > "$2"
|
||||||
}
|
}
|
||||||
function prevent_sudo_or_root(){
|
function prevent_sudo_or_root(){
|
||||||
case $(whoami) in
|
case $(whoami) in
|
||||||
@@ -287,3 +291,71 @@ function check_disk_space() {
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function auto_get_git_submodule(){
|
||||||
|
local git_submodules_list=()
|
||||||
|
|
||||||
|
while IFS= read -r path; do
|
||||||
|
[ -z "$path" ] && continue
|
||||||
|
git_submodules_list+=("$path")
|
||||||
|
done < <(git submodule status --recursive 2>/dev/null | awk '{print $2}')
|
||||||
|
|
||||||
|
local missing=0
|
||||||
|
|
||||||
|
for p in "${git_submodules_list[@]}"; do
|
||||||
|
if [ ! -d "$p" ] || [ -z "$(ls -A "$p" 2>/dev/null)" ]; then
|
||||||
|
missing=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$missing" -eq 1 ]; then
|
||||||
|
x git submodule update --init --recursive
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function backup_clashing_targets(){
|
||||||
|
# For non-recursive dirs/files under target_dir, only backup those which clashes with the ones under source_dir
|
||||||
|
# However, ignore the ones listed in ignored_list
|
||||||
|
|
||||||
|
# Deal with arguments
|
||||||
|
local source_dir="$1"
|
||||||
|
local target_dir="$2"
|
||||||
|
local backup_dir="$3"
|
||||||
|
local -a ignored_list=("${@:4}")
|
||||||
|
|
||||||
|
# Find clash dirs/files, save as clash_list
|
||||||
|
local clash_list=()
|
||||||
|
local source_list=($(ls -A "$source_dir"))
|
||||||
|
local target_list=($(ls -A "$target_dir"))
|
||||||
|
local -A target_map
|
||||||
|
for i in "${target_list[@]}"; do
|
||||||
|
target_map["$i"]=1
|
||||||
|
done
|
||||||
|
for i in "${source_list[@]}"; do
|
||||||
|
if [[ -n "${target_map[$i]}" ]]; then
|
||||||
|
clash_list+=("$i")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
local -A delk
|
||||||
|
for del in "${ignored_list[@]}" ; do delk[$del]=1 ; done
|
||||||
|
for k in "${!clash_list[@]}" ; do
|
||||||
|
[ "${delk[${clash_list[$k]}]-}" ] && unset 'clash_list[k]'
|
||||||
|
done
|
||||||
|
clash_list=("${clash_list[@]}")
|
||||||
|
|
||||||
|
# Construct args_includes for rsync
|
||||||
|
local args_includes=()
|
||||||
|
for i in "${clash_list[@]}"; do
|
||||||
|
if [[ -d "$target_dir/$i" ]]; then
|
||||||
|
args_includes+=(--include="/$i/")
|
||||||
|
args_includes+=(--include="/$i/**")
|
||||||
|
else
|
||||||
|
args_includes+=(--include="/$i")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
args_includes+=(--exclude='*')
|
||||||
|
|
||||||
|
x mkdir -p $backup_dir
|
||||||
|
x rsync -av --progress "${args_includes[@]}" "$target_dir/" "$backup_dir/"
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Check whether pkgs exist in AUR or repos of Arch.
|
||||||
|
#
|
||||||
|
# Do NOT abuse this since it consumes extra bandwidth from AUR server.
|
||||||
|
|
||||||
|
pkglistfile=$(mktemp)
|
||||||
|
pkglistfile_orig=${LIST_FILE_PATH}
|
||||||
|
pkglistfile_orig_s=${REPO_ROOT}/cache/dependencies_stripped.conf
|
||||||
|
#if ! "$(command -v curl)";then
|
||||||
|
# echo "Please install curl first.";exit 1
|
||||||
|
#fi
|
||||||
|
#if ! "$(command -v gzip)";then
|
||||||
|
# echo "Please install gzip first.";exit 1
|
||||||
|
#fi
|
||||||
|
#if ! "$(command -v pacman)";then
|
||||||
|
# echo "pacman not found, aborting...";exit 1
|
||||||
|
#fi
|
||||||
|
remove_bashcomments_emptylines $pkglistfile_orig $pkglistfile_orig_s
|
||||||
|
|
||||||
|
cat $pkglistfile_orig_s | sed "s_\ _\n_g" > $pkglistfile
|
||||||
|
|
||||||
|
echo "The non-existent pkgs in $pkglistfile_orig are listed as follows."
|
||||||
|
# Borrowed from https://bbs.archlinux.org/viewtopic.php?pid=1490795#p1490795
|
||||||
|
comm -23 <(sort -u $pkglistfile) <(sort -u <(curl https://aur.archlinux.org/packages.gz | gzip -cd | sort) <(pacman -Ssq))
|
||||||
|
echo "End of list. If nothing appears, then all pkgs exist."
|
||||||
|
rm $pkglistfile
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Handle args for subcmd: checkdeps
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
showhelp(){
|
||||||
|
echo -e "Syntax: $0 checkdeps [OPTIONS] <LIST_FILE_PATH>...
|
||||||
|
|
||||||
|
Check whether pkgs listed in <LIST_FILE_PATH> exist in AUR or repos of Arch.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Show this help message
|
||||||
|
"
|
||||||
|
}
|
||||||
|
# `man getopt` to see more
|
||||||
|
para=$(getopt \
|
||||||
|
-o h \
|
||||||
|
-l help \
|
||||||
|
-n "$0" -- "$@")
|
||||||
|
[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1
|
||||||
|
#####################################################################################
|
||||||
|
eval set -- "$para"
|
||||||
|
while true ; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help) showhelp;exit;;
|
||||||
|
--) shift;break ;;
|
||||||
|
*) sleep 0 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -f "$1" ]]; then
|
||||||
|
echo "Using list file \"$1\".";LIST_FILE_PATH="$1";shift 1
|
||||||
|
else
|
||||||
|
echo "Wrong path \"$1\" of list file.";exit 1
|
||||||
|
fi
|
||||||
@@ -541,6 +541,7 @@ if git remote get-url origin &>/dev/null; then
|
|||||||
log_info "Pulling changes from origin/$current_branch..."
|
log_info "Pulling changes from origin/$current_branch..."
|
||||||
if git pull; then
|
if git pull; then
|
||||||
log_success "Successfully pulled latest changes"
|
log_success "Successfully pulled latest changes"
|
||||||
|
git submodule update --init --recursive
|
||||||
else
|
else
|
||||||
log_warning "Failed to pull changes from remote. Continuing with local repository..."
|
log_warning "Failed to pull changes from remote. Continuing with local repository..."
|
||||||
log_info "You may need to resolve conflicts manually later."
|
log_info "You may need to resolve conflicts manually later."
|
||||||
@@ -3,6 +3,16 @@
|
|||||||
|
|
||||||
# shellcheck shell=bash
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# Notes by @clsty:
|
||||||
|
#
|
||||||
|
# I'm not the one who developed this script (see issue#2284 which discussed about the history).
|
||||||
|
# However it contains many unnecessary logics. This is typically what AI will do.
|
||||||
|
# I don't really care if it's AI-generated or not, it's just an extra option in addition to ./setup install, so as long as the users say it works, it should be fine.
|
||||||
|
# However, it's not easy to maintain something like this.
|
||||||
|
# The redundant logic should be cleaned up someday.
|
||||||
|
#
|
||||||
|
# This also applies for exp-update.tester.sh, TBH I don't think that file is really needed, and it also looks like AI-generated. Just guessing though.
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
#
|
#
|
||||||
# exp-update.sh - Enhanced dotfiles update script
|
# exp-update.sh - Enhanced dotfiles update script
|
||||||
@@ -834,6 +844,7 @@ if git remote get-url origin &>/dev/null; then
|
|||||||
else
|
else
|
||||||
if git pull --ff-only; then
|
if git pull --ff-only; then
|
||||||
log_success "Successfully pulled latest changes"
|
log_success "Successfully pulled latest changes"
|
||||||
|
git submodule update --init --recursive
|
||||||
# Verify we actually got new commits
|
# Verify we actually got new commits
|
||||||
if git rev-parse --verify HEAD@{1} &>/dev/null; then
|
if git rev-parse --verify HEAD@{1} &>/dev/null; then
|
||||||
if [[ "$(git rev-parse HEAD)" == "$(git rev-parse HEAD@{1})" ]]; then
|
if [[ "$(git rev-parse HEAD)" == "$(git rev-parse HEAD@{1})" ]]; then
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# exp-update-tester.sh - Test suite for exp-update.sh
|
# exp-update-tester.sh - Test suite for exp-update
|
||||||
#
|
#
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ log_header() { :; }
|
|||||||
log_die() { echo "ERROR: \$1"; exit 1; }
|
log_die() { echo "ERROR: \$1"; exit 1; }
|
||||||
STY_CYAN="" STY_RST="" STY_YELLOW=""
|
STY_CYAN="" STY_RST="" STY_YELLOW=""
|
||||||
|
|
||||||
# Set required environment variables for exp-update.sh
|
# Set required environment variables for exp-update/0.run.sh
|
||||||
SKIP_NOTICE=true
|
SKIP_NOTICE=true
|
||||||
REPO_ROOT="\$1"
|
REPO_ROOT="\$1"
|
||||||
CHECK_PACKAGES=false
|
CHECK_PACKAGES=false
|
||||||
@@ -139,7 +139,7 @@ VERBOSE=false
|
|||||||
NON_INTERACTIVE=true
|
NON_INTERACTIVE=true
|
||||||
SOURCE_ONLY=true
|
SOURCE_ONLY=true
|
||||||
|
|
||||||
source "$ORIGINAL_DIR/sdata/step/exp-update.sh"
|
source "$ORIGINAL_DIR/sdata/subcmd-exp-update/0.run.sh"
|
||||||
detected_dirs=\$(detect_repo_structure)
|
detected_dirs=\$(detect_repo_structure)
|
||||||
if [[ -n "\$detected_dirs" ]]; then
|
if [[ -n "\$detected_dirs" ]]; then
|
||||||
read -ra MONITOR_DIRS <<<"\$detected_dirs"
|
read -ra MONITOR_DIRS <<<"\$detected_dirs"
|
||||||
@@ -190,7 +190,7 @@ log_success() { :; }
|
|||||||
log_header() { :; }
|
log_header() { :; }
|
||||||
log_die() { echo "ERROR: \$1"; exit 1; }
|
log_die() { echo "ERROR: \$1"; exit 1; }
|
||||||
|
|
||||||
# Set required environment variables for exp-update.sh
|
# Set required environment variables for exp-update
|
||||||
SKIP_NOTICE=true
|
SKIP_NOTICE=true
|
||||||
REPO_ROOT="\$1"
|
REPO_ROOT="\$1"
|
||||||
CHECK_PACKAGES=false
|
CHECK_PACKAGES=false
|
||||||
@@ -200,7 +200,7 @@ VERBOSE=false
|
|||||||
NON_INTERACTIVE=true
|
NON_INTERACTIVE=true
|
||||||
SOURCE_ONLY=true
|
SOURCE_ONLY=true
|
||||||
|
|
||||||
source "$ORIGINAL_DIR/sdata/step/exp-update.sh"
|
source "$ORIGINAL_DIR/sdata/subcmd-exp-update/0.run.sh"
|
||||||
detected_dirs=\$(detect_repo_structure)
|
detected_dirs=\$(detect_repo_structure)
|
||||||
if [[ -n "\$detected_dirs" ]]; then
|
if [[ -n "\$detected_dirs" ]]; then
|
||||||
read -ra MONITOR_DIRS <<<"\$detected_dirs"
|
read -ra MONITOR_DIRS <<<"\$detected_dirs"
|
||||||
@@ -276,7 +276,7 @@ log_success() { :; }
|
|||||||
log_header() { :; }
|
log_header() { :; }
|
||||||
log_die() { echo "ERROR: \$1" >&2; exit 1; }
|
log_die() { echo "ERROR: \$1" >&2; exit 1; }
|
||||||
|
|
||||||
# FIXED: Set REPO_ROOT before sourcing exp-update.sh
|
# FIXED: Set REPO_ROOT before sourcing exp-update
|
||||||
REPO_ROOT="\$1"
|
REPO_ROOT="\$1"
|
||||||
export REPO_ROOT
|
export REPO_ROOT
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ HOME_UPDATE_IGNORE_FILE="/dev/null"
|
|||||||
|
|
||||||
# Source the production script to use the real should_ignore function
|
# Source the production script to use the real should_ignore function
|
||||||
# Redirect all unwanted output to stderr, then to /dev/null
|
# Redirect all unwanted output to stderr, then to /dev/null
|
||||||
source "$ORIGINAL_DIR/sdata/step/exp-update.sh" 2>/dev/null
|
source "$ORIGINAL_DIR/sdata/subcmd-exp-update/0.run.sh" 2>/dev/null
|
||||||
|
|
||||||
test_cases=(
|
test_cases=(
|
||||||
"\$REPO_ROOT/app.log:0"
|
"\$REPO_ROOT/app.log:0"
|
||||||
@@ -348,7 +348,7 @@ test_safe_read_security() {
|
|||||||
log_test "Testing safe_read uses secure assignment (printf -v)"
|
log_test "Testing safe_read uses secure assignment (printf -v)"
|
||||||
|
|
||||||
local safe_read_function
|
local safe_read_function
|
||||||
safe_read_function=$(awk '/^safe_read\(\) \{/,/^\}/' "$ORIGINAL_DIR/sdata/step/exp-update.sh")
|
safe_read_function=$(awk '/^safe_read\(\) \{/,/^\}/' "$ORIGINAL_DIR/sdata/subcmd-exp-update/0.run.sh")
|
||||||
|
|
||||||
if [[ -z "$safe_read_function" ]]; then
|
if [[ -z "$safe_read_function" ]]; then
|
||||||
log_fail "Could not find safe_read function"
|
log_fail "Could not find safe_read function"
|
||||||
@@ -547,7 +547,7 @@ log_success() { :; }
|
|||||||
log_header() { :; }
|
log_header() { :; }
|
||||||
log_die() { echo "ERROR: \$1" >&2; exit 1; }
|
log_die() { echo "ERROR: \$1" >&2; exit 1; }
|
||||||
|
|
||||||
# FIXED: Set REPO_ROOT before sourcing exp-update.sh
|
# FIXED: Set REPO_ROOT before sourcing exp-update
|
||||||
REPO_ROOT="\$1"
|
REPO_ROOT="\$1"
|
||||||
export REPO_ROOT
|
export REPO_ROOT
|
||||||
|
|
||||||
@@ -563,7 +563,7 @@ UPDATE_IGNORE_FILE="\${REPO_ROOT}/.updateignore"
|
|||||||
HOME_UPDATE_IGNORE_FILE="/dev/null"
|
HOME_UPDATE_IGNORE_FILE="/dev/null"
|
||||||
|
|
||||||
# Source the production script to use the real should_ignore function
|
# Source the production script to use the real should_ignore function
|
||||||
source "$ORIGINAL_DIR/sdata/step/exp-update.sh" 2>/dev/null
|
source "$ORIGINAL_DIR/sdata/subcmd-exp-update/0.run.sh" 2>/dev/null
|
||||||
|
|
||||||
# Load patterns into cache
|
# Load patterns into cache
|
||||||
load_ignore_patterns
|
load_ignore_patterns
|
||||||
@@ -649,7 +649,7 @@ VERBOSE=false
|
|||||||
NON_INTERACTIVE=true
|
NON_INTERACTIVE=true
|
||||||
SOURCE_ONLY=true
|
SOURCE_ONLY=true
|
||||||
|
|
||||||
source "$ORIGINAL_DIR/sdata/step/exp-update.sh" 2>/dev/null
|
source "$ORIGINAL_DIR/sdata/subcmd-exp-update/0.run.sh" 2>/dev/null
|
||||||
|
|
||||||
test_dir="/tmp/test-ensure-dir-\$\$"
|
test_dir="/tmp/test-ensure-dir-\$\$"
|
||||||
|
|
||||||
@@ -9,10 +9,8 @@ printf "${STY_CYAN}[$0]: Hi there! Before we start:${STY_RST}\n"
|
|||||||
printf "\n"
|
printf "\n"
|
||||||
printf "${STY_PURPLE}${STY_BOLD}[NEW] illogical-impulse is now powered by Quickshell.${STY_RST}\n"
|
printf "${STY_PURPLE}${STY_BOLD}[NEW] illogical-impulse is now powered by Quickshell.${STY_RST}\n"
|
||||||
printf "${STY_PURPLE}"
|
printf "${STY_PURPLE}"
|
||||||
|
printf '# NOTE: illogical-impulse on AGS is no longer supported.\n'
|
||||||
printf '# If you were using the old version with AGS and would like to keep it, do not run this script.\n'
|
printf '# If you were using the old version with AGS and would like to keep it, do not run this script.\n'
|
||||||
printf '# The AGS version, although uses less memory, has much worse performance (it uses Gtk3). \n'
|
|
||||||
printf '# If you aren'\''t running on ewaste, the Quickshell version is recommended. \n'
|
|
||||||
printf "# If you would like the AGS version anyway, run the following to switch to its branch first:\n ${STY_INVERT} git checkout ii-ags && ./install.sh ${STY_RST}\n"
|
|
||||||
printf "\n"
|
printf "\n"
|
||||||
pause
|
pause
|
||||||
printf "${STY_CYAN}${STY_BOLD}Quick overview about what this script does:${STY_RST}\n"
|
printf "${STY_CYAN}${STY_BOLD}Quick overview about what this script does:${STY_RST}\n"
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
# This script is meant to be sourced.
|
# This script is meant to be sourced.
|
||||||
# It's not for directly running.
|
# It's not for directly running.
|
||||||
|
printf "${STY_CYAN}[$0]: 1. Install dependencies\n${STY_RST}"
|
||||||
|
|
||||||
function outdate_detect(){
|
function outdate_detect(){
|
||||||
# Shallow clone prevent latest_commit_timestamp() from working.
|
# Shallow clone prevent latest_commit_timestamp() from working.
|
||||||
v git_auto_unshallow
|
x git_auto_unshallow
|
||||||
|
|
||||||
local source_path="$1"
|
local source_path="$1"
|
||||||
local target_path="$2"
|
local target_path="$2"
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
# This script is meant to be sourced.
|
# This script is meant to be sourced.
|
||||||
# It's not for directly running.
|
# It's not for directly running.
|
||||||
|
printf "${STY_CYAN}[$0]: 2. Setup for permissions/services etc\n${STY_RST}"
|
||||||
|
|
||||||
# shellcheck shell=bash
|
# shellcheck shell=bash
|
||||||
|
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
# This script is meant to be sourced.
|
||||||
|
# It's not for directly running.
|
||||||
|
|
||||||
|
# TODO: https://github.com/end-4/dots-hyprland/issues/2137
|
||||||
|
|
||||||
|
printf "${STY_CYAN}[$0]: 3. Copying config files (experimental YAML-based)${STY_RST}\n"
|
||||||
|
|
||||||
|
# Configuration file
|
||||||
|
CONFIG_FILE="sdata/subcmd-install/3.files.yaml"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ORIGINAL FUNCTIONS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
function warning_rsync_delete(){
|
||||||
|
printf "${STY_YELLOW}"
|
||||||
|
printf "The command below uses --delete for rsync which overwrites the destination folder.\n"
|
||||||
|
printf "${STY_RST}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function warning_rsync_normal(){
|
||||||
|
printf "${STY_YELLOW}"
|
||||||
|
printf "The command below uses rsync which overwrites the destination.\n"
|
||||||
|
printf "${STY_RST}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function backup_configs(){
|
||||||
|
backup_clashing_targets dots/.config $XDG_CONFIG_HOME "${BACKUP_DIR}/.config"
|
||||||
|
backup_clashing_targets dots/.local/share $XDG_DATA_HOME "${BACKUP_DIR}/.local/share"
|
||||||
|
printf "${STY_BLUE}Backup into \"${BACKUP_DIR}\" finished.${STY_RST}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ask_backup_configs(){
|
||||||
|
showfun backup_clashing_targets
|
||||||
|
printf "${STY_RED}"
|
||||||
|
printf "Would you like to backup clashing dirs/files under \"$XDG_CONFIG_HOME\" and \"$XDG_DATA_HOME\" to \"$BACKUP_DIR\"?"
|
||||||
|
printf "${STY_RST}"
|
||||||
|
while true;do
|
||||||
|
echo " y = Yes, backup"
|
||||||
|
echo " n/s = No, skip to next"
|
||||||
|
local p; read -p "====> " p
|
||||||
|
case $p in
|
||||||
|
[yY]) echo -e "${STY_BLUE}OK, doing backup...${STY_RST}" ;local backup=true;break ;;
|
||||||
|
[nNsS]) echo -e "${STY_BLUE}Alright, skipping...${STY_RST}" ;local backup=false;break ;;
|
||||||
|
*) echo -e "${STY_RED}Please enter [y/n].${STY_RST}";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if $backup;then backup_configs;fi
|
||||||
|
}
|
||||||
|
function auto_backup_configs(){
|
||||||
|
# Backup when $BACKUP_DIR does not exist
|
||||||
|
if [[ ! -d "$BACKUP_DIR" ]]; then backup_configs;fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
showfun auto_get_git_submodule
|
||||||
|
v auto_get_git_submodule
|
||||||
|
|
||||||
|
# In case some dirs does not exists
|
||||||
|
v mkdir -p $XDG_BIN_HOME $XDG_CACHE_HOME $XDG_CONFIG_HOME $XDG_DATA_HOME/icons
|
||||||
|
|
||||||
|
if [[ ! "${SKIP_BACKUP}" == true ]]; then
|
||||||
|
case $ask in
|
||||||
|
false) auto_backup_configs ;;
|
||||||
|
*) ask_backup_configs ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run user preference wizard
|
||||||
|
case $ask in
|
||||||
|
false) sleep 0 ;;
|
||||||
|
*) wizard_update_preferences ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Read patterns from YAML file
|
||||||
|
readarray patterns < <(yq -o=j -I=0 '.patterns[]' "$CONFIG_FILE")
|
||||||
|
|
||||||
|
# Process each pattern
|
||||||
|
for pattern in "${patterns[@]}"; do
|
||||||
|
from=$(echo "$pattern" | yq '.from' - | envsubst)
|
||||||
|
to=$(echo "$pattern" | yq '.to' - | envsubst)
|
||||||
|
mode=$(echo "$pattern" | yq '.mode' - | envsubst)
|
||||||
|
condition=$(echo "$pattern" | yq '.condition // "true"')
|
||||||
|
|
||||||
|
# Handle fontconfig fontset override
|
||||||
|
# If II_FONTSET_NAME is set and this is the fontconfig pattern, use the fontset instead
|
||||||
|
if [[ "$from" == "dots/.config/fontconfig" ]] && [[ -n "${II_FONTSET_NAME:-}" ]]; then
|
||||||
|
from="dots-extra/fontsets/${II_FONTSET_NAME}"
|
||||||
|
echo "Using fontset \"${II_FONTSET_NAME}\" for fontconfig"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if pattern should be processed
|
||||||
|
if ! should_process_pattern "$pattern"; then
|
||||||
|
# Format condition message nicely
|
||||||
|
if [[ "$condition" != "true" ]]; then
|
||||||
|
cond_type=$(echo "$condition" | yq -r '.type // ""')
|
||||||
|
cond_value=$(echo "$condition" | yq -r '.value // ""')
|
||||||
|
if [[ -n "$cond_type" && -n "$cond_value" ]]; then
|
||||||
|
echo "Skipping $from -> $to (condition not met: $cond_type == '$cond_value')"
|
||||||
|
else
|
||||||
|
echo "Skipping $from -> $to (condition not met)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Skipping $from -> $to (condition not met)"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Processing: $from -> $to (mode: $mode)"
|
||||||
|
|
||||||
|
# Build exclude arguments for rsync
|
||||||
|
excludes=()
|
||||||
|
if echo "$pattern" | yq -e '.excludes' >/dev/null 2>&1; then
|
||||||
|
while IFS= read -r exclude; do
|
||||||
|
excludes+=(--exclude "$exclude")
|
||||||
|
done < <(echo "$pattern" | yq -r '.excludes[]')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if source exists
|
||||||
|
if [[ ! -e "$from" ]]; then
|
||||||
|
echo "Warning: Source does not exist: $from (skipping)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure destination directory exists for files
|
||||||
|
if [[ -f "$from" ]]; then
|
||||||
|
v mkdir -p "$(dirname "$to")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute based on mode
|
||||||
|
case $mode in
|
||||||
|
"sync")
|
||||||
|
if [[ -d "$from" ]]; then
|
||||||
|
warning_rsync_delete
|
||||||
|
v rsync -av --delete "${excludes[@]}" "$from/" "$to/"
|
||||||
|
else
|
||||||
|
warning_rsync_normal
|
||||||
|
# For files, don't use trailing slash and don't use --delete
|
||||||
|
v rsync -av "${excludes[@]}" "$from" "$to"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"soft")
|
||||||
|
warning_rsync_normal
|
||||||
|
if [[ -d "$from" ]]; then
|
||||||
|
v rsync -av "${excludes[@]}" "$from/" "$to/"
|
||||||
|
else
|
||||||
|
# For files, don't use trailing slash
|
||||||
|
v rsync -av "${excludes[@]}" "$from" "$to"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"hard")
|
||||||
|
v cp -r "$from" "$to"
|
||||||
|
;;
|
||||||
|
"hard-backup")
|
||||||
|
if [[ -e "$to" ]]; then
|
||||||
|
if files_are_same "$from" "$to"; then
|
||||||
|
echo "Files are identical, skipping backup"
|
||||||
|
else
|
||||||
|
backup_number=$(get_next_backup_number "$to")
|
||||||
|
v mv "$to" "$to.old.$backup_number"
|
||||||
|
v cp -r "$from" "$to"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
v cp -r "$from" "$to"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"soft-backup")
|
||||||
|
if [[ -e "$to" ]]; then
|
||||||
|
if files_are_same "$from" "$to"; then
|
||||||
|
echo "Files are identical, skipping backup"
|
||||||
|
else
|
||||||
|
v cp -r "$from" "$to.new"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
v cp -r "$from" "$to"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"skip")
|
||||||
|
echo "Skipping $from"
|
||||||
|
;;
|
||||||
|
"skip-if-exists")
|
||||||
|
if [[ -e "$to" ]]; then
|
||||||
|
echo "Skipping $from (destination exists)"
|
||||||
|
else
|
||||||
|
v cp -r "$from" "$to"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown mode: $mode"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Prevent hyprland from not fully loaded
|
||||||
|
sleep 1
|
||||||
|
try hyprctl reload
|
||||||
|
|
||||||
|
# Rest of original script logic...
|
||||||
|
# (Keep the existing warning messages and file checks)
|
||||||
|
|
||||||
|
warn_files=()
|
||||||
|
warn_files_tests=()
|
||||||
|
warn_files_tests+=(/usr/local/lib/{GUtils-1.0.typelib,Gvc-1.0.typelib,libgutils.so,libgvc.so})
|
||||||
|
warn_files_tests+=(/usr/local/share/fonts/TTF/Rubik{,-Italic}'[wght]'.ttf)
|
||||||
|
warn_files_tests+=(/usr/local/share/licenses/ttf-rubik)
|
||||||
|
warn_files_tests+=(/usr/local/share/fonts/TTF/Gabarito-{Black,Bold,ExtraBold,Medium,Regular,SemiBold}.ttf)
|
||||||
|
warn_files_tests+=(/usr/local/share/licenses/ttf-gabarito)
|
||||||
|
warn_files_tests+=(/usr/local/share/icons/OneUI{,-dark,-light})
|
||||||
|
warn_files_tests+=(/usr/local/share/icons/Bibata-Modern-Classic)
|
||||||
|
warn_files_tests+=(/usr/local/bin/{LaTeX,res})
|
||||||
|
for i in ${warn_files_tests[@]}; do
|
||||||
|
echo $i
|
||||||
|
test -f $i && warn_files+=($i)
|
||||||
|
test -d $i && warn_files+=($i)
|
||||||
|
done
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# TODO: output the logs below to a temp file and cat that file, also show the path of the file so users will be able to read it again.
|
||||||
|
printf "\n"
|
||||||
|
printf "\n"
|
||||||
|
printf "\n"
|
||||||
|
printf "${STY_CYAN}[$0]: Finished${STY_RESET}\n"
|
||||||
|
printf "\n"
|
||||||
|
printf "${STY_CYAN}When starting Hyprland from your display manager (login screen) ${STY_RED} DO NOT SELECT UWSM ${STY_RESET}\n"
|
||||||
|
printf "\n"
|
||||||
|
printf "${STY_CYAN}If you are already running Hyprland,${STY_RESET}\n"
|
||||||
|
printf "${STY_CYAN}Press ${STY_BG_CYAN} Ctrl+Super+T ${STY_BG_CYAN} to select a wallpaper${STY_RESET}\n"
|
||||||
|
printf "${STY_CYAN}Press ${STY_BG_CYAN} Super+/ ${STY_CYAN} for a list of keybinds${STY_RESET}\n"
|
||||||
|
printf "\n"
|
||||||
|
printf "${STY_CYAN}For suggestions/hints after installation:${STY_RESET}\n"
|
||||||
|
printf "${STY_CYAN}${STY_UNDERLINE} https://ii.clsty.link/en/ii-qs/01setup/#post-installation ${STY_RESET}\n"
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
if [[ -z "${ILLOGICAL_IMPULSE_VIRTUAL_ENV}" ]]; then
|
||||||
|
printf "\n${STY_RED}[$0]: \!! Important \!! : Please ensure environment variable ${STY_RESET} \$ILLOGICAL_IMPULSE_VIRTUAL_ENV ${STY_RED} is set to proper value (by default \"~/.local/state/quickshell/.venv\"), or Quickshell config will not work. We have already provided this configuration in ~/.config/hypr/hyprland/env.conf, but you need to ensure it is included in hyprland.conf, and also a restart is needed for applying it.${STY_RESET}\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -z "${warn_files[@]}" ]]; then
|
||||||
|
printf "\n${STY_RED}[$0]: \!! Important \!! : Please delete ${STY_RESET} ${warn_files[*]} ${STY_RED} manually as soon as possible, since we\'re now using AUR package or local PKGBUILD to install them for Arch(based) Linux distros, and they'll take precedence over our installation, or at least take up more space.${STY_RESET}\n"
|
||||||
|
fi
|
||||||
@@ -1,84 +1,64 @@
|
|||||||
# This script is meant to be sourced.
|
# This script is meant to be sourced.
|
||||||
# It's not for directly running.
|
# It's not for directly running.
|
||||||
|
printf "${STY_CYAN}[$0]: 3. Copying config files\n${STY_RST}"
|
||||||
|
|
||||||
# shellcheck shell=bash
|
# shellcheck shell=bash
|
||||||
|
|
||||||
# TODO: https://github.com/end-4/dots-hyprland/issues/2137
|
# TODO: https://github.com/end-4/dots-hyprland/issues/2137
|
||||||
|
|
||||||
function warning_rsync(){
|
function warning_rsync_delete(){
|
||||||
printf "${STY_YELLOW}"
|
printf "${STY_YELLOW}"
|
||||||
printf "The commands using rsync will overwrite the destination when it exists already.\n"
|
printf "The command below uses --delete for rsync which overwrites the destination folder.\n"
|
||||||
printf "${STY_RST}"
|
printf "${STY_RST}"
|
||||||
}
|
}
|
||||||
|
|
||||||
function backup_clashing_targets(){
|
function warning_rsync_normal(){
|
||||||
# For dirs/files under target_dir, only backup those which clashes with the ones under source_dir
|
printf "${STY_YELLOW}"
|
||||||
|
printf "The command below uses rsync which overwrites the destination.\n"
|
||||||
|
printf "${STY_RST}"
|
||||||
|
}
|
||||||
|
|
||||||
# Deal with arguments
|
function backup_configs(){
|
||||||
local source_dir="$1"
|
backup_clashing_targets dots/.config $XDG_CONFIG_HOME "${BACKUP_DIR}/.config"
|
||||||
local target_dir="$2"
|
backup_clashing_targets dots/.local/share $XDG_DATA_HOME "${BACKUP_DIR}/.local/share"
|
||||||
local backup_dir="$3"
|
printf "${STY_BLUE}Backup into \"${BACKUP_DIR}\" finished.${STY_RST}\n"
|
||||||
|
|
||||||
# Find clash dirs/files, save as clash_list
|
|
||||||
local clash_list=()
|
|
||||||
local source_list=($(ls -A "$source_dir"))
|
|
||||||
local target_list=($(ls -A "$target_dir"))
|
|
||||||
declare -A target_map
|
|
||||||
for i in "${target_list[@]}"; do
|
|
||||||
target_map["$i"]=1
|
|
||||||
done
|
|
||||||
for i in "${source_list[@]}"; do
|
|
||||||
if [[ -n "${target_map[$i]}" ]]; then
|
|
||||||
clash_list+=("$i")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Construct args_includes for rsync
|
|
||||||
local args_includes=()
|
|
||||||
for i in "${clash_list[@]}"; do
|
|
||||||
if [[ -d "$target_dir/$i" ]]; then
|
|
||||||
args_includes+=(--include="/$i/")
|
|
||||||
args_includes+=(--include="/$i/**")
|
|
||||||
else
|
|
||||||
args_includes+=(--include="/$i")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
args_includes+=(--exclude='*')
|
|
||||||
|
|
||||||
x mkdir -p $backup_dir
|
|
||||||
x rsync -av --progress "${args_includes[@]}" "$target_dir/" "$backup_dir/"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ask_backup_configs(){
|
function ask_backup_configs(){
|
||||||
showfun backup_clashing_targets
|
showfun backup_clashing_targets
|
||||||
printf "${STY_RED}"
|
printf "${STY_RED}"
|
||||||
printf "Would you like to backup clashing dirs/files under \"$XDG_CONFIG_HOME\" and \"$XDG_DATA_HOME\" to \"$BACKUP_DIR\"?"
|
printf "Would you like to backup clashing dirs/files under \"$XDG_CONFIG_HOME\" and \"$XDG_DATA_HOME\" to \"$BACKUP_DIR\"?\n"
|
||||||
printf "${STY_RST}"
|
printf "${STY_RST}"
|
||||||
while true;do
|
while true;do
|
||||||
echo " y = Yes, backup"
|
echo " y = Yes, backup"
|
||||||
echo " n = No, skip to next"
|
echo " n/s = No, skip to next"
|
||||||
local p; read -p "====> " p
|
local p; read -p "====> " p
|
||||||
case $p in
|
case $p in
|
||||||
[yY]) echo -e "${STY_BLUE}OK, doing backup...${STY_RST}" ;local backup=true;break ;;
|
[yY]) echo -e "${STY_BLUE}OK, doing backup...${STY_RST}" ;local backup=true;break ;;
|
||||||
[nN]) echo -e "${STY_BLUE}Alright, skipping...${STY_RST}" ;local backup=false;break ;;
|
[nNsS]) echo -e "${STY_BLUE}Alright, skipping...${STY_RST}" ;local backup=false;break ;;
|
||||||
*) echo -e "${STY_RED}Please enter [y/n].${STY_RST}";;
|
*) echo -e "${STY_RED}Please enter [y/n].${STY_RST}";;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
if $backup;then
|
if $backup;then backup_configs;fi
|
||||||
backup_clashing_targets dots/.config $XDG_CONFIG_HOME "${BACKUP_DIR}/.config"
|
}
|
||||||
backup_clashing_targets dots/.local/share $XDG_DATA_HOME "${BACKUP_DIR}/.local/share"
|
function auto_backup_configs(){
|
||||||
fi
|
# Backup when $BACKUP_DIR does not exist
|
||||||
|
if [[ ! -d "$BACKUP_DIR" ]]; then backup_configs;fi
|
||||||
}
|
}
|
||||||
|
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
|
showfun auto_get_git_submodule
|
||||||
|
v auto_get_git_submodule
|
||||||
|
|
||||||
# In case some dirs does not exists
|
# In case some dirs does not exists
|
||||||
v mkdir -p $XDG_BIN_HOME $XDG_CACHE_HOME $XDG_CONFIG_HOME/quickshell $XDG_DATA_HOME
|
v mkdir -p $XDG_BIN_HOME $XDG_CACHE_HOME $XDG_CONFIG_HOME $XDG_DATA_HOME/icons
|
||||||
|
|
||||||
case $ask in
|
if [[ ! "${SKIP_BACKUP}" == true ]]; then
|
||||||
false) sleep 0 ;;
|
case $ask in
|
||||||
*) ask_backup_configs ;;
|
false) auto_backup_configs ;;
|
||||||
esac
|
*) ask_backup_configs ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
# TODO: A better method for users to choose their customization,
|
# TODO: A better method for users to choose their customization,
|
||||||
# for example some users may prefer ZSH over FISH, and foot over kitty.
|
# for example some users may prefer ZSH over FISH, and foot over kitty.
|
||||||
@@ -90,34 +70,45 @@ esac
|
|||||||
# original dotfiles and new ones in the SAME DIRECTORY
|
# original dotfiles and new ones in the SAME DIRECTORY
|
||||||
# (eg. in ~/.config/hypr) won't be mixed together
|
# (eg. in ~/.config/hypr) won't be mixed together
|
||||||
|
|
||||||
# MISC (For dots/.config/* but not quickshell, not fish, not Hyprland)
|
# MISC (For dots/.config/* but not quickshell, not fish, not Hyprland, not fontconfig)
|
||||||
case $SKIP_MISCCONF in
|
case $SKIP_MISCCONF in
|
||||||
true) sleep 0;;
|
true) sleep 0;;
|
||||||
*)
|
*)
|
||||||
for i in $(find dots/.config/ -mindepth 1 -maxdepth 1 ! -name 'quickshell' ! -name 'fish' ! -name 'hypr' -exec basename {} \;); do
|
for i in $(find dots/.config/ -mindepth 1 -maxdepth 1 ! -name 'quickshell' ! -name 'fish' ! -name 'hypr' ! -name 'fontconfig' -exec basename {} \;); do
|
||||||
# i="dots/.config/$i"
|
# i="dots/.config/$i"
|
||||||
echo "[$0]: Found target: dots/.config/$i"
|
echo "[$0]: Found target: dots/.config/$i"
|
||||||
if [ -d "dots/.config/$i" ];then warning_rsync; v rsync -av --delete "dots/.config/$i/" "$XDG_CONFIG_HOME/$i/"
|
if [ -d "dots/.config/$i" ];then warning_rsync_delete; v rsync -av --delete "dots/.config/$i/" "$XDG_CONFIG_HOME/$i/"
|
||||||
elif [ -f "dots/.config/$i" ];then warning_rsync; v rsync -av "dots/.config/$i" "$XDG_CONFIG_HOME/$i"
|
elif [ -f "dots/.config/$i" ];then warning_rsync_normal; v rsync -av "dots/.config/$i" "$XDG_CONFIG_HOME/$i"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
warning_rsync_delete; v rsync -av "dots/.local/share/konsole/" "${XDG_DATA_HOME:-$HOME/.local/share}"/konsole/
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case $SKIP_QUICKSHELL in
|
case $SKIP_QUICKSHELL in
|
||||||
true) sleep 0;;
|
true) sleep 0;;
|
||||||
*)
|
*)
|
||||||
warning_rsync; v rsync -av --delete dots/.config/quickshell/ii/ "$XDG_CONFIG_HOME"/quickshell/ii/
|
# Should overwriting the whole directory not only ~/.config/quickshell/ii/ cuz https://github.com/end-4/dots-hyprland/issues/2294#issuecomment-3448671064
|
||||||
|
warning_rsync_delete; v rsync -av --delete dots/.config/quickshell/ "$XDG_CONFIG_HOME"/quickshell/
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case $SKIP_FISH in
|
case $SKIP_FISH in
|
||||||
true) sleep 0;;
|
true) sleep 0;;
|
||||||
*)
|
*)
|
||||||
warning_rsync; v rsync -av --delete dots/.config/fish/ "$XDG_CONFIG_HOME"/fish/
|
warning_rsync_delete; v rsync -av --delete dots/.config/fish/ "$XDG_CONFIG_HOME"/fish/
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
case $SKIP_FONTCONFIG in
|
||||||
|
true) sleep 0;;
|
||||||
|
*)
|
||||||
|
case "$II_FONTSET_NAME" in
|
||||||
|
"") warning_rsync_delete; v rsync -av --delete dots/.config/fontconfig/ "$XDG_CONFIG_HOME"/fontconfig/ ;;
|
||||||
|
*) warning_rsync_delete; v rsync -av --delete dots-extra/fontsets/$II_FONTSET_NAME/ "$XDG_CONFIG_HOME"/fontconfig/ ;;
|
||||||
|
esac;;
|
||||||
|
esac
|
||||||
|
|
||||||
# For Hyprland
|
# For Hyprland
|
||||||
declare -a arg_excludes=()
|
declare -a arg_excludes=()
|
||||||
arg_excludes+=(--exclude '/custom')
|
arg_excludes+=(--exclude '/custom')
|
||||||
@@ -127,17 +118,25 @@ arg_excludes+=(--exclude '/hyprland.conf')
|
|||||||
case $SKIP_HYPRLAND in
|
case $SKIP_HYPRLAND in
|
||||||
true) sleep 0;;
|
true) sleep 0;;
|
||||||
*)
|
*)
|
||||||
warning_rsync; v rsync -av --delete "${arg_excludes[@]}" dots/.config/hypr/ "$XDG_CONFIG_HOME"/hypr/
|
warning_rsync_delete; v rsync -av --delete "${arg_excludes[@]}" dots/.config/hypr/ "$XDG_CONFIG_HOME"/hypr/
|
||||||
|
# When hypr/custom does not exist, we assume that it's the firstrun.
|
||||||
|
if [ -d "$XDG_CONFIG_HOME/hypr/custom" ];then ii_firstrun=false;else ii_firstrun=true;fi
|
||||||
t="$XDG_CONFIG_HOME/hypr/hyprland.conf"
|
t="$XDG_CONFIG_HOME/hypr/hyprland.conf"
|
||||||
if [ -f $t ];then
|
if [ -f $t ];then
|
||||||
echo -e "${STY_BLUE}[$0]: \"$t\" already exists.${STY_RST}"
|
echo -e "${STY_BLUE}[$0]: \"$t\" already exists.${STY_RST}"
|
||||||
v mv $t $t.old
|
if $ii_firstrun;then
|
||||||
v cp -f dots/.config/hypr/hyprland.conf $t
|
echo -e "${STY_BLUE}[$0]: It seems to be the firstrun.${STY_RST}"
|
||||||
existed_hypr_conf_firstrun=y
|
v mv $t $t.old
|
||||||
|
v cp -f dots/.config/hypr/hyprland.conf $t
|
||||||
|
existed_hypr_conf_firstrun=y
|
||||||
|
else
|
||||||
|
echo -e "${STY_BLUE}[$0]: It seems not a firstrun.${STY_RST}"
|
||||||
|
v cp -f dots/.config/hypr/hyprland.conf $t.new
|
||||||
|
existed_hypr_conf=y
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo -e "${STY_YELLOW}[$0]: \"$t\" does not exist yet.${STY_RST}"
|
echo -e "${STY_YELLOW}[$0]: \"$t\" does not exist yet.${STY_RST}"
|
||||||
v cp dots/.config/hypr/hyprland.conf $t
|
v cp dots/.config/hypr/hyprland.conf $t
|
||||||
existed_hypr_conf=n
|
|
||||||
fi
|
fi
|
||||||
t="$XDG_CONFIG_HOME/hypr/hypridle.conf"
|
t="$XDG_CONFIG_HOME/hypr/hypridle.conf"
|
||||||
if [ -f $t ];then
|
if [ -f $t ];then
|
||||||
@@ -164,7 +163,7 @@ case $SKIP_HYPRLAND in
|
|||||||
echo -e "${STY_BLUE}[$0]: \"$t\" already exists, will not do anything.${STY_RST}"
|
echo -e "${STY_BLUE}[$0]: \"$t\" already exists, will not do anything.${STY_RST}"
|
||||||
else
|
else
|
||||||
echo -e "${STY_YELLOW}[$0]: \"$t\" does not exist yet.${STY_RST}"
|
echo -e "${STY_YELLOW}[$0]: \"$t\" does not exist yet.${STY_RST}"
|
||||||
warning_rsync; v rsync -av --delete dots/.config/hypr/custom/ $t/
|
v rsync -av --delete dots/.config/hypr/custom/ $t/
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -174,8 +173,7 @@ declare -a arg_excludes=()
|
|||||||
# some foldes (eg. .local/bin) should be processed separately to avoid `--delete' for rsync,
|
# some foldes (eg. .local/bin) should be processed separately to avoid `--delete' for rsync,
|
||||||
# since the files here come from different places, not only about one program.
|
# since the files here come from different places, not only about one program.
|
||||||
# v rsync -av "dots/.local/bin/" "$XDG_BIN_HOME" # No longer needed since scripts are no longer in ~/.local/bin
|
# v rsync -av "dots/.local/bin/" "$XDG_BIN_HOME" # No longer needed since scripts are no longer in ~/.local/bin
|
||||||
warning_rsync; v rsync -av "dots/.local/share/icons/" "${XDG_DATA_HOME:-$HOME/.local/share}"/icons/
|
v cp -f "dots/.local/share/icons/illogical-impulse.svg" "${XDG_DATA_HOME}"/icons/illogical-impulse.svg
|
||||||
warning_rsync; v rsync -av "dots/.local/share/konsole/" "${XDG_DATA_HOME:-$HOME/.local/share}"/konsole/
|
|
||||||
|
|
||||||
# Prevent hyprland from not fully loaded
|
# Prevent hyprland from not fully loaded
|
||||||
sleep 1
|
sleep 1
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
version: "1.0"
|
||||||
|
user_preferences:
|
||||||
|
shell: "fish" # fish | zsh
|
||||||
|
terminal: "foot" # kitty | foot
|
||||||
|
keybindings: "default" # default | vim
|
||||||
|
patterns:
|
||||||
|
# Always install these files
|
||||||
|
- from: "dots/.config/quickshell"
|
||||||
|
to: "$XDG_CONFIG_HOME/quickshell"
|
||||||
|
mode: "sync"
|
||||||
|
# Conditionally install these files
|
||||||
|
- from: "dots/.config/fish"
|
||||||
|
to: "$XDG_CONFIG_HOME/fish"
|
||||||
|
mode: "sync"
|
||||||
|
condition:
|
||||||
|
type: "shell"
|
||||||
|
value: "fish"
|
||||||
|
- from: "dots/.config/zshrc.d"
|
||||||
|
to: "$XDG_CONFIG_HOME/zshrc.d"
|
||||||
|
mode: "sync"
|
||||||
|
condition:
|
||||||
|
type: "shell"
|
||||||
|
value: "zsh"
|
||||||
|
- from: "dots/.config/foot"
|
||||||
|
to: "$XDG_CONFIG_HOME/foot"
|
||||||
|
mode: "sync"
|
||||||
|
condition:
|
||||||
|
type: "terminal"
|
||||||
|
value: "foot"
|
||||||
|
- from: "dots/.config/kitty"
|
||||||
|
to: "$XDG_CONFIG_HOME/kitty"
|
||||||
|
mode: "sync"
|
||||||
|
condition:
|
||||||
|
type: "terminal"
|
||||||
|
value: "kitty"
|
||||||
|
# Hyprland
|
||||||
|
- from: "dots/.config/hypr"
|
||||||
|
to: "$XDG_CONFIG_HOME/hypr"
|
||||||
|
mode: "sync"
|
||||||
|
excludes: ["custom", "hyprlock.conf", "hypridle.conf", "hyprland.conf"]
|
||||||
|
# Hyprland special files
|
||||||
|
- from: "dots/.config/hypr/hyprland.conf"
|
||||||
|
to: "$XDG_CONFIG_HOME/hypr/hyprland.conf"
|
||||||
|
mode: "hard-backup"
|
||||||
|
- from: "dots/.config/hypr/hypridle.conf"
|
||||||
|
to: "$XDG_CONFIG_HOME/hypr/hypridle.conf"
|
||||||
|
mode: "soft-backup"
|
||||||
|
- from: "dots/.config/hypr/hyprlock.conf"
|
||||||
|
to: "$XDG_CONFIG_HOME/hypr/hyprlock.conf"
|
||||||
|
mode: "soft-backup"
|
||||||
|
- from: "dots/.config/hypr/custom"
|
||||||
|
to: "$XDG_CONFIG_HOME/hypr/custom"
|
||||||
|
mode: "skip-if-exists"
|
||||||
|
- from: "dots/.local/share/icons"
|
||||||
|
to: "$XDG_DATA_HOME/icons"
|
||||||
|
mode: "soft"
|
||||||
|
- from: "dots/.local/share/konsole"
|
||||||
|
to: "$XDG_DATA_HOME/konsole"
|
||||||
|
mode: "soft"
|
||||||
|
# Fontconfig (default - fontsets handled separately if II_FONTSET_NAME is set)
|
||||||
|
- from: "dots/.config/fontconfig"
|
||||||
|
to: "$XDG_CONFIG_HOME/fontconfig"
|
||||||
|
mode: "sync"
|
||||||
|
# MISC config directories (other .config directories)
|
||||||
|
- from: "dots/.config/fuzzel"
|
||||||
|
to: "$XDG_CONFIG_HOME/fuzzel"
|
||||||
|
mode: "sync"
|
||||||
|
- from: "dots/.config/kde-material-you-colors"
|
||||||
|
to: "$XDG_CONFIG_HOME/kde-material-you-colors"
|
||||||
|
mode: "sync"
|
||||||
|
- from: "dots/.config/Kvantum"
|
||||||
|
to: "$XDG_CONFIG_HOME/Kvantum"
|
||||||
|
mode: "sync"
|
||||||
|
- from: "dots/.config/matugen"
|
||||||
|
to: "$XDG_CONFIG_HOME/matugen"
|
||||||
|
mode: "sync"
|
||||||
|
- from: "dots/.config/mpv"
|
||||||
|
to: "$XDG_CONFIG_HOME/mpv"
|
||||||
|
mode: "sync"
|
||||||
|
- from: "dots/.config/qt5ct"
|
||||||
|
to: "$XDG_CONFIG_HOME/qt5ct"
|
||||||
|
mode: "sync"
|
||||||
|
- from: "dots/.config/qt6ct"
|
||||||
|
to: "$XDG_CONFIG_HOME/qt6ct"
|
||||||
|
mode: "sync"
|
||||||
|
- from: "dots/.config/wlogout"
|
||||||
|
to: "$XDG_CONFIG_HOME/wlogout"
|
||||||
|
mode: "sync"
|
||||||
|
- from: "dots/.config/xdg-desktop-portal"
|
||||||
|
to: "$XDG_CONFIG_HOME/xdg-desktop-portal"
|
||||||
|
mode: "sync"
|
||||||
|
# MISC config files (individual files in .config)
|
||||||
|
- from: "dots/.config/chrome-flags.conf"
|
||||||
|
to: "$XDG_CONFIG_HOME/chrome-flags.conf"
|
||||||
|
mode: "soft"
|
||||||
|
- from: "dots/.config/code-flags.conf"
|
||||||
|
to: "$XDG_CONFIG_HOME/code-flags.conf"
|
||||||
|
mode: "soft"
|
||||||
|
- from: "dots/.config/darklyrc"
|
||||||
|
to: "$XDG_CONFIG_HOME/darklyrc"
|
||||||
|
mode: "soft"
|
||||||
|
- from: "dots/.config/dolphinrc"
|
||||||
|
to: "$XDG_CONFIG_HOME/dolphinrc"
|
||||||
|
mode: "soft"
|
||||||
|
- from: "dots/.config/kdeglobals"
|
||||||
|
to: "$XDG_CONFIG_HOME/kdeglobals"
|
||||||
|
mode: "soft"
|
||||||
|
- from: "dots/.config/konsolerc"
|
||||||
|
to: "$XDG_CONFIG_HOME/konsolerc"
|
||||||
|
mode: "soft"
|
||||||
|
- from: "dots/.config/starship.toml"
|
||||||
|
to: "$XDG_CONFIG_HOME/starship.toml"
|
||||||
|
mode: "soft"
|
||||||
|
- from: "dots/.config/thorium-flags.conf"
|
||||||
|
to: "$XDG_CONFIG_HOME/thorium-flags.conf"
|
||||||
|
mode: "soft"
|
||||||
@@ -14,14 +14,18 @@ Options for install:
|
|||||||
--skip-allsetups Skip the whole process setting up permissions/services etc
|
--skip-allsetups Skip the whole process setting up permissions/services etc
|
||||||
--skip-allfiles Skip the whole process copying configuration files
|
--skip-allfiles Skip the whole process copying configuration files
|
||||||
-s, --skip-sysupdate Skip system package upgrade e.g. \"sudo pacman -Syu\"
|
-s, --skip-sysupdate Skip system package upgrade e.g. \"sudo pacman -Syu\"
|
||||||
|
--skip-plasmaintg Skip installing plasma-browser-integration
|
||||||
|
--skip-backup Skip backup conflicting files
|
||||||
--skip-quickshell Skip installing the config for Quickshell
|
--skip-quickshell Skip installing the config for Quickshell
|
||||||
--skip-hyprland Skip installing the config for Hyprland
|
--skip-hyprland Skip installing the config for Hyprland
|
||||||
--skip-fish Skip installing the config for Fish
|
--skip-fish Skip installing the config for Fish
|
||||||
--skip-plasmaintg Skip installing plasma-browser-integration
|
--skip-fontconfig Skip installing the config for fontconfig
|
||||||
--skip-miscconf Skip copying the dirs and files to \".configs\" except for
|
--skip-miscconf Skip copying the dirs and files to \".configs\" except for
|
||||||
Quickshell, Fish and Hyprland
|
Quickshell, Fish and Hyprland
|
||||||
|
--core Alias of --skip-{plasmaintg,fish,miscconf,fontconfig}
|
||||||
--exp-files Use experimental script for the third step copying files
|
--exp-files Use experimental script for the third step copying files
|
||||||
--fontset <set> (Unavailable yet) Use a set of pre-defined font and config
|
--fontset <set> Use a set of pre-defined font and config (currently only fontconfig).
|
||||||
|
Possible values of <set>: $(ls -A ${REPO_ROOT}/dots-extra/fontsets)
|
||||||
--via-nix (Unavailable yet) Use Nix to install dependencies
|
--via-nix (Unavailable yet) Use Nix to install dependencies
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
@@ -33,7 +37,7 @@ cleancache(){
|
|||||||
# `man getopt` to see more
|
# `man getopt` to see more
|
||||||
para=$(getopt \
|
para=$(getopt \
|
||||||
-o hfk:cs \
|
-o hfk:cs \
|
||||||
-l help,force,fontset:,clean,skip-allgreeting,skip-alldeps,skip-allsetups,skip-allfiles,skip-sysupdate,skip-quickshell,skip-fish,skip-hyprland,skip-plasmaintg,skip-miscconf,exp-files,via-nix \
|
-l help,force,fontset:,clean,skip-allgreeting,skip-alldeps,skip-allsetups,skip-allfiles,skip-sysupdate,skip-plasmaintg,skip-backup,skip-quickshell,skip-fish,skip-hyprland,skip-fontconfig,skip-miscconf,core,exp-files,via-nix \
|
||||||
-n "$0" -- "$@")
|
-n "$0" -- "$@")
|
||||||
[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1
|
[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
@@ -63,20 +67,23 @@ while true ; do
|
|||||||
--skip-allsetups) SKIP_ALLSETUPS=true;shift;;
|
--skip-allsetups) SKIP_ALLSETUPS=true;shift;;
|
||||||
--skip-allfiles) SKIP_ALLFILES=true;shift;;
|
--skip-allfiles) SKIP_ALLFILES=true;shift;;
|
||||||
-s|--skip-sysupdate) SKIP_SYSUPDATE=true;shift;;
|
-s|--skip-sysupdate) SKIP_SYSUPDATE=true;shift;;
|
||||||
|
--skip-plasmaintg) SKIP_PLASMAINTG=true;shift;;
|
||||||
|
--skip-backup) SKIP_BACKUP=true;shift;;
|
||||||
--skip-hyprland) SKIP_HYPRLAND=true;shift;;
|
--skip-hyprland) SKIP_HYPRLAND=true;shift;;
|
||||||
--skip-fish) SKIP_FISH=true;shift;;
|
--skip-fish) SKIP_FISH=true;shift;;
|
||||||
--skip-quickshell) SKIP_QUICKSHELL=true;shift;;
|
--skip-quickshell) SKIP_QUICKSHELL=true;shift;;
|
||||||
|
--skip-fontconfig) SKIP_FONTCONFIG=true;shift;;
|
||||||
--skip-miscconf) SKIP_MISCCONF=true;shift;;
|
--skip-miscconf) SKIP_MISCCONF=true;shift;;
|
||||||
--skip-plasmaintg) SKIP_PLASMAINTG=true;shift;;
|
--core) SKIP_PLASMAINTG=true;SKIP_FISH=true;SKIP_FONTCONFIG=true;SKIP_MISCCONF=true;shift;;
|
||||||
--exp-files) EXPERIMENTAL_FILES_SCRIPT=true;shift;;
|
--exp-files) EXPERIMENTAL_FILES_SCRIPT=true;shift;;
|
||||||
--via-nix) INSTALL_VIA_NIX=true;shift;;
|
--via-nix) INSTALL_VIA_NIX=true;shift;;
|
||||||
|
|
||||||
## Ones with parameter
|
## Ones with parameter
|
||||||
--fontset)
|
--fontset)
|
||||||
case $2 in
|
if [[ -d "${REPO_ROOT}/dots-extra/fontsets/$2" ]];
|
||||||
"default"|"zh-CN"|"vi") fontset="$2";;
|
then echo "Using fontset \"$2\".";II_FONTSET_NAME="$2";shift 2
|
||||||
*) echo -e "Wrong argument for $1.";exit 1;;
|
else echo "Wrong argument for $1.";exit 1
|
||||||
esac;echo "The fontset is ${fontset}.";shift 2;;
|
fi;;
|
||||||
|
|
||||||
## Ending
|
## Ending
|
||||||
--) break ;;
|
--) break ;;
|
||||||
@@ -24,6 +24,7 @@ Subcommands:
|
|||||||
exp-uninstall (Experimental) Uninstall illogical-impulse.
|
exp-uninstall (Experimental) Uninstall illogical-impulse.
|
||||||
exp-update (Experimental) Update illogical-impulse without fully reinstall.
|
exp-update (Experimental) Update illogical-impulse without fully reinstall.
|
||||||
exp-update-old (Experimental) exp-update but use behaves like old version.
|
exp-update-old (Experimental) exp-update but use behaves like old version.
|
||||||
|
checkdeps (For dev only) Check whether pkgs exist in AUR or repos of Arch.
|
||||||
help Show this help message.
|
help Show this help message.
|
||||||
|
|
||||||
For each <subcommand>, use -h for details:
|
For each <subcommand>, use -h for details:
|
||||||
@@ -34,75 +35,67 @@ case $1 in
|
|||||||
# Global help
|
# Global help
|
||||||
help|--help|-h)showhelp_global;exit;;
|
help|--help|-h)showhelp_global;exit;;
|
||||||
# Correct subcommand
|
# Correct subcommand
|
||||||
install|install-deps|install-setups|install-files|exp-uninstall|exp-update|exp-update-old)
|
install|exp-uninstall|exp-update|exp-update-old|checkdeps)
|
||||||
SCRIPT_SUBCOMMAND=$1;shift;;
|
SUBCMD_NAME=$1
|
||||||
# No subcommand
|
SUBCMD_DIR=./sdata/subcmd-$1
|
||||||
-*|"")SCRIPT_SUBCOMMAND=install;;
|
shift;;
|
||||||
|
# Correct subcommand but not using ./sdata/subcmd-$1
|
||||||
|
install-deps|install-setups|install-files)
|
||||||
|
SUBCMD_NAME=$1
|
||||||
|
SUBCMD_DIR=./sdata/subcmd-install
|
||||||
|
shift;;
|
||||||
|
# No subcommand, default to install
|
||||||
|
-*|"")
|
||||||
|
SUBCMD_NAME=install
|
||||||
|
SUBCMD_DIR=./sdata/subcmd-install
|
||||||
|
;;
|
||||||
# Wrong subcommand
|
# Wrong subcommand
|
||||||
*)printf "${STY_RED}Unknown subcommand \"$1\".${STY_RST}\n";showhelp_global;exit 1;;
|
*)printf "${STY_RED}Unknown subcommand \"$1\".${STY_RST}\n";showhelp_global;exit 1;;
|
||||||
esac
|
esac
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
case ${SCRIPT_SUBCOMMAND} in
|
if [[ -f "${SUBCMD_DIR}/options.sh" ]];
|
||||||
|
then source "${SUBCMD_DIR}/options.sh"
|
||||||
|
fi
|
||||||
|
case ${SUBCMD_NAME} in
|
||||||
install)
|
install)
|
||||||
source ./sdata/options/install.sh
|
|
||||||
if [[ "${SKIP_ALLGREETING}" != true ]]; then
|
if [[ "${SKIP_ALLGREETING}" != true ]]; then
|
||||||
source ./sdata/step/0.install-greeting.sh
|
source ${SUBCMD_DIR}/0.greeting.sh
|
||||||
fi
|
fi
|
||||||
if [[ "${SKIP_ALLDEPS}" != true ]]; then
|
if [[ "${SKIP_ALLDEPS}" != true ]]; then
|
||||||
printf "${STY_CYAN}[$0]: 1. Install dependencies\n${STY_RST}"
|
source ${SUBCMD_DIR}/1.deps-selector.sh
|
||||||
source ./sdata/step/1.install-deps-selector.sh
|
|
||||||
fi
|
fi
|
||||||
if [[ "${SKIP_ALLSETUPS}" != true ]]; then
|
if [[ "${SKIP_ALLSETUPS}" != true ]]; then
|
||||||
printf "${STY_CYAN}[$0]: 2. Setup for permissions/services etc\n${STY_RST}"
|
source ${SUBCMD_DIR}/2.setups-selector.sh
|
||||||
source ./sdata/step/2.install-setups-selector.sh
|
|
||||||
fi
|
fi
|
||||||
if [[ "${SKIP_ALLFILES}" != true ]]; then
|
if [[ "${SKIP_ALLFILES}" != true ]]; then
|
||||||
printf "${STY_CYAN}[$0]: 3. Copying config files\n${STY_RST}"
|
|
||||||
if [[ "${EXPERIMENTAL_FILES_SCRIPT}" == true ]]; then
|
if [[ "${EXPERIMENTAL_FILES_SCRIPT}" == true ]]; then
|
||||||
source ./sdata/step/3.install-files.experimental.sh
|
source ${SUBCMD_DIR}/3.files-exp.sh
|
||||||
else
|
else
|
||||||
source ./sdata/step/3.install-files.sh
|
source ${SUBCMD_DIR}/3.files.sh
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
install-deps)
|
install-deps)
|
||||||
source ./sdata/options/install.sh
|
|
||||||
if [[ "${SKIP_ALLDEPS}" != true ]]; then
|
if [[ "${SKIP_ALLDEPS}" != true ]]; then
|
||||||
printf "${STY_CYAN}[$0]: 1. Install dependencies\n${STY_RST}"
|
source ${SUBCMD_DIR}/1.deps-selector.sh
|
||||||
source ./sdata/step/1.install-deps-selector.sh
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
install-setups)
|
install-setups)
|
||||||
source ./sdata/options/install.sh
|
|
||||||
if [[ "${SKIP_ALLSETUPS}" != true ]]; then
|
if [[ "${SKIP_ALLSETUPS}" != true ]]; then
|
||||||
printf "${STY_CYAN}[$0]: 2. Setup for permissions/services etc\n${STY_RST}"
|
source ${SUBCMD_DIR}/2.setups-selector.sh
|
||||||
source ./sdata/step/2.install-setups-selector.sh
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
install-files)
|
install-files)
|
||||||
source ./sdata/options/install.sh
|
|
||||||
if [[ "${SKIP_ALLFILES}" != true ]]; then
|
if [[ "${SKIP_ALLFILES}" != true ]]; then
|
||||||
printf "${STY_CYAN}[$0]: 3. Copying config files\n${STY_RST}"
|
|
||||||
if [[ "${EXPERIMENTAL_FILES_SCRIPT}" == true ]]; then
|
if [[ "${EXPERIMENTAL_FILES_SCRIPT}" == true ]]; then
|
||||||
source ./sdata/step/3.install-files.experimental.sh
|
source ${SUBCMD_DIR}/3.files-exp.sh
|
||||||
else
|
else
|
||||||
source ./sdata/step/3.install-files.sh
|
source ${SUBCMD_DIR}/3.files.sh
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
exp-uninstall)
|
*)
|
||||||
source ./sdata/options/exp-uninstall.sh
|
source ${SUBCMD_DIR}/0.run.sh
|
||||||
source ./sdata/step/exp-uninstall.sh
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
exp-update)
|
|
||||||
source ./sdata/options/exp-update.sh
|
|
||||||
source ./sdata/step/exp-update.sh
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
exp-update-old)
|
|
||||||
source ./sdata/options/exp-update-old.sh
|
|
||||||
source ./sdata/step/exp-update-old.sh
|
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
Reference in New Issue
Block a user