See video. Any ideas?
In the video:
I have condition that when active window title is AI Chat then ⌘+D outpuds ⇧⌘S. In BetterTouchTool I have ⌘+D set as Enable/Disable Selected Trigger, which I show is working when this Trigger is Disabled. However when I enable it ⌘+D doesnt work anymore, because clearly it is errenously thinking that the current active window title is "AI Chat" (when it is not, it is BetterTouchTool, also the conditional output "currently false") since It is most likely now outputting ⇧⌘S instead of ⌘D, making the keyboard shortcut not work.
this is a limitation of advanced trigger conditions because they are evaluated after the event has been caught (see Trigger Conditions & Conditional Activation Groups · GitBook )
You‘d need to setup a duplicate with the inverse / negated condition that sends cmd+d
I'm having difficulty understanding - the shortcut doesn't function within the AI Chat window either, so is the focused window title condition essentially redundant? Do conditional activation groups offer a solution? I notice they don't seem to accommodate window title conditions.
The challenge lies with these specific windows in Raycast that require me to implement a complex system because:
- The Raycast launcher window (similar to Spotlight) is a special type of window - I'm not familiar with the technical terminology - where the active application name remains unchanged. This requires detection and implementation of an IF statement using a method like this:
- Additionally, there are two custom windows within Raycast - AI Chat and Raycast Notes - for which I'd like to set up custom keyboard shortcuts. And with BetterTouchTool I was thinking I had arrived at the solution with Window Title conditionals since these are different windows within same app. However, I haven't found an effective way to implement this, since they always end up clashing with each other in some way, nor have I discovered a working system that accommodates all three windows successfully. I've been grappling with this issue for quite some time. Very frustrating and confusing!
. I would greatly appreciate any guidance in the right direction!
Does the focused_window_title update in the BTT condition viewer if you activate these windows? It is possible they are non-activating, thus they will never be focused.
In that case you'd need to use the "visible window list" condition with a contains check:
Yes they actually turn to true:
In visible window list there are 4 Raycast windows:
Raycast - (null) - 0 = AI Chat window
Raycast - (null) - 8 = Raycast Notes
Raycast - (null) - 8 = Launcher/Spotlight window
So I actually can use the visible window list condition to set specific shortcuts for AI chat window This is great.
However Raycast Notes and the Launcher have same window list title so not a full solution with this method it seems.
I tried to gather all the data about all the three windows here:
Just realized that even though the visible window list works for ai chat, it seems to do a false positive also for other apps.
Seems same thing when using Conditional Activation Groups
(btw is this mistake that the condition is called Window Name? Is it supposed to be window title like it's called in the right sidebar? Is it just the same check as window title check from inside the trigger itself?)
Tried the inverse trigger method, no luck with either.
I think the problem is not within BTT but with the windows. If you use a Conditional Activation Group and the window title would be updated correctly, then it should work fine. However I think these are pretty special windows that can not be detected via the standard mechanisms.
You can try enabling this option on your CAG, this might help if the windows do not send a change notification
Window Name / Title are the same thing however the mechanism of retrieving it between advanced trigger conditions and conditional acitvation groups is very different.
Didn't work however Claude found the solution
These scripts detect specific Raycast windows using CoreGraphics in Keyboard Maestro's "If Then Else" actions with "Execute JavaScript for Automation".
Window Properties
Each Raycast window type has unique identifiers:
- Launcher: Layer 8, Window #8762
- Notes: Layer 8, Window #8763
- AI Chat: Layer 0, Window #8764
Detection Scripts
Launcher Detection
(() => {
ObjC.import("CoreGraphics");
const windowList = ObjC.castRefToObject($.CGWindowListCopyWindowInfo($.kCGWindowListOptionOnScreenOnly, $.kCGNullWindowID))
const raycastWindow = windowList.js.find(win =>
win.js["kCGWindowOwnerName"].js == "Raycast" &&
win.js["kCGWindowIsOnscreen"] &&
win.js["kCGWindowLayer"].js === 8 &&
win.js["kCGWindowNumber"].js === 8762
);
return raycastWindow !== undefined;
})()
Notes Detection
(() => {
ObjC.import("CoreGraphics");
const windowList = ObjC.castRefToObject($.CGWindowListCopyWindowInfo($.kCGWindowListOptionOnScreenOnly, $.kCGNullWindowID))
const raycastWindow = windowList.js.find(win =>
win.js["kCGWindowOwnerName"].js == "Raycast" &&
win.js["kCGWindowIsOnscreen"] &&
win.js["kCGWindowLayer"].js === 8 &&
win.js["kCGWindowNumber"].js === 8763
);
return raycastWindow !== undefined;
})()
AI Chat Detection
(() => {
ObjC.import("CoreGraphics");
const windowList = ObjC.castRefToObject($.CGWindowListCopyWindowInfo($.kCGWindowListOptionOnScreenOnly, $.kCGNullWindowID))
const raycastWindow = windowList.js.find(win =>
win.js["kCGWindowOwnerName"].js == "Raycast" &&
win.js["kCGWindowIsOnscreen"] &&
win.js["kCGWindowLayer"].js === 0 &&
win.js["kCGWindowNumber"].js === 8764
);
return raycastWindow !== undefined;
})()
Usage in Keyboard Maestro
- Add an "If Then Else" action
- Choose "Execute JavaScript for Automation" as the condition
- Paste the appropriate script
- Add your desired actions in the If/Else branches
Testing/Debugging
For debugging window properties, use this script in Script Editor:
(() => {
ObjC.import("CoreGraphics");
delay(3); // WAIT 3 SECONDS FOR LAUNCHER
const windowList = ObjC.castRefToObject($.CGWindowListCopyWindowInfo($.kCGWindowListOptionOnScreenOnly, $.kCGNullWindowID))
const raycastWindows = windowList.js.filter(win =>
win.js["kCGWindowOwnerName"].js == "Raycast"
);
return JSON.stringify({
message: "Window test - all Raycast windows:",
totalWindows: raycastWindows.length,
windows: raycastWindows.map(win => ({
layer: win.js["kCGWindowLayer"].js,
number: win.js["kCGWindowNumber"].js,
isOnscreen: win.js["kCGWindowIsOnscreen"] ? win.js["kCGWindowIsOnscreen"].js : false,
alpha: win.js["kCGWindowAlpha"] ? win.js["kCGWindowAlpha"].js : null
}))
}, null, 2);
})()
Important Notes
- Window numbers appear to be consistent across sessions
- Each script checks for:
- Raycast ownership
- Window visibility
- Specific layer
- Specific window number
- When testing in Script Editor, remember to add the 3-second delay for launcher detection
- No delay needed in Keyboard Maestro scripts
References
- CoreGraphics window detection
- Keyboard Maestro JavaScript conditions
- JavaScript for Automation (JXA)
these numbers will change whenever you restart your mac (there might be situations where they stay the same for a while, but this is absolutely not guaranteed)
(btw. without the window number this is essentially the same as using the visible window list )
Ah ok. Yep completely correct the window numbers did change on restart. However at least doing this if function works for the launcher if I forget about Raycast Notes for now:
So at least I can protect my already in place macros from the launcher window.
However, If I want to change a shortcut in the launcher, for examle I want to change ⌘S to ⌘Y but I can't because I get a conflict pallette if my active app also has a ⌘S trigger inside Keyboard Maestro.
Is it possible to do this if check with a JXA script inside BetterTouchTool? Can't get any of the other methods to work with BetterTouchTool for these windows.