rtfm-filemanager 6.0.5 → 6.0.7

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 (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/rtfm +293 -107
  3. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e57bcca8413887a30c4bb05ba3d370e1485d223e88d7b6aeea2c1fb6008f85f
4
- data.tar.gz: d067fd93312f608a7e5f4ad05135dcb023660172e0209431b83637698946b9b1
3
+ metadata.gz: 88ef15c96d6cd2e481ea3d0741da517dc256f7790673769457c9b78686721391
4
+ data.tar.gz: c1db40c673a60aa766fb3a79623e7ac912f0cf6898798db4d6731029cde8c7bb
5
5
  SHA512:
6
- metadata.gz: f793a2db1076bdf286356b746444eb88afd6f88e25cf58b15ebbe0f20bef404fa0153718c9dc0b9200ffaa2d318829b57bffff44e7c1518416f030b5eb5cd8e2
7
- data.tar.gz: 2e7d0e4a7bee42b0821ed29d6534366cad37d19029abd745e2060a07993b5c33d738a85cfb1505fd61e15f0ca9c48524f5be4e169384b012ae3b319145602233
6
+ metadata.gz: 7dd8b024cc288a1cb6b8fe2119dfaff80e657e16cbe34c0a0c3418943854f9a3bc9d2539031ab21b4fa1219a96fbd84e41bd24a1dff2eefd1db1b1504480e138
7
+ data.tar.gz: d4da7c1a24813ae78d768369e83decd456c4bde4e2b711d3b476bbbfd168db1e58632834a0db905bda1041cd44e63a6b8c3465b3758ae7620953dfea6e09000d
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 = '6.0.5' # Fixed symlink following and command bar cd functionality
21
+ @version = '6.0.7' # Fixed preview update after deletion + enhanced System Info with visual indicators
22
22
 
23
23
  # SAVE & STORE TERMINAL {{{1
24
24
  ORIG_STTY = `stty -g`.chomp
@@ -1413,9 +1413,25 @@ def follow_symlink # {{{3
1413
1413
  elsif File.file?(target)
1414
1414
  # If it's a file, change to the directory containing the file
1415
1415
  target_dir = File.dirname(target)
1416
+ target_basename = File.basename(target)
1416
1417
  Dir.chdir(target_dir)
1417
- # Select the target file in the new directory
1418
- @selected = target
1418
+ # Clear the index first to force a full refresh
1419
+ @index = 0
1420
+ @selected = nil
1421
+ # Refresh the directory listing
1422
+ dirlist
1423
+ # Find and select the target file in the new directory
1424
+ file_index = @files.index(target_basename)
1425
+ if file_index
1426
+ @index = file_index
1427
+ @selected = @files[@index]
1428
+ # Update the directory memory with the new index so it's not reset by the main loop
1429
+ @directory[Dir.pwd] = @index
1430
+ else
1431
+ # If file not found in listing (possibly filtered), still try to select it
1432
+ @selected = target_basename
1433
+ @directory[Dir.pwd] = 0
1434
+ end
1419
1435
  else
1420
1436
  @pB.say("Error: Symlink target does not exist or is not accessible".fg(196))
1421
1437
  @symlink_history&.clear
@@ -3208,7 +3224,22 @@ def delete_items # {{{3
3208
3224
  # Cannot undo permanent deletion, so don't add to undo history
3209
3225
  end
3210
3226
  @tagged.clear
3211
- refresh_right
3227
+
3228
+ # Refresh the file list to reflect deletions
3229
+ @pL.update = true
3230
+ render # This will update @files and @selected
3231
+
3232
+ # After render, check if we still have files
3233
+ if @files.empty?
3234
+ # No files left in directory - clear the preview
3235
+ @pR.text = ""
3236
+ @pR.refresh
3237
+ else
3238
+ # Files exist - update preview for new selection
3239
+ @pR.update = true
3240
+ render # Update the preview pane
3241
+ end
3242
+
3212
3243
  @pB.say("#{action} #{paths.size} items#{@trash ? " to #{TRASH_DIR}" : ''}".fg(204))
3213
3244
  end
3214
3245
  else
@@ -3617,7 +3648,6 @@ end
3617
3648
 
3618
3649
  # RIGHT PANE CONTROLS {{{2
3619
3650
  def refresh_right # {{{3
3620
- render
3621
3651
  @pR.full_refresh
3622
3652
  @pR.update = @pB.update = true
3623
3653
  end
@@ -3718,141 +3748,302 @@ end
3718
3748
 
3719
3749
  # SYSTEM SHORTCUTS {{{2
3720
3750
  def system_info # {{{3
3721
- text = "System Information\n".b.fg(156)
3751
+ text = "SYSTEM INFORMATION".b.fg(156) + " - " + Time.now.strftime("%Y-%m-%d %H:%M:%S").fg(249) + "\n"
3722
3752
  text << "=" * 50 + "\n\n"
3723
3753
 
3724
3754
  begin
3725
- # Operating System Information
3726
- text << "Operating System:\n".fg(226)
3755
+ # System Overview
3756
+ text << "System Overview".fg(226).b + "\n"
3757
+ text << "─" * 20 + "\n"
3758
+
3759
+ # Hostname and uptime
3760
+ hostname = `hostname 2>/dev/null`.chomp
3761
+ uptime_raw = `uptime -p 2>/dev/null`.chomp.sub('up ', '')
3762
+ boot_time = `uptime -s 2>/dev/null`.chomp
3763
+
3764
+ text << sprintf(" %-15s %s\n", "Hostname:".fg(249), hostname.fg(156))
3765
+ text << sprintf(" %-15s %s\n", "Uptime:".fg(249), uptime_raw.fg(156))
3766
+ text << sprintf(" %-15s %s\n", "Boot time:".fg(249), boot_time.fg(156))
3767
+
3768
+ # OS Info
3727
3769
  os_name = `awk -F '"' '/PRETTY/ {print $2}' /etc/os-release 2>/dev/null`.chomp
3728
3770
  kernel_version = `uname -r 2>/dev/null`.chomp
3729
3771
  architecture = `uname -m 2>/dev/null`.chomp
3730
- text << sprintf(" %-15s %s\n", "Distribution:", os_name.fg(156))
3731
- text << sprintf(" %-15s %s\n", "Kernel:", kernel_version.fg(156))
3732
- text << sprintf(" %-15s %s\n", "Architecture:", architecture.fg(156))
3772
+
3773
+ text << sprintf(" %-15s %s\n", "OS:".fg(249), os_name.fg(156))
3774
+ text << sprintf(" %-15s %s\n", "Kernel:".fg(249), kernel_version.fg(156))
3775
+ text << sprintf(" %-15s %s\n", "Architecture:".fg(249), architecture.fg(156))
3733
3776
  text << "\n"
3734
3777
  rescue # rubocop:disable Lint/SuppressedException
3735
3778
  end
3736
3779
 
3737
3780
  begin
3738
- # Hardware Information
3739
- text << "Hardware:\n".fg(226)
3781
+ # Hardware Information
3782
+ text << "Hardware".fg(226).b + "\n"
3783
+ text << "─" * 20 + "\n"
3784
+
3785
+ # CPU Info
3740
3786
  cpu_count = `nproc 2>/dev/null`.chomp
3741
3787
  cpuinfo = `lscpu 2>/dev/null`
3742
- cpu_model = cpuinfo[/^.*Model name:\s*(.*)/, 1] || "Unknown"
3743
- cpu_max = cpuinfo[/^.*CPU max MHz:\s*(.*)/, 1]&.to_i || 0
3744
- cpu_min = cpuinfo[/^.*CPU min MHz:\s*(.*)/, 1]&.to_i || 0
3745
-
3746
- text << sprintf(" %-15s %s cores\n", "CPU Count:", cpu_count.fg(156))
3747
- text << sprintf(" %-15s %s\n", "CPU Model:", cpu_model.fg(156))
3748
- if cpu_max > 0 && cpu_min > 0
3749
- text << sprintf(" %-15s %d-%d MHz\n", "CPU Speed:", cpu_min, cpu_max)
3788
+ cpu_model = cpuinfo[/^.*Model name:\s*(.*)/, 1]&.strip || "Unknown"
3789
+ cpu_model = cpu_model[0..45] + "..." if cpu_model.length > 48
3790
+
3791
+ # Current CPU frequency
3792
+ cpu_freq = `grep "cpu MHz" /proc/cpuinfo 2>/dev/null | head -1 | awk '{print $4}'`.chomp.to_f.round
3793
+
3794
+ # Load average
3795
+ loadavg = `cat /proc/loadavg 2>/dev/null`.chomp.split[0..2].join(' ')
3796
+
3797
+ text << sprintf(" %-15s %s\n", "CPU Model:".fg(249), cpu_model.fg(156))
3798
+ text << sprintf(" %-15s %s\n", "CPU Cores:".fg(249), "#{cpu_count} cores".fg(156))
3799
+ text << sprintf(" %-15s %s\n", "Current Freq:".fg(249), "#{cpu_freq} MHz".fg(156)) if cpu_freq > 0
3800
+ text << sprintf(" %-15s %s\n", "Load Average:".fg(249), loadavg.fg(156))
3801
+
3802
+ # Temperature if available
3803
+ if File.exist?("/sys/class/thermal/thermal_zone0/temp")
3804
+ temp = (File.read("/sys/class/thermal/thermal_zone0/temp").to_i / 1000.0).round(1)
3805
+ temp_color = temp > 80 ? 196 : temp > 60 ? 220 : 156
3806
+ text << sprintf(" %-15s %s\n", "CPU Temp:".fg(249), "#{temp}°C".fg(temp_color))
3750
3807
  end
3808
+
3751
3809
  text << "\n"
3752
3810
  rescue # rubocop:disable Lint/SuppressedException
3753
3811
  end
3754
3812
 
3755
3813
  begin
3756
- # Memory Information
3757
- text << "Memory Usage:\n".fg(226)
3758
- mem_output = `free -h 2>/dev/null`
3759
- if mem_output && !mem_output.empty?
3760
- mem_lines = mem_output.lines
3761
- mem_lines.each_with_index do |line, i|
3762
- if i == 0 # Header
3763
- text << " " + line.strip.fg(240) + "\n"
3764
- else
3765
- text << " " + line.strip.fg(156) + "\n"
3766
- end
3767
- end
3814
+ # Memory Information with visual bar
3815
+ text << "Memory".fg(226).b + "\n"
3816
+ text << "─" * 20 + "\n"
3817
+
3818
+ mem_info = `free -b 2>/dev/null | grep Mem:`
3819
+ if mem_info && !mem_info.empty?
3820
+ parts = mem_info.split
3821
+ total = parts[1].to_i
3822
+ used = parts[2].to_i
3823
+ available = parts[6].to_i
3824
+
3825
+ # Convert to human readable
3826
+ total_h = (total / 1024.0 / 1024.0 / 1024.0).round(1)
3827
+ used_h = (used / 1024.0 / 1024.0 / 1024.0).round(1)
3828
+ available_h = (available / 1024.0 / 1024.0 / 1024.0).round(1)
3829
+ percent = ((used.to_f / total) * 100).round
3830
+
3831
+ # Create visual bar (40 chars wide)
3832
+ bar_width = 40
3833
+ filled = (percent * bar_width / 100.0).round
3834
+ bar = "█" * filled + "░" * (bar_width - filled)
3835
+
3836
+ # Color based on usage
3837
+ bar_color = percent > 90 ? 196 : percent > 70 ? 220 : 156
3838
+
3839
+ # Bar first
3840
+ text << " " + bar.fg(bar_color) + " " + "#{percent}%".fg(bar_color) + "\n"
3841
+ text << sprintf(" %-15s %s\n", "Total:".fg(249), "#{total_h} GB".fg(156))
3842
+ text << sprintf(" %-15s %s\n", "Used:".fg(249), "#{used_h} GB".fg(bar_color))
3843
+ text << sprintf(" %-15s %s\n", "Available:".fg(249), "#{available_h} GB".fg(156))
3768
3844
  end
3845
+
3846
+ # Swap info
3847
+ swap_info = `free -b 2>/dev/null | grep Swap:`
3848
+ if swap_info && !swap_info.empty? && swap_info.split[1].to_i > 0
3849
+ parts = swap_info.split
3850
+ swap_total = (parts[1].to_i / 1024.0 / 1024.0 / 1024.0).round(1)
3851
+ swap_used = (parts[2].to_i / 1024.0 / 1024.0 / 1024.0).round(1)
3852
+ text << sprintf(" %-15s %s\n", "Swap:".fg(249), "#{swap_used}/#{swap_total} GB".fg(156))
3853
+ end
3854
+
3769
3855
  text << "\n"
3770
3856
  rescue # rubocop:disable Lint/SuppressedException
3771
3857
  end
3772
3858
 
3773
3859
  begin
3774
- # Environment Information
3775
- text << "Environment:\n".fg(226)
3776
- shell = `echo $SHELL 2>/dev/null`.sub(%r{.*/}, '').chomp
3777
- terminal = `echo $TERM 2>/dev/null`.chomp
3778
- packages = `dpkg-query -l 2>/dev/null | grep -c '^.i'`.chomp
3779
- packages = `pacman -Q 2>/dev/null | wc -l`.chomp if packages == '0'
3780
- packages = "Unknown" if packages == '0'
3781
-
3782
- text << sprintf(" %-15s %s\n", "Shell:", shell.fg(156))
3783
- text << sprintf(" %-15s %s\n", "Terminal:", terminal.fg(156))
3784
- text << sprintf(" %-15s %s\n", "Packages:", packages.fg(156))
3860
+ # Storage Information with visual bars
3861
+ text << "Storage".fg(226).b + "\n"
3862
+ text << "─" * 20 + "\n"
3863
+
3864
+ # Get disk usage for main filesystems only
3865
+ disk_output = `df -BG 2>/dev/null | grep -E '^/dev/' | grep -vE '/snap/|/tmp\\.| /run|/dev/loop'`
3866
+ if disk_output && !disk_output.empty?
3867
+ disk_output.lines.each do |line|
3868
+ parts = line.split
3869
+ next if parts.length < 6
3870
+
3871
+ filesystem = parts[0].sub('/dev/', '')
3872
+ size = parts[1].sub('G', '').to_i
3873
+ used = parts[2].sub('G', '').to_i
3874
+ avail = parts[3].sub('G', '').to_i
3875
+ percent = parts[4].sub('%', '').to_i
3876
+ mount = parts[5]
3877
+
3878
+ # Skip if too small
3879
+ next if size < 1
3880
+
3881
+ # Shorten mount point if needed
3882
+ mount_display = mount.length > 15 ? "...#{mount[-12..]}" : mount
3883
+
3884
+ # Mini bar (10 chars)
3885
+ bar_width = 10
3886
+ filled = (percent * bar_width / 100.0).round
3887
+ bar = "█" * filled + "░" * (bar_width - filled)
3888
+
3889
+ # Color based on usage
3890
+ color = percent > 90 ? 196 : percent > 80 ? 220 : 156
3891
+
3892
+ info = sprintf(" %-15s %3dG/%3dG %s %3d%%",
3893
+ mount_display, used, size, bar, percent)
3894
+ text << info.fg(color) + "\n"
3895
+ end
3896
+ end
3785
3897
  text << "\n"
3786
3898
  rescue # rubocop:disable Lint/SuppressedException
3787
3899
  end
3788
3900
 
3789
3901
  begin
3790
- # Disk Usage
3791
- text << "Disk Usage:\n".fg(226)
3792
- disk_output = `df -h 2>/dev/null | head -8`
3793
- if disk_output && !disk_output.empty?
3794
- disk_lines = disk_output.lines
3795
- disk_lines.each_with_index do |line, i|
3796
- if i == 0 # Header
3797
- text << " " + line.strip.fg(240) + "\n"
3798
- else
3799
- # Highlight usage percentage
3800
- colored_line = line.gsub(/(\d+)%/) do |match|
3801
- percent = $1.to_i
3802
- color = percent > 90 ? 196 : percent > 80 ? 220 : 156
3803
- match.fg(color)
3804
- end
3805
- text << " " + colored_line.strip.fg(249) + "\n"
3806
- end
3902
+ # Network Information
3903
+ text << "Network".fg(226).b + "\n"
3904
+ text << "─" * 20 + "\n"
3905
+
3906
+ # Get active network interfaces
3907
+ interfaces = `ip -o link show 2>/dev/null | grep -E 'state UP|UNKNOWN' | awk '{print $2}' | sed 's/://'`.chomp.split("\n")
3908
+
3909
+ interfaces.each do |iface|
3910
+ next if iface.empty? || iface == "lo"
3911
+
3912
+ # Get IP address
3913
+ ip_addr = `ip -4 addr show #{iface} 2>/dev/null | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}'`.chomp
3914
+ next if ip_addr.empty?
3915
+
3916
+ # Get RX/TX stats
3917
+ rx_bytes = File.read("/sys/class/net/#{iface}/statistics/rx_bytes").to_i rescue 0
3918
+ tx_bytes = File.read("/sys/class/net/#{iface}/statistics/tx_bytes").to_i rescue 0
3919
+
3920
+ rx_mb = (rx_bytes / 1024.0 / 1024.0).round(1)
3921
+ tx_mb = (tx_bytes / 1024.0 / 1024.0).round(1)
3922
+
3923
+ text << sprintf(" %-15s %s\n", "#{iface}:".fg(249), ip_addr.fg(156))
3924
+ text << sprintf(" %-15s %s\n", " Traffic:".fg(249), "↓#{rx_mb}MB ↑#{tx_mb}MB".fg(156))
3925
+ end
3926
+
3927
+ # Public IP if available (with timeout)
3928
+ begin
3929
+ require 'timeout'
3930
+ public_ip = Timeout.timeout(2) do
3931
+ `curl -s ifconfig.me 2>/dev/null`.chomp
3807
3932
  end
3933
+ text << sprintf(" %-15s %s\n", "Public IP:".fg(249), public_ip.fg(156)) unless public_ip.empty?
3934
+ rescue
3935
+ # Skip public IP if timeout or error
3808
3936
  end
3937
+
3809
3938
  text << "\n"
3810
3939
  rescue # rubocop:disable Lint/SuppressedException
3811
3940
  end
3812
3941
 
3813
3942
  begin
3814
- # Top Processes
3815
- text << "Top Processes (CPU & Memory):\n".fg(226)
3816
- ps_output = `ps -eo comm,pid,user,pcpu,pmem,stat --sort -pcpu,-pmem 2>/dev/null | head -8`
3817
- if ps_output && !ps_output.empty?
3818
- ps_lines = ps_output.lines
3819
- ps_lines.each_with_index do |line, i|
3820
- if i == 0 # Header
3821
- text << " " + line.strip.fg(240) + "\n"
3822
- else
3823
- text << " " + line.strip.fg(249) + "\n"
3824
- end
3825
- end
3943
+ # Environment Information
3944
+ text << "Environment".fg(226).b + "\n"
3945
+ text << "─" * 20 + "\n"
3946
+
3947
+ # Shell and terminal
3948
+ shell = ENV['SHELL']&.sub(%r{.*/}, '') || 'unknown'
3949
+ terminal = ENV['TERM'] || 'unknown'
3950
+ text << sprintf(" %-15s %s\n", "Shell:".fg(249), shell.fg(156))
3951
+ text << sprintf(" %-15s %s\n", "Terminal:".fg(249), terminal.fg(156))
3952
+
3953
+ # Package count
3954
+ if system("which dpkg >/dev/null 2>&1")
3955
+ packages = `dpkg-query -l 2>/dev/null | grep -c '^ii'`.chomp
3956
+ elsif system("which rpm >/dev/null 2>&1")
3957
+ packages = `rpm -qa 2>/dev/null | wc -l`.chomp
3958
+ elsif system("which pacman >/dev/null 2>&1")
3959
+ packages = `pacman -Q 2>/dev/null | wc -l`.chomp
3960
+ else
3961
+ packages = "unknown"
3962
+ end
3963
+ text << sprintf(" %-15s %s\n", "Packages:".fg(249), packages.fg(156))
3964
+
3965
+ # Desktop environment
3966
+ desktop = ENV['XDG_CURRENT_DESKTOP'] || ENV['DESKTOP_SESSION'] || 'none'
3967
+ text << sprintf(" %-15s %s\n", "Desktop:".fg(249), desktop.fg(156))
3968
+
3969
+ # GTK Theme if available
3970
+ if File.exist?("#{ENV['HOME']}/.config/gtk-3.0/settings.ini")
3971
+ gtk_theme = `grep gtk-theme-name ~/.config/gtk-3.0/settings.ini 2>/dev/null | cut -d= -f2`.chomp
3972
+ text << sprintf(" %-15s %s\n", "GTK Theme:".fg(249), gtk_theme.fg(156)) unless gtk_theme.empty?
3826
3973
  end
3974
+
3827
3975
  text << "\n"
3828
3976
  rescue # rubocop:disable Lint/SuppressedException
3829
3977
  end
3830
3978
 
3831
3979
  begin
3832
- # System Messages
3833
- text << "Recent System Messages:\n".fg(226)
3834
- dmesg_output = `dmesg 2>/dev/null | tail -5`
3835
- if dmesg_output && !dmesg_output.empty?
3836
- dmesg_output.lines.reverse.each do |line|
3837
- # Color code different message types
3838
- colored_line = case line
3839
- when /error|fail|critical/i then line.fg(196)
3840
- when /warn/i then line.fg(220)
3841
- when /info/i then line.fg(156)
3842
- else line.fg(249)
3843
- end
3844
- text << " " + colored_line.strip + "\n"
3980
+ # Services & Processes
3981
+ text << "Services & Processes".fg(226).b + "\n"
3982
+ text << "─" * 20 + "\n"
3983
+
3984
+ # Running services count
3985
+ services_count = `systemctl list-units --type=service --state=running 2>/dev/null | grep -c '\.service'`.chomp
3986
+ text << sprintf(" %-15s %s\n", "Services:".fg(249), "#{services_count} running".fg(156)) unless services_count == "0"
3987
+
3988
+ # Failed services
3989
+ failed_count = `systemctl list-units --type=service --state=failed 2>/dev/null | grep -c '\.service'`.chomp
3990
+ if failed_count.to_i > 0
3991
+ text << sprintf(" %-15s %s\n", "Failed:".fg(249), "#{failed_count} service#{failed_count.to_i > 1 ? 's' : ''}".fg(196))
3992
+ # Get the actual failed services (need to parse the full output)
3993
+ failed_output = `systemctl list-units --type=service --state=failed --no-legend 2>/dev/null`.chomp
3994
+ unless failed_output.empty?
3995
+ failed_output.lines.each do |line|
3996
+ # Skip the bullet point and extract service name (second field)
3997
+ parts = line.strip.split(/\s+/)
3998
+ if parts.length >= 2
3999
+ service_name = parts[1]
4000
+ if service_name && service_name.include?('.service')
4001
+ # Shorten long service names if needed
4002
+ display_name = service_name.length > 40 ? service_name[0..37] + "..." : service_name
4003
+ text << " → #{display_name}".fg(196) + "\n"
4004
+ end
4005
+ end
4006
+ end
3845
4007
  end
3846
- else
3847
- text << " " + "dmesg requires root access".fg(240) + "\n"
3848
- text << " " + "Run: sudo sysctl kernel.dmesg_restrict=0".fg(240) + "\n"
3849
4008
  end
4009
+
4010
+ # Process count
4011
+ proc_count = `ps aux | wc -l`.chomp.to_i - 1
4012
+ text << sprintf(" %-15s %s\n", "Processes:".fg(249), proc_count.to_s.fg(156))
4013
+
4014
+ # Users logged in
4015
+ users = `who | awk '{print $1}' | sort -u | wc -l`.chomp
4016
+ text << sprintf(" %-15s %s\n", "Users:".fg(249), "#{users} logged in".fg(156))
4017
+
4018
+ text << "\n"
3850
4019
  rescue # rubocop:disable Lint/SuppressedException
3851
4020
  end
3852
4021
 
4022
+ # Footer with system load average
4023
+ begin
4024
+ text << "─" * 50 + "\n"
4025
+
4026
+ # Get load average values
4027
+ loadavg = File.read("/proc/loadavg").split[0..2].map(&:to_f) rescue [0, 0, 0]
4028
+ cpu_count = `nproc 2>/dev/null`.chomp.to_i
4029
+ cpu_count = 1 if cpu_count == 0
4030
+
4031
+ # Format load values with color coding
4032
+ load_values = loadavg.map do |load|
4033
+ percent = (load / cpu_count * 100).round
4034
+ color = percent > 100 ? 196 : percent > 80 ? 220 : 156
4035
+ load.round(2).to_s.fg(color)
4036
+ end
4037
+
4038
+ text << "Load Average: " + load_values.join(" ") + " (1m 5m 15m)\n"
4039
+ text << "Updated: #{Time.now.strftime('%H:%M:%S')}".fg(240) + " - Press 'ENTER' to return".fg(249)
4040
+ rescue
4041
+ # Skip footer if error
4042
+ end
4043
+
3853
4044
  @pR.say(text)
3854
- rescue
3855
- @pR.say('Unable to show system info'.fg(196))
4045
+ rescue => e
4046
+ @pR.say("Unable to show system info\n#{e.message}".fg(196))
3856
4047
  end
3857
4048
 
3858
4049
  def make_directory # {{{3
@@ -4855,26 +5046,21 @@ def open_selected(html = nil) # OPEN SELECTED FILE {{{2
4855
5046
  end
4856
5047
  # Don't try to read large files or PDFs as text for validation
4857
5048
  if !@selected&.match(@pdffile) && File.size(@selected) < 1_000_000 && File.read(@selected).force_encoding('UTF-8').valid_encoding? # Pure text
4858
- # Flush any pending input before switching terminal modes
4859
- while IO.select([$stdin], nil, nil, 0)
4860
- begin
4861
- $stdin.read_nonblock(4096)
4862
- rescue IO::WaitReadable, EOFError
4863
- break
4864
- end
4865
- end
4866
- system("stty #{ORIG_STTY} < /dev/tty")
4867
- # Clear to top-left
5049
+ # Save terminal state before launching editor
5050
+ system("stty -g < /dev/tty > /tmp/rtfm_stty_$$")
5051
+ # Clear and reset terminal for editor
4868
5052
  system('clear < /dev/tty > /dev/tty')
4869
5053
  Cursor.show
4870
- editor = ENV.fetch('EDITOR', 'vi') # Launch $EDITOR on the real TTY
5054
+ # Launch editor
5055
+ editor = ENV.fetch('EDITOR', 'vi')
4871
5056
  system("#{editor} #{Shellwords.escape(@selected)}")
4872
- system('stty raw -echo isig < /dev/tty')
4873
- $stdin.raw!
4874
- $stdin.echo = false
4875
- Rcurses.init! # Reinitialize rcurses to fix input handling
5057
+ # Restore terminal state
5058
+ system("stty $(cat /tmp/rtfm_stty_$$) < /dev/tty")
5059
+ system("rm -f /tmp/rtfm_stty_$$")
5060
+ # Reinitialize rcurses
5061
+ Rcurses.init!
4876
5062
  Cursor.hide
4877
- Rcurses.clear_screen # Redraw RTFM
5063
+ Rcurses.clear_screen
4878
5064
  refresh
4879
5065
  render
4880
5066
  return
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rtfm-filemanager
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.5
4
+ version: 6.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-10 00:00:00.000000000 Z
11
+ date: 2025-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rcurses
@@ -53,8 +53,8 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '7.4'
55
55
  description: |-
56
- Major release - RTFM v6.0: Remote SSH/SFTP browsing with seamless navigation, interactive SSH shell integration, enhanced help system with color-coded formatting, SSH connection comments, comprehensive undo system, and performance optimizations.
57
- A full featured terminal browser with syntax highlighted files, images shown in the terminal, videos thumbnailed, etc. You can bookmark and jump around easily, delete, rename, copy, symlink and move files. RTFM is one of the most feature-packed terminal file managers.
56
+ RTFM v6.0.7: Fixed preview updates after file deletion and significantly enhanced System Info display with visual indicators, environment details, and service monitoring.
57
+ A full featured terminal browser with syntax highlighted files, images shown in the terminal, videos thumbnailed, etc. Features include remote SSH/SFTP browsing, interactive SSH shell, comprehensive undo system, bookmarks, and much more. You can bookmark and jump around easily, delete, rename, copy, symlink and move files. RTFM is one of the most feature-packed terminal file managers.
58
58
  email: g@isene.com
59
59
  executables:
60
60
  - rtfm