Hook 1.3.1 not respecting scripts?

I’m using Hook 1.3.1 and I’m absolutely loving the concept so far (I purchased Hook Pro)! I’m excited to use it in a number of places but for me to integrate with more things, JavaScript support (and ideally if I can find it, documentation on how to use it) is a must. I’d also like it to work with my editor (IntelliJ IDEA).

What I’ve found is that both creating new and editing old scripts seems to have no impact.

Two examples:

  • I tried updating the Google Chrome script.

    • I use Chrome Canary.
    • I updated the Get Name and Get URL scripts to tell application "Google Chrome Canary" instead of tell application "Google Chrome".
    • I tested those scripts in the Script Editor and they behave as expected (they get the URL/name from Canary).
    • When I try to open Hook over Chrome Canary however, it still launches /Applications/Google Chrome.app as though I hadn’t made any changes to the script (which is saved and has an asterisk next to it).
  • I tried adding support for IntelliJ IDEA.

    • The app is correctly identified as com.jetbrains.intellij
    • No matter the script that I write, I get ‘no linkable’ whenever I try to run Hook over the app.

Canary’s bundle id differs from that of vanilla Chrome, so we need to click the (+) icon at the bottom of the application list, and select the Google Chrome Canary.app file to create a new entry.

After that, a script that captures both name and address might be:

//JavaScript
(() => {
    'use strict';

    const
        tab = Application('Google Chrome Canary').windows.at(0).activeTab();

    return `[${tab.name()}](${tab.url()})`;
})();

In the case of IntelliJ, your current draft is assuming that the window name is a usable filepath.

I’m not a user of IntelliJ myself, but glancing at an image I see a window name which is not immediately usable, and from which the filepath would need to be extracted:

If that is the window name pattern which you are seeing in your configuration, then we need a string cropping line to drop the redundant text up to [ and trim off the closing ]

This worked perfectly, thank you!

Another question is whether IntelliJ has an osascript library that will respond to queries of its windows.

I don’t have a copy, so I’m not sure …

On the other hand here:

  • My implementation uses a regex to extract the file path – whilst I don’t expand it, it should still count as a link?
  • … and I previously tried changing it to simply return a constant value, to no avail. I’m not sure that the JS script is executing (and no, IDEA has no osascript library).

I also tried adding a script for Sidenotes just now and once again, whilst it works perfectly in Script Editor, I still see this from Hook:

In that case the osascript path to text in the window name is not an option.

Is intelliJ a webkit app ? In any case, I think you’ll need to ask Hook Support to advise on how it might be done. The JS script you tried wouldn’t be able to return a value without an osascript library to respond to it.

As you know UI event scripting is tricky, and various things, including timing and window focus, vary with the launch context.

The Hook coders have accumulated a lot of experience with these nuances by now – so I’m sure they can give good help if you explain that you have made a first sketch but still seem to be missing some part of the picture.

Hook does respect scripts, and that tends to be the problem – the details need to be right :slight_smile:

SideNotes

For SideNotes, you will need an Open script as well as a Get Address script.

For an initial rough sketch, the pair of scripts below seems to be working on my system, though I’m sure that the Hook support staff will be able to polish some rough edges off them, and you may find that you need to adjust some of the timing delays.

First, the Open script, assuming that we use a scheme named sidenotes:// (the final s is hidden by the field width limit in the script dialog) we could write something like:

AppleScript source for Open script
use framework "Foundation"

set msgID to decodedPath(drop(length of "sidenotes://", "$0"))

set tpl to splitOn("|", msgID)
set lng to length of tpl
if 0 < lng then
    set strLeft to item 1 of tpl
    tell application "SideNotes"
        activate
        if 1 < lng then
            set {idFolder, idNote} to {strLeft, item 2 of tpl}
            set oFolder to folder id idFolder
            try
                open folder oFolder note (note id idNote of oFolder)
            on error
                -- A fall-back in the case the folder remains,
                -- identifiable, despite the missing note.
                open folder oFolder
            end try
        else
            set {idFolder, idNote} to {missing value, strLeft}
            set fldrs to folders where id of its notes contains idNote
            if 0 < length of fldrs then
                set oFolder to item 1 of fldrs
                open folder oFolder note (note id idNote of oFolder)
            else
                -- UUID not found. The note may have been deleted.
            end if
        end if
    end tell
end if


-- decodedPath :: Percent Encoded String -> FilePath
on decodedPath(fp)
    tell current application
        (its ((NSString's stringWithString:fp)'s ¬
            stringByRemovingPercentEncoding)) as string
    end tell
end decodedPath

-- drop :: Int -> String -> String
on drop(n, xs)
    if n < length of xs then
        text (1 + n) thru -1 of xs
    else
        ""
    end if
end drop

-- splitOn :: String -> String -> [String]
on splitOn(pat, src)
    set {dlm, my text item delimiters} to ¬
        {my text item delimiters, pat}
    set xs to text items of src
    set my text item delimiters to dlm
    return xs
end splitOn

Then the Get Address script (which also fetches a name):

AppleScript source for Get Address script
use AppleScript version "2.4"
use scripting additions

on run
    -- Back-up of clipboard contents:
    set strExistingClip to the clipboard
    
    tell application "SideNotes"
        activate
        delay 0.5
    end tell
    
    tell application "System Events"
        keystroke "a" using {command down}
        keystroke "c" using {command down}
        delay (0.25)
        key code 124
    end tell
    
    set strClip to (the clipboard as «class utf8»)
    
    tell application "SideNotes"
        script go
            on |λ|(fldr)
                tell fldr to notes where its text = strClip
            end |λ|
        end script
        set xs to my concatMap(go, its folders)
    end tell
    set the clipboard to strExistingClip
    
    if {} ≠ xs then
        tell application "SideNotes"
            set refNote to item 1 of xs
            set idNote to id of refNote
            set idFolder to id of first folder where id of its notes contains idNote
        end tell
        set strName to unwords(take(7, words of ((text of refNote) as string)))
        "[" & strName & "](hook://sidenotes/" & idFolder & "|" & idNote & ")"
    else
        ""
    end if
end run


-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
    set lng to length of xs
    set acc to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set acc to acc & (|λ|(item i of xs, i, xs))
        end repeat
    end tell
    return acc
end concatMap

-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    -- 2nd class handler function lifted into 1st class script wrapper. 
    if script is class of f then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min

-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
    if 0 < n then
        items 1 thru min(n, length of xs) of xs
    else
        {}
    end if
end take

-- unwords :: [String] -> String
on unwords(xs)
    set {dlm, my text item delimiters} to ¬
        {my text item delimiters, space}
    set s to xs as text
    set my text item delimiters to dlm
    return s
end unwords

Thanks for trying this, @RobTrew!

I tried using your Get Address script to no avail unfortunately — I’m still told that there is no linkable item found. I messed with the delays, etc. but still no luck.

I think there’s something in my case that’s causing the scripts to not fire at all — even after launching Hook whilst focusing Sidenotes, the cursor in the currently-selected note doesn’t move — that would suggest that it never received the keystrokes from this script, since it runs Cmd+A, Cmd+C, Right (which would send the cursor to the end).

Hook will certainly be firing the scripts (subject to license type perhaps, but it seems that you are getting a return value from the Google Canary script)

The first questions that come to mind, if a usable value is not being returned, are:

  • Have you tried increasing the delay value ? (sounds like yes)
  • Which macOS are you running ?
  • Have you also installed the Open script ?
  • Have you entered the name of the sidenotes scheme in the field below the Open script ?

In any case, a question for @support

(You might, incidentally, also like to experiment with the draft of a Get Address for IntelliJ at


)

PS the system on which those SideNotes scripts are working here is Mojave 10.14.6

on an old MacBook Pro (Retina, 15-inch, Mid 2014)

Instead of using the cmd+a and cmd+c keystrokes, try using the menu items for Select All and Copy. I have generally had more success with menu items rather than keystrokes.

Absolutely, but does SideNotes have menu items ?

I don’t think it does …

On Sidenotes:

If menu items exist they’re certainly hidden (when it’s focused it doesn’t present a menu).

I managed to have success with these scripts:

Get Name
use AppleScript version "2.4"
use scripting additions

on run
    -- Back-up of clipboard contents:
    set strExistingClip to the clipboard
    
    tell application "SideNotes"
        activate
        delay 0.1
    end tell
    
    tell application "System Events"
        keystroke "a" using {command down}
        keystroke "c" using {command down}
        delay 0.1
        key code 124
    end tell
    
    set strClip to (the clipboard as «class utf8»)
    
    tell application "SideNotes"
        script go
            on |λ|(fldr)
                tell fldr to notes where its text = strClip
            end |λ|
        end script
        set xs to my concatMap(go, its folders)
    end tell
    set the clipboard to strExistingClip
    
    if {} ≠ xs then
        tell application "SideNotes"
            set refNote to item 1 of xs
            set idNote to id of refNote
            set idFolder to id of first folder where id of its notes contains idNote
        end tell
        set strName to take(1, paragraphs of ((text of refNote) as string))
        strName
    else
        ""
    end if
end run


-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
    set lng to length of xs
    set acc to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set acc to acc & (|λ|(item i of xs, i, xs))
        end repeat
    end tell
    return acc
end concatMap

-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    -- 2nd class handler function lifted into 1st class script wrapper. 
    if script is class of f then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min

-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
    if 0 < n then
        items 1 thru min(n, length of xs) of xs
    else
        {}
    end if
end take

-- unwords :: [String] -> String
on unwords(xs)
    set {dlm, my text item delimiters} to ¬
        {my text item delimiters, space}
    set s to xs as text
    set my text item delimiters to dlm
    return s
end unword
Get Address
use AppleScript version "2.4"
use scripting additions

on run
    -- Back-up of clipboard contents:
    set strExistingClip to the clipboard
    
    tell application "SideNotes"
        activate
        delay 0.1
    end tell
    
    tell application "System Events"
        keystroke "a" using {command down}
        keystroke "c" using {command down}
        delay 0.1
        key code 124
    end tell
    
    set strClip to (the clipboard as «class utf8»)
    
    tell application "SideNotes"
        script go
            on |λ|(fldr)
                tell fldr to notes where its text = strClip
            end |λ|
        end script
        set xs to my concatMap(go, its folders)
    end tell
    set the clipboard to strExistingClip
    
    if {} ≠ xs then
        tell application "SideNotes"
            set refNote to item 1 of xs
            set idNote to id of refNote
            set idFolder to id of first folder where id of its notes contains idNote
        end tell
        set strName to unwords(take(7, words of ((text of refNote) as string)))
        "hook://sidenotes/" & idFolder & "|" & idNote
    else
        ""
    end if
end run


-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
    set lng to length of xs
    set acc to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set acc to acc & (|λ|(item i of xs, i, xs))
        end repeat
    end tell
    return acc
end concatMap

-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    -- 2nd class handler function lifted into 1st class script wrapper. 
    if script is class of f then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min

-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
    if 0 < n then
        items 1 thru min(n, length of xs) of xs
    else
        {}
    end if
end take

-- unwords :: [String] -> String
on unwords(xs)
    set {dlm, my text item delimiters} to ¬
        {my text item delimiters, space}
    set s to xs as text
    set my text item delimiters to dlm
    return s
end unword

I wasn’t sure how to pull off the special code blocks above. :slight_smile:

I haven’t yet had any luck with Open Item, but all I really did to your script was turn the markdown-formatted return value into just the URL (for Get Address) and just the name (for Get Name).

I’m not sure if there’s a way of returning markdown links, but Hook seemed to be unhappy with it and it started working after that change.

I do see two flashes of text selection when I run the script (one for each script) — I’ve reached out to the author of Hook about providing more AppleScript support.

Thanks again for your help in getting to this point!


On IntelliJ IDEA:

I validated that the script is indeed firing by writing to a file as a debug output (perhaps I’m naïve to the options available, but that was what I used as my console.log equivalent).

It looks like it’s finding an application named idea but that that application’s windows.length is 0 and at that point we can’t continue forward.

Given that the (perfectly reasonable) “not respecting scripts” hypothesis hasn’t survived experiment (and this thread has moved out of ‘bug reports’ into ‘discussion and help’) perhaps these various expressions of interest in additional scripts each deserve a thread of their own ?

(With separate titles, more likely to be found through search engines by other users of:

  • Chrome Canary (com.google.Chrome.canary)
  • SideNotes (com.apptorium.SideNotes)
  • IntelliJ editors (various, including com.jetbrains.intellij.ce)

and thus more likely to attract interest, collaboration, and users running the same versions as you ?).

2 Likes

I remember quite recently moving a topic from Bug Reports to Discussion, but I thought it was a different one. Our Discourse admin checked the revision history of this topic and as far as he can tell it hasn’t been moved. Maybe we’re looking in the wrong spot in the Discourse UI?