rtfm-filemanager 8.5.0 → 8.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/bin/rtfm +132 -4
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1a3688f47ab7f42eaf8488abfd1035d3ffa36fba99368ab2dc1846dda3119415
|
|
4
|
+
data.tar.gz: 2bf042038eee3c18c764c136d87848f15ff623389ff08625c1f5a6dd2564abba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 89fec0164f4abec21cdcca454f77db2293dd4bed441f9edb26cb80148a7ffa0278b1af35c2e2d08eee19786ce1ff4fcc42afa128b6cd4349d71cf99695cca2d8
|
|
7
|
+
data.tar.gz: 827d9f52fb1cd639740b7f0d515ac076153f9bdde3ba4742f8d5bf71b1d9cc56df25029c25bd4e342a011707ee31ceb2c38edcfadebd75552bfbcede929edbd8
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to RTFM will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [8.7.2] - 2026-05-19
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **`$` script-evaluator clears right pane first** - Short output (or `(no output)`) used to leave residual lines from the prior directory preview underneath. Now calls `@pR.clear` before painting the new content
|
|
12
|
+
|
|
13
|
+
## [8.7.1] - 2026-05-19
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- **`$` script-evaluator output now sticks** - The right pane was getting overwritten by the directory preview after the script returned. Mirrors `command_mode`'s pattern: explicit `@pR.full_refresh` + `@pR.update = false` so the script's output remains visible until the user navigates
|
|
17
|
+
- **Directive trace footer** - Stderr directives (`cd:`, `select:`, `status:`) now produce a small `── directives applied ──` footer at the bottom of the output so it's clear *something* happened even when stdout is empty. Previously a pure-directive script just showed `(no output)`
|
|
18
|
+
|
|
19
|
+
## [8.7.0] - 2026-05-19
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- **Script evaluator (`$` key)** - Run any shell command (any language) with RTFM context exposed as environment variables. Output shows in the right pane. Variables: `RTFM_SELECTED` (full path), `RTFM_DIR` (cwd), `RTFM_TAGGED` (newline-separated tagged paths), `RTFM_INDEX` (0-based selection), `RTFM_COUNT` (file count), `RTFM_CONTEXT` (JSON object with all of the above). Scripts can control RTFM by emitting stderr directives one per line: `cd:/path` changes directory, `select:NAME` moves cursor onto NAME, `status:MSG` shows MSG in the bottom pane. The Ruby debug mode (`@`) is unchanged. Makes RTFM scriptable from Python, Bash, Perl, jq, anything — without writing Ruby plugins
|
|
23
|
+
|
|
24
|
+
## [8.6.0] - 2026-05-19
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **`Terminal=true` in `.desktop` files honored** - When opening a file, if the resolved `.desktop` file declares `Terminal=true`, RTFM treats the program as interactive even when it isn't in `@interactive`. Newly installed TUI tools now "just work" without editing config. The `@interactive` whitelist still wins for programs whose `.desktop` files lie about being terminal apps
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- **LS_COLORS fallback via `dircolors -b`** - When `$LS_COLORS` is unset or empty (cron launches, minimal login envs, some macOS setups), RTFM now seeds it from `dircolors -b` at startup so `ls --color` still emits ANSI. Directory listings stay colored in environments that don't export LS_COLORS
|
|
31
|
+
|
|
8
32
|
## [8.5.0] - 2026-05-19
|
|
9
33
|
|
|
10
34
|
### Added
|
data/bin/rtfm
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
# get a great understanding of the code itself by simply sending
|
|
19
19
|
# or pasting this whole file into you favorite AI for coding with
|
|
20
20
|
# a prompt like this: "Help me understand every part of this code".
|
|
21
|
-
@version = '8.
|
|
21
|
+
@version = '8.7.2' # $ script-eval clears right pane before writing output
|
|
22
22
|
|
|
23
23
|
# SAVE & STORE TERMINAL {{{1
|
|
24
24
|
ORIG_STTY = `stty -g`.chomp
|
|
@@ -59,6 +59,18 @@ rescue LoadError
|
|
|
59
59
|
# bootsnap not installed — proceed without compile cache
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# LS_COLORS FALLBACK {{{1
|
|
63
|
+
# RTFM relies on `ls --color` ANSI output for left/right pane colors, and
|
|
64
|
+
# `ls` itself reads $LS_COLORS. If the user's shell never exports it (cron
|
|
65
|
+
# launches, minimal login environments, some macOS setups) the listing
|
|
66
|
+
# comes back colorless. Seed it from `dircolors -b` when available.
|
|
67
|
+
if (ENV['LS_COLORS'].nil? || ENV['LS_COLORS'].empty?) && !`which dircolors 2>/dev/null`.strip.empty?
|
|
68
|
+
dc = `dircolors -b 2>/dev/null`
|
|
69
|
+
if (m = dc.match(/LS_COLORS='([^']*)'/))
|
|
70
|
+
ENV['LS_COLORS'] = m[1]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
62
74
|
# ENCODING {{{1
|
|
63
75
|
# encoding: utf-8
|
|
64
76
|
|
|
@@ -341,6 +353,20 @@ CONFIG_FILE = File.join(RTFM_HOME, 'conf')
|
|
|
341
353
|
RUBY DEBUG MODE
|
|
342
354
|
@ = Enter Ruby mode to execute any Ruby command (ENTER to execute, ESC to escape)
|
|
343
355
|
|
|
356
|
+
SCRIPT EVALUATOR
|
|
357
|
+
$ = Run any shell command (any language) with RTFM context as env vars.
|
|
358
|
+
Output is shown in the right pane.
|
|
359
|
+
RTFM_SELECTED Full path of selected item
|
|
360
|
+
RTFM_DIR Current working directory
|
|
361
|
+
RTFM_TAGGED Newline-separated tagged paths
|
|
362
|
+
RTFM_INDEX Selected index (0-based)
|
|
363
|
+
RTFM_COUNT Number of files in listing
|
|
364
|
+
RTFM_CONTEXT JSON object with all of the above
|
|
365
|
+
Scripts can control RTFM via stderr directives, one per line:
|
|
366
|
+
cd:/path Change to /path
|
|
367
|
+
select:NAME Place cursor on NAME in current directory
|
|
368
|
+
status:MSG Show MSG in the bottom pane
|
|
369
|
+
|
|
344
370
|
COPYRIGHT: Geir Isene, 2025+. No rights reserved. See http://isene.com for more.
|
|
345
371
|
HELPTEXT
|
|
346
372
|
|
|
@@ -1166,7 +1192,10 @@ KEYMAP = { # {{{2
|
|
|
1166
1192
|
'e' => :show_file_properties,
|
|
1167
1193
|
|
|
1168
1194
|
# RUBY MODE {{{3
|
|
1169
|
-
'@' => :ruby_debug
|
|
1195
|
+
'@' => :ruby_debug,
|
|
1196
|
+
|
|
1197
|
+
# SCRIPT EVALUATOR {{{3
|
|
1198
|
+
'$' => :script_eval
|
|
1170
1199
|
}
|
|
1171
1200
|
|
|
1172
1201
|
# USER KEYS (override or extend KEYMAP) {{{2
|
|
@@ -5562,6 +5591,98 @@ def ruby_debug # {{{3
|
|
|
5562
5591
|
@pB.full_refresh
|
|
5563
5592
|
end
|
|
5564
5593
|
|
|
5594
|
+
# SCRIPT EVALUATOR {{{2
|
|
5595
|
+
# Run a shell command (any language) with RTFM context exposed as env vars.
|
|
5596
|
+
# stdout shows in the right pane. stderr is scanned for control directives:
|
|
5597
|
+
# cd:/path change current directory
|
|
5598
|
+
# select:NAME move cursor onto NAME in the (new) cwd
|
|
5599
|
+
# status:MSG show MSG in the bottom pane
|
|
5600
|
+
# Any other stderr line is appended to the right-pane output in red.
|
|
5601
|
+
def script_eval # {{{3
|
|
5602
|
+
cmd = @pCmd.ask('$ ', '').to_s.strip
|
|
5603
|
+
if cmd.empty?
|
|
5604
|
+
@pB.clear; @pB.update = true
|
|
5605
|
+
return
|
|
5606
|
+
end
|
|
5607
|
+
|
|
5608
|
+
require 'json'
|
|
5609
|
+
selected = @selected.to_s
|
|
5610
|
+
tagged_paths = @tagged.dup
|
|
5611
|
+
context = {
|
|
5612
|
+
selected: selected,
|
|
5613
|
+
directory: Dir.pwd,
|
|
5614
|
+
tagged: tagged_paths,
|
|
5615
|
+
index: @index,
|
|
5616
|
+
count: (@files || []).size
|
|
5617
|
+
}
|
|
5618
|
+
|
|
5619
|
+
env = {
|
|
5620
|
+
'RTFM_SELECTED' => selected,
|
|
5621
|
+
'RTFM_DIR' => Dir.pwd,
|
|
5622
|
+
'RTFM_TAGGED' => tagged_paths.join("\n"),
|
|
5623
|
+
'RTFM_INDEX' => @index.to_s,
|
|
5624
|
+
'RTFM_COUNT' => (@files || []).size.to_s,
|
|
5625
|
+
'RTFM_CONTEXT' => context.to_json
|
|
5626
|
+
}
|
|
5627
|
+
|
|
5628
|
+
begin
|
|
5629
|
+
out, err, _status = Open3.capture3(env, 'sh', '-c', cmd)
|
|
5630
|
+
rescue StandardError => e
|
|
5631
|
+
@pB.say("script: #{e.class}: #{e.message}".fg(196))
|
|
5632
|
+
return
|
|
5633
|
+
end
|
|
5634
|
+
|
|
5635
|
+
# Process stderr line-by-line: directives consumed, other lines kept.
|
|
5636
|
+
leftover_err = []
|
|
5637
|
+
directive_msgs = []
|
|
5638
|
+
err.each_line do |line|
|
|
5639
|
+
l = line.chomp
|
|
5640
|
+
case l
|
|
5641
|
+
when /\Acd:(.+)\z/
|
|
5642
|
+
target = File.expand_path($1.strip)
|
|
5643
|
+
if File.directory?(target)
|
|
5644
|
+
@directory[Dir.pwd] = @index
|
|
5645
|
+
mark_latest
|
|
5646
|
+
Dir.chdir(target)
|
|
5647
|
+
directive_msgs << "cd: #{target}"
|
|
5648
|
+
else
|
|
5649
|
+
leftover_err << "cd: no such directory: #{target}"
|
|
5650
|
+
end
|
|
5651
|
+
when /\Aselect:(.+)\z/
|
|
5652
|
+
name = $1.strip
|
|
5653
|
+
# Applied after the upcoming render populates @files (handles both
|
|
5654
|
+
# same-dir selection and post-cd selection in the new dir).
|
|
5655
|
+
@startup_select = name
|
|
5656
|
+
directive_msgs << "select: #{name}"
|
|
5657
|
+
when /\Astatus:(.+)\z/
|
|
5658
|
+
msg = $1.strip
|
|
5659
|
+
@pB.say(msg.fg(156))
|
|
5660
|
+
directive_msgs << "status: #{msg}"
|
|
5661
|
+
else
|
|
5662
|
+
leftover_err << l unless l.empty?
|
|
5663
|
+
end
|
|
5664
|
+
end
|
|
5665
|
+
|
|
5666
|
+
body = out.dup
|
|
5667
|
+
body << "\n" << leftover_err.join("\n").fg(196) unless leftover_err.empty?
|
|
5668
|
+
|
|
5669
|
+
# Show directive trace as a subtle footer so the user can see what fired
|
|
5670
|
+
unless directive_msgs.empty?
|
|
5671
|
+
body << "\n\n" unless body.empty?
|
|
5672
|
+
body << '── directives applied ──'.fg(238) << "\n"
|
|
5673
|
+
body << directive_msgs.map { |m| " #{m}" }.join("\n").fg(244)
|
|
5674
|
+
end
|
|
5675
|
+
|
|
5676
|
+
clear_image
|
|
5677
|
+
@pR.clear # wipe leftover preview lines so short output doesn't sit atop them
|
|
5678
|
+
text = body.empty? ? ' (no output)'.fg(244) : body
|
|
5679
|
+
@pR.text = text
|
|
5680
|
+
@pR.ix = 0
|
|
5681
|
+
@pR.full_refresh
|
|
5682
|
+
@pR.update = false
|
|
5683
|
+
@pB.update = true
|
|
5684
|
+
end
|
|
5685
|
+
|
|
5565
5686
|
# GENERIC FUNCTIONS {{{1
|
|
5566
5687
|
def get_cached_dirlist(dir, ls_options, ls_options_with_long = nil) # {{{2
|
|
5567
5688
|
# Use ls_options_with_long for display, or fall back to ls_options
|
|
@@ -6508,11 +6629,18 @@ def get_interactive_program(file_path) # HELPER FOR OPEN_SELECTED TO USE @intera
|
|
|
6508
6629
|
if desktop_path
|
|
6509
6630
|
content = File.read(desktop_path)
|
|
6510
6631
|
exec_line = content[/^Exec=(.*)$/m, 1]
|
|
6632
|
+
# Honor Terminal=true in the .desktop file: any app declaring
|
|
6633
|
+
# itself a terminal program is treated as interactive even if
|
|
6634
|
+
# the user hasn't manually added it to @interactive. Lets newly
|
|
6635
|
+
# installed TUI tools "just work" without editing config.
|
|
6636
|
+
terminal_true = content =~ /^Terminal\s*=\s*true\b/i
|
|
6511
6637
|
if exec_line
|
|
6512
|
-
# Extract the program name (first word, remove path)
|
|
6513
6638
|
prog = exec_line.split.first
|
|
6514
6639
|
prog = File.basename(prog) if prog
|
|
6515
|
-
|
|
6640
|
+
if prog
|
|
6641
|
+
return prog if inter_list.include?(prog)
|
|
6642
|
+
return prog if terminal_true
|
|
6643
|
+
end
|
|
6516
6644
|
end
|
|
6517
6645
|
end
|
|
6518
6646
|
end
|