filecon 0.5.1 → 0.6.0
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 +26 -0
- data/README.md +12 -2
- data/bin/filecon +4 -3
- data/config.yaml +43 -50
- data/filecon.1 +15 -4
- data/lib/filecon/version.rb +1 -1
- data/lib/filecon.rb +210 -61
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e644e2e7cc6f76bb646d0738e9d83789a558696c7341a6c6a9cd3a749c1adc7b
|
|
4
|
+
data.tar.gz: a2d69517f4a4b7d9fc5af621440e9ca3a0bfaaf1be8987801b643725079f6256
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3603c6f4aada4a2c8ff06c6e0528f6f862c6bf8e929deba8920b959c818abc21bd8c979bf1ab51795692d1922cdc81fc5aa0be239d492bb8c90fec94eefee389
|
|
7
|
+
data.tar.gz: d4014f8166788f03d46576c195b5d4b5cb539c1ca70f28c71171db0ec196d00210a8c5f75cecc9b35f84e090eff3762913ec4ac926af75e6a9fab10de18a2a77
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
## [vx.y.z] — __DATE__
|
|
4
4
|
|
|
5
|
+
## [v0.6.0] — 2026-10-11
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Help panels use round borders joined by T-pieces at the seam
|
|
9
|
+
- All shell strings run under `bash -c` (never `sh`) through one `shell` helper; aliases and rc files do not resolve anywhere. Startup aborts cleanly without bash
|
|
10
|
+
- Help headers render from the configured `header:` color and command names blue from inline palette codes, which survive rebuilds and resizes under paint-once text
|
|
11
|
+
- Typr widget colors are configurable from the bundled config: `border:`, `header:`, `question:`, `answer:`, `selected:`, `alternate:`, `hints:`, and `search:` join the color mapping with per-key inline docs; every color key is documented and column-aligned (pinned by spec). All widget borders (help panels, pickers, conflict grids, error popups, `=` viewer) render from the configured `border:` color instead of hardcoded pairs
|
|
12
|
+
- Config is the authority on color: the `conflict:`, `help:`, and `help_border:` keys are gone (conflict uses the base `black` background, help titles use palette yellow inline with the keybinding-list header from `header:`, help borders use `border:`) and all color fallbacks are removed. Help command names render blue; help documents bash completion, recursive listing, history alt-edit, and all config locations
|
|
13
|
+
- Conflict picker is a single filling column, so no view shows through its rows
|
|
14
|
+
- Conflict popups use round borders on the base black background with all foregrounds kept; errors use the `error:` pair (dark red by default)
|
|
15
|
+
- Command execution from open_file, execute, and cmd_history runs through one `dispatch` (record except `!cd`, refresh, error popups); `run` drops its unused integer form. Per-prefix output: `|` detaches stdin and shows the first line, if any, then detaches; leftover pipe output is drained in a background thread so chatty detached commands survive instead of dying on SIGPIPE, (the pipe format spawns with `in: File::NULL`, so interactive children get EOF instead of the shared keyboard) and shows the first line, if any, then detaches. Stale home configs keep the old `open()` pipe format with shared stdin until reseeded, `+` detaches immediately, `=` streams live into the viewer, `!` results show with a keypress pause. The `regex:` config key is gone; only the `e[x]ecute` prompt tints (`:shell`). Requires typr `~> 1.8`
|
|
16
|
+
- Layered configuration — bundled defaults, then `/etc/filecon/config.yaml` (read if present, never written), then the user file; fresh user files are seeded as a commented template overriding nothing, and an explicit null deletes an inherited key. Existing user files keep working untouched. A layer file that parses to a non-mapping raises immediately naming the file
|
|
17
|
+
- Token blinking routes through `mode_code` instead of inline escapes (byte-identical output)
|
|
18
|
+
- Empty command results stay silent: no status line, no keypress pause
|
|
19
|
+
- Copy/move runs through rsync with its own progress repainted on the bottom line only (`--progress` stdout is piped, never scrolls); stderr is collected and shown in a borderless popup with the configurable `error:` colors at the end of the transfer; falls back to `FileUtils` where rsync is unavailable
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
- Bundled `+` prefix: no static terminal default is correct ($TERM names terminfo, not binaries); fresh seeds write the detected line, stale installs reseed by deleting the user file
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- First-run seeding writes a live `+` prefix with the detected terminal (first of kitty, foot, alacritty -e, urxvt -e, xterm -e, konsole -e, gnome-terminal --, ghostty -e, xfce4-terminal -e, wezterm start --, terminator -e, st -e found on PATH, else `$TERMINAL`), so windowed commands work out of the box (falls back to a literal `$TERMINAL` reference when nothing is detected); the `+` format no longer holds the window open
|
|
26
|
+
- Archive list/extract actions for 7z (`x-7z-compressed`) and rar (`vnd.rar`); `-y`/`-o+` keep extracts non-interactive under detached stdin
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- Terminal-window commands wrap the body in `bash -c`, so hashbang-less scripts run under terminals that exec directly instead of failing with exec format error
|
|
30
|
+
|
|
5
31
|
## [v0.5.1] — 2026-09-30
|
|
6
32
|
|
|
7
33
|
### Changed
|
data/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Filecon is a file and command manager that lives between terminal and shell, bui
|
|
|
9
9
|
Filecon is designed to be **intuitive**, **complete**, **powerful**, and **fast**. The whole interface is built around that principle:
|
|
10
10
|
|
|
11
11
|
- **Hint navigation** — every visible row carries a number hint (1–9, 0); pressing it acts on that row immediately, no cursor movement needed.
|
|
12
|
-
- **Live search in popups** —
|
|
12
|
+
- **Live search in popups** — popups open in `/` search mode; type to distill the list, then pick one of the few remaining results with a number. return key selects the first. auto selection of a single filter result.
|
|
13
13
|
- **Frequency-ordered history** — directory and command history are ranked by use count, so the most likely choice is always near the top.
|
|
14
14
|
|
|
15
15
|
Filecon runs on Typr, whose self-contained terminal layer ships its own precompiled terminfo key table and MIME-type database in the gem — no ncurses or external tooling required — so Filecon works on any raw, ANSI-capable terminal (including Termux).
|
|
@@ -33,6 +33,8 @@ Filecon runs on Typr, whose self-contained terminal layer ships its own precompi
|
|
|
33
33
|
gem install filecon
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
Requires `bash` (all shell commands run under `bash -c`, never `sh`; aliases and rc files do not resolve — full commands and bash syntax only) and `rsync` for transfers.
|
|
37
|
+
|
|
36
38
|
## Usage
|
|
37
39
|
|
|
38
40
|
```bash
|
|
@@ -85,9 +87,17 @@ Command and history popups open in `/` search mode. Type a few characters to **d
|
|
|
85
87
|
|
|
86
88
|
### Configuration
|
|
87
89
|
|
|
88
|
-
Config is
|
|
90
|
+
Config is layered, lowest to highest precedence: the bundled defaults,
|
|
91
|
+
`/etc/filecon/config.yaml` (system-wide, read if present, never written),
|
|
92
|
+
then `~/.config/filecon/config.yaml` (yours). On first run the user file is
|
|
93
|
+
created from the bundled config with every setting commented out except a
|
|
94
|
+
live + prefix with your detected terminal — a documented template that
|
|
95
|
+
overrides nothing else until edited, so new defaults
|
|
96
|
+
flow through on upgrade. Set only what you change; an explicit empty value
|
|
97
|
+
deletes an inherited key. It includes:
|
|
89
98
|
|
|
90
99
|
- Color settings (named colors, 8-bit palette, RGB triplets, base-palette overrides) — including the `colors.commands` pair used by the command picker and status line
|
|
100
|
+
- The `e[x]ecute` prompt always tints with the `:shell` scheme (first word red, `--options` blue); the `=` output viewer uses `:generic`
|
|
91
101
|
- Command definitions per MIME type, with execution-mode prefixes
|
|
92
102
|
- View configurations (columns that run shell commands)
|
|
93
103
|
- History limits
|
data/bin/filecon
CHANGED
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "tmpdir"
|
|
5
|
-
require "yaml"
|
|
6
5
|
require_relative "../lib/filecon"
|
|
7
6
|
|
|
8
7
|
log = nil
|
|
9
8
|
begin
|
|
9
|
+
abort "filecon requires bash" unless system("bash", "--version", out: File::NULL, err: File::NULL)
|
|
10
10
|
config_dir = File.expand_path("~/.config/filecon")
|
|
11
11
|
config_path = File.join(config_dir, "config.yaml")
|
|
12
|
+
bundled_path = File.join(__dir__, "../config.yaml")
|
|
12
13
|
unless File.exist?(config_path)
|
|
13
14
|
FileUtils.mkpath config_dir
|
|
14
|
-
|
|
15
|
+
File.write config_path, Filecon.config_template(File.read(bundled_path))
|
|
15
16
|
end
|
|
16
17
|
state_dir = File.expand_path("~/.local/share/filecon")
|
|
17
18
|
FileUtils.mkpath state_dir
|
|
@@ -23,7 +24,7 @@ begin
|
|
|
23
24
|
|
|
24
25
|
parse = ->(p) { File.readlines(p).map { |l| s = l.chomp.split(",", 2); [s[0].to_i, s[1]] } rescue [] }
|
|
25
26
|
|
|
26
|
-
config =
|
|
27
|
+
config = Filecon.load_config(bundled_path, "/etc/filecon/config.yaml", config_path)
|
|
27
28
|
|
|
28
29
|
Typr.init config["colors"]["default"]
|
|
29
30
|
Typr.clear
|
data/config.yaml
CHANGED
|
@@ -2,28 +2,23 @@
|
|
|
2
2
|
# Filecon Configuration
|
|
3
3
|
# =============================================================================
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# All settings are optional. Filecon will use sensible defaults if values
|
|
8
|
-
# are not specified.
|
|
5
|
+
# All settings are optional. Defaults are commented out.
|
|
9
6
|
#
|
|
10
7
|
# =============================================================================
|
|
11
8
|
|
|
12
9
|
# Maximum number of history entries to retain.
|
|
13
|
-
# Older entries beyond this limit are automatically pruned.
|
|
14
10
|
max_history: 10000
|
|
15
11
|
|
|
12
|
+
# most frequent entries to lead history lists before most recent entries
|
|
16
13
|
top_history: 6
|
|
14
|
+
|
|
17
15
|
# When true, active filters also hide directories that don't match.
|
|
18
16
|
filter_dirs: true
|
|
19
17
|
|
|
20
|
-
|
|
21
18
|
# =============================================================================
|
|
22
19
|
# COLORS
|
|
23
20
|
# =============================================================================
|
|
24
21
|
#
|
|
25
|
-
# Customize the color scheme for different file types in the browser view.
|
|
26
|
-
#
|
|
27
22
|
# Color values can be:
|
|
28
23
|
# - Named colors:
|
|
29
24
|
# Basic: black, red, green, yellow, blue, magenta, cyan, white, grey
|
|
@@ -53,8 +48,7 @@ filter_dirs: true
|
|
|
53
48
|
# dir_history directory history picker: [name, path] columns
|
|
54
49
|
# cmd_history command history picker: [prefix, command] columns
|
|
55
50
|
# top_history top-history field background color
|
|
56
|
-
#
|
|
57
|
-
# help_border help text border: [fg, bg]
|
|
51
|
+
# error error popup: [fg, bg]
|
|
58
52
|
#
|
|
59
53
|
# Usage:
|
|
60
54
|
# Foreground color only: "blue"
|
|
@@ -63,7 +57,7 @@ filter_dirs: true
|
|
|
63
57
|
# =============================================================================
|
|
64
58
|
|
|
65
59
|
colors:
|
|
66
|
-
# Base palette (comment out to use terminal colors)
|
|
60
|
+
# Base palette (comment out in bundled config to use terminal colors)
|
|
67
61
|
black: [21, 21, 21]
|
|
68
62
|
red: [172, 65, 66]
|
|
69
63
|
green: [126, 141, 80]
|
|
@@ -73,24 +67,30 @@ colors:
|
|
|
73
67
|
cyan: [125, 213, 207]
|
|
74
68
|
white: [208, 208, 208]
|
|
75
69
|
|
|
76
|
-
default: [white, black]
|
|
77
|
-
|
|
70
|
+
default: [white, black] # Global default foreground/background [fg, bg]
|
|
71
|
+
border: [white, black] # Widget borders
|
|
72
|
+
header: [black, grey50] # Column/panel headers
|
|
73
|
+
question: green # Prompt question text
|
|
74
|
+
answer: yellow # Typed input text
|
|
75
|
+
selected: grey25 # Highlighted / selected row background
|
|
76
|
+
alternate: grey10 # Alternating row background in lists
|
|
77
|
+
hints: [white, black] # Hint numbers/digits
|
|
78
|
+
commands: [green, red] # Command picker and status line: [name, args] colors
|
|
78
79
|
types:
|
|
79
80
|
image: blue
|
|
80
81
|
audio: green
|
|
81
82
|
video: cyan
|
|
82
83
|
directory: yellow
|
|
83
84
|
executable: red
|
|
84
|
-
actions:
|
|
85
|
+
actions: # Named action colors: filter, sort, views
|
|
85
86
|
filter: red
|
|
86
87
|
sort: blue
|
|
87
88
|
views: green
|
|
88
|
-
dir_history: [yellow, brown]
|
|
89
|
-
cmd_history: [red, blue]
|
|
90
|
-
top_history: grey20
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
dir_history: [yellow, brown] # Directory history picker: [name, path] columns
|
|
90
|
+
cmd_history: [red, blue] # Command history picker: [prefix, command] columns
|
|
91
|
+
top_history: grey20 # Top-history field background color
|
|
92
|
+
search: grey20 # Search-match highlight (text viewer)
|
|
93
|
+
error: [white, dark_red] # Error popup: [fg, bg]
|
|
94
94
|
|
|
95
95
|
# =============================================================================
|
|
96
96
|
# PREFIXES
|
|
@@ -100,17 +100,16 @@ colors:
|
|
|
100
100
|
# a format string. %s is replaced with the command body.
|
|
101
101
|
#
|
|
102
102
|
# Format strings starting with ! are evaluated as Ruby and their result is
|
|
103
|
-
# read; every other format
|
|
104
|
-
#
|
|
105
|
-
# and left running while the browser keeps going.
|
|
103
|
+
# read; every other format runs under bash. Aliases and rc files do not
|
|
104
|
+
# resolve anywhere; use full commands and bash syntax.
|
|
106
105
|
#
|
|
107
106
|
# =============================================================================
|
|
108
107
|
|
|
109
108
|
prefixes:
|
|
110
109
|
"!": "!StringIO.new((%s).to_s)"
|
|
111
|
-
"|": "!
|
|
112
|
-
"
|
|
113
|
-
"
|
|
110
|
+
"|": "!IO.popen('%s 2>&1', 'r', in: File::NULL)"
|
|
111
|
+
"=": "!Text.new(top: 1, right:-1, bottom:-2, header: '%s', colors: {regex: :generic}, input: open('|%s 2>&1')).pick :none"
|
|
112
|
+
"-": "!Text.new(top: 1, right:-1, bottom:-2, header: '%s', input: open('|%s 2>&1')).pick :none"
|
|
114
113
|
|
|
115
114
|
# =============================================================================
|
|
116
115
|
# BINDINGS
|
|
@@ -118,13 +117,10 @@ prefixes:
|
|
|
118
117
|
#
|
|
119
118
|
# Key bindings for the status bar. Each binding maps a label (with the key
|
|
120
119
|
# character in brackets) to either `!method` — a method call, dispatched
|
|
121
|
-
# directly — or any prefixed command, which is
|
|
122
|
-
# like
|
|
120
|
+
# directly — or any prefixed command, which is run
|
|
121
|
+
# like any command (`%name` resolves to the current directory).
|
|
123
122
|
# Commands from bindings are never added to the command history.
|
|
124
123
|
#
|
|
125
|
-
# Only printable single characters work as key bindings.
|
|
126
|
-
# Non-printable keys (arrows, escape, return, etc.) are hardcoded.
|
|
127
|
-
#
|
|
128
124
|
# =============================================================================
|
|
129
125
|
|
|
130
126
|
bindings:
|
|
@@ -151,9 +147,7 @@ bindings:
|
|
|
151
147
|
# COMMANDS
|
|
152
148
|
# =============================================================================
|
|
153
149
|
#
|
|
154
|
-
# Define commands
|
|
155
|
-
# category) maps to a set of named commands that can be executed on files of
|
|
156
|
-
# that type.
|
|
150
|
+
# Define commands for different MIME types.
|
|
157
151
|
#
|
|
158
152
|
# SUBSTITUTION VARIABLES:
|
|
159
153
|
#
|
|
@@ -186,21 +180,18 @@ bindings:
|
|
|
186
180
|
|
|
187
181
|
commands:
|
|
188
182
|
default:
|
|
189
|
-
# copy: "|rsync --progress %name %dir"
|
|
190
183
|
info: "=file %name"
|
|
191
|
-
symlink: "ln -s %name %dir"
|
|
192
184
|
copy: "!transfer(:copy, %name, %dir)"
|
|
193
185
|
move: "!transfer(:move, %name, %dir)"
|
|
194
|
-
# copy_shell: "|cp -R %name %dir"
|
|
195
|
-
# move_shell: "|mv %name %dir"
|
|
196
186
|
rename: "|mv %name %str"
|
|
197
187
|
delete: "|rm -rf %name"
|
|
188
|
+
symlink: "ln -s %name %dir"
|
|
198
189
|
open_with: "|%cmd %name %str"
|
|
199
|
-
|
|
190
|
+
open_in_terminal: "+%cmd %name %str"
|
|
200
191
|
xdg_open: "|xdg-open %name"
|
|
201
192
|
|
|
202
193
|
executable:
|
|
203
|
-
whatis: "
|
|
194
|
+
whatis: "|whatis %name"
|
|
204
195
|
help: "=%name --help"
|
|
205
196
|
manual: "+man %name"
|
|
206
197
|
run: "|%name %str"
|
|
@@ -217,14 +208,9 @@ commands:
|
|
|
217
208
|
new_file: "|touch %name/%str"
|
|
218
209
|
new_dir: "|mkdir %name/%str"
|
|
219
210
|
shell: "+$SHELL "
|
|
220
|
-
# shell: "+$SHELL -c 'cd %name && exec $SHELL'"
|
|
221
|
-
# zsh: "+zsh -c 'cd %name && exec zsh'"
|
|
222
211
|
disk_use: "|du -hs %name | cut -wf1"
|
|
223
212
|
play: "+mpv %name"
|
|
224
213
|
|
|
225
|
-
# text:
|
|
226
|
-
# count: "|wc -l %name | cut -wf1"
|
|
227
|
-
|
|
228
214
|
troff:
|
|
229
215
|
manual: "+man %name"
|
|
230
216
|
|
|
@@ -251,7 +237,7 @@ commands:
|
|
|
251
237
|
"x-python":
|
|
252
238
|
run: "+python3 %name"
|
|
253
239
|
|
|
254
|
-
"x
|
|
240
|
+
"x-perl":
|
|
255
241
|
run: "+perl %name"
|
|
256
242
|
|
|
257
243
|
"x-shellscript":
|
|
@@ -278,9 +264,9 @@ commands:
|
|
|
278
264
|
"x-c++":
|
|
279
265
|
run: "+g++ %name && ./a.out"
|
|
280
266
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
267
|
+
markdown:
|
|
268
|
+
view: "=glow %name"
|
|
269
|
+
edit: "+micro %name"
|
|
284
270
|
|
|
285
271
|
json:
|
|
286
272
|
view: "=cat %name | jq ."
|
|
@@ -305,6 +291,14 @@ commands:
|
|
|
305
291
|
list: "=tar -tf %name"
|
|
306
292
|
extract: "|tar -xf %name -C %dir"
|
|
307
293
|
|
|
294
|
+
"x-7z-compressed":
|
|
295
|
+
list: "=7z l %name"
|
|
296
|
+
extract: "|7z x -y %name -o%dir"
|
|
297
|
+
|
|
298
|
+
"vnd.rar":
|
|
299
|
+
list: "=unrar l %name"
|
|
300
|
+
extract: "|unrar x -o+ %name %dir"
|
|
301
|
+
|
|
308
302
|
"epub+zip":
|
|
309
303
|
view: "+folio %name"
|
|
310
304
|
|
|
@@ -325,7 +319,6 @@ commands:
|
|
|
325
319
|
# View values can be:
|
|
326
320
|
# - true Use the built-in column for this field
|
|
327
321
|
# - A string Command to execute (with %name substitution)
|
|
328
|
-
# - A symbol Reference to a system field (e.g., :size, :type, :subtype)
|
|
329
322
|
#
|
|
330
323
|
# Prefix ! to evaluate as Ruby
|
|
331
324
|
#
|
data/filecon.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH FILECON 1 "2026-
|
|
1
|
+
.TH FILECON 1 "2026-10-11" "filecon 0.6.0" "User Commands"
|
|
2
2
|
.SH NAME
|
|
3
3
|
filecon \- a file and command manager that lives between terminal and shell
|
|
4
4
|
.SH SYNOPSIS
|
|
@@ -93,12 +93,23 @@ first, then the remaining entries in most-recent order. The
|
|
|
93
93
|
setting in config.yaml (default 10) controls how many frequent entries lead
|
|
94
94
|
the list.
|
|
95
95
|
.SH CONFIGURATION
|
|
96
|
-
|
|
96
|
+
Configuration is layered, lowest to highest precedence: the bundled
|
|
97
97
|
.B config.yaml
|
|
98
|
-
|
|
99
|
-
.
|
|
98
|
+
in the installation directory,
|
|
99
|
+
.B /etc/filecon/config.yaml
|
|
100
|
+
(system-wide, read if present, never written),
|
|
101
|
+
then the user file below.
|
|
102
|
+
On first run a documented template is written to
|
|
103
|
+
.BR ~/.config/filecon/config.yaml :
|
|
104
|
+
the bundled defaults with every setting commented out except a live +
|
|
105
|
+
prefix with your detected terminal, overriding nothing else until edited.
|
|
106
|
+
Set only what you change; an explicit empty
|
|
107
|
+
value deletes an inherited key.
|
|
100
108
|
.SS Config File Locations
|
|
101
109
|
.TP
|
|
110
|
+
.B /etc/filecon/config.yaml
|
|
111
|
+
System-wide overrides (optional)
|
|
112
|
+
.TP
|
|
102
113
|
.B ~/.config/filecon/config.yaml
|
|
103
114
|
Main configuration (colors, commands, views)
|
|
104
115
|
.TP
|
data/lib/filecon/version.rb
CHANGED
data/lib/filecon.rb
CHANGED
|
@@ -6,9 +6,55 @@ end
|
|
|
6
6
|
|
|
7
7
|
require "fileutils"
|
|
8
8
|
require "find"
|
|
9
|
+
require "yaml"
|
|
10
|
+
require "tempfile"
|
|
9
11
|
|
|
10
12
|
require_relative "filecon/version"
|
|
11
13
|
|
|
14
|
+
module Filecon
|
|
15
|
+
|
|
16
|
+
# Layered configuration: bundled defaults < system (/etc) < user (~/.config).
|
|
17
|
+
# Merges +over+ into +base+: hashes recurse, scalars and arrays replace, an
|
|
18
|
+
# explicit null deletes an inherited key, absent keys fall through.
|
|
19
|
+
def self.layer base, over
|
|
20
|
+
over.each do |k, v|
|
|
21
|
+
if v.nil? and base.key?(k) then base.delete(k)
|
|
22
|
+
elsif v.is_a?(Hash) and base[k].is_a?(Hash) then layer(base[k], v)
|
|
23
|
+
else base[k] = v end
|
|
24
|
+
end
|
|
25
|
+
base
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Load and merge configuration layers in order; missing files are skipped.
|
|
29
|
+
# Every present layer must parse to a mapping; anything else raises naming
|
|
30
|
+
# the file, so a broken layer fails loud instead of failing deep in the
|
|
31
|
+
# merge or silently booting defaults.
|
|
32
|
+
def self.load_config *paths
|
|
33
|
+
paths.filter_map { |p|
|
|
34
|
+
next unless p and File.exist?(p)
|
|
35
|
+
cfg = YAML.safe_load(File.read(p), permitted_classes: [Symbol])
|
|
36
|
+
raise "#{p} must contain a YAML mapping, got #{cfg.class}" unless cfg.nil? or cfg.is_a?(Hash)
|
|
37
|
+
cfg
|
|
38
|
+
}.reduce({}) { |merged, cfg| layer(merged, cfg || {}) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Documented template: every setting commented out, docs kept — except one
|
|
42
|
+
# live + prefix with the detected terminal, so windowed commands work out
|
|
43
|
+
# of the box. Parses to just that override until edited. -e flags ride on
|
|
44
|
+
# the entries that need them; the format itself stays shared.
|
|
45
|
+
def self.config_template text, terminal = nil
|
|
46
|
+
on_path = ->(t) { ENV["PATH"].to_s.split(File::PATH_SEPARATOR).any? { |d| File.executable?(File.join(d, t.split.first)) } }
|
|
47
|
+
terminal ||= ["kitty", "foot", "alacritty -e", "urxvt -e", "xterm -e",
|
|
48
|
+
"konsole -e", "gnome-terminal --", "ghostty -e", "xfce4-terminal -e",
|
|
49
|
+
"wezterm start --", "terminator -e", "st -e"].find { |e| on_path.(e) } || "$TERMINAL"
|
|
50
|
+
body = text.gsub(/^([ \t]*)(?=\S)(?!#)/m, '\1# ')
|
|
51
|
+
stanza = "# Terminal for + commands, detected on first run (edit freely).\nprefixes:\n \"+\": \"#{terminal} bash -c '%s'\"\n\n"
|
|
52
|
+
anchor = "# filter_dirs: true\n"
|
|
53
|
+
body.include?(anchor) ? body.sub(anchor) { |m| m + stanza } : stanza + body
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
12
58
|
class Filecon::App < Typr::Browser
|
|
13
59
|
include Typr
|
|
14
60
|
|
|
@@ -59,19 +105,9 @@ class Filecon::App < Typr::Browser
|
|
|
59
105
|
def execute
|
|
60
106
|
cmd = @user.cli("> ", redraw: method(:show),
|
|
61
107
|
history: @cmd_history.map { |_, cmd| cmd },
|
|
62
|
-
prefixes: @prefixes&.keys&.join)
|
|
108
|
+
prefixes: @prefixes&.keys&.join, regex: :shell)
|
|
63
109
|
return unless cmd
|
|
64
|
-
|
|
65
|
-
io = run cmd
|
|
66
|
-
if io and @prefixes&.[](cmd[0].to_s)&.start_with? '!'
|
|
67
|
-
output = io.read.to_s.scrub.strip
|
|
68
|
-
nc = (@colors[:commands] || [:green, :red]).first
|
|
69
|
-
@user.show "> #{color_code(nc)}#{cmd[1..-1]}#{color_code(@colors[:default])} : #{output.lines.last}"
|
|
70
|
-
show
|
|
71
|
-
Typr.read_key
|
|
72
|
-
else
|
|
73
|
-
show
|
|
74
|
-
end
|
|
110
|
+
dispatch cmd
|
|
75
111
|
end
|
|
76
112
|
|
|
77
113
|
def executables; cd ENV["PATH"].split(":") end
|
|
@@ -102,7 +138,7 @@ class Filecon::App < Typr::Browser
|
|
|
102
138
|
StringIO.new eval( built[1..-1] ).to_s
|
|
103
139
|
else
|
|
104
140
|
cmd = built[0] == ?| ? built : ?| + built
|
|
105
|
-
|
|
141
|
+
shell(cmd + " 2>&1")
|
|
106
142
|
end
|
|
107
143
|
cell = io.read(width).to_s.scrub.strip
|
|
108
144
|
value = coerce_type cell
|
|
@@ -142,7 +178,7 @@ class Filecon::App < Typr::Browser
|
|
|
142
178
|
bottom: [ pop + commands.count - 1, Typr.height ].min,
|
|
143
179
|
input:commands, border: :light, format: [:min, :max],
|
|
144
180
|
borders: { top: nil }, alternate: false,
|
|
145
|
-
colors: {columns: @colors[:commands]
|
|
181
|
+
colors: {columns: @colors[:commands], border: @colors[:border]} )
|
|
146
182
|
return unless cmd = picker.pick( :row, column: 0 )
|
|
147
183
|
alt = picker.modifier == :alt
|
|
148
184
|
commands[ cmd ]
|
|
@@ -157,23 +193,7 @@ class Filecon::App < Typr::Browser
|
|
|
157
193
|
cmd = build(edited, id, pair.first)
|
|
158
194
|
return unless cmd
|
|
159
195
|
end
|
|
160
|
-
|
|
161
|
-
display = cmd[1..-1]
|
|
162
|
-
record :cmd, cmd
|
|
163
|
-
io = run( cmd )
|
|
164
|
-
output = io.read.to_s.scrub.strip
|
|
165
|
-
nc, ac = @colors[:commands] || [:green, :red]
|
|
166
|
-
@user.show "> #{color_code(nc)}#{pair.first}#{color_code(ac)}:#{display}" \
|
|
167
|
-
"#{color_code(@colors[:default])} : #{output.lines.last}"
|
|
168
|
-
show
|
|
169
|
-
Typr.read_key
|
|
170
|
-
else
|
|
171
|
-
record :cmd, cmd unless cmd[0..2] == '!cd'
|
|
172
|
-
run( cmd )
|
|
173
|
-
end
|
|
174
|
-
Array(id).sort.reverse.each { |i| refresh_row i } if id && cmd[0..2] != '!cd' && @directory == dir
|
|
175
|
-
@selected[:rows] = [] if id && @directory != dir
|
|
176
|
-
show #unless cmd[0] == ?|
|
|
196
|
+
dispatch cmd, name: pair.first, id: id, dir: dir
|
|
177
197
|
end
|
|
178
198
|
|
|
179
199
|
def refresh_row id
|
|
@@ -215,6 +235,7 @@ class Filecon::App < Typr::Browser
|
|
|
215
235
|
[path, File.join(dest, File.basename(src), path[(src.length + 1)..-1])] unless path == src }
|
|
216
236
|
}
|
|
217
237
|
policy, done, skipped, failed = nil, 0, 0, 0
|
|
238
|
+
err = Tempfile.new("filecon-rsync")
|
|
218
239
|
pairs.each_with_index do |(src, dst), i|
|
|
219
240
|
@user.show "#{op} #{i + 1}/#{pairs.size}: #{File.basename(src)}"
|
|
220
241
|
exists = File.exist?( dst ) || File.symlink?( dst )
|
|
@@ -238,13 +259,52 @@ class Filecon::App < Typr::Browser
|
|
|
238
259
|
dst = action if action.is_a?( String )
|
|
239
260
|
if File.directory?( src ) and !File.symlink?( src )
|
|
240
261
|
FileUtils.mkdir_p( dst )
|
|
241
|
-
|
|
242
|
-
FileUtils.mv( src, dst )
|
|
262
|
+
done += 1
|
|
243
263
|
else
|
|
244
|
-
|
|
264
|
+
if @has_rsync.nil?
|
|
265
|
+
begin
|
|
266
|
+
pid = Process.spawn("rsync", "--version", out: File::NULL, err: File::NULL)
|
|
267
|
+
Process.wait(pid)
|
|
268
|
+
@has_rsync = $?.success?
|
|
269
|
+
rescue Errno::ENOENT
|
|
270
|
+
@has_rsync = false
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
if @has_rsync
|
|
274
|
+
FileUtils.mkdir_p(File.dirname(dst))
|
|
275
|
+
cmd = ["rsync", "-a", "--progress", src, dst]
|
|
276
|
+
cmd.insert(2, "--remove-source-files") if op == :move
|
|
277
|
+
begin
|
|
278
|
+
rd, wr = IO.pipe
|
|
279
|
+
pid = Process.spawn(*cmd, out: wr, err: err.path)
|
|
280
|
+
wr.close
|
|
281
|
+
seg, shown = nil, nil
|
|
282
|
+
begin
|
|
283
|
+
loop do
|
|
284
|
+
rd.readpartial(4096).split(/[\r\n]/).each do |line|
|
|
285
|
+
seg = line unless line.empty?
|
|
286
|
+
end
|
|
287
|
+
if seg != shown
|
|
288
|
+
shown = seg
|
|
289
|
+
@user.show "#{op} #{i + 1}/#{pairs.size}: #{shown}"
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
rescue EOFError
|
|
293
|
+
end
|
|
294
|
+
rd.close
|
|
295
|
+
Process.wait(pid)
|
|
296
|
+
ok = $?.success?
|
|
297
|
+
rescue Errno::ENOENT
|
|
298
|
+
@has_rsync = false
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
unless @has_rsync
|
|
302
|
+
if op == :move then FileUtils.mv(src, dst) else FileUtils.cp(src, dst) end
|
|
303
|
+
ok = true
|
|
304
|
+
end
|
|
305
|
+
if ok then done += 1 else failed += 1 end
|
|
306
|
+
end
|
|
245
307
|
end
|
|
246
|
-
done += 1
|
|
247
|
-
end
|
|
248
308
|
rescue SystemCallError
|
|
249
309
|
failed += 1
|
|
250
310
|
end
|
|
@@ -255,6 +315,14 @@ class Filecon::App < Typr::Browser
|
|
|
255
315
|
end
|
|
256
316
|
tally = "#{op}: #{done} done, #{skipped} skipped" + ( failed > 0 ? ", #{failed} failed" : "" )
|
|
257
317
|
@user.show tally
|
|
318
|
+
errs = File.read(err.path).strip
|
|
319
|
+
err.close!
|
|
320
|
+
unless errs.empty?
|
|
321
|
+
Text.new(left: 0.15, right: 0.85, top: 0.3, bottom: 0.7,
|
|
322
|
+
header: "rsync errors", input: StringIO.new(errs),
|
|
323
|
+
colors: { default: @colors[:error], border: @colors[:border] },
|
|
324
|
+
alternate: false).pick(:none)
|
|
325
|
+
end
|
|
258
326
|
show
|
|
259
327
|
Typr.read_key
|
|
260
328
|
tally
|
|
@@ -263,12 +331,16 @@ class Filecon::App < Typr::Browser
|
|
|
263
331
|
def resolve_conflict src, dst
|
|
264
332
|
Typr.drain_input
|
|
265
333
|
info_top = top + 1
|
|
266
|
-
|
|
334
|
+
bg = [Array(@colors[:default])[0], @colors[:black]]
|
|
335
|
+
info = Grid.new(left:left+1, top:info_top, bottom:info_top+2, margin:' ', header:"file exists. overwrite?", input:[["from", File.basename(src), (File.size(src) rescue 0), (File.mtime(src).to_i rescue 0)], ["to", File.basename(dst), (File.size(dst) rescue 0), (File.mtime(dst).to_i rescue 0)]], border: :round, alternate:false, procs:{2=>:magnitudes, 3=>proc{|s| Time.at(s).strftime("%Y-%m-%d %H:%M:%S") rescue "?" }}, colors:{columns:@colors[:commands], default: bg, border: @colors[:border]})
|
|
267
336
|
act_top = info.bottom + 2
|
|
268
337
|
choices = %w[skip overwrite older smaller size_differs rename]
|
|
269
|
-
picker = Grid.new(left:left+1, top:act_top,
|
|
338
|
+
picker = Grid.new(left:left+1, top:act_top, bottom:[act_top+6, Typr.height].min, header:"alt-number remembers choice", input:choices, format:[:max], border: :round, alternate:false, colors:{columns:@colors[:commands], default: bg, border: @colors[:border]})
|
|
339
|
+
picker.borders[:top]=' '
|
|
340
|
+
picker.borders[:top_right]='╭'
|
|
341
|
+
picker.borders[:top_left]='│'
|
|
270
342
|
info.show
|
|
271
|
-
idx = picker.pick(:row
|
|
343
|
+
idx = picker.pick(:row)
|
|
272
344
|
show
|
|
273
345
|
return [:abort, nil] unless idx.is_a?(Integer)
|
|
274
346
|
mods = picker.modifier
|
|
@@ -283,7 +355,7 @@ class Filecon::App < Typr::Browser
|
|
|
283
355
|
end
|
|
284
356
|
|
|
285
357
|
def build str, id, name=nil
|
|
286
|
-
nc, ac = @colors[:commands]
|
|
358
|
+
nc, ac = @colors[:commands]
|
|
287
359
|
reset = color_code( @colors[:default] )
|
|
288
360
|
prefix = name ? "#{color_code(nc)}#{name}#{color_code(ac)}:" : ""
|
|
289
361
|
cmd = ""
|
|
@@ -297,7 +369,7 @@ class Filecon::App < Typr::Browser
|
|
|
297
369
|
@data[id][NAME].gsub(esc){ |m| "\\#{m}" } }.join(sep) :
|
|
298
370
|
@data[id][NAME].gsub(esc){ |m| "\\#{m}" } )+?"
|
|
299
371
|
when '%dir', '%file', '%cmd'
|
|
300
|
-
@user.show "> #{prefix}#{cmd}
|
|
372
|
+
@user.show "> #{prefix}#{cmd}#{mode_code(:slow)}#{tok}#{mode_code(:steady)}#{post}#{reset}"
|
|
301
373
|
result = pick( { '%dir' => :directory, '%file' => :file, '%cmd' => :cmd }[tok] )
|
|
302
374
|
return unless result
|
|
303
375
|
esc = cmd[0] == ?! ? /[\\"$#]/ : /[\\"$`]/
|
|
@@ -314,19 +386,90 @@ class Filecon::App < Typr::Browser
|
|
|
314
386
|
return cmd
|
|
315
387
|
end
|
|
316
388
|
|
|
389
|
+
# Run +cmd+ (a full shell string) under bash, never sh. Accepts the
|
|
390
|
+
# same mode/opts as IO.popen; a leading | marker (Kernel#open heritage)
|
|
391
|
+
# is stripped since the array form needs none.
|
|
392
|
+
def shell cmd, mode="r", **opts
|
|
393
|
+
IO.popen(["bash", "-c", cmd.sub(/\A\|/, "")], mode, **opts)
|
|
394
|
+
end
|
|
395
|
+
|
|
317
396
|
def run cmd=nil
|
|
318
397
|
return unless cmd
|
|
319
|
-
cmd = @cmd_history[cmd][1] if cmd.is_a? Integer
|
|
320
398
|
Typr.flush_overtype
|
|
321
399
|
fmt = @prefixes&.[](cmd[0].to_s)
|
|
322
400
|
if fmt
|
|
323
401
|
body = cmd[1..-1]
|
|
324
402
|
body = body.gsub(/[\\']/) { |m| "\\#{m}" } if fmt =~ /'[^']*%s/
|
|
325
403
|
result = fmt.gsub('%s') { body }
|
|
326
|
-
result.start_with?('!') ? eval(result[1..-1]) :
|
|
404
|
+
result.start_with?('!') ? eval(result[1..-1]) : shell(result, pgroup: true)
|
|
327
405
|
else
|
|
328
|
-
|
|
406
|
+
shell(cmd + " 2>&1", "r", in: File::NULL)
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Run a command line from any source — open_file, execute, history.
|
|
411
|
+
# Records history (except !cd), shows pipe and eval results with a
|
|
412
|
+
# keypress pause, reports errors in a popup, and refreshes rows picked
|
|
413
|
+
# from the listing. Returns the run result (or nil).
|
|
414
|
+
def dispatch cmd, name: nil, id: nil, dir: nil
|
|
415
|
+
return unless cmd
|
|
416
|
+
record :cmd, cmd unless cmd[0..2] == '!cd'
|
|
417
|
+
begin
|
|
418
|
+
io = run cmd unless cmd[0] == ?=
|
|
419
|
+
rescue => e
|
|
420
|
+
errs = e.message + "\n" + e.backtrace.first(3).join("\n")
|
|
421
|
+
Text.new(left: 0.15, right: 0.85, top: 0.3, bottom: 0.7,
|
|
422
|
+
header: "command error", input: StringIO.new(errs),
|
|
423
|
+
colors: { default: @colors[:error], border: @colors[:border] },
|
|
424
|
+
alternate: false).pick(:none)
|
|
425
|
+
return
|
|
426
|
+
end
|
|
427
|
+
if cmd[0] == ?=
|
|
428
|
+
body = cmd[1..-1]
|
|
429
|
+
text = Text.new(top: 1, right: -1, bottom: -2, header: body,
|
|
430
|
+
colors: { regex: :generic, border: @colors[:border] }, input: "")
|
|
431
|
+
io = shell("#{body} 2>&1", "r", in: File::NULL)
|
|
432
|
+
begin
|
|
433
|
+
loop do
|
|
434
|
+
chunk = io.readpartial(4096)
|
|
435
|
+
text << chunk
|
|
436
|
+
text.show
|
|
437
|
+
end
|
|
438
|
+
rescue EOFError
|
|
439
|
+
end
|
|
440
|
+
io.close
|
|
441
|
+
text.pick(:none)
|
|
442
|
+
elsif cmd[0] == ?|
|
|
443
|
+
line = io.gets
|
|
444
|
+
if line
|
|
445
|
+
nc, ac = @colors[:commands]
|
|
446
|
+
head = name ? "#{color_code(nc)}#{name}#{color_code(ac)}:" : color_code(nc)
|
|
447
|
+
@user.show "> #{head}#{cmd[1..-1]}#{color_code(@colors[:default])} : #{line.strip}"
|
|
448
|
+
end
|
|
449
|
+
elsif io and io.respond_to?(:read) and
|
|
450
|
+
@prefixes&.[](cmd[0].to_s)&.start_with? '!'
|
|
451
|
+
output = io.read.to_s.scrub.strip
|
|
452
|
+
unless output.empty?
|
|
453
|
+
nc, ac = @colors[:commands]
|
|
454
|
+
head = name ? "#{color_code(nc)}#{name}#{color_code(ac)}:" : color_code(nc)
|
|
455
|
+
@user.show "> #{head}#{cmd[1..-1]}#{color_code(@colors[:default])} : #{output.lines.last}"
|
|
456
|
+
show
|
|
457
|
+
Typr.read_key
|
|
458
|
+
end
|
|
329
459
|
end
|
|
460
|
+
if io.is_a?(IO) and !io.closed?
|
|
461
|
+
Thread.new(io) do |pipe|
|
|
462
|
+
begin
|
|
463
|
+
loop { pipe.readpartial(16384) }
|
|
464
|
+
rescue EOFError, IOError
|
|
465
|
+
end
|
|
466
|
+
pipe.close rescue nil
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
Array(id).sort.reverse.each { |i| refresh_row i } if id && cmd[0..2] != '!cd' && @directory == dir
|
|
470
|
+
@selected[:rows] = [] if id && @directory != dir
|
|
471
|
+
show
|
|
472
|
+
io
|
|
330
473
|
end
|
|
331
474
|
|
|
332
475
|
def cmd_history; history @cmd_history, :cmd end
|
|
@@ -346,12 +489,12 @@ class Filecon::App < Typr::Browser
|
|
|
346
489
|
[File.basename(e), e]
|
|
347
490
|
}
|
|
348
491
|
|
|
349
|
-
fc = is_cmd ? (@colors[:cmd_history]
|
|
492
|
+
fc = is_cmd ? (@colors[:cmd_history]) : (@colors[:dir_history])
|
|
350
493
|
fields = {}
|
|
351
494
|
ordered.each_with_index { |(cnt, e), i|
|
|
352
495
|
next unless top_set[e]
|
|
353
|
-
fields[[i,0]] = [fc[0], @colors[:top_history]
|
|
354
|
-
fields[[i,1]] = [fc[1], @colors[:top_history]
|
|
496
|
+
fields[[i,0]] = [fc[0], @colors[:top_history]]
|
|
497
|
+
fields[[i,1]] = [fc[1], @colors[:top_history]]
|
|
355
498
|
}
|
|
356
499
|
|
|
357
500
|
picker = popup(input: pairs, format:[:min,:max],
|
|
@@ -368,12 +511,10 @@ class Filecon::App < Typr::Browser
|
|
|
368
511
|
entry = build(edited, nil)
|
|
369
512
|
return unless entry
|
|
370
513
|
end
|
|
371
|
-
record type, entry
|
|
372
|
-
|
|
373
514
|
if is_cmd
|
|
374
|
-
|
|
375
|
-
input: StringIO.new(e.message + "\n" + e.backtrace.first(3).join("\n"))).pick :none; end
|
|
515
|
+
dispatch entry
|
|
376
516
|
else
|
|
517
|
+
record type, entry
|
|
377
518
|
unless Dir.exist? entry
|
|
378
519
|
@user.show "no such directory: #{entry}"; Typr.read_key; return
|
|
379
520
|
end
|
|
@@ -461,7 +602,11 @@ class Filecon::App < Typr::Browser
|
|
|
461
602
|
|
|
462
603
|
def help
|
|
463
604
|
grid = @user.list
|
|
464
|
-
|
|
605
|
+
grid.border = :round
|
|
606
|
+
grid.colors[:header] = @colors[:header]
|
|
607
|
+
grid.colors[:border] = @colors[:border]
|
|
608
|
+
y = color_code @colors[:yellow]
|
|
609
|
+
b = color_code @colors[:blue]
|
|
465
610
|
d = color_code @colors[:default]
|
|
466
611
|
text = Text.new( input: "\n#{y}FILECON#{d} - a file and command manager that lives between terminal and shell\n\n" \
|
|
467
612
|
"#{y}HINTS:#{d}\n press a number (1-9,0) to open that file with its default action.\n" \
|
|
@@ -470,19 +615,23 @@ class Filecon::App < Typr::Browser
|
|
|
470
615
|
" Mark files with m, then Return opens their command list.\n" \
|
|
471
616
|
" TAB cycles pages of hints.\n\n" \
|
|
472
617
|
"#{y}COMMANDS:#{d}\n" \
|
|
473
|
-
" [e]xecutables browse all executables on your PATH\n" \
|
|
474
|
-
" e[x]ecute type a command line and run it in the shell\n" \
|
|
475
|
-
"
|
|
476
|
-
" [d
|
|
477
|
-
" [
|
|
618
|
+
" #{b}[e]xecutables#{d} browse all executables on your PATH\n" \
|
|
619
|
+
" #{b}e[x]ecute#{d} type a command line and run it in the shell\n" \
|
|
620
|
+
" (TAB completes from bash)\n" \
|
|
621
|
+
" #{b}[R]ecurse#{d} expand the listing to every file below (with paths)\n" \
|
|
622
|
+
" #{b}[d]irectories#{d} jump to a recently visited directory\n" \
|
|
623
|
+
" #{b}[c]ommands#{d} re-run a recent command\n\n" \
|
|
478
624
|
" Directory and command history popups list the most frequently used\n" \
|
|
479
625
|
" entries first, then the rest in most-recent order. The top_history\n" \
|
|
480
626
|
" setting in config.yaml (default 10) controls how many most-used entries\n" \
|
|
481
|
-
" lead the list.\n\n" \
|
|
482
|
-
"#{y}CONFIG:#{d}\n ~/.config/filecon/config.yaml\n"
|
|
627
|
+
" lead the list. In command history, Alt+number edits the picked entry instead of running it.\n\n" \
|
|
628
|
+
"#{y}CONFIG:#{d}\n ~/.config/filecon/config.yaml\n /etc/filecon/config.yaml\n" \
|
|
629
|
+
" (bundled defaults ship with the gem; delete your file to reseed its template)\n",
|
|
483
630
|
top: 2, right: -2, bottom: -3,
|
|
484
|
-
colors: { border: @colors[:
|
|
485
|
-
border: :
|
|
631
|
+
colors: { border: @colors[:border] },
|
|
632
|
+
border: :round )
|
|
633
|
+
text.borders[:top_left] = '┬'
|
|
634
|
+
text.borders[:bottom_left] = '┴'
|
|
486
635
|
text.left = -> { grid.right + 2 }
|
|
487
636
|
loop do
|
|
488
637
|
grid.show
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: filecon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kilian Reitmayr
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-10-11 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: typr
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '1.
|
|
18
|
+
version: '1.8'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '1.
|
|
25
|
+
version: '1.8'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: unicode-display_width
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|