Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dda0541
docs: add mdBook setup and introduction
lusingander Oct 4, 2026
378d620
docs: add getting started overview
lusingander Oct 4, 2026
77e63fd
docs: add requirements page
lusingander Oct 4, 2026
a43a820
docs: add installation page
lusingander Oct 4, 2026
d55199e
docs: add basic usage page
lusingander Oct 4, 2026
6eb473a
docs: add command line reference
lusingander Oct 4, 2026
8440805
docs: add compatibility page
lusingander Oct 4, 2026
557ac74
docs: add configuration overview
lusingander Oct 4, 2026
67e3654
docs: add configuration reference
lusingander Oct 4, 2026
abb3ed7
docs: add glyph preview configuration guide
lusingander Oct 4, 2026
4d78079
docs: add default keybindings reference
lusingander Oct 4, 2026
177ea92
docs: add custom keybindings guide
lusingander Oct 4, 2026
5c51e2c
docs: add features overview
lusingander Oct 4, 2026
a084d00
docs: add inspector guide
lusingander Oct 4, 2026
5c3d211
docs: add search guide
lusingander Oct 4, 2026
ac05c41
docs: add browse guide
lusingander Oct 4, 2026
5aaf76d
docs: add sequence guide
lusingander Oct 4, 2026
737159a
docs: add normalization guide
lusingander Oct 4, 2026
d80412a
docs: add frequently asked questions
lusingander Oct 4, 2026
ba3587c
ci: deploy documentation to GitHub Pages
lusingander Oct 4, 2026
2cb10d1
docs: link README to documentation and showcase Sequence
lusingander Oct 4, 2026
afe7886
docs: use kbd tags for keyboard controls
lusingander Oct 5, 2026
5daf9e9
docs: simplify introduction and group demos
lusingander Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Deploy GitHub Pages

on:
push:
branches:
- master
paths:
- docs/**
- .github/workflows/gh-pages.yml
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
- name: Install mdbook
run: |
curl --fail --location --silent --show-error \
https://github.com/rust-lang/mdBook/releases/download/v0.4.52/mdbook-v0.4.52-x86_64-unknown-linux-gnu.tar.gz \
--output "${RUNNER_TEMP}/mdbook.tar.gz"
tar -xzf "${RUNNER_TEMP}/mdbook.tar.gz" -C "${RUNNER_TEMP}"
echo "${RUNNER_TEMP}" >> "${GITHUB_PATH}"
- name: Build with mdbook
run: mdbook build docs
- name: Upload artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: docs/book

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "sauva"
version = "0.3.0"
description = "Terminal Unicode Explorer"
authors = ["Kyosuke Fujimoto <kyoro.f@gmail.com>"]
homepage = "https://github.com/lusingander/sauva"
homepage = "https://lusingander.github.io/sauva/"
repository = "https://github.com/lusingander/sauva"
readme = "README.md"
license = "MIT"
Expand All @@ -12,7 +12,7 @@ categories = ["command-line-utilities"]
edition = "2024"
rust-version = "1.88.0"

exclude = ["/.github", "/img", "/data", "/tools"]
exclude = ["/.github", "/docs", "/img", "/data", "/tools"]

[dependencies]
arboard = { version = "3.6.1", default-features = false, features = ["wayland-data-control"] }
Expand Down
259 changes: 59 additions & 200 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,71 @@

Terminal Unicode Explorer 🪄

<img src="./img/demo.gif">
<img src="./img/demo.gif" alt="Sauva browsing and search demo">

## About

Sauva is a terminal application for searching and browsing Unicode code points. It shows Unicode properties and a glyph preview in a responsive TUI.

The bundled data is based on Unicode 17.0.0.
- Inspect Unicode properties, encodings, and decomposition mappings.
- Search by character, code point, Unicode name, or formal name alias.
- Browse planes, ranges, blocks, and code points.
- Analyze strings by code point and grapheme cluster in Sequence mode.
- Compare NFC, NFD, NFKC, and NFKD normalization forms.
- Customize colors, glyph preview fonts, and keybindings.

The bundled data is based on Unicode 17.0.0. See [Features](https://lusingander.github.io/sauva/features/) for details and screenshots.

## Documentation

For detailed usage, configuration, and terminal support, see the [full documentation](https://lusingander.github.io/sauva/).

## Installation

### [Cargo](https://crates.io/crates/sauva)
If you're using Cargo:

```
```sh
cargo install --locked sauva
```

### [Homebrew (macOS)](https://github.com/lusingander/homebrew-tap/blob/master/sauva.rb)
Homebrew and pre-built binaries are also available. See [Installation](https://lusingander.github.io/sauva/getting-started/installation.html) for all options.

```
brew install lusingander/tap/sauva
```
## Usage

### Downloading a binary
### Inspect a code point

Pre-built binaries are available from the [releases page](https://github.com/lusingander/sauva/releases).
Run `sauva` without arguments, or specify a character or code point:

## Usage
```sh
sauva
sauva あ
sauva U+2192
```

### Basic
### Analyze text in Sequence mode

Run sauva without arguments to open the default code point:
Pass a string to inspect its code points and grapheme clusters:

```sh
sauva 'Á👩‍💻'
```
sauva

Press <kbd>Enter</kbd> to inspect a selected code point, or <kbd>n</kbd> to compare normalization forms.

<img src="./img/demo-sequence.gif" alt="Sauva Sequence and normalization demo">

Use `--text` when the input would otherwise be interpreted as code point notation, or `--text -` to read literal UTF-8 text from stdin:

```sh
sauva --text 41
printf 'Á👩‍💻' | sauva --text -
```

See [Sequence](https://lusingander.github.io/sauva/features/sequence.html), [Normalization](https://lusingander.github.io/sauva/features/normalization.html), and [Basic Usage](https://lusingander.github.io/sauva/getting-started/basic-usage.html) for more examples.

### Options

```
```text
Sauva - Terminal Unicode Explorer 🪄

Usage: sauva [OPTIONS] [INPUT]
Expand All @@ -59,208 +85,41 @@ Options:
-V, --version Print version
```

#### Specifying `INPUT`

A single character can be inspected directly. Text sequences can be analyzed by code point and grapheme cluster, and compared with their NFC, NFD, NFKC, and NFKD normalized forms.

```
sauva あ
sauva 'Á👩‍💻'
```

Code points can also be given in `U+` notation, `0x` notation, or as two to six hexadecimal digits.

```
sauva U+2192
sauva 0x1F600
sauva 1F600
```

A one-character argument is treated as the character itself. Use a prefix for a one-digit hexadecimal value, such as `U+A` or `0xA`.

An input made entirely of hexadecimal digits keeps the code point interpretation. Use `--text` to force literal text when the input would otherwise be interpreted as notation:

```
sauva -t 41
sauva --text U+2192
```

Empty text is rejected.

#### Reading from stdin

Pass `-` to read the input from stdin. It accepts the same text and code point formats as `INPUT`. Use `--text -` (or `-t -`) to treat stdin as literal text without code point notation parsing.

```
printf 'U+2192' | sauva -
printf 'Á👩‍💻' | sauva -
printf '41' | sauva --text -
printf 'A\nB\n' | sauva -
sauva --text - < input.txt
```

Sauva reads UTF-8 input until EOF before opening the TUI. All whitespace, including trailing LF and CRLF, is preserved. For code point notation, use `printf` without a trailing newline: `echo U+2192 | sauva -` is rejected because the newline is part of the notation. Use `--text -` to inspect that input as literal text, including its newline. Empty input and invalid UTF-8 are rejected.

Stdin is read only when `-` is explicitly specified. Keyboard input continues to come from the terminal, so an interactive terminal is still required. Since `-` selects stdin, use `sauva U+002D` to inspect the hyphen character itself.

#### `-g, --graphics <MODE>`

- `auto` enables the Kitty graphics protocol for detected kitty and Ghostty terminals.
- `force` uses the Kitty graphics protocol without terminal detection.
- `iterm2` uses the iTerm2 inline image protocol.
- `off` disables glyph images.

See [Compatibility](#compatibility) for terminal support.

### Config

sauva loads the first applicable configuration path in this order:

1. The path specified by `SAUVA_CONFIG_FILE`
2. `$XDG_CONFIG_HOME/sauva/config.toml`
3. `$HOME/.config/sauva/config.toml`

If the default file does not exist, built-in settings are used. A missing file specified by `SAUVA_CONFIG_FILE`, or an invalid configuration file, causes startup to fail.

The complete built-in configuration can be printed without starting the TUI or loading a local configuration file:

```
sauva --print-default-config
sauva --print-default-config > config.toml
```

All settings are optional. The following example shows the main configuration areas:

<details>
<summary>Configuration example</summary>

```toml
[color]
fg = "reset"
bg = "reset"
muted = "darkgray"
accent = "cyan"
heading = "blue"
border = "darkgray"
match = "yellow"
key = "yellow"
link = "blue"

[color.selection]
fg = "black"
bg = "cyan"

[color.difference]
fg = "black"
bg = "yellow"

[color.status]
info = "green"
warning = "yellow"

[glyph_preview]
font_families = ["Iosevka", "Noto Sans"]
emoji_font_families = ["Noto Color Emoji"]
fg = "#f5f7fa"
bg = "#00000000"

[ui]
selection_cursor = "▸"
input_cursor = { text = "|" }
```

UI colors accept ANSI color names, `#RRGGBB`, or an indexed color from `0` to `255`. Glyph image colors accept `#RRGGBB` and `#RRGGBBAA`.

</details>

The complete configuration structure and defaults are defined in [config.schema.json](config.schema.json).
For input interpretation, stdin handling, and graphics modes, see [Command Line Options](https://lusingander.github.io/sauva/getting-started/command-line-options.html).

### Keybindings

These are the main built-in controls. The available controls depend on the current view.

| Key | Description |
| --- | --- |
| <kbd>F1</kbd> | Open or close contextual help |
| <kbd>Ctrl+c</kbd> | Quit from any view |
| <kbd>q</kbd> | Quit from the Inspector, Sequence, or Browse view |
| <kbd>Esc</kbd> | Cancel or go back; quit from the Inspector or input Sequence |
| <kbd>h</kbd> <kbd>j</kbd> <kbd>k</kbd> <kbd>l</kbd> or arrow keys | Move the selection |
| <kbd>/</kbd> | Search by character, code point, Unicode name, or formal name alias |
| <kbd>p</kbd> <kbd>r</kbd> <kbd>b</kbd> <kbd>c</kbd> | Browse planes, ranges, blocks, or code points |
| <kbd>Enter</kbd> | Open the selected item; inspect a code point from Sequence or Browse |
| <kbd>Backspace</kbd> | Go back in Browse; return from Inspector to its Sequence |
| <kbd>y</kbd> | Copy the selected Inspector value |

Press <kbd>F1</kbd> to view all controls for the current screen.
| <kbd>Ctrl-C</kbd> | Quit from any view |
| <kbd>j</kbd> / <kbd>k</kbd> or arrow keys | Move the selection; use <kbd>Up</kbd> / <kbd>Down</kbd> in Search |
| <kbd>/</kbd> | Open Search from the Inspector |
| <kbd>p</kbd>, <kbd>r</kbd>, <kbd>b</kbd>, <kbd>c</kbd> | Open Browse from the Inspector |
| <kbd>Enter</kbd> | Open the selected item or inspect a code point |
| <kbd>Backspace</kbd> | Go back in Browse or return from the Inspector to its Sequence |
| <kbd>y</kbd> | Copy the selected Inspector value or normalization result |
| <kbd>n</kbd> | Compare normalization forms from Sequence |

#### Custom keybindings
See [Keybindings](https://lusingander.github.io/sauva/keybindings/) for all controls and [Custom Keybindings](https://lusingander.github.io/sauva/keybindings/custom-keybindings.html) to customize them.

Keybindings can be replaced per command and context. A command can have multiple keys, and an empty array disables it in that context.
### Config

<details>
<summary>Keybinding example and syntax</summary>
Sauva loads the configuration selected by `SAUVA_CONFIG_FILE`, or `$XDG_CONFIG_HOME/sauva/config.toml` (using `~/.config` when `XDG_CONFIG_HOME` is unset or empty).

```toml
[keybindings.global]
help = ["f2"]
Print the complete built-in configuration with:

[keybindings.inspector]
next_code_point = ["l", "right", "n"]
back = ["backspace"]
browse_planes = []
```sh
sauva --print-default-config
```

Keys may be a single character, a named key such as `enter`, `esc`, `left`, or `f1`, or a modifier followed by one character, such as `ctrl-n`, `alt-j`, or `shift-g`. Conflicting and invalid bindings are rejected at startup.

See [config.schema.json](config.schema.json) for all contexts and command names.

</details>
See [Configurations](https://lusingander.github.io/sauva/configurations/) and [Config File Format](https://lusingander.github.io/sauva/configurations/config-file-format.html) for settings and defaults.

## Compatibility

### Operating systems

macOS and Linux are supported. macOS uses Core Text for system font lookup. Linux uses Fontconfig when available and falls back to a best-effort font database lookup if Fontconfig cannot be loaded.

### Terminal emulators

Glyph previews use the [Kitty graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) and its [Unicode placeholder](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) feature. The supported terminals are:

- [kitty](https://sw.kovidgoyal.net/kitty/)
- [Ghostty](https://ghostty.org)

Use `--graphics force` to try the Kitty protocol in another compatible terminal or when automatic detection does not enable it.

The [iTerm2 inline image protocol](https://iterm2.com/documentation-images.html) is available through `--graphics iterm2`, but compatibility and future support are not guaranteed. It is never selected automatically.

### Glyph preview fonts

Sauva does not bundle fonts. Available glyphs and the selected font therefore depend on the fonts installed on the system.

For each code point, sauva tries configured normal fonts, the system default text font, configured emoji fonts, and then system fallback fonts. Preferred fonts can be set with `glyph_preview.font_families` and `glyph_preview.emoji_font_families`.

## Screenshots

### Inspector

<img src="./img/inspector.png" width="400" alt="Inspector view">

### Browse Plane / Range / Code point

<img src="./img/plane.png" width="400" alt="Plane browser"> <img src="./img/range.png" width="400" alt="Range browser"> <img src="./img/code-points.png" width="400" alt="Code point browser">

### Browse Block

<img src="./img/block.png" width="400" alt="Block browser"> <img src="./img/block-code-points.png" width="400" alt="Block code point browser">

### Search

<img src="./img/filter.png" width="400" alt="Search results">

### Sequence
macOS and Linux are supported. Glyph images are automatically enabled in kitty and Ghostty, using the Kitty graphics protocol. The iTerm2 inline image protocol is available through `--graphics iterm2` with no guarantee of compatibility or future support.

<img src="./img/sequence.png" width="400" alt="Sequence"> <img src="./img/sequence-inspector.png" width="400" alt="Sequence inspector view"> <img src="./img/sequence-normalization.png" width="400" alt="Sequence normalization"> <img src="./img/sequence-normalization-result.png" width="400" alt="Sequence normalization result">
Text information and analysis remain available without glyph images. Sauva uses installed system fonts and does not bundle fonts. See [Compatibility](https://lusingander.github.io/sauva/getting-started/compatibility.html) for details.

## License

Expand Down
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
book
11 changes: 11 additions & 0 deletions docs/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[book]
authors = ["Kyosuke Fujimoto"]
language = "en"
src = "src"
title = "Sauva"
description = "Sauva Documentation"

[output.html]
git-repository-url = "https://github.com/lusingander/sauva"
site-url = "/sauva/"
default-theme = "rust"
Loading
Loading