Emacs & Tinderbox integration with AppleScript

I didn’t see much talk about it in the forum, but it’s quite easy to wrap AppleScript code inside Emacs Lisp functions. So, this is an example for some basic integration (based on @sumnerg 's examples):

(defun tinderbox-applescript--escape-quotes (str)
  (with-temp-buffer
    (insert str)
    (goto-char (point-min))
    (while (search-forward "\"" nil t)
      (replace-match "\\\"" "FIXEDCASE" "LITERAL"))
    (buffer-string)))

(defun tinderbox-applescript--quoted-string (str)
  (if str
      (format "\"%s\"" (tinderbox-applescript--escape-quotes str))
    "missing value"))

(defun tinderbox-applescript-set-attributes (attr-list)
  (mapconcat (lambda (x) (concat "set value of attribute \"" (car x) "\" to "
                                 (tinderbox-applescript--quoted-string (cdr x))))
             attr-list
             "\n"))

;;;###autoload
(defun tinderbox-make-or-update-note (name &optional text container attributes)
  "Make a new note.
`NAME' is the note name.
`TEXT' is the textual content of the note.
`ATTRIBUTES' is a list of cons cells with the format (attribute . value).
`CONTAINER' is a string with the name of the location (a note) where to create the new note.
If nil, the note will be created in the currently selected note."
  (let ((script-command (format "osascript <<EOF
set containerName to %s
set theName to %s
set theText to %s
tell application \"Tinderbox 8\"
	tell front document
		if not (exists) then error \"No Tinderbox document open.\"
		if containerName is not missing value and not (exists note containerName) then
			set newContainerNote to make new note at before first note
			tell newContainerNote to set name to containerName
		end if
		if containerName is not missing value then
			set theContainer to note containerName
		else
			set theContainer to the selected note
		end if
		tell theContainer
			if not (exists note theName) then
				set newNote to make new note at theContainer
			else
				set newNote to note theName
			end if
			tell newNote
				set its text to theText
				set value of attribute \"Name\" to theName
				%s
			end tell
		end tell
	end tell
end tell
EOF
"
                                (tinderbox-applescript--quoted-string container)
                                (tinderbox-applescript--quoted-string name)
                                (tinderbox-applescript--quoted-string text)
                                (tinderbox-applescript-set-attributes attributes))))
    (shell-command script-command "*Tinderbox output*" "*Tinderbox errors*")))

;;;###autoload
(defun tinderbox-initialize-attribute (attribute attr-type &optional default-value)
  "Create an attribute if needed"
  (let ((script-command (format "osascript <<EOF
set theAttributeName to %s
set theAttributeType to %s
set theDefaultValue to %s
tell application \"Tinderbox 8\"
	try
		set theDocument to front document
		if not (exists attribute theAttributeName in theDocument) then
			set theAttribute to make attribute in theDocument
			set type of theAttribute to theAttributeType
			set name of theAttribute to theAttributeName
			if theDefaultValue is not missing value then
				set defaultValue of theAttribute to theDefaultValue
			end if
		end if
	on error error_message number error_number
		if the error_number is not -128 then display alert message error_message as warning
		return
	end try
end tell
EOF
"
                                (tinderbox-applescript--quoted-string attribute)
                                (tinderbox-applescript--quoted-string attr-type)
                                (tinderbox-applescript--quoted-string default-value))))
    (shell-command script-command "*Tinderbox output*" "*Tinderbox errors*")))

(provide 'el-tinderbox)

This is part of a larger project, my phi-notes package for “Zettelkasten”-style note systems.

4 Likes

Can you explain what this is?

@satikusala , Emacs is a text editor written and extensible in the Lisp language.

The code above could be used programmatically as follows:

(tinderbox-make-or-update-note "Note name" "Text body...")

Something more useful, though, would be an interactive function to export the text file you’re working on (in Emacs terminology, the current buffer). This would do the trick:

(defun tinderbox-export-current-buffer ()
  "Export the current buffer as a child note of the selected note in Tinderbox."
  (interactive)
  (let ((name (file-name-sans-extension (file-name-nondirectory buffer-file-name)))
        (text (substring-no-properties (buffer-string))))
    (tinderbox-make-or-update-note name text)))

An interactive function can be called in Emacs by pressing meta (alt) + x and then typing its name (M-x tinderbox-export-current-buffer in our case). In practice, a user would probably bind it to some key combination (e. g. ^C ^E, or C-c C-e) declared somewhere in her configuration files:

(global-set-key (kbd "C-c C-e") 'tinderbox-export-current-buffer)

A more complex example, using Prototypes and other note attributes, can be found here.

1 Like

Useful for doing what? If the text can’t be edited properly in Tinderbox, wouldn’t it be better to resolve that shortcoming in Tinderbox rather than use a potential fragile inter-app shuttle. My apologies if I’ve mis-understood the issue here.

@mwra I think it really depends on the task and the workflow. I don’t use Tinderbox as the final destination for my notes (I prefer individual Markdown files accessible from Emacs, The Archive, 1Writer…).

What I’ve been doing now, while preparing some writing, is to “export” my notes and work with them visually in the Map view (exploding them, connecting them etc.). Concretelly, I’m gathering my notes and trying to make sense of how to connect the subjects and construct my argument; for this, the approach has been useful.

If I understand correctly you want your notes usable (openable) directly in multiple writing apps. So you really only want Tinderbox for working in Map view?

The problem I see with a canonical Markdown-based text-file-per-note system that I don’t believe Tinderbox can recreate links that only occur as Markdown code. The latter is necessary to capture inter-doc links outside Tinderbox. Or, does this not arise as your work style does not use links? Latter question reflects the fact there is no requirement in Tinderbox to make explicit links between notes.

Yes. I now use Tinderbox mainly as an analysis tool, rather than a repository for my notes. For this latter purpose, my system is a collection of Markdown, Zettelkasten-style files. I was radical enough to the point of storing a whole book I wrote in the system! (Using separate notes for chapters/sections, with the help of a script to assemble them and then converting for publishing.)

I have some TBX files with code/stamps/edicts for searching wiki links ([[nnnn]]) to connect the notes per their unique numerical ID. I used this approach before the advent of Ziplinks, which are actually incompatible with the approach (I disable this feature in all my documents).

One suggestion I think could perhaps be useful for general use, for people working with this kind of system: 1) have a convention for Zettelkasten-style IDs, say $ZettelID; 2) make the native Ziplinks aware of the field, so that a reference such as [[1234]] creates links to the note(s) whose $ZettelID == "1234".

2 Likes

Interesting, and your linking concept make perfect sense to me (i.e. within the context/constraints you face).

As regards ziplinks, the syntax/usage is already very baroque. The think the use of [[ was accidental and was mainly added to pacify those coming to Tinderbox from wiki-like tools (often using Markdown) who appear to only understand linking as invoked by use of [[.... I get that we are all prone to assumptions drawn from are most recent and most extensive experience.

One downside if the [[ trope is we now regularly see users refer to a think called ‘ziplinks’ when trying to talk about Tinderbox text links … which makes for a deal of confusion when trying to understand the problem being discussed. ‘zip’ links are better understood as a link-creation method (and just one of those available).

Going back to your current-buffer thought, do you want $Text of the current note or the export-rendered test. The first, with focus in the $Text area, is essentially Cmd+A then Cmd+C and the text string is on the clipboard. The second is the same, if you first select the ‘Preview’ tab of the Text pane. Is either/both is these what you want encapsulated into a single call?

Note that using the contents of ‘raw’ $Text will of course not evaluate any inline export code in $Text, e.g. ^include()^ or ^children()^ calls. That might therefore be another hidden assumption in the idea.

†. Internal preview mode has confused things here. But, ‘Preview’ is simply a way of seeing the export-rendered HTML (with or without Markdown) but without actually exporting. so, even if I never export form a TBX, in using preview I’m using code generated by the (HTML) export system. Whether or not a new ‘html’ or ‘md’ file is generated is of no relevance.

1 Like

Yes. I never link text this way myself. Besides, the wiki link syntax already isn’t universally accepted (iA Write for example never implemented them). That’s why I have doubts about pushing new “ziplink” feature suggestions. Anyways, the wiki link syntax is incredibly practical depending on your needs.

I send Markdown content as $Text, plus $Name at least. Other attributes (ID, bibliographical citekey etc.) are being imported, but I didn’t make practical use of them yet.

The appealing functionality is that I can quickly export and update the notes from inside Emacs. For the task at hand, this is nice because I can keep things organized on the map as my writing evolves, without alienating the “live” content to Tinderbox –– which would imply a much more complicated back-and-forth strategy. (This also allows me to use a 12-year old HP mini notebook running Peppermint Linux & Emacs as a secondary “typewriter”, which is pretty cool; it’s even possible to call osascript via ssh to communicate with Tinderbox running the Mac, which I did :slight_smile: ) .

A couple of years ago I complained about the “Preview” terminology. But I now agree that’s probably the only meaningful way to call this powerful feature! I use a nice responsive stylesheet which makes everything pleasant to work (either on the notebook screen or on an external 27-inch monitor). In this particular case (analysis, not publishing), I’m not even using pandoc, but the default Markdown processor, leaving pandoc bracketed citations as they are and benefitting from speed in rendering the preview.

1 Like

I’m still a bit lost on the current-buffer issue. Is the problem putting text on the OS clipboard? I should add I known what LISP and emacs are but have no practical experience of them, which limits my understanding a bit. I know the Tinderbox end better, and that’s the bit I’m trying to help resolve.

So, having established were’ dealing with—in Tinderbox terms—a note’s $Text (including Markdown markup), are we importing or exporting or both?

I’m also one who doesn’t use the zip-links method. But, it’s not antipathy to the method so much as practical issues:

  • I’m not a touch-typist (and inaccurate too) so a keyboard-only method is actually slower for me.
    is no real advantage, plus arrow and tab keys never work as I intuit so I get the wrong target note anyway.
  • My note names are often not unique within a project (for valid source data reasons).
  • My note names are often titles of documents or sources and those contain characters that mess up Tinderbox’s path parsing. Sure, I can gut the $Name to ease the parsing problem but it just shunts the problem elsewhere—especially if using export (I use export a lot).
  • I’ve been doing it the old way for years and am much faster with those controls.

None of those are problems of the zip method, but practical roadblocks for some users. Certainly, were I a fast touch-typist and using ordinary text (i.e. not tech language, odd symbols/characters, etc.) I can see the zip creation method is pretty cool. The fetishisation of [[ as a link indicator seems a passing fad. Today it’s Markdown, next year it will be something different/better and equally ‘urgently’ needed. Then again, if it’s what you know, it’s what you know. Wait long enough and fashion comes round again, too. :slight_smile:

I’m sorry, I think I didn’t get the question right. Is it on why to use something programmed in Lisp, rather than copying/pasting using the clipboard?

It’s essentially about making the process much more frictionless. I type M-m m c T (my keybinding in Spacemacs) and the current note is sent to a Notes container in the current TBX document, with appropriate $Name, $Text, and other attributes ($ZettelID, $Citekey, $ReadOnly, $URL with a custom scheme).

The same result could be obtained but would take some more time and attention to be done manually. In reality, I would simply not work with my notes in exactly the same way; the interesting thing with integrations is to find new possibilities to work with, I think.

More generally, for me, it’s a way of working comfortably with my notes from the ZK system to TBX (for analysis), and back again to the ZK system. The watched folder is another possibility, but when you have a large number of notes it gets quite complex to be maintained, usually requiring some CPU and often quite a lot of human time (I’ve been there…).

By the way, I have a Keyboard Maestro macro to open the URL for the selected note in Tinderbox with a keystroke. As my notes have a custom URL (x-phi://1234) to open them in Emacs, I navigate in Tinderbox and do the editing in Emacs.

Dear Bruno Conte,
Thank a lot initiating your phi-notes.
I try to install your phi-note from [GitHub - brunocbr/phi-notes: PHI notes emacs package] in my Spacemacs.
Of course creating a layer (M-x configuration-layer/create-layer) under ~/.emacs.d/private/phi. Add the following to packages.el:
But how to add in keybinding.el ?
Where and which one?
M-x phi-mode, I could not call this mode.
(Only delphi-mode is shown, but delphi-mode is an obsolate command.)

In your phi README.org , about Install section,
[You will need to add =phi= to existing =dotspacemacs-configuration-layers= list in this file.]
[=] is misleading label, I think.

Could you explain where should I add as a keybindings.el ?

[M-x phi-initialize-counter] , still I could not act on my order.
Thx and regards, WAKAMATSU

@WAKAMATSU Thank you for the feedback!

After editing packages.el, you can create keybindings.el (note the s) under ~/.emacs.d/private/phi. The file can contain the exact lines I suggested in the README.

In the install section, the information is correct (but perhaps not quite clear). The layer, if you follow the suggestion, will be called phi (it doesn’t need to have the same name as the package). After setting up the .el files, you have to enable the layer in your .spacemacs configuration file, inserting its symbol as an element in dotspacemacs-configuration-layers. Mine looks like this:

  dotspacemacs-configuration-layers
   '(
     python
     yaml
     helm
     emacs-lisp
     markdown
     org
     bibtex
     (shell :variables
            shell-default-height 30
            shell-default-position 'bottom)
     osx
     emoji
     phi
     olivetti
     )

If the phi element is missing, the layer won’t be loaded, so neither will the package. That’s most likely why you can’t evoke M-x phi-mode or M-x phi-initialize-counter. From the list above, helm and bibtex (and of course markdown) are required if you want to use all phi-mode features. olivetti is also highly recommended.

Please also note that the git repository (i. e. the phi-notes directory) should be cloned under ~/.emacs.d/private/phi/local.

Dear Bruno Conte,
Thanks a lot for your guidance.
I have not put including descriptions such as correct detailed bibtex
in dotspacemacs-configuration-layers.
This is one of the reason why I could not catch it, I think.
Yes, the phi-notes directory) is cloned under ~/.emacs.d/private/phi/local.
(/Users/kuni7/.emacs.d/private/phi/local/phi-notes)

I made mistaken in dotspacemacs-configuration-layers,
which was [phi] under dotspacemacs-additional-packages.
I will change the right place in configuration-layers.
And add under
bibtex
(shell :variables
shell-default-height 30
shell-default-position 'bottom)

After those sttings, M-x phi-mode, I got it.
In what manner, I need to find how to get the hang of phi-note, now.
I will conduct verification for Configure my note repository.
In the present situation, what is important is,
set up a study schedule.
In thankful acknowledgment, WAKAMATSU

Dear Bruno Conte,
Anything you could do for me would be very much appreciated.
I have a few questions about phi-note extention.
Should not I use [.md] or [.org] or other’s ?
I add a new folder at [/Users/kuni7/spacemacs/phi-note] for phi-note.
Is this correct repository location ?
I add using [ M-x phi-add-repository ] ut supra.

Object of the first exercise is first draft.
Could you show me an example for phi-note ?
This is my first trial phi-note using [ M-m O c ].
--------- quote--------Is this enouph for 1st draft ?
[0001 2022年02月04日.markdown]

id: Φ0001
citekey: 0
loc: 0
tags: 2nd note

此のノートは最初の書込みです。
-------close quote-------

Why has this Φ at the beginning of my id ?

Where and how should I change this expressions in ?

title:
id:
citekey:
loc:
tags:
originURL:

Respectfully,WAKAMATSU

P.S
Trouble configuring olivetti mode : Emacs, Software, Technology
I found this writing.
[[Olivetti mode for org files : emacs]]
Could you explain how to set up olivetti-mode in My Spacemacs?

Should I add only [(require 'olivetti)] in dotspacemacs ?
What else do I need?
Should I fix olivetti-body-width setting put forth by inder this site ?
[[distraction-free: Fix olivetti-body-width setting · 3b866c995e - emacs.d - MadHouse Git Repositories]]

Respectfully,WAKAMATSU

Dear @WAKAMATSU ,

The Φ sign is there for historical reasons, but it is not required. You may M-x customize-variable phi-header-pre and safely remove it from there. You can also M-x customize-variable phi-header-post, just don’t touch the %ss and the order of the fields (in the future, I should implement a more flexible template).

As for the tags: field, it is highly recommended that you use hashtags (e. g. #2nd-note). Some functions may not work perfectly otherwise.

This is something I use when exporting from other apps (such as Ulysses, Curio etc.). The idea is to use the field to keep a link back to the original file, where it should actually be edited.

olivetti-mode will require that you configure a layer for it (say, an olivetti layer). The packages.el file could be something like this (to get a very clean display, with no mode line, line spacing etc.):

(defconst olivetti-packages
  '(olivetti
    ))

(defun olivetti/init-olivetti ()
    (use-package olivetti))

(defun olivetti/markdown-mode-hook ()
  (spacemacs/toggle-mode-line-off)
  (set-fringe-mode 0)
  (setq line-spacing 13)
  (spacemacs/enable-smooth-scrolling)
  (olivetti-mode 1)
  (setq markdown-use-pandoc-style-yaml-metadata t)
  (smartparens-mode -1)
  (auto-highlight-symbol-mode 0)
  (scroll-bar-mode 0)
)

(remove-hook 'markdown-mode-hook #'olivetti/markdown-mode-hook)
(add-hook 'markdown-mode-hook #'olivetti/markdown-mode-hook)

I hope the package may be useful!

Dear Bruno Conte,
Thanks a lot.
For the Tag field, I will use the #Tag from upcoming
phi-note.
BTW, I use Zetteldeft and org-roam as a Zettelkasten.
Could you explain difference between your phi-note and those two.
There is room to hope for phi-note could join Tinderbox, I think.
But still now, I could not find a way to export from Zetteldeft and org-roam ver.2 to Tinderbox, simply.
I am afraid I did not read your feature description about export to Tinderbox, yet.
Will you please describe what bring out the best in your phi-note.
I am looking forward to get your recipe.
Truly Yours. WAKAMATSU

Dear Bruno Conte,
Thanks a lot for your guidance.
I got here after your phi-notes README.md.

phi-notes-withSidePane

Navigation Key bindings are usefull, I find.

Now how can I export those files to Tinderbox?
I would like to establish and maintain procedures for exporting behaviour.
Please teach me your practicable solution.
Anything you could do for me would be very much appreciated.

Respectfully, WAKAMATSU
P.S
My cirsumstance : 0.300.0@28.0.50(spacemacs) under OS10.14.6 Mojave.

Dear Bruno Conte,
I have a bunch of very real problems after launching Spacemacs.
Thanks in advance.
Could you offer hint of a solution how to take away those problems?

Errors: An error occurred while installing helm-gitignore (error:
(error Package ‘gitignore-mode-1.1.0’ is unavailable))

Warnings: Unknown layer olivetti declared in dotfile.

As noted in the Error.png attached.
I look forward to hearing from you at your earliest convenience.

Thx and regards, WAKAMATSU