tabula rasa

This commit is contained in:
🪞👃🪞 2025-03-02 18:00:19 +02:00
commit 2b855f43d7
9 changed files with 1601 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target

1325
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

13
Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "taggart"
version = "0.1.0"
edition = "2024"
[dependencies]
tek_tui = { git = "https://codeberg.org/unspeaker/tengri", ref = "47b3413" }
clap = { version = "4.5.4", features = [ "cargo" ] }
walkdir = "2"
id3 = "1.16"
moku = "0.2"
file_type = "0.7"
pad = "0.1"

34
shell.nix Executable file
View file

@ -0,0 +1,34 @@
#!/usr/bin/env nix-shell
{pkgs?import<nixpkgs>{}}:let
stdenv = pkgs.clang19Stdenv;
name = "tek";
nativeBuildInputs = with pkgs; [ pkg-config freetype libclang ];
buildInputs = with pkgs; let
#suil = pkgs.enableDebugging (pkgs.suil.overrideAttrs (a: b: {
#dontStrip = true; separateDebugInfo = true;
#}));
in [ jack2 lilv serd libclang /*suil*/ glib gtk3 ];
VST3_SDK_DIR = "/home/user/Lab/Music/tek/vst3sdk/";
LIBCLANG_PATH = "${pkgs.libclang.lib.outPath}/lib";
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (with pkgs; [
pipewire.jack
# for ChowKick.lv2:
freetype
libgcc.lib
# for Panagement
xorg.libX11
xorg.libXcursor
xorg.libXi
libxkbcommon
#suil
# for Helm:
alsa-lib
curl
libglvnd
#xorg_sys_opengl
]);
in pkgs.mkShell.override {
inherit stdenv;
} {
inherit name nativeBuildInputs buildInputs VST3_SDK_DIR LIBCLANG_PATH LD_LIBRARY_PATH;
}

0
src/keys.edn Normal file
View file

66
src/keys.rs Normal file
View file

@ -0,0 +1,66 @@
use crate::*;
impl Handle<TuiIn> for Taggart {
fn handle (&mut self, input: &TuiIn) -> Perhaps<bool> {
Ok(match &*input.event() {
Event::Key(KeyEvent {
code: KeyCode::Up,
kind: KeyEventKind::Press,
modifiers: KeyModifiers::NONE,
state: KeyEventState::NONE
}) => {
self.cursor = self.cursor.saturating_sub(1);
None
},
Event::Key(KeyEvent {
code: KeyCode::Down,
kind: KeyEventKind::Press,
modifiers: KeyModifiers::NONE,
state: KeyEventState::NONE
}) => {
self.cursor = self.cursor + 1;
None
},
Event::Key(KeyEvent {
code: KeyCode::PageUp,
kind: KeyEventKind::Press,
modifiers: KeyModifiers::NONE,
state: KeyEventState::NONE
}) => {
self.offset = self.offset.saturating_sub(5);
None
},
Event::Key(KeyEvent {
code: KeyCode::PageDown,
kind: KeyEventKind::Press,
modifiers: KeyModifiers::NONE,
state: KeyEventState::NONE
}) => {
self.offset += 5;
None
},
_ => None
})
}
}
#[moku::state_machine]
mod taggart {
use moku::*;
#[machine_module] mod machine {}
use self::machine::{TaggartState, TopSuperstates};
struct Top;
impl TopState<TaggartState> for Top {}
struct Tree(usize);
#[superstate(Top)] impl State<TaggartState> for Tree {
fn enter (_: &mut TopSuperstates<'_>) -> StateEntry<Self, TaggartState> {
StateEntry::State(Self(0))
}
}
struct File(usize);
#[superstate(Top)] impl State<TaggartState> for File {
fn enter (_: &mut TopSuperstates<'_>) -> StateEntry<Self, TaggartState> {
StateEntry::State(Self(0))
}
}
}

123
src/main.rs Normal file
View file

@ -0,0 +1,123 @@
#![feature(os_str_display)]
use tek_tui::*;
use tek_tui::tek_input::*;
use tek_tui::tek_output::*;
use crate::crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventState, KeyEventKind};
use clap::{arg, command, value_parser, ArgAction, Command};
use walkdir::WalkDir;
use std::sync::{Arc, RwLock};
use std::path::{Path, PathBuf};
use std::env::current_dir;
mod keys;
mod view;
type Usually<T> = std::result::Result<T, Box<dyn std::error::Error>>;
type Perhaps<T> = Usually<Option<T>>;
fn cli () -> clap::Command {
command!()
.arg(arg!([path] "Path to root directory").value_parser(value_parser!(PathBuf)))
}
struct Taggart {
root: PathBuf,
paths: Vec<Entry>,
cursor: usize,
offset: usize,
}
#[derive(Ord, Eq, PartialEq, PartialOrd, Default)]
struct Entry {
path: PathBuf,
is_dir: bool,
is_mus: bool,
is_img: bool,
depth: usize,
}
fn main () -> Usually<()> {
let args = cli().get_matches();
let path = args.get_one::<PathBuf>("path");
let state = Arc::new(RwLock::new(Taggart::new(path)?));
Tui::new()?.run(&state)
}
impl Taggart {
fn new (root: Option<&impl AsRef<Path>>) -> Usually<Self> {
let root = if let Some(root) = root {
root.as_ref().into()
} else {
current_dir()?
};
let mut paths = vec![];
for entry in WalkDir::new(&root)
.into_iter()
.filter_entry(|e|!e
.file_name()
.to_str()
.map(|s|s.starts_with("."))
.unwrap_or(false))
{
let entry = entry?;
if entry.depth() == 0 {
continue
}
let depth = entry.depth();
let path = entry.into_path();
paths.push(Entry {
path: path.strip_prefix(&root)?.into(),
is_dir: path.is_dir(),
is_mus: false,
is_img: false,
depth
});
}
paths.sort();
Ok(Self {
root,
paths,
cursor: 0,
offset: 0,
})
}
}
//pub enum Entry {
//Dir {
//path: PathBuf,
//name: OsString,
//entries: Vec<Box<FileTree>>,
//},
//File {
//path: PathBuf,
//name: OsString,
//}
//}
//impl Entry {
//fn new (path: &impl AsRef<Path>) -> Usually<Self> {
//let mut paths = vec![];
//for entry in WalkDir::new(&root)
//.into_iter()
//.filter_entry(|e|!e
//.file_name()
//.to_str()
//.map(|s|s.starts_with("."))
//.unwrap_or(false))
//{
//let path = entry?.into_path().strip_prefix(&root)?.into();
//paths.push(path);
//}
//paths.sort();
//}
//}
//struct FileTree {
//path: PathBuf,
//name: OsString,
//entries: Vec<Box<FileTree>>,
//}
//impl FileTree {
//}

0
src/view.edn Normal file
View file

39
src/view.rs Normal file
View file

@ -0,0 +1,39 @@
use crate::*;
use pad::PadStr;
impl Content<TuiOut> for Taggart {
fn layout (&self, area: [u16;4]) -> [u16;4] {
[area.x(), area.y(), 20, area.h()]
}
fn render (&self, to: &mut TuiOut) {
let area = to.area();
for (i, y) in area.iter_y().enumerate() {
let i_offset = i + self.offset;
if let Some(entry) = self.paths.get(i_offset) {
if entry.depth > 0 {
for (index, fragment) in entry.path.iter().enumerate() {
if index == entry.depth - 1 {
let cursor = if self.cursor == i_offset { ">" } else { " " };
let icon = if entry.is_dir {"+"} else {" "};
let name = fragment.display();
let indent = "".pad_to_width((entry.depth - 1) * 2);
let label = format!("{cursor} {indent}{icon} {name}");
let label = format!("{label:80} ARTIST ALBUM TITLE");
to.blit(&label, area.x(), y, None);
}
}
}
} else {
break
}
}
}
}
fn depth_of (path: &PathBuf) -> usize {
let mut depth = 0;
for _ in path.iter() {
depth += 1;
}
depth
}