shiki (私記)
← Full changelog

shiki v0.9.3

Released 2026-08-26.

Share: X Bluesky Reddit Hacker News WhatsApp Telegram Email
shiki v0.9.3, running in a terminal

Added

  • Git onboarding: creating a plain notebook in the TUI (when git.remote_template isn't configured) now asks once — "Git remote (URL or local path, empty = skip)" — so backup/sync gets set up on day one instead of never. shiki notebook create <name> --remote <url> does the same from the CLI, credentials redacted in all output.
  • shiki doctor git health checks: warns when user.name/ user.email aren't set anywhere (every sync would fail at commit time) — but only once notebooks exist; flags notebooks left with an unresolved merge, notebook directories without a git repo, and configured remote URLs in a form that can't be one (paste accidents like git clone https://…); and registers the new git_dash/diff bindings in the keybinding-collision scan.
  • Pending-changes marker on note titles: a note with uncommitted changes now shows a ● after its title in NOTES, colored by change type (same scheme as before — new/modified/deleted). The glyph survives the selected row's highlight repaint, so the dirty cue no longer vanishes exactly where you're looking; falls back to a plain bullet when Nerd Font icons are off.
  • Git dashboard (G in NOTEBOOKS): every notebook's sync state in one read-only modal, phrased in plain language instead of git jargon — "all synced", "2 uncommitted · 3 on remote — s saves, p pulls", "merge in progress — resolve conflicts first" — with each notebook's latest commits listed underneath and j/k navigation between notebooks. Configurable via [keybindings.notebooks] git_dash (default "G").
  • Pending-changes diff (d in PREVIEW): pressing d on a note with uncommitted edits now shows them in a "Working changes" popup — working tree vs last commit, same +/- pane as the history modal's diff view (without r revert, since there's nothing to revert to yet). On a clean note d opens the version history instead, so the key always answers "what changed here". Configurable via [keybindings.preview] diff (default "d"). Encrypted notebooks are refused (both sides would be ciphertext).
  • shiki diff [note] and shiki log [note]: the same git visibility on the command line. diff without a note prints every pending change in the notebook with per-file +/− stats; log lists recent commits, or every commit that touched one specific note.
  • Rename updates every inbound [[wikilink]]: renaming a note (the r prompt in NOTES) no longer silently breaks the links other notes hold to it. When the note has backlinks, a confirm asks first — y renames *and* rewrites every matching link across every notebook ([[old title]], [[old-slug]], preserving |display aliases and #heading/^block suffixes, skipping fenced code), n renames without touching anything, Esc cancels. Rewrites go through raw-text surgery per file (frontmatter skipped, never synthesized into plain notes) and respect encrypted notebooks via their cached passphrase; locked ones are skipped rather than half-touched.
  • aliases: frontmatter support — an optional list of alternate names that resolve exactly like the title does (same case-insensitive + slug fallback) in wikilink resolution, backlinks, and the graph, so a renamed note's old name keeps working even where a rewrite missed (an edit in flight on another machine). The key is the same one Obsidian writes, so imported vaults carry theirs over untouched; empty is omitted from serialization entirely, so existing notes round-trip byte-stable.
  • [[note#heading]] / [[note^block]] compatibility — Obsidian-style sub-addresses now parse as links to note everywhere (resolution, backlinks, graph edges, outgoing-links panel); the suffix is stripped before matching instead of being treated as part of the target text.
  • ![[embed]] images render in PREVIEW — an Obsidian-style embed line draws as terminal art (chafa) just like !alt does, resolving by bare file name anywhere in the notebook's immediate subfolders (the usual attachments/ layout of imported vaults) after the ordinary relative- path chain misses. Alias/sub-address parts don't change which file is meant.
  • Image paste (Ctrl+V) saves attachments: with an image (screenshot) on the clipboard while editing a note, it's saved as a PNG under the current notebook's [general] attachments_dir (default attachments/, resolved against the notebook root so the inserted !pasted-… link renders from any folder depth) and the markdown link lands at the cursor as one undo step. Text pastes are unaffected; [editor] paste_images (on) gates it, and it only engages for real note edits — not config.toml, snippet bodies, the scratchpad, or conflict files. Both toggles live in Settings (GENERAL/EDITOR).
  • shiki import obsidian <vault> — adopts an existing Obsidian vault as a notebook: in-place by default (registered under [notebooks.<name>] path, files stay put, --git-init runs git init when the vault isn't already a repo) or copied into the data dir with --copy. --tags merges inline #hashtags into each note's frontmatter tags through a raw-YAML edit that preserves foreign frontmatter verbatim (Obsidian doesn't write shiki's notebook:/ date: keys, and parsing through the strict struct would have synthesized replacements); plain notes without frontmatter are left untouched.
  • shiki import notion <export.zip | folder> — converts a Notion markdown export into a fresh notebook: the <Page> <32-hex-uuid> name suffixes are stripped from pages and folders, internal page links (URL-percent-encoded, relative or nested) become [[wikilinks]] with display aliases preserved when they differ from the target's name, and CSV databases plus non-markdown assets are counted and reported as skipped. Accepts the raw .zip or an already-extracted directory.
  • OS Capture supreme — shiki capture as system daemon: general.enable_capture_daemon is now on by default (was off), so a running TUI is live by construction. The capture wire protocol gains template=, url=, title=, source= headers; the daemon (shiki-tui/src/capture.rs) centralizes Source: title appending so every client (CLI, shiki-native-host, rofi/waybar pipes) gets the same provenance footer. CLI gains shiki capture --clip (OS clipboard via arboard, Wayland/X11/macOS), --url/--title (appended once as Source:), --template <name> (render through any {{title}}/{{date}}/{{body}} template), and --source (origin marker for logs). The browser extension and shiki-native-host now send those headers instead of pre-appending. Plus scripts/rofi-capture.sh (rofi/wofi/dmenu), scripts/waybar-shiki.sh (Waybar/Polybar overdue + daemon dot, shiki tasks --overdue --count + shiki capture --check), scripts/clip-capture.sh, and contrib/ examples (Hyprland, Waybar, Raycast). One command powers every hotkey: shiki capture.
  • Headless capture daemon (shiki daemon) — the same loopback-port capture daemon the TUI runs, standalone with no UI: shiki capture/--undo/--check work identically (same capture.port + wire protocol), every request is appended to the shared shiki.log, and encrypted-locked notebooks reply locked: instead of prompting a background process. contrib/shiki-daemon.service wires it to systemctl --user, so capture stays live from boot with no TUI ever opened. shiki doctor now reports daemon reachability.
  • Voice capture (shiki capture --voice) — records the microphone (arecord → ffmpeg → sox, watchdog-timed so a missing audio device fails fast instead of hanging) and transcribes locally via whisper.cpp: whisper-cli is auto-fetched from ggml-org/whisper.cpp's release on first use (same pattern as pretty-pdf), and the model (ggml-base.en.bin by default, --model to change) is downloaded once from Hugging Face into {data_dir}/bin/models/. Nothing leaves the machine. --seconds N sets the recording length; works with every other capture flag (--daily, --tags, --folder, -n). shiki doctor reports recorder + whisper availability.
  • shiki Desktop — a native GUI app (shiki-desktop, new workspace member): the same notebooks, config and themes as the TUI, in a Tauri 2 + Svelte 5 window (Windows/MSI + NSIS installers, Linux/macOS builds follow the same pipeline). Reads the real config.toml and data dir, lists notebooks with their encryption state, and paints the active theme via the same hex palettes the TUI uses. Ships a signed installer in every release (.msi + -setup.exe + minisign .sig), plus an in-app auto-update: the UI checks GitHub Releases on startup and offers a one-click "Update" button that downloads, verifies and installs the new version (passive mode on Windows, app relaunches itself).

Fixed

  • CLI no longer panics when piped into head/less: closing stdout mid-output (shiki diff | head) used to print a raw failed printing to stdout: Broken pipe panic. The process now exits silently on SIGPIPE like every other Unix tool (no effect on Windows).
  • A notebook untracked via delete's "keep files, just untrack" answer came back on every relaunch: startup (App::new) listed notebooks straight from disk without applying the hidden filter only reload_notebooks had. One shared filter (visible_notebooks) decides for both now. shiki notebook list hides them too (with a new --all flag showing them marked (hidden)), and the browser extension's notebook picker applies the same rule so untracked notebooks stop being capturable.
  • Every CLI subcommand launched the TUI instead of running: the pre-config extension check called cli.command.take() unconditionally, consuming whatever command was parsed and leaving None behind — which the dispatch reads as "no args, launch TUI". The take is now guarded so only a genuine Extension command is consumed.
  • The merge-conflict resolver modal (auto-opened when a pull comes back with conflicts, reopenable any time with p while mid-merge) gained i — edit inline: loads the conflicted file's raw content into the normal inline editor, and saving writes it back and stages it as that file's resolution, the same bookkeeping o/t/e trigger. Removing conflict markers no longer requires an external editor.
← v0.9.2 All releases v0.9.4 →