sidebar: android-style quick toggles & sliders (#2217)

Co-Authored-By: Vague Syntax <173799252+vaguesyntax@users.noreply.github.com>
This commit is contained in:
end-4
2025-10-18 19:07:10 +02:00
parent 77ae119d32
commit 5d1a9b1e9c
35 changed files with 1895 additions and 90 deletions
@@ -352,7 +352,7 @@ Singleton {
property JsonObject search: JsonObject {
property int nonAppResultDelay: 30 // This prevents lagging when typing
property string engineBaseUrl: "https://www.google.com/search?q="
property list<string> excludedSites: ["quora.com"]
property list<string> excludedSites: ["quora.com", "facebook.com"]
property bool sloppy: false // Uses levenshtein distance based scoring instead of fuzzy sort. Very weird.
property JsonObject prefix: JsonObject {
property bool showDefaultActionsWithoutPrefix: true
@@ -393,6 +393,28 @@ Singleton {
property bool visualize: false
property bool clicklessCornerEnd: true
}
property JsonObject quickToggles: JsonObject {
property string style: "android" // Options: classic, android
property JsonObject android: JsonObject {
property int columns: 5
property list<var> toggles: [
{ type: "network", size: 2 },
{ type: "bluetooth", size: 2 },
{ type: "idleinhibitor", size: 1 },
{ type: "easyeffects", size: 1 },
{ type: "nightlight", size: 2 },
{ type: "darkmode", size: 2 }
]
}
}
property JsonObject quickSliders: JsonObject {
property bool enable: false
property bool showMic: false
property bool showVolume: true
property bool showBrightness: true
}
}
property JsonObject time: JsonObject {
@@ -14,6 +14,7 @@ Rectangle {
property real spacing: 5
property real padding: 0
property int clickIndex: rowLayout.clickIndex
property int childrenCount: rowLayout.children.length
property real contentWidth: {
let total = 0;
@@ -22,13 +22,15 @@ Button {
property bool bounce: true
property real baseWidth: contentItem.implicitWidth + horizontalPadding * 2
property real baseHeight: contentItem.implicitHeight + verticalPadding * 2
property real clickedWidth: baseWidth + 20
property real clickedWidth: baseWidth + (isAtSide ? 10 : 20)
property real clickedHeight: baseHeight
property var parentGroup: root.parent
property int indexInParent: parentGroup?.children.indexOf(root) ?? 0
property int clickIndex: parentGroup?.clickIndex ?? -1
property bool isAtSide: indexInParent === 0 || indexInParent === (parentGroup?.childrenCount - 1)
Layout.fillWidth: (clickIndex - 1 <= parentGroup?.children.indexOf(root) && parentGroup?.children.indexOf(root) <= clickIndex + 1)
Layout.fillHeight: (clickIndex - 1 <= parentGroup?.children.indexOf(root) && parentGroup?.children.indexOf(root) <= clickIndex + 1)
Layout.fillWidth: (clickIndex - 1 <= indexInParent && indexInParent <= clickIndex + 1)
Layout.fillHeight: (clickIndex - 1 <= indexInParent && indexInParent <= clickIndex + 1)
implicitWidth: (root.down && bounce) ? clickedWidth : baseWidth
implicitHeight: (root.down && bounce) ? clickedHeight : baseHeight
@@ -599,6 +599,86 @@ ContentPage {
}
}
ContentSubsection {
title: Translation.tr("Quick toggles")
ConfigSelectionArray {
Layout.fillWidth: false
currentValue: Config.options.sidebar.quickToggles.style
onSelected: newValue => {
Config.options.sidebar.quickToggles.style = newValue;
}
options: [
{
displayName: Translation.tr("Classic"),
icon: "password_2",
value: "classic"
},
{
displayName: Translation.tr("Android"),
icon: "action_key",
value: "android"
}
]
}
ConfigSpinBox {
enabled: Config.options.sidebar.quickToggles.style === "android"
icon: "splitscreen_left"
text: Translation.tr("Columns")
value: Config.options.sidebar.quickToggles.android.columns
from: 1
to: 8
stepSize: 1
onValueChanged: {
Config.options.sidebar.quickToggles.android.columns = value;
}
}
}
ContentSubsection {
title: Translation.tr("Sliders")
ConfigSwitch {
buttonIcon: "check"
text: Translation.tr("Enable")
checked: Config.options.sidebar.quickSliders.enable
onCheckedChanged: {
Config.options.sidebar.quickSliders.enable = checked;
}
}
ConfigSwitch {
buttonIcon: "brightness_6"
text: Translation.tr("Brightness")
enabled: Config.options.sidebar.quickSliders.enable
checked: Config.options.sidebar.quickSliders.showBrightness
onCheckedChanged: {
Config.options.sidebar.quickSliders.showBrightness = checked;
}
}
ConfigSwitch {
buttonIcon: "volume_up"
text: Translation.tr("Volume")
enabled: Config.options.sidebar.quickSliders.enable
checked: Config.options.sidebar.quickSliders.showVolume
onCheckedChanged: {
Config.options.sidebar.quickSliders.showVolume = checked;
}
}
ConfigSwitch {
buttonIcon: "mic"
text: Translation.tr("Microphone")
enabled: Config.options.sidebar.quickSliders.enable
checked: Config.options.sidebar.quickSliders.showMic
onCheckedChanged: {
Config.options.sidebar.quickSliders.showMic = checked;
}
}
}
ContentSubsection {
title: Translation.tr("Corner open")
tooltip: Translation.tr("Allows you to open sidebars by clicking or hovering screen corners regardless of bar position")
@@ -0,0 +1,111 @@
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import Quickshell.Services.UPower
Rectangle {
id: root
property var screen: root.QsWindow.window?.screen
property var brightnessMonitor: Brightness.getMonitorForScreen(screen)
implicitWidth: contentItem.implicitWidth + root.horizontalPadding * 2
implicitHeight: contentItem.implicitHeight + root.verticalPadding * 2
radius: Appearance.rounding.normal
color: Appearance.colors.colLayer1
property real verticalPadding: 4
property real horizontalPadding: 12
Column {
id: contentItem
anchors {
fill: parent
leftMargin: root.horizontalPadding
rightMargin: root.horizontalPadding
topMargin: root.verticalPadding
bottomMargin: root.verticalPadding
}
Loader {
anchors {
left: parent.left
right: parent.right
}
visible: active
active: Config.options.sidebar.quickSliders.showBrightness
sourceComponent: QuickSlider {
materialSymbol: "brightness_6"
value: root.brightnessMonitor.brightness
onMoved: {
root.brightnessMonitor.setBrightness(value)
}
}
}
Loader {
anchors {
left: parent.left
right: parent.right
}
visible: active
active: Config.options.sidebar.quickSliders.showVolume
sourceComponent: QuickSlider {
materialSymbol: "volume_up"
value: Audio.sink.audio.volume
onMoved: {
Audio.sink.audio.volume = value
}
}
}
Loader {
anchors {
left: parent.left
right: parent.right
}
visible: active
active: Config.options.sidebar.quickSliders.showMic
sourceComponent: QuickSlider {
materialSymbol: "mic"
value: Audio.source.audio.volume
onMoved: {
Audio.source.audio.volume = value
}
}
}
}
component QuickSlider: StyledSlider {
id: quickSlider
required property string materialSymbol
configuration: StyledSlider.Configuration.M
stopIndicatorValues: []
MaterialSymbol {
id: icon
property bool nearFull: quickSlider.value >= 0.9
anchors {
verticalCenter: parent.verticalCenter
right: nearFull ? quickSlider.handle.right : parent.right
rightMargin: quickSlider.nearFull ? 14 : 8
}
iconSize: 20
color: nearFull ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
text: quickSlider.materialSymbol
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
Behavior on anchors.rightMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
}
}
}
@@ -2,9 +2,6 @@ import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import "./quickToggles/"
import "./wifiNetworks/"
import "./bluetoothDevices/"
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
@@ -12,6 +9,11 @@ import Quickshell
import Quickshell.Bluetooth
import Quickshell.Hyprland
import "./quickToggles/"
import "./quickToggles/classicStyle/"
import "./wifiNetworks/"
import "./bluetoothDevices/"
Item {
id: root
property int sidebarWidth: Appearance.sizes.sidebarWidth
@@ -19,6 +21,7 @@ Item {
property string settingsQmlPath: Quickshell.shellPath("settings.qml")
property bool showWifiDialog: false
property bool showBluetoothDialog: false
property bool editMode: false
Connections {
target: GlobalStates
@@ -52,94 +55,36 @@ Item {
anchors.margins: sidebarPadding
spacing: sidebarPadding
RowLayout {
SystemButtonRow {
Layout.fillHeight: false
spacing: 10
Layout.margins: 10
Layout.topMargin: 5
Layout.bottomMargin: 0
CustomIcon {
id: distroIcon
width: 25
height: 25
source: SystemInfo.distroIcon
colorize: true
color: Appearance.colors.colOnLayer0
}
StyledText {
font.pixelSize: Appearance.font.pixelSize.normal
color: Appearance.colors.colOnLayer0
text: Translation.tr("Up %1").arg(DateTime.uptime)
textFormat: Text.MarkdownText
}
Item {
Layout.fillWidth: true
}
ButtonGroup {
QuickToggleButton {
toggled: false
buttonIcon: "restart_alt"
onClicked: {
Hyprland.dispatch("reload");
Quickshell.reload(true);
}
StyledToolTip {
text: Translation.tr("Reload Hyprland & Quickshell")
}
}
QuickToggleButton {
toggled: false
buttonIcon: "settings"
onClicked: {
GlobalStates.sidebarRightOpen = false;
Quickshell.execDetached(["qs", "-p", root.settingsQmlPath]);
}
StyledToolTip {
text: Translation.tr("Settings")
}
}
QuickToggleButton {
toggled: false
buttonIcon: "power_settings_new"
onClicked: {
GlobalStates.sessionOpen = true;
}
StyledToolTip {
text: Translation.tr("Session")
}
}
}
}
ButtonGroup {
Layout.alignment: Qt.AlignHCenter
spacing: 5
padding: 5
color: Appearance.colors.colLayer1
Loader {
id: slidersLoader
Layout.fillWidth: true
visible: active
active: {
const configQuickSliders = Config.options.sidebar.quickSliders
if (!configQuickSliders.enable) return false
if (!configQuickSliders.showMic && !configQuickSliders.showVolume && !configQuickSliders.showBrightness) return false;
return true;
}
sourceComponent: QuickSliders {}
}
NetworkToggle {
altAction: () => {
Network.enableWifi();
Network.rescanWifi();
root.showWifiDialog = true;
}
LoaderedQuickPanelImplementation {
styleName: "classic"
sourceComponent: ClassicQuickPanel {}
}
LoaderedQuickPanelImplementation {
styleName: "android"
sourceComponent: AndroidQuickPanel {
editMode: root.editMode
}
BluetoothToggle {
altAction: () => {
Bluetooth.defaultAdapter.enabled = true;
Bluetooth.defaultAdapter.discovering = true;
root.showBluetoothDialog = true;
}
}
NightLight {}
GameMode {}
IdleInhibitor {}
EasyEffectsToggle {}
CloudflareWarp {}
}
CenterWidgetGroup {
@@ -207,4 +152,94 @@ Item {
}
}
}
component LoaderedQuickPanelImplementation: Loader {
id: quickPanelImplLoader
required property string styleName
Layout.alignment: item?.Layout.alignment ?? Qt.AlignHCenter
Layout.fillWidth: item?.Layout.fillWidth ?? false
visible: active
active: Config.options.sidebar.quickToggles.style === styleName
Connections {
target: quickPanelImplLoader.item
function onOpenWifiDialog() {
Network.enableWifi();
Network.rescanWifi();
root.showWifiDialog = true;
}
function onOpenBluetoothDialog() {
Bluetooth.defaultAdapter.enabled = true;
Bluetooth.defaultAdapter.discovering = true;
root.showBluetoothDialog = true;
}
}
}
component SystemButtonRow: RowLayout {
spacing: 10
CustomIcon {
id: distroIcon
width: 25
height: 25
source: SystemInfo.distroIcon
colorize: true
color: Appearance.colors.colOnLayer0
}
StyledText {
font.pixelSize: Appearance.font.pixelSize.normal
color: Appearance.colors.colOnLayer0
text: Translation.tr("Up %1").arg(DateTime.uptime)
textFormat: Text.MarkdownText
}
Item {
Layout.fillWidth: true
}
ButtonGroup {
QuickToggleButton {
toggled: root.editMode
visible: Config.options.sidebar.quickToggles.style === "android"
buttonIcon: "edit"
onClicked: root.editMode = !root.editMode
StyledToolTip {
text: Translation.tr("Edit quick toggles") + (root.editMode ? Translation.tr("\nLMB to enable/disable\nRMB to toggle size\nScroll to swap position") : "")
}
}
QuickToggleButton {
toggled: false
buttonIcon: "restart_alt"
onClicked: {
Hyprland.dispatch("reload");
Quickshell.reload(true);
}
StyledToolTip {
text: Translation.tr("Reload Hyprland & Quickshell")
}
}
QuickToggleButton {
toggled: false
buttonIcon: "settings"
onClicked: {
GlobalStates.sidebarRightOpen = false;
Quickshell.execDetached(["qs", "-p", root.settingsQmlPath]);
}
StyledToolTip {
text: Translation.tr("Settings")
}
}
QuickToggleButton {
toggled: false
buttonIcon: "power_settings_new"
onClicked: {
GlobalStates.sessionOpen = true;
}
StyledToolTip {
text: Translation.tr("Session")
}
}
}
}
}
@@ -0,0 +1,12 @@
import QtQuick
import qs.modules.common
Rectangle {
id: root
radius: Appearance.rounding.normal
color: Appearance.colors.colLayer1
signal openWifiDialog()
signal openBluetoothDialog()
}
@@ -0,0 +1,156 @@
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
import "./androidStyle/"
AbstractQuickPanel {
id: root
property bool editMode: false
Layout.fillWidth: true
implicitHeight: (editMode ? contentItem.implicitHeight : usedRows.implicitHeight) + root.padding * 2
Behavior on implicitHeight {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
}
property real spacing: 6
property real padding: 6
readonly property list<string> availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile"]
readonly property int columns: Config.options.sidebar.quickToggles.android.columns
readonly property list<var> toggles: Config.options.sidebar.quickToggles.android.toggles
readonly property list<var> toggleRows: toggleRowsForList(toggles)
readonly property list<var> unusedToggles: {
const types = availableToggleTypes.filter(type => !toggles.some(toggle => (toggle && toggle.type === type)))
return types.map(type => { return { type: type, size: 1 } })
}
readonly property list<var> unusedToggleRows: toggleRowsForList(unusedToggles)
readonly property real baseCellWidth: {
// This is the wrong calculation, but it looks correct in reality???
// (theoretically spacing should be multiplied by 1 column less)
const availableWidth = root.width - (root.padding * 2) - (root.spacing * (root.columns))
return availableWidth / root.columns
}
readonly property real baseCellHeight: 56
function toggleRowsForList(togglesList) {
var rows = [];
var row = [];
var totalSize = 0; // Total cols taken in current row
for (var i = 0; i < togglesList.length; i++) {
if (!togglesList[i]) continue;
if (totalSize + togglesList[i].size > columns) {
rows.push(row);
row = [];
totalSize = 0;
}
row.push(togglesList[i]);
totalSize += togglesList[i].size;
}
if (row.length > 0) {
rows.push(row);
}
return rows;
}
Column {
id: contentItem
anchors {
fill: parent
margins: root.padding
}
spacing: 12
Column {
id: usedRows
spacing: root.spacing
Repeater {
id: usedRowsRepeater
model: ScriptModel {
values: root.toggleRows
}
delegate: ButtonGroup {
id: toggleRow
required property var modelData
required property int index
property int startingIndex: {
const rows = usedRowsRepeater.model.values;
let sum = 0;
for (let i = 0; i < index; i++) {
sum += rows[i].length;
}
return sum;
}
spacing: root.spacing
Repeater {
model: ScriptModel {
values: toggleRow.modelData
}
delegate: AndroidToggleDelegateChooser {
startingIndex: toggleRow.startingIndex
editMode: root.editMode
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
spacing: root.spacing
onOpenWifiDialog: root.openWifiDialog()
onOpenBluetoothDialog: root.openBluetoothDialog()
}
}
}
}
}
FadeLoader {
shown: root.editMode
anchors {
left: parent.left
right: parent.right
leftMargin: root.baseCellHeight / 2
rightMargin: root.baseCellHeight / 2
}
sourceComponent: Rectangle {
implicitHeight: 1
color: Appearance.colors.colOutlineVariant
}
}
FadeLoader {
shown: root.editMode
sourceComponent: Column {
id: unusedRows
spacing: root.spacing
Repeater {
model: ScriptModel {
values: root.unusedToggleRows
}
delegate: ButtonGroup {
id: unusedToggleRow
required property var modelData
spacing: root.spacing
Repeater {
model: ScriptModel {
values: unusedToggleRow.modelData
}
delegate: AndroidToggleDelegateChooser {
startingIndex: -1
editMode: root.editMode
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
spacing: root.spacing
}
}
}
}
}
}
}
}
@@ -0,0 +1,39 @@
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import QtQuick
import QtQuick.Layouts
import Quickshell.Bluetooth
import "./classicStyle/"
AbstractQuickPanel {
id: root
Layout.alignment: Qt.AlignHCenter
implicitWidth: buttonGroup.implicitWidth
implicitHeight: buttonGroup.implicitHeight
color: "transparent"
ButtonGroup {
id: buttonGroup
spacing: 5
padding: 5
color: Appearance.colors.colLayer1
NetworkToggle {
altAction: () => {
root.openWifiDialog();
}
}
BluetoothToggle {
altAction: () => {
root.openBluetoothDialog();
}
}
NightLight {}
GameMode {}
IdleInhibitor {}
EasyEffectsToggle {}
CloudflareWarp {}
}
}
@@ -0,0 +1,18 @@
import qs
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Audio")
statusText: toggled ? Translation.tr("Unmuted") : Translation.tr("Muted")
toggled: !Audio.sink?.audio?.muted
buttonIcon: Audio.sink?.audio?.muted ? "volume_off" : "volume_up"
onClicked: {
Audio.sink.audio.muted = !Audio.sink.audio.muted
}
}
@@ -0,0 +1,27 @@
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
import QtQuick
import Quickshell
import Quickshell.Bluetooth
AndroidQuickToggleButton {
id: root
name: Translation.tr("Bluetooth")
statusText: BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("No device")
toggled: BluetoothStatus.enabled
buttonIcon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
onClicked: {
Bluetooth.defaultAdapter.enabled = !Bluetooth.defaultAdapter?.enabled
}
StyledToolTip {
text: Translation.tr("%1 | Right-click to configure").arg(
(BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("Bluetooth"))
+ (BluetoothStatus.activeDeviceCount > 1 ? ` +${BluetoothStatus.activeDeviceCount - 1}` : "")
)
}
}
@@ -0,0 +1,80 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
import Quickshell.Io
AndroidQuickToggleButton {
id: root
name: Translation.tr("Cloudflare WARP")
toggled: false
buttonIcon: "cloud_lock"
onClicked: {
if (toggled) {
root.toggled = false
Quickshell.execDetached(["warp-cli", "disconnect"])
} else {
root.toggled = true
Quickshell.execDetached(["warp-cli", "connect"])
}
}
Process {
id: connectProc
command: ["warp-cli", "connect"]
onExited: (exitCode, exitStatus) => {
if (exitCode !== 0) {
Quickshell.execDetached(["notify-send",
Translation.tr("Cloudflare WARP"),
Translation.tr("Connection failed. Please inspect manually with the <tt>warp-cli</tt> command")
, "-a", "Shell"
])
}
}
}
Process {
id: registrationProc
command: ["warp-cli", "registration", "new"]
onExited: (exitCode, exitStatus) => {
console.log("Warp registration exited with code and status:", exitCode, exitStatus)
if (exitCode === 0) {
connectProc.running = true
} else {
Quickshell.execDetached(["notify-send",
Translation.tr("Cloudflare WARP"),
Translation.tr("Registration failed. Please inspect manually with the <tt>warp-cli</tt> command"),
"-a", "Shell"
])
}
}
}
Process {
id: fetchActiveState
running: true
command: ["bash", "-c", "warp-cli status"]
stdout: StdioCollector {
id: warpStatusCollector
onStreamFinished: {
if (warpStatusCollector.text.length > 0) {
root.visible = true
}
if (warpStatusCollector.text.includes("Unable")) {
registrationProc.running = true
} else if (warpStatusCollector.text.includes("Connected")) {
root.toggled = true
} else if (warpStatusCollector.text.includes("Disconnected")) {
root.toggled = false
}
}
}
}
StyledToolTip {
text: Translation.tr("Cloudflare WARP (1.1.1.1)")
}
}
@@ -0,0 +1,26 @@
import qs
import qs.modules.common
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Color Picker")
toggled: false
buttonIcon: "colorize"
onClicked: {
GlobalStates.sidebarRightOpen = false;
delayedActionTimer.start()
}
Timer {
id: delayedActionTimer
interval: 300
repeat: false
onTriggered: {
Quickshell.execDetached(["hyprpicker", "-a"])
}
}
}
@@ -0,0 +1,23 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Dark Mode")
statusText: Appearance.m3colors.darkmode ? Translation.tr("Dark") : Translation.tr("Light")
toggled: Appearance.m3colors.darkmode
buttonIcon: Appearance.m3colors.darkmode ? "contrast" : "light_mode"
onClicked: event => {
if (Appearance.m3colors.darkmode) {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]);
} else {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]);
}
}
}
@@ -0,0 +1,32 @@
import qs
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("EasyEffects")
toggled: EasyEffects.active
buttonIcon: "graphic_eq"
Component.onCompleted: {
EasyEffects.fetchActiveState()
}
onClicked: {
EasyEffects.toggle()
}
altAction: () => {
Quickshell.execDetached(["bash", "-c", "flatpak run com.github.wwmm.easyeffects || easyeffects"])
GlobalStates.sidebarRightOpen = false
}
StyledToolTip {
text: Translation.tr("EasyEffects | Right-click to configure")
}
}
@@ -0,0 +1,34 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
import Quickshell.Io
AndroidQuickToggleButton {
id: root
name: Translation.tr("Game mode")
toggled: toggled
buttonIcon: "gamepad"
onClicked: {
root.toggled = !root.toggled
if (root.toggled) {
Quickshell.execDetached(["bash", "-c", `hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`])
} else {
Quickshell.execDetached(["hyprctl", "reload"])
}
}
Process {
id: fetchActiveState
running: true
command: ["bash", "-c", `test "$(hyprctl getoption animations:enabled -j | jq ".int")" -ne 0`]
onExited: (exitCode, exitStatus) => {
root.toggled = exitCode !== 0 // Inverted because enabled = nonzero exit
}
}
StyledToolTip {
text: Translation.tr("Game mode")
}
}
@@ -0,0 +1,21 @@
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
import QtQuick
AndroidQuickToggleButton {
id: root
name: Translation.tr("Idle Inhibitor")
toggled: Idle.inhibit
buttonIcon: "coffee"
onClicked: {
Idle.toggleInhibit()
}
StyledToolTip {
text: Translation.tr("Keep system awake")
}
}
@@ -0,0 +1,18 @@
import qs
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Microphone")
statusText: toggled ? Translation.tr("Enabled") : Translation.tr("Muted")
toggled: !Audio.source?.audio?.muted
buttonIcon: Audio.source?.audio?.muted ? "mic_off" : "mic"
onClicked: {
Audio.source.audio.muted = !Audio.source.audio.muted
}
}
@@ -0,0 +1,23 @@
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
import QtQuick
AndroidQuickToggleButton {
id: root
name: Translation.tr("Internet")
statusText: Network.networkName
toggled: Network.wifiStatus !== "disabled"
buttonIcon: Network.materialSymbol
onClicked: Network.toggleWifi()
altAction: () => {
root.openMenu()
}
StyledToolTip {
text: Translation.tr("%1 | Right-click to configure").arg(Network.networkName)
}
}
@@ -0,0 +1,33 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
property bool auto: Config.options.light.night.automatic
name: Translation.tr("Night Light")
statusText: (auto ? Translation.tr("Auto, ") : "") + (toggled ? Translation.tr("Active") : Translation.tr("Inactive"))
toggled: Hyprsunset.active
buttonIcon: auto ? "night_sight_auto" : "bedtime"
onClicked: {
Hyprsunset.toggle()
}
altAction: () => {
Config.options.light.night.automatic = !Config.options.light.night.automatic
}
Component.onCompleted: {
Hyprsunset.fetchState()
}
StyledToolTip {
text: Translation.tr("Night Light | Right-click to toggle Auto mode")
}
}
@@ -0,0 +1,18 @@
import qs
import qs.modules.common
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Notifications")
statusText: toggled ? Translation.tr("Show") : Translation.tr("Silent")
toggled: !Notifications.silent
buttonIcon: toggled ? "notifications_active" : "notifications_paused"
onClicked: {
Notifications.silent = !Notifications.silent;
}
}
@@ -0,0 +1,17 @@
import qs
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Virtual Keyboard")
toggled: GlobalStates.oskOpen
buttonIcon: toggled ? "keyboard_hide" : "keyboard"
onClicked: {
GlobalStates.oskOpen = !GlobalStates.oskOpen
}
}
@@ -0,0 +1,42 @@
import qs
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
import Quickshell.Services.UPower
AndroidQuickToggleButton {
id: root
name: Translation.tr("Power Profile")
toggled: PowerProfiles.profile !== PowerProfile.Balanced
buttonIcon: switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: return "energy_savings_leaf"
case PowerProfile.Balanced: return "settings_slow_motion"
case PowerProfile.Performance: return "local_fire_department"
}
statusText: switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: return "Power Saver"
case PowerProfile.Balanced: return "Balanced"
case PowerProfile.Performance: return "Performance"
}
onClicked: (event) => {
if (PowerProfiles.hasPerformanceProfile) {
switch(PowerProfiles.profile) {
case PowerProfile.PowerSaver: PowerProfiles.profile = PowerProfile.Balanced
break;
case PowerProfile.Balanced: PowerProfiles.profile = PowerProfile.Performance
break;
case PowerProfile.Performance: PowerProfiles.profile = PowerProfile.PowerSaver
break;
}
} else {
PowerProfiles.profile = PowerProfiles.profile == PowerProfile.Balanced ? PowerProfile.PowerSaver : PowerProfile.Balanced
}
}
StyledToolTip {
text: Translation.tr("Click to cycle through power profiles")
}
}
@@ -0,0 +1,173 @@
import QtQuick
import QtQuick.Layouts
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
GroupButton {
id: root
required property int buttonIndex
required property var buttonData
required property bool expandedSize
required property string buttonIcon
required property string name
property string statusText: toggled ? Translation.tr("Active") : Translation.tr("Inactive")
required property real baseCellWidth
required property real baseCellHeight
required property real cellSpacing
required property int cellSize
baseWidth: root.baseCellWidth * cellSize + cellSpacing * (cellSize - 1)
baseHeight: root.baseCellHeight
Behavior on baseWidth {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
}
property bool editMode: false
signal openMenu()
padding: 6
horizontalPadding: padding
verticalPadding: padding
colBackground: Appearance.colors.colLayer2
colBackgroundToggled: (altAction && expandedSize) ? Appearance.colors.colLayer2 : Appearance.colors.colPrimary
colBackgroundToggledHover: (altAction && expandedSize) ? Appearance.colors.colLayer2Hover : Appearance.colors.colPrimaryHover
colBackgroundToggledActive: (altAction && expandedSize) ? Appearance.colors.colLayer2Active : Appearance.colors.colPrimaryActive
buttonRadius: toggled ? Appearance.rounding.large : height / 2
buttonRadiusPressed: Appearance.rounding.normal
property color colText: (toggled && !(altAction && expandedSize)) ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer2
property color colIcon: expandedSize ? (root.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer3) : colText
contentItem: RowLayout {
id: contentItem
spacing: 4
anchors {
centerIn: root.expandedSize ? undefined : parent
fill: root.expandedSize ? parent : undefined
leftMargin: root.horizontalPadding
rightMargin: root.horizontalPadding
}
Rectangle {
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
Layout.topMargin: root.verticalPadding
Layout.bottomMargin: root.verticalPadding
implicitWidth: height
radius: root.radius - root.verticalPadding
color: {
const baseColor = root.toggled ? Appearance.colors.colPrimary : Appearance.colors.colLayer3
const transparentizeAmount = (root.altAction && root.expandedSize) ? 0 : 1
return ColorUtils.transparentize(baseColor, transparentizeAmount)
}
MaterialSymbol {
anchors.centerIn: parent
fill: root.toggled ? 1 : 0
iconSize: Appearance.font.pixelSize.huge
color: root.colIcon
text: root.buttonIcon
}
}
Loader {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
visible: root.expandedSize
active: visible
sourceComponent: Column {
StyledText {
anchors {
left: parent.left
right: parent.right
}
font.pixelSize: Appearance.font.pixelSize.smallie
color: root.colText
elide: Text.ElideRight
text: root.name
}
StyledText {
visible: root.statusText
anchors {
left: parent.left
right: parent.right
}
font {
pixelSize: Appearance.font.pixelSize.smaller
weight: Font.Light
}
color: root.colText
elide: Text.ElideRight
text: root.statusText
}
}
}
}
MouseArea { // Blocking MouseArea for edit interactions
id: editModeInteraction
visible: root.editMode
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
acceptedButtons: Qt.AllButtons
function toggleEnabled() {
const index = root.buttonIndex;
const toggleList = Config.options.sidebar.quickToggles.android.toggles;
const buttonType = root.buttonData.type;
if (!toggleList.find(toggle => toggle.type === buttonType)) {
toggleList.push({ type: buttonType, size: 1 });
} else {
toggleList.splice(index, 1);
}
}
function toggleSize() {
const index = root.buttonIndex;
const toggleList = Config.options.sidebar.quickToggles.android.toggles;
const buttonType = root.buttonData.type;
if (!toggleList.find(toggle => toggle.type === buttonType)) return;
toggleList[index].size = 3 - toggleList[index].size; // Alternate between 1 and 2
}
function movePositionBy(offset) {
const index = root.buttonIndex;
const toggleList = Config.options.sidebar.quickToggles.android.toggles;
const buttonType = root.buttonData.type;
const targetIndex = index + offset;
if (targetIndex < 0 || targetIndex >= toggleList.length) return;
const temp = toggleList[index];
toggleList[index] = toggleList[targetIndex];
toggleList[targetIndex] = temp;
}
onReleased: (event) => {
if (event.button === Qt.LeftButton)
toggleEnabled();
}
onPressed: (event) => {
if (event.button === Qt.RightButton) toggleSize();
}
onPressAndHold: (event) => { // Also toggle size
toggleSize();
}
onWheel: (event) => {
const index = root.buttonIndex;
const toggleList = Config.options.sidebar.quickToggles.android.toggles;
const buttonType = root.buttonData.type;
if (event.angleDelta.y < 0) { // Move to right
movePositionBy(1);
} else if (event.angleDelta.y > 0) { // Move to left
movePositionBy(-1);
}
event.accepted = true;
}
}
}
@@ -0,0 +1,27 @@
import qs
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import QtQuick
import Quickshell
AndroidQuickToggleButton {
id: root
name: Translation.tr("Screen snip")
toggled: false
buttonIcon: "screenshot_region"
onClicked: {
GlobalStates.sidebarRightOpen = false;
delayedActionTimer.start()
}
Timer {
id: delayedActionTimer
interval: 300
repeat: false
onTriggered: {
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath("screenshot.qml")])
}
}
}
@@ -0,0 +1,225 @@
pragma ComponentBehavior: Bound
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
import "./androidStyle/"
DelegateChooser {
id: root
property bool editMode: false
required property real baseCellWidth
required property real baseCellHeight
required property real spacing
required property int startingIndex
signal openWifiDialog()
signal openBluetoothDialog()
role: "type"
DelegateChoice { roleValue: "network"; AndroidNetworkToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
altAction: () => {
root.openWifiDialog()
}
} }
DelegateChoice { roleValue: "bluetooth"; AndroidBluetoothToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
altAction: () => {
root.openBluetoothDialog()
}
} }
DelegateChoice { roleValue: "idleInhibitor"; AndroidIdleInhibitorToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "easyEffects"; AndroidEasyEffectsToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "nightLight"; AndroidNightLightToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "darkMode"; AndroidDarkModeToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "cloudflareWarp"; AndroidCloudflareWarpToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "gameMode"; AndroidGameModeToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "screenSnip"; AndroidScreenSnipToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "colorPicker"; AndroidColorPickerToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "onScreenKeyboard"; AndroidOnScreenKeyboardToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "mic"; AndroidMicToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "audio"; AndroidAudioToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "notifications"; AndroidNotificationToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
DelegateChoice { roleValue: "powerProfile"; AndroidPowerProfileToggle {
required property int index
required property var modelData
buttonIndex: root.startingIndex + index
buttonData: modelData
editMode: root.editMode
expandedSize: modelData.size > 1
baseCellWidth: root.baseCellWidth
baseCellHeight: root.baseCellHeight
cellSpacing: root.spacing
cellSize: modelData.size
} }
}
@@ -8,8 +8,8 @@ import Quickshell.Hyprland
QuickToggleButton {
id: root
toggled: EasyEffects.active
visible: EasyEffects.available
toggled: EasyEffects.active
buttonIcon: "instant_mix"
Component.onCompleted: {
@@ -6,8 +6,7 @@ import Quickshell.Io
QuickToggleButton {
id: nightLightButton
property bool enabled: Hyprsunset.active
toggled: enabled
toggled: Hyprsunset.active
buttonIcon: Config.options.light.night.automatic ? "night_sight_auto" : "bedtime"
onClicked: {
Hyprsunset.toggle()
@@ -0,0 +1,511 @@
{
"Material cookie": "Material cookie",
"Style: Blurred": "Style: Blurred",
"Unknown device": "Unknown device",
"Change any time later with /dark, /light, /wallpaper in the launcher\nIf the shell's colors aren't changing:\n 1. Open the right sidebar with Super+N\n 2. Click \"Reload Hyprland & Quickshell\" in the top-right corner": "Change any time later with /dark, /light, /wallpaper in the launcher\nIf the shell's colors aren't changing:\n 1. Open the right sidebar with Super+N\n 2. Click \"Reload Hyprland & Quickshell\" in the top-right corner",
"No pending tasks": "No pending tasks",
"Positioning": "Positioning",
"Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.",
"Critical warning": "Critical warning",
"Unknown Artist": "Unknown Artist",
"Web search": "Web search",
"Load prompt from %1": "Load prompt from %1",
"Attach a file. Only works with Gemini.": "Attach a file. Only works with Gemini.",
"Reboot": "Reboot",
"API key:\n\n```txt\n%1\n```": "API key:\n\n```txt\n%1\n```",
"Pinned on startup": "Pinned on startup",
"Right": "Right",
"Reboot to firmware settings": "Reboot to firmware settings",
"Automatically hide": "Automatically hide",
"Waiting for response...": "Waiting for response...",
"To Do": "To Do",
"Full": "Full",
"Select Language": "Select Language",
"Password": "Password",
"Bluetooth devices": "Bluetooth devices",
"Enable": "Enable",
"Elements": "Elements",
"Start": "Start",
"Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers",
"The popular one | Best quantity, but quality can vary wildly": "The popular one | Best quantity, but quality can vary wildly",
"System uptime:": "System uptime:",
"illogical-impulse Welcome": "illogical-impulse Welcome",
"Code saved to file": "Code saved to file",
"Info": "Info",
"Preferred wallpaper zoom (%)": "Preferred wallpaper zoom (%)",
"Time": "Time",
"Help & Support": "Help & Support",
"Bubble": "Bubble",
"Large images | God tier quality, no NSFW.": "Large images | God tier quality, no NSFW.",
"Dark": "Dark",
"Center clock": "Center clock",
"Search, calculate or run": "Search, calculate or run",
"Region height": "Region height",
"Load chat": "Load chat",
"Gives the model search capabilities (immediately)": "Gives the model search capabilities (immediately)",
"Depends on workspace": "Depends on workspace",
"Blurred style": "Blurred style",
"Screenshot tool": "Screenshot tool",
"Enter password": "Enter password",
"Search the web": "Search the web",
"Local only": "Local only",
"at": "at",
"Math": "Math",
"Consider plugging in your device": "Consider plugging in your device",
"Workspaces shown": "Workspaces shown",
"Place the corners to trigger at the bottom": "Place the corners to trigger at the bottom",
"No API key\nSet it with /key YOUR_API_KEY": "No API key\nSet it with /key YOUR_API_KEY",
"Auto (System)": "Auto (System)",
"Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel",
"Critically low battery": "Critically low battery",
"Open editor": "Open editor",
"%1 notifications": "%1 notifications",
"Region width": "Region width",
"Max allowed increase": "Max allowed increase",
"Enable translator": "Enable translator",
"Constantly rotate": "Constantly rotate",
"Automatically suspends the system when battery is low": "Automatically suspends the system when battery is low",
"Cannot find a GPS service. Using the fallback method instead.": "Cannot find a GPS service. Using the fallback method instead.",
"Qt apps": "Qt apps",
"Color picker": "Color picker",
"Interface": "Interface",
"Tint app icons": "Tint app icons",
"Select the language for the user interface.\n\"Auto\" will use your system's locale.": "Select the language for the user interface.\n\"Auto\" will use your system's locale.",
"Show quote": "Show quote",
"Local Ollama model | %1": "Local Ollama model | %1",
"Show clock": "Show clock",
"Usage: <tt>%1superpaste NUM_OF_ENTRIES[i]</tt>\nSupply <tt>i</tt> when you want images\nExamples:\n<tt>%1superpaste 4i</tt> for the last 4 images\n<tt>%1superpaste 7</tt> for the last 7 entries": "Usage: <tt>%1superpaste NUM_OF_ENTRIES[i]</tt>\nSupply <tt>i</tt> when you want images\nExamples:\n<tt>%1superpaste 4i</tt> for the last 4 images\n<tt>%1superpaste 7</tt> for the last 7 entries",
"Audio": "Audio",
"Corner style": "Corner style",
"No media": "No media",
"Unknown function call: %1": "Unknown function call: %1",
"Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls": "Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls",
"Volume": "Volume",
"Medium": "Medium",
"Copy code": "Copy code",
"Exceeded max allowed": "Exceeded max allowed",
"Keep right sidebar loaded": "Keep right sidebar loaded",
"Left": "Left",
"High": "High",
"Rect": "Rect",
"Lap": "Lap",
"Clear": "Clear",
"Screen snip": "Screen snip",
"Reset": "Reset",
"Back": "Back",
"Dark/Light toggle": "Dark/Light toggle",
"12h am/pm": "12h am/pm",
"Download complete": "Download complete",
"Enable blur": "Enable blur",
"Second hand": "Second hand",
"Bar & screen": "Bar & screen",
"Discharging:": "Discharging:",
"Up %1": "Up %1",
"Low": "Low",
"Hour hand": "Hour hand",
"Clear chat history": "Clear chat history",
"Fruit Salad": "Fruit Salad",
"%1 Safe Storage": "%1 Safe Storage",
"Hibernate": "Hibernate",
"Delete": "Delete",
"OK": "OK",
"Settings": "Settings",
"This is usually safe and needed for your browser and AI sidebar anyway\nMostly useful for those who use lock on startup instead of a display manager that does it (GDM, SDDM, etc.)": "This is usually safe and needed for your browser and AI sidebar anyway\nMostly useful for those who use lock on startup instead of a display manager that does it (GDM, SDDM, etc.)",
"Use Hyprlock (instead of Quickshell)": "Use Hyprlock (instead of Quickshell)",
"Crosshair code (in Valorant's format)": "Crosshair code (in Valorant's format)",
"Silent": "Silent",
"Useless buttons": "Useless buttons",
"Hover to reveal": "Hover to reveal",
"Wallpaper & Colors": "Wallpaper & Colors",
"Auto": "Auto",
"Visibility": "Visibility",
"Shell & utilities": "Shell & utilities",
"Hollow": "Hollow",
"illogical-impulse": "illogical-impulse",
"Use the system file picker instead\nRight-click to make this the default behavior": "Use the system file picker instead\nRight-click to make this the default behavior",
"On-screen display": "On-screen display",
"Dotfiles": "Dotfiles",
"Search wallpapers": "Search wallpapers",
"Mic toggle": "Mic toggle",
"Input": "Input",
"Also unlock keyring": "Also unlock keyring",
"Configuration": "Configuration",
"Keep system awake": "Keep system awake",
"Unknown command:": "Unknown command:",
"Anime boorus": "Anime boorus",
"To Do:": "To Do:",
"Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.": "Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.",
"Bottom": "Bottom",
"Clear the current list of images": "Clear the current list of images",
"Sunrise": "Sunrise",
"Show app icons": "Show app icons",
"Format": "Format",
"Make sure your player has MPRIS support\nor try turning off duplicate player filtering": "Make sure your player has MPRIS support\nor try turning off duplicate player filtering",
"Pause": "Pause",
"Desktop": "Desktop",
"Conflicts with the shell's system tray implementation": "Conflicts with the shell's system tray implementation",
"Your package manager is running": "Your package manager is running",
"Conflicts with the shell's notification implementation": "Conflicts with the shell's notification implementation",
"Unknown Album": "Unknown Album",
"Pick wallpaper image on your system": "Pick wallpaper image on your system",
"Used:": "Used:",
"Cheat sheet": "Cheat sheet",
"Clock style": "Clock style",
"No audio source": "No audio source",
"Paired": "Paired",
"Documentation": "Documentation",
"No": "No",
"Pills": "Pills",
"Thought": "Thought",
"When this is off you'll have to click": "When this is off you'll have to click",
"Select output device": "Select output device",
"Logout": "Logout",
"Tip: Close a window with Super+Q": "Tip: Close a window with Super+Q",
"Finished tasks will go here": "Finished tasks will go here",
"Terminal: Harmony (%)": "Terminal: Harmony (%)",
"Corner open": "Corner open",
"Shell conflicts killer": "Shell conflicts killer",
"Clean stuff | Excellent quality, no NSFW": "Clean stuff | Excellent quality, no NSFW",
"Scroll to change volume": "Scroll to change volume",
"Wind": "Wind",
"API key is set\nChange with /key YOUR_API_KEY": "API key is set\nChange with /key YOUR_API_KEY",
"Neutral": "Neutral",
"12h AM/PM": "12h AM/PM",
"Number show delay when pressing Super (ms)": "Number show delay when pressing Super (ms)",
"Fill": "Fill",
"Always show numbers": "Always show numbers",
"Dot": "Dot",
"Provider set to": "Provider set to",
"Unknown Title": "Unknown Title",
"Click to toggle light/dark mode (applied when wallpaper is chosen)": "Click to toggle light/dark mode (applied when wallpaper is chosen)",
"Anime": "Anime",
"Refreshing (manually triggered)": "Refreshing (manually triggered)",
"Dock": "Dock",
"Require password to power off/restart": "Require password to power off/restart",
"Line": "Line",
"Weather": "Weather",
"All-rounder | Good quality, decent quantity": "All-rounder | Good quality, decent quantity",
"Scale (%)": "Scale (%)",
"Copy": "Copy",
"Usage": "Usage",
"Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window",
"Set the tool to use for the model.": "Set the tool to use for the model.",
"Disable tools": "Disable tools",
"Connect": "Connect",
"Allow NSFW": "Allow NSFW",
"Registration failed. Please inspect manually with the <tt>warp-cli</tt> command": "Registration failed. Please inspect manually with the <tt>warp-cli</tt> command",
"Time to full:": "Time to full:",
"Session": "Session",
"Services": "Services",
"Nothing here!": "Nothing here!",
"Overview": "Overview",
"Random: osu! seasonal": "Random: osu! seasonal",
"If you want to somehow use fingerprint unlock...": "If you want to somehow use fingerprint unlock...",
"Minute hand": "Minute hand",
"Notifications": "Notifications",
"Enable if you want clocks to show seconds accurately": "Enable if you want clocks to show seconds accurately",
"Timer": "Timer",
"Quote settings": "Quote settings",
"System prompt": "System prompt",
"Classic": "Classic",
"Close": "Close",
"Disconnect": "Disconnect",
"Go to source (%1)": "Go to source (%1)",
"EasyEffects | Right-click to configure": "EasyEffects | Right-click to configure",
"Forget": "Forget",
"Output": "Output",
"Date style": "Date style",
"System": "System",
"Usage: %1tool TOOL_NAME": "Usage: %1tool TOOL_NAME",
"Workspaces": "Workspaces",
"Calendar": "Calendar",
"**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key": "**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key",
"Volume limit": "Volume limit",
"Sunset": "Sunset",
"Dial style": "Dial style",
"Hi there! First things first...": "Hi there! First things first...",
"Save chat to %1": "Save chat to %1",
"Security": "Security",
"Total token count\nInput: %1\nOutput: %2": "Total token count\nInput: %1\nOutput: %2",
"Cancel wallpaper selection": "Cancel wallpaper selection",
"Please charge!\nAutomatic suspend triggers at %1": "Please charge!\nAutomatic suspend triggers at %1",
"Terminal: Harmonize threshold": "Terminal: Harmonize threshold",
"Be patient...": "Be patient...",
"Utility buttons": "Utility buttons",
"Tonal Spot": "Tonal Spot",
"Prevents abrupt increments and restricts volume limit": "Prevents abrupt increments and restricts volume limit",
"Set the current API provider": "Set the current API provider",
"Connection failed. Please inspect manually with the <tt>warp-cli</tt> command": "Connection failed. Please inspect manually with the <tt>warp-cli</tt> command",
"Networking": "Networking",
"Tint icons": "Tint icons",
"Low battery": "Low battery",
"Make icons pinned by default": "Make icons pinned by default",
"Get the next page of results": "Get the next page of results",
"Invalid API provider. Supported: \n-": "Invalid API provider. Supported: \n-",
"Show \"Locked\" text": "Show \"Locked\" text",
"**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key",
"Not visible to model": "Not visible to model",
"Lock screen": "Lock screen",
"Save to Downloads": "Save to Downloads",
"Expressive": "Expressive",
"Suspend at": "Suspend at",
"Jump to current month": "Jump to current month",
"Bold": "Bold",
"Waifus only | Excellent quality, limited quantity": "Waifus only | Excellent quality, limited quantity",
"Click to toggle light/dark mode\n(applied when wallpaper is chosen)": "Click to toggle light/dark mode\n(applied when wallpaper is chosen)",
"Visualize region": "Visualize region",
"Quote": "Quote",
"Sleep": "Sleep",
"Hit \"/\" to search": "Hit \"/\" to search",
"Hug": "Hug",
"Report a Bug": "Report a Bug",
"Precipitation": "Precipitation",
"Crosshair": "Crosshair",
"Model set to %1": "Model set to %1",
"Rows": "Rows",
"Top": "Top",
"Long break": "Long break",
"Superpaste": "Superpaste",
"Screen round corner": "Screen round corner",
"Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers": "Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers",
"Rainbow": "Rainbow",
"Weeb": "Weeb",
"Large language models": "Large language models",
"Online models disallowed\n\nControlled by `policies.ai` config option": "Online models disallowed\n\nControlled by `policies.ai` config option",
"Policies": "Policies",
"Temperature must be between 0 and 2": "Temperature must be between 0 and 2",
"Automatic suspend": "Automatic suspend",
"Extra wallpaper zoom (%)": "Extra wallpaper zoom (%)",
"GitHub": "GitHub",
"%1 | Right-click to configure": "%1 | Right-click to configure",
"**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1": "**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1",
"Edit directory": "Edit directory",
"Action": "Action",
"Search": "Search",
"Tip: right-clicking a group\nalso expands it": "Tip: right-clicking a group\nalso expands it",
"Bar": "Bar",
"Show regions of potential interest": "Show regions of potential interest",
"Clipboard": "Clipboard",
"Stopwatch": "Stopwatch",
"Enter text to translate...": "Enter text to translate...",
"App": "App",
"Sides": "Sides",
"No active player": "No active player",
"Use the system file picker instead": "Use the system file picker instead",
"Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.": "Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.",
"There might be a download in progress": "There might be a download in progress",
"Math result": "Math result",
"Fidelity": "Fidelity",
"Prefixes": "Prefixes",
"Terminal": "Terminal",
"Incorrect password": "Incorrect password",
"Line-separated": "Line-separated",
"Always": "Always",
"☕ Break: %1 minutes": "☕ Break: %1 minutes",
"Depends on sidebars": "Depends on sidebars",
"Tool set to: %1": "Tool set to: %1",
"Save chat": "Save chat",
"Crosshair overlay": "Crosshair overlay",
"Keybinds": "Keybinds",
"Launch": "Launch",
"Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)",
"Choose model": "Choose model",
"Base URL": "Base URL",
"Float": "Float",
"Wallpaper parallax": "Wallpaper parallax",
"Invalid arguments. Must provide `command`.": "Invalid arguments. Must provide `command`.",
"Fully charged": "Fully charged",
"Earbang protection": "Earbang protection",
"Low warning": "Low warning",
"Advanced": "Advanced",
"Scroll to change brightness": "Scroll to change brightness",
"Loaded the following system prompt\n\n---\n\n%1": "Loaded the following system prompt\n\n---\n\n%1",
"Show next time": "Show next time",
"Current tool: %1\nSet it with %2tool TOOL": "Current tool: %1\nSet it with %2tool TOOL",
"Unread indicator: show count": "Unread indicator: show count",
"Press Super+G to toggle appearance": "Press Super+G to toggle appearance",
"That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number",
"Dots": "Dots",
"Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)",
"Volume mixer": "Volume mixer",
"Config file": "Config file",
"API key set for %1": "API key set for %1",
"Online via %1 | %2's model": "Online via %1 | %2's model",
"Shell command": "Shell command",
"Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.",
"Reload Hyprland & Quickshell": "Reload Hyprland & Quickshell",
"Resources": "Resources",
"Brightness": "Brightness",
"Unknown": "Unknown",
"Polling interval (ms)": "Polling interval (ms)",
"Lock": "Lock",
"Thinking": "Thinking",
"Approve": "Approve",
"Unfinished": "Unfinished",
"Random: Konachan": "Random: Konachan",
"Connected": "Connected",
"Wallpaper safety enforced": "Wallpaper safety enforced",
"Invalid arguments. Must provide `key` and `value`.": "Invalid arguments. Must provide `key` and `value`.",
"24h": "24h",
"Allows you to open sidebars by clicking or hovering screen corners regardless of bar position": "Allows you to open sidebars by clicking or hovering screen corners regardless of bar position",
"Bar style": "Bar style",
"Load:": "Load:",
"Open file link": "Open file link",
"Ignored if terminal theming is not enabled": "Ignored if terminal theming is not enabled",
"Shutdown": "Shutdown",
"Hour marks": "Hour marks",
"Random osu! seasonal background\nImage is saved to ~/Pictures/Wallpapers": "Random osu! seasonal background\nImage is saved to ~/Pictures/Wallpapers",
"Online | Google's model\nFast, can perform searches for up-to-date information": "Online | Google's model\nFast, can perform searches for up-to-date information",
"Current model: %1\nSet it with %2model MODEL": "Current model: %1\nSet it with %2model MODEL",
"Select input device": "Select input device",
"Connect to Wi-Fi": "Connect to Wi-Fi",
"... and %1 more": "... and %1 more",
"Cookie clock settings": "Cookie clock settings",
"Brightness and volume": "Brightness and volume",
"Choose file": "Choose file",
"Invalid model. Supported: \n```": "Invalid model. Supported: \n```",
"Task Manager": "Task Manager",
"Charging:": "Charging:",
"Illegal increment": "Illegal increment",
"Total:": "Total:",
"or": "or",
"Battery": "Battery",
"Timeout duration (if not defined by notification) (ms)": "Timeout duration (if not defined by notification) (ms)",
"Cancel": "Cancel",
"Locked": "Locked",
"Temperature: %1": "Temperature: %1",
"Hover to trigger": "Hover to trigger",
"Command rejected by user": "Command rejected by user",
"User agent (for services that require it)": "User agent (for services that require it)",
"Saved to %1": "Saved to %1",
"Emojis": "Emojis",
"Color generation": "Color generation",
"Welcome app": "Welcome app",
"Humidity": "Humidity",
"Page %1": "Page %1",
"Feels like %1": "Feels like %1",
"Distro": "Distro",
"Transparency": "Transparency",
"%1 • %2 tasks": "%1 • %2 tasks",
"Markdown test": "Markdown test",
"Invalid tool. Supported tools:\n- %1": "Invalid tool. Supported tools:\n- %1",
"No notifications": "No notifications",
"The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "The hentai one | Great quantity, a lot of NSFW, quality varies wildly",
"Bluetooth": "Bluetooth",
"Resume": "Resume",
"Work safety": "Work safety",
"Temperature\nChange with /temp VALUE": "Temperature\nChange with /temp VALUE",
"Terminal: Foreground boost (%)": "Terminal: Foreground boost (%)",
"Night Light | Right-click to toggle Auto mode": "Night Light | Right-click to toggle Auto mode",
"Closet": "Closet",
"Yes": "Yes",
"Columns": "Columns",
"To set an API key, pass it with the %4 command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For %1:\n\n**Link**: %2\n\n%3": "To set an API key, pass it with the %4 command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For %1:\n\n**Link**: %2\n\n%3",
"Kill conflicting programs?": "Kill conflicting programs?",
"For storing API keys and other sensitive information": "For storing API keys and other sensitive information",
"Reject": "Reject",
"Set API key": "Set API key",
". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!",
"Content": "Content",
"Pomodoro": "Pomodoro",
"Vertical": "Vertical",
"Pick a wallpaper": "Pick a wallpaper",
"Load chat from %1": "Load chat from %1",
"Launch on startup": "Launch on startup",
"Add": "Add",
"Style: general": "Style: general",
"Use Levenshtein distance-based algorithm instead of fuzzy": "Use Levenshtein distance-based algorithm instead of fuzzy",
"Shell & utilities theming must also be enabled": "Shell & utilities theming must also be enabled",
"Workspace": "Workspace",
"Translator": "Translator",
"Free:": "Free:",
"🌿 Long break: %1 minutes": "🌿 Long break: %1 minutes",
"Value scroll": "Value scroll",
"Bar position": "Bar position",
"Language": "Language",
"Current API endpoint: %1\nSet it with %2mode PROVIDER": "Current API endpoint: %1\nSet it with %2mode PROVIDER",
"Remember that on most devices one can always hold the power button to force shutdown\nThis only makes it a tiny bit harder for accidents to happen": "Remember that on most devices one can always hold the power button to force shutdown\nThis only makes it a tiny bit harder for accidents to happen",
"AI": "AI",
"Task description": "Task description",
"Add task": "Add task",
"Donate": "Donate",
"Disable NSFW content": "Disable NSFW content",
"Set the system prompt for the model.": "Set the system prompt for the model.",
"Done": "Done",
"Focus": "Focus",
"Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json": "Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json",
"View Markdown source": "View Markdown source",
"Border": "Border",
"Temperature set to %1": "Temperature set to %1",
"Online | Google's model\nGoogle's state-of-the-art multipurpose model that excels at coding and complex reasoning tasks.": "Online | Google's model\nGoogle's state-of-the-art multipurpose model that excels at coding and complex reasoning tasks.",
"Message the model... \"%1\" for commands": "Message the model... \"%1\" for commands",
"Translation goes here...": "Translation goes here...",
"When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help": "When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help",
"For desktop wallpapers | Good quality": "For desktop wallpapers | Good quality",
"🔴 Focus: %1 minutes": "🔴 Focus: %1 minutes",
"The current system prompt is\n\n---\n\n%1": "The current system prompt is\n\n---\n\n%1",
"About": "About",
"Quick": "Quick",
"General": "General",
"UV Index": "UV Index",
"Force dark mode in terminal": "Force dark mode in terminal",
"Drag or click a region • LMB: Copy • RMB: Edit": "Drag or click a region • LMB: Copy • RMB: Edit",
"%1 characters": "%1 characters",
"Cloudflare WARP": "Cloudflare WARP",
"**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key",
"Monochrome": "Monochrome",
"Details": "Details",
"Issues": "Issues",
"Keyboard toggle": "Keyboard toggle",
"Might look ass. Unsupported.": "Might look ass. Unsupported.",
"Download": "Download",
"%1 does not require an API key": "%1 does not require an API key",
"Style & wallpaper": "Style & wallpaper",
"Second precision": "Second precision",
"Group style": "Group style",
"Break": "Break",
"Run": "Run",
"Enjoy! You can reopen the welcome app any time with <tt>Super+Shift+Alt+/</tt>. To open the settings app, hit <tt>Super+I</tt>": "Enjoy! You can reopen the welcome app any time with <tt>Super+Shift+Alt+/</tt>. To open the settings app, hit <tt>Super+I</tt>",
"Interface Language": "Interface Language",
"Game mode": "Game mode",
"Usage: %1save CHAT_NAME": "Usage: %1save CHAT_NAME",
"Thin": "Thin",
"Light": "Light",
"When not fullscreen": "When not fullscreen",
"Commands, edit configs, search.\nTakes an extra turn to switch to search mode if that's needed": "Commands, edit configs, search.\nTakes an extra turn to switch to search mode if that's needed",
"Privacy Policy": "Privacy Policy",
"Timeout (ms)": "Timeout (ms)",
"Allow NSFW content": "Allow NSFW content",
"Edit": "Edit",
"Digits in the middle": "Digits in the middle",
"Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput.": "Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput.",
"Weather Service": "Weather Service",
"Background": "Background",
"Pick random from this folder": "Pick random from this folder",
"Pressure": "Pressure",
"Save": "Save",
"Run command": "Run command",
"Time to empty:": "Time to empty:",
"Place at bottom": "Place at bottom",
"Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.",
"Performance Profile toggle": "Performance Profile toggle",
"Sidebars": "Sidebars",
"Usage: %1load CHAT_NAME": "Usage: %1load CHAT_NAME",
"Auto styling with Gemini": "Auto styling with Gemini",
"Simple digital": "Simple digital",
"No API key set for %1": "No API key set for %1",
"Enter tags, or \"%1\" for commands": "Enter tags, or \"%1\" for commands",
"%1 queries pending": "%1 queries pending",
"Discussions": "Discussions",
"Tray": "Tray",
"Numbers": "Numbers",
"Intelligence": "Intelligence",
"Open network portal": "Open network portal",
"<i>No further instruction provided</i>": "<i>No further instruction provided</i>",
"Language not listed or incomplete translations?\nYou can choose to generate translations for it with Gemini.\n1. Open the left sidebar with Super+A, set model to Gemini (if it isn't already)\n2. Type /key, hit Enter and follow the instructions\n3. Type /key YOUR_API_KEY\n4. Type the locale of your language below and press Generate": "Language not listed or incomplete translations?\nYou can choose to generate translations for it with Gemini.\n1. Open the left sidebar with Super+A, set model to Gemini (if it isn't already)\n2. Type /key, hit Enter and follow the instructions\n3. Type /key YOUR_API_KEY\n4. Type the locale of your language below and press Generate",
"Locale code, e.g. fr_FR, de_DE, zh_CN...": "Locale code, e.g. fr_FR, de_DE, zh_CN...",
"Select language": "Select language",
"Generate translation with Gemini": "Generate translation with Gemini",
"Generating...\nDon't close this window!": "Generating...\nDon't close this window!",
"Generate\nTypically takes 2 minutes": "Generate\nTypically takes 2 minutes"
}