Clipboard Contents Did Change calls forever

I'm trying to add simple URL rewrite functionality using "Clipboard Contents Did Change".

I've added this function with "Transform Clipboard Contents with JavaScript"

async (clipboardContentString) => {    

     await display_notification({title:'URL rewrite', subTitle: ' ' + clipboardContentString, soundName: 'frog' });

    const regexInstagram = /(https?:\/\/)?(www\.)?instagram\.com/i;

    if (regexInstagram.test(clipboardContentString)) {
        clipboardContentString = clipboardContentString
            .replace(regexInstagram, 'https://ddinstagram.com')
            .split('?')[0];
    }
    
    return clipboardContentString;
}

I'm getting notifications until I disable the "Clipboard Contents Did Change" completely.

I've replaced "Transform Clipboard with JavaScript" to "Run Real JavaScript" and change the clipboard through "set_clipboard_content". It works fine.

Although combination "Clipboard Contents Did Change" and "Transform Clipboard Contents with JavaScript" should not go into forever loop.

Yet, feature request to pass extra parameter to "Transform Clipboard with JavaScript" stating the type of clipboard content. For example: I'd like to ignore image or other types but text. The same applies to get_clipboard_content as of now, you get undefined

thanks for reporting! I’ll disable the change recognition for this

1 Like

@Andreas_Hegenberg thanks.

I have another issue with set_clipboard_content. I use Raycast for clipboard history and when I copy URL, the script replaces the content with new content and Raycast does not pick it up. Not even the one that's copied. Although the content is pasteable.

Therefore instead of using 'set_clipboard_content' I had to use a hack with AppleScript and now Raycast has two proper clipboard entries. Old and new one transformed.

It seems 'set_clipboard_content' blocks further processing for other apps.

If you need more information, please let me know.

if (clipboardNew) {
        clipboardNew = clipboardNew.split('?')[0];

        let appleScript = 'set the clipboard to "' + clipboardNew + '"';
		 let result = await runAppleScript(appleScript);

        // await runShellScript({ script: "echo '" + clipboardNew + "' | pbcopy" });
        // "NSPasteboardTypeFileURL"
        // await set_clipboard_content({content: clipboardNew, format: "NSPasteboardTypeURL"});
    }

that is weird. Set clipboard content does not block further processing. Maybe they are just listening to the standard copy

@Andreas_Hegenberg

Okay, I did some debugging and I can describe three different behaviors in Transform Clipboard Contents with JavaScript, set_clipboard_content and AppleScript update the clipboard.

I have "Clipboard Contents Did Change" and one action to transform clipboard to what I need.

1st option:
With combination "Clipboard Contents Did Change" + "Transform Clipboard Contents with JavaScript".

  1. Triggering happens on any clipboard change.
  2. Once the clipboard transformed and value returned it set to the clipboard content
  3. Clipboard Contents Did Change" triggered again because the clipboard did change.
    I expect this to be correct behavior, although if the clipboard content has not been altered I don't it makes sense to set clipboard content and avoid clipboard did change even emitted. (With the fix I believe this will be correct behaviour, or have extra value returned saying don't trigger Clipboard Did Change).
  4. Raycast detects ALL the changes to clipboard and everything tracked correctly, no matter I changed the clipboard or not.

2nd option:
Clipboard Did Change + Real JavaScript + set_clipboard_content

  1. Triggering happens correctly
  2. If the script does not call set_clipboard_content the content correctly detected by Raycast Clipboard history
  3. If the script calls set_clipboard_content. No 'Clipboard Did Change' emitted which seems correct, but nothing detected by Raycast History.
  4. It seems it's the correct behaviour but I'd probably wanted to have another "Clipboard Did Change" even and just skip processing because the clipboard content is already fixed.

3rd option (the only working solution):
Clipboard Did Change + Real JavaScript + AppleScript to change clipboard

  1. The event is triggered
  2. If the script does not update the clipboard, nothing happens, everything is okay
  3. If the script changes the content of the clipboard, 'Clipboard Did Change' is emitted and on second run there is nothing to change and it stops.
  4. Raycast detects old clipboard, and new one. So everything works as expected.

There is definitely different behaviour in set_clipboard_content and other options. Even 'Transform Clipboard with JS' stores the clipboard content correctly, and it is detected by other apps.
I believe 1 and 3 options are correct, although in the 1st option if nothing changed, no need to update the clipboard. And 2nd does not trigger 'Clipboard Did Change' which I think should trigger it.

Let me know if you need sample, or video, I can prepare different reproduction cases.

I might have an idea why raycast ignores the changes. Would be great if you could try v4.605 alpha (uploading now, available in ~10 min), I modified the set_clipboard_contents slightly.

I've tried 4.605 and sorry but the problem is still there, the behavior is exactly the same.

Why "Clipboard Did Change' is not triggered on set_clipboard_content? I feel like there could be an answer).

If you need the scripts to reproduce, let me know please.

„clipboard contents did change“ is explicitly not triggered to not cause recursive calls. However that’s just BTT internals and doesn’t affect anything else.
The question is why raycast ignores the changes, I‘m traveling this week but will have s look next week!

I've just tested Maccy clipboard manager and it has the same issue.

I don't think it's Raycast only problem, it seems something is off with 'set_clipboard_content' because Maccy pick none of values. but works perfectly fine with AppleScript workaround.

I think I have found the issue, will upload a new version later today!

Did this get fixed? If so what's the intended behavior?

I'm trying Clipboard Did Change for one specific app, and both of the following combinations call as long as the specific app is active and keep changing the clipboard:

  • Clipboard Contents Did Change + Transform Clipboard Contents with JavaScript
  • Clipboard Contents Did Change + Real JavaScript + set_clipboard_content

For now, I've managed to work around it with real Javascript by using a replacement function that flags if the clipboard content is actually matched and needs to be replaced:

async function replaceCitation() {
		let clipboardContentString = await get_clipboard_content();
		let matched = false;
		
		function replacer(match, p1, p2, offset, string) {
		    matched = true;
			return [p1, p2].join(", ");
		}
		
		let newContent = clipboardContentString.replace(/(\[@.+)@(\d+\])/, replacer);
		if (matched) {
		    set_clipboard_content({content: newContent, format: 'NSPasteboardTypeString'});
		}
		return true;
}

I'm not sure if this is more efficient that just using the normal regex.test() to check for a match and then replacing, but it would be one less regex call. :slight_smile:

I'm still wondering though if set_clipboard_content is meant to trigger Clipboard Contents Did Change?

I think I still need to introduce the options to prevent the change calls.

If you prefix the format with BTT_ it should already be ignored, but this is not really documented.

		    set_clipboard_content({content: newContent, format: 'BTT_NSPasteboardTypeString'});

1 Like

Thanks! It looks like BTT_NSPasteboardTypeString does change the system clipboard without triggering again, but then BTT's clipboard history doesn't get updated, so the record is lost (which will probably work for 99% of my use case).

There's no way to change items in the clipboard history, is there?

No yet!
More scriptability in this area is coming soon.

1 Like