mirror of
https://github.com/kristoferssolo/solorice.git
synced 2026-03-18 08:09:40 +00:00
Update 2026-03-13
This commit is contained in:
@@ -192,11 +192,10 @@ depends = [ "tmux", "zsh" ]
|
||||
"config/gtk-4.0/" = "~/.config/gtk-4.0/"
|
||||
|
||||
[codex.files]
|
||||
# "config/AGENTS.md" = "~/.codex/AGENTS.md"
|
||||
"config/AGENTS.md" = "~/.codex/AGENTS.md"
|
||||
|
||||
[claude.files]
|
||||
# "config/AGENTS.md" = "~/.claude/AGENTS.md"
|
||||
"config/AGENTS-CLAUDE.md" = "~/.claude/AGENTS.md"
|
||||
|
||||
[opencode.files]
|
||||
"config/opencode/" = "~/.config/opencode/"
|
||||
"config/AGENTS.md" = "~/.config/opencode/AGENTS.md"
|
||||
|
||||
1
config/AGENTS-CLAUDE.md
Symbolic link
1
config/AGENTS-CLAUDE.md
Symbolic link
@@ -0,0 +1 @@
|
||||
AGENTS.md
|
||||
@@ -891,7 +891,7 @@
|
||||
"authenticated": true,
|
||||
"url": "https://api.vencord.dev/",
|
||||
"settingsSync": true,
|
||||
"settingsSyncVersion": 1770991316025
|
||||
"settingsSyncVersion": 1773063985102
|
||||
},
|
||||
"enabledThemes": [],
|
||||
"eagerPatches": false,
|
||||
|
||||
@@ -2,6 +2,7 @@ log_level = "warn"
|
||||
outputs = "All"
|
||||
position = "Top"
|
||||
app_launcher_cmd = "fuzzel"
|
||||
enable_esc_key = true
|
||||
|
||||
[modules]
|
||||
left = [ [ "Workspaces" ] ]
|
||||
@@ -14,7 +15,7 @@ update_cmd = 'alacritty -e bash -c "paru; echo Done - Press enter to exit; read"
|
||||
|
||||
[workspaces]
|
||||
visibility_mode = "All"
|
||||
# enable_workspace_filling = true
|
||||
# enable_workspace_filling = false
|
||||
# disable_special_workspaces = true
|
||||
|
||||
[clock]
|
||||
@@ -34,6 +35,7 @@ reboot_cmd = "loginctl reboot"
|
||||
logout_cmd = "logout"
|
||||
remove_airplane_btn = true
|
||||
remove_idle_btn = true
|
||||
peripheral_indicators = {Specific = [ "Gamepad", "Keyboard" ]}
|
||||
|
||||
[system_info]
|
||||
indicators = [ "Cpu", "Memory", "Temperature" ]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
# Specify desired highlighting theme (e.g. "TwoDark"). Run `bat --list-themes`
|
||||
# for a list of all available themes
|
||||
--theme="Rosé Pine"
|
||||
--theme="rose-pine"
|
||||
|
||||
# Enable this to use italic text on the terminal. This is not supported on all
|
||||
# terminal emulators (like tmux, by default):
|
||||
|
||||
158
config/fish/completions/tree-sitter.fish
Normal file
158
config/fish/completions/tree-sitter.fish
Normal file
@@ -0,0 +1,158 @@
|
||||
# Print an optspec for argparse to handle cmd's options that are independent of any subcommand.
|
||||
function __fish_tree_sitter_global_optspecs
|
||||
string join \n h/help V/version
|
||||
end
|
||||
|
||||
function __fish_tree_sitter_needs_command
|
||||
# Figure out if the current invocation already has a command.
|
||||
set -l cmd (commandline -opc)
|
||||
set -e cmd[1]
|
||||
argparse -s (__fish_tree_sitter_global_optspecs) -- $cmd 2>/dev/null
|
||||
or return
|
||||
if set -q argv[1]
|
||||
# Also print the command, so this can be used to figure out what it is.
|
||||
echo $argv[1]
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function __fish_tree_sitter_using_subcommand
|
||||
set -l cmd (__fish_tree_sitter_needs_command)
|
||||
test -z "$cmd"
|
||||
and return 1
|
||||
contains -- $cmd[1] $argv
|
||||
end
|
||||
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -s V -l version -d 'Print version'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "init-config" -d 'Generate a default config file'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "init" -d 'Initialize a grammar repository'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "generate" -d 'Generate a parser'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "build" -d 'Compile a parser'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "parse" -d 'Parse files'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "test" -d 'Run a parser\'s tests'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "version" -d 'Increment the version of a grammar'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "fuzz" -d 'Fuzz a parser'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "query" -d 'Search files using a syntax tree query'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "highlight" -d 'Highlight a file'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "tags" -d 'Generate a list of tags'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "playground" -d 'Start local playground for a parser in the browser'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "dump-languages" -d 'Print info about all known language parsers'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_needs_command" -f -a "complete" -d 'Generate shell completions'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand init-config" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand init" -s u -l update -d 'Update outdated files'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand init" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -l abi -d 'Select the language ABI version to generate (default 15). Use --abi=latest to generate the newest supported version (15).' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -l libdir -d 'The path to the directory containing the parser library' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -s o -l output -d 'The path to output the generated source files' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -l report-states-for-rule -d 'Produce a report of the states for the given rule, use `-` to report every rule' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -l js-runtime -d 'The name or path of the JavaScript runtime to use for generating parsers' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -s l -l log -d 'Show debug log during generation'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -l no-bindings -d 'Deprecated (no-op)'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -s b -l build -d 'Compile all defined languages in the current dir'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -s 0 -l debug-build -d 'Compile a parser in debug mode'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -l json -d 'Report conflicts in a JSON format'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand generate" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand build" -s o -l output -d 'The path to output the compiled file' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand build" -s w -l wasm -d 'Build a WASM module instead of a dynamic library'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand build" -s d -l docker -d 'Run emscripten via docker even if it is installed locally (only if building a WASM module with --wasm)'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand build" -l reuse-allocator -d 'Make the parser reuse the same allocator as the library'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand build" -s 0 -l debug -d 'Compile a parser in debug mode'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand build" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l paths -d 'The path to a file with paths to source file(s)' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l scope -d 'Select a language by the scope instead of a file extension' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s d -l debug -d 'Show parsing debug log' -r -f -a "quiet\t''
|
||||
normal\t''
|
||||
pretty\t''"
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l timeout -d 'Interrupt the parsing process by timeout (µs)' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l edits -d 'Apply edits in the format: \\"row,col|position delcount insert_text\\", can be supplied multiple times' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l encoding -d 'The encoding of the input files' -r -f -a "utf8\t''
|
||||
utf16-le\t''
|
||||
utf16-be\t''"
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l config-path -d 'The path to an alternative config.json file' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s n -l test-number -d 'Parse the contents of a specific test' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s 0 -l debug-build -d 'Compile a parser in debug mode'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s D -l debug-graph -d 'Produce the log.html file with debug graphs'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l wasm -d 'Compile parsers to wasm instead of native dynamic libraries'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l dot -d 'Output the parse data with graphviz dot'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s x -l xml -d 'Output the parse data in XML format'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s c -l cst -d 'Output the parse data in a pretty-printed CST format'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s s -l stat -d 'Show parsing statistic'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s t -l time -d 'Measure execution time'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s q -l quiet -d 'Suppress main output'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l open-log -d 'Open `log.html` in the default browser, if `--debug-graph` is supplied'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s j -l json -d 'Output parsing results in a JSON format'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s r -l rebuild -d 'Force rebuild the parser'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -l no-ranges -d 'Omit ranges in the output'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand parse" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -s i -l include -d 'Only run corpus test cases whose name matches the given regex' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -s e -l exclude -d 'Only run corpus test cases whose name does not match the given regex' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -l file-name -d 'Only run corpus test cases from from a given filename' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -l config-path -d 'The path to an alternative config.json file' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -l stat -d 'Show parsing statistics' -r -f -a "all\t''
|
||||
outliers-and-total\t''
|
||||
total-only\t''"
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -s u -l update -d 'Update all syntax trees in corpus files with current parser output'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -s d -l debug -d 'Show parsing debug log'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -s 0 -l debug-build -d 'Compile a parser in debug mode'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -s D -l debug-graph -d 'Produce the log.html file with debug graphs'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -l wasm -d 'Compile parsers to wasm instead of native dynamic libraries'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -l open-log -d 'Open `log.html` in the default browser, if `--debug-graph` is supplied'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -l show-fields -d 'Force showing fields in test diffs'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -s r -l rebuild -d 'Force rebuild the parser'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -l overview-only -d 'Show only the pass-fail overview tree'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand test" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand version" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -s s -l skip -d 'List of test names to skip' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -l subdir -d 'Subdirectory to the language' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -l edits -d 'Maximum number of edits to perform per fuzz test' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -l iterations -d 'Number of fuzzing iterations to run per test' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -s i -l include -d 'Only fuzz corpus test cases whose name matches the given regex' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -s e -l exclude -d 'Only fuzz corpus test cases whose name does not match the given regex' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -l log-graphs -d 'Enable logging of graphs and input'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -s l -l log -d 'Enable parser logging'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -s r -l rebuild -d 'Force rebuild the parser'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand fuzz" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -l paths -d 'The path to a file with paths to source file(s)' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -l byte-range -d 'The range of byte offsets in which the query will be executed' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -l row-range -d 'The range of rows in which the query will be executed' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -l scope -d 'Select a language by the scope instead of a file extension' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -l config-path -d 'The path to an alternative config.json file' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -s n -l test-number -d 'Query the contents of a specific test' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -s t -l time -d 'Measure execution time'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -s q -l quiet -d 'Suppress main output'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -s c -l captures -d 'Order by captures instead of matches'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -l test -d 'Whether to run query tests or not'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand query" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -l captures-path -d 'The path to a file with captures' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -l query-paths -d 'The paths to files with queries' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -l scope -d 'Select a language by the scope instead of a file extension' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -l paths -d 'The path to a file with paths to source file(s)' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -l config-path -d 'The path to an alternative config.json file' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -s n -l test-number -d 'Highlight the contents of a specific test' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -s H -l html -d 'Generate highlighting as an HTML document'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -l css-classes -d 'When generating HTML, use css classes rather than inline styles'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -l check -d 'Check that highlighting captures conform strictly to standards'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -s t -l time -d 'Measure execution time'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -s q -l quiet -d 'Suppress main output'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand highlight" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand tags" -l scope -d 'Select a language by the scope instead of a file extension' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand tags" -l paths -d 'The path to a file with paths to source file(s)' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand tags" -l config-path -d 'The path to an alternative config.json file' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand tags" -s n -l test-number -d 'Generate tags from the contents of a specific test' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand tags" -s t -l time -d 'Measure execution time'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand tags" -s q -l quiet -d 'Suppress main output'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand tags" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand playground" -l grammar-path -d 'Path to the directory containing the grammar and wasm files' -r
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand playground" -s q -l quiet -d 'Don\'t open in default browser'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand playground" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand dump-languages" -l config-path -d 'The path to an alternative config.json file' -r -F
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand dump-languages" -s h -l help -d 'Print help'
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand complete" -s s -l shell -d 'The shell to generate completions for' -r -f -a "bash\t''
|
||||
elvish\t''
|
||||
fish\t''
|
||||
power-shell\t''
|
||||
zsh\t''
|
||||
nushell\t''"
|
||||
complete -c tree-sitter -n "__fish_tree_sitter_using_subcommand complete" -s h -l help -d 'Print help'
|
||||
@@ -2,33 +2,27 @@
|
||||
// "/-" comments out the following node.
|
||||
// Check the wiki for a full description of the configuration:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Introduction
|
||||
|
||||
workspace "browser" {
|
||||
open-on-output "DP-1"
|
||||
}
|
||||
|
||||
workspace "terminal" {
|
||||
open-on-output "DP-1"
|
||||
}
|
||||
|
||||
workspace "chat" {
|
||||
open-on-output "DP-2"
|
||||
}
|
||||
|
||||
workspace "music" {
|
||||
open-on-output "DP-2"
|
||||
}
|
||||
|
||||
environment {
|
||||
QT_QPA_PLATFORM "wayland"
|
||||
XDG_SESSION_TYPE "wayland"
|
||||
XDG_CURRENT_DESKTOP "niri"
|
||||
XDG_SESSION_DESKTOP "niri"
|
||||
WM "niri"
|
||||
DISPLAY ":0" // for X11 apps to run
|
||||
ELECTRON_OZONE_PLATFORM_HINT "auto"
|
||||
DISPLAY ":0"
|
||||
// for X11 apps to run ELECTRON_OZONE_PLATFORM_HINT "auto"
|
||||
}
|
||||
|
||||
// Input device configuration.
|
||||
// Find the full list of options on the wiki:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Input
|
||||
@@ -37,44 +31,36 @@ input {
|
||||
xkb {
|
||||
// You can set rules, model, layout, variant and options.
|
||||
// For more information, see xkeyboard-config(7).
|
||||
|
||||
// For example:
|
||||
layout "lv"
|
||||
// options "grp:win_space_toggle,compose:ralt,ctrl:nocaps"
|
||||
options "caps:escape"
|
||||
}
|
||||
|
||||
// Enable numlock on startup, omitting this setting disables it.
|
||||
repeat-delay 300
|
||||
repeat-rate 50
|
||||
track-layout "global"
|
||||
// numlock
|
||||
// numlock
|
||||
}
|
||||
|
||||
mouse {
|
||||
// off
|
||||
// natural-scroll
|
||||
accel-speed -0.6
|
||||
accel-profile "flat"
|
||||
// scroll-method "no-scroll"
|
||||
// scroll-method "no-scroll"
|
||||
}
|
||||
|
||||
// Uncomment this to make the mouse warp to the center of newly focused windows.
|
||||
warp-mouse-to-focus
|
||||
|
||||
// Focus windows and outputs automatically when moving the mouse into them.
|
||||
// Setting max-scroll-amount="0%" makes it work only on windows already fully on screen.
|
||||
focus-follows-mouse max-scroll-amount="95%"
|
||||
}
|
||||
|
||||
cursor {
|
||||
// xcursor-theme "breeze_cursors"
|
||||
xcursor-size 16
|
||||
|
||||
hide-when-typing
|
||||
hide-after-inactive-ms 1000
|
||||
}
|
||||
|
||||
// You can configure outputs by their name, which you can find
|
||||
// by running `niri msg outputs` while inside a niri instance.
|
||||
// The built-in laptop monitor is usually called "eDP-1".
|
||||
@@ -84,7 +70,6 @@ cursor {
|
||||
output "DP-1" {
|
||||
// Uncomment this line to disable this output.
|
||||
// off
|
||||
|
||||
// Resolution and, optionally, refresh rate of the output.
|
||||
// The format is "<width>x<height>" or "<width>x<height>@<refresh rate>".
|
||||
// If the refresh rate is omitted, niri will pick the highest refresh rate
|
||||
@@ -92,14 +77,14 @@ output "DP-1" {
|
||||
// If the mode is omitted altogether or is invalid, niri will pick one automatically.
|
||||
// Run `niri msg outputs` while inside a niri instance to list all outputs and their modes.
|
||||
mode "2560x1440@180.002"
|
||||
|
||||
// You can use integer or fractional scale, for example use 1.5 for 150% scale.
|
||||
scale 1
|
||||
|
||||
// Transform allows to rotate the output counter-clockwise, valid values are:
|
||||
// normal, 90, 180, 270, flipped, flipped-90, flipped-180 and flipped-270.
|
||||
transform "normal"
|
||||
|
||||
hot-corners {
|
||||
off
|
||||
}
|
||||
// Position of the output in the global coordinate space.
|
||||
// This affects directional monitor actions like "focus-monitor-left", and cursor movement.
|
||||
// The cursor can only move between directly adjacent outputs.
|
||||
@@ -110,34 +95,43 @@ output "DP-1" {
|
||||
// If the position is unset or results in an overlap, the output is instead placed
|
||||
// automatically.
|
||||
position x=0 y=0
|
||||
|
||||
focus-at-startup
|
||||
variable-refresh-rate on-demand=true
|
||||
background-color "#000"
|
||||
backdrop-color "#000"
|
||||
}
|
||||
|
||||
output "DP-2" {
|
||||
mode "1920x1080@74.973"
|
||||
scale 1
|
||||
|
||||
transform "normal"
|
||||
|
||||
position x=-1920 y=180
|
||||
|
||||
variable-refresh-rate on-demand=false
|
||||
focus-at-startup
|
||||
transform "90"
|
||||
position x=2560 y=-325
|
||||
hot-corners {
|
||||
off
|
||||
}
|
||||
background-color "#000"
|
||||
backdrop-color "#000"
|
||||
layout {
|
||||
default-column-width {
|
||||
proportion 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
output "HDMI-A-1" {
|
||||
mode "1920x1080@60"
|
||||
scale 1
|
||||
position x=-1920 y=245
|
||||
hot-corners {
|
||||
off
|
||||
}
|
||||
background-color "#000"
|
||||
backdrop-color "#000"
|
||||
}
|
||||
|
||||
|
||||
// Settings that influence how windows are positioned and sized.
|
||||
// Find more information on the wiki:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Layout
|
||||
layout {
|
||||
// Set gaps around windows in logical pixels.
|
||||
gaps 4
|
||||
|
||||
// When to center a column when changing focus, options are:
|
||||
// - "never", default behavior, focusing an off-screen column will keep at the left
|
||||
// or right edge of the screen.
|
||||
@@ -145,7 +139,6 @@ layout {
|
||||
// - "on-overflow", focusing a column will center it if it doesn't fit
|
||||
// together with the previously focused column.
|
||||
center-focused-column "never"
|
||||
|
||||
// You can customize the widths that "switch-preset-column-width" (Mod+R) toggles between.
|
||||
preset-column-widths {
|
||||
// Proportion sets the width as a fraction of the output width, taking gaps into account.
|
||||
@@ -154,19 +147,17 @@ layout {
|
||||
proportion 0.33333
|
||||
proportion 0.5
|
||||
proportion 0.66667
|
||||
|
||||
// Fixed sets the width in logical pixels exactly.
|
||||
// Fixed sets the width in logical pixels exactly.
|
||||
// fixed 1920
|
||||
}
|
||||
|
||||
// You can also customize the heights that "switch-preset-window-height" (Mod+Shift+R) toggles between.
|
||||
// preset-window-heights { }
|
||||
|
||||
// You can change the default width of the new windows.
|
||||
default-column-width { proportion 0.5; }
|
||||
default-column-width {
|
||||
proportion 0.5
|
||||
}
|
||||
// If you leave the brackets empty, the windows themselves will decide their initial width.
|
||||
// default-column-width {}
|
||||
|
||||
// By default focus ring and border are rendered as a solid background rectangle
|
||||
// behind windows. That is, they will show up through semitransparent windows.
|
||||
// This is because windows using client-side decorations can have an arbitrary shape.
|
||||
@@ -177,27 +168,21 @@ layout {
|
||||
//
|
||||
// Alternatively, you can override it with a window rule called
|
||||
// `draw-border-with-background`.
|
||||
|
||||
// You can change how the focus ring looks.
|
||||
focus-ring {
|
||||
// Uncomment this line to disable the focus ring.
|
||||
off
|
||||
|
||||
// How many logical pixels the ring extends out from the windows.
|
||||
width 2
|
||||
|
||||
// Colors can be set in a variety of ways:
|
||||
// - CSS named colors: "red"
|
||||
// - RGB hex: "#rgb", "#rgba", "#rrggbb", "#rrggbbaa"
|
||||
// - CSS-like notation: "rgb(255, 127, 0)", rgba(), hsl() and a few others.
|
||||
|
||||
// Color of the ring on the active monitor.
|
||||
active-color "#ebbcba"
|
||||
|
||||
// Color of the ring on inactive monitors.
|
||||
inactive-color "#6e6a86"
|
||||
|
||||
// You can also use gradients. They take precedence over solid colors.
|
||||
// You can also use gradients. They take precedence over solid colors.
|
||||
// Gradients are rendered the same as CSS linear-gradient(angle, from, to).
|
||||
// The angle is the same as in linear-gradient, and is optional,
|
||||
// defaulting to 180 (top-to-bottom gradient).
|
||||
@@ -212,29 +197,23 @@ layout {
|
||||
//
|
||||
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view"
|
||||
}
|
||||
|
||||
// You can also add a border. It's similar to the focus ring, but always visible.
|
||||
border {
|
||||
// The settings are the same as for the focus ring.
|
||||
// If you enable the border, you probably want to disable the focus ring.
|
||||
// off
|
||||
|
||||
width 2
|
||||
active-color "#ebbcba"
|
||||
inactive-color "#6e6a86"
|
||||
|
||||
// Color of the border around windows that request your attention.
|
||||
urgent-color "#eb6f92"
|
||||
|
||||
// active-gradient from="#ffbb66" to="#ffc880" angle=45 relative-to="workspace-view"
|
||||
// active-gradient from="#ffbb66" to="#ffc880" angle=45 relative-to="workspace-view"
|
||||
// inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view"
|
||||
}
|
||||
|
||||
// You can enable drop shadows for windows.
|
||||
shadow {
|
||||
// Uncomment the next line to enable shadows.
|
||||
// on
|
||||
|
||||
// By default, the shadow draws only around its window, and not behind it.
|
||||
// Uncomment this setting to make the shadow draw behind its window.
|
||||
//
|
||||
@@ -250,41 +229,34 @@ layout {
|
||||
// draws any.
|
||||
//
|
||||
// draw-behind-window true
|
||||
|
||||
// You can change how shadows look. The values below are in logical
|
||||
// pixels and match the CSS box-shadow properties.
|
||||
|
||||
// Softness controls the shadow blur radius.
|
||||
softness 30
|
||||
|
||||
// Spread expands the shadow.
|
||||
spread 5
|
||||
|
||||
// Offset moves the shadow relative to the window.
|
||||
offset x=0 y=5
|
||||
|
||||
// You can also change the shadow color and opacity.
|
||||
color "#0007"
|
||||
}
|
||||
|
||||
// Struts shrink the area occupied by windows, similarly to layer-shell panels.
|
||||
// You can think of them as a kind of outer gaps. They are set in logical pixels.
|
||||
// Left and right struts will cause the next window to the side to always be visible.
|
||||
// Top and bottom struts will simply add outer gaps in addition to the area occupied by
|
||||
// layer-shell panels and regular gaps.
|
||||
struts {
|
||||
// left 64
|
||||
// left 64
|
||||
// right 64
|
||||
// top 64
|
||||
// bottom 64
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Add lines like this to spawn processes at startup.
|
||||
// Note that running niri as a session supports xdg-desktop-autostart,
|
||||
// which may be more convenient to use.
|
||||
// See the binds section below for more spawn examples.
|
||||
|
||||
// This line starts waybar, a commonly used bar for Wayland compositors.
|
||||
spawn-at-startup "pipewire"
|
||||
spawn-at-startup "pipewire-pulse"
|
||||
@@ -305,38 +277,31 @@ spawn-at-startup "AyuGram"
|
||||
spawn-at-startup "discord"
|
||||
spawn-at-startup "swap-wallpaper"
|
||||
spawn-at-startup "spotify-launcher"
|
||||
|
||||
// Uncomment this line to ask the clients to omit their client-side decorations if possible.
|
||||
// If the client will specifically ask for CSD, the request will be honored.
|
||||
// Additionally, clients will be informed that they are tiled, removing some client-side rounded corners.
|
||||
// This option will also fix border/focus ring drawing behind some semitransparent windows.
|
||||
// After enabling or disabling this, you need to restart the apps for this to take effect.
|
||||
prefer-no-csd
|
||||
|
||||
// You can change the path where screenshots are saved.
|
||||
// A ~ at the front will be expanded to the home directory.
|
||||
// The path is formatted with strftime(3) to give you the screenshot date and time.
|
||||
screenshot-path "~/Pictures/screenshots/%Y-%m-%d_%H-%M-%S.png"
|
||||
|
||||
// You can also set this to null to disable saving screenshots to disk.
|
||||
// screenshot-path null
|
||||
|
||||
// Animation settings.
|
||||
// The wiki explains how to configure individual animations:
|
||||
// https://github.com/YaLTeR/niri/wiki/Configuration:-Animations
|
||||
animations {
|
||||
// Uncomment to turn off all animations.
|
||||
off
|
||||
|
||||
// Slow down all animations by this factor. Values below 1 speed them up instead.
|
||||
slowdown 3.0
|
||||
}
|
||||
|
||||
layer-rule {
|
||||
match namespace="^notifications$"
|
||||
block-out-from "screencast"
|
||||
}
|
||||
|
||||
window-rule {
|
||||
draw-border-with-background false
|
||||
}
|
||||
@@ -350,90 +315,80 @@ window-rule {
|
||||
// This regular expression is intentionally made as specific as possible,
|
||||
// since this is the default config, and we want no false positives.
|
||||
// You can get away with just app-id="wezterm" if you want.
|
||||
match app-id=r#"^org\.wezfurlong\.wezterm$"#
|
||||
default-column-width {}
|
||||
}
|
||||
match app-id="^org\\.wezfurlong\\.wezterm$"
|
||||
default-column-width {
|
||||
|
||||
}
|
||||
}
|
||||
// Open the Firefox picture-in-picture player as floating by default.
|
||||
window-rule {
|
||||
// This app-id regular expression will work for both:
|
||||
// - host Firefox (app-id is "firefox")
|
||||
// - Flatpak Firefox (app-id is "org.mozilla.firefox")
|
||||
match app-id=r#"firefox$"# title="^Picture-in-Picture$"
|
||||
match app-id=r#"floorp-default"# title="^Picture-in-Picture$"
|
||||
match app-id="firefox$" title="^Picture-in-Picture$"
|
||||
match app-id="floorp-default" title="^Picture-in-Picture$"
|
||||
open-floating true
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match title=r#"^Extension:.*Bitwarden.*"#
|
||||
|
||||
match title="^Extension:.*Bitwarden.*"
|
||||
default-floating-position x=10 y=10 relative-to="top-right"
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id="steam" title=r#"^notificationtoasts_\d+_desktop$"#
|
||||
match app-id="steam" title="^notificationtoasts_\\d+_desktop$"
|
||||
default-floating-position x=10 y=10 relative-to="bottom-right"
|
||||
}
|
||||
|
||||
// Example: block out two password managers from screen capture.
|
||||
// (This example rule is commented out with a "/-" in front.)
|
||||
window-rule {
|
||||
match app-id=r#"^org\.keepassxc\.KeePassXC$"#
|
||||
match app-id=r#"^org\.gnome\.World\.Secrets$"#
|
||||
match app-id=r#"^org\.gnome\.World\.Secrets$"#
|
||||
match app-id="^org\\.keepassxc\\.KeePassXC$"
|
||||
match app-id="^org\\.gnome\\.World\\.Secrets$"
|
||||
match app-id="^org\\.gnome\\.World\\.Secrets$"
|
||||
match title="Bitwarden"
|
||||
|
||||
block-out-from "screen-capture"
|
||||
|
||||
// Use this instead if you want them visible on third-party screenshot tools.
|
||||
// Use this instead if you want them visible on third-party screenshot tools.
|
||||
// block-out-from "screencast"
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match at-startup=true app-id="floorp-default"
|
||||
match at-startup=true app-id="floorp"
|
||||
open-on-workspace "browser"
|
||||
open-maximized true
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match at-startup=true app-id=r#"^org\.telegram\.desktop$"#
|
||||
match at-startup=true app-id=r#"^com\.ayugram\.desktop$"#
|
||||
match at-startup=true app-id=r#"^org\.gnome\.Fractal$"#
|
||||
match at-startup=true app-id=r#"discord"#
|
||||
match at-startup=true app-id=r#"vesktop"#
|
||||
|
||||
exclude app-id=r#"^com\.ayugram\.desktop$"# title="^Media viewer$"
|
||||
exclude app-id=r#"^org\.telegram\.desktop$"# title="^Media viewer$"
|
||||
match at-startup=true app-id="^org\\.telegram\\.desktop$"
|
||||
match at-startup=true app-id="^com\\.ayugram\\.desktop$"
|
||||
match at-startup=true app-id="^org\\.gnome\\.Fractal$"
|
||||
match at-startup=true app-id="discord"
|
||||
match at-startup=true app-id="vesktop"
|
||||
match at-startup=true app-id="Element"
|
||||
match at-startup=true app-id="^org\\.telegram\\.desktop$"
|
||||
match at-startup=true app-id="^com\\.ayugram\\.desktop$"
|
||||
exclude app-id="^com\\.ayugram\\.desktop$" title="^Media viewer$"
|
||||
exclude app-id="^org\\.telegram\\.desktop$" title="^Media viewer$"
|
||||
open-on-workspace "chat"
|
||||
}
|
||||
window-rule {
|
||||
match app-id=r#"^org\.telegram\.desktop$"#
|
||||
match app-id=r#"^com\.ayugram\.desktop$"#
|
||||
match app-id=r#"^org\.gnome\.Fractal$"#
|
||||
match app-id=r#"discord"#
|
||||
match app-id=r#"vesktop"#
|
||||
match app-id=r#"spotify"#
|
||||
|
||||
match app-id="^org\\.telegram\\.desktop$"
|
||||
match app-id="^com\\.ayugram\\.desktop$"
|
||||
match app-id="^org\\.gnome\\.Fractal$"
|
||||
match app-id="discord"
|
||||
match app-id="vesktop"
|
||||
match app-id="spotify"
|
||||
match app-id="Element"
|
||||
opacity 0.95
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id="mpv"
|
||||
open-fullscreen true
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match app-id=r#"^com\.ayugram\.desktop$"# title="^Media viewer$"
|
||||
match app-id=r#"^org\.telegram\.desktop$"# title="^Media viewer$"
|
||||
|
||||
match app-id="^com\\.ayugram\\.desktop$" title="^Media viewer$"
|
||||
match app-id="^org\\.telegram\\.desktop$" title="^Media viewer$"
|
||||
open-maximized true
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match at-startup=true app-id="spotify"
|
||||
open-maximized true
|
||||
|
||||
open-on-workspace "music"
|
||||
}
|
||||
window-rule {
|
||||
@@ -443,14 +398,12 @@ window-rule {
|
||||
open-floating true
|
||||
open-focused true
|
||||
}
|
||||
|
||||
// Example: enable rounded corners for all windows.
|
||||
// (This example rule is commented out with a "/-" in front.)
|
||||
/-window-rule {
|
||||
geometry-corner-radius 12
|
||||
clip-to-geometry true
|
||||
geometry-corner-radius 12
|
||||
clip-to-geometry true
|
||||
}
|
||||
|
||||
binds {
|
||||
// Keys consist of modifiers separated by + signs, followed by an XKB key name
|
||||
// in the end. To find an XKB name for a particular key, you may use a program
|
||||
@@ -461,115 +414,195 @@ binds {
|
||||
//
|
||||
// Most actions that you can bind here can also be invoked programmatically with
|
||||
// `niri msg action do-something`.
|
||||
|
||||
// Mod-Shift-/, which is usually the same as Mod-?,
|
||||
// shows a list of important hotkeys.
|
||||
Mod+Shift+Slash { show-hotkey-overlay; }
|
||||
|
||||
Mod+Shift+Slash {
|
||||
show-hotkey-overlay
|
||||
}
|
||||
// Suggested binds for running programs: terminal, app launcher, screen locker.
|
||||
Mod+Return hotkey-overlay-title="Open a Terminal: {{terminal}}" { spawn "{{terminal}}"; }
|
||||
Mod+P hotkey-overlay-title="Run an Application: fuzzel" { spawn "fuzzel"; }
|
||||
Mod+Return hotkey-overlay-title="Open a Terminal: {{terminal}}" {
|
||||
spawn "{{terminal}}"
|
||||
}
|
||||
Mod+P hotkey-overlay-title="Run an Application: fuzzel" {
|
||||
spawn "fuzzel"
|
||||
}
|
||||
// Super+Space allow-when-locked=true hotkey-overlay-title="Lock the Screen: swaylock" { spawn "swaylock"; }
|
||||
// Super+Space hotkey-overlay-title="Lock the Screen: hyprlock" { spawn "hyprlock"; }
|
||||
|
||||
// You can also use a shell. Do this if you need pipes, multiple commands, etc.
|
||||
// Note: the entire command goes as a single argument in the end.
|
||||
// Mod+T { spawn "bash" "-c" "notify-send hello && exec alacritty"; }
|
||||
|
||||
// Example volume keys mappings for PipeWire & WirePlumber.
|
||||
// The allow-when-locked=true property makes them work even when the session is locked.
|
||||
XF86AudioRaiseVolume allow-when-locked=true { spawn-sh "wpctl set-volume $(get-spotify-id) 0.02+"; }
|
||||
XF86AudioLowerVolume allow-when-locked=true { spawn-sh "wpctl set-volume $(get-spotify-id) 0.02-"; }
|
||||
XF86AudioMute allow-when-locked=true { spawn "sp" "play"; }
|
||||
XF86AudioMicMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; }
|
||||
Page_Down allow-when-locked=true {spawn "sp" "next"; }
|
||||
Page_Up allow-when-locked=true {spawn "sp" "prev"; }
|
||||
|
||||
XF86AudioRaiseVolume allow-when-locked=true {
|
||||
spawn-sh "wpctl set-volume $(get-spotify-id) 0.02+"
|
||||
}
|
||||
XF86AudioLowerVolume allow-when-locked=true {
|
||||
spawn-sh "wpctl set-volume $(get-spotify-id) 0.02-"
|
||||
}
|
||||
XF86AudioMute allow-when-locked=true {
|
||||
spawn "sp" "play"
|
||||
}
|
||||
XF86AudioMicMute allow-when-locked=true {
|
||||
spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"
|
||||
}
|
||||
Page_Down allow-when-locked=true {
|
||||
spawn "sp" "next"
|
||||
}
|
||||
Page_Up allow-when-locked=true {
|
||||
spawn "sp" "prev"
|
||||
}
|
||||
// Open/close the Overview: a zoomed-out view of workspaces and windows.
|
||||
// You can also move the mouse into the top-left hot corner,
|
||||
// or do a four-finger swipe up on a touchpad.
|
||||
Mod+O repeat=false { toggle-overview; }
|
||||
|
||||
Mod+Shift+Q { close-window; }
|
||||
|
||||
Mod+H { focus-column-left; }
|
||||
Mod+J { focus-window-down; }
|
||||
Mod+K { focus-window-up; }
|
||||
Mod+L { focus-column-right; }
|
||||
|
||||
Mod+Shift+H { move-column-left; }
|
||||
Mod+Shift+J { move-window-down; }
|
||||
Mod+Shift+K { move-window-up; }
|
||||
Mod+Shift+L { move-column-right; }
|
||||
|
||||
Mod+O repeat=false {
|
||||
toggle-overview
|
||||
}
|
||||
Mod+Shift+Q {
|
||||
close-window
|
||||
}
|
||||
Mod+H {
|
||||
focus-column-left
|
||||
}
|
||||
Mod+J {
|
||||
focus-window-down
|
||||
}
|
||||
Mod+K {
|
||||
focus-window-up
|
||||
}
|
||||
Mod+L {
|
||||
focus-column-right
|
||||
}
|
||||
Mod+Shift+H {
|
||||
move-column-left
|
||||
}
|
||||
Mod+Shift+J {
|
||||
move-window-down
|
||||
}
|
||||
Mod+Shift+K {
|
||||
move-window-up
|
||||
}
|
||||
Mod+Shift+L {
|
||||
move-column-right
|
||||
}
|
||||
// Alternative commands that move across workspaces when reaching
|
||||
// the first or last window in a column.
|
||||
// Mod+J { focus-window-or-workspace-down; }
|
||||
// Mod+K { focus-window-or-workspace-up; }
|
||||
// Mod+Ctrl+J { move-window-down-or-to-workspace-down; }
|
||||
// Mod+Ctrl+K { move-window-up-or-to-workspace-up; }
|
||||
|
||||
Mod+Home { focus-column-first; }
|
||||
Mod+End { focus-column-last; }
|
||||
Mod+Ctrl+Home { move-column-to-first; }
|
||||
Mod+Ctrl+End { move-column-to-last; }
|
||||
|
||||
Mod+Ctrl+H { focus-monitor-left; }
|
||||
Mod+Ctrl+J { focus-monitor-down; }
|
||||
Mod+Ctrl+K { focus-monitor-up; }
|
||||
Mod+Ctrl+L { focus-monitor-right; }
|
||||
|
||||
Mod+Shift+Ctrl+H { move-column-to-monitor-left; }
|
||||
Mod+Shift+Ctrl+J { move-column-to-monitor-down; }
|
||||
Mod+Shift+Ctrl+K { move-column-to-monitor-up; }
|
||||
Mod+Shift+Ctrl+L { move-column-to-monitor-right; }
|
||||
|
||||
Mod+Home {
|
||||
focus-column-first
|
||||
}
|
||||
Mod+End {
|
||||
focus-column-last
|
||||
}
|
||||
Mod+Ctrl+Home {
|
||||
move-column-to-first
|
||||
}
|
||||
Mod+Ctrl+End {
|
||||
move-column-to-last
|
||||
}
|
||||
Mod+Ctrl+H {
|
||||
focus-monitor-left
|
||||
}
|
||||
Mod+Ctrl+J {
|
||||
focus-monitor-down
|
||||
}
|
||||
Mod+Ctrl+K {
|
||||
focus-monitor-up
|
||||
}
|
||||
Mod+Ctrl+L {
|
||||
focus-monitor-right
|
||||
}
|
||||
Mod+Shift+Ctrl+H {
|
||||
move-column-to-monitor-left
|
||||
}
|
||||
Mod+Shift+Ctrl+J {
|
||||
move-column-to-monitor-down
|
||||
}
|
||||
Mod+Shift+Ctrl+K {
|
||||
move-column-to-monitor-up
|
||||
}
|
||||
Mod+Shift+Ctrl+L {
|
||||
move-column-to-monitor-right
|
||||
}
|
||||
// Alternatively, there are commands to move just a single window:
|
||||
// Mod+Shift+Ctrl+Left { move-window-to-monitor-left; }
|
||||
// ...
|
||||
|
||||
// And you can also move a whole workspace to another monitor:
|
||||
// Mod+Shift+Ctrl+Left { move-workspace-to-monitor-left; }
|
||||
// ...
|
||||
|
||||
Mod+Down { focus-workspace-down; }
|
||||
Mod+Up { focus-workspace-up; }
|
||||
Mod+Ctrl+Down { move-column-to-workspace-down; }
|
||||
Mod+Ctrl+Up { move-column-to-workspace-up; }
|
||||
|
||||
Mod+Alt+J { focus-workspace-down; }
|
||||
Mod+Alt+K { focus-workspace-up; }
|
||||
|
||||
Mod+Down {
|
||||
focus-workspace-down
|
||||
}
|
||||
Mod+Up {
|
||||
focus-workspace-up
|
||||
}
|
||||
Mod+Ctrl+Down {
|
||||
move-column-to-workspace-down
|
||||
}
|
||||
Mod+Ctrl+Up {
|
||||
move-column-to-workspace-up
|
||||
}
|
||||
Mod+Alt+J {
|
||||
focus-workspace-down
|
||||
}
|
||||
Mod+Alt+K {
|
||||
focus-workspace-up
|
||||
}
|
||||
// Alternatively, there are commands to move just a single window:
|
||||
// Mod+Ctrl+Page_Down { move-window-to-workspace-down; }
|
||||
// ...
|
||||
|
||||
Mod+Shift+Down { move-workspace-down; }
|
||||
Mod+Shift+Up { move-workspace-up; }
|
||||
|
||||
Mod+Shift+Down {
|
||||
move-workspace-down
|
||||
}
|
||||
Mod+Shift+Up {
|
||||
move-workspace-up
|
||||
}
|
||||
// You can bind mouse wheel scroll ticks using the following syntax.
|
||||
// These binds will change direction based on the natural-scroll setting.
|
||||
//
|
||||
// To avoid scrolling through workspaces really fast, you can use
|
||||
// the cooldown-ms property. The bind will be rate-limited to this value.
|
||||
// You can set a cooldown on any bind, but it's most useful for the wheel.
|
||||
Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }
|
||||
Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; }
|
||||
Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; }
|
||||
Mod+Ctrl+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; }
|
||||
|
||||
Mod+WheelScrollRight { focus-column-right; }
|
||||
Mod+WheelScrollLeft { focus-column-left; }
|
||||
Mod+Ctrl+WheelScrollRight { move-column-right; }
|
||||
Mod+Ctrl+WheelScrollLeft { move-column-left; }
|
||||
|
||||
Mod+WheelScrollDown cooldown-ms=150 {
|
||||
focus-workspace-down
|
||||
}
|
||||
Mod+WheelScrollUp cooldown-ms=150 {
|
||||
focus-workspace-up
|
||||
}
|
||||
Mod+Ctrl+WheelScrollDown cooldown-ms=150 {
|
||||
move-column-to-workspace-down
|
||||
}
|
||||
Mod+Ctrl+WheelScrollUp cooldown-ms=150 {
|
||||
move-column-to-workspace-up
|
||||
}
|
||||
Mod+WheelScrollRight {
|
||||
focus-column-right
|
||||
}
|
||||
Mod+WheelScrollLeft {
|
||||
focus-column-left
|
||||
}
|
||||
Mod+Ctrl+WheelScrollRight {
|
||||
move-column-right
|
||||
}
|
||||
Mod+Ctrl+WheelScrollLeft {
|
||||
move-column-left
|
||||
}
|
||||
// Usually scrolling up and down with Shift in applications results in
|
||||
// horizontal scrolling; these binds replicate that.
|
||||
Mod+Shift+WheelScrollDown { focus-column-right; }
|
||||
Mod+Shift+WheelScrollUp { focus-column-left; }
|
||||
Mod+Ctrl+Shift+WheelScrollDown { move-column-right; }
|
||||
Mod+Ctrl+Shift+WheelScrollUp { move-column-left; }
|
||||
|
||||
Mod+Shift+WheelScrollDown {
|
||||
focus-column-right
|
||||
}
|
||||
Mod+Shift+WheelScrollUp {
|
||||
focus-column-left
|
||||
}
|
||||
Mod+Ctrl+Shift+WheelScrollDown {
|
||||
move-column-right
|
||||
}
|
||||
Mod+Ctrl+Shift+WheelScrollUp {
|
||||
move-column-left
|
||||
}
|
||||
// Similarly, you can bind touchpad scroll "ticks".
|
||||
// Touchpad scrolling is continuous, so for these binds it is split into
|
||||
// discrete intervals.
|
||||
@@ -578,7 +611,6 @@ binds {
|
||||
// touchpads by default.
|
||||
// Mod+TouchpadScrollDown { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02+"; }
|
||||
// Mod+TouchpadScrollUp { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02-"; }
|
||||
|
||||
// You can refer to workspaces by index. However, keep in mind that
|
||||
// niri is a dynamic workspace system, so these commands are kind of
|
||||
// "best effort". Trying to refer to a workspace index bigger than
|
||||
@@ -587,57 +619,104 @@ binds {
|
||||
//
|
||||
// For example, with 2 workspaces + 1 empty, indices 3, 4, 5 and so on
|
||||
// will all refer to the 3rd workspace.
|
||||
Mod+1 { focus-workspace "browser"; }
|
||||
Mod+2 { focus-workspace "terminal"; }
|
||||
Mod+3 { focus-workspace 3; }
|
||||
Mod+4 { focus-workspace 4; }
|
||||
Mod+5 { focus-workspace 5; }
|
||||
Mod+6 { focus-workspace 6; }
|
||||
Mod+7 { focus-workspace 7; }
|
||||
Mod+8 { focus-workspace "chat"; }
|
||||
Mod+9 { focus-workspace "music"; }
|
||||
Mod+Shift+1 { move-column-to-workspace "browser"; }
|
||||
Mod+Shift+2 { move-column-to-workspace "terminal"; }
|
||||
Mod+Shift+3 { move-column-to-workspace 3; }
|
||||
Mod+Shift+4 { move-column-to-workspace 4; }
|
||||
Mod+Shift+5 { move-column-to-workspace 5; }
|
||||
Mod+Shift+6 { move-column-to-workspace 6; }
|
||||
Mod+Shift+7 { move-column-to-workspace 7; }
|
||||
Mod+Shift+8 { move-column-to-workspace "chat"; }
|
||||
Mod+Shift+9 { move-column-to-workspace "music"; }
|
||||
|
||||
Mod+1 {
|
||||
focus-workspace "browser"
|
||||
}
|
||||
Mod+2 {
|
||||
focus-workspace "terminal"
|
||||
}
|
||||
Mod+3 {
|
||||
focus-workspace 3
|
||||
}
|
||||
Mod+4 {
|
||||
focus-workspace 4
|
||||
}
|
||||
Mod+5 {
|
||||
focus-workspace 5
|
||||
}
|
||||
Mod+6 {
|
||||
focus-workspace 6
|
||||
}
|
||||
Mod+7 {
|
||||
focus-workspace 7
|
||||
}
|
||||
Mod+8 {
|
||||
focus-workspace "chat"
|
||||
}
|
||||
Mod+9 {
|
||||
focus-workspace "music"
|
||||
}
|
||||
Mod+Shift+1 {
|
||||
move-column-to-workspace "browser"
|
||||
}
|
||||
Mod+Shift+2 {
|
||||
move-column-to-workspace "terminal"
|
||||
}
|
||||
Mod+Shift+3 {
|
||||
move-column-to-workspace 3
|
||||
}
|
||||
Mod+Shift+4 {
|
||||
move-column-to-workspace 4
|
||||
}
|
||||
Mod+Shift+5 {
|
||||
move-column-to-workspace 5
|
||||
}
|
||||
Mod+Shift+6 {
|
||||
move-column-to-workspace 6
|
||||
}
|
||||
Mod+Shift+7 {
|
||||
move-column-to-workspace 7
|
||||
}
|
||||
Mod+Shift+8 {
|
||||
move-column-to-workspace "chat"
|
||||
}
|
||||
Mod+Shift+9 {
|
||||
move-column-to-workspace "music"
|
||||
}
|
||||
// Alternatively, there are commands to move just a single window:
|
||||
// Mod+Ctrl+1 { move-window-to-workspace 1; }
|
||||
|
||||
// Switches focus between the current and the previous workspace.
|
||||
// Mod+Tab { focus-workspace-previous; }
|
||||
|
||||
// The following binds move the focused window in and out of a column.
|
||||
// If the window is alone, they will consume it into the nearby column to the side.
|
||||
// If the window is already in a column, they will expel it out.
|
||||
Mod+Comma { consume-or-expel-window-left; }
|
||||
Mod+Period { consume-or-expel-window-right; }
|
||||
|
||||
Mod+Comma {
|
||||
consume-or-expel-window-left
|
||||
}
|
||||
Mod+Period {
|
||||
consume-or-expel-window-right
|
||||
}
|
||||
// Consume one window from the right to the bottom of the focused column.
|
||||
// Mod+Comma { consume-window-into-column; }
|
||||
// Expel the bottom window from the focused column to the right.
|
||||
// Mod+Period { expel-window-from-column; }
|
||||
|
||||
Mod+R { switch-preset-column-width; }
|
||||
Mod+Shift+R { switch-preset-window-height; }
|
||||
Mod+Ctrl+R { reset-window-height; }
|
||||
Mod+M { maximize-column; }
|
||||
Mod+F { fullscreen-window; }
|
||||
|
||||
Mod+R {
|
||||
switch-preset-column-width
|
||||
}
|
||||
Mod+Shift+R {
|
||||
switch-preset-window-height
|
||||
}
|
||||
Mod+Ctrl+R {
|
||||
reset-window-height
|
||||
}
|
||||
Mod+M {
|
||||
maximize-column
|
||||
}
|
||||
Mod+F {
|
||||
fullscreen-window
|
||||
}
|
||||
// Expand the focused column to space not taken up by other fully visible columns.
|
||||
// Makes the column "fill the rest of the space".
|
||||
Mod+Ctrl+F { expand-column-to-available-width; }
|
||||
|
||||
Mod+C { center-column; }
|
||||
|
||||
Mod+Ctrl+F {
|
||||
expand-column-to-available-width
|
||||
}
|
||||
Mod+C {
|
||||
center-column
|
||||
}
|
||||
// Center all fully visible columns on screen.
|
||||
Mod+Ctrl+C { center-visible-columns; }
|
||||
|
||||
Mod+Ctrl+C {
|
||||
center-visible-columns
|
||||
}
|
||||
// Finer width adjustments.
|
||||
// This command can also:
|
||||
// * set width in pixels: "1000"
|
||||
@@ -646,24 +725,33 @@ binds {
|
||||
// * adjust width as a percentage of screen width: "-10%" or "+10%"
|
||||
// Pixel sizes use logical, or scaled, pixels. I.e. on an output with scale 2.0,
|
||||
// set-column-width "100" will make the column occupy 200 physical screen pixels.
|
||||
Mod+Minus { set-column-width "-10%"; }
|
||||
Mod+Equal { set-column-width "+10%"; }
|
||||
|
||||
Mod+Minus {
|
||||
set-column-width "-10%"
|
||||
}
|
||||
Mod+Equal {
|
||||
set-column-width "+10%"
|
||||
}
|
||||
// Finer height adjustments when in column with other windows.
|
||||
Mod+Shift+Minus { set-window-height "-10%"; }
|
||||
Mod+Shift+Equal { set-window-height "+10%"; }
|
||||
|
||||
Mod+Shift+Minus {
|
||||
set-window-height "-10%"
|
||||
}
|
||||
Mod+Shift+Equal {
|
||||
set-window-height "+10%"
|
||||
}
|
||||
// Move the focused window between the floating and the tiling layout.
|
||||
Mod+Ctrl+Space { toggle-window-floating; }
|
||||
Mod+Shift+V { switch-focus-between-floating-and-tiling; }
|
||||
|
||||
Mod+Ctrl+Space {
|
||||
toggle-window-floating
|
||||
}
|
||||
Mod+Shift+V {
|
||||
switch-focus-between-floating-and-tiling
|
||||
}
|
||||
// Mod+Shift+Space { spawn "nsticky" "sticky" "toggle-active"; }
|
||||
|
||||
// Toggle tabbed column display mode.
|
||||
// Windows in this column will appear as vertical tabs,
|
||||
// rather than stacked on top of each other.
|
||||
Mod+W { toggle-column-tabbed-display; }
|
||||
|
||||
Mod+W {
|
||||
toggle-column-tabbed-display
|
||||
}
|
||||
// Actions to switch layouts.
|
||||
// Note: if you uncomment these, make sure you do NOT have
|
||||
// a matching layout switch hotkey configured in xkb options above.
|
||||
@@ -671,11 +759,15 @@ binds {
|
||||
// since it will switch twice upon pressing the hotkey (once by xkb, once by niri).
|
||||
// Mod+Space { switch-layout "next"; }
|
||||
// Mod+Shift+Space { switch-layout "prev"; }
|
||||
|
||||
Mod+Delete { screenshot; }
|
||||
Mod+Shift+Delete { screenshot-screen; }
|
||||
Mod+Alt+Delete { screenshot-window; }
|
||||
|
||||
Mod+Delete {
|
||||
screenshot
|
||||
}
|
||||
Mod+Shift+Delete {
|
||||
screenshot-screen
|
||||
}
|
||||
Mod+Alt+Delete {
|
||||
screenshot-window
|
||||
}
|
||||
// Applications such as remote-desktop clients and software KVM switches may
|
||||
// request that niri stops processing the keyboard shortcuts defined here
|
||||
// so they may, for example, forward the key presses as-is to a remote machine.niri
|
||||
@@ -684,21 +776,27 @@ binds {
|
||||
//
|
||||
// The allow-inhibiting=false property can be applied to other binds as well,
|
||||
// which ensures niri always processes them, even when an inhibitor is active.
|
||||
Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
|
||||
|
||||
Mod+Escape allow-inhibiting=false {
|
||||
toggle-keyboard-shortcuts-inhibit
|
||||
}
|
||||
// The quit action will show a confirmation dialog to avoid accidental exits.
|
||||
Mod+Shift+E { quit; }
|
||||
Ctrl+Alt+Delete { quit; }
|
||||
|
||||
Mod+Shift+E {
|
||||
quit
|
||||
}
|
||||
Ctrl+Alt+Delete {
|
||||
quit
|
||||
}
|
||||
// Powers off the monitors. To turn them back on, do any input like
|
||||
// moving the mouse or pressing any other key.
|
||||
// Mod+Shift+P { power-off-monitors; }
|
||||
|
||||
Mod+Shift+P { spawn-sh "swap-wallpaper -n"; }
|
||||
Mod+B { spawn "{{browser}}"; }
|
||||
Mod+Shift+P {
|
||||
spawn-sh "swap-wallpaper -n"
|
||||
}
|
||||
Mod+B {
|
||||
spawn "{{browser}}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hotkey-overlay {
|
||||
skip-at-startup
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
<string>comment</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<key>foreground</key>
|
||||
<string>#797593</string>
|
||||
</dict>
|
||||
@@ -38,7 +40,7 @@
|
||||
<key>name</key>
|
||||
<string>String</string>
|
||||
<key>scope</key>
|
||||
<string>string</string>
|
||||
<string>string, punctuation.definition.string</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
@@ -53,7 +55,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#907aa9</string>
|
||||
<string>#ea9d34</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -63,8 +65,10 @@
|
||||
<string>constant.language</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#907aa9</string>
|
||||
<string>#ea9d34</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -75,7 +79,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#907aa9</string>
|
||||
<string>#ea9d34</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -86,9 +90,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>italic</string>
|
||||
<key>foreground</key>
|
||||
<string>#d7827e</string>
|
||||
<string>#575279</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -99,7 +103,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#b4637a</string>
|
||||
<string>#286983</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -112,7 +116,7 @@
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#b4637a</string>
|
||||
<string>#56949f</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -123,7 +127,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#56949f</string>
|
||||
</dict>
|
||||
@@ -136,7 +140,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string> bold</string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#286983</string>
|
||||
</dict>
|
||||
@@ -162,9 +166,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>italic</string>
|
||||
<key>foreground</key>
|
||||
<string>#286983</string>
|
||||
<string>#d7827e</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -175,9 +179,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#ea9d34</string>
|
||||
<string>#907aa9</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -188,9 +192,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#b4637a</string>
|
||||
<string>#286983</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -203,7 +207,7 @@
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#286983</string>
|
||||
<string>#907aa9</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -214,9 +218,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#56949f</string>
|
||||
<string>#d7827e</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -227,9 +231,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#56949f</string>
|
||||
<string>#ea9d34</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -240,7 +244,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#56949f</string>
|
||||
</dict>
|
||||
@@ -253,7 +257,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#b4637a</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -284,16 +290,27 @@
|
||||
<string>#575279</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Punctuation, Operators</string>
|
||||
<key>scope</key>
|
||||
<string>punctuation, keyword.operator</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#797593</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>uuid</key>
|
||||
<string>dac29768-bcff-4df3-936c-88c7540d550d</string>
|
||||
<key>colorSpaceName</key>
|
||||
<string>sRGB</string>
|
||||
<key>semanticClass</key>
|
||||
<string>theme.light.rosé_pine-dawn</string>
|
||||
<string>theme.light.rose-pine-dawn</string>
|
||||
<key>author</key>
|
||||
<string>oplik0</string>
|
||||
<string>arrrgi</string>
|
||||
<key>comment</key>
|
||||
<string>soho vibes - modified from the sublime text theme by ThatOneCalculator</string>
|
||||
<string>All natural pine, faux fur and a bit of soho vibes for the classy minimalist</string>
|
||||
<key>uuid</key>
|
||||
<string>BB4B4616-E742-41D5-BB5B-63D45FA614F</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -20,7 +20,7 @@
|
||||
<key>lineHighlight</key>
|
||||
<string>#2a283e</string>
|
||||
<key>selection</key>
|
||||
<string>#6e6a86</string>
|
||||
<string>#44415a</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -30,6 +30,8 @@
|
||||
<string>comment</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<key>foreground</key>
|
||||
<string>#908caa</string>
|
||||
</dict>
|
||||
@@ -38,7 +40,7 @@
|
||||
<key>name</key>
|
||||
<string>String</string>
|
||||
<key>scope</key>
|
||||
<string>string</string>
|
||||
<string>string, punctuation.definition.string</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
@@ -53,7 +55,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#c4a7e7</string>
|
||||
<string>#f6c177</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -63,8 +65,10 @@
|
||||
<string>constant.language</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#c4a7e7</string>
|
||||
<string>#f6c177</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -75,7 +79,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#c4a7e7</string>
|
||||
<string>#f6c177</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -86,9 +90,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>italic</string>
|
||||
<key>foreground</key>
|
||||
<string>#ea9a97</string>
|
||||
<string>#e0def4</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -99,7 +103,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#eb6f92</string>
|
||||
<string>#3e8fb0</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -112,7 +116,7 @@
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#eb6f92</string>
|
||||
<string>#9ccfd8</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -123,7 +127,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#9ccfd8</string>
|
||||
</dict>
|
||||
@@ -136,7 +140,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string> bold</string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#3e8fb0</string>
|
||||
</dict>
|
||||
@@ -162,9 +166,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>italic</string>
|
||||
<key>foreground</key>
|
||||
<string>#3e8fb0</string>
|
||||
<string>#ea9a97</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -175,9 +179,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#f6c177</string>
|
||||
<string>#c4a7e7</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -188,9 +192,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#eb6f92</string>
|
||||
<string>#3e8fb0</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -203,7 +207,7 @@
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#3e8fb0</string>
|
||||
<string>#c4a7e7</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -214,9 +218,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#9ccfd8</string>
|
||||
<string>#ea9a97</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -227,9 +231,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#9ccfd8</string>
|
||||
<string>#f6c177</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -240,7 +244,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#9ccfd8</string>
|
||||
</dict>
|
||||
@@ -253,7 +257,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#eb6f92</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -284,16 +290,27 @@
|
||||
<string>#e0def4</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Punctuation, Operators</string>
|
||||
<key>scope</key>
|
||||
<string>punctuation, keyword.operator</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#908caa</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>uuid</key>
|
||||
<string>a65f621e-b84b-48fb-afec-e5c085e8debf</string>
|
||||
<key>colorSpaceName</key>
|
||||
<string>sRGB</string>
|
||||
<key>semanticClass</key>
|
||||
<string>theme.dark.rosé_pine-moon</string>
|
||||
<string>theme.dark.rose-pine-moon</string>
|
||||
<key>author</key>
|
||||
<string>oplik0</string>
|
||||
<string>arrrgi</string>
|
||||
<key>comment</key>
|
||||
<string>soho vibes - modified from the sublime text theme by ThatOneCalculator</string>
|
||||
<string>All natural pine, faux fur and a bit of soho vibes for the classy minimalist</string>
|
||||
<key>uuid</key>
|
||||
<string>CC28B8FB-96BA-43EB-B71F-5AA3D3EBB0BB</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -30,6 +30,8 @@
|
||||
<string>comment</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<key>foreground</key>
|
||||
<string>#908caa</string>
|
||||
</dict>
|
||||
@@ -38,7 +40,7 @@
|
||||
<key>name</key>
|
||||
<string>String</string>
|
||||
<key>scope</key>
|
||||
<string>string</string>
|
||||
<string>string, punctuation.definition.string</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
@@ -53,7 +55,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#c4a7e7</string>
|
||||
<string>#f6c177</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -63,8 +65,10 @@
|
||||
<string>constant.language</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#c4a7e7</string>
|
||||
<string>#f6c177</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -75,7 +79,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#c4a7e7</string>
|
||||
<string>#f6c177</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -86,9 +90,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>italic</string>
|
||||
<key>foreground</key>
|
||||
<string>#ebbcba</string>
|
||||
<string>#e0def4</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -99,7 +103,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#eb6f92</string>
|
||||
<string>#31748f</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -112,7 +116,7 @@
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#eb6f92</string>
|
||||
<string>#9ccfd8</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -123,7 +127,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#9ccfd8</string>
|
||||
</dict>
|
||||
@@ -136,7 +140,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string> bold</string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#31748f</string>
|
||||
</dict>
|
||||
@@ -162,9 +166,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>italic</string>
|
||||
<key>foreground</key>
|
||||
<string>#31748f</string>
|
||||
<string>#ebbcba</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -175,9 +179,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#f6c177</string>
|
||||
<string>#c4a7e7</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -188,9 +192,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#eb6f92</string>
|
||||
<string>#31748f</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -203,7 +207,7 @@
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<key>foreground</key>
|
||||
<string>#31748f</string>
|
||||
<string>#c4a7e7</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -214,9 +218,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#9ccfd8</string>
|
||||
<string>#ebbcba</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -227,9 +231,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#9ccfd8</string>
|
||||
<string>#f6c177</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -240,7 +244,7 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string>italic</string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#9ccfd8</string>
|
||||
</dict>
|
||||
@@ -253,7 +257,9 @@
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>fontStyle</key>
|
||||
<string></string>
|
||||
<string>bold</string>
|
||||
<key>foreground</key>
|
||||
<string>#eb6f92</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
@@ -284,16 +290,27 @@
|
||||
<string>#e0def4</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Punctuation, Operators</string>
|
||||
<key>scope</key>
|
||||
<string>punctuation, keyword.operator</string>
|
||||
<key>settings</key>
|
||||
<dict>
|
||||
<key>foreground</key>
|
||||
<string>#908caa</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>uuid</key>
|
||||
<string>c3af112c-80e3-45fe-8890-43ca225fda21</string>
|
||||
<key>colorSpaceName</key>
|
||||
<string>sRGB</string>
|
||||
<key>semanticClass</key>
|
||||
<string>theme.dark.rosé_pine</string>
|
||||
<string>theme.dark.rose-pine</string>
|
||||
<key>author</key>
|
||||
<string>oplik0</string>
|
||||
<string>arrrgi</string>
|
||||
<key>comment</key>
|
||||
<string>soho vibes - modified from the sublime text theme by ThatOneCalculator</string>
|
||||
<string>All natural pine, faux fur and a bit of soho vibes for the classy minimalist</string>
|
||||
<key>uuid</key>
|
||||
<string>14991673-80EB-41A2-BEFF-03216A233730</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,236 +0,0 @@
|
||||
{
|
||||
"layer": "top", // Waybar at top layer
|
||||
// "position": "bottom", // Waybar position (top|bottom|left|right)
|
||||
"height": 30, // Waybar height (to be removed for auto height)
|
||||
// "width": 1280, // Waybar width
|
||||
"spacing": 4, // Gaps between modules (4px)
|
||||
// Choose the order of the modules
|
||||
"modules-left": ["wlr/workspaces", "custom/media"],
|
||||
"modules-center": ["hyprland/window"],
|
||||
"modules-right": ["network", "custom/pacman", "backlight", "temperature", "pulseaudio", "battery", "custom/dunst", "custom/weather", "tray", "clock"],
|
||||
// Modules configuration
|
||||
"wlr/workspaces": {
|
||||
"format": "{icon}",
|
||||
"on-click": "activate",
|
||||
"format-icons": {
|
||||
// "1": "1",
|
||||
// "2": "2",
|
||||
// "3": "3",
|
||||
// "4": "4",
|
||||
// "5": "5",
|
||||
// "6": "6",
|
||||
// "7": "7",
|
||||
// "8": "8",
|
||||
// "9": "9",
|
||||
// "10": "10",
|
||||
// "urgent": "",
|
||||
// "active": "",
|
||||
// "default": ""
|
||||
},
|
||||
"sort-by-number": true
|
||||
},
|
||||
|
||||
"hyprland/window": {
|
||||
"format": "{}",
|
||||
"separate-outputs": true
|
||||
},
|
||||
|
||||
"hyprland/language": {
|
||||
"format": "{}",
|
||||
"format-us": "us",
|
||||
"format-lv": "lv",
|
||||
"keyboard-name": "AT Translated Set 2 keyboard"
|
||||
},
|
||||
|
||||
"keyboard-state": {
|
||||
"numlock": true,
|
||||
"capslock": true,
|
||||
"format": "{name} {icon}",
|
||||
"format-icons": {
|
||||
"locked": "",
|
||||
"unlocked": ""
|
||||
}
|
||||
},
|
||||
|
||||
"mpd": {
|
||||
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ",
|
||||
"format-disconnected": "Disconnected ",
|
||||
"format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ",
|
||||
"unknown-tag": "N/A",
|
||||
"interval": 2,
|
||||
"consume-icons": {
|
||||
"on": " "
|
||||
},
|
||||
"random-icons": {
|
||||
"off": "<span color=\"#f53c3c\"></span> ",
|
||||
"on": " "
|
||||
},
|
||||
"repeat-icons": {
|
||||
"on": " "
|
||||
},
|
||||
"single-icons": {
|
||||
"on": "1 "
|
||||
},
|
||||
"state-icons": {
|
||||
"paused": "",
|
||||
"playing": ""
|
||||
},
|
||||
"tooltip-format": "MPD (connected)",
|
||||
"tooltip-format-disconnected": "MPD (disconnected)"
|
||||
},
|
||||
|
||||
"idle_inhibitor": {
|
||||
"format": "{icon}",
|
||||
"format-icons": {
|
||||
"activated": "",
|
||||
"deactivated": ""
|
||||
}
|
||||
},
|
||||
|
||||
"tray": {
|
||||
"icon-size": 21,
|
||||
"spacing": 10
|
||||
},
|
||||
|
||||
"clock": {
|
||||
"format": "{:%d.%m.%Y %H:%M:%S}",
|
||||
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
|
||||
"interval": 1
|
||||
},
|
||||
|
||||
"cpu": {
|
||||
"format": "{usage}% ",
|
||||
"tooltip": false
|
||||
},
|
||||
|
||||
"memory": {
|
||||
"interval": 30,
|
||||
"format": "{used:0.1f}G/{total:0.1f}G"
|
||||
},
|
||||
|
||||
"temperature": {
|
||||
// "thermal-zone": 2,
|
||||
// "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
|
||||
"critical-threshold": 80,
|
||||
"format-critical": "{temperatureC}°C ",
|
||||
"format": "{temperatureC}°C ",
|
||||
"format-icons": ["", "", ""]
|
||||
},
|
||||
|
||||
"backlight": {
|
||||
// "device": "acpi_video1",
|
||||
"format": "{percent}% {icon}",
|
||||
"format-icons": ["", "", "", "", "", "", "", "", ""],
|
||||
"on-click": "brightnessctl -q set 60%",
|
||||
"on-scroll-down": "brightnessctl -q set +1%",
|
||||
"on-scroll-up": "brightnessctl -q set 1%-",
|
||||
|
||||
},
|
||||
|
||||
"battery": {
|
||||
"states": {
|
||||
"good": 95,
|
||||
"warning": 30,
|
||||
"critical": 10
|
||||
},
|
||||
"format": "{capacity}% {icon}",
|
||||
"format-charging": "{capacity}% ",
|
||||
"format-plugged": "{capacity}% ",
|
||||
"format-alt": "{time} {icon}",
|
||||
// "format-good": "", // An empty format will hide the module
|
||||
// "format-full": "",
|
||||
"format-icons": ["", "", "", "", ""]
|
||||
},
|
||||
|
||||
"network": {
|
||||
"format-wifi": "{essid} ({signalStrength}%) ",
|
||||
"format-ethernet": "{ipaddr}/{cidr} ",
|
||||
"tooltip-format": "{ifname} via {gwaddr} ",
|
||||
"format-linked": "{ifname} (No IP) ",
|
||||
"format-disconnected": "Disconnected ⚠",
|
||||
"format-alt": "{ifname}: {ipaddr}/{cidr}"
|
||||
},
|
||||
|
||||
"wireplumber": {
|
||||
"format": "{volume}% {icon}",
|
||||
"format-muted": "",
|
||||
"on-click": "pulsemixer --toggle-mute",
|
||||
"format-icons": ["", "", ""]
|
||||
},
|
||||
|
||||
"pulseaudio": {
|
||||
"scroll-step": 1, // %, can be a float
|
||||
"format": "{volume}% {icon} {format_source}",
|
||||
"format-bluetooth": "{volume}% {icon} {format_source}",
|
||||
"format-bluetooth-muted": "{volume}% {icon} {format_source}",
|
||||
"format-muted": "{volume}% {icon} {format_source}",
|
||||
"format-source": "{volume}% ",
|
||||
"format-source-muted": "",
|
||||
"format-icons": {
|
||||
"headphone": "",
|
||||
"hands-free": "",
|
||||
"headset": "",
|
||||
"phone": "",
|
||||
"portable": "",
|
||||
"car": "",
|
||||
"default": ["", "", ""]
|
||||
},
|
||||
"on-click": "pulsemixer --toggle-mute"
|
||||
},
|
||||
|
||||
"custom/media": {
|
||||
"format": "{icon} {}",
|
||||
"return-type": "json",
|
||||
// "max-length": 40,
|
||||
"on-click": "playerctl play-pause",
|
||||
"on-click-right": "playerctl stop",
|
||||
"on-scroll-up": "playerctl next",
|
||||
"on-scroll-down": "playerctl previous",
|
||||
"format-icons": {
|
||||
"spotify": "",
|
||||
"default": "🎜"
|
||||
},
|
||||
"escape": true,
|
||||
"smooth-scrolling-threshold": 10, // This value was tested using a trackpad, it should be lowered if using a mouse.
|
||||
"exec": "$HOME/.config/waybar/scripts/mediaplayer.py 2> /dev/null" // Script in resources folder
|
||||
// "exec": "$HOME/.config/waybar/scripts/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name */
|
||||
},
|
||||
|
||||
|
||||
"custom/weather": {
|
||||
"exec": "curl 'https://wttr.in/?format=1'",
|
||||
"interval": 600,
|
||||
"on-click": "curl 'https://wttr.in/?format=1'",
|
||||
},
|
||||
|
||||
"custom/vpn": {
|
||||
"format": "VPN ",
|
||||
"exec": "echo '{\"class\": \"connected\"}'",
|
||||
"exec-if": "test -d /proc/sys/net/ipv4/conf/tun0",
|
||||
"return-type": "json",
|
||||
"interval": 5
|
||||
},
|
||||
|
||||
"custom/pipewire": {
|
||||
"tooltip": true,
|
||||
"max-length": 10,
|
||||
"exec": "$HOME/.config/waybar/scripts/pipewire.sh",
|
||||
"on-click": "pulsemixer --toggle-mute",
|
||||
"on-click-right": "qpwgraph"
|
||||
},
|
||||
|
||||
"custom/github": {
|
||||
"format": "{} ",
|
||||
"return-type": "json",
|
||||
"interval": 60,
|
||||
"exec": "$HOME/.config/waybar/scripts/github.sh",
|
||||
"on-click": "xdg-open https://github.com/notifications"
|
||||
},
|
||||
|
||||
"custom/dunst": {
|
||||
"exec": "~/.config/waybar/scripts/dunst.sh",
|
||||
"on-click": "dunstctl set-paused toggle",
|
||||
"restart-interval": 1,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
"spacing": 0, // Gaps between modules (4px)
|
||||
// Choose the order of the modules
|
||||
"modules-left": [
|
||||
// "wlr/taskbar"
|
||||
"cffi/niri-taskbar"
|
||||
"wlr/taskbar"
|
||||
// "cffi/niri-taskbar"
|
||||
],
|
||||
"modules-center": [],
|
||||
"modules-right": [
|
||||
@@ -51,7 +51,7 @@
|
||||
},
|
||||
"wlr/taskbar": {
|
||||
// "all-outputs": true,
|
||||
"format": "{title} | {app_id}",
|
||||
"format": "{app_id}",
|
||||
// "format": "{icon}",
|
||||
"tooltip-format": "{title} | {app_id}",
|
||||
"on-click": "activate",
|
||||
|
||||
@@ -10,8 +10,8 @@ hash = "e02a788e5b8ae0fb47fd0193dda589cc"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "hankertrix/augment-command"
|
||||
rev = "7c12bdf"
|
||||
hash = "f7e6d377e4efee567ec6d1c2355f2ca3"
|
||||
rev = "681158d"
|
||||
hash = "63ed5325016895306781d13690b246bf"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "kirasok/torrent-preview"
|
||||
@@ -20,8 +20,8 @@ hash = "d849ad596b8a77902e62a42403aeba40"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "ndtoan96/ouch"
|
||||
rev = "594b8a2"
|
||||
hash = "c9e628fc0312d198db22ae2fa74883b"
|
||||
rev = "406ce6c"
|
||||
hash = "f5afc904d5106ee368c8aa2ded43bd74"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "pirafrank/what-size"
|
||||
@@ -30,38 +30,38 @@ hash = "57056b9728006881d580ccabe8154a9c"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:git"
|
||||
rev = "e07bf41"
|
||||
hash = "270915fa8282a19908449530ff66f7e2"
|
||||
rev = "1962818"
|
||||
hash = "26db011a778f261d730d4f5f8bf24b3f"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:chmod"
|
||||
rev = "e07bf41"
|
||||
hash = "8da0b15a97b5dfd13941d1ecc617ac7c"
|
||||
rev = "1962818"
|
||||
hash = "f0c8c378184d5f8abd1b095a443d336d"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:full-border"
|
||||
rev = "e07bf41"
|
||||
hash = "3996fc74044bc44144b323686f887e1"
|
||||
rev = "1962818"
|
||||
hash = "6fa6a05a81c98dd000fbca3cca6e9682"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:mount"
|
||||
rev = "e07bf41"
|
||||
hash = "563e4068979d1466d3dfc2e70a296947"
|
||||
rev = "1962818"
|
||||
hash = "91937a4a9b779eabc6983e258befdfe9"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:smart-filter"
|
||||
rev = "e07bf41"
|
||||
hash = "407d19bc4fb46eff5fa8ff8337644847"
|
||||
rev = "1962818"
|
||||
hash = "c887903a63a2ff520081b6d90a4b3392"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:diff"
|
||||
rev = "e07bf41"
|
||||
hash = "2f08b8249b57737e7257298a3b2a2edc"
|
||||
rev = "1962818"
|
||||
hash = "8b1af6b5a69797ee951f2a80ce570818"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "AnirudhG07/rich-preview"
|
||||
rev = "573b275"
|
||||
hash = "c3e2871c9ef244fd181f203791f9b0d2"
|
||||
rev = "7d616ad"
|
||||
hash = "d64feec9761392cbc250d199ab4b8a3a"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "macydnah/office"
|
||||
@@ -70,8 +70,8 @@ hash = "5805affd3ae8adcb3c72b6997d21c0a6"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "boydaihungst/mediainfo"
|
||||
rev = "dc61636"
|
||||
hash = "2fa34959353b6f1a1c33659f50e098fd"
|
||||
rev = "6fbed8d"
|
||||
hash = "57ae6b43c477e117802f176683b78e74"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "iynaix/time-travel"
|
||||
|
||||
@@ -452,10 +452,6 @@ local get_mime_type_without_prefix_template_pattern =
|
||||
---@type string
|
||||
local shell_variable_pattern = "%%[hs]%d?"
|
||||
|
||||
-- The pattern to match the bat command
|
||||
---@type string
|
||||
local bat_command_pattern = "%f[%a]bat%f[%A]"
|
||||
|
||||
-- Utility functions
|
||||
|
||||
-- Function to merge tables.
|
||||
@@ -1415,8 +1411,8 @@ local get_path_of_hovered_item = ya.sync(function(_, quote)
|
||||
-- If there is no hovered item, exit the function
|
||||
if not hovered_item then return end
|
||||
|
||||
-- Convert the url of the hovered item to a string
|
||||
local hovered_item_path = tostring(cx.active.current.hovered.url)
|
||||
-- Convert the path of the hovered item to a string
|
||||
local hovered_item_path = tostring(cx.active.current.hovered.url.path)
|
||||
|
||||
-- If the quote flag is passed,
|
||||
-- then quote the path of the hovered item
|
||||
@@ -1470,8 +1466,8 @@ local get_paths_of_selected_items = ya.sync(function(_, quote)
|
||||
for _, item in pairs(selected_items) do
|
||||
--
|
||||
|
||||
-- Convert the url of the item to a string
|
||||
local item_path = tostring(item)
|
||||
-- Convert the path of the item to a string
|
||||
local item_path = tostring(item.path)
|
||||
|
||||
-- If the quote flag is passed,
|
||||
-- then quote the path of the item
|
||||
@@ -3481,11 +3477,35 @@ local function handle_create(args, config)
|
||||
return execute_create(full_url, is_directory, args, config)
|
||||
end
|
||||
|
||||
-- Function to match a binary name against a search string
|
||||
---@param binary_name string The name of the binary
|
||||
---@param search_string string The string to search for the binary name
|
||||
---@return string binary_pattern The pattern for the binary
|
||||
---@return string? binary_path The path to the binary
|
||||
local function match_binary_name(binary_name, search_string)
|
||||
--
|
||||
|
||||
-- The binary pattern
|
||||
local binary_pattern = "%f[%w_%-%.].*" .. binary_name .. "%f[%W%s]"
|
||||
|
||||
-- Get the binary path
|
||||
local binary_path = search_string:match(binary_pattern)
|
||||
|
||||
-- Escape the binary path if it's not nil
|
||||
if binary_path ~= nil then
|
||||
binary_path = escape_replacement_string(binary_path)
|
||||
end
|
||||
|
||||
-- Return the binary pattern and the path
|
||||
return binary_pattern, binary_path
|
||||
end
|
||||
|
||||
-- Function to remove the F flag from the less command
|
||||
---@param command string The shell command containing the less command
|
||||
---@param less_binary_pattern string The pattern to match the less binary
|
||||
---@return string command The command with the F flag removed
|
||||
---@return boolean f_flag_found Whether the F flag was found
|
||||
local function remove_f_flag_from_less_command(command)
|
||||
local function remove_f_flag_from_less_command(command, less_binary_pattern)
|
||||
--
|
||||
|
||||
-- Initialise the variable to store if the F flag is found
|
||||
@@ -3494,9 +3514,13 @@ local function remove_f_flag_from_less_command(command)
|
||||
-- Initialise the variable to store the replacement count
|
||||
local replacement_count = 0
|
||||
|
||||
-- Initialised the modified command
|
||||
local modified_command = command
|
||||
|
||||
-- Remove the F flag when it is passed at the start
|
||||
-- of the flags given to the less command
|
||||
command, replacement_count = command:gsub("(%f[%a]less%f[%A].*)%-F", "%1")
|
||||
modified_command, replacement_count =
|
||||
modified_command:gsub("(" .. less_binary_pattern .. ".*)%-F", "%1")
|
||||
|
||||
-- If the replacement count is not 0,
|
||||
-- set the f_flag_found variable to true
|
||||
@@ -3504,27 +3528,36 @@ local function remove_f_flag_from_less_command(command)
|
||||
|
||||
-- Remove the F flag when it is passed in the middle
|
||||
-- or end of the flags given to the less command command
|
||||
command, replacement_count =
|
||||
command:gsub("(%f[%a]less%f[%A].*%-)(%a*)F(%a*)", "%1%2%3")
|
||||
modified_command, replacement_count = modified_command:gsub(
|
||||
"(" .. less_binary_pattern .. ".*%-)(%a*)F(%a*)",
|
||||
"%1%2%3"
|
||||
)
|
||||
|
||||
-- If the replacement count is not 0,
|
||||
-- set the f_flag_found variable to true
|
||||
if replacement_count ~= 0 then f_flag_found = true end
|
||||
|
||||
-- Return the command and whether or not the F flag was found
|
||||
return command, f_flag_found
|
||||
return modified_command, f_flag_found
|
||||
end
|
||||
|
||||
-- Function to fix a command containing less.
|
||||
-- All this function does is remove
|
||||
-- the F flag from a command containing less.
|
||||
---@param command string The shell command containing the less command
|
||||
---@param less_binary_pattern string The pattern to match the less binary
|
||||
---@param less_binary_path string The path to the less binary
|
||||
---@return string command The fixed shell command
|
||||
local function fix_shell_command_containing_less(command)
|
||||
local function fix_shell_command_containing_less(
|
||||
command,
|
||||
less_binary_pattern,
|
||||
less_binary_path
|
||||
)
|
||||
--
|
||||
|
||||
-- Remove the F flag from the given command
|
||||
local fixed_command = remove_f_flag_from_less_command(command)
|
||||
local fixed_command =
|
||||
remove_f_flag_from_less_command(command, less_binary_pattern)
|
||||
|
||||
-- Get the LESS environment variable
|
||||
local less_environment_variable = os.getenv("LESS")
|
||||
@@ -3536,7 +3569,10 @@ local function fix_shell_command_containing_less(command)
|
||||
-- Otherwise, remove the F flag from the LESS environment variable
|
||||
-- and check if the F flag was found
|
||||
local less_command_with_modified_env_variables, f_flag_found =
|
||||
remove_f_flag_from_less_command("less " .. less_environment_variable)
|
||||
remove_f_flag_from_less_command(
|
||||
string.format("%s %s", less_binary_path, less_environment_variable),
|
||||
less_binary_pattern
|
||||
)
|
||||
|
||||
-- If the F flag isn't found,
|
||||
-- then return the given command with the F flag removed
|
||||
@@ -3544,7 +3580,7 @@ local function fix_shell_command_containing_less(command)
|
||||
|
||||
-- Add the less environment variable flags to the less command
|
||||
fixed_command = fixed_command:gsub(
|
||||
"%f[%a]less%f[%A]",
|
||||
less_binary_pattern,
|
||||
escape_replacement_string(less_command_with_modified_env_variables)
|
||||
)
|
||||
|
||||
@@ -3557,13 +3593,22 @@ end
|
||||
|
||||
-- Function to fix the bat default pager command
|
||||
---@param command string The command containing the bat default pager command
|
||||
---@param bat_binary_pattern string The pattern to match the bat binary
|
||||
---@param bat_binary_path string The path to the bat binary
|
||||
---@return string command The fixed bat command
|
||||
local function fix_shell_command_containing_bat(command)
|
||||
local function fix_shell_command_containing_bat(
|
||||
command,
|
||||
bat_binary_pattern,
|
||||
bat_binary_path
|
||||
)
|
||||
--
|
||||
|
||||
-- The pattern to match the pager argument for the bat command
|
||||
local bat_pager_pattern = "(%-%-pager)%s+(%S+)"
|
||||
|
||||
-- The default bat pager command without the -F flag
|
||||
local bat_default_pager_command_without_f_flag = "less -RX"
|
||||
|
||||
-- Get the pager argument for the bat command
|
||||
local _, pager_argument = command:match(bat_pager_pattern)
|
||||
|
||||
@@ -3606,16 +3651,13 @@ local function fix_shell_command_containing_bat(command)
|
||||
return modified_command
|
||||
end
|
||||
|
||||
-- If there is no pager argument,
|
||||
-- initialise the default pager command for bat without the F flag
|
||||
local bat_default_pager_command_without_f_flag = "less -RX"
|
||||
|
||||
-- Replace the bat command with the command to use the
|
||||
-- bat default pager command without the F flag
|
||||
-- Replace the bat command with the command to use
|
||||
-- the bat default pager command without the F flag
|
||||
local modified_command = command:gsub(
|
||||
bat_command_pattern,
|
||||
bat_binary_pattern,
|
||||
string.format(
|
||||
"bat --pager '%s'",
|
||||
"%s --pager '%s'",
|
||||
bat_binary_path,
|
||||
bat_default_pager_command_without_f_flag
|
||||
),
|
||||
1
|
||||
@@ -3631,24 +3673,43 @@ end
|
||||
local function fix_shell_command(command)
|
||||
--
|
||||
|
||||
-- If the given command contains the bat command
|
||||
if command:find(bat_command_pattern) ~= nil then
|
||||
-- Get the bat binary pattern and path from the command
|
||||
local bat_binary_pattern, bat_binary_path =
|
||||
match_binary_name("bat", command)
|
||||
|
||||
-- Initialise the fixed command
|
||||
local fixed_command = command
|
||||
|
||||
-- If the bat binary is in the command
|
||||
if bat_binary_path ~= nil then
|
||||
--
|
||||
|
||||
-- Calls the command to fix the bat command
|
||||
command = fix_shell_command_containing_bat(command)
|
||||
fixed_command = fix_shell_command_containing_bat(
|
||||
command,
|
||||
bat_binary_pattern,
|
||||
bat_binary_path
|
||||
)
|
||||
end
|
||||
|
||||
-- If the given command includes the less command
|
||||
if command:find("%f[%a]less%f[%A]") ~= nil then
|
||||
-- Get the less binary pattern and path from the fixed command
|
||||
local less_binary_pattern, less_binary_path =
|
||||
match_binary_name("less", fixed_command)
|
||||
|
||||
-- If the less binary is in the command
|
||||
if less_binary_path ~= nil then
|
||||
--
|
||||
|
||||
-- Fix the command containing less
|
||||
command = fix_shell_command_containing_less(command)
|
||||
fixed_command = fix_shell_command_containing_less(
|
||||
fixed_command,
|
||||
less_binary_pattern,
|
||||
less_binary_path
|
||||
)
|
||||
end
|
||||
|
||||
-- Return the modified command
|
||||
return command
|
||||
-- Return the fixed command
|
||||
return fixed_command
|
||||
end
|
||||
|
||||
-- Function to handle a shell command
|
||||
|
||||
@@ -21,7 +21,7 @@ run = "plugin chmod"
|
||||
desc = "Chmod on selected files"
|
||||
```
|
||||
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other commands/plugins.
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other actions/plugins.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- @since 25.12.29
|
||||
--- @since 26.1.22
|
||||
|
||||
local selected_or_hovered = ya.sync(function()
|
||||
local tab, paths = cx.active, {}
|
||||
@@ -37,7 +37,7 @@ return {
|
||||
return
|
||||
end
|
||||
|
||||
local output, err = Command("chmod"):arg(value):arg(urls):stderr(Command.PIPED):output()
|
||||
local output, err = Command("chmod"):arg(value):arg(urls):output()
|
||||
if not output then
|
||||
fail("Failed to run chmod: %s", err)
|
||||
elseif not output.status.success then
|
||||
|
||||
@@ -21,7 +21,7 @@ run = "plugin diff"
|
||||
desc = "Diff the selected with the hovered file"
|
||||
```
|
||||
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other commands/plugins.
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other actions/plugins.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ local function setup(_, opts)
|
||||
local c = self._chunks
|
||||
self._chunks = {
|
||||
c[1]:pad(ui.Pad.y(1)),
|
||||
c[2]:pad(ui.Pad(1, c[3].w > 0 and 0 or 1, 1, c[1].w > 0 and 0 or 1)),
|
||||
-- TODO: remove this compatibility hack
|
||||
fs.unique and c[2]:pad(ui.Pad.y(1)) or c[2]:pad(ui.Pad(1, c[3].w > 0 and 0 or 1, 1, c[1].w > 0 and 0 or 1)),
|
||||
c[3]:pad(ui.Pad.y(1)),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- @since 25.12.29
|
||||
--- @since 26.1.22
|
||||
|
||||
local WINDOWS = ya.target_family() == "windows"
|
||||
|
||||
@@ -224,7 +224,6 @@ local function fetch(_, job)
|
||||
:cwd(tostring(cwd))
|
||||
:arg({ "--no-optional-locks", "-c", "core.quotePath=", "status", "--porcelain", "-unormal", "--no-renames", "--ignored=matching" })
|
||||
:arg(paths)
|
||||
:stdout(Command.PIPED)
|
||||
:output()
|
||||
if not output then
|
||||
return true, Err("Cannot spawn `git` command, error: %s", err)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
Copyright (c) 2024 Lauri Niskanen
|
||||
Copyright (c) 2025 Huy Hoang
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -13,7 +13,8 @@ This is a Yazi plugin for previewing media files. The preview shows thumbnail
|
||||
using `ffmpeg` if available and media metadata using `mediainfo`.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Minimum version: yazi v25.5.31.
|
||||
> Minimum version: yazi v26.1.22.
|
||||
> Check it via command `yazi --debug`
|
||||
|
||||
## Preview
|
||||
|
||||
@@ -70,29 +71,48 @@ Config folder for each OS: https://yazi-rs.github.io/docs/configuration/overview
|
||||
|
||||
Create `.../yazi/yazi.toml` and add:
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> For yazi (>=v25.12.29) replace `name` with `url`
|
||||
|
||||
```toml
|
||||
[plugin]
|
||||
prepend_preloaders = [
|
||||
# Replace magick, image, video with mediainfo
|
||||
{ mime = "{audio,video,image}/*", run = "mediainfo" },
|
||||
{ mime = "application/subrip", run = "mediainfo" },
|
||||
# Adobe Illustrator, Adobe Photoshop is image/adobe.photoshop, already handled above
|
||||
|
||||
# Adobe Photoshop is image/adobe.photoshop, already handled above
|
||||
# Adobe Illustrator
|
||||
{ mime = "application/postscript", run = "mediainfo" },
|
||||
{ mime = "application/illustrator", run = "mediainfo" },
|
||||
{ mime = "application/dvb.ait", run = "mediainfo" },
|
||||
{ mime = "application/vnd.adobe.illustrator", run = "mediainfo" },
|
||||
{ mime = "image/x-eps", run = "mediainfo" },
|
||||
{ mime = "application/eps", run = "mediainfo" },
|
||||
|
||||
# Sometimes AI file is recognized as "application/pdf". Lmao.
|
||||
# In this case use file extension instead:
|
||||
{ url = "*.{ai,eps,ait}", run = "mediainfo" },
|
||||
]
|
||||
prepend_previewers = [
|
||||
# Replace magick, image, video with mediainfo
|
||||
{ mime = "{audio,video,image}/*", run = "mediainfo"},
|
||||
{ mime = "application/subrip", run = "mediainfo" },
|
||||
# Adobe Illustrator, Adobe Photoshop is image/adobe.photoshop, already handled above
|
||||
|
||||
# Adobe Photoshop is image/adobe.photoshop, already handled above
|
||||
# Adobe Illustrator
|
||||
{ mime = "application/postscript", run = "mediainfo" },
|
||||
{ mime = "application/illustrator", run = "mediainfo" },
|
||||
{ mime = "application/dvb.ait", run = "mediainfo" },
|
||||
{ mime = "application/vnd.adobe.illustrator", run = "mediainfo" },
|
||||
{ mime = "image/x-eps", run = "mediainfo" },
|
||||
{ mime = "application/eps", run = "mediainfo" },
|
||||
|
||||
# Sometimes AI file is recognized as "application/pdf". Lmao.
|
||||
# In this case use file extension instead:
|
||||
{ url = "*.{ai,eps,ait}", run = "mediainfo" },
|
||||
]
|
||||
# There are more extensions which are supported by mediainfo.
|
||||
# There are more extensions, mime types which are supported by mediainfo.
|
||||
# Just add file's MIME type to `previewers`, `preloaders` above.
|
||||
# https://mediaarea.net/en/MediaInfo/Support/Formats
|
||||
# If it's not working, file an issue at https://github.com/boydaihungst/mediainfo.yazi/issues
|
||||
|
||||
# For a large file like Adobe Illustrator, Adobe Photoshop, etc
|
||||
# you may need to increase the memory limit if no image is rendered.
|
||||
@@ -104,7 +124,7 @@ Create `.../yazi/yazi.toml` and add:
|
||||
|
||||
## Custom theme
|
||||
|
||||
Using the same style with spotter. [Read more](https://github.com/sxyazi/yazi/pull/2391)
|
||||
Using the same style with spotter windows. [Read more](https://github.com/sxyazi/yazi/pull/2391)
|
||||
|
||||
Edit or add `yazi/theme.toml`:
|
||||
|
||||
|
||||
283
config/yazi/plugins/mediainfo.yazi/adobe.lua
Normal file
283
config/yazi/plugins/mediainfo.yazi/adobe.lua
Normal file
@@ -0,0 +1,283 @@
|
||||
--- @since 26.1.22
|
||||
|
||||
local M = {}
|
||||
local const = require(".const")
|
||||
local utils = require(".utils")
|
||||
|
||||
local function image_layer_count(job)
|
||||
local cache = ya.file_cache({ file = job.file, skip = 0 })
|
||||
if not cache then
|
||||
return 0
|
||||
end
|
||||
local layer_count = utils.get_state("f" .. tostring(cache))
|
||||
if layer_count then
|
||||
return layer_count
|
||||
end
|
||||
local output, err = Command("identify")
|
||||
:arg({ tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) })
|
||||
:output()
|
||||
if err or not output then
|
||||
return 0
|
||||
end
|
||||
layer_count = 0
|
||||
for line in output.stdout:gmatch("[^\r\n]+") do
|
||||
if line:match("%S") then
|
||||
layer_count = layer_count + 1
|
||||
end
|
||||
end
|
||||
utils.set_state("f" .. tostring(cache), layer_count)
|
||||
return layer_count
|
||||
end
|
||||
|
||||
function M:peek(job)
|
||||
local preload_status, preload_err = self:preload(job)
|
||||
-- Stop if preload failed
|
||||
if not preload_status then
|
||||
return
|
||||
end
|
||||
|
||||
local cache_img_url = ya.file_cache(job)
|
||||
|
||||
local cache_img_url_no_skip = ya.file_cache({ file = job.file, skip = 0 })
|
||||
|
||||
local hide_metadata = utils.get_state(const.STATE_KEY.hide_metadata)
|
||||
local mediainfo_job_skip = job.skip
|
||||
::recalc_mediainfo_job_skip::
|
||||
local mediainfo_height = 0
|
||||
local lines = {}
|
||||
local limit = job.area.h
|
||||
local last_line = 0
|
||||
local EOF_mediainfo = true
|
||||
local is_wrap = rt.preview.wrap == "yes" or rt.preview.wrap == ui.Wrap.YES
|
||||
|
||||
if not hide_metadata then
|
||||
local cache_mediainfo_path = tostring(cache_img_url_no_skip) .. const.suffix
|
||||
local output = utils.read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
if output then
|
||||
local max_width = math.max(1, job.area.w)
|
||||
if output:match("^Error:") then
|
||||
job.args.force_reload_mediainfo = true
|
||||
preload_status, preload_err = self:preload(job)
|
||||
if not preload_status or preload_err then
|
||||
return
|
||||
end
|
||||
output = utils.read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
end
|
||||
|
||||
output = output:gsub("\n+$", "")
|
||||
|
||||
local iter = output:gmatch("[^\n]*")
|
||||
local str = iter()
|
||||
|
||||
while str ~= nil do
|
||||
local next_str = iter()
|
||||
local label, value = str:match("(.*[^ ]) +: (.*)")
|
||||
local line
|
||||
if label then
|
||||
if not const.skip_labels[label] then
|
||||
line = ui.Line({
|
||||
ui.Span(label .. ": "):style(ui.Style():fg("reset"):bold()),
|
||||
ui.Span(value):style(th.spot.tbl_col or ui.Style():fg("blue")),
|
||||
})
|
||||
end
|
||||
elseif str ~= "General" then
|
||||
line = ui.Line({ ui.Span(str):style(th.spot.title or ui.Style():fg("green")) })
|
||||
end
|
||||
|
||||
if line then
|
||||
local line_height = ui.height
|
||||
and ui.height(str, { width = max_width, ansi = true, wrap = rt.preview.wrap })
|
||||
or (math.max(1, is_wrap and math.ceil(ui.width(line) / max_width) or 1))
|
||||
if next_str == nil and line_height == 1 then
|
||||
EOF_mediainfo = true
|
||||
end
|
||||
if (last_line + line_height) > mediainfo_job_skip then
|
||||
table.insert(lines, line)
|
||||
end
|
||||
if (last_line + line_height) >= mediainfo_job_skip + limit then
|
||||
last_line = mediainfo_job_skip + limit
|
||||
EOF_mediainfo = false
|
||||
break
|
||||
end
|
||||
last_line = last_line + line_height
|
||||
end
|
||||
str = next_str
|
||||
end
|
||||
end
|
||||
mediainfo_height = math.min(limit, last_line)
|
||||
end
|
||||
|
||||
if not hide_metadata then
|
||||
if EOF_mediainfo and #lines == 0 and mediainfo_job_skip > 0 then
|
||||
if
|
||||
image_layer_count(job)
|
||||
< (
|
||||
1
|
||||
+ math.floor(
|
||||
math.max(
|
||||
0,
|
||||
utils.get_state(const.STATE_KEY.units)
|
||||
and (math.abs(job.skip / utils.get_state(const.STATE_KEY.units)))
|
||||
or 0
|
||||
)
|
||||
)
|
||||
)
|
||||
then
|
||||
ya.emit("peek", {
|
||||
math.max(0, (job.skip - (utils.get_state(const.STATE_KEY.units) or 0))),
|
||||
only_if = job.file.url,
|
||||
upper_bound = true,
|
||||
})
|
||||
return
|
||||
else
|
||||
local last_valid_mediainfo_skip = utils.get_state(const.STATE_KEY.last_valid_mediainfo_skip)
|
||||
mediainfo_job_skip = last_valid_mediainfo_skip
|
||||
and last_valid_mediainfo_skip[tostring(cache_img_url_no_skip)]
|
||||
or math.max(0, mediainfo_job_skip - (utils.get_state(const.STATE_KEY.units) or 0))
|
||||
|
||||
goto recalc_mediainfo_job_skip
|
||||
end
|
||||
else
|
||||
utils.set_state(
|
||||
const.STATE_KEY.last_valid_mediainfo_skip,
|
||||
{ [tostring(cache_img_url_no_skip)] = mediainfo_job_skip }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
if utils.get_state(const.STATE_KEY.prev_metadata_area) then
|
||||
ya.preview_widget(job, {
|
||||
ui.Clear(ui.Rect(utils.get_state(const.STATE_KEY.prev_metadata_area))),
|
||||
})
|
||||
end
|
||||
utils.force_render()
|
||||
|
||||
local rendered_img_rect = cache_img_url
|
||||
and fs.cha(cache_img_url)
|
||||
and ya.image_show(
|
||||
cache_img_url,
|
||||
ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y,
|
||||
w = job.area.w,
|
||||
h = mediainfo_height > 0 and math.max(job.area.h - mediainfo_height, job.area.h / 2) or job.area.h,
|
||||
})
|
||||
)
|
||||
or nil
|
||||
local image_height = rendered_img_rect and rendered_img_rect.h or 0
|
||||
|
||||
-- Handle image preload error
|
||||
if preload_err then
|
||||
table.insert(lines, ui.Line(tostring(preload_err)):style(th.spot.title or ui.Style():fg("red")))
|
||||
end
|
||||
|
||||
ya.preview_widget(job, {
|
||||
ui.Text(lines)
|
||||
:area(ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
}))
|
||||
:wrap(is_wrap and ui.Wrap.YES or ui.Wrap.NO),
|
||||
})
|
||||
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
utils.set_state(const.STATE_KEY.prev_metadata_area, not hide_metadata and {
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
} or nil)
|
||||
end
|
||||
|
||||
function M:preload(job)
|
||||
local cmd = "mediainfo"
|
||||
local err_msg = ""
|
||||
local is_valid_utf8_path = utils.is_valid_utf8(tostring(job.file.path or job.file.cache or job.file.url))
|
||||
|
||||
-- NOTE: Preload image
|
||||
|
||||
local cache_img_url = ya.file_cache(job)
|
||||
local cache_img_url_cha = cache_img_url and fs.cha(cache_img_url)
|
||||
|
||||
-- NOTE: Only generate preview image when cache image is not exist
|
||||
if not cache_img_url_cha or cache_img_url_cha.len <= 0 then
|
||||
local cache_img_status, image_preload_err
|
||||
local layer_index = 0
|
||||
local units = utils.get_state(const.STATE_KEY.units)
|
||||
if units ~= nil then
|
||||
local max_layer = image_layer_count(job)
|
||||
layer_index = math.floor(math.max(0, math.abs(job.skip / units)))
|
||||
if layer_index + 1 > max_layer then
|
||||
layer_index = math.max(0, max_layer - 1)
|
||||
end
|
||||
end
|
||||
local cache_img_url_tmp = Url(cache_img_url .. ".tmp")
|
||||
if fs.cha(cache_img_url_tmp) then
|
||||
fs.remove("file", cache_img_url_tmp)
|
||||
end
|
||||
local tmp_file_path, _ = type(fs.unique) == "function" and fs.unique("file", cache_img_url_tmp)
|
||||
or fs.unique_name(cache_img_url_tmp)
|
||||
cache_img_status, image_preload_err = require("magick")
|
||||
.with_limit()
|
||||
:arg({
|
||||
"-background",
|
||||
"none",
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) .. "[" .. tostring(
|
||||
layer_index
|
||||
) .. "]",
|
||||
"-auto-orient",
|
||||
"-strip",
|
||||
"-resize",
|
||||
string.format("%dx%d>", rt.preview.max_width, rt.preview.max_height),
|
||||
"-quality",
|
||||
rt.preview.image_quality,
|
||||
string.format("PNG32:%s", tostring(tmp_file_path)),
|
||||
})
|
||||
:status()
|
||||
if cache_img_status then
|
||||
os.rename(tostring(tmp_file_path), tostring(cache_img_url))
|
||||
end
|
||||
|
||||
if not cache_img_status and image_preload_err then
|
||||
ya.dbg("mediainfo", image_preload_err)
|
||||
err_msg = err_msg .. (image_preload_err and (tostring(image_preload_err)) or "")
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: Get mediainfo and save to cache folder
|
||||
local cache_mediainfo_url = Url(tostring(ya.file_cache({ file = job.file, skip = 0 })) .. const.suffix)
|
||||
local cache_mediainfo_cha = fs.cha(cache_mediainfo_url)
|
||||
-- Case peek function called preload to refetch mediainfo
|
||||
if cache_mediainfo_cha and not job.args.force_reload_mediainfo then
|
||||
return true, err_msg ~= "" and ("Error: " .. err_msg) or nil
|
||||
end
|
||||
|
||||
local output, err
|
||||
if is_valid_utf8_path then
|
||||
output, err = Command(cmd)
|
||||
:arg({ tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) })
|
||||
:output()
|
||||
else
|
||||
cmd = "cd "
|
||||
.. utils.path_quote(tostring((job.file.path or job.file.cache or job.file.url.path or job.file.url).parent))
|
||||
.. " && "
|
||||
.. cmd
|
||||
.. " "
|
||||
.. utils.path_quote(tostring((job.file.path or job.file.cache or job.file.url).name))
|
||||
output, err = Command(const.SHELL):arg({ "-c", cmd }):output()
|
||||
end
|
||||
if err then
|
||||
ya.dbg("mediainfo", tostring(err))
|
||||
err_msg = err_msg .. string.format("Failed to start `%s`. \n Do you have `%s` installed?\n", cmd, cmd)
|
||||
end
|
||||
|
||||
return fs.write(
|
||||
cache_mediainfo_url,
|
||||
(err_msg ~= "" and ("Error: " .. err_msg) or "") .. (output and output.stdout or "")
|
||||
)
|
||||
end
|
||||
|
||||
return M
|
||||
314
config/yazi/plugins/mediainfo.yazi/audio.lua
Normal file
314
config/yazi/plugins/mediainfo.yazi/audio.lua
Normal file
@@ -0,0 +1,314 @@
|
||||
--- @since 26.1.22
|
||||
|
||||
local M = {}
|
||||
local const = require(".const")
|
||||
local utils = require(".utils")
|
||||
|
||||
local function cover_layer_count(job)
|
||||
local cache = ya.file_cache({ file = job.file, skip = 0 })
|
||||
if not cache then
|
||||
return 0
|
||||
end
|
||||
local layer_count = utils.get_state("f" .. tostring(cache))
|
||||
if layer_count then
|
||||
return layer_count
|
||||
end
|
||||
local output, err = Command("ffprobe"):arg({
|
||||
"-v",
|
||||
"error",
|
||||
"-select_streams",
|
||||
"v",
|
||||
"-show_entries",
|
||||
"stream=index:stream_disposition=attached_pic",
|
||||
"-of",
|
||||
"json",
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url),
|
||||
}):output()
|
||||
if err or not output then
|
||||
return 0
|
||||
end
|
||||
layer_count = 0
|
||||
local data = ya.json_decode(output.stdout)
|
||||
layer_count = #data.streams
|
||||
utils.set_state("f" .. tostring(cache), layer_count)
|
||||
return layer_count
|
||||
end
|
||||
|
||||
function M:peek(job)
|
||||
local preload_status, preload_err = self:preload(job)
|
||||
-- Stop if preload failed
|
||||
if not preload_status then
|
||||
return
|
||||
end
|
||||
|
||||
local cache_img_url = ya.file_cache(job)
|
||||
|
||||
local cache_img_url_no_skip = ya.file_cache({ file = job.file, skip = 0 })
|
||||
|
||||
local hide_metadata = utils.get_state(const.STATE_KEY.hide_metadata)
|
||||
local mediainfo_job_skip = job.skip
|
||||
::recalc_mediainfo_job_skip::
|
||||
local mediainfo_height = 0
|
||||
local lines = {}
|
||||
local limit = job.area.h
|
||||
local last_line = 0
|
||||
local EOF_mediainfo = true
|
||||
local is_wrap = rt.preview.wrap == "yes" or rt.preview.wrap == ui.Wrap.YES
|
||||
|
||||
if not hide_metadata then
|
||||
local cache_mediainfo_path = tostring(cache_img_url_no_skip) .. const.suffix
|
||||
local output = utils.read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
if output then
|
||||
local max_width = math.max(1, job.area.w)
|
||||
if output:match("^Error:") then
|
||||
job.args.force_reload_mediainfo = true
|
||||
preload_status, preload_err = self:preload(job)
|
||||
if not preload_status or preload_err then
|
||||
return
|
||||
end
|
||||
output = utils.read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
end
|
||||
|
||||
output = output:gsub("\n+$", "")
|
||||
|
||||
local iter = output:gmatch("[^\n]*")
|
||||
local str = iter()
|
||||
|
||||
while str ~= nil do
|
||||
local next_str = iter()
|
||||
local label, value = str:match("(.*[^ ]) +: (.*)")
|
||||
local line
|
||||
if label then
|
||||
if not const.skip_labels[label] then
|
||||
line = ui.Line({
|
||||
ui.Span(label .. ": "):style(ui.Style():fg("reset"):bold()),
|
||||
ui.Span(value):style(th.spot.tbl_col or ui.Style():fg("blue")),
|
||||
})
|
||||
end
|
||||
elseif str ~= "General" then
|
||||
line = ui.Line({ ui.Span(str):style(th.spot.title or ui.Style():fg("green")) })
|
||||
end
|
||||
|
||||
if line then
|
||||
local line_height = ui.height
|
||||
and ui.height(str, { width = max_width, ansi = true, wrap = rt.preview.wrap })
|
||||
or (math.max(1, is_wrap and math.ceil(ui.width(line) / max_width) or 1))
|
||||
if next_str == nil and line_height == 1 then
|
||||
EOF_mediainfo = true
|
||||
end
|
||||
if (last_line + line_height) > mediainfo_job_skip then
|
||||
table.insert(lines, line)
|
||||
end
|
||||
if (last_line + line_height) >= mediainfo_job_skip + limit then
|
||||
last_line = mediainfo_job_skip + limit
|
||||
EOF_mediainfo = false
|
||||
break
|
||||
end
|
||||
last_line = last_line + line_height
|
||||
end
|
||||
str = next_str
|
||||
end
|
||||
end
|
||||
mediainfo_height = math.min(limit, last_line)
|
||||
end
|
||||
|
||||
if not hide_metadata then
|
||||
if EOF_mediainfo and #lines == 0 and mediainfo_job_skip > 0 then
|
||||
if
|
||||
cover_layer_count(job)
|
||||
< (
|
||||
1
|
||||
+ math.floor(
|
||||
math.max(
|
||||
0,
|
||||
utils.get_state(const.STATE_KEY.units)
|
||||
and (math.abs(job.skip / utils.get_state(const.STATE_KEY.units)))
|
||||
or 0
|
||||
)
|
||||
)
|
||||
)
|
||||
then
|
||||
ya.emit("peek", {
|
||||
math.max(0, (job.skip - (utils.get_state(const.STATE_KEY.units) or 0))),
|
||||
only_if = job.file.url,
|
||||
upper_bound = true,
|
||||
})
|
||||
return
|
||||
else
|
||||
-- NOTE: Recalculate mediainfo using cached latest valid skip value when reach the end of mediainfo output
|
||||
local last_valid_mediainfo_skip = utils.get_state(const.STATE_KEY.last_valid_mediainfo_skip)
|
||||
mediainfo_job_skip = last_valid_mediainfo_skip
|
||||
and last_valid_mediainfo_skip[tostring(cache_img_url_no_skip)]
|
||||
or math.max(0, mediainfo_job_skip - (utils.get_state(const.STATE_KEY.units) or 0))
|
||||
|
||||
goto recalc_mediainfo_job_skip
|
||||
end
|
||||
else
|
||||
utils.set_state(
|
||||
const.STATE_KEY.last_valid_mediainfo_skip,
|
||||
{ [tostring(cache_img_url_no_skip)] = mediainfo_job_skip }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
if utils.get_state(const.STATE_KEY.prev_metadata_area) then
|
||||
ya.preview_widget(job, {
|
||||
ui.Clear(ui.Rect(utils.get_state(const.STATE_KEY.prev_metadata_area))),
|
||||
})
|
||||
end
|
||||
utils.force_render()
|
||||
|
||||
local rendered_img_rect = cache_img_url
|
||||
and fs.cha(cache_img_url)
|
||||
and ya.image_show(
|
||||
cache_img_url,
|
||||
ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y,
|
||||
w = job.area.w,
|
||||
h = mediainfo_height > 0 and math.max(job.area.h - mediainfo_height, job.area.h / 2) or job.area.h,
|
||||
})
|
||||
)
|
||||
or nil
|
||||
local image_height = rendered_img_rect and rendered_img_rect.h or 0
|
||||
|
||||
-- Handle image preload error
|
||||
if preload_err then
|
||||
table.insert(lines, ui.Line(tostring(preload_err)):style(th.spot.title or ui.Style():fg("red")))
|
||||
end
|
||||
|
||||
ya.preview_widget(job, {
|
||||
ui.Text(lines)
|
||||
:area(ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
}))
|
||||
:wrap(is_wrap and ui.Wrap.YES or ui.Wrap.NO),
|
||||
})
|
||||
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
utils.set_state(const.STATE_KEY.prev_metadata_area, not hide_metadata and {
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
} or nil)
|
||||
end
|
||||
|
||||
function M:preload(job)
|
||||
local cmd = "mediainfo"
|
||||
local err_msg = ""
|
||||
local is_valid_utf8_path = utils.is_valid_utf8(tostring(job.file.path or job.file.cache or job.file.url))
|
||||
|
||||
-- NOTE: Preload image
|
||||
|
||||
local cache_img_url = ya.file_cache(job)
|
||||
local cache_img_url_cha = cache_img_url and fs.cha(cache_img_url)
|
||||
|
||||
-- NOTE: Only generate preview image when cache image is not exist
|
||||
if not cache_img_url_cha or cache_img_url_cha.len <= 0 then
|
||||
local cover_index = 0
|
||||
local units = utils.get_state(const.STATE_KEY.units)
|
||||
if units ~= nil then
|
||||
local max_layer = cover_layer_count(job)
|
||||
cover_index = math.floor(math.max(0, math.abs(job.skip / units)))
|
||||
if cover_index + 1 > max_layer then
|
||||
cover_index = math.max(0, max_layer - 1)
|
||||
end
|
||||
end
|
||||
local qv = 31 - math.floor(rt.preview.image_quality * 0.3)
|
||||
local audio_preload_output, audio_preload_err = Command("ffmpeg"):arg({
|
||||
"-v",
|
||||
"error",
|
||||
"-threads",
|
||||
1,
|
||||
"-i",
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url),
|
||||
"-map",
|
||||
string.format("0:v:%d?", cover_index),
|
||||
"-an",
|
||||
"-sn",
|
||||
"-dn",
|
||||
"-vframes",
|
||||
1,
|
||||
"-q:v",
|
||||
qv,
|
||||
"-vf",
|
||||
string.format("scale=-1:'min(%d,ih)':flags=fast_bilinear", rt.preview.max_height / 2),
|
||||
"-f",
|
||||
"image2",
|
||||
"-y",
|
||||
tostring(cache_img_url),
|
||||
}):output()
|
||||
-- NOTE: Some audio types doesn't have cover image -> error ""
|
||||
if
|
||||
(
|
||||
audio_preload_output
|
||||
and audio_preload_output.stderr ~= nil
|
||||
and audio_preload_output.stderr ~= ""
|
||||
and not audio_preload_output.stderr:find("Output file does not contain any stream")
|
||||
) or audio_preload_err
|
||||
then
|
||||
ya.dbg("mediainfo", audio_preload_err)
|
||||
ya.dbg("mediainfo", audio_preload_output.stderr)
|
||||
err_msg = err_msg
|
||||
.. string.format("Failed to start `%s`.\n Do you have `%s` installed?\n", "ffmpeg", "ffmpeg")
|
||||
else
|
||||
cache_img_url_cha, _ = fs.cha(cache_img_url)
|
||||
if not cache_img_url_cha then
|
||||
-- NOTE: Workaround case audio has no cover image. Prevent regenerate preview image
|
||||
audio_preload_output, audio_preload_err = require("magick")
|
||||
.with_limit()
|
||||
:arg({
|
||||
"-size",
|
||||
"1x1",
|
||||
"canvas:none",
|
||||
string.format("PNG32:%s", cache_img_url),
|
||||
})
|
||||
:output()
|
||||
if (audio_preload_output.stderr ~= nil and audio_preload_output.stderr ~= "") or audio_preload_err then
|
||||
ya.dbg("mediainfo", image_preload_err)
|
||||
err_msg = err_msg
|
||||
.. string.format("Failed to start `%s`.\n Do you have `%s` installed?\n", "magick", "magick")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: Get mediainfo and save to cache folder
|
||||
local cache_mediainfo_url = Url(tostring(ya.file_cache({ file = job.file, skip = 0 })) .. const.suffix)
|
||||
local cache_mediainfo_cha = fs.cha(cache_mediainfo_url)
|
||||
-- Case peek function called preload to refetch mediainfo
|
||||
if cache_mediainfo_cha and not job.args.force_reload_mediainfo then
|
||||
return true, err_msg ~= "" and ("Error: " .. err_msg) or nil
|
||||
end
|
||||
|
||||
local output, err
|
||||
if is_valid_utf8_path then
|
||||
output, err = Command(cmd)
|
||||
:arg({ tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) })
|
||||
:output()
|
||||
else
|
||||
cmd = "cd "
|
||||
.. utils.path_quote(tostring((job.file.path or job.file.cache or job.file.url.path or job.file.url).parent))
|
||||
.. " && "
|
||||
.. cmd
|
||||
.. " "
|
||||
.. utils.path_quote(tostring((job.file.path or job.file.cache or job.file.url).name))
|
||||
output, err = Command(const.SHELL):arg({ "-c", cmd }):output()
|
||||
end
|
||||
if err then
|
||||
ya.dbg("mediainfo", tostring(err))
|
||||
err_msg = err_msg .. string.format("Failed to start `%s`. \n Do you have `%s` installed?\n", cmd, cmd)
|
||||
end
|
||||
|
||||
return fs.write(
|
||||
cache_mediainfo_url,
|
||||
(err_msg ~= "" and ("Error: " .. err_msg) or "") .. (output and output.stdout or "")
|
||||
)
|
||||
end
|
||||
|
||||
return M
|
||||
57
config/yazi/plugins/mediainfo.yazi/const.lua
Normal file
57
config/yazi/plugins/mediainfo.yazi/const.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
--- @since 26.1.22
|
||||
|
||||
local M = {}
|
||||
|
||||
M.skip_labels = {
|
||||
["Complete name"] = true,
|
||||
["CompleteName_Last"] = true,
|
||||
["Unique ID"] = true,
|
||||
["File size"] = true,
|
||||
["Format/Info"] = true,
|
||||
["Codec ID/Info"] = true,
|
||||
["MD5 of the unencoded content"] = true,
|
||||
}
|
||||
|
||||
M.ENTRY_ACTION = {
|
||||
toggle_metadata = "toggle-metadata",
|
||||
}
|
||||
|
||||
M.STATE_KEY = {
|
||||
units = "units",
|
||||
hide_metadata = "hide_metadata",
|
||||
prev_metadata_area = "prev_metadata_area",
|
||||
prev_image_height = "prev_image_height",
|
||||
last_valid_mediainfo_skip = "last_valid_mediainfo_skip",
|
||||
}
|
||||
|
||||
M.magick_image_mimes = {
|
||||
avif = true,
|
||||
hei = true,
|
||||
heic = true,
|
||||
heif = true,
|
||||
["heif-sequence"] = true,
|
||||
["heic-sequence"] = true,
|
||||
jxl = true,
|
||||
tiff = true,
|
||||
xml = true,
|
||||
-- ["svg+xml"] = true,
|
||||
["canon-cr2"] = true,
|
||||
}
|
||||
|
||||
M.seekable_mimes = {
|
||||
-- NOTE: Adobe illustrator photoshop mimetypes
|
||||
["application/postscript"] = true,
|
||||
["application/dvb.ait"] = true,
|
||||
["application/illustrator"] = true,
|
||||
["application/vnd.adobe.illustrator"] = true,
|
||||
["image/x-eps"] = true,
|
||||
["application/eps"] = true,
|
||||
["application/pdf"] = true,
|
||||
|
||||
["image/adobe.photoshop"] = true,
|
||||
}
|
||||
|
||||
M.suffix = "_mediainfo"
|
||||
M.SHELL = os.getenv("SHELL") or ""
|
||||
|
||||
return M
|
||||
234
config/yazi/plugins/mediainfo.yazi/image.lua
Normal file
234
config/yazi/plugins/mediainfo.yazi/image.lua
Normal file
@@ -0,0 +1,234 @@
|
||||
--- @since 26.1.22
|
||||
|
||||
local M = {}
|
||||
local const = require(".const")
|
||||
local utils = require(".utils")
|
||||
|
||||
function M:peek(job)
|
||||
local preload_status, preload_err = self:preload(job)
|
||||
-- Stop if preload failed
|
||||
if not preload_status then
|
||||
return
|
||||
end
|
||||
|
||||
local cache_img_url = ya.file_cache({
|
||||
skip = 0,
|
||||
args = job.args,
|
||||
file = job.file,
|
||||
area = job.area,
|
||||
})
|
||||
|
||||
local cache_img_url_no_skip = ya.file_cache({ file = job.file, skip = 0 })
|
||||
|
||||
local hide_metadata = utils.get_state(const.STATE_KEY.hide_metadata)
|
||||
local mediainfo_height = 0
|
||||
local lines = {}
|
||||
local limit = job.area.h
|
||||
local last_line = 0
|
||||
local EOF_mediainfo = true
|
||||
local is_wrap = rt.preview.wrap == "yes" or rt.preview.wrap == ui.Wrap.YES
|
||||
|
||||
if not hide_metadata then
|
||||
local cache_mediainfo_path = tostring(cache_img_url_no_skip) .. const.suffix
|
||||
local output = utils.read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
if output then
|
||||
local max_width = math.max(1, job.area.w)
|
||||
if output:match("^Error:") then
|
||||
job.args.force_reload_mediainfo = true
|
||||
preload_status, preload_err = self:preload(job)
|
||||
if not preload_status or preload_err then
|
||||
return
|
||||
end
|
||||
output = utils.read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
end
|
||||
|
||||
output = output:gsub("\n+$", "")
|
||||
|
||||
local iter = output:gmatch("[^\n]*")
|
||||
local str = iter()
|
||||
|
||||
while str ~= nil do
|
||||
local next_str = iter()
|
||||
local label, value = str:match("(.*[^ ]) +: (.*)")
|
||||
local line
|
||||
if label then
|
||||
if not const.skip_labels[label] then
|
||||
line = ui.Line({
|
||||
ui.Span(label .. ": "):style(ui.Style():fg("reset"):bold()),
|
||||
ui.Span(value):style(th.spot.tbl_col or ui.Style():fg("blue")),
|
||||
})
|
||||
end
|
||||
elseif str ~= "General" then
|
||||
line = ui.Line({ ui.Span(str):style(th.spot.title or ui.Style():fg("green")) })
|
||||
end
|
||||
|
||||
if line then
|
||||
local line_height = ui.height
|
||||
and ui.height(str, { width = max_width, ansi = true, wrap = rt.preview.wrap })
|
||||
or (math.max(1, is_wrap and math.ceil(ui.width(line) / max_width) or 1))
|
||||
if next_str == nil and line_height == 1 then
|
||||
EOF_mediainfo = true
|
||||
end
|
||||
if (last_line + line_height) > job.skip then
|
||||
table.insert(lines, line)
|
||||
end
|
||||
if (last_line + line_height) >= job.skip + limit then
|
||||
last_line = job.skip + limit
|
||||
EOF_mediainfo = false
|
||||
break
|
||||
end
|
||||
last_line = last_line + line_height
|
||||
end
|
||||
str = next_str
|
||||
end
|
||||
end
|
||||
mediainfo_height = math.min(limit, last_line)
|
||||
end
|
||||
|
||||
if not hide_metadata and EOF_mediainfo and #lines == 0 and job.skip > 0 then
|
||||
ya.emit("peek", {
|
||||
math.max(0, (job.skip - (utils.get_state(const.STATE_KEY.units) or 0))),
|
||||
only_if = job.file.url,
|
||||
upper_bound = true,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
utils.force_render()
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
if utils.get_state(const.STATE_KEY.prev_metadata_area) then
|
||||
ya.preview_widget(job, {
|
||||
ui.Clear(ui.Rect(utils.get_state(const.STATE_KEY.prev_metadata_area))),
|
||||
})
|
||||
end
|
||||
|
||||
local rendered_img_rect = cache_img_url
|
||||
and fs.cha(cache_img_url)
|
||||
and ya.image_show(
|
||||
cache_img_url,
|
||||
ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y,
|
||||
w = job.area.w,
|
||||
h = mediainfo_height > 0 and math.max(job.area.h - mediainfo_height, job.area.h / 2) or job.area.h,
|
||||
})
|
||||
)
|
||||
or nil
|
||||
local image_height = rendered_img_rect and rendered_img_rect.h or 0
|
||||
|
||||
-- Handle image preload error
|
||||
if preload_err then
|
||||
table.insert(lines, ui.Line(tostring(preload_err)):style(th.spot.title or ui.Style():fg("red")))
|
||||
end
|
||||
|
||||
ya.preview_widget(job, {
|
||||
ui.Text(lines)
|
||||
:area(ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
}))
|
||||
:wrap(is_wrap and ui.Wrap.YES or ui.Wrap.NO),
|
||||
})
|
||||
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
utils.set_state(const.STATE_KEY.prev_metadata_area, not hide_metadata and {
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
} or nil)
|
||||
end
|
||||
|
||||
function M:preload(job)
|
||||
local cmd = "mediainfo"
|
||||
local err_msg = ""
|
||||
local is_valid_utf8_path = utils.is_valid_utf8(tostring(job.file.path or job.file.cache or job.file.url))
|
||||
|
||||
-- NOTE: Preload image
|
||||
|
||||
local mime = job.mime:match(".*/(.*)$")
|
||||
local is_svg = mime == "svg+xml"
|
||||
local is_magick = const.magick_image_mimes[mime]
|
||||
local no_skip_job = { skip = 0, file = job.file, args = job.args, area = job.area }
|
||||
local cache_img_url = ya.file_cache(no_skip_job)
|
||||
local cache_img_url_cha = cache_img_url and fs.cha(cache_img_url)
|
||||
|
||||
-- NOTE: Only generate preview image when cache image is not exist
|
||||
if not cache_img_url_cha or cache_img_url_cha.len <= 0 then
|
||||
local cache_img_status, image_preload_err
|
||||
if not is_valid_utf8_path then
|
||||
-- NOTE: Case not valid utf8 path, use trick to generate preview image
|
||||
if is_svg then
|
||||
local cache_img_url_tmp = Url(cache_img_url .. ".tmp")
|
||||
if fs.cha(cache_img_url_tmp) then
|
||||
fs.remove("file", cache_img_url_tmp)
|
||||
end
|
||||
local tmp_file_path, _ = type(fs.unique) == "function" and fs.unique("file", cache_img_url_tmp)
|
||||
or fs.unique_name(cache_img_url_tmp)
|
||||
-- svg under invalid utf8 path
|
||||
cache_img_status, image_preload_err = require("magick")
|
||||
.with_limit()
|
||||
:arg({
|
||||
"-background",
|
||||
"none",
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url),
|
||||
"-auto-orient",
|
||||
"-strip",
|
||||
string.format("%dx%d>", rt.preview.max_width, rt.preview.max_height),
|
||||
"-quality",
|
||||
rt.preview.image_quality,
|
||||
string.format("PNG32:%s", tostring(tmp_file_path)),
|
||||
})
|
||||
:status()
|
||||
if cache_img_status then
|
||||
os.rename(tostring(tmp_file_path), tostring(cache_img_url))
|
||||
end
|
||||
end
|
||||
else
|
||||
-- NOTE: Case valid utf8 path, use image, svg, or magick module
|
||||
local image_module = is_svg and "svg" or (is_magick and "magick" or "image")
|
||||
cache_img_status, image_preload_err = require(image_module):preload(no_skip_job)
|
||||
end
|
||||
|
||||
if not cache_img_status and image_preload_err then
|
||||
ya.dbg("mediainfo", image_preload_err)
|
||||
err_msg = err_msg .. (image_preload_err and (tostring(image_preload_err)) or "")
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: Get mediainfo and save to cache folder
|
||||
local cache_mediainfo_url = Url(tostring(ya.file_cache({ file = job.file, skip = 0 })) .. const.suffix)
|
||||
local cache_mediainfo_cha = fs.cha(cache_mediainfo_url)
|
||||
-- Case peek function called preload to refetch mediainfo
|
||||
if cache_mediainfo_cha and not job.args.force_reload_mediainfo then
|
||||
return true, err_msg ~= "" and ("Error: " .. err_msg) or nil
|
||||
end
|
||||
|
||||
local output, err
|
||||
if is_valid_utf8_path then
|
||||
output, err = Command(cmd)
|
||||
:arg({ tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) })
|
||||
:output()
|
||||
else
|
||||
cmd = "cd "
|
||||
.. utils.path_quote(tostring((job.file.path or job.file.cache or job.file.url.path or job.file.url).parent))
|
||||
.. " && "
|
||||
.. cmd
|
||||
.. " "
|
||||
.. utils.path_quote(tostring((job.file.path or job.file.cache or job.file.url).name))
|
||||
output, err = Command(const.SHELL):arg({ "-c", cmd }):output()
|
||||
end
|
||||
if err then
|
||||
ya.dbg("mediainfo", tostring(err))
|
||||
err_msg = err_msg .. string.format("Failed to start `%s`. \n Do you have `%s` installed?\n", cmd, cmd)
|
||||
end
|
||||
|
||||
return fs.write(
|
||||
cache_mediainfo_url,
|
||||
(err_msg ~= "" and ("Error: " .. err_msg) or "") .. (output and output.stdout or "")
|
||||
)
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -1,472 +1,79 @@
|
||||
--- @since 25.5.31
|
||||
|
||||
local skip_labels = {
|
||||
["Complete name"] = true,
|
||||
["CompleteName_Last"] = true,
|
||||
["Unique ID"] = true,
|
||||
["File size"] = true,
|
||||
["Format/Info"] = true,
|
||||
["Codec ID/Info"] = true,
|
||||
["MD5 of the unencoded content"] = true,
|
||||
}
|
||||
|
||||
local ENTRY_ACTION = {
|
||||
toggle_metadata = "toggle-metadata",
|
||||
}
|
||||
|
||||
local STATE_KEY = {
|
||||
units = "units",
|
||||
hide_metadata = "hide_metadata",
|
||||
prev_metadata_area = "prev_metadata_area",
|
||||
}
|
||||
|
||||
local magick_image_mimes = {
|
||||
avif = true,
|
||||
hei = true,
|
||||
heic = true,
|
||||
heif = true,
|
||||
["heif-sequence"] = true,
|
||||
["heic-sequence"] = true,
|
||||
jxl = true,
|
||||
tiff = true,
|
||||
xml = true,
|
||||
["svg+xml"] = true,
|
||||
["canon-cr2"] = true,
|
||||
}
|
||||
|
||||
local seekable_mimes = {
|
||||
["application/postscript"] = true,
|
||||
["image/adobe.photoshop"] = true,
|
||||
}
|
||||
--- @since 26.1.22
|
||||
|
||||
local M = {}
|
||||
local suffix = "_mediainfo"
|
||||
local SHELL = os.getenv("SHELL") or ""
|
||||
|
||||
local function is_valid_utf8(str)
|
||||
return utf8.len(str) ~= nil
|
||||
end
|
||||
|
||||
local function path_quote(path)
|
||||
if not path or tostring(path) == "" then
|
||||
return path
|
||||
end
|
||||
local result = "'" .. string.gsub(tostring(path), "'", "'\\''") .. "'"
|
||||
return result
|
||||
end
|
||||
|
||||
local function read_mediainfo_cached_file(file_path)
|
||||
-- Open the file in read mode
|
||||
local file = io.open(file_path, "r")
|
||||
|
||||
if file then
|
||||
-- Read the entire file content
|
||||
local content = file:read("*all")
|
||||
file:close()
|
||||
return content
|
||||
end
|
||||
end
|
||||
|
||||
local set_state = ya.sync(function(state, key, value)
|
||||
state[key] = value
|
||||
end)
|
||||
|
||||
local get_state = ya.sync(function(state, key)
|
||||
return state[key]
|
||||
end)
|
||||
|
||||
local force_render = ya.sync(function(_, _)
|
||||
(ui.render or ya.render)()
|
||||
end)
|
||||
|
||||
local function image_layer_count(job)
|
||||
local cache = ya.file_cache({ file = job.file, skip = 0 })
|
||||
if not cache then
|
||||
return 0
|
||||
end
|
||||
local layer_count = get_state("f" .. tostring(cache))
|
||||
if layer_count then
|
||||
return layer_count
|
||||
end
|
||||
local output, err = Command("identify")
|
||||
:arg({ tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) })
|
||||
:output()
|
||||
if err then
|
||||
return 0
|
||||
end
|
||||
layer_count = 0
|
||||
for line in output.stdout:gmatch("[^\r\n]+") do
|
||||
if line:match("%S") then
|
||||
layer_count = layer_count + 1
|
||||
end
|
||||
end
|
||||
set_state("f" .. tostring(cache), layer_count)
|
||||
return layer_count
|
||||
end
|
||||
local const = require(".const")
|
||||
local utils = require(".utils")
|
||||
local adobe = require(".adobe")
|
||||
local audio = require(".audio")
|
||||
local image = require(".image")
|
||||
local video = require(".video")
|
||||
|
||||
function M:peek(job)
|
||||
-- debounce peek
|
||||
local start = os.clock()
|
||||
local cache_img_url_no_skip = ya.file_cache({ file = job.file, skip = 0 })
|
||||
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
|
||||
|
||||
-- Need mime to decide which module to use
|
||||
if not job.mime then
|
||||
return
|
||||
end
|
||||
|
||||
local is_video = string.find(job.mime, "^video/")
|
||||
local is_audio = string.find(job.mime, "^audio/")
|
||||
local is_image = string.find(job.mime, "^image/")
|
||||
local is_seekable = seekable_mimes[job.mime] or is_video
|
||||
local cache_img_url = (is_audio or is_image) and cache_img_url_no_skip
|
||||
local is_adobe = const.seekable_mimes[job.mime]
|
||||
|
||||
if is_seekable then
|
||||
cache_img_url = ya.file_cache(job)
|
||||
if is_adobe then
|
||||
return adobe:peek(job)
|
||||
elseif is_image then
|
||||
return image:peek(job)
|
||||
elseif is_video then
|
||||
return video:peek(job)
|
||||
elseif is_audio then
|
||||
return audio:peek(job)
|
||||
end
|
||||
local preload_status, preload_err = self:preload(job)
|
||||
if not preload_status then
|
||||
return
|
||||
end
|
||||
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
|
||||
local hide_metadata = get_state(STATE_KEY.hide_metadata)
|
||||
local mediainfo_height = 0
|
||||
local lines = {}
|
||||
local limit = job.area.h
|
||||
local last_line = 0
|
||||
local is_wrap = rt.preview.wrap == "yes"
|
||||
if not hide_metadata then
|
||||
local cache_mediainfo_path = tostring(cache_img_url_no_skip) .. suffix
|
||||
local output = read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
if output then
|
||||
local max_width = math.max(1, job.area.w)
|
||||
if output:match("^Error:") then
|
||||
job.args.force_reload_mediainfo = true
|
||||
preload_status, preload_err = self:preload(job)
|
||||
if not preload_status or preload_err then
|
||||
return
|
||||
end
|
||||
output = read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
end
|
||||
|
||||
for str in output:gsub("\n+$", ""):gmatch("[^\n]*") do
|
||||
local label, value = str:match("(.*[^ ]) +: (.*)")
|
||||
local line
|
||||
if label then
|
||||
if not skip_labels[label] then
|
||||
line = ui.Line({
|
||||
ui.Span(label .. ": "):style(ui.Style():fg("reset"):bold()),
|
||||
ui.Span(value):style(th.spot.tbl_col or ui.Style():fg("blue")),
|
||||
})
|
||||
end
|
||||
elseif str ~= "General" then
|
||||
line = ui.Line({ ui.Span(str):style(th.spot.title or ui.Style():fg("green")) })
|
||||
end
|
||||
|
||||
if line then
|
||||
local line_height = math.max(1, is_wrap and math.ceil(ui.width(line) / max_width) or 1)
|
||||
if (last_line + line_height) > job.skip then
|
||||
table.insert(lines, line)
|
||||
end
|
||||
if (last_line + line_height) >= job.skip + limit then
|
||||
last_line = job.skip + limit
|
||||
break
|
||||
end
|
||||
last_line = last_line + line_height
|
||||
end
|
||||
end
|
||||
end
|
||||
mediainfo_height = math.min(limit, last_line)
|
||||
end
|
||||
|
||||
if
|
||||
(job.skip > 0 and #lines == 0 and not hide_metadata)
|
||||
and (
|
||||
not is_seekable
|
||||
or (is_video and job.skip >= 90)
|
||||
or (
|
||||
(job.mime == "image/adobe.photoshop" or job.mime == "application/postscript")
|
||||
and image_layer_count(job)
|
||||
< (1 + math.floor(
|
||||
math.max(0, get_state(STATE_KEY.units) and (job.skip / get_state(STATE_KEY.units)) or 0)
|
||||
))
|
||||
)
|
||||
)
|
||||
then
|
||||
ya.emit("peek", {
|
||||
math.max(0, job.skip - (get_state(STATE_KEY.units) or limit)),
|
||||
only_if = job.file.url,
|
||||
upper_bound = true,
|
||||
})
|
||||
return
|
||||
end
|
||||
force_render()
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
if hide_metadata and get_state(STATE_KEY.prev_metadata_area) then
|
||||
ya.preview_widget(job, {
|
||||
ui.Clear(ui.Rect(get_state(STATE_KEY.prev_metadata_area))),
|
||||
})
|
||||
ya.sleep(0.1)
|
||||
end
|
||||
local rendered_img_rect = cache_img_url
|
||||
and fs.cha(cache_img_url)
|
||||
and ya.image_show(
|
||||
cache_img_url,
|
||||
ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y,
|
||||
w = job.area.w,
|
||||
h = mediainfo_height > 0 and math.max(job.area.h - mediainfo_height, job.area.h / 2) or job.area.h,
|
||||
})
|
||||
)
|
||||
or nil
|
||||
local image_height = rendered_img_rect and rendered_img_rect.h or 0
|
||||
|
||||
-- NOTE: Workaround case audio has no cover image. Prevent regenerate preview image
|
||||
if is_audio and image_height == 1 then
|
||||
local info = ya.image_info(cache_img_url)
|
||||
if not info or (info.w == 1 and info.h == 1) then
|
||||
image_height = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: Workaround case video.lua doesn't doesn't generate preview image because of `skip` overflow video duration
|
||||
if is_video and not rendered_img_rect then
|
||||
image_height = math.max(job.area.h - mediainfo_height, 0)
|
||||
end
|
||||
|
||||
-- Handle image preload error
|
||||
if preload_err then
|
||||
table.insert(lines, ui.Line(tostring(preload_err)):style(th.spot.title or ui.Style():fg("red")))
|
||||
end
|
||||
|
||||
ya.preview_widget(job, {
|
||||
ui.Text(lines)
|
||||
:area(ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
}))
|
||||
:wrap(is_wrap and ui.Wrap.YES or ui.Wrap.NO),
|
||||
})
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
set_state(STATE_KEY.prev_metadata_area, not hide_metadata and {
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
} or nil)
|
||||
end
|
||||
|
||||
function M:seek(job)
|
||||
local h = cx.active.current.hovered
|
||||
if h and h.url == job.file.url then
|
||||
set_state(STATE_KEY.units, job.units)
|
||||
utils.set_state(const.STATE_KEY.units, job.units)
|
||||
ya.emit("peek", {
|
||||
math.max(0, cx.active.preview.skip + job.units),
|
||||
only_if = job.file.url,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function M:preload(job)
|
||||
local cache_img_url = ya.file_cache({ file = job.file, skip = 0 })
|
||||
if not cache_img_url then
|
||||
ya.dbg("mediainfo", "Can't access yazi cache folder")
|
||||
return true
|
||||
end
|
||||
local cache_mediainfo_url = Url(tostring(cache_img_url) .. suffix)
|
||||
cache_img_url = seekable_mimes[job.mime] and ya.file_cache(job) or cache_img_url
|
||||
local cache_img_url_cha = cache_img_url and fs.cha(cache_img_url)
|
||||
local err_msg = ""
|
||||
local is_valid_utf8_path = is_valid_utf8(tostring(job.file.path or job.file.cache or job.file.url))
|
||||
-- video mimetype
|
||||
if job.mime then
|
||||
if string.find(job.mime, "^video/") then
|
||||
local cache_img_status, video_preload_err = require("video"):preload(job)
|
||||
if not cache_img_status and video_preload_err then
|
||||
err_msg = err_msg
|
||||
.. string.format("Failed to start `%s`, Do you have `%s` installed?\n", "ffmpeg", "ffmpeg")
|
||||
end
|
||||
-- audo and image mimetype
|
||||
elseif cache_img_url and (not cache_img_url_cha or cache_img_url_cha.len <= 0) then
|
||||
-- audio
|
||||
if string.find(job.mime, "^audio/") then
|
||||
local qv = 31 - math.floor(rt.preview.image_quality * 0.3)
|
||||
local audio_preload_output, audio_preload_err = Command("ffmpeg"):arg({
|
||||
"-v",
|
||||
"error",
|
||||
"-threads",
|
||||
1,
|
||||
"-an",
|
||||
"-sn",
|
||||
"-dn",
|
||||
"-i",
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url),
|
||||
"-vframes",
|
||||
1,
|
||||
"-q:v",
|
||||
qv,
|
||||
"-vf",
|
||||
string.format("scale=-1:'min(%d,ih)':flags=fast_bilinear", rt.preview.max_height / 2),
|
||||
"-f",
|
||||
"image2",
|
||||
"-y",
|
||||
tostring(cache_img_url),
|
||||
}):output()
|
||||
-- NOTE: Some audio types doesn't have cover image -> error ""
|
||||
if
|
||||
(
|
||||
audio_preload_output
|
||||
and audio_preload_output.stderr ~= nil
|
||||
and audio_preload_output.stderr ~= ""
|
||||
and not audio_preload_output.stderr:find("Output file does not contain any stream")
|
||||
) or audio_preload_err
|
||||
then
|
||||
err_msg = err_msg
|
||||
.. string.format("Failed to start `%s`, Do you have `%s` installed?\n", "ffmpeg", "ffmpeg")
|
||||
else
|
||||
cache_img_url_cha, _ = fs.cha(cache_img_url)
|
||||
if not cache_img_url_cha then
|
||||
-- NOTE: Workaround case audio has no cover image. Prevent regenerate preview image
|
||||
audio_preload_output, audio_preload_err = require("magick")
|
||||
.with_limit()
|
||||
:arg({
|
||||
"-size",
|
||||
"1x1",
|
||||
"canvas:none",
|
||||
string.format("PNG32:%s", cache_img_url),
|
||||
})
|
||||
:output()
|
||||
if
|
||||
(audio_preload_output.stderr ~= nil and audio_preload_output.stderr ~= "")
|
||||
or audio_preload_err
|
||||
then
|
||||
err_msg = err_msg
|
||||
.. string.format(
|
||||
"Failed to start `%s`, Do you have `%s` installed?\n",
|
||||
"magick",
|
||||
"magick"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- image
|
||||
elseif string.find(job.mime, "^image/") or job.mime == "application/postscript" then
|
||||
local svg_plugin_ok, svg_plugin = pcall(require, "svg")
|
||||
local magick_plugin_ok, magick_plugin = pcall(require, "magick")
|
||||
local mime = job.mime:match(".*/(.*)$")
|
||||
if not job.mime then
|
||||
return false
|
||||
end
|
||||
local is_video = string.find(job.mime, "^video/")
|
||||
local is_audio = string.find(job.mime, "^audio/")
|
||||
local is_image = string.find(job.mime, "^image/")
|
||||
local is_adobe = const.seekable_mimes[job.mime]
|
||||
|
||||
local image_plugin = magick_image_mimes[mime]
|
||||
and ((mime == "svg+xml" and svg_plugin_ok) and svg_plugin or (magick_plugin_ok and magick_plugin))
|
||||
or require("image")
|
||||
|
||||
local cache_img_status, image_preload_err
|
||||
-- psd, ai, eps
|
||||
if mime == "adobe.photoshop" or job.mime == "application/postscript" then
|
||||
local layer_index = 0
|
||||
local units = get_state(STATE_KEY.units)
|
||||
if units ~= nil then
|
||||
local max_layer = image_layer_count(job)
|
||||
layer_index = math.floor(math.max(0, job.skip / units))
|
||||
if layer_index + 1 > max_layer then
|
||||
layer_index = max_layer - 1
|
||||
end
|
||||
end
|
||||
local cache_img_url_tmp = Url(cache_img_url .. ".tmp")
|
||||
if fs.cha(cache_img_url_tmp) then
|
||||
fs.remove("file", cache_img_url_tmp)
|
||||
end
|
||||
local tmp_file_path, _ = fs.unique_name(cache_img_url_tmp)
|
||||
cache_img_status, image_preload_err = magick_plugin
|
||||
.with_limit()
|
||||
:arg({
|
||||
"-background",
|
||||
"none",
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url)
|
||||
.. "["
|
||||
.. tostring(layer_index)
|
||||
.. "]",
|
||||
"-auto-orient",
|
||||
"-strip",
|
||||
"-resize",
|
||||
string.format("%dx%d>", rt.preview.max_width, rt.preview.max_height),
|
||||
"-quality",
|
||||
rt.preview.image_quality,
|
||||
string.format("PNG32:%s", tostring(tmp_file_path)),
|
||||
})
|
||||
:status()
|
||||
if cache_img_status then
|
||||
os.rename(tostring(tmp_file_path), tostring(cache_img_url))
|
||||
end
|
||||
elseif mime == "svg+xml" and not is_valid_utf8_path then
|
||||
local cache_img_url_tmp = Url(cache_img_url .. ".tmp")
|
||||
if fs.cha(cache_img_url_tmp) then
|
||||
fs.remove("file", cache_img_url_tmp)
|
||||
end
|
||||
local tmp_file_path, _ = fs.unique_name(cache_img_url_tmp)
|
||||
-- svg under invalid utf8 path
|
||||
cache_img_status, image_preload_err = magick_plugin
|
||||
.with_limit()
|
||||
:arg({
|
||||
"-background",
|
||||
"none",
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url),
|
||||
"-auto-orient",
|
||||
"-strip",
|
||||
"-flatten",
|
||||
"-resize",
|
||||
string.format("%dx%d>", rt.preview.max_width, rt.preview.max_height),
|
||||
"-quality",
|
||||
rt.preview.image_quality,
|
||||
string.format("PNG32:%s", tostring(tmp_file_path)),
|
||||
})
|
||||
:status()
|
||||
if cache_img_status then
|
||||
os.rename(tostring(tmp_file_path), tostring(cache_img_url))
|
||||
end
|
||||
else
|
||||
-- other image
|
||||
local no_skip_job = { skip = 0, file = job.file, args = {} }
|
||||
cache_img_status, image_preload_err = image_plugin:preload(no_skip_job)
|
||||
end
|
||||
if not cache_img_status then
|
||||
err_msg = err_msg .. (image_preload_err and (tostring(image_preload_err)) or "")
|
||||
end
|
||||
end
|
||||
end
|
||||
if is_adobe then
|
||||
return adobe:preload(job)
|
||||
elseif is_image then
|
||||
return image:preload(job)
|
||||
elseif is_video then
|
||||
return video:preload(job)
|
||||
elseif is_audio then
|
||||
return audio:preload(job)
|
||||
end
|
||||
local cache_mediainfo_cha = fs.cha(cache_mediainfo_url)
|
||||
if cache_mediainfo_cha and not job.args.force_reload_mediainfo then
|
||||
return true, err_msg ~= "" and ("Error: " .. err_msg) or nil
|
||||
end
|
||||
local cmd = "mediainfo"
|
||||
local output, err
|
||||
if is_valid_utf8_path then
|
||||
output, err = Command(cmd)
|
||||
:arg({ tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) })
|
||||
:output()
|
||||
else
|
||||
cmd = "cd "
|
||||
.. path_quote(job.file.path or job.file.cache or (job.file.url.path or job.file.url).parent)
|
||||
.. " && "
|
||||
.. cmd
|
||||
.. " "
|
||||
.. path_quote(tostring(job.file.path or job.file.cache or job.file.url.name))
|
||||
output, err = Command(SHELL)
|
||||
:arg({ "-c", cmd })
|
||||
:arg({ tostring(job.file.path or job.file.cache or (job.file.url.path or job.file.url)) })
|
||||
:output()
|
||||
end
|
||||
if err then
|
||||
err_msg = err_msg .. string.format("Failed to start `%s`, Do you have `%s` installed?\n", cmd, cmd)
|
||||
end
|
||||
return fs.write(
|
||||
cache_mediainfo_url,
|
||||
(err_msg ~= "" and ("Error: " .. err_msg) or "") .. (output and output.stdout or "")
|
||||
)
|
||||
end
|
||||
|
||||
function M:entry(job)
|
||||
local action = job.args[1]
|
||||
|
||||
if action == ENTRY_ACTION.toggle_metadata then
|
||||
set_state(STATE_KEY.hide_metadata, not get_state(STATE_KEY.hide_metadata))
|
||||
if action == const.ENTRY_ACTION.toggle_metadata then
|
||||
utils.set_state(const.STATE_KEY.hide_metadata, not utils.get_state(const.STATE_KEY.hide_metadata))
|
||||
ya.emit("peek", {
|
||||
force = true,
|
||||
})
|
||||
|
||||
41
config/yazi/plugins/mediainfo.yazi/utils.lua
Normal file
41
config/yazi/plugins/mediainfo.yazi/utils.lua
Normal file
@@ -0,0 +1,41 @@
|
||||
--- @since 26.1.22
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.is_valid_utf8(str)
|
||||
return utf8.len(str) ~= nil
|
||||
end
|
||||
|
||||
function M.path_quote(path)
|
||||
if not path or tostring(path) == "" then
|
||||
return path
|
||||
end
|
||||
local result = "'" .. string.gsub(tostring(path), "'", "'\\''") .. "'"
|
||||
return result
|
||||
end
|
||||
|
||||
function M.read_mediainfo_cached_file(file_path)
|
||||
-- Open the file in read mode
|
||||
local file = io.open(file_path, "r")
|
||||
|
||||
if file then
|
||||
-- Read the entire file content
|
||||
local content = file:read("*all")
|
||||
file:close()
|
||||
return content
|
||||
end
|
||||
end
|
||||
|
||||
M.force_render = ya.sync(function(_, _)
|
||||
(ui.render or ya.render)()
|
||||
end)
|
||||
|
||||
M.set_state = ya.sync(function(state, key, value)
|
||||
state[key] = value
|
||||
end)
|
||||
|
||||
M.get_state = ya.sync(function(state, key)
|
||||
return state[key]
|
||||
end)
|
||||
|
||||
return M
|
||||
222
config/yazi/plugins/mediainfo.yazi/video.lua
Normal file
222
config/yazi/plugins/mediainfo.yazi/video.lua
Normal file
@@ -0,0 +1,222 @@
|
||||
--- @since 26.1.22
|
||||
|
||||
local M = {}
|
||||
local const = require(".const")
|
||||
local utils = require(".utils")
|
||||
|
||||
function M:peek(job)
|
||||
local preload_status, preload_err = self:preload(job)
|
||||
-- Stop if preload failed
|
||||
if not preload_status then
|
||||
return
|
||||
end
|
||||
|
||||
local cache_img_url = ya.file_cache({
|
||||
skip = job.skip > 90 and 90 or job.skip,
|
||||
args = job.args,
|
||||
file = job.file,
|
||||
area = job.area,
|
||||
})
|
||||
local cache_img_url_no_skip = ya.file_cache({ file = job.file, skip = 0 })
|
||||
|
||||
local hide_metadata = utils.get_state(const.STATE_KEY.hide_metadata)
|
||||
local mediainfo_job_skip = job.skip
|
||||
::recalc_mediainfo_job_skip::
|
||||
local mediainfo_height = 0
|
||||
local lines = {}
|
||||
local limit = job.area.h
|
||||
local last_line = 0
|
||||
local EOF_mediainfo = true
|
||||
local is_wrap = rt.preview.wrap == "yes" or rt.preview.wrap == ui.Wrap.YES
|
||||
|
||||
if not hide_metadata then
|
||||
local cache_mediainfo_path = tostring(cache_img_url_no_skip) .. const.suffix
|
||||
local output = utils.read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
if output then
|
||||
local max_width = math.max(1, job.area.w)
|
||||
if output:match("^Error:") then
|
||||
job.args.force_reload_mediainfo = true
|
||||
preload_status, preload_err = self:preload(job)
|
||||
if not preload_status or preload_err then
|
||||
return
|
||||
end
|
||||
output = utils.read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
end
|
||||
|
||||
output = output:gsub("\n+$", "")
|
||||
|
||||
local iter = output:gmatch("[^\n]*")
|
||||
local str = iter()
|
||||
|
||||
while str ~= nil do
|
||||
local next_str = iter()
|
||||
local label, value = str:match("(.*[^ ]) +: (.*)")
|
||||
local line
|
||||
if label then
|
||||
if not const.skip_labels[label] then
|
||||
line = ui.Line({
|
||||
ui.Span(label .. ": "):style(ui.Style():fg("reset"):bold()),
|
||||
ui.Span(value):style(th.spot.tbl_col or ui.Style():fg("blue")),
|
||||
})
|
||||
end
|
||||
elseif str ~= "General" then
|
||||
line = ui.Line({ ui.Span(str):style(th.spot.title or ui.Style():fg("green")) })
|
||||
end
|
||||
|
||||
if line then
|
||||
local line_height = ui.height
|
||||
and ui.height(str, { width = max_width, ansi = true, wrap = rt.preview.wrap })
|
||||
or (math.max(1, is_wrap and math.ceil(ui.width(line) / max_width) or 1))
|
||||
if next_str == nil and line_height == 1 then
|
||||
EOF_mediainfo = true
|
||||
end
|
||||
if (last_line + line_height) > mediainfo_job_skip then
|
||||
table.insert(lines, line)
|
||||
end
|
||||
if (last_line + line_height) >= mediainfo_job_skip + limit then
|
||||
last_line = mediainfo_job_skip + limit
|
||||
EOF_mediainfo = false
|
||||
break
|
||||
end
|
||||
last_line = last_line + line_height
|
||||
end
|
||||
str = next_str
|
||||
end
|
||||
end
|
||||
mediainfo_height = math.min(limit, last_line)
|
||||
end
|
||||
|
||||
if not hide_metadata then
|
||||
if EOF_mediainfo and #lines == 0 and mediainfo_job_skip > 0 then
|
||||
if job.skip > 90 then
|
||||
ya.emit("peek", {
|
||||
math.max(0, (job.skip - (utils.get_state(const.STATE_KEY.units) or 0))),
|
||||
only_if = job.file.url,
|
||||
upper_bound = true,
|
||||
})
|
||||
return
|
||||
else
|
||||
-- NOTE: Recalculate mediainfo using cached latest valid skip value when reach the end of mediainfo output
|
||||
local last_valid_mediainfo_skip = utils.get_state(const.STATE_KEY.last_valid_mediainfo_skip)
|
||||
mediainfo_job_skip = last_valid_mediainfo_skip
|
||||
and last_valid_mediainfo_skip[tostring(cache_img_url_no_skip)]
|
||||
or math.max(0, mediainfo_job_skip - (utils.get_state(const.STATE_KEY.units) or 0))
|
||||
|
||||
goto recalc_mediainfo_job_skip
|
||||
end
|
||||
else
|
||||
utils.set_state(
|
||||
const.STATE_KEY.last_valid_mediainfo_skip,
|
||||
{ [tostring(cache_img_url_no_skip)] = mediainfo_job_skip }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
if utils.get_state(const.STATE_KEY.prev_metadata_area) then
|
||||
ya.preview_widget(job, {
|
||||
ui.Clear(ui.Rect(utils.get_state(const.STATE_KEY.prev_metadata_area))),
|
||||
})
|
||||
end
|
||||
|
||||
utils.force_render()
|
||||
local rendered_img_rect = cache_img_url
|
||||
and fs.cha(cache_img_url)
|
||||
and ya.image_show(
|
||||
cache_img_url,
|
||||
ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y,
|
||||
w = job.area.w,
|
||||
h = mediainfo_height > 0 and math.max(job.area.h - mediainfo_height, job.area.h / 2) or job.area.h,
|
||||
})
|
||||
)
|
||||
or nil
|
||||
local image_height = rendered_img_rect and rendered_img_rect.h or 0
|
||||
|
||||
-- NOTE: Workaround case video.lua doesn't doesn't generate preview image because of `skip` overflow video duration
|
||||
if not rendered_img_rect then
|
||||
local prev_image_height = utils.get_state(const.STATE_KEY.prev_image_height)
|
||||
image_height = prev_image_height and prev_image_height[tostring(cache_img_url_no_skip)] or 0
|
||||
else
|
||||
utils.set_state(const.STATE_KEY.prev_image_height, { [tostring(cache_img_url_no_skip)] = image_height })
|
||||
end
|
||||
|
||||
-- Handle image preload error
|
||||
if preload_err then
|
||||
table.insert(lines, ui.Line(tostring(preload_err)):style(th.spot.title or ui.Style():fg("red")))
|
||||
end
|
||||
|
||||
ya.preview_widget(job, {
|
||||
ui.Text(lines)
|
||||
:area(ui.Rect({
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
}))
|
||||
:wrap(is_wrap and ui.Wrap.YES or ui.Wrap.NO),
|
||||
})
|
||||
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
utils.set_state(const.STATE_KEY.prev_metadata_area, not hide_metadata and {
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
} or nil)
|
||||
end
|
||||
|
||||
function M:preload(job)
|
||||
local cmd = "mediainfo"
|
||||
local err_msg = ""
|
||||
|
||||
-- NOTE: Preload image from video
|
||||
|
||||
local cache_img_status, video_preload_err = require("video"):preload({
|
||||
skip = job.skip > 90 and 90 or job.skip,
|
||||
args = job.args,
|
||||
file = job.file,
|
||||
area = job.area,
|
||||
})
|
||||
|
||||
if not cache_img_status and video_preload_err then
|
||||
ya.dbg("mediainfo", video_preload_err)
|
||||
err_msg = err_msg .. string.format("Failed to start `%s`.\n Do you have `%s` installed?\n", "ffmpeg", "ffmpeg")
|
||||
end
|
||||
|
||||
-- NOTE: Get mediainfo and save to cache folder
|
||||
local cache_mediainfo_url = Url(tostring(ya.file_cache({ file = job.file, skip = 0 })) .. const.suffix)
|
||||
local cache_mediainfo_cha = fs.cha(cache_mediainfo_url)
|
||||
-- Case peek function called preload to refetch mediainfo
|
||||
if cache_mediainfo_cha and not job.args.force_reload_mediainfo then
|
||||
return true, err_msg ~= "" and ("Error: " .. err_msg) or nil
|
||||
end
|
||||
|
||||
local output, err
|
||||
local is_valid_utf8_path = utils.is_valid_utf8(tostring(job.file.path or job.file.cache or job.file.url))
|
||||
if is_valid_utf8_path then
|
||||
output, err = Command(cmd)
|
||||
:arg({ tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) })
|
||||
:output()
|
||||
else
|
||||
cmd = "cd "
|
||||
.. utils.path_quote(tostring((job.file.path or job.file.cache or job.file.url.path or job.file.url).parent))
|
||||
.. " && "
|
||||
.. cmd
|
||||
.. " "
|
||||
.. utils.path_quote(tostring((job.file.path or job.file.cache or job.file.url).name))
|
||||
output, err = Command(const.SHELL):arg({ "-c", cmd }):output()
|
||||
end
|
||||
if err then
|
||||
ya.dbg("mediainfo", tostring(err))
|
||||
err_msg = err_msg .. string.format("Failed to start `%s`. \n Do you have `%s` installed?\n", cmd, cmd)
|
||||
end
|
||||
|
||||
return fs.write(
|
||||
cache_mediainfo_url,
|
||||
(err_msg ~= "" and ("Error: " .. err_msg) or "") .. (output and output.stdout or "")
|
||||
)
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -25,7 +25,7 @@ on = "M"
|
||||
run = "plugin mount"
|
||||
```
|
||||
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other commands/plugins.
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other actions/plugins.
|
||||
|
||||
## Actions
|
||||
|
||||
|
||||
66
config/yazi/plugins/mount.yazi/cross.lua
Normal file
66
config/yazi/plugins/mount.yazi/cross.lua
Normal file
@@ -0,0 +1,66 @@
|
||||
local M = {}
|
||||
|
||||
--- @param type "mount"|"unmount"|"eject"
|
||||
--- @param partition table
|
||||
function M.operate(type, partition)
|
||||
if not partition then
|
||||
return
|
||||
elseif not partition.sub then
|
||||
return -- TODO: mount/unmount main disk
|
||||
end
|
||||
|
||||
local cmd, output, err
|
||||
if ya.target_os() == "macos" then
|
||||
cmd, output, err = "diskutil", M.diskutil(type, partition.src)
|
||||
elseif ya.target_os() == "linux" then
|
||||
if type == "eject" and partition.src:match("^/dev/sr%d+") then
|
||||
M.udisksctl("unmount", partition.src)
|
||||
cmd, output, err = "eject", M.eject(partition.src)
|
||||
elseif type == "eject" then
|
||||
M.udisksctl("unmount", partition.src)
|
||||
cmd, output, err = "udisksctl", M.udisksctl("power-off", partition.src)
|
||||
else
|
||||
cmd, output, err = "udisksctl", M.udisksctl(type, partition.src)
|
||||
end
|
||||
end
|
||||
|
||||
if not cmd then
|
||||
M.fail("mount.yazi is not currently supported on your platform")
|
||||
elseif not output then
|
||||
M.fail("Failed to spawn `%s`: %s", cmd, err)
|
||||
elseif not output.status.success then
|
||||
M.fail("Failed to %s `%s`: %s", type, partition.src, output.stderr)
|
||||
end
|
||||
end
|
||||
|
||||
--- @param type "mount"|"unmount"|"eject"
|
||||
--- @param src string
|
||||
--- @return Output? output
|
||||
--- @return Error? err
|
||||
function M.diskutil(type, src) return Command("diskutil"):arg({ type, src }):output() end
|
||||
|
||||
--- @param type "mount"|"unmount"|"power-off"
|
||||
--- @param src string
|
||||
--- @return Output? output
|
||||
--- @return Error? err
|
||||
function M.udisksctl(type, src)
|
||||
local args = { type, "-b", src, "--no-user-interaction" }
|
||||
local output, err = Command("udisksctl"):arg(args):output()
|
||||
|
||||
if not output or err then
|
||||
return nil, err
|
||||
elseif output.stderr:find("org.freedesktop.UDisks2.Error.NotAuthorizedCanObtain", 1, true) then
|
||||
return require(".sudo").run_with_sudo("udisksctl", args)
|
||||
else
|
||||
return output
|
||||
end
|
||||
end
|
||||
|
||||
--- @param src string
|
||||
--- @return Output? output
|
||||
--- @return Error? err
|
||||
function M.eject(src) return Command("eject"):arg({ "--traytoggle", src }):output() end
|
||||
|
||||
function M.fail(...) ya.notify { title = "Mount", content = string.format(...), timeout = 10, level = "error" } end
|
||||
|
||||
return M
|
||||
@@ -129,11 +129,11 @@ function M:entry(job)
|
||||
if run == "quit" then
|
||||
break
|
||||
elseif run == "mount" then
|
||||
self.operate("mount")
|
||||
require(".cross").operate("mount", active_partition())
|
||||
elseif run == "unmount" then
|
||||
self.operate("unmount")
|
||||
require(".cross").operate("unmount", active_partition())
|
||||
elseif run == "eject" then
|
||||
self.operate("eject")
|
||||
require(".cross").operate("eject", active_partition())
|
||||
end
|
||||
until not run
|
||||
end
|
||||
@@ -249,48 +249,6 @@ function M.fillin(tbl)
|
||||
return tbl
|
||||
end
|
||||
|
||||
function M.operate(type)
|
||||
local active = active_partition()
|
||||
if not active then
|
||||
return
|
||||
elseif not active.sub then
|
||||
return -- TODO: mount/unmount main disk
|
||||
end
|
||||
|
||||
local cmd
|
||||
if ya.target_os() == "macos" then
|
||||
cmd = Command("diskutil"):arg { type, active.src }
|
||||
end
|
||||
if ya.target_os() == "linux" then
|
||||
if type == "eject" and active.src:match("^/dev/sr%d+") then
|
||||
Command("udisksctl"):arg({ "unmount", "-b", active.src }):status()
|
||||
cmd = Command("eject"):arg { "--traytoggle", active.src }
|
||||
elseif type == "eject" then
|
||||
Command("udisksctl"):arg({ "unmount", "-b", active.src }):status()
|
||||
cmd = Command("udisksctl"):arg { "power-off", "-b", active.src }
|
||||
else
|
||||
cmd = Command("udisksctl"):arg { type, "-b", active.src }
|
||||
end
|
||||
end
|
||||
|
||||
if not cmd then
|
||||
return M.fail("mount.yazi is not currently supported on your platform")
|
||||
end
|
||||
|
||||
local output, err = cmd:output()
|
||||
if not output then
|
||||
if cmd.program then
|
||||
M.fail("Failed to spawn `%s`: %s", cmd.program, err)
|
||||
else
|
||||
M.fail("Failed to spawn `udisksctl`: %s", err) -- TODO: remove
|
||||
end
|
||||
elseif not output.status.success then
|
||||
M.fail("Failed to %s `%s`: %s", type, active.src, output.stderr)
|
||||
end
|
||||
end
|
||||
|
||||
function M.fail(...) ya.notify { title = "Mount", content = string.format(...), timeout = 10, level = "error" } end
|
||||
|
||||
function M:click() end
|
||||
|
||||
function M:scroll() end
|
||||
|
||||
54
config/yazi/plugins/mount.yazi/sudo.lua
Normal file
54
config/yazi/plugins/mount.yazi/sudo.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
local M = {}
|
||||
|
||||
--- Verify if `sudo` is already authenticated
|
||||
--- @return boolean
|
||||
--- @return Error?
|
||||
function M.sudo_already()
|
||||
local status, err = Command("sudo"):arg({ "--validate", "--non-interactive" }):status()
|
||||
return status and status.success or false, err
|
||||
end
|
||||
|
||||
--- Run a program with `sudo` privilege
|
||||
--- @param program string
|
||||
--- @param args table
|
||||
--- @return Output? output
|
||||
--- @return Error? err
|
||||
function M.run_with_sudo(program, args)
|
||||
local cmd = Command("sudo")
|
||||
:arg({ "--stdin", "--user", "#" .. ya.uid(), "--", program })
|
||||
:arg(args)
|
||||
:stdin(Command.PIPED)
|
||||
:stdout(Command.PIPED)
|
||||
:stderr(Command.PIPED)
|
||||
|
||||
if M.sudo_already() then
|
||||
return cmd:output()
|
||||
end
|
||||
|
||||
local value, event = ya.input {
|
||||
pos = { "top-center", y = 3, w = 40 },
|
||||
title = string.format("Password for `sudo %s`:", program),
|
||||
obscure = true,
|
||||
}
|
||||
if not value or event ~= 1 then
|
||||
return nil, Err("Sudo password input cancelled")
|
||||
end
|
||||
|
||||
local child, err = cmd:spawn()
|
||||
if not child or err then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
child:write_all(value .. "\n")
|
||||
child:flush()
|
||||
local output, err = child:wait_with_output()
|
||||
if not output or err then
|
||||
return nil, err
|
||||
elseif output.status.success or M.sudo_already() then
|
||||
return output
|
||||
else
|
||||
return nil, Err("Incorrect sudo password")
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -1,3 +1,35 @@
|
||||
|
||||
<div align="right">
|
||||
<details>
|
||||
<summary >🌐 Language</summary>
|
||||
<div>
|
||||
<div align="center">
|
||||
<a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=en">English</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=zh-CN">简体中文</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=zh-TW">繁體中文</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=ja">日本語</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=ko">한국어</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=hi">हिन्दी</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=th">ไทย</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=fr">Français</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=de">Deutsch</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=es">Español</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=it">Italiano</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=ru">Русский</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=pt">Português</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=nl">Nederlands</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=pl">Polski</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=ar">العربية</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=fa">فارسی</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=tr">Türkçe</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=vi">Tiếng Việt</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=id">Bahasa Indonesia</a>
|
||||
| <a href="https://openaitx.github.io/view.html?user=ndtoan96&project=ouch.yazi&lang=as">অসমীয়া</
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
# ouch.yazi
|
||||
|
||||
[ouch](https://github.com/ouch-org/ouch) plugin for [Yazi](https://github.com/sxyazi/yazi).
|
||||
|
||||
@@ -2,6 +2,7 @@ local M = {}
|
||||
|
||||
function M:peek(job)
|
||||
local child = Command("rich")
|
||||
:env("COLUMNS", tostring(job.area.w))
|
||||
:arg({
|
||||
"-j",
|
||||
"--left",
|
||||
|
||||
@@ -21,7 +21,7 @@ run = "plugin smart-filter"
|
||||
desc = "Smart filter"
|
||||
```
|
||||
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other commands/plugins.
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other actions/plugins.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
Reference in New Issue
Block a user