JXA Script to Add any Group to the "Recent Destinations" List

I came across a situation recently where I wanted to be able to programmatically add a group to the “Recent Destinations” list in DT3.

There is no official way to do this by script. But I realized that “Duplicating” a record causes DT3 to add the applicable group to the Recent Destinations list. Using that observation this script accomplishes the goal. So it’s sort of a hack but it works as intended with no harm.

(Many thanks to @chrillek for much mentoring over the last couple of years on JXA - I think it finally clicked.)


(() => {
    "use strict";
    const app = Application("DEVONthink 3");

// Script to Select a Group from Group Selector and add to Recent Destinations

// This Script creates a Test record in the group, duplicates the Test record to the same group, and then deletes both test records.  The act of duplicating the record triggers DT3 to add that group to the "Recent Destinations" list.

  
const group=app.displayGroupSelector("Choose a Group to Add to Recent Destinations");

const NewRecord1=app.createRecordWith({"name":"Only a Test","type":"markdown","content":"Please ignore"},{in: group} );

const NewRecord2=app.duplicate( {record: NewRecord1, to: group});

app.delete({record:NewRecord1},  {in: group });

app.delete({record:NewRecord2},  {in: group });
  
})();
2 Likes

It’s indeed a hack but I didn’t suggest this as I don’t like modifying databases just for UI purposes. But maybe that’s just me :slight_smile:

1 Like

I agree this would not score me points on a Google interview or code review :slight_smile: Reminds me much more of my early days trying to fit a full app into a 32K computer.

Maybe someday after you finish 1,000 more important things you might add the feature to the scripting dictionary.

But for now it will work fine through the indirect route.

Just one note: "use strict" inside a function (as here) only applies to this function. I’d therefore suggest putting it at the beginning of the script, outside any functions. In that case, it applies to the whole script.

And of course, thanks for your kind words!

Thanks

One other question… the scripting dictionary says that the list of records to be deleted could be a list. I tried [NewRecord1, NewRecord2] both with and without the brackets but neither worked.

What would be the syntax to combine the two delete statements into one?

Well, yes … I guess that’s one of the cases where JXA simply does not work with DT as advertised. AFAICT, no method expecting a list of records as an unnamed parameter works. We’ve had some posts about that here, eg for summarizeHighlights and mergeRecords. According to @cgrunenberg, it’s a bug in the scripting bridge.

1 Like

Thanks - that is helpful.

If there are situations which arise when for some reason JXA or Applescript is preferable, is there a way to do a hybrid script and execute Applescript from JXA or vice versa?

I found this

And I came up with this example:

(() => {
  const scriptString = `tell application id "DNtp"
    set rec to summarize highlights of records (selected records) to markdown
    uuid of rec
    end tell`
  const as = $.NSAppleScript.alloc.initWithSource($(scriptString));
  const error = Ref();
  const result = as.executeAndReturnError(error);
/* NOTE: The result is an NSAppleEventDescriptor object. It has to be converted
   to the correct type if it's to be used in JavaScript – see the text below*/
})()

But the return value is an NSAppleEventDescriptor. Since the uuid of rec is a string, you can coerce/access/whatever this string aspect of the descriptor in your JavaScript code with result.stringValue.js. stringValue gets the string as an Objective C object, and appending js converts it to a JavaScript string.

So, yes, it’s feasible. But not elegant.

1 Like

Interesting- thanks. That could work in a pinch for sure - like the Merge situation for which there seems to be no JXA solution.

I suppose another way to do hybrid code may be MacOS shortcuts - passing parameters between JXA and Applescript steps of a shortcut.

Good luck with that. Or rather: I very much doubt that Apple has set up their stuff for that to work: Imagine your AppleScript step returning a list of DT records… (Not that that would be easily or even at all possible with the approach I presented :wink: )

I just meant that you could pass a query parameter or similar that can be used in the next step to set up the action to be performed in the DT database. For sure passing complex data would be challenging.

BTW @chrillek - your example of running Applescript from JXA works well to solve the Merge Records problem discussed in the Forum back in 2020. That’s a snippet worth keeping around for situations when JXA does not work for some reason. Thanks.

(Actually the result is undefined so the console.log does not work - but the action does occur.)

//Merge Records - Calling Applescript from JXA

(() => {
  const scriptString = `tell application "DEVONthink 3"
    merge records (selected records) in current group
    return name of current group
	end tell`
  const as = $.NSAppleScript.alloc.initWithSource($(scriptString));
  const error = Ref();
  const result = as.executeAndReturnError(error);
  console.log(result);
})()

Please check what I wrote below that script in that post. I’ll make clear in the comments that this is not the final version of the script.

1 Like

This works to return the name of the group.

Thanks!

//Merge Records - Calling Applescript from JXA

(() => {
  const scriptString = `tell application "DEVONthink 3"
    merge records (selected records) in current group
    return name of current group
	end tell`
  const as = $.NSAppleScript.alloc.initWithSource($(scriptString));
  const error = Ref();
  const result = as.executeAndReturnError(error);
  const jresult = result.stringValue.js;
  console.log(jresult);
  return jresult;
})()
1 Like

For those who may be interested - this post illustrates two methods for the reverse i.e. how to run JXA from inside an Applescript

1 Like

Nice tip! :+1: