Any success moving from Joplin?

Anyone have a good workflow moving from Joplin to Devonthink? I know its a longshot.

The issues involved:

–Need the tags to port over from the Joplin Markdown front matter into Devonthink tags.

–When you clip articles in Joplin it goes in “source” metadata, but Devonthink has it in URL.

–The imported notes have todays date in Created/Modified metadata, instead of the Created/Updated Joplin Markdown front matter.

–Images do not display, for example (…/_resources/NSFileHandle-20.jpg) is broken link even though the _resources group exists inside Devonthink. (PDF is an option, but then lose all metadata and front matter.

That at least can be scripted. See below


That doesn’t work, as explained in the documentation:

Linking: You can reference local images, scripts, and other resources using item links, downward-relative (traveling subgroups; it’s not possible to travel up with ‘..’ as documents can have multiple parents) or absolute (start with a forward slash) paths.

Script to move some front matter stuff into DT metadata

This JavaScript script works on the current selection. Modification to make it work as a smart rule script is left to the reader.

(() => {
  const app = Application("DEVONthink 3");
  const selection = app.selectedRecords();
  /* Use only markdown records from current selection and loop over them */
  selection.filter(r => r.type() === "markdown").forEach(r => {
    /* Get the text of the record */
    const txt = r.plainText();
    /* Get the frontmatter of the record, each "entry" as an element in the array "frontmatter" */
    const frontmatter = txt.split(/^---/m)[1].split(/^(?=\S)/m).filter(l => !/^$/.test(l));
    /* Loop over all frontmatter entries */
    frontmatter.forEach(f => {
      /* We're only interested in 'created', 'updated' and 'tags' */
      const linetype = f.match(/(updated|created|tags):\s+(.*)/s);
      if (linetype) {
        if (linetype[1] === 'updated' || linetype[1] === 'created'){
          /* Set the creation or modification date */
          const dateString = linetype[2];
          /* Joplin's date format is non-standard: 
          replace the space with a "T" and remove the trailing newline */
          const date = new Date(dateString.replace(" ","T").replace("\n",""));
          if (linetype[1] === 'updated') {
            r.modificationDate = date;
          } else {
            r.creationDate = date;
        } else {
          /* Tags: Split everything pertaining to this entry into its own array */
          const tags = linetype[2].split(/-\s*/ms);
          /* merge the current tags of the crecord with the ones found in the note */
          r.tags = r.tags().concat(tags);

Some explanations:

  • selection is an Array. As such, it provides the method filter which returns a new Array, containing only those entries matching a criterion. Here, type() === "markdown", so that the result of applying filter to selection is an Array containing only markdown records.
  • forEach is another Array method that applies the function passed in as a parameter on every element of the Array.
  • The record’s plainText property is stored in txt which is then passed through several split calls. The first one (split(/^---/m) returns an Array consisting of an empty element, the front matter, and the rest of the markdown element: ^/---/ uses lines beginning with three dashes as text separator. Then [1] extracts the 2nd element of the Array, which is the front matter, and applies split(/^(?=\S)/m) to it. That separates the front matter into single lines (as Array elements) beginning with anything that is not a space character (\S). This ensures that tags: ... is kept together with the following lines, i.e. the tag names, in a single Array element. The expression (?=\S) is “zero-width”, which means that it is used to separate the lines, but the character(s) are not gobbled up. Using simply \S instead would result in lines like reated: ... and ags: ...
  • The final call to filter removes all empty lines from the array.
  • Inside the frontmatter.forEach loop, the f.match matches only those front matter lines we’re interested in, which are beginning with updated, created, or tags, | being the alternation operator. The string is saved in a first capturing group, the rest of the line in the second one.

Note: Joplin stores dates in UTC, whereas DT displays them in local time. So, 9:00 in Joplin becomes 11:00 in DT when your computer’s time zone is CEST.

1 Like

Thank you very very much for the great response chrillek!

Now to learn scripting :slight_smile: