Script: Modify x-devonthink item links to reveal records

This script appends ?reveal=1 to all clipboard occurrences of an manually copied item link.

  • If an viewer window exists clicking a modified link reveals the record.
  • If no viewer window exists clicking a modified link opens one.

Usage ideas

  • If you copy links via shortcut and use Keyboard Maestro you could create a macro with a
    Select or Show Menu Item action to copy the link

  • If you copy via menu you could use Keyboard Maestro
    with trigger The system clipboard changes
    and

    • action If All Conditions Met Execute Actions
      • If all of the following are true
      • The clipboard
      • System Clipboard
      • matches
      • ^x-devonthink://|^x-devonthink-item://|^x-devonthink-smartgroup://|^x-devonthink-smartrule://
    • and an additional condition as described by @dansroka here.

Now that I finally wrote it I know again why I started and stopped trying to write it several times: It’s unbelievable hard to write a script and to remember to copy a link again before the next test run. In the end I used a macro that automatically copied the link after each error :roll_eyes:

Script inspiration here by @dansroka and here by @mhucka.

-- Modify x-devonthink item links to reveal records

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use scripting additions

try
	set thePasteboard to current application's NSPasteboard's generalPasteboard()
	set thisPasteboardItem to (thePasteboard's pasteboardItems())'s objectAtIndex:0
	set thisPasteboardItem_Types to thisPasteboardItem's |types|()
	set theDataArray to current application's NSMutableArray's arrayWithArray:{}
	
	repeat with i from 0 to (thisPasteboardItem_Types's |count|()) - 1
		set thisType to (thisPasteboardItem_Types's objectAtIndex:i)
		
		if (thisType's isEqualTo:"dyn.ah62d4rv4gu8zs3pcnzme2641rf4guzdmsv0gn64uqm10c6xenv61a3k") then
			set thisPropertyList to (thisPasteboardItem's propertyListForType:thisType)
			set thisPropertyList_firstObject to (thisPropertyList's firstObject())
			repeat with j from 0 to (thisPropertyList_firstObject's |count|()) - 1
				(thisPropertyList_firstObject's replaceObjectAtIndex:j withObject:(((thisPropertyList_firstObject's objectAtIndex:j)'s stringByAppendingString:"?reveal=1")))
			end repeat
			(thisPropertyList's replaceObjectAtIndex:0 withObject:thisPropertyList_firstObject)
			(theDataArray's addObject:(current application's NSPropertyListSerialization's dataWithPropertyList:thisPropertyList ¬
				|format|:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(missing value)))
			
		else if (thisType's isEqualTo:"dyn.ah62d4rv4gu8yc6durvwwaznwmuuha2pxsvw0e55bsmwca7d3sbwu") then
			set thisPropertyList to (thisPasteboardItem's propertyListForType:thisType)
			(thisPropertyList's replaceObjectAtIndex:(0) withObject:(thisPropertyList's firstObject()'s stringByAppendingString:"?reveal=1"))
			(theDataArray's addObject:(current application's NSPropertyListSerialization's dataWithPropertyList:thisPropertyList ¬
				|format|:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(missing value)))
			
		else
			set theString to (thisPasteboardItem's stringForType:thisType)
			if (theString's hasPrefix:"x-devonthink") then
				set theStrings to (theString's componentsSeparatedByString:linefeed)
				repeat with j from 0 to (theStrings's |count|()) - 1
					set thisString to (theStrings's objectAtIndex:j)
					if not (thisString's isEqualTo:"") then
						set thisString to (thisString's stringByAppendingString:"?reveal=1")
						try
							(theStrings's replaceObjectAtIndex:j withObject:thisString)
						on error
							set theStrings to (current application's NSArray's arrayWithArray:{thisString})
						end try
					end if
				end repeat
				set newString to (theStrings's componentsJoinedByString:linefeed)
				(theDataArray's addObject:(newString's dataUsingEncoding:(current application's NSUTF8StringEncoding)))
			else
				(theDataArray's addObject:(thisPasteboardItem's dataForType:thisType))
			end if
		end if
	end repeat
	
	thePasteboard's clearContents()
	
	repeat with i from 0 to (theDataArray's |count|()) - 1
		(thePasteboard's setData:(theDataArray's objectAtIndex:i) forType:(thisPasteboardItem_Types's objectAtIndex:i))
	end repeat
	
on error error_message number error_number
	display alert "Error: \"Hmm\"" message error_message as warning
	return
end try

2 Likes

Wow, thank you very much for this! In my testing so far, using a Keyboard Maestro shortcut, it has worked exactly as hoped.

You have obviously gone down further into the rabbit hole of using Foundation classes in AppleScript than I have :smiley:. Thanks for taking the time to write this and posting it.

2 Likes

Awesome!
…and crazy ironic, Pete, if you didn’t spot my post from eight days ago over on the KM forum! :smile: :grinning: :grinning:

Updated script, it now can also modify more than one copied link.

I did but I didn’t understand what you’d like to do :slight_smile:

Pete, this is wild, thanks.

I have it running via KM triggered by a hot key combination. To have it run when you copy the item link via menu, would you recommend having KM trigger when “the system clipboard changes”?

Great! Have been asking about this for Hook with KBM. Will test.

Yes. Updated my post

I found that in my attempt at this, the script modifying the clipboard would trigger the script over and over. So I added an additional condition to stop it:

  • The clipboard
  • System Clipboard
  • does not contain
  • "?reveal=1

Like so:

1 Like

Thanks! Added a link to your post.

1 Like

Here’s a new version. This one

  • only modifies if necessary
  • also works with a PDF page link
  • got a meaningful error title
-- Modify x-devonthink item links to reveal records

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use scripting additions

set theClipboard to (the clipboard) as string

if theClipboard starts with "x-devonthink" and theClipboard does not contain "&?reveal=1" then
	try
		set thePasteboard to current application's NSPasteboard's generalPasteboard()
		set thisPasteboardItem to (thePasteboard's pasteboardItems())'s objectAtIndex:0
		set thisPasteboardItem_Types to thisPasteboardItem's |types|()
		set theDataArray to current application's NSMutableArray's arrayWithArray:{}
		
		repeat with i from 0 to (thisPasteboardItem_Types's |count|()) - 1
			set thisType to (thisPasteboardItem_Types's objectAtIndex:i)
			
			if (thisType's isEqualTo:"dyn.ah62d4rv4gu8zs3pcnzme2641rf4guzdmsv0gn64uqm10c6xenv61a3k") then
				set thisPropertyList to (thisPasteboardItem's propertyListForType:thisType)
				set thisPropertyList_firstObject to (thisPropertyList's firstObject())
				repeat with j from 0 to (thisPropertyList_firstObject's |count|()) - 1
					(thisPropertyList_firstObject's replaceObjectAtIndex:j withObject:(((thisPropertyList_firstObject's objectAtIndex:j)'s stringByAppendingString:"&?reveal=1")))
				end repeat
				(thisPropertyList's replaceObjectAtIndex:0 withObject:thisPropertyList_firstObject)
				(theDataArray's addObject:(current application's NSPropertyListSerialization's dataWithPropertyList:thisPropertyList ¬
					|format|:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(missing value)))
				
			else if (thisType's isEqualTo:"dyn.ah62d4rv4gu8yc6durvwwaznwmuuha2pxsvw0e55bsmwca7d3sbwu") then
				set thisPropertyList to (thisPasteboardItem's propertyListForType:thisType)
				(thisPropertyList's replaceObjectAtIndex:(0) withObject:(thisPropertyList's firstObject()'s stringByAppendingString:"&?reveal=1"))
				(theDataArray's addObject:(current application's NSPropertyListSerialization's dataWithPropertyList:thisPropertyList ¬
					|format|:(current application's NSPropertyListXMLFormat_v1_0) options:0 |error|:(missing value)))
				
			else
				set theString to (thisPasteboardItem's stringForType:thisType)
				if (theString's hasPrefix:"x-devonthink") then
					set theStrings to (theString's componentsSeparatedByString:linefeed)
					repeat with j from 0 to (theStrings's |count|()) - 1
						set thisString to (theStrings's objectAtIndex:j)
						if not (thisString's isEqualTo:"") then
							set thisString to (thisString's stringByAppendingString:"&?reveal=1")
							try
								(theStrings's replaceObjectAtIndex:j withObject:thisString)
							on error
								set theStrings to (current application's NSArray's arrayWithArray:{thisString})
							end try
						end if
					end repeat
					set newString to (theStrings's componentsJoinedByString:linefeed)
					(theDataArray's addObject:(newString's dataUsingEncoding:(current application's NSUTF8StringEncoding)))
				else
					(theDataArray's addObject:(thisPasteboardItem's dataForType:thisType))
				end if
			end if
		end repeat
		
		thePasteboard's clearContents()
		
		repeat with i from 0 to (theDataArray's |count|()) - 1
			(thePasteboard's setData:(theDataArray's objectAtIndex:i) forType:(thisPasteboardItem_Types's objectAtIndex:i))
		end repeat
		
	on error error_message number error_number
		display alert "Error: \"Modify x-devonthink item links to reveal records\"" message error_message as warning
		return
	end try
end if

3 Likes

Script: Copy RTF and Markdown link

Maybe I don’t get it, but why not just:

Copy DT Reveal Link.kmmacros.zip (1.2 KB)

:thinking:

That would only be a plain text link.

Oh, you want RTF/md one

No, it’s not RTF/md.

The script manipulates what DEVONthink had put on the pasteboard. The script’s output works the same way as an unmanipulated DEVONthink link works, i.e. if you get an RTF link in app A then you’ll also get an RTF link with the script - only difference is that it reveals the record.