some stuff

This commit is contained in:
Matt Nish-Lapidus 2025-02-26 22:18:59 -05:00
parent 8200fed53f
commit 31f443be36
15 changed files with 992 additions and 77 deletions

48
flake.lock generated
View file

@ -68,11 +68,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1740500634,
"narHash": "sha256-uCC1g2xo01Q9WS6ivR9iBqO+9baoaY5op6f1EZz46IE=",
"lastModified": 1740622367,
"narHash": "sha256-7TpBfGb24DrQrdW94T5x0vHN0GF2I50fkP+YX5r6JqE=",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "d82d304bcd1398e223dee8de927db024dced5128",
"rev": "ee8442abce734e9a1d0e5824818062bcbf358e73",
"type": "github"
},
"original": {
@ -322,11 +322,11 @@
]
},
"locked": {
"lastModified": 1740494361,
"narHash": "sha256-Dd/GhJ9qKmUwuhgt/PAROG8J6YdU2ZjtJI9SQX5sVQI=",
"lastModified": 1740606115,
"narHash": "sha256-GKe3vrIWcei4gSTckEzHr5Zf/g9NSofmsAnbkNYU+lM=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "74f0a8546e3f2458c870cf90fc4b38ac1f498b17",
"rev": "6be185eb76295e7562f5bf2da42afe374b8beb15",
"type": "github"
},
"original": {
@ -711,11 +711,11 @@
"xwayland-satellite-unstable": "xwayland-satellite-unstable"
},
"locked": {
"lastModified": 1740480783,
"narHash": "sha256-5l/WnJ4BELbckzTd1rmTlEGbcqBf71K2tx6pCNb2xM8=",
"lastModified": 1740592142,
"narHash": "sha256-v+Qg8V0UHkXCDSgqKowqMyJR2LGKIJGA0HbwCRgZN/0=",
"owner": "sodiboo",
"repo": "niri-flake",
"rev": "0da1abb83ef2a37fd885de79730759486a407c41",
"rev": "259a8cc3e351d0a34063ae857d3c730b1ae4ad56",
"type": "github"
},
"original": {
@ -744,11 +744,11 @@
"niri-unstable": {
"flake": false,
"locked": {
"lastModified": 1740476031,
"narHash": "sha256-8YuYgIzExIAenYMaSQTP7zYBzaJPN83pGRrcwQCochY=",
"lastModified": 1740587638,
"narHash": "sha256-/BQ67VCF0ZpqCvxmVR18HdnqFy81ABWaKjz1FFwL65g=",
"owner": "YaLTeR",
"repo": "niri",
"rev": "c153349c62ed44762bf2ae8be6d5812faa9d5c6d",
"rev": "693d9355386c6217bb9cca5cb30c2b4248f19d8c",
"type": "github"
},
"original": {
@ -892,11 +892,11 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1740339700,
"narHash": "sha256-cbrw7EgQhcdFnu6iS3vane53bEagZQy/xyIkDWpCgVE=",
"lastModified": 1740463929,
"narHash": "sha256-4Xhu/3aUdCKeLfdteEHMegx5ooKQvwPHNkOgNCXQrvc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "04ef94c4c1582fd485bbfdb8c4a8ba250e359195",
"rev": "5d7db4668d7a0c6cc5fc8cf6ef33b008b2b1ed8b",
"type": "github"
},
"original": {
@ -908,11 +908,11 @@
},
"nixpkgs-stable_2": {
"locked": {
"lastModified": 1740339700,
"narHash": "sha256-cbrw7EgQhcdFnu6iS3vane53bEagZQy/xyIkDWpCgVE=",
"lastModified": 1740463929,
"narHash": "sha256-4Xhu/3aUdCKeLfdteEHMegx5ooKQvwPHNkOgNCXQrvc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "04ef94c4c1582fd485bbfdb8c4a8ba250e359195",
"rev": "5d7db4668d7a0c6cc5fc8cf6ef33b008b2b1ed8b",
"type": "github"
},
"original": {
@ -940,11 +940,11 @@
},
"nixpkgs-stable_4": {
"locked": {
"lastModified": 1740339700,
"narHash": "sha256-cbrw7EgQhcdFnu6iS3vane53bEagZQy/xyIkDWpCgVE=",
"lastModified": 1740463929,
"narHash": "sha256-4Xhu/3aUdCKeLfdteEHMegx5ooKQvwPHNkOgNCXQrvc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "04ef94c4c1582fd485bbfdb8c4a8ba250e359195",
"rev": "5d7db4668d7a0c6cc5fc8cf6ef33b008b2b1ed8b",
"type": "github"
},
"original": {
@ -1393,11 +1393,11 @@
"rust-overlay": "rust-overlay_2"
},
"locked": {
"lastModified": 1740496374,
"narHash": "sha256-DJaJ4jAIex650tuoeudOVCBCfdT/3cZ7z/QJ7TpKI2c=",
"lastModified": 1740594495,
"narHash": "sha256-Q339KulMU5xw/WKdCoHq/3Mr+09Yne+gglsNhBro1zE=",
"owner": "sxyazi",
"repo": "yazi",
"rev": "235f6888d467cbd469afe3da45ee048d9d38ee27",
"rev": "20c99c6a06a7aa48c2e13fc0b6763d77c44c9b1a",
"type": "github"
},
"original": {

View file

@ -2,6 +2,14 @@
format = "$all$line_break$character"
[env_var.yazi_level]
description = "Indicate the shell was launched by `yazi`"
variable = "YAZI_LEVEL"
# 🦆: U+1f986 Duck
# Alternatively: " ": U+E795 <Private Use>
symbol = "🦆"
format = '[ $symbol ]($style)'
[directory]
truncation_length = 0
truncate_to_repo = false

View file

@ -2,10 +2,26 @@ require("git"):setup()
require("yatline-symlink"):setup()
require("dir-rules"):setup()
require("starship"):setup {
config_file = "~/.config/starship.toml",
}
require("smart-enter"):setup {
open_multi = true,
}
require("fg"):setup({
default_action = "menu",
})
-- ~/.config/yazi/init.lua
THEME.git = THEME.git or {}
THEME.git.added_sign = "A"
THEME.git.ignored_sign = "I"
THEME.git.updated_sign = "U"
THEME.git.modified_sign = "M"
THEME.git.deleted_sign = "D"
require("yatline"):setup({
--theme = my_theme,
section_separator = { open = "", close = "" },

View file

@ -0,0 +1,274 @@
local toggle_ui = ya.sync(function(self)
if self.children then
Modal:children_remove(self.children)
self.children = nil
else
self.children = Modal:children_add(self, 10)
end
ya.render()
end)
local init_ui_data = ya.sync(function(self,file_url)
self.opt = {"nvim", "jump"}
self.title = "fg"
self.title_color = "#82ab3a"
self.cursor = 0
self.file_url = file_url and file_url or ""
ya.render()
end)
local set_option = ya.sync(function(self,enable)
if enable then
self.active_opt = self.opt[self.cursor+1]
else
self.active_opt = nil
end
end)
local get_option = ya.sync(function(self)
return self.active_opt
end)
local get_default_action = ya.sync(function(self)
return self.default_action
end)
local update_cursor = ya.sync(function(self, cursor)
self.cursor = ya.clamp(0, self.cursor + cursor, 1)
ya.render()
end)
local M = {
keys = {
{ on = "q", run = "quit" },
{ on = "<Esc>", run = "quit" },
{ on = "<Enter>", run = "select" },
{ on = "k", run = "up" },
{ on = "j", run = "down" },
{ on = "<Up>", run = "up" },
{ on = "<Down>", run = "down" },
},
}
function M:new(area)
self:layout(area)
return self
end
function M:layout(area)
local chunks = ui.Layout()
:constraints({
ui.Constraint.Percentage(10),
ui.Constraint.Percentage(80),
ui.Constraint.Percentage(10),
})
:split(area)
local chunks = ui.Layout()
:direction(ui.Layout.HORIZONTAL)
:constraints({
ui.Constraint.Percentage(10),
ui.Constraint.Percentage(80),
ui.Constraint.Percentage(10),
})
:split(chunks[2])
self._area = chunks[2]
end
local function splitAndGetNth(inputstr, sep, index)
if sep == nil then
sep = "%s"
end
local count = 0
local start = 1
while true do
local sepStart, sepEnd = string.find(inputstr, sep, start)
if not sepStart then
break
end
count = count + 1
if count == index then
return string.sub(inputstr, start, sepStart - 1)
end
start = sepEnd + 1
end
if index == 1 then
return inputstr
end
return nil -- 如果没有足够的分割部分返回nil
end
local state = ya.sync(function() return tostring(cx.active.current.cwd) end)
local function fail(s, ...) ya.notify { title = "Fzf", content = string.format(s, ...), timeout = 5, level = "error" } end
function M:entry(job)
local args = job.args
local _permit = ya.hide()
local cwd = state()
local shell_value = ya.target_family() == "windows" and "nu" or os.getenv("SHELL"):match(".*/(.*)")
local cmd_args = ""
local preview_cmd = [===[line={2} && begin=$( if [[ $line -lt 7 ]]; then echo $((line-1)); else echo 6; fi ) && bat --highlight-line={2} --color=always --line-range $((line-begin)):$((line+10)) {1}]===]
if ya.target_family() == "windows" then
preview_cmd = [[bat --highlight-line={2} --color=always --line-range {2}: {1}]]
elseif shell_value == "fish" then
preview_cmd = [[set line {2} && set begin ( test $line -lt 7 && echo (math "$line-1") || echo 6 ) && bat --highlight-line={2} --color=always --line-range (math "$line-$begin"):(math "$line+10") {1}]]
elseif shell_value == "nu" then
preview_cmd = [[let line = ({2} | into int); let begin = if $line < 7 { $line - 1 } else { 6 }; bat --highlight-line={2} --color=always --line-range $'($line - $begin):($line + 10)' {1}]]
end
if ya.target_family() == "windows" and args[1] == "fzf" then
cmd_args = [[fzf --preview="bat --color=always {}"]]
elseif ya.target_family() == "windows" and args[1] == "rg" then
local rg_prefix = [[rg --colors "path:fg:blue" --colors "line:fg:red" --colors "column:fg:yellow" --column --line-number --no-heading --color=always --smart-case ]]
cmd_args = [[fzf --ansi --disabled --bind "start:reload:]]
.. rg_prefix
.. [[{q}" --bind "change:reload:]]
.. rg_prefix
.. [[{q}" --delimiter ":" --preview "]]
.. preview_cmd
.. [[" --preview-window "up,60%" --nth "3.."]]
elseif ya.target_family() == "windows" then
cmd_args = [[rg --color=always --line-number --no-heading --smart-case "" | fzf --ansi --preview="]] .. preview_cmd .. [[" --delimiter=":" --preview-window="up:60%" --nth="3.."]]
elseif args[1] == "fzf" then
cmd_args = [[fzf --preview="bat --color=always {}"]]
elseif args[1] == "rg" and shell_value == "fish" then
cmd_args = [[
RG_PREFIX="rg --colors 'path:fg:blue' --colors 'line:fg:red' --colors 'column:fg:yellow' --column --line-number --no-heading --color=always --smart-case " \
fzf --ansi --disabled \
--bind "start:reload:$RG_PREFIX {q}" \
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
--delimiter : \
--preview ']] .. preview_cmd .. [[' \
--preview-window 'up,60%' \
--nth '3..'
]]
elseif args[1] == "rg" and (shell_value == "bash" or shell_value == "zsh") then
cmd_args = [[
RG_PREFIX="rg --colors 'path:fg:blue' --colors 'line:fg:red' --colors 'column:fg:yellow' --column --line-number --no-heading --color=always --smart-case "
fzf --ansi --disabled \
--bind "start:reload:$RG_PREFIX {q}" \
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
--delimiter : \
--preview ']] .. preview_cmd .. [[' \
--preview-window 'up,60%' \
--nth '3..'
]]
elseif args[1] == "rg" and shell_value == "nu" then
local rg_prefix = "rg --colors 'path:fg:blue' --colors 'line:fg:red' --colors 'column:fg:yellow' --column --line-number --no-heading --color=always --smart-case "
cmd_args = [[fzf --ansi --disabled --bind "start:reload:]]
.. rg_prefix
.. [[{q}" --bind "change:reload:sleep 100ms; try { ]]
.. rg_prefix
.. [[{q} }" --delimiter : --preview ']]
.. preview_cmd
.. [[' --preview-window 'up,60%' --nth '3..']]
else
cmd_args = [[rg --color=always --line-number --no-heading --smart-case '' | fzf --ansi --preview=']] .. preview_cmd .. [[' --delimiter=':' --preview-window='up:60%' --nth='3..']]
end
local child, err =
Command(shell_value):args({"-c", cmd_args}):cwd(cwd):stdin(Command.INHERIT):stdout(Command.PIPED):stderr(Command.INHERIT):spawn()
if not child then
return fail("Spawn `rfzf` failed with error code %s. Do you have it installed?", err)
end
local output, err = child:wait_with_output()
if not output then
return fail("Cannot read `fzf` output, error code %s", err)
elseif not output.status.success and output.status.code ~= 130 then
return fail("`fzf` exited with error code %s", output.status.code)
end
if output.stdout == "" then
return
end
local target = output.stdout:gsub("\n$", "")
local file_url = splitAndGetNth(target,":",1)
local line_number = splitAndGetNth(target,":",2)
line_number = line_number and line_number or 1
init_ui_data(cwd.."/"..file_url)
local default_action = get_default_action()
if (default_action == "menu" or default_action == nil) and args[1] ~= "fzf" then
_permit:drop()
toggle_ui()
while true do
local cand = self.keys[ya.which { cands = self.keys, silent = true }]
if cand then
if cand.run == "quit" then
set_option(false)
toggle_ui()
break
elseif cand.run == "select" then
set_option(true)
toggle_ui()
break
elseif cand.run == "down" then
update_cursor(1)
elseif cand.run == "up" then
update_cursor(-1)
end
end
end
_permit = ya.hide()
end
if (default_action == "nvim" or get_option() == "nvim" ) and args[1] ~= "fzf" then
os.execute("nvim +"..line_number.." -n "..file_url)
elseif (default_action == "jump" or get_option() == "jump" or args[1] == "fzf") and file_url ~= "" then
ya.manager_emit(file_url:match("[/\\]$") and "cd" or "reveal", { file_url })
else
return
end
end
function M:reflow() return { self } end
function M:redraw()
local rows = {}
rows[1] = ui.Row { "open with nvim" }
rows[2] = ui.Row { "reach at yazi" }
return {
ui.Clear(self._area),
ui.Border(ui.Border.ALL)
:area(self._area)
:type(ui.Border.ROUNDED)
:style(ui.Style():fg("#82ab3a"))
:title(ui.Line(self.title):align(ui.Line.CENTER):fg(self.title_color)),
ui.Table(rows)
:area(self._area:pad(ui.Pad(1, 2, 1, 2)))
:header(ui.Row({ "Action for:"..self.file_url }):style(ui.Style():bold():fg("#e73c80")))
:row(self.cursor)
:row_style(ui.Style():fg("#82ab3a"):underline()),
}
end
function M.fail(s, ...)
ya.manager_emit("plugin", {"mount", args = "refresh" })
ya.notify { title = "fg", content = string.format(s, ...), timeout = 10, level = "error" }
end
function M:click() end
function M:scroll() end
function M:touch() end
function M:setup(config)
self.default_action = (config and config.default_action) and config.default_action or "menu"
end
return M

View file

@ -1,32 +0,0 @@
--- @since 25.2.7
local AVAILABLE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789."
local changed = ya.sync(function(st, new)
local b = st.last ~= new
st.last = new
return b or not cx.active.finder
end)
local escape = function(s) return s == "." and "\\." or s end
return {
entry = function()
local cands = {}
for i = 1, #AVAILABLE_CHARS do
cands[#cands + 1] = { on = AVAILABLE_CHARS:sub(i, i) }
end
local idx = ya.which { cands = cands, silent = true }
if not idx then
return
end
local kw = escape(cands[idx].on)
if changed(kw) then
ya.manager_emit("find_do", { "^" .. kw })
else
ya.manager_emit("find_arrow", {})
end
end,
}

View file

@ -0,0 +1,20 @@
return {
entry = function(_, job)
local block = job.args[1] and job.args[1] == "block"
local value, event = ya.input({
title = block and "Open with (block):" or "Open with:",
position = { "hovered", y = 1, w = 50 },
})
if event == 1 then
local s = ya.target_family() == "windows" and " %*" or ' "$@"'
ya.manager_emit("shell", {
value .. s,
block = block,
orphan = true,
confirm = true,
})
end
end,
}

View file

@ -0,0 +1,280 @@
--- @since 25.2.7
local M = {}
local shell = os.getenv("SHELL") or ""
local PackageName = "Restore"
local function success(s, ...)
ya.notify({ title = PackageName, content = string.format(s, ...), timeout = 5, level = "info" })
end
local function fail(s, ...)
ya.notify({ title = PackageName, content = string.format(s, ...), timeout = 5, level = "error" })
end
---@enum STATE
local STATE = {
POSITION = "position",
SHOW_CONFIRM = "show_confirm",
THEME = "theme",
}
local set_state = ya.sync(function(state, key, value)
if state then
state[key] = value
else
state = {}
state[key] = value
end
end)
local get_state = ya.sync(function(state, key)
if state then
return state[key]
else
return nil
end
end)
---@enum File_Type
local File_Type = {
File = "file",
Dir = "dir_all",
None_Exist = "unknown",
}
---@alias TRASHED_ITEM {trash_index: number, trashed_date_time: string, trashed_path: string, type: File_Type} Item in trash list
function get_basename(filepath)
return filepath:match("^.+/(.+)$") or filepath
end
local get_cwd = ya.sync(function()
return tostring(cx.active.current.cwd)
end)
local function path_quote(path)
local result = "'" .. string.gsub(path, "'", "'\\''") .. "'"
return result
end
local function get_file_type(path)
local cha, _ = fs.cha(Url(path))
if cha then
return cha.is_dir and File_Type.Dir or File_Type.File
else
return File_Type.None_Exist
end
end
local function get_trash_volume()
local cwd = get_cwd()
local trash_volumes_stream, cmr_err =
Command("trash-list"):args({ "--volumes" }):stdout(Command.PIPED):stderr(Command.PIPED):output()
local matched_vol_path = nil
if trash_volumes_stream then
local matched_vol_length = 0
for vol in trash_volumes_stream.stdout:gmatch("[^\r\n]+") do
local vol_length = utf8.len(vol) or 0
if cwd:sub(1, vol_length) == vol and vol_length > matched_vol_length then
matched_vol_path = vol
matched_vol_length = vol_length
end
end
if not matched_vol_path then
fail("Can't get trash directory")
end
else
fail("Failed to start `trash-list` with error: `%s`. Do you have `trash-cli` installed?", cmr_err)
end
return matched_vol_path
end
---get list of latest files/folders trashed
---@param curr_working_volume currently working volume
---@return TRASHED_ITEM[]|nil
local function get_latest_trashed_items(curr_working_volume)
---@type TRASHED_ITEM[]
local restorable_items = {}
local fake_enter = Command("printf"):stderr(Command.PIPED):stdout(Command.PIPED):spawn():take_stdout()
local trash_list_stream, err_cmd = Command(shell)
:args({ "-c", "trash-restore " .. path_quote(curr_working_volume) })
:stdin(fake_enter)
:stdout(Command.PIPED)
:stderr(Command.PIPED)
:output()
if trash_list_stream then
---@type TRASHED_ITEM[]
local trash_list = {}
for line in trash_list_stream.stdout:gmatch("[^\r\n]+") do
-- remove leading spaces
line = line:match("^%s*(.+)$")
local trash_index, item_date, item_path = line:match("^(%d+) (%S+ %S+) (.+)$")
if item_date and item_path and trash_index ~= nil then
table.insert(trash_list, {
trash_index = tonumber(trash_index),
trashed_date_time = item_date,
trashed_path = item_path,
type = File_Type.None_Exist,
})
end
end
if #trash_list == 0 then
success("Nothing left to restore")
return
end
local last_item_datetime = trash_list[#trash_list].trashed_date_time
for _, trash_item in ipairs(trash_list) do
if trash_item then
if trash_item.trashed_date_time == last_item_datetime then
trash_item.type = get_file_type(trash_item.trashed_path)
table.insert(restorable_items, trash_item)
end
end
end
else
fail("Failed to start `trash-restore` with error: `%s`. Do you have `trash-cli` installed?", err_cmd)
return
end
return restorable_items
-- return newest_trashed_items
end
---@param trash_list TRASHED_ITEM[]
local function filter_none_exised_paths(trash_list)
---@type TRASHED_ITEM[]
local existed_trash_items = {}
for _, v in ipairs(trash_list) do
if v.type ~= File_Type.None_Exist then
table.insert(existed_trash_items, v)
end
end
return existed_trash_items
end
local function restore_files(curr_working_volume, start_index, end_index)
if type(start_index) ~= "number" or type(end_index) ~= "number" or start_index < 0 or end_index < 0 then
fail("Failed to restore file(s): out of range")
return
end
ya.manager_emit("shell", {
"echo " .. ya.quote(start_index .. "-" .. end_index) .. " | trash-restore --overwrite " .. path_quote(
curr_working_volume
),
confirm = true,
})
local file_to_restore_count = end_index - start_index + 1
success("Restored " .. tostring(file_to_restore_count) .. " file" .. (file_to_restore_count > 1 and "s" or ""))
end
function M:setup(opts)
if opts and opts.position and type(opts.position) == "table" then
set_state(STATE.POSITION, opts.position)
else
set_state(STATE.POSITION, { "center", w = 70, h = 40 })
end
if opts and opts.show_confirm then
set_state(STATE.SHOW_CONFIRM, opts.show_confirm)
else
set_state(STATE.SHOW_CONFIRM, false)
end
if opts and opts.theme and type(opts.theme) == "table" then
set_state(STATE.THEME, opts.theme)
else
set_state(STATE.THEME, {})
end
end
---@param trash_list TRASHED_ITEM[]
local function get_components(trash_list)
local theme = get_state(STATE.THEME) or {}
theme.list_item = theme.list_item or {
odd = "blue",
even = "blue",
}
local trashed_items_components = {}
for idx, item in pairs(trash_list) do
local fg_color = theme.list_item.odd or "blue"
if idx % 2 == 0 then
fg_color = theme.list_item.even or "blue"
end
table.insert(
trashed_items_components,
ui.Line({
ui.Span(" "),
ui.Span(item.trashed_path):fg(fg_color),
}):align(ui.Line.LEFT)
)
end
return trashed_items_components
end
function M:entry()
local curr_working_volume = get_trash_volume()
if not curr_working_volume then
return
end
local trashed_items = get_latest_trashed_items(curr_working_volume)
if trashed_items == nil then
return
end
local collided_items = filter_none_exised_paths(trashed_items)
local overwrite_confirmed = true
local show_confirm = get_state(STATE.SHOW_CONFIRM)
local pos = get_state(STATE.POSITION)
pos = pos or { "center", w = 70, h = 40 }
local theme = get_state(STATE.THEME) or {}
theme.title = theme.title or "blue"
theme.header = theme.header or "green"
theme.header_warning = theme.header_warning or "yellow"
if ya.confirm and show_confirm then
local continue_restore = ya.confirm({
title = ui.Line("Restore files/folders"):fg(theme.title):bold(),
content = ui.Text({
ui.Line(""),
ui.Line("The following files and folders are going to be restored:"):fg(theme.header),
ui.Line(""),
table.unpack(get_components(trashed_items)),
})
:align(ui.Text.LEFT)
:wrap(ui.Text.WRAP),
--TODO: still wating for API :/
-- list = ui.List({
-- table.unpack(get_components(trashed_items)),
-- }),
pos = pos,
})
-- stopping
if not continue_restore then
return
end
end
-- show Confirm dialog with list of collided items
if #collided_items > 0 then
overwrite_confirmed = ya.confirm({
title = ui.Line("Restore files/folders"):fg(theme.title):bold(),
content = ui.Text({
ui.Line(""),
ui.Line("The following files and folders are existed, overwrite?"):fg(theme.header_warning),
ui.Line(""),
table.unpack(get_components(collided_items)),
})
:align(ui.Text.LEFT)
:wrap(ui.Text.WRAP),
pos = pos,
})
end
if overwrite_confirmed then
restore_files(curr_working_volume, trashed_items[1].trash_index, trashed_items[#trashed_items].trash_index)
end
end
return M

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Rolv Apneseth
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,109 @@
# starship.yazi
Starship prompt plugin for [Yazi](https://github.com/sxyazi/yazi)
<https://github.com/Rolv-Apneseth/starship.yazi/assets/69486699/f7314687-5cb1-4d66-8d9d-cca960ba6716>
## Requirements
- [Yazi](https://github.com/sxyazi/yazi)
- [starship](https://github.com/starship/starship)
## Installation
```bash
ya pack -a Rolv-Apneseth/starship
```
### Manual
```sh
# Linux / MacOS
git clone https://github.com/Rolv-Apneseth/starship.yazi.git ~/.config/yazi/plugins/starship.yazi
# Windows
git clone https://github.com/Rolv-Apneseth/starship.yazi.git %AppData%\yazi\config\plugins\starship.yazi
```
## Usage
Add this to `~/.config/yazi/init.lua`:
```lua
require("starship"):setup()
```
Make sure you have [starship](https://github.com/starship/starship) installed and in your `PATH`.
## Config
Here is an example with all available config options:
```lua
require("starship"):setup({
-- Hide flags (such as filter, find and search). This is recommended for starship themes which
-- are intended to go across the entire width of the terminal.
hide_flags = false, -- Default: false
-- Whether to place flags after the starship prompt. False means the flags will be placed before the prompt.
flags_after_prompt = true, -- Default: true
-- Custom starship configuration file to use
config_file = "~/.config/starship_full.toml", -- Default: nil
})
```
## Extra
If you use a `starship` theme with a background colour, it might look a bit to cramped on just the one line `Yazi` gives the header by default. To fix this, you can add this to your `init.lua`:
<details>
<summary>Click to expand</summary>
```lua
local old_build = Tab.build
Tab.build = function(self, ...)
local bar = function(c, x, y)
if x <= 0 or x == self._area.w - 1 then
return ui.Bar(ui.Bar.TOP):area(ui.Rect.default)
end
return ui.Bar(ui.Bar.TOP)
:area(ui.Rect({
x = x,
y = math.max(0, y),
w = ya.clamp(0, self._area.w - x, 1),
h = math.min(1, self._area.h),
}))
:symbol(c)
end
local c = self._chunks
self._chunks = {
c[1]:padding(ui.Padding.y(1)),
c[2]:padding(ui.Padding(c[1].w > 0 and 0 or 1, c[3].w > 0 and 0 or 1, 1, 1)),
c[3]:padding(ui.Padding.y(1)),
}
local style = THEME.manager.border_style
self._base = ya.list_merge(self._base or {}, {
ui.Border(ui.Border.ALL):area(self._area):type(ui.Border.ROUNDED):style(style),
ui.Bar(ui.Bar.RIGHT):area(self._chunks[1]):style(style),
ui.Bar(ui.Bar.LEFT):area(self._chunks[1]):style(style),
bar("┬", c[1].right - 1, c[1].y),
bar("┴", c[1].right - 1, c[1].bottom - 1),
bar("┬", c[2].right, c[2].y),
bar("┴", c[2].right, c[2].bottom - 1),
})
old_build(self, ...)
end
```
</details>
> [!NOTE]
> This works by overriding your `Tab.build` function so make sure this is the only place you're doing that in your config. For example, this would be incompatible with the [full-border plugin](https://github.com/yazi-rs/plugins/tree/main/full-border.yazi)
## Thanks
- [sxyazi](https://github.com/sxyazi) for providing the code for this plugin and the demo video [in this comment](https://github.com/sxyazi/yazi/issues/767#issuecomment-1977082834)

View file

@ -0,0 +1,136 @@
--- @since 25.2.7
-- For development
--[[ local function notify(message) ]]
--[[ ya.notify({ title = "Starship", content = message, timeout = 3 }) ]]
--[[ end ]]
local save = ya.sync(function(st, _cwd, output)
st.output = output
ya.render()
end)
-- Helper function for accessing the `config_file` state variable
---@return string
local get_config_file = ya.sync(function(st)
return st.config_file
end)
return {
---User arguments for setup method
---@class SetupArgs
---@field config_file string Absolute path to a starship config file
---@field hide_flags boolean Whether to hide all flags (such as filter and search). Recommended for themes which are intended to take the full width of the terminal.
---@field flags_after_prompt boolean Whether to place flags (such as filter and search) after the starship prompt. By default this is true.
--- Setup plugin
--- @param st table State
--- @param args SetupArgs|nil
setup = function(st, args)
local hide_flags = false
local flags_after_prompt = true
-- Check setup args
if args ~= nil then
if args.config_file ~= nil then
local url = Url(args.config_file)
if url.is_regular then
local config_file = args.config_file
-- Manually replace '~' and '$HOME' at the start of the path with the OS environment variable
local home = os.getenv("HOME")
if home then
home = tostring(home)
config_file = config_file:gsub("^~", home):gsub("^$HOME", home)
end
st.config_file = config_file
end
end
if args.hide_flags ~= nil then
hide_flags = args.hide_flags
end
if args.flags_after_prompt ~= nil then
flags_after_prompt = args.flags_after_prompt
end
end
-- Replace default header widget
Header:children_remove(1, Header.LEFT)
Header:children_add(function(self)
local max = self._area.w - self._right_width
if max <= 0 then
return ""
end
if hide_flags or not st.output then
return ui.Line.parse(st.output or "")
end
-- Split `st.output` at the first line break (or keep as is if none was found)
local output = st.output:match("([^\n]*)\n?") or st.output
local flags = self:flags()
if flags_after_prompt then
output = output .. " " .. flags
else
output = flags .. " " .. output
end
return ui.Line.parse(output)
end, 1000, Header.LEFT)
-- Pass current working directory and custom config path (if specified) to the plugin's entry point
---Callback for subscribers to update the prompt
local callback = function()
local cwd = cx.active.current.cwd
if st.cwd ~= cwd then
st.cwd = cwd
if ya.confirm then
-- >= yazi 25.2.7
ya.manager_emit("plugin", {
st._id,
ya.quote(tostring(cwd), true),
})
else
-- < yazi 25.2.7
ya.manager_emit("plugin", {
st._id,
args = ya.quote(tostring(cwd), true),
})
end
end
end
-- Subscribe to events
ps.sub("cd", callback)
ps.sub("tab", callback)
end,
entry = function(_, job_or_args)
-- yazi 2024-11-29 changed the way arguments are passed to the plugin
-- entry point. They were moved inside {args = {...}}. If the user is using
-- a version before this change, they can use the old implementation.
-- https://github.com/sxyazi/yazi/pull/1966
local args = job_or_args.args or job_or_args
local command = Command("starship")
:arg("prompt")
:stdin(Command.INHERIT)
:cwd(args[1])
:env("STARSHIP_SHELL", "")
-- Point to custom starship config
local config_file = get_config_file()
if config_file then
command = command:env("STARSHIP_CONFIG", config_file)
end
local output = command:output()
if output then
save(args[1], output.stdout:gsub("^%s+", ""))
end
end,
}

View file

@ -0,0 +1,10 @@
column_width = 100
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 4
quote_style = "AutoPreferDouble"
call_parentheses = "Always"
collapse_simple_statement = "Never"
[sort_requires]
enabled = false

View file

@ -0,0 +1,72 @@
-- function to get paths of selected elements or current directory
-- of no elements are selected
local get_paths = ya.sync(function()
local paths = {}
-- get selected files
for _, u in pairs(cx.active.selected) do
paths[#paths + 1] = tostring(u)
end
-- if no files are selected, get current directory
if #paths == 0 then
if cx.active.current.cwd then
paths[1] = tostring(cx.active.current.cwd)
else
ya.err("what-size would return nil paths")
end
end
return paths
end)
-- Function to get total size from du output
local get_total_size = function(s)
local lines = {}
for line in s:gmatch("[^\n]+") do lines[#lines + 1] = line end
local last_line = lines[#lines]
local last_line_parts = {}
for part in last_line:gmatch("%S+") do last_line_parts[#last_line_parts + 1] = part end
local total_size = last_line_parts[1]
return total_size
end
-- Function to format file size
local function format_size(size)
local units = { "B", "KB", "MB", "GB", "TB" }
local unit_index = 1
while size > 1024 and unit_index < #units do
size = size / 1024
unit_index = unit_index + 1
end
return string.format("%.2f %s", size, units[unit_index])
end
return {
entry = function(self, job)
-- defaults not to use clipboard, use it only if required by the user
local clipboard = job.args.clipboard or job.args[1] == '-c'
local items = get_paths()
local cmd = "du"
local output, err = Command(cmd):arg("-scb"):args(items):output()
if not output then
ya.err("Failed to run diff, error: " .. err)
else
local total_size = get_total_size(output.stdout)
local formatted_size = format_size(tonumber(total_size))
local notification_content = "Total size: " .. formatted_size
if clipboard then
ya.clipboard(formatted_size)
notification_content = notification_content .. "\nCopied to clipboard."
end
ya.notify {
title = "What size",
content = notification_content,
timeout = 5,
}
end
end,
}

View file

@ -123,8 +123,9 @@
protonplus
fstl
nwg-look
librewolf
# screen recording and streaming
kooha
obs-studio
obs-studio-plugins.wlrobs
@ -202,7 +203,7 @@
categories = [ "System" ];
};
mirror-laptop = {
name = "mirror laptop to external display";
name = "mirror laptop";
exec = "/home/emene/.local/bin/mirror-laptop";
categories = [ "System" ];
};

View file

@ -72,23 +72,6 @@ in {
}
];
}
{
name = "laptop-mirror";
exec = [ "notify-send shikane \"Profile $SHIKANE_PROFILE_NAME has been applied\"" "wl-present mirror eDP-1 & sleep .5; wl-present fullscreen-output HDMI-A-1; wl-present fullscreen" ];
output = [
{
enable = true;
search = [ "m=TL140ADXP04-0" "s=" "v=Thermotrex Corporation" ];
mode = "2560x1600@240Hz";
position = "0,0";
adaptive_sync = true;
}
{
enable = true;
search = "n/HDMI-[ABC]-[1-9]";
}
];
}
];
};
}

View file

@ -7,6 +7,7 @@
initLua = ../../homes/emenel/dotfiles/dot_config/yazi/init.lua;
keymap = {
manager.prepend_keymap = [
{ on = "!"; run = "shell '$SHELL' --block"; desc = "Open shell here"; }
{ on = ["c" "d"]; run = "shell 'ripdrag \"$@\" -x 2>/dev/null &' --confirm"; desc = "Drag selection";}
{ on = ["c" "c"]; run = "yank"; desc = "Copy file"; }
{ on = ["c" "n"]; run = "copy filename"; desc = "Copy file name"; }
@ -25,7 +26,6 @@
{ on = "M"; run = "plugin mount"; }
{ on = "p"; run = "plugin smart-paste"; desc = "Paste into the hovered directory or CWD"; }
{ on = ["g" "l"]; run = "plugin lazygit"; desc = "lazygit"; }
{ on = "F"; run = "plugin jump-to-char"; desc = "jump to char"; }
{ on = "k"; run = "plugin arrow -1"; desc = "up"; }
{ on = "j"; run = "plugin arrow 1"; desc = "down"; }
{ on = "<Up>"; run = "plugin arrow -1"; desc = "up"; }
@ -36,7 +36,15 @@
{ on = "<Enter>"; run = "plugin smart-enter"; desc = "Enter the child directory, or open the file"; }
{ on = "<Right>"; run = "plugin smart-enter"; desc = "Enter the child directory, or open the file"; }
{ on = "<Backspace>"; run = "remove"; }
{ on = ["t" "i"]; run = "inspect"; desc = "inspect"; }
{ on = [ "t" "s" ]; run = "plugin what-size"; desc = "Calc size of selection or cwd"; }
{ on = ["d" "u"]; run = "plugin restore"; desc = "Restore last deleted files/folders"; }
{ on = ["d" "d"]; run = "remove"; desc = "Delete files/folders"; }
{ on = ["F" "g"]; run = "plugin fg"; desc = "Find file by content (fuzzy)"; }
{ on = ["F" "G"]; run = "plugin fg rg"; desc = "Find file by content (ripgrep)"; }
{ on = ["F" "f"]; run = "plugin fg fzf"; desc = "Find file by name (fuzzy)"; }
];
};
settings = {
@ -46,6 +54,10 @@
show_symlink = true;
};
opener = {
open = [
{ run = "plugin open-with-cmd --args=block"; desc = "Open with command in the terminal"; }
{ run = "plugin open-with-cmd"; desc = "Open with command"; }
];
edit = [
{ run = "emacsclient -r -n \"$@\""; desc = "open in current emacsclient"; orphan = true; }
];
@ -68,6 +80,9 @@
{ mime = "application/x-xz"; run = "ouch"; }
{ mime = "application/octet-stream"; run = "ouch"; }
{ name = "*.el"; run = "code"; }
{ name = "*.lisp"; run = "code"; }
{ name = "*.org"; run = "glow"; }
{ name = "*"; run = "file-extra-metadata"; }
{ name = "*.md"; run = "glow"; }
{ mime = "{image,audio,video}/*"; run = "mediainfo";}
@ -94,6 +109,8 @@
name = "*/";
run = "git";
}
{ id = "simple-tag"; name = "*"; run = "simple-tag"; }
{ id = "simple-tag"; name = "*/"; run = "simple-tag"; }
];
};
};