Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and Termleaf uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.3.3] - 2026-08-01

### Changed

- Consolidated typing-sound controls in the F10 panel, renamed the master
toggle to "Typing sound," reassigned F5 to the macOS-safe page-width toggle,
and made the footer shortcuts adapt to narrow terminals.

## [0.3.2] - 2026-08-01

### Fixed
Expand Down Expand Up @@ -99,7 +107,8 @@ and Termleaf uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Made forward Delete work for characters and line boundaries.
- Made Save As reliable through `F12`, including a Markdown default extension.

[Unreleased]: https://github.com/andy5090/termleaf/compare/v0.3.2...HEAD
[Unreleased]: https://github.com/andy5090/termleaf/compare/v0.3.3...HEAD
[0.3.3]: https://github.com/andy5090/termleaf/compare/v0.3.2...v0.3.3
[0.3.2]: https://github.com/andy5090/termleaf/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/andy5090/termleaf/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/andy5090/termleaf/compare/v0.2.4...v0.3.0
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "termleaf"
version = "0.3.2"
version = "0.3.3"
edition = "2021"
description = "A distraction-free terminal text editor built for focused writing."
repository = "https://github.com/andy5090/termleaf"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,13 @@ release artifacts.
| `F2` | toggle Termleaf's optional `Live Korean` composer |
| `F3` | toggle focus mode |
| `F4` | toggle the big-font zone |
| `F5` | toggle system-audio keystroke sound |
| `F5` | toggle the centered page-width (paper) mode |
| `F6` | cycle `paper` / `night` / `xt` / `amber` theme |
| `F7` / `F8` | decrease / increase big-font size across five effective levels |
| `Alt+L` | cycle document line spacing through levels 1–3 |
| `Alt+P` | toggle the centered 80-column page-width mode |
| `Alt+P` | alternate page-width shortcut where the terminal preserves Alt |
| `F9` | toggle English / Korean interface guidance |
| `F10` | open detailed sound settings (master / delete / return / key style) |
| `F10` | open sound settings (typing / delete / return / key style) |
| `F11` | cycle `classic` / `deep` / `soft` typewriter sound |
| `Ctrl+O` | open a file (a missing path starts a new file there) |
| `Ctrl+S` | save; the first save asks for a filename |
Expand Down
6 changes: 3 additions & 3 deletions src/config/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ pub struct Config {
pub live_composition: bool,
/// Focus mode hides the status bar and other chrome.
pub focus_mode: bool,
/// Play mechanical key and backspace effects.
/// Enable typing sound effects and their optional delete/return sounds.
pub sound: bool,
/// Play the separate backspace effect when master sound is enabled.
/// Play the separate backspace effect when typing sound is enabled.
pub backspace_sound: bool,
/// Play the carriage-return bell when master sound is enabled.
/// Play the carriage-return bell when typing sound is enabled.
pub return_sound: bool,
/// Selected printing-key sound (`classic`, `deep`, or `soft`).
pub sound_profile: String,
Expand Down
7 changes: 5 additions & 2 deletions src/input/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ pub enum Action {
ShowHelp,
ToggleFocus,
ToggleBigFont,
ToggleSound,
ToggleTheme,
ToggleLanguage,
ShowSoundSettings,
Expand Down Expand Up @@ -67,7 +66,7 @@ pub fn map_key(key: KeyEvent, live_composition: bool) -> Action {
KeyCode::F(2) => Action::ToggleLiveComposition,
KeyCode::F(3) => Action::ToggleFocus,
KeyCode::F(4) => Action::ToggleBigFont,
KeyCode::F(5) => Action::ToggleSound,
KeyCode::F(5) => Action::TogglePageWidth,
KeyCode::F(6) => Action::ToggleTheme,
KeyCode::F(7) => Action::FontDec,
KeyCode::F(8) => Action::FontInc,
Expand Down Expand Up @@ -145,6 +144,10 @@ mod tests {
map_key(KeyEvent::new(KeyCode::F(10), KeyModifiers::NONE), false),
Action::ShowSoundSettings
);
assert_eq!(
map_key(KeyEvent::new(KeyCode::F(5), KeyModifiers::NONE), false),
Action::TogglePageWidth
);
assert_eq!(
map_key(KeyEvent::new(KeyCode::F(11), KeyModifiers::NONE), false),
Action::CycleSoundProfile
Expand Down
7 changes: 0 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,6 @@ fn apply(
}
Action::ToggleFocus => cfg.focus_mode = !cfg.focus_mode,
Action::ToggleBigFont => cfg.big_font = !cfg.big_font,
Action::ToggleSound => {
cfg.sound = !cfg.sound;
if cfg.sound {
sound.play_key(&cfg.sound_profile);
}
}
Action::ToggleTheme => {
cfg.theme = Theme::next(&cfg.theme).to_string();
*theme = Theme::by_name(&cfg.theme);
Expand Down Expand Up @@ -280,7 +274,6 @@ fn handle_sound_settings_key(
match key.code {
KeyCode::Up => active.select_previous(),
KeyCode::Down => active.select_next(),
KeyCode::F(5) => toggle_sound_option(cfg, sound, 0, true),
KeyCode::F(11) => toggle_sound_option(cfg, sound, 3, true),
KeyCode::Char(' ') | KeyCode::Right => {
toggle_sound_option(cfg, sound, active.selected, true);
Expand Down
65 changes: 56 additions & 9 deletions src/renderer/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,14 @@ fn draw_sound_settings(
};
let lines = if korean {
[
format!("[{}] 전체 소리 (F5)", enabled(cfg.sound)),
format!("[{}] 타이핑 소리", enabled(cfg.sound)),
format!("[{}] 삭제 소리", enabled(cfg.backspace_sound)),
format!("[{}] 캐리지 리턴 소리", enabled(cfg.return_sound)),
format!("[{}] 타자기 종류 (F11)", cfg.sound_profile),
]
} else {
[
format!("[{}] Master sound (F5)", enabled(cfg.sound)),
format!("[{}] Typing sound", enabled(cfg.sound)),
format!("[{}] Delete sound", enabled(cfg.backspace_sound)),
format!("[{}] Carriage-return sound", enabled(cfg.return_sound)),
format!("[{}] Key style (F11)", cfg.sound_profile),
Expand Down Expand Up @@ -397,8 +397,8 @@ fn draw_help(
"",
"파일: Ctrl+O 열기 · Ctrl+S 저장 · F12 다른 이름 (.md 기본)",
"화면: F3 집중 · F4 큰글자 · F6 테마 · F7/F8 크기 1–5",
"보기: Alt+L 줄간격 1–3 · Alt+P 페이지 폭",
"소리: F5 전체 · F10 세부 설정(삭제/엔터) · F11 타자음",
"보기: Alt+L 줄간격 1–3 · F5 종이 폭 (Alt+P 보조)",
"소리: F10 설정(타이핑/삭제/엔터) · F11 타자음",
"기타: Backspace/Delete 삭제 · F9 English · F1 도움말 · Ctrl+Q 종료",
]
} else {
Expand All @@ -409,8 +409,8 @@ fn draw_help(
"",
"Files: Ctrl+O Open · Ctrl+S Save · F12 Save as (.md default)",
"View: F3 Focus · F4 Big text · F6 Theme · F7/F8 Size 1–5",
"Reading: Alt+L Line spacing 1–3 · Alt+P Page width",
"Sound: F5 Master · F10 Details (delete/return) · F11 Key style",
"Reading: Alt+L Line spacing 1–3 · F5 Page width (Alt+P alternate)",
"Sound: F10 Settings (typing/delete/return) · F11 Key style",
"Other: Backspace/Delete · F9 Korean · F1 Help · Ctrl+Q Quit",
]
};
Expand Down Expand Up @@ -925,10 +925,8 @@ fn draw_shortcuts(
" Enter 저장 Esc 취소 │ 확장자 생략 시 .md F9 영어 ".to_string()
} else if prompt.is_some() {
" Enter Save Esc Cancel │ No extension → .md F9 Korean ".to_string()
} else if korean {
" F1 도움 ^O 열기 ^S 저장 F12 다른이름 ^Q 종료 │ Alt+L 줄간격 Alt+P 페이지폭 F2 한글 F3 집중 F4 큰글자 F5 소리 F6 테마 F7/8 크기 F9 영어 F10 소리설정 ".to_string()
} else {
" F1 Help ^O Open ^S Save F12 Save-as ^Q Quit │ Alt+L Line spacing Alt+P Page width F2 Korean F3 Focus F4 Big F5 Sound F6 Theme F7/8 Size F9 한국어 F10 Sound setup ".to_string()
shortcut_guide(korean, cols).to_string()
};
let bar: String = " ".repeat(cols as usize);
queue!(
Expand All @@ -942,6 +940,35 @@ fn draw_shortcuts(
)
}

fn shortcut_guide(korean: bool, cols: u16) -> &'static str {
let choices = if korean {
[
" F1 도움 F5 종이폭 F10 소리설정 ^O 열기 ^S 저장 F12 다른이름 ^Q 종료 │ Alt+L 줄간격 F2 한글 F3 집중 F4 큰글자 F6 테마 F7/8 크기 F9 영어 ",
" F1 도움 F5 종이폭 F10 소리설정 ^O 열기 ^S 저장 ^Q 종료 │ F3 집중 F6 테마 F7/8 크기 ",
" F1 도움 F5 종이폭 F10 소리설정 ^S 저장 ^Q 종료 ",
" F1 도움 F5 종이 F10 소리 ",
" F5 종이 F10 소리 ",
]
} else {
[
" F1 Help F5 Page width F10 Sound setup ^O Open ^S Save F12 Save-as ^Q Quit │ Alt+L Line spacing F2 Korean F3 Focus F4 Big F6 Theme F7/8 Size F9 한국어 ",
" F1 Help F5 Page F10 Sound ^O Open ^S Save ^Q Quit │ F3 Focus F6 Theme F7/8 Size ",
" F1 Help F5 Page F10 Sound ^S Save ^Q Quit ",
" F1 Help F5 Page F10 Sound ",
" F5 Page F10 Sound ",
]
};

choices
.into_iter()
.find(|text| text_width(text) <= cols)
.unwrap_or(choices[4])
Comment on lines +962 to +965

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add a width-safe final shortcut fallback

When the terminal is narrower than 19 cells in English or 18 in Korean, no candidate fits and this fallback returns choices[4] even though it is wider than cols. The caller consequently clips it; for example, at 10 columns the footer becomes F5 Page F, hiding the intended F10 shortcut even though a compact F5 F10 hint would fit. Add shorter fallbacks or otherwise ensure the selected guide fits the supplied width.

Useful? React with 👍 / 👎.

}

fn text_width(text: &str) -> u16 {
text.chars().map(char_width).sum()
}

fn draw_file_prompt(
out: &mut Stdout,
prompt: &FilePrompt,
Expand Down Expand Up @@ -1022,6 +1049,26 @@ fn clipped_from_end(text: &str, max_width: u16) -> String {
mod tests {
use super::*;

#[test]
fn shortcut_guide_adapts_to_terminal_width_without_hiding_sound_settings() {
for korean in [false, true] {
for width in [24, 40, 80, 180] {
let guide = shortcut_guide(korean, width);
assert!(text_width(guide) <= width);
assert!(guide.contains("F5"));
assert!(guide.contains("F10"));
if width >= 40 {
assert!(guide.contains("F1"));
}
}
}

assert!(!shortcut_guide(false, 40).contains("Alt+L"));
assert!(shortcut_guide(false, 180).contains("Alt+L"));
assert!(!shortcut_guide(true, 40).contains("Alt+L"));
assert!(shortcut_guide(true, 180).contains("Alt+L"));
}

#[test]
fn five_font_levels_remain_distinct_on_a_standard_height() {
let profiles: Vec<(u16, u16)> = (1..=5)
Expand Down
Loading