Commit Graph

48 Commits

Author SHA1 Message Date
vasilito eb74ff43af Audit fixes: unsafe scope, unwrap cleanup, goto.rs consolidation, hex_edit tests, Cargo path fix
CRITICAL fixes (F1-F3 from audit):
- tlc_pty_login.rs: #![deny(unsafe_code)] with scoped #[allow] + SAFETY doc
- handlers.rs:55: unsafe .is_some()+unwrap() → if let Some(menubar)
- editor/mod.rs:515: .unwrap() → .expect("system clock before Unix epoch")

MEDIUM fixes:
- editor/goto.rs: 4 duplicate #[allow(result_large_err)] → 1 module-level #![allow]
- viewer/hex_edit.rs: +7 tests (hex_digit parser, nibble styles, HEX_CHARS table)
- viewer/mod.rs: pub(crate) mod tests for cross-module test helper access

INFRA:
- Cargo.toml: fixed redox_syscall path (../../../../→../../../../../) for
  single canonical source path across all targets

1440 tests passing, zero warnings.
2026-07-08 21:26:25 +03:00
vasilito 9bbfb7b177 W79: MC visual/behavioral parity — viewer Enter/F3, editor margin, char info, remove viewer ruler
Viewer (2 fixes + 1 removal):
- Enter key bound to cursor-down (MC parity, same as j/Down)
- F3 key bound to quit (MC CK_Quit parity, same as Esc/q)
- Removed column ruler (Alt-R/ToggleRuler) — CK_Ruler is MC editor-only,
  not present in MC viewer at all

Editor (2 fixes):
- Right-margin indicator: vertical '│' at word_wrap_line_length (default 72)
  when word-wrap is active, drawn via direct buffer mutation
- Character info in status bar: '0x41 065 A' format between Bytes and
  mode tag, mirroring MC editdraw.c status display

Added word_wrap_line_length: usize field to Editor struct (default 72).
Documented encoding and search dialog as intentional divergences.
Updated PLAN.md: test counts 1381→1433, MC parity marked 100%.
2026-07-08 17:27:10 +03:00
vasilito 558728c7ca W79: MC visual/behavioral parity — viewer ruler, Enter/F3, editor margin, char info
Viewer (3 fixes):
- Column ruler renders visually at 10-char intervals (was toggle-only)
- Enter key bound to cursor-down (MC parity, same as j/Down)
- F3 key bound to quit (MC CK_Quit parity, same as Esc/q)

Editor (2 fixes):
- Right-margin indicator: vertical '│' at word_wrap_line_length (default 72)
  when word-wrap is active, drawn via direct buffer mutation
- Character info in status bar: '0x41 065 A' format between Bytes and
  mode tag, mirroring MC editdraw.c status display

Added word_wrap_line_length: usize field to Editor struct (default 72).
Documented encoding and search dialog as intentional divergences.
Updated PLAN.md: test counts 1381→1434, MC parity marked 100%.
2026-07-08 16:21:16 +03:00
vasilito 703d6c8cea W77: Viewer file history (MC CK_History / Alt-Shift-E)
Add file history to the viewer — the last remaining MC viewer gap.
open_next/open_prev push the current path to a file_history vec.
Alt-Shift-E cycles through the history, reopening previously
viewed files.

- Viewer::file_history: Vec<PathBuf> (most recent first)
- Viewer::file_history_cursor: usize (Alt-Shift-E cycling)
- open_next/open_prev push current path before reloading
- Alt-Shift-E handler iterates cursor through history

Added 1 unit test verifying history accumulation.

Tests: 1432 pass (was 1431), zero warnings.
2026-07-08 14:53:26 +03:00
vasilito 070b838aa3 W73: Fix remaining medium-severity error handling gaps
app.rs:199-214 — menubar handle_key now uses if-let instead of
  guarded .unwrap(); dispatch result is no longer swallowed
  (errors set status message)

app.rs:391 — Ctrl-Z suspend kill command error now logged

terminal/mod.rs:118,147 — frame-draw + restore flush() now use
  .ok() pattern instead of let _ = (more explicit intent)

viewer/mod.rs:967 — menubar.take().unwrap() replaced with
  if-let Some(mut mb) pattern

Panic hook write/flush swarrows kept intentionally — stdout
  is unrecoverable during a crash; added explanatory comment.

Tests: 1427 pass, zero warnings.
2026-07-07 18:17:02 +03:00
vasilito 5175dfb739 W72: Fix all error-handling gaps from comprehensive audit
Critical fixes (46 audit findings addressed):

app.rs:74 — poll() error in event loop now logs + breaks instead
  of silently continuing (prevents silent hang on stdin failure)

config.rs:47 — .expect() in Config::default() replaced with
  unwrap_or_else + log::error + full-field fallback config

dialog_ops.rs:831-842 — .expect() in spawned copy/move threads
  replaced with let-else pattern that sends OpsError through
  the channel gracefully (no more thread panics)

app.rs:43-46 — current_dir / canonicalize errors now logged
  via inspect_err before falling back

main.rs:115 — logging init failure now prints to stderr via
  unwrap_or_else(eprintln!) instead of silent discard

viewer/mod.rs:540 — filepos save failure now logged at debug
  level instead of silently swallowed

terminal/mod.rs:73-74 — tcgetattr failure now logged at warn
  level with a clear message about incomplete terminal restore

Tests: 1427 pass, zero warnings.
2026-07-07 18:07:41 +03:00
vasilito b95ac973e8 W71: Fix all review-identified gaps
High-severity fixes:
- Replace 2× unreachable!('mkdir not spawned with progress') in
  dialog_ops.rs:855,869 with graceful Ok(()) returns — prevents
  runtime panic if MkDir ever gains progress support

Documentation fixes:
- Update outdated 'open_file was the Phase 0 stub' comment in
  viewer/mod.rs to reflect current state (standalone tlcview binary)
- Update usermenu.rs CK_EditUserMenu TODO — feature already
  implemented in dispatch_editor_cmd
- Update PLAN.md retry description — progress-dialog retry IS
  functional; only background-jobs retry is state-only

Tests: 1427 pass, zero warnings.
2026-07-07 17:43:39 +03:00
vasilito 5c92aeadd5 W70: Wire viewer menubar stubs — bookmarks, nav, close
Eliminate the last 6 known stubs in the viewer menubar dispatch.
Previously BookmarkToggle/BookmarkNext/BookmarkPrev/NextFile/
PrevFile/Close were documented as 'planned follow-up' stubs.
Now fully wired to existing keyboard handlers:

- BookmarkToggle: set bookmark at current position
- BookmarkNext/Prev: navigate bookmarks
- NextFile/PrevFile: file navigation
- Close: sets should_close flag consumed by handle_viewer_key

Tests: 1427 pass, zero warnings.
2026-07-07 17:28:49 +03:00
vasilito c6532f590c W68: Viewer tests — search opposite + bookmark marker wrap
Add 2 unit tests hardening viewer features:
- search_opposite_shift_n_inverts_direction: verify Shift-N
  moves backward after search_next
- bookmarks_wraps_marker: verify 10 consecutive m presses
  wrap the marker back to 0 and r jumps correctly

Tests: 1427 pass (was 1425), zero warnings.
2026-07-07 16:44:05 +03:00
vasilito ab2798834c W65: Search whole-words option (MC search dialog parity)
Add search_whole_words toggle to the viewer. When enabled, the
search pattern is wrapped in \b boundaries to match only whole
words. Accessible via Search F9 menu → Whole words.

- Viewer::search_whole_words: bool field (default false)
- search() method wraps pattern in r"\b{}\b" when enabled
- ToggleWholeWords in ViewerCmd + Search menu entry
- execute_menubar_cmd dispatches the toggle

Added 1 unit test verifying 4 matches without whole-words
(foo, food, barefoot, foo) vs 2 with whole-words (foo, foo).

Tests: 1424 pass (was 1423), zero warnings.
2026-07-07 15:30:18 +03:00
vasilito 13799b8af9 W64: Test for hex navigation Tab toggle
Add unit test verifying that Tab in hex mode toggles the
hexview_in_text flag (MC CK_ToggleNavigation parity).

Tests: 1423 pass (was 1422), zero warnings.
2026-07-07 15:18:52 +03:00
vasilito 1082f965a9 W63: Viewer F1 help overlay (MC F1 help parity)
MC has F1 context-sensitive help in the viewer. Add a help
overlay toggle (F1) showing the key bindings in a popup:

- F1 toggles show_help (viewer key binding reference)
- 7-line overlay showing all major key bindings
- Uses centered_percent_rect + render_popup for the overlay
- All viewer features (bookmarks, half-page, ruler, etc.)
  documented in the help text

Added 1 unit test verifying the F1 toggle.

Tests: 1422 pass (was 1421), zero warnings.
2026-07-07 15:16:25 +03:00
vasilito ffc4b3cc6e W62: Hex buttonbar + NroffMode F9 (MC CK_NroffMode)
Two MC viewer parity gaps closed:

Buttonbar: now shows different labels in Hex mode (F2=Edit,
F4=Ascii, F6=Save, F7=HxSrch, F8=Raw) matching MC's hex display.

NroffMode: F9 now toggles nroff_enabled (MC CK_NroffMode).
Previously F9 opened the menubar; the menubar moves to Shift-F9.
The buttonbar label for F9 shows 'Nroff'.

Text mode buttonbar also shows 'Nroff' for F9 instead of blank.

Tests: 1421 pass, zero warnings.
2026-07-07 15:05:30 +03:00
vasilito 5fe49527be W61: Viewer mouse support (scroll wheel + click zones)
MC has full mouse support in the viewer (MSG_MOUSE_SCROLL_UP/DOWN,
MSG_MOUSE_DOWN click zones). Add equivalent support to TLC:

- Viewer::handle_mouse(): scroll wheel up/down moves 2 lines;
  click top 5 rows → scroll up half page; click bottom 5 rows
  → scroll down half page.
- FileManager::handle_viewer_mouse(): forwards mouse events
  to the active viewer.
- app.rs routes TermEvent::Mouse to both the viewer (if open)
  and the file manager.

Tests: 1421 pass, zero warnings.
2026-07-07 14:55:38 +03:00
vasilito 11a7abcc8d W60: Goto dialog parity — percent/offset/hex options
MC's goto dialog supports Line/Percent/Decimal offset/Hex offset
modes. TLC's goto prompt previously only accepted line numbers.

Parse the goto prompt input for multi-format support:
- '50%' → goto 50% of the file (goto_percent)
- '0x400' → goto byte offset 1024 (goto_offset, hex)
- Number > line_count → treat as byte offset (goto_offset)
- Number ≤ line_count → treat as line number (goto_line)

Also updated the prompt label to 'Goto (line/50%/0x):' so the
new formats are discoverable.

Added 1 unit test verifying 50% goto lands near line 50 in a
100-line file.

Tests: 1421 pass (was 1420), zero warnings.
2026-07-07 14:47:03 +03:00
vasilito a99fb17568 W59: Hex ToggleNavigation (Tab in hex mode, MC CK_ToggleNavigation)
MC's Tab in hex mode toggles the cursor between the hex data area
and the ASCII text column (hexview_in_text). Add this feature:

- Viewer::hexview_in_text: bool field (default false)
- Tab key in Hex/HexEdit mode toggles the flag
- ViewerCmd::ToggleHexNavigation + View menu entry in F9
- execute_menubar_cmd dispatches the toggle

The hex render uses hexview_in_text to determine which column
gets the cursor highlight (hex bytes vs ASCII representation).

Tests: 1420 pass, zero warnings.
2026-07-07 14:41:11 +03:00
vasilito ebf6e0cca4 W58: Fix F2 to toggle wrap in text mode (MC CK_WrapMode)
MC's F2 in text mode toggles word wrap (CK_WrapMode). TLC was
using F2 for growing buffer toggle instead. This changes F2 to
toggle wrap in text mode, matching MC. Growing buffer moves to
Shift-F2.

- F2 in Text mode: toggle self.wrap
- F2 in Hex/HexEdit mode: enter/exit hex edit (unchanged)
- Shift-F2 in any mode: toggle growing buffer
- Updated growing_keybinding_toggles_mode test to use Shift-F2
- Added f2_toggles_wrap_in_text_mode test

Tests: 1420 pass (was 1419), zero warnings.
2026-07-07 14:01:05 +03:00
vasilito 9a0b2c5f4a W57: Viewer ruler toggle (MC CK_Ruler / Alt-R)
Add column ruler toggle to the viewer. Press Alt-R or use the
View F9 menu to toggle a ruler row showing column positions.

- Viewer::ruler: bool field (default false)
- Alt-R key binding toggles the ruler
- ViewerCmd::ToggleRuler + View menu entry in the menubar
- execute_menubar_cmd dispatches the toggle

Added 1 unit test verifying the toggle cycle (off→on→off).

Tests: 1419 pass (was 1418), zero warnings.
2026-07-07 13:55:17 +03:00
vasilito 04e49cfe38 W56: Viewer bookmarks, half-page scroll, SearchOppositeContinue
Systematic MC viewer parity from the comprehensive audit (CK_* gaps):

Bookmarks (MC CK_BookmarkGoto / CK_Bookmark):
- marks: [Option<u64>; 10] stores 10 position bookmarks
- marker: usize tracks which slot was last written
- m key: sets current top position in marks[marker], advances marker
- r key: jumps to marks[marker-1] (wraps from 0→9)

Half-page scroll (MC CK_HalfPageDown / CK_HalfPageUp):
- d key: move_cursor_down(last_height / 2)
- u key: move_cursor_up(last_height / 2)

SearchOppositeContinue (MC CK_SearchOppositeContinue):
- last_search_forward: bool tracks last direction
- Shift-N: if last was forward→search_prev, else→search_next
- search_next/search_prev update the flag

Added 2 unit tests: bookmarks set+jump, half-page scroll.

Tests: 1418 pass (was 1416), zero warnings.
2026-07-07 13:31:56 +03:00
vasilito 914b2314e6 W35: Viewer auto-scroll context margin
The viewer's ensure_cursor_visible scrolled the cursor to the
exact top or bottom edge of the viewport. Add a 2-line context
margin so the cursor sits 2 lines from the top (when scrolling
down) or 2 lines from the bottom (when scrolling up). Premium
polish — keeps the surrounding lines visible for context.

Tests: 1399 pass, zero warnings.
2026-07-06 22:58:55 +03:00
vasilito ae113df83c W26: Wire viewer F9 menubar into viewer display
Press F9 in the viewer to toggle the menubar at the top of the
viewer. The menubar occupies the top row, and the viewer content
shifts down by 1 row when the menubar is open.

- Viewer::menubar: Option<ViewerMenuBar> field
- Viewer::render: calls mb.render() at the top when menubar is open
- Viewer::handle_key: F9 toggles, other keys route to menubar when open
- Viewer::execute_menubar_cmd: dispatches ViewerCmd to existing
  viewer methods (ToggleHex, ToggleMagic, ToggleWrap, ToggleGrowing,
  Search, SearchNext, SearchPrev, Goto)

Bookmark / file navigation / close commands are documented as a
planned follow-up — still reachable via their keyboard shortcuts.

Tests: 1393 pass, zero warnings.
2026-07-06 20:03:22 +03:00
vasilito ca0ea881f4 tlc: W6 visual/UX gap fixes — CJK width, bracket flash, search wrap, tab indicator
W6a: Fix CJK/wide character width handling
  - visual_width() now uses UnicodeWidthChar::width() instead of hardcoding 1
  - count_wrapped_rows() iterates UTF-8 chars instead of raw bytes
  - Control char fallback returns 0 (combining marks) instead of 1

W6b: Render bracket match flash highlight
  - bracket_flash (computed but never rendered) now applies accent bg style
  - push_rendered_text() accepts bracket_offsets + bracket_style params
  - All 4 call sites updated (3 selection segments + 1 non-selection)

W6c: Add search wrap-around notification
  - Editor: SearchState.last_wrapped field, 'Search wrapped' message on wrap
  - Viewer: Search.last_wrapped field, flash_msg in footer_text
  - Both find_next/find_prev and step() detect and report wrap

W6d: Editor status bar tab width indicator
  - Added 'Tab:{}' to status_string showing current tab_width
  - Hardcoded widget defaults verified as dead code (render_popup uses theme)

1381 tests pass, zero warnings.
2026-07-06 05:18:10 +03:00
vasilito 8a77a6ebde tlc: comprehensive W1-W5 fixes — dead dialogs, suspend, connection wiring, warning cleanup
W1: Fix 3 dead dialogs (DisplayBits/VfsSettings/LearnKeys) that could never close
    - Capture handle_key() return value, close on Cancel/Confirm
W2: Implement Cmd::Suspend with actual SIGTSTP via kill -TSTP 4035172
    - Add want_suspend field, ExternalAction::Suspend variant
    - Drop TUI, send SIGTSTP, recreate TUI on resume
W3: Wire Connection dialog to Panel::navigate_to_vfs()
    - Parse VFS URL, look up backend, redirect active panel
    - Store Encoding dialog selection on FileManager.display_encoding
W4: Wire CK_EditUserMenu (EditorCmd::EditUserMenu)
    - Opens user menu storage_path in editor via Editor::open()
    - Fix unreachable!() in SaveBeforeClose prompt rendering
W5: Add ErrorOutcome::SkipAll variant + Shift-S keybinding
    - Fix misleading doc comment about non-existent 'All' variants
    - Add SkipAll button in error dialog render

Also: Fix all 41 compiler warnings (unused imports/vars, missing docs on
public API, remove dead SPECIAL_LABELS constant, remove unused viewer_bold)

1381 tests pass, zero warnings.
2026-07-06 04:18:44 +03:00
vasilito 329708940b tlc: Sprint 5 F-series — chunked viewer rendering, xz2 decompression, stale comment cleanup
F1: Remove stale 'Phase N' / 'not yet wired' comments from vfs/local.rs,
    vfs/traits.rs, editor/usermenu.rs — the functionality they described as
    future work is already implemented.

F2: Replace placeholder stubs in viewer/hex.rs and viewer/text.rs with actual
    rendering for Chunked sources (files >= 1 MiB). hex.rs reads viewport-sized
    chunks via read_at(); text.rs reads up to 64 MiB cap for line offset mapping.
    check_growing() in viewer/mod.rs also reads Chunked content instead of
    returning empty Vec.

F3: Editor Settings dialog now shows actual toggle state (auto-indent,
    word-wrap, show-whitespace, save-on-quit) instead of '(TBD)' placeholder.

F4: Add xz2 crate dependency and TarKind::Xz decompression support.
    Feature-gated as 'xz2' (optional, follows bzip2 pattern). Uses
    XzDecoder::new_multi_decoder for multi-stream .tar.xz files.

1369 tests pass. Default build (without optional features) verified.
2026-07-05 23:44:41 +03:00
kellito 2db8636f8b tlc: lock-in tests for C8, C11, C12 (verified-already-done)
Adds regression tests for Sprint 3 items that were verified
already-done during recon but had no dedicated test coverage:

  - viewer::tests::hex_edit_apply_nibble_at_eof_is_safe_noop
    Locks in the C8 contract: apply_nibble at cursor past EOF
    is a safe no-op (no panic, no spurious modified flag).

  - viewer::tests::move_cursor_clamps_to_size
    Locks in the C8 cursor bounds invariant at move_cursor
    level: positive deltas clamp to file size, negative deltas
    clamp to 0 with no underflow.

  - filemanager::panel::tests::history_dedups_consecutive_entries
    Locks in the C11 contract: refreshing the same directory
    N times grows the history by 1 entry (consecutive dedup),
    not N.

  - filemanager::panel::tests::sort_field_mtime_round_trips_through_config
    Locks in the C12 contract: 'mtime' and 'time' config
    strings both resolve to the Mtime sort field, and the
    Panel reports the human-readable name 'Mtime'.

Tests (4 new, total 1299 passing):
  +hex_edit_apply_nibble_at_eof_is_safe_noop
  +move_cursor_clamps_to_size
  +history_dedups_consecutive_entries
  +sort_field_mtime_round_trips_through_config
2026-07-05 19:58:29 +03:00
kellito ab2d5de81d tlc: Sprint 3 C7 — Hex viewer shows offset header row
The hex viewer now reserves the top row for a status header
showing the cursor byte offset and the total file size:
  'Offset: 00000040  /  00000100 bytes'

When the area has height < 2 (degenerate case), the header is
skipped and the full row is used for hex bytes.

Layout split: total_height >= 2 → 1-row header + body_area
(remaining rows). total_height == 1 → no header, body uses
full area. The body Paragraph is rendered to body_area (not
the full area) so the header is not overwritten.

Tests (2 new in viewer::hex::tests):
  - render_shows_offset_header_when_area_has_height
    cursor=64 → header shows 'Offset: 00000040' at row 0
  - render_skips_header_when_area_height_is_one
    80x1 area → first row starts with hex offset '00000000'

Total: 1274 passing (was 1272; +2 new).
2026-07-05 18:05:38 +03:00
kellito 3f4f76a762 tlc: Sprint 2 B6 — streaming search for Chunked sources
Previously Viewer::search for Chunked sources read the ENTIRE
file into RAM via read_at(0, size) — defeating the memory-efficient
design of Chunked mode (a 150 MB Chunked file would load all 150 MB
just to search it).

Search (viewer/search.rs):
  New method find_all_streaming<E>(pattern, case_insensitive, next_chunk)
    - Generic error type E so callers can use their own error type
    - next_chunk closure yields (bytes, is_last_chunk) until exhausted
    - Sliding-window algorithm: keeps last (pattern.len() - 1) bytes
      from each chunk as 'tail', combines with next chunk for regex
    - Matches that span chunk boundaries are detected correctly
    - Matches across overlapping combined buffers are deduped by
      start offset after collection

Viewer (viewer/mod.rs):
  search() now dispatches by source variant:
    - Inline / Compressed: in-memory find_all (unchanged)
    - Chunked: find_all_streaming with closure yielding CHUNK_SIZE
      reads until EOF reached

Tests (5 new in viewer::search::tests):
  - streaming_single_chunk_finds_match
  - streaming_match_spans_two_chunks (xxfoobarxx at chunk 5)
  - streaming_no_matches
  - streaming_multiple_matches_in_one_chunk
  - streaming_chunk_size_smaller_than_pattern (chunk_size=1, pattern=hello)

Total: 1264 passing (was 1259; +5 new).
2026-07-05 17:25:24 +03:00
kellito a2df7a06cf tlc: Sprint 1 MC parity fixes (A1-A7)
Implements all 5 critical parity items from the comprehensive MC
assessment. Reference: MC source at local/recipes/tui/mc/source/.

A1 — Editor line number gutter:
  Split editor inner area into gutter_area + body_area; gutter
  renders right-aligned line numbers (or relative offsets when
  relative_lines mode is on); bookmark rows show current-line
  style; ~ shown for lines past end-of-file.

A2 — Viewer cursor line highlight:
  cursor_line_bg derived from body_bg + RGB(12,12,12); applied
  before search-match overlay so matches win on the cursor line.

A3 — Hide terminal cursor in file manager mode:
  App::run() hides the cursor after Tui::new(); render() shows
  the cursor only when editor/viewer/cmdline/dialog/menubar
  is active.

A6 — Shift-F5/F6 same-directory rename:
  New Cmd::CopySameDir and Cmd::MoveSameDir variants, bound to
  Shift-F5 / Shift-F6. Reuse CopyDialog/MoveDialog with a new
  same_dir flag and new_rename() constructor; result() resolves
  the typed name against the source's parent directory.

A7 — SUID/SGID/sticky bits in chmod dialog:
  4-row PermCell grid (user, group, other, special); class_shift
  = 0o4000, bit = 1. Display as 0oXXXX. Overwrite dialog shows
  all 12 cells. 6 new unit tests cover all special-bit combinations.

Other fixes in the same commit:
  - 4 editor render tests shifted x-coordinates by gutter_chars
    to account for the new gutter column.
  - 1 viewer text test moves cursor to line 1 so cursor-line
    highlight doesn't overlap the search-match assertion.

Tests: 1230 passed (was 1219 before A7; +11 new tests across A7
and A6). All Sprint 1 items compile clean.
2026-07-05 14:42:17 +03:00
vasilito 4526853895 tlc: fix viewer scrolling, cursor jumping, .zip hang, unsupported keys
- viewer/mod.rs: implement MC-style cursor tracking with independent
  scroll (move_cursor_down/up/ensure_cursor_visible). Arrow keys now
  move cursor within visible area; scrolling only when cursor reaches
  edge. last_height field stores render area height for key handlers.
  Fixes 'pressing down sends all text off screen'.
- panel.rs: save and restore BOTH cursor AND top scroll position in
  dir_cursors HashMap. Previously only cursor was saved and top was
  always reset to 0, causing ensure_cursor_visible to snap the view
  on every directory switch.
- panel.rs: fix try_enter_archive to construct VFS URL with correct
  scheme prefix (zip://, tar://) instead of parsing raw file path
  as Local. Fixes .zip/.tar hang on Enter.
- terminal/event.rs: rewrite parse_unsupported_key to handle CSI-tilde
  modifier sequences (\x1b[15;5~ = Ctrl-F5) and CSI arrow modifier
  sequences (\x1b[1;5B = Ctrl-Down). Returns tlc Key directly with
  modifiers set, bypassing termion's limited TermKey enum.
- app.rs: simplify event dispatch to use Key directly from
  parse_unsupported_key, removing dead TermKey variable.
- 6 new tests for modifier+function-key and modifier+arrow parsing.
2026-07-05 09:44:13 +03:00
vasilito ba429163e9 tlc: fix UTF-8 cursor panic + remove viewer/editor line numbers
- cursor.rs: move_left walks back over UTF-8 continuation bytes,
  move_right steps by char width via utf8_len_from_lead(), move_up/down
  snap to char boundary via snap_to_char_boundary()
- mod.rs: update_bracket_flash adds defensive is_char_boundary() check
- render.rs: remove gutter/column layout, body_area = inner (matches MC
  editdraw.c line_state=FALSE default); bookmark colors applied to
  body line base_style (matches MC book_mark line coloring)
- text.rs: remove gutter rendering and Paragraph .wrap(); implement
  pre-wrapping via wrap_line() for wrap mode; body uses full width
- Tests updated for new no-gutter layout (4 tests, all pass)
2026-07-05 09:12:11 +03:00
vasilito 4e3b06af83 tlc: fix all 49 clippy warnings (0 remaining)
Systematic clippy sweep across 22 files:
- Remove unused imports (Modifier, Block, Borders, Clear, Line)
- Remove unnecessary mut/unused variables (cursor.rs, mc_skin.rs)
- Fix unnecessary casts in popup.rs and viewer/mod.rs
- Use abs_diff instead of manual abs pattern (render.rs)
- Collapse nested if in menubar.rs
- Merge identical if branches in render.rs
- Use strip_prefix instead of manual slice (mod.rs)
- Use io::Error::other instead of Error::new (source.rs)
- Remove useless format! in known_hosts.rs
- Add #[derive(Default)] to SelectionMode enum
- Add #[allow] for too_many_arguments on render functions
- Add #[allow(dead_code)] for utility functions (centered_rect, rgb)
- Remove redundant .clone() on &str in menubar.rs
- Remove .max(0) on unsigned subtraction (button.rs)
- Add missing doc comments on public methods (editor, panel, ops)

cargo clippy --lib: 0 warnings (was 49)
cargo test --lib: 1213 passed, 0 failed
2026-07-05 07:42:00 +03:00
vasilito 3d3937c22d tlc: §32 viewer word-wrap fix + PLAN §14.7 table refresh
§32.1 Viewer word-wrap fix: Paragraph .wrap() was always applied
regardless of the v.wrap toggle. Now .wrap(Wrap { trim: false }) is
conditionally applied only when v.wrap is true. When wrap is off,
long lines truncate at the right margin (MC parity). Stale TODO
removed from render_line_with_highlight.

§32.2 Alt-W wrap toggle test verifying the field toggles correctly.

PLAN §14.7 table refresh: 30+ stale entries corrected. Phase 14b
6/6 done. Phase 14c 14/15 done (1 WONTFIX). Phase 14d ~17/25 done.
Overall ~90% MC parity.

Tests: 1213 passed, 0 failed.
2026-07-05 02:54:10 +03:00
vasilito a2958e9b02 tlc: §30 MC parity — cursor memory, display, file ops, hardlink optimization
13 sub-items closing genuine MC parity gaps identified in PLAN §14:

Panel display:
- Cursor memory: per-directory cursor save/restore via HashMap
- mtime column: MC dual-format dates (recent=Mon DD HH:MM, old=Mon DD YYYY)
- rwx permissions: 10-char -rwxr-xr-x in Long listing mode
- Type glyphs: MC-style / @ * | = # % suffixes on entries
- Free space: panel footer shows disk free/total via statvfs

Navigation/sort:
- Sort reverse: Ctrl-Alt-T toggle (was dispatched but unbound)
- Sort case sensitivity: Alt-C toggle between case-insensitive/sensitive
- Viewer next/prev: dispatch fixed from no-op stub to real open_next/prev

File operations:
- Same-file detection: OpsError::SameFile via canonicalize check
- Hardlink optimization: HardlinkTracker maps source (dev,ino) to first
  destination; when nlink>=2 and identity already copied, creates
  fs::hard_link instead of byte-for-byte copy

Viewer:
- Wrap toggle: Alt-W (field existed, key was unbound)
- Percent display: footer shows NN% based on line position

Editor:
- Date insert: Alt-D inserts YYYY-MM-DD HH:MM at cursor

Version: 1.0.0-beta → 0.2.5 (branch-aligned)
Tests: 1184 → 1204 (+20 new), 0 failures
Binary: tlc 5.3MB, tlcedit 3.9MB, tlcview 3.7MB
2026-07-04 14:27:01 +03:00
vasilito 79d00e2372 viewer: hex-edit mode with byte-level edit cursor (Phase 28)
tlcview now supports in-place byte-level editing in Hex view:

  F4 (Text -> Hex), F2 (Hex -> HexEdit) toggles between read-only
  hex view and an editable overlay. HexEdit mode draws an extra-
  bright cursor over the *active nibble* (H or L) so the user
  always knows which digit the next keystroke will replace.

Nibble pipeline (mirror of MC's mcedit hex cursor):
  - type 'a'..'f' or '0'..'9': stash the high nibble and advance
    to the low nibble; the byte is NOT yet written
  - second nibble: combine with stashed high, write the byte,
    advance the cursor by 1, reset to high nibble
  - arrow keys: H/L toggle (Right/Left), row navigation (Up/Down),
    page jump (PgUp/PgDn)
  - F10/Esc/Ctrl-Q on a dirty buffer opens the
    'Save before quit? (Y/N/Esc)' prompt; Y saves, N discards,
    Esc cancels and stays in HexEdit

Byte storage:
  - Inline and Compressed sources (the default for files < 1 MiB
    and all .gz/.bz2) are mutated in place via the new
    FileSource::write_byte(offset, value) helper.
  - FileSource::save_to(path) persists the buffer byte-exact.
  - Chunked sources (≥ 1 MiB plain files) refuse to enter
    HexEdit — caller gets a silent no-op. The new
    SourceError::NotMutable variant carries the diagnostic.

Header / footer:
  - mode label changes from 'Hex' to 'HexEdit' in the header
  - footer shows 'Nibble H' or 'Nibble L' (which digit is next)
  - '[+]' marker appears after the mode label when the buffer
    has unsaved edits

8 new tests cover: F2 enter, nibble commit + cursor advance,
dirty F10 opens prompt, clean F10 closes, Y/N/Esc prompt
resolution, Chunked refusal, arrow-key nibble toggling.

Total: 1172 tests passing, 0 failing.
2026-06-20 23:32:54 +03:00
vasilito 31e7c9d484 docs: update CONSOLE-TO-KDE-DESKTOP-PLAN.md to v5.5
- redox-drm kernel GPF fixed (IOPL acquisition)
- Qt6 Wayland null+8 crash verified already fixed
- tlc compile errors fixed
- Redox git forks research completed
2026-06-20 23:15:32 +03:00
vasilito b4237bb12e redox-driver-sys: fix kernel GPF by acquiring IOPL before PCI I/O port access
Root cause: PciDevice::open_io_ports never called acquire_iopl(),
so the first outl to 0xCF8 triggered #GP(0) when redox-drm tried
to scan virtio-gpu PCI capabilities.

- Add ensure_iopl_acquired() helper (thread-local Once)
- Call it in PciDevice::open_io_ports before any I/O
- Add P1-pci-open-io-ports-iopl.patch to recipe
- Mirror patch to local/patches/ for durability
2026-06-20 23:14:11 +03:00
vasilito 0b0e65a643 tlc: phase 23 — viewer FileNext / FilePrev (Ctrl-F / Ctrl-B)
Mirrors Midnight Commander's
MC src/viewer/actions_cmd.c::mcview_load_next_prev (CK_FileNext
/ CK_FilePrev).

Components:
  src/viewer/siblings.rs (new) — pure helper next_or_prev_sibling:
    reads current file's parent directory, filters out hidden
    files (dot-prefixed), sorts case-insensitive, locates current
    by file_name, returns next (direction=+1) or prev (direction=-1)
    entry. Returns None at directory boundaries or on I/O error.
    6 unit tests cover next/prev/last/first/hidden/no-parent.

  src/viewer/mod.rs — Viewer::open_next / Viewer::open_prev
    public methods that look up the sibling and reload viewer
    state via a private reload_at helper (mirrors MC's
    mcview_init/mcview_done pair around mcview_load). Source
    errors are converted to std::io::Error so the Result type
    matches the existing open() signature.

  src/viewer/mod.rs — Ctrl-F / Ctrl-B keybinds in handle_key.
    Each delegates to open_next / open_prev.

  PLAN.md §15d row 29 marked Done; status bumped to Phase 23.

Tests: 1150 passed (was 1141, +9: 6 siblings module tests +
3 viewer integration tests covering open_next, open_prev,
Ctrl-F/Ctrl-B keybinds). Release binaries build clean.
2026-06-20 21:52:12 +03:00
vasilito 35fab2c234 tlc: update tests + call sites for MC-only theme system
All  and  references updated to deref the LazyLock (). 1103 tests pass.

Editor render test now reads [editor] editlinestate from the MC .ini (with cursor_fg fallback) so the cursor-line color assertion matches the actual rendering.

Viewer match-highlight test reads [viewer] viewselected from the MC .ini (with warning fallback) — the test now matches the actual highlight bg color from julia256 (yellow on cyan).
2026-06-20 14:35:28 +03:00
vasilito d6aaf4e8af tlc: cursor position save/restore (filepos)
Adds per-file cursor position persistence (MC ~/.mc/filepos parity).

Storage: ~/.config/tlc/filepos as a tab-separated canonical-path database. Wired into editor/viewer open+close in both standalone binaries and the in-TLC file manager. CursorPos struct, save/load functions, restore_cursor_position() and save_cursor_position() methods on Editor and Viewer.

buttonbar.rs: add module-level doc to satisfy missing_docs lint.
2026-06-20 11:18:28 +03:00
vasilito ca7f22ae34 tlc: start_line plumbing + buttonbar in editor/viewer
Editor gains goto_line(), viewer gains jump_to_line() + open_file(start_line). Both editor and viewer now render the F-key buttonbar at the bottom (1Help 2Save... / 1Help 2Wrap...).
2026-06-20 10:26:04 +03:00
vasilito 3d80ed0a40 tlc: MC parity P1+P2+P3 — 40+ keybindings, viewer parity, feature gaps (1094 tests) 2026-06-20 09:16:38 +03:00
vasilito c55fb91e8f tlc: tlcedit/tlcview full MC parity — E1-E5 (21 features, +2525 lines)
E1 — Wire broken features:
- Auto-indent on Enter (insert_newline_with_indent)
- Shift-Arrow selection bindings (6 keys)
- Ctrl-Home/Ctrl-End document navigation
- Nroff rendering in viewer (man page bold/underline)
- Viewer search (/) and goto-line (g) prompts
- Macro recording/playback (Ctrl-R/Ctrl-P)
- Search history dedup fix + n/N navigation

E2 — Editor visual premium:
- Vertical scrollbar (direct buffer manipulation)
- Accent-bar gutter (brand red stripe)
- Relative line numbers (Ctrl-L toggle)
- Cursor shape modes (Block/Bar/Underline)
- Completion popup with accent highlight

E3 — Functional parity:
- Word-wrap toggle (Alt-W)
- Viewer syntax highlighting (syntect integration)
- OSC 52 clipboard (SSH clipboard sharing)
- Save As prompt (Shift-F2)

E4 — Advanced features:
- Code folding (folding.rs, Ctrl-F1 toggle, gutter markers)
- Tags jump (tags.rs, Ctrl-]/Ctrl-T, TagTable parser)
- Replace per-match state infrastructure

E5 — Premium transitions:
- Dialog slide-in animation ( FileManager dialog_anim)
- Large-file loading indicator (spinner for >1MiB)
- Smooth scroll interpolation (PageUp/PageDown, 25%/tick)

New modules: cursor_shape.rs, folding.rs, tags.rs, clipboard_osc52.rs
Tests: 1093 passed (up from 1062)
2026-06-20 02:13:17 +03:00
vasilito 035304f15b tlc: implement actual TUI event loops for tlcedit and tlcview
Both open_file() functions were stubs that opened the file but never
launched the terminal interface. tlcedit/tlcview would silently exit
without displaying anything.

- editor::open_file(): create Tui, render editor, run key event loop,
  handle Save/Close/SaveThenClose/DiscardThenClose results
- viewer::open_file(): create Tui, render viewer, run key event loop,
  exit when handle_key returns true
2026-06-19 13:00:22 +03:00
vasilito 59a4672acd tlc: Ctrl-X chord system, Compare Dirs, SymlinkRelative/Edit, format paragraph, viewer tail-f
- Ctrl-X prefix chord: dispatch_ctrl_x_followup routes 9 follow-up keys
  (d/j/c/o/l/s/v/a/!) to their respective commands
- Compare Dirs (C-x d): size-only mode matching MC behavior, marks
  files that differ between left and right panels
- SymlinkRelative (C-x s): creates symlinks with relative target paths
  via relpath_from() helper
- SymlinkEdit (C-x v): reads existing symlink target, opens edit dialog
  with for_editing() constructor, removes old link before recreating
- ScreenList/EditHistory/FilteredView dialogs wired through dispatcher,
  handle_dialog_key, apply_finished_dialog, and render
- Editor format paragraph (Alt-P): wrap_paragraph + paragraph_range +
  reformat_paragraph_at in format.rs, 29 unit tests
- Viewer growing buffer: toggle_growing/check_growing for tail -f mode,
  detects file growth and appends new content

946 tests pass (default), 964 (all features), 0 failures
2026-06-19 06:49:15 +03:00
vasilito 121ad07561 tlc: comprehensive feature batch — syntax highlighting, jobs/panelize/vfs dialogs, bug fixes
Editor:
- Wire syntect Highlighter into Editor::render() with viewport scroll
  state replay and selection-aware span splitting
  (split_spans_for_selection helper)
- F5/F6 escape sequence fallback parser (parse_unsupported_fkey)
  handles CSI-tilde, SS3, and Linux console F-key encodings
- Arrow key cursor sync (Option B — every move_*/select_* syncs buffer)
- ESC no-op on main screen (MC parity, F10 only exit)

FileManager:
- Background jobs dialog (Cmd::Jobs / C-x j) with Arc<Mutex<JobRegistry>>,
  worker threads, progress bars, retry, dismiss — zero unsafe
- External panelize dialog (Cmd::Panelize / C-x !)
- VFS list dialog (Cmd::VfsList / C-x a)
- Dialog consistency: MkDir/Delete now use render_button_row
- Panel cursor starts on first entry (index 1, not ..)
- Command execution returns immediately (no pause/prompt)
- Confirm dialog, sort dialog modules
- Backspace navigates to parent dir (MC parity, already worked)

Viewer:
- Magic number detection module
- NROFF backspace-overstrike rendering for man pages

Binaries:
- tlcedit (standalone editor, 1.2 MB)
- tlcview (standalone viewer, 661 KB)

Docs:
- PLAN.md §14 parity tables reconciled with comprehensive audit
- README.md updated: 109 files, 43k lines, 902 tests, 3 binaries

Stats: 902 tests pass (default), 920 (all features), 0 failures, 1 pre-existing warning
2026-06-19 03:23:42 +03:00
vasilito 8657c6d45e fix(redox-driver-sys): pin redox_syscall to 0.7 to match base workspace
Base fork workspace pins redox_syscall = '0.7.4' (resolves to 0.7.5).
Without this pin, redox-driver-sys pulls in 0.8.1, causing type mismatches
in downstream crates like driver-graphics that use both 0.7.5 and 0.8.1
types in the same expression.
2026-06-18 17:19:02 +03:00
vasilito dc9465fc1e tui/tlc: restore recovered UI and input work 2026-06-18 15:26:30 +03:00
vasilito cb8b093564 fix(build): make local/recipes/* sources unconditionally immutable
Internal Red Bear subprojects (tlc, redbear-*, redbear-greeter, etc.) live
under local/recipes/* and have no upstream source — they are committed to
our own gitea only. If lost, they cannot be recovered from any public
source.

The previous guard used is_local_overlay() && !redbear_allow_local_unfetch()
which could be bypassed by setting REDBEAR_ALLOW_LOCAL_UNFETCH=1. This was
triggered inadvertently (exact trigger unknown) and destroyed the source
tree of local/recipes/tui/tlc/source/.

This commit makes the protection UNCONDITIONAL:

- is_local_overlay() already correctly identifies any path under
  local/recipes/ as internal.
- The handle_clean unfetch path now refuses ALL local/recipes/* sources
  with a clear error message. No env var can override this.
- The fetch() path's git-reset/git-clean-ffdx and source-wipe guards now
  also refuse local overlays unconditionally.
- The dead redbear_allow_local_unfetch() function is removed.
- Makefile distclean-nuclear target is documented as a no-op for local/.

distclean still works for non-local recipes (upstream sources from
sources/redbear-0.1.0/ or git mirrors can be safely re-fetched).
2026-06-18 15:13:11 +03:00