267 lines
5.9 KiB
Markdown
267 lines
5.9 KiB
Markdown
# um
|
|
|
|
An (U)ltralight *zettelkasten* for (M)arkdown composition.
|
|
|
|
`um` is a *zettelkasten* specification, a CLI for creating compositions, and an emacs toolkit. It uses unique filenames, plaintext tags, the builtin emacs `project` package, and simple commandline conventions. It's grown out of my own scripts and emacs hacks over the years.
|
|
|
|
It tries to be stupid-simple on the filesystem side, while offering powerful conveniences on the tooling side. The idea is to prioritize the moment of creation and get all noise out of the way.
|
|
|
|
It consists of two parts:
|
|
|
|
1. A commandline interface written in Go. I prefer a CLI for file creation and management rather than more emacs functions, because chaining shell commands is easy and I think of the commandline as the point of reference for all filesystem management.
|
|
|
|
2. [Elisp](https://git.bth.moi/bth/um-mode) for functionality within emacs. Much of it is integrated directly with the `project` interface, and consists of the ability to find a file from a filename and search for tags.
|
|
|
|
Conceptually, `um` is somewhat like org-roam, except without any database dependency. And it assumes Markdown rather than org, which I find too heavy-handed.
|
|
|
|
This "database" depends on a few simple ideas:
|
|
|
|
1. A sequentially numbered filename specification which serves as unique id, like this: `001.foo.md`. The filesystem is the database. Note that the string descriptor is optional. In regex terms:
|
|
|
|
```sh
|
|
ls | egrep '^[[:digit:]]+\.?.*\.md|txt'
|
|
```
|
|
|
|
Or:
|
|
|
|
```
|
|
digits.[descriptor.]md
|
|
```
|
|
|
|
2. A file header consisting of the title, date, and optional tags. These tags can be used by the CLI to construct expressive queries.
|
|
|
|
```markdown
|
|
# 001.foo.md
|
|
: 2024.01.14
|
|
- place_optional
|
|
+ tag_optional
|
|
```
|
|
|
|
3. Using a "root" project defined by `um-root-glob`, where source files should first be composed and where we can assume a file exists if not elsewhere. This matters when trying to navigate back to a source file.
|
|
|
|
4. Using the built-in emacs `project` package and the CLI to organize compositions built from these source files.
|
|
|
|
# install
|
|
|
|
```sh
|
|
go install https://git.bth.moi/bth/um@latest
|
|
```
|
|
|
|
# CLI
|
|
|
|
The command line interface is a set of conveniences: since this is all plain text, we could just as easily create everything manually.
|
|
|
|
Try:
|
|
|
|
```
|
|
um help
|
|
um tag --help
|
|
```
|
|
|
|
## seed
|
|
|
|
To get started, create an empty directory to serve as content origin. It doesn't matter where or what it's called, since the CLI only assumes a sequentially numbered collection of files. Then create your first file, while seeding the zero-width. 4 zeros is plenty, since that means 10k files. My zettelkasten is 20 years old and has about 3000 entries with almost a million words:
|
|
|
|
```
|
|
touch 0000.md
|
|
```
|
|
|
|
## um next
|
|
|
|
To create a new file:
|
|
|
|
```sh
|
|
um next
|
|
```
|
|
|
|
This will create the file write the header:
|
|
|
|
```markdown
|
|
# 01.md
|
|
: 2024.01.14
|
|
```
|
|
|
|
If you run `um next` again you get:
|
|
|
|
```markdown
|
|
# 02.md
|
|
: 2024.01.14
|
|
```
|
|
|
|
Create a new file with an optional descriptor:
|
|
|
|
```sh
|
|
um next foo
|
|
```
|
|
|
|
Yields:
|
|
|
|
```markdown
|
|
# 03.foo.md
|
|
: 2024.01.14
|
|
```
|
|
|
|
Create a new file with an optional descriptor and tag:
|
|
|
|
```sh
|
|
um next foo bar
|
|
```
|
|
|
|
Yields:
|
|
|
|
```markdown
|
|
# 04.foo.md
|
|
: 2024.01.14
|
|
+ bar
|
|
```
|
|
|
|
Or just append `+` to add the descriptor as a tag:
|
|
|
|
```sh
|
|
um next foo +
|
|
```
|
|
|
|
Yields:
|
|
|
|
```markdown
|
|
# 05.foo.md
|
|
: 2024.01.14
|
|
+ foo
|
|
```
|
|
|
|
Or send a list of tags separated by commas. `+` still works:
|
|
|
|
```sh
|
|
um next foo +,bar,baz
|
|
```
|
|
|
|
Yields:
|
|
|
|
```markdown
|
|
# 05.foo.md
|
|
: 2024.01.14
|
|
+ foo
|
|
+ bar
|
|
+ baz
|
|
```
|
|
|
|
## um last
|
|
|
|
`um last` will print the name of the last numbered file.
|
|
|
|
## um mv
|
|
|
|
The `um mv` command makes it easier to add or change the string descriptor, while also updating the header:
|
|
|
|
```sh
|
|
um mv 02.foo.md bar
|
|
```
|
|
|
|
And we get `02.bar.md`:
|
|
|
|
```markdown
|
|
# 02.bar.md
|
|
: 2024.01.14
|
|
```
|
|
|
|
## um tag
|
|
|
|
The file header allows for an optional list of tags, one per line, marked by a leading `+`:
|
|
|
|
```markdown
|
|
# 02.md
|
|
: 2024.01.14
|
|
+ foo
|
|
+ bar
|
|
```
|
|
|
|
```markdown
|
|
# 03.md
|
|
: 2024.01.14
|
|
+ foo
|
|
```
|
|
|
|
This is the most powerful aspect of `um`: a simple list of tags applied to source files. It encourages small files organized from the bottom up, rather than topdown management - which gets in the way of good creative moods.
|
|
|
|
We can then search for files containing these tags. This list just goes to stdout, so the idea is to pipe it into a file for reordering within emacs:
|
|
|
|
```sh
|
|
um tag foo > some/filelist.um
|
|
```
|
|
|
|
### set logic
|
|
|
|
The query supports union as `+` and intersection as `,`:
|
|
|
|
```sh
|
|
> um tag foo+bar
|
|
|
|
02.md
|
|
|
|
> um tag foo,bar
|
|
|
|
02.md
|
|
03.md
|
|
```
|
|
|
|
And the complement:
|
|
|
|
```sh
|
|
> um tag foo --invert
|
|
|
|
03.md
|
|
```
|
|
|
|
Pipe them together to use a big union from which to subtract:
|
|
|
|
```sh
|
|
um tag foo,bar | um tag baz --invert
|
|
```
|
|
|
|
Run `um tag --help` to see what it can do.
|
|
|
|
## um sort
|
|
|
|
When working with the filelists produced by `um tag`, we'll want to rearrange the order of files and add or remove tags. Then when we update our filelist by rerunning `um tag`, we want the output to respect our updated order. `um sort` does this:
|
|
|
|
```sh
|
|
um tag foo+bar | um sort --key some/filelist.um
|
|
```
|
|
|
|
And we can tell `um sort` to write new list back to the `--key`:
|
|
|
|
```sh
|
|
um tag foo+bar | um sort --key some/filelist.um --write
|
|
```
|
|
|
|
This allows me to write book outlines as a shell script:
|
|
|
|
```sh
|
|
# chapter 1: foo
|
|
um tag book+foo | um sort -w -k ../projects/book/1.foo.um
|
|
|
|
# chapter 2: bar
|
|
um tag book+bar | um sort -w -k ../projects/book/2.bar.um
|
|
```
|
|
|
|
## um cat
|
|
|
|
This command is designed to work with the filelists produced by `um tag`. It separates files with a Markdown horizontal rule `---` while stripping their headers:
|
|
|
|
```sh
|
|
um tag foo | um cat
|
|
```
|
|
|
|
As the last step in composing larger pieces, it accepts a filelist and a base directory to find those files. We can then pipe the output wherever we like:
|
|
|
|
```sh
|
|
um cat filelist.um --base ../ > finished.md
|
|
```
|
|
|
|
Or from a real project to give you an idea:
|
|
|
|
```sh
|
|
um cat $SOT/1*um --strip-file-links --base $JRN >> $SOT/concat/100.foreword.md
|
|
```
|
|
|
|
And there you have the virtue of the Unix philosophy.
|