diff --git a/Functions/MovePet.qml b/Functions/MovePet.qml new file mode 100644 index 0000000..bb98e19 --- /dev/null +++ b/Functions/MovePet.qml @@ -0,0 +1,16 @@ +import QtQuick + +MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + drag.target: null // Not using built-in drag behavior + + onPositionChanged: { + if (mouse.buttons & Qt.LeftButton) { + petMarch.mleft = mouse.x + petMarch.mbottom = mouse.y + console.log("mleft:", petMarch.mleft, "mbottom:", petMarch.mbottom) + } + } +} diff --git a/Functions/ToggleLayer.qml b/Functions/ToggleLayer.qml index 604ffa8..91a81b7 100644 --- a/Functions/ToggleLayer.qml +++ b/Functions/ToggleLayer.qml @@ -1,15 +1,15 @@ -import QtQuick -import Quickshell.Wayland - -QtObject { - // The main toggle function - function toggleLayer() { - if (!onTop) { - mainWindow.WlrLayershell.layer = WlrLayer.Top - onTop = true - } else { - mainWindow.WlrLayershell.layer = WlrLayer.Bottom - onTop = false - } - } -} +import QtQuick +import Quickshell.Wayland + +QtObject { + // The main toggle function + function toggleLayer() { + if (!onTop) { + mainWindow.WlrLayershell.layer = WlrLayer.Top + onTop = true + } else { + mainWindow.WlrLayershell.layer = WlrLayer.Bottom + onTop = false + } + } +} diff --git a/Gifs/24pobgj850vf1.gif b/Gifs/24pobgj850vf1.gif new file mode 100644 index 0000000..01d0f08 Binary files /dev/null and b/Gifs/24pobgj850vf1.gif differ diff --git a/Gifs/evernight.gif b/Gifs/evernight.gif old mode 100755 new mode 100644 diff --git a/Gifs/kkpt7gm8035d1.gif b/Gifs/kkpt7gm8035d1.gif new file mode 100644 index 0000000..92ccb06 Binary files /dev/null and b/Gifs/kkpt7gm8035d1.gif differ diff --git a/Modules/Areapicker.qml b/Modules/Areapicker.qml deleted file mode 100644 index 859aa0c..0000000 --- a/Modules/Areapicker.qml +++ /dev/null @@ -1,84 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.components.containers -import qs.components.misc -import qs.Modules -import Quickshell -import Quickshell.Wayland -import Quickshell.Io - -Scope { - LazyLoader { - id: root - - property bool freeze - property bool closing - - Variants { - model: Quickshell.screens - - StyledWindow { - id: win - - required property ShellScreen modelData - - screen: modelData - name: "area-picker" - WlrLayershell.exclusionMode: ExclusionMode.Ignore - WlrLayershell.layer: WlrLayer.Overlay - WlrLayershell.keyboardFocus: root.closing ? WlrKeyboardFocus.None : WlrKeyboardFocus.Exclusive - mask: root.closing ? empty : null - - anchors.top: true - anchors.bottom: true - anchors.left: true - anchors.right: true - - Region { - id: empty - } - - Picker { - loader: root - screen: win.modelData - } - } - } - } - - IpcHandler { - target: "picker" - - function open(): void { - root.freeze = false; - root.closing = false; - root.activeAsync = true; - } - - function openFreeze(): void { - root.freeze = true; - root.closing = false; - root.activeAsync = true; - } - } - - CustomShortcut { - name: "screenshot" - description: "Open screenshot tool" - onPressed: { - root.freeze = false; - root.closing = false; - root.activeAsync = true; - } - } - - CustomShortcut { - name: "screenshotFreeze" - description: "Open screenshot tool (freeze mode)" - onPressed: { - root.freeze = true; - root.closing = false; - root.activeAsync = true; - } - } -} diff --git a/Modules/PetMarch.qml b/Modules/PetMarch.qml index 064408b..e9a2817 100644 --- a/Modules/PetMarch.qml +++ b/Modules/PetMarch.qml @@ -1,22 +1,45 @@ -import QtQuick - -Rectangle { - anchors.fill: parent - color: "transparent" - - AnimatedImage { - anchors.fill: parent - source: "../Gifs/evernight.gif" - fillMode: Image.PreserveAspectFit - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.MiddleButton - onClicked: (mouse) => { - if (mouse.button === Qt.MiddleButton) { - toggleHelper.toggleLayer() - } - } - } -} +pragma ComponentBehavior: Bound +import QtQuick +import Quickshell.Io +import Quickshell.Wayland + +Rectangle { + color: "transparent" + anchors.fill: parent + + AnimatedImage { + anchors.fill: parent + source: "../Gifs/evernight.gif" + fillMode: Image.PreserveAspectFit + } + + // margins { + // mainWindow.left: 50 + // } + + IpcHandler { + target: "command" + + // Keybind swap layer + function toggleLayer(): void { + if ( !mainWindow.onTop ) { + mainWindow.WlrLayershell.layer = WlrLayer.Top + mainWindow.onTop = true + } else { + mainWindow.WlrLayershell.layer = WlrLayer.Bottom + mainWindow.onTop = false + } + } + + // Keybind swap overlay + function toggleOverlay(): void { + if (!mainWindow.onTop) { + mainWindow.WlrLayershell.layer = WlrLayer.Overlay + mainWindow.onTop = true + } else { + mainWindow.WlrLayershell.layer = WlrLayer.Bottom + mainWindow.onTop = false + } + } + } +} diff --git a/Modules/Picker.qml b/Modules/Picker.qml deleted file mode 100644 index 9eefe53..0000000 --- a/Modules/Picker.qml +++ /dev/null @@ -1,293 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.components -import qs.services -import qs.config -import Quickshell -import Quickshell.Wayland -import QtQuick -import QtQuick.Effects - -MouseArea { - id: root - - required property LazyLoader loader - required property ShellScreen screen - - property bool onClient - - property real realBorderWidth: onClient ? (Hypr.options["general:border_size"] ?? 1) : 2 - property real realRounding: onClient ? (Hypr.options["decoration:rounding"] ?? 0) : 0 - - property real ssx - property real ssy - - property real sx: 0 - property real sy: 0 - property real ex: screen.width - property real ey: screen.height - - property real rsx: Math.min(sx, ex) - property real rsy: Math.min(sy, ey) - property real sw: Math.abs(sx - ex) - property real sh: Math.abs(sy - ey) - - property list clients: { - const mon = Hypr.monitorFor(screen); - if (!mon) - return []; - - const special = mon.lastIpcObject.specialWorkspace; - const wsId = special.name ? special.id : mon.activeWorkspace.id; - - return Hypr.toplevels.values.filter(c => c.workspace?.id === wsId).sort((a, b) => { - // Pinned first, then fullscreen, then floating, then any other - const ac = a.lastIpcObject; - const bc = b.lastIpcObject; - return (bc.pinned - ac.pinned) || ((bc.fullscreen !== 0) - (ac.fullscreen !== 0)) || (bc.floating - ac.floating); - }); - } - - function checkClientRects(x: real, y: real): void { - for (const client of clients) { - if (!client) - continue; - - let { - at: [cx, cy], - size: [cw, ch] - } = client.lastIpcObject; - cx -= screen.x; - cy -= screen.y; - if (cx <= x && cy <= y && cx + cw >= x && cy + ch >= y) { - onClient = true; - sx = cx; - sy = cy; - ex = cx + cw; - ey = cy + ch; - break; - } - } - } - - function save(): void { - const tmpfile = Qt.resolvedUrl(`/tmp/caelestia-picker-${Quickshell.processId}-${Date.now()}.png`); - CUtils.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached(["swappy", "-f", path])); - closeAnim.start(); - } - - onClientsChanged: checkClientRects(mouseX, mouseY) - - anchors.fill: parent - opacity: 0 - hoverEnabled: true - cursorShape: Qt.CrossCursor - - Component.onCompleted: { - Hypr.extras.refreshOptions(); - - // Break binding if frozen - if (loader.freeze) - clients = clients; - - opacity = 1; - - const c = clients[0]; - if (c) { - const cx = c.lastIpcObject.at[0] - screen.x; - const cy = c.lastIpcObject.at[1] - screen.y; - onClient = true; - sx = cx; - sy = cy; - ex = cx + c.lastIpcObject.size[0]; - ey = cy + c.lastIpcObject.size[1]; - } else { - sx = screen.width / 2 - 100; - sy = screen.height / 2 - 100; - ex = screen.width / 2 + 100; - ey = screen.height / 2 + 100; - } - } - - onPressed: event => { - ssx = event.x; - ssy = event.y; - } - - onReleased: { - if (closeAnim.running) - return; - - if (root.loader.freeze) { - save(); - } else { - overlay.visible = border.visible = false; - screencopy.visible = false; - screencopy.active = true; - } - } - - onPositionChanged: event => { - const x = event.x; - const y = event.y; - - if (pressed) { - onClient = false; - sx = ssx; - sy = ssy; - ex = x; - ey = y; - } else { - checkClientRects(x, y); - } - } - - focus: true - Keys.onEscapePressed: closeAnim.start() - - SequentialAnimation { - id: closeAnim - - PropertyAction { - target: root.loader - property: "closing" - value: true - } - ParallelAnimation { - Anim { - target: root - property: "opacity" - to: 0 - duration: Appearance.anim.durations.large - } - ExAnim { - target: root - properties: "rsx,rsy" - to: 0 - } - ExAnim { - target: root - property: "sw" - to: root.screen.width - } - ExAnim { - target: root - property: "sh" - to: root.screen.height - } - } - PropertyAction { - target: root.loader - property: "activeAsync" - value: false - } - } - - Loader { - id: screencopy - - anchors.fill: parent - - active: root.loader.freeze - asynchronous: true - - sourceComponent: ScreencopyView { - captureSource: root.screen - - onHasContentChanged: { - if (hasContent && !root.loader.freeze) { - overlay.visible = border.visible = true; - root.save(); - } - } - } - } - - StyledRect { - id: overlay - - anchors.fill: parent - color: Colours.palette.m3secondaryContainer - opacity: 0.3 - - layer.enabled: true - layer.effect: MultiEffect { - maskSource: selectionWrapper - maskEnabled: true - maskInverted: true - maskSpreadAtMin: 1 - maskThresholdMin: 0.5 - } - } - - Item { - id: selectionWrapper - - anchors.fill: parent - layer.enabled: true - visible: false - - Rectangle { - id: selectionRect - - radius: root.realRounding - x: root.rsx - y: root.rsy - implicitWidth: root.sw - implicitHeight: root.sh - } - } - - Rectangle { - id: border - - color: "transparent" - radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0 - border.width: root.realBorderWidth - border.color: Colours.palette.m3primary - - x: selectionRect.x - root.realBorderWidth - y: selectionRect.y - root.realBorderWidth - implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2 - implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2 - - Behavior on border.color { - CAnim {} - } - } - - Behavior on opacity { - Anim { - duration: Appearance.anim.durations.large - } - } - - Behavior on rsx { - enabled: !root.pressed - - ExAnim {} - } - - Behavior on rsy { - enabled: !root.pressed - - ExAnim {} - } - - Behavior on sw { - enabled: !root.pressed - - ExAnim {} - } - - Behavior on sh { - enabled: !root.pressed - - ExAnim {} - } - - component ExAnim: Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } -} diff --git a/Modules/background/Background.qml b/Modules/background/Background.qml deleted file mode 100644 index bdba570..0000000 --- a/Modules/background/Background.qml +++ /dev/null @@ -1,75 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.components -import qs.components.containers -import qs.services -import qs.config -import Quickshell -import Quickshell.Wayland -import QtQuick - -Loader { - asynchronous: true - active: Config.background.enabled - - sourceComponent: Variants { - model: Quickshell.screens - - StyledWindow { - id: win - - required property ShellScreen modelData - - screen: modelData - name: "background" - WlrLayershell.exclusionMode: ExclusionMode.Ignore - WlrLayershell.layer: WlrLayer.Background - color: "black" - - anchors.top: true - anchors.bottom: true - anchors.left: true - anchors.right: true - - Wallpaper { - id: wallpaper - } - - Loader { - readonly property bool shouldBeActive: Config.background.visualiser.enabled && (!Config.background.visualiser.autoHide || Hypr.monitorFor(win.modelData).activeWorkspace.toplevels.values.every(t => t.lastIpcObject.floating)) ? 1 : 0 - property real offset: shouldBeActive ? 0 : win.modelData.height * 0.2 - - anchors.fill: parent - anchors.topMargin: offset - anchors.bottomMargin: -offset - opacity: shouldBeActive ? 1 : 0 - active: opacity > 0 - asynchronous: true - - sourceComponent: Visualiser { - screen: win.modelData - wallpaper: wallpaper - } - - Behavior on offset { - Anim {} - } - - Behavior on opacity { - Anim {} - } - } - - Loader { - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: Appearance.padding.large - - active: Config.background.desktopClock.enabled - asynchronous: true - - source: "DesktopClock.qml" - } - } - } -} diff --git a/Modules/background/DesktopClock.qml b/Modules/background/DesktopClock.qml deleted file mode 100644 index 6dc6b6b..0000000 --- a/Modules/background/DesktopClock.qml +++ /dev/null @@ -1,18 +0,0 @@ -import qs.components -import qs.services -import qs.config -import QtQuick - -Item { - implicitWidth: timeText.implicitWidth + Appearance.padding.large * 2 - implicitHeight: timeText.implicitHeight + Appearance.padding.large * 2 - - StyledText { - id: timeText - - anchors.centerIn: parent - text: Time.format(Config.services.useTwelveHourClock ? "hh:mm:ss A" : "hh:mm:ss") - font.pointSize: Appearance.font.size.extraLarge - font.bold: true - } -} diff --git a/Modules/background/Visualiser.qml b/Modules/background/Visualiser.qml deleted file mode 100644 index e5a8a9b..0000000 --- a/Modules/background/Visualiser.qml +++ /dev/null @@ -1,120 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.components -import qs.services -import qs.config -import Caelestia.Services -import Quickshell -import Quickshell.Widgets -import QtQuick -import QtQuick.Effects - -Item { - id: root - - required property ShellScreen screen - required property Wallpaper wallpaper - - ServiceRef { - service: Audio.cava - } - - MultiEffect { - anchors.fill: parent - source: root.wallpaper - maskSource: wrapper - maskEnabled: true - blurEnabled: true - blur: 1 - blurMax: 32 - autoPaddingEnabled: false - } - - Item { - id: wrapper - - anchors.fill: parent - layer.enabled: true - - Item { - id: content - - anchors.fill: parent - anchors.margins: Config.border.thickness - anchors.leftMargin: Visibilities.bars.get(root.screen).exclusiveZone + Appearance.spacing.small * Config.background.visualiser.spacing - - Side {} - Side { - isRight: true - } - - Behavior on anchors.leftMargin { - Anim {} - } - } - } - - component Side: Repeater { - id: side - - property bool isRight - - model: Config.services.visualiserBars - - ClippingRectangle { - id: bar - - required property int modelData - property real value: Math.max(0, Math.min(1, Audio.cava.values[side.isRight ? modelData : side.count - modelData - 1])) - - clip: true - - x: modelData * ((content.width * 0.4) / Config.services.visualiserBars) + (side.isRight ? content.width * 0.6 : 0) - implicitWidth: (content.width * 0.4) / Config.services.visualiserBars - Appearance.spacing.small * Config.background.visualiser.spacing - - y: content.height - height - implicitHeight: bar.value * content.height * 0.4 - - color: "transparent" - topLeftRadius: Appearance.rounding.small * Config.background.visualiser.rounding - topRightRadius: Appearance.rounding.small * Config.background.visualiser.rounding - - Rectangle { - topLeftRadius: parent.topLeftRadius - topRightRadius: parent.topRightRadius - - gradient: Gradient { - orientation: Gradient.Vertical - - GradientStop { - position: 0 - color: Qt.alpha(Colours.palette.m3primary, 0.7) - - Behavior on color { - CAnim {} - } - } - GradientStop { - position: 1 - color: Qt.alpha(Colours.palette.m3inversePrimary, 0.7) - - Behavior on color { - CAnim {} - } - } - } - - anchors.left: parent.left - anchors.right: parent.right - y: parent.height - height - implicitHeight: content.height * 0.4 - } - - Behavior on value { - Anim { - duration: Appearance.anim.durations.small - } - } - } - } -} diff --git a/Modules/background/Wallpaper.qml b/Modules/background/Wallpaper.qml deleted file mode 100644 index a4d99ee..0000000 --- a/Modules/background/Wallpaper.qml +++ /dev/null @@ -1,143 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.components -import qs.components.images -import qs.components.filedialog -import qs.services -import qs.config -import qs.utils -import QtQuick - -Item { - id: root - - property string source: Wallpapers.current - property Image current: one - - anchors.fill: parent - - onSourceChanged: { - if (!source) - current = null; - else if (current === one) - two.update(); - else - one.update(); - } - - Loader { - anchors.fill: parent - - active: !root.source - asynchronous: true - - sourceComponent: StyledRect { - color: Colours.palette.m3surfaceContainer - - Row { - anchors.centerIn: parent - spacing: Appearance.spacing.large - - MaterialIcon { - text: "sentiment_stressed" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.extraLarge * 5 - } - - Column { - anchors.verticalCenter: parent.verticalCenter - spacing: Appearance.spacing.small - - StyledText { - text: qsTr("Wallpaper missing?") - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.extraLarge * 2 - font.bold: true - } - - StyledRect { - implicitWidth: selectWallText.implicitWidth + Appearance.padding.large * 2 - implicitHeight: selectWallText.implicitHeight + Appearance.padding.small * 2 - - radius: Appearance.rounding.full - color: Colours.palette.m3primary - - FileDialog { - id: dialog - - title: qsTr("Select a wallpaper") - filterLabel: qsTr("Image files") - filters: Images.validImageExtensions - onAccepted: path => Wallpapers.setWallpaper(path) - } - - StateLayer { - radius: parent.radius - color: Colours.palette.m3onPrimary - - function onClicked(): void { - dialog.open(); - } - } - - StyledText { - id: selectWallText - - anchors.centerIn: parent - - text: qsTr("Set it now!") - color: Colours.palette.m3onPrimary - font.pointSize: Appearance.font.size.large - } - } - } - } - } - } - - Img { - id: one - } - - Img { - id: two - } - - component Img: CachingImage { - id: img - - function update(): void { - if (path === root.source) - root.current = this; - else - path = root.source; - } - - anchors.fill: parent - - opacity: 0 - scale: Wallpapers.showPreview ? 1 : 0.8 - - onStatusChanged: { - if (status === Image.Ready) - root.current = this; - } - - states: State { - name: "visible" - when: root.current === img - - PropertyChanges { - img.opacity: 1 - img.scale: 1 - } - } - - transitions: Transition { - Anim { - target: img - properties: "opacity,scale" - } - } - } -} diff --git a/README.md b/README.md index d1a2296..7289f5b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -
-

Pet March (Evernight)

-

My selfmade desktop pet using QT

- -
- -## Feature to-do list --[] Hyprland keybind support +
+

Pet March (Evernight)

+

My selfmade desktop pet using QT

+ +
+ +## Feature to-do list +-[] Hyprland keybind support diff --git a/components/Anim.qml b/components/Anim.qml deleted file mode 100644 index 6883a79..0000000 --- a/components/Anim.qml +++ /dev/null @@ -1,8 +0,0 @@ -import qs.config -import QtQuick - -NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard -} diff --git a/components/CAnim.qml b/components/CAnim.qml deleted file mode 100644 index 49484b7..0000000 --- a/components/CAnim.qml +++ /dev/null @@ -1,8 +0,0 @@ -import qs.config -import QtQuick - -ColorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard -} diff --git a/components/MaterialIcon.qml b/components/MaterialIcon.qml deleted file mode 100644 index a1d19d3..0000000 --- a/components/MaterialIcon.qml +++ /dev/null @@ -1,16 +0,0 @@ -import qs.services -import qs.config - -StyledText { - property real fill - property int grade: Colours.light ? 0 : -25 - - font.family: Appearance.font.family.material - font.pointSize: Appearance.font.size.larger - font.variableAxes: ({ - FILL: fill.toFixed(1), - GRAD: grade, - opsz: fontInfo.pixelSize, - wght: fontInfo.weight - }) -} diff --git a/components/StateLayer.qml b/components/StateLayer.qml deleted file mode 100644 index d86e782..0000000 --- a/components/StateLayer.qml +++ /dev/null @@ -1,94 +0,0 @@ -import qs.services -import qs.config -import QtQuick - -MouseArea { - id: root - - property bool disabled - property color color: Colours.palette.m3onSurface - property real radius: parent?.radius ?? 0 - property alias rect: hoverLayer - - function onClicked(): void { - } - - anchors.fill: parent - - enabled: !disabled - cursorShape: disabled ? undefined : Qt.PointingHandCursor - hoverEnabled: true - - onPressed: event => { - if (disabled) - return; - - rippleAnim.x = event.x; - rippleAnim.y = event.y; - - const dist = (ox, oy) => ox * ox + oy * oy; - rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y), dist(event.x, height - event.y), dist(width - event.x, event.y), dist(width - event.x, height - event.y))); - - rippleAnim.restart(); - } - - onClicked: event => !disabled && onClicked(event) - - SequentialAnimation { - id: rippleAnim - - property real x - property real y - property real radius - - PropertyAction { - target: ripple - property: "x" - value: rippleAnim.x - } - PropertyAction { - target: ripple - property: "y" - value: rippleAnim.y - } - PropertyAction { - target: ripple - property: "opacity" - value: 0.08 - } - Anim { - target: ripple - properties: "implicitWidth,implicitHeight" - from: 0 - to: rippleAnim.radius * 2 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - Anim { - target: ripple - property: "opacity" - to: 0 - } - } - - StyledClippingRect { - id: hoverLayer - - anchors.fill: parent - - color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0) - radius: root.radius - - StyledRect { - id: ripple - - radius: Appearance.rounding.full - color: root.color - opacity: 0 - - transform: Translate { - x: -ripple.width / 2 - y: -ripple.height / 2 - } - } - } -} diff --git a/components/StyledClippingRect.qml b/components/StyledClippingRect.qml deleted file mode 100644 index 8f2630c..0000000 --- a/components/StyledClippingRect.qml +++ /dev/null @@ -1,12 +0,0 @@ -import Quickshell.Widgets -import QtQuick - -ClippingRectangle { - id: root - - color: "transparent" - - Behavior on color { - CAnim {} - } -} diff --git a/components/StyledRect.qml b/components/StyledRect.qml deleted file mode 100644 index f5d5143..0000000 --- a/components/StyledRect.qml +++ /dev/null @@ -1,11 +0,0 @@ -import QtQuick - -Rectangle { - id: root - - color: "transparent" - - Behavior on color { - CAnim {} - } -} diff --git a/components/StyledText.qml b/components/StyledText.qml deleted file mode 100644 index ed961d2..0000000 --- a/components/StyledText.qml +++ /dev/null @@ -1,48 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.services -import qs.config -import QtQuick - -Text { - id: root - - property bool animate: false - property string animateProp: "scale" - property real animateFrom: 0 - property real animateTo: 1 - property int animateDuration: Appearance.anim.durations.normal - - renderType: Text.NativeRendering - textFormat: Text.PlainText - color: Colours.palette.m3onSurface - font.family: Appearance.font.family.sans - font.pointSize: Appearance.font.size.smaller - - Behavior on color { - CAnim {} - } - - Behavior on text { - enabled: root.animate - - SequentialAnimation { - Anim { - to: root.animateFrom - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - PropertyAction {} - Anim { - to: root.animateTo - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - } - } - - component Anim: NumberAnimation { - target: root - property: root.animateProp - duration: root.animateDuration / 2 - easing.type: Easing.BezierSpline - } -} diff --git a/components/containers/StyledFlickable.qml b/components/containers/StyledFlickable.qml deleted file mode 100644 index bc6ae0f..0000000 --- a/components/containers/StyledFlickable.qml +++ /dev/null @@ -1,14 +0,0 @@ -import ".." -import QtQuick - -Flickable { - id: root - - maximumFlickVelocity: 3000 - - rebound: Transition { - Anim { - properties: "x,y" - } - } -} diff --git a/components/containers/StyledListView.qml b/components/containers/StyledListView.qml deleted file mode 100644 index 626d206..0000000 --- a/components/containers/StyledListView.qml +++ /dev/null @@ -1,14 +0,0 @@ -import ".." -import QtQuick - -ListView { - id: root - - maximumFlickVelocity: 3000 - - rebound: Transition { - Anim { - properties: "x,y" - } - } -} diff --git a/components/containers/StyledWindow.qml b/components/containers/StyledWindow.qml deleted file mode 100644 index 8c6e39f..0000000 --- a/components/containers/StyledWindow.qml +++ /dev/null @@ -1,9 +0,0 @@ -import Quickshell -import Quickshell.Wayland - -PanelWindow { - required property string name - - WlrLayershell.namespace: `caelestia-${name}` - color: "transparent" -} diff --git a/components/controls/CircularIndicator.qml b/components/controls/CircularIndicator.qml deleted file mode 100644 index 957899e..0000000 --- a/components/controls/CircularIndicator.qml +++ /dev/null @@ -1,108 +0,0 @@ -import ".." -import qs.services -import qs.config -import Caelestia.Internal -import QtQuick -import QtQuick.Templates - -BusyIndicator { - id: root - - enum AnimType { - Advance = 0, - Retreat - } - - enum AnimState { - Stopped, - Running, - Completing - } - - property real implicitSize: Appearance.font.size.normal * 3 - property real strokeWidth: Appearance.padding.small * 0.8 - property color fgColour: Colours.palette.m3primary - property color bgColour: Colours.palette.m3secondaryContainer - - property alias type: manager.indeterminateAnimationType - readonly property alias progress: manager.progress - - property real internalStrokeWidth: strokeWidth - property int animState - - padding: 0 - implicitWidth: implicitSize - implicitHeight: implicitSize - - Component.onCompleted: { - if (running) { - running = false; - running = true; - } - } - - onRunningChanged: { - if (running) { - manager.completeEndProgress = 0; - animState = CircularIndicator.Running; - } else { - if (animState == CircularIndicator.Running) - animState = CircularIndicator.Completing; - } - } - - states: State { - name: "stopped" - when: !root.running - - PropertyChanges { - root.opacity: 0 - root.internalStrokeWidth: root.strokeWidth / 3 - } - } - - transitions: Transition { - Anim { - properties: "opacity,internalStrokeWidth" - duration: manager.completeEndDuration * Appearance.anim.durations.scale - } - } - - contentItem: CircularProgress { - anchors.fill: parent - strokeWidth: root.internalStrokeWidth - fgColour: root.fgColour - bgColour: root.bgColour - padding: root.padding - rotation: manager.rotation - startAngle: manager.startFraction * 360 - value: manager.endFraction - manager.startFraction - } - - CircularIndicatorManager { - id: manager - } - - NumberAnimation { - running: root.animState !== CircularIndicator.Stopped - loops: Animation.Infinite - target: manager - property: "progress" - from: 0 - to: 1 - duration: manager.duration * Appearance.anim.durations.scale - } - - NumberAnimation { - running: root.animState === CircularIndicator.Completing - target: manager - property: "completeEndProgress" - from: 0 - to: 1 - duration: manager.completeEndDuration * Appearance.anim.durations.scale - onFinished: { - if (root.animState === CircularIndicator.Completing) - root.animState = CircularIndicator.Stopped; - } - } -} diff --git a/components/controls/CircularProgress.qml b/components/controls/CircularProgress.qml deleted file mode 100644 index a15cd90..0000000 --- a/components/controls/CircularProgress.qml +++ /dev/null @@ -1,69 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Shapes - -Shape { - id: root - - property real value - property int startAngle: -90 - property int strokeWidth: Appearance.padding.smaller - property int padding: 0 - property int spacing: Appearance.spacing.small - property color fgColour: Colours.palette.m3primary - property color bgColour: Colours.palette.m3secondaryContainer - - readonly property real size: Math.min(width, height) - readonly property real arcRadius: (size - padding - strokeWidth) / 2 - readonly property real vValue: value || 1 / 360 - readonly property real gapAngle: ((spacing + strokeWidth) / (arcRadius || 1)) * (180 / Math.PI) - - preferredRendererType: Shape.CurveRenderer - asynchronous: true - - ShapePath { - fillColor: "transparent" - strokeColor: root.bgColour - strokeWidth: root.strokeWidth - capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap - - PathAngleArc { - startAngle: root.startAngle + 360 * root.vValue + root.gapAngle - sweepAngle: Math.max(-root.gapAngle, 360 * (1 - root.vValue) - root.gapAngle * 2) - radiusX: root.arcRadius - radiusY: root.arcRadius - centerX: root.size / 2 - centerY: root.size / 2 - } - - Behavior on strokeColor { - CAnim { - duration: Appearance.anim.durations.large - } - } - } - - ShapePath { - fillColor: "transparent" - strokeColor: root.fgColour - strokeWidth: root.strokeWidth - capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap - - PathAngleArc { - startAngle: root.startAngle - sweepAngle: 360 * root.vValue - radiusX: root.arcRadius - radiusY: root.arcRadius - centerX: root.size / 2 - centerY: root.size / 2 - } - - Behavior on strokeColor { - CAnim { - duration: Appearance.anim.durations.large - } - } - } -} diff --git a/components/controls/CustomMouseArea.qml b/components/controls/CustomMouseArea.qml deleted file mode 100644 index 7c973c2..0000000 --- a/components/controls/CustomMouseArea.qml +++ /dev/null @@ -1,21 +0,0 @@ -import QtQuick - -MouseArea { - property int scrollAccumulatedY: 0 - - function onWheel(event: WheelEvent): void { - } - - onWheel: event => { - // Update accumulated scroll - if (Math.sign(event.angleDelta.y) !== Math.sign(scrollAccumulatedY)) - scrollAccumulatedY = 0; - scrollAccumulatedY += event.angleDelta.y; - - // Trigger handler and reset if above threshold - if (Math.abs(scrollAccumulatedY) >= 120) { - onWheel(event); - scrollAccumulatedY = 0; - } - } -} diff --git a/components/controls/CustomSpinBox.qml b/components/controls/CustomSpinBox.qml deleted file mode 100644 index e2ed508..0000000 --- a/components/controls/CustomSpinBox.qml +++ /dev/null @@ -1,108 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -RowLayout { - id: root - - property int value - property real max: Infinity - property real min: -Infinity - property alias repeatRate: timer.interval - - signal valueModified(value: int) - - spacing: Appearance.spacing.small - - StyledTextField { - inputMethodHints: Qt.ImhFormattedNumbersOnly - text: root.value - onAccepted: root.valueModified(text) - - padding: Appearance.padding.small - leftPadding: Appearance.padding.normal - rightPadding: Appearance.padding.normal - - background: StyledRect { - implicitWidth: 100 - radius: Appearance.rounding.small - color: Colours.tPalette.m3surfaceContainerHigh - } - } - - StyledRect { - radius: Appearance.rounding.small - color: Colours.palette.m3primary - - implicitWidth: implicitHeight - implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 - - StateLayer { - id: upState - - color: Colours.palette.m3onPrimary - - onPressAndHold: timer.start() - onReleased: timer.stop() - - function onClicked(): void { - root.valueModified(Math.min(root.max, root.value + 1)); - } - } - - MaterialIcon { - id: upIcon - - anchors.centerIn: parent - text: "keyboard_arrow_up" - color: Colours.palette.m3onPrimary - } - } - - StyledRect { - radius: Appearance.rounding.small - color: Colours.palette.m3primary - - implicitWidth: implicitHeight - implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2 - - StateLayer { - id: downState - - color: Colours.palette.m3onPrimary - - onPressAndHold: timer.start() - onReleased: timer.stop() - - function onClicked(): void { - root.valueModified(Math.max(root.min, root.value - 1)); - } - } - - MaterialIcon { - id: downIcon - - anchors.centerIn: parent - text: "keyboard_arrow_down" - color: Colours.palette.m3onPrimary - } - } - - Timer { - id: timer - - interval: 100 - repeat: true - triggeredOnStart: true - onTriggered: { - if (upState.pressed) - upState.onClicked(); - else if (downState.pressed) - downState.onClicked(); - } - } -} diff --git a/components/controls/FilledSlider.qml b/components/controls/FilledSlider.qml deleted file mode 100644 index 78b8a5c..0000000 --- a/components/controls/FilledSlider.qml +++ /dev/null @@ -1,146 +0,0 @@ -import ".." -import "../effects" -import qs.services -import qs.config -import QtQuick -import QtQuick.Templates - -Slider { - id: root - - required property string icon - property real oldValue - property bool initialized - - orientation: Qt.Vertical - - background: StyledRect { - color: Colours.tPalette.m3surfaceContainer - radius: Appearance.rounding.full - - StyledRect { - anchors.left: parent.left - anchors.right: parent.right - - y: root.handle.y - implicitHeight: parent.height - y - - color: Colours.palette.m3secondary - radius: parent.radius - } - } - - handle: Item { - id: handle - - property alias moving: icon.moving - - y: root.visualPosition * (root.availableHeight - height) - implicitWidth: root.width - implicitHeight: root.width - - Elevation { - anchors.fill: parent - radius: rect.radius - level: handleInteraction.containsMouse ? 2 : 1 - } - - StyledRect { - id: rect - - anchors.fill: parent - - color: Colours.palette.m3inverseSurface - radius: Appearance.rounding.full - - MouseArea { - id: handleInteraction - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - acceptedButtons: Qt.NoButton - } - - MaterialIcon { - id: icon - - property bool moving - - function update(): void { - animate = !moving; - binding.when = moving; - font.pointSize = moving ? Appearance.font.size.small : Appearance.font.size.larger; - font.family = moving ? Appearance.font.family.sans : Appearance.font.family.material; - } - - text: root.icon - color: Colours.palette.m3inverseOnSurface - anchors.centerIn: parent - - onMovingChanged: anim.restart() - - Binding { - id: binding - - target: icon - property: "text" - value: Math.round(root.value * 100) - when: false - } - - SequentialAnimation { - id: anim - - Anim { - target: icon - property: "scale" - to: 0 - duration: Appearance.anim.durations.normal / 2 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - ScriptAction { - script: icon.update() - } - Anim { - target: icon - property: "scale" - to: 1 - duration: Appearance.anim.durations.normal / 2 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - } - } - } - } - - onPressedChanged: handle.moving = pressed - - onValueChanged: { - if (!initialized) { - initialized = true; - return; - } - if (Math.abs(value - oldValue) < 0.01) - return; - oldValue = value; - handle.moving = true; - stateChangeDelay.restart(); - } - - Timer { - id: stateChangeDelay - - interval: 500 - onTriggered: { - if (!root.pressed) - handle.moving = false; - } - } - - Behavior on value { - Anim { - duration: Appearance.anim.durations.large - } - } -} diff --git a/components/controls/IconButton.qml b/components/controls/IconButton.qml deleted file mode 100644 index dc3b04b..0000000 --- a/components/controls/IconButton.qml +++ /dev/null @@ -1,83 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick - -StyledRect { - id: root - - enum Type { - Filled, - Tonal, - Text - } - - property alias icon: label.text - property bool checked - property bool toggle - property real padding: type === IconButton.Text ? Appearance.padding.small / 2 : Appearance.padding.smaller - property alias font: label.font - property int type: IconButton.Filled - property bool disabled - - property alias stateLayer: stateLayer - property alias label: label - property alias radiusAnim: radiusAnim - - property bool internalChecked - property color activeColour: type === IconButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary - property color inactiveColour: { - if (!toggle && type === IconButton.Filled) - return Colours.palette.m3primary; - return type === IconButton.Filled ? Colours.tPalette.m3surfaceContainer : Colours.palette.m3secondaryContainer; - } - property color activeOnColour: type === IconButton.Filled ? Colours.palette.m3onPrimary : type === IconButton.Tonal ? Colours.palette.m3onSecondary : Colours.palette.m3primary - property color inactiveOnColour: { - if (!toggle && type === IconButton.Filled) - return Colours.palette.m3onPrimary; - return type === IconButton.Tonal ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurfaceVariant; - } - property color disabledColour: Qt.alpha(Colours.palette.m3onSurface, 0.1) - property color disabledOnColour: Qt.alpha(Colours.palette.m3onSurface, 0.38) - - signal clicked - - onCheckedChanged: internalChecked = checked - - radius: internalChecked ? Appearance.rounding.small : implicitHeight / 2 - color: type === IconButton.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour - - implicitWidth: implicitHeight - implicitHeight: label.implicitHeight + padding * 2 - - StateLayer { - id: stateLayer - - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - disabled: root.disabled - - function onClicked(): void { - if (root.toggle) - root.internalChecked = !root.internalChecked; - root.clicked(); - } - } - - MaterialIcon { - id: label - - anchors.centerIn: parent - color: root.disabled ? root.disabledOnColour : root.internalChecked ? root.activeOnColour : root.inactiveOnColour - fill: !root.toggle || root.internalChecked ? 1 : 0 - - Behavior on fill { - Anim {} - } - } - - Behavior on radius { - Anim { - id: radiusAnim - } - } -} diff --git a/components/controls/IconTextButton.qml b/components/controls/IconTextButton.qml deleted file mode 100644 index 78e7c5b..0000000 --- a/components/controls/IconTextButton.qml +++ /dev/null @@ -1,85 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick - -StyledRect { - id: root - - enum Type { - Filled, - Tonal, - Text - } - - property alias icon: iconLabel.text - property alias text: label.text - property bool checked - property bool toggle - property real horizontalPadding: Appearance.padding.normal - property real verticalPadding: Appearance.padding.smaller - property alias font: label.font - property int type: IconTextButton.Filled - - property alias stateLayer: stateLayer - property alias iconLabel: iconLabel - property alias label: label - - property bool internalChecked - property color activeColour: type === IconTextButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary - property color inactiveColour: type === IconTextButton.Filled ? Colours.tPalette.m3surfaceContainer : Colours.palette.m3secondaryContainer - property color activeOnColour: type === IconTextButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary - property color inactiveOnColour: type === IconTextButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer - - signal clicked - - onCheckedChanged: internalChecked = checked - - radius: internalChecked ? Appearance.rounding.small : implicitHeight / 2 - color: type === IconTextButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour - - implicitWidth: row.implicitWidth + horizontalPadding * 2 - implicitHeight: row.implicitHeight + verticalPadding * 2 - - StateLayer { - id: stateLayer - - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - - function onClicked(): void { - if (root.toggle) - root.internalChecked = !root.internalChecked; - root.clicked(); - } - } - - Row { - id: row - - anchors.centerIn: parent - spacing: Appearance.spacing.small - - MaterialIcon { - id: iconLabel - - anchors.verticalCenter: parent.verticalCenter - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - fill: root.internalChecked ? 1 : 0 - - Behavior on fill { - Anim {} - } - } - - StyledText { - id: label - - anchors.verticalCenter: parent.verticalCenter - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - } - } - - Behavior on radius { - Anim {} - } -} diff --git a/components/controls/Menu.qml b/components/controls/Menu.qml deleted file mode 100644 index c763b54..0000000 --- a/components/controls/Menu.qml +++ /dev/null @@ -1,113 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import "../effects" -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -Elevation { - id: root - - property list items - property MenuItem active: items[0] ?? null - property bool expanded - - signal itemSelected(item: MenuItem) - - radius: Appearance.rounding.small / 2 - level: 2 - - implicitWidth: Math.max(200, column.implicitWidth) - implicitHeight: root.expanded ? column.implicitHeight : 0 - opacity: root.expanded ? 1 : 0 - - StyledClippingRect { - anchors.fill: parent - radius: parent.radius - color: Colours.palette.m3surfaceContainer - - ColumnLayout { - id: column - - anchors.left: parent.left - anchors.right: parent.right - spacing: 0 - - Repeater { - model: root.items - - StyledRect { - id: item - - required property int index - required property MenuItem modelData - readonly property bool active: modelData === root.active - - Layout.fillWidth: true - implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2 - - color: Qt.alpha(Colours.palette.m3secondaryContainer, active ? 1 : 0) - - StateLayer { - color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - disabled: !root.expanded - - function onClicked(): void { - root.itemSelected(item.modelData); - root.active = item.modelData; - root.expanded = false; - } - } - - RowLayout { - id: menuOptionRow - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.small - - MaterialIcon { - Layout.alignment: Qt.AlignVCenter - text: item.modelData.icon - color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurfaceVariant - } - - StyledText { - Layout.alignment: Qt.AlignVCenter - Layout.fillWidth: true - text: item.modelData.text - color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - } - - Loader { - Layout.alignment: Qt.AlignVCenter - active: item.modelData.trailingIcon.length > 0 - visible: active - - sourceComponent: MaterialIcon { - text: item.modelData.trailingIcon - color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - } - } - } - } - } - } - } - - Behavior on opacity { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - } - } - - Behavior on implicitHeight { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } -} diff --git a/components/controls/MenuItem.qml b/components/controls/MenuItem.qml deleted file mode 100644 index 5348bbe..0000000 --- a/components/controls/MenuItem.qml +++ /dev/null @@ -1,11 +0,0 @@ -import QtQuick - -QtObject { - required property string text - property string icon - property string trailingIcon - property string activeIcon: icon - property string activeText: text - - signal clicked -} diff --git a/components/controls/SplitButton.qml b/components/controls/SplitButton.qml deleted file mode 100644 index d7f2651..0000000 --- a/components/controls/SplitButton.qml +++ /dev/null @@ -1,164 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -Row { - id: root - - enum Type { - Filled, - Tonal - } - - property real horizontalPadding: Appearance.padding.normal - property real verticalPadding: Appearance.padding.smaller - property int type: SplitButton.Filled - property bool disabled - property bool menuOnTop - property string fallbackIcon - property string fallbackText - - property alias menuItems: menu.items - property alias active: menu.active - property alias expanded: menu.expanded - property alias menu: menu - property alias iconLabel: iconLabel - property alias label: label - property alias stateLayer: stateLayer - - property color colour: type == SplitButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondaryContainer - property color textColour: type == SplitButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondaryContainer - property color disabledColour: Qt.alpha(Colours.palette.m3onSurface, 0.1) - property color disabledTextColour: Qt.alpha(Colours.palette.m3onSurface, 0.38) - - spacing: Math.floor(Appearance.spacing.small / 2) - - StyledRect { - radius: implicitHeight / 2 - topRightRadius: Appearance.rounding.small / 2 - bottomRightRadius: Appearance.rounding.small / 2 - color: root.disabled ? root.disabledColour : root.colour - - implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2 - implicitHeight: expandBtn.implicitHeight - - StateLayer { - id: stateLayer - - rect.topRightRadius: parent.topRightRadius - rect.bottomRightRadius: parent.bottomRightRadius - color: root.textColour - disabled: root.disabled - - function onClicked(): void { - root.active?.clicked(); - } - } - - RowLayout { - id: textRow - - anchors.centerIn: parent - anchors.horizontalCenterOffset: Math.floor(root.verticalPadding / 4) - spacing: Appearance.spacing.small - - MaterialIcon { - id: iconLabel - - Layout.alignment: Qt.AlignVCenter - animate: true - text: root.active?.activeIcon ?? root.fallbackIcon - color: root.disabled ? root.disabledTextColour : root.textColour - fill: 1 - } - - StyledText { - id: label - - Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: implicitWidth - animate: true - text: root.active?.activeText ?? root.fallbackText - color: root.disabled ? root.disabledTextColour : root.textColour - clip: true - - Behavior on Layout.preferredWidth { - Anim { - easing.bezierCurve: Appearance.anim.curves.emphasized - } - } - } - } - } - - StyledRect { - id: expandBtn - - property real rad: root.expanded ? implicitHeight / 2 : Appearance.rounding.small / 2 - - radius: implicitHeight / 2 - topLeftRadius: rad - bottomLeftRadius: rad - color: root.disabled ? root.disabledColour : root.colour - - implicitWidth: implicitHeight - implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2 - - StateLayer { - id: expandStateLayer - - rect.topLeftRadius: parent.topLeftRadius - rect.bottomLeftRadius: parent.bottomLeftRadius - color: root.textColour - disabled: root.disabled - - function onClicked(): void { - root.expanded = !root.expanded; - } - } - - MaterialIcon { - id: expandIcon - - anchors.centerIn: parent - anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4) - - text: "expand_more" - color: root.disabled ? root.disabledTextColour : root.textColour - rotation: root.expanded ? 180 : 0 - - Behavior on anchors.horizontalCenterOffset { - Anim {} - } - - Behavior on rotation { - Anim {} - } - } - - Behavior on rad { - Anim {} - } - - Menu { - id: menu - - states: State { - when: root.menuOnTop - - AnchorChanges { - target: menu - anchors.top: undefined - anchors.bottom: expandBtn.top - } - } - - anchors.top: parent.bottom - anchors.right: parent.right - anchors.topMargin: Appearance.spacing.small - anchors.bottomMargin: Appearance.spacing.small - } - } -} diff --git a/components/controls/StyledRadioButton.qml b/components/controls/StyledRadioButton.qml deleted file mode 100644 index b72fc77..0000000 --- a/components/controls/StyledRadioButton.qml +++ /dev/null @@ -1,57 +0,0 @@ -import qs.components -import qs.services -import qs.config -import QtQuick -import QtQuick.Templates - -RadioButton { - id: root - - font.pointSize: Appearance.font.size.smaller - - implicitWidth: implicitIndicatorWidth + implicitContentWidth + contentItem.anchors.leftMargin - implicitHeight: Math.max(implicitIndicatorHeight, implicitContentHeight) - - indicator: Rectangle { - id: outerCircle - - implicitWidth: 20 - implicitHeight: 20 - radius: Appearance.rounding.full - color: "transparent" - border.color: root.checked ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant - border.width: 2 - anchors.verticalCenter: parent.verticalCenter - - StateLayer { - anchors.margins: -Appearance.padding.smaller - color: root.checked ? Colours.palette.m3onSurface : Colours.palette.m3primary - z: -1 - - function onClicked(): void { - root.click(); - } - } - - StyledRect { - anchors.centerIn: parent - implicitWidth: 8 - implicitHeight: 8 - - radius: Appearance.rounding.full - color: Qt.alpha(Colours.palette.m3primary, root.checked ? 1 : 0) - } - - Behavior on border.color { - CAnim {} - } - } - - contentItem: StyledText { - text: root.text - font.pointSize: root.font.pointSize - anchors.verticalCenter: parent.verticalCenter - anchors.left: outerCircle.right - anchors.leftMargin: Appearance.spacing.smaller - } -} diff --git a/components/controls/StyledScrollBar.qml b/components/controls/StyledScrollBar.qml deleted file mode 100644 index fc641b5..0000000 --- a/components/controls/StyledScrollBar.qml +++ /dev/null @@ -1,108 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Templates - -ScrollBar { - id: root - - required property Flickable flickable - property bool shouldBeActive - property real nonAnimPosition - property bool animating - - onHoveredChanged: { - if (hovered) - shouldBeActive = true; - else - shouldBeActive = flickable.moving; - } - - onPositionChanged: { - if (position === nonAnimPosition) - animating = false; - else if (!animating) - nonAnimPosition = position; - } - - position: nonAnimPosition - implicitWidth: Appearance.padding.small - - contentItem: StyledRect { - anchors.left: parent.left - anchors.right: parent.right - opacity: { - if (root.size === 1) - return 0; - if (fullMouse.pressed) - return 1; - if (mouse.containsMouse) - return 0.8; - if (root.policy === ScrollBar.AlwaysOn || root.shouldBeActive) - return 0.6; - return 0; - } - radius: Appearance.rounding.full - color: Colours.palette.m3secondary - - MouseArea { - id: mouse - - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - acceptedButtons: Qt.NoButton - } - - Behavior on opacity { - Anim {} - } - } - - Connections { - target: root.flickable - - function onMovingChanged(): void { - if (root.flickable.moving) - root.shouldBeActive = true; - else - hideDelay.restart(); - } - } - - Timer { - id: hideDelay - - interval: 600 - onTriggered: root.shouldBeActive = root.flickable.moving || root.hovered - } - - CustomMouseArea { - id: fullMouse - - anchors.fill: parent - preventStealing: true - - onPressed: event => { - root.animating = true; - root.nonAnimPosition = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2)); - } - - onPositionChanged: event => root.nonAnimPosition = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2)) - - function onWheel(event: WheelEvent): void { - root.animating = true; - if (event.angleDelta.y > 0) - root.nonAnimPosition = Math.max(0, root.nonAnimPosition - 0.1); - else if (event.angleDelta.y < 0) - root.nonAnimPosition = Math.min(1 - root.size, root.nonAnimPosition + 0.1); - } - } - - Behavior on position { - enabled: !fullMouse.pressed - - Anim {} - } -} diff --git a/components/controls/StyledSlider.qml b/components/controls/StyledSlider.qml deleted file mode 100644 index 92c8aa8..0000000 --- a/components/controls/StyledSlider.qml +++ /dev/null @@ -1,57 +0,0 @@ -import qs.components -import qs.services -import qs.config -import QtQuick -import QtQuick.Templates - -Slider { - id: root - - background: Item { - StyledRect { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.topMargin: root.implicitHeight / 3 - anchors.bottomMargin: root.implicitHeight / 3 - - implicitWidth: root.handle.x - root.implicitHeight / 6 - - color: Colours.palette.m3primary - radius: Appearance.rounding.full - topRightRadius: root.implicitHeight / 15 - bottomRightRadius: root.implicitHeight / 15 - } - - StyledRect { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - anchors.topMargin: root.implicitHeight / 3 - anchors.bottomMargin: root.implicitHeight / 3 - - implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 6 - - color: Colours.tPalette.m3surfaceContainer - radius: Appearance.rounding.full - topLeftRadius: root.implicitHeight / 15 - bottomLeftRadius: root.implicitHeight / 15 - } - } - - handle: StyledRect { - x: root.visualPosition * root.availableWidth - implicitWidth / 2 - - implicitWidth: root.implicitHeight / 4.5 - implicitHeight: root.implicitHeight - - color: Colours.palette.m3primary - radius: Appearance.rounding.full - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.NoButton - cursorShape: Qt.PointingHandCursor - } - } -} diff --git a/components/controls/StyledSwitch.qml b/components/controls/StyledSwitch.qml deleted file mode 100644 index ce93cd5..0000000 --- a/components/controls/StyledSwitch.qml +++ /dev/null @@ -1,152 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Templates -import QtQuick.Shapes - -Switch { - id: root - - property int cLayer: 1 - - implicitWidth: implicitIndicatorWidth - implicitHeight: implicitIndicatorHeight - - indicator: StyledRect { - radius: Appearance.rounding.full - color: root.checked ? Colours.palette.m3primary : Colours.layer(Colours.palette.m3surfaceContainerHighest, root.cLayer) - - implicitWidth: implicitHeight * 1.7 - implicitHeight: Appearance.font.size.normal + Appearance.padding.smaller * 2 - - StyledRect { - readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight - - radius: Appearance.rounding.full - color: root.checked ? Colours.palette.m3onPrimary : Colours.layer(Colours.palette.m3outline, root.cLayer + 1) - - x: root.checked ? parent.implicitWidth - nonAnimWidth - Appearance.padding.small / 2 : Appearance.padding.small / 2 - implicitWidth: nonAnimWidth - implicitHeight: parent.implicitHeight - Appearance.padding.small - anchors.verticalCenter: parent.verticalCenter - - StyledRect { - anchors.fill: parent - radius: parent.radius - - color: root.checked ? Colours.palette.m3primary : Colours.palette.m3onSurface - opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0 - - Behavior on opacity { - Anim {} - } - } - - Shape { - id: icon - - property point start1: { - if (root.pressed) - return Qt.point(width * 0.2, height / 2); - if (root.checked) - return Qt.point(width * 0.15, height / 2); - return Qt.point(width * 0.15, height * 0.15); - } - property point end1: { - if (root.pressed) { - if (root.checked) - return Qt.point(width * 0.4, height / 2); - return Qt.point(width * 0.8, height / 2); - } - if (root.checked) - return Qt.point(width * 0.4, height * 0.7); - return Qt.point(width * 0.85, height * 0.85); - } - property point start2: { - if (root.pressed) { - if (root.checked) - return Qt.point(width * 0.4, height / 2); - return Qt.point(width * 0.2, height / 2); - } - if (root.checked) - return Qt.point(width * 0.4, height * 0.7); - return Qt.point(width * 0.15, height * 0.85); - } - property point end2: { - if (root.pressed) - return Qt.point(width * 0.8, height / 2); - if (root.checked) - return Qt.point(width * 0.85, height * 0.2); - return Qt.point(width * 0.85, height * 0.15); - } - - anchors.centerIn: parent - width: height - height: parent.implicitHeight - Appearance.padding.small * 2 - preferredRendererType: Shape.CurveRenderer - asynchronous: true - - ShapePath { - strokeWidth: Appearance.font.size.larger * 0.15 - strokeColor: root.checked ? Colours.palette.m3primary : Colours.palette.m3surfaceContainerHighest - fillColor: "transparent" - capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap - - startX: icon.start1.x - startY: icon.start1.y - - PathLine { - x: icon.end1.x - y: icon.end1.y - } - PathMove { - x: icon.start2.x - y: icon.start2.y - } - PathLine { - x: icon.end2.x - y: icon.end2.y - } - - Behavior on strokeColor { - CAnim {} - } - } - - Behavior on start1 { - PropAnim {} - } - Behavior on end1 { - PropAnim {} - } - Behavior on start2 { - PropAnim {} - } - Behavior on end2 { - PropAnim {} - } - } - - Behavior on x { - Anim {} - } - - Behavior on implicitWidth { - Anim {} - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - enabled: false - } - - component PropAnim: PropertyAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } -} diff --git a/components/controls/StyledTextField.qml b/components/controls/StyledTextField.qml deleted file mode 100644 index 4db87e9..0000000 --- a/components/controls/StyledTextField.qml +++ /dev/null @@ -1,76 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Controls - -TextField { - id: root - - color: Colours.palette.m3onSurface - placeholderTextColor: Colours.palette.m3outline - font.family: Appearance.font.family.sans - font.pointSize: Appearance.font.size.smaller - renderType: TextField.NativeRendering - cursorVisible: !readOnly - - background: null - - cursorDelegate: StyledRect { - id: cursor - - property bool disableBlink - - implicitWidth: 2 - color: Colours.palette.m3primary - radius: Appearance.rounding.normal - - Connections { - target: root - - function onCursorPositionChanged(): void { - if (root.activeFocus && root.cursorVisible) { - cursor.opacity = 1; - cursor.disableBlink = true; - enableBlink.restart(); - } - } - } - - Timer { - id: enableBlink - - interval: 100 - onTriggered: cursor.disableBlink = false - } - - Timer { - running: root.activeFocus && root.cursorVisible && !cursor.disableBlink - repeat: true - triggeredOnStart: true - interval: 500 - onTriggered: parent.opacity = parent.opacity === 1 ? 0 : 1 - } - - Binding { - when: !root.activeFocus || !root.cursorVisible - cursor.opacity: 0 - } - - Behavior on opacity { - Anim { - duration: Appearance.anim.durations.small - } - } - } - - Behavior on color { - CAnim {} - } - - Behavior on placeholderTextColor { - CAnim {} - } -} diff --git a/components/controls/TextButton.qml b/components/controls/TextButton.qml deleted file mode 100644 index ef84185..0000000 --- a/components/controls/TextButton.qml +++ /dev/null @@ -1,78 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick - -StyledRect { - id: root - - enum Type { - Filled, - Tonal, - Text - } - - property alias text: label.text - property bool checked - property bool toggle - property real horizontalPadding: Appearance.padding.normal - property real verticalPadding: Appearance.padding.smaller - property alias font: label.font - property int type: TextButton.Filled - - property alias stateLayer: stateLayer - property alias label: label - - property bool internalChecked - property color activeColour: type === TextButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary - property color inactiveColour: { - if (!toggle && type === TextButton.Filled) - return Colours.palette.m3primary; - return type === TextButton.Filled ? Colours.tPalette.m3surfaceContainer : Colours.palette.m3secondaryContainer; - } - property color activeOnColour: { - if (type === TextButton.Text) - return Colours.palette.m3primary; - return type === TextButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary; - } - property color inactiveOnColour: { - if (!toggle && type === TextButton.Filled) - return Colours.palette.m3onPrimary; - if (type === TextButton.Text) - return Colours.palette.m3primary; - return type === TextButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer; - } - - signal clicked - - onCheckedChanged: internalChecked = checked - - radius: internalChecked ? Appearance.rounding.small : implicitHeight / 2 - color: type === TextButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour - - implicitWidth: label.implicitWidth + horizontalPadding * 2 - implicitHeight: label.implicitHeight + verticalPadding * 2 - - StateLayer { - id: stateLayer - - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - - function onClicked(): void { - if (root.toggle) - root.internalChecked = !root.internalChecked; - root.clicked(); - } - } - - StyledText { - id: label - - anchors.centerIn: parent - color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour - } - - Behavior on radius { - Anim {} - } -} diff --git a/components/effects/ColouredIcon.qml b/components/effects/ColouredIcon.qml deleted file mode 100644 index 5ef4d4c..0000000 --- a/components/effects/ColouredIcon.qml +++ /dev/null @@ -1,35 +0,0 @@ -pragma ComponentBehavior: Bound - -import Caelestia -import Quickshell.Widgets -import QtQuick - -IconImage { - id: root - - required property color colour - - asynchronous: true - - layer.enabled: true - layer.effect: Colouriser { - sourceColor: analyser.dominantColour - colorizationColor: root.colour - } - - layer.onEnabledChanged: { - if (layer.enabled && status === Image.Ready) - analyser.requestUpdate(); - } - - onStatusChanged: { - if (layer.enabled && status === Image.Ready) - analyser.requestUpdate(); - } - - ImageAnalyser { - id: analyser - - sourceItem: root - } -} diff --git a/components/effects/Colouriser.qml b/components/effects/Colouriser.qml deleted file mode 100644 index 2948155..0000000 --- a/components/effects/Colouriser.qml +++ /dev/null @@ -1,14 +0,0 @@ -import ".." -import QtQuick -import QtQuick.Effects - -MultiEffect { - property color sourceColor: "black" - - colorization: 1 - brightness: 1 - sourceColor.hslLightness - - Behavior on colorizationColor { - CAnim {} - } -} diff --git a/components/effects/Elevation.qml b/components/effects/Elevation.qml deleted file mode 100644 index fb29f16..0000000 --- a/components/effects/Elevation.qml +++ /dev/null @@ -1,18 +0,0 @@ -import ".." -import qs.services -import QtQuick -import QtQuick.Effects - -RectangularShadow { - property int level - property real dp: [0, 1, 3, 6, 8, 12][level] - - color: Qt.alpha(Colours.palette.m3shadow, 0.7) - blur: (dp * 5) ** 0.7 - spread: -dp * 0.3 + (dp * 0.1) ** 2 - offset.y: dp / 2 - - Behavior on dp { - Anim {} - } -} diff --git a/components/effects/InnerBorder.qml b/components/effects/InnerBorder.qml deleted file mode 100644 index d4a751f..0000000 --- a/components/effects/InnerBorder.qml +++ /dev/null @@ -1,44 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Effects - -StyledRect { - property alias innerRadius: maskInner.radius - property alias thickness: maskInner.anchors.margins - property alias leftThickness: maskInner.anchors.leftMargin - property alias topThickness: maskInner.anchors.topMargin - property alias rightThickness: maskInner.anchors.rightMargin - property alias bottomThickness: maskInner.anchors.bottomMargin - - anchors.fill: parent - color: Colours.tPalette.m3surfaceContainer - - layer.enabled: true - layer.effect: MultiEffect { - maskSource: mask - maskEnabled: true - maskInverted: true - maskThresholdMin: 0.5 - maskSpreadAtMin: 1 - } - - Item { - id: mask - - anchors.fill: parent - layer.enabled: true - visible: false - - Rectangle { - id: maskInner - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - radius: Appearance.rounding.small - } - } -} diff --git a/components/effects/OpacityMask.qml b/components/effects/OpacityMask.qml deleted file mode 100644 index 22e4249..0000000 --- a/components/effects/OpacityMask.qml +++ /dev/null @@ -1,9 +0,0 @@ -import Quickshell -import QtQuick - -ShaderEffect { - required property Item source - required property Item maskSource - - fragmentShader: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/shaders/opacitymask.frag.qsb`) -} diff --git a/components/filedialog/CurrentItem.qml b/components/filedialog/CurrentItem.qml deleted file mode 100644 index bb87133..0000000 --- a/components/filedialog/CurrentItem.qml +++ /dev/null @@ -1,102 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Shapes - -Item { - id: root - - required property var currentItem - - implicitWidth: content.implicitWidth + Appearance.padding.larger + content.anchors.rightMargin - implicitHeight: currentItem ? content.implicitHeight + Appearance.padding.normal + content.anchors.bottomMargin : 0 - - Shape { - preferredRendererType: Shape.CurveRenderer - - ShapePath { - id: path - - readonly property real rounding: Appearance.rounding.small - readonly property bool flatten: root.implicitHeight < rounding * 2 - readonly property real roundingY: flatten ? root.implicitHeight / 2 : rounding - - strokeWidth: -1 - fillColor: Colours.tPalette.m3surfaceContainer - - startX: root.implicitWidth - startY: root.implicitHeight - - PathLine { - relativeX: -(root.implicitWidth + path.rounding) - relativeY: 0 - } - PathArc { - relativeX: path.rounding - relativeY: -path.roundingY - radiusX: path.rounding - radiusY: Math.min(path.rounding, root.implicitHeight) - direction: PathArc.Counterclockwise - } - PathLine { - relativeX: 0 - relativeY: -(root.implicitHeight - path.roundingY * 2) - } - PathArc { - relativeX: path.rounding - relativeY: -path.roundingY - radiusX: path.rounding - radiusY: Math.min(path.rounding, root.implicitHeight) - } - PathLine { - relativeX: root.implicitHeight > 0 ? root.implicitWidth - path.rounding * 2 : root.implicitWidth - relativeY: 0 - } - PathArc { - relativeX: path.rounding - relativeY: -path.rounding - radiusX: path.rounding - radiusY: path.rounding - direction: PathArc.Counterclockwise - } - - Behavior on fillColor { - CAnim {} - } - } - } - - Item { - anchors.fill: parent - clip: true - - StyledText { - id: content - - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.rightMargin: Appearance.padding.larger - Appearance.padding.small - anchors.bottomMargin: Appearance.padding.normal - Appearance.padding.small - - Connections { - target: root - - function onCurrentItemChanged(): void { - if (root.currentItem) - content.text = qsTr(`"%1" selected`).arg(root.currentItem.modelData.name); - } - } - } - } - - Behavior on implicitWidth { - enabled: !!root.currentItem - - Anim {} - } - - Behavior on implicitHeight { - Anim {} - } -} diff --git a/components/filedialog/DialogButtons.qml b/components/filedialog/DialogButtons.qml deleted file mode 100644 index bde9ac2..0000000 --- a/components/filedialog/DialogButtons.qml +++ /dev/null @@ -1,93 +0,0 @@ -import ".." -import qs.services -import qs.config -import QtQuick.Layouts - -StyledRect { - id: root - - required property var dialog - required property FolderContents folder - - implicitHeight: inner.implicitHeight + Appearance.padding.normal * 2 - - color: Colours.tPalette.m3surfaceContainer - - RowLayout { - id: inner - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.small - - StyledText { - text: qsTr("Filter:") - } - - StyledRect { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.rightMargin: Appearance.spacing.normal - - color: Colours.tPalette.m3surfaceContainerHigh - radius: Appearance.rounding.small - - StyledText { - anchors.fill: parent - anchors.margins: Appearance.padding.normal - - text: `${root.dialog.filterLabel} (${root.dialog.filters.map(f => `*.${f}`).join(", ")})` - } - } - - StyledRect { - color: Colours.tPalette.m3surfaceContainerHigh - radius: Appearance.rounding.small - - implicitWidth: cancelText.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: cancelText.implicitHeight + Appearance.padding.normal * 2 - - StateLayer { - disabled: !root.dialog.selectionValid - - function onClicked(): void { - root.dialog.accepted(root.folder.currentItem.modelData.path); - } - } - - StyledText { - id: selectText - - anchors.centerIn: parent - anchors.margins: Appearance.padding.normal - - text: qsTr("Select") - color: root.dialog.selectionValid ? Colours.palette.m3onSurface : Colours.palette.m3outline - } - } - - StyledRect { - color: Colours.tPalette.m3surfaceContainerHigh - radius: Appearance.rounding.small - - implicitWidth: cancelText.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: cancelText.implicitHeight + Appearance.padding.normal * 2 - - StateLayer { - function onClicked(): void { - root.dialog.rejected(); - } - } - - StyledText { - id: cancelText - - anchors.centerIn: parent - anchors.margins: Appearance.padding.normal - - text: qsTr("Cancel") - } - } - } -} diff --git a/components/filedialog/FileDialog.qml b/components/filedialog/FileDialog.qml deleted file mode 100644 index f3187a5..0000000 --- a/components/filedialog/FileDialog.qml +++ /dev/null @@ -1,102 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.components -import qs.services -import Quickshell -import QtQuick -import QtQuick.Layouts - -LazyLoader { - id: loader - - property list cwd: ["Home"] - property string filterLabel: "All files" - property list filters: ["*"] - property string title: qsTr("Select a file") - - signal accepted(path: string) - signal rejected - - function open(): void { - activeAsync = true; - } - - function close(): void { - rejected(); - } - - onAccepted: activeAsync = false - onRejected: activeAsync = false - - FloatingWindow { - id: root - - property list cwd: loader.cwd - property string filterLabel: loader.filterLabel - property list filters: loader.filters - - readonly property bool selectionValid: { - const file = folderContents.currentItem?.modelData; - return (file && !file.isDir && (filters.includes("*") || filters.includes(file.suffix))) ?? false; - } - - function accepted(path: string): void { - loader.accepted(path); - } - - function rejected(): void { - loader.rejected(); - } - - implicitWidth: 1000 - implicitHeight: 600 - color: Colours.tPalette.m3surface - title: loader.title - - onVisibleChanged: { - if (!visible) - rejected(); - } - - RowLayout { - anchors.fill: parent - - spacing: 0 - - Sidebar { - Layout.fillHeight: true - dialog: root - } - - ColumnLayout { - Layout.fillWidth: true - Layout.fillHeight: true - - spacing: 0 - - HeaderBar { - Layout.fillWidth: true - dialog: root - } - - FolderContents { - id: folderContents - - Layout.fillWidth: true - Layout.fillHeight: true - dialog: root - } - - DialogButtons { - Layout.fillWidth: true - dialog: root - folder: folderContents - } - } - } - - Behavior on color { - CAnim {} - } - } -} diff --git a/components/filedialog/FolderContents.qml b/components/filedialog/FolderContents.qml deleted file mode 100644 index c3b371b..0000000 --- a/components/filedialog/FolderContents.qml +++ /dev/null @@ -1,229 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import "../controls" -import "../images" -import qs.services -import qs.config -import qs.utils -import Caelestia.Models -import Quickshell -import QtQuick -import QtQuick.Layouts -import QtQuick.Effects - -Item { - id: root - - required property var dialog - property alias currentItem: view.currentItem - - StyledRect { - anchors.fill: parent - color: Colours.tPalette.m3surfaceContainer - - layer.enabled: true - layer.effect: MultiEffect { - maskSource: mask - maskEnabled: true - maskInverted: true - maskThresholdMin: 0.5 - maskSpreadAtMin: 1 - } - } - - Item { - id: mask - - anchors.fill: parent - layer.enabled: true - visible: false - - Rectangle { - anchors.fill: parent - anchors.margins: Appearance.padding.small - radius: Appearance.rounding.small - } - } - - Loader { - anchors.centerIn: parent - - opacity: view.count === 0 ? 1 : 0 - active: opacity > 0 - asynchronous: true - - sourceComponent: ColumnLayout { - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "scan_delete" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.extraLarge * 2 - font.weight: 500 - } - - StyledText { - text: qsTr("This folder is empty") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.large - font.weight: 500 - } - } - - Behavior on opacity { - Anim {} - } - } - - GridView { - id: view - - anchors.fill: parent - anchors.margins: Appearance.padding.small + Appearance.padding.normal - - cellWidth: Sizes.itemWidth + Appearance.spacing.small - cellHeight: Sizes.itemWidth + Appearance.spacing.small * 2 + Appearance.padding.normal * 2 + 1 - - clip: true - focus: true - currentIndex: -1 - Keys.onEscapePressed: currentIndex = -1 - - Keys.onReturnPressed: { - if (root.dialog.selectionValid) - root.dialog.accepted(currentItem.modelData.path); - } - Keys.onEnterPressed: { - if (root.dialog.selectionValid) - root.dialog.accepted(currentItem.modelData.path); - } - - StyledScrollBar.vertical: StyledScrollBar { - flickable: view - } - - model: FileSystemModel { - path: { - if (root.dialog.cwd[0] === "Home") - return `${Paths.home}/${root.dialog.cwd.slice(1).join("/")}`; - else - return root.dialog.cwd.join("/"); - } - onPathChanged: view.currentIndex = -1 - } - - delegate: StyledRect { - id: item - - required property int index - required property FileSystemEntry modelData - - readonly property real nonAnimHeight: icon.implicitHeight + name.anchors.topMargin + name.implicitHeight + Appearance.padding.normal * 2 - - implicitWidth: Sizes.itemWidth - implicitHeight: nonAnimHeight - - radius: Appearance.rounding.normal - color: Qt.alpha(Colours.tPalette.m3surfaceContainerHighest, GridView.isCurrentItem ? Colours.tPalette.m3surfaceContainerHighest.a : 0) - z: GridView.isCurrentItem || implicitHeight !== nonAnimHeight ? 1 : 0 - clip: true - - StateLayer { - onDoubleClicked: { - if (item.modelData.isDir) - root.dialog.cwd.push(item.modelData.name); - else if (root.dialog.selectionValid) - root.dialog.accepted(item.modelData.path); - } - - function onClicked(): void { - view.currentIndex = item.index; - } - } - - CachingIconImage { - id: icon - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: Appearance.padding.normal - - implicitSize: Sizes.itemWidth - Appearance.padding.normal * 2 - - Component.onCompleted: { - const file = item.modelData; - if (file.isImage) - source = Qt.resolvedUrl(file.path); - else if (!file.isDir) - source = Quickshell.iconPath(file.mimeType.replace("/", "-"), "application-x-zerosize"); - else if (root.dialog.cwd.length === 1 && ["Desktop", "Documents", "Downloads", "Music", "Pictures", "Public", "Templates", "Videos"].includes(file.name)) - source = Quickshell.iconPath(`folder-${file.name.toLowerCase()}`); - else - source = Quickshell.iconPath("inode-directory"); - } - } - - StyledText { - id: name - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: icon.bottom - anchors.topMargin: Appearance.spacing.small - anchors.margins: Appearance.padding.normal - - horizontalAlignment: Text.AlignHCenter - elide: item.GridView.isCurrentItem ? Text.ElideNone : Text.ElideRight - wrapMode: item.GridView.isCurrentItem ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap - - Component.onCompleted: text = item.modelData.name - } - - Behavior on implicitHeight { - Anim {} - } - } - - add: Transition { - Anim { - properties: "opacity,scale" - from: 0 - to: 1 - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - - remove: Transition { - Anim { - property: "opacity" - to: 0 - } - Anim { - property: "scale" - to: 0.5 - } - } - - displaced: Transition { - Anim { - properties: "opacity,scale" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - Anim { - properties: "x,y" - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - } - - CurrentItem { - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: Appearance.padding.small - - currentItem: view.currentItem - } -} diff --git a/components/filedialog/HeaderBar.qml b/components/filedialog/HeaderBar.qml deleted file mode 100644 index b6e5dba..0000000 --- a/components/filedialog/HeaderBar.qml +++ /dev/null @@ -1,142 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -StyledRect { - id: root - - required property var dialog - - implicitWidth: inner.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: inner.implicitHeight + Appearance.padding.normal * 2 - - color: Colours.tPalette.m3surfaceContainer - - RowLayout { - id: inner - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.small - - Item { - implicitWidth: implicitHeight - implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 - - StateLayer { - radius: Appearance.rounding.small - disabled: root.dialog.cwd.length === 1 - - function onClicked(): void { - root.dialog.cwd.pop(); - } - } - - MaterialIcon { - id: upIcon - - anchors.centerIn: parent - text: "drive_folder_upload" - color: root.dialog.cwd.length === 1 ? Colours.palette.m3outline : Colours.palette.m3onSurface - grade: 200 - } - } - - StyledRect { - Layout.fillWidth: true - - radius: Appearance.rounding.small - color: Colours.tPalette.m3surfaceContainerHigh - - implicitHeight: pathComponents.implicitHeight + pathComponents.anchors.margins * 2 - - RowLayout { - id: pathComponents - - anchors.fill: parent - anchors.margins: Appearance.padding.small / 2 - anchors.leftMargin: 0 - - spacing: Appearance.spacing.small - - Repeater { - model: root.dialog.cwd - - RowLayout { - id: folder - - required property string modelData - required property int index - - spacing: 0 - - Loader { - Layout.rightMargin: Appearance.spacing.small - active: folder.index > 0 - asynchronous: true - sourceComponent: StyledText { - text: "/" - color: Colours.palette.m3onSurfaceVariant - font.bold: true - } - } - - Item { - implicitWidth: homeIcon.implicitWidth + (homeIcon.active ? Appearance.padding.small : 0) + folderName.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: folderName.implicitHeight + Appearance.padding.small * 2 - - Loader { - anchors.fill: parent - active: folder.index < root.dialog.cwd.length - 1 - asynchronous: true - sourceComponent: StateLayer { - radius: Appearance.rounding.small - - function onClicked(): void { - root.dialog.cwd = root.dialog.cwd.slice(0, folder.index + 1); - } - } - } - - Loader { - id: homeIcon - - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: Appearance.padding.normal - - active: folder.index === 0 && folder.modelData === "Home" - asynchronous: true - sourceComponent: MaterialIcon { - text: "home" - color: root.dialog.cwd.length === 1 ? Colours.palette.m3onSurface : Colours.palette.m3onSurfaceVariant - fill: 1 - } - } - - StyledText { - id: folderName - - anchors.left: homeIcon.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: homeIcon.active ? Appearance.padding.small : 0 - - text: folder.modelData - color: folder.index < root.dialog.cwd.length - 1 ? Colours.palette.m3onSurfaceVariant : Colours.palette.m3onSurface - font.bold: true - } - } - } - } - - Item { - Layout.fillWidth: true - } - } - } - } -} diff --git a/components/filedialog/Sidebar.qml b/components/filedialog/Sidebar.qml deleted file mode 100644 index b55d7b3..0000000 --- a/components/filedialog/Sidebar.qml +++ /dev/null @@ -1,113 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -StyledRect { - id: root - - required property var dialog - - implicitWidth: Sizes.sidebarWidth - implicitHeight: inner.implicitHeight + Appearance.padding.normal * 2 - - color: Colours.tPalette.m3surfaceContainer - - ColumnLayout { - id: inner - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.small / 2 - - StyledText { - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: Appearance.padding.small / 2 - Layout.bottomMargin: Appearance.spacing.normal - text: qsTr("Files") - color: Colours.palette.m3onSurface - font.pointSize: Appearance.font.size.larger - font.bold: true - } - - Repeater { - model: ["Home", "Downloads", "Desktop", "Documents", "Music", "Pictures", "Videos"] - - StyledRect { - id: place - - required property string modelData - readonly property bool selected: modelData === root.dialog.cwd[root.dialog.cwd.length - 1] - - Layout.fillWidth: true - implicitHeight: placeInner.implicitHeight + Appearance.padding.normal * 2 - - radius: Appearance.rounding.full - color: Qt.alpha(Colours.palette.m3secondaryContainer, selected ? 1 : 0) - - StateLayer { - color: place.selected ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - - function onClicked(): void { - if (place.modelData === "Home") - root.dialog.cwd = ["Home"]; - else - root.dialog.cwd = ["Home", place.modelData]; - } - } - - RowLayout { - id: placeInner - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large - - spacing: Appearance.spacing.normal - - MaterialIcon { - text: { - const p = place.modelData; - if (p === "Home") - return "home"; - if (p === "Downloads") - return "file_download"; - if (p === "Desktop") - return "desktop_windows"; - if (p === "Documents") - return "description"; - if (p === "Music") - return "music_note"; - if (p === "Pictures") - return "image"; - if (p === "Videos") - return "video_library"; - return "folder"; - } - color: place.selected ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - font.pointSize: Appearance.font.size.large - fill: place.selected ? 1 : 0 - - Behavior on fill { - Anim {} - } - } - - StyledText { - Layout.fillWidth: true - text: place.modelData - color: place.selected ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - font.pointSize: Appearance.font.size.normal - elide: Text.ElideRight - } - } - } - } - } -} diff --git a/components/filedialog/Sizes.qml b/components/filedialog/Sizes.qml deleted file mode 100644 index 2ad31f9..0000000 --- a/components/filedialog/Sizes.qml +++ /dev/null @@ -1,8 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { - property int itemWidth: 103 - property int sidebarWidth: 200 -} diff --git a/components/images/CachingIconImage.qml b/components/images/CachingIconImage.qml deleted file mode 100644 index 1acc6a1..0000000 --- a/components/images/CachingIconImage.qml +++ /dev/null @@ -1,42 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.utils -import Quickshell.Widgets -import QtQuick - -Item { - id: root - - readonly property int status: loader.item?.status ?? Image.Null - readonly property real actualSize: Math.min(width, height) - property real implicitSize - property url source - - implicitWidth: implicitSize - implicitHeight: implicitSize - - Loader { - id: loader - - anchors.fill: parent - sourceComponent: root.source ? root.source.toString().startsWith("image://icon/") ? iconImage : cachingImage : null - } - - Component { - id: cachingImage - - CachingImage { - path: Paths.toLocalFile(root.source) - fillMode: Image.PreserveAspectFit - } - } - - Component { - id: iconImage - - IconImage { - source: root.source - asynchronous: true - } - } -} diff --git a/components/images/CachingImage.qml b/components/images/CachingImage.qml deleted file mode 100644 index e8f957a..0000000 --- a/components/images/CachingImage.qml +++ /dev/null @@ -1,28 +0,0 @@ -import qs.utils -import Caelestia.Internal -import Quickshell -import QtQuick - -Image { - id: root - - property alias path: manager.path - - asynchronous: true - fillMode: Image.PreserveAspectCrop - - Connections { - target: QsWindow.window - - function onDevicePixelRatioChanged(): void { - manager.updateSource(); - } - } - - CachingImageManager { - id: manager - - item: root - cacheDir: Qt.resolvedUrl(Paths.imagecache) - } -} diff --git a/components/misc/CustomShortcut.qml b/components/misc/CustomShortcut.qml deleted file mode 100644 index aa35ed8..0000000 --- a/components/misc/CustomShortcut.qml +++ /dev/null @@ -1,5 +0,0 @@ -import Quickshell.Hyprland - -GlobalShortcut { - appid: "caelestia" -} diff --git a/components/misc/Ref.qml b/components/misc/Ref.qml deleted file mode 100644 index 0a694a4..0000000 --- a/components/misc/Ref.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick - -QtObject { - required property var service - - Component.onCompleted: service.refCount++ - Component.onDestruction: service.refCount-- -} diff --git a/components/widgets/ExtraIndicator.qml b/components/widgets/ExtraIndicator.qml deleted file mode 100644 index db73ea0..0000000 --- a/components/widgets/ExtraIndicator.qml +++ /dev/null @@ -1,51 +0,0 @@ -import ".." -import "../effects" -import qs.services -import qs.config -import QtQuick - -StyledRect { - required property int extra - - anchors.right: parent.right - anchors.margins: Appearance.padding.normal - - color: Colours.palette.m3tertiary - radius: Appearance.rounding.small - - implicitWidth: count.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: count.implicitHeight + Appearance.padding.small * 2 - - opacity: extra > 0 ? 1 : 0 - scale: extra > 0 ? 1 : 0.5 - - Elevation { - anchors.fill: parent - radius: parent.radius - opacity: parent.opacity - z: -1 - level: 2 - } - - StyledText { - id: count - - anchors.centerIn: parent - animate: parent.opacity > 0 - text: qsTr("+%1").arg(parent.extra) - color: Colours.palette.m3onTertiary - } - - Behavior on opacity { - Anim { - duration: Appearance.anim.durations.expressiveFastSpatial - } - } - - Behavior on scale { - Anim { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - } -} diff --git a/config/Appearance.qml b/config/Appearance.qml deleted file mode 100644 index 241c21a..0000000 --- a/config/Appearance.qml +++ /dev/null @@ -1,14 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { - // Literally just here to shorten accessing stuff :woe: - // Also kinda so I can keep accessing it with `Appearance.xxx` instead of `Config.appearance.xxx` - readonly property AppearanceConfig.Rounding rounding: Config.appearance.rounding - readonly property AppearanceConfig.Spacing spacing: Config.appearance.spacing - readonly property AppearanceConfig.Padding padding: Config.appearance.padding - readonly property AppearanceConfig.FontStuff font: Config.appearance.font - readonly property AppearanceConfig.Anim anim: Config.appearance.anim - readonly property AppearanceConfig.Transparency transparency: Config.appearance.transparency -} diff --git a/config/AppearanceConfig.qml b/config/AppearanceConfig.qml deleted file mode 100644 index b25945b..0000000 --- a/config/AppearanceConfig.qml +++ /dev/null @@ -1,92 +0,0 @@ -import Quickshell.Io - -JsonObject { - property Rounding rounding: Rounding {} - property Spacing spacing: Spacing {} - property Padding padding: Padding {} - property FontStuff font: FontStuff {} - property Anim anim: Anim {} - property Transparency transparency: Transparency {} - - component Rounding: JsonObject { - property real scale: 1 - property int small: 12 * scale - property int normal: 17 * scale - property int large: 25 * scale - property int full: 1000 * scale - } - - component Spacing: JsonObject { - property real scale: 1 - property int small: 7 * scale - property int smaller: 10 * scale - property int normal: 12 * scale - property int larger: 15 * scale - property int large: 20 * scale - } - - component Padding: JsonObject { - property real scale: 1 - property int small: 5 * scale - property int smaller: 7 * scale - property int normal: 10 * scale - property int larger: 12 * scale - property int large: 15 * scale - } - - component FontFamily: JsonObject { - property string sans: "Rubik" - property string mono: "CaskaydiaCove NF" - property string material: "Material Symbols Rounded" - property string clock: "Rubik" - } - - component FontSize: JsonObject { - property real scale: 1 - property int small: 11 * scale - property int smaller: 12 * scale - property int normal: 13 * scale - property int larger: 15 * scale - property int large: 18 * scale - property int extraLarge: 28 * scale - } - - component FontStuff: JsonObject { - property FontFamily family: FontFamily {} - property FontSize size: FontSize {} - } - - component AnimCurves: JsonObject { - property list emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1] - property list emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1] - property list emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1] - property list standard: [0.2, 0, 0, 1, 1, 1] - property list standardAccel: [0.3, 0, 1, 1, 1, 1] - property list standardDecel: [0, 0, 0, 1, 1, 1] - property list expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1] - property list expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1] - property list expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1] - } - - component AnimDurations: JsonObject { - property real scale: 1 - property int small: 200 * scale - property int normal: 400 * scale - property int large: 600 * scale - property int extraLarge: 1000 * scale - property int expressiveFastSpatial: 350 * scale - property int expressiveDefaultSpatial: 500 * scale - property int expressiveEffects: 200 * scale - } - - component Anim: JsonObject { - property AnimCurves curves: AnimCurves {} - property AnimDurations durations: AnimDurations {} - } - - component Transparency: JsonObject { - property bool enabled: false - property real base: 0.85 - property real layers: 0.4 - } -} diff --git a/config/BackgroundConfig.qml b/config/BackgroundConfig.qml deleted file mode 100644 index af46053..0000000 --- a/config/BackgroundConfig.qml +++ /dev/null @@ -1,18 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool enabled: true - property DesktopClock desktopClock: DesktopClock {} - property Visualiser visualiser: Visualiser {} - - component DesktopClock: JsonObject { - property bool enabled: false - } - - component Visualiser: JsonObject { - property bool enabled: false - property bool autoHide: true - property real rounding: 1 - property real spacing: 1 - } -} diff --git a/config/BarConfig.qml b/config/BarConfig.qml deleted file mode 100644 index 86c2a40..0000000 --- a/config/BarConfig.qml +++ /dev/null @@ -1,102 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool persistent: true - property bool showOnHover: true - property int dragThreshold: 20 - property ScrollActions scrollActions: ScrollActions {} - property Workspaces workspaces: Workspaces {} - property Tray tray: Tray {} - property Status status: Status {} - property Clock clock: Clock {} - property Sizes sizes: Sizes {} - - property list entries: [ - { - id: "logo", - enabled: true - }, - { - id: "workspaces", - enabled: true - }, - { - id: "spacer", - enabled: true - }, - { - id: "activeWindow", - enabled: true - }, - { - id: "spacer", - enabled: true - }, - { - id: "tray", - enabled: true - }, - { - id: "clock", - enabled: true - }, - { - id: "statusIcons", - enabled: true - }, - { - id: "power", - enabled: true - } - ] - - component ScrollActions: JsonObject { - property bool workspaces: true - property bool volume: true - property bool brightness: true - } - - component Workspaces: JsonObject { - property int shown: 5 - property bool activeIndicator: true - property bool occupiedBg: false - property bool showWindows: true - property bool showWindowsOnSpecialWorkspaces: showWindows - property bool activeTrail: false - property bool perMonitorWorkspaces: true - property string label: " " // if empty, will show workspace name's first letter - property string occupiedLabel: "󰮯" - property string activeLabel: "󰮯" - property string capitalisation: "preserve" // upper, lower, or preserve - relevant only if label is empty - property list specialWorkspaceIcons: [] - } - - component Tray: JsonObject { - property bool background: false - property bool recolour: false - property bool compact: false - property list iconSubs: [] - } - - component Status: JsonObject { - property bool showAudio: false - property bool showMicrophone: false - property bool showKbLayout: false - property bool showNetwork: true - property bool showBluetooth: true - property bool showBattery: true - property bool showLockStatus: true - } - - component Clock: JsonObject { - property bool showIcon: true - } - - component Sizes: JsonObject { - property int innerWidth: 40 - property int windowPreviewSize: 400 - property int trayMenuWidth: 300 - property int batteryWidth: 250 - property int networkWidth: 320 - } -} diff --git a/config/BorderConfig.qml b/config/BorderConfig.qml deleted file mode 100644 index b15811f..0000000 --- a/config/BorderConfig.qml +++ /dev/null @@ -1,6 +0,0 @@ -import Quickshell.Io - -JsonObject { - property int thickness: Appearance.padding.normal - property int rounding: Appearance.rounding.large -} diff --git a/config/Config.qml b/config/Config.qml deleted file mode 100644 index 818b04a..0000000 --- a/config/Config.qml +++ /dev/null @@ -1,76 +0,0 @@ -pragma Singleton - -import qs.utils -import Quickshell -import Quickshell.Io - -Singleton { - id: root - - property alias appearance: adapter.appearance - property alias general: adapter.general - property alias background: adapter.background - property alias bar: adapter.bar - property alias border: adapter.border - property alias dashboard: adapter.dashboard - property alias controlCenter: adapter.controlCenter - property alias launcher: adapter.launcher - property alias notifs: adapter.notifs - property alias osd: adapter.osd - property alias session: adapter.session - property alias winfo: adapter.winfo - property alias lock: adapter.lock - property alias utilities: adapter.utilities - property alias sidebar: adapter.sidebar - property alias services: adapter.services - property alias paths: adapter.paths - - ElapsedTimer { - id: timer - } - - FileView { - path: `${Paths.config}/shell.json` - watchChanges: true - onFileChanged: { - timer.restart(); - reload(); - } - onLoaded: { - try { - JSON.parse(text()); - if (adapter.utilities.toasts.configLoaded) - Toaster.toast(qsTr("Config loaded"), qsTr("Config loaded in %1ms").arg(timer.elapsedMs()), "rule_settings"); - } catch (e) { - Toaster.toast(qsTr("Failed to load config"), e.message, "settings_alert", Toast.Error); - } - } - onLoadFailed: err => { - if (err !== FileViewError.FileNotFound) - Toaster.toast(qsTr("Failed to read config file"), FileViewError.toString(err), "settings_alert", Toast.Warning); - } - onSaveFailed: err => Toaster.toast(qsTr("Failed to save config"), FileViewError.toString(err), "settings_alert", Toast.Error) - - JsonAdapter { - id: adapter - - property AppearanceConfig appearance: AppearanceConfig {} - property GeneralConfig general: GeneralConfig {} - property BackgroundConfig background: BackgroundConfig {} - property BarConfig bar: BarConfig {} - property BorderConfig border: BorderConfig {} - property DashboardConfig dashboard: DashboardConfig {} - property ControlCenterConfig controlCenter: ControlCenterConfig {} - property LauncherConfig launcher: LauncherConfig {} - property NotifsConfig notifs: NotifsConfig {} - property OsdConfig osd: OsdConfig {} - property SessionConfig session: SessionConfig {} - property WInfoConfig winfo: WInfoConfig {} - property LockConfig lock: LockConfig {} - property UtilitiesConfig utilities: UtilitiesConfig {} - property SidebarConfig sidebar: SidebarConfig {} - property ServiceConfig services: ServiceConfig {} - property UserPaths paths: UserPaths {} - } - } -} diff --git a/config/ControlCenterConfig.qml b/config/ControlCenterConfig.qml deleted file mode 100644 index a588949..0000000 --- a/config/ControlCenterConfig.qml +++ /dev/null @@ -1,10 +0,0 @@ -import Quickshell.Io - -JsonObject { - property Sizes sizes: Sizes {} - - component Sizes: JsonObject { - property real heightMult: 0.7 - property real ratio: 16 / 9 - } -} diff --git a/config/DashboardConfig.qml b/config/DashboardConfig.qml deleted file mode 100644 index 030292b..0000000 --- a/config/DashboardConfig.qml +++ /dev/null @@ -1,25 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool enabled: true - property bool showOnHover: true - property int mediaUpdateInterval: 500 - property int dragThreshold: 50 - property Sizes sizes: Sizes {} - - component Sizes: JsonObject { - readonly property int tabIndicatorHeight: 3 - readonly property int tabIndicatorSpacing: 5 - readonly property int infoWidth: 200 - readonly property int infoIconSize: 25 - readonly property int dateTimeWidth: 110 - readonly property int mediaWidth: 200 - readonly property int mediaProgressSweep: 180 - readonly property int mediaProgressThickness: 8 - readonly property int resourceProgessThickness: 10 - readonly property int weatherWidth: 250 - readonly property int mediaCoverArtSize: 150 - readonly property int mediaVisualiserSize: 80 - readonly property int resourceSize: 200 - } -} diff --git a/config/GeneralConfig.qml b/config/GeneralConfig.qml deleted file mode 100644 index eecca01..0000000 --- a/config/GeneralConfig.qml +++ /dev/null @@ -1,59 +0,0 @@ -import Quickshell.Io - -JsonObject { - property Apps apps: Apps {} - property Idle idle: Idle {} - property Battery battery: Battery {} - - component Apps: JsonObject { - property list terminal: ["foot"] - property list audio: ["pavucontrol"] - property list playback: ["mpv"] - property list explorer: ["thunar"] - } - - component Idle: JsonObject { - property bool lockBeforeSleep: true - property bool inhibitWhenAudio: true - property list timeouts: [ - { - timeout: 180, - idleAction: "lock" - }, - { - timeout: 300, - idleAction: "dpms off", - returnAction: "dpms on" - }, - { - timeout: 600, - idleAction: ["systemctl", "suspend-then-hibernate"] - } - ] - } - - component Battery: JsonObject { - property list warnLevels: [ - { - level: 20, - title: qsTr("Low battery"), - message: qsTr("You might want to plug in a charger"), - icon: "battery_android_frame_2" - }, - { - level: 10, - title: qsTr("Did you see the previous message?"), - message: qsTr("You should probably plug in a charger now"), - icon: "battery_android_frame_1" - }, - { - level: 5, - title: qsTr("Critical battery level"), - message: qsTr("PLUG THE CHARGER RIGHT NOW!!"), - icon: "battery_android_alert", - critical: true - }, - ] - property int criticalLevel: 3 - } -} diff --git a/config/LauncherConfig.qml b/config/LauncherConfig.qml deleted file mode 100644 index 9d9c50c..0000000 --- a/config/LauncherConfig.qml +++ /dev/null @@ -1,138 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool enabled: true - property bool showOnHover: false - property int maxShown: 7 - property int maxWallpapers: 9 // Warning: even numbers look bad - property string specialPrefix: "@" - property string actionPrefix: ">" - property bool enableDangerousActions: false // Allow actions that can cause losing data, like shutdown, reboot and logout - property int dragThreshold: 50 - property bool vimKeybinds: false - property list hiddenApps: [] - property UseFuzzy useFuzzy: UseFuzzy {} - property Sizes sizes: Sizes {} - - component UseFuzzy: JsonObject { - property bool apps: false - property bool actions: false - property bool schemes: false - property bool variants: false - property bool wallpapers: false - } - - component Sizes: JsonObject { - property int itemWidth: 600 - property int itemHeight: 57 - property int wallpaperWidth: 280 - property int wallpaperHeight: 200 - } - - property list actions: [ - { - name: "Calculator", - icon: "calculate", - description: "Do simple math equations (powered by Qalc)", - command: ["autocomplete", "calc"], - enabled: true, - dangerous: false - }, - { - name: "Scheme", - icon: "palette", - description: "Change the current colour scheme", - command: ["autocomplete", "scheme"], - enabled: true, - dangerous: false - }, - { - name: "Wallpaper", - icon: "image", - description: "Change the current wallpaper", - command: ["autocomplete", "wallpaper"], - enabled: true, - dangerous: false - }, - { - name: "Variant", - icon: "colors", - description: "Change the current scheme variant", - command: ["autocomplete", "variant"], - enabled: true, - dangerous: false - }, - { - name: "Transparency", - icon: "opacity", - description: "Change shell transparency", - command: ["autocomplete", "transparency"], - enabled: false, - dangerous: false - }, - { - name: "Random", - icon: "casino", - description: "Switch to a random wallpaper", - command: ["caelestia", "wallpaper", "-r"], - enabled: true, - dangerous: false - }, - { - name: "Light", - icon: "light_mode", - description: "Change the scheme to light mode", - command: ["setMode", "light"], - enabled: true, - dangerous: false - }, - { - name: "Dark", - icon: "dark_mode", - description: "Change the scheme to dark mode", - command: ["setMode", "dark"], - enabled: true, - dangerous: false - }, - { - name: "Shutdown", - icon: "power_settings_new", - description: "Shutdown the system", - command: ["systemctl", "poweroff"], - enabled: true, - dangerous: true - }, - { - name: "Reboot", - icon: "cached", - description: "Reboot the system", - command: ["systemctl", "reboot"], - enabled: true, - dangerous: true - }, - { - name: "Logout", - icon: "exit_to_app", - description: "Log out of the current session", - command: ["loginctl", "terminate-user", ""], - enabled: true, - dangerous: true - }, - { - name: "Lock", - icon: "lock", - description: "Lock the current session", - command: ["loginctl", "lock-session"], - enabled: true, - dangerous: false - }, - { - name: "Sleep", - icon: "bedtime", - description: "Suspend then hibernate", - command: ["systemctl", "suspend-then-hibernate"], - enabled: true, - dangerous: false - } - ] -} diff --git a/config/LockConfig.qml b/config/LockConfig.qml deleted file mode 100644 index 2af4e2c..0000000 --- a/config/LockConfig.qml +++ /dev/null @@ -1,14 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool recolourLogo: false - property bool enableFprint: true - property int maxFprintTries: 3 - property Sizes sizes: Sizes {} - - component Sizes: JsonObject { - property real heightMult: 0.7 - property real ratio: 16 / 9 - property int centerWidth: 600 - } -} diff --git a/config/NotifsConfig.qml b/config/NotifsConfig.qml deleted file mode 100644 index 25d8680..0000000 --- a/config/NotifsConfig.qml +++ /dev/null @@ -1,17 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool expire: true - property int defaultExpireTimeout: 5000 - property real clearThreshold: 0.3 - property int expandThreshold: 20 - property bool actionOnClick: false - property int groupPreviewNum: 3 - property Sizes sizes: Sizes {} - - component Sizes: JsonObject { - property int width: 400 - property int image: 41 - property int badge: 20 - } -} diff --git a/config/OsdConfig.qml b/config/OsdConfig.qml deleted file mode 100644 index 543fc41..0000000 --- a/config/OsdConfig.qml +++ /dev/null @@ -1,14 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool enabled: true - property int hideDelay: 2000 - property bool enableBrightness: true - property bool enableMicrophone: false - property Sizes sizes: Sizes {} - - component Sizes: JsonObject { - property int sliderWidth: 30 - property int sliderHeight: 150 - } -} diff --git a/config/ServiceConfig.qml b/config/ServiceConfig.qml deleted file mode 100644 index 36a51aa..0000000 --- a/config/ServiceConfig.qml +++ /dev/null @@ -1,20 +0,0 @@ -import Quickshell.Io -import QtQuick - -JsonObject { - property string weatherLocation: "" // A lat,long pair or empty for autodetection, e.g. "37.8267,-122.4233" - property bool useFahrenheit: [Locale.ImperialUSSystem, Locale.ImperialSystem].includes(Qt.locale().measurementSystem) - property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a") - property string gpuType: "" - property int visualiserBars: 45 - property real audioIncrement: 0.1 - property real maxVolume: 1.0 - property bool smartScheme: true - property string defaultPlayer: "Spotify" - property list playerAliases: [ - { - "from": "com.github.th_ch.youtube_music", - "to": "YT Music" - } - ] -} diff --git a/config/SessionConfig.qml b/config/SessionConfig.qml deleted file mode 100644 index f65ec6d..0000000 --- a/config/SessionConfig.qml +++ /dev/null @@ -1,21 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool enabled: true - property int dragThreshold: 30 - property bool vimKeybinds: false - property Commands commands: Commands {} - - property Sizes sizes: Sizes {} - - component Commands: JsonObject { - property list logout: ["loginctl", "terminate-user", ""] - property list shutdown: ["systemctl", "poweroff"] - property list hibernate: ["systemctl", "hibernate"] - property list reboot: ["systemctl", "reboot"] - } - - component Sizes: JsonObject { - property int button: 80 - } -} diff --git a/config/SidebarConfig.qml b/config/SidebarConfig.qml deleted file mode 100644 index a871562..0000000 --- a/config/SidebarConfig.qml +++ /dev/null @@ -1,11 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool enabled: true - property int dragThreshold: 80 - property Sizes sizes: Sizes {} - - component Sizes: JsonObject { - property int width: 430 - } -} diff --git a/config/UserPaths.qml b/config/UserPaths.qml deleted file mode 100644 index f8de267..0000000 --- a/config/UserPaths.qml +++ /dev/null @@ -1,8 +0,0 @@ -import qs.utils -import Quickshell.Io - -JsonObject { - property string wallpaperDir: `${Paths.pictures}/Wallpapers` - property string sessionGif: "root:/assets/kurukuru.gif" - property string mediaGif: "root:/assets/bongocat.gif" -} diff --git a/config/UtilitiesConfig.qml b/config/UtilitiesConfig.qml deleted file mode 100644 index 5779d88..0000000 --- a/config/UtilitiesConfig.qml +++ /dev/null @@ -1,34 +0,0 @@ -import Quickshell.Io - -JsonObject { - property bool enabled: true - property int maxToasts: 4 - - property Sizes sizes: Sizes {} - property Toasts toasts: Toasts {} - property Vpn vpn: Vpn {} - - component Sizes: JsonObject { - property int width: 430 - property int toastWidth: 430 - } - - component Toasts: JsonObject { - property bool configLoaded: true - property bool chargingChanged: true - property bool gameModeChanged: true - property bool dndChanged: true - property bool audioOutputChanged: true - property bool audioInputChanged: true - property bool capsLockChanged: true - property bool numLockChanged: true - property bool kbLayoutChanged: true - property bool vpnChanged: true - property bool nowPlaying: false - } - - component Vpn: JsonObject { - property bool enabled: false - property list provider: ["netbird"] - } -} diff --git a/config/WInfoConfig.qml b/config/WInfoConfig.qml deleted file mode 100644 index 5025780..0000000 --- a/config/WInfoConfig.qml +++ /dev/null @@ -1,10 +0,0 @@ -import Quickshell.Io - -JsonObject { - property Sizes sizes: Sizes {} - - component Sizes: JsonObject { - property real heightMult: 0.7 - property real detailsWidth: 500 - } -} diff --git a/services/Audio.qml b/services/Audio.qml deleted file mode 100644 index f71e2e4..0000000 --- a/services/Audio.qml +++ /dev/null @@ -1,122 +0,0 @@ -pragma Singleton - -import qs.config -import Quickshell -import Quickshell.Services.Pipewire -import QtQuick - -Singleton { - id: root - - property string previousSinkName: "" - property string previousSourceName: "" - - readonly property var nodes: Pipewire.nodes.values.reduce((acc, node) => { - if (!node.isStream) { - if (node.isSink) - acc.sinks.push(node); - else if (node.audio) - acc.sources.push(node); - } - return acc; - }, { - sources: [], - sinks: [] - }) - - readonly property list sinks: nodes.sinks - readonly property list sources: nodes.sources - - readonly property PwNode sink: Pipewire.defaultAudioSink - readonly property PwNode source: Pipewire.defaultAudioSource - - readonly property bool muted: !!sink?.audio?.muted - readonly property real volume: sink?.audio?.volume ?? 0 - - readonly property bool sourceMuted: !!source?.audio?.muted - readonly property real sourceVolume: source?.audio?.volume ?? 0 - - readonly property alias cava: cava - readonly property alias beatTracker: beatTracker - - function setVolume(newVolume: real): void { - if (sink?.ready && sink?.audio) { - sink.audio.muted = false; - sink.audio.volume = Math.max(0, Math.min(Config.services.maxVolume, newVolume)); - } - } - - function incrementVolume(amount: real): void { - setVolume(volume + (amount || Config.services.audioIncrement)); - } - - function decrementVolume(amount: real): void { - setVolume(volume - (amount || Config.services.audioIncrement)); - } - - function setSourceVolume(newVolume: real): void { - if (source?.ready && source?.audio) { - source.audio.muted = false; - source.audio.volume = Math.max(0, Math.min(Config.services.maxVolume, newVolume)); - } - } - - function incrementSourceVolume(amount: real): void { - setSourceVolume(sourceVolume + (amount || Config.services.audioIncrement)); - } - - function decrementSourceVolume(amount: real): void { - setSourceVolume(sourceVolume - (amount || Config.services.audioIncrement)); - } - - function setAudioSink(newSink: PwNode): void { - Pipewire.preferredDefaultAudioSink = newSink; - } - - function setAudioSource(newSource: PwNode): void { - Pipewire.preferredDefaultAudioSource = newSource; - } - - onSinkChanged: { - if (!sink?.ready) - return; - - const newSinkName = sink.description || sink.name || qsTr("Unknown Device"); - - if (previousSinkName && previousSinkName !== newSinkName && Config.utilities.toasts.audioOutputChanged) - Toaster.toast(qsTr("Audio output changed"), qsTr("Now using: %1").arg(newSinkName), "volume_up"); - - previousSinkName = newSinkName; - } - - onSourceChanged: { - if (!source?.ready) - return; - - const newSourceName = source.description || source.name || qsTr("Unknown Device"); - - if (previousSourceName && previousSourceName !== newSourceName && Config.utilities.toasts.audioInputChanged) - Toaster.toast(qsTr("Audio input changed"), qsTr("Now using: %1").arg(newSourceName), "mic"); - - previousSourceName = newSourceName; - } - - Component.onCompleted: { - previousSinkName = sink?.description || sink?.name || qsTr("Unknown Device"); - previousSourceName = source?.description || source?.name || qsTr("Unknown Device"); - } - - PwObjectTracker { - objects: [...root.sinks, ...root.sources] - } - - CavaProvider { - id: cava - - bars: Config.services.visualiserBars - } - - BeatTracker { - id: beatTracker - } -} diff --git a/services/Brightness.qml b/services/Brightness.qml deleted file mode 100644 index ac905fd..0000000 --- a/services/Brightness.qml +++ /dev/null @@ -1,225 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import qs.components.misc -import Quickshell -import Quickshell.Io -import QtQuick - -Singleton { - id: root - - property list ddcMonitors: [] - readonly property list monitors: variants.instances - property bool appleDisplayPresent: false - - function getMonitorForScreen(screen: ShellScreen): var { - return monitors.find(m => m.modelData === screen); - } - - function getMonitor(query: string): var { - if (query === "active") { - return monitors.find(m => Hypr.monitorFor(m.modelData)?.focused); - } - - if (query.startsWith("model:")) { - const model = query.slice(6); - return monitors.find(m => m.modelData.model === model); - } - - if (query.startsWith("serial:")) { - const serial = query.slice(7); - return monitors.find(m => m.modelData.serialNumber === serial); - } - - if (query.startsWith("id:")) { - const id = parseInt(query.slice(3), 10); - return monitors.find(m => Hypr.monitorFor(m.modelData)?.id === id); - } - - return monitors.find(m => m.modelData.name === query); - } - - function increaseBrightness(): void { - const monitor = getMonitor("active"); - if (monitor) - monitor.setBrightness(monitor.brightness + 0.1); - } - - function decreaseBrightness(): void { - const monitor = getMonitor("active"); - if (monitor) - monitor.setBrightness(monitor.brightness - 0.1); - } - - onMonitorsChanged: { - ddcMonitors = []; - ddcProc.running = true; - } - - Variants { - id: variants - - model: Quickshell.screens - - Monitor {} - } - - Process { - running: true - command: ["sh", "-c", "asdbctl get"] // To avoid warnings if asdbctl is not installed - stdout: StdioCollector { - onStreamFinished: root.appleDisplayPresent = text.trim().length > 0 - } - } - - Process { - id: ddcProc - - command: ["ddcutil", "detect", "--brief"] - stdout: StdioCollector { - onStreamFinished: root.ddcMonitors = text.trim().split("\n\n").filter(d => d.startsWith("Display ")).map(d => ({ - busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)[1], - connector: d.match(/DRM connector:\s+(.*)/)[1].replace(/^card\d+-/, "") // strip "card1-" - })) - } - } - - CustomShortcut { - name: "brightnessUp" - description: "Increase brightness" - onPressed: root.increaseBrightness() - } - - CustomShortcut { - name: "brightnessDown" - description: "Decrease brightness" - onPressed: root.decreaseBrightness() - } - - IpcHandler { - target: "brightness" - - function get(): real { - return getFor("active"); - } - - // Allows searching by active/model/serial/id/name - function getFor(query: string): real { - return root.getMonitor(query)?.brightness ?? -1; - } - - function set(value: string): string { - return setFor("active", value); - } - - // Handles brightness value like brightnessctl: 0.1, +0.1, 0.1-, 10%, +10%, 10%- - function setFor(query: string, value: string): string { - const monitor = root.getMonitor(query); - if (!monitor) - return "Invalid monitor: " + query; - - let targetBrightness; - if (value.endsWith("%-")) { - const percent = parseFloat(value.slice(0, -2)); - targetBrightness = monitor.brightness - (percent / 100); - } else if (value.startsWith("+") && value.endsWith("%")) { - const percent = parseFloat(value.slice(1, -1)); - targetBrightness = monitor.brightness + (percent / 100); - } else if (value.endsWith("%")) { - const percent = parseFloat(value.slice(0, -1)); - targetBrightness = percent / 100; - } else if (value.startsWith("+")) { - const increment = parseFloat(value.slice(1)); - targetBrightness = monitor.brightness + increment; - } else if (value.endsWith("-")) { - const decrement = parseFloat(value.slice(0, -1)); - targetBrightness = monitor.brightness - decrement; - } else if (value.includes("%") || value.includes("-") || value.includes("+")) { - return `Invalid brightness format: ${value}\nExpected: 0.1, +0.1, 0.1-, 10%, +10%, 10%-`; - } else { - targetBrightness = parseFloat(value); - } - - if (isNaN(targetBrightness)) - return `Failed to parse value: ${value}\nExpected: 0.1, +0.1, 0.1-, 10%, +10%, 10%-`; - - monitor.setBrightness(targetBrightness); - - return `Set monitor ${monitor.modelData.name} brightness to ${+monitor.brightness.toFixed(2)}`; - } - } - - component Monitor: QtObject { - id: monitor - - required property ShellScreen modelData - readonly property bool isDdc: root.ddcMonitors.some(m => m.connector === modelData.name) - readonly property string busNum: root.ddcMonitors.find(m => m.connector === modelData.name)?.busNum ?? "" - readonly property bool isAppleDisplay: root.appleDisplayPresent && modelData.model.startsWith("StudioDisplay") - property real brightness - property real queuedBrightness: NaN - - readonly property Process initProc: Process { - stdout: StdioCollector { - onStreamFinished: { - if (monitor.isAppleDisplay) { - const val = parseInt(text.trim()); - monitor.brightness = val / 101; - } else { - const [, , , cur, max] = text.split(" "); - monitor.brightness = parseInt(cur) / parseInt(max); - } - } - } - } - - readonly property Timer timer: Timer { - interval: 500 - onTriggered: { - if (!isNaN(monitor.queuedBrightness)) { - monitor.setBrightness(monitor.queuedBrightness); - monitor.queuedBrightness = NaN; - } - } - } - - function setBrightness(value: real): void { - value = Math.max(0, Math.min(1, value)); - const rounded = Math.round(value * 100); - if (Math.round(brightness * 100) === rounded) - return; - - if (isDdc && timer.running) { - queuedBrightness = value; - return; - } - - brightness = value; - - if (isAppleDisplay) - Quickshell.execDetached(["asdbctl", "set", rounded]); - else if (isDdc) - Quickshell.execDetached(["ddcutil", "-b", busNum, "setvcp", "10", rounded]); - else - Quickshell.execDetached(["brightnessctl", "s", `${rounded}%`]); - - if (isDdc) - timer.restart(); - } - - function initBrightness(): void { - if (isAppleDisplay) - initProc.command = ["asdbctl", "get"]; - else if (isDdc) - initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"]; - else - initProc.command = ["sh", "-c", "echo a b c $(brightnessctl g) $(brightnessctl m)"]; - - initProc.running = true; - } - - onBusNumChanged: initBrightness() - Component.onCompleted: initBrightness() - } -} diff --git a/services/Colours.qml b/services/Colours.qml deleted file mode 100644 index cd86c8f..0000000 --- a/services/Colours.qml +++ /dev/null @@ -1,237 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import qs.config -import qs.utils -import Caelestia -import Quickshell -import Quickshell.Io -import QtQuick - -Singleton { - id: root - - property bool showPreview - property string scheme - property string flavour - readonly property bool light: showPreview ? previewLight : currentLight - property bool currentLight - property bool previewLight - readonly property M3Palette palette: showPreview ? preview : current - readonly property M3TPalette tPalette: M3TPalette {} - readonly property M3Palette current: M3Palette {} - readonly property M3Palette preview: M3Palette {} - readonly property Transparency transparency: Transparency {} - readonly property alias wallLuminance: analyser.luminance - - function getLuminance(c: color): real { - if (c.r == 0 && c.g == 0 && c.b == 0) - return 0; - return Math.sqrt(0.299 * (c.r ** 2) + 0.587 * (c.g ** 2) + 0.114 * (c.b ** 2)); - } - - function alterColour(c: color, a: real, layer: int): color { - const luminance = getLuminance(c); - - const offset = (!light || layer == 1 ? 1 : -layer / 2) * (light ? 0.2 : 0.3) * (1 - transparency.base) * (1 + wallLuminance * (light ? (layer == 1 ? 3 : 1) : 2.5)); - const scale = (luminance + offset) / luminance; - const r = Math.max(0, Math.min(1, c.r * scale)); - const g = Math.max(0, Math.min(1, c.g * scale)); - const b = Math.max(0, Math.min(1, c.b * scale)); - - return Qt.rgba(r, g, b, a); - } - - function layer(c: color, layer: var): color { - if (!transparency.enabled) - return c; - - return layer === 0 ? Qt.alpha(c, transparency.base) : alterColour(c, transparency.layers, layer ?? 1); - } - - function on(c: color): color { - if (c.hslLightness < 0.5) - return Qt.hsla(c.hslHue, c.hslSaturation, 0.9, 1); - return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1); - } - - function load(data: string, isPreview: bool): void { - const colours = isPreview ? preview : current; - const scheme = JSON.parse(data); - - if (!isPreview) { - root.scheme = scheme.name; - flavour = scheme.flavour; - currentLight = scheme.mode === "light"; - } else { - previewLight = scheme.mode === "light"; - } - - for (const [name, colour] of Object.entries(scheme.colours)) { - const propName = name.startsWith("term") ? name : `m3${name}`; - if (colours.hasOwnProperty(propName)) - colours[propName] = `#${colour}`; - } - } - - function setMode(mode: string): void { - Quickshell.execDetached(["caelestia", "scheme", "set", "--notify", "-m", mode]); - } - - FileView { - path: `${Paths.state}/scheme.json` - watchChanges: true - onFileChanged: reload() - onLoaded: root.load(text(), false) - } - - ImageAnalyser { - id: analyser - - source: Wallpapers.current - } - - component Transparency: QtObject { - readonly property bool enabled: Appearance.transparency.enabled - readonly property real base: Appearance.transparency.base - (root.light ? 0.1 : 0) - readonly property real layers: Appearance.transparency.layers - } - - component M3TPalette: QtObject { - readonly property color m3primary_paletteKeyColor: root.layer(root.palette.m3primary_paletteKeyColor) - readonly property color m3secondary_paletteKeyColor: root.layer(root.palette.m3secondary_paletteKeyColor) - readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor) - readonly property color m3neutral_paletteKeyColor: root.layer(root.palette.m3neutral_paletteKeyColor) - readonly property color m3neutral_variant_paletteKeyColor: root.layer(root.palette.m3neutral_variant_paletteKeyColor) - readonly property color m3background: root.layer(root.palette.m3background, 0) - readonly property color m3onBackground: root.layer(root.palette.m3onBackground) - readonly property color m3surface: root.layer(root.palette.m3surface, 0) - readonly property color m3surfaceDim: root.layer(root.palette.m3surfaceDim, 0) - readonly property color m3surfaceBright: root.layer(root.palette.m3surfaceBright, 0) - readonly property color m3surfaceContainerLowest: root.layer(root.palette.m3surfaceContainerLowest) - readonly property color m3surfaceContainerLow: root.layer(root.palette.m3surfaceContainerLow) - readonly property color m3surfaceContainer: root.layer(root.palette.m3surfaceContainer) - readonly property color m3surfaceContainerHigh: root.layer(root.palette.m3surfaceContainerHigh) - readonly property color m3surfaceContainerHighest: root.layer(root.palette.m3surfaceContainerHighest) - readonly property color m3onSurface: root.layer(root.palette.m3onSurface) - readonly property color m3surfaceVariant: root.layer(root.palette.m3surfaceVariant, 0) - readonly property color m3onSurfaceVariant: root.layer(root.palette.m3onSurfaceVariant) - readonly property color m3inverseSurface: root.layer(root.palette.m3inverseSurface, 0) - readonly property color m3inverseOnSurface: root.layer(root.palette.m3inverseOnSurface) - readonly property color m3outline: root.layer(root.palette.m3outline) - readonly property color m3outlineVariant: root.layer(root.palette.m3outlineVariant) - readonly property color m3shadow: root.layer(root.palette.m3shadow) - readonly property color m3scrim: root.layer(root.palette.m3scrim) - readonly property color m3surfaceTint: root.layer(root.palette.m3surfaceTint) - readonly property color m3primary: root.layer(root.palette.m3primary) - readonly property color m3onPrimary: root.layer(root.palette.m3onPrimary) - readonly property color m3primaryContainer: root.layer(root.palette.m3primaryContainer) - readonly property color m3onPrimaryContainer: root.layer(root.palette.m3onPrimaryContainer) - readonly property color m3inversePrimary: root.layer(root.palette.m3inversePrimary) - readonly property color m3secondary: root.layer(root.palette.m3secondary) - readonly property color m3onSecondary: root.layer(root.palette.m3onSecondary) - readonly property color m3secondaryContainer: root.layer(root.palette.m3secondaryContainer) - readonly property color m3onSecondaryContainer: root.layer(root.palette.m3onSecondaryContainer) - readonly property color m3tertiary: root.layer(root.palette.m3tertiary) - readonly property color m3onTertiary: root.layer(root.palette.m3onTertiary) - readonly property color m3tertiaryContainer: root.layer(root.palette.m3tertiaryContainer) - readonly property color m3onTertiaryContainer: root.layer(root.palette.m3onTertiaryContainer) - readonly property color m3error: root.layer(root.palette.m3error) - readonly property color m3onError: root.layer(root.palette.m3onError) - readonly property color m3errorContainer: root.layer(root.palette.m3errorContainer) - readonly property color m3onErrorContainer: root.layer(root.palette.m3onErrorContainer) - readonly property color m3success: root.layer(root.palette.m3success) - readonly property color m3onSuccess: root.layer(root.palette.m3onSuccess) - readonly property color m3successContainer: root.layer(root.palette.m3successContainer) - readonly property color m3onSuccessContainer: root.layer(root.palette.m3onSuccessContainer) - readonly property color m3primaryFixed: root.layer(root.palette.m3primaryFixed) - readonly property color m3primaryFixedDim: root.layer(root.palette.m3primaryFixedDim) - readonly property color m3onPrimaryFixed: root.layer(root.palette.m3onPrimaryFixed) - readonly property color m3onPrimaryFixedVariant: root.layer(root.palette.m3onPrimaryFixedVariant) - readonly property color m3secondaryFixed: root.layer(root.palette.m3secondaryFixed) - readonly property color m3secondaryFixedDim: root.layer(root.palette.m3secondaryFixedDim) - readonly property color m3onSecondaryFixed: root.layer(root.palette.m3onSecondaryFixed) - readonly property color m3onSecondaryFixedVariant: root.layer(root.palette.m3onSecondaryFixedVariant) - readonly property color m3tertiaryFixed: root.layer(root.palette.m3tertiaryFixed) - readonly property color m3tertiaryFixedDim: root.layer(root.palette.m3tertiaryFixedDim) - readonly property color m3onTertiaryFixed: root.layer(root.palette.m3onTertiaryFixed) - readonly property color m3onTertiaryFixedVariant: root.layer(root.palette.m3onTertiaryFixedVariant) - } - - component M3Palette: QtObject { - property color m3primary_paletteKeyColor: "#a8627b" - property color m3secondary_paletteKeyColor: "#8e6f78" - property color m3tertiary_paletteKeyColor: "#986e4c" - property color m3neutral_paletteKeyColor: "#807477" - property color m3neutral_variant_paletteKeyColor: "#837377" - property color m3background: "#191114" - property color m3onBackground: "#efdfe2" - property color m3surface: "#191114" - property color m3surfaceDim: "#191114" - property color m3surfaceBright: "#403739" - property color m3surfaceContainerLowest: "#130c0e" - property color m3surfaceContainerLow: "#22191c" - property color m3surfaceContainer: "#261d20" - property color m3surfaceContainerHigh: "#31282a" - property color m3surfaceContainerHighest: "#3c3235" - property color m3onSurface: "#efdfe2" - property color m3surfaceVariant: "#514347" - property color m3onSurfaceVariant: "#d5c2c6" - property color m3inverseSurface: "#efdfe2" - property color m3inverseOnSurface: "#372e30" - property color m3outline: "#9e8c91" - property color m3outlineVariant: "#514347" - property color m3shadow: "#000000" - property color m3scrim: "#000000" - property color m3surfaceTint: "#ffb0ca" - property color m3primary: "#ffb0ca" - property color m3onPrimary: "#541d34" - property color m3primaryContainer: "#6f334a" - property color m3onPrimaryContainer: "#ffd9e3" - property color m3inversePrimary: "#8b4a62" - property color m3secondary: "#e2bdc7" - property color m3onSecondary: "#422932" - property color m3secondaryContainer: "#5a3f48" - property color m3onSecondaryContainer: "#ffd9e3" - property color m3tertiary: "#f0bc95" - property color m3onTertiary: "#48290c" - property color m3tertiaryContainer: "#b58763" - property color m3onTertiaryContainer: "#000000" - property color m3error: "#ffb4ab" - property color m3onError: "#690005" - property color m3errorContainer: "#93000a" - property color m3onErrorContainer: "#ffdad6" - property color m3success: "#B5CCBA" - property color m3onSuccess: "#213528" - property color m3successContainer: "#374B3E" - property color m3onSuccessContainer: "#D1E9D6" - property color m3primaryFixed: "#ffd9e3" - property color m3primaryFixedDim: "#ffb0ca" - property color m3onPrimaryFixed: "#39071f" - property color m3onPrimaryFixedVariant: "#6f334a" - property color m3secondaryFixed: "#ffd9e3" - property color m3secondaryFixedDim: "#e2bdc7" - property color m3onSecondaryFixed: "#2b151d" - property color m3onSecondaryFixedVariant: "#5a3f48" - property color m3tertiaryFixed: "#ffdcc3" - property color m3tertiaryFixedDim: "#f0bc95" - property color m3onTertiaryFixed: "#2f1500" - property color m3onTertiaryFixedVariant: "#623f21" - property color term0: "#353434" - property color term1: "#ff4c8a" - property color term2: "#ffbbb7" - property color term3: "#ffdedf" - property color term4: "#b3a2d5" - property color term5: "#e98fb0" - property color term6: "#ffba93" - property color term7: "#eed1d2" - property color term8: "#b39e9e" - property color term9: "#ff80a3" - property color term10: "#ffd3d0" - property color term11: "#fff1f0" - property color term12: "#dcbc93" - property color term13: "#f9a8c2" - property color term14: "#ffd1c0" - property color term15: "#ffffff" - } -} diff --git a/services/GameMode.qml b/services/GameMode.qml deleted file mode 100644 index 83770b7..0000000 --- a/services/GameMode.qml +++ /dev/null @@ -1,76 +0,0 @@ -pragma Singleton - -import qs.services -import qs.config -import Caelestia -import Quickshell -import Quickshell.Io -import QtQuick - -Singleton { - id: root - - property alias enabled: props.enabled - - function setDynamicConfs(): void { - Hypr.extras.applyOptions({ - "animations:enabled": 0, - "decoration:shadow:enabled": 0, - "decoration:blur:enabled": 0, - "general:gaps_in": 0, - "general:gaps_out": 0, - "general:border_size": 1, - "decoration:rounding": 0, - "general:allow_tearing": 1 - }); - } - - onEnabledChanged: { - if (enabled) { - setDynamicConfs(); - if (Config.utilities.toasts.gameModeChanged) - Toaster.toast(qsTr("Game mode enabled"), qsTr("Disabled Hyprland animations, blur, gaps and shadows"), "gamepad"); - } else { - Hypr.extras.message("reload"); - if (Config.utilities.toasts.gameModeChanged) - Toaster.toast(qsTr("Game mode disabled"), qsTr("Hyprland settings restored"), "gamepad"); - } - } - - PersistentProperties { - id: props - - property bool enabled: Hypr.options["animations:enabled"] === 0 - - reloadableId: "gameMode" - } - - Connections { - target: Hypr - - function onConfigReloaded(): void { - if (props.enabled) - root.setDynamicConfs(); - } - } - - IpcHandler { - target: "gameMode" - - function isEnabled(): bool { - return props.enabled; - } - - function toggle(): void { - props.enabled = !props.enabled; - } - - function enable(): void { - props.enabled = true; - } - - function disable(): void { - props.enabled = false; - } - } -} diff --git a/services/Hypr.qml b/services/Hypr.qml deleted file mode 100644 index f537792..0000000 --- a/services/Hypr.qml +++ /dev/null @@ -1,143 +0,0 @@ -pragma Singleton - -import qs.components.misc -import qs.config -import Caelestia -import Caelestia.Internal -import Quickshell -import Quickshell.Hyprland -import Quickshell.Io -import QtQuick - -Singleton { - id: root - - readonly property var toplevels: Hyprland.toplevels - readonly property var workspaces: Hyprland.workspaces - readonly property var monitors: Hyprland.monitors - - readonly property HyprlandToplevel activeToplevel: Hyprland.activeToplevel?.wayland?.activated ? Hyprland.activeToplevel : null - readonly property HyprlandWorkspace focusedWorkspace: Hyprland.focusedWorkspace - readonly property HyprlandMonitor focusedMonitor: Hyprland.focusedMonitor - readonly property int activeWsId: focusedWorkspace?.id ?? 1 - - readonly property HyprKeyboard keyboard: extras.devices.keyboards.find(kb => kb.main) ?? null - readonly property bool capsLock: keyboard?.capsLock ?? false - readonly property bool numLock: keyboard?.numLock ?? false - readonly property string defaultKbLayout: keyboard?.layout.split(",")[0] ?? "??" - readonly property string kbLayoutFull: keyboard?.activeKeymap ?? "Unknown" - readonly property string kbLayout: kbMap.get(kbLayoutFull) ?? "??" - readonly property var kbMap: new Map() - - readonly property alias extras: extras - readonly property alias options: extras.options - readonly property alias devices: extras.devices - - property bool hadKeyboard - - signal configReloaded - - function dispatch(request: string): void { - Hyprland.dispatch(request); - } - - function monitorFor(screen: ShellScreen): HyprlandMonitor { - return Hyprland.monitorFor(screen); - } - - function reloadDynamicConfs(): void { - extras.batchMessage(["keyword bindlni ,Caps_Lock,global,caelestia:refreshDevices", "keyword bindlni ,Num_Lock,global,caelestia:refreshDevices"]); - } - - Component.onCompleted: reloadDynamicConfs() - - onCapsLockChanged: { - if (!Config.utilities.toasts.capsLockChanged) - return; - - if (capsLock) - Toaster.toast(qsTr("Caps lock enabled"), qsTr("Caps lock is currently enabled"), "keyboard_capslock_badge"); - else - Toaster.toast(qsTr("Caps lock disabled"), qsTr("Caps lock is currently disabled"), "keyboard_capslock"); - } - - onNumLockChanged: { - if (!Config.utilities.toasts.numLockChanged) - return; - - if (numLock) - Toaster.toast(qsTr("Num lock enabled"), qsTr("Num lock is currently enabled"), "looks_one"); - else - Toaster.toast(qsTr("Num lock disabled"), qsTr("Num lock is currently disabled"), "timer_1"); - } - - onKbLayoutFullChanged: { - if (hadKeyboard && Config.utilities.toasts.kbLayoutChanged) - Toaster.toast(qsTr("Keyboard layout changed"), qsTr("Layout changed to: %1").arg(kbLayoutFull), "keyboard"); - - hadKeyboard = !!keyboard; - } - - Connections { - target: Hyprland - - function onRawEvent(event: HyprlandEvent): void { - const n = event.name; - if (n.endsWith("v2")) - return; - - if (n === "configreloaded") { - root.configReloaded(); - root.reloadDynamicConfs(); - } else if (["workspace", "moveworkspace", "activespecial", "focusedmon"].includes(n)) { - Hyprland.refreshWorkspaces(); - Hyprland.refreshMonitors(); - } else if (["openwindow", "closewindow", "movewindow"].includes(n)) { - Hyprland.refreshToplevels(); - Hyprland.refreshWorkspaces(); - } else if (n.includes("mon")) { - Hyprland.refreshMonitors(); - } else if (n.includes("workspace")) { - Hyprland.refreshWorkspaces(); - } else if (n.includes("window") || n.includes("group") || ["pin", "fullscreen", "changefloatingmode", "minimize"].includes(n)) { - Hyprland.refreshToplevels(); - } - } - } - - FileView { - id: kbLayoutFile - - path: Quickshell.env("CAELESTIA_XKB_RULES_PATH") || "/usr/share/X11/xkb/rules/base.lst" - onLoaded: { - const lines = text().match(/! layout\n([\s\S]*?)\n\n/)[1].split("\n"); - for (const line of lines) { - if (!line.trim() || line.trim().startsWith("!")) - continue; - - const match = line.match(/^\s*([a-z]{2,})\s+([a-zA-Z() ]+)$/); - if (match) - root.kbMap.set(match[2], match[1]); - } - } - } - - IpcHandler { - target: "hypr" - - function refreshDevices(): void { - extras.refreshDevices(); - } - } - - CustomShortcut { - name: "refreshDevices" - description: "Reload devices" - onPressed: extras.refreshDevices() - onReleased: extras.refreshDevices() - } - - HyprExtras { - id: extras - } -} diff --git a/services/IdleInhibitor.qml b/services/IdleInhibitor.qml deleted file mode 100644 index 29409ab..0000000 --- a/services/IdleInhibitor.qml +++ /dev/null @@ -1,56 +0,0 @@ -pragma Singleton - -import Quickshell -import Quickshell.Io -import Quickshell.Wayland - -Singleton { - id: root - - property alias enabled: props.enabled - readonly property alias enabledSince: props.enabledSince - - onEnabledChanged: { - if (enabled) - props.enabledSince = new Date(); - } - - PersistentProperties { - id: props - - property bool enabled - property date enabledSince - - reloadableId: "idleInhibitor" - } - - IdleInhibitor { - enabled: props.enabled - window: PanelWindow { - implicitWidth: 0 - implicitHeight: 0 - color: "transparent" - mask: Region {} - } - } - - IpcHandler { - target: "idleInhibitor" - - function isEnabled(): bool { - return props.enabled; - } - - function toggle(): void { - props.enabled = !props.enabled; - } - - function enable(): void { - props.enabled = true; - } - - function disable(): void { - props.enabled = false; - } - } -} diff --git a/services/Network.qml b/services/Network.qml deleted file mode 100644 index 2c31065..0000000 --- a/services/Network.qml +++ /dev/null @@ -1,190 +0,0 @@ -pragma Singleton - -import Quickshell -import Quickshell.Io -import QtQuick - -Singleton { - id: root - - readonly property list networks: [] - readonly property AccessPoint active: networks.find(n => n.active) ?? null - property bool wifiEnabled: true - readonly property bool scanning: rescanProc.running - - function enableWifi(enabled: bool): void { - const cmd = enabled ? "on" : "off"; - enableWifiProc.exec(["nmcli", "radio", "wifi", cmd]); - } - - function toggleWifi(): void { - const cmd = wifiEnabled ? "off" : "on"; - enableWifiProc.exec(["nmcli", "radio", "wifi", cmd]); - } - - function rescanWifi(): void { - rescanProc.running = true; - } - - function connectToNetwork(ssid: string, password: string): void { - // TODO: Implement password - connectProc.exec(["nmcli", "conn", "up", ssid]); - } - - function disconnectFromNetwork(): void { - if (active) { - disconnectProc.exec(["nmcli", "connection", "down", active.ssid]); - } - } - - function getWifiStatus(): void { - wifiStatusProc.running = true; - } - - Process { - running: true - command: ["nmcli", "m"] - stdout: SplitParser { - onRead: getNetworks.running = true - } - } - - Process { - id: wifiStatusProc - - running: true - command: ["nmcli", "radio", "wifi"] - environment: ({ - LANG: "C.UTF-8", - LC_ALL: "C.UTF-8" - }) - stdout: StdioCollector { - onStreamFinished: { - root.wifiEnabled = text.trim() === "enabled"; - } - } - } - - Process { - id: enableWifiProc - - onExited: { - root.getWifiStatus(); - getNetworks.running = true; - } - } - - Process { - id: rescanProc - - command: ["nmcli", "dev", "wifi", "list", "--rescan", "yes"] - onExited: { - getNetworks.running = true; - } - } - - Process { - id: connectProc - - stdout: SplitParser { - onRead: getNetworks.running = true - } - stderr: StdioCollector { - onStreamFinished: console.warn("Network connection error:", text) - } - } - - Process { - id: disconnectProc - - stdout: SplitParser { - onRead: getNetworks.running = true - } - } - - Process { - id: getNetworks - - running: true - command: ["nmcli", "-g", "ACTIVE,SIGNAL,FREQ,SSID,BSSID,SECURITY", "d", "w"] - environment: ({ - LANG: "C.UTF-8", - LC_ALL: "C.UTF-8" - }) - stdout: StdioCollector { - onStreamFinished: { - const PLACEHOLDER = "STRINGWHICHHOPEFULLYWONTBEUSED"; - const rep = new RegExp("\\\\:", "g"); - const rep2 = new RegExp(PLACEHOLDER, "g"); - - const allNetworks = text.trim().split("\n").map(n => { - const net = n.replace(rep, PLACEHOLDER).split(":"); - return { - active: net[0] === "yes", - strength: parseInt(net[1]), - frequency: parseInt(net[2]), - ssid: net[3]?.replace(rep2, ":") ?? "", - bssid: net[4]?.replace(rep2, ":") ?? "", - security: net[5] ?? "" - }; - }).filter(n => n.ssid && n.ssid.length > 0); - - // Group networks by SSID and prioritize connected ones - const networkMap = new Map(); - for (const network of allNetworks) { - const existing = networkMap.get(network.ssid); - if (!existing) { - networkMap.set(network.ssid, network); - } else { - // Prioritize active/connected networks - if (network.active && !existing.active) { - networkMap.set(network.ssid, network); - } else if (!network.active && !existing.active) { - // If both are inactive, keep the one with better signal - if (network.strength > existing.strength) { - networkMap.set(network.ssid, network); - } - } - // If existing is active and new is not, keep existing - } - } - - const networks = Array.from(networkMap.values()); - - const rNetworks = root.networks; - - const destroyed = rNetworks.filter(rn => !networks.find(n => n.frequency === rn.frequency && n.ssid === rn.ssid && n.bssid === rn.bssid)); - for (const network of destroyed) - rNetworks.splice(rNetworks.indexOf(network), 1).forEach(n => n.destroy()); - - for (const network of networks) { - const match = rNetworks.find(n => n.frequency === network.frequency && n.ssid === network.ssid && n.bssid === network.bssid); - if (match) { - match.lastIpcObject = network; - } else { - rNetworks.push(apComp.createObject(root, { - lastIpcObject: network - })); - } - } - } - } - } - - component AccessPoint: QtObject { - required property var lastIpcObject - readonly property string ssid: lastIpcObject.ssid - readonly property string bssid: lastIpcObject.bssid - readonly property int strength: lastIpcObject.strength - readonly property int frequency: lastIpcObject.frequency - readonly property bool active: lastIpcObject.active - readonly property string security: lastIpcObject.security - readonly property bool isSecure: security.length > 0 - } - - Component { - id: apComp - - AccessPoint {} - } -} diff --git a/services/Notifs.qml b/services/Notifs.qml deleted file mode 100644 index 4a89c7f..0000000 --- a/services/Notifs.qml +++ /dev/null @@ -1,334 +0,0 @@ -pragma Singleton -pragma ComponentBehavior: Bound - -import qs.components.misc -import qs.config -import qs.utils -import Caelestia -import Quickshell -import Quickshell.Io -import Quickshell.Services.Notifications -import QtQuick - -Singleton { - id: root - - property list list: [] - readonly property list notClosed: list.filter(n => !n.closed) - readonly property list popups: list.filter(n => n.popup) - property alias dnd: props.dnd - - property bool loaded - - onDndChanged: { - if (!Config.utilities.toasts.dndChanged) - return; - - if (dnd) - Toaster.toast(qsTr("Do not disturb enabled"), qsTr("Popup notifications are now disabled"), "do_not_disturb_on"); - else - Toaster.toast(qsTr("Do not disturb disabled"), qsTr("Popup notifications are now enabled"), "do_not_disturb_off"); - } - - onListChanged: { - if (loaded) - saveTimer.restart(); - } - - Timer { - id: saveTimer - - interval: 1000 - onTriggered: storage.setText(JSON.stringify(root.notClosed.map(n => ({ - time: n.time, - id: n.id, - summary: n.summary, - body: n.body, - appIcon: n.appIcon, - appName: n.appName, - image: n.image, - expireTimeout: n.expireTimeout, - urgency: n.urgency, - resident: n.resident, - hasActionIcons: n.hasActionIcons, - actions: n.actions - })))) - } - - PersistentProperties { - id: props - - property bool dnd - - reloadableId: "notifs" - } - - NotificationServer { - id: server - - keepOnReload: false - actionsSupported: true - bodyHyperlinksSupported: true - bodyImagesSupported: true - bodyMarkupSupported: true - imageSupported: true - persistenceSupported: true - - onNotification: notif => { - notif.tracked = true; - - const comp = notifComp.createObject(root, { - popup: !props.dnd && ![...Visibilities.screens.values()].some(v => v.sidebar), - notification: notif - }); - root.list = [comp, ...root.list]; - } - } - - FileView { - id: storage - - path: `${Paths.state}/notifs.json` - onLoaded: { - const data = JSON.parse(text()); - for (const notif of data) - root.list.push(notifComp.createObject(root, notif)); - root.list.sort((a, b) => b.time - a.time); - root.loaded = true; - } - onLoadFailed: err => { - if (err === FileViewError.FileNotFound) { - root.loaded = true; - setText("[]"); - } - } - } - - CustomShortcut { - name: "clearNotifs" - description: "Clear all notifications" - onPressed: { - for (const notif of root.list.slice()) - notif.close(); - } - } - - IpcHandler { - target: "notifs" - - function clear(): void { - for (const notif of root.list.slice()) - notif.close(); - } - - function isDndEnabled(): bool { - return props.dnd; - } - - function toggleDnd(): void { - props.dnd = !props.dnd; - } - - function enableDnd(): void { - props.dnd = true; - } - - function disableDnd(): void { - props.dnd = false; - } - } - - component Notif: QtObject { - id: notif - - property bool popup - property bool closed - property var locks: new Set() - - property date time: new Date() - readonly property string timeStr: { - const diff = Time.date.getTime() - time.getTime(); - const m = Math.floor(diff / 60000); - - if (m < 1) - return qsTr("now"); - - const h = Math.floor(m / 60); - const d = Math.floor(h / 24); - - if (d > 0) - return `${d}d`; - if (h > 0) - return `${h}h`; - return `${m}m`; - } - - property Notification notification - property string id - property string summary - property string body - property string appIcon - property string appName - property string image - property real expireTimeout: Config.notifs.defaultExpireTimeout - property int urgency: NotificationUrgency.Normal - property bool resident - property bool hasActionIcons - property list actions - - readonly property Timer timer: Timer { - running: true - interval: notif.expireTimeout > 0 ? notif.expireTimeout : Config.notifs.defaultExpireTimeout - onTriggered: { - if (Config.notifs.expire) - notif.popup = false; - } - } - - readonly property LazyLoader dummyImageLoader: LazyLoader { - active: false - - PanelWindow { - implicitWidth: Config.notifs.sizes.image - implicitHeight: Config.notifs.sizes.image - color: "transparent" - mask: Region {} - - Image { - anchors.fill: parent - source: Qt.resolvedUrl(notif.image) - fillMode: Image.PreserveAspectCrop - cache: false - asynchronous: true - opacity: 0 - - onStatusChanged: { - if (status !== Image.Ready) - return; - - const cacheKey = notif.appName + notif.summary + notif.id; - let h1 = 0xdeadbeef, h2 = 0x41c6ce57, ch; - for (let i = 0; i < cacheKey.length; i++) { - ch = cacheKey.charCodeAt(i); - h1 = Math.imul(h1 ^ ch, 2654435761); - h2 = Math.imul(h2 ^ ch, 1597334677); - } - h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507); - h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909); - h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507); - h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909); - const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0); - - const cache = `${Paths.notifimagecache}/${hash}.png`; - CUtils.saveItem(this, Qt.resolvedUrl(cache), () => { - notif.image = cache; - notif.dummyImageLoader.active = false; - }); - } - } - } - } - - readonly property Connections conn: Connections { - target: notif.notification - - function onClosed(): void { - notif.close(); - } - - function onSummaryChanged(): void { - notif.summary = notif.notification.summary; - } - - function onBodyChanged(): void { - notif.body = notif.notification.body; - } - - function onAppIconChanged(): void { - notif.appIcon = notif.notification.appIcon; - } - - function onAppNameChanged(): void { - notif.appName = notif.notification.appName; - } - - function onImageChanged(): void { - notif.image = notif.notification.image; - if (notif.notification?.image) - notif.dummyImageLoader.active = true; - } - - function onExpireTimeoutChanged(): void { - notif.expireTimeout = notif.notification.expireTimeout; - } - - function onUrgencyChanged(): void { - notif.urgency = notif.notification.urgency; - } - - function onResidentChanged(): void { - notif.resident = notif.notification.resident; - } - - function onHasActionIconsChanged(): void { - notif.hasActionIcons = notif.notification.hasActionIcons; - } - - function onActionsChanged(): void { - notif.actions = notif.notification.actions.map(a => ({ - identifier: a.identifier, - text: a.text, - invoke: () => a.invoke() - })); - } - } - - function lock(item: Item): void { - locks.add(item); - } - - function unlock(item: Item): void { - locks.delete(item); - if (closed) - close(); - } - - function close(): void { - closed = true; - if (locks.size === 0 && root.list.includes(this)) { - root.list = root.list.filter(n => n !== this); - notification?.dismiss(); - destroy(); - } - } - - Component.onCompleted: { - if (!notification) - return; - - id = notification.id; - summary = notification.summary; - body = notification.body; - appIcon = notification.appIcon; - appName = notification.appName; - image = notification.image; - if (notification?.image) - dummyImageLoader.active = true; - expireTimeout = notification.expireTimeout; - urgency = notification.urgency; - resident = notification.resident; - hasActionIcons = notification.hasActionIcons; - actions = notification.actions.map(a => ({ - identifier: a.identifier, - text: a.text, - invoke: () => a.invoke() - })); - } - } - - Component { - id: notifComp - - Notif {} - } -} diff --git a/services/Players.qml b/services/Players.qml deleted file mode 100644 index 1191696..0000000 --- a/services/Players.qml +++ /dev/null @@ -1,126 +0,0 @@ -pragma Singleton - -import qs.components.misc -import qs.config -import Quickshell -import Quickshell.Io -import Quickshell.Services.Mpris -import QtQml -import Caelestia - -Singleton { - id: root - - readonly property list list: Mpris.players.values - readonly property MprisPlayer active: props.manualActive ?? list.find(p => getIdentity(p) === Config.services.defaultPlayer) ?? list[0] ?? null - property alias manualActive: props.manualActive - - function getIdentity(player: MprisPlayer): string { - const alias = Config.services.playerAliases.find(a => a.from === player.identity); - return alias?.to ?? player.identity; - } - - Connections { - target: active - - function onPostTrackChanged() { - if (!Config.utilities.toasts.nowPlaying) { - return; - } - if (active.trackArtist != "" && active.trackTitle != "") { - Toaster.toast(qsTr("Now Playing"), qsTr("%1 - %2").arg(active.trackArtist).arg(active.trackTitle), "music_note"); - } - } - } - - PersistentProperties { - id: props - - property MprisPlayer manualActive - - reloadableId: "players" - } - - CustomShortcut { - name: "mediaToggle" - description: "Toggle media playback" - onPressed: { - const active = root.active; - if (active && active.canTogglePlaying) - active.togglePlaying(); - } - } - - CustomShortcut { - name: "mediaPrev" - description: "Previous track" - onPressed: { - const active = root.active; - if (active && active.canGoPrevious) - active.previous(); - } - } - - CustomShortcut { - name: "mediaNext" - description: "Next track" - onPressed: { - const active = root.active; - if (active && active.canGoNext) - active.next(); - } - } - - CustomShortcut { - name: "mediaStop" - description: "Stop media playback" - onPressed: root.active?.stop() - } - - IpcHandler { - target: "mpris" - - function getActive(prop: string): string { - const active = root.active; - return active ? active[prop] ?? "Invalid property" : "No active player"; - } - - function list(): string { - return root.list.map(p => root.getIdentity(p)).join("\n"); - } - - function play(): void { - const active = root.active; - if (active?.canPlay) - active.play(); - } - - function pause(): void { - const active = root.active; - if (active?.canPause) - active.pause(); - } - - function playPause(): void { - const active = root.active; - if (active?.canTogglePlaying) - active.togglePlaying(); - } - - function previous(): void { - const active = root.active; - if (active?.canGoPrevious) - active.previous(); - } - - function next(): void { - const active = root.active; - if (active?.canGoNext) - active.next(); - } - - function stop(): void { - root.active?.stop(); - } - } -} diff --git a/services/Recorder.qml b/services/Recorder.qml deleted file mode 100644 index e4ce6a8..0000000 --- a/services/Recorder.qml +++ /dev/null @@ -1,82 +0,0 @@ -pragma Singleton - -import Quickshell -import Quickshell.Io -import QtQuick - -Singleton { - id: root - - readonly property alias running: props.running - readonly property alias paused: props.paused - readonly property alias elapsed: props.elapsed - property bool needsStart - property list startArgs - property bool needsStop - property bool needsPause - - function start(extraArgs: list): void { - needsStart = true; - startArgs = extraArgs; - checkProc.running = true; - } - - function stop(): void { - needsStop = true; - checkProc.running = true; - } - - function togglePause(): void { - needsPause = true; - checkProc.running = true; - } - - PersistentProperties { - id: props - - property bool running: false - property bool paused: false - property real elapsed: 0 // Might get too large for int - - reloadableId: "recorder" - } - - Process { - id: checkProc - - running: true - command: ["pidof", "gpu-screen-recorder"] - onExited: code => { - props.running = code === 0; - - if (code === 0) { - if (root.needsStop) { - Quickshell.execDetached(["caelestia", "record"]); - props.running = false; - props.paused = false; - } else if (root.needsPause) { - Quickshell.execDetached(["caelestia", "record", "-p"]); - props.paused = !props.paused; - } - } else if (root.needsStart) { - Quickshell.execDetached(["caelestia", "record", ...root.startArgs]); - props.running = true; - props.paused = false; - props.elapsed = 0; - } - - root.needsStart = false; - root.needsStop = false; - root.needsPause = false; - } - } - - Connections { - target: Time - // enabled: props.running && !props.paused - - function onSecondsChanged(): void { - props.elapsed++; - } - } -} diff --git a/services/SystemUsage.qml b/services/SystemUsage.qml deleted file mode 100644 index bd02da3..0000000 --- a/services/SystemUsage.qml +++ /dev/null @@ -1,222 +0,0 @@ -pragma Singleton - -import qs.config -import Quickshell -import Quickshell.Io -import QtQuick - -Singleton { - id: root - - property real cpuPerc - property real cpuTemp - readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType - property string autoGpuType: "NONE" - property real gpuPerc - property real gpuTemp - property real memUsed - property real memTotal - readonly property real memPerc: memTotal > 0 ? memUsed / memTotal : 0 - property real storageUsed - property real storageTotal - property real storagePerc: storageTotal > 0 ? storageUsed / storageTotal : 0 - - property real lastCpuIdle - property real lastCpuTotal - - property int refCount - - function formatKib(kib: real): var { - const mib = 1024; - const gib = 1024 ** 2; - const tib = 1024 ** 3; - - if (kib >= tib) - return { - value: kib / tib, - unit: "TiB" - }; - if (kib >= gib) - return { - value: kib / gib, - unit: "GiB" - }; - if (kib >= mib) - return { - value: kib / mib, - unit: "MiB" - }; - return { - value: kib, - unit: "KiB" - }; - } - - Timer { - running: root.refCount > 0 - interval: 3000 - repeat: true - triggeredOnStart: true - onTriggered: { - stat.reload(); - meminfo.reload(); - storage.running = true; - gpuUsage.running = true; - sensors.running = true; - } - } - - FileView { - id: stat - - path: "/proc/stat" - onLoaded: { - const data = text().match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/); - if (data) { - const stats = data.slice(1).map(n => parseInt(n, 10)); - const total = stats.reduce((a, b) => a + b, 0); - const idle = stats[3] + (stats[4] ?? 0); - - const totalDiff = total - root.lastCpuTotal; - const idleDiff = idle - root.lastCpuIdle; - root.cpuPerc = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0; - - root.lastCpuTotal = total; - root.lastCpuIdle = idle; - } - } - } - - FileView { - id: meminfo - - path: "/proc/meminfo" - onLoaded: { - const data = text(); - root.memTotal = parseInt(data.match(/MemTotal: *(\d+)/)[1], 10) || 1; - root.memUsed = (root.memTotal - parseInt(data.match(/MemAvailable: *(\d+)/)[1], 10)) || 0; - } - } - - Process { - id: storage - - command: ["sh", "-c", "df | grep '^/dev/' | awk '{print $1, $3, $4}'"] - stdout: StdioCollector { - onStreamFinished: { - const deviceMap = new Map(); - - for (const line of text.trim().split("\n")) { - if (line.trim() === "") - continue; - - const parts = line.trim().split(/\s+/); - if (parts.length >= 3) { - const device = parts[0]; - const used = parseInt(parts[1], 10) || 0; - const avail = parseInt(parts[2], 10) || 0; - - // Only keep the entry with the largest total space for each device - if (!deviceMap.has(device) || (used + avail) > (deviceMap.get(device).used + deviceMap.get(device).avail)) { - deviceMap.set(device, { - used: used, - avail: avail - }); - } - } - } - - let totalUsed = 0; - let totalAvail = 0; - - for (const [device, stats] of deviceMap) { - totalUsed += stats.used; - totalAvail += stats.avail; - } - - root.storageUsed = totalUsed; - root.storageTotal = totalUsed + totalAvail; - } - } - } - - Process { - id: gpuTypeCheck - - running: !Config.services.gpuType - command: ["sh", "-c", "if command -v nvidia-smi &>/dev/null && nvidia-smi -L &>/dev/null; then echo NVIDIA; elif ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | grep -q .; then echo GENERIC; else echo NONE; fi"] - stdout: StdioCollector { - onStreamFinished: root.autoGpuType = text.trim() - } - } - - Process { - id: gpuUsage - - command: root.gpuType === "GENERIC" ? ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent"] : root.gpuType === "NVIDIA" ? ["nvidia-smi", "--query-gpu=utilization.gpu,temperature.gpu", "--format=csv,noheader,nounits"] : ["echo"] - stdout: StdioCollector { - onStreamFinished: { - if (root.gpuType === "GENERIC") { - const percs = text.trim().split("\n"); - const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0); - root.gpuPerc = sum / percs.length / 100; - } else if (root.gpuType === "NVIDIA") { - const [usage, temp] = text.trim().split(","); - root.gpuPerc = parseInt(usage, 10) / 100; - root.gpuTemp = parseInt(temp, 10); - } else { - root.gpuPerc = 0; - root.gpuTemp = 0; - } - } - } - } - - Process { - id: sensors - - command: ["sensors"] - environment: ({ - LANG: "C.UTF-8", - LC_ALL: "C.UTF-8" - }) - stdout: StdioCollector { - onStreamFinished: { - let cpuTemp = text.match(/(?:Package id [0-9]+|Tdie):\s+((\+|-)[0-9.]+)(°| )C/); - if (!cpuTemp) - // If AMD Tdie pattern failed, try fallback on Tctl - cpuTemp = text.match(/Tctl:\s+((\+|-)[0-9.]+)(°| )C/); - - if (cpuTemp) - root.cpuTemp = parseFloat(cpuTemp[1]); - - if (root.gpuType !== "GENERIC") - return; - - let eligible = false; - let sum = 0; - let count = 0; - - for (const line of text.trim().split("\n")) { - if (line === "Adapter: PCI adapter") - eligible = true; - else if (line === "") - eligible = false; - else if (eligible) { - let match = line.match(/^(temp[0-9]+|GPU core|edge)+:\s+\+([0-9]+\.[0-9]+)(°| )C/); - if (!match) - // Fall back to junction/mem if GPU doesn't have edge temp (for AMD GPUs) - match = line.match(/^(junction|mem)+:\s+\+([0-9]+\.[0-9]+)(°| )C/); - - if (match) { - sum += parseFloat(match[2]); - count++; - } - } - } - - root.gpuTemp = count > 0 ? sum / count : 0; - } - } - } -} diff --git a/services/Time.qml b/services/Time.qml deleted file mode 100644 index c4b3913..0000000 --- a/services/Time.qml +++ /dev/null @@ -1,20 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { - property alias enabled: clock.enabled - readonly property date date: clock.date - readonly property int hours: clock.hours - readonly property int minutes: clock.minutes - readonly property int seconds: clock.seconds - - function format(fmt: string): string { - return Qt.formatDateTime(clock.date, fmt); - } - - SystemClock { - id: clock - precision: SystemClock.Seconds - } -} diff --git a/services/VPN.qml b/services/VPN.qml deleted file mode 100644 index 10e5e7e..0000000 --- a/services/VPN.qml +++ /dev/null @@ -1,176 +0,0 @@ -pragma Singleton - -import Quickshell -import Quickshell.Io -import QtQuick -import qs.config -import Caelestia - -Singleton { - id: root - - property bool connected: false - - readonly property bool connecting: connectProc.running || disconnectProc.running - readonly property bool enabled: Config.utilities.vpn.enabled - readonly property var providerInput: (Config.utilities.vpn.provider && Config.utilities.vpn.provider.length > 0) ? Config.utilities.vpn.provider[0] : "wireguard" - readonly property bool isCustomProvider: typeof providerInput === "object" - readonly property string providerName: isCustomProvider ? (providerInput.name || "custom") : String(providerInput) - readonly property string interfaceName: isCustomProvider ? (providerInput.interface || "") : "" - readonly property var currentConfig: { - const name = providerName; - const iface = interfaceName; - const defaults = getBuiltinDefaults(name, iface); - - if (isCustomProvider) { - const custom = providerInput; - return { - connectCmd: custom.connectCmd || defaults.connectCmd, - disconnectCmd: custom.disconnectCmd || defaults.disconnectCmd, - interface: custom.interface || defaults.interface, - displayName: custom.displayName || defaults.displayName - }; - } - - return defaults; - } - - function getBuiltinDefaults(name, iface) { - const builtins = { - "wireguard": { - connectCmd: ["pkexec", "wg-quick", "up", iface], - disconnectCmd: ["pkexec", "wg-quick", "down", iface], - interface: iface, - displayName: iface - }, - "warp": { - connectCmd: ["warp-cli", "connect"], - disconnectCmd: ["warp-cli", "disconnect"], - interface: "CloudflareWARP", - displayName: "Warp" - }, - "netbird": { - connectCmd: ["netbird", "up"], - disconnectCmd: ["netbird", "down"], - interface: "wt0", - displayName: "NetBird" - }, - "tailscale": { - connectCmd: ["tailscale", "up"], - disconnectCmd: ["tailscale", "down"], - interface: "tailscale0", - displayName: "Tailscale" - } - }; - - return builtins[name] || { - connectCmd: [name, "up"], - disconnectCmd: [name, "down"], - interface: iface || name, - displayName: name - }; - } - - function connect(): void { - if (!connected && !connecting && root.currentConfig && root.currentConfig.connectCmd) { - connectProc.exec(root.currentConfig.connectCmd); - } - } - - function disconnect(): void { - if (connected && !connecting && root.currentConfig && root.currentConfig.disconnectCmd) { - disconnectProc.exec(root.currentConfig.disconnectCmd); - } - } - - function toggle(): void { - if (connected) { - disconnect(); - } else { - connect(); - } - } - - function checkStatus(): void { - if (root.enabled) { - statusProc.running = true; - } - } - - onConnectedChanged: { - if (!Config.utilities.toasts.vpnChanged) - return; - - const displayName = root.currentConfig ? (root.currentConfig.displayName || "VPN") : "VPN"; - if (connected) { - Toaster.toast(qsTr("VPN connected"), qsTr("Connected to %1").arg(displayName), "vpn_key"); - } else { - Toaster.toast(qsTr("VPN disconnected"), qsTr("Disconnected from %1").arg(displayName), "vpn_key_off"); - } - } - - Component.onCompleted: root.enabled && statusCheckTimer.start() - - Process { - id: nmMonitor - - running: root.enabled - command: ["nmcli", "monitor"] - stdout: SplitParser { - onRead: statusCheckTimer.restart() - } - } - - Process { - id: statusProc - - command: ["ip", "link", "show"] - environment: ({ - LANG: "C.UTF-8", - LC_ALL: "C.UTF-8" - }) - stdout: StdioCollector { - onStreamFinished: { - const iface = root.currentConfig ? root.currentConfig.interface : ""; - root.connected = iface && text.includes(iface + ":"); - } - } - } - - Process { - id: connectProc - - onExited: statusCheckTimer.start() - stderr: StdioCollector { - onStreamFinished: { - const error = text.trim(); - if (error && !error.includes("[#]") && !error.includes("already exists")) { - console.warn("VPN connection error:", error); - } else if (error.includes("already exists")) { - root.connected = true; - } - } - } - } - - Process { - id: disconnectProc - - onExited: statusCheckTimer.start() - stderr: StdioCollector { - onStreamFinished: { - const error = text.trim(); - if (error && !error.includes("[#]")) { - console.warn("VPN disconnection error:", error); - } - } - } - } - - Timer { - id: statusCheckTimer - - interval: 500 - onTriggered: root.checkStatus() - } -} diff --git a/services/Visibilities.qml b/services/Visibilities.qml deleted file mode 100644 index 5ddde0c..0000000 --- a/services/Visibilities.qml +++ /dev/null @@ -1,16 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { - property var screens: new Map() - property var bars: new Map() - - function load(screen: ShellScreen, visibilities: var): void { - screens.set(Hypr.monitorFor(screen), visibilities); - } - - function getForActive(): PersistentProperties { - return screens.get(Hypr.focusedMonitor); - } -} diff --git a/services/Wallpapers.qml b/services/Wallpapers.qml deleted file mode 100644 index cb96bc5..0000000 --- a/services/Wallpapers.qml +++ /dev/null @@ -1,93 +0,0 @@ -pragma Singleton - -import qs.config -import qs.utils -import Caelestia.Models -import Quickshell -import Quickshell.Io -import QtQuick - -Searcher { - id: root - - readonly property string currentNamePath: `${Paths.state}/wallpaper/path.txt` - readonly property list smartArg: Config.services.smartScheme ? [] : ["--no-smart"] - - property bool showPreview: false - readonly property string current: showPreview ? previewPath : actualCurrent - property string previewPath - property string actualCurrent - property bool previewColourLock - - function setWallpaper(path: string): void { - actualCurrent = path; - Quickshell.execDetached(["caelestia", "wallpaper", "-f", path, ...smartArg]); - } - - function preview(path: string): void { - previewPath = path; - showPreview = true; - - if (Colours.scheme === "dynamic") - getPreviewColoursProc.running = true; - } - - function stopPreview(): void { - showPreview = false; - if (!previewColourLock) - Colours.showPreview = false; - } - - list: wallpapers.entries - key: "relativePath" - useFuzzy: Config.launcher.useFuzzy.wallpapers - extraOpts: useFuzzy ? ({}) : ({ - forward: false - }) - - IpcHandler { - target: "wallpaper" - - function get(): string { - return root.actualCurrent; - } - - function set(path: string): void { - root.setWallpaper(path); - } - - function list(): string { - return root.list.map(w => w.path).join("\n"); - } - } - - FileView { - path: root.currentNamePath - watchChanges: true - onFileChanged: reload() - onLoaded: { - root.actualCurrent = text().trim(); - root.previewColourLock = false; - } - } - - FileSystemModel { - id: wallpapers - - recursive: true - path: Paths.wallsdir - filter: FileSystemModel.Images - } - - Process { - id: getPreviewColoursProc - - command: ["caelestia", "wallpaper", "-p", root.previewPath, ...root.smartArg] - stdout: StdioCollector { - onStreamFinished: { - Colours.load(text, true); - Colours.showPreview = true; - } - } - } -} diff --git a/services/Weather.qml b/services/Weather.qml deleted file mode 100644 index 73e0b77..0000000 --- a/services/Weather.qml +++ /dev/null @@ -1,40 +0,0 @@ -pragma Singleton - -import qs.config -import qs.utils -import Caelestia -import Quickshell -import QtQuick - -Singleton { - id: root - - property string city - property var cc - property var forecast - readonly property string icon: cc ? Icons.getWeatherIcon(cc.weatherCode) : "cloud_alert" - readonly property string description: cc?.weatherDesc[0].value ?? qsTr("No weather") - readonly property string temp: Config.services.useFahrenheit ? `${cc?.temp_F ?? 0}°F` : `${cc?.temp_C ?? 0}°C` - readonly property string feelsLike: Config.services.useFahrenheit ? `${cc?.FeelsLikeF ?? 0}°F` : `${cc?.FeelsLikeC ?? 0}°C` - readonly property int humidity: cc?.humidity ?? 0 - - function reload(): void { - if (Config.services.weatherLocation) - city = Config.services.weatherLocation; - else if (!city || timer.elapsed() > 900) - Requests.get("https://ipinfo.io/json", text => { - city = JSON.parse(text).city ?? ""; - timer.restart(); - }); - } - - onCityChanged: Requests.get(`https://wttr.in/${city}?format=j1`, text => { - const json = JSON.parse(text); - cc = json.current_condition[0]; - forecast = json.weather; - }) - - ElapsedTimer { - id: timer - } -} diff --git a/shell.qml b/shell.qml old mode 100755 new mode 100644 index 1ebc771..bda7f1f --- a/shell.qml +++ b/shell.qml @@ -1,61 +1,34 @@ -pragma ComponentBehavior: Bound -import QtQuick -import Quickshell -import Quickshell.Io -import Quickshell.Wayland -import qs.Modules -import qs.Functions - -PanelWindow { - id: mainWindow - WlrLayershell.layer: WlrLayer.Top - color: "transparent" - anchors { - left: true - bottom: true - } - margins { - left: 0 - bottom: 9 - } - - surfaceFormat.opaque: false - implicitWidth: 320 - implicitHeight: 293 - - property bool onTop: true - - IpcHandler { - target: "command" - - // Keybind swap layer - function toggleLayer(): void { - if ( !mainWindow.onTop ) { - mainWindow.WlrLayershell.layer = WlrLayer.Top - mainWindow.onTop = true - } else { - mainWindow.WlrLayershell.layer = WlrLayer.Bottom - mainWindow.onTop = false - } - } - - // Keybind swap overlay - function toggleOverlay(): void { - if (!mainWindow.onTop) { - mainWindow.WlrLayershell.layer = WlrLayer.Overlay - mainWindow.onTop = true - } else { - mainWindow.WlrLayershell.layer = WlrLayer.Bottom - mainWindow.onTop = false - } - } - } - - ToggleLayer { - id: toggleHelper - } - - PetMarch{ - id:petMarch - } -} +pragma ComponentBehavior: Bound +import QtQuick +import Quickshell +import Quickshell.Wayland +import qs.Modules +import qs.Functions + +PanelWindow { + id: mainWindow + color: "transparent" + property bool onTop: true + WlrLayershell.layer: WlrLayer.Top + anchors { + left: true + bottom: true + } + + margins { + left: 0 + bottom: 9 + } + + surfaceFormat.opaque: false + implicitWidth: 320 + implicitHeight: 293 + + ToggleLayer { + id: toggleHelper + } + + PetMarch { + id: petMarch + } +} diff --git a/utils/Icons.qml b/utils/Icons.qml deleted file mode 100644 index e946c4f..0000000 --- a/utils/Icons.qml +++ /dev/null @@ -1,228 +0,0 @@ -pragma Singleton - -import qs.config -import Quickshell -import Quickshell.Services.Notifications - -Singleton { - id: root - - readonly property var weatherIcons: ({ - "113": "clear_day", - "116": "partly_cloudy_day", - "119": "cloud", - "122": "cloud", - "143": "foggy", - "176": "rainy", - "179": "rainy", - "182": "rainy", - "185": "rainy", - "200": "thunderstorm", - "227": "cloudy_snowing", - "230": "snowing_heavy", - "248": "foggy", - "260": "foggy", - "263": "rainy", - "266": "rainy", - "281": "rainy", - "284": "rainy", - "293": "rainy", - "296": "rainy", - "299": "rainy", - "302": "weather_hail", - "305": "rainy", - "308": "weather_hail", - "311": "rainy", - "314": "rainy", - "317": "rainy", - "320": "cloudy_snowing", - "323": "cloudy_snowing", - "326": "cloudy_snowing", - "329": "snowing_heavy", - "332": "snowing_heavy", - "335": "snowing", - "338": "snowing_heavy", - "350": "rainy", - "353": "rainy", - "356": "rainy", - "359": "weather_hail", - "362": "rainy", - "365": "rainy", - "368": "cloudy_snowing", - "371": "snowing", - "374": "rainy", - "377": "rainy", - "386": "thunderstorm", - "389": "thunderstorm", - "392": "thunderstorm", - "395": "snowing" - }) - - readonly property var categoryIcons: ({ - WebBrowser: "web", - Printing: "print", - Security: "security", - Network: "chat", - Archiving: "archive", - Compression: "archive", - Development: "code", - IDE: "code", - TextEditor: "edit_note", - Audio: "music_note", - Music: "music_note", - Player: "music_note", - Recorder: "mic", - Game: "sports_esports", - FileTools: "files", - FileManager: "files", - Filesystem: "files", - FileTransfer: "files", - Settings: "settings", - DesktopSettings: "settings", - HardwareSettings: "settings", - TerminalEmulator: "terminal", - ConsoleOnly: "terminal", - Utility: "build", - Monitor: "monitor_heart", - Midi: "graphic_eq", - Mixer: "graphic_eq", - AudioVideoEditing: "video_settings", - AudioVideo: "music_video", - Video: "videocam", - Building: "construction", - Graphics: "photo_library", - "2DGraphics": "photo_library", - RasterGraphics: "photo_library", - TV: "tv", - System: "host", - Office: "content_paste" - }) - - function getAppIcon(name: string, fallback: string): string { - const icon = DesktopEntries.heuristicLookup(name)?.icon; - if (fallback !== "undefined") - return Quickshell.iconPath(icon, fallback); - return Quickshell.iconPath(icon); - } - - function getAppCategoryIcon(name: string, fallback: string): string { - const categories = DesktopEntries.heuristicLookup(name)?.categories; - - if (categories) - for (const [key, value] of Object.entries(categoryIcons)) - if (categories.includes(key)) - return value; - return fallback; - } - - function getNetworkIcon(strength: int): string { - if (strength >= 80) - return "signal_wifi_4_bar"; - if (strength >= 60) - return "network_wifi_3_bar"; - if (strength >= 40) - return "network_wifi_2_bar"; - if (strength >= 20) - return "network_wifi_1_bar"; - return "signal_wifi_0_bar"; - } - - function getBluetoothIcon(icon: string): string { - if (icon.includes("headset") || icon.includes("headphones")) - return "headphones"; - if (icon.includes("audio")) - return "speaker"; - if (icon.includes("phone")) - return "smartphone"; - if (icon.includes("mouse")) - return "mouse"; - if (icon.includes("keyboard")) - return "keyboard"; - return "bluetooth"; - } - - function getWeatherIcon(code: string): string { - if (weatherIcons.hasOwnProperty(code)) - return weatherIcons[code]; - return "air"; - } - - function getNotifIcon(summary: string, urgency: int): string { - summary = summary.toLowerCase(); - if (summary.includes("reboot")) - return "restart_alt"; - if (summary.includes("recording")) - return "screen_record"; - if (summary.includes("battery")) - return "power"; - if (summary.includes("screenshot")) - return "screenshot_monitor"; - if (summary.includes("welcome")) - return "waving_hand"; - if (summary.includes("time") || summary.includes("a break")) - return "schedule"; - if (summary.includes("installed")) - return "download"; - if (summary.includes("update")) - return "update"; - if (summary.includes("unable to")) - return "deployed_code_alert"; - if (summary.includes("profile")) - return "person"; - if (summary.includes("file")) - return "folder_copy"; - if (urgency === NotificationUrgency.Critical) - return "release_alert"; - return "chat"; - } - - function getVolumeIcon(volume: real, isMuted: bool): string { - if (isMuted) - return "no_sound"; - if (volume >= 0.5) - return "volume_up"; - if (volume > 0) - return "volume_down"; - return "volume_mute"; - } - - function getMicVolumeIcon(volume: real, isMuted: bool): string { - if (!isMuted && volume > 0) - return "mic"; - return "mic_off"; - } - - function getSpecialWsIcon(name: string): string { - name = name.toLowerCase().slice("special:".length); - - for (const iconConfig of Config.bar.workspaces.specialWorkspaceIcons) { - if (iconConfig.name === name) { - return iconConfig.icon; - } - } - - if (name === "special") - return "star"; - if (name === "communication") - return "forum"; - if (name === "music") - return "music_cast"; - if (name === "todo") - return "checklist"; - if (name === "sysmon") - return "monitor_heart"; - return name[0].toUpperCase(); - } - - function getTrayIcon(id: string, icon: string): string { - for (const sub of Config.bar.tray.iconSubs) - if (sub.id === id) - return sub.image ? Qt.resolvedUrl(sub.image) : Quickshell.iconPath(sub.icon); - - if (icon.includes("?path=")) { - const [name, path] = icon.split("?path="); - icon = Qt.resolvedUrl(`${path}/${name.slice(name.lastIndexOf("/") + 1)}`); - } - return icon; - } -} diff --git a/utils/Images.qml b/utils/Images.qml deleted file mode 100644 index ac76f51..0000000 --- a/utils/Images.qml +++ /dev/null @@ -1,12 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { - readonly property list validImageTypes: ["jpeg", "png", "webp", "tiff", "svg"] - readonly property list validImageExtensions: ["jpg", "jpeg", "png", "webp", "tif", "tiff", "svg"] - - function isValidImageByName(name: string): bool { - return validImageExtensions.some(t => name.endsWith(`.${t}`)); - } -} diff --git a/utils/Paths.qml b/utils/Paths.qml deleted file mode 100644 index 71cdd23..0000000 --- a/utils/Paths.qml +++ /dev/null @@ -1,36 +0,0 @@ -pragma Singleton - -import qs.config -import Quickshell - -Singleton { - id: root - - readonly property string home: Quickshell.env("HOME") - readonly property string pictures: Quickshell.env("XDG_PICTURES_DIR") || `${home}/Pictures` - readonly property string videos: Quickshell.env("XDG_VIDEOS_DIR") || `${home}/Videos` - - readonly property string data: `${Quickshell.env("XDG_DATA_HOME") || `${home}/.local/share`}/caelestia` - readonly property string state: `${Quickshell.env("XDG_STATE_HOME") || `${home}/.local/state`}/caelestia` - readonly property string cache: `${Quickshell.env("XDG_CACHE_HOME") || `${home}/.cache`}/caelestia` - readonly property string config: `${Quickshell.env("XDG_CONFIG_HOME") || `${home}/.config`}/caelestia` - - readonly property string imagecache: `${cache}/imagecache` - readonly property string notifimagecache: `${imagecache}/notifs` - readonly property string wallsdir: Quickshell.env("CAELESTIA_WALLPAPERS_DIR") || absolutePath(Config.paths.wallpaperDir) - readonly property string recsdir: Quickshell.env("CAELESTIA_RECORDINGS_DIR") || `${videos}/Recordings` - readonly property string libdir: Quickshell.env("CAELESTIA_LIB_DIR") || "/usr/lib/caelestia" - - function toLocalFile(path: url): string { - path = Qt.resolvedUrl(path); - return path.toString() ? CUtils.toLocalFile(path) : ""; - } - - function absolutePath(path: string): string { - return toLocalFile(path.replace("~", home)); - } - - function shortenHome(path: string): string { - return path.replace(home, "~"); - } -} diff --git a/utils/Searcher.qml b/utils/Searcher.qml deleted file mode 100644 index 053b73b..0000000 --- a/utils/Searcher.qml +++ /dev/null @@ -1,56 +0,0 @@ -import Quickshell - -import "scripts/fzf.js" as Fzf -import "scripts/fuzzysort.js" as Fuzzy -import QtQuick - -Singleton { - required property list list - property string key: "name" - property bool useFuzzy: false - property var extraOpts: ({}) - - // Extra stuff for fuzzy - property list keys: [key] - property list weights: [1] - - readonly property var fzf: useFuzzy ? [] : new Fzf.Finder(list, Object.assign({ - selector - }, extraOpts)) - readonly property list fuzzyPrepped: useFuzzy ? list.map(e => { - const obj = { - _item: e - }; - for (const k of keys) - obj[k] = Fuzzy.prepare(e[k]); - return obj; - }) : [] - - function transformSearch(search: string): string { - return search; - } - - function selector(item: var): string { - // Only for fzf - return item[key]; - } - - function query(search: string): list { - search = transformSearch(search); - if (!search) - return [...list]; - - if (useFuzzy) - return Fuzzy.go(search, fuzzyPrepped, Object.assign({ - all: true, - keys, - scoreFn: r => weights.reduce((a, w, i) => a + r[i].score * w, 0) - }, extraOpts)).map(r => r.obj._item); - - return fzf.find(search).sort((a, b) => { - if (a.score === b.score) - return selector(a.item).trim().length - selector(b.item).trim().length; - return b.score - a.score; - }).map(r => r.item); - } -} diff --git a/utils/SysInfo.qml b/utils/SysInfo.qml deleted file mode 100644 index ab2699d..0000000 --- a/utils/SysInfo.qml +++ /dev/null @@ -1,72 +0,0 @@ -pragma Singleton - -import Quickshell -import Quickshell.Io -import QtQuick - -Singleton { - id: root - - property string osName - property string osPrettyName - property string osId - property list osIdLike - property string osLogo: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/logo.svg`) - property bool isDefaultLogo: true - - property string uptime - readonly property string user: Quickshell.env("USER") - readonly property string wm: Quickshell.env("XDG_CURRENT_DESKTOP") || Quickshell.env("XDG_SESSION_DESKTOP") - readonly property string shell: Quickshell.env("SHELL").split("/").pop() - - FileView { - id: osRelease - - path: "/etc/os-release" - onLoaded: { - const lines = text().split("\n"); - - const fd = key => lines.find(l => l.startsWith(`${key}=`))?.split("=")[1].replace(/"/g, "") ?? ""; - - root.osName = fd("NAME"); - root.osPrettyName = fd("PRETTY_NAME"); - root.osId = fd("ID"); - root.osIdLike = fd("ID_LIKE").split(" "); - - const logo = Quickshell.iconPath(fd("LOGO"), true); - if (logo) { - root.osLogo = logo; - root.isDefaultLogo = false; - } - } - } - - Timer { - running: true - repeat: true - interval: 15000 - onTriggered: fileUptime.reload() - } - - FileView { - id: fileUptime - - path: "/proc/uptime" - onLoaded: { - const up = parseInt(text().split(" ")[0] ?? 0); - - const days = Math.floor(up / 86400); - const hours = Math.floor((up % 86400) / 3600); - const minutes = Math.floor((up % 3600) / 60); - - let str = ""; - if (days > 0) - str += `${days} day${days === 1 ? "" : "s"}`; - if (hours > 0) - str += `${str ? ", " : ""}${hours} hour${hours === 1 ? "" : "s"}`; - if (minutes > 0 || !str) - str += `${str ? ", " : ""}${minutes} minute${minutes === 1 ? "" : "s"}`; - root.uptime = str; - } - } -} diff --git a/utils/scripts/fuzzysort.js b/utils/scripts/fuzzysort.js deleted file mode 100644 index 94308ff..0000000 --- a/utils/scripts/fuzzysort.js +++ /dev/null @@ -1,705 +0,0 @@ -.pragma library - -/* -https://github.com/farzher/fuzzysort - -MIT License - -Copyright (c) 2018 Stephen Kamenar - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -var single = (search, target) => { - if(!search || !target) return NULL - - var preparedSearch = getPreparedSearch(search) - if(!isPrepared(target)) target = getPrepared(target) - - var searchBitflags = preparedSearch.bitflags - if((searchBitflags & target._bitflags) !== searchBitflags) return NULL - - return algorithm(preparedSearch, target) -} - -var go = (search, targets, options) => { - if(!search) return options?.all ? all(targets, options) : noResults - - var preparedSearch = getPreparedSearch(search) - var searchBitflags = preparedSearch.bitflags - var containsSpace = preparedSearch.containsSpace - - var threshold = denormalizeScore( options?.threshold || 0 ) - var limit = options?.limit || INFINITY - - var resultsLen = 0; var limitedCount = 0 - var targetsLen = targets.length - - function push_result(result) { - if(resultsLen < limit) { q.add(result); ++resultsLen } - else { - ++limitedCount - if(result._score > q.peek()._score) q.replaceTop(result) - } - } - - // This code is copy/pasted 3 times for performance reasons [options.key, options.keys, no keys] - - // options.key - if(options?.key) { - var key = options.key - for(var i = 0; i < targetsLen; ++i) { var obj = targets[i] - var target = getValue(obj, key) - if(!target) continue - if(!isPrepared(target)) target = getPrepared(target) - - if((searchBitflags & target._bitflags) !== searchBitflags) continue - var result = algorithm(preparedSearch, target) - if(result === NULL) continue - if(result._score < threshold) continue - - result.obj = obj - push_result(result) - } - - // options.keys - } else if(options?.keys) { - var keys = options.keys - var keysLen = keys.length - - outer: for(var i = 0; i < targetsLen; ++i) { var obj = targets[i] - - { // early out based on bitflags - var keysBitflags = 0 - for (var keyI = 0; keyI < keysLen; ++keyI) { - var key = keys[keyI] - var target = getValue(obj, key) - if(!target) { tmpTargets[keyI] = noTarget; continue } - if(!isPrepared(target)) target = getPrepared(target) - tmpTargets[keyI] = target - - keysBitflags |= target._bitflags - } - - if((searchBitflags & keysBitflags) !== searchBitflags) continue - } - - if(containsSpace) for(let i=0; i -1000) { - if(keysSpacesBestScores[i] > NEGATIVE_INFINITY) { - var tmp = (keysSpacesBestScores[i] + allowPartialMatchScores[i]) / 4/*bonus score for having multiple matches*/ - if(tmp > keysSpacesBestScores[i]) keysSpacesBestScores[i] = tmp - } - } - if(allowPartialMatchScores[i] > keysSpacesBestScores[i]) keysSpacesBestScores[i] = allowPartialMatchScores[i] - } - } - - if(containsSpace) { - for(let i=0; i -1000) { - if(score > NEGATIVE_INFINITY) { - var tmp = (score + result._score) / 4/*bonus score for having multiple matches*/ - if(tmp > score) score = tmp - } - } - if(result._score > score) score = result._score - } - } - - objResults.obj = obj - objResults._score = score - if(options?.scoreFn) { - score = options.scoreFn(objResults) - if(!score) continue - score = denormalizeScore(score) - objResults._score = score - } - - if(score < threshold) continue - push_result(objResults) - } - - // no keys - } else { - for(var i = 0; i < targetsLen; ++i) { var target = targets[i] - if(!target) continue - if(!isPrepared(target)) target = getPrepared(target) - - if((searchBitflags & target._bitflags) !== searchBitflags) continue - var result = algorithm(preparedSearch, target) - if(result === NULL) continue - if(result._score < threshold) continue - - push_result(result) - } - } - - if(resultsLen === 0) return noResults - var results = new Array(resultsLen) - for(var i = resultsLen - 1; i >= 0; --i) results[i] = q.poll() - results.total = resultsLen + limitedCount - return results -} - - -// this is written as 1 function instead of 2 for minification. perf seems fine ... -// except when minified. the perf is very slow -var highlight = (result, open='', close='') => { - var callback = typeof open === 'function' ? open : undefined - - var target = result.target - var targetLen = target.length - var indexes = result.indexes - var highlighted = '' - var matchI = 0 - var indexesI = 0 - var opened = false - var parts = [] - - for(var i = 0; i < targetLen; ++i) { var char = target[i] - if(indexes[indexesI] === i) { - ++indexesI - if(!opened) { opened = true - if(callback) { - parts.push(highlighted); highlighted = '' - } else { - highlighted += open - } - } - - if(indexesI === indexes.length) { - if(callback) { - highlighted += char - parts.push(callback(highlighted, matchI++)); highlighted = '' - parts.push(target.substr(i+1)) - } else { - highlighted += char + close + target.substr(i+1) - } - break - } - } else { - if(opened) { opened = false - if(callback) { - parts.push(callback(highlighted, matchI++)); highlighted = '' - } else { - highlighted += close - } - } - } - highlighted += char - } - - return callback ? parts : highlighted -} - - -var prepare = (target) => { - if(typeof target === 'number') target = ''+target - else if(typeof target !== 'string') target = '' - var info = prepareLowerInfo(target) - return new_result(target, {_targetLower:info._lower, _targetLowerCodes:info.lowerCodes, _bitflags:info.bitflags}) -} - -var cleanup = () => { preparedCache.clear(); preparedSearchCache.clear() } - - -// Below this point is only internal code -// Below this point is only internal code -// Below this point is only internal code -// Below this point is only internal code - - -class Result { - get ['indexes']() { return this._indexes.slice(0, this._indexes.len).sort((a,b)=>a-b) } - set ['indexes'](indexes) { return this._indexes = indexes } - ['highlight'](open, close) { return highlight(this, open, close) } - get ['score']() { return normalizeScore(this._score) } - set ['score'](score) { this._score = denormalizeScore(score) } -} - -class KeysResult extends Array { - get ['score']() { return normalizeScore(this._score) } - set ['score'](score) { this._score = denormalizeScore(score) } -} - -var new_result = (target, options) => { - const result = new Result() - result['target'] = target - result['obj'] = options.obj ?? NULL - result._score = options._score ?? NEGATIVE_INFINITY - result._indexes = options._indexes ?? [] - result._targetLower = options._targetLower ?? '' - result._targetLowerCodes = options._targetLowerCodes ?? NULL - result._nextBeginningIndexes = options._nextBeginningIndexes ?? NULL - result._bitflags = options._bitflags ?? 0 - return result -} - - -var normalizeScore = score => { - if(score === NEGATIVE_INFINITY) return 0 - if(score > 1) return score - return Math.E ** ( ((-score + 1)**.04307 - 1) * -2) -} -var denormalizeScore = normalizedScore => { - if(normalizedScore === 0) return NEGATIVE_INFINITY - if(normalizedScore > 1) return normalizedScore - return 1 - Math.pow((Math.log(normalizedScore) / -2 + 1), 1 / 0.04307) -} - - -var prepareSearch = (search) => { - if(typeof search === 'number') search = ''+search - else if(typeof search !== 'string') search = '' - search = search.trim() - var info = prepareLowerInfo(search) - - var spaceSearches = [] - if(info.containsSpace) { - var searches = search.split(/\s+/) - searches = [...new Set(searches)] // distinct - for(var i=0; i { - if(target.length > 999) return prepare(target) // don't cache huge targets - var targetPrepared = preparedCache.get(target) - if(targetPrepared !== undefined) return targetPrepared - targetPrepared = prepare(target) - preparedCache.set(target, targetPrepared) - return targetPrepared -} -var getPreparedSearch = (search) => { - if(search.length > 999) return prepareSearch(search) // don't cache huge searches - var searchPrepared = preparedSearchCache.get(search) - if(searchPrepared !== undefined) return searchPrepared - searchPrepared = prepareSearch(search) - preparedSearchCache.set(search, searchPrepared) - return searchPrepared -} - - -var all = (targets, options) => { - var results = []; results.total = targets.length // this total can be wrong if some targets are skipped - - var limit = options?.limit || INFINITY - - if(options?.key) { - for(var i=0;i= limit) return results - } - } else if(options?.keys) { - for(var i=0;i= 0; --keyI) { - var target = getValue(obj, options.keys[keyI]) - if(!target) { objResults[keyI] = noTarget; continue } - if(!isPrepared(target)) target = getPrepared(target) - target._score = NEGATIVE_INFINITY - target._indexes.len = 0 - objResults[keyI] = target - } - objResults.obj = obj - objResults._score = NEGATIVE_INFINITY - results.push(objResults); if(results.length >= limit) return results - } - } else { - for(var i=0;i= limit) return results - } - } - - return results -} - - -var algorithm = (preparedSearch, prepared, allowSpaces=false, allowPartialMatch=false) => { - if(allowSpaces===false && preparedSearch.containsSpace) return algorithmSpaces(preparedSearch, prepared, allowPartialMatch) - - var searchLower = preparedSearch._lower - var searchLowerCodes = preparedSearch.lowerCodes - var searchLowerCode = searchLowerCodes[0] - var targetLowerCodes = prepared._targetLowerCodes - var searchLen = searchLowerCodes.length - var targetLen = targetLowerCodes.length - var searchI = 0 // where we at - var targetI = 0 // where you at - var matchesSimpleLen = 0 - - // very basic fuzzy match; to remove non-matching targets ASAP! - // walk through target. find sequential matches. - // if all chars aren't found then exit - for(;;) { - var isMatch = searchLowerCode === targetLowerCodes[targetI] - if(isMatch) { - matchesSimple[matchesSimpleLen++] = targetI - ++searchI; if(searchI === searchLen) break - searchLowerCode = searchLowerCodes[searchI] - } - ++targetI; if(targetI >= targetLen) return NULL // Failed to find searchI - } - - var searchI = 0 - var successStrict = false - var matchesStrictLen = 0 - - var nextBeginningIndexes = prepared._nextBeginningIndexes - if(nextBeginningIndexes === NULL) nextBeginningIndexes = prepared._nextBeginningIndexes = prepareNextBeginningIndexes(prepared.target) - targetI = matchesSimple[0]===0 ? 0 : nextBeginningIndexes[matchesSimple[0]-1] - - // Our target string successfully matched all characters in sequence! - // Let's try a more advanced and strict test to improve the score - // only count it as a match if it's consecutive or a beginning character! - var backtrackCount = 0 - if(targetI !== targetLen) for(;;) { - if(targetI >= targetLen) { - // We failed to find a good spot for this search char, go back to the previous search char and force it forward - if(searchI <= 0) break // We failed to push chars forward for a better match - - ++backtrackCount; if(backtrackCount > 200) break // exponential backtracking is taking too long, just give up and return a bad match - - --searchI - var lastMatch = matchesStrict[--matchesStrictLen] - targetI = nextBeginningIndexes[lastMatch] - - } else { - var isMatch = searchLowerCodes[searchI] === targetLowerCodes[targetI] - if(isMatch) { - matchesStrict[matchesStrictLen++] = targetI - ++searchI; if(searchI === searchLen) { successStrict = true; break } - ++targetI - } else { - targetI = nextBeginningIndexes[targetI] - } - } - } - - // check if it's a substring match - var substringIndex = searchLen <= 1 ? -1 : prepared._targetLower.indexOf(searchLower, matchesSimple[0]) // perf: this is slow - var isSubstring = !!~substringIndex - var isSubstringBeginning = !isSubstring ? false : substringIndex===0 || prepared._nextBeginningIndexes[substringIndex-1] === substringIndex - - // if it's a substring match but not at a beginning index, let's try to find a substring starting at a beginning index for a better score - if(isSubstring && !isSubstringBeginning) { - for(var i=0; i { - var score = 0 - - var extraMatchGroupCount = 0 - for(var i = 1; i < searchLen; ++i) { - if(matches[i] - matches[i-1] !== 1) {score -= matches[i]; ++extraMatchGroupCount} - } - var unmatchedDistance = matches[searchLen-1] - matches[0] - (searchLen-1) - - score -= (12+unmatchedDistance) * extraMatchGroupCount // penality for more groups - - if(matches[0] !== 0) score -= matches[0]*matches[0]*.2 // penality for not starting near the beginning - - if(!successStrict) { - score *= 1000 - } else { - // successStrict on a target with too many beginning indexes loses points for being a bad target - var uniqueBeginningIndexes = 1 - for(var i = nextBeginningIndexes[0]; i < targetLen; i=nextBeginningIndexes[i]) ++uniqueBeginningIndexes - - if(uniqueBeginningIndexes > 24) score *= (uniqueBeginningIndexes-24)*10 // quite arbitrary numbers here ... - } - - score -= (targetLen - searchLen)/2 // penality for longer targets - - if(isSubstring) score /= 1+searchLen*searchLen*1 // bonus for being a full substring - if(isSubstringBeginning) score /= 1+searchLen*searchLen*1 // bonus for substring starting on a beginningIndex - - score -= (targetLen - searchLen)/2 // penality for longer targets - - return score - } - - if(!successStrict) { - if(isSubstring) for(var i=0; i { - var seen_indexes = new Set() - var score = 0 - var result = NULL - - var first_seen_index_last_search = 0 - var searches = preparedSearch.spaceSearches - var searchesLen = searches.length - var changeslen = 0 - - // Return _nextBeginningIndexes back to its normal state - var resetNextBeginningIndexes = () => { - for(let i=changeslen-1; i>=0; i--) target._nextBeginningIndexes[nextBeginningIndexesChanges[i*2 + 0]] = nextBeginningIndexesChanges[i*2 + 1] - } - - var hasAtLeast1Match = false - for(var i=0; i=0; i--) { - if(toReplace !== target._nextBeginningIndexes[i]) break - target._nextBeginningIndexes[i] = newBeginningIndex - nextBeginningIndexesChanges[changeslen*2 + 0] = i - nextBeginningIndexesChanges[changeslen*2 + 1] = toReplace - changeslen++ - } - } - } - - score += result._score / searchesLen - allowPartialMatchScores[i] = result._score / searchesLen - - // dock points based on order otherwise "c man" returns Manifest.cpp instead of CheatManager.h - if(result._indexes[0] < first_seen_index_last_search) { - score -= (first_seen_index_last_search - result._indexes[0]) * 2 - } - first_seen_index_last_search = result._indexes[0] - - for(var j=0; j score) { - if(allowPartialMatch) { - for(var i=0; i str.replace(/\p{Script=Latin}+/gu, match => match.normalize('NFD')).replace(/[\u0300-\u036f]/g, '') - -var prepareLowerInfo = (str) => { - str = remove_accents(str) - var strLen = str.length - var lower = str.toLowerCase() - var lowerCodes = [] // new Array(strLen) sparse array is too slow - var bitflags = 0 - var containsSpace = false // space isn't stored in bitflags because of how searching with a space works - - for(var i = 0; i < strLen; ++i) { - var lowerCode = lowerCodes[i] = lower.charCodeAt(i) - - if(lowerCode === 32) { - containsSpace = true - continue // it's important that we don't set any bitflags for space - } - - var bit = lowerCode>=97&&lowerCode<=122 ? lowerCode-97 // alphabet - : lowerCode>=48&&lowerCode<=57 ? 26 // numbers - // 3 bits available - : lowerCode<=127 ? 30 // other ascii - : 31 // other utf8 - bitflags |= 1< { - var targetLen = target.length - var beginningIndexes = []; var beginningIndexesLen = 0 - var wasUpper = false - var wasAlphanum = false - for(var i = 0; i < targetLen; ++i) { - var targetCode = target.charCodeAt(i) - var isUpper = targetCode>=65&&targetCode<=90 - var isAlphanum = isUpper || targetCode>=97&&targetCode<=122 || targetCode>=48&&targetCode<=57 - var isBeginning = isUpper && !wasUpper || !wasAlphanum || !isAlphanum - wasUpper = isUpper - wasAlphanum = isAlphanum - if(isBeginning) beginningIndexes[beginningIndexesLen++] = i - } - return beginningIndexes -} -var prepareNextBeginningIndexes = (target) => { - target = remove_accents(target) - var targetLen = target.length - var beginningIndexes = prepareBeginningIndexes(target) - var nextBeginningIndexes = [] // new Array(targetLen) sparse array is too slow - var lastIsBeginning = beginningIndexes[0] - var lastIsBeginningI = 0 - for(var i = 0; i < targetLen; ++i) { - if(lastIsBeginning > i) { - nextBeginningIndexes[i] = lastIsBeginning - } else { - lastIsBeginning = beginningIndexes[++lastIsBeginningI] - nextBeginningIndexes[i] = lastIsBeginning===undefined ? targetLen : lastIsBeginning - } - } - return nextBeginningIndexes -} - -var preparedCache = new Map() -var preparedSearchCache = new Map() - -// the theory behind these being globals is to reduce garbage collection by not making new arrays -var matchesSimple = []; var matchesStrict = [] -var nextBeginningIndexesChanges = [] // allows straw berry to match strawberry well, by modifying the end of a substring to be considered a beginning index for the rest of the search -var keysSpacesBestScores = []; var allowPartialMatchScores = [] -var tmpTargets = []; var tmpResults = [] - -// prop = 'key' 2.5ms optimized for this case, seems to be about as fast as direct obj[prop] -// prop = 'key1.key2' 10ms -// prop = ['key1', 'key2'] 27ms -// prop = obj => obj.tags.join() ??ms -var getValue = (obj, prop) => { - var tmp = obj[prop]; if(tmp !== undefined) return tmp - if(typeof prop === 'function') return prop(obj) // this should run first. but that makes string props slower - var segs = prop - if(!Array.isArray(prop)) segs = prop.split('.') - var len = segs.length - var i = -1 - while (obj && (++i < len)) obj = obj[segs[i]] - return obj -} - -var isPrepared = (x) => { return typeof x === 'object' && typeof x._bitflags === 'number' } -var INFINITY = Infinity; var NEGATIVE_INFINITY = -INFINITY -var noResults = []; noResults.total = 0 -var NULL = null - -var noTarget = prepare('') - -// Hacked version of https://github.com/lemire/FastPriorityQueue.js -var fastpriorityqueue=r=>{var e=[],o=0,a={},v=r=>{for(var a=0,v=e[a],c=1;c>1]=e[a],c=1+(a<<1)}for(var f=a-1>>1;a>0&&v._score>1)e[a]=e[f];e[a]=v};return a.add=(r=>{var a=o;e[o++]=r;for(var v=a-1>>1;a>0&&r._score>1)e[a]=e[v];e[a]=r}),a.poll=(r=>{if(0!==o){var a=e[0];return e[0]=e[--o],v(),a}}),a.peek=(r=>{if(0!==o)return e[0]}),a.replaceTop=(r=>{e[0]=r,v()}),a} -var q = fastpriorityqueue() // reuse this - diff --git a/utils/scripts/fzf.js b/utils/scripts/fzf.js deleted file mode 100644 index 995a093..0000000 --- a/utils/scripts/fzf.js +++ /dev/null @@ -1,1307 +0,0 @@ -.pragma library - -/* -https://github.com/ajitid/fzf-for-js - -BSD 3-Clause License - -Copyright (c) 2021, Ajit -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -const normalized = { - 216: "O", - 223: "s", - 248: "o", - 273: "d", - 295: "h", - 305: "i", - 320: "l", - 322: "l", - 359: "t", - 383: "s", - 384: "b", - 385: "B", - 387: "b", - 390: "O", - 392: "c", - 393: "D", - 394: "D", - 396: "d", - 398: "E", - 400: "E", - 402: "f", - 403: "G", - 407: "I", - 409: "k", - 410: "l", - 412: "M", - 413: "N", - 414: "n", - 415: "O", - 421: "p", - 427: "t", - 429: "t", - 430: "T", - 434: "V", - 436: "y", - 438: "z", - 477: "e", - 485: "g", - 544: "N", - 545: "d", - 549: "z", - 564: "l", - 565: "n", - 566: "t", - 567: "j", - 570: "A", - 571: "C", - 572: "c", - 573: "L", - 574: "T", - 575: "s", - 576: "z", - 579: "B", - 580: "U", - 581: "V", - 582: "E", - 583: "e", - 584: "J", - 585: "j", - 586: "Q", - 587: "q", - 588: "R", - 589: "r", - 590: "Y", - 591: "y", - 592: "a", - 593: "a", - 595: "b", - 596: "o", - 597: "c", - 598: "d", - 599: "d", - 600: "e", - 603: "e", - 604: "e", - 605: "e", - 606: "e", - 607: "j", - 608: "g", - 609: "g", - 610: "G", - 613: "h", - 614: "h", - 616: "i", - 618: "I", - 619: "l", - 620: "l", - 621: "l", - 623: "m", - 624: "m", - 625: "m", - 626: "n", - 627: "n", - 628: "N", - 629: "o", - 633: "r", - 634: "r", - 635: "r", - 636: "r", - 637: "r", - 638: "r", - 639: "r", - 640: "R", - 641: "R", - 642: "s", - 647: "t", - 648: "t", - 649: "u", - 651: "v", - 652: "v", - 653: "w", - 654: "y", - 655: "Y", - 656: "z", - 657: "z", - 663: "c", - 665: "B", - 666: "e", - 667: "G", - 668: "H", - 669: "j", - 670: "k", - 671: "L", - 672: "q", - 686: "h", - 867: "a", - 868: "e", - 869: "i", - 870: "o", - 871: "u", - 872: "c", - 873: "d", - 874: "h", - 875: "m", - 876: "r", - 877: "t", - 878: "v", - 879: "x", - 7424: "A", - 7427: "B", - 7428: "C", - 7429: "D", - 7431: "E", - 7432: "e", - 7433: "i", - 7434: "J", - 7435: "K", - 7436: "L", - 7437: "M", - 7438: "N", - 7439: "O", - 7440: "O", - 7441: "o", - 7442: "o", - 7443: "o", - 7446: "o", - 7447: "o", - 7448: "P", - 7449: "R", - 7450: "R", - 7451: "T", - 7452: "U", - 7453: "u", - 7454: "u", - 7455: "m", - 7456: "V", - 7457: "W", - 7458: "Z", - 7522: "i", - 7523: "r", - 7524: "u", - 7525: "v", - 7834: "a", - 7835: "s", - 8305: "i", - 8341: "h", - 8342: "k", - 8343: "l", - 8344: "m", - 8345: "n", - 8346: "p", - 8347: "s", - 8348: "t", - 8580: "c" -}; -for (let i = "\u0300".codePointAt(0); i <= "\u036F".codePointAt(0); ++i) { - const diacritic = String.fromCodePoint(i); - for (const asciiChar of "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") { - const withDiacritic = (asciiChar + diacritic).normalize(); - const withDiacriticCodePoint = withDiacritic.codePointAt(0); - if (withDiacriticCodePoint > 126) { - normalized[withDiacriticCodePoint] = asciiChar; - } - } -} -const ranges = { - a: [7844, 7863], - e: [7870, 7879], - o: [7888, 7907], - u: [7912, 7921] -}; -for (const lowerChar of Object.keys(ranges)) { - const upperChar = lowerChar.toUpperCase(); - for (let i = ranges[lowerChar][0]; i <= ranges[lowerChar][1]; ++i) { - normalized[i] = i % 2 === 0 ? upperChar : lowerChar; - } -} -function normalizeRune(rune) { - if (rune < 192 || rune > 8580) { - return rune; - } - const normalizedChar = normalized[rune]; - if (normalizedChar !== void 0) - return normalizedChar.codePointAt(0); - return rune; -} -function toShort(number) { - return number; -} -function toInt(number) { - return number; -} -function maxInt16(num1, num2) { - return num1 > num2 ? num1 : num2; -} -const strToRunes = (str) => str.split("").map((s) => s.codePointAt(0)); -const runesToStr = (runes) => runes.map((r) => String.fromCodePoint(r)).join(""); -const whitespaceRunes = new Set( - " \f\n\r \v\xA0\u1680\u2028\u2029\u202F\u205F\u3000\uFEFF".split("").map((v) => v.codePointAt(0)) -); -for (let codePoint = "\u2000".codePointAt(0); codePoint <= "\u200A".codePointAt(0); codePoint++) { - whitespaceRunes.add(codePoint); -} -const isWhitespace = (rune) => whitespaceRunes.has(rune); -const whitespacesAtStart = (runes) => { - let whitespaces = 0; - for (const rune of runes) { - if (isWhitespace(rune)) - whitespaces++; - else - break; - } - return whitespaces; -}; -const whitespacesAtEnd = (runes) => { - let whitespaces = 0; - for (let i = runes.length - 1; i >= 0; i--) { - if (isWhitespace(runes[i])) - whitespaces++; - else - break; - } - return whitespaces; -}; -const MAX_ASCII = "\x7F".codePointAt(0); -const CAPITAL_A_RUNE = "A".codePointAt(0); -const CAPITAL_Z_RUNE = "Z".codePointAt(0); -const SMALL_A_RUNE = "a".codePointAt(0); -const SMALL_Z_RUNE = "z".codePointAt(0); -const NUMERAL_ZERO_RUNE = "0".codePointAt(0); -const NUMERAL_NINE_RUNE = "9".codePointAt(0); -function indexAt(index, max, forward) { - if (forward) { - return index; - } - return max - index - 1; -} -const SCORE_MATCH = 16, SCORE_GAP_START = -3, SCORE_GAP_EXTENTION = -1, BONUS_BOUNDARY = SCORE_MATCH / 2, BONUS_NON_WORD = SCORE_MATCH / 2, BONUS_CAMEL_123 = BONUS_BOUNDARY + SCORE_GAP_EXTENTION, BONUS_CONSECUTIVE = -(SCORE_GAP_START + SCORE_GAP_EXTENTION), BONUS_FIRST_CHAR_MULTIPLIER = 2; -function createPosSet(withPos) { - if (withPos) { - return /* @__PURE__ */ new Set(); - } - return null; -} -function alloc16(offset, slab2, size) { - if (slab2 !== null && slab2.i16.length > offset + size) { - const subarray = slab2.i16.subarray(offset, offset + size); - return [offset + size, subarray]; - } - return [offset, new Int16Array(size)]; -} -function alloc32(offset, slab2, size) { - if (slab2 !== null && slab2.i32.length > offset + size) { - const subarray = slab2.i32.subarray(offset, offset + size); - return [offset + size, subarray]; - } - return [offset, new Int32Array(size)]; -} -function charClassOfAscii(rune) { - if (rune >= SMALL_A_RUNE && rune <= SMALL_Z_RUNE) { - return 1; - } else if (rune >= CAPITAL_A_RUNE && rune <= CAPITAL_Z_RUNE) { - return 2; - } else if (rune >= NUMERAL_ZERO_RUNE && rune <= NUMERAL_NINE_RUNE) { - return 4; - } else { - return 0; - } -} -function charClassOfNonAscii(rune) { - const char = String.fromCodePoint(rune); - if (char !== char.toUpperCase()) { - return 1; - } else if (char !== char.toLowerCase()) { - return 2; - } else if (char.match(/\p{Number}/gu) !== null) { - return 4; - } else if (char.match(/\p{Letter}/gu) !== null) { - return 3; - } - return 0; -} -function charClassOf(rune) { - if (rune <= MAX_ASCII) { - return charClassOfAscii(rune); - } - return charClassOfNonAscii(rune); -} -function bonusFor(prevClass, currClass) { - if (prevClass === 0 && currClass !== 0) { - return BONUS_BOUNDARY; - } else if (prevClass === 1 && currClass === 2 || prevClass !== 4 && currClass === 4) { - return BONUS_CAMEL_123; - } else if (currClass === 0) { - return BONUS_NON_WORD; - } - return 0; -} -function bonusAt(input, idx) { - if (idx === 0) { - return BONUS_BOUNDARY; - } - return bonusFor(charClassOf(input[idx - 1]), charClassOf(input[idx])); -} -function trySkip(input, caseSensitive, char, from) { - let rest = input.slice(from); - let idx = rest.indexOf(char); - if (idx === 0) { - return from; - } - if (!caseSensitive && char >= SMALL_A_RUNE && char <= SMALL_Z_RUNE) { - if (idx > 0) { - rest = rest.slice(0, idx); - } - const uidx = rest.indexOf(char - 32); - if (uidx >= 0) { - idx = uidx; - } - } - if (idx < 0) { - return -1; - } - return from + idx; -} -function isAscii(runes) { - for (const rune of runes) { - if (rune >= 128) { - return false; - } - } - return true; -} -function asciiFuzzyIndex(input, pattern, caseSensitive) { - if (!isAscii(input)) { - return 0; - } - if (!isAscii(pattern)) { - return -1; - } - let firstIdx = 0, idx = 0; - for (let pidx = 0; pidx < pattern.length; pidx++) { - idx = trySkip(input, caseSensitive, pattern[pidx], idx); - if (idx < 0) { - return -1; - } - if (pidx === 0 && idx > 0) { - firstIdx = idx - 1; - } - idx++; - } - return firstIdx; -} -const fuzzyMatchV2 = (caseSensitive, normalize, forward, input, pattern, withPos, slab2) => { - const M = pattern.length; - if (M === 0) { - return [{ start: 0, end: 0, score: 0 }, createPosSet(withPos)]; - } - const N = input.length; - if (slab2 !== null && N * M > slab2.i16.length) { - return fuzzyMatchV1(caseSensitive, normalize, forward, input, pattern, withPos); - } - const idx = asciiFuzzyIndex(input, pattern, caseSensitive); - if (idx < 0) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - let offset16 = 0, offset32 = 0, H0 = null, C0 = null, B = null, F = null; - [offset16, H0] = alloc16(offset16, slab2, N); - [offset16, C0] = alloc16(offset16, slab2, N); - [offset16, B] = alloc16(offset16, slab2, N); - [offset32, F] = alloc32(offset32, slab2, M); - const [, T] = alloc32(offset32, slab2, N); - for (let i = 0; i < T.length; i++) { - T[i] = input[i]; - } - let maxScore = toShort(0), maxScorePos = 0; - let pidx = 0, lastIdx = 0; - const pchar0 = pattern[0]; - let pchar = pattern[0], prevH0 = toShort(0), prevCharClass = 0, inGap = false; - let Tsub = T.subarray(idx); - let H0sub = H0.subarray(idx).subarray(0, Tsub.length), C0sub = C0.subarray(idx).subarray(0, Tsub.length), Bsub = B.subarray(idx).subarray(0, Tsub.length); - for (let [off, char] of Tsub.entries()) { - let charClass = null; - if (char <= MAX_ASCII) { - charClass = charClassOfAscii(char); - if (!caseSensitive && charClass === 2) { - char += 32; - } - } else { - charClass = charClassOfNonAscii(char); - if (!caseSensitive && charClass === 2) { - char = String.fromCodePoint(char).toLowerCase().codePointAt(0); - } - if (normalize) { - char = normalizeRune(char); - } - } - Tsub[off] = char; - const bonus = bonusFor(prevCharClass, charClass); - Bsub[off] = bonus; - prevCharClass = charClass; - if (char === pchar) { - if (pidx < M) { - F[pidx] = toInt(idx + off); - pidx++; - pchar = pattern[Math.min(pidx, M - 1)]; - } - lastIdx = idx + off; - } - if (char === pchar0) { - const score = SCORE_MATCH + bonus * BONUS_FIRST_CHAR_MULTIPLIER; - H0sub[off] = score; - C0sub[off] = 1; - if (M === 1 && (forward && score > maxScore || !forward && score >= maxScore)) { - maxScore = score; - maxScorePos = idx + off; - if (forward && bonus === BONUS_BOUNDARY) { - break; - } - } - inGap = false; - } else { - if (inGap) { - H0sub[off] = maxInt16(prevH0 + SCORE_GAP_EXTENTION, 0); - } else { - H0sub[off] = maxInt16(prevH0 + SCORE_GAP_START, 0); - } - C0sub[off] = 0; - inGap = true; - } - prevH0 = H0sub[off]; - } - if (pidx !== M) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - if (M === 1) { - const result = { - start: maxScorePos, - end: maxScorePos + 1, - score: maxScore - }; - if (!withPos) { - return [result, null]; - } - const pos2 = /* @__PURE__ */ new Set(); - pos2.add(maxScorePos); - return [result, pos2]; - } - const f0 = F[0]; - const width = lastIdx - f0 + 1; - let H = null; - [offset16, H] = alloc16(offset16, slab2, width * M); - { - const toCopy = H0.subarray(f0, lastIdx + 1); - for (const [i, v] of toCopy.entries()) { - H[i] = v; - } - } - let [, C] = alloc16(offset16, slab2, width * M); - { - const toCopy = C0.subarray(f0, lastIdx + 1); - for (const [i, v] of toCopy.entries()) { - C[i] = v; - } - } - const Fsub = F.subarray(1); - const Psub = pattern.slice(1).slice(0, Fsub.length); - for (const [off, f] of Fsub.entries()) { - let inGap2 = false; - const pchar2 = Psub[off], pidx2 = off + 1, row = pidx2 * width, Tsub2 = T.subarray(f, lastIdx + 1), Bsub2 = B.subarray(f).subarray(0, Tsub2.length), Csub = C.subarray(row + f - f0).subarray(0, Tsub2.length), Cdiag = C.subarray(row + f - f0 - 1 - width).subarray(0, Tsub2.length), Hsub = H.subarray(row + f - f0).subarray(0, Tsub2.length), Hdiag = H.subarray(row + f - f0 - 1 - width).subarray(0, Tsub2.length), Hleft = H.subarray(row + f - f0 - 1).subarray(0, Tsub2.length); - Hleft[0] = 0; - for (const [off2, char] of Tsub2.entries()) { - const col = off2 + f; - let s1 = 0, s2 = 0, consecutive = 0; - if (inGap2) { - s2 = Hleft[off2] + SCORE_GAP_EXTENTION; - } else { - s2 = Hleft[off2] + SCORE_GAP_START; - } - if (pchar2 === char) { - s1 = Hdiag[off2] + SCORE_MATCH; - let b = Bsub2[off2]; - consecutive = Cdiag[off2] + 1; - if (b === BONUS_BOUNDARY) { - consecutive = 1; - } else if (consecutive > 1) { - b = maxInt16(b, maxInt16(BONUS_CONSECUTIVE, B[col - consecutive + 1])); - } - if (s1 + b < s2) { - s1 += Bsub2[off2]; - consecutive = 0; - } else { - s1 += b; - } - } - Csub[off2] = consecutive; - inGap2 = s1 < s2; - const score = maxInt16(maxInt16(s1, s2), 0); - if (pidx2 === M - 1 && (forward && score > maxScore || !forward && score >= maxScore)) { - maxScore = score; - maxScorePos = col; - } - Hsub[off2] = score; - } - } - const pos = createPosSet(withPos); - let j = f0; - if (withPos && pos !== null) { - let i = M - 1; - j = maxScorePos; - let preferMatch = true; - while (true) { - const I = i * width, j0 = j - f0, s = H[I + j0]; - let s1 = 0, s2 = 0; - if (i > 0 && j >= F[i]) { - s1 = H[I - width + j0 - 1]; - } - if (j > F[i]) { - s2 = H[I + j0 - 1]; - } - if (s > s1 && (s > s2 || s === s2 && preferMatch)) { - pos.add(j); - if (i === 0) { - break; - } - i--; - } - preferMatch = C[I + j0] > 1 || I + width + j0 + 1 < C.length && C[I + width + j0 + 1] > 0; - j--; - } - } - return [{ start: j, end: maxScorePos + 1, score: maxScore }, pos]; -}; -function calculateScore(caseSensitive, normalize, text, pattern, sidx, eidx, withPos) { - let pidx = 0, score = 0, inGap = false, consecutive = 0, firstBonus = toShort(0); - const pos = createPosSet(withPos); - let prevCharClass = 0; - if (sidx > 0) { - prevCharClass = charClassOf(text[sidx - 1]); - } - for (let idx = sidx; idx < eidx; idx++) { - let rune = text[idx]; - const charClass = charClassOf(rune); - if (!caseSensitive) { - if (rune >= CAPITAL_A_RUNE && rune <= CAPITAL_Z_RUNE) { - rune += 32; - } else if (rune > MAX_ASCII) { - rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0); - } - } - if (normalize) { - rune = normalizeRune(rune); - } - if (rune === pattern[pidx]) { - if (withPos && pos !== null) { - pos.add(idx); - } - score += SCORE_MATCH; - let bonus = bonusFor(prevCharClass, charClass); - if (consecutive === 0) { - firstBonus = bonus; - } else { - if (bonus === BONUS_BOUNDARY) { - firstBonus = bonus; - } - bonus = maxInt16(maxInt16(bonus, firstBonus), BONUS_CONSECUTIVE); - } - if (pidx === 0) { - score += bonus * BONUS_FIRST_CHAR_MULTIPLIER; - } else { - score += bonus; - } - inGap = false; - consecutive++; - pidx++; - } else { - if (inGap) { - score += SCORE_GAP_EXTENTION; - } else { - score += SCORE_GAP_START; - } - inGap = true; - consecutive = 0; - firstBonus = 0; - } - prevCharClass = charClass; - } - return [score, pos]; -} -function fuzzyMatchV1(caseSensitive, normalize, forward, text, pattern, withPos, slab2) { - if (pattern.length === 0) { - return [{ start: 0, end: 0, score: 0 }, null]; - } - if (asciiFuzzyIndex(text, pattern, caseSensitive) < 0) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - let pidx = 0, sidx = -1, eidx = -1; - const lenRunes = text.length; - const lenPattern = pattern.length; - for (let index = 0; index < lenRunes; index++) { - let rune = text[indexAt(index, lenRunes, forward)]; - if (!caseSensitive) { - if (rune >= CAPITAL_A_RUNE && rune <= CAPITAL_Z_RUNE) { - rune += 32; - } else if (rune > MAX_ASCII) { - rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0); - } - } - if (normalize) { - rune = normalizeRune(rune); - } - const pchar = pattern[indexAt(pidx, lenPattern, forward)]; - if (rune === pchar) { - if (sidx < 0) { - sidx = index; - } - pidx++; - if (pidx === lenPattern) { - eidx = index + 1; - break; - } - } - } - if (sidx >= 0 && eidx >= 0) { - pidx--; - for (let index = eidx - 1; index >= sidx; index--) { - const tidx = indexAt(index, lenRunes, forward); - let rune = text[tidx]; - if (!caseSensitive) { - if (rune >= CAPITAL_A_RUNE && rune <= CAPITAL_Z_RUNE) { - rune += 32; - } else if (rune > MAX_ASCII) { - rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0); - } - } - const pidx_ = indexAt(pidx, lenPattern, forward); - const pchar = pattern[pidx_]; - if (rune === pchar) { - pidx--; - if (pidx < 0) { - sidx = index; - break; - } - } - } - if (!forward) { - const sidxTemp = sidx; - sidx = lenRunes - eidx; - eidx = lenRunes - sidxTemp; - } - const [score, pos] = calculateScore( - caseSensitive, - normalize, - text, - pattern, - sidx, - eidx, - withPos - ); - return [{ start: sidx, end: eidx, score }, pos]; - } - return [{ start: -1, end: -1, score: 0 }, null]; -}; -const exactMatchNaive = (caseSensitive, normalize, forward, text, pattern, withPos, slab2) => { - if (pattern.length === 0) { - return [{ start: 0, end: 0, score: 0 }, null]; - } - const lenRunes = text.length; - const lenPattern = pattern.length; - if (lenRunes < lenPattern) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - if (asciiFuzzyIndex(text, pattern, caseSensitive) < 0) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - let pidx = 0; - let bestPos = -1, bonus = toShort(0), bestBonus = toShort(-1); - for (let index = 0; index < lenRunes; index++) { - const index_ = indexAt(index, lenRunes, forward); - let rune = text[index_]; - if (!caseSensitive) { - if (rune >= CAPITAL_A_RUNE && rune <= CAPITAL_Z_RUNE) { - rune += 32; - } else if (rune > MAX_ASCII) { - rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0); - } - } - if (normalize) { - rune = normalizeRune(rune); - } - const pidx_ = indexAt(pidx, lenPattern, forward); - const pchar = pattern[pidx_]; - if (pchar === rune) { - if (pidx_ === 0) { - bonus = bonusAt(text, index_); - } - pidx++; - if (pidx === lenPattern) { - if (bonus > bestBonus) { - bestPos = index; - bestBonus = bonus; - } - if (bonus === BONUS_BOUNDARY) { - break; - } - index -= pidx - 1; - pidx = 0; - bonus = 0; - } - } else { - index -= pidx; - pidx = 0; - bonus = 0; - } - } - if (bestPos >= 0) { - let sidx = 0, eidx = 0; - if (forward) { - sidx = bestPos - lenPattern + 1; - eidx = bestPos + 1; - } else { - sidx = lenRunes - (bestPos + 1); - eidx = lenRunes - (bestPos - lenPattern + 1); - } - const [score] = calculateScore(caseSensitive, normalize, text, pattern, sidx, eidx, false); - return [{ start: sidx, end: eidx, score }, null]; - } - return [{ start: -1, end: -1, score: 0 }, null]; -}; -const prefixMatch = (caseSensitive, normalize, forward, text, pattern, withPos, slab2) => { - if (pattern.length === 0) { - return [{ start: 0, end: 0, score: 0 }, null]; - } - let trimmedLen = 0; - if (!isWhitespace(pattern[0])) { - trimmedLen = whitespacesAtStart(text); - } - if (text.length - trimmedLen < pattern.length) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - for (const [index, r] of pattern.entries()) { - let rune = text[trimmedLen + index]; - if (!caseSensitive) { - rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0); - } - if (normalize) { - rune = normalizeRune(rune); - } - if (rune !== r) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - } - const lenPattern = pattern.length; - const [score] = calculateScore( - caseSensitive, - normalize, - text, - pattern, - trimmedLen, - trimmedLen + lenPattern, - false - ); - return [{ start: trimmedLen, end: trimmedLen + lenPattern, score }, null]; -}; -const suffixMatch = (caseSensitive, normalize, forward, text, pattern, withPos, slab2) => { - const lenRunes = text.length; - let trimmedLen = lenRunes; - if (pattern.length === 0 || !isWhitespace(pattern[pattern.length - 1])) { - trimmedLen -= whitespacesAtEnd(text); - } - if (pattern.length === 0) { - return [{ start: trimmedLen, end: trimmedLen, score: 0 }, null]; - } - const diff = trimmedLen - pattern.length; - if (diff < 0) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - for (const [index, r] of pattern.entries()) { - let rune = text[index + diff]; - if (!caseSensitive) { - rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0); - } - if (normalize) { - rune = normalizeRune(rune); - } - if (rune !== r) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - } - const lenPattern = pattern.length; - const sidx = trimmedLen - lenPattern; - const eidx = trimmedLen; - const [score] = calculateScore(caseSensitive, normalize, text, pattern, sidx, eidx, false); - return [{ start: sidx, end: eidx, score }, null]; -}; -const equalMatch = (caseSensitive, normalize, forward, text, pattern, withPos, slab2) => { - const lenPattern = pattern.length; - if (lenPattern === 0) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - let trimmedLen = 0; - if (!isWhitespace(pattern[0])) { - trimmedLen = whitespacesAtStart(text); - } - let trimmedEndLen = 0; - if (!isWhitespace(pattern[lenPattern - 1])) { - trimmedEndLen = whitespacesAtEnd(text); - } - if (text.length - trimmedLen - trimmedEndLen != lenPattern) { - return [{ start: -1, end: -1, score: 0 }, null]; - } - let match = true; - if (normalize) { - const runes = text; - for (const [idx, pchar] of pattern.entries()) { - let rune = runes[trimmedLen + idx]; - if (!caseSensitive) { - rune = String.fromCodePoint(rune).toLowerCase().codePointAt(0); - } - if (normalizeRune(pchar) !== normalizeRune(rune)) { - match = false; - break; - } - } - } else { - let runesStr = runesToStr(text).substring(trimmedLen, text.length - trimmedEndLen); - if (!caseSensitive) { - runesStr = runesStr.toLowerCase(); - } - match = runesStr === runesToStr(pattern); - } - if (match) { - return [ - { - start: trimmedLen, - end: trimmedLen + lenPattern, - score: (SCORE_MATCH + BONUS_BOUNDARY) * lenPattern + (BONUS_FIRST_CHAR_MULTIPLIER - 1) * BONUS_BOUNDARY - }, - null - ]; - } - return [{ start: -1, end: -1, score: 0 }, null]; -}; -const SLAB_16_SIZE = 100 * 1024; -const SLAB_32_SIZE = 2048; -function makeSlab(size16, size32) { - return { - i16: new Int16Array(size16), - i32: new Int32Array(size32) - }; -} -const slab = makeSlab(SLAB_16_SIZE, SLAB_32_SIZE); -var TermType = /* @__PURE__ */ ((TermType2) => { - TermType2[TermType2["Fuzzy"] = 0] = "Fuzzy"; - TermType2[TermType2["Exact"] = 1] = "Exact"; - TermType2[TermType2["Prefix"] = 2] = "Prefix"; - TermType2[TermType2["Suffix"] = 3] = "Suffix"; - TermType2[TermType2["Equal"] = 4] = "Equal"; - return TermType2; -})(TermType || {}); -const termTypeMap = { - [0]: fuzzyMatchV2, - [1]: exactMatchNaive, - [2]: prefixMatch, - [3]: suffixMatch, - [4]: equalMatch -}; -function buildPatternForExtendedMatch(fuzzy, caseMode, normalize, str) { - let cacheable = true; - str = str.trimLeft(); - { - const trimmedAtRightStr = str.trimRight(); - if (trimmedAtRightStr.endsWith("\\") && str[trimmedAtRightStr.length] === " ") { - str = trimmedAtRightStr + " "; - } else { - str = trimmedAtRightStr; - } - } - let sortable = false; - let termSets = []; - termSets = parseTerms(fuzzy, caseMode, normalize, str); - Loop: - for (const termSet of termSets) { - for (const [idx, term] of termSet.entries()) { - if (!term.inv) { - sortable = true; - } - if (!cacheable || idx > 0 || term.inv || fuzzy && term.typ !== 0 || !fuzzy && term.typ !== 1) { - cacheable = false; - if (sortable) { - break Loop; - } - } - } - } - return { - str, - termSets, - sortable, - cacheable, - fuzzy - }; -} -function parseTerms(fuzzy, caseMode, normalize, str) { - str = str.replace(/\\ /g, " "); - const tokens = str.split(/ +/); - const sets = []; - let set = []; - let switchSet = false; - let afterBar = false; - for (const token of tokens) { - let typ = 0, inv = false, text = token.replace(/\t/g, " "); - const lowerText = text.toLowerCase(); - const caseSensitive = caseMode === "case-sensitive" || caseMode === "smart-case" && text !== lowerText; - const normalizeTerm = normalize && lowerText === runesToStr(strToRunes(lowerText).map(normalizeRune)); - if (!caseSensitive) { - text = lowerText; - } - if (!fuzzy) { - typ = 1; - } - if (set.length > 0 && !afterBar && text === "|") { - switchSet = false; - afterBar = true; - continue; - } - afterBar = false; - if (text.startsWith("!")) { - inv = true; - typ = 1; - text = text.substring(1); - } - if (text !== "$" && text.endsWith("$")) { - typ = 3; - text = text.substring(0, text.length - 1); - } - if (text.startsWith("'")) { - if (fuzzy && !inv) { - typ = 1; - } else { - typ = 0; - } - text = text.substring(1); - } else if (text.startsWith("^")) { - if (typ === 3) { - typ = 4; - } else { - typ = 2; - } - text = text.substring(1); - } - if (text.length > 0) { - if (switchSet) { - sets.push(set); - set = []; - } - let textRunes = strToRunes(text); - if (normalizeTerm) { - textRunes = textRunes.map(normalizeRune); - } - set.push({ - typ, - inv, - text: textRunes, - caseSensitive, - normalize: normalizeTerm - }); - switchSet = true; - } - } - if (set.length > 0) { - sets.push(set); - } - return sets; -} -const buildPatternForBasicMatch = (query, casing, normalize) => { - let caseSensitive = false; - switch (casing) { - case "smart-case": - if (query.toLowerCase() !== query) { - caseSensitive = true; - } - break; - case "case-sensitive": - caseSensitive = true; - break; - case "case-insensitive": - query = query.toLowerCase(); - caseSensitive = false; - break; - } - let queryRunes = strToRunes(query); - if (normalize) { - queryRunes = queryRunes.map(normalizeRune); - } - return { - queryRunes, - caseSensitive - }; -}; -function iter(algoFn, tokens, caseSensitive, normalize, forward, pattern, slab2) { - for (const part of tokens) { - const [res, pos] = algoFn(caseSensitive, normalize, forward, part.text, pattern, true, slab2); - if (res.start >= 0) { - const sidx = res.start + part.prefixLength; - const eidx = res.end + part.prefixLength; - if (pos !== null) { - const newPos = /* @__PURE__ */ new Set(); - pos.forEach((v) => newPos.add(part.prefixLength + v)); - return [[sidx, eidx], res.score, newPos]; - } - return [[sidx, eidx], res.score, pos]; - } - } - return [[-1, -1], 0, null]; -} -function computeExtendedMatch(text, pattern, fuzzyAlgo, forward) { - const input = [ - { - text, - prefixLength: 0 - } - ]; - const offsets = []; - let totalScore = 0; - const allPos = /* @__PURE__ */ new Set(); - for (const termSet of pattern.termSets) { - let offset = [0, 0]; - let currentScore = 0; - let matched = false; - for (const term of termSet) { - let algoFn = termTypeMap[term.typ]; - if (term.typ === TermType.Fuzzy) { - algoFn = fuzzyAlgo; - } - const [off, score, pos] = iter( - algoFn, - input, - term.caseSensitive, - term.normalize, - forward, - term.text, - slab - ); - const sidx = off[0]; - if (sidx >= 0) { - if (term.inv) { - continue; - } - offset = off; - currentScore = score; - matched = true; - if (pos !== null) { - pos.forEach((v) => allPos.add(v)); - } else { - for (let idx = off[0]; idx < off[1]; ++idx) { - allPos.add(idx); - } - } - break; - } else if (term.inv) { - offset = [0, 0]; - currentScore = 0; - matched = true; - continue; - } - } - if (matched) { - offsets.push(offset); - totalScore += currentScore; - } - } - return { offsets, totalScore, allPos }; -} -function getResultFromScoreMap(scoreMap, limit) { - const scoresInDesc = Object.keys(scoreMap).map((v) => parseInt(v, 10)).sort((a, b) => b - a); - let result = []; - for (const score of scoresInDesc) { - result = result.concat(scoreMap[score]); - if (result.length >= limit) { - break; - } - } - return result; -} -function getBasicMatchIter(scoreMap, queryRunes, caseSensitive) { - return (idx) => { - const itemRunes = this.runesList[idx]; - if (queryRunes.length > itemRunes.length) - return; - let [match, positions] = this.algoFn( - caseSensitive, - this.opts.normalize, - this.opts.forward, - itemRunes, - queryRunes, - true, - slab - ); - if (match.start === -1) - return; - if (this.opts.fuzzy === false) { - positions = /* @__PURE__ */ new Set(); - for (let position = match.start; position < match.end; ++position) { - positions.add(position); - } - } - const scoreKey = this.opts.sort ? match.score : 0; - if (scoreMap[scoreKey] === void 0) { - scoreMap[scoreKey] = []; - } - scoreMap[scoreKey].push(Object.assign({ - item: this.items[idx], - positions: positions != null ? positions : /* @__PURE__ */ new Set() - }, match)); - }; -} -function getExtendedMatchIter(scoreMap, pattern) { - return (idx) => { - const runes = this.runesList[idx]; - const match = computeExtendedMatch(runes, pattern, this.algoFn, this.opts.forward); - if (match.offsets.length !== pattern.termSets.length) - return; - let sidx = -1, eidx = -1; - if (match.allPos.size > 0) { - sidx = Math.min(...match.allPos); - eidx = Math.max(...match.allPos) + 1; - } - const scoreKey = this.opts.sort ? match.totalScore : 0; - if (scoreMap[scoreKey] === void 0) { - scoreMap[scoreKey] = []; - } - scoreMap[scoreKey].push({ - score: match.totalScore, - item: this.items[idx], - positions: match.allPos, - start: sidx, - end: eidx - }); - }; -} -function basicMatch(query) { - const { queryRunes, caseSensitive } = buildPatternForBasicMatch( - query, - this.opts.casing, - this.opts.normalize - ); - const scoreMap = {}; - const iter2 = getBasicMatchIter.bind(this)( - scoreMap, - queryRunes, - caseSensitive - ); - for (let i = 0, len = this.runesList.length; i < len; ++i) { - iter2(i); - } - return getResultFromScoreMap(scoreMap, this.opts.limit); -} -function extendedMatch(query) { - const pattern = buildPatternForExtendedMatch( - Boolean(this.opts.fuzzy), - this.opts.casing, - this.opts.normalize, - query - ); - const scoreMap = {}; - const iter2 = getExtendedMatchIter.bind(this)(scoreMap, pattern); - for (let i = 0, len = this.runesList.length; i < len; ++i) { - iter2(i); - } - return getResultFromScoreMap(scoreMap, this.opts.limit); -} -const defaultOpts = { - limit: Infinity, - selector: (v) => v, - casing: "smart-case", - normalize: true, - fuzzy: "v2", - tiebreakers: [], - sort: true, - forward: true, - match: basicMatch -}; -class Finder { - constructor(list, ...optionsTuple) { - this.opts = Object.assign(defaultOpts, optionsTuple[0]); - this.items = list; - this.runesList = list.map((item) => strToRunes(this.opts.selector(item).normalize())); - this.algoFn = exactMatchNaive; - switch (this.opts.fuzzy) { - case "v2": - this.algoFn = fuzzyMatchV2; - break; - case "v1": - this.algoFn = fuzzyMatchV1; - break; - } - } - find(query) { - if (query.length === 0 || this.items.length === 0) - return this.items.slice(0, this.opts.limit).map(createResultItemWithEmptyPos); - query = query.normalize(); - let result = this.opts.match.bind(this)(query); - return postProcessResultItems(result, this.opts); - } -} -function createResultItemWithEmptyPos(item) { - return ({ - item, - start: -1, - end: -1, - score: 0, - positions: /* @__PURE__ */ new Set() - }) -}; -function postProcessResultItems(result, opts) { - if (opts.sort) { - const { selector } = opts; - result.sort((a, b) => { - if (a.score === b.score) { - for (const tiebreaker of opts.tiebreakers) { - const diff = tiebreaker(a, b, selector); - if (diff !== 0) { - return diff; - } - } - } - return 0; - }); - } - if (Number.isFinite(opts.limit)) { - result.splice(opts.limit); - } - return result; -} -function byLengthAsc(a, b, selector) { - return selector(a.item).length - selector(b.item).length; -} -function byStartAsc(a, b) { - return a.start - b.start; -}