Using Frontmost Application?

Hi folks.

I’m designing a script for Launchbar, which should copy the currently selected text, do something like substitute characters within that text (this is done in Ruby), then paste that text back where the original focus was.

Example 1: Finder file filename is Plugable-ud-ultc4k-triple-4k-display-dock-ports_thumb800-2877640892.jpg and I want to change the dashes to proper spaces.

In this instance, the app is the Finder igself. But I want this script to sniff out what app I’m in, save that in a variable, do the text manipulation (Ruby), then go back to that same app and replace the text. BBEdit is where I live and I often just use these scripts there, but I’d like to extend this to any app. Or hope to do so. I can see some limitations as I have to activate the editing of the filename for the Finder to work. All the same, it saves a lot of frustrating filename edits.

Here is what I have for BBEdit so far, which works on a library of edits:

[format]def osascript(script) = system ‘osascript’, *script.split(/\n/).map { |line| [‘-e’, line] }.flatten
mys=<<-TEXT
tell application “BBEdit” to set the clipboard to (selection as text)
TEXT
osascript(mys)

Clipboard.copy(Clipboard.paste.gsub(‘-’, ’ '))

mys=<<-TEXT
tell application “System Events” to keystroke “v” using command down
TEXT
osascript(mys)[/format]

The issue I have is with the return of the modified text to the original app.

tell application "<original app>" to keystroke "v" using command down

Will that variable work in there?

A Script Menu script I have does it like so:

tell application id "sevs" to set appFront to name of first process whose frontmost is true
-- <snip>
tell application id "sevs"
	tell process appFront
		keystroke "your text here"
	end tell
end tell

Further investigation has me using this, which works in the Script Editor:

tell application "System Events" to set frontApp to name of first application process whose frontmost is true

However, when launched from Launchbar, that focus disappears. So I can’t even get to recognizing the text, because the script has no app reference.

Not sure how to get around this. But ya, your fist line is the same as mine.

@dabee,

I would create Automator Quick Action (service), which receives: Text, from application: Any. Then I would assign system-wide shortcut in the System Preferences to this service.

When executing compiled script, current application (and frontmost) is editor. When executing script saved as application, current application (and frontmost) is script app. So, if you still prefer the usual script (or script app), then you should hide the current application to get other front process.


set thisAppName to name of current application

tell application "System Events"
	set visible of process thisAppName to false
	set frontApp to name of first application process whose frontmost is true
end tell

NOTE: all keystrokes goes to front window (of frontmost process) telling only to System Events.app. So, telling some other app to keystroke doesn’t make any sense.

To test my suggestion, I created text in the TextEdit.app, then selected one piece of this text. Following script successfully replaces the selected piece of text with “Hello”:


set thisAppName to name of current application

tell application "System Events"
	set visible of process thisAppName to false
	set frontApp to name of first application process whose frontmost is true
	keystroke "Hello"
end tell

The keystrokes are about taking focus in the front app, then copying and pasting of modified text. I’ve used keystrokes (like copy and paste) in other apps all the time.

I’m not trying to replace it with static text. I need Ruby to modify the text accordingly. This doesn’t bring in that at all. I have no need to replace text with “Hello”.

As I said in the original post. I’m trying to modify the text that’s selected.

Plugable-ud-ultc4k-triple-4k-Port.jpg => Plugable ud ultc4k triple 4k Ports.jpg

I don’t force you to send “Hello”. This is just an example, isn’t it obvious to you? You can send another keystroke, and somewhere in the middle of the actual script, Ruby has to modify the contents of the clipboard. My script simply demonstrates how to work with the right front process.

The copy and paste you use all time in other apps is commands of this other apps. And the keystroke command belongs only to System Events.app

The issue with your approach is that you said to make something in Automator. THIS is what takes Ruby out of the equation. An Automator Quick Action in Launchbar means the script itself is run. As standalone. So there is no governing script that integrates Ruby at all. A master governing Ruby script can implement AppleScript through the syntax that I posted.

I see what you did by shoving the (either Launchbar or Automator Quick Action) focus aside, leaving the second-in-line app (the editor) having focus. Unfortunately this didn’t do the trick either. The problem is that this isn’t about visibility. You’re still living in an AppleScript world with that instruction. All this should be able to run in the background, as Launchbar disappears after you select an Action. This is why I’m choosing Ruby to use AppleScripts to bounce around, as the master controller. You know that AppleScript can instruct an application to accomplish something without having it visible or active.

Here is the script and the error:

require 'clipboard'
def osascript(script) = system 'osascript', *script.split(/\n/).map { |line| ['-e', line] }.flatten

# mys=<<-TEXT
# tell application "System Events" to keystroke "c" using command down
# TEXT

mys=<<-TEXT
set thisAppName to name of current application
tell application "System Events"
   set visible of process thisAppName to false
   set the clipboard to the name of first application process whose frontmost is true
end tell
TEXT
osascript(mys)

r = ''
text = Clipboard.paste
text.strip.split("\n").each do |i|
  begin
    stuff = i.strip.split(/\s*:\s*/)
    r += stuff.first.strip.rjust(find_largest_key(text)+5) + " : " + stuff[1].strip + "\n"
  rescue
    r += i
  end
end

Clipboard.copy(r)

mys=<<-TEXT
tell application "System Events" to keystroke "v" using command down
TEXT
osascript(mys)

[format]
RF spacecase (com.viaduct.LaunchBar.action.RFspacecase): Script Error: </Users/rich/.rbenv/shims/ruby /Users/rich/Library/Application Support/LaunchBar/Actions/RF spacecase.lbaction/Contents/Scripts/default.rb>
58:62: execution error: Can’t make selection of application “” into type text. (-1700)
[/format]

I think at this point, I simply need to get that app into the clipboard and reduce the rest of the functionality, to keep this simple.

[format]require ‘clipboard’
def osascript(script) = system ‘osascript’, *script.split(/\n/).map { |line| [‘-e’, line] }.flatten
r = ‘’

mys=<<-TEXT
tell application “System Events” to set the clipboard to name of first application process whose frontmost is true
TEXT
osascript(mys)

origapp = Clipboard.paste
r += “origapp: #{ origapp }\n”

mys = “tell application "#{ origapp }" to set the clipboard to (selection as text)”
r += “mys: #{ mys }\n”
osascript(mys)

Clipboard.copy(r)

mys=<<-TEXT
tell application “BBEdit” to activate
tell application “System Events” to keystroke “v” using command down
TEXT
osascript(mys)[/format]

Unfortunately, I don’t understand anything about Ruby.

The only thing I can say is that the description of the error tells you not to try to use the reserved word text (which is the name of the class, otherwise the name of the type) as a variable.

The error should be here:

text = Clipboard.paste

Ya I’ve tried different things in that area. The issue is that I still can’t get the frontmost app, outside of any text manipulation.

I think here you need to send the keystroke to the target process:

Something like

set myProcess to first application process whose frontmost is true
set the clipboard to the name of myProccess

--<SNIP>--

tell application "System Events"  to tell myProcess to keystroke "v" using command down

Ya I thought as much. Problem is, myProcess is never defined. It won’t capture what I consider “my current app”.

The first part of what I posted defines myProcess.

As I understand it your current app is behind LaunchBar when executed. When you set the visible of LaunchBar to false, that makes your current app frontmost. Next, set the variable myProcess to that process and then direct your UI commands to that process within System Events.

Or am I missing something?

Yes. It doesn’t work.

OK, I can’t help with any of your Ruby stuff, so I’ve commented that all out, and this is just the appleScript stuff.

What I think is happening is that because your appleScripts segments are not part of the same script they are not sharing variables, so the variable myProcess, defined in the first appleScript segment is not defined in the second.

But, there’s a conflict in your script. When you do a command-c you’re setting the same clipboard that the “set the clipboard to” command does, so that overwrites the value the script copies with command-c.

The solution is to define myProcess again. This should work because that app should still be frontmost.

I tried this with a few different apps (including BBEdit) and it worked (but without the Ruby stuff)

 
--require 'clipboard'
--def osascript(script) = system 'osascript', *script.split(/\n/).map { |line| ['-e', line] }.flatten
--
--# mys=<<-TEXT
----# tell application "System Events" to keystroke "c" using command down--<<This line  conflicts with the clipboard command, and there's no need to have this script segment at all
--# TEXT
--
--mys=<<-TEXT
set thisAppName to name of current application
tell application "System Events"
	set visible of process thisAppName to false
	set myProcess to first application process whose frontmost is true
	--set processName to (the name of myProcess)
	tell myProcess
		keystroke "c" using command down
	end tell
	log (the clipboard)
	
end tell
--set the clipboard to processName--<<This line  conflicts with the keystroke command and there's no need for it now either. 
log (the clipboard) 

--TEXT
--osascript(mys)
--
--r = ''
--text = Clipboard.paste
--text.strip.split("\n").each do |i|
--  begin
--    stuff = i.strip.split(/\s*:\s*/)
--    r += stuff.first.strip.rjust(find_largest_key(text)+5) + " : " + stuff[1].strip + "\n"
--  rescue
--    r += i
--  end
--end
--
--Clipboard.copy(r)
--
--mys=<<-TEXT
tell application "System Events"
	log (the clipboard)
	set myProcess to first application process whose frontmost is true
	tell myProcess
		keystroke "v" using command down
	end tell
end tell

–osascript(mys)

If this doesn’t work you’ll need to find a way to move variables from AppleScript to Ruby and from Ruby to AppleScript.

Hope that helps.

Excellent. THIS works.

Not sure what log (the clipboard) means. Any chance you can elaborate on that quickly?

Here’s the final script:

[format]require ‘clipboard’
def osascript(script) = system ‘osascript’, *script.split(/\n/).map { |line| [‘-e’, line] }.flatten

mys=<<-TEXT
set thisAppName to name of current application
tell application “System Events”
set visible of process thisAppName to false
set myProcess to first application process whose frontmost is true
tell myProcess
keystroke “c” using command down
end tell
end tell
TEXT
osascript(mys)

Clipboard.copy(Clipboard.paste.gsub(‘-’, ’ '))

mys=<<-TEXT
tell application “System Events”
set myProcess to first application process whose frontmost is true
tell myProcess
keystroke “v” using command down
end tell
end tell
TEXT
osascript(mys)[/format]

As I see it, the correct solution is using my suggestion of process visibility. Let me remind you that @daBee answered me literally the following:

“I see what you did by shoving the (either Launchbar or Automator Quick Action) focus aside, leaving the second-in-line app (the editor) having focus. Unfortunately this didn’t do the trick either. The problem is that this isn’t about visibility.”

Also, I remind you that the keystroke commands belong to the System Events application itself. Therefore, processes do not need to be told.

mys=<<-TEXT
tell application “System Events” to keystroke “v” using command down
TEXT
osascript(mys)

The same with keystroke “c”

mys=<<-TEXT
set thisAppName to name of current application
tell application “System Events”
set visible of process thisAppName to false
set myProcess to first application process whose frontmost is true
keystroke “c” using command down
end tell
TEXT
osascript(mys)

Well, then visibility would have told me that the current focus on activating the LaunchBar script, would be something as frontmost application, which didn’t occur. It returned nil result. Automator further shields any interaction. Automator I would only use for direct applications, however packaged.

In any case, I’m happy it works now, which is the goal. Having to push aside an empty focus, is an AS issue. Other implementations using LaunchBar, is straight forward. Managing operations in specific applications is predictable.

Glad it worked.

If you’re editing AppleScripts in Script Editor or Script Debugger the log command shows you the value of whatever your are logging at that moment during execution of your script.

It’s ignored at runtime.

Speaking of automator, I believe you could use automator to build a service that would do the same thing, via a menu item. (Once that service is built and saved Automator would be out of the picture)

All the Apple documentations I’ve seen and all the expert tools (ScriptDebugger; UI Browser) all address UI commands to the process within System Events that own the UI elements.

It wasn’t long ago where someone was posting UI scripts that required delays without sending the commands to the process inside SE, and did not require delays once that was corrected.

OK, log X isn’t what I can use.

Automator is the last thing I would use. I live in another language and Launchbar allows that. It also allows the AS and Automator options, but they have been quite convoluted for…decades.

The errors/results that I see are in the Console, and even with successful results, Launchbar successes are…not presenting as I would expect.

Cheers.

Hey thanks for the insight/conversation. New stuff for an application (Launchbar) that isn’t clear.