Converting Clipboard or Variable Plain text

Trying to create a Macro that gets a link from either GoodLinks or Safari so I can paste into another app.

If the active app is goodlinks, it asks that app for the link otherwise it gets the info from the frontmost browser. So far so good.

When goodlinks puts the link in the clipboard I think it does so as rich text (why?). When I attempt to paste that Obsidian it fails. So I tried to invoke the Remove Styles clipboard filter.

Clearly I've failed:
Keyboard_Maestro_Editor_—_Grab_LInk_and_Title

I think this is Rich vs Plain Text problem because pasting as plain text makes Obsidian happy.

Current state of the clipboard:
Clipboard_History_Switcher

What self evident thing have I missed?

  • Mark - a human

I can't test your macro because I don't have either of your apps. So you will have to tell me why you wrote "Clearly, I failed." What failed? What happened? I can't see what happened. I have suspicions, but I don't want to speculate until I know what happened.

@Sleepy thanks for the quick reply.

I expect Filter System Clipboard with Remove Styles to take the current element on the system Clipboard and make it plain text.

Proof it worked would be Obsidian accepting my attempt to paste OR the Clipboard viewer to change to
Clipboard_History_Switcher_and_Converting_Clipboard_or_Variable_Plain_text_-Questions___Suggestions-_Keyboard_Maestro_Discourse
I got this version by using the paste plain function built into Peter's clipboard history switcher.

I expected that too, but I tried it on a text value and it did not make it plain text. I think we both misunderstand what that filter does, then. The wiki says:

The filters are:

  • Remove Styles (clipboards only, make the clipboard plain text).

...this code shows that the bold text is indeed removed:

(when you run it, the bold text is removed).

But if hyperlinks are not being removed, (I'm still testing that) I think this means that hyperlinks aren't the same thing as styled text. I don't understand the implications, but this is a hint that should bring us one step closer to a solution. I'm still pondering it. I don't give up easily.

Ok I placed a link in that box and got what I suspected:

The result is the title, not the hyperlink. (I pasted a hyperlink from a news site.) My suspicion that I alluded to is that when a hyperlink is filtered, the display name is kept rather than its hyperlink value. So now we know the problem, and we have to find a solution. I'm still pondering how to solve it. I think we'll find a solution soon.

No, Goodreads is creating a clipboard type of public.url

You can read it with with an Execute JavaScript for Automation action, e.g.

Clipboard content of type public.url.kmmacros (3.1 KB)


Expand disclosure triangle to view JS Source
(() => {
    "use strict";

    ObjC.import("AppKit");

    const main = () =>
        clipOfType("public.url");

    // --------------------- GENERIC ---------------------

    // clipOfType :: String -> Either String String
    const clipOfType = utiOrBundleID => {
        const
            clip = ObjC.deepUnwrap(
                $.NSString.alloc.initWithDataEncoding(
                    $.NSPasteboard.generalPasteboard
                    .dataForType(utiOrBundleID),
                    $.NSUTF8StringEncoding
                )
            );

        return 0 < clip.length ? (
            clip
        ) : (
            "No clipboard content found " + (
                `for type '${utiOrBundleID}'`
            )
        );
    };

    return main();
})();
1 Like

Can you think of any way to solve this with a KM action? I've tried many, many things and I'm still not finding a solution. but I don't give up easily.

AHA! I found a solution! Give me a few minutes to make it work cleanly.

I found a way to solve your problem using only KM actions (I don't mind if you prefer using the Javascript solution provided above, but for personal reasons I had to find a solution that uses KM.) I can't guarantee it will work with your app because I didn't test your app's links. But here's how it works for me using hyperlinks created from web browsers placed into the system clipboard:

The trick was finding a way that produced both the hyperlink and its display name. My solution was to write the clipboard to a file specifying plaintext output, and then reading the file back in. But some forms of reading didn't work, like the KM action. My method works, but I don't really understand why. I may be able to simplify it, but this is pretty simple.

As you can see my method extracts the link using a regex. I'm not sure if this method will work for you because I can't see the details of your app's links. And my method may not work for any link whose display name or hyperlink contains a double quote. But it should work for the majority of cases. If you want to fine tune it, we can try that later.

OOPS there appears to be a filename typo. I'll fix that. It's fixed.

Let me see if I have understood ...

  • You feel that a Keyboard Maestro Execute Shell Script action is a Keyboard Maestro Action, but
  • a Keyboard Maestro Execute JavaScript for Automation action is not a Keyboard Maestro action ?

Ok, you got me. I was wrong. I lose, ergo, you win. You're the better person. (This is a humorous expression I use in real life a lot.)

On the other hand, the word "cat" seems easier to understand than:

(() => {
    "use strict";

    ObjC.import("AppKit");

    const main = () =>
        clipOfType("public.url");

    // --------------------- GENERIC ---------------------

    // clipOfType :: String -> Either String String
    const clipOfType = utiOrBundleID => {
        const
            clip = ObjC.deepUnwrap(
                $.NSString.alloc.initWithDataEncoding(
                    $.NSPasteboard.generalPasteboard
                    .dataForType(utiOrBundleID),
                    $.NSUTF8StringEncoding
                )
            );

        return 0 < clip.length ? (
            clip
        ) : (
            "No clipboard content found " + (
                `for type '${utiOrBundleID}'`
            )
        );
    };

    return main();
})();
1 Like

I agree that Apple Script is hard to read. I'm a recovering programmer and while I can this code, I couldn't have written.

That said it does the job. I almost wonder if I should refactor into a "library" like function for general use in KM. That said I've always like the rule of '3' from the Gang of Four patterns book. You need to see something in use three times before attempting to generalize.

I think that was JavaScript not AppleScript. Which proves my point. :slight_smile:

1 Like

True. I still managed to parse it. I think it was the use of NSObject, etc which my brain sees as very mac like. Caveat emptor, I last coded on Mac professionally when munger was a function.

It's JavaScript, or more technically, JavaScript for Automation, otherwise known as "JXA". It uses a call to the Objective-C library available in JXA, which is what makes it look so strange.

Yes it's a good idea to take code like this and stick it in a subroutine-type of macro.

If I want to make a piece of copied text plain text without any embedded links etc, I make it into a Variable. Variables are plain text with no other info such as styles or links.

1 Like

Using that Set Variable action on a link strips out the hyperlink rather than stripping out the link's visible text, which is the opposite of what needs to be done in this case.

If I recall, it took me four hours to come up with a solution that strips out the visible text instead of the hyperlink. It was a tough problem.

I did try a version of that early on and it didn't work. Since I can't easily tell the state of the clipboard from visual inspection I couldn't tell the why.

I almost wish that @peternlewis would give us an assertion facility for KM. Then I could assert that the clipboard was in a known state, the assertion would fail and I would iterate.

(Can you tell I spent 20+ yrs writing code and was one of the earliest adopters of both JUnit and NUnit :wink:

1 Like

I'm not sure how an assert action would help you here. In the languages that I know, Assert's simply abort the macro if the condition isn't true.

But in fact, there is an assert action in KM with dozens of different tests you can perform on the clipboard... like this one...

1 Like

Well who knew? Not me.

Use - I would Assert that the Clipboard wasn't plain text and display a message to that effect. I allow me to test my assumptions more cleanly.

I played for 10 secs. The missing detail - there is no builtin way to test whether on not the clipboard is plain text.

You might find the answer to your question here:

You will need to test, but this seems to do it:

Of course, my code will actually solve the problem, not just detect the type of the clipboard.

1 Like

Ah. I had missunderstood. I thought he wanted to turn the copied text into plain text and strip out a hyperlink. In my defence that’s what he seemed to be asking :slightly_smiling_face: