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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/bin/rtfm +132 -4
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f884960bd43345ca3368b1213be174efecf43943d20be674d722b70b848a7edf
4
- data.tar.gz: 5c3b0d4c294ee89015df17b5b5adc4ba78b7080fee34fa5825d1a7a88f92531c
3
+ metadata.gz: 1a3688f47ab7f42eaf8488abfd1035d3ffa36fba99368ab2dc1846dda3119415
4
+ data.tar.gz: 2bf042038eee3c18c764c136d87848f15ff623389ff08625c1f5a6dd2564abba
5
5
  SHA512:
6
- metadata.gz: ef00e7d08a4b579bb5a989cdbac6c30d02a77462401faeddaf30de42a464ccbbb547dcca7f369d050f0e64b83a8be8e4930eb38500d548bd010f4b77815f72c3
7
- data.tar.gz: c063661025b9887def4c49f146042a78cc6dd3cf0dbc7fd5b42b929f3219fc64e5e79412bb2a6afa9cc2cfaa3188cf7f2f5380102573ecd4cd5ef0f07bd56544
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.5.0' # Crash log, persistent per-dir cursor, Claude AI, non-blocking `:` commands
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
- return prog if prog && inter_list.include?(prog)
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rtfm-filemanager
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.5.0
4
+ version: 8.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene