pull/664/head v0.21.3
Arijit Basu 9 months ago committed by GitHub
commit 8af1647c09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,14 @@
# Why dynamic linking?
# See https://github.com/sayanarijit/xplr/issues/309
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-args=-rdynamic"]
[target.aarch64-unknown-linux-gnu]
rustflags = ["-C", "linker=aarch64-linux-gnu-gcc", "-C", "link-args=-rdynamic"]
[target.aarch64-linux-android]
rustflags = ["-C", "linker=aarch64-linux-android-clang", "-C", "link-args=-rdynamic", "-C", "default-linker-libraries"]
[target.arm-unknown-linux-gnueabihf]
rustflags = ["-C", "linker=arm-linux-gnueabihf-gcc", "-C", "link-args=-rdynamic"]

@ -12,71 +12,74 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
- ubuntu-20.04
build:
- macos
- macos-aarch64
- linux
- linux-musl
- linux-aarch64
- linux-arm
rust: [stable]
include:
# See the list: https://github.com/cross-rs/cross
- os: macos-latest
artifact_prefix: macos
- build: macos
os: macos-latest
target: x86_64-apple-darwin
binary_postfix: ""
- os: ubuntu-latest
artifact_prefix: linux
- build: macos-aarch64
os: macos-latest
target: aarch64-apple-darwin
- build: linux
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
binary_postfix: ""
- os: ubuntu-20.04
artifact_prefix: linux-musl
- build: linux-musl
os: ubuntu-latest
target: x86_64-unknown-linux-musl
binary_postfix: ""
# Will see later
- build: linux-aarch64
os: ubuntu-latest
target: aarch64-unknown-linux-gnu
# - os: ubuntu-latest
# artifact_prefix: x86_64-android
# target: x86_64-linux-android
# binary_postfix: ''
#
# - os: ubuntu-latest
# artifact_prefix: aarch64-android
# target: aarch64-linux-android
# binary_postfix: ''
- build: linux-arm
os: ubuntu-latest
target: arm-unknown-linux-gnueabihf
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Installing Rust toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Installing needed macOS dependencies
if: matrix.os == 'macos-latest'
run: brew install openssl@1.1
- name: Installing needed Ubuntu dependencies
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-20.04'
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update --fix-missing
sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
sudo apt-get install -y --no-install-recommends liblua5.1-0-dev libluajit-5.1-dev gcc pkg-config curl git make ca-certificates
- if: matrix.build == 'linux-musl'
run: sudo apt-get install -y musl-tools
- if: matrix.build == 'linux-aarch64'
run: sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Checking out sources
uses: actions/checkout@v1
- if: matrix.build == 'linux-arm'
run: |
sudo apt-get install -y gcc-multilib
sudo apt-get install -y gcc-arm-linux-gnueabihf
sudo ln -s /usr/include/asm-generic/ /usr/include/asm
- name: Running cargo build
uses: actions-rs/cargo@v1
with:
use-cross: true
command: build
toolchain: ${{ matrix.rust }}
args: --locked --release --target ${{ matrix.target }}
run: cargo build --locked --release --target ${{ matrix.target }}
- name: Install gpg secret key
run: |
@ -87,9 +90,9 @@ jobs:
shell: bash
run: |
cd target/${{ matrix.target }}/release
BINARY_NAME=xplr${{ matrix.binary_postfix }}
BINARY_NAME=xplr
strip $BINARY_NAME
RELEASE_NAME=xplr-${{ matrix.artifact_prefix }}
RELEASE_NAME=xplr-${{ matrix.build }}
tar czvf $RELEASE_NAME.tar.gz $BINARY_NAME
shasum -a 256 $RELEASE_NAME.tar.gz > $RELEASE_NAME.sha256
cat <(echo "${{ secrets.GPG_PASS }}") | gpg --pinentry-mode loopback --passphrase-fd 0 --detach-sign --armor $RELEASE_NAME.tar.gz
@ -98,9 +101,9 @@ jobs:
uses: softprops/action-gh-release@v1
with:
files: |
target/${{ matrix.target }}/release/xplr-${{ matrix.artifact_prefix }}.tar.gz
target/${{ matrix.target }}/release/xplr-${{ matrix.artifact_prefix }}.sha256
target/${{ matrix.target }}/release/xplr-${{ matrix.artifact_prefix }}.tar.gz.asc
target/${{ matrix.target }}/release/xplr-${{ matrix.build }}.tar.gz
target/${{ matrix.target }}/release/xplr-${{ matrix.build }}.sha256
target/${{ matrix.target }}/release/xplr-${{ matrix.build }}.tar.gz.asc
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -108,7 +111,7 @@ jobs:
name: Publishing GPG signature
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install gpg secret key
run: |
cat <(echo -e "${{ secrets.GPG_SECRET }}") | gpg --batch --import
@ -133,20 +136,16 @@ jobs:
name: Publishing to Cargo
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
override: true
- run: |
sudo apt-get update --fix-missing
sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
- uses: actions-rs/cargo@v1
with:
command: publish
args: --allow-dirty
- run: cargo publish --allow-dirty
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_KEY }}

@ -11,130 +11,134 @@ jobs:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
profile: minimal
override: true
- uses: actions-rs/cargo@v1
- run: cargo check
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: rustfmt
- run: cargo fmt --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: clippy
- run: cargo clippy -- -D warnings
spellcheck:
name: Spellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: codespell-project/actions-codespell@v1
with:
command: check
ignore_words_file: .codespellignore
test:
name: Test Suite
runs-on: ${{ matrix.os }}
needs:
- check
- fmt
- clippy
- spellcheck
strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
- ubuntu-20.04
build:
- macos
- macos-aarch64
- linux
- linux-musl
- linux-aarch64
- linux-arm
rust: [stable]
include:
- os: macos-latest
artifact_prefix: macos
# See the list: https://github.com/cross-rs/cross
- build: macos
os: macos-latest
target: x86_64-apple-darwin
binary_postfix: ""
- os: ubuntu-latest
artifact_prefix: linux
- build: macos-aarch64
os: macos-latest
target: aarch64-apple-darwin
- build: linux
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
binary_postfix: ""
- os: ubuntu-20.04
artifact_prefix: linux-musl
- build: linux-musl
os: ubuntu-latest
target: x86_64-unknown-linux-musl
binary_postfix: ""
- build: linux-aarch64
os: ubuntu-latest
target: aarch64-unknown-linux-gnu
- build: linux-arm
os: ubuntu-latest
target: arm-unknown-linux-gnueabihf
env:
RUST_BACKTRACE: full
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Installing Rust toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Installing needed macOS dependencies
if: matrix.os == 'macos-latest'
run: brew install openssl@1.1
- name: Installing needed Ubuntu dependencies
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-20.04'
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update --fix-missing
sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
sudo apt-get install -y --no-install-recommends liblua5.1-0-dev libluajit-5.1-dev gcc pkg-config curl git make ca-certificates
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
toolchain: ${{ matrix.rust }}
args: --target ${{ matrix.target }}
- if: matrix.build == 'linux-musl'
run: sudo apt-get install -y musl-tools
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
toolchain: ${{ matrix.rust }}
args: --target ${{ matrix.target }}
- if: matrix.build == 'linux-aarch64'
run: sudo apt-get install -y gcc-aarch64-linux-gnu
- if: matrix.build == 'linux-arm'
run: |
sudo apt-get install -y gcc-multilib
sudo apt-get install -y gcc-arm-linux-gnueabihf
sudo ln -s /usr/include/asm-generic/ /usr/include/asm
- run: cargo build --target ${{ matrix.target }}
- if: matrix.build == 'macos' || matrix.build == 'linux'
run: cargo test --target ${{ matrix.target }}
# bench:
# name: Benchmarks
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - uses: actions-rs/toolchain@v1
# - uses: actions/checkout@v3
# - uses: dtolnay/rust-toolchain@stable
# with:
# toolchain: stable
# profile: minimal
# override: true
# # These dependencies are required for `clipboard`
# - run: sudo apt-get install -y -qq libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
# - uses: actions-rs/cargo@v1
# with:
# command: bench
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
components: rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
components: clippy
- uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
spellcheck:
name: Spellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: codespell-project/actions-codespell@v1
with:
ignore_words_file: .codespellignore
# - run: cargo bench

3
.gitignore vendored

@ -17,3 +17,6 @@ book/
# direnv
.direnv/
# nix
result

651
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -8,7 +8,7 @@ path = './benches/criterion.rs'
[package]
name = 'xplr'
version = '0.21.2'
version = '0.21.3'
authors = ['Arijit Basu <hi@arijitbasu.in>']
edition = '2021'
description = 'A hackable, minimal, fast TUI file explorer'
@ -22,29 +22,29 @@ categories = ['command-line-interface', 'command-line-utilities']
include = ['src/**/*', 'docs/en/src/**/*', 'LICENSE', 'README.md']
[dependencies]
libc = "0.2.144"
libc = "0.2.147"
humansize = "2.1.3"
natord = "1.0.9"
anyhow = "1.0.71"
serde_yaml = "0.9.21"
crossterm = { version = "0.26.1", features = [], default-features = false }
ansi-to-tui = "3.0.0"
regex = "1.8.1"
anyhow = "1.0.72"
serde_yaml = "0.9.25"
crossterm = { version = "0.27.0", features = [], default-features = false }
ansi-to-tui = "3.1.0"
regex = "1.9.3"
gethostname = "0.4.3"
serde_json = "1.0.96"
serde_json = "1.0.104"
path-absolutize = "3.1.0"
which = "4.4.0"
nu-ansi-term = "0.47.0"
nu-ansi-term = "0.49.0"
textwrap = "0.16"
snailquote = "0.3.1"
skim = { version = "0.10.4", default-features = false }
time = { version = "0.3.21", features = ["serde", "local-offset", "formatting", "macros"] }
jf = "0.3.1"
xdg = "2.5.0"
time = { version = "0.3.25", features = ["serde", "local-offset", "formatting", "macros"] }
jf = "0.6.2"
xdg = "2.5.2"
home = "0.5.5"
[dependencies.lscolors]
version = "0.14.0"
version = "0.15.0"
default-features = false
features = ["nu-ansi-term"]
@ -57,18 +57,18 @@ version = "2.0.4"
default-features = false
[dependencies.tui]
version = "0.20.1"
version = "0.22.0"
default-features = false
features = ['crossterm', 'serde']
package = 'ratatui'
[dependencies.serde]
version = "1.0.163"
version = "1.0.183"
features = []
default-features = false
[dependencies.indexmap]
version = "1.9.3"
version = "2.0.0"
features = ['serde']
[dependencies.mlua]
@ -76,16 +76,17 @@ version = "0.8.9"
features = ['luajit', 'vendored', 'serialize', 'send']
[dependencies.tui-input]
version = "0.7.0"
version = "0.8.0"
features = ['serde']
[dev-dependencies]
criterion = "0.4.0"
assert_cmd = "2.0.11"
criterion = "0.5.1"
assert_cmd = "2.0.12"
[profile.release]
lto = true
codegen-units = 1
panic = 'abort'
strip = true
[features]

@ -60,6 +60,8 @@ integration][15], enabling you to achieve insane terminal productivity.
- [[Article] What is a TUI file explorer & why would you need one? ~ xplr.stck.me](https://xplr.stck.me/post/25252/What-is-a-TUI-file-explorer-why-would-you-need-one)
- [[Article] FOSSPicks - Linux Magazine](https://www.linux-magazine.com/Issues/2022/258/FOSSPicks/(offset)/6)
## Packaging
Package maintainers please refer to the [RELEASE.md](./RELEASE.md).

@ -131,6 +131,14 @@ return {
{ LogSuccess = "Switched layout" },
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_layout_switch" },
}
-- Add messages to send when the selection changes
--
-- Type: list of [Message](https://xplr.dev/en/message#message)s
on_selection_change = {
{ LogSuccess = "Selection changed" },
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_selection_change" },
}
}
```

@ -587,12 +587,16 @@ Type: nullable list of [Node Sorter](https://xplr.dev/en/sorting#node-sorter-app
#### xplr.config.general.initial_mode
The name of one of the modes to use when xplr loads.
This isn't the default mode. To modify the default mode, overwrite
[xplr.config.modes.builtin.default](https://xplr.dev/en/modes#xplrconfigmodesbuiltindefault).
Type: nullable string
#### xplr.config.general.initial_layout
The name of one of the layouts to use when xplr loads.
This isn't the default layout. To modify the default layout, overwrite
[xplr.config.layouts.builtin.default](https://xplr.dev/en/layouts#xplrconfiglayoutsbuiltindefault).
Type: nullable string

@ -198,44 +198,20 @@ sudo cp target/release/xplr /usr/local/bin/
## Android
### [Termux][23]
### [Termux][24]
[![xplr-termuxfd3c398d3cf4bcbc.md.jpg][24]][25]
> Please note that xplr isn't heavily tested on Termux, hence things might
> need a little tweaking and fixing for a smooth usage experience.
- Install build dependencies
```bash
pkg install rustc cargo make
```
- Install `xplr`
```bash
cargo install --locked --force xplr
```
- Setup storage
```bash
termux-setup-storage
```
- Setup config and runtime dir
```bash
pkg install rust make binutils
cargo install --locked xplr
```bash
export XDG_CONFIG_HOME="$PWD/storage/.config"
export XDG_RUNTIME_DIR="$PWD/storage/run"
# Run
~/.cargo/bin/xplr
```
mkdir -p "$XDG_CONFIG_HOME" "$XDG_RUNTIME_DIR"
```
> Please note that xplr isn't heavily tested on Termux, hence things might need
> a little tweaking and fixing for a smooth user experience.
- Run
```bash
~/.cargo/bin/xplr
```
![termux demo][23]
[1]: #direct-download
[2]: #from-cratesio
@ -259,8 +235,8 @@ sudo cp target/release/xplr /usr/local/bin/
[20]: https://gcc.gnu.org/
[21]: https://www.gnu.org/software/make/
[22]: https://git-scm.com/
[23]: https://termux.com/
[24]: https://s3.gifyu.com/images/xplr-termuxfd3c398d3cf4bcbc.md.jpg
[23]: https://github.com/sayanarijit/xplr/assets/11632726/3b61e8c8-76f0-48e8-8734-50e9e7e495b7
[24]: https://termux.dev/
[25]: https://gifyu.com/image/tF2D
[26]: https://github.com/sayanarijit/xplr/releases/latest/download/xplr-linux-musl.tar.gz
[27]: https://pkgs.alpinelinux.org/packages?name=xplr

@ -2,15 +2,13 @@
#### Example: Defining Custom Layout
[![layout.png][23]][24]
```lua
xplr.config.layouts.builtin.default = {
Horizontal = {
config = {
margin = 1,
horizontal_margin = 2,
vertical_margin = 3,
horizontal_margin = 1,
vertical_margin = 1,
constraints = {
{ Percentage = 50 },
{ Percentage = 50 },
@ -24,6 +22,21 @@ xplr.config.layouts.builtin.default = {
}
```
Result:
```
╭ /home ─────────────╮╭ Help [default] ────╮
│ ╭─── path ││. show hidden │
│ ├▸[ð Desktop/] ││/ search │
│ ├ ð Documents/ ││: action │
│ ├ ð Downloads/ ││? global help │
│ ├ ð GitHub/ ││G go to bottom │
│ ├ ð Music/ ││V select/unselect│
│ ├ ð Pictures/ ││ctrl duplicate as │
│ ├ ð Public/ ││ctrl next visit │
╰────────────────────╯╰────────────────────╯
```
A layout is a [sum type][56] can be one of the following:
- [Nothing][8]
@ -86,7 +99,7 @@ Type: { Static = [Custom Panel][27] }
This is a custom layout to render dynamic content using a function defined in
[xplr.fn][28] that takes [Content Renderer Argument][36] and returns [Custom Panel][27].
Type: { Dynamic = [Content Renderer][35] }
Type: { Dynamic = "[Content Renderer][35]" }
### Horizontal
@ -97,7 +110,7 @@ It contains the following information:
- [config][15]
- [splits][17]
Type: { Horizontal = { config = [config][15], splits = [splits][17] }
Type: { Vertical = { config = [Layout Config][15], splits = { [Layout][17], ... } }
### Vertical
@ -108,7 +121,7 @@ It contains the following information:
- [config][15]
- [splits][17]
Type: { Vertical = { config = [config][15], splits = [splits][17] }
Type: { Vertical = { config = [Layout Config][15], splits = { [Layout][17], ... } }
## Layout Config
@ -200,6 +213,16 @@ xplr.config.layouts.builtin.default = {
}
```
Result:
```
╭ custom title ────────╮
│custom body │
│ │
│ │
╰──────────────────────╯
```
#### Example: Render a custom dynamic paragraph
```lua
@ -215,6 +238,23 @@ xplr.fn.custom.render_layout = function(ctx)
end
```
Result:
```
╭/home/sayanarijit───────────────────────────╮
│mime_essence: inode/directory │
│relative_path: Desktop │
│is_symlink: false │
│is_readonly: false │
│parent: /home/sayanarijit │
│absolute_path: /home/sayanarijit/Desktop │
│is_broken: false │
│created: 1668087850396758714 │
│size: 4096 │
│gid: 100 │
╰────────────────────────────────────────────╯
```
### CustomList
A list to render. It contains the following fields:
@ -235,6 +275,17 @@ xplr.config.layouts.builtin.default = {
}
```
Result:
```
╭ custom title ─────────────╮
│1 │
│2 │
│3 │
│ │
╰───────────────────────────╯
```
#### Example: Render a custom dynamic list
```lua
@ -254,6 +305,18 @@ xplr.fn.custom.render_layout = function(ctx)
end
```
Result:
```
╭/home/sayanarijit──────────╮
│Desktop │
│0.21.2 │
│17336 │
│ │
│ │
╰───────────────────────────╯
```
## CustomTable
A custom table to render. It contains the following fields:
@ -283,6 +346,18 @@ xplr.config.layouts.builtin.default = {
}
```
Result:
```
╭ custom title ────────────────────╮
│a b │
│c d │
│ │
│ │
│ │
╰──────────────────────────────────╯
```
#### Example: Render a custom dynamic table
```lua
@ -309,6 +384,23 @@ xplr.fn.custom.render_layout = function(ctx)
end
```
Result:
```
╭/home/sayanarijit───────────────────────────╮
│ │
│Layout height 12 │
│Layout width 46 │
│ │
│Screen height 12 │
│Screen width 46 │
│ │
│ │
│ │
│ │
╰────────────────────────────────────────────╯
```
### CustomLayout
A whole custom layout to render. It doesn't make sense to use it as a
@ -347,6 +439,40 @@ xplr.fn.custom.render_layout = function(ctx)
end
```
Result:
```
╭─────────────────────╮╭─────────────────────╮
│Try your luck... ││Press ctrl-r │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
╰─────────────────────╯╰─────────────────────╯
```
Or
```
╭────────────────────────────────────────────╮
│Try your luck... │
│ │
│ │
│ │
╰────────────────────────────────────────────╯
╭────────────────────────────────────────────╮
│Press ctrl-r │
│ │
│ │
│ │
╰────────────────────────────────────────────╯
```
## Panel UI Config
It contains the following optional fields:
@ -429,8 +555,6 @@ Hence, only the following fields are available.
[20]: #vertical_margin
[21]: #constraints
[22]: #constraint
[23]: https://s6.gifyu.com/images/layout.png
[24]: https://gifyu.com/image/1X38
[25]: #static
[26]: #dynamic
[27]: #custom-panel

@ -12,15 +12,13 @@ You can add new panels in `xplr.config.layouts.custom`.
##### Example: Defining Custom Layout
![demo](https://s6.gifyu.com/images/layout.png)
```lua
xplr.config.layouts.builtin.default = {
Horizontal = {
config = {
margin = 1,
horizontal_margin = 2,
vertical_margin = 3,
horizontal_margin = 1,
vertical_margin = 1,
constraints = {
{ Percentage = 50 },
{ Percentage = 50 },
@ -34,6 +32,21 @@ xplr.config.layouts.builtin.default = {
}
```
Result:
```
╭ /home ─────────────╮╭ Help [default] ────╮
│ ╭─── path ││. show hidden │
│ ├▸[ð Desktop/] ││/ search │
│ ├ ð Documents/ ││: action │
│ ├ ð Downloads/ ││? global help │
│ ├ ð GitHub/ ││G go to bottom │
│ ├ ð Music/ ││V select/unselect│
│ ├ ð Pictures/ ││ctrl duplicate as │
│ ├ ð Public/ ││ctrl next visit │
╰────────────────────╯╰────────────────────╯
```
#### xplr.config.layouts.builtin.default
The default layout

@ -29,23 +29,23 @@ them, but only a few modern programming languages allow nesting other types
into a sum type.
```rust
enum Result {
Ok,
Err,
enum Color {
Red,
Green,
}
```
Here, `Result` can be one of two possible set of values: `Ok` and `Err`, just
like `boolean`, but unlike `boolean`, being tagged allows `Result` to have more
Here, `Color` can be one of two possible set of values: `Red` and `Green`, just
like `boolean`, but unlike `boolean`, being tagged allows `Color` to have more
than two variants if required, by changing the definition.
e.g.
```rust
enum Result {
Ok,
Err,
Pending,
enum Color {
Red,
Green,
Blue,
}
```
@ -53,35 +53,39 @@ We'd document it here as:
> Result is a sum type that can be one of the following:
>
> - "Ok"
> - "Err"
> - "Pending"
> - "Red"
> - "Green"
> - "Blue"
But some languages (like Rust, Haskell, Elm etc.) go even further, allowing us
to associate each branch of the enum with further nested types like:
```rust
enum Result {
Ok(bool),
Err(Error),
Pending,
enum Layout {
Table,
HelpMenu,
Horizontal {
config: LayoutConfig, // A product type (similar to class/struct)
splits: Vec<Layout> // A list of "Layout"s (i.e. list of sum types)
},
}
```
Here, as we can see, unlike the first example, some of `Result`'s possible
variants can have further nested types associated with them. Note that `Error`
here can be either a sum type (e.g. enum), or a product type (e.g.
class/struct), but whatever it is, it will only exist when `Result` is `Err`.
Here, as we can see, unlike the first example, some of `Layout`'s possible
variants can have further nested types associated with them. Note that
`Horizontal` here can have a sum type (e.g. enum), or a product type (e.g.
class/struct), or both (any number of them actually) nested in it. But the
nested values will only exist when `Layout` is `Horizontal`.
We'd document it here as:
> Result is a sum type that can be one of the following:
> Layout is a sum type that can be one of the following:
>
> - { Ok = bool }
> - { Err = Error }
> - "Pending"
> - "Table"
> - "HelpMenu"
> - { Horizontal = { config = Layout Config, splits = { Layout, ... } }
And then we'd go on documenting whatever `Error` is.
And then we'd go on documenting whatever `Layout Config` is.
So, there you go. This is exactly what sum types are - glorified enums that can
have nested types in each branch.
@ -89,7 +93,7 @@ have nested types in each branch.
---
If you're still confused about something, or if you found an error in this
explaination, feel free to [discuss together][5].
explanation, feel free to [discuss together][5].
[1]: https://en.wikipedia.org/wiki/Tagged_union
[2]: layout.md

@ -45,7 +45,7 @@ compatibility.
### Instructions
#### [v0.20.2][48] -> [v0.21.2][49]
#### [v0.20.2][48] -> [v0.21.3][49]
- Some plugins might stop rendering colors. Wait for them to update.
- Rename `xplr.config.general.sort_and_filter_ui.search_identifier` to
@ -81,7 +81,7 @@ compatibility.
- `:ss` to create softlink of the selected items.
- `:sh` to create hardlink of the selected items.
- `:se` to edit selection list in your $EDITOR.
- Better conflict handling: add suffix rather than overriding/skipping.
- Better conflict handling: prompt for action.
- Navigate between the selected paths using the following messages:
- FocusPreviousSelection (`ctrl-p`)
- FocusNextSelection (`ctrl-n`)
@ -118,6 +118,7 @@ compatibility.
- xplr.util.shell_escape
- Executables will me marked with the mime type: `application/x-executable`.
- macOS legacy coreutils will be generally supported, but please update it.
- Since v0.21.2 you can use the on_selection_change hook.
Thanks to @noahmayr for contributing to a major part of this release.
@ -516,5 +517,5 @@ Else do the following:
[46]: https://github.com/sayanarijit/xplr/releases/tag/v0.18.0
[47]: https://github.com/sayanarijit/xplr/releases/tag/v0.19.4
[48]: https://github.com/sayanarijit/xplr/releases/tag/v0.20.2
[49]: https://github.com/sayanarijit/xplr/releases/tag/v0.21.2
[49]: https://github.com/sayanarijit/xplr/releases/tag/v0.21.3
[50]: https://github.com/lotabout/skim#search-syntax

@ -151,7 +151,7 @@
<li class="nav-item">
<a
class="nav-link page-scroll"
href="https://xplr.stck.me"
href="https://blog.xplr.dev"
target="_blank"
rel="noreferrer noopener"
>

@ -1,97 +1,12 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nix": {
"inputs": {
"lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1676545802,
"narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=",
"owner": "domenkozar",
"repo": "nix",
"rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "relaxed-flakes",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1657693803,
"narHash": "sha256-G++2CJ9u0E7NNTAi9n5G8TdDmGJXcIjkJ3NF8cetQB8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "365e1b3a859281cf11b94f87231adeabbdd878a2",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05-small",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1684472219,
"narHash": "sha256-cBTmj5Ad5pkU41GkyW/id7/A+s5FUlvj9jr3A35+lIE=",
"lastModified": 1689422397,
"narHash": "sha256-fnopownlSBGTBYxGdTdUPM215yG/UEEj3wgheBLIbHs=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "9c82602247a58de900df71fdcc0cfdae2bf29189",
"rev": "45ae0efbbce2aada6d5e8de6ace0c803b08ac9c7",
"type": "github"
},
"original": {
@ -102,9 +17,7 @@
},
"root": {
"inputs": {
"flake-compat": "flake-compat",
"nix": "nix",
"nixpkgs": "nixpkgs_2"
"nixpkgs": "nixpkgs"
}
}
},

@ -3,30 +3,26 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs";
nix.url = "github:domenkozar/nix/relaxed-flakes";
flake-compat = {
url = "github:edolstra/flake-compat";
flake = false;
};
};
outputs = { self, nixpkgs, nix, ... }:
outputs = inputs@{ self, nixpkgs, ... }:
let
systems = [
"x86_64-linux"
"i686-linux"
"x86_64-darwin"
"aarch64-linux"
"aarch64-darwin"
];
forAllSystems = f: builtins.listToAttrs (map (name: { inherit name; value = f name; }) systems);
lib = nixpkgs.lib;
darwin = [ "x86_64-darwin" "aarch64-darwin" ];
linux = [ "x86_64-linux" "x86_64-linux-musl" "aarch64-linux" "aarch64-linux-android" "i86_64-linux" ];
allSystems = darwin ++ linux;
forEachSystem = systems: f: lib.genAttrs systems (system: f system);
forAllSystems = forEachSystem allSystems;
in
{
packages = forAllSystems (system:
let
pkgs = import nixpkgs { inherit system; };
in
{
rec {
# e.g. nix build .#xplr
xplr = pkgs.rustPlatform.buildRustPackage rec {
name = "xplr";
src = ./.;
@ -34,6 +30,14 @@
lockFile = ./Cargo.lock;
};
};
# e.g. nix build .#cross.x86_64-linux-musl.xplr --impure
cross = forEachSystem (lib.filter (sys: sys != system) allSystems) (targetSystem:
let
crossPkgs = import nixpkgs { localSystem = system; crossSystem = targetSystem; };
in
{ inherit (crossPkgs) xplr; }
);
}
);
defaultPackage = forAllSystems (system: self.packages.${system}.xplr);
@ -54,6 +58,9 @@
default = pkgs.mkShell {
RUST_BACKTRACE = 1;
# For cross compilation
NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM = 1;
buildInputs = devRequirements;
packages = devRequirements;
};

@ -1034,19 +1034,23 @@ impl App {
}
fn enter(self) -> Result<Self> {
if let Some(path) = self.focused_node().map(|n| n.absolute_path.clone()) {
self.change_directory(&path, true)
if let Some(node) = self.focused_node() {
if node.is_dir || node.symlink.as_ref().map(|s| s.is_dir).unwrap_or(false) {
let path = node.absolute_path.clone();
self.change_directory(&path, true)
} else {
Ok(self)
}
} else {
Ok(self)
}
}
fn back(self) -> Result<Self> {
if let Some(p) = PathBuf::from(self.pwd.clone())
.parent()
.and_then(|p| p.to_str())
{
self.change_directory(p, true)
let pwd = self.pwd.clone();
if let Some(p) = PathBuf::from(&pwd).parent().and_then(|p| p.to_str()) {
self.change_directory(p, false)
.and_then(|a| a.focus_path(&pwd, true))
} else {
Ok(self)
}
@ -1486,7 +1490,7 @@ impl App {
if dir.parent == self.pwd {
self.directory_buffer = Some(dir);
// Migh as well refresh the selection
// Might as well refresh the selection
self = self.refresh_selection()?;
};
@ -2035,8 +2039,10 @@ impl App {
let read_only = self.config.general.read_only;
let global_kb = &self.config.general.global_key_bindings;
builtin.into_iter()
.chain(custom.into_iter())
let modes = builtin.into_iter().chain(custom.into_iter());
std::iter::once((self.mode.name.clone(), self.mode.clone()))
.chain(modes)
.map(|(name, mode)| {
(name, mode.sanitized(read_only, global_kb.clone()))
})

@ -206,12 +206,10 @@ pub fn print_msg_in(args: Vec<String>) -> Result<()> {
fn fmt_msg_in(args: Vec<String>) -> Result<String> {
let msg = match jf::format(args.into_iter().map(Into::into)) {
Ok(msg) => msg,
Err(jf::Error::Usage) => {
bail!("usage: xplr -m TEMPLATE [VALUE]... [NAME=VALUE]...")
}
Err(jf::Error::Jf(e)) => bail!("xplr -m: {e}"),
Err(jf::Error::Json(e)) => bail!("xplr -m: json: {e}"),
Err(jf::Error::Yaml(e)) => bail!("xplr -m: yaml: {e}"),
Err(jf::Error::Io(e)) => bail!("xplr -m: io: {e}"),
};
// validate

@ -680,11 +680,15 @@ xplr.config.general.initial_sorting = {
}
-- The name of one of the modes to use when xplr loads.
-- This isn't the default mode. To modify the default mode, overwrite
-- [xplr.config.modes.builtin.default](https://xplr.dev/en/modes#xplrconfigmodesbuiltindefault).
--
-- Type: nullable string
xplr.config.general.initial_mode = "default"
-- The name of one of the layouts to use when xplr loads.
-- This isn't the default layout. To modify the default layout, overwrite
-- [xplr.config.layouts.builtin.default](https://xplr.dev/en/layouts#xplrconfiglayoutsbuiltindefault).
--
-- Type: nullable string
xplr.config.general.initial_layout = "default"
@ -701,6 +705,17 @@ xplr.config.general.start_fifo = nil
-- Type: [Key Bindings](https://xplr.dev/en/configure-key-bindings#key-bindings)
xplr.config.general.global_key_bindings = {
on_key = {
["f1"] = {
help = "global help menu",
messages = {
{
BashExec = [===[
[ -z "$PAGER" ] && PAGER="less -+F"
cat -- "${XPLR_PIPE_GLOBAL_HELP_MENU_OUT}" | ${PAGER:?}
]===],
},
},
},
["esc"] = {
messages = {
"PopMode",
@ -852,15 +867,13 @@ xplr.config.node_types.special = {}
--
-- ##### Example: Defining Custom Layout
--
-- ![demo](https://s6.gifyu.com/images/layout.png)
--
-- ```lua
-- xplr.config.layouts.builtin.default = {
-- Horizontal = {
-- config = {
-- margin = 1,
-- horizontal_margin = 2,
-- vertical_margin = 3,
-- horizontal_margin = 1,
-- vertical_margin = 1,
-- constraints = {
-- { Percentage = 50 },
-- { Percentage = 50 },
@ -873,6 +886,21 @@ xplr.config.node_types.special = {}
-- }
-- }
-- ```
--
-- Result:
--
-- ```
-- ╭ /home ─────────────╮╭ Help [default] ────╮
-- │ ╭─── path ││. show hidden │
-- │ ├▸[ð Desktop/] ││/ search │
-- │ ├ ð Documents/ ││: action │
-- │ ├ ð Downloads/ ││? global help │
-- │ ├ ð GitHub/ ││G go to bottom │
-- │ ├ ð Music/ ││V select/unselect│
-- │ ├ ð Pictures/ ││ctrl duplicate as │
-- │ ├ ð Public/ ││ctrl next visit │
-- ╰────────────────────╯╰────────────────────╯
-- ```
-- The default layout
--
@ -1062,17 +1090,6 @@ xplr.config.modes.builtin.default = {
{ SwitchModeBuiltin = "action" },
},
},
["?"] = {
help = "global help menu",
messages = {
{
BashExec = [===[
[ -z "$PAGER" ] && PAGER="less -+F"
cat -- "${XPLR_PIPE_GLOBAL_HELP_MENU_OUT}" | ${PAGER:?}
]===],
},
},
},
["G"] = {
help = "go to bottom",
messages = {
@ -1295,6 +1312,8 @@ xplr.config.modes.builtin.default.key_bindings.on_key["l"] =
xplr.config.modes.builtin.default.key_bindings.on_key["right"]
xplr.config.modes.builtin.default.key_bindings.on_key["tab"] =
xplr.config.modes.builtin.default.key_bindings.on_key["ctrl-i"] -- compatibility workaround
xplr.config.modes.builtin.default.key_bindings.on_key["?"] =
xplr.config.general.global_key_bindings.on_key["f1"]
-- The builtin debug error mode.
--
@ -1978,7 +1997,9 @@ xplr.config.modes.builtin.delete = {
messages = {
{
BashExec0 = [===[
cat "${XPLR_PIPE_RESULT_OUT:?}" | xargs -0 printf '%q\n'
while IFS= read -r -d '' PTH; do
printf '%q\n' "$PTH"
done < "${XPLR_PIPE_RESULT_OUT:?}"
echo
read -p "Permanently delete these files? [Y/n]: " ANS
[ "${ANS:-Y}" = "Y" ] || [ "$ANS" = "y" ] || exit 0
@ -2006,7 +2027,9 @@ xplr.config.modes.builtin.delete = {
messages = {
{
BashExec0 = [===[
cat "${XPLR_PIPE_RESULT_OUT:?}" | xargs -0 printf '%q\n'
while IFS= read -r -d '' PTH; do
printf '%q\n' "$PTH"
done < "${XPLR_PIPE_RESULT_OUT:?}"
echo
read -p "Permanently delete these files? [Y/n]: " ANS
[ "${ANS:-Y}" = "Y" ] || [ "$ANS" = "y" ] || exit 0
@ -2967,7 +2990,8 @@ xplr.fn.builtin.fmt_general_table_row_cols_1 = function(m)
if m.is_broken then
r = r .. "×"
else
local symlink_path = xplr.util.shorten(m.symlink.absolute_path)
local symlink_path =
xplr.util.shorten(m.symlink.absolute_path, { base = m.parent })
if m.symlink.is_dir then
symlink_path = symlink_path .. "/"
end
@ -3076,6 +3100,14 @@ xplr.fn.custom = {}
-- { LogSuccess = "Switched layout" },
-- { CallLuaSilently = "custom.some_plugin_with_hooks.on_layout_switch" },
-- }
--
-- -- Add messages to send when the selection changes
-- --
-- -- Type: list of [Message](https://xplr.dev/en/message#message)s
-- on_selection_change = {
-- { LogSuccess = "Selection changed" },
-- { CallLuaSilently = "custom.some_plugin_with_hooks.on_selection_change" },
-- }
-- }
-- ```

@ -160,24 +160,24 @@ mod tests {
assert!(check_version(VERSION, "foo path").is_ok());
// Current release if OK
assert!(check_version("0.21.2", "foo path").is_ok());
assert!(check_version("0.21.3", "foo path").is_ok());
// Prev major release is ERR
// - Not yet
// Prev minor release is ERR (Change when we get to v1)
assert!(check_version("0.20.2", "foo path").is_err());
assert!(check_version("0.20.3", "foo path").is_err());
// Prev bugfix release is OK
assert!(check_version("0.21.1", "foo path").is_ok());
assert!(check_version("0.21.2", "foo path").is_ok());
// Next major release is ERR
assert!(check_version("1.20.2", "foo path").is_err());
assert!(check_version("1.20.3", "foo path").is_err());
// Next minor release is ERR
assert!(check_version("0.22.2", "foo path").is_err());
assert!(check_version("0.22.3", "foo path").is_err());
// Next bugfix release is ERR (Change when we get to v1)
assert!(check_version("0.21.3", "foo path").is_err());
assert!(check_version("0.21.4", "foo path").is_err());
}
}

@ -216,17 +216,20 @@ mod tests {
#[test]
fn test_relative_to_parent() {
let path = std::env::current_dir().unwrap();
let path = std::env::current_dir().unwrap().join("docs");
let parent = path.parent().unwrap();
let relative = relative_to(parent, NONE).unwrap();
let base = default().with_base(path.to_str().unwrap());
let relative = relative_to(parent, Some(&base)).unwrap();
assert_eq!(relative, PathBuf::from(".."));
let relative = relative_to(parent, Some(&default().with_prefix_dots())).unwrap();
let relative =
relative_to(parent, Some(&base.clone().with_prefix_dots())).unwrap();
assert_eq!(relative, PathBuf::from(".."));
let relative =
relative_to(parent, Some(&default().without_suffix_dots())).unwrap();
relative_to(parent, Some(&base.clone().without_suffix_dots())).unwrap();
assert_eq!(
relative,
PathBuf::from("../..").join(parent.file_name().unwrap())
@ -234,12 +237,12 @@ mod tests {
let relative = relative_to(
parent,
Some(&default().with_prefix_dots().without_suffix_dots()),
Some(&base.clone().with_prefix_dots().without_suffix_dots()),
)
.unwrap();
assert_eq!(
relative,
PathBuf::from("../..").join(parent.file_name().unwrap())
PathBuf::from("../..").join(parent.clone().file_name().unwrap())
);
}

@ -40,15 +40,14 @@ pub fn get_tty() -> Result<fs::File> {
// returns physical path. As a workaround, this function tries to use `PWD`
// environment variable that is configured by shell.
fn get_current_dir() -> Result<PathBuf, std::io::Error> {
let cur = std::env::current_dir();
if let Ok(pwd) = std::env::var("PWD") {
if pwd.is_empty() {
cur
std::env::current_dir()
} else {
Ok(PathBuf::from(pwd))
}
} else {
cur
std::env::current_dir()
}
}

@ -20,7 +20,7 @@ use tui::backend::Backend;
use tui::layout::Rect as TuiRect;
use tui::layout::{Constraint as TuiConstraint, Direction, Layout as TuiLayout};
use tui::style::{Color, Modifier as TuiModifier, Style as TuiStyle};
use tui::text::{Span, Spans, Text};
use tui::text::{Line, Span, Text};
use tui::widgets::{
Block, BorderType as TuiBorderType, Borders as TuiBorders, Cell, List, ListItem,
Paragraph, Row, Table,
@ -329,6 +329,7 @@ impl Into<TuiStyle> for Style {
TuiStyle {
fg: self.fg,
bg: self.bg,
underline_color: None,
add_modifier: TuiModifier::from_bits_truncate(xor(self.add_modifiers)),
sub_modifier: TuiModifier::from_bits_truncate(xor(self.sub_modifiers)),
}
@ -401,22 +402,22 @@ impl Into<nu_ansi_term::Style> for Style {
style.add_modifiers.as_ref().map_or(false, f)
}
nu_ansi_term::Style {
foreground: self.fg.and_then(convert_color),
background: self.bg.and_then(convert_color),
is_bold: match_modifiers(&self, |m| m.contains(&Modifier::Bold)),
is_dimmed: match_modifiers(&self, |m| m.contains(&Modifier::Dim)),
is_italic: match_modifiers(&self, |m| m.contains(&Modifier::Italic)),
is_underline: match_modifiers(&self, |m| m.contains(&Modifier::Underlined)),
is_blink: match_modifiers(&self, |m| {
m.contains(&Modifier::SlowBlink) || m.contains(&Modifier::RapidBlink)
}),
is_reverse: match_modifiers(&self, |m| m.contains(&Modifier::Reversed)),
is_hidden: match_modifiers(&self, |m| m.contains(&Modifier::Hidden)),
is_strikethrough: match_modifiers(&self, |m| {
m.contains(&Modifier::CrossedOut)
}),
}
let mut style = nu_ansi_term::Style::new();
style.foreground = self.fg.and_then(convert_color);
style.background = self.bg.and_then(convert_color);
style.is_bold = match_modifiers(&self, |m| m.contains(&Modifier::Bold));
style.is_dimmed = match_modifiers(&self, |m| m.contains(&Modifier::Dim));
style.is_italic = match_modifiers(&self, |m| m.contains(&Modifier::Italic));
style.is_underline =
match_modifiers(&self, |m| m.contains(&Modifier::Underlined));
style.is_blink = match_modifiers(&self, |m| {
m.contains(&Modifier::SlowBlink) || m.contains(&Modifier::RapidBlink)
});
style.is_reverse = match_modifiers(&self, |m| m.contains(&Modifier::Reversed));
style.is_hidden = match_modifiers(&self, |m| m.contains(&Modifier::Hidden));
style.is_strikethrough =
match_modifiers(&self, |m| m.contains(&Modifier::CrossedOut));
style
}
}
@ -669,6 +670,7 @@ fn draw_table<B: Backend>(
let header_height = app_config.general.table.header.height.unwrap_or(1);
let height: usize =
(layout_size.height.max(header_height + 2) - (header_height + 2)).into();
let row_style = app_config.general.table.row.style.to_owned();
let rows = app
.directory_buffer
@ -767,17 +769,17 @@ fn draw_table<B: Backend>(
c.format.as_ref().map(|f| {
let out = lua::call(lua, f, v.clone())
.unwrap_or_else(|e| format!("{e:?}"));
string_to_text(out)
(string_to_text(out), c.style.to_owned())
})
})
.collect::<Vec<Text>>()
.collect::<Vec<(Text, Style)>>()
})
.unwrap_or_default()
.iter()
.map(|x| Cell::from(x.to_owned()))
.into_iter()
.map(|(text, style)| Cell::from(text).style(style.into()))
.collect::<Vec<Cell>>();
Row::new(cols)
Row::new(cols).style(row_style.to_owned().into())
})
.collect::<Vec<Row>>()
})
@ -831,7 +833,10 @@ fn draw_table<B: Backend>(
.to_owned()
.unwrap_or_default()
.iter()
.map(|c| Cell::from(c.format.to_owned().unwrap_or_default()))
.map(|c| {
Cell::from(c.format.to_owned().unwrap_or_default())
.style(c.style.to_owned().into())
})
.collect::<Vec<Cell>>(),
)
.height(header_height)
@ -878,7 +883,10 @@ fn draw_selection<B: Backend>(
.unwrap_or_else(|| n.absolute_path.clone());
string_to_text(out)
})
.map(ListItem::new)
.map(|i| {
ListItem::new(i)
.style(app.config.general.selection.item.style.to_owned().into())
})
.collect();
// Selected items
@ -973,7 +981,7 @@ fn draw_input_buffer<B: Backend>(
let width = layout_size.width.max(offset_width) - offset_width;
let scroll = input.visual_scroll(width.into()) as u16;
let input_buf = Paragraph::new(Spans::from(vec![
let input_buf = Paragraph::new(Line::from(vec![
Span::styled(
app.input.prompt.to_owned(),
app.config.general.prompt.style.to_owned().into(),
@ -1114,7 +1122,7 @@ fn draw_sort_n_filter<B: Backend>(
format!("({item_count}) ")
};
let p = Paragraph::new(Spans::from(spans))
let p = Paragraph::new(Line::from(spans))
.block(block(config, format!(" Sort & filter {item_count}")));
f.render_widget(p, layout_size);

Loading…
Cancel
Save