The card-case system needs each card to have a unique and permanent id number. This id is used for finding cross-referenced cards. Leo creates a unique id for each node, so I decided to use this identifier, known as a *gnx*. An example gnx is *tom.20210521095407.1*. This id can be extracted by a bit of code in a script.
Make in last year's thread, I had suggested that our "zettel" cards could be marked up with a bit of Restructured Text. Identifiers and key words, like the id, could be written in RsT's "Field List" format. This would make them easy to parse, and they would display very readably if rendered as RsT. This format sets off the key word between colons, like this: :id: tom.20210521095407.1 :seealso: tom.20210321135407.1 :eventdate: 2021-05-21 It also turned out to be very helpful to add the name of target cards to the field list lines, and the name of the node as a header for the card. With these additions, the body of a card looks like this: William Collins ---------------- :id: tom.20210520181722.1 :created: 2021-05-20 18:17 :type: person :tag: Collins Immigration :marriage: tom.20210605195824.1 `Collins-King`_ :born: ~1615 :died: 1687 Born in Maidstone, Kent County, England. Died at Isle of Wight County, VA. Immigration Into America ^^^^^^^^^^^^^^^^^^^^^^^^^ William Collins emigrated from Kent County, England to King and Queen County, VA in 1635 aboard the ship "Plain Jane". The two underlines tell RsT to render the previous line as different title levels. They are optional but look good in a RsT rendered view of a larger part of a tree of cards. The backticks followed by an underscore are an optional markup that lets an RsT processor turn them into clickable links to the target. They do not add much visual clutter, and allow for navigation in a rendered browser view. A rendered view of a card or a subtree can be had using the Viewrendered3 plugin. This plugin can also export the rendered HTML to your browser. The attached Leo outline contains the little scripts I use, together with the key bindings I use for them. You would put them into the *@settings* tree of your card-case outline, or into *MyLeoSettings.leo* if you want them available in all outlines. The two most used commands are *zettel-convert-card* (F8) and *zettel-go-to-target* (F9). When you select a node and press F8, the current node will be converted into a zettel card. This means that the node headline is inserted with its underline as the title, and the id and date fields are also inserted. When you select or put the cursor on a field list line that contains a gnx as the second field, the selection jumps to the target card. This makes navigation extremely fast and easy. Almost as important is *zettel-goto-with-backlink* (F6). This jumps to the target node like F9 but also inserts a backlink to the sending card if the target does not already have one. If the target has a backlink but the backlink line does not contain a label, the script will insert the sending card's title as the label. Since backlinks are a key feature of any zettelkasten system, this script makes generating them accurate and nearly painless. The new backlink will be inserted as the first line of the target node, with the generic name *:link:*. You probably will want to move the line somewhere else, and to replace the string `link` with something more appropriate. But even if you don't, navigation will still function normally. The command *zettel-copy-gnx* (F7) lets you easily copy a card's gnx to the clipboard so you can paste it into another card as a target. Then F6 will take you to that target and insert a backlink. Another F6 will add the target's label to the link line of the sending card. Now both cards have been labeled and equipped with mutual backlinks. Pleas note that my original F8 script from last year had a bug that sometimes inserted an incorrect gnx. Also, it created entire new car, but I find my new way of converting an existing node to be easier and quicker. To summarize this navigation system, the field list lines like :link: gnx label let you navigate to any crosslinked card within a leo outline, and the optional RsT label/backtick/underscore markup lets you navigate in a rendered view displayed in a web browser. You can also use any of Leo's search capabilities to search through the cards. I find the Nav pane to be especially good for this. In my next post, I will cover ways to get your data out of Leo if you need to. It can be very simple. On Thursday, June 17, 2021 at 11:10:26 PM UTC-4 [email protected] wrote: > Over a year ago we had a long thread on the "Zettelkasten" system: > > https://groups.google.com/g/leo-editor/c/TqiNdBfnEig > > To briefly reprise, a *zettelkasten* is a system for capturing and > cross-linking of knowledge. The term can be translated as "card case", and > the original - or at least the one that became somewhat well known - > comprises a set of index cards, a group of file drawers to store them, and > a way of numbering the cards so that they cross-refer to other cards. Each > card (or "zettel") is expected to carry a short, narrowly focused, thought > or essay-like paragraph about some topic. > > The system was written about by Niklas Luhmann, who apparently was wildly > prolific as a writer and known for his deep knowledge of a number of fields > of study. He credited his zettelkasen with much of his success, referring > to it as a second brain or a very smart companion. > > See, e.g., "Communicating with Slip Boxes by Niklas Luhmann": > > http://luhmann.surge.sh/communicating-with-slip-boxes > > Luhmann had developed a way of numbering and cross-linking his cards that > seems to have worked extraordinarily well for him. > > The thread on Google Groups was mostly concerned with whether Leo could be > used for such a system, and how that would work. There are several > software systems that claim to be designed for this purpose, but I didn't > find any of them satisfactory, and it would generally have been hard to > impossible to get your data back out in a usable form if you wanted to > switch to another way of working. > > Eventually I distilled the points that had been discussed into a set of > user requirements, and I realized that with a few simple scripts and a > standard way of working, a zettelkasten system in Leo would be extremely > easy and effective. > > One of the key points is to have a way to extract your data easily, which > means working with a text format or something that can be converted to a > text format easily. As you will see, that is quite simple. > > After all this, I got diverted and didn't do much with it until recently, > a year and more later. I wanted to capture some genealogy/family history > data and organize it somehow. So I have been working with the card-case > system, and I've learned some practical things. This has led me to > slightly change the scripts, and to settle on a way to format and use the > cards. I think it's working really well, and I'd like to pass this > experience along. > > I have attached the requirements I came up with along with some links to > more material about this kind of system. In my next post I will talk about > the card format and the scripts that make it all work. > -- You received this message because you are subscribed to the Google Groups "leo-editor" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/leo-editor/ae515146-7e35-4ccf-aaf6-9351ee56a844n%40googlegroups.com.
<?xml version="1.0" encoding="utf-8"?> <!-- Created by Leo: http://leoeditor.com/leo_toc.html --> <leo_file xmlns:leo="http://leoeditor.com/namespaces/leo-python-editor/1.1" > <leo_header file_format="2"/> <globals/> <preferences/> <find_panel_settings/> <vnodes> <v t="tom.20210617221750.2"><vh>Scripts</vh> <v t="tom.20210617221858.1"><vh>Zettel commands</vh> <v t="tom.20210617221858.2"><vh>@command zettel-goto-with-backlink @key=Ctrl+F6</vh></v> <v t="tom.20210617221858.3"><vh>@command zettel-copy-gnx @key=Ctrl+F7</vh></v> <v t="tom.20210617221858.4"><vh>@command zettel-convert-card @key = Ctrl+F8</vh></v> <v t="tom.20210617221858.5"><vh>@command zettel-go-to-target @key = Ctrl+F9</vh></v> <v t="tom.20210617221858.6"><vh>@command zettel-show-id</vh></v> </v> </v> </vnodes> <tnodes> <t tx="tom.20210617221750.2"></t> <t tx="tom.20210617221858.1"></t> <t tx="tom.20210617221858.2">@language python """Extract gnx from an RsT "Field List" line and jump to its node, inserting a backlink in target. insert a backlink to this node in the target if the target does not have one, and jump to the target node. Does nothing if cursor is not on a line starting with ":xxx:". The part written "xxx" can be any string value, but not longer than 14 characters for best rendering. If the backlink exists on the target node, but it has no label, then add a label. The link specifier line is expected to look like this:: :xxx: TomP.20200223153257.1 About Leo Where `xxx` can be any string without spaces or non-alpha, and the string following the gnx is called the `lable` and can be any string except a gnx. If a label on the backlink exists, it is not changed, but if the link has no label, then one is constructed from the headline of the node. """ import re FIELDre = '^\s*:[^\s]+:\s+' GNXre = '.+\.\d+\.\d+' LABELre = '.*' FIELDLISTre = f'^{FIELDre}({GNXre})\s*({LABELre})$' def get_gnx_from_link(line): gnx = lable = '' matched = re.match(FIELDLISTre, line) if not matched: return '', '' gnx = matched[1] lable = matched[2].strip() return gnx, lable # List of line(s) containing cursor or selection cursor_lines = c.getBodyLines()[1] line = cursor_lines[0] if cursor_lines else '' line = line.lstrip() gnx, sending_label = get_gnx_from_link(line) own_gnx = c.p.gnx if gnx: if gnx == own_gnx: g.es('Target gnx is this node') else: start_gnx = own_gnx node_lable = p.h if node_lable.startswith('@'): node_lable = ' '.join(node_lable.split()[1:]) start_link = f':link: {start_gnx}' start_link_lable = f'{start_link} {node_lable}' _found_target = False for _p in c.all_unique_positions(): if _p.gnx == gnx: c.selectPosition(_p) _found_target = True break if _found_target: lines = _p.b.split('\n') # Three cases for backlinks to sending node: # 1. Sending node has no backlink # 2. Sending node has backlink, but the # backlink has no label # 3. Sending node has backlink with label. got_start_link = False for i, line in enumerate(lines): line = line.lstrip() gnx1, lable1 = get_gnx_from_link(line) if not gnx1: continue if gnx1 == own_gnx: got_start_link = True # case 2 if not lable1 : lines[i] += f' {node_lable}' _p.b = '\n'.join(lines) else: # Case 3 pass break if not got_start_link: # case 1 _p.b = f'{start_link_lable}\n{_p.b}' </t> <t tx="tom.20210617221858.3">@language python from leo.core.leoQt import QtWidgets QApp = QtWidgets.QApplication cb = QApp.clipboard() cb.setText(p.gnx) </t> <t tx="tom.20210617221858.4">@language python """Convert node to Zettel card with title, id and timestamp.""" title = p.h title += '\n' + '-'*(len(title) + 1) time_string = ':created :' + c.getTime(body=True) + '\n' zettel_str = f'{title}\n\n:id: {p.gnx}\n{time_string}\n' undoType = 'zettel-insert-node' w = c.frame.body.wrapper w.setInsertPoint(0) oldSel = (0, len(p.b)) w.insert(0, zettel_str) c.frame.body.onBodyChanged(undoType, oldSel=oldSel) </t> <t tx="tom.20210617221858.5">@language python """Extract gnx from an RsT "Field List" line and jump to its node.""" import re FIELDre = '^\s*:[^\s]+:\s+' LABELre = '.*' GNXre = '.+\.\d+\.\d+' FIELDLISTre = f'^{FIELDre}({GNXre})({LABELre})' # List of line(s) containing cursor or selection cursor_lines = c.getBodyLines()[1] line = cursor_lines[0] if cursor_lines else '' matched = re.match(FIELDLISTre, line) target = matched[1] if matched else None if target: found_gnx = False target_is_self = False if c.p.gnx == target: found_gnx = target_is_self = True else: for p in c.all_unique_positions(): if p.v.gnx == target: found_gnx = True break if found_gnx: if target_is_self: g.es('Target gnx is this node') else: c.selectPosition(p) else: g.es(f"Can't find target gnx {target}") else: g.es("Can't find a target gnx in line") </t> <t tx="tom.20210617221858.6">"""Show node id in log panel.""" g.es(p.gnx, p.h)</t> </tnodes> </leo_file>
