I have a macro that involves a fair bit of JS to focus on an area, do a CMD+A to select all, CMD+C to copy and this goes into a named clipboard. Despite the source being rich text, when it runs, I can see in the clipboard it is set as plain text, immediately.
If I copy that same text myself, then paste it directly into the clipboard in KM preferences, I can see the the formatting is retained.
I see no options here that might force it to strip the rich text that is copied. Copy to named clipboard macro seems pretty basic, not really obvious why this is happening. Any thoughts?
the copying application typically creates several pasteBoard items in different formats (e.g. plain text, RTF, HTML etc)
the pasting or consuming application (here KM) selects whatever it considers the most appropriate of those pasteboard items to work with.
My understanding of named Keyboard Maestro clipboards is that they are essentially Keyboard Maestro variables. All KM variables have the type text, and will take the plain text pasteboard item rather than any other.
To inspect the different pasteboard items when you copy something:
“Named Clipboards allow you to permanently store clipboard contents (like images or styled text) that can later be used to place on the System Clipboard.
The Cut, Copy, and Paste to/from Named Clipboard actions let you store or retrieve clipboard data via the System Clipboard.”
Ah, OK, so it's Google Chrome, but the rich text editor is CKEditor running in a Chrome extension. If I copy directly from that editor and paste into an RTF file, or into the named clipboard in the preferences panel, the formatting is retained. It is the action of running that command in KM that appears not to work.
I can't provide a URL for an example as this is an extension that is not public (internal company tool), but I could record a quick screencast to demo it happening tomorrow. Any suggestions for things I can try to include in the vide by way of diagnosis?
So, here is a video showing me copying the rich text into an RTF file and then directly in to the clipboard in KM:
Then, here is the same content which I highlight and then copy to KM clipboard using a hotkey to run that macro. As you can see formatting is not retained doing it this way:
Once you have copied from CKEditor, but before you paste, try running the clipboard viewer macro in post 2 of this thread, and pasting here the JSON view of clipboard contents which it creates.
(If there is something about the formatted pasteboard item(s) which KM is not recognizing, then it might be possible to add a macro stage which pre-cooked the CKEditor formatted clipboard contents into a form more readily recognised by Keyboard Maestro)
I find, for example, copying some formatted text from this CKEditor demo:
That it creates pasteboard items of types:
public.utf8-plain-text
public.html
It doesn't create any public.rtf content, but the public.html pastes without any obvious problem into a KM named clipboard.
It seems that at some stage the public.utf8-plain-text content is getting selected rather than the public.html.
The script below replaces the public.utf8-plain-text and public.html pasteboard items with a single public.rtf pasteboard item, which does seem to paste OK into named variables.
Expand disclosure triangle to view JS Source
(() => {
"use strict";
ObjC.import("AppKit");
// Rob Trew @2022
// Sketch of creating a clipboard containing only a
// public.rtf pasteboard item, based on any existing
// public.html.
// No plain text pasteboard item is retained.
const main = () =>
bindLR(
clipOfTypeLR("public.html")
)(
html => bindLR(
rtfFromHTML(html)
)(
rtf => Right(
setClipOfTextType("public.rtf")(rtf)
)
)
);
// --------------------- GENERIC ---------------------
// Left :: a -> Either a b
const Left = x => ({
type: "Either",
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: "Either",
Right: x
});
// bindLR (>>=) :: Either a ->
// (a -> Either b) -> Either b
const bindLR = m =>
mf => m.Left ? (
m
) : mf(m.Right);
// clipOfTypeLR :: String -> Either String String
const clipOfTypeLR = utiOrBundleID => {
// ObjC.import("AppKit")
const
clip = ObjC.deepUnwrap(
$.NSString.alloc.initWithDataEncoding(
$.NSPasteboard.generalPasteboard
.dataForType(utiOrBundleID),
$.NSUTF8StringEncoding
)
);
return 0 < clip.length ? (
Right(clip)
) : Left(
"No clipboard content found " + (
`for type '${utiOrBundleID}'`
)
);
};
// rtfFromHTML :: String -> Either String String
const rtfFromHTML = strHTML => {
const
as = $.NSAttributedString.alloc
.initWithHTMLDocumentAttributes($(strHTML)
.dataUsingEncoding($.NSUTF8StringEncoding),
0
);
return bindLR(
"function" !== typeof as
.dataFromRangeDocumentAttributesError ? (
Left("String could not be parsed as HTML")
) : Right(as)
)(
// Function bound if Right value obtained above:
htmlAS => {
const
error = $(),
rtfData = htmlAS
.dataFromRangeDocumentAttributesError({
"location": 0,
"length": htmlAS.length
}, {
DocumentType: "NSRTF"
},
error
);
return Boolean(
ObjC.unwrap(rtfData) && !error.code
) ? Right(
ObjC.unwrap($.NSString.alloc
.initWithDataEncoding(
rtfData,
$.NSUTF8StringEncoding
))
) : Left(ObjC.unwrap(
error.localizedDescription
));
}
);
};
// setClipOfTextType :: String -> String -> IO String
const setClipOfTextType = utiOrBundleID =>
txt => {
const pb = $.NSPasteboard.generalPasteboard;
return (
pb.clearContents,
pb.setStringForType(
$(txt),
utiOrBundleID
),
txt
);
};
// MAIN ---
return main();
})();
It might be worth experimenting with placing this:
in an Execute JavaScript for Automation action in your macro,
after a general Copy action,
and before a set named clipboard to past clipboard 1 action
Though here, I notice, just this already suffices to copy formatted content from the CKEditor demo to a named KM clipboard:
Thank you for this. I tried hooking it up into my larger macro but it was still not working for me. So I created a simple macro that has a Copy command, your script, then it sets a named clipboard, like this:
When I run it, the named clipboard is still getting plain text, but if I then paste into an RTF file, I get the styled text. Same if I paste directly into the clipboard in KM prefs>Clipboards panel.
Real head-scratcher for me.
Can I ask some other questions on a related note?
In my current macro there are two processes where it copies from the CKEditor, pastes into two different named clipboards. Then I write to a file (with nothing, to make it blank), then I append text to a file from these two clipboards. When I look at how I do this with Rich Text, I note that if I choose Append text, the option for RTF is greyed out:
It seems unlikely that the "append" operation – straightforward enough over a simple linear structure like plain text – would prove readily definable for any of those more composite (tree-like) format definitions.
“Write or append the current system clipboard, a named clipboard, a variable, some text or some styled text (you can write but cannot append styled text).”
It's always a good idea to read the documentation...