Initial Commit.

This commit is contained in:
cyteen 2026-03-16 01:07:36 +00:00
parent b973b346ad
commit 4c30d7374a
1 changed files with 681 additions and 0 deletions

681
020_yazi-plugin_nicotine.sh Normal file
View File

@ -0,0 +1,681 @@
#!/usr/bin/env bash
: <<TODO
TODO
DEST=${1:-/etc/skel/}
YAZI_HOME="${DEST}/.config/yazi"
YAZI_PLUGIN_HOME="${YAZI_HOME}/plugins"
NICOTINE_PLUGIN_HOME="${YAZI_PLUGIN_HOME}/nicotine.yazi"
mkdir -p "${NICOTINE_PLUGIN_HOME}"
conf_print_nicotine_main() {
cat <<'EOF'
local DEBUG_MODE = true
local function log(message)
if DEBUG_MODE then
ya.err(string.format("[Nicotine Debug] %s", message))
end
end
-- 1. Grab current state (Sync to access cx)
local setup = ya.sync(function()
local targets = {}
local selected = cx.active.selected
if #selected == 0 then
local hovered = cx.active.current.hovered
if hovered then
table.insert(targets, tostring(hovered.url))
end
else
for _, item in pairs(selected) do
table.insert(targets, tostring(item.url))
end
end
return {
targets = targets,
cwd = tostring(cx.active.current.cwd)
}
end)
return {
entry = function(self, args)
log("Plugin started")
local state = setup()
local targets = state.targets
local cwd = state.cwd
if #targets == 0 then
return ya.notify({ title = "Nicotine", content = "No files selected", level = "warn" })
end
local is_archive = args[1] == "--archive"
-- Create a unique temp container
local tmp_container = string.format("%s/yazi-nicotine-%d", os.getenv("TMPDIR") or "/tmp", math.random(100000, 999999))
local status = Command("mkdir"):arg("-p"):arg(tmp_container):spawn():wait()
if not status or not status.success then
return ya.notify({ title = "Nicotine", content = "Failed to create temp dir", level = "error" })
end
ya.notify({
title = "Nicotine",
content = is_archive and "Creating archive..." or "Generating Git history...",
timeout = 2
})
-- Build Nicotine Command
-- Note: using --single-repo as seen in your test script to ensure one combined git
local cmd = Command("nicotine")
cmd:arg("--output-dir"):arg(tmp_container)
cmd:arg("--single-repo")
if not is_archive then
cmd:arg("--no-archive")
end
for _, path in ipairs(targets) do
cmd:arg(path)
end
local nicotine_status = cmd:spawn():wait()
if not nicotine_status or not nicotine_status.success then
Command("rm"):arg("-rf"):arg(tmp_container):spawn():wait()
return ya.notify({ title = "Nicotine Error", content = "Nicotine execution failed.", level = "error" })
end
-- Path fix: nicotine creates a sub-directory
local repo_path = tmp_container .. "/nicotine-combined-git"
if is_archive then
local archive_src = tmp_container .. "/nicotine-combined-git.tar.gz"
local status = Command("cp")
:arg(archive_src)
:arg(cwd)
:spawn()
:wait()
if not status or not status.success then
ya.notify({ title = "Nicotine", content = "Failed to copy archive", level = "error" })
return
end
ya.notify({ title = "Nicotine", content = "Archive copied to current directory", timeout = 3 })
else
-- Git/Lazygit Section
local permit = ui.hide()
local child_status = Command("lazygit")
:cwd(repo_path) -- Enter the actual git directory
:stdin(Command.INHERIT)
:stdout(Command.INHERIT)
:stderr(Command.INHERIT)
:spawn()
:wait()
permit:drop()
end
-- Cleanup container
Command("rm"):arg("-rf"):arg(tmp_container):spawn():wait()
if not is_archive then
ya.notify({ title = "Nicotine", content = "Session complete.", timeout = 2 })
end
end
}
EOF
}
conf_print_nicotine_main | tee "${NICOTINE_PLUGIN_HOME}/main.lua"
conf_print_nicotine_main() {
cat <<EOF
local DEBUG_MODE = true
local function log(message)
if DEBUG_MODE then
-- ya.err writes to the yazi log file/stderr
ya.err(string.format("[Nicotine Debug] %s", message))
end
end
-- 1. Grab current state
local setup = ya.sync(function()
local targets = {}
local selected = cx.active.selected
if #selected == 0 then
local hovered = cx.active.current.hovered
if hovered then
table.insert(targets, tostring(hovered.url))
end
else
for _, item in pairs(selected) do
table.insert(targets, tostring(item.url))
end
end
return {
targets = targets,
cwd = tostring(cx.active.current.cwd)
}
end)
return {
entry = function(self, args)
log("Plugin started")
local state = setup()
local targets = state.targets
local cwd = state.cwd
if #targets == 0 then
log("No targets found.")
return ya.notify({ title = "Nicotine", content = "No files selected", level = "warn" })
end
local is_archive = args[1] == "--archive"
-- Generate a safer unique temp directory
local tmp_dir = string.format("%s/yazi-nicotine-%d", os.getenv("TMPDIR") or "/tmp", math.random(100000, 999999))
local status = Command("mkdir"):arg("-p"):arg(tmp_dir):spawn():wait()
if not status or not status.success then
return ya.notify({ title = "Nicotine", content = "Failed to create temp dir", level = "error" })
end
ya.notify({
title = "Nicotine",
content = is_archive and "Creating archive..." or "Generating Git history...",
timeout = 2
})
-- Build Nicotine Command
local cmd = Command("nicotine")
cmd:arg("--output-dir"):arg(tmp_dir)
if not is_archive then
cmd:arg("--no-archive")
end
for _, path in ipairs(targets) do
cmd:arg(path)
end
local nicotine_status = cmd:spawn():wait()
if not nicotine_status or not nicotine_status.success then
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
return ya.notify({ title = "Nicotine Error", content = "Nicotine execution failed.", level = "error" })
end
if is_archive then
-- Use 'cp -a' to preserve attributes if possible
Command("cp"):arg("-r"):arg(tmp_dir):arg(cwd):spawn():wait()
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
return ya.notify({ title = "Nicotine", content = "Archive saved to current directory.", timeout = 3 })
end
-- Git/Lazygit Section
local permit = ui.hide()
local child_status = Command("lazygit")
:cwd(tmp_dir)
:stdin(Command.INHERIT)
:stdout(Command.INHERIT)
:stderr(Command.INHERIT)
:spawn()
:wait()
permit:drop()
-- Cleanup
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
if child_status and child_status.success then
ya.notify({ title = "Nicotine", content = "Session complete.", timeout = 2 })
end
end
}
EOF
}
# conf_print_nicotine_main | tee "${NICOTINE_PLUGIN_HOME}/main.lua"
conf_print_nicotine_main() {
cat <<'EOF'
-- 1. Use sync only to grab the current state (selected files, CWD, etc.)
local setup = ya.sync(function(self)
local targets = {}
local selected = cx.active.selected
if #selected == 0 then
local hovered = cx.active.current.hovered
if hovered then
table.insert(targets, tostring(hovered.url))
end
else
for _, item in pairs(selected) do
table.insert(targets, tostring(item.url))
end
end
return {
targets = targets,
cwd = tostring(cx.active.current.cwd)
}
end) -- Removed the trailing comma here
return {
entry = function(self, args)
-- Call our sync setup to get the data
-- Note: we call 'setup' (the local variable), not 'self:setup'
local state = setup()
local targets = state.targets
local cwd = state.cwd
if #targets == 0 then
return ya.notify({ title = "Nicotine", content = "No files selected", level = "warn" })
end
-- 2. Logic and Command execution happens here (Async context)
local is_archive = args[1] == "--archive"
local tmp_base = os.getenv("TMPDIR") or "/tmp"
local tmp_dir = string.format("%s/yazi-nicotine-%.0f-%d", tmp_base, os.clock() * 1000, math.random(1000, 9999))
local mkdir_status = Command("mkdir"):arg("-p"):arg(tmp_dir):spawn():wait()
if not mkdir_status or not mkdir_status.success then
return ya.notify({ title = "Nicotine", content = "Failed to create temp directory", level = "error" })
end
ya.notify({ title = "Nicotine", content = is_archive and "Creating compressed archive..." or "Generating Git history...", timeout = 2 })
local cmd = Command("nicotine")
cmd:arg("--output-dir"):arg(tmp_dir)
if not is_archive then
cmd:arg("--no-archive")
end
for _, path in ipairs(targets) do
cmd:arg(path)
end
local nicotine_status = cmd:stderr(Command.PIPED):spawn():wait()
if not nicotine_status or not nicotine_status.success then
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
return ya.notify({ title = "Nicotine Error", content = "Nicotine failed.", level = "error" })
end
if is_archive then
-- Moving contents instead of just copying the folder into the CWD
Command("cp"):arg("-r"):arg(tmp_dir):arg(cwd):spawn():wait()
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
return ya.notify({ title = "Nicotine", content = "Archive saved.", timeout = 3 })
end
-- Git/Lazygit Section
local permit = ui.hide()
local child, err = Command("lazygit")
:cwd(tmp_dir)
:stdin(Command.INHERIT)
:stdout(Command.INHERIT)
:stderr(Command.INHERIT)
:spawn()
if not child then
permit:drop()
return ya.notify({ title = "Nicotine", content = "Failed: " .. tostring(err), level = "error" })
end
child:wait()
permit:drop()
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
ya.notify({ title = "Nicotine", content = "Session ended.", timeout = 3 })
end
}
EOF
}
# conf_print_nicotine_main | tee "${NICOTINE_PLUGIN_HOME}/main.lua"
conf_print_nicotine_main() {
cat <<EOF
return {
--- @sync entry
entry = ya.sync(function(self, args)
local targets = {}
local selected = cx.active.selected
if #selected == 0 then
table.insert(targets, tostring(cx.active.current.hovered.url))
else
for _, item in pairs(selected) do
table.insert(targets, tostring(item.url))
end
end
-- Check if we want an archive or a live session
local is_archive = args[1] == "--archive"
local tmp_base = os.getenv("TMPDIR") or "/tmp"
local tmp_dir = string.format("%s/yazi-nicotine-%d-%d", tmp_base, os.clock() * 1000, math.random(1000, 9999))
local mkdir_status = Command("mkdir"):arg("-p"):arg(tmp_dir):spawn():wait()
if not mkdir_status or not mkdir_status.success then
return ya.notify({ title = "Nicotine", content = "Failed to create temp directory", level = "error" })
end
ya.notify({ title = "Nicotine", content = is_archive and "Creating compressed archive..." or "Generating Git history...", timeout = 2 })
local cmd = Command("nicotine")
:arg("--output-dir"):arg(tmp_dir)
-- If NOT an archive, we pass --no-archive to keep the raw git repo for lazygit
if not is_archive then
cmd:arg("--no-archive")
end
for _, path in ipairs(targets) do
cmd:arg(path)
end
local nicotine_status = cmd:stderr(Command.PIPED):spawn():wait()
if not nicotine_status or not nicotine_status.success then
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
return ya.notify({
title = "Nicotine Error",
content = "Nicotine failed to process the selection.",
level = "error"
})
end
-- Handle Archive Mode
if is_archive then
-- In archive mode, nicotine usually generates a .tar.gz or .zip inside the output dir
-- We move it to the current directory for the user
local dest = tostring(cx.active.current.cwd)
Command("cp"):arg("-r"):arg(tmp_dir):arg(dest):spawn():wait()
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
return ya.notify({ title = "Nicotine", content = "Archive saved to current directory.", timeout = 3 })
end
-- Handle Git History Mode (lazygit)
local git_check = Command("git")
:arg("-C"):arg(tmp_dir)
:arg("rev-parse"):arg("--is-inside-work-tree")
:stdout(Command.PIPED)
:spawn()
:wait()
if not git_check or not git_check.success then
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
return ya.notify({
title = "Nicotine Error",
content = "Generated directory is not a valid Git repository.",
level = "error"
})
end
local permit = ui.hide()
local child, err = Command("lazygit")
:cwd(tmp_dir)
:stdin(Command.INHERIT)
:stdout(Command.INHERIT)
:stderr(Command.PIPED)
:spawn()
if not child then
permit:drop()
return ya.notify({ title = "Nicotine", content = "Failed to spawn lazygit: " .. tostring(err), level = "error" })
end
child:wait()
permit:drop()
Command("rm"):arg("-rf"):arg(tmp_dir):spawn():wait()
ya.notify({ title = "Nicotine", content = "Session ended. Temp files cleared.", timeout = 3 })
end)
}
EOF
}
# conf_print_nicotine_main | tee "${NICOTINE_PLUGIN_HOME}/main.lua"
#----------------------------------------------------------------
conf_print_httm_keymap() {
cat <<'EOF'
[[mgr.prepend_keymap]]
on = [ "g", "n" ]
run = "plugin nicotine"
desc = "Run Nicotine Git snapshot history (lazygit)"
[[mgr.prepend_keymap]]
on = [ "g", "z" ]
run = "plugin nicotine --args='--archive'"
desc = "Create compressed Nicotine archive of selection"
EOF
}
# conf_print_httm_keymap | tee -a "${YAZI_HOME}/keymap.toml"
#----------------------------------------------------------------
# Create a version of nicotine that has the --single-repo option.
conf_print_nicotine_script() {
cat <<'EOF'
#!/usr/bin/env bash
set -euf -o pipefail
#set -x
print_version() {
printf "\
nicotine $(httm --version | cut -f2 -d' ')
" 1>&2
exit 0
}
print_usage() {
local nicotine="\e[31mnicotine\e[0m"
local httm="\e[31mhttm\e[0m"
local git="\e[31mgit\e[0m"
local tar="\e[31mtar\e[0m"
printf "\
$nicotine is a wrapper script for $httm which converts unique snapshot file versions to a $git archive.
USAGE:
nicotine [OPTIONS]... [file1 file2...]
OPTIONS:
--output-dir:
Select the output directory. Default is current working directory.
--no-archive
Disable archive creation. Create a new $git repository.
--single-repo:
Add all provided files/dirs into a single git repository instead of one per argument.
--debug:
Show $git and $tar command output.
--help:
Display this dialog.
--version:
Display script version.
" 1>&2
exit 1
}
prep_exec() {
for cmd in find readlink git tar mktemp mkdir httm; do
command -v "$cmd" >/dev/null 2>&1 || { printf "Error: '$cmd' is required.\n" 1>&2; exit 1; }
done
}
function copy_add_commit {
local debug=$1; shift
local path="$1"; shift
local dest_dir="$1"; shift
if [[ -d "$path" ]]; then
cp -a "$path" "$dest_dir/"
return 0
else
cp -a "$path" "$dest_dir"
fi
local commit_date
commit_date=$(date -d "$(stat -c %y "$path")")
if [[ "$debug" = true ]]; then
git add --all "$dest_dir"
git commit -m "httm commit from ZFS snapshot: $(basename "$path")" --date "$commit_date" || true
else
git add --all "$dest_dir" > /dev/null
git commit -q -m "httm commit from ZFS snapshot: $(basename "$path")" --date "$commit_date" > /dev/null || true
fi
}
function get_unique_versions {
local debug=$1; shift
local path="$1"; shift
local dest_dir="$1"; shift
local -a version_list=()
if [[ ! -d "$path" ]]; then
while read -r line; do
[[ -n "$line" ]] && version_list+=("$line")
done <<<"$(httm -n --omit-ditto "$path")"
fi
if [[ -d "$path" ]] || [[ ${#version_list[@]} -le 1 ]]; then
copy_add_commit "$debug" "$path" "$dest_dir"
else
for version in "${version_list[@]}"; do
copy_add_commit "$debug" "$version" "$dest_dir"
done
fi
}
function traverse {
local debug=$1; shift
local path="$1"; shift
local dest_dir="$1"; shift
get_unique_versions "$debug" "$path" "$dest_dir"
[[ -d "$path" ]] || return 0
local basename
basename=$(basename "$path")
while read -r entry; do
[[ -z "$entry" ]] && continue
if [[ -d "$entry" ]]; then
traverse "$debug" "$entry" "$dest_dir/$basename"
else
get_unique_versions "$debug" "$entry" "$dest_dir/$basename"
fi
done <<<"$(find "$path" -mindepth 1 -maxdepth 1)"
}
function nicotine {
(
prep_exec
local debug=false
local no_archive=false
local single_repo=false
local output_dir="$(pwd)"
local -a input_files=()
while [[ $# -ge 1 ]]; do
case "$1" in
--output-dir) shift; output_dir="$(realpath "$1")"; shift ;;
--debug) debug=true; shift ;;
--no-archive) no_archive=true; shift ;;
--single-repo) single_repo=true; shift ;;
--help|-h) print_usage ;;
--version|-V) print_version ;;
*) input_files+=("$1"); shift ;;
esac
done
[[ ${#input_files[@]} -gt 0 ]] || { printf "Error: No input files.\n" 1>&2; exit 1; }
local tmp_dir
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
if [[ "$single_repo" = true ]]; then
local repo_name="nicotine-combined"
local archive_dir="$tmp_dir/$repo_name"
mkdir -p "$archive_dir"
cd "$archive_dir"
git init -q
for file in "${input_files[@]}"; do
local can_path
can_path=$(realpath "$file" 2>/dev/null) || continue
[[ -e "$can_path" ]] || continue
# if [[ -d "$can_path" ]]; then
# traverse "$debug" "$can_path" "$tmp_dir"
# else
# traverse "$debug" "$can_path" "$archive_dir"
# fi
traverse "$debug" "$can_path" "$archive_dir"
done
finalize_output "$debug" "$no_archive" "$tmp_dir" "$output_dir" "$repo_name"
else
for file in "${input_files[@]}"; do
local can_path
can_path=$(realpath "$file" 2>/dev/null) || continue
[[ -e "$can_path" ]] || continue
local base
base=$(basename "$can_path")
base="${base#.}"
local archive_dir="$tmp_dir/$base"
mkdir -p "$archive_dir"
(
cd "$archive_dir"
git init -q
#
# if [[ -d "$can_path" ]]; then
# traverse "$debug" "$can_path" "$tmp_dir"
# else
# traverse "$debug" "$can_path" "$archive_dir"
# fi
traverse "$debug" "$can_path" "$archive_dir"
)
finalize_output "$debug" "$no_archive" "$tmp_dir" "$output_dir" "$base"
rm -rf "${archive_dir:?}"
done
fi
)
}
function finalize_output {
local debug=$1 no_archive=$2 tmp=$3 out=$4 base=$5
if [[ "$no_archive" = true ]]; then
cp -ra "$tmp/$base" "$out/$base-git"
printf "Repository created: $out/$base-git\n"
else
local out_file="$out/$base-git.tar.gz"
tar -C "$tmp" -zcf "$out_file" "$base"
printf "Archive created: $out_file\n"
fi
}
nicotine "${@}"
EOF
}
# So as to not loose the original nicotine bash script.
if [[ -f /usr/bin/nicotine ]]; then
if [[ -f /usr/bin/nicotine.bak ]]; then
sudo mv /usr/bin/nicotine.bak /usr/bin/nicotine
fi
sudo mv /usr/bin/nicotine /usr/bin/nicotine.bak
conf_print_nicotine_script | sudo tee /usr/bin/nicotine
sudo chmod +x /usr/bin/nicotine
fi