From 4b09d1d2fa5179f65dc364cdcda1bf5f2d20f527 Mon Sep 17 00:00:00 2001 From: "Andy D.K Lee" Date: Sun, 2 Aug 2026 03:43:08 +0900 Subject: [PATCH] Improve macOS layout shortcuts for v0.3.4 --- CHANGELOG.md | 12 +++++++++++- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 11 +++++++++-- src/input/events.rs | 5 +++++ src/renderer/terminal.rs | 27 ++++++++++++++------------- src/ui/layout.rs | 36 ++++++++++++++++++++++++++++++++++-- 7 files changed, 75 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e0babf..40c89fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and Termleaf uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.3.4] - 2026-08-02 + +### Changed + +- Added a one-row top margin above the document when big text is disabled, + while preserving all available editing rows in very small terminals. +- Added `Shift+F5` as a platform-neutral line-spacing shortcut while retaining + macOS `Option+L` and Windows/Linux `Alt+L` as terminal-dependent alternates. + ## [0.3.3] - 2026-08-01 ### Changed @@ -107,7 +116,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.3...HEAD +[Unreleased]: https://github.com/andy5090/termleaf/compare/v0.3.4...HEAD +[0.3.4]: https://github.com/andy5090/termleaf/compare/v0.3.3...v0.3.4 [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 diff --git a/Cargo.lock b/Cargo.lock index 9e44969..e459c92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -729,7 +729,7 @@ dependencies = [ [[package]] name = "termleaf" -version = "0.3.3" +version = "0.3.4" dependencies = [ "crossterm", "rodio", diff --git a/Cargo.toml b/Cargo.toml index 92d9894..a49bf61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "termleaf" -version = "0.3.3" +version = "0.3.4" edition = "2021" description = "A distraction-free terminal text editor built for focused writing." repository = "https://github.com/andy5090/termleaf" diff --git a/README.md b/README.md index 1f69b9d..f387b3d 100644 --- a/README.md +++ b/README.md @@ -139,10 +139,11 @@ release artifacts. | `F3` | toggle focus mode | | `F4` | toggle the big-font zone | | `F5` | toggle the centered page-width (paper) mode | +| `Shift+F5` | cycle document line spacing through levels 1–3 | | `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` | alternate page-width shortcut where the terminal preserves Alt | +| `Option+L` (macOS) / `Alt+L` (Windows/Linux) | alternate line-spacing shortcut | +| `Option+P` (macOS) / `Alt+P` (Windows/Linux) | alternate page-width shortcut | | `F9` | toggle English / Korean interface guidance | | `F10` | open sound settings (typing / delete / return / key style) | | `F11` | cycle `classic` / `deep` / `soft` typewriter sound | @@ -153,6 +154,12 @@ release artifacts. | `Ctrl+Q` / `Ctrl+C` | quit | | arrows / Home / End / Backspace / Delete / Enter | usual editing | +`Ctrl` means the Control key on every operating system, not macOS Command or +the Windows key. macOS Option is the equivalent of Alt in terminal input; +Termleaf does not bind Command or the Windows key. Depending on macOS keyboard +settings, function-key shortcuts may require `Fn` as well—for example, +`Fn+Shift+F5` for line spacing. + `Enter` plays a typewriter margin bell and carriage-return effect. In the `F10` panel, use `↑`/`↓` to select an option, `Space` or `←`/`→` to change it, and `Enter`/`Esc` to close. diff --git a/src/input/events.rs b/src/input/events.rs index a7e9b36..7982bfc 100644 --- a/src/input/events.rs +++ b/src/input/events.rs @@ -66,6 +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) if key.modifiers.contains(KeyModifiers::SHIFT) => Action::CycleLineSpacing, KeyCode::F(5) => Action::TogglePageWidth, KeyCode::F(6) => Action::ToggleTheme, KeyCode::F(7) => Action::FontDec, @@ -148,6 +149,10 @@ mod tests { map_key(KeyEvent::new(KeyCode::F(5), KeyModifiers::NONE), false), Action::TogglePageWidth ); + assert_eq!( + map_key(KeyEvent::new(KeyCode::F(5), KeyModifiers::SHIFT), false), + Action::CycleLineSpacing + ); assert_eq!( map_key(KeyEvent::new(KeyCode::F(11), KeyModifiers::NONE), false), Action::CycleSoundProfile diff --git a/src/renderer/terminal.rs b/src/renderer/terminal.rs index 5c6c2d6..c40e1ee 100644 --- a/src/renderer/terminal.rs +++ b/src/renderer/terminal.rs @@ -397,7 +397,8 @@ fn draw_help( "", "파일: Ctrl+O 열기 · Ctrl+S 저장 · F12 다른 이름 (.md 기본)", "화면: F3 집중 · F4 큰글자 · F6 테마 · F7/F8 크기 1–5", - "보기: Alt+L 줄간격 1–3 · F5 종이 폭 (Alt+P 보조)", + "보기: F5 종이 폭 · Shift+F5 줄간격 1–3", + "보조: macOS Option+P/L · Windows/Linux Alt+P/L", "소리: F10 설정(타이핑/삭제/엔터) · F11 타자음", "기타: Backspace/Delete 삭제 · F9 English · F1 도움말 · Ctrl+Q 종료", ] @@ -409,7 +410,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 · F5 Page width (Alt+P alternate)", + "Reading: F5 Page width · Shift+F5 Line spacing 1–3", + "Alternates: macOS Option+P/L · Windows/Linux Alt+P/L", "Sound: F10 Settings (typing/delete/return) · F11 Key style", "Other: Backspace/Delete · F9 Korean · F1 Help · Ctrl+Q Quit", ] @@ -943,18 +945,18 @@ 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 소리 ", + " F1 도움 F5 종이폭 ⇧F5 줄간격 F10 소리설정 ^O 열기 ^S 저장 F12 다른이름 ^Q 종료 │ F2 한글 F3 집중 F4 큰글자 F6 테마 F7/8 크기 F9 영어 ", + " F1 도움 F5 종이폭 ⇧F5 줄간격 F10 소리설정 ^O 열기 ^S 저장 ^Q 종료 │ F3 집중 F6 테마 F7/8 크기 ", + " F1 도움 F5 종이 ⇧F5 간격 F10 소리 ^Q 종료 ", + " F5 종이 ⇧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 ", + " F1 Help F5 Page ⇧F5 Line spacing F10 Sound setup ^O Open ^S Save F12 Save-as ^Q Quit │ F2 Korean F3 Focus F4 Big F6 Theme F7/8 Size F9 한국어 ", + " F1 Help F5 Page ⇧F5 Spacing F10 Sound ^O Open ^S Save ^Q Quit │ F3 Focus F6 Theme F7/8 Size ", + " F1 Help F5 Page ⇧F5 Space F10 Sound ^Q Quit ", + " F5 Page ⇧F5 Space F10 Sound ", " F5 Page F10 Sound ", ] }; @@ -1050,7 +1052,7 @@ mod tests { use super::*; #[test] - fn shortcut_guide_adapts_to_terminal_width_without_hiding_sound_settings() { + fn shortcut_guide_adapts_without_hiding_primary_reading_and_sound_settings() { for korean in [false, true] { for width in [24, 40, 80, 180] { let guide = shortcut_guide(korean, width); @@ -1059,14 +1061,13 @@ mod tests { assert!(guide.contains("F10")); if width >= 40 { assert!(guide.contains("F1")); + assert!(guide.contains("⇧F5")); } } } 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] diff --git a/src/ui/layout.rs b/src/ui/layout.rs index 977a019..4d5adcb 100644 --- a/src/ui/layout.rs +++ b/src/ui/layout.rs @@ -53,8 +53,14 @@ impl Layout { } } - let doc_top = big_height; - let doc_height = avail.saturating_sub(big_height).max(1); + // Without the big-font zone, leave one quiet row above the document + // when doing so still preserves at least two editable rows. + let top_padding = u16::from(!cfg.big_font && avail >= 3); + let doc_top = big_height + top_padding; + let doc_height = avail + .saturating_sub(big_height) + .saturating_sub(top_padding) + .max(1); let status_row = if status_visible { Some(rows.saturating_sub(status_reserve)) } else { @@ -112,6 +118,32 @@ mod tests { assert_eq!(focused.doc_height, 4); } + #[test] + fn disabled_big_font_adds_top_padding_without_starving_compact_layouts() { + let mut cfg = Config { + big_font: false, + ..Config::default() + }; + + let normal = Layout::compute(80, 24, &cfg); + assert_eq!(normal.doc_top, 1); + assert_eq!(normal.doc_height, 21); + + cfg.focus_mode = true; + let focused = Layout::compute(80, 24, &cfg); + assert_eq!(focused.doc_top, 1); + assert_eq!(focused.doc_height, 23); + + let compact = Layout::compute(20, 4, &cfg); + assert_eq!(compact.doc_top, 1); + assert_eq!(compact.doc_height, 3); + + cfg.focus_mode = false; + let constrained = Layout::compute(20, 4, &cfg); + assert_eq!(constrained.doc_top, 0); + assert_eq!(constrained.doc_height, 2); + } + #[test] fn big_zone_uses_available_height_without_starving_the_document() { let mut cfg = Config::default();