DT3 Script: Create stack for each document for multiple cards/snippets with backlink, cited text or note, comment, and tagging & perhaps a synergy with the Bookend script

I owe many ideas and pieces of codes from @korm, @Frederiko, @bluefrog, @cgrunenberg and many other members in finishing this very interesting script (I think). IMHO, the most genuine inspirations are coming from Make an Annotation with Links, Notes, Tags v2 and Annotation Pane (Annotation with Links, Notes, Tags v3).

Two things are in my mind before I start this project: (1) DT3 has dramatically and simultaneously increased both the ease of use as well as the level of customisation (2) I admire v2 and V3, but I feel that the scripts are not giving enough flexibility in configuration (but it’s gracefully expected coz they are likely personalised version with lots of additional efforts to help other DT users to use them)- some users like to structure their ideas before writing, some users like to do it the other way round. So, I want to take the challenge to utilise some of the new features of DT3, and provide a script that can allow a very disciplined or very flexible way for building up multiple notes/cited text/snippets form each source/annotated documents.

EDITED on 4 July to reflect the changes in V0.90
(* Two Main Purposes*)
– (1) To cite a piece of selected text from pdf or text file and take notes in one card (snippet). You can create many cards for different snippets. Each card can be tagged and comment individually. All cards from the same document are gathered in one document-stack. All document-stacks are gathered in one topstack.
– (2) To create multiple notes (with no cited text) on the same document, and all notes from the same document are gathered in one docuement-stack. All docuement-stacks are gathered in one topstack.

(* Features *)
– two options of topstack: (opt1) each database has one topstack to gather all document-stacks within the database (opt2) one topstack to gather all document-stacks from all databases
– for (opt1): You need to create a group name “Stack” at the root of each database that you want to collect muliple notes and snippets.
– for (opt2): For advance user who knows how to extract the uuid of a group. The centralised topstack can be placed in anywhere in a database, in any database, and by any name you choose. You can change the name and relocate the top stack anytime WARNING: however, frequent chnage is discouraged.
– you can track how many notes/snippets were created at inspector bar of the source document
– you can call up the whole document-stack of notes/snippets from the inspector bar of the source document
– cited text will be highlighted in the source docuement, at a colour specifed by you, or by underlying.
– you can create as many linked notes on the same document as you want.
– there are many ways to tag each snippets/cards. e.g, none, manual input at time of card-creation, inherit all or some of the tags from the source, and selection of multiple tags all at once from the entire database or from a preset list of tag (constrained list).
– the card can be named by the full name or the aliases of the source document, followed by an unique identifer
– you can choose to be asked, or not, to store a very short summary of each card to the comment field at the time of card-creation. This may help you to recall what are those cards about.
– you can pre-set the font type of the note.
– you can preset where on the screen the newly created note is placed when it is first created.
– Back-link is based on page-link in pdf, and just backlink for text file. However, paragraph link for text file will be possible when DT3 final version is out (I hope)

(* Notes *)
– topstack (opt1) is easier for topstack setup, and the only option if you need to have different topstack for each database.
– topstack (opt2) is only for those advance users who know scripting, and really want to have a centralised topstack.
– You need to know basic scripting to setup the stack and constrained tag list and to configure all of the above functions.
– the Script is written with many handlers, easier for debugging and for customisation BUT,
– for beginners, handlers can be confusing, because variables used by main program and handlers have many similar but not identical names
– I did quite a lot of debugging, but can’t guarantee every options will work, use it at ur own risk
– I might not be able to answer your question (my holiday is over), I develop this script based on personal need but trying my best to make it more approachable to for other users and for different usages.
– I release the code beacuse I learn so much by reading from this forum and I think codes should be shared among each others. You are welcome to take control of the script and modify it.
– Don’t rush to use the script, study the (SET UP) in the script carefully. Different sections of codes will look familar to those experienced members. But I try to code as clean as possible.
– IMHO, it’s best to use 3rd debugger for viewing and customising the script, it’s rather long.
– I understand that there are also script/s to import paper-group of notes from bookend. This script could be an integration to such structure. But I’ll leave it to others as I don’t use bookend.

The most important setup:

Option 1: Use one topstack in each database (should be easier, I hope)
(1) Create a group named “Stack” in the root of the database.
(2) Create tag named “ParentTag” in the root of the Tags of the database. However, you may not need to use it.

Option 2: use one centralised topstack for all databases (for very experienced users only)
(1) You can create the group “Stack” and the tag “ParentTag” anywhere. But you need to extract the UUID of both items for later use. Read the script for details.

Common to Option1 and 2
(3) Create two custom meta data fields.
one for linking to the stack, another to keep track of the number of cards that were created in each document-stack. If u don’t touch that field, each note will have an unique identifier. Of course, you can always reset the count after the testing phase.

A friendly reminder:
Test the script with a test database.
If you are uncomfortable with the script, IMHO the best thing is not to use it right away. Try taking time to read more forum posts about scripts, try running simple scripts as a start, gain experience, and perhaps going back to more complicated script including this script (that’s what I did). If this feature has real application value, someone will/may modify it either from the ease-of-setup and configuration or from feature perspectives
Check out my other posts in this thread, now I know how hard a job @bluefrog is having!

(* SET UP*)

-- Option for Stacks and ParentTag. 
property byLocation : true -- "true" for opt1 (one topstack-one database), "false" for opt2 (one topstack- many database)

-- For opt1 -  if byLocation : true
-- user should create a group "Stack" at the root of the database, 
property stackLocation : "/Stack" -- don't touch
-- user only need to create a ParentTag if they need to use constrained tag list
-- user should create a tag "ParentTag" at the root of Tags and move all tags that you may want to use for constrained tagging under "ParentTag" 
property parentTagLocation : "/Tags/ParentTag" -- don't tocuh

-- WARNING: only for very experienced user
--- For opt2 -  if byLocation : false 
-- the name and location of group don't matter as long as  absTopStackUUID is correct
property absTopStackUUID : "7D11E68B-AA65-4465-98E8-BFD7CA1A8D23" -- please set this property to your centralised topstack's uuid
-- the name and location of parentTag don't matter as long as absParentTagUUID is correct, the constrained list will be the same for all databases
property absParentTagUUID : "160F4034-CA47-4A77-A415-C335E5FC2D12" -- please set this property to centralised your parentTag's uuid

-- You need to create two custom meta data fields to hold the location of document-stack and keep track of the number of cards to create an unique card identifier
set mdCardNum to "mdcardnum" as string -- data type is "Integer", keep track of the number of cards created in each document-stack 
set mdStackLink to "mdstacklink" as string -- data type is "URL", hold the link to the document-stack

-- Type, naming, format, numerate of card
-- IniCardType : "C" is more flexible, if do have not selected any text, the script will ask you whether to create blank-note or to quit
property IniCardType : "C" -- "C" for creating note with cited text or blank note,  "N" for blank note only
property cardNameFormat : "F" -- "F" card name = full name of the annotated document + unique card ID,  "A" = aliases + + unique card ID
property cardCountDig : 3 -- 3 digits = from 001 to 999 cards for each document
property fontOfCard : "<font face=\"Helvetica\">" -- Font to use in the card
-- property fontOfCard : "<font face=\"SF Pro Text\"  style=\"font-weight:100;\">". -- my personal font face
property addCardComment : true -- true if user want to add short comment to the Finder comment.

-- Tagging
global theTags -- don't touch
global cardType -- don't touch
property cardTagging : "A" -- Tagging methods: "N"= None, "M"=Manually input, "C1"=Constrained to one level tagsunder one parent tag, "CA"=  Constrained to to all level tags under one parent tag "A"=All tags in database, "IA"=Inherit All, "IC"=Inherit by choice


-- Misc Settings
set maxLen to 2000 -- as you wish but I don't know the consequence 
property noDecoration : "\" style=\"text-decoration:none;" -- don't touch

-- you can ignore the belowed settings if you don't care where the card window is
global screenWidth, screenHeight -- don't touch
set {screenWidth, screenHeight} to {3200, 1800} -- set to ur main screen's resolution
property cardPosition : "UL" -- placement of newly created card. UL=upper left, ML=middle left, LL=lower left, UM/MM/LM/UR/MR/LR etc. "N" = not specific placement

(*MAIN SCRIPT*)
tell application id "DNtp"
	try
		
		set cardType to IniCardType
		set thisItem to the first item of (the selection as list)
		if thisItem is {} then error "Please select something"
		
		set theCitedText to selected text of think window 1 as string
		
		if theCitedText is "" then
			display dialog "No text is selected, change to note-only card? OK to continue, Cancel to quit" buttons {"OK", "Cancel"} default button 1
			set cardType to "N"
		end if
		
		set TopStackUUID to my getUUID(byLocation, "S")
		set ParentTagUUID to my getUUID(byLocation, "T")
		
		set topStack to (get record with uuid TopStackUUID)
		set topStackname to (name of topStack)
		set theDoc to content record of think window 1
		set theDocUUID to (uuid of theDoc) as string
		set theDocName to (the name of theDoc)
		set theDocAliases to (the aliases of theDoc)
		set theStackName to theDocName & " (Stack)"
		
		-- Create cited-text note or plain-note
		if cardType is "C" then
			if length of theCitedText is greater than maxLen then error "That selection is longer than " & maxLen
			-- highlight or underline the selected text
			tell application "System Events"
				delay 0.05
				-- highlight colour number 7, use a specific color to show that the text is cited in the cards, the short-cut to be set in macOS is shift-ctrl-7, or any shortcut at ur choice
				-- but you need to know how to do it
				key code 26 using {shift down, control down}
				-- keystroke "u" using {command down} -- it's easier to use this if you choose to underline the cited text
			end tell
			
			-- to remove the symbol "- ", it is common for older journal articles to have many trancated word in sentences' return3
			-- comment this chunk of code if you don't need the trim
			set AppleScript's text item delimiters to the "- "
			set the item_list to every text item of theCitedText
			set AppleScript's text item delimiters to the ""
			set theCitedText to the item_list as string
			set AppleScript's text item delimiters to ""
			
		else
			set theCitedText to "Start writing"
		end if
		
		-- update the card number, get the location of document-stack if exist, create a new document-stack for 1st time creation
		tell theDoc
			set MDCustom to the custom meta data of theDoc
			try
				set theCardNum to mdCardNum of MDCustom
				set theCardNum to theCardNum + 1
				add custom meta data theCardNum for mdCardNum to theDoc
			on error
				set theCardNum to 1
				add custom meta data theCardNum for mdCardNum to theDoc
			end try
			
			try
				set theStackLink to mdStackLink of MDCustom
				set theStackUUID to texts 21 thru -1 of theStackLink
				set theStack to (get record with uuid theStackUUID)
				
			on error
				set theStack to create record with {name:theStackName, type:group} in topStack
				set theStackUUID to (get uuid of theStack)
				add custom meta data "x-devonthink-item://" & theStackUUID for mdStackLink to theDoc
				display alert "A new stack for this document is created" giving up after 1
			end try
		end tell
		
		-- set the name and card identifier for new card
		set theCardName to my setCardName(theDoc, theCardNum, cardNameFormat, cardCountDig, cardType)
		-- format the text in new card
		set theCardContent to my setCitationFormat(theDoc, theCitedText, cardType, think window 1)
		
		-- create the card
		set theNewCard to create record with {name:theCardName, source:theCardContent, type:rtf} in theStack
		
		-- ask for comment if the option is enabled in (SET UP)
		if addCardComment is true then
			set theCardComment to text returned of (display dialog "Add 10 words or less to the comment field:" with title "Comment" default answer "")
			my newCardComment(theNewCard, theCardComment)
		end if
		-- open the card and place it at specfic location on the screen
		open window for record theNewCard
		my newCardPosition(cardPosition)
		-- ask for tagging, methods depends on the option enabled in (SET UP)
		my newCardTag(theDoc, theNewCard, cardTagging, ParentTagUUID)
		
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink Pro" message error_message as warning
	end try
end tell


(*ALL HANDLERS*)

on setCardName(theDoc, theCardNum, theNameFormat, theCounter, theType)
	local theCardName, theDate
	set theDate to do shell script "date +'%Y.%m.%d'"
	if theType = "C" then
		set theCode to ".C"
	else
		set theCode to ".N"
	end if
	tell application id "DNtp"
		if theNameFormat is "F" then
			set theCardName to (name of theDoc) & " (Card " & theDate & theCode & my add_leading_zeros(theCardNum, theCounter - 1) & ")"
		else
			set theCardName to (aliases of theDoc) & " (Card " & theDate & theCode & my add_leading_zeros(theCardNum, theCounter - 1) & ")"
		end if
	end tell
	return theCardName
end setCardName

on add_leading_zeros(this_number, max_leading_zeros)
	set the threshold_number to (10 ^ max_leading_zeros) as integer
	if this_number is less than the threshold_number then
		set the leading_zeros to ""
		set the digit_count to the length of ((this_number div 1) as string)
		set the character_count to (max_leading_zeros + 1) - digit_count
		repeat character_count times
			set the leading_zeros to (the leading_zeros & "0") as string
		end repeat
		return (leading_zeros & (this_number as text)) as string
	else
		return this_number as text
	end if
end add_leading_zeros

on findAndReplaceInText(theText, theSearchString, theReplacementString)
	set AppleScript's text item delimiters to theSearchString
	set theTextItems to every text item of theText
	set AppleScript's text item delimiters to theReplacementString
	set theText to theTextItems as string
	set AppleScript's text item delimiters to ""
	return theText
end findAndReplaceInText

on setCitationFormat(theRecord, theText, theCardType, theWin)
	tell application id "DNtp"
		try
			-- set thePage to the ((current page of think window 1) as string)
			
			if the kind of theRecord is in {"PDF+Text"} then
				set thePage to (current page of theWin)
				set theLink to (the reference URL of theRecord) & "?page=" & (thePage)
				set thePageLink to "<a href=\"" & theLink & noDecoration & "\">" & " Back-Link to pdf page " & (thePage + 1) & "</a>"
				set thePageString to " (:" & (thePage + 1) & ")"
			else if the kind of theRecord is not {"PDF+Text"} then
				-- wait for DT3v4!
				-- set theLink to (the reference URL of theRecord) & "?paragraph=" & theCitation
				set theLink to (the reference URL of theRecord)
				set thePageLink to " <a href=\"" & theLink & noDecoration & "\"> Back-Link to text paragraph source </a>"
				set thePageString to ""
			end if
			
			if theCardType = "C" then
				set theAnnotation to "<meta charset=\"UTF-8\">" & fontOfCard & "<p><b>" & thePageLink & "</b></p>" & "<p>" & "<b>Cited Text:</b><br>" & theText & "<br><b>" & thePageString & "</b></p>" & "<b>Notes:</b><br></font>"
			else if theCardType = "N" then
				-- set theAnnotation to "<meta charset=\"UTF-8\">" & fontOfCard & "<p><b>" & thePageLink & "</b></p><b>" & thePageString & "</b></p><b>Notes:</b><br></font>"
				-- set theAnnotation to "<meta charset=\"UTF-8\">" & fontOfCard & "<p><b>" & thePageLink & "</b></p><b>" & thePageString & "</b></p><b>Notes:</b><br>" & theText & "</font>"
				set theAnnotation to "<meta charset=\"UTF-8\">" & fontOfCard & "<p><b>" & thePageLink & "</b></p><b>Notes related to:</b><br>" & theText & "</font>"
			end if
			-- from (F)
			-- set o_theAnnotation to do shell script "echo " & theAnnotation & " | textutil -format html -convert rtf -stdin -stdout | pbcopy -Prefer rtf"
			-- from (k)
			set o_theAnnotation to (do shell script "echo " & quoted form of theAnnotation & " | textutil -format html -convert rtf -stdin -stdout")
			
			return o_theAnnotation
			
		on error error_message number error_number
			if the error_number is not -128 then display alert "setCitationFormat() has error" message error_message as warning
		end try
	end tell
end setCitationFormat

on newCardPosition(thePosition)
	tell application id "DNtp"
		try
			set {WinWidth, WinHeight} to {screenWidth / 3, (screenHeight - 22) / 3} -- newly opened main window sized as a quarter of the screen
			set yAxis to 22
			set xAxis to 0
			if thePosition is "UL" then set bounds of window 1 to {xAxis, yAxis, xAxis + WinWidth, yAxis + WinHeight}
			if thePosition is "ML" then set bounds of window 1 to {xAxis, yAxis + WinHeight, xAxis + WinWidth, yAxis + 2 * WinHeight}
			if thePosition is "LL" then set bounds of window 1 to {xAxis, yAxis + 2 * WinHeight, xAxis + WinWidth, yAxis + 3 * WinHeight}
			if thePosition is "UM" then set bounds of window 1 to {xAxis + WinWidth, yAxis, xAxis + 2 * WinWidth, yAxis + WinHeight}
			if thePosition is "MM" then set bounds of window 1 to {xAxis + WinWidth, yAxis + WinHeight, xAxis + 2 * WinWidth, yAxis + 2 * WinHeight}
			if thePosition is "LM" then set bounds of window 1 to {xAxis + WinWidth, yAxis + 2 * WinHeight, xAxis + 2 * WinWidth, yAxis + 3 * WinHeight}
			if thePosition is "UR" then set bounds of window 1 to {xAxis + 2 * WinWidth, yAxis, xAxis + 3 * WinWidth, yAxis + WinHeight}
			if thePosition is "MR" then set bounds of window 1 to {xAxis + 2 * WinWidth, yAxis + WinHeight, xAxis + 3 * WinWidth, yAxis + 2 * WinHeight}
			if thePosition is "LR" then set bounds of window 1 to {xAxis + 2 * WinWidth, yAxis + 2 * WinHeight, xAxis + 3 * WinWidth, yAxis + 3 * WinHeight}
		on error error_message number error_number
			if the error_number is not -128 then display alert "NewCardPosition() has error" message error_message as warning
		end try
	end tell
end newCardPosition

on newCardComment(theRecord, theComment)
	tell application id "DNtp"
		try
			set comment of theRecord to theComment
		on error error_message number error_number
			if the error_number is not -128 then display alert "newCardComment() has error" message error_message as warning
		end try
	end tell
end newCardComment

on newCardTag(theSourceDoc, theRecord, theTagType, theTagUUID)
	local theTags
	tell application id "DNtp"
		set parentTag to (get record with uuid theTagUUID)
		try
			if theTagType is "M" then
				repeat
					set theTagList to display name editor "Add Tags" info "Tags (separated by comma):"
					if theTagList is not "" then exit repeat
				end repeat
			else if theTagType is "IA" then
				set theTagList to tags of theSourceDoc
			else if theTagType is "IC" then
				set theTags to tags of theSourceDoc
				if theTags is {} then
					display alert "Your source document does not have any tag" giving up after 1
					set theTagList to {}
				else
					set theSortTags to my sortlist(theTags)
					set theTagList to (choose from list theSortTags with prompt {"Choose tags to apply:"} default items "" with multiple selections allowed)
				end if
			else if theTagType is "C1" then
				set theTags to (name of every child of parentTag) whose (tag type is ordinary tag)
				set theSortTags to my sortlist(theTags)
				set theTagList to (choose from list theSortTags with prompt {"Choose tags to apply:"} default items "" with multiple selections allowed)
			else if theTagType is "CA" then
				set theTags to my gettags(parentTag)
				set theSortTags to my sortlist(theTags)
				set theTagList to (choose from list theSortTags with prompt {"Choose tags to apply:"} default items "" with multiple selections allowed)
			else if theTagType is "A" then -- could be slow if db has many tags
				set theTags to (name of every parent of current database whose (tag type is ordinary tag))
				set theSortTags to my sortlist(theTags)
				set theTagList to (choose from list theSortTags with prompt {"Choose tags to apply:"} default items "" with multiple selections allowed)
			else
				set theTagList to {}
			end if
			
			set the tags of theRecord to theTagList
			
		on error error_message number error_number
			if the error_number is not -128 then display alert "newCardTag() has error" message error_message as warning
		end try
	end tell
end newCardTag

on gettags(theParent)
	local theTagList, tagName
	set theTagList to {}
	tell application id "DNtp"
		repeat with theChild in children of theParent
			if tag type of theChild is ordinary tag then
				set tagName to the name of theChild
				set theTagList to theTagList & tagName
				set theTagList to theTagList & my gettags(theChild)
			end if
		end repeat
	end tell
	return theTagList
end gettags

on sortlist(theList)
	set theIndexList to {}
	set theSortedList to {}
	repeat (length of theList) times
		set theLowItem to ""
		repeat with a from 1 to (length of theList)
			if a is not in theIndexList then
				set theCurrentItem to item a of theList as text
				if theLowItem is "" then
					set theLowItem to theCurrentItem
					set theLowItemIndex to a
				else if theCurrentItem comes before theLowItem then
					set theLowItem to theCurrentItem
					set theLowItemIndex to a
				end if
			end if
		end repeat
		set end of theSortedList to theLowItem
		set end of theIndexList to theLowItemIndex
	end repeat
	return theSortedList
end sortlist

on getUUID(thePathType, whichUUID)
	tell application id "DNtp"
		local theUUID, theRecord
		try
			if thePathType is true then
				if whichUUID is "S" then
					set theRecord to (get record at stackLocation)
					set theUUID to (get uuid of theRecord)
				else if whichUUID is "T" then
					set theRecord to (get record at parentTagLocation)
					set theUUID to (get uuid of theRecord)
				end if
			else
				if whichUUID is "S" then
					set theRecord to (get record with uuid absTopStackUUID)
					set theUUID to (get uuid of theRecord)
				else if whichUUID is "T" then
					set theRecord to (get record with uuid absParentTagUUID)
					set theUUID to (get uuid of theRecord)
				end if
			end if
			
			return theUUID
		on error error_message number error_number
			if the error_number is not -128 then display alert "getUUID() has error" message error_message as warning
		end try
	end tell
end getUUID

2 Likes

That’s a very good suggestion. I did something similar in my last tagging script exercise but kind of exhausted and too exited to post the script… Will do that after further testing and debugging. I hope that’s Not too much beyond my ability! Just begin picking up programming again after many many years and my holiday is almost over…

EDITED: uuid method only works for one centralised stack to many databases configuration.

Using uuid is more user-unfriendly than other methods (e.g. Easy way will be requiring to put a specific group with a specific name in specific location), but UUID give user the more/most flexibility for future usage. No matter where you move the top stack (within the same database) or whatever new name you give to the top stack, uuid never changed. Unless you create a new top stack. But that’s also easy, all u need is to move the stacks under the old stack to the new top stack, and update the top stack’s uuid in the script again - you just need to be careful and verify the uuid. The best way is to test it on a testing database to build up your confidence (that’s what I did) While I agree with @korm, I need to learn another chuck of codes to write the setup (I thought about it). So, this is the suggested steps to get the uuid and the name of the two fields.

You can attach the script to a button or in the script menu. Customise it to whatever way you like and share ur codes and usage!

Cheers

This is how to get the uuid of the top stack.

Uuid of parent tag: Same method applies to the parent tag - if you don’t need to constrain the choice of tags to a specific group of tags (“C1” and “CA”) , you don’t need the uuid of the parent tag at all. (Personally, I like using constrained list a lot!).

property cardTagging : "C1" -- or "CA"

A usual kind reminder, test the script with a test database. And, if you are uncomfortable with the script, IMHO the best thing is not to use it, take time to read more forum posts about scripts, try running simple scripts first, gain experience, and perhaps going back to more complicated script, or this one, later (that’s what I did, too). If this feature has real value, someone will/may modify it either from the ease-of-use or from the feature perspectives.

This is how to get the uuid
Create a stack group, create also a plain text file:

ctrl-click on the group “Stack”, choose copy item link

choose the text file (not rich text!), choose paste

You will see the URL now, just copy the number and paste it to the script.

Regarding the meta data field, the field name is what’s being shown in the identifier field with the addition of “md” as prefix. In the image, the identifier is “cardnum” and “stacklink”, so the field names are “mdcardnum” and “mdstacklink”


One more step in setting up automatic highlighting. This one is more indirect:

Step 1:
Go seek this piece of code in the MAIN script

tell application "System Events"
		delay 0.05
		key code 26 using {shift down, control down} -- highlight number 7 in light grey
		-- keystroke "u" using {command down} -- if underline
end tell

If you are happy with underlying only, comment the first line (“key Coe 26…”) and uncomment the “keystroke “u”…”. That’s the easiest way. In my case the code in the box is equivalent to shift-ctrl-7

If you “really” want to highlight, I’d suggest you setup your 7th colour
21
Go setup a key shortcut under macOS preference->keyboard->short cut-> app shortcuts->DEVONthink 3,app. Add the exact name of your 7th colour and assign the short cut key.


The apple key short-cut is not too reliable, most of the time the highlighting will work.

Attached is a table of key stoke code, this might come handy for anyone who needs to program key short-cut in AppleScript.

Key codes for scripting.pdf (135.3 KB)

Thanks for the PDF.

Do note: UI scripting is a fiddly and fragile thing. You should use it very sparingly and don’t become reliant on it.

@ngan thank you for your continued work on annotations. I am particularly interested to see how the stack function (what you mention as Main Purpose 2) works in practice.
My understanding of the scripting is limited, so I need to ask for your clarification: will the script you wrote address my request made in point 2 in our earlier conversation to which you replied here.

I also like that this solution is being developed from within DT leveraging its features and functions, and can exist alongside and simultaneously with what the DT app developers have designed.

Demo: Note only, add comment, choose from tag list, full naming.

User have created a stack and start working on a paper:
Notice that the two custom fields “IdxCardN” and “StackLink” are empty.


User have a tag parent like this:

The first time time user writes a note on the paper:
A new stack for the paper will be created under the group “Stack”

Right before a new note is created, script asking for a short comment on the note:

Note pops up and ask for tags from a constrained list:
Multiple tags are selected. (There are many options for tagging)

After creating a few notes, going back to the annotated/source document:
You’ll see that three notes are created, and there is now an URL under the StackLink field.
Choose Launch.

And you will see this:
So, the group of notes, or notes on cited text (if cited note option is setup in script), will always connected to the source by linking the source with a stack.

Reminder again. Donot jump to it, test it with new database or don’t use it until someone is more familiar with simpler script. It took me 1 year to start understanding how the v2 and v3 are working and dare to test the program. I make this one more flexible in suiting different users’ needs, but the trade-off is certain level of script experience is required.

My holiday is over soon and won’t be able to refine the script (it’s good enough for me) although I might be working on it again much later. BUT creation is always more fun than perfection when scripting is a leisure. So I hope some forum members who may want to use this feature will continue to improve the script and making it more user-friendly in the initial setup and configuration and more feature rich in meeting different needs.

Wow! Thanks for this - will be sure to give this a go.

UPDATE:
Some small changes to make the script smarter. If user always take notes in combination of note with cited text and blank note, use “C” as the option.
When “C” is used, and if no text is selected when calling the script, a dialogue box will ask whether user wants to continue to take a blank note or want to cancel. I have updated the script as V0.8 in the first post.

property IniCardType : "C" 
-- "C" for note with cited text BUT when no text is selected, script will ask whether user wants to quit or to take blank note instead for this specific note.  "N" for blank note only

UPDATE 2:
One more change to make tagging slightly more flexible by splitting the “I” of “property cardTagging :” into “IA” (Inherit all tags from source document) and “IC” (Inherit tags from source document by choice).
Code amended in V0.81.

SHORT DEMO: Note with Cited Text enable, Inherit by choice (“IC”)

property IniCardType : "C"
property cardTagging : "IC"

The source document has some tags:


After the cited-text note is created, a popup list offering all the tags of the source document:

This represents a great deal of work and a wonderful contribution from @ngan – one of many. Well done!

1 Like

Thanks Korm.
@Korm and another very experienced DT programmer point out that using uuid method is uncommon and may reduce the number of users that may be benefit from the direct adoption of the script, and suggest using more standard method, such as asking users to create a group with a specific name and at a specific location, or write another setup script to facilitate the process of configuration. I am reluctant to do it due to the lack of time and knowledge in scripting, and the fact that I really like to use uuid! BUT, using uuid is creating an unexpected BUT extremely interesting (to me) consequence.
I “will/must” offer a switch for user to create a specific group/location instead of just offering the uuid method. I will try to make some minor change in the next version asap before I am back to my work.

I’ve never considered the consequence of uuid method due to my experience as a 2-database user (one for home and one for my work):
UUID is an absolute address method. It’s pointing to an “absolute top stack” and “absolute parent tag”. If the stack/parent tag is located at a specific database (say db1), as long as db1 remains open, even if your source document/s is/are located at different databases, the document stacks and the list of tagging (for constrained list) will still be created in the same top stack and based on the same parent tag in db1! That’s interesting, because it means that you have now created a centralised area/db that holds all of you notes from different databases regardless of how many projects/database you are working on. I haven’t tested the theory by creating many more new databases but I think I’m correct. If user don’t understand what I’m saying, definitely shouldn’t use uuid in this script. (EDITED, I initial use “you” instead of “user” in which definitely does not refer to Korm!)

Creating a top stack and parent tag in specific location in “a” database is relative address. So notes/cards/choice of tags are based on the whichever database the source documents are located. It’s more flexible and may be essential for multiple-database users.

So, the modified version will have two options of setting up the top stack and parent tag: (1) default method will be requiring user to create a “TopStack” group and “ParentTag” tag at root level (2) advance users can switch off the property and use uuid instead if they know what they are doing.

Ver 0.9 is updated in the 1st post. If you have question in understanding the options in the (* SET UP *) section of the script, I’ll try my best to explain to you. If you have debugging question, I might not be of help. Please accept my apology in advance.

I’ll have a final version (I hope) that should be much easier to config. Will post at a new thread later today.

Final version posted here