Wikilinks to notes automatically

That’s the exact mentality which kicked up enshittification. We as a society prefer crappy things to nothing, so we eventually end up with a crappy world to live with. Have you ever seen one beautiful advertisement after the AI takeover in the last couple of years? When was your previous visit to a new website which is both aesthetically pleasing and not Tailwind-ish?

A single act of asking AI for a script is not directly consequential in its own, however it’s part of a society-wide pattern which is indeed consequential. We as individuals are responsible in so far as we have contributed to the pattern by being a part of it.

5 Likes

so, what am I supposed to do?
I have work I need to do.
Should I hire top talent for $10,000 an hour to produce something for my simple needs?
Am I supposed to go to Tibet and seek wisdom to help me figure out how to best approach this?
Should I post on upwork for the highest paid, and best?
Or just use some glue, some glitter, and polish up a turd that does what I want in 10 min… Even if it looks like frankenstein?

Really? What am I supposed to do?

Should I hire top talent for $10,000 an hour to produce something for my simple needs?

You can only define your end product as “simple”, not necessarily the manufacturing leading to the end product.

It seems like you are imagining a system, not automating one you’re already working with. Is that the case? If so, that is rarely a good idea, especially as you may abandon the approach then have to start anew. Automation should be used to make an existing process more efficient, a process you can already do comfortably and manually. That way, when the automation breaks, you can continue the work manually.

I have work I need to do.

Which is what specifically? We don’t know anything about the nature of your work or use case. Well at least I don’t.

Once these pages are created, I plan to add item links to track how many notes are connected to each heading. Additionally, I would like to group these “heading pages” into a separate, dedicated group to make them easier to manage and distinguish from my regular notes. This grouping would serve as a catalog for the headings.

What do you plan to do about these things?


By the way, manually clicking the links to create WikiLinked documents provide more functionality than what you’re doing. Look at the template section in Settings > WikiLinks. That can be used to create documents with data already in them, e.g., a backlink to the originating document, dates, etc.

For example…

Everything in the WikiLinked document was added via a WikiLink template…

1 Like

Thanks for the reply…
Yes, I can go through the notes one by one and click on the wikilink to create the page, then copy the item link to the created wikipage manually, but I’d rather throw the computer out the window. That will take ages.

The headings is like an “index” of sorts. I’m already using tags, but they’re specific for items in the text, whereas the headings are “overall” concepts in the text.

So, for example, if I’m exploring topic X, I can look at relevant tags, then go to a more broader heading section which should have more related ideas.

The steps are simple, but very tedious to do manually.

the wiki settings… If I have several notes with the same [[heading]] will they be linked through the process you just posted? Let me see if I can make your way work

Do you mean the document’s title is the heading?
If so, then WikiLinking wouldn’t work for your purpose anyways. A WikiLink points to a single document with a specific name, not to many nor to a choice of documents with the same name.

No… it only links the note on creation, but doesn’t update the “heading” page even if other notes have that heading.


Title: Overview of Cognitive Processes: Differentiating Between Automatic and Strategic

Headings:

  • [[Cognitive Processes]]
  • [[Automatic Processing]]
  • [[Strategic Processing]]
  • [[Psychology]]

Tags: cognition, automatic processing, strategic processing, mental processes, psychology, brain function, attention


that’s what a note’s metadata looks like…
The notes are more specific to the text in the note, the headings are the concepts that will be shared amongst many more notes. Somewhat like an “index”…

You do what you want to do, of course. You are a free person.

Please do be aware, though, that even “obvious” things have their implications. It seems you’re keenly aware of the obstacles to writing a script yourself. I (and perhaps others who post human-written scripts on this forum, too) hope you be aware of the issues of AI-generated scripting as well. It’s up to you whether you will care about the implications. In the end, no option is perfect, so it’s essentially a choice between two evils.

1 Like

It was an honest question. I have this problem, and I need to work through it. You suggested learn to code so that I don’t create crappy stuff with ai, but that just reminds me of people in the old times saying “n00b” and just ignore the person, or said, read the man pages. Not very helpful.
I can find a “guru” to tell me the best practices, or whatnot, but I need a tool now, not to learn to program…
I’m trying my best to come up with a tool I need, if I can’t get it quite right, then I ask here… for suggestions… not “be better – write by hand”

Yes, I can go get a degree in CS, and maybe I’ll be able to work through my programming needs, but that’s not my profession.

Just a friendly reminder that this thread started with someone posting obviously AI-written code and asking for human help.

2 Likes

I use Wikilinks in DEVONthink to categorize and provide context for my notes. These Wikilinks act as “heading categories,” helping me organize and interconnect my ideas. Over time, I’ve accumulated a large number of these headings, but creating the corresponding Wikilink pages manually is time-consuming and tedious.

What I want is a way to automate the creation of Wikilink pages for all the links in my notes. Once the pages are created, I plan to take it a step further by:

  1. Adding item links to track how many notes are connected to each heading.
  2. Organizing these pages into a dedicated group in DEVONthink. This would make it easier to manage and distinguish these “heading pages” from my regular notes. The group would essentially act as a catalog for my headings.

This automation would save me a lot of time and effort.


My Current Situation

In my notes, headings look something like this:

  • [[Cognitive Aging]]
  • [[Performance Assessment]]
  • [[Speed-Accuracy Tradeoff]]
  • [[Baseline Performance]]

I want to ensure there’s a corresponding page in DEVONthink for each of these headings without having to create them one by one.


What I’m Looking For

I need a script or tool that can:

  1. Find all the Wikilinks in my notes.
  2. Automatically generate a new page in DEVONthink for each unique Wikilink that doesn’t already have a page.
  3. Optionally: Add item links to track related notes, so I can see how many notes connect to each heading.
  4. Organize all these created pages into a dedicated group, keeping them separate from my regular notes for easier navigation and management.

are you a purist or something? You’re on a PC for Pete’s sake… how much assembly have you written?

Everyone step back and breathe. Let’s all manage our expectations and reactions. :slight_smile:

At a glance, @meowky’s script provides the majority of the functionality you mentioned, not all, but most. It’s 1am here. I will pop in tomorrow.

4 Likes

Now it’s 3am and like a dog worrying a bone…

Fully scripted. No WikiLink settings used. Doesn’t create redundant documents. Processes WikiLinks inline, not just segregated in lists. And it’s pure vanilla AppleScript - no shell, not ASOC, not shenanigans (and trust me, it’s harder to accomplish in pure AS).

Half-asleep at the wheel so I don’t have the wherewithal to discuss formatting and frills. But this fulfils the requirements.

L8R :sleeping:

3 Likes

:smiling_face_with_three_hearts:
I’ll wait for the script since I’m not sure where it is…

I like how you use Set to get unique linkNames. And at first I thought the match()?. was a typo, until I looked it up – thanks for teaching me something new!

A remark on

(what does the ? do after the match()?)
First, I’d use push instead of concat – that appends to the linkNames and makes the assignment unnecessary.
Second, since you’re using the g flag on the match anyway, why not use matchAll, too? And perhaps with a capturing group? Well, it might become a tad more unruly with that.
linkNames.push([... r.plainText().matchAll(/\[\[(.*?)\]\]/gm)]?.map(item => item[1]) || []);

Not sure if ?. is needed in this case as matchAll should only return successful matches anyway.

1 Like

matchAll produces an iterator instead of an array, which can’t be mapped directly. (Just tested that in Script Editor.) So I think match + slice and matchAll + spread are equal — both requiring two basic actions.

AFAIK array1 = array1.concat(array2) and array1.push(array2) does not produce the same result.

match() returns null if no match is found. Running null.map() will not work. Optional chaining avoids this potential error.

We keep learning — I picked up the [...new Set(array)] trick fresh last night from Stack Exchange :slight_smile:

Ok, this one seems to work without crashing, and doing what I need…

use AppleScript version "2.4"
use scripting additions

tell application id "DNtp"
	try
		-- Get the current database and selected records
		set theDB to current database
		if theDB is missing value then error "Please open a database first."
		
		set selectedRecords to selected records
		if (count of selectedRecords) is 0 then error "Please select some documents to process."
		
		-- Get or create the headings group
		set headingsGroup to get record at "/Heading Pages" in theDB
		if headingsGroup is missing value then
			-- Create the Heading Pages group
			set headingsGroup to create record with {name:"Heading Pages", type:group} in theDB
			set comment of headingsGroup to "This group contains automatically generated pages for all wikilinks found in your documents. Each page tracks which documents reference that particular heading."
			display alert "Created Heading Pages Group" message "Created a new group to store wikilink pages."
		end if
		
		-- Show progress
		show progress indicator "Processing Records" steps (count of selectedRecords)
		
		-- Process each selected record
		repeat with theRecord in selectedRecords
			if type of theRecord is in {markdown, txt, rtf, rtfd} then
				step progress indicator ("Processing " & (name of theRecord as string))
				
				-- Get content and look for wikilinks
				set theContent to plain text of theRecord
				
				-- Save original delimiters
				set savedDelimiters to AppleScript's text item delimiters
				
				-- Find all wikilinks in the content
				set wikilinks to {}
				set currentPos to 1
				set contentLength to length of theContent
				
				repeat while currentPos ≤ contentLength
					-- Find next "[[" marker
					set startPos to offset of "[[" in (texts currentPos thru contentLength of theContent)
					if startPos is 0 then exit repeat
					
					-- Adjust position to actual location
					set startPos to currentPos + startPos + 1
					
					-- Find closing "]]"
					set remainingText to texts startPos thru contentLength of theContent
					set endPos to offset of "]]" in remainingText
					if endPos is 0 then exit repeat
					
					-- Extract the wikilink text
					set linkText to texts 1 thru (endPos - 1) of remainingText
					
					-- Add to wikilinks if not empty and not already found
					if linkText is not "" and linkText is not in wikilinks then
						set end of wikilinks to linkText
					end if
					
					-- Move past this wikilink
					set currentPos to startPos + endPos + 1
				end repeat
				
				-- Process found wikilinks
				repeat with linkText in wikilinks
					-- Create or get wikilink page
					set wikiPage to get record at ("/Heading Pages/" & linkText) in theDB
					if wikiPage is missing value then
						set wikiPage to create record with {name:linkText, type:markdown} in headingsGroup
						set plain text of wikiPage to "# " & linkText & return & return & "## Documents referencing this heading" & return
					end if
					
					-- Add reference only if it doesn't exist
					set docURL to reference URL of theRecord
					set docLink to "- [" & (name of theRecord as string) & "](" & docURL & ")"
					
					set pageContent to plain text of wikiPage
					if pageContent does not contain docLink then
						if last character of pageContent is not return then
							set pageContent to pageContent & return
						end if
						set plain text of wikiPage to pageContent & docLink & return
					end if
				end repeat
				
				-- Restore delimiters
				set AppleScript's text item delimiters to savedDelimiters
			end if
		end repeat
		
		hide progress indicator
		display alert "Complete" message "Finished processing selected documents."
		
	on error error_message number error_number
		-- Restore delimiters in case of error
		set AppleScript's text item delimiters to savedDelimiters
		hide progress indicator
		if error_number is not -128 then
			display alert "Error" message error_message
		end if
	end try
end tell

Correct. That’s why I used the array constructor [... matchAll(…)]. Alternatively, Array.from(matchAll(…)) does the same thing – build an Array from something else.

Correct. I overlooked that you’re concating arrays.

See Script: Add page label to PDF annotations
It’s buried there, too :wink:

1 Like

This one will even remove references to notes in the heading pages if you remove the wikilink [[ ]] from notes referencing a heading.

use AppleScript version "2.4"
use scripting additions

tell application id "DNtp"
	try
		-- Get the current database and selected records
		set theDB to current database
		if theDB is missing value then error "Please open a database first."
		
		set selectedRecords to selected records
		if (count of selectedRecords) is 0 then error "Please select some documents to process."
		
		-- Get or create the headings group
		set headingsGroup to get record at "/Heading Pages" in theDB
		if headingsGroup is missing value then
			-- Create the Heading Pages group
			set headingsGroup to create record with {name:"Heading Pages", type:group} in theDB
			set comment of headingsGroup to "This group contains automatically generated pages for all wikilinks found in your documents. Each page tracks which documents reference that particular heading."
			display alert "Created Heading Pages Group" message "Created a new group to store wikilink pages."
		end if
		
		-- Show progress
		show progress indicator "Processing Records" steps (count of selectedRecords)
		
		-- Process each selected record
		repeat with theRecord in selectedRecords
			if type of theRecord is in {markdown, txt, rtf, rtfd} then
				step progress indicator ("Processing " & (name of theRecord as string))
				
				-- Get content and look for wikilinks
				set theContent to plain text of theRecord
				
				-- Save original delimiters
				set savedDelimiters to AppleScript's text item delimiters
				
				-- Find all wikilinks in the content
				set wikilinks to {}
				set currentPos to 1
				set contentLength to length of theContent
				
				repeat while currentPos ≤ contentLength
					-- Find next "[[" marker
					set startPos to offset of "[[" in (texts currentPos thru contentLength of theContent)
					if startPos is 0 then exit repeat
					
					-- Adjust position to actual location
					set startPos to currentPos + startPos + 1
					
					-- Find closing "]]"
					set remainingText to texts startPos thru contentLength of theContent
					set endPos to offset of "]]" in remainingText
					if endPos is 0 then exit repeat
					
					-- Extract the wikilink text
					set linkText to texts 1 thru (endPos - 1) of remainingText
					
					-- Add to wikilinks if not empty and not already found
					if linkText is not "" and linkText is not in wikilinks then
						set end of wikilinks to linkText
					end if
					
					-- Move past this wikilink
					set currentPos to startPos + endPos + 1
				end repeat
				
				-- Process found wikilinks
				repeat with linkText in wikilinks
					-- Create or get wikilink page
					set wikiPage to get record at ("/Heading Pages/" & linkText) in theDB
					if wikiPage is missing value then
						set wikiPage to create record with {name:linkText, type:markdown} in headingsGroup
						set plain text of wikiPage to "# " & linkText & return & return & "## Documents referencing this heading" & return
					end if
					
					-- Add reference only if it doesn't exist
					set docURL to reference URL of theRecord
					set docLink to "- [" & (name of theRecord as string) & "](" & docURL & ")"
					
					set pageContent to plain text of wikiPage
					if pageContent does not contain docLink then
						if last character of pageContent is not return then
							set pageContent to pageContent & return
						end if
						set plain text of wikiPage to pageContent & docLink & return
					end if
				end repeat
				
				-- Restore delimiters
				set AppleScript's text item delimiters to savedDelimiters
			end if
		end repeat
		
		-- After processing wikilinks, check all heading pages for orphaned references
		repeat with theRecord in selectedRecords
			if type of theRecord is in {markdown, txt, rtf, rtfd} then
				-- Get content to check for wikilinks
				set theContent to plain text of theRecord
				set docURL to reference URL of theRecord
				set docLink to "- [" & (name of theRecord as string) & "](" & docURL & ")"
				
				-- Get all heading pages
				set headingRecords to children of headingsGroup
				
				-- Check each heading page
				repeat with headingPage in headingRecords
					set pageContent to plain text of headingPage
					set headingName to name of headingPage
					
					-- If page contains a reference to this document
					if pageContent contains docLink then
						-- Check if the document still contains this wikilink
						set hasWikilink to false
						set linkToFind to "[[" & headingName & "]]"
						
						if theContent contains linkToFind then
							set hasWikilink to true
						end if
						
						-- If wikilink is gone, remove the reference
						if not hasWikilink then
							-- Split content into lines
							set AppleScript's text item delimiters to return
							set contentLines to text items of pageContent
							
							-- Remove the line containing the link
							set newContent to {}
							repeat with aLine in contentLines
								if aLine does not contain docLink then
									set end of newContent to aLine
								end if
							end repeat
							
							-- Join lines back together
							set newPageContent to ""
							repeat with aLine in newContent
								set newPageContent to newPageContent & aLine & return
							end repeat
							
							-- Update the heading page
							set plain text of headingPage to newPageContent
						end if
					end if
				end repeat
			end if
		end repeat
		
		hide progress indicator
		display alert "Complete" message "Finished processing selected documents."
		
	on error error_message number error_number
		-- Restore delimiters in case of error
		set AppleScript's text item delimiters to savedDelimiters
		hide progress indicator
		if error_number is not -128 then
			display alert "Error" message error_message
		end if
	end try
end tell

This piece of code

illustrates perfectly why AppleScript might not be the best tool for all tasks. Perhaps it’s just me, but this kind of string processing in the style of the 80’s BASIC dialects makes me cringe.

const wikilinks = [... record.plainText().matchAll(/\[\[(.+?)\]\]/g)].map(item => item[1]);

gets you all non-empty wikilinks in a single line. In another programming language, though. But aren’t computers and programming languages there to make our life easier?

Why do you use as string in the context of name of theRecord? What else would the name property be, if not a string?

Equally cringe-worthy. And proof that ChatGPT (or whatever wrote that code) has no idea of what the code means. It’s just stringing words together. Shudder.

2 Likes