Kifu

approvedunreviewed

by Lux Betancourt

Draw print-style Go (baduk, weiqi) diagrams in your notes: positions, numbered figures, interactive problems, and SGF files.

↓ 29 downloadsUpdated 2d agoGPL-3.0

Kifu

Go boards for Obsidian notes that look like the diagrams in a book: black ink, thin lines, numbered stones, nothing shiny. A board can be

  • a position — the whole board or just a corner, with triangles, squares and letters on it;
  • a numbered figure — a sequence shown as stones 1, 2, 3…;
  • a problem — you click a move and the lines you saved answer it, until the line ends correct or incorrect.

Boards are stored as SGF, either right in the note or in an .sgf file the note points at.

A numbered figure, a problem and a diagram with marks

(The pictures on this page were made with the plugin's test page, which imitates Obsidian's default theme.)

Install

In Obsidian open Settings → Community plugins → Browse, search for Kifu, install it and switch it on.

To install it by hand instead, download main.js, manifest.json and styles.css from the latest release, put them in a folder <your vault>/.obsidian/plugins/kifu/, and switch Kifu on under Settings → Community plugins (press the refresh button next to Installed plugins if it is not listed).

Needs Obsidian 1.13 or later. It works on desktop, tablet and phone. It reads nothing outside your vault, and goes online only for games and problems from OGS or goproblems.com (see below): when you import one, or when a note that embeds a link to one is shown. It then asks that site for that one game or problem, and nothing else is ever sent anywhere. The only time it looks through the vault's files is when you open the SGF file picker, which lists the .sgf files to choose from; a board reads only the file it points at.

To see everything at once, copy the two files in demo/ into your vault and open Kifu demo.

A first board

Run the command Kifu: Insert board (or right-click in the editor: Kifu → Insert 19 × 19 board, 13 × 13 or 9 × 9), or type the block yourself:

```kifu
```

That is an empty 19 × 19 board.

  • Hover over it: a lock appears beside its upper right corner. (On a touch screen it is always there, faintly.)
  • Locked (the normal state), clicking plays stones — captures, ko and all — but nothing you do is saved. Once you have played, ‹ › buttons appear under the lock: ‹ takes back your last move, › plays it again. They only step through what you played, never into the moves saved in the block, so they do not give a problem's answer away. The ↺ button, or Esc, puts everything back.
  • Click the lock to edit. Tools appear under the board, and what you do now is written into the block as SGF: once you pause for three seconds, when you lock the board again, or when the board leaves the screen (the note is closed, you scroll away). Waiting keeps quick input from being lost, because writing makes Obsidian draw the block afresh. Click the lock again when you are done.

What a locked board shows:

  • a problem shows its starting position;
  • any other board shows the last position of its main line — so a sequence you drew with the play tool looks the way you left it;
  • a numbers: line turns that into a numbered figure, and a move: line picks another position (move: 0 is the start). The ⚑ tool writes that line for you.

Editing

While a board is unlocked the whole board is shown, with the part that will be hidden afterwards faded, and the moves carry their numbers (unless a numbers: line or the settings say otherwise).

A board being edited, with the move tree beside it

ToolWhat it does
① PlayPlays moves, alternating colours, and builds the tree: go back and play something else to add a variation. Click the tool again to change who plays next from here.
● ○ StonesPuts a stone down without it being a move (for setting up a position). Click a stone of the same colour to take it off.
△ □ ○ ✕Marks. They belong to the move you are on, so every move can carry its own.
ALabels. The little box holds the next label: type a, A, 1, 51… and each click counts on from there.
EraserRemoves a mark or label; a second click removes the stone under it.
CropDrag to choose the part of the board the locked board shows. Double-click (or double-tap) to go back to the automatic crop.
⚑ StartMakes the position on the board the one the locked board opens at, by writing a move: line. Click it again on that position to take the choice back. Only positions on the main line can be chosen: step there with ‹ › first. A problem chosen this way is solved from that position.
#Makes the locked board a numbered figure of the main line, or takes the numbers off again. Whatever a numbers: line said before is put back, so a range like 51-100, or an off, is not lost by trying the switch.
↶Undo (Ctrl/Cmd+Z while the board has the keyboard; Ctrl/Cmd+Shift+Z or Ctrl+Y redoes).
‹ ›Previous / next move.
✓ ✗Mark the line through this move as correct or incorrect.
↑Move this variation up, towards being the main line.
🗑Delete this move and everything after it.

Two things worth knowing:

  • If you have been playing on a locked board and then unlock it, the line that is on the board is kept and becomes part of the tree. (Undo takes it out again.)
  • Locking puts the board back to the position the note shows.

Comments are written in the move tree panel (see below).

Problems

A problem is a starting position plus the lines you saved from it.

  1. Unlock an empty board and set the position up with the stone tools (stones of both colours: that is what tells the plugin it is a problem).
  2. Switch to Play and play the answer; the plugin alternates colours. Go back (‹) and play other tries to add them as variations.
  3. Mark lines with ✓ and ✗ if you like, add comments in the panel, lock.

On the locked board the solver's move is looked up in the tree. If it is there, a saved reply is played after a short pause (where you saved several, one of them at random, so the problem does not always answer the same way; the settings can make it always the first); if it is not, the move is simply incorrect. When a line ends a large ✓ or ✗ appears over the board for a moment, and the verdict and the line's comment stay underneath.

Stuck? The eye button under the lock shows the solution: the board starts over and plays out the first saved line judged correct, with the moves numbered, and the move tree panel shows all the saved lines. Step back through it with ‹ ›, or start over with ↺. (No ✓ appears for an answer that was shown rather than found.)

A wrong answer: the saved reply, the verdict and the comment

How a line is judged:

  • A ✓ or ✗ on a move covers everything after it; the mark nearest the end of the line wins.
  • A line with no mark at all is incorrect if some other line is marked ✓. If nothing anywhere is marked ✓, the main line (the first one you entered) is the answer and every other line is incorrect.
  • "Main line" is about the solver's moves only: where the other side has several saved replies, every one of them keeps you on it.

So the quickest way to write a problem is: enter the answer first, then the failures, and mark nothing.

SGF files from elsewhere work too. TE and BM are read as ✓ and ✗ (that is also what the plugin writes). So is a comment on a move that opens with "Correct", "Right", "Solved", "Success", "Wrong", "Incorrect" or "Fail…" as a word of its own, or that begins or ends with RIGHT in capitals, the way files from goproblems.com do.

A board is treated as a problem when its position was set up with stones of both colours and there are moves saved from it. Marks alone do not make one: a reviewed game has its good and bad moves marked too, and stays a game record. Say problem: yes or problem: no to decide yourself, or use the switch: the ? button under the lock while a board is unlocked, or Kifu → Problem board in the editor's right-click menu (for the board you right-clicked, or the block the cursor is in). The switch writes a problem: line only when the board would not decide the same by itself. A board that is not a problem still follows saved moves and answers them, but it does not judge by itself: only a line you marked ✓ or ✗ shows its verdict, once it has been played out to the end. With problem: no, nothing is judged at all.

To keep a problem from looking familiar, press the shuffle button under the lock while the board is unlocked (or write randomize: on). Each time the note is opened, the locked board is then shown turned or mirrored (any of the eight ways for a square board; a board that is not square is only mirrored), and half the time with the colours swapped, so a "Black to play" problem becomes "White to play". Captions and comments follow: "Black" and "White" in them change places. Only the picture changes: what you play is looked up as recorded, and unlocking shows the board the way it is written.

Figures and numbers

Press # while editing — or add numbers: on — and the locked board shows the main line played out with the stones numbered.

```kifu
numbers: on
(;GM[1]SZ[19];B[pd];W[nc];B[qf];W[pb];B[qc];W[kc])
```

For a stretch of a longer game, give the moves:

LineShows
numbers: onevery move, numbered from 1
numbers: 51-100the position after move 100, with moves 51–100 numbered 51–100
numbers: 51-100 from 1the same stones numbered 1–50
numbers: on from 7every move, the first one called 7
numbers: offno numbers on this board, not even while playing
move: 50just the position after move 50, no numbers

A figure is drawn the way kifu are printed: a stone captured during the figure stays where it was, and a move played on an occupied point is listed underneath ("⑦ at ③"). As soon as you step through the moves or play on, you see the real position.

To number stones that are not moves — or to start anywhere you like — use the label tool and type the first number into its box.

Showing part of the board

By default a board is cropped to its stones (every variation counts), with two lines of margin, and snaps to the edge when the edge is close. Lines that run off a cut side are left open, as in print; real edges get the heavy border. Star points are drawn for any size.

Use the crop tool, or a view: line:

LineShows
view: fullthe whole board
view: autocropped to the stones
view: top-righta corner (also top-left, bottom-left, bottom-right; tr, ne … work too)
view: topa side (also bottom, left, right)
view: centerthe middle
view: top-right 9x7a corner 9 columns wide and 7 rows tall
view: top 6the top six rows
view: K10-T19exactly this rectangle

Board size comes from the SGF (SZ[13], SZ[19:13]). For a new board use size: 13 or size: 13x9, or change the default in the settings.

Pointing at SGF files

```kifu
sgf: [[Cho Chikun - Elementary.sgf]]
game: 12
```
  • sgf: takes a wikilink, a file name, or a path from the vault root or the note's folder. The command Kifu: Insert board from an SGF file, or Kifu → Display SGF file… in the editor's right-click menu, lets you pick one. Only .sgf files are ever used: a note that happens to have the same name is left alone.
  • game: picks a game from a file that holds several (problem collections usually do). The first is 1.
  • Every other option works as usual, so several boards can show different moves or corners of one file.
  • Unlocking and editing such a board changes the file; the note keeps pointing at it. Other boards on the same file follow. If the file changes on disk (sync, another program) open boards reload.
  • A board that could not show its file (not found, no such game in it) looks again when a file appears, moves or changes.
  • A file that is not UTF-8, or whose game is cut short (no closing parenthesis), is shown but cannot be edited from here.
  • A game record shows its last position; add move: or numbers: to show another.

Obsidian only lists .sgf files in its file explorer when Settings → Files and links → Detect all file extensions is on. If a board cannot find a file that is there, turn that setting on.

Boards from links

Embed a link to a game, review or puzzle on OGS, or a problem on goproblems.com, the way you would embed an image:

![](https://online-go.com/game/12345)
![](https://online-go.com/puzzle/2)
![](https://www.goproblems.com/problems/5)

Instead of an image you get a board, fetched from the site each time the note is shown, in the reading view and in Live Preview (put the cursor on the link to edit it). A puzzle or problem can be solved on it and a game stepped through, like any other board, but nothing is ever written: to change one, import it instead (see below) and edit the copy in your note. A link that is shown again within a few minutes is not fetched again. A link to anything else stays an ordinary embed.

Games and puzzles from OGS

Run Kifu: Import a game or puzzle from online-go.com (or right-click in the editor: Kifu → Import from online-go.com…) and paste a link to a game, a review (demo board) or a puzzle on OGS. A bare number works too: choose what it is under Kind.

  • A game or review arrives as a game record, with its variations and comments; above it, a link back to it with the players, their ranks and the result. Chat that OGS could not write into its files ("someone: [object Object]") is left out.
  • A puzzle arrives as a problem: its stones, the player to move, every line from the puzzle, and a ✓ or ✗ on the lines OGS calls correct or wrong, with their comments.

Only public games, reviews and puzzles can be imported (OGS needs no account for them).

Problems from goproblems.com

Run Kifu: Import a problem from goproblems.com (or right-click in the editor: Kifu → Import from goproblems.com…) and give the problem's number, or paste a link to it. The problem arrives where the cursor is, variations and comments included, as a board of its own with a link back to its page above it:

[goproblems.com #5](https://www.goproblems.com/problems/5) · Tesuji, 14 kyu, by adum

```kifu
problem: yes
(;AW[pe][pf]…
```

goproblems.com marks a correct line by ending its comment with RIGHT. On import that becomes a ✓ on the move (and the word leaves the comment), so the problem judges itself the way it does on the site.

Public problems need nothing more. To import problems that are not public, create an API token in your goproblems.com account and choose it under Settings → Kifu → Importing from goproblems.com. It is kept in Obsidian's secret storage, not in the plugin's settings file, and is sent only to goproblems.com, only with an import.

Network use: the plugin goes online only to fetch a game or problem from OGS or goproblems.com: when you import one, or when a note embedding a link to one is shown. Each time it asks one site (online-go.com or goproblems.com) for that one game or problem, sending its number (and, for goproblems.com, your token if you chose one), and nothing else.

The move tree panel

The panel lives in the right sidebar (command Kifu: Show move tree; on a computer it also comes forward when you unlock a board). It follows the board you last used and shows

  • the tree, moves running downwards and variations branching to the right — click any move to go there; dashed stones are ones you played on a locked board and are not saved;
  • ✓ / ✗ on lines that carry a verdict, and a dot on moves that have a comment;
  • the comment of the current move — a text box while the board is unlocked.

While a problem is locked the panel keeps the answers to itself: it shows only the moves that are on the board. The eye button shows all the saved lines; starting over hides them again. (There is a setting for this. Stepping forward with → walks along a saved line, so that shows the answer too.)

Keys

With the keyboard on a board (click it) or on the panel:

Key
← →previous / next move
↑ ↓previous / next variation
Home Endfirst position / end of the line
Esclocked: back to the resting position · unlocked: lock · while dragging a crop: never mind
Ctrl/Cmd+Zunlocked: undo · locked: take back your last step
Ctrl/Cmd+Shift+Z, Ctrl+Yunlocked: redo · locked: make that step again

In the panel's comment box every key belongs to the text you are typing, undo included.

While a board has the keyboard, what you type, paste or undo stays with the board and never reaches the note's text. Obsidian's own hotkeys keep working, though — including ones that act on the editor.

All options

Option lines come first in the block, one per line, before any SGF.

OptionValues
sgflink or pathtake the game from a file
gamenumberwhich game, when the file (or the block) holds several
size19, 13x9size of a board with no SGF yet
viewsee abovepart of the board to show
movenumber, lastposition to show
numberssee abovenumbered figure
scale80%, 1.5size of this board
coordson / offcoordinates beside the board's edges (the real ones, where the part shown has any)
captiontexttext under the board at rest
commentson / offshow move comments under the board
problemyes / nojudge moves or not
stylepaper / themewhite paper, or the theme's colours
alignleft / center / right
randomizeon / offshow the board turned, mirrored and maybe recoloured, a new way each time the note opens

Settings

Settings → Kifu holds the defaults: scale, colours, crop, coordinates, alignment, new board size, whether played moves are numbered, whether comments show under the board, how replies are chosen (a random saved answer, or always the first) and how long they wait, whether the panel hides a problem's answers, and whether unlocking brings the panel forward. Every one of them is only a default: an option line in a block wins.

Changing the look

The drawing is plain SVG coloured by CSS variables, so a snippet can restyle it:

.kifu {
	--kifu-font: "Gill Sans", sans-serif; /* numbers and letters */
	--kifu-ink: #111;                      /* lines, outlines, labels */
	--kifu-paper: #fdfcf8;                 /* background */
	--kifu-black: #111;                    /* black stones */
	--kifu-white: #fff;                    /* white stones */
}

Speed

  • The whole plugin is one small file (about 75 kB) with no libraries in it.
  • Starting Obsidian only registers things: nothing is read, parsed or drawn, and no settings file is opened, until a board is actually on screen. In the test page, loading the file takes about 3 ms and starting the plugin well under 1 ms.
  • A board is around fifteen SVG nodes however many stones it has (all stones of one colour share a path), plus one per label. Drawing one takes a millisecond or so; a full board with 180 numbered stones, about 5 ms.

Good to know

  • Saving changes only the lines between a block's two fences, and of those only the option lines and the one game the board shows: a second game or a remark you put in the block stays as it is. While the note is open in an editor the change goes through the editor, so the note's own undo takes it back.
  • Which block is whose. A board knows its block by its place among the note's boards (the third of five, say) and by the block's exact text. As the note changes, that place is carried along one change at a time, the way an editor carries a cursor: text typed elsewhere, or boards added, removed and moved around, do not send an edit to the wrong lines. In an editor the board simply moves with its block. An edit is written only where place and exact text both still fit. If the block itself was changed or removed while an edit was waiting for it (by hand, by a sync, by the same board open in another pane), or the note was rearranged so thoroughly in one go that it is no longer certain which block is meant, nothing is written and a notice says so. An edit that was refused is gone, it does not wait for a later chance: the board goes back to what it was drawn from.
  • Boards that read exactly the same (a row of empty boards, say) are kept apart by their places. The one thing a note's text cannot show is which is which once identical blocks have traded places; an edit then goes to the block under the board that shows it. A board that was left unlocked or played out and then scrolled out of sight comes back the way you left it, with one exception: a board that has twins comes back locked if the note was edited by hand meanwhile, rather than risk unlocking the wrong one.
  • Where Obsidian does not tell a board which lines it stands on (an embedded note is the likely case), the board is found by its text, provided no other board of the note reads the same. Two that do cannot be told apart there: the edit is refused with a notice, and giving one of them a caption: line settles it. In the editor and the reading view this does not arise.
  • If the note or an SGF file cannot be written (a sync program has it locked, a drive has stopped answering), the edit stays on the board and saving is tried again by itself; a notice says so once. Edits still waiting when you switch the plugin off or quit Obsidian are written first.
  • A block whose SGF is not closed properly (a missing )) is shown but cannot be edited, so that nothing after it gets swallowed. A game: line asking for a game the block does not have shows a message instead of a board.
  • Changing only an option (the crop, the figure switch) leaves the SGF text exactly as you wrote it. Changing the game writes that game out afresh: properties the plugin does not understand are kept, line breaks are its own.
  • An SGF file that is not UTF-8 is displayed using its CA charset.
  • Links inside code blocks are not tracked by Obsidian, so renaming an .sgf file does not update sgf: lines. Moving it to another folder is fine as long as the name stays unique.
  • Passes in an SGF file are stepped over correctly, but there is no pass button yet.
  • A board in a Canvas text card can be viewed and played, not edited.

Status

Version 1.0.0, the first public release. Besides being used in Obsidian on the desktop, it is checked automatically: the built plugin runs in a browser against a stand-in for the Obsidian API, with Live Preview played by a real CodeMirror editor, and on touch screens in Safari's engine (WebKit) as well as Chromium. Wherever Obsidian's behaviour is not documented, the checks are run with the stand-in behaving both ways. The part that writes into notes got the most attention: it was reviewed independently three times, and is exercised by a randomized test on top of the fixed scenarios.

Found a bug, or a board that looks wrong? Please open an issue, with the block's text if you can.

License

GNU General Public License v3.0.

Building it yourself

npm install
npm run build          # type-check and bundle main.js
npm test               # unit tests: SGF, rules, sessions, finding blocks in Markdown
npm run test:browser   # the built plugin in Chromium (once: npx playwright install chromium)
npm run test:monkey    # randomized runs of the same, a few minutes
npm run lint           # Obsidian's own lint rules
  • tests/*.test.ts — unit tests. markdown.test.ts checks the code that finds and rewrites a block against a CommonMark parser on thousands of generated notes.
  • tests/harness/obsidian-mock.js — the stand-in for the Obsidian API; cm-lp.ts — Live Preview on a real CodeMirror 6.
  • tests/harness/touch.mjs — taps on tablet and phone profiles, in WebKit (once: npx playwright install webkit) and Chromium.
  • tests/harness/interact.mjs, editor.mjs, identity.mjs — the scenarios (the last one is all about boards that read the same, notes rearranged under a board, panes that are behind, and writes that fail); ONLY='pattern' in the environment runs the scenarios whose names match. perf.mjs — timings; shots.mjs, docs.mjs — pictures.
  • tests/harness/monkey.mjs — a "monkey" that places and removes stones, marks and labels, types comments, locks, scrolls and (in its rougher profiles) edits the note by hand at the same time: types into blocks, deletes, copies and moves them, undoes, changes the file from outside. It does so in ten arrangements of panes and five kinds of note, most of them full of identical boards. After every step it checks that nothing is in a block other than the one whose board was used, and at the end that nothing outside the blocks changed, that every board shows what its block says, that no edit vanished without a notice, that none vanished at all from a block nobody had touched by hand, and that nothing typed by hand was taken back. node tests/harness/monkey.mjs live,both hands 1-10 runs chosen arrangements, profiles and seeds.

Releasing

  1. Put the new version (x.y.z) in manifest.json and package.json, and add it to versions.json with the lowest Obsidian version it needs.
  2. Commit, tag the commit with exactly the version, and push only the tag: git tag -a 1.0.1 -m "Kifu 1.0.1" && git push origin 1.0.1.
  3. The release workflow (.github/workflows/release.yml) checks that the tag matches the manifest, builds, runs the unit tests and publishes the release with main.js, manifest.json and styles.css.
  4. Once the release is there, push the branch: git push.

The order matters. Obsidian installs a plugin by reading manifest.json on the default branch and downloading the release with that version; if the branch names a version whose release does not exist yet, installing fails.

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.