I have Named Clipboards set up in Keyboard Maestro named like so:
Clipboard 01
Clipboard 02
Clipboard 03
Clipboard 04
etc.
I’m trying to get the text from one of the Named Clipboards and set as a variable in AppleScript (not run from with Keyboard Maestro)
I’ve tried the following in Script Editor without any luck:
tell application "Keyboard Maestro Engine"
set kmClipboard_01 to getvariable "Clipboard 01"
log kmClipboard_01
end tell
tell application "Keyboard Maestro Engine"
set kmClipboard_01 to getvariable "NamedClipboard Clipboard 01"
log kmClipboard_01
end tell
tell application "Keyboard Maestro Engine"
set kmClipboard_01 to getvariable "%NamedClipboard%Clipboard 01%"
log kmClipboard_01
end tell
I guess it's just a case of me formatting the AppleScript properly to get the text from Keyboard Maestro's Named Clipboards? How do I do this? :slight_smile:
You have the syntax/formatting right; you just need a different command. Since the contents of named clipboards aren’t variables, the getvariable command won’t help you here. Instead, since named clipboards can be accessed via KM text tokens using the %NamedClipboard%[CLIPBOARD NAME]% syntax, you can use the process tokens command, like this:
tell application "Keyboard Maestro Engine"
set kmClipboard_01 to process tokens "%NamedClipboard%Clipboard 01%"
log kmClipboard_01
end tell
The KM Engine method that you need is processTokens (rather than getvariable)
For Execute Applescript actions:
-- Name of clipboard -> contents of named clipboard
-- namedClipContents :: String -> String
on namedClipContents(strClipName)
tell application "Keyboard Maestro Engine"
process tokens ("%NamedClipboard%" & strClipName & "%")
end tell
end namedClipContents
on run
namedClipContents("Gamma")
end run
for Execute JavaScript for Automation actions:
(() => {
// Name of clipboard -> contents of named clipboard
// namedClipContents :: String -> String
const namedClipContents = strClipName =>
Application('Keyboard Maestro Engine')
.processTokens('%NamedClipboard%' + strClipName + '%')
return namedClipContents('Gamma');
})();
This is a little more complex, but it can be done. The easiest way (that I’ve found, anyway, is to copy a “Set Named Clipboard to text” action as XML, paste the XML in the AppleScript, and replace the part of the XML containing the string with "& VariableName &" like this:
tell application "Keyboard Maestro Engine"
set clipText to "Test"
do script "<dict>
<key>JustDisplay</key>
<false/>
<key>MacroActionType</key>
<string>SetClipboardToText</string>
<key>TargetNamedClipboardRedundantDisplayName</key>
<string>Sig</string>
<key>TargetNamedClipboardUID</key>
<string>15F896EC-ABA6-476A-BA07-4FA02B9B71B7</string>
<key>TargetUseNamedClipboard</key>
<true/>
<key>Text</key>
<string>" & clipText & "</string>
</dict>
</array>
</plist>
"
end tell
I’m afraid you’ll need to replicate this with your own clipboards, since this one is unique to my system, but the principle should be the same.
…so now I’m trying to grab the contents of the Keyboard Maestro Named Clipboards and show them in LaunchBar (with the idea being I could show the named clipboards in LaunchBar and paste them into other apps from LaunchBar).
This will show the contents of each clipboard (where return isn’t commented out)
I guess I need to somehow turn the contents of each clipboard into variables? And then combine them in a script like this, inserting the variables in place of the existing titles?
Given that your set of clipboard names may vary, it might be helpful to derive that list at run-time, rather than hard-wiring your current list into your script.
Something, for example, like this, to obtain a list of {name: clipName, text: String} records:
If I run it in Script Editor it runs fine, but if I use the code in a LaunchBar action (paste the code into the script in LaunchBar's Action Editor) it shows the following error:
So I guess this is something that LaunchBar doesn't like, and not a problem but the script.
Any idea why it would throw up that error, as it looks like the variable makes sense to me? (But I don't know anything, haha )
LaunchBar uses its own internal copy of the macOS (JSContext ) JavaScript interpreter, which doesn’t include the Application object, but does have some additional objects and methods of its own.
Application is a special library added to the JSContext used by Script Editor, osascript etc.
(Where they say JavaScript, they mean their own JSContext)
Another, possibly simpler, approach would be to follow the instructions for using a shell script, and to construct a shell script with this kind of pattern:
OK – useful result, looks like the shell doesn’t give osascript permission to read the plist.
Next option, go the the LB documentation of working with AppleScripts, and try this AS version of the same thing:
use framework "Foundation"
use scripting additions
-- clipboardNames :: () -> [String]
on clipboardNames()
set ca to current application
set strPath to "~/Library/Application Support/Keyboard Maestro/Keyboard Maestro Clipboards.plist"
set oPath to (ca's NSString's stringWithString:strPath)'s ¬
stringByStandardizingPath
script clipName
on |λ|(x)
|name| of x
end |λ|
end script
sort(map(clipName, unwrap((ca's NSArray's arrayWithContentsOfFile:(oPath)))))
end clipboardNames
on run
clipboardNames()
end run
-- GENERIC FUNCTIONS ------------------------------------------------------------------
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- sort :: Ord a => [a] -> [a]
on sort(xs)
((current application's NSArray's arrayWithArray:xs)'s ¬
sortedArrayUsingSelector:"compare:") as list
end sort
-- unwrap :: NSObject -> a
on unwrap(objCValue)
if objCValue is missing value then
missing value
else
set ca to current application
item 1 of ((ca's NSArray's arrayWithObject:objCValue) as list)
end if
end unwrap
Tho curiously enough, the AS version is allowed by the shell to read the plist.
This seems to work (though simpler to use the AppleScript directly)
#!/bin/bash
osascript <<AS_END 2>/dev/null
use framework "Foundation"
use scripting additions
-- clipboardNames :: () -> [String]
on clipboardNames()
set ca to current application
set strPath to "~/Library/Application Support/Keyboard Maestro/Keyboard Maestro Clipboards.plist"
set oPath to (ca's NSString's stringWithString:strPath)'s ¬
stringByStandardizingPath
script clipName
on |λ|(x)
|name| of x
end |λ|
end script
sort(map(clipName, unwrap((ca's NSArray's arrayWithContentsOfFile:(oPath)))))
end clipboardNames
on run
unlines(clipboardNames())
end run
-- GENERIC FUNCTIONS ------------------------------------------------------------------
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- sort :: Ord a => [a] -> [a]
on sort(xs)
((current application's NSArray's arrayWithArray:xs)'s ¬
sortedArrayUsingSelector:"compare:") as list
end sort
-- unlines :: [String] -> String
on unlines(xs)
intercalate(linefeed, xs)
end unlines
-- intercalate :: [a] -> [[a]] -> [a]
-- intercalate :: String -> [String] -> String
on intercalate(sep, xs)
concat(intersperse(sep, xs))
end intercalate
-- concat :: [[a]] -> [a]
-- concat :: [String] -> String
on concat(xs)
if length of xs > 0 and class of (item 1 of xs) is string then
set acc to ""
else
set acc to {}
end if
repeat with i from 1 to length of xs
set acc to acc & item i of xs
end repeat
acc
end concat
-- intersperse(0, [1,2,3]) -> [1, 0, 2, 0, 3]
-- intersperse :: Char -> String -> String
-- intersperse :: a -> [a] -> [a]
on intersperse(sep, xs)
set lng to length of xs
if lng > 1 then
set acc to {item 1 of xs}
repeat with i from 2 to lng
set acc to acc & {sep, item i of xs}
end repeat
if class of xs is string then
concat(acc)
else
acc
end if
else
xs
end if
end intersperse
-- unwrap :: NSObject -> a
on unwrap(objCValue)
if objCValue is missing value then
missing value
else
set ca to current application
item 1 of ((ca's NSArray's arrayWithObject:objCValue) as list)
end if
end unwrap
AS_END