This commit is contained in:
2026-04-14 22:57:32 +02:00
parent 7f41488da6
commit 1f52782226
33 changed files with 168 additions and 85 deletions
+35 -27
View File
@@ -2,48 +2,56 @@ theme = "catppuccin_mocha"
[editor]
default-yank-register = "+"
shell = ["bash", "-c"]
line-number = "relative"
indent-guides.render = true
shell = ["bash", "-c"]
line-number = "relative"
indent-guides.render = true
trim-trailing-whitespace = true
[editor.cursor-shape]
insert = "bar"
normal = "block"
select = "underline"
[editor.whitespace.render]
space = "none"
tab = "all"
nbsp = "none"
nnbsp = "none"
newline = "none"
[keys.normal]
# "esc" = "collapse_selection"
"-" = "collapse_selection"
"-" = "collapse_selection"
"A-minus" = "flip_selections"
"#" = "switch_to_lowercase"
"A-#" = "switch_to_uppercase"
"#" = "switch_to_lowercase"
"A-#" = "switch_to_uppercase"
"+" = "select_register"
# Previously "["
[keys.normal."ö"]
"d" = "goto_prev_diag"
"D" = "goto_first_diag"
"f" = "goto_prev_function"
"t" = "goto_prev_class"
"a" = "goto_prev_parameter"
"c" = "goto_prev_comment"
"T" = "goto_prev_test"
"p" = "goto_prev_paragraph"
"g" = "goto_prev_change"
"G" = "goto_first_change"
"d" = "goto_prev_diag"
"D" = "goto_first_diag"
"f" = "goto_prev_function"
"t" = "goto_prev_class"
"a" = "goto_prev_parameter"
"c" = "goto_prev_comment"
"T" = "goto_prev_test"
"p" = "goto_prev_paragraph"
"g" = "goto_prev_change"
"G" = "goto_first_change"
"space" = "add_newline_above"
# Previously "]"
[keys.normal."ä"]
"d" = "goto_next_diag"
"D" = "goto_last_diag"
"f" = "goto_next_function"
"t" = "goto_next_class"
"a" = "goto_next_parameter"
"c" = "goto_next_comment"
"T" = "goto_next_test"
"p" = "goto_next_paragraph"
"g" = "goto_next_change"
"G" = "goto_last_change"
"d" = "goto_next_diag"
"D" = "goto_last_diag"
"f" = "goto_next_function"
"t" = "goto_next_class"
"a" = "goto_next_parameter"
"c" = "goto_next_comment"
"T" = "goto_next_test"
"p" = "goto_next_paragraph"
"g" = "goto_next_change"
"G" = "goto_last_change"
"space" = "add_newline_below"
# [keys.insert."j"]
@@ -1,13 +1,7 @@
[[language]]
name = "c"
auto-format = true
indent = { tab-width = 4, unit = " " }
[[language]]
name = "cpp"
auto-format = true
indent = { tab-width = 4, unit = " " }
[[language]]
name = "toml"
auto-format = true
+5 -5
View File
@@ -36,13 +36,13 @@ binds {
Mod+X repeat=false { spawn "qs" "ipc" "call" "notes" "openRecent"; }
Mod+Shift+X repeat=false { spawn "qs" "ipc" "call" "notes" "create"; }
// Rofi
Mod+D repeat=false { spawn-sh "pkill -x rofi || rofi -show run"; }
Alt+Space repeat=false { spawn-sh "pkill -x rofi || rofi -show drun"; }
// Launcher
Alt+Space repeat=false { spawn "vicinae" "toggle"; }
Mod+V repeat=false { spawn "vicinae" "vicinae://launch/clipboard/history?toggle=true"; }
Mod+Period repeat=false { spawn "vicinae" "vicinae://launch/core/search-emojis?toggle=true"; }
Mod+D repeat=false { spawn "vicinae" "vicinae://launch/system/run?toggle=true"; }
// Actions
Mod+V repeat=false { spawn-sh "ghostty +new-window -e fzfclip-wrap"; }
Mod+Period repeat=false { spawn-sh "pkill -x rofi || rofi-emoji"; }
Print repeat=false { screenshot-screen; }
Mod+Shift+S repeat=false { screenshot; }
Mod+Ctrl+Shift+S repeat=false { screenshot-window; }
+2 -14
View File
@@ -1,21 +1,11 @@
// Switch configs
spawn-at-startup "config-switch" "niri"
// Not necessary maybe ...
spawn-at-startup "fcitx5"
// Core
spawn-at-startup "nm-applet"
spawn-at-startup "gnome-keyring-daemon" "--start" "--components=secrets"
spawn-at-startup "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1"
// Clipboard history
spawn-at-startup "wl-paste" "--type" "text" "--watch" "cliphist" "store"
spawn-at-startup "wl-paste" "--type" "image" "--watch" "cliphist" "store"
// wlsunset
// spawn-at-startup "sunset" // managed by quickshell
// Logitech
spawn-at-startup "solaar" "-w" "hide"
@@ -30,7 +20,5 @@ spawn-at-startup "hypridle"
// QuickShell
spawn-at-startup "quickshell"
// According to (https://ghostty.org/docs/linux/systemd#starting-ghostty-at-login)
// spawn-sh-at-startup "systemctl start --user app-com.mitchellh.ghostty.service"
//
// spawn-at-startup "flatpak" "run" "com.gopeed.Gopeed" "--hidden"
// Vicinae
spawn-at-startup "vicinae" "server"
+1
View File
@@ -2,6 +2,7 @@ screenshot-path "~/Pictures/Screenshots/niri_screenshot_%Y-%m-%d_%H-%M-%S.png"
debug {
render-drm-device "/dev/dri/renderD129"
honor-xdg-activation-with-invalid-serial
}
// gestures {
-9
View File
@@ -1,9 +0,0 @@
tt.*
.tests
doc/tags
debug
.repro
foo.*
*.log
data
lazy-lock.json
-15
View File
@@ -1,15 +0,0 @@
{
"neodev": {
"library": {
"enabled": true,
"plugins": true
}
},
"neoconf": {
"plugins": {
"lua_ls": {
"enabled": true
}
}
}
}
-2
View File
@@ -1,2 +0,0 @@
-- bootstrap lazy.nvim, LazyVim and your plugins
require("config.lazy")
-18
View File
@@ -1,18 +0,0 @@
{
"extras": [
"lazyvim.plugins.extras.lang.clangd",
"lazyvim.plugins.extras.lang.cmake",
"lazyvim.plugins.extras.lang.git",
"lazyvim.plugins.extras.lang.go",
"lazyvim.plugins.extras.lang.json",
"lazyvim.plugins.extras.lang.markdown",
"lazyvim.plugins.extras.lang.python",
"lazyvim.plugins.extras.lang.typescript",
"lazyvim.plugins.extras.util.mini-hipatterns"
],
"install_version": 7,
"news": {
"NEWS.md": "11866"
},
"version": 8
}
@@ -1,10 +0,0 @@
-- Autocmds are automatically loaded on the VeryLazy event
-- Default autocmds that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/autocmds.lua
--
-- Add any additional autocmds here
-- with `vim.api.nvim_create_autocmd`
--
-- Or remove existing autocmds by their group name (which is prefixed with `lazyvim_` for the defaults)
-- e.g. vim.api.nvim_del_augroup_by_name("lazyvim_wrap_spell")
vim.api.nvim_del_augroup_by_name("lazyvim_wrap_spell")
@@ -1,16 +0,0 @@
-- Keymaps are automatically loaded on the VeryLazy event
-- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua
-- Add any additional keymaps here
local km = vim.keymap
local map = vim.keymap.set
-- split windows horizontally and vertically
map("n", "<leader>sv", "<C-w>v")
map("n", "<leader>sh", "<C-w>s")
-- no highlight
map("n", "<leader>nh", ":nohl<CR>")
map("i", "jk", "<ESC>")
@@ -1,53 +0,0 @@
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)
require("lazy").setup({
spec = {
-- add LazyVim and import its plugins
{ "LazyVim/LazyVim", import = "lazyvim.plugins" },
-- import/override with your plugins
{ import = "plugins" },
},
defaults = {
-- By default, only LazyVim plugins will be lazy-loaded. Your custom plugins will load during startup.
-- If you know what you're doing, you can set this to `true` to have all your custom plugins lazy-loaded by default.
lazy = false,
-- It's recommended to leave version=false for now, since a lot the plugin that support versioning,
-- have outdated releases, which may break your Neovim install.
version = false, -- always use the latest git commit
-- version = "*", -- try installing the latest stable version for plugins that support semver
},
install = { colorscheme = { "tokyonight", "habamax" } },
checker = {
enabled = true, -- check for plugin updates periodically
notify = false, -- notify on update
}, -- automatically check for plugin updates
performance = {
rtp = {
-- disable some rtp plugins
disabled_plugins = {
"gzip",
-- "matchit",
-- "matchparen",
-- "netrwPlugin",
"tarPlugin",
"tohtml",
"tutor",
"zipPlugin",
},
},
},
})
@@ -1,11 +0,0 @@
-- Options are automatically loaded before lazy.nvim startup
-- Default options that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/options.lua
-- Add any additional options before
-- vim.opt.relativenumber = false
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.expandtab = true
vim.opt.autoindent = true
@@ -1,51 +0,0 @@
return {
{
"catppuccin/nvim",
opts = {
flavor = "mocha",
transparent_background = true,
styles = {
functions = { "bold" },
keywords = { "bold" },
},
},
},
-- {
-- "olimorris/onedarkpro.nvim",
-- priority = 1000, -- Ensure it loads first
-- config = function()
-- require("onedarkpro").setup({
-- options = {
-- transparency = true,
-- },
-- styles = {
-- comments = "italic",
-- keywords = "bold",
-- },
-- })
-- require("onedarkpro").load()
-- end,
-- },
{
"LazyVim/LazyVim",
opts = {
colorscheme = "catppuccin",
},
},
}
-- return {
-- {
-- "folke/tokyonight.nvim",
-- lazy = true,
-- opts = {
-- style = "moon",
-- },
-- },
-- {
-- "LazyVim/LazyVim",
-- opts = {
-- colorscheme = "tokyonight",
-- },
-- },
-- }
@@ -1,197 +0,0 @@
-- since this is just an example spec, don't actually load anything here and return an empty spec
-- stylua: ignore
if true then return {} end
-- every spec file under the "plugins" directory will be loaded automatically by lazy.nvim
--
-- In your plugin files, you can:
-- * add extra plugins
-- * disable/enabled LazyVim plugins
-- * override the configuration of LazyVim plugins
return {
-- add gruvbox
{ "ellisonleao/gruvbox.nvim" },
-- Configure LazyVim to load gruvbox
{
"LazyVim/LazyVim",
opts = {
colorscheme = "gruvbox",
},
},
-- change trouble config
{
"folke/trouble.nvim",
-- opts will be merged with the parent spec
opts = { use_diagnostic_signs = true },
},
-- disable trouble
{ "folke/trouble.nvim", enabled = false },
-- override nvim-cmp and add cmp-emoji
{
"hrsh7th/nvim-cmp",
dependencies = { "hrsh7th/cmp-emoji" },
---@param opts cmp.ConfigSchema
opts = function(_, opts)
table.insert(opts.sources, { name = "emoji" })
end,
},
-- change some telescope options and a keymap to browse plugin files
{
"nvim-telescope/telescope.nvim",
keys = {
-- add a keymap to browse plugin files
-- stylua: ignore
{
"<leader>fp",
function() require("telescope.builtin").find_files({ cwd = require("lazy.core.config").options.root }) end,
desc = "Find Plugin File",
},
},
-- change some options
opts = {
defaults = {
layout_strategy = "horizontal",
layout_config = { prompt_position = "top" },
sorting_strategy = "ascending",
winblend = 0,
},
},
},
-- add pyright to lspconfig
{
"neovim/nvim-lspconfig",
---@class PluginLspOpts
opts = {
---@type lspconfig.options
servers = {
-- pyright will be automatically installed with mason and loaded with lspconfig
pyright = {},
},
},
},
-- add tsserver and setup with typescript.nvim instead of lspconfig
{
"neovim/nvim-lspconfig",
dependencies = {
"jose-elias-alvarez/typescript.nvim",
init = function()
require("lazyvim.util").lsp.on_attach(function(_, buffer)
-- stylua: ignore
vim.keymap.set( "n", "<leader>co", "TypescriptOrganizeImports", { buffer = buffer, desc = "Organize Imports" })
vim.keymap.set("n", "<leader>cR", "TypescriptRenameFile", { desc = "Rename File", buffer = buffer })
end)
end,
},
---@class PluginLspOpts
opts = {
---@type lspconfig.options
servers = {
-- tsserver will be automatically installed with mason and loaded with lspconfig
tsserver = {},
},
-- you can do any additional lsp server setup here
-- return true if you don't want this server to be setup with lspconfig
---@type table<string, fun(server:string, opts:_.lspconfig.options):boolean?>
setup = {
-- example to setup with typescript.nvim
tsserver = function(_, opts)
require("typescript").setup({ server = opts })
return true
end,
-- Specify * to use this function as a fallback for any server
-- ["*"] = function(server, opts) end,
},
},
},
-- for typescript, LazyVim also includes extra specs to properly setup lspconfig,
-- treesitter, mason and typescript.nvim. So instead of the above, you can use:
{ import = "lazyvim.plugins.extras.lang.typescript" },
-- add more treesitter parsers
{
"nvim-treesitter/nvim-treesitter",
opts = {
ensure_installed = {
"bash",
"html",
"javascript",
"json",
"lua",
"markdown",
"markdown_inline",
"python",
"query",
"regex",
"tsx",
"typescript",
"vim",
"yaml",
},
},
},
-- since `vim.tbl_deep_extend`, can only merge tables and not lists, the code above
-- would overwrite `ensure_installed` with the new value.
-- If you'd rather extend the default config, use the code below instead:
{
"nvim-treesitter/nvim-treesitter",
opts = function(_, opts)
-- add tsx and treesitter
vim.list_extend(opts.ensure_installed, {
"tsx",
"typescript",
})
end,
},
-- the opts function can also be used to change the default opts:
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
opts = function(_, opts)
table.insert(opts.sections.lualine_x, {
function()
return "😄"
end,
})
end,
},
-- or you can return new options to override all the defaults
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
opts = function()
return {
--[[add your custom lualine config here]]
}
end,
},
-- use mini.starter instead of alpha
{ import = "lazyvim.plugins.extras.ui.mini-starter" },
-- add jsonls and schemastore packages, and setup treesitter for json, json5 and jsonc
{ import = "lazyvim.plugins.extras.lang.json" },
-- add any tools you want to have installed below
{
"williamboman/mason.nvim",
opts = {
ensure_installed = {
"stylua",
"shellcheck",
"shfmt",
"flake8",
},
},
},
}
@@ -1,14 +0,0 @@
return {
{
'mikesmithgh/kitty-scrollback.nvim',
enabled = true,
lazy = true,
cmd = { 'KittyScrollbackGenerateKittens', 'KittyScrollbackCheckHealth' },
event = { 'User KittyScrollbackLaunch' },
config = function()
vim.keymap.set({ 'n' }, '<Esc>', '<Plug>(KsbCloseOrQuitAll)', {}) -- quit kitty-scrollback.nvim with Esc key
-- vim.keymap.set({ 'n' }, 'q', '<Plug>(KsbCloseOrQuitAll)', {}) -- uncomment if you would like to also quit with the q key
require('kitty-scrollback').setup()
end,
},
}
@@ -1,16 +0,0 @@
return {
"snacks.nvim",
opts = {
dashboard = {
preset = {
header = [[
███╗ ██╗███████╗ ██████╗ ██╗ ██╗██╗███╗ ███╗
████╗ ██║██╔════╝██╔═══██╗██║ ██║██║████╗ ████║
██╔██╗ ██║█████╗ ██║ ██║██║ ██║██║██╔████╔██║
██║╚██╗██║██╔══╝ ██║ ██║╚██╗ ██╔╝██║██║╚██╔╝██║
██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║
╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═══╝ ╚═╝╚═╝ ╚═╝]],
},
},
},
}
@@ -1,18 +0,0 @@
return {
"nvim-neo-tree/neo-tree.nvim",
opts = {
filesystem = {
filtered_items = {
hide_dotfiles = false, -- show dotfiles
hide_gitignored = false, -- show gitignored files
},
follow_current_file = {
enabled = true, -- focus on the current file in the tree
},
use_libuv_file_watcher = true, -- use libuv for file watching
},
window = {
width = 25,
},
},
}
-3
View File
@@ -1,3 +0,0 @@
indent_type = "Spaces"
indent_width = 2
column_width = 120
+2 -1
View File
@@ -12,8 +12,9 @@ gtk-enable-input-feedback-sounds=0
gtk-font-name=Sarasa UI SC, 10
gtk-icon-theme-name=Papirus
gtk-menu-images=true
gtk-modules=colorreload-gtk-module
gtk-modules=colorreload-gtk-module:appmenu-gtk-module
gtk-primary-button-warps-slider=true
gtk-shell-shows-menubar=1
gtk-sound-theme-name=ocean
gtk-theme-name=catppuccin-mocha-blue-standard+default
gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR
@@ -22,7 +22,7 @@ Text {
id: action
running: false
command: ["rofi", "-show", "drun"]
command: ["vicinae", "toggle"]
}
}
@@ -39,7 +39,7 @@ Singleton {
function openNote(path) {
recentNotePath = path;
Quickshell.execDetached(["ghostty", "+new-window", "-e", "nvim", path]);
Quickshell.execDetached(["ghostty", "+new-window", "-e", "helix", path]);
}
function openRecent() {
-21
View File
@@ -1,21 +0,0 @@
#!/bin/bash
[ -f "$HOME/.local/snippets/apply-color-helper" ] || {
echo "Missing helper script: $HOME/.local/snippets/apply-color-helper"
exit 1
}
. "$HOME/.local/snippets/apply-color-helper"
file="$path"/config.rasi
if pgrep -x "rofi" -u "$USER" >/dev/null; then
pkill -x rofi || log_error "Failed to kill rofi process"
# and move on
fi
sed -i -E "s/^(\s*primary:\s*#)([0-9A-Fa-f]{6})(;)/\1${colorHex}\3/" "$file" || {
log_error "Failed to edit ${file}"
exit 1
}
log_success "rofi"
-229
View File
@@ -1,229 +0,0 @@
/*****----- ALL -----*****/
* {
font: "Maple Mono NF CN Semibold 10";
base: #1e1e2e;
mantle: #181825;
text: #cdd6f4;
subtext0: #a6adc8;
surface2: #585b70;
surface: #292a3c;
red: #f38ba8;
green: #a6e3a1;
yellow: #f9e2af;
orange: #fab387;
blue: #89b4fa;
purple: #cba6f7;
cyan: #89dceb;
lavender: #b4befe;
sapphire: #74c7ec;
teal: #94e2d5;
peach: #fab387;
search: rgba(49, 50, 68, 0.5); // alpha(@surface, 0.5)
primary: #89b4fa;
}
/*****----- Configuration -----*****/
configuration {
terminal: "/usr/bin/kitty";
modi: "drun,run,ssh";
show-icons: true;
display-drun: "";
display-run: "";
display-ssh: "";
drun-display-format: "{name}";
}
/*****----- Main Window -----*****/
window {
/* properties for window widget */
transparency: "real";
location: center;
anchor: center;
fullscreen: false;
width: 600px;
x-offset: 0px;
y-offset: 0px;
/* properties for all widgets */
enabled: true;
margin: 0px;
padding: 0px;
border: 2px solid;
border-radius: 24px;
border-color: @primary;
cursor: "default";
background-color: @base;
}
/*****----- Main Box -----*****/
mainbox {
enabled: true;
spacing: 0px;
margin: 0px;
padding: 30px;
background-color: transparent;
children: [ "inputbar", "message", "listview", "mode-switcher" ];
}
/*****----- Inputbar -----*****/
inputbar {
enabled: true;
spacing: 0px;
padding: 75px 30px;
border-radius: 14px 14px 0 0;
background-color: transparent;
background-image: url("~/Pictures/backgrounds/nao-stars-crop-adjust-flop.jpg", width);
text-color: @text;
children: [ "textbox-prompt-colon", "entry" ];
}
prompt {
enabled: true;
background-color: inherit;
text-color: inherit;
}
textbox-prompt-colon {
enabled: true;
padding: 12px 15px;
expand: false;
str: " ";
background-color: @search;
border-radius: 14px;
text-color: inherit;
}
entry {
enabled: true;
padding: 12px 16px;
text-color: inherit;
cursor: text;
background-color: @search;
border-radius: 14px;
placeholder: "Search...";
placeholder-color: inherit;
}
/*****----- Listview -----*****/
listview {
enabled: true;
columns: 2;
lines: 6;
cycle: true;
dynamic: true;
scrollbar: true;
layout: vertical;
reverse: false;
fixed-height: true;
fixed-columns: true;
padding: 14px;
spacing: 5px;
background-color: @surface;
text-color: @text;
cursor: "default";
}
scrollbar {
handle-width: 5px ;
handle-color: @primary;
border-radius: 10px;
background-color: @mantle;
}
/*****----- Elements -----*****/
element {
enabled: true;
spacing: 10px;
margin: 0px;
padding: 6px;
border-radius: 0px;
background-color: transparent;
text-color: @text;
cursor: pointer;
}
element normal.normal,
element alternate.normal {
background-color: transparent;
text-color: @text;
}
element normal.urgent,
element alternate.urgent,
element selected.active {
background-color: @primary;
text-color: @base;
border-radius: 10px;
}
element normal.active,
element alternate.active,
element selected.urgent {
background-color: @red;
text-color: @base;
}
element selected.normal {
background-color: @surface2;
text-color: @text;
border-radius: 10px;
}
element-icon {
background-color: transparent;
text-color: inherit;
size: 24px;
cursor: inherit;
}
element-text {
background-color: transparent;
text-color: inherit;
highlight: inherit;
cursor: inherit;
vertical-align: 0.5;
horizontal-align: 0.0;
}
/*****----- Mode Switcher -----*****/
mode-switcher{
enabled: true;
spacing: 10px;
margin: 0px;
padding: 0px;
background-color: transparent;
text-color: @text;
}
button {
padding: 10px;
border-radius: 10px;
background-color: @base;
text-color: inherit;
cursor: pointer;
}
button selected {
background-color: @surface;
text-color: @primary;
border-radius: 0 0 14px 14px;
border-color: @primary;
}
/*****----- Message -----*****/
message {
enabled: true;
margin: 0px;
padding: 10px;
border-radius: 0px;
background-color: @mantle;
text-color: @text;
}
textbox {
background-color: transparent;
text-color: @text;
vertical-align: 0.5;
horizontal-align: 0.0;
highlight: none;
placeholder-color: @text;
blink: true;
markup: true;
}
error-message {
padding: 30px;
background-color: @base;
text-color: @text;
}
-20
View File
@@ -1,20 +0,0 @@
@import "config.rasi"
configuration {
modi: "";
}
window {
width: 450px;
children: [ "inputbar", "message", "listview" ];
}
inputbar {
padding: 60px 30px;
}
listview {
columns: 1;
lines: 10;
border-radius: 0 0 14px 14px;
}
-453
View File
@@ -1,453 +0,0 @@
#!/usr/bin/env bash
# Description:
# View and manage clipboard history using fzf, with support for
# image preview in compatible terminals.
# Requirements:
# - fzf
# - cliphist
# - wl-clipboard (including wl-copy and wl-paste)
# - python3 with urllib (for URL quoting/unquoting)
# - chafa (optional, for image preview)
# - ffmpegthumbnailer (optional, for video thumbnails)
# Credits:
# - Original idea and some code adapted from https://github.com/SHORiN-KiWATA/shorinclip
# License:
# # MIT License
# #
# # Copyright (c) 2026 shorinkiwata
# #
# # Permission is hereby granted, free of charge, to any person obtaining a copy
# # of this software and associated documentation files (the "Software"), to deal
# # in the Software without restriction, including without limitation the rights
# # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# # copies of the Software, and to permit persons to whom the Software is
# # furnished to do so, subject to the following conditions:
# #
# # The above copyright notice and this permission notice shall be included in all
# # copies or substantial portions of the Software.
# #
# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# # SOFTWARE.
set -euo pipefail
SHELL=$(command -v bash)
export SHELL
_cleanup() {
if [ -n "${CACHE_DIR:-}" ] && [ -d "$CACHE_DIR" ]; then
rm -rf "$CACHE_DIR"
fi
if [ -n "${WATCH_PID:-}" ]; then
kill "$WATCH_PID" 2>/dev/null || true
fi
if [ -n "${FZF_SOCKET:-}" ] && [ -S "$FZF_SOCKET" ]; then
rm -f "$FZF_SOCKET"
fi
}
trap _cleanup EXIT
_check_dependencies() {
local missing=()
for cmd in fzf cliphist wl-copy wl-paste python3; do
if ! type "$cmd" &>/dev/null; then
missing+=("$cmd")
fi
done
if [ ${#missing[@]} -ne 0 ]; then
echo "Error: Missing dependencies: ${missing[*]}" >&2
exit 1
fi
for cmd in chafa ffmpegthumbnailer; do
if ! type "$cmd" &>/dev/null; then
echo "Warning: Optional dependency '$cmd' not found. Some features may be unavailable." >&2
fi
done
}
_check_dependencies
CACHE_DIR=$(mktemp -d)
export CACHE_DIR
export C_TERTIARY='\x1b[1;35m'
export C_PRIMARY='\x1b[1;34m'
export C_CYAN='\x1b[1;36m'
export C_RESET='\x1b[0m'
export C_PATTERN='\x1b\[[0-9];?([0-9]+)?m'
# Check for terminal graphics support and set environment variables accordingly
_graphics_query() {
# Port of [graphics-query](https://github.com/Uyanide/dotfiles/blob/main/config/scripts/.local/scripts/graphics-query)
# Ensure in a interactive terminal
[ ! -t 0 ] && return
(
# Construct query
KGP_QUERY_ID=$RANDOM
KGP_QUERY_CODE=$(printf "\033_Gi=%d,s=1,v=1,a=q,t=d,f=24;AAAA\033\\" "$KGP_QUERY_ID")
ITERM2_QUERY_CODE=$(printf "\033]1337;ReportCellSize\a")
KGP_EXPECTED_RESPONSE=$(printf "\033_Gi=%d;OK\033\\" "$KGP_QUERY_ID")
ITERM2_EXPECTED_RESPONSE=$(printf "\033]1337;") # followed by "ReportCellSize=...", but only the prefix is enough
FENCE_CODE=$(printf "\033[c")
# Set terminal to raw mode with timeout
stty_orig=$(stty -g)
trap 'stty "$stty_orig"' EXIT
stty -echo -icanon min 1 time 0
printf "%s%s%s" "$ITERM2_QUERY_CODE" "$KGP_QUERY_CODE" "$FENCE_CODE" >/dev/tty
support_kgp=0
support_iterm2=0
support_sixel=0
response=""
while true; do
IFS= read -r -N 1 -t 0.3 char || {
[ -z "$char" ] && break
}
response+="$char"
if [[ "$response" == *"$KGP_EXPECTED_RESPONSE"* ]]; then
support_kgp=1
fi
if [[ "$response" == *"$ITERM2_EXPECTED_RESPONSE"* ]]; then
support_iterm2=1
fi
if [[ "$response" == *$'\033['*'c' ]]; then
break
fi
if [ ${#response} -gt 1024 ]; then
break
fi
done
if [[ "$response" =~ $'\x1b'\[\?([0-9;]*)c ]]; then
params="${BASH_REMATCH[1]}"
IFS=';' read -ra codes <<<"$params"
for code in "${codes[@]}"; do
if [[ "$code" == "4" ]]; then
support_sixel=1
break
fi
done
fi
if [ "$support_kgp" -eq 1 ]; then
echo "kitty"
fi
if [ "$support_iterm2" -eq 1 ]; then
echo "iterm"
fi
if [ "$support_sixel" -eq 1 ]; then
echo "sixels"
fi
)
}
SUPPORT_ICAT=0
SUPPORT_SIXEL=0
SUPPORT_ITERM2=0
ENABLE_ICAT=0
ENABLE_SIXEL=0
ENABLE_ITERM2=0
_check_graphics_support() {
local result
result=$(_graphics_query)
if [[ "$result" == *"kitty"* ]]; then
SUPPORT_ICAT=1
fi
if [[ "$result" == *"sixels"* ]]; then
SUPPORT_SIXEL=1
fi
if [[ "$result" == *"iterm"* ]]; then
SUPPORT_ITERM2=1
fi
}
_check_kitty_icat() {
# # workaround for WezTerm
if [ -n "${WEZTERM_EXECUTABLE:-}" ]; then
return 1
fi
[[ "$SUPPORT_ICAT" -eq 1 ]]
}
_check_sixel() {
# workaround for Zellij
if [ -n "${ZELLIJ_SESSION_NAME:-}" ]; then
return 1
# same for tmux, unless otherwise configured
elif [ -n "${TMUX:-}" ]; then
return 1
fi
[[ "$SUPPORT_SIXEL" -eq 1 ]]
}
_check_iterm2() {
[[ "$SUPPORT_ITERM2" -eq 1 ]]
}
# Priority: KGP > sixel > iterm2
_check_graphics_support
if _check_kitty_icat; then
ENABLE_ICAT=1
elif _check_sixel; then
ENABLE_SIXEL=1
elif _check_iterm2; then
ENABLE_ITERM2=1
fi
export ENABLE_ICAT
export ENABLE_SIXEL
export ENABLE_ITERM2
# URL handling (for file:// URLs)
url_unquote() {
if type python3 &>/dev/null; then
python3 -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()), end='')"
else
cat
fi
}
url_quote() {
if type python3 &>/dev/null; then
python3 -c "import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()), end='')"
else
cat
fi
}
export -f url_unquote
export -f url_quote
# Preview functions
_clear_preview() {
# chafa --clear will simply send '\x1b[H\x1b[2J' which may not work
# for images displayed with KGP, so we send the specific clear sequence
# manually in this case.
if [ "$ENABLE_ICAT" -eq 1 ]; then
printf "\x1b_Ga=d\x1b\\"
fi
}
export -f _clear_preview
_preview_image() {
local file="$1"
if ! type chafa >/dev/null 2>&1; then
text="Preview not available (chafa not found)."$'\n'
text+="Image: $file"
_preview_text "$text"
return
fi
# Though chafa is able to detect which output format to use based on the terminal capabilities,
# it is yet not always reliable, so we force it based on our checks.
if [ "$ENABLE_ICAT" -eq 1 ]; then
chafa -f kitty --size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
elif [ "$ENABLE_SIXEL" -eq 1 ]; then
chafa -f sixels --size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
elif [ "$ENABLE_ITERM2" -eq 1 ]; then
chafa -f iterm2 -size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
else
chafa -f symbols --size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
fi
}
export -f _preview_image
_preview_text() {
local content="$1"
printf "%s" "$content" | head -n 100
}
export -f _preview_text
_preview_video() {
local video_hash thumb_file path
path="$1"
video_hash=$(echo -n "$path" | md5sum | cut -d" " -f1)
thumb_file="$CACHE_DIR/$video_hash.png"
if [ ! -f "$thumb_file" ]; then
if type ffmpegthumbnailer &>/dev/null; then
ffmpegthumbnailer -i "$path" -o "$thumb_file" -s 480 -t 0 >/dev/null 2>&1
else
_preview_text "Thumbnail not available (ffmpegthumbnailer not found)."$'\n'
fi
fi
if [ -s "$thumb_file" ]; then
_preview_image "$thumb_file"
else
_preview_text "Video: $path"
fi
}
export -f _preview_video
# Kinda buggy right now
# _preview_audio() {
# local path="$1"
# if type mpv &>/dev/null; then
# echo "Playing audio: $path" >&2
# exec mpv --no-video --keep-open=no --loop-file=no --loop-playlist=no "$path" &>/dev/null
# else
# _preview_text "Audio: $path"
# fi
# }
# export -f _preview_audio
_preview_file() {
path="$1"
path_mime=$(file -b --mime-type "$path")
if [[ $path_mime =~ image ]]; then
_preview_image "$path"
elif [[ "$path_mime" =~ video ]]; then
_preview_video "$path"
# elif [[ "$path_mime" =~ audio ]]; then
# _preview_audio "$path"
else
_preview_text "$path"
fi
}
export -f _preview_file
preview() {
_clear_preview
entry="$1"
mimeType=$(echo -n "$entry" | cliphist decode | file -b --mime-type -)
ext=$(echo -n "$mimeType" | awk -F"/" "{print \$2}")
if [[ $mimeType =~ image ]]; then
img_hash=$(echo -n "$entry" | cliphist decode | md5sum | cut -d" " -f1)
cache_file="$CACHE_DIR/$img_hash.$ext"
[ -f "$cache_file" ] || echo -n "$entry" | cliphist decode >"$cache_file"
_preview_image "$cache_file"
elif path=$(echo -n "$entry" | cliphist decode) && [[ "$path" == /* ]]; then
if [ -e "$path" ]; then
_preview_file "$path"
else
_preview_text "$path does not exist."
fi
elif decoded=$(echo -n "$entry" | cliphist decode) && [[ "$decoded" == file://* ]]; then
paths=()
for path in $decoded; do
raw_path="${path#file://}"
raw_path=$(echo -n "$raw_path" | url_unquote)
paths+=("$raw_path")
done
if [ "${#paths[@]}" -eq 1 ] && [ -e "${paths[0]}" ]; then
_preview_file "${paths[0]}"
else
text="Multiple files:"$'\n'
for p in "${paths[@]}"; do
text+="$p"$'\n'
done
_preview_text "$text"
fi
else
if [ "$ENABLE_ICAT" = 1 ]; then
printf "\x1b_Ga=d\x1b\\"
fi
_preview_text "$(echo -n "$entry" | cliphist decode)"
fi
}
export -f preview
# Optimize entry formatting
format_clip_list() {
sed -E \
-e "s/(\t).*\.(mp4|mkv|webm|avi|mov|flv|wmv)$/\1${C_TERTIARY}[VIDEO]File.\2${C_RESET}/" \
-e "s/(\t)file:\/\/.*\.(mp4|mkv|webm|avi|mov|flv|wmv)$/\1${C_TERTIARY}[URL]Video.\2${C_RESET}/" \
-e "s/(\t)file:\/\/.*\.gif$/\1${C_PRIMARY}[URL]Image.gif${C_RESET}/" \
-e "s/(\t)file:\/\/.*\.(png|jpg|jpeg|webp|bmp)$/\1${C_TERTIARY}[URL]Image.\2${C_RESET}/" \
-e "s/(\t)file:\/\/.*/\1${C_CYAN}[URL]File${C_RESET}/" \
-e "s/(\t)\/.*\.gif$/\1${C_PRIMARY}[PATH]Image.gif${C_RESET}/" \
-e "s/(\t)\/.*\.(png|jpg|jpeg|webp|bmp)$/\1${C_TERTIARY}[PATH]Image.\2${C_RESET}/" \
-e "s/\[\[ binary data .* (png|jpg|jpeg|gif|webp) .*\]\]/${C_TERTIARY}[IMG]Bin.\1${C_RESET}/" \
-e "s/\[\[ binary data .* \]\]/${C_CYAN}[BINARY]${C_RESET}/"
}
export -f format_clip_list
add_num() {
awk -F '\t' '{printf "%s\t\x1b[90m%-2d \x1b[0m%s\n", $1, NR, $2}'
}
export -f add_num
# Action when confirmed
copy_selection() {
local input="$1"
local content
content=$(echo -n "$input" | awk "{print \$3}")
if [[ "$content" == "[URL]"* ]]; then
echo -n "$input" | cliphist decode | wl-copy --type text/uri-list
else
echo -n "$input" | cliphist decode | wl-copy
fi
echo -n "$input" | cliphist delete || true
}
export -f copy_selection
# Reload mechanism
RUNTIME_DIR="${XDG_RUNTIME_DIR:-/tmp}"
FZF_SOCKET=$(mktemp -u "$RUNTIME_DIR/fzfclip.XXXXXX.sock")
RELOAD_CMD="cliphist list | format_clip_list | add_num"
wl-paste --watch bash -c "curl -s --unix-socket '$FZF_SOCKET' -X POST -d 'reload($RELOAD_CMD)' http://localhost" &>/dev/null &
WATCH_PID=$!
# Ensure terminal is large enough
wait_timeout=50
while [[ $(tput cols) -lt 35 || $(tput lines) -lt 25 ]]; do
printf "\rWaiting for terminal size at least 35x25... %d" "$wait_timeout"
sleep 1
((wait_timeout--))
[ "$wait_timeout" -eq 0 ] && exit 1
done
$RELOAD_CMD | fzf \
--ansi \
--listen "$FZF_SOCKET" \
--bind "ctrl-r:reload($RELOAD_CMD)" \
--bind "ctrl-x:execute-silent(bash -c 'cliphist delete <<< \"\$1\"' -- {})+reload($RELOAD_CMD)" \
--prompt="󰅍 > " \
--header='CTRL-X: Delete | CTRL-R: Reload | ENTER: Paste' \
--color='header:italic:yellow,prompt:blue,pointer:blue' \
--info=hidden \
--no-sort \
--layout=reverse \
--with-nth 2.. \
--delimiter '\t' \
--preview-window=down:60%,wrap \
--preview "preview {}" \
--bind "enter:execute-silent(bash -c 'copy_selection \"\$1\"' -- {})+accept" \
>/dev/null || [ $? -eq 141 ]
@@ -1,10 +0,0 @@
#!/usr/bin/env bash
lockfile=/tmp/fzfclip.lock
exec {LOCK_FD}>"$lockfile"
flock -n "$LOCK_FD" || {
echo "Another instance is running." >&2
exit 1
}
fzfclip "$@" {LOCK_FD}>&-
@@ -1,32 +0,0 @@
#!/usr/bin/env bash
# Description:
# ~~Quick~~ snippet for cliphist + rofi + wl-copy
#
# Credit: https://github.com/sentriz/cliphist/blob/master/contrib/cliphist-rofi-img
tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT
mkdir -p "$tmp_dir"
read -r -d '' prog <<EOF
/^[0-9]+\s<meta http-equiv=/ { next }
match(\$0, /^([0-9]+)\s(\[\[\s)?binary.*(jpg|jpeg|png|bmp)/, grp) {
system("echo " grp[1] "\\\\\t | cliphist decode >$tmp_dir/"grp[1]"."grp[3])
print \$0"\0icon\x1f$tmp_dir/"grp[1]"."grp[3]
next
}
1
EOF
# Pipeline logic:
# 1. cliphist list: gives "ID <tab> Content"
# 2. gawk: adds icon paths
# 3. rofi -dmenu: shows list, hides column 1 (ID), returns "ID <tab> Content" on select
# 4. cliphist decode: reads ID from line, gets original content
result=$(cliphist list | gawk "$prog" | rofi -dmenu -display-columns 2 -config "$HOME/.config/rofi/dmenu.rasi" -show-icons -p "Clipboard")
if [[ -n "$result" ]]; then
echo "$result" | cliphist decode | wl-copy
fi
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
// This configuration is merged with the default vicinae configuration file, which you can obtain by running the `vicinae config default` command.
// Every item defined in this file takes precedence over the values defined in the default config or any other imported file.
//
// You can make manual edits to this file, however you should keep in mind that this file may be written to by vicinae when a configuration change is made through the GUI.
// When that happens, any custom comments or formatting will be lost.
//
// If you want to maintain a configuration file with your own comments and formatting, you should create a separate file and add it to the 'imports' array.
//
// Learn more about configuration at https://docs.vicinae.com/config
{
"$schema": "https://vicinae.com/schemas/config.json",
"font": {
"normal": {
"family": "LXGW WenKai"
}
},
"theme": {
"dark": {
"name": "catppuccin-mocha"
}
}
}