# Changelog

## [0.12.5](https://github.com/erikjuhani/basalt/releases/tag/basalt/0.12.5) (May, 16 2026)

### Added

- [d8eb23e](https://github.com/erikjuhani/basalt/commit/d8eb23e62a260d01ff31fc3690b7fe221504e6c5) Add experimental vault path override via BASALT_EXP_VAULT_PATH by @erikjuhani

> Allows opening an arbitrary directory as a vault by setting the env var,
> skipping the splash screen and going straight to the explorer.

- [9bb26d8](https://github.com/erikjuhani/basalt/commit/9bb26d8e618ed09b2f3d1ebab5709a08d6b82acf) Add scrollbar to vault selector by @erikjuhani

> The scrollbar is now visible in both splash screen and the vault
> selector modal. It's not optimal, but I realized that vault selection
> needs a rewrite, thus I didn't bother to do this properly for example
> the scrollbar is always shown if we have more than 4 items regardless if
> we have space to show all items and not needing a scrollbar.

- [8e248e7](https://github.com/erikjuhani/basalt/commit/8e248e736afd27a36ba941479f68c8fa858bda9f) Add basalt CLI layer

> Initial CLI layer with --version and --help flags. The version output is
> in a "standard" format similar to cargo version output `0.12.5 (abc123def 2026-05-15)`.
>
> The values are populated by a build phase (compile time) using env vars.

### Changed

- [022d988](https://github.com/erikjuhani/basalt/commit/022d988f1709a10d4ef45449e4a138390b585339) Add indentation to floating rename input by @erikjuhani

> The rename input in explorer pane was fixed to left border of explorer
> pane, and forced vision to jump in deeply nested fields. This commit
> adds indentation to the floating rename input which is calculated by the
> depth of the explorer item. Additionally added depth field to items.

- [f53a500](https://github.com/erikjuhani/basalt/commit/f53a5009a6f8e55474320bf20548bab5a635df21) Create new notes and folders under the selected folder by @erikjuhani

> CreateUntitledNote and CreateUntitledFolder now resolve a target
> directory from the explorer's current item (the directory itself, a
> file's parent, or the vault root as fallback) instead of always creating
> at the vault root. After creation the explorer expands that directory so
> the new item is visible.
>
> Add an explorer Open message that always expands a directory rather than
> toggling, backed by a new ExplorerState::open. The existing toggle
> behavior moves to a Select message, and the ExplorerOpen command and
> shell-return path now map to Select. toggle_item_in_tree takes an
> always_open flag to support both.

- [44e673e](https://github.com/erikjuhani/basalt/commit/44e673ecc5ef1ca09d21e51668c67c92bf3255eb) Bump basalt-core to 0.9.0 and update CHANGELOG by @erikjuhani

> Bump basalt-core to `0.9.0` version in basalt`

### Dependencies

- [c2e878a](https://github.com/erikjuhani/basalt/commit/c2e878a7259cf6d23c4f4d7b79c8e14d49cc5dd0) Pin dependencies by @renovate-updater[bot]

> | datasource | package  | from   | to     |
> | ---------- | -------- | ------ | ------ |
> | crate      | indoc    | 2.0.7  | 2.0.7  |
> | crate      | insta    | 1.46.3 | 1.43.2 |
> | crate      | tempfile | 3.26.0 | 3.23.0 |

- [2fac95c](https://github.com/erikjuhani/basalt/commit/2fac95c8056ccd751e6d218e2e275203f891a9ca) Update Rust crate insta to v1.47.2 by @renovate-updater[bot]

> | datasource | package | from   | to     |
> | ---------- | ------- | ------ | ------ |
> | crate      | insta   | 1.43.2 | 1.47.2 |

### Fixed

- [ca05c25](https://github.com/erikjuhani/basalt/commit/ca05c25e359cc061d855cb1607979537af6acc02) Merge with previous block when backspacing at block start by @erikjuhani

> Backspace at the start of a block's text buffer used to do
> nothing. It now merges the current block into the previous one,
> preserving in-progress edits. The merge is a no-op when previous
> and current block indices coincide, so backspace at the start of
> the first block does not collapse the only AST node.
>
> Supporting changes:
>
> - Add `previous_block_idx`, rename `current_block` to
>   `current_block_idx`. Both return owned `usize`.
> - Add `TextBuffer::insert_at_start`, which prepends bytes and
>   moves both the live and original source range starts so a
>   later commit overwrites the joined region.
> - Walk children recursively in `shift_nodes` via a new
>   `ast::Node::children_as_mut`. Without this the cursor lands on
>   a stale child range after editing a paragraph above a list.
> - `commit_text_buffer` re-parses on `buffer.modified` rather
>   than on content change, since the merge mutates `ast_nodes` in
>   place and a buffer that round-trips needs a re-parse.
> - In `update_layout`'s deferred init, pick the editing block by
>   cursor source offset, not by a stale `virtual_row` lookup.
> - Rename `insertion_offset` to `source_pos`.

- [f06575f](https://github.com/erikjuhani/basalt/commit/f06575f97043cfaba999d2f80199dbd95837f2e5) Render trailing content after a heading in edit mode by @erikjuhani

> A heading's source range can include a paragraph that follows it
> when the user types a newline mid-edit. The raw renderer treated
> the whole range as one heading line, so the new content was
> invisible until exit. Split on the first newline in raw mode and
> render the rest with `render_raw`.
>
> Also drop the synthetic empty trailer in raw mode so paragraphs
> don't gain a stray blank row when entering edit mode.

- [78c7299](https://github.com/erikjuhani/basalt/commit/78c7299576af8b4459d5c3ac390e489827af039e) Account for meta header rows when scrolling the editor by @erikjuhani

> `ensure_cursor_visible` was comparing `cursor.virtual_row`
> against `viewport.top()` and a `viewport.bottom()` shrunk by
> `meta_len`. The meta rows live above the content, so the cursor's
> screen row is `virtual_row + meta_len`. The old comparison
> scrolled the editor up too early when the meta header was
> visible, hiding the first content rows.
>
> Add `meta_len` to the cursor's screen position before comparing
> against the viewport bounds, thread it through `CursorWidget` so
> it draws at the same offset, and add `Viewport::scroll_up` so
> vertical motion that consumes more rows than the viewport
> contains scrolls the document.

- [db69b04](https://github.com/erikjuhani/basalt/commit/db69b04d93ec54da925cb2453b637591246a54e8) Preserve nested file depth in explorer refresh by @erikjuhani

> `map_to_item` handled `VaultEntry::File` via `Item::from`, which
> hardcodes `depth: 0`. Files still rendered correctly, but the rename
> input modal positions from `Item::depth()`, placing it flush-left for
> nested files.

- [6da6548](https://github.com/erikjuhani/basalt/commit/6da6548325725d96a712665fe81273526da0c4df) Preserve note scroll position when toggling explorer folders by @erikjuhani

> Toggling a folder open or closed re-emitted SelectNote for the currently
> selected note, which rebuilt the note editor and reset its viewport
> scroll position.
>
> Explorer state `open()` and `select()` now report (bool) whether a note
> was actually selected, and the explorer only emits SelectNote in that
> case, which then effectively retains the scroll position of the
> viewport.

- [79082f3](https://github.com/erikjuhani/basalt/commit/79082f317efc499febb6bc7c892cd7f6c84b2887) Use config symbols for status bar component badge by @erikjuhani

> Pass the configured symbol preset into StatusBar so the active component
> badge falls back to ASCII-safe glyphs when the Ascii preset is selected.

## [0.12.4](https://github.com/erikjuhani/basalt/releases/tag/basalt/0.12.4) (Apr, 09 2026)

### Added

- [c3f2b41](https://github.com/erikjuhani/basalt/commit/c3f2b412c5e6eb09992287ead881311fc25b4b08) Add `Symbols` config types with presets by @erikjuhani

> Introduces symbols and symbol presets  as the foundation for
> configurable UI symbols. Each preset provides a complete set of defaults
> for all visual glyphs used across the interface. Users pick a preset in
> `[symbols]` and optionally override individual fields on top of it.
>
> Also added derives `PartialEq` and `serde::Deserialize` on `FontStyle`
> so heading and title font styles can be configured from toml.
>
> Related to #424

- [7d53d0a](https://github.com/erikjuhani/basalt/commit/7d53d0abf84306b3207ca2115a57c00c5cc309ff) Surface config errors as warning toasts by @erikjuhani

> Previously config parsing errors were silently swallowed. Now we return
> warnings alongside the config so they can be shown to the user. Invalid
> config files produce an `InvalidConfig` error with the TOML parser's
> message. The `UserConfigNotFound` case is still silently ignored since
> most users won't have a config file.
>
> Additionally toasts now support word-wrapped multi-line messages via
> `textwrap` and compute their height dynamically instead of using a fixed
> constant. This lets longer error messages display fully rather than
> getting truncated. Toast stacking in `render_toasts` accounts for
> variable heights.
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

- [7d3e968](https://github.com/erikjuhani/basalt/commit/7d3e968b21c116a8faa00812c19c28dc08e3b518) Add auto-detection for symbol preset based on terminal capabilities by @erikjuhani

> Extend Preset with an Auto variant that detects terminal capabilities at
> startup by inspecting TERM, LC_ALL, LC_CTYPE and LANG environment
> variables to choose between Unicode and Ascii presets.
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

### Changed

- [a67ebed](https://github.com/erikjuhani/basalt/commit/a67ebed0f430f4d3a5c68be902a522731fde7249) Add `rust-version` to basalt Cargo manifest by @erikjuhani

> Setting `rust-version` lets Cargo emit a clear error when someone
> tries to build with a toolchain older than 1.91.0 and enables
> version-aware dependency resolver behavior.
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

- [9cdaa1c](https://github.com/erikjuhani/basalt/commit/9cdaa1c4f50993b7b8de1bc0f949243a88d2ca67) Use marker width instead of prefix width when rendering by @erikjuhani

> Related to #424
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

- [7cb70c5](https://github.com/erikjuhani/basalt/commit/7cb70c5a77b683233a3c2d91b533e0e5fcbe393f) Use symbols through note editor rendering by @erikjuhani

> All render functions now take `&Symbols` parameter so application
> symbols are read from config instead of being hardcoded.
>
> List markers cycle through the configured list based on nesting depth.
> `VirtualDocument` stores a `Symbols` instance and uses it for the title
> font style and horizontal rule.
>
> Additionally add support for cycling through list markers so different
> depth levels of list indentation have different markers. Can
> define n-amount of symbols.
>
> Related to #424
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

- [e6d8edf](https://github.com/erikjuhani/basalt/commit/e6d8edf7efa0dee819bd430c181bf4f048755635) Use symbols in explorer by @erikjuhani

> Replaces hardcoded symbols in the explorer with configurable ones.
> Unselected files also render `symbols.unselected` instead of blank space
> so the collapsed view is consistent across presets.
>
> Added more emphasis on selected note with bold and underline styles.
>
> Related to #424
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

- [de90cfd](https://github.com/erikjuhani/basalt/commit/de90cfd50b522db4a585ead7d6825e49c9400a89) Use symbols config in Outline by @erikjuhani

> Replaces hardcoded glyphs with configurable symbols.
>
> Related to #424
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

- [dd49a8a](https://github.com/erikjuhani/basalt/commit/dd49a8a57af9753a1a49fbf5f032c44d9ec57b39) Use symbols in app and set default preset by @erikjuhani

> Config is now loaded once in `App::start` and passed into `App::new` so
> that `config.symbols` is available when constructing component state.
> The default config sets `preset = "unicode"` in `config.toml`.
>
> Related to #424
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

- [b4e22bf](https://github.com/erikjuhani/basalt/commit/b4e22bf25684200902c634da5c414a618af0afea) Use border style from symbols config by @erikjuhani

> Previously border style was hardcoded in code, however, this is now also
> controllable from the symbols map.
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

- [1801eea](https://github.com/erikjuhani/basalt/commit/1801eeae075c0fe3aa66d6dcdf699e93cbc812f1) Add configurable toast icons per symbol preset by @erikjuhani

> Toast icons were hardcoded. This commit adds toast_success, toast_info,
> toast_error and toast_warning fields to the symbol config so each preset
> can define appropriate icons. ASCII uses simple text characters, Unicode
> keeps the existing icons, and NerdFont uses its own glyph variants. The
> icon is resolved at render time from the active symbols config.
>
> Signed-off-by: Erik Kinnunen <erik.kinn@gmail.com>

## [0.12.3](https://github.com/erikjuhani/basalt/releases/tag/basalt/0.12.3) (Mar, 10 2026)

### Added

- [eb66637](https://github.com/erikjuhani/basalt/commit/eb66637bd7498abe7dfae0d97069440eb9a35d38) Add support for sequential keys by @erikjuhani

> This change adds support for a sequence (or chord) of keys like 'gg' or
> 'bn' or 'gth'.
>
> The keys maps are separated into Single keys (which can contain
> modifiers) or a 'sequence' of keys called Chord.
>
> Additionally added support for uppercased chars in key bindings, which
> means that users can now use shift + char to run commands.
>
> Organized and structured the key code parsing, so it's more readable and
> the 'special' cases are clearly represented.

- [50d3b96](https://github.com/erikjuhani/basalt/commit/50d3b96d49cece4fb4b2936cc46553a641b2da31) Add sequence-aware key dispatch to app by @erikjuhani

> This commit adds key sequence handling to basalt, which means that users
> can create key bindings with a key sequence like `gg` or `ciw`.
>
> - Replace single-key lookup with `pending_keys: Vec<Keystroke>` on
>   `AppState`, `handle_event` and `handle_key_event` take `&mut AppState`
>   to drive accumulation and prefix/exact matching
> - Add `ConfigSection::sequence_to_message` and `is_sequence_prefix`
> - Remove old `key_to_message` / `handle_active_component_event`, editing
>   bypass is now inlined in `handle_key_event`
> - `handle_editing_event` in input and note_editor now take `KeyEvent` by
>   value for consistency
>
> Related to #400
> Related to #212

- [2cc478d](https://github.com/erikjuhani/basalt/commit/2cc478d3d592f19e70b26fbe0620166088ec95b0) Add scroll-to-top and scroll-to-bottom commands by @erikjuhani

> Adds ScrollToTop/ScrollToBottom to the note editor and explorer message
> enums, wired through new command variants: note_editor_scroll_to_top,
> note_editor_scroll_to_bottom, explorer_scroll_to_top,
> explorer_scroll_to_bottom.
>
> Note editor ScrollToBottom uses cursor_jump to the last block rather
> than cursor_down(usize::MAX), which silently does nothing because
> saturating_add clamps to usize::MAX and skip(lines.len()) exhausts the
> iterator.

- [0a1dfc9](https://github.com/erikjuhani/basalt/commit/0a1dfc926b2ef48220dc01c06f6c4bf791f0dec6) Add vim_mode config flag and vim.toml preset by @erikjuhani

> When vim_mode = true in the user config, a vim.toml preset is merged
> between the base config and the user config, so users can still override
> individual bindings. The preset adds:
>
> - ctrl+f / ctrl+b for half-page scrolling in note editor, explorer,
>   and help modal (replacing ctrl+d / ctrl+u)
> - gg / G for jump to top/bottom in note editor, explorer, and outline

- [ae58ea4](https://github.com/erikjuhani/basalt/commit/ae58ea403b4a80e9fee71c2b5dd4ef457cbe6efb) Add vim Normal/Insert sub-modes to note editor by @erikjuhani

> Add Normal/Insert sub-modes within EDIT mode. `i` enters Insert mode,
> `Esc` returns to Normal for hjkl navigation, `Esc` again exits to READ.
>
> Fix block navigation in edit mode: track `editing_block` explicitly to
> prevent layout oscillation, commit text_buffer before switching blocks,
> fix `modified()` after block switch, and use AST source ranges for
> correct cursor positioning when entering code blocks.

- [687ab46](https://github.com/erikjuhani/basalt/commit/687ab4694e681d1d117164932e62641c85fa31c8) Add create untitled note and folder commands

> Bind `n` to create a new untitled note and `N` to create a new
> untitled folder in the explorer. Both commands select the newly
> created item in the explorer after creation.

### Changed

- [0c9883c](https://github.com/erikjuhani/basalt/commit/0c9883c058c71b097bf7a55ed63cb61733c0734d) Replace default keybindings with vim preset when vim_mode is enabled by @erikjuhani

> Instead of merging vim.toml bindings on top of the defaults, vim mode
> now replaces the entire key_bindings set for each section it defines.
> This ensures vim-style bindings like `gg` and `G` are the only way to
> scroll to top/bottom, without leftover default bindings like
> ctrl+shift+up/down.

- [697b856](https://github.com/erikjuhani/basalt/commit/697b85682cb037cda90a2860652a2d3a82ab632d) Normalize lowercase+SHIFT keystrokes to uppercase by @erikjuhani

> Some terminals send 'g'+SHIFT instead of 'G'+SHIFT. Normalize this in
> `Keystroke::from` so key bindings match regardless of terminal behavior.
> Also fix `From<&KeyEvent>` to go through the normalization path.

### Fixed

- [90528cb](https://github.com/erikjuhani/basalt/commit/90528cb74b944d840297d68969116ba10680b45f) Fix integer overflow in explorer by @erikjuhani


- [b98cdf5](https://github.com/erikjuhani/basalt/commit/b98cdf5913e8c81df50e21e0630c0147587e7efd) Fix vim_mode config flag by @erikjuhani

> Fix inverted vim_mode condition that loaded vim config when disabled...
>
> Add w/b word motion bindings for input modal in vim.toml and rename
> VIM_CONFIG_STR to VIM_CONFIGURATION_STR for consistency

- [cda1b25](https://github.com/erikjuhani/basalt/commit/cda1b25c784ae0f3eb3c23d401883d235d1ebe93) Fix cursor movement skipping blocks with no accessible content lines by @erikjuhani

> When moving up/down, if the target line has no content (e.g. a synthetic
> line in a visual code block), search in the opposite direction as a
> fallback. This fixes ScrollToTop not working when the first element is a
> code block, and ScrollToBottom not reaching the last line.

- [ef9f76d](https://github.com/erikjuhani/basalt/commit/ef9f76dc160030a2313fa07bec0457acdd51b5bf) Fix first line not rendering in edit mode and vim save

> When entering edit mode before layout, `enter_insert` couldn't find
> blocks and created a spurious empty paragraph node, hiding the real
> first line. Guard the empty-node fallback to only fire for genuinely
> empty files.
>
> Also commit the text buffer when exiting vim insert mode so that save
> writes the updated content.

- [fea931b](https://github.com/erikjuhani/basalt/commit/fea931b76ad38e617cb7092c4fbacbc1e26365e1) Fix Explorer lexicographical order in item sorting

> When you had items with number suffixes like 1, 2, 3, 100, the standard
> rust comparison and ordering would not work as expected, as users are
> usually expecting natural ordering. What happened was that we received:
> "item 1, item 10, item 2" instead of "item 1, item 2, item 10".
>
> Added natord crate for natural sort ordering, and now explorer sorts the
> items in expected order "item 1, item 2, and item 10".

- [e80e4e2](https://github.com/erikjuhani/basalt/commit/e80e4e235a49b0feb1dd87727544605e8b80653c) Fix faulty app state after renaming

> In some conditions after rename the state would be left in "limbo" and
> "frozen" state, which meant that users could not navigate normally
> anymore. This happened because we didn't correctly change to Explorer
> pane after exiting from rename input.

- [d7fb1c5](https://github.com/erikjuhani/basalt/commit/d7fb1c533f8ba33120030b41cd97c2a231623a35) Fix stale rendering after exiting vim insert mode

> When using vim mode with NORMAL and INSERT modes, when creating writing
> new nodes for example a heading and then exiting would result in visual
> bug that appeared as if the current node merged with the next one, e.g.
> a paragraph after our newly created heading looked liked it was merged
> into it.
>
> Fixed the visual bug by accessing the ast_nodes directly, since
> virtual_document in this case would have "stale" data.

## [0.12.2](https://github.com/erikjuhani/basalt/releases/tag/basalt/0.12.2) (Feb, 21 2026)

### Added

- [57017bb](https://github.com/erikjuhani/basalt/commit/57017bb04ecd065f59e1051803a93e839c17efe2) Add toast notification system

> Introduces toast module with level-based notifications (info, warn,
> error, success) that auto-expire via a 250ms tick loop. Toasts render
> in the top-right corner with colored borders and icons per level.
>
> Includes snapshot tests for all toast variants and unit tests for
> expiry behavior.
>
> Related to #83

- [7bd859b](https://github.com/erikjuhani/basalt/commit/7bd859bccfa88398ad94bcd45c03317d54f2be14) Add `Batch` message variant for sequential message dispatch

> Enables multiple app messages to be dispatched and fully processed in
> sequence, which is needed to combine actions like saving a note and
> showing a toast notification in a single update cycle.
>
> Related to #83

- [99b79b1](https://github.com/erikjuhani/basalt/commit/99b79b13150a61fe347f2276bd1b517f24e0076b) Add toast notifications for file save feedback

> Show a success toast when a modified file is saved and an error toast
> when saving fails, replacing the previous silent no-op error handling.
>
> Fixes #83

### Fixed

- [5f93090](https://github.com/erikjuhani/basalt/commit/5f93090f2679eaa0785f56a398317cdc82629d13) Fix viewport not scrolling with cursor in edit mode by @erikjuhani

> Call `ensure_cursor_visible` after cursor movement and text editing
> operations (`insert_char`, `delete_char`, `cursor_left`, `cursor_right`,
> `cursor_word_forward`, `cursor_word_backward`) so the viewport follows
> the cursor. These calls were already present in `cursor_up` and
> `cursor_down` but missing from the other methods.
>
> Fixes #316

- [5fd1359](https://github.com/erikjuhani/basalt/commit/5fd1359843bf407a2d80b8035ac258794d2c71f3) Fix input modal position when viewport is scrolled by @erikjuhani

> Account for the list scroll offset when calculating the input modal
> y-position in `ToggleInputRename`. Previously, the position used the
> absolute selected index, which placed the modal outside the visible area
> when the list was scrolled.

- [a4ed28f](https://github.com/erikjuhani/basalt/commit/a4ed28f19898a64c39936a2a66c08a0734e39620) Disable smart punctuation by @erikjuhani

> Smart punctuation transforms characters like ' and " to curly variants
> like ‘ and “. The latter variants have different byte lengths. This had
> an effect that made the source offset not match with the rendered offset
> causing issues like inability to move the cursor downwards if the
> current paragraph contained characters that were transformed into
> 'smart' variants due to overlapping offsets it the length difference
> caused.
>
> The fix was to disable the smart punctuation, and come back to it at a
> later date and do the change holistically. This needs most likely some
> architectural change to allow smart punctuation to work. The smart
> punctuation should be treated as virtual variant that only has an effect
> in the rendering part of the text.
>
> This fixes: #371

- [2f83c49](https://github.com/erikjuhani/basalt/commit/2f83c492ebdfe089b0d0b638f30011c3b91d1d69) Fix editing when file is empty by @erikjuhani

> When opening an empty file and entering edit mode, no text or cursor was
> visible until pressing ESC. This was caused by three issues:
>
> - No AST nodes existed for empty files, so layout produced no virtual
>   lines and typed text was invisible
> - Cursor rendering was gated on non-empty content, which is only updated
>   on exit from edit mode
> - `render_raw` produced no content lines for empty content, leaving the
>   cursor with no valid position
>
> Fix by creating a placeholder paragraph node when entering insert mode
> on an empty document, allowing cursor rendering in edit mode, and
> producing a content line in `render_raw` for the empty content case.

## [0.12.1](https://github.com/erikjuhani/basalt/releases/tag/basalt/0.12.1) (Jan, 26 2026)

### Changed

- [3f81196](https://github.com/erikjuhani/basalt/commit/3f811966f359cb3ebd162478ebb5b95093e0afc7) Swap the sort symbol to a more common one by @erikjuhani

> The previous 𝌆 tetragram for centre symbol had multiple issues between
> different terminal emulators and recently the update of unicode-width
> that changed classification on some symbols making the width differ,
> from the previous version.
>
> Without this change I cannot update to newer version of unicode-width.

- [9716042](https://github.com/erikjuhani/basalt/commit/97160424a5186e9615ec2e8e40f12a68254ec24a) Remove ~beta suffix from version string

> Obsolete feature as the 0. major version should tell enough about the
> instability of this application.

### Fixed

- [001fe3e](https://github.com/erikjuhani/basalt/commit/001fe3e6d0cab7cb85bc77c588ad0c6de2691dab) Fix cursor movement for multi-byte unicode characters by @erikjuhani

> The cursor now correctly handles multi-byte characters (emojis, unicode
> symbols) when moving left/right and when calculating visual positions.
> Previously, the editor assumed 1 byte per character, causing the cursor
> to land in the middle of multi-byte sequences.
>
> Key changes:
>
> - Use byte lengths instead of character counts for source range tracking
> - Convert between byte offsets and character boundaries properly
> - Update `insert_char` and `delete_char` to account for variable byte
>   widths
> - Fix `source_offset_to_virtual_column` to use byte indices instead of
>   char indices
>
> Fixes #314

- [225184b](https://github.com/erikjuhani/basalt/commit/225184b9e69147162ae171836fe322f420be3014) Fix cursor movement through empty lines in code blocks by @erikjuhani

> The cursor could not move upwards past empty lines in code blocks in both
> edit and read modes.
>
> In edit mode, `virtual_position_to_source_offset` incorrectly returned
> `source_range.end` for empty lines because `cur_col` included synthetic
> span widths. Added `content_col` to track only content character widths.
>
> In read mode, the Visual rendering of code blocks didn't account for
> newlines when calculating source ranges, causing empty lines to have
> empty ranges (e.g., 5..5). Now uses `line_range()` which properly adds 1
> for newlines.
>
> Fixes #321

- [3f31151](https://github.com/erikjuhani/basalt/commit/3f31151e2566495b51353202d820f8c0bb4da970) Update all wiki-links in notes after rename

> Now all wiki-links will be updated after renaming a note in basalt.
> We call `update_wiki_links` in the RefreshVault message handler to
> automatically update links across the vault when a note is renamed.
>
> Also refreshed the note editor content after rename to reflect any
> wiki-link changes in the currently open note, otherwise the content
> would not be refreshed properly.
>
> Fixes #307

## [0.12.0](https://github.com/erikjuhani/basalt/releases/tag/basalt/0.12.0) (Jan, 18 2026)

### Added

- [342fbd6](https://github.com/erikjuhani/basalt/commit/342fbd6f9bd91e595aba95c7c0dd050873e5e205) Add input modal with note and directory rename functionality by @erikjuhani

> > [!CAUTION]
> > BEWARE! This rename implementation does not cover updating the
> > wiki-links. If you use the rename on notes that are referenced as
> > wiki-links—these links will be broken after and needs to be manually
> > corrected.
>
> Add an input modal that provides dynamic modal text editing. The modal
> features a text input widget and cursor navigation, which supports
> character-by-character and word-based movement. The vim-like modes are
> limited. Only aforementioned movement and text editing.
>
> The modal is integrated with the explorer pane, and is available by
> pressing 'r' (default key binding) on the selected item. The rename
> operation leverages the `rename_note` and `rename_dir` functions added
> in affec53 and a347325, the vault is 'reloaded' after rename.

### Dependencies

- [0b7e9ab](https://github.com/erikjuhani/basalt/commit/0b7e9ab2ac648a982de13c395b4d33a23a85bcd3) Update Rust crate ratatui to 0.30.0 by @renovate-updater[bot]

> | datasource | package | from   | to     |
> | ---------- | ------- | ------ | ------ |
> | crate      | ratatui | 0.29.0 | 0.30.0 |

### Fixed

- [5f9d342](https://github.com/erikjuhani/basalt/commit/5f9d34224a498ae3d7d010adf887fdcdb07317c9) Fix sorting to match Obsidian sorting

> Fixes #69. Previously, when user sorted the vault items the folders
> would also be sorted according to the same rules as notes, however, this
> is not how obsidian sorts. Obsidian sorts only files by default, not
> directories. Directories are initially sorted A-z, and then kept in that
> order when sorting files.

## [0.11.2](https://github.com/erikjuhani/basalt/releases/tag/basalt/0.11.2) (Dec, 21 2025)

### Changed

- [88ec357](https://github.com/erikjuhani/basalt/commit/88ec3577ef32f31e2277050bfe572d8fe18506cf) Update basalt-core version to 0.7.0

> Use direct definitions in the respective crates instead of using
> workspace dependencies for basalt-core and basalt-widgets.

### Fixed

- [8744562](https://github.com/erikjuhani/basalt/commit/8744562b37d04d5522ac81b13914d605e31a053a) Fix nested task list rendering to properly indent subtasks by @erikjuhani

> The parser now correctly nests subtasks within their parent task nodes
> rather than treating them as siblings. The task_kind field changed from
> Option to Vec to track nested task states, similar to item_kind.
>
> Nested task lists are now properly rendered following the same
> implementation as in the list items code.

- [5b54928](https://github.com/erikjuhani/basalt/commit/5b54928c66133f6755c82ac34ce9cbc5fa8b7026) Fixes 'sticky' symbols when switching between read and edit by @erikjuhani

> The sticky key effect was visible for example with task lists when tasks
> were intended with tabs in the source. These tab characters would never
> replace the existing symbols from the buffer. The sticky symbols issue
> was fixed by replacing the tab characters with two spaces.

## [0.11.1](https://github.com/erikjuhani/basalt/releases/tag/basalt/0.11.1) (Dec, 08 2025)

### Fixed

- [63b5e9f](https://github.com/erikjuhani/basalt/commit/63b5e9f618aac66bf47f8b4160a959600dcab28d) Use basalt-core 0.6.3 version in basalt

> basalt-core 0.6.3 fixes vault json deserializer for `ts` field and sets
> it as optional. It was previously set as required. If the `ts` field was
> missing it would crash basalt.

## [0.11.0](https://github.com/erikjuhani/basalt/releases/tag/basalt/0.11.0) (Nov, 30 2025)

Basalt author and maintainer here! Wanted to write a few words before the _regular_ changelog.

Phew, this took longer than expected, but here we are. The editor feature does not offer feature parity with the tui-textarea that was being used previously, however, it can properly wrap the text while writing, which frankly, I find quite pleasing.

If you encounter any bugs or additional strangeness please open an issue! The editor was made by me and most likely contains errors. Use with caution! :)

I'm taking a small break from basalt for the advent of code puzzles! So expect slower development during December.

This release fixes the following issues: [#105](https://github.com/erikjuhani/basalt/issues/105), [#104](https://github.com/erikjuhani/basalt/issues/104), [#95](https://github.com/erikjuhani/basalt/issues/95)

Demo:

![basalt demo of new 0.11.0 version editor capabilities](https://github.com/erikjuhani/basalt/blob/083ca1abc96ae35bf8ba144476c0224a63854259/assets/basalt-0-11-0.gif?raw=true)

### Added

- [ba8f3a0](https://github.com/erikjuhani/basalt/commit/ba8f3a0b260f935e88346a0f451572bdeac8ffe8) Introduce virtual document structure with rendering by @erikjuhani

> I decided to implement a virtual document, which is essentially the
> virtually rendered version of the markdown document. This virtual
> document is a collection of virtual blocks, virtual lines and virtual
> spans.
>
> Virtual lines and spans are turned into Ratatui variants to render them
> in terminal.
>
> Virtual spans are separated into two concepts, synthetic and content.
>
> - Synthetic spans are elements that are not calculated as part of the
>   markdown source.
> - Content spans on the other hand are elements that are calculated as
>   part of the markdown source.
>
> This separation enables more fluid use cases and easier management
> codewise for more rendered content, like text wrapping symbols,
> additional emphasize lines or spans, etc.
>
> Rendering is a collection of functions that are turned into virtual
> blocks. These virtual blocks map directly into top level markdown nodes.
>
> All rendered functions wrap the text with the given max width. The text
> wrapping is a generalized wrapping function that can be now run for "any"
> markdown node that is defined in the ast module.

- [943256b](https://github.com/erikjuhani/basalt/commit/943256b8710cdd2081836c877623a3db8be21b70) Add Cursor module by @erikjuhani

> Cursor module is responsible for keeping up with the cursor state.
> Cursor can be switched between two modes, read and edit.
>
> Each mode behaves a bit differently, read only considers the virtual
> elements, and edit mode considers the source content.
>
> For now only the read mode variant is properly implemented.
>
> The rendering is handled with a separate stateful CursorWidget component
> that takes the cursor state as an input and draws the cursor
> accordingly.
>
> In read mode the cursor is drawn as a full-width line cursor.

- [b26f0ff](https://github.com/erikjuhani/basalt/commit/b26f0ffbf5b2ca7d86c6fc1bb882b8259d5c1610) Add soft break parsing to markdown parser

> Also add empty_line() helper function to TextSegment struct. This
> creates a new empty line with "\n" as the content.
>
> This empty line can then be split in the render functions, but still
> keep the content inside a single markdown node (e.g. paragraph).

- [5222b7e](https://github.com/erikjuhani/basalt/commit/5222b7e316227076d287f897500cc479386db53e) Add text wrapping helper utility

> The text wrapper module exposes `wrap_preserve_trailing`, which, as name
> implies, keeps the trailing whitespace.
>
> I'm using the `textwrap::WordSeparator` to find and iterate over the
> words in the text, and then determine if the word fits in the current
> line by using the passed max width variable. The `textwrap` crate itself
> did not ship with a premade wrapping utility that would have preserved
> the whitespace, at least, I did not find such utility.

- [bf2c86d](https://github.com/erikjuhani/basalt/commit/bf2c86d9cc4791517364297dca2173e2a0cd2828) Add a simple viewport abstraction

> The viewport abstraction wraps the ratatui layout structure Rect and
> uses additional layout data structures like Size and Offset.

- [5b83d5f](https://github.com/erikjuhani/basalt/commit/5b83d5ffd30892b89353329b0c224000fa1896a1) Add `chars` and `char_indices` methods to virtual span

> These helper methods will allow easier access to the underlying chars
> and their byte indices.

### Changed

- [ee4976f](https://github.com/erikjuhani/basalt/commit/ee4976f7734fc2ef67145195be3fa0f7f8fd3ecf) Replace old editor with new implementation by @erikjuhani

> The old note editor variant was hard to maintain and was lacking proper
> structure. Adding new ast nodes or elements was a cumbersome process.
>
> The new variant uses logical structures like virtual document and
> separate rendering functions to achieve a more cohesive end result.
>
> The editor now requires to have a viewport in order to render anything
> properly. This is a requirement for example to decide the correct
> wrapping width for text elements.
>
> This change also introduces fix for scrolling. Now scrolling works
> properly and cursor is always visible in the viewport. This fixes the
> issue #104.
>
> The rendering is simplified drastically due to the use of more logical
> structures.
>
> In this commit, only read mode is enabled and the edit mode support is
> missing.

- [88be5fa](https://github.com/erikjuhani/basalt/commit/88be5fa7e73b0165681db8f366db752cfdbc0075) Return a reference instead of owned RichText

> No reason to not return a reference, and we avoid allocation.

- [739704a](https://github.com/erikjuhani/basalt/commit/739704a371032b0ae4c985145694657315ab2ed4) In virtual span width() returns both content and synthetic width

> Previously only content width was taken into account, however, this does
> not work as intended as the synthetic width needs to be calculated as
> well to find for example the correct offset for cursor column.

- [fa59ed5](https://github.com/erikjuhani/basalt/commit/fa59ed566d7f504d9af0a635754fccc1c7b9207f) Remove unused methods from virtual line

> Also simplified and improved the existing methods. For example virtual
> spans now retuns a slice instead of owned Vec.

- [d00f9d4](https://github.com/erikjuhani/basalt/commit/d00f9d41fa05274644a8250d2bd1684193da78af) Implement custom text editor

> This custom implementation replaces tui-textarea with proper text
> wrapping and better WYSIWYG experience. Also the custom implementation
> allows for more granular control over how elements are, rendered and
> positioned.
>
> I tried to mimic the previous functionality in a way so as little is
> lost as possible feature wise, obviously, since this is made from
> scratch the feature parity is far off still.
>
> Additionally there is some known issues, like cursor positioning is
> incorrect with unicode symbols in source content. This can be observed
> by wrong end position when moving the cursor to the right most end. The
> cursor appears as if it is stuck, but that is due to wrong count
> somewhere, which should be fixed, but in a different commit.
>
> For now the implementation is very limited and implements only a
> restricted set of features: Editing markdown nodes, saving changes to
> file, moving by words and scrolling by half pages.
>
> This commit has quite many changes, and, unfortunately the nature of how
> this refactor was introduced, was difficult to separate the commits
> atomically and cleanly to smaller pieces.
>
> But the most notable ones are:
>
> - Cursor changes, which include the cursor movement by columns and words
>   and proper cursor positioning from the source offset location.
>
> - Editor state changes to allow insertion, deletion and saving of files,
>   and source range shifting, which is related to the editor
>   functionality, which essentially shifts the end of the source range,
>   if the source ranges are not shifted properly and if the text buffer
>   exceeds the range start of next node the text buffer would be
>   replicated and rendered in-place of the next node.
>
> - Render changes, which introduce a new consolidated text wrapping and
>   handling for newline characters in both visual and raw rendering
>   modes. The new consolidated text wrapping uses the new whitespace
>   preserving text wrap function. Additionally source offset for rendered
>   virtual lines were fixed. Also added unicode-width dependency for
>   accurate unicode character width calculations in render.

### Dependencies

- [2985a13](https://github.com/erikjuhani/basalt/commit/2985a13678b0783af61d9dbd16a74c2c1d639b87) Update Rust crate etcetera to 0.11.0 by @renovate-updater[bot]

> | datasource | package  | from   | to     |
> | ---------- | -------- | ------ | ------ |
> | crate      | etcetera | 0.10.0 | 0.11.0 |

### New Contributors
* @erikjuhani made their first contribution in [#191](https://github.com/erikjuhani/basalt/pull/191)
* @renovate-updater[bot] made their first contribution
* @istudyatuni made their first contribution


**Full Changelog**: https://github.com/erikjuhani/basalt/compare/basalt/v0.10.4...basalt/0.11.0


## 0.11.0 (Unreleased)

This release adds new improved note editor with proper text wrapping for all markdown elements (excluding code blocks).

The new improved and refactored editor code should enable faster feature creation.

### Added

- [Add Cursor module](https://github.com/erikjuhani/basalt/commit/943256b8710cdd2081836c877623a3db8be21b70)
- [Introduce virtual document structure with rendering](https://github.com/erikjuhani/basalt/commit/ba8f3a0b260f935e88346a0f451572bdeac8ffe8)

### Changed

- [Replace old editor with new implementation](https://github.com/erikjuhani/basalt/commit/ee4976f7734fc2ef67145195be3fa0f7f8fd3ecf)

## 0.10.4 (Oct, 6 2025)

This release adds note name support in the editor pane. Follows a similar approach as in the Obsidian app itself. The note name can not be changed yet in basalt.

Additionally contains some fixes and slight changes to headings to make them work better with the new note name implementation.

### Added

- [Add support to render filename in the editor pane](https://github.com/erikjuhani/basalt/commit/f23e1c40c04cad62aa23e99983adf9dc9bc4474c)

### Fixed

- [Fix Outline state allowing to move past visible items](https://github.com/erikjuhani/basalt/commit/3339182706790d0865abb2f2bd1ceee129183397)
- [Fix sub item rendering](https://github.com/erikjuhani/basalt/commit/e5c2835a90d38dd239b5ed820cd95b65a5934321)

### Changed

- [Change markdown heading level 1 and 2 to more subtle](https://github.com/erikjuhani/basalt/commit/e3db134e45621215e66991a88edf5d9388db23eb)
- [Only text is crossed over for "hard checked" tasks](https://github.com/erikjuhani/basalt/commit/13a8a26a69ad84f29c6edc7cf76e52a905b2996f)

## 0.10.3 (Sep, 15 2025)

This release adds the support to easily hide and expand the explorer pane (file tree). Expanding and hiding is done with h, l and arrow left, and arrow right.

When explorer is expanded a ⟹  symbol is shown for clarity of the current state.

### Added

- [Support expandable explorer commands in Explorer widget](https://github.com/erikjuhani/basalt/commit/4e815790a30f4ee949fbc25648e2c676dd19ab59)
- [Add hide_pane and expand_pane explorer commands](https://github.com/erikjuhani/basalt/commit/18f2f0b06c6b73eaa120852a845e4d33796980b1)

### Fixed

- [Fix crash when note editor has no width available](https://github.com/erikjuhani/basalt/commit/f52d084cdbec9c7e49d82a7f0c89a0b6d5d950a7)

## 0.10.2 (Sep, 13 2025)

Deprecated the following config commands:

- "note_editor_experimental_set_edit_mode"
- "note_editor_experimental_set_read_mode"
- "note_editor_experimental_exit_mode"

Use these instead:

- "note_editor_experimental_set_edit_view"
- "note_editor_experimental_set_read_view" and
- "note_editor_experimental_exit"

### Changed

- [Change note editor views and modes to follow Obsidian equivalent](https://github.com/erikjuhani/basalt/commit/371df9adf40624762dbf81b36c7395c7a5c34d3b)

## 0.10.1 (2025-08-31)

### Added

- [Add arbitrary (sync, spawn) command execution](https://github.com/erikjuhani/basalt/commit/750108f3282af5e947c23eb88ff3b5f8f196d0e4)

### Changed

- [Adjusted Explorer folded border to match outline](https://github.com/erikjuhani/basalt/commit/56ee16be7cfc8a211a980295818a2a2204009f98)

## 0.10.0 (2025-08-21)

### Added

- [Add `Outline` module](https://github.com/erikjuhani/basalt/commit/f02ac878102915d749ae79d60203ec512c5ef484)

### Changed

- [Change focus switch to support previous and next panes](https://github.com/erikjuhani/basalt/commit/d1cb962370cf03ec3f3da0527427d037fa81ccfd)

## 0.9.0 (2025-07-30)

### Added

- [Add experimental note editor support](https://github.com/erikjuhani/basalt/commit/924e2e25d9515b08cead11f3f4ef0413ef500a22)

## 0.8.0 (2025-06-25)

### Added

- [Add user configuration file support for customizable key bindings](https://github.com/erikjuhani/basalt/commit/b04b41a13a84aa2fce3300fa1b4cc44954f62f4f)
- [Adds a 'config' field to the AppState, which is based on a toml file (#25)](https://github.com/erikjuhani/basalt/commit/ed24f4c649b5ea66896911e5350ba27ea03b4694)

### Fixed

- [Fix display issue with active Pane UI element](https://github.com/erikjuhani/basalt/commit/f05eb3af66e18b886c774670f972284c2bcce427)

## 0.7.0 (2025-06-15)

### Changed

- [Refactor state management](https://github.com/erikjuhani/basalt/commit/0d49afb9dd7078215ed3fb15ee6dea23da1c0ba9)

### Added

- [Add visiblity and visiblity helper methods to HelpModal](https://github.com/erikjuhani/basalt/commit/8f92863932325157ffe0e181470d194ee90b2a23)
- [Add visibility and helper methods to VaultSelectorModal](https://github.com/erikjuhani/basalt/commit/1243a33d62d0cac04d2bb7556477e44867b491f8)
- [Add active field to MarkdownView to indicate active state](https://github.com/erikjuhani/basalt/commit/5880a160f30628ebec4f6e043e97b83ccb8a1899)

## 0.6.1 (2025-06-07)

### Fixed

- [Use snap folder `/current` instead of `/common`](https://github.com/erikjuhani/basalt/commit/ac0ee653250e0ca052063506f10d61a9ce2f7735)

## 0.6.0 (2025-06-01)

### Added

- [Add `Explorer` module](https://github.com/erikjuhani/basalt/commit/5d1f05fcbe5c0add6f687512fc3cf538a2df1148)

### Fixed

- [Fix large size difference between variants](https://github.com/erikjuhani/basalt/commit/159ae7ab22ab5cd4351075b2fe526a5628cfb3b9)

## 0.5.0 (2025-05-25)

### Fixed

- [Support deeper block quotes with proper prefix recursion](https://github.com/erikjuhani/basalt/commit/3f1ed73a0edcfbb17800cb27d7bda145b93369f6)
- [Add two space indentation to list items](https://github.com/erikjuhani/basalt/commit/b1a021e25759c39cee00cd1b787ccfafa1ad4ad4)
- [Fix code block rendering](https://github.com/erikjuhani/basalt/commit/cae8fae154d7a6da2ec0ffb6b28ac85b2cc73023)

### Changed

- [Change Markdown headings to stylized variants](https://github.com/erikjuhani/basalt/commit/30321916b5d6f79afe2a58f9b45b6eaa963ac12e)

## 0.4.1 (2025-05-25)

### Changed

- [Use dark gray color instead of black](https://github.com/erikjuhani/basalt/commit/237c7e436c76d61fe4339aa961e1f77a2ffbb43d)

## 0.4.0 (2025-05-25)

### Fixed

- [Update basalt-core to version 0.5.0](https://github.com/erikjuhani/basalt/commit/a30d611b79a98b661aabd27eca0c8caa69e27fa8), which potentially fixes #44

Check basalt-core CHANGELOG [here](../basalt-core/CHANGELOG.md).

## 0.3.7 (2025-05-22)

### Added

- [Add `stylized_text` module](https://github.com/erikjuhani/basalt/commit/47db925ef858831672be69fb11bcf272522e1b3a)
- [Add `lib.rs` which allows basalt to be used as a library](https://github.com/erikjuhani/basalt/commit/ce094ed8aab1945aad36955bce83eeea09085177)

### Fixed

- [Use a regular loop instead of recursion for rendering](https://github.com/erikjuhani/basalt/commit/4d9e6c83f2342b12501c2f316dbab05ab68119ab)

## 0.3.6 (2025-05-21)

### Fixed

- [Fix panic, when there are no notes inside a vault](https://github.com/erikjuhani/basalt/commit/4644f90a595f8000e983475b78e0d3605a5bc16e)

## 0.3.5

### Fixed

- [Use config_dir() to locate obsidian.json on Windows (#38)](https://github.com/erikjuhani/basalt/commit/839674c3e8fa1d8a9e6b7852bcc659dbd88e45dc)

## 0.3.4

### Added

- [Refactor Markdown event parser (#28)](https://github.com/erikjuhani/basalt/commit/4e82e7523a72064afe98c6c6de6ba8e84a334b71)
- [Add support for `LooselyChecked` task kind (#29)](https://github.com/erikjuhani/basalt/commit/1b9df5b0e167442f039fc02f8221a6a390e44acc)
- [Add support for ordered lists](https://github.com/erikjuhani/basalt/commit/7f715bb04c66066959588abfca5f29a3b3df22a7)
- [Add text wrapping for paragraphs](https://github.com/erikjuhani/basalt/commit/4a57d9a91e22c511bdbe23ae90fb6a3244d2dc32)

### Changed

- [Change checkbox symbol (#30)](https://github.com/erikjuhani/basalt/commit/11b944cbca19a020d984fbb272724ec80d1119e0)
- [Render code block as a full-width block](https://github.com/erikjuhani/basalt/commit/67905b4bacbff266c5579ac78be9ee65d9c23c85)

## 0.3.1

### Fixed

- [Adjusted the conditional config location for linux from ~/.../Obsidian to ~/.../obsidian](https://github.com/erikjuhani/basalt/commit/1bcc0375b9cb101e3fe8ace979c055ab0206bbd1)

## 0.3.0

### Added

- [Add `app` module](https://github.com/erikjuhani/basalt/commit/bd615f8da8813312fd9351b1ccdcf5e29b164d6d)
- [Add `start` module](https://github.com/erikjuhani/basalt/commit/e5ce84bee9b3801fdc4aecd43eb091c3055050fd)
- [Add `help_modal` module with `help.txt`](https://github.com/erikjuhani/basalt/commit/617e688bc277e4534d2f8fafaf9f0288cd026702)
- [Add `statusbar` module](https://github.com/erikjuhani/basalt/commit/05b42183514172c1b640c0d7ae5d6e3683942d5f)
- [Add `sidepanel` module](https://github.com/erikjuhani/basalt/commit/537917da8905db138c0839a05df2e80795f29524)
- [Add `vault_selector` and `vault_selector_modal`](https://github.com/erikjuhani/basalt/commit/8a42a008c094088a5bfb76178d566fd71246d380)
- [Add `text_counts` module](https://github.com/erikjuhani/basalt/commit/f646b8a1c2b0e055b7dd4c5b6f0963759200c731)
