1
0
mirror of https://github.com/chylex/Bark-Browser.git synced 2025-09-15 23:32:11 +02:00

Compare commits

..

8 Commits

12 changed files with 140 additions and 28 deletions

BIN
.github/readme/screenshot.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/.idea/
/out/
/target/

View File

@@ -2,10 +2,8 @@ FROM rust:1.71.0 as builder
WORKDIR /app
COPY . .
RUN cargo build --release
RUN ./scripts/build.sh
FROM scratch as exporter
COPY --from=builder /app/target/release/bark .
# docker build --output out .
COPY --from=builder /app/out/ .

44
README.md Normal file
View File

@@ -0,0 +1,44 @@
# Bark
`bark` is a tree-based terminal filesystem browser and file manager with `vim`-style key bindings.
![Bark Browser Screenshot](.github/readme/screenshot.png)
# Features
- `ls`-style file listing
- `vim`-style navigation adapted for tree hierarchies
- Basic file management (create, rename, edit, delete)
- Support for Linux and Windows
See [action/mod.rs](https://github.com/chylex/Bark-Browser/blob/main/src/component/filesystem/action/mod.rs) for an up-to-date list of all key bindings.
# Roadmap
- Settings
- File search
- Visual mode for selecting multiple files
- Ex commands for more complex operations
- Directory statistics (total size, number of files, etc.)
- Tree filtering (views that only include certain files)
- Rebindable keys and macros
# Building
1. Install [Rust](https://www.rust-lang.org/tools/install).
2. Run `cargo run` to launch the application.
3. Run `scripts/build.sh` or `scripts/build.bat` to build a release binary into the `out/` folder.
## Windows Subsystem for Linux
Run `scripts/wsl.sh` from a Debian-based WSL environment to quickly install Rust and CMake into WSL.
## Docker
Run `docker build --output out .` to build a release binary into the `out/` folder on the host. BuildKit is required.
# Contributing
This project exists 1) because I couldn't find any tree-based file manager I liked and 2) because I wanted to have fun writing Rust, and I don't really want to spend time reading and reviewing pull requests.
For now, issues are closed, and I'm not accepting any major contributions — especially ones related to the roadmap. If you have a small idea, issue, or pull request, feel free to start a [discussion](https://github.com/chylex/Bark-Browser/discussions).

22
scripts/build.ps1 Normal file
View File

@@ -0,0 +1,22 @@
Set-Location (Split-Path $PSScriptRoot -Parent)
if (!(Test-Path "out")) {
mkdir "out"
}
cargo build --release
$version = & "target\release\bark.exe" --version
$arch = switch ((Get-WMIObject -Class Win32_Processor).Architecture) {
0 { "x86" }
1 { "mips" }
2 { "alpha" }
3 { "ppc" }
5 { "arm" }
6 { "ia64" }
9 { "x64" }
12 { "arm64" }
DEFAULT { "unknown" }
}
Compress-Archive -Force -Path "target\release\bark.exe" -DestinationPath "out\bark-${version}-windows-${arch}.zip"

20
scripts/build.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
cd ..
mkdir -p out
cargo build --release
cd target/release
VERSION=$(./bark --version)
ARCH=$(uname -m)
if [[ "$ARCH" == "x86_64" ]]; then
ARCH="x64"
elif [[ "$ARCH" == "aarch64" ]]; then
ARCH="arm64"
fi
tar czf "../../out/bark-${VERSION}-linux-${ARCH}.tar.gz" --owner=0 --group=0 -- bark

7
wsl.sh → scripts/wsl.sh Normal file → Executable file
View File

@@ -6,6 +6,7 @@ sudo apt-get install -y \
apt-transport-https \
build-essential \
ca-certificates \
cmake \
gdb \
gnupg \
software-properties-common \
@@ -15,9 +16,3 @@ sudo apt-get install -y \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
rustup component add rust-src
# CMake
wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add -
sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main'
sudo apt-get update
sudo apt-get install -y cmake

View File

@@ -53,11 +53,7 @@ impl<'a> Layer for InputFieldDialogLayer<'a> {
}
_ => {
if self.field.handle_input(key_binding) {
ActionResult::Draw
} else {
ActionResult::Nothing
}
ActionResult::draw_if(self.field.handle_input(key_binding))
}
}
}

View File

@@ -53,7 +53,7 @@ fn handle_delete_view_node(layer: &mut FsLayer, view_node_id: NodeId) {
let view = &mut layer.tree.view;
if layer.selected_view_node_id == view_node_id {
layer.selected_view_node_id = view.get_node_above_id(view_node_id).unwrap_or_else(|| view.root_id());
layer.selected_view_node_id = view.get_node_below_id(view_node_id).or_else(|| view.get_node_above_id(view_node_id)).unwrap_or_else(|| view.root_id());
}
if let Some(view_node) = view.remove(view_node_id) {

View File

@@ -209,7 +209,7 @@ impl<'a> StatefulWidget for InputFieldWidget<'a> {
}
let style = Style::default()
.fg(Color::White)
.fg(Color::Black)
.bg(self.default_background);
Clear.render(area, buf);
@@ -222,7 +222,7 @@ impl<'a> StatefulWidget for InputFieldWidget<'a> {
if has_truncated_end {
buf.get_mut(area.right().saturating_sub(1), area.y)
.set_char('~')
.set_fg(Color::White)
.set_fg(Color::Black)
.set_bg(self.trimmed_background);
}

View File

@@ -1,5 +1,7 @@
use crossterm::event::{KeyCode, KeyModifiers};
use ratatui::style::Color;
use ratatui::layout::Rect;
use ratatui::style::{Color, Style};
use ratatui::widgets::Paragraph;
use crate::component::input::InputField;
use crate::input::keymap::KeyBinding;
@@ -8,21 +10,23 @@ use crate::state::Environment;
use crate::state::layer::Layer;
use crate::state::view::Frame;
pub struct InputFieldOverlayLayer {
pub struct InputFieldOverlayLayer<'a> {
field: InputField,
read_only_prefix: &'a str,
confirm_action: Box<dyn Fn(String) -> ActionResult>,
}
impl InputFieldOverlayLayer {
pub fn new(confirm_action: Box<dyn Fn(String) -> ActionResult>) -> Self {
impl<'a> InputFieldOverlayLayer<'a> {
pub fn new(read_only_prefix: &'a str, confirm_action: Box<dyn Fn(String) -> ActionResult>) -> Self {
Self {
field: InputField::new(),
read_only_prefix,
confirm_action,
}
}
}
impl Layer for InputFieldOverlayLayer {
impl<'a> Layer for InputFieldOverlayLayer<'a> {
#[allow(clippy::wildcard_enum_match_arm)]
fn handle_input(&mut self, _environment: &Environment, key_binding: KeyBinding) -> ActionResult {
match (key_binding.code(), key_binding.modifiers()) {
@@ -35,18 +39,40 @@ impl Layer for InputFieldOverlayLayer {
(self.confirm_action)(self.field.text().to_owned())
}
_ => {
if self.field.handle_input(key_binding) {
ActionResult::Draw
(KeyCode::Backspace, KeyModifiers::NONE) => {
if self.field.text().is_empty() {
ActionResult::PopLayer
} else {
ActionResult::Nothing
ActionResult::draw_if(self.field.handle_input(key_binding))
}
}
_ => {
ActionResult::draw_if(self.field.handle_input(key_binding))
}
}
}
fn render(&mut self, frame: &mut Frame) {
let size = frame.size();
self.field.render(frame, size.x, size.bottom().saturating_sub(1), size.width, Color::LightYellow, Color::Yellow);
if size.width < 1 || size.height < 1 {
return;
}
let x = size.x;
let y = size.bottom().saturating_sub(1);
let prefix_style = Style::new()
.fg(Color::Black)
.bg(Color::LightYellow);
let prefix_paragraph = Paragraph::new(self.read_only_prefix)
.style(prefix_style);
frame.render_widget(prefix_paragraph, Rect { x, y, width: 1, height: 1 });
if size.width > 1 {
self.field.render(frame, x.saturating_add(1), y, size.width.saturating_sub(1), Color::LightYellow, Color::Yellow);
}
}
}

View File

@@ -13,3 +13,13 @@ pub enum ActionResult {
ReplaceLayer(Box<dyn Layer>),
PopLayer,
}
impl ActionResult {
pub const fn draw_if(condition: bool) -> Self {
if condition {
Self::Draw
} else {
Self::Nothing
}
}
}