feat: add support for custom user action scripts

Add ability to define custom actions by placing executable scripts in
~/.config/illogical-impulse/actions/

- Script filename becomes the action name (extension stripped)
- Use /script-name in search bar to execute
- Arguments are passed to the script
- Uses FolderListModel for auto-reload when scripts are added/removed
- Follows existing pattern of user-customizable directories (ai/prompts)
This commit is contained in:
stewart86
2025-12-10 18:10:15 +08:00
parent 80f4a0549c
commit b966e2d539
2 changed files with 35 additions and 1 deletions
@@ -44,6 +44,7 @@ Singleton {
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/switchwall.sh`)
property string defaultAiPrompts: Quickshell.shellPath("defaults/ai/prompts")
property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`)
property string userActions: FileUtils.trimFileProtocol(`${Directories.shellConfig}/actions`)
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`)
@@ -59,6 +60,7 @@ Singleton {
Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`])
Quickshell.execDetached(["mkdir", "-p", `${aiChats}`])
Quickshell.execDetached(["mkdir", "-p", `${userActions}`])
Quickshell.execDetached(["rm", "-rf", `${tempImages}`])
}
}
@@ -4,6 +4,7 @@ import qs.modules.common
import qs.modules.common.models
import qs.modules.common.functions
import QtQuick
import Qt.labs.folderlistmodel
import Quickshell
import Quickshell.Io
@@ -31,6 +32,34 @@ Singleton {
return acc;
}, []).sort()
// Load user action scripts from ~/.config/illogical-impulse/actions/
// Uses FolderListModel to auto-reload when scripts are added/removed
property var userActionScripts: {
const actions = [];
for (let i = 0; i < userActionsFolder.count; i++) {
const fileName = userActionsFolder.get(i, "fileName");
const filePath = userActionsFolder.get(i, "filePath");
if (fileName && filePath) {
const actionName = fileName.replace(/\.[^/.]+$/, ""); // strip extension
actions.push({
action: actionName,
execute: ((path) => (args) => {
Quickshell.execDetached([path, ...(args ? args.split(" ") : [])]);
})(filePath.toString().replace("file://", ""))
});
}
}
return actions;
}
FolderListModel {
id: userActionsFolder
folder: `file://${Directories.userActions}`
showDirs: false
showHidden: false
sortField: FolderListModel.Name
}
property var searchActions: [
{
action: "accentcolor",
@@ -90,6 +119,9 @@ Singleton {
},
]
// Combined built-in and user actions
property var allActions: searchActions.concat(userActionScripts)
property string mathResult: ""
property bool clipboardWorkSafetyActive: {
const enabled = Config.options.workSafety.enable.clipboard;
@@ -273,7 +305,7 @@ Singleton {
Qt.openUrlExternally(url);
}
});
const launcherActionObjects = root.searchActions.map(action => {
const launcherActionObjects = root.allActions.map(action => {
const actionString = `${Config.options.search.prefix.action}${action.action}`;
if (actionString.startsWith(root.query) || root.query.startsWith(actionString)) {
return resultComp.createObject(null, {