diff --git a/.github/README.md b/.github/README.md
index 12d645adf..2020ab82b 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -78,7 +78,7 @@ Widget system: Quickshell | Support: Yes
| AI, settings app | Some widgets |
|:---|:---------------|
-|
|
|
+|
|
|
| Window management | Weeb power |
|
|
|
diff --git a/dots/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml b/dots/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml
index fb9e55367..79dc39709 100644
--- a/dots/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml
+++ b/dots/.config/quickshell/ii/modules/cheatsheet/Cheatsheet.qml
@@ -31,7 +31,6 @@ Scope { // Scope
sourceComponent: PanelWindow { // Window
id: cheatsheetRoot
visible: cheatsheetLoader.active
- property int selectedTab: 0
anchors {
top: true
@@ -86,16 +85,16 @@ Scope { // Scope
}
if (event.modifiers === Qt.ControlModifier) {
if (event.key === Qt.Key_PageDown) {
- cheatsheetRoot.selectedTab = Math.min(cheatsheetRoot.selectedTab + 1, root.tabButtonList.length - 1);
+ tabBar.incrementCurrentIndex();
event.accepted = true;
} else if (event.key === Qt.Key_PageUp) {
- cheatsheetRoot.selectedTab = Math.max(cheatsheetRoot.selectedTab - 1, 0);
+ tabBar.decrementCurrentIndex();
event.accepted = true;
} else if (event.key === Qt.Key_Tab) {
- cheatsheetRoot.selectedTab = (cheatsheetRoot.selectedTab + 1) % root.tabButtonList.length;
+ tabBar.setCurrentIndex((tabBar.currentIndex + 1) % root.tabButtonList.length);
event.accepted = true;
} else if (event.key === Qt.Key_Backtab) {
- cheatsheetRoot.selectedTab = (cheatsheetRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length;
+ tabBar.setCurrentIndex((tabBar.currentIndex - 1 + root.tabButtonList.length) % root.tabButtonList.length);
event.accepted = true;
}
}
@@ -141,11 +140,14 @@ Scope { // Scope
}
text: Translation.tr("Cheat sheet")
}
- PrimaryTabBar { // Tab strip
- id: tabBar
- tabButtonList: root.tabButtonList
- Synchronizer on currentIndex {
- property alias source: cheatsheetRoot.selectedTab
+
+ Toolbar {
+ Layout.alignment: Qt.AlignHCenter
+ enableShadow: false
+ ToolbarTabBar {
+ id: tabBar
+ tabButtonList: root.tabButtonList
+ currentIndex: swipeView.currentIndex
}
}
@@ -154,26 +156,11 @@ Scope { // Scope
Layout.topMargin: 5
Layout.fillWidth: true
Layout.fillHeight: true
+ currentIndex: tabBar.currentIndex
spacing: 10
- Behavior on implicitWidth {
- id: contentWidthBehavior
- enabled: false
- animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
- }
- Behavior on implicitHeight {
- id: contentHeightBehavior
- enabled: false
- animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
- }
-
- currentIndex: cheatsheetRoot.selectedTab
- onCurrentIndexChanged: {
- contentWidthBehavior.enabled = true;
- contentHeightBehavior.enabled = true;
- tabBar.enableIndicatorAnimation = true;
- cheatsheetRoot.selectedTab = currentIndex;
- }
+ implicitWidth: Math.max.apply(null, contentChildren.map(child => child.implicitWidth || 0))
+ implicitHeight: Math.max.apply(null, contentChildren.map(child => child.implicitHeight || 0))
clip: true
layer.enabled: true
diff --git a/dots/.config/quickshell/ii/modules/cheatsheet/CheatsheetPeriodicTable.qml b/dots/.config/quickshell/ii/modules/cheatsheet/CheatsheetPeriodicTable.qml
index 158e707d3..6262ed77a 100644
--- a/dots/.config/quickshell/ii/modules/cheatsheet/CheatsheetPeriodicTable.qml
+++ b/dots/.config/quickshell/ii/modules/cheatsheet/CheatsheetPeriodicTable.qml
@@ -11,6 +11,7 @@ Item {
Column {
id: mainLayout
+ anchors.centerIn: parent
spacing: root.spacing
Repeater { // Main table rows
diff --git a/dots/.config/quickshell/ii/modules/cheatsheet/ElementTile.qml b/dots/.config/quickshell/ii/modules/cheatsheet/ElementTile.qml
index d84abedfd..e05b76a9c 100644
--- a/dots/.config/quickshell/ii/modules/cheatsheet/ElementTile.qml
+++ b/dots/.config/quickshell/ii/modules/cheatsheet/ElementTile.qml
@@ -7,8 +7,8 @@ RippleButton {
id: root
required property var element
opacity: element.type != "empty" ? 1 : 0
- implicitHeight: 60
- implicitWidth: 60
+ implicitHeight: 70
+ implicitWidth: 70
colBackground: Appearance.colors.colLayer2
buttonRadius: Appearance.rounding.small
diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml
index c72263a9c..4d4d56335 100644
--- a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml
+++ b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml
@@ -51,8 +51,8 @@ Item {
id: activeIndicator
z: 0
color: Appearance.colors.colSecondaryContainer
- implicitWidth: contentItem.children[root.currentIndex].implicitWidth
- implicitHeight: contentItem.children[root.currentIndex].implicitHeight
+ implicitWidth: contentItem.children[root.currentIndex]?.implicitWidth ?? 0
+ implicitHeight: contentItem.children[root.currentIndex]?.implicitHeight ?? 0
radius: height / 2
// Animation
property Item targetItem: contentItem.children[root.currentIndex]
diff --git a/dots/.config/quickshell/ii/modules/regionSelector/OptionsToolbar.qml b/dots/.config/quickshell/ii/modules/regionSelector/OptionsToolbar.qml
index d4e54ddda..63a3e8c7e 100644
--- a/dots/.config/quickshell/ii/modules/regionSelector/OptionsToolbar.qml
+++ b/dots/.config/quickshell/ii/modules/regionSelector/OptionsToolbar.qml
@@ -65,18 +65,16 @@ Toolbar {
}
}
- IconAndTextToolbarButton {
- iconText: "activity_zone"
- text: Translation.tr("Rect")
- toggled: root.selectionMode === RegionSelection.SelectionMode.RectCorners
- onClicked: root.selectionMode = RegionSelection.SelectionMode.RectCorners
- }
-
- IconAndTextToolbarButton {
- iconText: "gesture"
- text: Translation.tr("Circle")
- toggled: root.selectionMode === RegionSelection.SelectionMode.Circle
- onClicked: root.selectionMode = RegionSelection.SelectionMode.Circle
+ ToolbarTabBar {
+ id: tabBar
+ tabButtonList: [
+ {"icon": "activity_zone", "name": Translation.tr("Rect")},
+ {"icon": "gesture", "name": Translation.tr("Circle")}
+ ]
+ currentIndex: root.selectionMode === RegionSelection.SelectionMode.RectCorners ? 0 : 1
+ onCurrentIndexChanged: {
+ root.selectionMode = currentIndex === 0 ? RegionSelection.SelectionMode.RectCorners : RegionSelection.SelectionMode.Circle;
+ }
}
}
diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml
index eff0483d3..e4ded1eeb 100644
--- a/dots/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml
+++ b/dots/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml
@@ -13,27 +13,28 @@ import Quickshell.Io
Item {
id: root
+ property real padding: 4
property var inputField: messageInputField
property string commandPrefix: "/"
property var suggestionQuery: ""
property var suggestionList: []
- onFocusChanged: (focus) => {
+ onFocusChanged: focus => {
if (focus) {
- root.inputField.forceActiveFocus()
+ root.inputField.forceActiveFocus();
}
}
- Keys.onPressed: (event) => {
- messageInputField.forceActiveFocus()
+ Keys.onPressed: event => {
+ messageInputField.forceActiveFocus();
if (event.modifiers === Qt.NoModifier) {
if (event.key === Qt.Key_PageUp) {
- messageListView.contentY = Math.max(0, messageListView.contentY - messageListView.height / 2)
- event.accepted = true
+ messageListView.contentY = Math.max(0, messageListView.contentY - messageListView.height / 2);
+ event.accepted = true;
} else if (event.key === Qt.Key_PageDown) {
- messageListView.contentY = Math.min(messageListView.contentHeight - messageListView.height / 2, messageListView.contentY + messageListView.height / 2)
- event.accepted = true
+ messageListView.contentY = Math.min(messageListView.contentHeight - messageListView.height / 2, messageListView.contentY + messageListView.height / 2);
+ event.accepted = true;
}
}
if ((event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier) && event.key === Qt.Key_O) {
@@ -45,21 +46,21 @@ Item {
{
name: "attach",
description: Translation.tr("Attach a file. Only works with Gemini."),
- execute: (args) => {
+ execute: args => {
Ai.attachFile(args.join(" ").trim());
}
},
{
name: "model",
description: Translation.tr("Choose model"),
- execute: (args) => {
+ execute: args => {
Ai.setModel(args[0]);
}
},
{
name: "tool",
description: Translation.tr("Set the tool to use for the model."),
- execute: (args) => {
+ execute: args => {
// console.log(args)
if (args.length == 0 || args[0] == "get") {
Ai.addMessage(Translation.tr("Usage: %1tool TOOL_NAME").arg(root.commandPrefix), Ai.interfaceRole);
@@ -75,7 +76,7 @@ Item {
{
name: "prompt",
description: Translation.tr("Set the system prompt for the model."),
- execute: (args) => {
+ execute: args => {
if (args.length === 0 || args[0] === "get") {
Ai.printPrompt();
return;
@@ -86,9 +87,9 @@ Item {
{
name: "key",
description: Translation.tr("Set API key"),
- execute: (args) => {
+ execute: args => {
if (args[0] == "get") {
- Ai.printApiKey()
+ Ai.printApiKey();
} else {
Ai.setApiKey(args[0]);
}
@@ -97,25 +98,25 @@ Item {
{
name: "save",
description: Translation.tr("Save chat"),
- execute: (args) => {
- const joinedArgs = args.join(" ")
+ execute: args => {
+ const joinedArgs = args.join(" ");
if (joinedArgs.trim().length == 0) {
Ai.addMessage(Translation.tr("Usage: %1save CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole);
return;
}
- Ai.saveChat(joinedArgs)
+ Ai.saveChat(joinedArgs);
}
},
{
name: "load",
description: Translation.tr("Load chat"),
- execute: (args) => {
- const joinedArgs = args.join(" ")
+ execute: args => {
+ const joinedArgs = args.join(" ");
if (joinedArgs.trim().length == 0) {
Ai.addMessage(Translation.tr("Usage: %1load CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole);
return;
}
- Ai.loadChat(joinedArgs)
+ Ai.loadChat(joinedArgs);
}
},
{
@@ -128,10 +129,10 @@ Item {
{
name: "temp",
description: Translation.tr("Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5."),
- execute: (args) => {
+ execute: args => {
// console.log(args)
if (args.length == 0 || args[0] == "get") {
- Ai.printTemperature()
+ Ai.printTemperature();
} else {
const temp = parseFloat(args[0]);
Ai.setTemperature(temp);
@@ -191,8 +192,7 @@ Inline w/ double dollar signs: $$\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\p
Inline w/ backslash and square brackets \\[\\int_0^\\infty \\frac{1}{x^2} dx = \\infty\\]
Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
-`,
- Ai.interfaceRole);
+`, Ai.interfaceRole);
}
},
]
@@ -208,13 +208,12 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
} else {
Ai.addMessage(Translation.tr("Unknown command: ") + command, Ai.interfaceRole);
}
- }
- else {
+ } else {
Ai.sendUserMessage(inputText);
}
-
+
// Always scroll to bottom when user sends a message
- messageListView.positionViewAtEnd()
+ messageListView.positionViewAtEnd();
}
Process {
@@ -223,16 +222,14 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
property string imageDecodeFileName: "image"
property string imageDecodeFilePath: `${imageDecodePath}/${imageDecodeFileName}`
function handleEntry(entry: string) {
- imageDecodeFileName = parseInt(entry.match(/^(\d+)\t/)[1])
- decodeImageAndAttachProc.exec(["bash", "-c",
- `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'`
- ])
+ imageDecodeFileName = parseInt(entry.match(/^(\d+)\t/)[1]);
+ decodeImageAndAttachProc.exec(["bash", "-c", `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'`]);
}
onExited: (exitCode, exitStatus) => {
if (exitCode === 0) {
Ai.attachFile(imageDecodeFilePath);
} else {
- console.error("[AiChat] Failed to decode image in clipboard content")
+ console.error("[AiChat] Failed to decode image in clipboard content");
}
}
}
@@ -278,37 +275,14 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
ColumnLayout {
id: columnLayout
- anchors.fill: parent
-
- RowLayout { // Status
- Layout.alignment: Qt.AlignHCenter
- spacing: 10
-
- StatusItem {
- icon: Ai.currentModelHasApiKey ? "key" : "key_off"
- statusText: ""
- description: Ai.currentModelHasApiKey ? Translation.tr("API key is set\nChange with /key YOUR_API_KEY") : Translation.tr("No API key\nSet it with /key YOUR_API_KEY")
- }
- StatusSeparator {}
- StatusItem {
- icon: "device_thermostat"
- statusText: Ai.temperature.toFixed(1)
- description: Translation.tr("Temperature\nChange with /temp VALUE")
- }
- StatusSeparator {
- visible: Ai.tokenCount.total > 0
- }
- StatusItem {
- visible: Ai.tokenCount.total > 0
- icon: "token"
- statusText: Ai.tokenCount.total
- description: Translation.tr("Total token count\nInput: %1\nOutput: %2")
- .arg(Ai.tokenCount.input)
- .arg(Ai.tokenCount.output)
- }
+ anchors {
+ fill: parent
+ margins: root.padding
}
+ spacing: root.padding
- Item { // Messages
+ Item {
+ // Messages
Layout.fillWidth: true
Layout.fillHeight: true
layer.enabled: true
@@ -320,6 +294,55 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
}
}
+ StyledRectangularShadow {
+ z: 1
+ target: statusBg
+ opacity: messageListView.atYBeginning ? 0 : 1
+ visible: opacity > 0
+ Behavior on opacity {
+ animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
+ }
+ }
+ Rectangle {
+ id: statusBg
+ z: 2
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: parent.top
+ topMargin: 4
+ }
+ implicitWidth: statusRowLayout.implicitWidth + 10 * 2
+ implicitHeight: Math.max(statusRowLayout.implicitHeight, 38)
+ radius: Appearance.rounding.normal - root.padding
+ color: Appearance.colors.colLayer2
+ RowLayout {
+ id: statusRowLayout
+ anchors.centerIn: parent
+ spacing: 10
+
+ StatusItem {
+ icon: Ai.currentModelHasApiKey ? "key" : "key_off"
+ statusText: ""
+ description: Ai.currentModelHasApiKey ? Translation.tr("API key is set\nChange with /key YOUR_API_KEY") : Translation.tr("No API key\nSet it with /key YOUR_API_KEY")
+ }
+ StatusSeparator {}
+ StatusItem {
+ icon: "device_thermostat"
+ statusText: Ai.temperature.toFixed(1)
+ description: Translation.tr("Temperature\nChange with /temp VALUE")
+ }
+ StatusSeparator {
+ visible: Ai.tokenCount.total > 0
+ }
+ StatusItem {
+ visible: Ai.tokenCount.total > 0
+ icon: "token"
+ statusText: Ai.tokenCount.total
+ description: Translation.tr("Total token count\nInput: %1\nOutput: %2").arg(Ai.tokenCount.input).arg(Ai.tokenCount.output)
+ }
+ }
+ }
+
ScrollEdgeFade {
z: 1
target: messageListView
@@ -332,16 +355,20 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
anchors.fill: parent
spacing: 10
popin: false
+ topMargin: statusBg.implicitHeight + statusBg.anchors.topMargin * 2
touchpadScrollFactor: Config.options.interactions.scrolling.touchpadScrollFactor * 1.4
mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4
property int lastResponseLength: 0
onContentHeightChanged: {
- if (atYEnd) Qt.callLater(positionViewAtEnd);
+ if (atYEnd)
+ Qt.callLater(positionViewAtEnd);
}
- onCountChanged: { // Auto-scroll when new messages are added
- if (atYEnd) Qt.callLater(positionViewAtEnd);
+ onCountChanged: {
+ // Auto-scroll when new messages are added
+ if (atYEnd)
+ Qt.callLater(positionViewAtEnd);
}
add: null // Prevent function calls from being janky
@@ -357,7 +384,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
required property int index
messageIndex: index
messageData: {
- Ai.messageByID[modelData]
+ Ai.messageByID[modelData];
}
messageInputField: root.inputField
}
@@ -393,8 +420,8 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
Repeater {
id: suggestionRepeater
model: {
- suggestions.selectedIndex = 0
- return root.suggestionList.slice(0, 10)
+ suggestions.selectedIndex = 0;
+ return root.suggestionList.slice(0, 10);
}
delegate: ApiCommandButton {
id: commandButton
@@ -413,7 +440,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
}
}
onClicked: {
- suggestions.acceptSuggestion(modelData.name)
+ suggestions.acceptSuggestion(modelData.name);
}
}
}
@@ -443,14 +470,10 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
id: inputWrapper
property real spacing: 5
Layout.fillWidth: true
- radius: Appearance.rounding.small
- color: Appearance.colors.colLayer1
- implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin
- + commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + spacing, 45)
- + (attachedFileIndicator.implicitHeight + spacing + attachedFileIndicator.anchors.topMargin)
+ radius: Appearance.rounding.normal - root.padding
+ color: Appearance.colors.colLayer2
+ implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin + commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + spacing, 45) + (attachedFileIndicator.implicitHeight + spacing + attachedFileIndicator.anchors.topMargin)
clip: true
- border.color: Appearance.colors.colOutlineVariant
- border.width: 1
Behavior on implicitHeight {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
@@ -488,121 +511,122 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
background: null
- onTextChanged: { // Handle suggestions
+ onTextChanged: {
+ // Handle suggestions
if (messageInputField.text.length === 0) {
- root.suggestionQuery = ""
- root.suggestionList = []
- return
+ root.suggestionQuery = "";
+ root.suggestionList = [];
+ return;
} else if (messageInputField.text.startsWith(`${root.commandPrefix}model`)) {
- root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
+ root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "";
const modelResults = Fuzzy.go(root.suggestionQuery, Ai.modelList.map(model => {
return {
name: Fuzzy.prepare(model),
- obj: model,
- }
+ obj: model
+ };
}), {
all: true,
key: "name"
- })
+ });
root.suggestionList = modelResults.map(model => {
return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "model ") : ""}${model.target}`,
displayName: `${Ai.models[model.target].name}`,
- description: `${Ai.models[model.target].description}`,
- }
- })
+ description: `${Ai.models[model.target].description}`
+ };
+ });
} else if (messageInputField.text.startsWith(`${root.commandPrefix}prompt`)) {
- root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
+ root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "";
const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.promptFiles.map(file => {
return {
name: Fuzzy.prepare(file),
- obj: file,
- }
+ obj: file
+ };
}), {
all: true,
key: "name"
- })
+ });
root.suggestionList = promptFileResults.map(file => {
return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "prompt ") : ""}${file.target}`,
displayName: `${FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target))}`,
- description: Translation.tr("Load prompt from %1").arg(file.target),
- }
- })
+ description: Translation.tr("Load prompt from %1").arg(file.target)
+ };
+ });
} else if (messageInputField.text.startsWith(`${root.commandPrefix}save`)) {
- root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
+ root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "";
const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => {
return {
name: Fuzzy.prepare(file),
- obj: file,
- }
+ obj: file
+ };
}), {
all: true,
key: "name"
- })
+ });
root.suggestionList = promptFileResults.map(file => {
- const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim()
+ const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim();
return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "save ") : ""}${chatName}`,
displayName: `${chatName}`,
- description: Translation.tr("Save chat to %1").arg(chatName),
- }
- })
+ description: Translation.tr("Save chat to %1").arg(chatName)
+ };
+ });
} else if (messageInputField.text.startsWith(`${root.commandPrefix}load`)) {
- root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
+ root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "";
const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => {
return {
name: Fuzzy.prepare(file),
- obj: file,
- }
+ obj: file
+ };
}), {
all: true,
key: "name"
- })
+ });
root.suggestionList = promptFileResults.map(file => {
- const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim()
+ const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim();
return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "load ") : ""}${chatName}`,
displayName: `${chatName}`,
- description: Translation.tr(`Load chat from %1`).arg(file.target),
- }
- })
+ description: Translation.tr(`Load chat from %1`).arg(file.target)
+ };
+ });
} else if (messageInputField.text.startsWith(`${root.commandPrefix}tool`)) {
- root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
+ root.suggestionQuery = messageInputField.text.split(" ")[1] ?? "";
const toolResults = Fuzzy.go(root.suggestionQuery, Ai.availableTools.map(tool => {
return {
name: Fuzzy.prepare(tool),
- obj: tool,
- }
+ obj: tool
+ };
}), {
all: true,
key: "name"
- })
+ });
root.suggestionList = toolResults.map(tool => {
- const toolName = tool.target
+ const toolName = tool.target;
return {
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "tool ") : ""}${tool.target}`,
displayName: toolName,
- description: Ai.toolDescriptions[toolName],
- }
- })
- } else if(messageInputField.text.startsWith(root.commandPrefix)) {
- root.suggestionQuery = messageInputField.text
+ description: Ai.toolDescriptions[toolName]
+ };
+ });
+ } else if (messageInputField.text.startsWith(root.commandPrefix)) {
+ root.suggestionQuery = messageInputField.text;
root.suggestionList = root.allCommands.filter(cmd => cmd.name.startsWith(messageInputField.text.substring(1))).map(cmd => {
return {
name: `${root.commandPrefix}${cmd.name}`,
- description: `${cmd.description}`,
- }
- })
+ description: `${cmd.description}`
+ };
+ });
}
}
function accept() {
- root.handleInput(text)
- text = ""
+ root.handleInput(text);
+ text = "";
}
- Keys.onPressed: (event) => {
+ Keys.onPressed: event => {
if (event.key === Qt.Key_Tab) {
suggestions.acceptSelectedWord();
event.accepted = true;
@@ -615,35 +639,41 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
} else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) {
if (event.modifiers & Qt.ShiftModifier) {
// Insert newline
- messageInputField.insert(messageInputField.cursorPosition, "\n")
- event.accepted = true
- } else { // Accept text
- const inputText = messageInputField.text
- messageInputField.clear()
- root.handleInput(inputText)
- event.accepted = true
+ messageInputField.insert(messageInputField.cursorPosition, "\n");
+ event.accepted = true;
+ } else {
+ // Accept text
+ const inputText = messageInputField.text;
+ messageInputField.clear();
+ root.handleInput(inputText);
+ event.accepted = true;
}
- } else if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_V) { // Intercept Ctrl+V to handle image/file pasting
- if (event.modifiers & Qt.ShiftModifier) { // Let Shift+Ctrl+V = plain paste
- messageInputField.text += Quickshell.clipboardText
+ } else if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_V) {
+ // Intercept Ctrl+V to handle image/file pasting
+ if (event.modifiers & Qt.ShiftModifier) {
+ // Let Shift+Ctrl+V = plain paste
+ messageInputField.text += Quickshell.clipboardText;
event.accepted = true;
return;
}
// Try image paste first
- const currentClipboardEntry = Cliphist.entries[0]
- const cleanCliphistEntry = StringUtils.cleanCliphistEntry(currentClipboardEntry)
- if (/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(currentClipboardEntry)) { // First entry = currently copied entry = image?
- decodeImageAndAttachProc.handleEntry(currentClipboardEntry)
+ const currentClipboardEntry = Cliphist.entries[0];
+ const cleanCliphistEntry = StringUtils.cleanCliphistEntry(currentClipboardEntry);
+ if (/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(currentClipboardEntry)) {
+ // First entry = currently copied entry = image?
+ decodeImageAndAttachProc.handleEntry(currentClipboardEntry);
event.accepted = true;
return;
- } else if (cleanCliphistEntry.startsWith("file://")) { // First entry = currently copied entry = image?
- const fileName = decodeURIComponent(cleanCliphistEntry)
+ } else if (cleanCliphistEntry.startsWith("file://")) {
+ // First entry = currently copied entry = image?
+ const fileName = decodeURIComponent(cleanCliphistEntry);
Ai.attachFile(fileName);
event.accepted = true;
return;
}
event.accepted = false; // No image, let text pasting proceed
- } else if (event.key === Qt.Key_Escape) { // Esc to detach file
+ } else if (event.key === Qt.Key_Escape) {
+ // Esc to detach file
if (Ai.pendingFilePath.length > 0) {
Ai.attachFile("");
event.accepted = true;
@@ -668,19 +698,18 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
anchors.fill: parent
cursorShape: sendButton.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: {
- const inputText = messageInputField.text
- root.handleInput(inputText)
- messageInputField.clear()
+ const inputText = messageInputField.text;
+ root.handleInput(inputText);
+ messageInputField.clear();
}
}
contentItem: MaterialSymbol {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
- iconSize: Appearance.font.pixelSize.larger
- // fill: sendButton.enabled ? 1 : 0
+ iconSize: 22
color: sendButton.enabled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2Disabled
- text: "send"
+ text: "arrow_upward"
}
}
}
@@ -699,59 +728,58 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
{
name: "",
sendDirectly: false,
- dontAddSpace: true,
- },
+ dontAddSpace: true
+ },
{
name: "clear",
- sendDirectly: true,
- },
+ sendDirectly: true
+ },
]
- ApiInputBoxIndicator { // Model indicator
+ ApiInputBoxIndicator {
+ // Model indicator
icon: "api"
text: Ai.getModel().name
- tooltipText: Translation.tr("Current model: %1\nSet it with %2model MODEL")
- .arg(Ai.getModel().name)
- .arg(root.commandPrefix)
+ tooltipText: Translation.tr("Current model: %1\nSet it with %2model MODEL").arg(Ai.getModel().name).arg(root.commandPrefix)
}
- ApiInputBoxIndicator { // Tool indicator
+ ApiInputBoxIndicator {
+ // Tool indicator
icon: "service_toolbox"
text: Ai.currentTool.charAt(0).toUpperCase() + Ai.currentTool.slice(1)
- tooltipText: Translation.tr("Current tool: %1\nSet it with %2tool TOOL")
- .arg(Ai.currentTool)
- .arg(root.commandPrefix)
+ tooltipText: Translation.tr("Current tool: %1\nSet it with %2tool TOOL").arg(Ai.currentTool).arg(root.commandPrefix)
}
- Item { Layout.fillWidth: true }
+ Item {
+ Layout.fillWidth: true
+ }
- ButtonGroup { // Command buttons
+ ButtonGroup {
+ // Command buttons
padding: 0
- Repeater { // Command buttons
+ Repeater {
+ // Command buttons
model: commandButtonsRow.commandsShown
delegate: ApiCommandButton {
property string commandRepresentation: `${root.commandPrefix}${modelData.name}`
buttonText: commandRepresentation
downAction: () => {
if (modelData.sendDirectly) {
- root.handleInput(commandRepresentation)
+ root.handleInput(commandRepresentation);
} else {
- messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " ")
- messageInputField.cursorPosition = messageInputField.text.length
- messageInputField.forceActiveFocus()
+ messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " ");
+ messageInputField.cursorPosition = messageInputField.text.length;
+ messageInputField.forceActiveFocus();
}
if (modelData.name === "clear") {
- messageInputField.text = ""
+ messageInputField.text = "";
}
}
}
}
}
}
-
}
-
}
-
}
diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml
index 1e1d483c9..ff4b22d42 100644
--- a/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml
+++ b/dots/.config/quickshell/ii/modules/sidebarLeft/Anime.qml
@@ -12,6 +12,8 @@ import Quickshell
Item {
id: root
+ property real padding: 4
+
property var inputField: tagInputField
readonly property var responses: Booru.responses
property string previewDownloadPath: Directories.booruPreviews
@@ -141,7 +143,11 @@ Item {
ColumnLayout {
id: columnLayout
- anchors.fill: parent
+ anchors {
+ fill: parent
+ margins: root.padding
+ }
+ spacing: root.padding
Item {
Layout.fillWidth: true
@@ -317,14 +323,12 @@ Item {
id: tagInputContainer
property real columnSpacing: 5
Layout.fillWidth: true
- radius: Appearance.rounding.small
- color: Appearance.colors.colLayer1
+ radius: Appearance.rounding.normal - root.padding
+ color: Appearance.colors.colLayer2
implicitWidth: tagInputField.implicitWidth
implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin
+ commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + columnSpacing, 45)
clip: true
- border.color: Appearance.colors.colOutlineVariant
- border.width: 1
Behavior on implicitHeight {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
@@ -456,10 +460,9 @@ Item {
contentItem: MaterialSymbol {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
- iconSize: Appearance.font.pixelSize.larger
- // fill: sendButton.enabled ? 1 : 0
+ iconSize: 22
color: sendButton.enabled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2Disabled
- text: "send"
+ text: "arrow_upward"
}
}
}
diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml
index 063a2866f..f3ebfd356 100644
--- a/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml
+++ b/dots/.config/quickshell/ii/modules/sidebarLeft/SidebarLeftContent.qml
@@ -37,14 +37,6 @@ Item {
swipeView.decrementCurrentIndex()
event.accepted = true;
}
- else if (event.key === Qt.Key_Tab) {
- swipeView.setCurrentIndex((swipeView.currentIndex + 1) % swipeView.count);
- event.accepted = true;
- }
- else if (event.key === Qt.Key_Backtab) {
- swipeView.setCurrentIndex((swipeView.currentIndex - 1 + swipeView.count) % swipeView.count);
- event.accepted = true;
- }
}
}
@@ -66,29 +58,36 @@ Item {
}
}
- SwipeView { // Content pages
- id: swipeView
- Layout.topMargin: 5
+ Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
- spacing: 10
- currentIndex: tabBar.currentIndex
+ implicitWidth: swipeView.implicitWidth
+ implicitHeight: swipeView.implicitHeight
+ radius: Appearance.rounding.normal
+ color: Appearance.colors.colLayer1
- clip: true
- layer.enabled: true
- layer.effect: OpacityMask {
- maskSource: Rectangle {
- width: swipeView.width
- height: swipeView.height
- radius: Appearance.rounding.small
+ SwipeView { // Content pages
+ id: swipeView
+ anchors.fill: parent
+ spacing: 10
+ currentIndex: tabBar.currentIndex
+
+ clip: true
+ layer.enabled: true
+ layer.effect: OpacityMask {
+ maskSource: Rectangle {
+ width: swipeView.width
+ height: swipeView.height
+ radius: Appearance.rounding.small
+ }
}
- }
- contentChildren: [
- ...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []),
- ...(root.translatorEnabled ? [translator.createObject()] : []),
- ...(root.animeEnabled ? [anime.createObject()] : [])
- ]
+ contentChildren: [
+ ...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []),
+ ...(root.translatorEnabled ? [translator.createObject()] : []),
+ ...(root.animeEnabled ? [anime.createObject()] : [])
+ ]
+ }
}
Component {
diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/Translator.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/Translator.qml
index 41f4fffab..bde48f532 100644
--- a/dots/.config/quickshell/ii/modules/sidebarLeft/Translator.qml
+++ b/dots/.config/quickshell/ii/modules/sidebarLeft/Translator.qml
@@ -13,17 +13,24 @@ import Quickshell.Io
*/
Item {
id: root
+
+ // Sizes
+ property real padding: 4
+
// Widgets
property var inputField: inputCanvas.inputTextArea
+
// Widget variables
property bool translationFor: false // Indicates if the translation is for an autocorrected text
property string translatedText: ""
property list languages: []
+
// Options
property string targetLanguage: Config.options.language.translator.targetLanguage
property string sourceLanguage: Config.options.language.translator.sourceLanguage
property string hostLanguage: targetLanguage
+ // States
property bool showLanguageSelector: false
property bool languageSelectorTarget: false // true for target language, false for source language
@@ -99,7 +106,11 @@ Item {
}
ColumnLayout {
- anchors.fill: parent
+ anchors {
+ fill: parent
+ margins: root.padding
+ }
+
StyledFlickable {
Layout.fillWidth: true
Layout.fillHeight: true
diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/translator/TextCanvas.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/translator/TextCanvas.qml
index 7b4841217..b585a07d7 100644
--- a/dots/.config/quickshell/ii/modules/sidebarLeft/translator/TextCanvas.qml
+++ b/dots/.config/quickshell/ii/modules/sidebarLeft/translator/TextCanvas.qml
@@ -17,10 +17,8 @@ Rectangle {
default property alias actionButtons: actions.data
Layout.fillWidth: true
implicitHeight: Math.max(150, inputColumn.implicitHeight)
- color: isInput ? Appearance.colors.colLayer1 : Appearance.colors.colSurfaceContainer
+ color: Appearance.colors.colLayer2
radius: Appearance.rounding.normal
- border.color: isInput ? Appearance.colors.colOutlineVariant : "transparent"
- border.width: isInput ? 1 : 0
signal inputTextChanged(); // Signal emitted when text changes