diff --git a/.bashrc b/.bashrc deleted file mode 100644 index ccd21ed..0000000 --- a/.bashrc +++ /dev/null @@ -1,41 +0,0 @@ -# -# ~/.bashrc -# - -# If not running interactively, don't do anything -[[ $- != *i* ]] && return - - -alias grep='grep --color=auto' -alias fzf="fzf --preview color='always {}'" -alias pacs="sudo pacman -Syu" -alias nmaps="sudo nmap -sn 192.168.0.0/24" -alias mem="df -H --output=source,size,used,avail | grep 480G | sort -u" -alias kittyimg="kitten icat" -alias vpn="sudo wg-quick up wg0" -alias vpnoff="sudo wg-quick down wg0" -alias xremaps="sudo xremap ~/.config/xremap/config.yml" -alias llama="~/projects/llama.cpp/build/bin/llama-server -m /home/anon/projects/llama.cpp/models/Llama-3.2-3B-Instruct-F16.gguf" -alias dots="cd ~/.dotfiles/.config/ && nvim" -alias aliases="bat ~/.config/fish/config.fish" - -# List Directory -alias ls="lsd" -alias l="ls -l" -alias la="ls -a" -alias lla="ls -la" -alias lt="ls --tree" - -# Git -alias gs='git status' -alias gca='git add -p . && git commit' -alias gd="git diff --word-diff" -alias gl='git log --graph --show-signature' -alias gla="git log --all --decorate --oneline --graph" -alias gm='git merge' -alias gis='git status' - -PS1='[\u@\h \W]\$' -set -o vi - -eval "$(zoxide init --cmd cd bash)" diff --git a/.config/.gitignore b/.config/.gitignore deleted file mode 100644 index 9fce120..0000000 --- a/.config/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -greet.txt -fish_variables -secrets.fish diff --git a/.config/aichat/config.yaml b/.config/aichat/config.yaml deleted file mode 100644 index 90372cc..0000000 --- a/.config/aichat/config.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# see https://github.com/sigoden/aichat/blob/main/config.example.yaml -keybindings: vi -editor: nvim -model: duskadiy:Qwen3-Coder-30B-Instruct-IQ4_XS -clients: -- type: openai-compatible - name: local - api_base: http://192.168.0.204:11343/v1 - models: - - name: Qwen3.5-27B-UD-Q3_K_XL - - name: Qwen3.5-9B-Q8_0 - - name: Qwen3-Coder-30B-Instruct-IQ4_XS - - name: GLM-4.7-Flash-UD-Q3_K_XL - - name: DeepSeek-Coder-V2-Lite-Instruct-Q8_0 - - name: gemma-4-E4B-it-UD-Q8_K_XL - - name: gemma-4-26B-A4B-it-UD-IQ4_XS -- type: openai-compatible - name: duskadiy - api_base: https://llm.duskadiy.com/api/v1 - models: - - name: Qwen3.5-27B-UD-Q3_K_XL - - name: Qwen3.5-9B-Q8_0 - - name: Qwen3-Coder-30B-Instruct-IQ4_XS - - name: GLM-4.7-Flash-UD-Q3_K_XL - - name: DeepSeek-Coder-V2-Lite-Instruct-Q8_0 - - name: gemma-4-E4B-it-UD-Q8_K_XL - - name: gemma-4-26B-A4B-it-UD-IQ4_XS diff --git a/.config/bat/themes/Catppuccin Frappe.tmTheme b/.config/bat/themes/Catppuccin Frappe.tmTheme deleted file mode 100644 index bfa7e0b..0000000 --- a/.config/bat/themes/Catppuccin Frappe.tmTheme +++ /dev/null @@ -1,2081 +0,0 @@ - - - - - name - Catppuccin Frappé - semanticClass - theme.dark.catppuccin-frappé - uuid - e0ada983-8938-490c-86f0-97a1a0ec58e4 - author - Catppuccin Org - colorSpaceName - sRGB - settings - - - settings - - background - #303446 - foreground - #c6d0f5 - caret - #f2d5cf - lineHighlight - #414559 - misspelling - #e78284 - accent - #ca9ee6 - selection - #949cbb40 - activeGuide - #51576d - findHighlight - #506373 - gutterForeground - #838ba7 - - - - name - Basic text & variable names (incl. leading punctuation) - scope - text, source, variable.other.readwrite, punctuation.definition.variable - settings - - foreground - #c6d0f5 - - - - name - Parentheses, Brackets, Braces - scope - punctuation - settings - - foreground - #949cbb - fontStyle - - - - - name - Comments - scope - comment, punctuation.definition.comment - settings - - foreground - #737994 - fontStyle - italic - - - - scope - string, punctuation.definition.string - settings - - foreground - #a6d189 - - - - scope - constant.character.escape - settings - - foreground - #f4b8e4 - - - - name - Booleans, constants, numbers - scope - constant.numeric, variable.other.constant, entity.name.constant, constant.language.boolean, constant.language.false, constant.language.true, keyword.other.unit.user-defined, keyword.other.unit.suffix.floating-point - settings - - foreground - #ef9f76 - - - - scope - keyword, keyword.operator.word, keyword.operator.new, variable.language.super, support.type.primitive, storage.type, storage.modifier, punctuation.definition.keyword - settings - - foreground - #ca9ee6 - fontStyle - - - - - scope - entity.name.tag.documentation - settings - - foreground - #ca9ee6 - - - - name - Punctuation - scope - keyword.operator, punctuation.accessor, punctuation.definition.generic, meta.function.closure punctuation.section.parameters, punctuation.definition.tag, punctuation.separator.key-value - settings - - foreground - #81c8be - - - - scope - entity.name.function, meta.function-call.method, support.function, support.function.misc, variable.function - settings - - foreground - #8caaee - fontStyle - italic - - - - name - Classes - scope - entity.name.class, entity.other.inherited-class, support.class, meta.function-call.constructor, entity.name.struct - settings - - foreground - #e5c890 - fontStyle - italic - - - - name - Enum - scope - entity.name.enum - settings - - foreground - #e5c890 - fontStyle - italic - - - - name - Enum member - scope - meta.enum variable.other.readwrite, variable.other.enummember - settings - - foreground - #81c8be - - - - name - Object properties - scope - meta.property.object - settings - - foreground - #81c8be - - - - name - Types - scope - meta.type, meta.type-alias, support.type, entity.name.type - settings - - foreground - #e5c890 - fontStyle - italic - - - - name - Decorators - scope - meta.annotation variable.function, meta.annotation variable.annotation.function, meta.annotation punctuation.definition.annotation, meta.decorator, punctuation.decorator - settings - - foreground - #ef9f76 - - - - scope - variable.parameter, meta.function.parameters - settings - - foreground - #ea999c - fontStyle - italic - - - - name - Built-ins - scope - constant.language, support.function.builtin - settings - - foreground - #e78284 - - - - scope - entity.other.attribute-name.documentation - settings - - foreground - #e78284 - - - - name - Preprocessor directives - scope - keyword.control.directive, punctuation.definition.directive - settings - - foreground - #e5c890 - - - - name - Type parameters - scope - punctuation.definition.typeparameters - settings - - foreground - #99d1db - - - - name - Namespaces - scope - entity.name.namespace - settings - - foreground - #e5c890 - - - - name - Property names (left hand assignments in json/yaml/css) - scope - support.type.property-name.css - settings - - foreground - #8caaee - fontStyle - - - - - name - This/Self keyword - scope - variable.language.this, variable.language.this punctuation.definition.variable - settings - - foreground - #e78284 - - - - name - Object properties - scope - variable.object.property - settings - - foreground - #c6d0f5 - - - - name - String template interpolation - scope - string.template variable, string variable - settings - - foreground - #c6d0f5 - - - - name - `new` as bold - scope - keyword.operator.new - settings - - fontStyle - bold - - - - name - C++ extern keyword - scope - storage.modifier.specifier.extern.cpp - settings - - foreground - #ca9ee6 - - - - name - C++ scope resolution - scope - entity.name.scope-resolution.template.call.cpp, entity.name.scope-resolution.parameter.cpp, entity.name.scope-resolution.cpp, entity.name.scope-resolution.function.definition.cpp - settings - - foreground - #e5c890 - - - - name - C++ doc keywords - scope - storage.type.class.doxygen - settings - - fontStyle - - - - - name - C++ operators - scope - storage.modifier.reference.cpp - settings - - foreground - #81c8be - - - - name - C# Interpolated Strings - scope - meta.interpolation.cs - settings - - foreground - #c6d0f5 - - - - name - C# xml-style docs - scope - comment.block.documentation.cs - settings - - foreground - #c6d0f5 - - - - name - Classes, reflecting the className color in JSX - scope - source.css entity.other.attribute-name.class.css, entity.other.attribute-name.parent-selector.css punctuation.definition.entity.css - settings - - foreground - #e5c890 - - - - name - Operators - scope - punctuation.separator.operator.css - settings - - foreground - #81c8be - - - - name - Pseudo classes - scope - source.css entity.other.attribute-name.pseudo-class - settings - - foreground - #81c8be - - - - scope - source.css constant.other.unicode-range - settings - - foreground - #ef9f76 - - - - scope - source.css variable.parameter.url - settings - - foreground - #a6d189 - fontStyle - - - - - name - CSS vendored property names - scope - support.type.vendored.property-name - settings - - foreground - #99d1db - - - - name - Less/SCSS right-hand variables (@/$-prefixed) - scope - source.css meta.property-value variable, source.css meta.property-value variable.other.less, source.css meta.property-value variable.other.less punctuation.definition.variable.less, meta.definition.variable.scss - settings - - foreground - #ea999c - - - - name - CSS variables (--prefixed) - scope - source.css meta.property-list variable, meta.property-list variable.other.less, meta.property-list variable.other.less punctuation.definition.variable.less - settings - - foreground - #8caaee - - - - name - CSS Percentage values, styled the same as numbers - scope - keyword.other.unit.percentage.css - settings - - foreground - #ef9f76 - - - - name - CSS Attribute selectors, styled the same as strings - scope - source.css meta.attribute-selector - settings - - foreground - #a6d189 - - - - name - JSON/YAML keys, other left-hand assignments - scope - keyword.other.definition.ini, punctuation.support.type.property-name.json, support.type.property-name.json, punctuation.support.type.property-name.toml, support.type.property-name.toml, entity.name.tag.yaml, punctuation.support.type.property-name.yaml, support.type.property-name.yaml - settings - - foreground - #8caaee - fontStyle - - - - - name - JSON/YAML constants - scope - constant.language.json, constant.language.yaml - settings - - foreground - #ef9f76 - - - - name - YAML anchors - scope - entity.name.type.anchor.yaml, variable.other.alias.yaml - settings - - foreground - #e5c890 - fontStyle - - - - - name - TOML tables / ini groups - scope - support.type.property-name.table, entity.name.section.group-title.ini - settings - - foreground - #e5c890 - - - - name - TOML dates - scope - constant.other.time.datetime.offset.toml - settings - - foreground - #f4b8e4 - - - - name - YAML anchor puctuation - scope - punctuation.definition.anchor.yaml, punctuation.definition.alias.yaml - settings - - foreground - #f4b8e4 - - - - name - YAML triple dashes - scope - entity.other.document.begin.yaml - settings - - foreground - #f4b8e4 - - - - name - Markup Diff - scope - markup.changed.diff - settings - - foreground - #ef9f76 - - - - name - Diff - scope - meta.diff.header.from-file, meta.diff.header.to-file, punctuation.definition.from-file.diff, punctuation.definition.to-file.diff - settings - - foreground - #8caaee - - - - name - Diff Inserted - scope - markup.inserted.diff - settings - - foreground - #a6d189 - - - - name - Diff Deleted - scope - markup.deleted.diff - settings - - foreground - #e78284 - - - - name - dotenv left-hand side assignments - scope - variable.other.env - settings - - foreground - #8caaee - - - - name - dotenv reference to existing env variable - scope - string.quoted variable.other.env - settings - - foreground - #c6d0f5 - - - - name - GDScript functions - scope - support.function.builtin.gdscript - settings - - foreground - #8caaee - - - - name - GDScript constants - scope - constant.language.gdscript - settings - - foreground - #ef9f76 - - - - name - Comment keywords - scope - comment meta.annotation.go - settings - - foreground - #ea999c - - - - name - go:embed, go:build, etc. - scope - comment meta.annotation.parameters.go - settings - - foreground - #ef9f76 - - - - name - Go constants (nil, true, false) - scope - constant.language.go - settings - - foreground - #ef9f76 - - - - name - GraphQL variables - scope - variable.graphql - settings - - foreground - #c6d0f5 - - - - name - GraphQL aliases - scope - string.unquoted.alias.graphql - settings - - foreground - #eebebe - - - - name - GraphQL enum members - scope - constant.character.enum.graphql - settings - - foreground - #81c8be - - - - name - GraphQL field in types - scope - meta.objectvalues.graphql constant.object.key.graphql string.unquoted.graphql - settings - - foreground - #eebebe - - - - name - HTML/XML DOCTYPE as keyword - scope - keyword.other.doctype, meta.tag.sgml.doctype punctuation.definition.tag, meta.tag.metadata.doctype entity.name.tag, meta.tag.metadata.doctype punctuation.definition.tag - settings - - foreground - #ca9ee6 - - - - name - HTML/XML-like <tags/> - scope - entity.name.tag - settings - - foreground - #8caaee - fontStyle - - - - - name - Special characters like &amp; - scope - text.html constant.character.entity, text.html constant.character.entity punctuation, constant.character.entity.xml, constant.character.entity.xml punctuation, constant.character.entity.js.jsx, constant.charactger.entity.js.jsx punctuation, constant.character.entity.tsx, constant.character.entity.tsx punctuation - settings - - foreground - #e78284 - - - - name - HTML/XML tag attribute values - scope - entity.other.attribute-name - settings - - foreground - #e5c890 - - - - name - Components - scope - support.class.component, support.class.component.jsx, support.class.component.tsx, support.class.component.vue - settings - - foreground - #f4b8e4 - fontStyle - - - - - name - Annotations - scope - punctuation.definition.annotation, storage.type.annotation - settings - - foreground - #ef9f76 - - - - name - Java enums - scope - constant.other.enum.java - settings - - foreground - #81c8be - - - - name - Java imports - scope - storage.modifier.import.java - settings - - foreground - #c6d0f5 - - - - name - Javadoc - scope - comment.block.javadoc.java keyword.other.documentation.javadoc.java - settings - - fontStyle - - - - - name - Exported Variable - scope - meta.export variable.other.readwrite.js - settings - - foreground - #ea999c - - - - name - JS/TS constants & properties - scope - variable.other.constant.js, variable.other.constant.ts, variable.other.property.js, variable.other.property.ts - settings - - foreground - #c6d0f5 - - - - name - JSDoc; these are mainly params, so styled as such - scope - variable.other.jsdoc, comment.block.documentation variable.other - settings - - foreground - #ea999c - fontStyle - - - - - name - JSDoc keywords - scope - storage.type.class.jsdoc - settings - - fontStyle - - - - - scope - support.type.object.console.js - settings - - foreground - #c6d0f5 - - - - name - Node constants as keywords (module, etc.) - scope - support.constant.node, support.type.object.module.js - settings - - foreground - #ca9ee6 - - - - name - implements as keyword - scope - storage.modifier.implements - settings - - foreground - #ca9ee6 - - - - name - Builtin types - scope - constant.language.null.js, constant.language.null.ts, constant.language.undefined.js, constant.language.undefined.ts, support.type.builtin.ts - settings - - foreground - #ca9ee6 - - - - scope - variable.parameter.generic - settings - - foreground - #e5c890 - - - - name - Arrow functions - scope - keyword.declaration.function.arrow.js, storage.type.function.arrow.ts - settings - - foreground - #81c8be - - - - name - Decorator punctuations (decorators inherit from blue functions, instead of styleguide peach) - scope - punctuation.decorator.ts - settings - - foreground - #8caaee - fontStyle - italic - - - - name - Extra JS/TS keywords - scope - keyword.operator.expression.in.js, keyword.operator.expression.in.ts, keyword.operator.expression.infer.ts, keyword.operator.expression.instanceof.js, keyword.operator.expression.instanceof.ts, keyword.operator.expression.is, keyword.operator.expression.keyof.ts, keyword.operator.expression.of.js, keyword.operator.expression.of.ts, keyword.operator.expression.typeof.ts - settings - - foreground - #ca9ee6 - - - - name - Julia macros - scope - support.function.macro.julia - settings - - foreground - #81c8be - fontStyle - italic - - - - name - Julia language constants (true, false) - scope - constant.language.julia - settings - - foreground - #ef9f76 - - - - name - Julia other constants (these seem to be arguments inside arrays) - scope - constant.other.symbol.julia - settings - - foreground - #ea999c - - - - name - LaTeX preamble - scope - text.tex keyword.control.preamble - settings - - foreground - #81c8be - - - - name - LaTeX be functions - scope - text.tex support.function.be - settings - - foreground - #99d1db - - - - name - LaTeX math - scope - constant.other.general.math.tex - settings - - foreground - #eebebe - - - - name - Lua docstring keywords - scope - comment.line.double-dash.documentation.lua storage.type.annotation.lua - settings - - foreground - #ca9ee6 - fontStyle - - - - - name - Lua docstring variables - scope - comment.line.double-dash.documentation.lua entity.name.variable.lua, comment.line.double-dash.documentation.lua variable.lua - settings - - foreground - #c6d0f5 - - - - scope - heading.1.markdown punctuation.definition.heading.markdown, heading.1.markdown, markup.heading.atx.1.mdx, markup.heading.atx.1.mdx punctuation.definition.heading.mdx, markup.heading.setext.1.markdown, markup.heading.heading-0.asciidoc - settings - - foreground - #e78284 - - - - scope - heading.2.markdown punctuation.definition.heading.markdown, heading.2.markdown, markup.heading.atx.2.mdx, markup.heading.atx.2.mdx punctuation.definition.heading.mdx, markup.heading.setext.2.markdown, markup.heading.heading-1.asciidoc - settings - - foreground - #ef9f76 - - - - scope - heading.3.markdown punctuation.definition.heading.markdown, heading.3.markdown, markup.heading.atx.3.mdx, markup.heading.atx.3.mdx punctuation.definition.heading.mdx, markup.heading.heading-2.asciidoc - settings - - foreground - #e5c890 - - - - scope - heading.4.markdown punctuation.definition.heading.markdown, heading.4.markdown, markup.heading.atx.4.mdx, markup.heading.atx.4.mdx punctuation.definition.heading.mdx, markup.heading.heading-3.asciidoc - settings - - foreground - #a6d189 - - - - scope - heading.5.markdown punctuation.definition.heading.markdown, heading.5.markdown, markup.heading.atx.5.mdx, markup.heading.atx.5.mdx punctuation.definition.heading.mdx, markup.heading.heading-4.asciidoc - settings - - foreground - #8caaee - - - - scope - heading.6.markdown punctuation.definition.heading.markdown, heading.6.markdown, markup.heading.atx.6.mdx, markup.heading.atx.6.mdx punctuation.definition.heading.mdx, markup.heading.heading-5.asciidoc - settings - - foreground - #ca9ee6 - - - - scope - markup.bold - settings - - foreground - #e78284 - fontStyle - bold - - - - scope - markup.italic - settings - - foreground - #e78284 - fontStyle - italic - - - - scope - markup.strikethrough - settings - - foreground - #a5adce - fontStyle - strikethrough - - - - name - Markdown auto links - scope - punctuation.definition.link, markup.underline.link - settings - - foreground - #8caaee - - - - name - Markdown links - scope - text.html.markdown punctuation.definition.link.title, string.other.link.title.markdown, markup.link, punctuation.definition.constant.markdown, constant.other.reference.link.markdown, markup.substitution.attribute-reference - settings - - foreground - #babbf1 - - - - name - Markdown code spans - scope - punctuation.definition.raw.markdown, markup.inline.raw.string.markdown, markup.raw.block.markdown - settings - - foreground - #a6d189 - - - - name - Markdown triple backtick language identifier - scope - fenced_code.block.language - settings - - foreground - #99d1db - - - - name - Markdown triple backticks - scope - markup.fenced_code.block punctuation.definition, markup.raw support.asciidoc - settings - - foreground - #949cbb - - - - name - Markdown quotes - scope - markup.quote, punctuation.definition.quote.begin - settings - - foreground - #f4b8e4 - - - - name - Markdown separators - scope - meta.separator.markdown - settings - - foreground - #81c8be - - - - name - Markdown list bullets - scope - punctuation.definition.list.begin.markdown, markup.list.bullet - settings - - foreground - #81c8be - - - - name - Nix attribute names - scope - entity.other.attribute-name.multipart.nix, entity.other.attribute-name.single.nix - settings - - foreground - #8caaee - - - - name - Nix parameter names - scope - variable.parameter.name.nix - settings - - foreground - #c6d0f5 - fontStyle - - - - - name - Nix interpolated parameter names - scope - meta.embedded variable.parameter.name.nix - settings - - foreground - #babbf1 - fontStyle - - - - - name - Nix paths - scope - string.unquoted.path.nix - settings - - foreground - #f4b8e4 - fontStyle - - - - - name - PHP Attributes - scope - support.attribute.builtin, meta.attribute.php - settings - - foreground - #e5c890 - - - - name - PHP Parameters (needed for the leading dollar sign) - scope - meta.function.parameters.php punctuation.definition.variable.php - settings - - foreground - #ea999c - - - - name - PHP Constants (null, __FILE__, etc.) - scope - constant.language.php - settings - - foreground - #ca9ee6 - - - - name - PHP functions - scope - text.html.php support.function - settings - - foreground - #99d1db - - - - name - PHPdoc keywords - scope - keyword.other.phpdoc.php - settings - - fontStyle - - - - - name - Python argument functions reset to text, otherwise they inherit blue from function-call - scope - support.variable.magic.python, meta.function-call.arguments.python - settings - - foreground - #c6d0f5 - - - - name - Python double underscore functions - scope - support.function.magic.python - settings - - foreground - #99d1db - fontStyle - italic - - - - name - Python `self` keyword - scope - variable.parameter.function.language.special.self.python, variable.language.special.self.python - settings - - foreground - #e78284 - fontStyle - italic - - - - name - python keyword flow/logical (for ... in) - scope - keyword.control.flow.python, keyword.operator.logical.python - settings - - foreground - #ca9ee6 - - - - name - python storage type - scope - storage.type.function.python - settings - - foreground - #ca9ee6 - - - - name - python function support - scope - support.token.decorator.python, meta.function.decorator.identifier.python - settings - - foreground - #99d1db - - - - name - python function calls - scope - meta.function-call.python - settings - - foreground - #8caaee - - - - name - python function decorators - scope - entity.name.function.decorator.python, punctuation.definition.decorator.python - settings - - foreground - #ef9f76 - fontStyle - italic - - - - name - python placeholder reset to normal string - scope - constant.character.format.placeholder.other.python - settings - - foreground - #f4b8e4 - - - - name - Python exception & builtins such as exit() - scope - support.type.exception.python, support.function.builtin.python - settings - - foreground - #ef9f76 - - - - name - entity.name.type - scope - support.type.python - settings - - foreground - #ef9f76 - - - - name - python constants (True/False) - scope - constant.language.python - settings - - foreground - #ca9ee6 - - - - name - Arguments accessed later in the function body - scope - meta.indexed-name.python, meta.item-access.python - settings - - foreground - #ea999c - fontStyle - italic - - - - name - Python f-strings/binary/unicode storage types - scope - storage.type.string.python - settings - - foreground - #a6d189 - fontStyle - italic - - - - name - Python type hints - scope - meta.function.parameters.python - settings - - fontStyle - - - - - name - Regex string begin/end in JS/TS - scope - string.regexp punctuation.definition.string.begin, string.regexp punctuation.definition.string.end - settings - - foreground - #f4b8e4 - - - - name - Regex anchors (^, $) - scope - keyword.control.anchor.regexp - settings - - foreground - #ca9ee6 - - - - name - Regex regular string match - scope - string.regexp.ts - settings - - foreground - #c6d0f5 - - - - name - Regex group parenthesis & backreference (\1, \2, \3, ...) - scope - punctuation.definition.group.regexp, keyword.other.back-reference.regexp - settings - - foreground - #a6d189 - - - - name - Regex character class [] - scope - punctuation.definition.character-class.regexp - settings - - foreground - #e5c890 - - - - name - Regex character classes (\d, \w, \s) - scope - constant.other.character-class.regexp - settings - - foreground - #f4b8e4 - - - - name - Regex range - scope - constant.other.character-class.range.regexp - settings - - foreground - #f2d5cf - - - - name - Regex quantifier - scope - keyword.operator.quantifier.regexp - settings - - foreground - #81c8be - - - - name - Regex constant/numeric - scope - constant.character.numeric.regexp - settings - - foreground - #ef9f76 - - - - name - Regex lookaheads, negative lookaheads, lookbehinds, negative lookbehinds - scope - punctuation.definition.group.no-capture.regexp, meta.assertion.look-ahead.regexp, meta.assertion.negative-look-ahead.regexp - settings - - foreground - #8caaee - - - - name - Rust attribute - scope - meta.annotation.rust, meta.annotation.rust punctuation, meta.attribute.rust, punctuation.definition.attribute.rust - settings - - foreground - #e5c890 - fontStyle - italic - - - - name - Rust attribute strings - scope - meta.attribute.rust string.quoted.double.rust, meta.attribute.rust string.quoted.single.char.rust - settings - - fontStyle - - - - - name - Rust keyword - scope - entity.name.function.macro.rules.rust, storage.type.module.rust, storage.modifier.rust, storage.type.struct.rust, storage.type.enum.rust, storage.type.trait.rust, storage.type.union.rust, storage.type.impl.rust, storage.type.rust, storage.type.function.rust, storage.type.type.rust - settings - - foreground - #ca9ee6 - fontStyle - - - - - name - Rust u/i32, u/i64, etc. - scope - entity.name.type.numeric.rust - settings - - foreground - #ca9ee6 - fontStyle - - - - - name - Rust generic - scope - meta.generic.rust - settings - - foreground - #ef9f76 - - - - name - Rust impl - scope - entity.name.impl.rust - settings - - foreground - #e5c890 - fontStyle - italic - - - - name - Rust module - scope - entity.name.module.rust - settings - - foreground - #ef9f76 - - - - name - Rust trait - scope - entity.name.trait.rust - settings - - foreground - #e5c890 - fontStyle - italic - - - - name - Rust struct - scope - storage.type.source.rust - settings - - foreground - #e5c890 - - - - name - Rust union - scope - entity.name.union.rust - settings - - foreground - #e5c890 - - - - name - Rust enum member - scope - meta.enum.rust storage.type.source.rust - settings - - foreground - #81c8be - - - - name - Rust macro - scope - support.macro.rust, meta.macro.rust support.function.rust, entity.name.function.macro.rust - settings - - foreground - #8caaee - fontStyle - italic - - - - name - Rust lifetime - scope - storage.modifier.lifetime.rust, entity.name.type.lifetime - settings - - foreground - #8caaee - fontStyle - italic - - - - name - Rust string formatting - scope - string.quoted.double.rust constant.other.placeholder.rust - settings - - foreground - #f4b8e4 - - - - name - Rust return type generic - scope - meta.function.return-type.rust meta.generic.rust storage.type.rust - settings - - foreground - #c6d0f5 - - - - name - Rust functions - scope - meta.function.call.rust - settings - - foreground - #8caaee - - - - name - Rust angle brackets - scope - punctuation.brackets.angle.rust - settings - - foreground - #99d1db - - - - name - Rust constants - scope - constant.other.caps.rust - settings - - foreground - #ef9f76 - - - - name - Rust function parameters - scope - meta.function.definition.rust variable.other.rust - settings - - foreground - #ea999c - - - - name - Rust closure variables - scope - meta.function.call.rust variable.other.rust - settings - - foreground - #c6d0f5 - - - - name - Rust self - scope - variable.language.self.rust - settings - - foreground - #e78284 - - - - name - Rust metavariable names - scope - variable.other.metavariable.name.rust, meta.macro.metavariable.rust keyword.operator.macro.dollar.rust - settings - - foreground - #f4b8e4 - - - - name - Shell shebang - scope - comment.line.shebang, comment.line.shebang punctuation.definition.comment, comment.line.shebang, punctuation.definition.comment.shebang.shell, meta.shebang.shell - settings - - foreground - #f4b8e4 - fontStyle - italic - - - - name - Shell shebang command - scope - comment.line.shebang constant.language - settings - - foreground - #81c8be - fontStyle - italic - - - - name - Shell interpolated command - scope - meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation, meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation - settings - - foreground - #e78284 - - - - name - Shell interpolated command variable - scope - meta.string meta.interpolation.parameter.shell variable.other.readwrite - settings - - foreground - #ef9f76 - fontStyle - italic - - - - scope - source.shell punctuation.section.interpolation, punctuation.definition.evaluation.backticks.shell - settings - - foreground - #81c8be - - - - name - Shell EOF - scope - entity.name.tag.heredoc.shell - settings - - foreground - #ca9ee6 - - - - name - Shell quoted variable - scope - string.quoted.double.shell variable.other.normal.shell - settings - - foreground - #c6d0f5 - - - - name - JSON Keys - scope - source.json meta.mapping.key string - settings - - foreground - #8caaee - - - - name - JSON key surrounding quotes - scope - source.json meta.mapping.key punctuation.definition.string.begin, source.json meta.mapping.key punctuation.definition.string.end - settings - - foreground - #949cbb - - - - scope - markup.heading.synopsis.man, markup.heading.title.man, markup.heading.other.man, markup.heading.env.man - settings - - foreground - #ca9ee6 - - - - scope - markup.heading.commands.man - settings - - foreground - #8caaee - - - - scope - markup.heading.env.man - settings - - foreground - #f4b8e4 - - - - name - Man page options - scope - entity.name - settings - - foreground - #81c8be - - - - scope - markup.heading.1.markdown - settings - - foreground - #e78284 - - - - scope - markup.heading.2.markdown - settings - - foreground - #ef9f76 - - - - scope - markup.heading.markdown - settings - - foreground - #e5c890 - - - - - \ No newline at end of file diff --git a/.config/bat/themes/Catppuccin Latte.tmTheme b/.config/bat/themes/Catppuccin Latte.tmTheme deleted file mode 100644 index 4783c16..0000000 --- a/.config/bat/themes/Catppuccin Latte.tmTheme +++ /dev/null @@ -1,2081 +0,0 @@ - - - - - name - Catppuccin Latte - semanticClass - theme.light.catppuccin-latte - uuid - 96a262cd-4b2f-49f5-9125-8dd0077cbfe1 - author - Catppuccin Org - colorSpaceName - sRGB - settings - - - settings - - background - #eff1f5 - foreground - #4c4f69 - caret - #dc8a78 - lineHighlight - #ccd0da - misspelling - #d20f39 - accent - #8839ef - selection - #7c7f934d - activeGuide - #bcc0cc - findHighlight - #a9daf0 - gutterForeground - #8c8fa1 - - - - name - Basic text & variable names (incl. leading punctuation) - scope - text, source, variable.other.readwrite, punctuation.definition.variable - settings - - foreground - #4c4f69 - - - - name - Parentheses, Brackets, Braces - scope - punctuation - settings - - foreground - #7c7f93 - fontStyle - - - - - name - Comments - scope - comment, punctuation.definition.comment - settings - - foreground - #9ca0b0 - fontStyle - italic - - - - scope - string, punctuation.definition.string - settings - - foreground - #40a02b - - - - scope - constant.character.escape - settings - - foreground - #ea76cb - - - - name - Booleans, constants, numbers - scope - constant.numeric, variable.other.constant, entity.name.constant, constant.language.boolean, constant.language.false, constant.language.true, keyword.other.unit.user-defined, keyword.other.unit.suffix.floating-point - settings - - foreground - #fe640b - - - - scope - keyword, keyword.operator.word, keyword.operator.new, variable.language.super, support.type.primitive, storage.type, storage.modifier, punctuation.definition.keyword - settings - - foreground - #8839ef - fontStyle - - - - - scope - entity.name.tag.documentation - settings - - foreground - #8839ef - - - - name - Punctuation - scope - keyword.operator, punctuation.accessor, punctuation.definition.generic, meta.function.closure punctuation.section.parameters, punctuation.definition.tag, punctuation.separator.key-value - settings - - foreground - #179299 - - - - scope - entity.name.function, meta.function-call.method, support.function, support.function.misc, variable.function - settings - - foreground - #1e66f5 - fontStyle - italic - - - - name - Classes - scope - entity.name.class, entity.other.inherited-class, support.class, meta.function-call.constructor, entity.name.struct - settings - - foreground - #df8e1d - fontStyle - italic - - - - name - Enum - scope - entity.name.enum - settings - - foreground - #df8e1d - fontStyle - italic - - - - name - Enum member - scope - meta.enum variable.other.readwrite, variable.other.enummember - settings - - foreground - #179299 - - - - name - Object properties - scope - meta.property.object - settings - - foreground - #179299 - - - - name - Types - scope - meta.type, meta.type-alias, support.type, entity.name.type - settings - - foreground - #df8e1d - fontStyle - italic - - - - name - Decorators - scope - meta.annotation variable.function, meta.annotation variable.annotation.function, meta.annotation punctuation.definition.annotation, meta.decorator, punctuation.decorator - settings - - foreground - #fe640b - - - - scope - variable.parameter, meta.function.parameters - settings - - foreground - #e64553 - fontStyle - italic - - - - name - Built-ins - scope - constant.language, support.function.builtin - settings - - foreground - #d20f39 - - - - scope - entity.other.attribute-name.documentation - settings - - foreground - #d20f39 - - - - name - Preprocessor directives - scope - keyword.control.directive, punctuation.definition.directive - settings - - foreground - #df8e1d - - - - name - Type parameters - scope - punctuation.definition.typeparameters - settings - - foreground - #04a5e5 - - - - name - Namespaces - scope - entity.name.namespace - settings - - foreground - #df8e1d - - - - name - Property names (left hand assignments in json/yaml/css) - scope - support.type.property-name.css - settings - - foreground - #1e66f5 - fontStyle - - - - - name - This/Self keyword - scope - variable.language.this, variable.language.this punctuation.definition.variable - settings - - foreground - #d20f39 - - - - name - Object properties - scope - variable.object.property - settings - - foreground - #4c4f69 - - - - name - String template interpolation - scope - string.template variable, string variable - settings - - foreground - #4c4f69 - - - - name - `new` as bold - scope - keyword.operator.new - settings - - fontStyle - bold - - - - name - C++ extern keyword - scope - storage.modifier.specifier.extern.cpp - settings - - foreground - #8839ef - - - - name - C++ scope resolution - scope - entity.name.scope-resolution.template.call.cpp, entity.name.scope-resolution.parameter.cpp, entity.name.scope-resolution.cpp, entity.name.scope-resolution.function.definition.cpp - settings - - foreground - #df8e1d - - - - name - C++ doc keywords - scope - storage.type.class.doxygen - settings - - fontStyle - - - - - name - C++ operators - scope - storage.modifier.reference.cpp - settings - - foreground - #179299 - - - - name - C# Interpolated Strings - scope - meta.interpolation.cs - settings - - foreground - #4c4f69 - - - - name - C# xml-style docs - scope - comment.block.documentation.cs - settings - - foreground - #4c4f69 - - - - name - Classes, reflecting the className color in JSX - scope - source.css entity.other.attribute-name.class.css, entity.other.attribute-name.parent-selector.css punctuation.definition.entity.css - settings - - foreground - #df8e1d - - - - name - Operators - scope - punctuation.separator.operator.css - settings - - foreground - #179299 - - - - name - Pseudo classes - scope - source.css entity.other.attribute-name.pseudo-class - settings - - foreground - #179299 - - - - scope - source.css constant.other.unicode-range - settings - - foreground - #fe640b - - - - scope - source.css variable.parameter.url - settings - - foreground - #40a02b - fontStyle - - - - - name - CSS vendored property names - scope - support.type.vendored.property-name - settings - - foreground - #04a5e5 - - - - name - Less/SCSS right-hand variables (@/$-prefixed) - scope - source.css meta.property-value variable, source.css meta.property-value variable.other.less, source.css meta.property-value variable.other.less punctuation.definition.variable.less, meta.definition.variable.scss - settings - - foreground - #e64553 - - - - name - CSS variables (--prefixed) - scope - source.css meta.property-list variable, meta.property-list variable.other.less, meta.property-list variable.other.less punctuation.definition.variable.less - settings - - foreground - #1e66f5 - - - - name - CSS Percentage values, styled the same as numbers - scope - keyword.other.unit.percentage.css - settings - - foreground - #fe640b - - - - name - CSS Attribute selectors, styled the same as strings - scope - source.css meta.attribute-selector - settings - - foreground - #40a02b - - - - name - JSON/YAML keys, other left-hand assignments - scope - keyword.other.definition.ini, punctuation.support.type.property-name.json, support.type.property-name.json, punctuation.support.type.property-name.toml, support.type.property-name.toml, entity.name.tag.yaml, punctuation.support.type.property-name.yaml, support.type.property-name.yaml - settings - - foreground - #1e66f5 - fontStyle - - - - - name - JSON/YAML constants - scope - constant.language.json, constant.language.yaml - settings - - foreground - #fe640b - - - - name - YAML anchors - scope - entity.name.type.anchor.yaml, variable.other.alias.yaml - settings - - foreground - #df8e1d - fontStyle - - - - - name - TOML tables / ini groups - scope - support.type.property-name.table, entity.name.section.group-title.ini - settings - - foreground - #df8e1d - - - - name - TOML dates - scope - constant.other.time.datetime.offset.toml - settings - - foreground - #ea76cb - - - - name - YAML anchor puctuation - scope - punctuation.definition.anchor.yaml, punctuation.definition.alias.yaml - settings - - foreground - #ea76cb - - - - name - YAML triple dashes - scope - entity.other.document.begin.yaml - settings - - foreground - #ea76cb - - - - name - Markup Diff - scope - markup.changed.diff - settings - - foreground - #fe640b - - - - name - Diff - scope - meta.diff.header.from-file, meta.diff.header.to-file, punctuation.definition.from-file.diff, punctuation.definition.to-file.diff - settings - - foreground - #1e66f5 - - - - name - Diff Inserted - scope - markup.inserted.diff - settings - - foreground - #40a02b - - - - name - Diff Deleted - scope - markup.deleted.diff - settings - - foreground - #d20f39 - - - - name - dotenv left-hand side assignments - scope - variable.other.env - settings - - foreground - #1e66f5 - - - - name - dotenv reference to existing env variable - scope - string.quoted variable.other.env - settings - - foreground - #4c4f69 - - - - name - GDScript functions - scope - support.function.builtin.gdscript - settings - - foreground - #1e66f5 - - - - name - GDScript constants - scope - constant.language.gdscript - settings - - foreground - #fe640b - - - - name - Comment keywords - scope - comment meta.annotation.go - settings - - foreground - #e64553 - - - - name - go:embed, go:build, etc. - scope - comment meta.annotation.parameters.go - settings - - foreground - #fe640b - - - - name - Go constants (nil, true, false) - scope - constant.language.go - settings - - foreground - #fe640b - - - - name - GraphQL variables - scope - variable.graphql - settings - - foreground - #4c4f69 - - - - name - GraphQL aliases - scope - string.unquoted.alias.graphql - settings - - foreground - #dd7878 - - - - name - GraphQL enum members - scope - constant.character.enum.graphql - settings - - foreground - #179299 - - - - name - GraphQL field in types - scope - meta.objectvalues.graphql constant.object.key.graphql string.unquoted.graphql - settings - - foreground - #dd7878 - - - - name - HTML/XML DOCTYPE as keyword - scope - keyword.other.doctype, meta.tag.sgml.doctype punctuation.definition.tag, meta.tag.metadata.doctype entity.name.tag, meta.tag.metadata.doctype punctuation.definition.tag - settings - - foreground - #8839ef - - - - name - HTML/XML-like <tags/> - scope - entity.name.tag - settings - - foreground - #1e66f5 - fontStyle - - - - - name - Special characters like &amp; - scope - text.html constant.character.entity, text.html constant.character.entity punctuation, constant.character.entity.xml, constant.character.entity.xml punctuation, constant.character.entity.js.jsx, constant.charactger.entity.js.jsx punctuation, constant.character.entity.tsx, constant.character.entity.tsx punctuation - settings - - foreground - #d20f39 - - - - name - HTML/XML tag attribute values - scope - entity.other.attribute-name - settings - - foreground - #df8e1d - - - - name - Components - scope - support.class.component, support.class.component.jsx, support.class.component.tsx, support.class.component.vue - settings - - foreground - #ea76cb - fontStyle - - - - - name - Annotations - scope - punctuation.definition.annotation, storage.type.annotation - settings - - foreground - #fe640b - - - - name - Java enums - scope - constant.other.enum.java - settings - - foreground - #179299 - - - - name - Java imports - scope - storage.modifier.import.java - settings - - foreground - #4c4f69 - - - - name - Javadoc - scope - comment.block.javadoc.java keyword.other.documentation.javadoc.java - settings - - fontStyle - - - - - name - Exported Variable - scope - meta.export variable.other.readwrite.js - settings - - foreground - #e64553 - - - - name - JS/TS constants & properties - scope - variable.other.constant.js, variable.other.constant.ts, variable.other.property.js, variable.other.property.ts - settings - - foreground - #4c4f69 - - - - name - JSDoc; these are mainly params, so styled as such - scope - variable.other.jsdoc, comment.block.documentation variable.other - settings - - foreground - #e64553 - fontStyle - - - - - name - JSDoc keywords - scope - storage.type.class.jsdoc - settings - - fontStyle - - - - - scope - support.type.object.console.js - settings - - foreground - #4c4f69 - - - - name - Node constants as keywords (module, etc.) - scope - support.constant.node, support.type.object.module.js - settings - - foreground - #8839ef - - - - name - implements as keyword - scope - storage.modifier.implements - settings - - foreground - #8839ef - - - - name - Builtin types - scope - constant.language.null.js, constant.language.null.ts, constant.language.undefined.js, constant.language.undefined.ts, support.type.builtin.ts - settings - - foreground - #8839ef - - - - scope - variable.parameter.generic - settings - - foreground - #df8e1d - - - - name - Arrow functions - scope - keyword.declaration.function.arrow.js, storage.type.function.arrow.ts - settings - - foreground - #179299 - - - - name - Decorator punctuations (decorators inherit from blue functions, instead of styleguide peach) - scope - punctuation.decorator.ts - settings - - foreground - #1e66f5 - fontStyle - italic - - - - name - Extra JS/TS keywords - scope - keyword.operator.expression.in.js, keyword.operator.expression.in.ts, keyword.operator.expression.infer.ts, keyword.operator.expression.instanceof.js, keyword.operator.expression.instanceof.ts, keyword.operator.expression.is, keyword.operator.expression.keyof.ts, keyword.operator.expression.of.js, keyword.operator.expression.of.ts, keyword.operator.expression.typeof.ts - settings - - foreground - #8839ef - - - - name - Julia macros - scope - support.function.macro.julia - settings - - foreground - #179299 - fontStyle - italic - - - - name - Julia language constants (true, false) - scope - constant.language.julia - settings - - foreground - #fe640b - - - - name - Julia other constants (these seem to be arguments inside arrays) - scope - constant.other.symbol.julia - settings - - foreground - #e64553 - - - - name - LaTeX preamble - scope - text.tex keyword.control.preamble - settings - - foreground - #179299 - - - - name - LaTeX be functions - scope - text.tex support.function.be - settings - - foreground - #04a5e5 - - - - name - LaTeX math - scope - constant.other.general.math.tex - settings - - foreground - #dd7878 - - - - name - Lua docstring keywords - scope - comment.line.double-dash.documentation.lua storage.type.annotation.lua - settings - - foreground - #8839ef - fontStyle - - - - - name - Lua docstring variables - scope - comment.line.double-dash.documentation.lua entity.name.variable.lua, comment.line.double-dash.documentation.lua variable.lua - settings - - foreground - #4c4f69 - - - - scope - heading.1.markdown punctuation.definition.heading.markdown, heading.1.markdown, markup.heading.atx.1.mdx, markup.heading.atx.1.mdx punctuation.definition.heading.mdx, markup.heading.setext.1.markdown, markup.heading.heading-0.asciidoc - settings - - foreground - #d20f39 - - - - scope - heading.2.markdown punctuation.definition.heading.markdown, heading.2.markdown, markup.heading.atx.2.mdx, markup.heading.atx.2.mdx punctuation.definition.heading.mdx, markup.heading.setext.2.markdown, markup.heading.heading-1.asciidoc - settings - - foreground - #fe640b - - - - scope - heading.3.markdown punctuation.definition.heading.markdown, heading.3.markdown, markup.heading.atx.3.mdx, markup.heading.atx.3.mdx punctuation.definition.heading.mdx, markup.heading.heading-2.asciidoc - settings - - foreground - #df8e1d - - - - scope - heading.4.markdown punctuation.definition.heading.markdown, heading.4.markdown, markup.heading.atx.4.mdx, markup.heading.atx.4.mdx punctuation.definition.heading.mdx, markup.heading.heading-3.asciidoc - settings - - foreground - #40a02b - - - - scope - heading.5.markdown punctuation.definition.heading.markdown, heading.5.markdown, markup.heading.atx.5.mdx, markup.heading.atx.5.mdx punctuation.definition.heading.mdx, markup.heading.heading-4.asciidoc - settings - - foreground - #1e66f5 - - - - scope - heading.6.markdown punctuation.definition.heading.markdown, heading.6.markdown, markup.heading.atx.6.mdx, markup.heading.atx.6.mdx punctuation.definition.heading.mdx, markup.heading.heading-5.asciidoc - settings - - foreground - #8839ef - - - - scope - markup.bold - settings - - foreground - #d20f39 - fontStyle - bold - - - - scope - markup.italic - settings - - foreground - #d20f39 - fontStyle - italic - - - - scope - markup.strikethrough - settings - - foreground - #6c6f85 - fontStyle - strikethrough - - - - name - Markdown auto links - scope - punctuation.definition.link, markup.underline.link - settings - - foreground - #1e66f5 - - - - name - Markdown links - scope - text.html.markdown punctuation.definition.link.title, string.other.link.title.markdown, markup.link, punctuation.definition.constant.markdown, constant.other.reference.link.markdown, markup.substitution.attribute-reference - settings - - foreground - #7287fd - - - - name - Markdown code spans - scope - punctuation.definition.raw.markdown, markup.inline.raw.string.markdown, markup.raw.block.markdown - settings - - foreground - #40a02b - - - - name - Markdown triple backtick language identifier - scope - fenced_code.block.language - settings - - foreground - #04a5e5 - - - - name - Markdown triple backticks - scope - markup.fenced_code.block punctuation.definition, markup.raw support.asciidoc - settings - - foreground - #7c7f93 - - - - name - Markdown quotes - scope - markup.quote, punctuation.definition.quote.begin - settings - - foreground - #ea76cb - - - - name - Markdown separators - scope - meta.separator.markdown - settings - - foreground - #179299 - - - - name - Markdown list bullets - scope - punctuation.definition.list.begin.markdown, markup.list.bullet - settings - - foreground - #179299 - - - - name - Nix attribute names - scope - entity.other.attribute-name.multipart.nix, entity.other.attribute-name.single.nix - settings - - foreground - #1e66f5 - - - - name - Nix parameter names - scope - variable.parameter.name.nix - settings - - foreground - #4c4f69 - fontStyle - - - - - name - Nix interpolated parameter names - scope - meta.embedded variable.parameter.name.nix - settings - - foreground - #7287fd - fontStyle - - - - - name - Nix paths - scope - string.unquoted.path.nix - settings - - foreground - #ea76cb - fontStyle - - - - - name - PHP Attributes - scope - support.attribute.builtin, meta.attribute.php - settings - - foreground - #df8e1d - - - - name - PHP Parameters (needed for the leading dollar sign) - scope - meta.function.parameters.php punctuation.definition.variable.php - settings - - foreground - #e64553 - - - - name - PHP Constants (null, __FILE__, etc.) - scope - constant.language.php - settings - - foreground - #8839ef - - - - name - PHP functions - scope - text.html.php support.function - settings - - foreground - #04a5e5 - - - - name - PHPdoc keywords - scope - keyword.other.phpdoc.php - settings - - fontStyle - - - - - name - Python argument functions reset to text, otherwise they inherit blue from function-call - scope - support.variable.magic.python, meta.function-call.arguments.python - settings - - foreground - #4c4f69 - - - - name - Python double underscore functions - scope - support.function.magic.python - settings - - foreground - #04a5e5 - fontStyle - italic - - - - name - Python `self` keyword - scope - variable.parameter.function.language.special.self.python, variable.language.special.self.python - settings - - foreground - #d20f39 - fontStyle - italic - - - - name - python keyword flow/logical (for ... in) - scope - keyword.control.flow.python, keyword.operator.logical.python - settings - - foreground - #8839ef - - - - name - python storage type - scope - storage.type.function.python - settings - - foreground - #8839ef - - - - name - python function support - scope - support.token.decorator.python, meta.function.decorator.identifier.python - settings - - foreground - #04a5e5 - - - - name - python function calls - scope - meta.function-call.python - settings - - foreground - #1e66f5 - - - - name - python function decorators - scope - entity.name.function.decorator.python, punctuation.definition.decorator.python - settings - - foreground - #fe640b - fontStyle - italic - - - - name - python placeholder reset to normal string - scope - constant.character.format.placeholder.other.python - settings - - foreground - #ea76cb - - - - name - Python exception & builtins such as exit() - scope - support.type.exception.python, support.function.builtin.python - settings - - foreground - #fe640b - - - - name - entity.name.type - scope - support.type.python - settings - - foreground - #fe640b - - - - name - python constants (True/False) - scope - constant.language.python - settings - - foreground - #8839ef - - - - name - Arguments accessed later in the function body - scope - meta.indexed-name.python, meta.item-access.python - settings - - foreground - #e64553 - fontStyle - italic - - - - name - Python f-strings/binary/unicode storage types - scope - storage.type.string.python - settings - - foreground - #40a02b - fontStyle - italic - - - - name - Python type hints - scope - meta.function.parameters.python - settings - - fontStyle - - - - - name - Regex string begin/end in JS/TS - scope - string.regexp punctuation.definition.string.begin, string.regexp punctuation.definition.string.end - settings - - foreground - #ea76cb - - - - name - Regex anchors (^, $) - scope - keyword.control.anchor.regexp - settings - - foreground - #8839ef - - - - name - Regex regular string match - scope - string.regexp.ts - settings - - foreground - #4c4f69 - - - - name - Regex group parenthesis & backreference (\1, \2, \3, ...) - scope - punctuation.definition.group.regexp, keyword.other.back-reference.regexp - settings - - foreground - #40a02b - - - - name - Regex character class [] - scope - punctuation.definition.character-class.regexp - settings - - foreground - #df8e1d - - - - name - Regex character classes (\d, \w, \s) - scope - constant.other.character-class.regexp - settings - - foreground - #ea76cb - - - - name - Regex range - scope - constant.other.character-class.range.regexp - settings - - foreground - #dc8a78 - - - - name - Regex quantifier - scope - keyword.operator.quantifier.regexp - settings - - foreground - #179299 - - - - name - Regex constant/numeric - scope - constant.character.numeric.regexp - settings - - foreground - #fe640b - - - - name - Regex lookaheads, negative lookaheads, lookbehinds, negative lookbehinds - scope - punctuation.definition.group.no-capture.regexp, meta.assertion.look-ahead.regexp, meta.assertion.negative-look-ahead.regexp - settings - - foreground - #1e66f5 - - - - name - Rust attribute - scope - meta.annotation.rust, meta.annotation.rust punctuation, meta.attribute.rust, punctuation.definition.attribute.rust - settings - - foreground - #df8e1d - fontStyle - italic - - - - name - Rust attribute strings - scope - meta.attribute.rust string.quoted.double.rust, meta.attribute.rust string.quoted.single.char.rust - settings - - fontStyle - - - - - name - Rust keyword - scope - entity.name.function.macro.rules.rust, storage.type.module.rust, storage.modifier.rust, storage.type.struct.rust, storage.type.enum.rust, storage.type.trait.rust, storage.type.union.rust, storage.type.impl.rust, storage.type.rust, storage.type.function.rust, storage.type.type.rust - settings - - foreground - #8839ef - fontStyle - - - - - name - Rust u/i32, u/i64, etc. - scope - entity.name.type.numeric.rust - settings - - foreground - #8839ef - fontStyle - - - - - name - Rust generic - scope - meta.generic.rust - settings - - foreground - #fe640b - - - - name - Rust impl - scope - entity.name.impl.rust - settings - - foreground - #df8e1d - fontStyle - italic - - - - name - Rust module - scope - entity.name.module.rust - settings - - foreground - #fe640b - - - - name - Rust trait - scope - entity.name.trait.rust - settings - - foreground - #df8e1d - fontStyle - italic - - - - name - Rust struct - scope - storage.type.source.rust - settings - - foreground - #df8e1d - - - - name - Rust union - scope - entity.name.union.rust - settings - - foreground - #df8e1d - - - - name - Rust enum member - scope - meta.enum.rust storage.type.source.rust - settings - - foreground - #179299 - - - - name - Rust macro - scope - support.macro.rust, meta.macro.rust support.function.rust, entity.name.function.macro.rust - settings - - foreground - #1e66f5 - fontStyle - italic - - - - name - Rust lifetime - scope - storage.modifier.lifetime.rust, entity.name.type.lifetime - settings - - foreground - #1e66f5 - fontStyle - italic - - - - name - Rust string formatting - scope - string.quoted.double.rust constant.other.placeholder.rust - settings - - foreground - #ea76cb - - - - name - Rust return type generic - scope - meta.function.return-type.rust meta.generic.rust storage.type.rust - settings - - foreground - #4c4f69 - - - - name - Rust functions - scope - meta.function.call.rust - settings - - foreground - #1e66f5 - - - - name - Rust angle brackets - scope - punctuation.brackets.angle.rust - settings - - foreground - #04a5e5 - - - - name - Rust constants - scope - constant.other.caps.rust - settings - - foreground - #fe640b - - - - name - Rust function parameters - scope - meta.function.definition.rust variable.other.rust - settings - - foreground - #e64553 - - - - name - Rust closure variables - scope - meta.function.call.rust variable.other.rust - settings - - foreground - #4c4f69 - - - - name - Rust self - scope - variable.language.self.rust - settings - - foreground - #d20f39 - - - - name - Rust metavariable names - scope - variable.other.metavariable.name.rust, meta.macro.metavariable.rust keyword.operator.macro.dollar.rust - settings - - foreground - #ea76cb - - - - name - Shell shebang - scope - comment.line.shebang, comment.line.shebang punctuation.definition.comment, comment.line.shebang, punctuation.definition.comment.shebang.shell, meta.shebang.shell - settings - - foreground - #ea76cb - fontStyle - italic - - - - name - Shell shebang command - scope - comment.line.shebang constant.language - settings - - foreground - #179299 - fontStyle - italic - - - - name - Shell interpolated command - scope - meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation, meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation - settings - - foreground - #d20f39 - - - - name - Shell interpolated command variable - scope - meta.string meta.interpolation.parameter.shell variable.other.readwrite - settings - - foreground - #fe640b - fontStyle - italic - - - - scope - source.shell punctuation.section.interpolation, punctuation.definition.evaluation.backticks.shell - settings - - foreground - #179299 - - - - name - Shell EOF - scope - entity.name.tag.heredoc.shell - settings - - foreground - #8839ef - - - - name - Shell quoted variable - scope - string.quoted.double.shell variable.other.normal.shell - settings - - foreground - #4c4f69 - - - - name - JSON Keys - scope - source.json meta.mapping.key string - settings - - foreground - #1e66f5 - - - - name - JSON key surrounding quotes - scope - source.json meta.mapping.key punctuation.definition.string.begin, source.json meta.mapping.key punctuation.definition.string.end - settings - - foreground - #7c7f93 - - - - scope - markup.heading.synopsis.man, markup.heading.title.man, markup.heading.other.man, markup.heading.env.man - settings - - foreground - #8839ef - - - - scope - markup.heading.commands.man - settings - - foreground - #1e66f5 - - - - scope - markup.heading.env.man - settings - - foreground - #ea76cb - - - - name - Man page options - scope - entity.name - settings - - foreground - #179299 - - - - scope - markup.heading.1.markdown - settings - - foreground - #d20f39 - - - - scope - markup.heading.2.markdown - settings - - foreground - #fe640b - - - - scope - markup.heading.markdown - settings - - foreground - #df8e1d - - - - - \ No newline at end of file diff --git a/.config/bat/themes/Catppuccin Macchiato.tmTheme b/.config/bat/themes/Catppuccin Macchiato.tmTheme deleted file mode 100644 index 2e82914..0000000 --- a/.config/bat/themes/Catppuccin Macchiato.tmTheme +++ /dev/null @@ -1,2081 +0,0 @@ - - - - - name - Catppuccin Macchiato - semanticClass - theme.dark.catppuccin-macchiato - uuid - 02b2bdf3-9eb7-4396-bf04-f17f1468f99f - author - Catppuccin Org - colorSpaceName - sRGB - settings - - - settings - - background - #24273a - foreground - #cad3f5 - caret - #f4dbd6 - lineHighlight - #363a4f - misspelling - #ed8796 - accent - #c6a0f6 - selection - #939ab740 - activeGuide - #494d64 - findHighlight - #455c6d - gutterForeground - #8087a2 - - - - name - Basic text & variable names (incl. leading punctuation) - scope - text, source, variable.other.readwrite, punctuation.definition.variable - settings - - foreground - #cad3f5 - - - - name - Parentheses, Brackets, Braces - scope - punctuation - settings - - foreground - #939ab7 - fontStyle - - - - - name - Comments - scope - comment, punctuation.definition.comment - settings - - foreground - #6e738d - fontStyle - italic - - - - scope - string, punctuation.definition.string - settings - - foreground - #a6da95 - - - - scope - constant.character.escape - settings - - foreground - #f5bde6 - - - - name - Booleans, constants, numbers - scope - constant.numeric, variable.other.constant, entity.name.constant, constant.language.boolean, constant.language.false, constant.language.true, keyword.other.unit.user-defined, keyword.other.unit.suffix.floating-point - settings - - foreground - #f5a97f - - - - scope - keyword, keyword.operator.word, keyword.operator.new, variable.language.super, support.type.primitive, storage.type, storage.modifier, punctuation.definition.keyword - settings - - foreground - #c6a0f6 - fontStyle - - - - - scope - entity.name.tag.documentation - settings - - foreground - #c6a0f6 - - - - name - Punctuation - scope - keyword.operator, punctuation.accessor, punctuation.definition.generic, meta.function.closure punctuation.section.parameters, punctuation.definition.tag, punctuation.separator.key-value - settings - - foreground - #8bd5ca - - - - scope - entity.name.function, meta.function-call.method, support.function, support.function.misc, variable.function - settings - - foreground - #8aadf4 - fontStyle - italic - - - - name - Classes - scope - entity.name.class, entity.other.inherited-class, support.class, meta.function-call.constructor, entity.name.struct - settings - - foreground - #eed49f - fontStyle - italic - - - - name - Enum - scope - entity.name.enum - settings - - foreground - #eed49f - fontStyle - italic - - - - name - Enum member - scope - meta.enum variable.other.readwrite, variable.other.enummember - settings - - foreground - #8bd5ca - - - - name - Object properties - scope - meta.property.object - settings - - foreground - #8bd5ca - - - - name - Types - scope - meta.type, meta.type-alias, support.type, entity.name.type - settings - - foreground - #eed49f - fontStyle - italic - - - - name - Decorators - scope - meta.annotation variable.function, meta.annotation variable.annotation.function, meta.annotation punctuation.definition.annotation, meta.decorator, punctuation.decorator - settings - - foreground - #f5a97f - - - - scope - variable.parameter, meta.function.parameters - settings - - foreground - #ee99a0 - fontStyle - italic - - - - name - Built-ins - scope - constant.language, support.function.builtin - settings - - foreground - #ed8796 - - - - scope - entity.other.attribute-name.documentation - settings - - foreground - #ed8796 - - - - name - Preprocessor directives - scope - keyword.control.directive, punctuation.definition.directive - settings - - foreground - #eed49f - - - - name - Type parameters - scope - punctuation.definition.typeparameters - settings - - foreground - #91d7e3 - - - - name - Namespaces - scope - entity.name.namespace - settings - - foreground - #eed49f - - - - name - Property names (left hand assignments in json/yaml/css) - scope - support.type.property-name.css - settings - - foreground - #8aadf4 - fontStyle - - - - - name - This/Self keyword - scope - variable.language.this, variable.language.this punctuation.definition.variable - settings - - foreground - #ed8796 - - - - name - Object properties - scope - variable.object.property - settings - - foreground - #cad3f5 - - - - name - String template interpolation - scope - string.template variable, string variable - settings - - foreground - #cad3f5 - - - - name - `new` as bold - scope - keyword.operator.new - settings - - fontStyle - bold - - - - name - C++ extern keyword - scope - storage.modifier.specifier.extern.cpp - settings - - foreground - #c6a0f6 - - - - name - C++ scope resolution - scope - entity.name.scope-resolution.template.call.cpp, entity.name.scope-resolution.parameter.cpp, entity.name.scope-resolution.cpp, entity.name.scope-resolution.function.definition.cpp - settings - - foreground - #eed49f - - - - name - C++ doc keywords - scope - storage.type.class.doxygen - settings - - fontStyle - - - - - name - C++ operators - scope - storage.modifier.reference.cpp - settings - - foreground - #8bd5ca - - - - name - C# Interpolated Strings - scope - meta.interpolation.cs - settings - - foreground - #cad3f5 - - - - name - C# xml-style docs - scope - comment.block.documentation.cs - settings - - foreground - #cad3f5 - - - - name - Classes, reflecting the className color in JSX - scope - source.css entity.other.attribute-name.class.css, entity.other.attribute-name.parent-selector.css punctuation.definition.entity.css - settings - - foreground - #eed49f - - - - name - Operators - scope - punctuation.separator.operator.css - settings - - foreground - #8bd5ca - - - - name - Pseudo classes - scope - source.css entity.other.attribute-name.pseudo-class - settings - - foreground - #8bd5ca - - - - scope - source.css constant.other.unicode-range - settings - - foreground - #f5a97f - - - - scope - source.css variable.parameter.url - settings - - foreground - #a6da95 - fontStyle - - - - - name - CSS vendored property names - scope - support.type.vendored.property-name - settings - - foreground - #91d7e3 - - - - name - Less/SCSS right-hand variables (@/$-prefixed) - scope - source.css meta.property-value variable, source.css meta.property-value variable.other.less, source.css meta.property-value variable.other.less punctuation.definition.variable.less, meta.definition.variable.scss - settings - - foreground - #ee99a0 - - - - name - CSS variables (--prefixed) - scope - source.css meta.property-list variable, meta.property-list variable.other.less, meta.property-list variable.other.less punctuation.definition.variable.less - settings - - foreground - #8aadf4 - - - - name - CSS Percentage values, styled the same as numbers - scope - keyword.other.unit.percentage.css - settings - - foreground - #f5a97f - - - - name - CSS Attribute selectors, styled the same as strings - scope - source.css meta.attribute-selector - settings - - foreground - #a6da95 - - - - name - JSON/YAML keys, other left-hand assignments - scope - keyword.other.definition.ini, punctuation.support.type.property-name.json, support.type.property-name.json, punctuation.support.type.property-name.toml, support.type.property-name.toml, entity.name.tag.yaml, punctuation.support.type.property-name.yaml, support.type.property-name.yaml - settings - - foreground - #8aadf4 - fontStyle - - - - - name - JSON/YAML constants - scope - constant.language.json, constant.language.yaml - settings - - foreground - #f5a97f - - - - name - YAML anchors - scope - entity.name.type.anchor.yaml, variable.other.alias.yaml - settings - - foreground - #eed49f - fontStyle - - - - - name - TOML tables / ini groups - scope - support.type.property-name.table, entity.name.section.group-title.ini - settings - - foreground - #eed49f - - - - name - TOML dates - scope - constant.other.time.datetime.offset.toml - settings - - foreground - #f5bde6 - - - - name - YAML anchor puctuation - scope - punctuation.definition.anchor.yaml, punctuation.definition.alias.yaml - settings - - foreground - #f5bde6 - - - - name - YAML triple dashes - scope - entity.other.document.begin.yaml - settings - - foreground - #f5bde6 - - - - name - Markup Diff - scope - markup.changed.diff - settings - - foreground - #f5a97f - - - - name - Diff - scope - meta.diff.header.from-file, meta.diff.header.to-file, punctuation.definition.from-file.diff, punctuation.definition.to-file.diff - settings - - foreground - #8aadf4 - - - - name - Diff Inserted - scope - markup.inserted.diff - settings - - foreground - #a6da95 - - - - name - Diff Deleted - scope - markup.deleted.diff - settings - - foreground - #ed8796 - - - - name - dotenv left-hand side assignments - scope - variable.other.env - settings - - foreground - #8aadf4 - - - - name - dotenv reference to existing env variable - scope - string.quoted variable.other.env - settings - - foreground - #cad3f5 - - - - name - GDScript functions - scope - support.function.builtin.gdscript - settings - - foreground - #8aadf4 - - - - name - GDScript constants - scope - constant.language.gdscript - settings - - foreground - #f5a97f - - - - name - Comment keywords - scope - comment meta.annotation.go - settings - - foreground - #ee99a0 - - - - name - go:embed, go:build, etc. - scope - comment meta.annotation.parameters.go - settings - - foreground - #f5a97f - - - - name - Go constants (nil, true, false) - scope - constant.language.go - settings - - foreground - #f5a97f - - - - name - GraphQL variables - scope - variable.graphql - settings - - foreground - #cad3f5 - - - - name - GraphQL aliases - scope - string.unquoted.alias.graphql - settings - - foreground - #f0c6c6 - - - - name - GraphQL enum members - scope - constant.character.enum.graphql - settings - - foreground - #8bd5ca - - - - name - GraphQL field in types - scope - meta.objectvalues.graphql constant.object.key.graphql string.unquoted.graphql - settings - - foreground - #f0c6c6 - - - - name - HTML/XML DOCTYPE as keyword - scope - keyword.other.doctype, meta.tag.sgml.doctype punctuation.definition.tag, meta.tag.metadata.doctype entity.name.tag, meta.tag.metadata.doctype punctuation.definition.tag - settings - - foreground - #c6a0f6 - - - - name - HTML/XML-like <tags/> - scope - entity.name.tag - settings - - foreground - #8aadf4 - fontStyle - - - - - name - Special characters like &amp; - scope - text.html constant.character.entity, text.html constant.character.entity punctuation, constant.character.entity.xml, constant.character.entity.xml punctuation, constant.character.entity.js.jsx, constant.charactger.entity.js.jsx punctuation, constant.character.entity.tsx, constant.character.entity.tsx punctuation - settings - - foreground - #ed8796 - - - - name - HTML/XML tag attribute values - scope - entity.other.attribute-name - settings - - foreground - #eed49f - - - - name - Components - scope - support.class.component, support.class.component.jsx, support.class.component.tsx, support.class.component.vue - settings - - foreground - #f5bde6 - fontStyle - - - - - name - Annotations - scope - punctuation.definition.annotation, storage.type.annotation - settings - - foreground - #f5a97f - - - - name - Java enums - scope - constant.other.enum.java - settings - - foreground - #8bd5ca - - - - name - Java imports - scope - storage.modifier.import.java - settings - - foreground - #cad3f5 - - - - name - Javadoc - scope - comment.block.javadoc.java keyword.other.documentation.javadoc.java - settings - - fontStyle - - - - - name - Exported Variable - scope - meta.export variable.other.readwrite.js - settings - - foreground - #ee99a0 - - - - name - JS/TS constants & properties - scope - variable.other.constant.js, variable.other.constant.ts, variable.other.property.js, variable.other.property.ts - settings - - foreground - #cad3f5 - - - - name - JSDoc; these are mainly params, so styled as such - scope - variable.other.jsdoc, comment.block.documentation variable.other - settings - - foreground - #ee99a0 - fontStyle - - - - - name - JSDoc keywords - scope - storage.type.class.jsdoc - settings - - fontStyle - - - - - scope - support.type.object.console.js - settings - - foreground - #cad3f5 - - - - name - Node constants as keywords (module, etc.) - scope - support.constant.node, support.type.object.module.js - settings - - foreground - #c6a0f6 - - - - name - implements as keyword - scope - storage.modifier.implements - settings - - foreground - #c6a0f6 - - - - name - Builtin types - scope - constant.language.null.js, constant.language.null.ts, constant.language.undefined.js, constant.language.undefined.ts, support.type.builtin.ts - settings - - foreground - #c6a0f6 - - - - scope - variable.parameter.generic - settings - - foreground - #eed49f - - - - name - Arrow functions - scope - keyword.declaration.function.arrow.js, storage.type.function.arrow.ts - settings - - foreground - #8bd5ca - - - - name - Decorator punctuations (decorators inherit from blue functions, instead of styleguide peach) - scope - punctuation.decorator.ts - settings - - foreground - #8aadf4 - fontStyle - italic - - - - name - Extra JS/TS keywords - scope - keyword.operator.expression.in.js, keyword.operator.expression.in.ts, keyword.operator.expression.infer.ts, keyword.operator.expression.instanceof.js, keyword.operator.expression.instanceof.ts, keyword.operator.expression.is, keyword.operator.expression.keyof.ts, keyword.operator.expression.of.js, keyword.operator.expression.of.ts, keyword.operator.expression.typeof.ts - settings - - foreground - #c6a0f6 - - - - name - Julia macros - scope - support.function.macro.julia - settings - - foreground - #8bd5ca - fontStyle - italic - - - - name - Julia language constants (true, false) - scope - constant.language.julia - settings - - foreground - #f5a97f - - - - name - Julia other constants (these seem to be arguments inside arrays) - scope - constant.other.symbol.julia - settings - - foreground - #ee99a0 - - - - name - LaTeX preamble - scope - text.tex keyword.control.preamble - settings - - foreground - #8bd5ca - - - - name - LaTeX be functions - scope - text.tex support.function.be - settings - - foreground - #91d7e3 - - - - name - LaTeX math - scope - constant.other.general.math.tex - settings - - foreground - #f0c6c6 - - - - name - Lua docstring keywords - scope - comment.line.double-dash.documentation.lua storage.type.annotation.lua - settings - - foreground - #c6a0f6 - fontStyle - - - - - name - Lua docstring variables - scope - comment.line.double-dash.documentation.lua entity.name.variable.lua, comment.line.double-dash.documentation.lua variable.lua - settings - - foreground - #cad3f5 - - - - scope - heading.1.markdown punctuation.definition.heading.markdown, heading.1.markdown, markup.heading.atx.1.mdx, markup.heading.atx.1.mdx punctuation.definition.heading.mdx, markup.heading.setext.1.markdown, markup.heading.heading-0.asciidoc - settings - - foreground - #ed8796 - - - - scope - heading.2.markdown punctuation.definition.heading.markdown, heading.2.markdown, markup.heading.atx.2.mdx, markup.heading.atx.2.mdx punctuation.definition.heading.mdx, markup.heading.setext.2.markdown, markup.heading.heading-1.asciidoc - settings - - foreground - #f5a97f - - - - scope - heading.3.markdown punctuation.definition.heading.markdown, heading.3.markdown, markup.heading.atx.3.mdx, markup.heading.atx.3.mdx punctuation.definition.heading.mdx, markup.heading.heading-2.asciidoc - settings - - foreground - #eed49f - - - - scope - heading.4.markdown punctuation.definition.heading.markdown, heading.4.markdown, markup.heading.atx.4.mdx, markup.heading.atx.4.mdx punctuation.definition.heading.mdx, markup.heading.heading-3.asciidoc - settings - - foreground - #a6da95 - - - - scope - heading.5.markdown punctuation.definition.heading.markdown, heading.5.markdown, markup.heading.atx.5.mdx, markup.heading.atx.5.mdx punctuation.definition.heading.mdx, markup.heading.heading-4.asciidoc - settings - - foreground - #8aadf4 - - - - scope - heading.6.markdown punctuation.definition.heading.markdown, heading.6.markdown, markup.heading.atx.6.mdx, markup.heading.atx.6.mdx punctuation.definition.heading.mdx, markup.heading.heading-5.asciidoc - settings - - foreground - #c6a0f6 - - - - scope - markup.bold - settings - - foreground - #ed8796 - fontStyle - bold - - - - scope - markup.italic - settings - - foreground - #ed8796 - fontStyle - italic - - - - scope - markup.strikethrough - settings - - foreground - #a5adcb - fontStyle - strikethrough - - - - name - Markdown auto links - scope - punctuation.definition.link, markup.underline.link - settings - - foreground - #8aadf4 - - - - name - Markdown links - scope - text.html.markdown punctuation.definition.link.title, string.other.link.title.markdown, markup.link, punctuation.definition.constant.markdown, constant.other.reference.link.markdown, markup.substitution.attribute-reference - settings - - foreground - #b7bdf8 - - - - name - Markdown code spans - scope - punctuation.definition.raw.markdown, markup.inline.raw.string.markdown, markup.raw.block.markdown - settings - - foreground - #a6da95 - - - - name - Markdown triple backtick language identifier - scope - fenced_code.block.language - settings - - foreground - #91d7e3 - - - - name - Markdown triple backticks - scope - markup.fenced_code.block punctuation.definition, markup.raw support.asciidoc - settings - - foreground - #939ab7 - - - - name - Markdown quotes - scope - markup.quote, punctuation.definition.quote.begin - settings - - foreground - #f5bde6 - - - - name - Markdown separators - scope - meta.separator.markdown - settings - - foreground - #8bd5ca - - - - name - Markdown list bullets - scope - punctuation.definition.list.begin.markdown, markup.list.bullet - settings - - foreground - #8bd5ca - - - - name - Nix attribute names - scope - entity.other.attribute-name.multipart.nix, entity.other.attribute-name.single.nix - settings - - foreground - #8aadf4 - - - - name - Nix parameter names - scope - variable.parameter.name.nix - settings - - foreground - #cad3f5 - fontStyle - - - - - name - Nix interpolated parameter names - scope - meta.embedded variable.parameter.name.nix - settings - - foreground - #b7bdf8 - fontStyle - - - - - name - Nix paths - scope - string.unquoted.path.nix - settings - - foreground - #f5bde6 - fontStyle - - - - - name - PHP Attributes - scope - support.attribute.builtin, meta.attribute.php - settings - - foreground - #eed49f - - - - name - PHP Parameters (needed for the leading dollar sign) - scope - meta.function.parameters.php punctuation.definition.variable.php - settings - - foreground - #ee99a0 - - - - name - PHP Constants (null, __FILE__, etc.) - scope - constant.language.php - settings - - foreground - #c6a0f6 - - - - name - PHP functions - scope - text.html.php support.function - settings - - foreground - #91d7e3 - - - - name - PHPdoc keywords - scope - keyword.other.phpdoc.php - settings - - fontStyle - - - - - name - Python argument functions reset to text, otherwise they inherit blue from function-call - scope - support.variable.magic.python, meta.function-call.arguments.python - settings - - foreground - #cad3f5 - - - - name - Python double underscore functions - scope - support.function.magic.python - settings - - foreground - #91d7e3 - fontStyle - italic - - - - name - Python `self` keyword - scope - variable.parameter.function.language.special.self.python, variable.language.special.self.python - settings - - foreground - #ed8796 - fontStyle - italic - - - - name - python keyword flow/logical (for ... in) - scope - keyword.control.flow.python, keyword.operator.logical.python - settings - - foreground - #c6a0f6 - - - - name - python storage type - scope - storage.type.function.python - settings - - foreground - #c6a0f6 - - - - name - python function support - scope - support.token.decorator.python, meta.function.decorator.identifier.python - settings - - foreground - #91d7e3 - - - - name - python function calls - scope - meta.function-call.python - settings - - foreground - #8aadf4 - - - - name - python function decorators - scope - entity.name.function.decorator.python, punctuation.definition.decorator.python - settings - - foreground - #f5a97f - fontStyle - italic - - - - name - python placeholder reset to normal string - scope - constant.character.format.placeholder.other.python - settings - - foreground - #f5bde6 - - - - name - Python exception & builtins such as exit() - scope - support.type.exception.python, support.function.builtin.python - settings - - foreground - #f5a97f - - - - name - entity.name.type - scope - support.type.python - settings - - foreground - #f5a97f - - - - name - python constants (True/False) - scope - constant.language.python - settings - - foreground - #c6a0f6 - - - - name - Arguments accessed later in the function body - scope - meta.indexed-name.python, meta.item-access.python - settings - - foreground - #ee99a0 - fontStyle - italic - - - - name - Python f-strings/binary/unicode storage types - scope - storage.type.string.python - settings - - foreground - #a6da95 - fontStyle - italic - - - - name - Python type hints - scope - meta.function.parameters.python - settings - - fontStyle - - - - - name - Regex string begin/end in JS/TS - scope - string.regexp punctuation.definition.string.begin, string.regexp punctuation.definition.string.end - settings - - foreground - #f5bde6 - - - - name - Regex anchors (^, $) - scope - keyword.control.anchor.regexp - settings - - foreground - #c6a0f6 - - - - name - Regex regular string match - scope - string.regexp.ts - settings - - foreground - #cad3f5 - - - - name - Regex group parenthesis & backreference (\1, \2, \3, ...) - scope - punctuation.definition.group.regexp, keyword.other.back-reference.regexp - settings - - foreground - #a6da95 - - - - name - Regex character class [] - scope - punctuation.definition.character-class.regexp - settings - - foreground - #eed49f - - - - name - Regex character classes (\d, \w, \s) - scope - constant.other.character-class.regexp - settings - - foreground - #f5bde6 - - - - name - Regex range - scope - constant.other.character-class.range.regexp - settings - - foreground - #f4dbd6 - - - - name - Regex quantifier - scope - keyword.operator.quantifier.regexp - settings - - foreground - #8bd5ca - - - - name - Regex constant/numeric - scope - constant.character.numeric.regexp - settings - - foreground - #f5a97f - - - - name - Regex lookaheads, negative lookaheads, lookbehinds, negative lookbehinds - scope - punctuation.definition.group.no-capture.regexp, meta.assertion.look-ahead.regexp, meta.assertion.negative-look-ahead.regexp - settings - - foreground - #8aadf4 - - - - name - Rust attribute - scope - meta.annotation.rust, meta.annotation.rust punctuation, meta.attribute.rust, punctuation.definition.attribute.rust - settings - - foreground - #eed49f - fontStyle - italic - - - - name - Rust attribute strings - scope - meta.attribute.rust string.quoted.double.rust, meta.attribute.rust string.quoted.single.char.rust - settings - - fontStyle - - - - - name - Rust keyword - scope - entity.name.function.macro.rules.rust, storage.type.module.rust, storage.modifier.rust, storage.type.struct.rust, storage.type.enum.rust, storage.type.trait.rust, storage.type.union.rust, storage.type.impl.rust, storage.type.rust, storage.type.function.rust, storage.type.type.rust - settings - - foreground - #c6a0f6 - fontStyle - - - - - name - Rust u/i32, u/i64, etc. - scope - entity.name.type.numeric.rust - settings - - foreground - #c6a0f6 - fontStyle - - - - - name - Rust generic - scope - meta.generic.rust - settings - - foreground - #f5a97f - - - - name - Rust impl - scope - entity.name.impl.rust - settings - - foreground - #eed49f - fontStyle - italic - - - - name - Rust module - scope - entity.name.module.rust - settings - - foreground - #f5a97f - - - - name - Rust trait - scope - entity.name.trait.rust - settings - - foreground - #eed49f - fontStyle - italic - - - - name - Rust struct - scope - storage.type.source.rust - settings - - foreground - #eed49f - - - - name - Rust union - scope - entity.name.union.rust - settings - - foreground - #eed49f - - - - name - Rust enum member - scope - meta.enum.rust storage.type.source.rust - settings - - foreground - #8bd5ca - - - - name - Rust macro - scope - support.macro.rust, meta.macro.rust support.function.rust, entity.name.function.macro.rust - settings - - foreground - #8aadf4 - fontStyle - italic - - - - name - Rust lifetime - scope - storage.modifier.lifetime.rust, entity.name.type.lifetime - settings - - foreground - #8aadf4 - fontStyle - italic - - - - name - Rust string formatting - scope - string.quoted.double.rust constant.other.placeholder.rust - settings - - foreground - #f5bde6 - - - - name - Rust return type generic - scope - meta.function.return-type.rust meta.generic.rust storage.type.rust - settings - - foreground - #cad3f5 - - - - name - Rust functions - scope - meta.function.call.rust - settings - - foreground - #8aadf4 - - - - name - Rust angle brackets - scope - punctuation.brackets.angle.rust - settings - - foreground - #91d7e3 - - - - name - Rust constants - scope - constant.other.caps.rust - settings - - foreground - #f5a97f - - - - name - Rust function parameters - scope - meta.function.definition.rust variable.other.rust - settings - - foreground - #ee99a0 - - - - name - Rust closure variables - scope - meta.function.call.rust variable.other.rust - settings - - foreground - #cad3f5 - - - - name - Rust self - scope - variable.language.self.rust - settings - - foreground - #ed8796 - - - - name - Rust metavariable names - scope - variable.other.metavariable.name.rust, meta.macro.metavariable.rust keyword.operator.macro.dollar.rust - settings - - foreground - #f5bde6 - - - - name - Shell shebang - scope - comment.line.shebang, comment.line.shebang punctuation.definition.comment, comment.line.shebang, punctuation.definition.comment.shebang.shell, meta.shebang.shell - settings - - foreground - #f5bde6 - fontStyle - italic - - - - name - Shell shebang command - scope - comment.line.shebang constant.language - settings - - foreground - #8bd5ca - fontStyle - italic - - - - name - Shell interpolated command - scope - meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation, meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation - settings - - foreground - #ed8796 - - - - name - Shell interpolated command variable - scope - meta.string meta.interpolation.parameter.shell variable.other.readwrite - settings - - foreground - #f5a97f - fontStyle - italic - - - - scope - source.shell punctuation.section.interpolation, punctuation.definition.evaluation.backticks.shell - settings - - foreground - #8bd5ca - - - - name - Shell EOF - scope - entity.name.tag.heredoc.shell - settings - - foreground - #c6a0f6 - - - - name - Shell quoted variable - scope - string.quoted.double.shell variable.other.normal.shell - settings - - foreground - #cad3f5 - - - - name - JSON Keys - scope - source.json meta.mapping.key string - settings - - foreground - #8aadf4 - - - - name - JSON key surrounding quotes - scope - source.json meta.mapping.key punctuation.definition.string.begin, source.json meta.mapping.key punctuation.definition.string.end - settings - - foreground - #939ab7 - - - - scope - markup.heading.synopsis.man, markup.heading.title.man, markup.heading.other.man, markup.heading.env.man - settings - - foreground - #c6a0f6 - - - - scope - markup.heading.commands.man - settings - - foreground - #8aadf4 - - - - scope - markup.heading.env.man - settings - - foreground - #f5bde6 - - - - name - Man page options - scope - entity.name - settings - - foreground - #8bd5ca - - - - scope - markup.heading.1.markdown - settings - - foreground - #ed8796 - - - - scope - markup.heading.2.markdown - settings - - foreground - #f5a97f - - - - scope - markup.heading.markdown - settings - - foreground - #eed49f - - - - - \ No newline at end of file diff --git a/.config/bat/themes/Catppuccin Mocha.tmTheme b/.config/bat/themes/Catppuccin Mocha.tmTheme deleted file mode 100644 index 6bd0f29..0000000 --- a/.config/bat/themes/Catppuccin Mocha.tmTheme +++ /dev/null @@ -1,2081 +0,0 @@ - - - - - name - Catppuccin Mocha - semanticClass - theme.dark.catppuccin-mocha - uuid - 627ce890-fabb-4d39-9819-7be71f4bdca7 - author - Catppuccin Org - colorSpaceName - sRGB - settings - - - settings - - background - #1e1e2e - foreground - #cdd6f4 - caret - #f5e0dc - lineHighlight - #313244 - misspelling - #f38ba8 - accent - #cba6f7 - selection - #9399b240 - activeGuide - #45475a - findHighlight - #3e5767 - gutterForeground - #7f849c - - - - name - Basic text & variable names (incl. leading punctuation) - scope - text, source, variable.other.readwrite, punctuation.definition.variable - settings - - foreground - #cdd6f4 - - - - name - Parentheses, Brackets, Braces - scope - punctuation - settings - - foreground - #9399b2 - fontStyle - - - - - name - Comments - scope - comment, punctuation.definition.comment - settings - - foreground - #6c7086 - fontStyle - italic - - - - scope - string, punctuation.definition.string - settings - - foreground - #a6e3a1 - - - - scope - constant.character.escape - settings - - foreground - #f5c2e7 - - - - name - Booleans, constants, numbers - scope - constant.numeric, variable.other.constant, entity.name.constant, constant.language.boolean, constant.language.false, constant.language.true, keyword.other.unit.user-defined, keyword.other.unit.suffix.floating-point - settings - - foreground - #fab387 - - - - scope - keyword, keyword.operator.word, keyword.operator.new, variable.language.super, support.type.primitive, storage.type, storage.modifier, punctuation.definition.keyword - settings - - foreground - #cba6f7 - fontStyle - - - - - scope - entity.name.tag.documentation - settings - - foreground - #cba6f7 - - - - name - Punctuation - scope - keyword.operator, punctuation.accessor, punctuation.definition.generic, meta.function.closure punctuation.section.parameters, punctuation.definition.tag, punctuation.separator.key-value - settings - - foreground - #94e2d5 - - - - scope - entity.name.function, meta.function-call.method, support.function, support.function.misc, variable.function - settings - - foreground - #89b4fa - fontStyle - italic - - - - name - Classes - scope - entity.name.class, entity.other.inherited-class, support.class, meta.function-call.constructor, entity.name.struct - settings - - foreground - #f9e2af - fontStyle - italic - - - - name - Enum - scope - entity.name.enum - settings - - foreground - #f9e2af - fontStyle - italic - - - - name - Enum member - scope - meta.enum variable.other.readwrite, variable.other.enummember - settings - - foreground - #94e2d5 - - - - name - Object properties - scope - meta.property.object - settings - - foreground - #94e2d5 - - - - name - Types - scope - meta.type, meta.type-alias, support.type, entity.name.type - settings - - foreground - #f9e2af - fontStyle - italic - - - - name - Decorators - scope - meta.annotation variable.function, meta.annotation variable.annotation.function, meta.annotation punctuation.definition.annotation, meta.decorator, punctuation.decorator - settings - - foreground - #fab387 - - - - scope - variable.parameter, meta.function.parameters - settings - - foreground - #eba0ac - fontStyle - italic - - - - name - Built-ins - scope - constant.language, support.function.builtin - settings - - foreground - #f38ba8 - - - - scope - entity.other.attribute-name.documentation - settings - - foreground - #f38ba8 - - - - name - Preprocessor directives - scope - keyword.control.directive, punctuation.definition.directive - settings - - foreground - #f9e2af - - - - name - Type parameters - scope - punctuation.definition.typeparameters - settings - - foreground - #89dceb - - - - name - Namespaces - scope - entity.name.namespace - settings - - foreground - #f9e2af - - - - name - Property names (left hand assignments in json/yaml/css) - scope - support.type.property-name.css - settings - - foreground - #89b4fa - fontStyle - - - - - name - This/Self keyword - scope - variable.language.this, variable.language.this punctuation.definition.variable - settings - - foreground - #f38ba8 - - - - name - Object properties - scope - variable.object.property - settings - - foreground - #cdd6f4 - - - - name - String template interpolation - scope - string.template variable, string variable - settings - - foreground - #cdd6f4 - - - - name - `new` as bold - scope - keyword.operator.new - settings - - fontStyle - bold - - - - name - C++ extern keyword - scope - storage.modifier.specifier.extern.cpp - settings - - foreground - #cba6f7 - - - - name - C++ scope resolution - scope - entity.name.scope-resolution.template.call.cpp, entity.name.scope-resolution.parameter.cpp, entity.name.scope-resolution.cpp, entity.name.scope-resolution.function.definition.cpp - settings - - foreground - #f9e2af - - - - name - C++ doc keywords - scope - storage.type.class.doxygen - settings - - fontStyle - - - - - name - C++ operators - scope - storage.modifier.reference.cpp - settings - - foreground - #94e2d5 - - - - name - C# Interpolated Strings - scope - meta.interpolation.cs - settings - - foreground - #cdd6f4 - - - - name - C# xml-style docs - scope - comment.block.documentation.cs - settings - - foreground - #cdd6f4 - - - - name - Classes, reflecting the className color in JSX - scope - source.css entity.other.attribute-name.class.css, entity.other.attribute-name.parent-selector.css punctuation.definition.entity.css - settings - - foreground - #f9e2af - - - - name - Operators - scope - punctuation.separator.operator.css - settings - - foreground - #94e2d5 - - - - name - Pseudo classes - scope - source.css entity.other.attribute-name.pseudo-class - settings - - foreground - #94e2d5 - - - - scope - source.css constant.other.unicode-range - settings - - foreground - #fab387 - - - - scope - source.css variable.parameter.url - settings - - foreground - #a6e3a1 - fontStyle - - - - - name - CSS vendored property names - scope - support.type.vendored.property-name - settings - - foreground - #89dceb - - - - name - Less/SCSS right-hand variables (@/$-prefixed) - scope - source.css meta.property-value variable, source.css meta.property-value variable.other.less, source.css meta.property-value variable.other.less punctuation.definition.variable.less, meta.definition.variable.scss - settings - - foreground - #eba0ac - - - - name - CSS variables (--prefixed) - scope - source.css meta.property-list variable, meta.property-list variable.other.less, meta.property-list variable.other.less punctuation.definition.variable.less - settings - - foreground - #89b4fa - - - - name - CSS Percentage values, styled the same as numbers - scope - keyword.other.unit.percentage.css - settings - - foreground - #fab387 - - - - name - CSS Attribute selectors, styled the same as strings - scope - source.css meta.attribute-selector - settings - - foreground - #a6e3a1 - - - - name - JSON/YAML keys, other left-hand assignments - scope - keyword.other.definition.ini, punctuation.support.type.property-name.json, support.type.property-name.json, punctuation.support.type.property-name.toml, support.type.property-name.toml, entity.name.tag.yaml, punctuation.support.type.property-name.yaml, support.type.property-name.yaml - settings - - foreground - #89b4fa - fontStyle - - - - - name - JSON/YAML constants - scope - constant.language.json, constant.language.yaml - settings - - foreground - #fab387 - - - - name - YAML anchors - scope - entity.name.type.anchor.yaml, variable.other.alias.yaml - settings - - foreground - #f9e2af - fontStyle - - - - - name - TOML tables / ini groups - scope - support.type.property-name.table, entity.name.section.group-title.ini - settings - - foreground - #f9e2af - - - - name - TOML dates - scope - constant.other.time.datetime.offset.toml - settings - - foreground - #f5c2e7 - - - - name - YAML anchor puctuation - scope - punctuation.definition.anchor.yaml, punctuation.definition.alias.yaml - settings - - foreground - #f5c2e7 - - - - name - YAML triple dashes - scope - entity.other.document.begin.yaml - settings - - foreground - #f5c2e7 - - - - name - Markup Diff - scope - markup.changed.diff - settings - - foreground - #fab387 - - - - name - Diff - scope - meta.diff.header.from-file, meta.diff.header.to-file, punctuation.definition.from-file.diff, punctuation.definition.to-file.diff - settings - - foreground - #89b4fa - - - - name - Diff Inserted - scope - markup.inserted.diff - settings - - foreground - #a6e3a1 - - - - name - Diff Deleted - scope - markup.deleted.diff - settings - - foreground - #f38ba8 - - - - name - dotenv left-hand side assignments - scope - variable.other.env - settings - - foreground - #89b4fa - - - - name - dotenv reference to existing env variable - scope - string.quoted variable.other.env - settings - - foreground - #cdd6f4 - - - - name - GDScript functions - scope - support.function.builtin.gdscript - settings - - foreground - #89b4fa - - - - name - GDScript constants - scope - constant.language.gdscript - settings - - foreground - #fab387 - - - - name - Comment keywords - scope - comment meta.annotation.go - settings - - foreground - #eba0ac - - - - name - go:embed, go:build, etc. - scope - comment meta.annotation.parameters.go - settings - - foreground - #fab387 - - - - name - Go constants (nil, true, false) - scope - constant.language.go - settings - - foreground - #fab387 - - - - name - GraphQL variables - scope - variable.graphql - settings - - foreground - #cdd6f4 - - - - name - GraphQL aliases - scope - string.unquoted.alias.graphql - settings - - foreground - #f2cdcd - - - - name - GraphQL enum members - scope - constant.character.enum.graphql - settings - - foreground - #94e2d5 - - - - name - GraphQL field in types - scope - meta.objectvalues.graphql constant.object.key.graphql string.unquoted.graphql - settings - - foreground - #f2cdcd - - - - name - HTML/XML DOCTYPE as keyword - scope - keyword.other.doctype, meta.tag.sgml.doctype punctuation.definition.tag, meta.tag.metadata.doctype entity.name.tag, meta.tag.metadata.doctype punctuation.definition.tag - settings - - foreground - #cba6f7 - - - - name - HTML/XML-like <tags/> - scope - entity.name.tag - settings - - foreground - #89b4fa - fontStyle - - - - - name - Special characters like &amp; - scope - text.html constant.character.entity, text.html constant.character.entity punctuation, constant.character.entity.xml, constant.character.entity.xml punctuation, constant.character.entity.js.jsx, constant.charactger.entity.js.jsx punctuation, constant.character.entity.tsx, constant.character.entity.tsx punctuation - settings - - foreground - #f38ba8 - - - - name - HTML/XML tag attribute values - scope - entity.other.attribute-name - settings - - foreground - #f9e2af - - - - name - Components - scope - support.class.component, support.class.component.jsx, support.class.component.tsx, support.class.component.vue - settings - - foreground - #f5c2e7 - fontStyle - - - - - name - Annotations - scope - punctuation.definition.annotation, storage.type.annotation - settings - - foreground - #fab387 - - - - name - Java enums - scope - constant.other.enum.java - settings - - foreground - #94e2d5 - - - - name - Java imports - scope - storage.modifier.import.java - settings - - foreground - #cdd6f4 - - - - name - Javadoc - scope - comment.block.javadoc.java keyword.other.documentation.javadoc.java - settings - - fontStyle - - - - - name - Exported Variable - scope - meta.export variable.other.readwrite.js - settings - - foreground - #eba0ac - - - - name - JS/TS constants & properties - scope - variable.other.constant.js, variable.other.constant.ts, variable.other.property.js, variable.other.property.ts - settings - - foreground - #cdd6f4 - - - - name - JSDoc; these are mainly params, so styled as such - scope - variable.other.jsdoc, comment.block.documentation variable.other - settings - - foreground - #eba0ac - fontStyle - - - - - name - JSDoc keywords - scope - storage.type.class.jsdoc - settings - - fontStyle - - - - - scope - support.type.object.console.js - settings - - foreground - #cdd6f4 - - - - name - Node constants as keywords (module, etc.) - scope - support.constant.node, support.type.object.module.js - settings - - foreground - #cba6f7 - - - - name - implements as keyword - scope - storage.modifier.implements - settings - - foreground - #cba6f7 - - - - name - Builtin types - scope - constant.language.null.js, constant.language.null.ts, constant.language.undefined.js, constant.language.undefined.ts, support.type.builtin.ts - settings - - foreground - #cba6f7 - - - - scope - variable.parameter.generic - settings - - foreground - #f9e2af - - - - name - Arrow functions - scope - keyword.declaration.function.arrow.js, storage.type.function.arrow.ts - settings - - foreground - #94e2d5 - - - - name - Decorator punctuations (decorators inherit from blue functions, instead of styleguide peach) - scope - punctuation.decorator.ts - settings - - foreground - #89b4fa - fontStyle - italic - - - - name - Extra JS/TS keywords - scope - keyword.operator.expression.in.js, keyword.operator.expression.in.ts, keyword.operator.expression.infer.ts, keyword.operator.expression.instanceof.js, keyword.operator.expression.instanceof.ts, keyword.operator.expression.is, keyword.operator.expression.keyof.ts, keyword.operator.expression.of.js, keyword.operator.expression.of.ts, keyword.operator.expression.typeof.ts - settings - - foreground - #cba6f7 - - - - name - Julia macros - scope - support.function.macro.julia - settings - - foreground - #94e2d5 - fontStyle - italic - - - - name - Julia language constants (true, false) - scope - constant.language.julia - settings - - foreground - #fab387 - - - - name - Julia other constants (these seem to be arguments inside arrays) - scope - constant.other.symbol.julia - settings - - foreground - #eba0ac - - - - name - LaTeX preamble - scope - text.tex keyword.control.preamble - settings - - foreground - #94e2d5 - - - - name - LaTeX be functions - scope - text.tex support.function.be - settings - - foreground - #89dceb - - - - name - LaTeX math - scope - constant.other.general.math.tex - settings - - foreground - #f2cdcd - - - - name - Lua docstring keywords - scope - comment.line.double-dash.documentation.lua storage.type.annotation.lua - settings - - foreground - #cba6f7 - fontStyle - - - - - name - Lua docstring variables - scope - comment.line.double-dash.documentation.lua entity.name.variable.lua, comment.line.double-dash.documentation.lua variable.lua - settings - - foreground - #cdd6f4 - - - - scope - heading.1.markdown punctuation.definition.heading.markdown, heading.1.markdown, markup.heading.atx.1.mdx, markup.heading.atx.1.mdx punctuation.definition.heading.mdx, markup.heading.setext.1.markdown, markup.heading.heading-0.asciidoc - settings - - foreground - #f38ba8 - - - - scope - heading.2.markdown punctuation.definition.heading.markdown, heading.2.markdown, markup.heading.atx.2.mdx, markup.heading.atx.2.mdx punctuation.definition.heading.mdx, markup.heading.setext.2.markdown, markup.heading.heading-1.asciidoc - settings - - foreground - #fab387 - - - - scope - heading.3.markdown punctuation.definition.heading.markdown, heading.3.markdown, markup.heading.atx.3.mdx, markup.heading.atx.3.mdx punctuation.definition.heading.mdx, markup.heading.heading-2.asciidoc - settings - - foreground - #f9e2af - - - - scope - heading.4.markdown punctuation.definition.heading.markdown, heading.4.markdown, markup.heading.atx.4.mdx, markup.heading.atx.4.mdx punctuation.definition.heading.mdx, markup.heading.heading-3.asciidoc - settings - - foreground - #a6e3a1 - - - - scope - heading.5.markdown punctuation.definition.heading.markdown, heading.5.markdown, markup.heading.atx.5.mdx, markup.heading.atx.5.mdx punctuation.definition.heading.mdx, markup.heading.heading-4.asciidoc - settings - - foreground - #89b4fa - - - - scope - heading.6.markdown punctuation.definition.heading.markdown, heading.6.markdown, markup.heading.atx.6.mdx, markup.heading.atx.6.mdx punctuation.definition.heading.mdx, markup.heading.heading-5.asciidoc - settings - - foreground - #cba6f7 - - - - scope - markup.bold - settings - - foreground - #f38ba8 - fontStyle - bold - - - - scope - markup.italic - settings - - foreground - #f38ba8 - fontStyle - italic - - - - scope - markup.strikethrough - settings - - foreground - #a6adc8 - fontStyle - strikethrough - - - - name - Markdown auto links - scope - punctuation.definition.link, markup.underline.link - settings - - foreground - #89b4fa - - - - name - Markdown links - scope - text.html.markdown punctuation.definition.link.title, string.other.link.title.markdown, markup.link, punctuation.definition.constant.markdown, constant.other.reference.link.markdown, markup.substitution.attribute-reference - settings - - foreground - #b4befe - - - - name - Markdown code spans - scope - punctuation.definition.raw.markdown, markup.inline.raw.string.markdown, markup.raw.block.markdown - settings - - foreground - #a6e3a1 - - - - name - Markdown triple backtick language identifier - scope - fenced_code.block.language - settings - - foreground - #89dceb - - - - name - Markdown triple backticks - scope - markup.fenced_code.block punctuation.definition, markup.raw support.asciidoc - settings - - foreground - #9399b2 - - - - name - Markdown quotes - scope - markup.quote, punctuation.definition.quote.begin - settings - - foreground - #f5c2e7 - - - - name - Markdown separators - scope - meta.separator.markdown - settings - - foreground - #94e2d5 - - - - name - Markdown list bullets - scope - punctuation.definition.list.begin.markdown, markup.list.bullet - settings - - foreground - #94e2d5 - - - - name - Nix attribute names - scope - entity.other.attribute-name.multipart.nix, entity.other.attribute-name.single.nix - settings - - foreground - #89b4fa - - - - name - Nix parameter names - scope - variable.parameter.name.nix - settings - - foreground - #cdd6f4 - fontStyle - - - - - name - Nix interpolated parameter names - scope - meta.embedded variable.parameter.name.nix - settings - - foreground - #b4befe - fontStyle - - - - - name - Nix paths - scope - string.unquoted.path.nix - settings - - foreground - #f5c2e7 - fontStyle - - - - - name - PHP Attributes - scope - support.attribute.builtin, meta.attribute.php - settings - - foreground - #f9e2af - - - - name - PHP Parameters (needed for the leading dollar sign) - scope - meta.function.parameters.php punctuation.definition.variable.php - settings - - foreground - #eba0ac - - - - name - PHP Constants (null, __FILE__, etc.) - scope - constant.language.php - settings - - foreground - #cba6f7 - - - - name - PHP functions - scope - text.html.php support.function - settings - - foreground - #89dceb - - - - name - PHPdoc keywords - scope - keyword.other.phpdoc.php - settings - - fontStyle - - - - - name - Python argument functions reset to text, otherwise they inherit blue from function-call - scope - support.variable.magic.python, meta.function-call.arguments.python - settings - - foreground - #cdd6f4 - - - - name - Python double underscore functions - scope - support.function.magic.python - settings - - foreground - #89dceb - fontStyle - italic - - - - name - Python `self` keyword - scope - variable.parameter.function.language.special.self.python, variable.language.special.self.python - settings - - foreground - #f38ba8 - fontStyle - italic - - - - name - python keyword flow/logical (for ... in) - scope - keyword.control.flow.python, keyword.operator.logical.python - settings - - foreground - #cba6f7 - - - - name - python storage type - scope - storage.type.function.python - settings - - foreground - #cba6f7 - - - - name - python function support - scope - support.token.decorator.python, meta.function.decorator.identifier.python - settings - - foreground - #89dceb - - - - name - python function calls - scope - meta.function-call.python - settings - - foreground - #89b4fa - - - - name - python function decorators - scope - entity.name.function.decorator.python, punctuation.definition.decorator.python - settings - - foreground - #fab387 - fontStyle - italic - - - - name - python placeholder reset to normal string - scope - constant.character.format.placeholder.other.python - settings - - foreground - #f5c2e7 - - - - name - Python exception & builtins such as exit() - scope - support.type.exception.python, support.function.builtin.python - settings - - foreground - #fab387 - - - - name - entity.name.type - scope - support.type.python - settings - - foreground - #fab387 - - - - name - python constants (True/False) - scope - constant.language.python - settings - - foreground - #cba6f7 - - - - name - Arguments accessed later in the function body - scope - meta.indexed-name.python, meta.item-access.python - settings - - foreground - #eba0ac - fontStyle - italic - - - - name - Python f-strings/binary/unicode storage types - scope - storage.type.string.python - settings - - foreground - #a6e3a1 - fontStyle - italic - - - - name - Python type hints - scope - meta.function.parameters.python - settings - - fontStyle - - - - - name - Regex string begin/end in JS/TS - scope - string.regexp punctuation.definition.string.begin, string.regexp punctuation.definition.string.end - settings - - foreground - #f5c2e7 - - - - name - Regex anchors (^, $) - scope - keyword.control.anchor.regexp - settings - - foreground - #cba6f7 - - - - name - Regex regular string match - scope - string.regexp.ts - settings - - foreground - #cdd6f4 - - - - name - Regex group parenthesis & backreference (\1, \2, \3, ...) - scope - punctuation.definition.group.regexp, keyword.other.back-reference.regexp - settings - - foreground - #a6e3a1 - - - - name - Regex character class [] - scope - punctuation.definition.character-class.regexp - settings - - foreground - #f9e2af - - - - name - Regex character classes (\d, \w, \s) - scope - constant.other.character-class.regexp - settings - - foreground - #f5c2e7 - - - - name - Regex range - scope - constant.other.character-class.range.regexp - settings - - foreground - #f5e0dc - - - - name - Regex quantifier - scope - keyword.operator.quantifier.regexp - settings - - foreground - #94e2d5 - - - - name - Regex constant/numeric - scope - constant.character.numeric.regexp - settings - - foreground - #fab387 - - - - name - Regex lookaheads, negative lookaheads, lookbehinds, negative lookbehinds - scope - punctuation.definition.group.no-capture.regexp, meta.assertion.look-ahead.regexp, meta.assertion.negative-look-ahead.regexp - settings - - foreground - #89b4fa - - - - name - Rust attribute - scope - meta.annotation.rust, meta.annotation.rust punctuation, meta.attribute.rust, punctuation.definition.attribute.rust - settings - - foreground - #f9e2af - fontStyle - italic - - - - name - Rust attribute strings - scope - meta.attribute.rust string.quoted.double.rust, meta.attribute.rust string.quoted.single.char.rust - settings - - fontStyle - - - - - name - Rust keyword - scope - entity.name.function.macro.rules.rust, storage.type.module.rust, storage.modifier.rust, storage.type.struct.rust, storage.type.enum.rust, storage.type.trait.rust, storage.type.union.rust, storage.type.impl.rust, storage.type.rust, storage.type.function.rust, storage.type.type.rust - settings - - foreground - #cba6f7 - fontStyle - - - - - name - Rust u/i32, u/i64, etc. - scope - entity.name.type.numeric.rust - settings - - foreground - #cba6f7 - fontStyle - - - - - name - Rust generic - scope - meta.generic.rust - settings - - foreground - #fab387 - - - - name - Rust impl - scope - entity.name.impl.rust - settings - - foreground - #f9e2af - fontStyle - italic - - - - name - Rust module - scope - entity.name.module.rust - settings - - foreground - #fab387 - - - - name - Rust trait - scope - entity.name.trait.rust - settings - - foreground - #f9e2af - fontStyle - italic - - - - name - Rust struct - scope - storage.type.source.rust - settings - - foreground - #f9e2af - - - - name - Rust union - scope - entity.name.union.rust - settings - - foreground - #f9e2af - - - - name - Rust enum member - scope - meta.enum.rust storage.type.source.rust - settings - - foreground - #94e2d5 - - - - name - Rust macro - scope - support.macro.rust, meta.macro.rust support.function.rust, entity.name.function.macro.rust - settings - - foreground - #89b4fa - fontStyle - italic - - - - name - Rust lifetime - scope - storage.modifier.lifetime.rust, entity.name.type.lifetime - settings - - foreground - #89b4fa - fontStyle - italic - - - - name - Rust string formatting - scope - string.quoted.double.rust constant.other.placeholder.rust - settings - - foreground - #f5c2e7 - - - - name - Rust return type generic - scope - meta.function.return-type.rust meta.generic.rust storage.type.rust - settings - - foreground - #cdd6f4 - - - - name - Rust functions - scope - meta.function.call.rust - settings - - foreground - #89b4fa - - - - name - Rust angle brackets - scope - punctuation.brackets.angle.rust - settings - - foreground - #89dceb - - - - name - Rust constants - scope - constant.other.caps.rust - settings - - foreground - #fab387 - - - - name - Rust function parameters - scope - meta.function.definition.rust variable.other.rust - settings - - foreground - #eba0ac - - - - name - Rust closure variables - scope - meta.function.call.rust variable.other.rust - settings - - foreground - #cdd6f4 - - - - name - Rust self - scope - variable.language.self.rust - settings - - foreground - #f38ba8 - - - - name - Rust metavariable names - scope - variable.other.metavariable.name.rust, meta.macro.metavariable.rust keyword.operator.macro.dollar.rust - settings - - foreground - #f5c2e7 - - - - name - Shell shebang - scope - comment.line.shebang, comment.line.shebang punctuation.definition.comment, comment.line.shebang, punctuation.definition.comment.shebang.shell, meta.shebang.shell - settings - - foreground - #f5c2e7 - fontStyle - italic - - - - name - Shell shebang command - scope - comment.line.shebang constant.language - settings - - foreground - #94e2d5 - fontStyle - italic - - - - name - Shell interpolated command - scope - meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation, meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation - settings - - foreground - #f38ba8 - - - - name - Shell interpolated command variable - scope - meta.string meta.interpolation.parameter.shell variable.other.readwrite - settings - - foreground - #fab387 - fontStyle - italic - - - - scope - source.shell punctuation.section.interpolation, punctuation.definition.evaluation.backticks.shell - settings - - foreground - #94e2d5 - - - - name - Shell EOF - scope - entity.name.tag.heredoc.shell - settings - - foreground - #cba6f7 - - - - name - Shell quoted variable - scope - string.quoted.double.shell variable.other.normal.shell - settings - - foreground - #cdd6f4 - - - - name - JSON Keys - scope - source.json meta.mapping.key string - settings - - foreground - #89b4fa - - - - name - JSON key surrounding quotes - scope - source.json meta.mapping.key punctuation.definition.string.begin, source.json meta.mapping.key punctuation.definition.string.end - settings - - foreground - #9399b2 - - - - scope - markup.heading.synopsis.man, markup.heading.title.man, markup.heading.other.man, markup.heading.env.man - settings - - foreground - #cba6f7 - - - - scope - markup.heading.commands.man - settings - - foreground - #89b4fa - - - - scope - markup.heading.env.man - settings - - foreground - #f5c2e7 - - - - name - Man page options - scope - entity.name - settings - - foreground - #94e2d5 - - - - scope - markup.heading.1.markdown - settings - - foreground - #f38ba8 - - - - scope - markup.heading.2.markdown - settings - - foreground - #fab387 - - - - scope - markup.heading.markdown - settings - - foreground - #f9e2af - - - - - \ No newline at end of file diff --git a/.config/btop/btop.log b/.config/btop/btop.log deleted file mode 100644 index 7dc494a..0000000 --- a/.config/btop/btop.log +++ /dev/null @@ -1,40 +0,0 @@ - -2024/11/19 (20:36:51) | ===> btop++ v.1.4.0 -2024/11/19 (20:36:51) | WARNING: Intel GPU: Failed to initialize PMU -2024/11/19 (20:37:28) | INFO: Logger set to INFO - -2024/11/26 (00:47:27) | ===> btop++ v.1.4.0 -2024/11/26 (00:47:27) | WARNING: Intel GPU: Failed to initialize PMU - -2024/11/26 (00:47:32) | ===> btop++ v.1.4.0 -2024/11/26 (00:47:32) | WARNING: Intel GPU: Failed to initialize PMU - -2024/12/16 (20:32:28) | ===> btop++ v.1.4.0 -2024/12/16 (20:32:28) | WARNING: Intel GPU: Failed to initialize PMU - -2025/01/06 (01:34:00) | ===> btop++ v.1.4.0 -2025/01/06 (01:34:00) | WARNING: Intel GPU: Failed to initialize PMU - -2025/02/23 (01:19:32) | ===> btop++ v.1.4.0 -2025/02/23 (01:19:32) | WARNING: Intel GPU: Failed to initialize PMU - -2025/03/12 (19:08:38) | ===> btop++ v.1.4.0 -2025/03/12 (19:08:38) | WARNING: Intel GPU: Failed to initialize PMU - -2025/03/13 (00:34:24) | ===> btop++ v.1.4.0 -2025/03/13 (00:34:24) | WARNING: Intel GPU: Failed to initialize PMU - -2025/03/19 (17:51:06) | ===> btop++ v.1.4.0 -2025/03/19 (17:51:06) | WARNING: Intel GPU: Failed to initialize PMU - -2025/03/22 (05:43:25) | ===> btop++ v.1.4.0 -2025/03/22 (05:43:25) | WARNING: Intel GPU: Failed to initialize PMU - -2025/03/22 (05:43:39) | ===> btop++ v.1.4.0 -2025/03/22 (05:43:39) | WARNING: Intel GPU: Failed to initialize PMU - -2025/03/24 (02:13:29) | ===> btop++ v.1.4.0 -2025/03/24 (02:13:29) | WARNING: Intel GPU: Failed to initialize PMU - -2025/03/25 (22:21:48) | ===> btop++ v.1.4.0 -2025/03/25 (22:21:48) | WARNING: Intel GPU: Failed to initialize PMU diff --git a/.config/cava/Wall-Dcol b/.config/cava/Wall-Dcol deleted file mode 100644 index 8fac5f6..0000000 --- a/.config/cava/Wall-Dcol +++ /dev/null @@ -1,13 +0,0 @@ - -[color] - - gradient = 1 - gradient_count = 8 - gradient_color_1 = '#332952' - gradient_color_2 = '#463A6B' - gradient_color_3 = '#7D4B5F' - gradient_color_4 = '#8F576D' - gradient_color_5 = '#A36578' - gradient_color_6 = '#C27A90' - gradient_color_7 = '#E69A9D' - gradient_color_8 = '#F0AAAD' diff --git a/.config/cava/themes/frappe-transparent.cava b/.config/cava/themes/frappe-transparent.cava deleted file mode 100644 index 42f012d..0000000 --- a/.config/cava/themes/frappe-transparent.cava +++ /dev/null @@ -1,11 +0,0 @@ -[color] -gradient = 1 - -gradient_color_1 = '#81c8be' -gradient_color_2 = '#99d1db' -gradient_color_3 = '#85c1dc' -gradient_color_4 = '#8caaee' -gradient_color_5 = '#ca9ee6' -gradient_color_6 = '#f4b8e4' -gradient_color_7 = '#ea999c' -gradient_color_8 = '#e78284' diff --git a/.config/cava/themes/frappe.cava b/.config/cava/themes/frappe.cava deleted file mode 100644 index f8c0174..0000000 --- a/.config/cava/themes/frappe.cava +++ /dev/null @@ -1,13 +0,0 @@ -[color] -background = '#303446' - -gradient = 1 - -gradient_color_1 = '#81c8be' -gradient_color_2 = '#99d1db' -gradient_color_3 = '#85c1dc' -gradient_color_4 = '#8caaee' -gradient_color_5 = '#ca9ee6' -gradient_color_6 = '#f4b8e4' -gradient_color_7 = '#ea999c' -gradient_color_8 = '#e78284' diff --git a/.config/cava/themes/latte-transparent.cava b/.config/cava/themes/latte-transparent.cava deleted file mode 100644 index 0f29d5a..0000000 --- a/.config/cava/themes/latte-transparent.cava +++ /dev/null @@ -1,11 +0,0 @@ -[color] -gradient = 1 - -gradient_color_1 = '#179299' -gradient_color_2 = '#04a5e5' -gradient_color_3 = '#209fb5' -gradient_color_4 = '#1e66f5' -gradient_color_5 = '#8839ef' -gradient_color_6 = '#ea76cb' -gradient_color_7 = '#e64553' -gradient_color_8 = '#d20f39' diff --git a/.config/cava/themes/latte.cava b/.config/cava/themes/latte.cava deleted file mode 100644 index 77ca349..0000000 --- a/.config/cava/themes/latte.cava +++ /dev/null @@ -1,13 +0,0 @@ -[color] -background = '#eff1f5' - -gradient = 1 - -gradient_color_1 = '#179299' -gradient_color_2 = '#04a5e5' -gradient_color_3 = '#209fb5' -gradient_color_4 = '#1e66f5' -gradient_color_5 = '#8839ef' -gradient_color_6 = '#ea76cb' -gradient_color_7 = '#e64553' -gradient_color_8 = '#d20f39' diff --git a/.config/cava/themes/macchiato-transparent.cava b/.config/cava/themes/macchiato-transparent.cava deleted file mode 100644 index 9f1b051..0000000 --- a/.config/cava/themes/macchiato-transparent.cava +++ /dev/null @@ -1,11 +0,0 @@ -[color] -gradient = 1 - -gradient_color_1 = '#8bd5ca' -gradient_color_2 = '#91d7e3' -gradient_color_3 = '#7dc4e4' -gradient_color_4 = '#8aadf4' -gradient_color_5 = '#c6a0f6' -gradient_color_6 = '#f5bde6' -gradient_color_7 = '#ee99a0' -gradient_color_8 = '#ed8796' diff --git a/.config/cava/themes/macchiato.cava b/.config/cava/themes/macchiato.cava deleted file mode 100644 index af0797c..0000000 --- a/.config/cava/themes/macchiato.cava +++ /dev/null @@ -1,13 +0,0 @@ -[color] -background = '#24273a' - -gradient = 1 - -gradient_color_1 = '#8bd5ca' -gradient_color_2 = '#91d7e3' -gradient_color_3 = '#7dc4e4' -gradient_color_4 = '#8aadf4' -gradient_color_5 = '#c6a0f6' -gradient_color_6 = '#f5bde6' -gradient_color_7 = '#ee99a0' -gradient_color_8 = '#ed8796' diff --git a/.config/cava/themes/mocha-transparent.cava b/.config/cava/themes/mocha-transparent.cava deleted file mode 100644 index e2f033c..0000000 --- a/.config/cava/themes/mocha-transparent.cava +++ /dev/null @@ -1,11 +0,0 @@ -[color] -gradient = 1 - -gradient_color_1 = '#94e2d5' -gradient_color_2 = '#89dceb' -gradient_color_3 = '#74c7ec' -gradient_color_4 = '#89b4fa' -gradient_color_5 = '#cba6f7' -gradient_color_6 = '#f5c2e7' -gradient_color_7 = '#eba0ac' -gradient_color_8 = '#f38ba8' diff --git a/.config/cava/themes/mocha.cava b/.config/cava/themes/mocha.cava deleted file mode 100644 index 594c473..0000000 --- a/.config/cava/themes/mocha.cava +++ /dev/null @@ -1,13 +0,0 @@ -[color] -background = '#1e1e2e' - -gradient = 1 - -gradient_color_1 = '#94e2d5' -gradient_color_2 = '#89dceb' -gradient_color_3 = '#74c7ec' -gradient_color_4 = '#89b4fa' -gradient_color_5 = '#cba6f7' -gradient_color_6 = '#f5c2e7' -gradient_color_7 = '#eba0ac' -gradient_color_8 = '#f38ba8' diff --git a/.config/fish/conf.d/fnm.fish b/.config/fish/conf.d/fnm.fish deleted file mode 100644 index dab58c6..0000000 --- a/.config/fish/conf.d/fnm.fish +++ /dev/null @@ -1 +0,0 @@ -fnm env --use-on-cd --shell fish | source diff --git a/.config/fish/config.fish b/.config/fish/config.fish deleted file mode 100644 index 777cc28..0000000 --- a/.config/fish/config.fish +++ /dev/null @@ -1,93 +0,0 @@ -set -gx QT_FONT_DPI 96 -set -gx EDITOR nvim -set -gx PAGER less -set -gx PATH "/run/user/1000/fnm_multishells/14287_1763490645306/bin" $PATH; -set -gx FNM_MULTISHELL_PATH "/run/user/1000/fnm_multishells/14287_1763490645306"; -set -gx FNM_VERSION_FILE_STRATEGY "local"; -set -gx FNM_DIR "/home/anon/.local/share/fnm"; -set -gx FNM_LOGLEVEL "info"; -set -gx FNM_NODE_DIST_MIRROR "https://nodejs.org/dist"; -set -gx FNM_COREPACK_ENABLED "false"; -set -gx FNM_RESOLVE_ENGINES "true"; -set -gx FNM_ARCH "x64"; -set -Ux MANPAGER "nvim +Man!" -# set -gx DUSKADIY_API_KEY "*****" -set -x NEWT_COLORS 'root=black,black;window=black,black;border=white,black;listbox=white,black;label=blue,black;checkbox=red,black;title=green,black;button=white,red;actsellistbox=white,red;actlistbox=white,gray;compactbutton=white,gray;actcheckbox=white,blue;entry=lightgray,black;textbox=blue,black' nmtui -# set -gx BAT_THEME "Catppuccin Mocha" - -alias dmz="cat ~/.config/fish/dmz.txt" -alias matrix="~/projects/random-clones/matrix/matrix" -alias nmatrix="neo-matrix -DS 3" - -# LLMs -alias llamasrv="~/projects/pkgs/llama.cpp/build/bin/llama-server" -abbr llamastart "llamasrv --host 0.0.0.0 --port 11343 --models-max 3 --models-preset ~/Documents/models/config.ini" - -# Shorts -alias fzf="fzf --multi --preview 'bat --style=numbers --color=always {}' | xargs -n 1 nvim" -alias grep="grep --color=auto" - -# Git -alias gs="git status --short" -alias gca="git add -p . && git commit" -alias gd="git diff --word-diff" -alias gl="git log --graph --show-signature" -alias gla="git log --all --decorate --oneline --graph" -alias gls="serie" -alias gm="git merge" -alias gm="git merge" - -# List Directory -alias ls="lsd" -alias l="ls -l" -alias la="ls -a" -alias lla="ls -la" -alias lt="ls --tree" - -alias cdots="cd ~/.dotfiles/.config/" -alias cpnotes="cd ~/sync/PersonalNotes/" -abbr dots "cdots && nvim" -abbr pnotes "cpnotes && nvim" -abbr todo "cpnotes && nvim ToDoNext.md" - -abbr cat "bat -p" -abbr copy "wl-copy" -abbr img "kitten icat" -abbr fm "yazi" ## file manager -abbr lg "lazygit" -abbr mkdir "mkdir -p" -abbr faillock "sudo faillock --reset" -abbr ipadd "sudo ip route add 192.168.0.234 dev wg0" -abbr xremaps "sudo xremap --watch ~/.config/xremap/config.yml" -abbr pacs "sudo pacman -Syu --noconfirm" -abbr yays "yay --noconfirm --sudoloop" -abbr nmaps "sudo nmap -sn 192.168.0.0/24" -abbr scrcpyc 'scrcpy -wSK -m 1920 --window-borderless --always-on-top --power-off-on-close' -abbr scrcpys 'scrcpy -wS --power-off-on-close' -abbr aliases "bat ~/.config/fish/config.fish" -abbr tts "tt -notheme -bold -showwpm -json" - -# Handy change dir shortcuts -abbr .. "cd .." -abbr ... "cd ../.." -abbr .3 "cd ../../.." -abbr .4 "cd ../../../.." -abbr .5 "cd ../../../../.." - - -zoxide init --cmd cd fish | source -export PATH="$HOME/.cargo/bin:$PATH" -thefuck --alias | source - -# Created by `pipx` on 2025-05-31 20:14:06 -set PATH $PATH /home/anon/.local/bin - -# pnpm -set -gx PNPM_HOME "/home/anon/.local/share/pnpm" -if not string match -q -- $PNPM_HOME $PATH - set -gx PATH "$PNPM_HOME" $PATH -end -# pnpm end - - -fnm env --use-on-cd --shell fish | source diff --git a/.config/fish/fish_variables b/.config/fish/fish_variables deleted file mode 100644 index 756ca35..0000000 --- a/.config/fish/fish_variables +++ /dev/null @@ -1,7 +0,0 @@ -# This file contains fish universal variable definitions. -# VERSION: 3.0 -SETUVAR --export EDITOR:nvim -SETUVAR --export MANPAGER:nvim\x20\x2bMan\x21 -SETUVAR --export MANROFFOPT:\x2dP\x20\x2dc -SETUVAR TMP:/tmp/tmp\x2eYYqSsNsses -SETUVAR __fish_initialized:4300 diff --git a/.config/fish/functions/askllama.fish b/.config/fish/functions/askllama.fish deleted file mode 100644 index 2415f49..0000000 --- a/.config/fish/functions/askllama.fish +++ /dev/null @@ -1,3 +0,0 @@ -function askllama --description 'answer from llama' - ~/projects/pkgs/llama.cpp/build/bin/llama-simple -m ~/Documents/models/Qwen3-4B-Instruct-2507.Q4_K_M.gguf $(string join '+' $argv[1..]) -end diff --git a/.config/fish/functions/clis.fish b/.config/fish/functions/clis.fish deleted file mode 100644 index 649d069..0000000 --- a/.config/fish/functions/clis.fish +++ /dev/null @@ -1,3 +0,0 @@ -function clis --description 'fzf select cli' - eval (string split "\n" (cat $HOME/.config/misc/clis.txt) | bashs "fzf --multi") -end diff --git a/.config/fish/functions/fish_greeting.fish b/.config/fish/functions/fish_greeting.fish deleted file mode 100644 index 7db9545..0000000 --- a/.config/fish/functions/fish_greeting.fish +++ /dev/null @@ -1,7 +0,0 @@ - -function fish_greeting - #curl "wttr.in/Belgrade?format=%l:+%c+%t+|+%h+|+%w+|+(%%M+%m)\n" - cat ~/.config/fish/greet.txt ## populated by cronjob - date +"📅 %d. %b 🕗 %H:%M - %A" -end - diff --git a/.config/fish/functions/sshf.fish b/.config/fish/functions/sshf.fish deleted file mode 100644 index e847f4a..0000000 --- a/.config/fish/functions/sshf.fish +++ /dev/null @@ -1,3 +0,0 @@ -function sshf --description 'fzf select for ssh hosts' - ssh $(grep Host -w ~/.ssh/config | awk '{print $2}' | bashs "fzf --multi") -end diff --git a/.config/fish/functions/tuis.fish b/.config/fish/functions/tuis.fish deleted file mode 100644 index 79008c5..0000000 --- a/.config/fish/functions/tuis.fish +++ /dev/null @@ -1,3 +0,0 @@ -function tuis --description 'fzf select tui' - eval (string split "\n" (cat $HOME/.config/misc/tuis.txt) | bashs "fzf --multi") -end diff --git a/.config/hypr/battery.sh b/.config/hypr/battery.sh deleted file mode 100755 index a8ed79f..0000000 --- a/.config/hypr/battery.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Get the current battery percentage -battery_percentage=$(cat /sys/class/power_supply/BAT0/capacity) - -# Get the battery status (Charging or Discharging) -battery_status=$(cat /sys/class/power_supply/BAT0/status) - -# Define the battery icons for each 10% segment -battery_icons=("󰂃" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰁹") - -# Define the charging icon -charging_icon="󰂄" - -# Calculate the index for the icon array -icon_index=$((battery_percentage / 10)) - -# Get the corresponding icon -battery_icon=${battery_icons[icon_index]} - -# Check if the battery is charging -if [ "$battery_status" = "Charging" ]; then - battery_icon="$charging_icon" -fi - -# Output the battery percentage and icon -echo "$battery_percentage% $battery_icon" diff --git a/.config/hypr/hypridle.conf b/.config/hypr/hypridle.conf deleted file mode 100644 index d43ef04..0000000 --- a/.config/hypr/hypridle.conf +++ /dev/null @@ -1,42 +0,0 @@ -#!   ░▒▒▒░░░▓▓          ___________ -#! ░░▒▒▒░░░░░▓▓        //___________/ -#! ░░▒▒▒░░░░░▓▓     _   _ _    _ _____ -#! ░░▒▒░░░░░▓▓▓▓▓▓ | | | | |  | |  __/ -#! ░▒▒░░░░▓▓   ▓▓ | |_| | |_/ /| |___ -#!  ░▒▒░░▓▓   ▓▓   \__  |____/ |____/ -#!    ░▒▓▓   ▓▓  //____/ - -# $LOCKSCREEN = hyprlock # preferred LOCKSCREEN e.g swaylock -$LOCKSCREEN = lockscreen.sh # Calls $LOCKSCREEN set from hyprland - -general { - lock_cmd = pidof hyprlock || hyprlock - on_unlock_cmd = bash -c 'notify-send "Welcome back!"' - before_sleep_cmd = loginctl lock-session - after_sleep_cmd = bash -c 'brightnessctl -r; brightnessctl -d tpacpi::kbd_backlight set 1; hyprctl dispatch dpms on' -} - -listener { - timeout = 180 - on-timeout = bash -c 'brightnessctl -s set 50' - on-resume = bash -c 'brightnessctl -r' -} - -listener { - timeout = 250 # 5min - on-timeout = loginctl lock-session # lock screen when timeout has passed -} - -listener { - timeout = 350 # 3.6 minutes - on-timeout = bash -c 'brightnessctl -r; brightnessctl -s set 10; brightnessctl -d tpacpi::kbd_backlight set 0; hyprctl dispatch dpms off' - on-resume = bash -c 'brightnessctl -r; brightnessctl -d tpacpi::kbd_backlight set 1; hyprctl dispatch dpms on' -} - - -listener { - timeout = 550 # 5.5min - on-timeout = hyprctl dispatch dpms off # screen off when timeout has passed - on-resume = hyprctl dispatch dpms on # screen on when activity is detected after timeout has fired. -} - diff --git a/.config/hypr/hyprlock.conf b/.config/hypr/hyprlock.conf deleted file mode 100644 index 62f6665..0000000 --- a/.config/hypr/hyprlock.conf +++ /dev/null @@ -1,116 +0,0 @@ -#! █░█ █▄█ █▀█ █▀█ █░░ █▀█ █▀▀ █▄▀ -#! █▀█ ░█░ █▀▀ █▀▄ █▄▄ █▄█ █▄▄ █░█ - -#*  Hyprlock Configuration File  - -$fontColor = rgba(255,255,255,0.5) -$fontFamily = SourceCodeVF ExtraBold -$timeFontFamily = Cantarell ExtraBold - -general { - no_fade_in = true - grade = 1 - ignore_empty_input = true - text_trim = true - fade_in_time = 0.8 - disable_loading_bar = 0 -} - -background { - monitor = - path = ~/sync/Drive/pics/foss-world-crop.png - blur_passes = 2 - blur_size = 4 - contrast = 0.7 - brightness = 0.3 - vibrancy = 0.15 - vibrancy_darkness = 0.5 -} - -image { - monitor = - path = ~/sync/Drive/pics/matrixom-crop.png - position = 0, 80 - reload_time = -1 - reload_cmd = -} - -input-field { - monitor = - size = 20%, 5% - position = 0, -60 - outline_thickness = 3 - dots_size = 0.08 - dots_spacing = 0.2 - dots_center = true - outer_color = rgba(0,0,0,0.1) - inner_color = $fontColor - fade_on_empty = true -} - -label { - monitor = - text = cmd[update:5000] echo "$(uptime -p)" - color = $fontColor - font_family = $fontFamily - font_size = 15 - position = 0, 350 -} - -label { - monitor = - text = cmd[update:300000] echo "$(cat ~/.config/fish/greet.txt)" - color = $fontColor - font_family = $fontFamily - font_size = 14 - position = 0, 310 -} - -label { - monitor = - text = cmd[update:1000] echo "$(date +"%H:%M")" - color = $fontColor - font_family = $timeFontFamily - font_size = 120 - position = 0, -180 - animation = pulse 2s infinite - opacity = 0.5 - shadow_passes = 1 - shadow_size = 1 - shadow_color = rgba(0, 0, 0, 0.5) -} - -label { - monitor = - text = cmd[update:1000] echo "$(LC_TIME=en_US.UF-8 date +'%A, %B %d')" - color = $fontColor - font_family = $fontFamily - font_size = 25 - position = 0, -300 -} - -label { - monitor = - text = cmd[update:1000] echo -e "$(~/.config/hypr/battery.sh)" - color = $fontColor - font_family = $fontFamily - font_size = 14 - position = -50, 30 - halign = right - valign = bottom -} - -label { - monitor = - color = $fontColor - text = cmd[] echo "" - font-size=24 - onclick = systemctl poweroff - position = 50, 30 - halign = left - valign = bottom -} - - - - diff --git a/.config/hypr/hyprlock/theme.conf b/.config/hypr/hyprlock/theme.conf deleted file mode 100644 index 3324804..0000000 --- a/.config/hypr/hyprlock/theme.conf +++ /dev/null @@ -1,138 +0,0 @@ -# HyDE default layout -# Wallbash generated layout - -# Local Variables -$fontFamily = JetBrainsMono Nerd Font -$fn_greet=echo "Good $(date +%H | awk '{if ($1 < 12) print "Morning"; else if ($1 < 18) print "Afternoon"; else print "Evening"}'), ${USER}" - -# GENERAL -general { - no_fade_in = false - grace = 0 - disable_loading_bar = true -} - -# BACKGROUND -background { - monitor = - path = $BACKGROUND_PATH - blur_passes = 2 -} - -# TIME -label { - monitor = - text = $TIME - color = $wallbash_txt1_rgba - font_size = 90 - font_family = $fontFamily - position = -30, 0 - halign = right - valign = top -} - -# DATE -label { - monitor = - text = cmd[update:43200000] date +"%A, %d %B %Y" - color = $wallbash_txt2_rgba - font_size = 25 - font_family = $fontFamily - position = -30, -150 - halign = right - valign = top -} - -# USER AVATAR -image { - monitor = - path = $MPRIS_IMAGE - size = 150 # lesser side if not 1:1 ratio - reload_time = 0 # seconds between reloading, 0 to reload with SIGUSR2 - reload_cmd = # command to get new path. if empty, old path will be used. don't run "follow" commands like tail -F - position = 0, 0 - halign = center - valign = center -} - -# INPUT FIELD -input-field { - monitor = - size = 200, 50 #! - outline_thickness = 3 - dots_size = 0.33 # Scale of input-field height, 0.2 - 0.8 - dots_spacing = 0.15 # Scale of dots' absolute size, 0.0 - 1.0 - dots_center = true - dots_rounding = -1 # -1 default circle, -2 follow input-field rounding - outer_color = $wallbash_pry4_rgba - inner_color = $wallbash_pry2_rgba - font_color = $wallbash_3xa9_rgba - fade_on_empty = true - fade_timeout = 1000 # Milliseconds before fade_on_empty is triggered. - placeholder_text = Input Password... # Text rendered in the input box when it's empty. - hide_input = false - rounding = -1 # -1 means complete rounding (circle/oval) - check_color = $wallbash_pry4_rgba - fail_color = rgba(FF0000FF) # if authentication failed, changes outer_color and fail message color - fail_text = $FAIL ($ATTEMPTS) # can be set to empty - fail_transition = 300 # transition time in ms between normal outer_color and fail_color - capslock_color = -1 - numlock_color = -1 - bothlock_color = -1 # when both locks are active. -1 means don't change outer color (same for above) - invert_numlock = false # change color if numlock is off - swap_font_color = true # see below - position = 0, 80 - halign = center - valign = bottom -} - - -# USER Greeting -label { - monitor = - text = cmd[update:60000] $fn_greet - color = $text - font_size = 20 - font_family = $fontFamily Bold - position = 0, -190 - halign = center - valign = center -} - -# Mpris and SPLASH -label { - monitor = - text = cmd[update:1000] $SPLASH_CMD # Outputs the song title when mpris is available, otherwise, it will output the splash command. - color = $wallbash_txt2_rgba - font_family = $fontFamily - font_size = 15 - position = 0, 0 - halign = center - valign = bottom -} - - - -# Battery Status if present -label { - monitor = - text = cmd[update:5000] $BATTERY_ICON - color = $wallbash_4xa9_rgba - font_size = 20 - font_family = JetBrainsMono Nerd Font - position = -1%, 1% - halign = right - valign = bottom -} - -# Current Keyboard Layout -label { - monitor = - text = cmd[update:1000] $KEYBOARD_LAYOUT - color = $wallbash_4xa9_rgba - font_size = 20 - font_family = $fontFamily - position = -2%, 1% - halign = right - valign = bottom -} diff --git a/.config/hypr/themes/colors.conf b/.config/hypr/themes/colors.conf deleted file mode 100644 index e69de29..0000000 diff --git a/.config/hypr/themes/common.conf b/.config/hypr/themes/common.conf deleted file mode 100644 index 45759cc..0000000 --- a/.config/hypr/themes/common.conf +++ /dev/null @@ -1,28 +0,0 @@ - -# █▀▀ █░█ █▀█ █▀ █▀█ █▀█ -# █▄▄ █▄█ █▀▄ ▄█ █▄█ █▀▄ - -exec = hyprctl setcursor Bibata-Modern-Ice 20 -exec = gsettings set org.gnome.desktop.interface cursor-theme 'Bibata-Modern-Ice' -exec = gsettings set org.gnome.desktop.interface cursor-size 20 - - -# █▀▀ █▀█ █▄░█ ▀█▀ -# █▀░ █▄█ █░▀█ ░█░ - -exec = gsettings set org.gnome.desktop.interface font-name 'Cantarell 10' -exec = gsettings set org.gnome.desktop.interface document-font-name 'Cantarell 10' -exec = gsettings set org.gnome.desktop.interface monospace-font-name 'CaskaydiaCove Nerd Font Mono 9' -exec = gsettings set org.gnome.desktop.interface font-antialiasing 'rgba' -exec = gsettings set org.gnome.desktop.interface font-hinting 'full' - - -# █▀ █▀█ █▀▀ █▀▀ █ ▄▀█ █░░ -# ▄█ █▀▀ ██▄ █▄▄ █ █▀█ █▄▄ - -decoration { - dim_special = 0.3 - blur { - special = true - } -} diff --git a/.config/hypr/themes/theme.conf b/.config/hypr/themes/theme.conf deleted file mode 100644 index 966753e..0000000 --- a/.config/hypr/themes/theme.conf +++ /dev/null @@ -1,35 +0,0 @@ -exec = gsettings set org.gnome.desktop.interface icon-theme 'Tela-circle-dracula' -exec = gsettings set org.gnome.desktop.interface gtk-theme 'Catppuccin-Mocha' -exec = gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' - -general { - gaps_in = 2 #3 - gaps_out = 4 #8 - border_size = 1 #2 - col.active_border = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg - col.inactive_border = rgba(b4befecc) rgba(6c7086cc) 45deg - layout = dwindle - resize_on_border = true -} - -group { - col.border_active = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg - col.border_inactive = rgba(b4befecc) rgba(6c7086cc) 45deg - col.border_locked_active = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg - col.border_locked_inactive = rgba(b4befecc) rgba(6c7086cc) 45deg -} - -decoration { - rounding = 4 #10 - # drop_shadow = true #false - blur { - enabled = yes - size = 6 - passes = 3 - new_optimizations = on - ignore_opacity = on - xray = false - } -} - -# layerrule = blur,waybar diff --git a/.config/hypr/themes/wallbash.conf b/.config/hypr/themes/wallbash.conf deleted file mode 100644 index 31fdb12..0000000 --- a/.config/hypr/themes/wallbash.conf +++ /dev/null @@ -1,50 +0,0 @@ -# Auto-generated by HyDE // Read-only -# // ---------------------------- -# HyDE Theme: Catppuccin Mocha -# Configuration File: "/home/coja/.config/hyde/themes/Catppuccin Mocha/hypr.theme" -# Wallbash Mode : theme -# // ---------------------------- - -$HYDE_THEME=Catppuccin Mocha -$GTK_THEME=Catppuccin-Mocha -$COLOR-SCHEME=prefer-dark -$ICON_THEME=Tela-circle-dracula - -$CURSOR_THEME=Bibata-Modern-Ice -$CURSOR_SIZE=24 - -$FONT=Cantarell -$FONT_SIZE=10 -$DOCUMENT_FONT=Cantarell -$DOCUMENT_FONT_SIZE=10 -$MONOSPACE_FONT=CaskaydiaCove Nerd Font Mono -$MONOSPACE_FONT_SIZE=9 - - -$CODE_THEME=Wallbash -$SDDM_THEME= - -# // ---------------------------- -# README: -# Values above are derived and sanitized from the theme.conf file, -# This is to ensure themes won't have any 'exec' or 'source' -# commands that could potentially harm the system -# or undesired behavior. -# -# Dear Theme Owner: -# You can still add your own custom 'exec' or 'source' commands -# by adding it as variable, examples (you can name the variable anything): -# Note that you should indicate it in your README.md -# -# -# -- ⌨️ theme.conf -- -# $RUN_CMD="some_command" -# $SOURCE_FILE="/some/files" -# -# -# -- ⌨️ userprefs.conf -- -# exec = ${RUN_CMD}" -# source = ${SOURCE_FILE} -# exec = hyde-shell walbash code $CODE_THEME # Setting the code theme - -# // ---------------------------- diff --git a/.config/hypr/windowrules.conf b/.config/hypr/windowrules.conf deleted file mode 100644 index cec0676..0000000 --- a/.config/hypr/windowrules.conf +++ /dev/null @@ -1,72 +0,0 @@ - -# █░█░█ █ █▄░█ █▀▄ █▀█ █░█░█   █▀█ █░█ █░░ █▀▀ █▀ -# ▀▄▀▄▀ █ █░▀█ █▄▀ █▄█ ▀▄▀▄▀   █▀▄ █▄█ █▄▄ ██▄ ▄█ - -# See https://wiki.hyprland.org/Configuring/Window-Rules/ -# hyprctl clients to get names - -windowrule = opacity 0.80 0.80, match:class ^(code-url-handler)$ -windowrule = opacity 0.80 0.80, match:class ^(code-insiders-url-handler)$ -windowrule = opacity 0.80 0.80, match:class ^(org.kde.dolphin)$ -windowrule = opacity 0.80 0.80, float on, match:class ^(org.kde.ark)$ -windowrule = opacity 0.80 0.80, float on, match:class ^(nwg-look)$ -windowrule = opacity 0.80 0.80, float on, match:class ^(qt5ct)$ -windowrule = opacity 0.80 0.80, float on, match:class ^(qt6ct)$ -windowrule = opacity 0.80 0.80, float on, match:class ^(kvantummanager)$ -windowrule = opacity 0.80 0.70, float on, match:class ^(org.pulseaudio.pavucontrol)$ -windowrule = opacity 0.80 0.70, match:class ^(lagrange)$ -windowrule = opacity 0.60 0.60, match:class ^(com.github.wwmm.easyeffects)$ -windowrule = opacity 0.80 0.70, float on, match:class ^(blueman-manager)$ -windowrule = opacity 0.80 0.70, float on, match:class ^(nm-applet)$ -windowrule = opacity 0.80 0.70, float on, match:class ^(nm-connection-editor)$ -windowrule = opacity 0.80 0.70, float on, match:class ^(org.kde.polkit-kde-authentication-agent-1)$ -windowrule = opacity 0.80 0.70, match:class ^(polkit-gnome-authentication-agent-1)$ -windowrule = opacity 0.80 0.70, match:class ^(org.freedesktop.impl.portal.desktop.gtk)$ -windowrule = opacity 0.80 0.70, match:class ^(org.freedesktop.impl.portal.desktop.hyprland)$ -windowrule = opacity 0.70 0.70, match:class ^([Ss]team)$ - -windowrule = opacity 0.90 0.90, float on, match:class ^(com.github.rafostar.Clapper)$ # Clapper-Gtk -windowrule = opacity 0.80 0.80, match:class ^(com.github.tchx84.Flatseal)$ # Flatseal-Gtk -windowrule = opacity 0.80 0.80, match:class ^(hu.kramo.Cartridges)$ # Cartridges-Gtk -windowrule = opacity 0.80 0.80, match:class ^(com.obsproject.Studio)$ # Obs-Qt -windowrule = opacity 0.80 0.80, match:class ^(gnome-boxes)$ # Boxes-Gtk -windowrule = opacity 0.80 0.80, match:class ^(discord)$ # Discord-Electron -windowrule = opacity 0.80 0.80, match:class ^(WebCord)$ # WebCord-Electron -windowrule = opacity 0.80 0.80, match:class ^(ArmCord)$ # ArmCord-Electron -windowrule = opacity 0.80 0.80, float on, match:class ^(app.drey.Warp)$ # Warp-Gtk -windowrule = opacity 0.80 0.80, float on, match:class ^(net.davidotek.pupgui2)$ # ProtonUp-Qt -windowrule = opacity 0.80 0.80, float on, match:class ^(yad)$ # Protontricks-Gtk -windowrule = opacity 0.80 0.80, float on, match:class ^(Signal)$ # Signal-Gtk -windowrule = opacity 0.80 0.80, float on, match:class ^(io.github.alainm23.planify)$ # planify-Gtk -windowrule = opacity 0.80 0.80, float on, match:class ^(io.gitlab.theevilskeleton.Upscaler)$ # Upscaler-Gtk -windowrule = opacity 0.80 0.80, match:class ^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gtk -windowrule = opacity 0.80 0.80, float on, match:class ^(io.gitlab.adhami3310.Impression)$ # Impression-Gtk -windowrule = opacity 0.80 0.80, float on, match:class ^(io.missioncenter.MissionCenter)$ # MissionCenter-Gtk -windowrule = opacity 0.80 0.80, match:class ^(io.github.flattool.Warehouse)$ # Warehouse-Gtk - -windowrule = float on, match:class ^(org.kde.dolphin)$, match:title ^(Progress Dialog — Dolphin)$ -windowrule = float on, match:class ^(org.kde.dolphin)$, match:title ^(Copying — Dolphin)$ -windowrule = float on, match:title ^(About Mozilla Firefox)$ -windowrule = float on, match:class ^(firefox)$, match:title ^(Picture-in-Picture)$ -windowrule = float on, match:class ^(firefox)$, match:title ^(Library)$ -windowrule = float on, match:class ^(kitty)$, match:title ^(top)$ -windowrule = float on, match:class ^(kitty)$, match:title ^(btop)$ -windowrule = float on, match:class ^(kitty)$, match:title ^(htop)$ -windowrule = float on, match:class ^(vlc)$ - -windowrule = float on, match:class ^(eog)$ # Imageviewer-Gtk -windowrule = float on, match:class ^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gkk - - -# █░░ ▄▀█ █▄█ █▀▀ █▀█   █▀█ █░█ █░░ █▀▀ █▀ -# █▄▄ █▀█ ░█░ ██▄ █▀▄   █▀▄ █▄█ █▄▄ ██▄ ▄█ - -# layerrule = blur,rofi -# layerrule = ignorezero,rofi -# layerrule = blur,notifications -# layerrule = ignorezero,notifications -# layerrule = blur,swaync-notification-window -# layerrule = ignorezero,swaync-notification-window -# layerrule = blur,swaync-control-center -# layerrule = ignorezero,swaync-control-center -# layerrule = blur,logout_dialog diff --git a/.config/hypr/workspaces.conf b/.config/hypr/workspaces.conf deleted file mode 100644 index e69de29..0000000 diff --git a/.config/kitty/c2.conf b/.config/kitty/c2.conf deleted file mode 100644 index d9ab8d5..0000000 --- a/.config/kitty/c2.conf +++ /dev/null @@ -1,17 +0,0 @@ -# run with `kitty --session ~/.config/kitty/c2.conf` -layout tall - -# create a new tab -new_tab c2 - -cd ~/projects/wingman/c2/ -launch --var window=first nvim - -new_tab git - -cd ~/projects/wingman/c2/ -launch --var window=second lazygit - -# focus neovim -# focus first - diff --git a/.config/kitty/music.conf b/.config/kitty/music.conf deleted file mode 100644 index 4a1b90d..0000000 --- a/.config/kitty/music.conf +++ /dev/null @@ -1,17 +0,0 @@ -# run with `kitty --session ~/.config/kitty/music.conf` -layout tall - -# os_window_size 220c 64c -# create a new tab -new_tab music - -# set the working directory for windows in the current tab -cd - -launch --var window=first cava -# create additional terminals -launch --location=hsplit pyradio -resize_window taller 10 -focus pyradio -launch --location=vsplit pipes-rs -resize_window wider 20 diff --git a/.config/kitty/runners.conf b/.config/kitty/runners.conf deleted file mode 100644 index 6f7975c..0000000 --- a/.config/kitty/runners.conf +++ /dev/null @@ -1,20 +0,0 @@ -# run with `kitty --session ~/.config/kitty/runners.conf` -layout stack - -# create a new tab -new_tab runners - -cd ~/projects/wingman/c2/ - -launch --var window=first pnpm dev - -launch --location=hsplit react-devtools -resize_window narrower 20 - -launch --location=hsplit lazydocker -resize_window wider 120 - -# new_tab simulator -# cd ~/wmclient -# launch --var window=first wmsimulator.sh - diff --git a/.config/misc/clis.txt b/.config/misc/clis.txt deleted file mode 100644 index 06306a4..0000000 --- a/.config/misc/clis.txt +++ /dev/null @@ -1,29 +0,0 @@ -cal -calc -speedtest -aichat -e -askllama -ask -tldr -man -info -rsync -avkP -lsd -zioxide -httpie -bat -xxd -mpv -nix -distrobox -scrcpy -adb -watch -unzip -open -nmcli -iwctl -lsusb -thefuck -fastfetch - diff --git a/.config/misc/tuis.txt b/.config/misc/tuis.txt deleted file mode 100644 index 773bdc0..0000000 --- a/.config/misc/tuis.txt +++ /dev/null @@ -1,42 +0,0 @@ -browsh -wiki-tui -wikiman -yazi -tdf -dysk -dust -ncdu -systemctl-tui -btop -bandwhich -mtr -gping -bmon -profanity -nmtui -bluetui -drawterm -hunk -lazygit -lazydocker -lazyjorunal -pastel -hexyl -pyradio -kew -tt -gittype -matrix -cava -tetrigo -tui-mines -nyaa -stig -tmux -zellij -nvim -vim -tuios -llmfit -battop -subtui diff --git a/.config/mpv/input.conf b/.config/mpv/input.conf deleted file mode 100644 index 0c460eb..0000000 --- a/.config/mpv/input.conf +++ /dev/null @@ -1,21 +0,0 @@ -# Volume -9 add volume -5 -0 add volume 5 - -UP add volume 2 -DOWN add volume -2 - -k add volume 2 -j add volume -2 - -# Seek backward / forward -LEFT seek -5 -RIGHT seek 5 - -l seek 5 -h seek -5 - -# Bigger jumps -Shift+LEFT seek -30 -Shift+RIGHT seek 30 - diff --git a/.config/nvim/lazy-lock.json b/.config/nvim/lazy-lock.json deleted file mode 100644 index 3c0db08..0000000 --- a/.config/nvim/lazy-lock.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "LuaSnip": { "branch": "master", "commit": "a62e1083a3cfe8b6b206e7d3d33a51091df25357" }, - "NvChad": { "branch": "v2.5", "commit": "d042cc975247c2aa55fcb228e5d146dc1dc6c648" }, - "base46": { "branch": "v2.5", "commit": "fde7a2cd54599e148d376f82980407c2d24b0fa2" }, - "cmp-async-path": { "branch": "main", "commit": "f8af3f726e07f2e9d37672eaa9102581aefce149" }, - "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" }, - "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" }, - "cmp-nvim-lua": { "branch": "main", "commit": "e3a22cb071eb9d6508a156306b102c45cd2d573d" }, - "cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" }, - "codecompanion.nvim": { "branch": "main", "commit": "680c445eb357c72129d175ea5d481aa9e92ae6a5" }, - "conform.nvim": { "branch": "master", "commit": "086a40dc7ed8242c03be9f47fbcee68699cc2395" }, - "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" }, - "gitsigns.nvim": { "branch": "main", "commit": "6d808f99bd63303646794406e270bd553ad7792e" }, - "indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" }, - "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" }, - "mason.nvim": { "branch": "main", "commit": "12ddd182d9efbdc848b540f16484a583d52da0fb" }, - "menu": { "branch": "main", "commit": "7a0a4a2896b715c066cfbe320bdc048091874cc6" }, - "minty": { "branch": "main", "commit": "aafc9e8e0afe6bf57580858a2849578d8d8db9e0" }, - "nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" }, - "nvim-cmp": { "branch": "main", "commit": "a1d504892f2bc56c2e79b65c6faded2fd21f3eca" }, - "nvim-lspconfig": { "branch": "master", "commit": "e146efacbafed3789ac568abcc5a981c5decaa58" }, - "nvim-tree.lua": { "branch": "master", "commit": "d277467fc0d1d0e2bca88165a1de6b526f9f6fe8" }, - "nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" }, - "nvim-web-devicons": { "branch": "master", "commit": "c72328a5494b4502947a022fe69c0c47e53b6aa6" }, - "plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" }, - "render-markdown.nvim": { "branch": "main", "commit": "d67113f11384c0dad96fced2f7b91f1fc811e97f" }, - "snacks.nvim": { "branch": "main", "commit": "ad9ede6a9cddf16cedbd31b8932d6dcdee9b716e" }, - "telescope.nvim": { "branch": "master", "commit": "028d9a0695a0cc4cfa893889f8c408ed7ccc8adc" }, - "ui": { "branch": "v3.0", "commit": "cb75908a86720172594b30de147272c1b3a7f452" }, - "volt": { "branch": "main", "commit": "620de1321f275ec9d80028c68d1b88b409c0c8b1" }, - "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" } -} diff --git a/.config/nvim/lua/configs/codecompanion.lua b/.config/nvim/lua/configs/codecompanion.lua deleted file mode 100644 index 1516211..0000000 --- a/.config/nvim/lua/configs/codecompanion.lua +++ /dev/null @@ -1,34 +0,0 @@ -local model = "Qwen3-Coder-30B-Instruct-IQ4_XS" - -return { - adapters = { - http = { - llama_cpp = function() - return require("codecompanion.adapters").extend("openai_compatible", { - env = { - url = "http://localhost:11343", - chat_url = "/v1/chat/completions", - models_endpoint = "/v1/models", - api_key = "dummy", - }, - schema = { - model = { - default = model, - }, - }, - }) - end, - }, - }, - interactions = { - chat = { - adapter = "llama_cpp", - }, - inline = { - adapter = "llama_cpp", - }, - cmd = { - adapter = "llama_cpp", - }, - }, -} diff --git a/.config/waybar/config.ctl b/.config/waybar/config.ctl deleted file mode 100644 index 1e5cab1..0000000 --- a/.config/waybar/config.ctl +++ /dev/null @@ -1,24 +0,0 @@ -0|28|bottom|( cpu memory custom/cpuinfo custom/gpuinfo ) ( idle_inhibitor clock )|( hyprland/workspaces hyprland/window )|( backlight network pulseaudio pulseaudio#microphone custom/updates custom/keybindhint ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|28|top|( cpu memory custom/cpuinfo custom/gpuinfo ) ( idle_inhibitor clock )|( hyprland/workspaces hyprland/window )|( backlight network pulseaudio pulseaudio#microphone custom/updates custom/keybindhint ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|28|bottom|( cpu memory custom/cpuinfo custom/gpuinfo ) ( idle_inhibitor clock ) ( hyprland/workspaces )|( hyprland/window )|( backlight network pulseaudio pulseaudio#microphone custom/updates custom/keybindhint ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|28|top|( cpu memory custom/cpuinfo ) ( idle_inhibitor clock ) ( hyprland/workspaces )|( hyprland/window )|( backlight network pulseaudio pulseaudio#microphone custom/updates custom/keybindhint ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0||bottom|( hyprland/workspaces hyprland/window )|( idle_inhibitor clock )|( cpu memory custom/cpuinfo custom/gpuinfo ) ( backlight network pulseaudio pulseaudio#microphone custom/updates custom/keybindhint ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0||top|( hyprland/workspaces hyprland/window )|( idle_inhibitor clock )|( cpu memory custom/cpuinfo custom/gpuinfo ) ( backlight network pulseaudio pulseaudio#microphone custom/updates custom/keybindhint ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|31|bottom|( custom/power custom/cliphist custom/wbar custom/theme custom/wallchange ) ( wlr/taskbar custom/spotify ) |( idle_inhibitor clock )|( tray battery ) ( backlight network pulseaudio pulseaudio#microphone custom/notifications custom/keybindhint ) -0|31|left|( custom/power custom/cliphist custom/wbar custom/theme custom/wallchange ) ( wlr/taskbar custom/spotify ) |( idle_inhibitor clock )|( tray battery ) ( backlight network pulseaudio pulseaudio#microphone custom/keybindhint ) -0|31|top|( custom/power custom/cliphist custom/wbar custom/theme custom/wallchange ) ( wlr/taskbar custom/spotify ) |( idle_inhibitor clock )|( tray battery ) ( backlight network pulseaudio pulseaudio#microphone custom/keybindhint ) -0|31|right|( custom/power custom/cliphist custom/wbar custom/theme custom/wallchange ) ( wlr/taskbar custom/spotify ) |( idle_inhibitor clock )|( tray battery ) ( backlight network pulseaudio pulseaudio#microphone custom/keybindhint ) -0|32|bottom||( custom/power ) ( tray battery ) ( wlr/taskbar idle_inhibitor clock ) ( custom/cliphist ) ( custom/wbar ) ( custom/wallchange ) ( custom/theme )| -0|32|left||( custom/power ) ( tray battery ) ( wlr/taskbar idle_inhibitor clock ) ( custom/cliphist ) ( custom/wbar ) ( custom/wallchange ) ( custom/theme )| -1|32|top||( custom/power ) ( tray battery ) ( wlr/taskbar idle_inhibitor clock ) ( custom/cliphist ) ( custom/wbar ) ( custom/wallchange ) ( custom/theme )| -0|32|right||( custom/power ) ( tray battery ) ( wlr/taskbar idle_inhibitor clock ) ( custom/cliphist ) ( custom/wbar ) ( custom/wallchange ) ( custom/theme )| -0|31|bottom|( cpu memory custom/cpuinfo custom/gpuinfo ) ( idle_inhibitor clock ) ( hyprland/workspaces )|( wlr/taskbar )|( backlight network pulseaudio pulseaudio#microphone custom/updates custom/keybindhint ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|31|top|( cpu memory custom/cpuinfo custom/gpuinfo ) ( idle_inhibitor clock ) ( hyprland/workspaces )|( wlr/taskbar )|( backlight network pulseaudio pulseaudio#microphone custom/updates custom/keybindhint ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|29|bottom|( wlr/taskbar mpris )|( idle_inhibitor clock )|( backlight network pulseaudio pulseaudio#microphone custom/updates ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|29|left|( wlr/taskbar mpris )|( idle_inhibitor clock )|( backlight network pulseaudio pulseaudio#microphone custom/updates ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|29|top|( wlr/taskbar mpris )|( idle_inhibitor clock )|( backlight network pulseaudio pulseaudio#microphone custom/updates ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|29|right|( wlr/taskbar mpris )|( idle_inhibitor clock )|( backlight network pulseaudio pulseaudio#microphone custom/updates ) ( tray battery ) ( custom/wallchange custom/theme custom/wbar custom/cliphist custom/power ) -0|28|bottom|( custom/power custom/cliphist custom/wbar custom/theme custom/wallchange ) ( idle_inhibitor clock custom/spotify )|( wlr/taskbar )|( tray ) ( backlight network pulseaudio pulseaudio#microphone ) -0|28|left|( custom/power custom/cliphist custom/wbar custom/theme custom/wallchange ) ( idle_inhibitor clock custom/spotify )|( wlr/taskbar )|( tray ) ( backlight network pulseaudio pulseaudio#microphone ) -0|28|top|( custom/power custom/cliphist custom/wbar custom/theme custom/wallchange ) ( idle_inhibitor clock custom/spotify )|( wlr/taskbar )|( tray ) ( backlight network pulseaudio pulseaudio#microphone ) -0|28|right|( custom/power custom/cliphist custom/wbar custom/theme custom/wallchange ) ( idle_inhibitor clock custom/spotify )|( wlr/taskbar )|( tray ) ( backlight network pulseaudio pulseaudio#microphone ) diff --git a/.config/waybar/modules/window.jsonc b/.config/waybar/modules/window.jsonc deleted file mode 100644 index b0c4c7a..0000000 --- a/.config/waybar/modules/window.jsonc +++ /dev/null @@ -1,17 +0,0 @@ - "hyprland/window": { - "format": "  {}", - "rotate": ${r_deg}, - "separate-outputs": true, - "rewrite": { - "${USER}@${set_sysname}:(.*)": "$1 ", - "(.*) — Mozilla Firefox": "$1 󰈹", - "(.*)Mozilla Firefox": "Firefox 󰈹", - "(.*) - Visual Studio Code": "$1 󰨞", - "(.*)Visual Studio Code": "Code 󰨞", - "(.*) — Dolphin": "$1 󰉋", - "(.*)Spotify": "Spotify 󰓇", - "(.*)Steam": "Steam 󰓓" - }, - "max-length": 1000 - }, - diff --git a/.config/xremap/config.yml b/.config/xremap/config.yml deleted file mode 100644 index 8c8aef1..0000000 --- a/.config/xremap/config.yml +++ /dev/null @@ -1,26 +0,0 @@ -modmap: - - name: CapsLock to LeftCtrl/Esc - remap: - CapsLock: - held: leftctrl - alone: esc - alone_timeout_millis: 150 -# keymap: -# - name: Alt+hjkl to Arrows -# remap: -# # Alt-H: Alt+left #back -# Alt-h: left -# Alt-l: right -# Alt-j: down -# Alt-k: up -# Alt-u: pageup -# Alt-d: pagedown -# keymap: -# - name: Freetube remap -# application: -# only: FreeTube -# remap: -# Alt-h: Left -# Alt-l: Right -# Alt-j: Down -# Alt-k: Up diff --git a/.config/zellij/config.kdl.bak b/.config/zellij/config.kdl.bak deleted file mode 100644 index fa30b12..0000000 --- a/.config/zellij/config.kdl.bak +++ /dev/null @@ -1,452 +0,0 @@ -keybinds clear-defaults=true { - locked { - bind "Ctrl g" { SwitchToMode "normal"; } - } - pane { - bind "left" { MoveFocus "left"; } - bind "down" { MoveFocus "down"; } - bind "up" { MoveFocus "up"; } - bind "right" { MoveFocus "right"; } - bind "c" { SwitchToMode "renamepane"; PaneNameInput 0; } - bind "d" { NewPane "down"; SwitchToMode "normal"; } - bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } - bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } - bind "h" { MoveFocus "left"; } - bind "i" { TogglePanePinned; SwitchToMode "normal"; } - bind "j" { MoveFocus "down"; } - bind "k" { MoveFocus "up"; } - bind "l" { MoveFocus "right"; } - bind "n" { NewPane; SwitchToMode "normal"; } - bind "p" { SwitchFocus; } - bind "Ctrl p" { SwitchToMode "normal"; } - bind "r" { NewPane "right"; SwitchToMode "normal"; } - bind "w" { ToggleFloatingPanes; SwitchToMode "normal"; } - bind "z" { TogglePaneFrames; SwitchToMode "normal"; } - } - tab { - bind "left" { GoToPreviousTab; } - bind "down" { GoToNextTab; } - bind "up" { GoToPreviousTab; } - bind "right" { GoToNextTab; } - bind "1" { GoToTab 1; SwitchToMode "normal"; } - bind "2" { GoToTab 2; SwitchToMode "normal"; } - bind "3" { GoToTab 3; SwitchToMode "normal"; } - bind "4" { GoToTab 4; SwitchToMode "normal"; } - bind "5" { GoToTab 5; SwitchToMode "normal"; } - bind "6" { GoToTab 6; SwitchToMode "normal"; } - bind "7" { GoToTab 7; SwitchToMode "normal"; } - bind "8" { GoToTab 8; SwitchToMode "normal"; } - bind "9" { GoToTab 9; SwitchToMode "normal"; } - bind "[" { BreakPaneLeft; SwitchToMode "normal"; } - bind "]" { BreakPaneRight; SwitchToMode "normal"; } - bind "b" { BreakPane; SwitchToMode "normal"; } - bind "h" { GoToPreviousTab; } - bind "j" { GoToNextTab; } - bind "k" { GoToPreviousTab; } - bind "l" { GoToNextTab; } - bind "n" { NewTab; SwitchToMode "normal"; } - bind "r" { SwitchToMode "renametab"; TabNameInput 0; } - bind "s" { ToggleActiveSyncTab; SwitchToMode "normal"; } - bind "Ctrl t" { SwitchToMode "normal"; } - bind "x" { CloseTab; SwitchToMode "normal"; } - bind "tab" { ToggleTab; } - } - resize { - bind "left" { Resize "Increase left"; } - bind "down" { Resize "Increase down"; } - bind "up" { Resize "Increase up"; } - bind "right" { Resize "Increase right"; } - bind "+" { Resize "Increase"; } - bind "-" { Resize "Decrease"; } - bind "=" { Resize "Increase"; } - bind "H" { Resize "Decrease left"; } - bind "J" { Resize "Decrease down"; } - bind "K" { Resize "Decrease up"; } - bind "L" { Resize "Decrease right"; } - bind "h" { Resize "Increase left"; } - bind "j" { Resize "Increase down"; } - bind "k" { Resize "Increase up"; } - bind "l" { Resize "Increase right"; } - bind "Ctrl n" { SwitchToMode "normal"; } - } - move { - bind "left" { MovePane "left"; } - bind "down" { MovePane "down"; } - bind "up" { MovePane "up"; } - bind "right" { MovePane "right"; } - bind "h" { MovePane "left"; } - bind "Ctrl h" { SwitchToMode "normal"; } - bind "j" { MovePane "down"; } - bind "k" { MovePane "up"; } - bind "l" { MovePane "right"; } - bind "n" { MovePane; } - bind "p" { MovePaneBackwards; } - bind "tab" { MovePane; } - } - scroll { - bind "e" { EditScrollback; SwitchToMode "normal"; } - bind "s" { SwitchToMode "entersearch"; SearchInput 0; } - } - search { - bind "c" { SearchToggleOption "CaseSensitivity"; } - bind "n" { Search "down"; } - bind "o" { SearchToggleOption "WholeWord"; } - bind "p" { Search "up"; } - bind "w" { SearchToggleOption "Wrap"; } - } - session { - bind "a" { - LaunchOrFocusPlugin "zellij:about" { - floating true - move_to_focused_tab true - } - SwitchToMode "normal" - } - bind "c" { - LaunchOrFocusPlugin "configuration" { - floating true - move_to_focused_tab true - } - SwitchToMode "normal" - } - bind "Ctrl o" { SwitchToMode "normal"; } - bind "p" { - LaunchOrFocusPlugin "plugin-manager" { - floating true - move_to_focused_tab true - } - SwitchToMode "normal" - } - bind "w" { - LaunchOrFocusPlugin "session-manager" { - floating true - move_to_focused_tab true - } - SwitchToMode "normal" - } - } - shared_except "locked" { - bind "Alt left" { MoveFocusOrTab "left"; } - bind "Alt down" { MoveFocus "down"; } - bind "Alt up" { MoveFocus "up"; } - bind "Alt right" { MoveFocusOrTab "right"; } - bind "Alt +" { Resize "Increase"; } - bind "Alt -" { Resize "Decrease"; } - bind "Alt =" { Resize "Increase"; } - bind "Alt [" { PreviousSwapLayout; } - bind "Alt ]" { NextSwapLayout; } - bind "Alt f" { ToggleFloatingPanes; } - bind "Ctrl g" { SwitchToMode "locked"; } - bind "Alt h" { MoveFocusOrTab "left"; } - bind "Alt i" { MoveTab "left"; } - bind "Alt j" { MoveFocus "down"; } - bind "Alt k" { MoveFocus "up"; } - bind "Alt l" { MoveFocusOrTab "right"; } - bind "Alt n" { NewPane; } - bind "Alt o" { MoveTab "right"; } - bind "Ctrl q" { Quit; } - } - shared_except "locked" "move" { - bind "Ctrl h" { SwitchToMode "move"; } - } - shared_except "locked" "session" { - bind "Ctrl o" { SwitchToMode "session"; } - } - shared_except "locked" "scroll" "search" "tmux" { - bind "Ctrl b" { SwitchToMode "tmux"; } - } - shared_except "locked" "scroll" "search" { - bind "Ctrl s" { SwitchToMode "scroll"; } - } - shared_except "locked" "tab" { - bind "Ctrl t" { SwitchToMode "tab"; } - } - shared_except "locked" "pane" { - bind "Ctrl p" { SwitchToMode "pane"; } - } - shared_except "locked" "resize" { - bind "Ctrl n" { SwitchToMode "resize"; } - } - shared_except "normal" "locked" "entersearch" { - bind "enter" { SwitchToMode "normal"; } - } - shared_except "normal" "locked" "entersearch" "renametab" "renamepane" { - bind "esc" { SwitchToMode "normal"; } - } - shared_among "pane" "tmux" { - bind "x" { CloseFocus; SwitchToMode "normal"; } - } - shared_among "scroll" "search" { - bind "PageDown" { PageScrollDown; } - bind "PageUp" { PageScrollUp; } - bind "left" { PageScrollUp; } - bind "down" { ScrollDown; } - bind "up" { ScrollUp; } - bind "right" { PageScrollDown; } - bind "Ctrl b" { PageScrollUp; } - bind "Ctrl c" { ScrollToBottom; SwitchToMode "normal"; } - bind "d" { HalfPageScrollDown; } - bind "Ctrl f" { PageScrollDown; } - bind "h" { PageScrollUp; } - bind "j" { ScrollDown; } - bind "k" { ScrollUp; } - bind "l" { PageScrollDown; } - bind "Ctrl s" { SwitchToMode "normal"; } - bind "u" { HalfPageScrollUp; } - } - entersearch { - bind "Ctrl c" { SwitchToMode "scroll"; } - bind "esc" { SwitchToMode "scroll"; } - bind "enter" { SwitchToMode "search"; } - } - renametab { - bind "esc" { UndoRenameTab; SwitchToMode "tab"; } - } - shared_among "renametab" "renamepane" { - bind "Ctrl c" { SwitchToMode "normal"; } - } - renamepane { - bind "esc" { UndoRenamePane; SwitchToMode "pane"; } - } - shared_among "session" "tmux" { - bind "d" { Detach; } - } - tmux { - bind "left" { MoveFocus "left"; SwitchToMode "normal"; } - bind "down" { MoveFocus "down"; SwitchToMode "normal"; } - bind "up" { MoveFocus "up"; SwitchToMode "normal"; } - bind "right" { MoveFocus "right"; SwitchToMode "normal"; } - bind "space" { NextSwapLayout; } - bind "\"" { NewPane "down"; SwitchToMode "normal"; } - bind "%" { NewPane "right"; SwitchToMode "normal"; } - bind "," { SwitchToMode "renametab"; } - bind "[" { SwitchToMode "scroll"; } - bind "Ctrl b" { Write 2; SwitchToMode "normal"; } - bind "c" { NewTab; SwitchToMode "normal"; } - bind "h" { MoveFocus "left"; SwitchToMode "normal"; } - bind "j" { MoveFocus "down"; SwitchToMode "normal"; } - bind "k" { MoveFocus "up"; SwitchToMode "normal"; } - bind "l" { MoveFocus "right"; SwitchToMode "normal"; } - bind "n" { GoToNextTab; SwitchToMode "normal"; } - bind "o" { FocusNextPane; } - bind "p" { GoToPreviousTab; SwitchToMode "normal"; } - bind "z" { ToggleFocusFullscreen; SwitchToMode "normal"; } - } -} - -// Plugin aliases - can be used to change the implementation of Zellij -// changing these requires a restart to take effect -plugins { - about location="zellij:about" - compact-bar location="zellij:compact-bar" - configuration location="zellij:configuration" - filepicker location="zellij:strider" { - cwd "/" - } - plugin-manager location="zellij:plugin-manager" - session-manager location="zellij:session-manager" - status-bar location="zellij:status-bar" - strider location="zellij:strider" - tab-bar location="zellij:tab-bar" - welcome-screen location="zellij:session-manager" { - welcome_screen true - } -} - -// Plugins to load in the background when a new session starts -// eg. "file:/path/to/my-plugin.wasm" -// eg. "https://example.com/my-plugin.wasm" -load_plugins { -} - -// Use a simplified UI without special fonts (arrow glyphs) -// Options: -// - true -// - false (Default) -// -// simplified_ui true - -// Choose the theme that is specified in the themes section. -// Default: default -// -// theme "dracula" - -// Choose the base input mode of zellij. -// Default: normal -// -// default_mode "locked" - -// Choose the path to the default shell that zellij will use for opening new panes -// Default: $SHELL -// -// default_shell "fish" - -// Choose the path to override cwd that zellij will use for opening new panes -// -// default_cwd "/tmp" - -// The name of the default layout to load on startup -// Default: "default" -// -// default_layout "compact" - -// The folder in which Zellij will look for layouts -// (Requires restart) -// -// layout_dir "/tmp" - -// The folder in which Zellij will look for themes -// (Requires restart) -// -// theme_dir "/tmp" - -// Toggle enabling the mouse mode. -// On certain configurations, or terminals this could -// potentially interfere with copying text. -// Options: -// - true (default) -// - false -// -// mouse_mode false - -// Toggle having pane frames around the panes -// Options: -// - true (default, enabled) -// - false -// -// pane_frames false - -// When attaching to an existing session with other users, -// should the session be mirrored (true) -// or should each user have their own cursor (false) -// (Requires restart) -// Default: false -// -// mirror_session true - -// Choose what to do when zellij receives SIGTERM, SIGINT, SIGQUIT or SIGHUP -// eg. when terminal window with an active zellij session is closed -// (Requires restart) -// Options: -// - detach (Default) -// - quit -// -// on_force_close "quit" - -// Configure the scroll back buffer size -// This is the number of lines zellij stores for each pane in the scroll back -// buffer. Excess number of lines are discarded in a FIFO fashion. -// (Requires restart) -// Valid values: positive integers -// Default value: 10000 -// -// scroll_buffer_size 10000 - -// Provide a command to execute when copying text. The text will be piped to -// the stdin of the program to perform the copy. This can be used with -// terminal emulators which do not support the OSC 52 ANSI control sequence -// that will be used by default if this option is not set. -// Examples: -// -// copy_command "xclip -selection clipboard" // x11 -// copy_command "wl-copy" // wayland -// copy_command "pbcopy" // osx -// -// copy_command "pbcopy" - -// Choose the destination for copied text -// Allows using the primary selection buffer (on x11/wayland) instead of the system clipboard. -// Does not apply when using copy_command. -// Options: -// - system (default) -// - primary -// -// copy_clipboard "primary" - -// Enable automatic copying (and clearing) of selection when releasing mouse -// Default: true -// -// copy_on_select true - -// Path to the default editor to use to edit pane scrollbuffer -// Default: $EDITOR or $VISUAL -// scrollback_editor "/usr/bin/vim" - -// A fixed name to always give the Zellij session. -// Consider also setting `attach_to_session true,` -// otherwise this will error if such a session exists. -// Default: -// -// session_name "My singleton session" - -// When `session_name` is provided, attaches to that session -// if it is already running or creates it otherwise. -// Default: false -// -// attach_to_session true - -// Toggle between having Zellij lay out panes according to a predefined set of layouts whenever possible -// Options: -// - true (default) -// - false -// -// auto_layout false - -// Whether sessions should be serialized to the cache folder (including their tabs/panes, cwds and running commands) so that they can later be resurrected -// Options: -// - true (default) -// - false -// -// session_serialization false - -// Whether pane viewports are serialized along with the session, default is false -// Options: -// - true -// - false (default) -// -// serialize_pane_viewport false - -// Scrollback lines to serialize along with the pane viewport when serializing sessions, 0 -// defaults to the scrollback size. If this number is higher than the scrollback size, it will -// also default to the scrollback size. This does nothing if `serialize_pane_viewport` is not true. -// -// scrollback_lines_to_serialize 10000 - -// Enable or disable the rendering of styled and colored underlines (undercurl). -// May need to be disabled for certain unsupported terminals -// (Requires restart) -// Default: true -// -// styled_underlines false - -// How often in seconds sessions are serialized -// -// serialization_interval 10000 - -// Enable or disable writing of session metadata to disk (if disabled, other sessions might not know -// metadata info on this session) -// (Requires restart) -// Default: false -// -// disable_session_metadata false - -// Enable or disable support for the enhanced Kitty Keyboard Protocol (the host terminal must also support it) -// (Requires restart) -// Default: true (if the host terminal supports it) -// -// support_kitty_keyboard_protocol false - -// Whether to stack panes when resizing beyond a certain size -// Default: true -// -// stacked_resize false - -// Whether to show tips on startup -// Default: true -// -// show_startup_tips false - -// Whether to show release notes on first version run -// Default: true -// -// show_release_notes false diff --git a/.gitignore b/.gitignore index ba47bd8..49bcdfb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,34 @@ -!.gitignore -.config/hyde/themes/ -.config/fish/greet.txt +# Secrets / local-only (matched at any depth) +secrets.fish +fish_variables +greet.txt +settings.local.json + +# pi agent runtime + machine-local auth/state +**/.pi/agent/auth.json +**/.pi/agent/sessions/ +**/.pi/agent/bin/ +**/.pi/agent/git/ +**/.pi/agent/npm/ +**/.pi/agent/trust.json + +# tmux plugins (tpm-managed, not vendored) +**/tmux/plugins/ + +# HyDE generated theme + wallpaper assets (per-machine, huge — bloated the old repo to 255MB) +**/hyde/themes/ +**/wallpapers/ + +# Claude Code project dir (worktrees, local settings, history) — never part of the dotfiles +.claude/ + +# backups / logs / package noise / os +*.bak +*.bak.* +*.log +package-lock.json +__pycache__/ +**/hypr/themes/ +# HyDE writes these at theme-apply time (sourced by animations/theme.conf & hyprlock/theme.conf) +**/hypr/animations/HyDE.conf +**/hypr/hyprlock/HyDE.conf diff --git a/.ignore b/.ignore new file mode 100644 index 0000000..7c2f5bc --- /dev/null +++ b/.ignore @@ -0,0 +1,12 @@ +# Search config for THIS repo only (ripgrep / fd / telescope read .ignore). +# +# Everything this repo holds lives in dotfiles and dotdirs — common/.config, gui/.config, +# common/.pi, common/.bashrc — which rg and fd skip by default, so an unqualified search +# here returned nothing at all. Un-hide them, then put back the two dirs that would swamp +# every result: negating .* alone drags in the whole of .git, and .claude/worktrees holds +# entire checkouts of this same repo (background sessions create them), so every hit would +# appear several times over. Scoped to this directory tree; searches elsewhere on the +# machine are unaffected. +!.* +.git/ +.claude/ diff --git a/.inputrc b/.inputrc deleted file mode 100644 index 5b40c08..0000000 --- a/.inputrc +++ /dev/null @@ -1,21 +0,0 @@ -# Vi mode -set editing-mode vi -set keymap vi -set show-mode-in-prompt on -set vi-ins-mode-string "\1\e[32m\2[I] \1\e[0m\2" -set vi-cmd-mode-string "\1\e[31m\2[N] \1\e[0m\2" - -# exclude .dotfles -set match-hidden-files off - -set show-all-if-ambiguous on -set show-all-if-unmodified on -set colored-stats on -set colored-completion-prefix on -set mark-symlinked-directories on -set completion-ignore-case on - -# Ignore duplicates in history where supported -set skip-completed-text on - - diff --git a/.pi/agent/models.json b/.pi/agent/models.json deleted file mode 100644 index df30de3..0000000 --- a/.pi/agent/models.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "providers": { - "duskadiy": { - "baseUrl": "https://llm.duskadiy.com/api/v1", - "api": "openai-completions", - "apiKey": "no-key-required", - "compat": { - "supportsDeveloperRole": false, - "supportsReasoningEffort": false, - "maxTokensField": "max_tokens" - }, - "models": [ - { - "id": "DeepSeek-Coder-V2-Lite-Instruct-Q8_0", - "name": "DeepSeek Coder V2", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "gemma-4-E4B-it-UD-Q8_K_XL", - "name": "Gemma 4 E4B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "gemma-4-26B-A4B-it-UD-IQ4_XS", - "name": "Gemma 4 26B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3-Coder-30B-Instruct-IQ4_XS", - "name": "Qwen3 Coder A3B", - "reasoning": false, - "input": ["text"], - "contextWindow": 8192, - "maxTokens": 4096, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.6-27B-UD-IQ3_XXS", - "name": "Qwen3.6 27B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.5-27B-UD-Q3_K_XL", - "name": "Qwen3.5 27B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.5-9B-Q8_0", - "name": "Qwen3.5 9B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ3_M", - "name": "Qwen3.6 27B Uncensored", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "GLM-4.7-Flash-UD-Q3_K_XL", - "name": "GLM-4.7 Flash", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - } - ] - }, - "localcpp": { - "baseUrl": "http://192.168.0.204:11343/v1", - "api": "openai-completions", - "apiKey": "no-key-required", - "compat": { - "supportsDeveloperRole": false, - "supportsReasoningEffort": false, - "maxTokensField": "max_tokens" - }, - "models": [ - { - "id": "DeepSeek-Coder-V2-Lite-Instruct-Q8_0", - "name": "DeepSeek Coder V2", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "gemma-4-E4B-it-UD-Q8_K_XL", - "name": "Gemma 4 E4B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "gemma-4-26B-A4B-it-UD-IQ4_XS", - "name": "Gemma 4 26B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3-Coder-30B-Instruct-IQ4_XS", - "name": "Qwen3 Coder A3B", - "reasoning": false, - "input": ["text"], - "contextWindow": 8192, - "maxTokens": 4096, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.6-27B-UD-IQ3_XXS", - "name": "Qwen3.6 27B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.5-27B-UD-Q3_K_XL", - "name": "Qwen3.5 27B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.5-9B-Q8_0", - "name": "Qwen3.5 9B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ3_M", - "name": "Qwen3.6 27B Uncensored", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "GLM-4.7-Flash-UD-Q3_K_XL", - "name": "GLM-4.7 Flash", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - } - ] - } - } -} diff --git a/.pi/models.json b/.pi/models.json deleted file mode 100644 index d8b3a8d..0000000 --- a/.pi/models.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "providers": { - "duskadiy": { - "baseUrl": "https://llm.duskadiy.com/api/v1", - "api": "openai-completions", - "apiKey": "$DUSKADIY_API_KEY", - "compat": { - "supportsDeveloperRole": false, - "supportsReasoningEffort": false, - "maxTokensField": "max_tokens" - }, - "models": [ - { - "id": "DeepSeek-Coder-V2-Lite-Instruct-Q8_0", - "name": "DeepSeek Coder V2", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "gemma-4-E4B-it-UD-Q8_K_XL", - "name": "Gemma 4 E4B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "gemma-4-26B-A4B-it-UD-IQ4_XS", - "name": "Gemma 4 26B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3-Coder-30B-Instruct-IQ4_XS", - "name": "Qwen3 Coder A3B", - "reasoning": false, - "input": ["text"], - "contextWindow": 8192, - "maxTokens": 4096, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.6-27B-UD-IQ3_XXS", - "name": "Qwen3.6 27B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.5-27B-UD-Q3_K_XL", - "name": "Qwen3.5 27B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.5-9B-Q8_0", - "name": "Qwen3.5 9B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ3_M", - "name": "Qwen3.6 27B Uncensored", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "GLM-4.7-Flash-UD-Q3_K_XL", - "name": "GLM-4.7 Flash", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - } - ] - }, - "localcpp": { - "baseUrl": "http://192.168.0.204:11343/v1", - "api": "openai-completions", - "apiKey": "no-key-required", - "compat": { - "supportsDeveloperRole": false, - "supportsReasoningEffort": false, - "maxTokensField": "max_tokens" - }, - "models": [ - { - "id": "DeepSeek-Coder-V2-Lite-Instruct-Q8_0", - "name": "DeepSeek Coder V2", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "gemma-4-E4B-it-UD-Q8_K_XL", - "name": "Gemma 4 E4B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "gemma-4-26B-A4B-it-UD-IQ4_XS", - "name": "Gemma 4 26B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3-Coder-30B-Instruct-IQ4_XS", - "name": "Qwen3 Coder A3B", - "reasoning": false, - "input": ["text"], - "contextWindow": 8192, - "maxTokens": 4096, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.6-27B-UD-IQ3_XXS", - "name": "Qwen3.6 27B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.5-27B-UD-Q3_K_XL", - "name": "Qwen3.5 27B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.5-9B-Q8_0", - "name": "Qwen3.5 9B", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ3_M", - "name": "Qwen3.6 27B Uncensored", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - }, - { - "id": "GLM-4.7-Flash-UD-Q3_K_XL", - "name": "GLM-4.7 Flash", - "reasoning": false, - "input": ["text"], - "contextWindow": 81920, - "maxTokens": 16384, - "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } - } - ] - } - } -} diff --git a/.pi/prompts/commit.md b/.pi/prompts/commit.md deleted file mode 100644 index 4734d38..0000000 --- a/.pi/prompts/commit.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -description: Write a Conventional Commits message for the staged changes -argument-hint: "[extra context]" ---- -Inspect the staged changes with `git diff --cached` (and `git status` for context). Then write a single Conventional Commits message: - -- A `(): ` summary line, imperative mood, ≤72 chars (types: feat, fix, refactor, docs, chore, test, perf, build). -- An optional short body explaining the *why* only when it isn't obvious from the diff. -- Do NOT run `git commit`. Output only the message inside a code block so I can review it. - -Extra context to incorporate: ${1:-none} diff --git a/.pi/prompts/review.md b/.pi/prompts/review.md deleted file mode 100644 index b8ad575..0000000 --- a/.pi/prompts/review.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -description: Review the current diff for bugs and issues -argument-hint: "[focus area]" ---- -Review the current changes. Check both `git diff` (unstaged) and `git diff --cached` (staged). Focus on: - -- Correctness and logic bugs -- Error handling and missing edge cases -- Security issues (injection, secrets, unsafe input) -- Anything that doesn't match the surrounding code's conventions - -List concrete findings as `file:line — issue — suggested fix`, ordered by severity. Be concise; skip praise. If nothing is wrong, say so. - -Extra focus this round: ${1:-general correctness} diff --git a/.pi/settings.json b/.pi/settings.json deleted file mode 100644 index db4d114..0000000 --- a/.pi/settings.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "defaultProvider": "duskadiy", - "defaultModel": "gemma-4-26B-A4B-it-UD-IQ4_XS", - "defaultThinkingLevel": "on", - "theme": "catppuccin-mocha", - "quietStartup": false, - "defaultProjectTrust": "ask", - "enableInstallTelemetry": false, - "enabledModels": [ - "localcpp/*", - "duskadiy/*" - ], - "compaction": { - "enabled": true, - "reserveTokens": 16384, - "keepRecentTokens": 20000 - }, - "retry": { - "enabled": true, - "maxRetries": 3, - "baseDelayMs": 2000, - "provider": { - "timeoutMs": 3600000 - } - }, - "httpIdleTimeoutMs": 600000, - "npmCommand": ["fnm", "exec", "--using=22", "--", "npm"], - "showHardwareCursor": true, - "lastChangelogVersion": "0.80.1" -} diff --git a/.pi/themes/catppuccin-mocha.json b/.pi/themes/catppuccin-mocha.json deleted file mode 100644 index ade4e41..0000000 --- a/.pi/themes/catppuccin-mocha.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json", - "name": "catppuccin-mocha", - "vars": { - "rosewater": "#f5e0dc", - "flamingo": "#f2cdcd", - "pink": "#f5c2e7", - "mauve": "#cba6f7", - "red": "#f38ba8", - "maroon": "#eba0ac", - "peach": "#fab387", - "yellow": "#f9e2af", - "green": "#a6e3a1", - "teal": "#94e2d5", - "sky": "#89dceb", - "sapphire": "#74c7ec", - "blue": "#89b4fa", - "lavender": "#b4befe", - "fg": "#cdd6f4", - "subtext1": "#bac2de", - "subtext0": "#a6adc8", - "overlay2": "#9399b2", - "overlay1": "#7f849c", - "overlay0": "#6c7086", - "surface2": "#585b70", - "surface1": "#45475a", - "surface0": "#313244", - "base": "#1e1e2e", - "mantle": "#181825", - "crust": "#11111b" - }, - "colors": { - "accent": "mauve", - "border": "surface1", - "borderAccent": "mauve", - "borderMuted": "surface0", - "success": "green", - "error": "red", - "warning": "yellow", - "muted": "subtext0", - "dim": "overlay0", - "text": "fg", - "thinkingText": "overlay1", - "selectedBg": "surface0", - "userMessageBg": "surface0", - "userMessageText": "fg", - "customMessageBg": "surface0", - "customMessageText": "fg", - "customMessageLabel": "mauve", - "toolPendingBg": "mantle", - "toolSuccessBg": "#1e2b22", - "toolErrorBg": "#2b1e22", - "toolTitle": "mauve", - "toolOutput": "fg", - "mdHeading": "mauve", - "mdLink": "blue", - "mdLinkUrl": "sapphire", - "mdCode": "green", - "mdCodeBlock": "fg", - "mdCodeBlockBorder": "surface1", - "mdQuote": "subtext0", - "mdQuoteBorder": "surface2", - "mdHr": "surface1", - "mdListBullet": "mauve", - "toolDiffAdded": "green", - "toolDiffRemoved": "red", - "toolDiffContext": "overlay0", - "syntaxComment": "overlay0", - "syntaxKeyword": "mauve", - "syntaxFunction": "blue", - "syntaxVariable": "fg", - "syntaxString": "green", - "syntaxNumber": "peach", - "syntaxType": "yellow", - "syntaxOperator": "sky", - "syntaxPunctuation": "overlay2", - "thinkingOff": "surface1", - "thinkingMinimal": "overlay0", - "thinkingLow": "blue", - "thinkingMedium": "teal", - "thinkingHigh": "peach", - "thinkingXhigh": "red", - "bashMode": "peach" - }, - "export": { - "pageBg": "#11111b", - "cardBg": "#1e1e2e", - "infoBg": "#313244" - } -} diff --git a/LICENSE b/LICENSE index 8cedc75..9d532de 100644 --- a/LICENSE +++ b/LICENSE @@ -209,7 +209,7 @@ If you develop a new program, and you want it to be of the greatest possible use To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. dots - Copyright (C) 2025 coja + Copyright (C) 2026 coja This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. @@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - dots Copyright (C) 2025 coja + dots Copyright (C) 2026 coja This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/README.md b/README.md index 9d953fc..88dbe7c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,196 @@ +# dots -using `gnu stow` +Unified dotfiles for all machines: one shared base + per-host overlays, deployed with +[GNU Stow](https://www.gnu.org/software/stow/). -Customized [Hyde](https://github.com/HyDE-Project/HyDE) +## Layout -`hyde`, `waybar` and `fish` are from hyprdots with customizations +Every top-level directory is a Stow *package* (they all share one stow dir, which `--override` +requires). `bin/` holds repo tooling and is **not** a package (never stowed): -`nvim` is [nvchad](https://nvchad.com/) custom conf +``` +common/ # shared everywhere (mirrors $HOME): fish, nvim, bat, btop, tmux, pi, claude, presenterm, ... +gui/ # GUI configs shared by graphical hosts: hypr, waybar, kitty, alacritty, mpv, zellij +wm/ # desktop: 4K monitors, NVIDIA, cloud LLM (duskadiy), work aliases +lw/ # laptop: no NVIDIA, roaming VPNs, network/LLM tools +fl/ # KDE Plasma box: a few Hyprland overrides + llamacpp, evremap +bin/ # tooling (not a stow package): install.sh companions +install.sh +``` +A **headless** (terminal-only) machine has no overlay package — it runs `common` only. Mark any box +headless with `touch ~/.config/dots-headless` (a machine-local, hostname-independent marker); it then +skips `gui` too, so the same terminal-only setup replicates across any number of machines. In other +words `common` *is* the headless baseline; `gui` and the host packages layer graphical machines on top. +### How it deploys +Deploy order is `common → gui → `; later layers win (the host package is stowed last with +`--override`, so a host file replaces the common/gui file at the same path). Stow runs with +`--no-folding`, so each *file* is symlinked individually (directories stay real) — this is what lets +a host override a single file in a shared dir without shadowing the rest. +**Editing a config edits the repo copy directly** (it's a symlink) — no deploy step. You only re-run +`install.sh` when files are **added or removed** (to create/prune symlinks). See **Syncing** below. +The repo must be cloned to **`~/.dots`** (the relative `../.dots` symlink convention). A host's +package name must match `hostname -s` (or pass the host explicitly, e.g. `./install.sh lw`). + +## Install (new machine) + +```fish +sudo pacman -S stow +git clone ~/.dots +cd ~/.dots +./install.sh # auto-detects host via `hostname -s` +``` + +`./install.sh -h` lists all options: `-n` dry-run, `-a` adopt (first run), `-D` unstow (revert), +`-h` help. GUI hosts also need **HyDE installed** first — see *HyDE dependency* below. + +Once per machine afterwards: `bat cache --build` (registers the repo's custom bat themes — +without it every `bat` run warns `Unknown theme 'onedark'`), and `:Lazy restore` inside nvim +(`restore` obeys the shared `lazy-lock.json` pins; `sync`/`update` would rewrite them). +nvim's treesitter (`main` branch) builds parsers via the `tree-sitter` CLI — `pacman -S +tree-sitter-cli` first, or `:TSInstallAll` fails with `ENOENT ... 'tree-sitter'`. + +### First run on a machine that still has real config files + +Stow **aborts the whole package on the first conflict** (one real file blocks *everything* in +`common`), and refuses to clobber real files without `--adopt` — you'll see +`cannot stow … since neither a link nor a directory`. Adopt them, review, then keep the repo's +versions: + +```fish +cd ~/.dots +git status # make sure the repo is clean first (see the restore caveat) +./install.sh -a # --adopt: pull existing files into the repo, symlink them back +git diff --stat # what this machine had vs the repo (review the drift!) +git restore . # discard adopted content, keep the repo versions +``` + +`--adopt` overwrites the repo copies with the machine's files, so `git restore .` is what keeps the +repo versions. Cautions: adopted changes can land under `common/`/`gui/` even when host-specific, and +`git restore .` drops **all** uncommitted edits — so commit/stash anything you want first. To keep a +genuine machine change (e.g. custom pyradio `stations.csv`), hand-copy it into the right package +instead of restoring that path. + +## Syncing (day-to-day) + +Because configs are symlinks into the repo, **you almost never run `install.sh` to sync**: + +- **Pull/propagate edits:** `cd ~/.dots && git pull` — changes are live in place instantly. No + re-stow, so **no symlink churn and no app reloads** (kitty etc. keep runtime state). +- **`bin/dotsync`** does this safely: `git pull`, then re-link via `install.sh` **only if** the pull + added/removed/renamed files (pure edits skip the re-stow). `dotsync -n` previews incoming changes. +- **`install.sh` is a (re)deploy tool, not a sync tool** — its `stow -R` deletes+recreates every + symlink, which is what makes apps reload. Only needed for the initial link or when files are + added/removed. +- **Never `stow` the repo root** (`stow .dots`, `stow */`) — it folds package dirs into `~` as stray + `~/common`, `~/gui`, … symlinks. `install.sh` only ever links package *contents*. +- **New host:** create a top-level `/` package (at least a `host.fish`), commit, then + `./install.sh`. If it's terminal-only, skip the package and just `touch ~/.config/dots-headless` + (it runs `common` only). + +## Tooling (`bin/`) + +| Command | What it does | +|---|---| +| `./install.sh [host]` | Deploy `common → gui → `. Flags: `-n` dry-run, `-a` adopt (first run), `-D`/`--unstow` revert (remove this host's symlinks; repo + real files kept), `-h` help. Self-heals: headless hosts get any stray `gui` unstowed; fails loudly (exit 1) if a package couldn't stow. | +| `./bin/dotsync [-n]` | Sync with least churn: `git pull`, re-stow only if files were added/removed. | +| `./bin/reconcile-hyde.sh [-i\|--relink] [host]` | After a HyDE update overwrote `~/.config/hypr`, reconcile HyDE's new files against your dots versions (report / `-i` keep·take·merge each / `--relink` re-symlink). | + +## How per-host differences are expressed + +| Difference | Mechanism | +|---|---| +| Monitors | `/.config/hypr/monitors.conf` (sourced by the shared `hyprland.conf`) | +| NVIDIA | `wm/.config/hypr/nvidia.conf`, pulled in by wm's `host.conf` (fl/lw have no NVIDIA) | +| Shell aliases | shared `config.fish` sources `~/.config/fish/host.fish` (per host) | +| Bash extras | shared `.bashrc` sources `~/.bashrc.host` (optional, per host) | +| fl's Hyprland | overrides ~9 hypr files (`hyprland.conf`, keybinds, windowrules, theme colors, monitors); inherits the rest from `gui` | +| Unique apps | in the owning host overlay (fl: llamacpp/evremap; lw: xremap) | + +`aichat`, `pi`, and `opencode` are **single shared configs** in `common` (LAN-first, with the cloud +provider also listed so any host can switch) — no per-host overrides. + +## HyDE dependency (important for GUI hosts) + +The Hyprland config is [HyDE](https://github.com/HyDE-Project/HyDE)-based. `gui/.config/hypr/*` is +your customization layer, but it **sources HyDE's framework**: +`source = ~/.local/share/hyde/hyprland.conf` — which defines `$mainMod` and other variables and is +**not tracked by dots** (HyDE generates it). So a GUI host must have **HyDE installed**, or Hyprland +fails to start (`source globbing error` + `invalid mod $mainMod`). + +- Install/update HyDE with its own installer (`~/HyDE/Scripts/install.sh`); it also pulls the + required apps (`xdg-desktop-portal-hyprland`, `xdg-desktop-portal-gtk`, `hyprpolkitagent`). +- HyDE's `-r`/restore overwrites `~/.config/hypr` — use **`bin/reconcile-hyde.sh`** afterward to fold + its new defaults back against your dots configs. +- **Wallpapers and generated themes are HyDE-owned, never in dots** (`**/hyde/themes/` and + `**/wallpapers/` are gitignored — they once bloated a source repo to 255 MB). A fresh machine gets + them from HyDE, not from this repo. + +## Waybar (two worlds: HyDE-generated vs the standalone rig) + +**HyDE owns `~/.config/waybar/config.jsonc` AND `style.css`** — its `waybar.py` *writes* both on +any `--set`/`--update`, **through the stow symlinks, i.e. into this repo's checkout**. So dots +track neither. HyDE-managed customization goes via: + +- `gui/.config/waybar/layouts/*.jsonc` — dots-tracked layouts; switch with `hyde-shell waybar --set `. +- `gui/.config/waybar/modules/*.jsonc` — module defs, globbed into the generated config. + +**The custom cava rig is NOT HyDE-managed**: `layouts/custom.jsonc` + `layouts/custom.css` form a +self-contained two-bar config launched raw (`waybar -c …custom.jsonc -s …custom.css` — exec-once, +the Ctrl+Alt+W / Super+Shift+R binds, and the `wbar` abbr). **Never route it through +`hyde-shell waybar --set`** — waybar.py would clobber the repo's files as above. HyDE only reads +from `layouts/`, which is what makes them safe to track. On a rig host, HyDE's own bar (systemd +user unit `hyde--bar.service`, auto-restarting) must be disabled once: +`systemctl --user stop ; systemctl --user mask `. + +Note: the Arch `waybar` build has **no native `cava`** module — the rig uses the script module +`custom/cava` running `cava-waybar.sh` (self-contained, no HyDE dependency). + +## Secrets + +Never committed. Put `DUSKADIY_API_KEY` etc. in `~/.config/fish/conf.d/secrets.fish` (gitignored). +`pi`'s `auth.json` is **not** tracked — it only ever holds `$VAR` references, but pi's `/login` +rewrites it with the literal key, so one stray `/login` would commit a real token. A new host +bootstraps it with `cp common/.pi/agent/auth.json.example common/.pi/agent/auth.json`, then +`./install.sh`. Also gitignored: `fish_variables`, +`greet.txt`, pi agent runtime/state, `tmux/plugins/`, `.claude/` (Claude Code project dir/worktrees), +`*.bak`/`*.log`. + +## Merge notes (history — the one-time unification) + +The four former per-machine repos had drifted; the merge made some judgment calls: + +- **`config.fish` / `.bashrc`** were hand-reconciled into a neutral shared base + per-host + `host.fish` / `.bashrc.host` (removed a committed `fnm_multishells` runtime PATH, stray + absolute home-dir paths, duplicate git aliases). +- **`pi` lives at `~/.pi`** (repo `.pi/`); **`tmux` at `~/.config/tmux/`** (delete a legacy + `~/.tmux.conf` if present after migrating). +- **Appearance drift was unified to one canonical** (mostly wm): bat, btop, cava, fastfetch, lazygit, + mpv, pyradio, waybar, zellij, most of nvim. +- **nvim** is shared entirely (no host overrides); plugin set / `lazy-lock.json` are shared — run + `:Lazy restore` per machine once (obeys the lockfile pins — `:Lazy update` on one machine + + commit is how versions move forward; other machines converge with `restore`). +- **`zellij`** is one shared config with **vim keybinds** (h/j/k/l) + the `guvbox-dark` theme. +- **`misc/{clis,tuis}.txt`** launcher inventories use wm's lists in `common`; a machine needing its + own can override in its overlay. + +## Third-party content and licensing + +The repo is GPL-3.0 (`LICENSE`). What that does and doesn't cover: + +- **`hypr/`, `waybar/` and the other desktop configs are derived from + [HyDE](https://github.com/HyDE-Project/HyDE)** (and, further back, legacy Hyprdots). HyDE is + itself GPL-3.0, so the derivation and this repo's licence agree — no conflict, and the + Hyprdots-era ASCII banners in those files are left in place as attribution. +- **`common/.config/aichat/dark.tmTheme`** is *One Half Dark* by Son A. Pham, **MIT**. The + licence header at the top of that file is the attribution — don't strip it. +- **`common/.config/fastfetch/pngs/`** holds project and distro marks (Arch, the Open Source + Initiative and Open Source Hardware logos, …). Trademarks aren't relicensable, so GPL-3 does + not extend to them; they are bundled as nominative use, the way most dotfiles repos do. +- **`gui/.config/hypr/hyprlock/assets/`** — the lock screen background and logo. EXIF is + stripped, so the files carry no provenance; confirm these are yours to redistribute before + publishing, or swap them for something you know the origin of. diff --git a/bin/dotsync b/bin/dotsync new file mode 100755 index 0000000..f3c09f3 --- /dev/null +++ b/bin/dotsync @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# dotsync — sync dotfiles with the least churn. +# +# Deployed configs are symlinks into this repo, so `git pull` makes edits LIVE instantly +# with no re-stow — meaning no symlink churn and no app reloads (kitty etc. keep their +# runtime state). Re-stowing (install.sh) is only needed when files are ADDED / REMOVED, +# to create or prune symlinks. dotsync does the minimum: pull, and re-link ONLY if the +# pull added/removed/renamed files. +# +# dotsync pull, then relink only if the file set changed +# dotsync -n preview incoming changes (fetch only; no pull, no relink) +set -uo pipefail +DOTS="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)" +cd "$DOTS" || exit 1 + +if [ "${1:-}" = "-n" ] || [ "${1:-}" = "--dry-run" ]; then + git fetch -q 2>/dev/null || { echo "fetch failed (offline?)"; exit 1; } + up="$(git rev-parse --abbrev-ref '@{u}' 2>/dev/null)" || { echo "no upstream set"; exit 1; } + # rev-list, not diff: a plain diff also fires when the LOCAL side is ahead, mislabelling + # your own unpushed commits as incoming. Count commits that exist only on the upstream. + n="$(git rev-list --count "HEAD..$up")" + if [ "$n" -eq 0 ]; then echo "already up to date with $up."; else + echo "incoming from $up ($n commit(s)):"; git --no-pager diff --stat "HEAD...$up" + fi + exit 0 +fi + +before="$(git rev-parse HEAD)" +if ! git pull --ff-only; then + echo "pull not fast-forward (local commits or conflicts) — commit/stash, then retry." >&2 + exit 1 +fi +after="$(git rev-parse HEAD)" + +if [ "$before" = "$after" ]; then + echo "already up to date — nothing to sync." + exit 0 +fi + +# Services that read config ONCE at startup don't pick up pulled edits — nudge loudly. +# (Symlinked edits are live for everything else; these are the exceptions that bit us.) +if git diff --name-only "$before" "$after" | grep -q '\.config/llamacpp/' \ + && systemctl cat llama.service &>/dev/null; then + echo "" + echo "⚠⚠ llamacpp presets changed — the router reads them only at startup. Apply with:" + echo " sudo systemctl restart llama.service" + echo "" +fi + +# A unit file is read from /etc, not through the stow symlink — needs daemon-reload as well. +if git diff --name-only "$before" "$after" | grep -q '\.config/whisper/' \ + && systemctl cat whisper.service &>/dev/null; then + echo "" + echo "⚠⚠ whisper unit changed — apply with:" + echo " sudo systemctl daemon-reload && sudo systemctl restart whisper.service" + echo "" +fi + +# Modified files (M) are already live through the symlinks. Only Added/Deleted/Renamed +# need (un)linking, which is the only case that justifies the churny re-stow. +if git diff --name-status "$before" "$after" | grep -qE '^(A|D|R)'; then + echo "files added/removed/renamed — re-linking via install.sh (this may reload some apps)…" + exec "$DOTS/install.sh" +else + echo "edits only — already live through the symlinks; no re-stow, no reload needed." +fi diff --git a/bin/reconcile-hyde.sh b/bin/reconcile-hyde.sh new file mode 100755 index 0000000..c383afd --- /dev/null +++ b/bin/reconcile-hyde.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +# reconcile-hyde.sh — reconcile a HyDE update with your dots-tracked configs. +# +# HyDE's updater (`Scripts/install.sh -r`) overwrites every config listed in +# Scripts/restore_cfg.psv — including ~/.config/hypr/*. It backs the old ones up to +# ~/.config/cfg_backups and REPLACES your stow symlinks with real files (HyDE's new +# defaults). This tool, run ON THE GUI MACHINE right after that update, shows per file: +# your dots version vs HyDE's new one +# lets you keep / take / merge each, then removes the severed real files and re-runs +# install.sh so everything is symlinked back to the repo. +# +# ./bin/reconcile-hyde.sh [host] report only — safe, changes nothing (default) +# ./bin/reconcile-hyde.sh -i [host] interactive: decide each conflicting file +# ./bin/reconcile-hyde.sh --relink [host] skip decisions: back up severed files, +# then re-stow (repo versions win) +# +# host defaults to `hostname -s`. Commit your repo first so choices are reviewable/revertable. +set -uo pipefail + +# Config subtrees where HyDE and your dots overlap. Add/remove as your setup needs. +SUBTREES=(.config/hypr .config/waybar) + +# --- args --- +MODE=report +HOST="" +for a in "$@"; do + case "$a" in + -i|--interactive) MODE=interactive ;; + --relink) MODE=relink ;; + -h|--help) awk 'NR > 1 && !/^#/ { exit } NR > 1' "$0"; exit 0 ;; + -*) echo "unknown option: $a" >&2; exit 2 ;; + *) HOST="$a" ;; + esac +done +[ -z "$HOST" ] && HOST="$(hostname -s)" +skip_relink=() # files the user chose to skip during interactive reconcile + +DOTS="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)" # repo root (script in bin/) +[ -x "$DOTS/install.sh" ] || { echo "error: $DOTS/install.sh not found — run from inside the dots repo" >&2; exit 1; } + +# repo package search order: host overlay wins, then gui, then common +PKGS=("$HOST" gui common) +repo_src() { # repo_src -> print the repo file stow would link, or nothing + local rel="$1" p + for p in "${PKGS[@]}"; do + [ -f "$DOTS/$p/$rel" ] && { printf '%s\n' "$DOTS/$p/$rel"; return 0; } + done + return 1 +} + +echo "host=$HOST dots=$DOTS mode=$MODE" +if [ -n "$(git -C "$DOTS" status --porcelain 2>/dev/null)" ]; then + echo "note: $DOTS has uncommitted changes — commit first so reconcile choices are reviewable." >&2 +fi +[ -d "$HOME/.config/cfg_backups" ] && echo "HyDE's pre-update backups are in ~/.config/cfg_backups" +echo + +# --- classify every live file under the managed subtrees --- +linked=0; foreign=0 +same=(); conflicts=(); new_files=() +for st in "${SUBTREES[@]}"; do + [ -d "$HOME/$st" ] || continue + while IFS= read -r -d '' live; do + rel="${live#"$HOME"/}" + if [ -L "$live" ]; then # still a symlink + tgt="$(readlink -f "$live" 2>/dev/null || true)" + case "$tgt" in + "$DOTS"/*) linked=$((linked+1)) ;; # intact -> HyDE didn't sever it + *) foreign=$((foreign+1)); echo " ? $rel -> symlink outside repo: $tgt" ;; + esac + continue + fi + # real file: HyDE overwrote (severed) it, or it is brand new + src="$(repo_src "$rel" || true)" + if [ -z "$src" ]; then + new_files+=("$rel") + elif diff -q "$src" "$live" >/dev/null 2>&1; then + same+=("$rel") # content == repo; just relink + else + conflicts+=("$rel") # HyDE's new content differs from yours + fi + done < <(find "$HOME/$st" \( -type f -o -type l \) -print0) +done + +echo "== summary ==" +echo " intact symlinks (untouched): $linked" +echo " severed but identical (relink): ${#same[@]}" +echo " severed & CHANGED (need decision): ${#conflicts[@]}" +echo " new HyDE files not tracked by dots: ${#new_files[@]}" +[ "$foreign" -gt 0 ] && echo " foreign symlinks flagged above: $foreign" +echo +if [ "$linked" -gt 0 ]; then + echo "For intact symlinks, check whether HyDE wrote THROUGH them into the repo:" + echo " git -C $DOTS diff -- '*/.config/hypr/*' '*/.config/waybar/*'" + echo +fi +if [ "${#new_files[@]}" -gt 0 ]; then + echo "New HyDE files (left as-is; add to a package + re-stow if you want them tracked):" + printf ' %s\n' "${new_files[@]}" + echo +fi +if [ "${#conflicts[@]}" -gt 0 ]; then + echo "Changed files (your dots version vs HyDE's new default):" + for rel in "${conflicts[@]}"; do + src="$(repo_src "$rel")" + printf ' %-45s %s changed lines\n' "$rel" "$(diff "$src" "$HOME/$rel" | grep -cE '^[<>]')" + done + echo +fi + +# report-only ends here +if [ "$MODE" = report ]; then + if [ "${#conflicts[@]}" -gt 0 ]; then + echo "Re-run with -i to decide each changed file, or --relink to keep your repo versions wholesale." + elif [ "${#same[@]}" -gt 0 ]; then + echo "No content conflicts. Re-run with --relink to drop the severed files and re-symlink." + else + echo "Nothing to reconcile." + fi + exit 0 +fi + +# --- interactive: decide each changed file, writing the choice into the repo copy --- +if [ "$MODE" = interactive ] && [ "${#conflicts[@]}" -gt 0 ]; then + for rel in "${conflicts[@]}"; do + src="$(repo_src "$rel")"; live="$HOME/$rel" + echo "──────── $rel ────────" + echo " repo (yours): ${src#"$DOTS"/}" + diff "$src" "$live" | sed 's/^/ /' | head -60 + echo " [k]eep yours (default) [t]ake HyDE's [m]erge in \$EDITOR -d [s]kip" + read -r -p " choice> " ans took HyDE's version into $src" ;; + m|merge) "${EDITOR:-nvim}" -d "$src" "$live" /dev/tty 2>&1; echo " -> merged (saved $src)" ;; + s|skip) echo " -> skipped (left severed; not relinked)"; skip_relink+=("$rel") ;; + *) echo " -> kept your repo version" ;; + esac + done + echo +fi + +# --- relink: back up severed files that the repo owns, remove them, re-stow --- +BK="$HOME/.config/hyde-reconcile-backup-$(date +%Y%m%d-%H%M%S)" +to_relink=() +for st in "${SUBTREES[@]}"; do + [ -d "$HOME/$st" ] || continue + while IFS= read -r -d '' live; do + [ -L "$live" ] && continue # only real (severed) files + rel="${live#"$HOME"/}" + repo_src "$rel" >/dev/null || continue # only files the repo can provide + for s in "${skip_relink[@]}"; do [ "$s" = "$rel" ] && continue 2; done + to_relink+=("$rel") + done < <(find "$HOME/$st" -type f -print0) +done + +if [ "${#to_relink[@]}" -eq 0 ]; then + echo "nothing to relink."; exit 0 +fi +echo "will back up + remove ${#to_relink[@]} severed file(s), then re-stow so they link to the repo:" +printf ' %s\n' "${to_relink[@]}" +read -r -p "proceed? [y/N] " go /dev/null || git describe --contains --all HEAD 2>/dev/null); then + if [[ -n $(git status --porcelain 2>/dev/null | head -n1) ]]; then + __pl_seg "$yellow" "$fg" "${__pl_branch} ${b} ✱" + else + __pl_seg "$green" "$fg" "${__pl_branch} ${b}" + fi + fi + ((s != 0)) && __pl_seg "$red" "$fg" "✘ ${s}" + [[ $EUID == 0 ]] && __pl_seg "$red" "$fg" '#' + __pl_end +} +PROMPT_COMMAND=__prompt + +set -o vi +# eval "$(zoxide init --cmd cd bash)" + +# colorized --help +h() { "$@" --help 2>&1 | bat --plain --language=help; } + +# Record lookup against the local dmz db (adjust the db path to this machine) +lk(){ + local DB="$HOME/projects/dmz/soft/lk/db.rec" + passes=0 count=0; until [ "$count" -eq "1" ] || [ "$passes" -gt 2 ] ; do \ + query="$(recsel "$DB" -p aim,tag | recsel -iq "$query" -CP aim,tag | sort -u | fzf --preview="recsel \"$DB\" -e \"aim~{}\"")" \ + && count="$(recsel "$DB" -q "$query" -c )" ;\ + passes=$(( passes + 1 )) ;\ + done \ + && recsel "$DB" -q "$query" | recfmt -f "$HOME/projects/dmz/soft/lk/lists.fmt" | less +} + +# Keepassxc search +kp() { + local DB="${KP_DB:?Error: KP_DB environment variable is not set. Check .bash_profile.}" + read -sp "Enter db password: " pass; echo ""; printf '%s\n' "$pass" | keepassxc-cli ls -q -R "$DB" | fzf | (read -r entry; printf '%s\n' "$pass" | keepassxc-cli clip -q "$DB" "$entry" 0) +} + +# Per-host extras +[ -f ~/.bashrc.host ] && . ~/.bashrc.host + diff --git a/common/.config/aichat/config.yaml b/common/.config/aichat/config.yaml new file mode 100644 index 0000000..ffa3118 --- /dev/null +++ b/common/.config/aichat/config.yaml @@ -0,0 +1,56 @@ +# see https://github.com/sigoden/aichat/blob/main/config.example.yaml +keybindings: vi +editor: nvim +model: local:Qwen3-Coder-30B-Instruct-UD-Q3_K_XL + +# Sessions: persist REPL sessions and keep more history before summarizing. +# The default compress_threshold (4000) summarizes far too early for 24k+ windows. +save_session: true +compress_threshold: 16000 + +# REPL prompts show live context usage (needs max_input_tokens, set per model below) +left_prompt: '{color.green}{?session {session}{?role /}}{role}{color.cyan}{?rag @{rag}}{color.reset}> ' +right_prompt: '{color.purple}{?session {consume_tokens}/{max_input_tokens} }{color.reset}' + +# NOTE: temperature/top_p are intentionally unset — the LAN server applies tuned +# per-model sampling via --jinja (e.g. GLM 0.6/0.95); a global value would clobber it. + +# max_input_tokens = real ctx-size (from the router) minus output headroom. +clients: +# LAN access (fast; only reachable on the home network) +- type: openai-compatible + name: local + api_base: http://192.168.0.204:11343/v1 + models: &lan_models + - name: Qwen3-Coder-30B-Instruct-UD-Q3_K_XL + max_input_tokens: 30000 # ctx 32768 + - name: Qwen3-Coder-Next-UD-IQ3_XXS + max_input_tokens: 128000 # ctx 131072 + - name: Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS + max_input_tokens: 22000 # ctx 24576 + supports_vision: true + - name: Qwen3.6-35B-A3B-Thinking + max_input_tokens: 22000 # ctx 24576 + supports_vision: true + - name: Qwen3.5-9B-UD-Q6_K_XL + max_input_tokens: 30000 # ctx 32768 + supports_vision: true + - name: gemma-4-26B-A4B-it-UD-IQ4_XS + max_input_tokens: 22000 # ctx 24576 + supports_vision: true + - name: gemma-4-E4B-it-UD-Q8_K_XL + max_input_tokens: 62000 # ctx 65536 + supports_vision: true + - name: GLM-4.7-Flash-UD-Q4_K_XL + max_input_tokens: 22000 # ctx 24576 + - name: gpt-oss-20b + max_input_tokens: 62000 # ctx 65536 + - name: gpt-oss-20b-low + max_input_tokens: 62000 # ctx 65536 +# Remote access via duskadiy.com (reachable from anywhere; requires an API key — +# $DUSKADIY_API_KEY, from the gitignored conf.d/secrets.fish). The LAN endpoint above is keyless. +# Same server, same model ids — reuse the list above via a YAML anchor. +- type: openai-compatible + name: duskadiy + api_base: https://llm.duskadiy.com/api/v1 + models: *lan_models diff --git a/common/.config/aichat/dark.tmTheme b/common/.config/aichat/dark.tmTheme new file mode 100644 index 0000000..b16050c --- /dev/null +++ b/common/.config/aichat/dark.tmTheme @@ -0,0 +1,663 @@ + + + + + + name + OneHalfLight + semanticClass + theme.dark.one_half_dark + uuid + + colorSpaceName + sRGB + author + Son A. Pham <sp@sonpham.me> + comment + A dark iTerm color scheme based on Atom's One. See github.com/sonph/onehalf for installation instructions, a light color scheme, and versions for other editors/terminals such as (Neo)Vim and Sublime Text. + settings + + + settings + + fontStyle + + foreground + #dcdfe4 + background + #282c34 + bracketsOptions + underline + caret + #a3b3cc + gutter + #282c34 + gutterForeground + #919baa + invisibles + #5c6370 + lineHighlight + #313640 + selection + #474e5d + selectionBorder + #474e5d + tagsForeground + + tagsOptions + stippled_underline + bracketContentsOptions + underline + + + + name + Comments + scope + comment + settings + + foreground + #5c6370 + + + + name + Text + scope + variable.parameter.function + settings + + foreground + #dcdfe4 + + + + name + Delimiters + scope + none + settings + + foreground + + + + + name + Operators + scope + keyword.operator + settings + + foreground + + + + + name + Keywords + scope + keyword + settings + + foreground + #c678dd + + + + name + Variables + scope + variable + settings + + foreground + #e06c75 + + + + name + Functions + scope + entity.name.function, meta.require, support.function.any-method + settings + + foreground + #61afef + + + + name + Classes + scope + support.class, entity.name.class, entity.name.type.class + settings + + foreground + #e5c07b + + + + name + Classes + scope + meta.class + settings + + foreground + #e5c07b + + + + name + Methods + scope + keyword.other.special-method + settings + + foreground + #61afef + + + + name + Storage + scope + storage + settings + + foreground + #c678dd + + + + name + Support + scope + support.function + settings + + foreground + #61afef + + + + name + Strings, Inherited Class + scope + string + settings + + foreground + #98c379 + + + + name + Integers + scope + constant.numeric + settings + + foreground + #e5c07b + + + + name + Floats + scope + none + settings + + foreground + #e5c07b + + + + name + Boolean + scope + none + settings + + foreground + #e5c07b + + + + name + Constants + scope + constant + settings + + foreground + #e5c07b + + + + name + HTML: Tags + scope + entity.name.tag + settings + + foreground + #e06c75 + + + + name + HTML: Tag attributes + scope + entity.other.attribute-name + settings + + foreground + #e5c07b + + + + name + Attribute IDs + scope + entity.other.attribute-name.id, punctuation.definition.entity + settings + + foreground + #e5c07b + + + + name + Selector + scope + meta.selector + settings + + foreground + #c678dd + + + + name + Markdown: Headings + scope + markup.heading punctuation.definition.heading, entity.name.section + settings + + fontStyle + + foreground + #61afef + + + + name + Markdown: Bold + scope + markup.bold, punctuation.definition.bold + settings + + foreground + #c678dd + + + + name + Markdown: Italic + scope + markup.italic, punctuation.definition.italic + settings + + foreground + #c678dd + + + + name + Markdown: Code + scope + markup.raw.inline + settings + + foreground + #98c379 + + + + name + Markdown: Link Text + scope + string.other.link, punctuation.definition.string.end.markdown + settings + + foreground + + + + + name + Markdown: Link Url + scope + meta.link + settings + + foreground + #98c379 + + + + name + Markdown: Lists + scope + markup.list + settings + + foreground + + + + + name + Markdown: Quotes + scope + markup.quote + settings + + foreground + #98c379 + + + + name + Java Source + scope + source.java meta.class.java meta.method.java + settings + + foreground + #dcdfe4 + + + + name + Java Class Body + scope + source.java meta.class.java meta.class.body.java + settings + + foreground + #dcdfe4 + + + + name + Javascript: Function Arguments + scope + source.js meta.function.js variable.parameter.function.js + settings + + foreground + #e06c75 + + + + name + Javascript: New Variables + scope + source.js variable.other.readwrite.js + settings + + foreground + #e06c75 + + + + name + Javascript: Variables + scope + source.js variable.other.object.js + settings + + foreground + #dcdfe4 + + + + name + Javascript: Variables in Function Calls + scope + source.js meta.function-call.method.js variable.other.readwrite.js + settings + + foreground + #e06c75 + + + + name + Javascript: New Block Variables + scope + source.js meta.block.js variable.other.readwrite.js + settings + + foreground + #e06c75 + + + + name + Javascript: Block Variables + scope + source.js meta.block.js variable.other.object.js + settings + + foreground + #dcdfe4 + + + + name + Javascript: Block Variables in Function Calls + scope + source.js meta.block.js meta.function-call.method.js variable.other.readwrite.js + settings + + foreground + #dcdfe4 + + + + name + Javascript: Function Calls + scope + source.js meta.function-call.method.js variable.function.js + settings + + foreground + #dcdfe4 + + + + name + Javascript: Properties + scope + source.js meta.property.object.js entity.name.function.js + settings + + foreground + #61afef + + + + name + Javascript: Prototypes + scope + source.js support.constant.prototype.js + settings + + foreground + #dcdfe4 + + + + name + Separator + scope + meta.separator + settings + + background + + foreground + + + + + name + Inserted + scope + markup.inserted + settings + + foreground + #98c379 + + + + name + Deleted + scope + markup.deleted + settings + + foreground + #e06c75 + + + + name + Changed + scope + markup.changed + settings + + foreground + #e5c07b + + + + name + Regular Expressions + scope + string.regexp + settings + + foreground + #98c379 + + + + name + Escape Characters + scope + constant.character.escape + settings + + foreground + #56b6c2 + + + + name + Embedded + scope + punctuation.section.embedded, variable.interpolation + settings + + foreground + + + + + name + Illegal + scope + invalid.illegal + settings + + background + #e06c75 + foreground + #dcdfe4 + + + + name + Broken + scope + invalid.broken + settings + + background + #e5c07b + foreground + #dcdfe4 + + + + name + Deprecated + scope + invalid.deprecated + settings + + background + #e5c07b + foreground + #dcdfe4 + + + + name + Unimplemented + scope + invalid.unimplemented + settings + + background + #c678dd + foreground + #dcdfe4 + + + + + \ No newline at end of file diff --git a/common/.config/aichat/roles/code.md b/common/.config/aichat/roles/code.md new file mode 100644 index 0000000..a541cf0 --- /dev/null +++ b/common/.config/aichat/roles/code.md @@ -0,0 +1,9 @@ +--- +temperature: 0.2 +--- +You are a terse coding assistant. + +- Lead with the code, as a single fenced block. +- Add at most 1–2 short sentences of explanation, and only if genuinely needed. No preamble, no "here is", no closing summary. +- Match the language and style implied by the question; if unspecified, infer the most likely one. +- If the request is ambiguous, make the most reasonable assumption and state it in one line above the code. diff --git a/common/.config/aichat/roles/commit.md b/common/.config/aichat/roles/commit.md new file mode 100644 index 0000000..5e24f95 --- /dev/null +++ b/common/.config/aichat/roles/commit.md @@ -0,0 +1,11 @@ +--- +temperature: 0.2 +--- +You write a git commit message from a diff. Output ONLY the message — no preamble, no code fences, no explanation. + +- Subject: `[Scope] imperative summary`, where Scope is a short tag inferred from the changed paths, matching the style of recent `git log` (e.g. [Waybar], [Fish], [LLM]). Keep it under 70 characters. +- Imperative mood ("add", "fix", "rename"), never past tense. +- For a non-trivial change, leave a blank line then add 1–4 terse bullets ("- ...") covering what changed and why. Omit the body for small, self-explanatory changes. +- Describe only what the diff actually shows; do not invent rationale or files. + +The user's input is the diff (typically `git diff --cached`). diff --git a/common/.config/aichat/roles/fish.md b/common/.config/aichat/roles/fish.md new file mode 100644 index 0000000..ecaaf30 --- /dev/null +++ b/common/.config/aichat/roles/fish.md @@ -0,0 +1,9 @@ +--- +temperature: 0.1 +--- +You translate a request into a single shell command for the fish shell on Arch Linux. Output ONLY the command on one line — no code fence, no explanation. + +- Use fish syntax, not bash. Notably: `set -x VAR value` (not `export`), `$status` (not `$?`), the `test` / `string` / `math` builtins, and `(cmd)` for command substitution (not `$(cmd)`). `&&` / `||` work in modern fish. +- Package management is pacman / yay (`yay -S`, `pacman -Qi`, `pacman -Qo`). Clipboard is Wayland: `wl-copy` / `wl-paste`. +- Prefer one pipeline. If several steps are unavoidable, chain them with `; and`. +- If it can't be done safely in one command, output a single `# ...` comment saying why instead. diff --git a/common/.config/aichat/roles/review.md b/common/.config/aichat/roles/review.md new file mode 100644 index 0000000..6749d82 --- /dev/null +++ b/common/.config/aichat/roles/review.md @@ -0,0 +1,11 @@ +--- +temperature: 0.2 +--- +You review a code diff for defects. Be concise and specific. + +- Focus on correctness bugs, edge cases, error handling, security issues, and clear regressions. Skip style and formatting nits unless they cause a bug. +- Format each finding as `file:line — issue — why it matters`, most severe first. +- Reference exact identifiers and lines from the diff; do not speculate about code you cannot see. +- If nothing substantive is wrong, say so in one line rather than inventing issues. + +The user's input is the diff (typically `git diff`). diff --git a/common/.config/aichat/roles/shell.md b/common/.config/aichat/roles/shell.md new file mode 100644 index 0000000..f154c1c --- /dev/null +++ b/common/.config/aichat/roles/shell.md @@ -0,0 +1,10 @@ +--- +model: local:gemma-4-E4B-it-UD-Q8_K_XL +temperature: 0.1 +--- +You translate a request into a single shell command for the fish shell on Arch Linux. Output ONLY the command on one line — no code fence, no explanation. + +- Use fish syntax, not bash. Notably: `set -x VAR value` (not `export`), `$status` (not `$?`), the `test` / `string` / `math` builtins, and `(cmd)` for command substitution (not `$(cmd)`). `&&` / `||` work in modern fish. +- Package management is pacman / yay (`yay -S`, `pacman -Qi`, `pacman -Qo`). Clipboard is Wayland: `wl-copy` / `wl-paste`. +- Prefer one pipeline. If several steps are unavoidable, chain them with `; and`. +- If it can't be done safely in one command, output a single `# ...` comment saying why instead. diff --git a/.config/bat/config b/common/.config/bat/config similarity index 68% rename from .config/bat/config rename to common/.config/bat/config index a4dc157..9a5d3a9 100644 --- a/.config/bat/config +++ b/common/.config/bat/config @@ -4,12 +4,13 @@ # Specify desired highlighting theme (e.g. "TwoDark"). Run `bat --list-themes` # for a list of all available themes ---theme="Catppuccin Mocha" ---style="numbers,changes,header" +# Custom theme lives in ./themes/ — run `bat cache --build` after changing it +--theme="OneHalfDark" +--style="numbers,changes,header,snip" # Enable this to use italic text on the terminal. This is not supported on all # terminal emulators (like tmux, by default): -#--italic-text=always +--italic-text=always # Uncomment the following line to disable automatic paging: #--paging=never @@ -17,10 +18,17 @@ # Uncomment the following line if you are using less version >= 551 and want to # enable mouse scrolling support in `bat` when running inside tmux. This might # disable text selection, unless you press shift. ---pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse" +--pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse --ignore-case" # Syntax mappings: map a certain filename pattern to a language. # Example 1: use the C++ syntax for Arduino .ino files # Example 2: Use ".gitignore"-style highlighting for ".ignore" files --map-syntax "*.ino:C++" --map-syntax ".ignore:Git Ignore" +--map-syntax "**/hypr/**/*.conf:INI" +--map-syntax "**/kitty/*.conf:INI" +--map-syntax "*.ctl:INI" +--map-syntax ".inputrc:bash" +--map-syntax "**/cava/config:INI" +--map-syntax "**/mpv/*.conf:INI" +--map-syntax "**/btop/btop.conf:INI" diff --git a/common/.config/bat/themes/onedark.tmTheme b/common/.config/bat/themes/onedark.tmTheme new file mode 100644 index 0000000..aa14d99 --- /dev/null +++ b/common/.config/bat/themes/onedark.tmTheme @@ -0,0 +1,260 @@ + + + + + name + One Dark (NvChad base46) + uuid + 3c1e2b84-9d5f-4a7c-8b2e-6f4a1d9c7e5b + settings + + + settings + + background + #1e222a + foreground + #abb2bf + caret + #abb2bf + selection + #3e4451 + lineHighlight + #252931 + gutterForeground + #545862 + + + + name + Comment + scope + comment, punctuation.definition.comment + settings + + foreground + #6f737b + fontStyle + italic + + + + name + String + scope + string + settings + + foreground + #98c379 + + + + name + Number, constant + scope + constant.numeric, constant.language, constant.other + settings + + foreground + #d19a66 + + + + name + Escape character + scope + constant.character.escape + settings + + foreground + #a3b8ef + + + + name + Keyword, storage + scope + keyword, storage + settings + + foreground + #c882e7 + + + + name + Operator + scope + keyword.operator + settings + + foreground + #abb2bf + + + + name + Function + scope + entity.name.function, support.function, meta.function-call.generic + settings + + foreground + #61afef + + + + name + Class, type + scope + entity.name.class, entity.name.type, entity.name.struct, entity.name.enum, support.type, support.class + settings + + foreground + #e7c787 + + + + name + Variable + scope + variable, variable.other + settings + + foreground + #e06c75 + + + + name + Function parameter + scope + variable.parameter + settings + + foreground + #e06c75 + fontStyle + italic + + + + name + Attribute + scope + entity.other.attribute-name + settings + + foreground + #d19a66 + + + + name + Tag + scope + entity.name.tag, meta.tag + settings + + foreground + #e06c75 + + + + name + Markup heading + scope + markup.heading, entity.name.section + settings + + foreground + #e06c75 + fontStyle + bold + + + + name + Markup bold + scope + markup.bold + settings + + fontStyle + bold + + + + name + Markup italic + scope + markup.italic + settings + + fontStyle + italic + + + + name + Markup link + scope + markup.underline.link, string.other.link + settings + + foreground + #61afef + + + + name + Markup quote + scope + markup.quote + settings + + foreground + #6f737b + fontStyle + italic + + + + name + Diff inserted + scope + markup.inserted + settings + + foreground + #98c379 + + + + name + Diff deleted + scope + markup.deleted + settings + + foreground + #e06c75 + + + + name + Invalid + scope + invalid, invalid.illegal + settings + + foreground + #ffffff + background + #be5046 + + + + + diff --git a/common/.config/bat/themes/synthwave84.tmTheme b/common/.config/bat/themes/synthwave84.tmTheme new file mode 100644 index 0000000..30ee7b7 --- /dev/null +++ b/common/.config/bat/themes/synthwave84.tmTheme @@ -0,0 +1,262 @@ + + + + + name + Synthwave 84 + uuid + 7b2c3a10-4b6e-4d8f-9c1a-2e5f8d7a9b3c + settings + + + settings + + background + #262335 + foreground + #ffffff + caret + #f97e72 + selection + #463465 + lineHighlight + #2a2139 + gutterForeground + #848bbd + + + + name + Comment + scope + comment, punctuation.definition.comment + settings + + foreground + #848bbd + fontStyle + italic + + + + name + String + scope + string + settings + + foreground + #ff8b39 + + + + name + Number, constant + scope + constant.numeric, constant.language, constant.other + settings + + foreground + #f97e72 + + + + name + Escape character + scope + constant.character.escape + settings + + foreground + #36f9f6 + + + + name + Keyword, storage + scope + keyword, storage, keyword.operator + settings + + foreground + #fede5d + + + + name + Storage type + scope + storage.type, storage.modifier + settings + + foreground + #fede5d + fontStyle + italic + + + + name + Function + scope + entity.name.function, support.function, meta.function-call.generic + settings + + foreground + #36f9f6 + + + + name + Class, type + scope + entity.name.class, entity.name.type, entity.name.struct, entity.name.enum, support.type, support.class + settings + + foreground + #fe4450 + + + + name + Variable + scope + variable, variable.other + settings + + foreground + #ff7edb + + + + name + Function parameter + scope + variable.parameter + settings + + foreground + #ff7edb + fontStyle + italic + + + + name + Attribute + scope + entity.other.attribute-name + settings + + foreground + #fede5d + + + + name + Tag + scope + entity.name.tag, meta.tag + settings + + foreground + #72f1b8 + + + + name + Markup heading + scope + markup.heading, entity.name.section + settings + + foreground + #fe4450 + fontStyle + bold + + + + name + Markup bold + scope + markup.bold + settings + + fontStyle + bold + + + + name + Markup italic + scope + markup.italic + settings + + fontStyle + italic + + + + name + Markup link + scope + markup.underline.link, string.other.link + settings + + foreground + #36f9f6 + + + + name + Markup quote + scope + markup.quote + settings + + foreground + #848bbd + fontStyle + italic + + + + name + Diff inserted + scope + markup.inserted + settings + + foreground + #72f1b8 + + + + name + Diff deleted + scope + markup.deleted + settings + + foreground + #fe4450 + + + + name + Invalid + scope + invalid, invalid.illegal + settings + + foreground + #ffffff + background + #fe4450 + + + + + diff --git a/.config/btop/btop.conf b/common/.config/btop/btop.conf similarity index 99% rename from .config/btop/btop.conf rename to common/.config/btop/btop.conf index 512f23d..3f004cb 100644 --- a/.config/btop/btop.conf +++ b/common/.config/btop/btop.conf @@ -253,7 +253,7 @@ show_battery_watts = true log_level = "WARNING" #* Automatically save current settings to config file on exit. -save_config_on_exit = true +save_config_on_exit = false #* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards. nvml_measure_pcie_speeds = true diff --git a/.config/cava/config b/common/.config/cava/config similarity index 99% rename from .config/cava/config rename to common/.config/cava/config index e1b8891..33e05f8 100644 --- a/.config/cava/config +++ b/common/.config/cava/config @@ -4,7 +4,7 @@ [general] # Smoothing mode. Can be 'normal', 'scientific' or 'waves'. DEPRECATED as of 0.6.0 -mode = scientific +# mode = scientific # deprecated since cava 0.6.0, ignored # Accepts only non-negative values. framerate = 60 diff --git a/.config/cava/shaders/bar_spectrum.frag b/common/.config/cava/shaders/bar_spectrum.frag similarity index 100% rename from .config/cava/shaders/bar_spectrum.frag rename to common/.config/cava/shaders/bar_spectrum.frag diff --git a/.config/cava/shaders/eye_of_phi.frag b/common/.config/cava/shaders/eye_of_phi.frag similarity index 100% rename from .config/cava/shaders/eye_of_phi.frag rename to common/.config/cava/shaders/eye_of_phi.frag diff --git a/.config/cava/shaders/northern_lights.frag b/common/.config/cava/shaders/northern_lights.frag similarity index 100% rename from .config/cava/shaders/northern_lights.frag rename to common/.config/cava/shaders/northern_lights.frag diff --git a/.config/cava/shaders/pass_through.vert b/common/.config/cava/shaders/pass_through.vert similarity index 100% rename from .config/cava/shaders/pass_through.vert rename to common/.config/cava/shaders/pass_through.vert diff --git a/.config/cava/shaders/spectrogram.frag b/common/.config/cava/shaders/spectrogram.frag similarity index 100% rename from .config/cava/shaders/spectrogram.frag rename to common/.config/cava/shaders/spectrogram.frag diff --git a/.config/cava/shaders/winamp_line_style_spectrum.frag b/common/.config/cava/shaders/winamp_line_style_spectrum.frag similarity index 100% rename from .config/cava/shaders/winamp_line_style_spectrum.frag rename to common/.config/cava/shaders/winamp_line_style_spectrum.frag diff --git a/.config/cava/themes/solarized_dark b/common/.config/cava/themes/solarized_dark similarity index 100% rename from .config/cava/themes/solarized_dark rename to common/.config/cava/themes/solarized_dark diff --git a/.config/cava/themes/tricolor b/common/.config/cava/themes/tricolor similarity index 100% rename from .config/cava/themes/tricolor rename to common/.config/cava/themes/tricolor diff --git a/common/.config/claude/CLAUDE.md b/common/.config/claude/CLAUDE.md new file mode 100644 index 0000000..38c82f0 --- /dev/null +++ b/common/.config/claude/CLAUDE.md @@ -0,0 +1,21 @@ +# Global user preferences (applies to every project) + +## Environment +- Arch Linux, Wayland, fish shell, kitty terminal. Prefer fish-compatible syntax in shell snippets (not bash-isms) when writing things the user will run interactively. Clipboard is `wl-copy`. +- Package manager: pacman / yay. Common abbrs: `pacs`, `yays`. + +## Local-first LLMs +- The user self-hosts models on the LAN at `192.168.0.204`: + - llama.cpp server — `http://192.168.0.204:11343/v1` (OpenAI-compatible). + - Ollama — `192.168.0.204:11434`. +- Recurring models: Qwen3-Coder-30B, gemma-4-26B, DeepSeek-Coder-V2-Lite, GLM-4.7-Flash. When configuring any AI/agent tool, default to these endpoints rather than a cloud API unless asked otherwise. + +## Git +- **Do not run `git commit` or `git push`, and never offer or ask to do the committing/pushing yourself** — the user reviews and commits entirely manually (these are also denied in settings.json). Stage/prepare changes and stop there. You may suggest a commit message for the user to use, but nothing more. +- To hand off a suggested commit message, write it to the per-worktree file `$(git rev-parse --git-path CLAUDE_COMMIT_MSG)` — **not** `COMMIT_EDITMSG` (git overwrites that on `git commit`). A `prepare-commit-msg` hook prefills the editor from it (one-shot) on the next `git commit` / lazygit `C`. Just the raw message, no `#` comments. + +## Formatting +- Lua: format with `stylua` (2-space indent, 120 col, no call parentheses — see any `.stylua.toml`). + +## Dotfiles +- Configs live in the dots repo under `.config/` and are surfaced via per-directory symlinks into `~/.config/` (and `~/.claude/`). Editing a file under the repo's `.config/` is editing the live config — no deploy step. diff --git a/common/.config/claude/README.md b/common/.config/claude/README.md new file mode 100644 index 0000000..9ec13f9 --- /dev/null +++ b/common/.config/claude/README.md @@ -0,0 +1,91 @@ +# Claude Code config + +Portable [Claude Code](https://claude.com/claude-code) customizations, synced via these +dots. Claude Code still uses its default `~/.claude/` directory; we surface the tracked +files below into it via per-file/dir symlinks, so edits made through Claude write straight +back into this repo. + +## What's tracked here + +- `settings.json` — global settings: permissions (deny git commit/push + read of secret + files), default `model`, `enabledPlugins`, vim mode, dark theme, `effortLevel`, fullscreen + TUI, `statusLine`, `hooks`, `worktree` defaults, … +- `statusline.py` — rich status line (dir, git, model, context %, cost, disk — system metrics live in the tmux bar). + See [StatusBar.md](StatusBar.md) for what each segment and colour means. +- `keybindings.json` — custom keybindings (vim-style scroll/navigation). +- `CLAUDE.md` — global user preferences applied to every project. +- `hooks/format.py` — PostToolUse hook: formats edited files by extension (stylua / ruff / + prettier / gofmt / rustfmt / fish_indent / shfmt / taplo). Opinionated formatters (stylua, ruff, + prettier, taplo) only run where the project opts in via a config file found walking up; gofmt and + rustfmt gate on a project marker (`go.mod` / `Cargo.toml`); fish_indent and shfmt run on sight. + No-ops if the formatter isn't installed, and always exits 0. +- `skills/` — custom [Agent Skills](https://code.claude.com/docs/en/skills). See **Skills** below. +- `link.sh` — idempotent bootstrap that creates the `~/.claude` symlinks below. + +Paths inside `settings.json` reference `~/.claude/...` via `$HOME`/`PATH` (`/usr/bin/env +python3 ~/.claude/...`), so they work regardless of the username, python location, or where the +dots repo is cloned (`~/.dots`, …). + +## What is **not** tracked (stays local, per device) + +Everything else under `~/.claude/` is machine-specific or secret and must never be committed: +credentials (`.credentials.json`), session history, `projects/` (transcripts + memory), +caches, and the `plugins/` cache/binaries. + +## Setup on a new device + +```sh +# 1. Deploy the dotfiles repo — install.sh stows ~/.config/claude AND runs link.sh for you +cd ~/.dots && ./install.sh + +# 2. Launch Claude and log in once (credentials are NOT synced) +claude + +# 3. Reinstall the plugins from settings.json -> enabledPlugins +# (typescript-lsp, frontend-design — anthropics/claude-plugins-official) via /plugin. +# Optional: install any formatters you want the hook to use (stylua, ruff, prettier, …). +``` + +`install.sh` deploys `~/.config/claude` (via stow) and then runs `link.sh` automatically. You only +need to run `link.sh` by hand (`bash ~/.config/claude/link.sh`) if a link later gets clobbered +(e.g. Claude's `/config` replaces one with a plain file) — it's idempotent and self-healing. + +A **new skill/hook** added to the repo shows up after a re-stow — `./install.sh` (or `dotsync`) links +the new file into `~/.config/claude`, which `link.sh` then surfaces into `~/.claude`. + +## Skills + +Custom skills live in `skills//SKILL.md` (the directory name is the `/command`) and are +linked in as `~/.claude/skills`. All are `disable-model-invocation: true` — explicit `/` only, +so Claude never auto-triggers them. Run them inside the repo whose branch you're working on. + +- **`/review-branch [base]`** — reviews the current branch's diff (vs its merge-base with the + base branch) for 🐞 bugs / 🔒 security / ⚡ optimizations / 📖 readability, reports grouped + findings with `file:line`, then applies the readability + safe fixes *after you approve*. Never + commits. + Borrows `/code-review`'s discipline: CLAUDE.md-aware, changed-lines-only, verified bugs with a + false-positive filter (skips what linters/CI catch and pre-existing issues). Ends by pointing + you at the relevant built-in follow-ups (`/security-review`, `/code-review`, `/verify`, …). +- **`/pr-description [base]`** — auto-detects this repo's GitHub PR template + (`.github/pull_request_template.md`, …), fills it from the branch diff + commits, prints a + copy-paste markdown block, and copies it to the clipboard with `wl-copy`. Leaves verification + checkboxes for you; never creates/pushes the PR. +- **`/commit-msg [hint]`** — reads the **staged** diff, infers the repo's commit style from recent + `git log` (e.g. this repo's `[Scope] summary`), drafts a matching message, prints it, and copies + it to the clipboard with `wl-copy`. Never stages or commits — draft only. +- **`/pr-loop [pr]`** — babysits the current branch's PR in a self-paced loop: fixes review + comments that don't need your input — from **any** reviewer (Codex, Claude, humans) plus any + `@claude` request — `git add`s them, drafts the commit message (`/commit-msg` style) and marks + each thread with a reaction instead of an "Addressed — …" reply: **👍** fixed, **👀** parked for + your decision, **👎** a bot false positive (bots only, always with a one-line why). Then pings you + to commit & push and re-checks every ~10 min (via `ScheduleWakeup`). Resolves threads once their + fix is pushed — dismissals right away, since there's nothing to land. Stops when no unresolved comments + remain or the automated reviewers (Codex/Claude) hit their limit. Prep-only — never + commits/pushes/merges; needs `gh` authed and the session left open. Each cycle it prints when it + will re-check and that `Esc` (between cycles) stops it; on the last cycle it prints a clear + **"Finished — no more active issues"** and does not reschedule. + +## Adding more config later + +Drop the file/dir under `.config/claude/` here, add its name to the `items` list in `link.sh`, +and re-run `bash ~/.config/claude/link.sh`. For example `commands/` or `agents/`. diff --git a/common/.config/claude/StatusBar.md b/common/.config/claude/StatusBar.md new file mode 100644 index 0000000..09c62b6 --- /dev/null +++ b/common/.config/claude/StatusBar.md @@ -0,0 +1,76 @@ +# Status line (`statusline.py`) + +The custom Claude Code status line, ordered **work info (left) → system info (right)**, +segments joined by a light-gray `│`. It's **responsive**: always a single line — if the +rendered width would exceed the terminal (`$COLUMNS`, which Claude Code re-exports on +resize), trailing (lowest-priority) segments are dropped from the right until it fits. + +Wide — everything: + +``` +📁 ~/.dots │ 🌿 .dots main* │ 🤖 Opus 4.8 (1M) · xhigh │ 📝 84% (843k) │ 📊 34% 2h54m │ 💰 $0.40 $12.00/h │ 💾 81% +``` + +Narrow — trailing segments trimmed: + +``` +📁 ~/.dots │ 🌿 .dots main* │ 🤖 Opus 4.8 (1M) · xhigh │ 📝 84% (843k) │ 📊 34% 2h54m │ 💰 $0.40 $12.00/h +``` + +Every segment is defensive: if its data is missing or a command fails, the segment is +simply omitted (the bar never crashes the UI). Secondary detail (the parts in light gray) +is supplementary to the main colored value. + +## Live segments + +| | Segment | Shows | Notes | +|---|---|---|---| +| | **Vim mode** | `[I]`/`[N]`/`[V]` editor mode | only with `editorMode: vim`; pair with `hideVimModeIndicator: true` so the built-in one doesn't duplicate it | +| 📁 | **Directory** | Current working dir, with `~` for home | cyan | +| 🌿 | **Git** | `repo branch` + `*` if dirty + `↑N`/`↓N` ahead/behind upstream | repo = magenta; branch = green when clean, yellow + red `*` when dirty; `↑` cyan, `↓` yellow. Omitted outside a repo | +| 🤖 | **Model** | Active model display name + `· effort` level (`low`/`medium`/`high`/`xhigh`) | name = blue, effort = light gray; effort omitted for models without the param | +| 📝 | **Context** | `% of context window used` + `(Nk)` tokens | window = 1M for `[1m]` models, else 200k. Adds a red **⚠compact** at ≥80% | +| 📊 | **5h usage** | `% of the 5-hour rolling limit used` + time until it resets | from `rate_limits.five_hour`; Pro/Max only, and absent until the first API response of a session | +| 💰 | **Cost** | `$` session cost so far + `$/h` burn rate | burn rate shown once the session exceeds ~30s | +| 💾 | **Disk** | `% used` of the filesystem at the cwd | from `statvfs` | + +## Colour legend + +Most numbers are **green / yellow / red** by threshold — green = healthy, red = needs +attention. Light gray = secondary detail. Thresholds (`green < … < yellow < … ≤ red`): + +| Segment | green | yellow | red | +|---|---|---|---| +| Context | `< 50%` | `50–80%` | `≥ 80%` (⚠compact) | +| 5h usage | `< 50%` | `50–80%` | `≥ 80%` | +| Disk | `< 75%` | `75–90%` | `≥ 90%` | + +> Light gray is `\033[37m`; dimming (`\033[2m`) is disabled because it blended the gray +> detail text into dark terminal backgrounds. To go brighter, set `"gray"` to `\033[97m` +> (bright white) in the `C` table near the top of `statusline.py`. + +## Optional segments (defined but not shown) + +These functions exist in `statusline.py` but aren't in the output. Enable one by adding it +to the `work` or `system` list in `main()`: + +RAM/CPU/temp were **retired 2026-07-26**: the status line only refreshes on message +events, so system metrics sat visibly stale between turns — the tmux status bar +(`tmux/scripts/`) owns live system metrics now. + +| | Function | Shows | +|---|---|---| +| 🧠 | `ram_segment` | `% used` + `used/totalG` from `/proc/meminfo` (green `<70%` / yellow / red `≥85%`) | +| 🖥️ | `cpu_segment` | `% busy` (diffed `/proc/stat` snapshot) + loadavg + cores (green `<60%` / red `≥85%`) | +| 🌡️ | `temp_segment` | hottest CPU thermal zone °C (green `<60` / red `≥80`) | +| ✏️ | `velocity_segment` | `+added/-removed` lines this session + lines/min | +| ♻️ | `cache_segment` | prompt-cache hit % (higher is better) | +| ⚙️ | `api_segment` | share of wall-clock time spent in API/inference | +| | `version_segment` | Claude Code version (`vX.Y.Z`) | +| 🎨 | `style_segment` | active output-style name | + +## Where it lives + +`statusline.py` is tracked in this repo and symlinked to `~/.claude/statusline.py`; +`settings.json` runs it via `statusLine` (`/usr/bin/env python3 ~/.claude/statusline.py`). +Edits take effect on the next status refresh. diff --git a/common/.config/claude/hooks/format.py b/common/.config/claude/hooks/format.py new file mode 100755 index 0000000..67ffac3 --- /dev/null +++ b/common/.config/claude/hooks/format.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""PostToolUse hook: format an edited file with the right formatter. + +Reads the hook JSON from stdin, extracts the edited file path, and runs a +language-appropriate formatter -- but, to avoid imposing a style on a project +that never asked for it, opinionated formatters (stylua, ruff, prettier, taplo) +only run when the project opts in via a config file discoverable by walking up +from the edited file. Canonical single-style toolchains run on sight: gofmt and +rustfmt on their go.mod / Cargo.toml marker; fish_indent and shfmt whenever the +tool itself is installed (shell and fish have one de-facto style, no config). + +Best-effort: always exits 0 so a format hiccup never blocks an edit. jq is not +guaranteed on the host, hence python for the stdin JSON parse. +""" +import json +import os +import shutil +import subprocess +import sys + + +def find_up(start, names): + """Nearest ancestor dir (incl. the file's own) containing any of `names`.""" + d = os.path.dirname(os.path.abspath(start)) + while True: + if any(os.path.exists(os.path.join(d, n)) for n in names): + return d + parent = os.path.dirname(d) + if parent == d: + return None + d = parent + + +def has_toml_section(start, filename, section): + """True if the nearest ancestor `filename` contains `section`.""" + d = find_up(start, [filename]) + if not d: + return False + try: + with open(os.path.join(d, filename), errors="ignore") as fh: + return section in fh.read() + except Exception: + return False + + +def stylua_cmd(path): + exe = os.path.expanduser("~/.local/share/nvim/mason/bin/stylua") + if not os.access(exe, os.X_OK): + exe = shutil.which("stylua") + if exe and find_up(path, [".stylua.toml", "stylua.toml"]): + return [exe, path] + return None + + +def ruff_cmd(path): + exe = shutil.which("ruff") + if exe and (find_up(path, ["ruff.toml", ".ruff.toml"]) + or has_toml_section(path, "pyproject.toml", "[tool.ruff")): + return [exe, "format", path] + return None + + +_PRETTIER_CFGS = [ + ".prettierrc", ".prettierrc.json", ".prettierrc.yaml", ".prettierrc.yml", + ".prettierrc.json5", ".prettierrc.js", ".prettierrc.cjs", ".prettierrc.mjs", + ".prettierrc.toml", "prettier.config.js", "prettier.config.cjs", + "prettier.config.mjs", +] + + +def prettier_cmd(path): + exe = shutil.which("prettier") + if exe and find_up(path, _PRETTIER_CFGS): + return [exe, "--write", path] + return None + + +def gofmt_cmd(path): + exe = shutil.which("gofmt") + if exe and find_up(path, ["go.mod"]): # one canonical style + return [exe, "-w", path] + return None + + +def rustfmt_cmd(path): + exe = shutil.which("rustfmt") + if exe and find_up(path, ["Cargo.toml", "rustfmt.toml", ".rustfmt.toml"]): + return [exe, path] + return None + + +def fish_cmd(path): + exe = shutil.which("fish_indent") # ships with fish, one canonical style + return [exe, "-w", path] if exe else None + + +def shfmt_cmd(path): + exe = shutil.which("shfmt") # de-facto shell formatter, sane defaults + return [exe, "-w", path] if exe else None + + +def taplo_cmd(path): + exe = shutil.which("taplo") + # opt-in: TOML reflow/align is intrusive, so only where the project asked. + if exe and find_up(path, ["taplo.toml", ".taplo.toml"]): + return [exe, "format", path] + return None + + +HANDLERS = { + ".lua": stylua_cmd, + ".py": ruff_cmd, + ".js": prettier_cmd, ".jsx": prettier_cmd, ".mjs": prettier_cmd, + ".cjs": prettier_cmd, ".ts": prettier_cmd, ".tsx": prettier_cmd, + ".css": prettier_cmd, ".scss": prettier_cmd, ".less": prettier_cmd, + ".html": prettier_cmd, ".vue": prettier_cmd, ".json": prettier_cmd, + ".md": prettier_cmd, ".yaml": prettier_cmd, ".yml": prettier_cmd, + ".go": gofmt_cmd, + ".rs": rustfmt_cmd, + ".fish": fish_cmd, + ".sh": shfmt_cmd, ".bash": shfmt_cmd, + ".toml": taplo_cmd, +} + + +def main(): + try: + data = json.load(sys.stdin) + except Exception: + return + ti = data.get("tool_input") or {} + tr = data.get("tool_response") or {} + path = ti.get("file_path") or tr.get("filePath") or "" + if not path or not os.path.isfile(path): + return + handler = HANDLERS.get(os.path.splitext(path)[1].lower()) + if not handler: + return + cmd = handler(path) + if not cmd: + return + try: + subprocess.run(cmd, timeout=15, capture_output=True) + except Exception: + pass + + +if __name__ == "__main__": + main() diff --git a/common/.config/claude/keybindings.json b/common/.config/claude/keybindings.json new file mode 100644 index 0000000..597ee81 --- /dev/null +++ b/common/.config/claude/keybindings.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://www.schemastore.org/claude-code-keybindings.json", + "$docs": "https://code.claude.com/docs/en/keybindings", + "bindings": [ + { + "context": "Global", + "bindings": { + "alt+j": "scroll:lineDown", + "alt+k": "scroll:lineUp", + "ctrl+shift+j": "scroll:lineDown", + "ctrl+shift+k": "scroll:lineUp" + } + }, + { + "context": "MessageSelector", + "bindings": { + "g": "messageSelector:top", + "shift+g": "messageSelector:bottom" + } + }, + { + "context": "Select", + "bindings": { + "g": "select:first", + "shift+g": "select:last" + } + }, + { + "context": "Footer", + "bindings": { + "j": "footer:down", + "k": "footer:up" + } + } + ] +} diff --git a/common/.config/claude/link.sh b/common/.config/claude/link.sh new file mode 100755 index 0000000..d5c9493 --- /dev/null +++ b/common/.config/claude/link.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# Surface this repo's Claude Code config into ~/.claude via symlinks. +# +# `stow` deploys the repo to ~/.config/claude; Claude itself reads from ~/.claude, +# so we link the individual files/dirs across. Idempotent and self-healing: re-run +# any time. It also flags any file that has broken out of stow into a plain file — +# which silently stops Claude's edits from reaching the repo. +# (New skills/hooks added to the repo appear after a re-stow: `./install.sh` or `dotsync`.) +# +# bash ~/.config/claude/link.sh +set -eu + +SRC="$HOME/.config/claude" +DST="$HOME/.claude" + +if [ ! -e "$SRC" ]; then + echo "error: $SRC not found — run 'cd ~/.dots && ./install.sh' first" >&2 + exit 1 +fi + +mkdir -p "$DST" + +# This repo's claude dir, resolved via link.sh's own symlink (location-independent). +repo=$(dirname "$(readlink -f "$SRC/link.sh")") + +# If link.sh itself has broken out of stow into a plain file, $repo resolves to $SRC and +# the drift heal below would relink each drifted file onto ITSELF — destroying the only +# copy of its content. Refuse to continue. +if [ "$repo" = "$SRC" ]; then + echo "error: link.sh itself is a plain file (broken out of stow) — re-stow first:" >&2 + echo " cd ~/.dots && ./install.sh" >&2 + exit 1 +fi + +# Tracked items to expose in ~/.claude. Add new ones (e.g. commands agents) here. +items="settings.json statusline.py keybindings.json CLAUDE.md hooks skills" + +# Expose each item in ~/.claude (where Claude actually reads). +for item in $items; do + [ -e "$SRC/$item" ] || continue + if [ -d "$DST/$item" ] && [ ! -L "$DST/$item" ]; then + echo "skip ~/.claude/$item (real directory in the way — move it aside first)" >&2 + continue + fi + # A plain FILE here means an atomic write replaced the link (Claude writes e.g. + # ~/.claude/settings.json) — it may hold newer edits, so never clobber it silently: + # heal only when provably lossless, warn otherwise (mirrors the SRC drift guard below). + if [ -f "$DST/$item" ] && [ ! -L "$DST/$item" ]; then + same=0 + case "$item" in + *.json) + python3 -c 'import json,sys; sys.exit(0 if json.load(open(sys.argv[1]))==json.load(open(sys.argv[2])) else 1)' \ + "$DST/$item" "$SRC/$item" 2>/dev/null && same=1 ;; + *) + cmp -s "$DST/$item" "$SRC/$item" && same=1 ;; + esac + if [ "$same" != 1 ]; then + echo "DRIFT ~/.claude/$item is a real file that differs from the repo copy —" >&2 + echo " it may hold newer edits. Review, then re-link:" >&2 + echo " diff '$DST/$item' '$SRC/$item' # compare; keep whichever is right" >&2 + echo " ln -sfn '$SRC/$item' '$DST/$item' # re-link" >&2 + continue + fi + echo "healed ~/.claude/$item (plain copy identical to repo — re-linking)" + fi + ln -sfn "$SRC/$item" "$DST/$item" + echo "linked ~/.claude/$item -> ~/.config/claude/$item" +done + +# Stow-layer drift guard: each SRC *file* should be a symlink into the repo, so edits +# Claude writes (e.g. via /config) flow back and stay tracked. If an atomic write +# replaced one with a plain file, it has silently broken out of stow. Auto-heal only +# when provably lossless — the plain file is identical to the repo copy (JSON compared +# ignoring key order, since /config often just reorders keys). Otherwise warn and stop: +# the file may hold newer edits worth diffing in before re-linking. +for item in $items; do + [ -f "$SRC/$item" ] && [ ! -L "$SRC/$item" ] || continue # a real file where a link belongs + same=0 + if [ -f "$repo/$item" ]; then + case "$item" in + *.json) + python3 -c 'import json,sys; sys.exit(0 if json.load(open(sys.argv[1]))==json.load(open(sys.argv[2])) else 1)' \ + "$SRC/$item" "$repo/$item" 2>/dev/null && same=1 ;; + *) + cmp -s "$SRC/$item" "$repo/$item" && same=1 ;; + esac + fi + if [ "$same" = 1 ]; then + ln -sfrn "$repo/$item" "$SRC/$item" # -r: relative link, so stow keeps owning it + echo "healed ~/.config/claude/$item (plain copy identical to repo — re-linked)" + continue + fi + echo "DRIFT ~/.config/claude/$item is a real file that differs from the repo copy —" >&2 + echo " it may hold edits not yet tracked. Review, then re-link:" >&2 + echo " diff '$SRC/$item' '$repo/$item' # compare; keep whichever is right" >&2 + echo " ln -sfrn '$repo/$item' '$SRC/$item' # re-link (relative, stow-owned)" >&2 +done diff --git a/common/.config/claude/settings.json b/common/.config/claude/settings.json new file mode 100644 index 0000000..d62594e --- /dev/null +++ b/common/.config/claude/settings.json @@ -0,0 +1,79 @@ +{ + "attribution": { + "commit": "", + "pr": "" + }, + "permissions": { + "deny": [ + "Bash(git push)", + "Bash(git push:*)", + "Bash(git commit)", + "Bash(git commit:*)", + "Bash(git reset --hard)", + "Bash(git reset --hard:*)", + "Bash(git clean)", + "Bash(git clean:*)", + "Read(**/.env)", + "Read(**/.env.*)", + "Read(**/*.env)", + "Read(**/*.pem)", + "Read(**/*.key)", + "Read(**/*.p12)", + "Read(**/*.pfx)", + "Read(**/*.keystore)", + "Read(**/id_rsa*)", + "Read(**/.ssh/**)", + "Read(**/.aws/**)", + "Read(**/.npmrc)", + "Read(**/.netrc)", + "Read(**/.git-credentials)", + "Read(**/.claude/.credentials.json)", + "Read(**/secrets.fish)", + "Read(**/*.token)", + "Read(**/.envrc)" + ], + "defaultMode": "auto" + }, + "model": "opus[1m]", + "enableWorkflows": false, + "skipWorkflowUsageWarning": true, + "enabledPlugins": { + "typescript-lsp@claude-plugins-official": true, + "frontend-design@claude-plugins-official": true + }, + "statusLine": { + "type": "command", + "command": "/usr/bin/env python3 ~/.claude/statusline.py", + "padding": 0, + "hideVimModeIndicator": true + }, + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "/usr/bin/env python3 ~/.claude/hooks/format.py", + "timeout": 20, + "statusMessage": "format" + } + ] + } + ] + }, + "worktree": { + "baseRef": "fresh" + }, + "effortLevel": "xhigh", + "advisorModel": "fable", + "tui": "fullscreen", + "theme": "dark", + "editorMode": "vim", + "verbose": true, + "autoCompactEnabled": true, + "remoteControlAtStartup": false, + "inputNeededNotifEnabled": false, + "agentPushNotifEnabled": false, + "skipAutoPermissionPrompt": true +} diff --git a/common/.config/claude/skills/commit-msg/SKILL.md b/common/.config/claude/skills/commit-msg/SKILL.md new file mode 100644 index 0000000..974869b --- /dev/null +++ b/common/.config/claude/skills/commit-msg/SKILL.md @@ -0,0 +1,42 @@ +--- +description: Draft a commit message for the staged changes, matching this repo's commit style, then print it in the reply, save it to the repo's CLAUDE_COMMIT_MSG file, and copy it to the clipboard. Never stages or commits. Use once you've staged what you want to commit. +argument-hint: [optional focus/scope hint] +disable-model-invocation: true +allowed-tools: Bash(git diff:*), Bash(git log:*), Bash(git status:*), Bash(git rev-parse:*), Bash(wl-copy:*), Read, Glob, Write +--- + +# Draft a commit message for the staged changes + +Context for the current commit — what's staged, plus this repo's recent style: + +!`echo "=== Staged (git diff --cached --stat) ==="; git --no-pager diff --cached --stat 2>/dev/null; echo; echo "=== Working tree (git status --short) ==="; git --no-pager status --short 2>/dev/null; echo; echo "=== Recent subjects — match this convention ==="; git --no-pager log -20 --pretty=format:'%s' 2>/dev/null` + +## What to do + +1. **See exactly what's being committed.** Read the full staged diff with + `git --no-pager diff --cached`. If nothing is staged (the stat above is empty), tell me and + **stop** — I stage changes myself; never run `git add`. +2. **Learn the repo's commit style** from the recent subjects above — prefix convention (this repo + uses `[Scope] summary`), imperative vs. past tense, subject length, whether bodies are used. + Match what's already there; don't impose Conventional Commits / gitmoji if the repo doesn't use + them. +3. **Write the message from the *actual* staged diff** (not a guess): + - A concise subject in the repo's style (~50 chars, imperative). Fold in my hint if given: + **$ARGUMENTS**. + - Add a short body (wrapped ~72 cols) only when the change needs the *why*; otherwise + subject-only. Don't invent motivation that isn't evident from the diff. +4. **Print the message in your reply first** — as a single fenced ` ```text ` block, copy-paste + ready. This in-reply copy is the durable one: it lives in the transcript, so it survives even if + the clipboard gets overwritten later (by me copying something else, or another Claude session + running `wl-copy`). Always print it; don't rely on the clipboard alone. +5. **Save it to a file in the repo** so I can recover it any time: resolve the path with + `git rev-parse --git-path CLAUDE_COMMIT_MSG` (per-worktree correct), then use the Write tool to + write the raw message there — **not** `COMMIT_EDITMSG`, which git overwrites on every + `git commit` before the editor opens. My `prepare-commit-msg` hook prefills the commit editor + from `CLAUDE_COMMIT_MSG` (one-shot) on the next `git commit` / lazygit `C`. The file lives + inside the git dir so it never shows up in `git status`. +6. **Also copy it to the clipboard** as a convenience: `wl-copy < `. Then + tell me all three places it is: **printed above**, **saved to `CLAUDE_COMMIT_MSG`** (hook + prefills the next commit), and **on the clipboard** — so I never lose it to a stray copy. +7. **Never** run `git add`, `git commit`, or `git push`, and never offer to — I stage and commit + manually. Draft only. diff --git a/common/.config/claude/skills/pr-description/SKILL.md b/common/.config/claude/skills/pr-description/SKILL.md new file mode 100644 index 0000000..fab23d8 --- /dev/null +++ b/common/.config/claude/skills/pr-description/SKILL.md @@ -0,0 +1,36 @@ +--- +description: Draft a ready-to-paste PR description by filling this repo's GitHub PR template from the branch diff and commits, then copy it to the clipboard. Use after finishing a feature. +argument-hint: [base-branch] +disable-model-invocation: true +allowed-tools: Bash(git diff:*), Bash(git log:*), Bash(git merge-base:*), Bash(git symbolic-ref:*), Bash(git rev-parse:*), Bash(git show-ref:*), Bash(ls:*), Bash(cat:*), Bash(wl-copy:*), Bash(mktemp:*), Bash(rm:*), Read, Glob +--- + +# Draft this branch's PR description + +This repo's PR template (first match), plus the branch's changes: + +!`for f in .github/pull_request_template.md .github/PULL_REQUEST_TEMPLATE.md docs/pull_request_template.md docs/PULL_REQUEST_TEMPLATE.md PULL_REQUEST_TEMPLATE.md pull_request_template.md; do if [ -f "$f" ]; then echo "=== PR template: $f ==="; cat "$f"; found=1; break; fi; done; if [ -z "$found" ]; then if [ -d .github/PULL_REQUEST_TEMPLATE ]; then echo "=== template dir .github/PULL_REQUEST_TEMPLATE/ (pick one) ==="; ls .github/PULL_REQUEST_TEMPLATE/; else echo "(no PR template found — use the fallback structure)"; fi; fi` + +!`b=$(git symbolic-ref --short -q refs/remotes/origin/HEAD 2>/dev/null | sed 's@^origin/@@'); [ -z "$b" ] && { git show-ref -q --verify refs/heads/main && b=main || b=master; }; m=$(git merge-base "$b" HEAD 2>/dev/null); echo "Branch: $(git rev-parse --abbrev-ref HEAD 2>/dev/null) Base: $b Range: ${m:-?}..HEAD"; echo; echo "Commits:"; git --no-pager log --oneline "${m}..HEAD" 2>/dev/null; echo; echo "Files changed:"; git --no-pager diff --stat "${m}...HEAD" 2>/dev/null` + +## What to do + +1. **Resolve the base branch** (use the invocation argument if given — **$ARGUMENTS** — else the + detected base) and read the full diff + commit messages for `merge-base..HEAD`. +2. **Fill the PR template shown above** from the *actual* changes: + - Write the Summary / motivation and any "Implementation Notes" from the diff and commits. + - Fill the **Type of Change** field or boxes with the kinds that genuinely match (feature / + fix / refactor / perf / tests / docs / chore). + - Tick `Self-reviewed` and `LLM-assisted` if those boxes exist. + - **Leave verification boxes unchecked** — e.g. "tests passed", "`validate:full` passes", + "pre-commit hooks passed". I confirm those myself; never tick them for me. + - Fill **Related Issues** from issue numbers in the branch name or commit trailers; delete any + placeholder lines you can't fill (never leave a dangling `Closes #`), and drop the whole + section if there are no issues to reference. + - If a multi-template dir was listed (no single file), pick the most fitting one and say which. + - **If no template exists**, use this fallback: `## Summary` / `## Changes` / `## Testing` / + `## Notes`. +3. **Output** the filled description as a single fenced ```markdown code block, copy-paste ready. +4. **Copy it to the clipboard**: write the same markdown to a temp file (`mktemp`) and run + `wl-copy < "$tmpfile"`, then remove the temp file. Tell me it's on the clipboard. +5. **Do not** create, push, or edit the PR (`gh pr ...`) — I add the text to the PR myself. diff --git a/common/.config/claude/skills/pr-loop/SKILL.md b/common/.config/claude/skills/pr-loop/SKILL.md new file mode 100644 index 0000000..57a22a1 --- /dev/null +++ b/common/.config/claude/skills/pr-loop/SKILL.md @@ -0,0 +1,275 @@ +--- +description: Babysit the current branch's PR — address review comments that don't need my input, re-check every ~10 min, and stop when there are no open comments left or the automated reviewers (Codex, Claude) hit their limit. Prep-only — stages fixes and drafts the commit message; never commits, pushes, or merges. +argument-hint: [pr number or url — optional; defaults to the current branch's PR] +disable-model-invocation: true +allowed-tools: Bash(gh:*), Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(git rev-parse:*), Bash(git fetch:*), Bash(git add:*), Bash(wl-copy:*), Read, Edit, Write, Grep, Glob, ScheduleWakeup +--- + +# Watch this PR and resolve review comments (prep-only) + +A self-paced loop over one PR's review feedback: fix the comments that don't need my judgement, +stage them, draft a commit message, then wait ~10 min and re-check — until there are no unresolved +comments left or the automated reviewers (Codex/Claude) hit a usage limit. + +**The contract — read this first:** +- **I never commit, push, or merge.** Each cycle you *prepare*: edit + `git add` + draft a commit + message + reply on the threads you handled. Then you **ping me to commit & push**, and the loop + resumes after I do. This honours the global no-commit/no-push rule; nothing in `settings.json` + changes. +- **React, don't reply — then resolve.** A handled thread gets a 👍 **reaction** on the reviewer's + comment while unpushed — never an "Addressed — …" reply, those are just noise. Once I've pushed + and its fix is live, you **resolve the thread on GitHub** (or minimize the bot comment as + RESOLVED) rather than leaving it open — see step 6. Never resolve unpushed work or a needs-input + thread. The **only** comments you ever post are the one-line decision questions for needs-input + items (step 7) and the one-line reason on a dismissal. +- **The marker vocabulary — exactly these three, never others.** Every item you triage gets exactly + one reaction, so I can read the PR's state at a glance without opening threads: + - 👍 `+1` — **addressed**: fix staged; the thread gets resolved once I push. + - 👀 `eyes` — **parked**: needs a decision from me, paired with the one-line question (step 7). + - 👎 `-1` — **dismissed**: a *bot* finding that's wrong, paired with a one-line why, then + resolved/minimized straight away (step 8). + + **Never 👎 a human's comment.** If I'm the one who's wrong, write the reasoning as a reply and + park it as 👀 — I decide, not you. And don't invent other reactions (🎉/❤️/🚀/😄/😕): an + unexplained emoji is worse than no emoji. +- The 10-minute cadence uses `ScheduleWakeup`, so the loop only advances **while this Claude + session stays open**. **To stop it: press `Esc` while I'm idle between cycles** (that clears the + queued wake-up); closing the session also stops it. A plain message does **not** cancel a pending + wake-up — use `Esc`. +- **Every token counts.** Each wake-up re-reads the whole conversation with a cold prompt cache + (the 10-min cadence outlives the 5-min cache TTL), so cost compounds with every cycle and every + extra line in context. Therefore: fetch **filtered** data only (use the `--jq` filters below — + never let raw unfiltered JSON into the conversation), summarize deltas rather than restating + unchanged state, and keep idle cycles to **one cheap API call and ≤2 lines of output**. + +Current state (auth + the current branch's PR): + +!`echo "=== gh auth ==="; gh auth status 2>&1 | head -3; echo; echo "=== PR (current branch) ==="; gh pr view --json number,url,state,headRefName,baseRefName,headRefOid 2>/dev/null || echo "(no open PR for current branch — or gh not authed)"; echo; echo "=== repo ==="; gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null` + +## Each cycle (one invocation / wake-up) + +1. **Preflight.** If `gh auth status` failed above → **stop** and tell me to run `! gh auth login`, + then re-run `/pr-loop`. Do nothing else. Capture `owner`/`repo` from `nameWithOwner`. + +2. **Load state & resolve the PR.** Read `/pr-loop-state.json` (find `` with + `git rev-parse --absolute-git-dir`). It's inside `.git/`, so it never shows in `git status` and + survives wake-ups. Shape: + ```json + { "number": 0, "lastHeadOid": "", "lastSeenUpdatedAt": "", "handledThreadIds": [], + "handledCommentIds": [], "needsInputThreadIds": [], "needsInputCommentIds": [], + "dismissedIds": [], "awaitingPush": false, "idleCount": 0, "cycleCount": 0 } + ``` + Resolve the target PR **in this order**: my argument if given (**$ARGUMENTS**) → the `number` + in the state file (wake-ups re-invoke `/pr-loop` *without* arguments — the state file is the + durable copy) → the current branch's PR from the context block. If none → **stop** and say so. + If the state file is missing or for a different `number`, start fresh. Increment `cycleCount`. + + **Then print one status line before any network call** — so I always see the loop is alive: + > 🔄 pr-loop cycle — PR #: checking… + +3. **Cheap activity check — one API call, before any heavy fetch.** + `gh pr view NUM --json headRefOid,updatedAt` + - If `updatedAt == lastSeenUpdatedAt` **and** `headRefOid == lastHeadOid` → nothing happened at + all (no comment, review, or push). This is an **idle cycle**: `idleCount += 1`; if a safety + cap (step 5) is now exceeded, STOP per step 5. Otherwise save state and jump straight to + step 10's reschedule. Total output ≤2 lines — if `awaitingPush`, one line is a brief + commit-&-push reminder (remind at most twice across idle cycles, then just the status line). + - Otherwise: remember the fetched `headRefOid` for step 6 and continue. (Don't update + `lastSeenUpdatedAt` yet — step 10 refreshes it *after* you've posted replies, so your own + replies don't defeat the next cycle's cheap check.) + +4. **Fetch review state — filtered at the source, both calls in parallel** (one message, two + tool calls). Never fetch unfiltered `reviews,comments`. + + Unresolved review threads (resolved/unresolved is GraphQL-only): + ```bash + gh api graphql --paginate -F owner=OWNER -F repo=REPO -F number=NUM -f query=' + query($owner:String!,$repo:String!,$number:Int!,$cursor:String){ + repository(owner:$owner,name:$repo){ pullRequest(number:$number){ + reviewThreads(first:100,after:$cursor){ pageInfo{hasNextPage endCursor} + nodes{ id isResolved isOutdated + comments(first:100){ nodes{ id databaseId author{login} path line body } } } } } } }' \ + --jq '.data.repository.pullRequest.reviewThreads.nodes[] + | select(.isResolved | not) + | {id, isOutdated, + comments: [.comments.nodes[] | {id, databaseId, author: .author.login, path, line, body}]}' + ``` + Mind the two id kinds: the thread's own `id` (`PRRT_…`) is what you resolve; a + `comments[].id` (`PRRC_…`) is what you react to. + + Automated-reviewer activity + `@claude` requests from the PR conversation — latest review per + bot (for the limit check in step 5) and only the relevant comments, not the whole history: + ```bash + gh pr view NUM --json reviews,comments --jq '{ + latestBotReviews: ([.reviews[] | select(.author.login | test("codex|claude"; "i"))] + | group_by(.author.login) | map(max_by(.submittedAt)) + | map({author: .author.login, state, submittedAt, body})), + relevantComments: ([.comments[] | select((.isMinimized | not) + and ((.body | test("@claude")) or (.author.login | test("codex|claude"; "i")))) + | {id, author: .author.login, body}] | .[-10:]) + }' + ``` + Act on feedback from **any** reviewer — especially the automated ones, **Codex and Claude** + (`author.login` matching `codex`/`claude`, or a Bot account). Treat any comment — inline review + **or** PR conversation — whose body **mentions `@claude`** as an explicit request to act on, + same as a review comment. + + Some bots post **findings as a plain PR-conversation comment** rather than inline review threads + (e.g. Claude when its tooling can't post inline). Triage those findings like thread findings; + since they have no thread, you "resolve" them by **minimizing** the comment once fixed (step 8). + `relevantComments[].id` is the comment's GraphQL node id (`IC_…`) used for that, and the + `isMinimized` filter above already drops ones you've resolved. A bot's pure **summary / status / + usage-limit** comment carries no finding — triage/minimize only comments with concrete asks. + +5. **Check stop conditions — before doing any work:** + - **Nothing left to address** → no unresolved review threads, no un-minimized bot **findings** + comments, and no open `@claude` requests. Anything in `dismissedIds` doesn't count as + outstanding (if a resolve/minimize failed, the 👎 + reason still stands). The PR is clean: + report done, save state, and **do not** schedule another wake-up. STOP. + - **The automated reviewers are spent** → an automated reviewer's most recent review/comment + body matches a limit signal (case-insensitive: `rate/usage/quota/credit … limit`, + `limit reached`, `exceeded … quota`, `out of … credits`). Automated reviewers = authors whose + `login` matches `codex` or `claude` (or a `[bot]` that leaves review comments; confirm with + `gh api users/ --jq .type` → `Bot`). STOP and report which reviewer hit the limit + **only** when no unresolved threads (incl. `@claude` requests) remain that you can still act + on. If one reviewer is limited but other threads still need work, handle those first, then + re-check. + - **Safety caps** (these apply on idle cycles too) → if `cycleCount > 20`, or `idleCount > 4`, + or `gh api rate_limit --jq .resources.core.remaining` is `< 100` → STOP and hand back to me + with a summary. + + **When any stop fires:** save state, do **not** call `ScheduleWakeup`, and end with a clear + final line so I know the loop is over and won't run again — e.g.: + > ✅ **Finished — no more active issues.** I won't re-check again. + + Adapt it to the reason: `✅ Finished — hit its usage limit; nothing left to address, + I won't re-check again.` or `⏹ Stopped — hit safety cap (); re-run \`/pr-loop\` to resume.` + +6. **Did I push since last cycle?** Compare the `headRefOid` from step 3 to `lastHeadOid` (no + extra API call). + - If **unchanged and `awaitingPush` is true** → I haven't pushed yet. Don't re-fix anything for + threads already handled: `idleCount += 1`, then continue with step 7 **only for genuinely new + threads/comments** (something new must exist, or step 3 would have short-circuited). + - If **changed** → I pushed. Set `awaitingPush=false`, `idleCount=0`, update `lastHeadOid`. Then, + **before handling anything new, resolve on GitHub every already-handled item whose fix is now + live** — don't leave them lingering as open, 👍-only threads. For each id in `handledThreadIds` + that is still unresolved and not re-flagged, run `resolveReviewThread`; for each id in + `handledCommentIds` not re-flagged, `minimizeComment` as RESOLVED (mutations in step 8). + Resolved threads drop out of the step-4 fetch, so each fix is resolved exactly once. Then + continue to any genuinely new threads. + +7. **Triage** each unresolved review thread, every bot **findings** comment on the PR conversation, + and every `@claude` request **not already handled**: + - **Auto-handle (no input needed):** typos, lint/format, naming, missing null/error checks, + applying the reviewer's concrete suggested diff, docs/comments, and localized bugs with one + correct fix — **including fixes that touch a shared type/contract or span multiple layers, + when the repo's existing conventions determine the shape.** Blast radius (multi-file, + contract-touching, "I'd have to pick among a few representations") is **not** a reason to + defer: pick the minimal idiomatic shape that matches existing patterns, implement it, and let + me veto. A reviewer finding that names the concrete fix is almost always auto-handleable. + - **Leave for me (needs input) — 👀:** defer **only** for genuine ambiguity — two or more + *materially different* correct behaviours, a real security/performance trade-off, or missing + product/domain knowledge that existing code can't settle. Never guess these. Post the + `path:line` + a **one-line decision I can answer in a word**, and record the id in + `needsInputThreadIds` (or `needsInputCommentIds` for a plain conversation comment) so step 3 + re-surfaces it every idle cycle instead of letting it fall silent. + - **Not an issue (dismiss) — 👎:** a **bot** finding that is simply wrong — it misread the code, + the behaviour is intentional and the surrounding code proves it, or the concern is already + handled elsewhere. Don't "fix" it to make it go away, and don't park it as needs-input either: + that leaves a false positive propping the loop open until a safety cap fires. Reply with the + one-line reason (facts, not opinion: the line/behaviour that disproves it), 👎 it, then resolve + the thread / minimize the comment **immediately** — no push required, since there's no fix to + land. Record the id in `dismissedIds` so a re-fetch can't re-litigate it. + **Only bots get dismissed.** A *human* comment you think is wrong is a needs-input item: state + your reasoning in a reply, mark it 👀, and let me settle it. If you'd be dismissing more than + one or two findings in a cycle, you're probably the one who's wrong — park them for me instead. + + Apply the same split to Codex's and Claude's review suggestions and to every `@claude` request: + a concrete ask is auto-handled; a genuine judgement call is left for me. Skip anything whose id + is already in `handledThreadIds` / `handledCommentIds` / `dismissedIds`. When I answer a needs-input item (or you + push a fix for it), drop its id from the needs-input arrays and handle/resolve it normally. + +8. **Prepare the auto-handled set (no commit, no push):** + - Apply the edits (the PostToolUse format hook auto-formats). `git add` the changed files. + - **Mark every triaged item with its reaction — never an "Addressed — …" reply.** The reaction + goes on the reviewer's comment (for a thread, its *first* comment — the finding itself): + 👍 what you fixed this cycle, 👀 what you parked for me, 👎 a dismissed bot finding. + + One mutation covers both surfaces. Pass the **comment** node id — `comments[].id` (`PRRC_…`) + from the thread query, or `relevantComments[].id` (`IC_…`) for a conversation comment; *not* + the thread's own `PRRT_…` id. `content` is `THUMBS_UP` / `EYES` / `THUMBS_DOWN`: + ```bash + gh api graphql -f query='mutation($id:ID!,$c:ReactionContent!){ + addReaction(input:{subjectId:$id,content:$c}){ reaction{ content } } }' \ + -F id=COMMENT_NODE_ID -F c=THUMBS_UP + ``` + Re-adding the same reaction is a harmless no-op, so a repeat cycle can't double-post. When an + item **changes class** — I answer a 👀, or a reviewer re-flags something you'd 👍'd — clear the + stale marker first, same call shape, so nothing ever carries two contradictory markers: + ```bash + gh api graphql -f query='mutation($id:ID!,$c:ReactionContent!){ + removeReaction(input:{subjectId:$id,content:$c}){ reaction{ content } } }' \ + -F id=COMMENT_NODE_ID -F c=EYES + ``` + - Add handled review-thread ids to `handledThreadIds` and handled conversation/`@claude` + comment ids to `handledCommentIds`; set `awaitingPush=true`. + - **Resolve** a thread — `gh api graphql -f query='mutation($id:ID!){ + resolveReviewThread(input:{threadId:$id}){ thread{ id isResolved } } }' -F id=THREAD_ID` — + **only** once its fix is live (a cycle where `headRefOid` advanced, per step 6) and the + reviewer hasn't re-flagged it. Never resolve unpushed work or a "needs input" thread. This + resolves the review *conversation*, not any linked GitHub Issue. + **The one exception is a 👎 dismissal:** there's no fix to land, so resolve/minimize it in the + same cycle you post the reason — otherwise the false positive keeps the loop alive forever. + - **Resolve a plain PR-conversation findings comment** (a bot finding with no inline thread — + e.g. Claude's) by **minimizing it as resolved** — the issue-comment equivalent, under the same + rules (only once its fix is live and not re-flagged): + ```bash + gh api graphql -f query='mutation($id:ID!){ minimizeComment(input:{subjectId:$id, + classifier:RESOLVED}){ minimizedComment{ isMinimized } } }' -F id=COMMENT_NODE_ID + ``` + `COMMENT_NODE_ID` is the `relevantComments[].id` (`IC_…`). Never minimize unpushed work, a + "needs input" comment, or a summary/status/usage-limit comment. + +9. **Draft the commit message — exactly like `/commit-msg`:** from the staged diff and the repo's + recent `git log` style (this repo uses `[Scope] summary`), write a message that matches. Then: + - **(a) Print it** in your reply as a fenced ` ```text ` block — the durable copy. + - **(b) Write it** with the Write tool to the path from `git rev-parse --git-path CLAUDE_COMMIT_MSG` + (**not** `COMMIT_EDITMSG` — git overwrites that on `git commit`; the `prepare-commit-msg` hook + prefills the editor from `CLAUDE_COMMIT_MSG`). + - **(c) Copy it:** `wl-copy < `. + - Tell me all three locations. **Never** run `git commit`. + (Skip this step when nothing new was staged this cycle.) + +10. **Ping + schedule.** Give me a tight summary — deltas only, don't restate unchanged threads: + - 👍 fixed & staged this cycle: threads (with `path:line`); + - 👀 left for you: threads + one-line reason each; + - 👎 dismissed as not-an-issue: thread + the one-line why (call these out explicitly — a + dismissal is me trusting your judgement, so I should see every one); + - resolved on GitHub this cycle (if any); + - the commit message (printed above), then "commit & push when ready". + + Refresh `lastSeenUpdatedAt` with one `gh pr view NUM --json updatedAt` call **after** any + needs-input questions are posted (reactions don't bump `updatedAt`, comments do — so your own + activity doesn't defeat the next cheap check), then save state + to `/pr-loop-state.json`. Then schedule: + - **normal cycle:** `ScheduleWakeup(delaySeconds=600, prompt="/pr-loop", reason="recheck PR + # review comments")`; + - **idle cycle** (step 3 short-circuited): back off — `delaySeconds=1200`, I'm clearly away; + re-running `/pr-loop` checks immediately. + + End the turn with a sign-off that states when you'll run again and how to stop, e.g.: + > ⏳ **Next check in ~10 min** (~20 when idle). To stop, press `Esc` while I'm idle between + > cycles (or close the session). + + (When a stop condition in step 5 fired, skip both the wake-up and this sign-off — use the + **Finished** line from step 5 instead.) + +## Guardrails + +- **Never** `git commit`, `git push`, `gh pr merge`, `gh pr create`, `gh pr close`, or `gh pr edit` + — I do all of those. You stage, react, resolve, and draft; nothing more. The only comment you may + post is a needs-input decision question. +- Act only on the target PR. Don't touch unrelated files or other PRs. +- **Keep context lean:** no raw JSON dumps in replies, no re-listing threads that haven't changed, + idle cycles ≤2 lines. Every line you emit is re-read (uncached) on every later cycle. +- If anything is unclear or risky, leave it for me rather than guessing. diff --git a/common/.config/claude/skills/review-branch/SKILL.md b/common/.config/claude/skills/review-branch/SKILL.md new file mode 100644 index 0000000..d16fcdd --- /dev/null +++ b/common/.config/claude/skills/review-branch/SKILL.md @@ -0,0 +1,69 @@ +--- +description: Review all changes on the current branch (diff vs its base) for bugs, security issues, optimizations, and readability/scannability, then apply the readability + safe improvements after you approve. Use right before opening a PR. +argument-hint: [base-branch] +disable-model-invocation: true +allowed-tools: Bash(git diff:*), Bash(git log:*), Bash(git status:*), Bash(git merge-base:*), Bash(git symbolic-ref:*), Bash(git rev-parse:*), Bash(git show-ref:*), Bash(git branch:*), Read, Grep, Glob +--- + +# Review this branch + +Context auto-collected for the current branch vs. its base: + +!`b=$(git symbolic-ref --short -q refs/remotes/origin/HEAD 2>/dev/null | sed 's@^origin/@@'); [ -z "$b" ] && { git show-ref -q --verify refs/heads/main && b=main || b=master; }; m=$(git merge-base "$b" HEAD 2>/dev/null); echo "Current branch: $(git rev-parse --abbrev-ref HEAD 2>/dev/null)"; echo "Base branch: $b"; echo "Diff range: ${m:-?}..HEAD"; echo; echo "Commits:"; git --no-pager log --oneline "${m}..HEAD" 2>/dev/null; echo; echo "Files changed:"; git --no-pager diff --stat "${m}...HEAD" 2>/dev/null` + +## What to do + +1. **Resolve the base branch.** If a base branch was passed when invoking (it appears here: + **$ARGUMENTS** — empty if none), use that. Otherwise use the base detected above. The changes + to review are the diff `git merge-base HEAD`..`HEAD`. +2. **Gather context.** Read the full diff for that range (`git diff ...HEAD`) and + review **only the lines this branch changed** — not pre-existing code. Read surrounding code + with the Read tool when you need it to judge a finding. Also read the repo's `CLAUDE.md` (root, + and any in the directories the branch touched) and treat its guidance as a review lens. +3. **Find issues** across four lenses: + - 🐞 **Correctness / bugs** — logic errors, unhandled edge cases, swallowed/ignored errors, + race conditions, wrong/loose types, off-by-ones, and anything that violates the repo's + `CLAUDE.md`. + - 🔒 **Security** — injection (SQL / command / path traversal), missing input validation or + output encoding, authn/authz gaps, secrets or credentials committed / logged / echoed, + unsafe deserialization, SSRF, weak crypto or randomness, unsafe defaults, and sensitive data + leaked in logs or error messages. + - ⚡ **Optimizations** — redundant work, needless allocations/copies, N+1 patterns, a simpler + or standard-library equivalent. + - 📖 **Readability & scannability** — naming, structure, dead code, stale/misleading comments, + formatting, over-long functions, unclear control flow. +4. **Verify the bugs and security findings before reporting.** Double-check each 🐞 and 🔒 + finding against the real code and keep only the ones you're confident are real and exploitable + / will bite in practice. **Do not flag:** + - issues on lines the branch didn't change (pre-existing); + - theoretical vulnerabilities with no reachable exploit path in the changed code, or anything a + SAST / dependency scanner would own; + - anything a linter / type-checker / compiler / CI catches (imports, type errors, formatting) — + assume those run separately; + - pedantic nitpicks a senior engineer wouldn't raise, or "needs more tests/docs" unless the + repo's `CLAUDE.md` requires it; + - changes that are clearly intentional and part of the feature. + + (This filter is for 🐞 bugs and 🔒 security. ⚡ optimizations and 📖 readability are the + deliberate polish pass — minor suggestions there are welcome, since the goal is PR-readiness.) +5. **Report**, grouped by the four lenses, most-severe-first, each finding one or two lines with + a `path:line` reference so it's scannable. If a category is clean, say so in one line. No walls + of text. +6. **Then stop and ask** whether to apply the fixes. On approval, apply **only** the readability + improvements and behavior-preserving optimizations. Anything that changes behavior, public API, + or semantics: list it separately and let me decide — do **not** apply it silently. Treat 🔒 + security fixes the same way: propose them, but never apply them silently. After editing, re-run + the repo's formatter/linter if it has one. +7. **Point me at the relevant built-in follow-ups.** After reporting, list the Claude Code + commands worth running next as separate, deeper passes — tailored to what this review + surfaced, not a blanket dump. Only include the ones that actually fit: + - `/security-review` — a deeper, whole-branch security pass; suggest whenever the 🔒 lens + flagged something or the diff touches auth, crypto, input handling, secrets, or network I/O. + - `/code-review` (add `--fix` to apply, `--comment` for inline PR comments) — a second review + of the working-tree diff at a chosen effort level. + - `/simplify` — apply reuse / simplification / efficiency cleanups beyond the safe ones applied + here. + - `/verify` — run the app to confirm the change behaves, if this branch changed behavior. + - `/pr-description` — draft the PR text from this branch once the review is clean. + - `/review` — review the PR on GitHub after it's open. +8. **Never** run `git commit` or `git push` — I do that manually. diff --git a/common/.config/claude/statusline.py b/common/.config/claude/statusline.py new file mode 100755 index 0000000..8aa5749 --- /dev/null +++ b/common/.config/claude/statusline.py @@ -0,0 +1,517 @@ +#!/usr/bin/env python3 +"""Claude Code status line. + +One line: vim mode | dir (repo-relative, hidden at the root) | +git(repo/branch +dirty +ahead/behind) | model (+effort) | +context% (+compact warn) | 5h usage (+reset eta) | cost (+burn rate) | disk. If the +rendered line would overflow the terminal, trailing (lowest-priority) segments are +dropped until it fits. +(Dormant, re-addable in main(): ram, cpu%, temp -- retired 2026-07-26 because the +status line only refreshes on message events, so system metrics sat visibly stale; +tmux's status bar owns those now -- plus velocity, cache hit %, api ratio, version, +style.) + +Reads the status JSON from stdin (Claude Code statusLine command). Every segment +is wrapped defensively so the status line can never crash the UI -- on any error +a segment is simply omitted. +""" +import json +import os +import re +import shutil +import subprocess +import sys +import time +import unicodedata + +# --- ANSI helpers ----------------------------------------------------------- +RESET = "\033[0m" +DIM = "" # disabled: "\033[2m" blended the gray detail text into dark backgrounds +BOLD = "\033[1m" +C = { + "cyan": "\033[36m", + "green": "\033[32m", + "yellow": "\033[33m", + "red": "\033[31m", + "magenta": "\033[35m", + "blue": "\033[34m", + "gray": "\033[37m", # light gray (was "\033[90m" bright-black, too dark to read) +} + + +def color(text, name="", dim=False, bold=False): + pre = "" + if dim: + pre += DIM + if bold: + pre += BOLD + pre += C.get(name, "") + return f"{pre}{text}{RESET}" if pre else str(text) + + +def bucket(value, lo, hi, invert=False): + """green/yellow/red by threshold. invert=True -> high is good.""" + if invert: + return "green" if value >= hi else ("yellow" if value >= lo else "red") + return "green" if value < lo else ("yellow" if value < hi else "red") + + +DIV = color(" │ ", "gray", dim=True) + + +def join_line(segs): + return DIV.join(s for s in segs if s) + + +_ANSI_RE = re.compile(r"\x1b\[[0-9;]*m") + + +def vlen(text): + """Visible width of a rendered string: ANSI stripped, emoji counted as 2.""" + text = _ANSI_RE.sub("", text) + w = 0 + for ch in text: + if ch in ("\u200d", "\ufe0f", "\ufe0e") or unicodedata.combining(ch): + continue # ZWJ / variation selectors / combining marks: zero width + o = ord(ch) + if o >= 0x1F000 or 0x2600 <= o <= 0x27BF or unicodedata.east_asian_width(ch) in ("W", "F"): + w += 2 + else: + w += 1 + return w + + +def term_cols(): + """Terminal width. Claude Code exports COLUMNS; 0 if unknown -> stay one line.""" + try: + return shutil.get_terminal_size(fallback=(0, 0)).columns + except Exception: + return 0 + + +# --- shared transcript usage ------------------------------------------------ +def last_usage(data): + """Most recent `message.usage` block from the transcript, or {}.""" + try: + path = data.get("transcript_path") + if not path or not os.path.exists(path): + return {} + usage = {} + with open(path, "r", errors="ignore") as fh: + for line in fh: + line = line.strip() + if not line or '"usage"' not in line: + continue + try: + obj = json.loads(line) + except Exception: + continue + msg = obj.get("message") + if isinstance(msg, dict) and isinstance(msg.get("usage"), dict): + usage = msg["usage"] + return usage + except Exception: + return {} + + +# --- row 1: work ------------------------------------------------------------ +def vim_segment(data): + """Vim editor mode as a compact [I]/[N]/[V] (only present with editorMode: vim). + + Pair with `"hideVimModeIndicator": true` in the statusLine settings block so + Claude's own built-in indicator doesn't duplicate this one. + """ + mode = (data.get("vim") or {}).get("mode") + if not mode: + return "" + m = mode.upper() + if m.startswith("INSERT"): + return color("[I]", "green", bold=True) + if m.startswith("VISUAL"): + return color("[V]", "magenta", bold=True) + if m.startswith("NORMAL"): + return color("[N]", "blue", bold=True) + return color(f"[{m[:1]}]", "gray") + + +def abbrev(path): + """fish-style prompt_pwd: shrink every component but the last to one char, + keeping a leading dot (`.config` -> `.c`).""" + parts = path.split(os.sep) + out = [] + for i, p in enumerate(parts): + if i == len(parts) - 1 or p in ("", "~"): + out.append(p) + elif p.startswith("."): + out.append(p[:2]) + else: + out.append(p[:1]) + return os.sep.join(out) + + +def dir_segment(cwd, top=None): + """Path *inside* the repo -- git_segment already names the repo itself, so + repeating the full ~-path here was pure duplication. Empty at the repo root; + a fish-shortened ~-path when we're not in a repo at all.""" + try: + rel = os.path.relpath(cwd, top) if top else None + if rel and not rel.startswith(os.pardir): # `..` -> cwd is outside `top` (symlinked in); use the ~-path + if rel == os.curdir: + return "" + shown = rel if len(rel) <= 28 else abbrev(rel) + else: + home = os.path.expanduser("~") + shown = cwd + if cwd == home: + shown = "~" + elif cwd.startswith(home + os.sep): + shown = "~" + cwd[len(home):] + shown = abbrev(shown) + return "📁 " + color(shown, "cyan") + except Exception: + return "" + + +def _git(cwd, *args): + return subprocess.run( + ["git", "-C", cwd, *args], capture_output=True, text=True, timeout=1 + ) + + +def git_top(cwd): + """Absolute path of the enclosing work tree, or None.""" + try: + r = _git(cwd, "rev-parse", "--show-toplevel") + return r.stdout.strip() or None if r.returncode == 0 else None + except Exception: + return None + + +def git_segment(cwd, top): + try: + if not top: + return "" + repo = os.path.basename(top) or "repo" + branch = _git(cwd, "rev-parse", "--abbrev-ref", "HEAD").stdout.strip() or "?" + dirty = bool(_git(cwd, "status", "--porcelain").stdout.strip()) + + # ahead/behind vs upstream (omitted when no upstream is configured) + ab = "" + rl = _git(cwd, "rev-list", "--left-right", "--count", "@{upstream}...HEAD") + if rl.returncode == 0 and rl.stdout.strip(): + try: + behind, ahead = (int(x) for x in rl.stdout.split()) + bits = [] + if ahead: + bits.append(color(f"↑{ahead}", "cyan")) + if behind: + bits.append(color(f"↓{behind}", "yellow")) + if bits: + ab = " " + "".join(bits) + except Exception: + ab = "" + + mark = color("*", "red") if dirty else "" + label = color(repo, "magenta", bold=True) + br = color(branch, "yellow" if dirty else "green") + return f"🌿 {label} {br}{mark}{ab}" + except Exception: + return "" + + +def model_segment(data): + name = (data.get("model") or {}).get("display_name") + if not name: + return "" + name = name.replace(" context)", ")") # "Opus 4.8 (1M context)" -> "Opus 4.8 (1M)" + out = "🤖 " + color(name, "blue") + level = (data.get("effort") or {}).get("level") + if level: + out += color(f" · {level}", "gray") + return out + + +def context_segment(data, usage): + try: + if not usage: + return "" + used = ( + usage.get("input_tokens", 0) + + usage.get("cache_creation_input_tokens", 0) + + usage.get("cache_read_input_tokens", 0) + ) + model_id = (data.get("model") or {}).get("id", "") + window = 1_000_000 if "1m" in model_id.lower() else 200_000 + pct = used / window * 100 if window else 0 + col = bucket(pct, 50, 80) + out = "📝 " + color(f"{pct:.0f}%", col) + color(f" ({used / 1000:.0f}k)", "gray", dim=True) + if pct >= 80: + out += color(" ⚠compact", "red", bold=True) + return out + except Exception: + return "" + + +def usage_segment(data): + """5-hour rolling usage window: used % (+ time until it resets).""" + try: + five = (data.get("rate_limits") or {}).get("five_hour") or {} + pct = five.get("used_percentage") + if pct is None: + return "" + out = "📊 " + color(f"{pct:.0f}%", bucket(pct, 50, 80)) + resets = five.get("resets_at") + if resets: + secs = int(resets) - int(time.time()) + if secs > 0: + h, m = divmod(secs // 60, 60) + out += color(f" {h}h{m:02d}m" if h else f" {m}m", "gray") + return out + except Exception: + return "" + + +def cost_segment(data): + try: + cost = data.get("cost") or {} + usd = cost.get("total_cost_usd") + if usd is None: + return "" + out = "💰 " + color(f"${usd:.2f}", "yellow") + dur_ms = cost.get("total_duration_ms") or 0 + if dur_ms > 30_000: # need a meaningful window before extrapolating + per_hr = usd / (dur_ms / 3_600_000) + out += color(f" ${per_hr:.2f}/h", "gray", dim=True) + return out + except Exception: + return "" + + +def velocity_segment(data): + try: + cost = data.get("cost") or {} + added = cost.get("total_lines_added", 0) + removed = cost.get("total_lines_removed", 0) + if not (added or removed): + return "" + out = "✏️ " + color(f"+{added}", "green") + color("/", "gray", dim=True) + color(f"-{removed}", "red") + dur_ms = cost.get("total_duration_ms") or 0 + if dur_ms > 30_000: + per_min = added / (dur_ms / 60_000) + out += color(f" {per_min:.0f}/m", "gray", dim=True) + return out + except Exception: + return "" + + +def cache_segment(usage): + try: + if not usage: + return "" + read = usage.get("cache_read_input_tokens", 0) + total = ( + usage.get("input_tokens", 0) + + usage.get("cache_creation_input_tokens", 0) + + read + ) + if total <= 0: + return "" + pct = read / total * 100 + return "♻️ " + color(f"{pct:.0f}%", bucket(pct, 50, 80, invert=True)) + except Exception: + return "" + + +def api_segment(data): + """Share of wall-clock time spent in API/inference (rough 'busy' ratio).""" + try: + cost = data.get("cost") or {} + wall = cost.get("total_duration_ms") or 0 + api = cost.get("total_api_duration_ms") or 0 + if wall <= 0 or api <= 0: + return "" + pct = min(api / wall * 100, 100) + return "⚙️ " + color(f"{pct:.0f}%", "blue") + except Exception: + return "" + + +# --- row 2: system ---------------------------------------------------------- +def ram_segment(): + try: + info = {} + with open("/proc/meminfo") as fh: + for line in fh: + k, _, v = line.partition(":") + info[k.strip()] = int(v.split()[0]) # kB + total = info.get("MemTotal", 0) + avail = info.get("MemAvailable", info.get("MemFree", 0)) + if total <= 0: + return "" + used = total - avail + pct = used / total * 100 + g = 1024 * 1024 + return ( + "🧠 " + color(f"{pct:.0f}%", bucket(pct, 70, 85)) + + color(f" {used / g:.1f}/{total / g:.0f}G", "gray", dim=True) + ) + except Exception: + return "" + + +def disk_segment(cwd): + try: + st = os.statvfs(cwd) + total = st.f_blocks + if total <= 0: + return "" + used = total - st.f_bfree + pct = used / total * 100 + return "💾 " + color(f"{pct:.0f}%", bucket(pct, 75, 90)) + except Exception: + return "" + + +def _cpu_pct(): + """Live CPU% diffed against a cached /proc/stat snapshot (no sleeping).""" + try: + with open("/proc/stat") as fh: + parts = fh.readline().split() + if not parts or parts[0] != "cpu": + return None + vals = [int(x) for x in parts[1:]] + idle = vals[3] + (vals[4] if len(vals) > 4 else 0) # idle + iowait + total = sum(vals) + + state = os.path.expanduser("~/.claude/cache/statusline.cpu") + prev = None + try: + with open(state) as fh: + pt, pi = fh.read().split() + prev = (int(pt), int(pi)) + except Exception: + prev = None + try: + os.makedirs(os.path.dirname(state), exist_ok=True) + with open(state, "w") as fh: + fh.write(f"{total} {idle}") + except Exception: + pass + + if not prev: + return None + dt = total - prev[0] + di = idle - prev[1] + if dt <= 0 or di < 0: + return None + return max(0.0, min(100.0, (1 - di / dt) * 100)) + except Exception: + return None + + +def cpu_segment(): + try: + load1 = os.getloadavg()[0] + cores = os.cpu_count() or 1 + pct = _cpu_pct() + if pct is not None: + head = color(f"{pct:.0f}%", bucket(pct, 60, 85)) + tail = color(f" {load1:.1f} {cores}c", "gray", dim=True) + else: + head = color(f"{load1:.2f}", bucket(load1 / cores, 0.7, 1.0)) + tail = color(f" {cores}c", "gray", dim=True) + return "🖥️ " + head + tail + except Exception: + return "" + + +def temp_segment(): + """Hottest CPU-ish thermal zone in °C, falling back to the max zone.""" + try: + base = "/sys/class/thermal" + if not os.path.isdir(base): + return "" + prefer = ("x86_pkg_temp", "coretemp", "cpu", "k10temp", "tctl") + chosen = None + fallback = None + for name in os.listdir(base): + if not name.startswith("thermal_zone"): + continue + d = os.path.join(base, name) + try: + with open(os.path.join(d, "temp")) as fh: + milli = int(fh.read().strip()) + except Exception: + continue + ztype = "" + try: + with open(os.path.join(d, "type")) as fh: + ztype = fh.read().strip().lower() + except Exception: + pass + if fallback is None or milli > fallback: + fallback = milli + if any(p in ztype for p in prefer) and (chosen is None or milli > chosen): + chosen = milli + milli = chosen if chosen is not None else fallback + if milli is None: + return "" + c = milli / 1000.0 + return "🌡️ " + color(f"{c:.0f}°C", bucket(c, 60, 80)) + except Exception: + return "" + + +def version_segment(data): + v = data.get("version") + return color(f"v{v}", "gray", dim=True) if v else "" + + +def style_segment(data): + name = (data.get("output_style") or {}).get("name") + return color(f"🎨{name}", "gray", dim=True) if name else "" + + +def main(): + try: + data = json.load(sys.stdin) + except Exception: + data = {} + + cwd = ( + (data.get("workspace") or {}).get("current_dir") + or data.get("cwd") + or os.getcwd() + ) + usage = last_usage(data) + top = git_top(cwd) + + # One row: work (left) then system (right), joined. If it would overflow the + # terminal, trailing (lowest-priority) segments are dropped so it stays one line. + # Dormant helpers kept above for easy re-add: ram_segment, cpu_segment, + # temp_segment (retired -- they only refresh on message events, so they sat + # stale; the tmux status bar owns system metrics now), velocity_segment, + # cache_segment, api_segment, version_segment, style_segment. + work = [s for s in ( + vim_segment(data), + dir_segment(cwd, top), + git_segment(cwd, top), + model_segment(data), + context_segment(data, usage), + usage_segment(data), + cost_segment(data), + ) if s] + system = [s for s in ( + disk_segment(cwd), + ) if s] + + segs = work + system + cols = term_cols() + if cols: # trim from the right until it fits one line (cols=0 -> width unknown, keep all) + while len(segs) > 1 and vlen(join_line(segs)) > cols: + segs.pop() + sys.stdout.write(join_line(segs)) + + +if __name__ == "__main__": + main() diff --git a/.config/fastfetch/config.jsonc b/common/.config/fastfetch/config.jsonc similarity index 100% rename from .config/fastfetch/config.jsonc rename to common/.config/fastfetch/config.jsonc diff --git a/.config/fastfetch/pngs/anon.png b/common/.config/fastfetch/pngs/anon.png similarity index 100% rename from .config/fastfetch/pngs/anon.png rename to common/.config/fastfetch/pngs/anon.png diff --git a/.config/fastfetch/pngs/arch.png b/common/.config/fastfetch/pngs/arch.png similarity index 100% rename from .config/fastfetch/pngs/arch.png rename to common/.config/fastfetch/pngs/arch.png diff --git a/.config/fastfetch/pngs/archlinux.png b/common/.config/fastfetch/pngs/archlinux.png similarity index 100% rename from .config/fastfetch/pngs/archlinux.png rename to common/.config/fastfetch/pngs/archlinux.png diff --git a/.config/fastfetch/pngs/dmz-rabbit.png b/common/.config/fastfetch/pngs/dmz-rabbit.png similarity index 100% rename from .config/fastfetch/pngs/dmz-rabbit.png rename to common/.config/fastfetch/pngs/dmz-rabbit.png diff --git a/common/.config/fastfetch/pngs/dmz-rabbit2.png b/common/.config/fastfetch/pngs/dmz-rabbit2.png new file mode 100644 index 0000000..eca1d86 Binary files /dev/null and b/common/.config/fastfetch/pngs/dmz-rabbit2.png differ diff --git a/.config/fastfetch/pngs/neonom.png b/common/.config/fastfetch/pngs/neonom.png similarity index 100% rename from .config/fastfetch/pngs/neonom.png rename to common/.config/fastfetch/pngs/neonom.png diff --git a/.config/fastfetch/pngs/network.png b/common/.config/fastfetch/pngs/network.png similarity index 100% rename from .config/fastfetch/pngs/network.png rename to common/.config/fastfetch/pngs/network.png diff --git a/common/.config/fastfetch/pngs/network_12.png b/common/.config/fastfetch/pngs/network_12.png new file mode 100644 index 0000000..b261948 Binary files /dev/null and b/common/.config/fastfetch/pngs/network_12.png differ diff --git a/common/.config/fastfetch/pngs/networkandtext.png b/common/.config/fastfetch/pngs/networkandtext.png new file mode 100644 index 0000000..bc24110 Binary files /dev/null and b/common/.config/fastfetch/pngs/networkandtext.png differ diff --git a/.config/fastfetch/pngs/openhardware.png b/common/.config/fastfetch/pngs/openhardware.png similarity index 100% rename from .config/fastfetch/pngs/openhardware.png rename to common/.config/fastfetch/pngs/openhardware.png diff --git a/common/.config/fastfetch/pngs/opensource.png b/common/.config/fastfetch/pngs/opensource.png new file mode 100644 index 0000000..99c2816 Binary files /dev/null and b/common/.config/fastfetch/pngs/opensource.png differ diff --git a/.config/fastfetch/pngs/opensourcee.png b/common/.config/fastfetch/pngs/opensourcee.png similarity index 100% rename from .config/fastfetch/pngs/opensourcee.png rename to common/.config/fastfetch/pngs/opensourcee.png diff --git a/common/.config/fastfetch/pngs/opensourcehardware.png b/common/.config/fastfetch/pngs/opensourcehardware.png new file mode 100644 index 0000000..1cd749a Binary files /dev/null and b/common/.config/fastfetch/pngs/opensourcehardware.png differ diff --git a/common/.config/fish/completions/c2-worktree.fish b/common/.config/fish/completions/c2-worktree.fish new file mode 100644 index 0000000..a4a2a4c --- /dev/null +++ b/common/.config/fish/completions/c2-worktree.fish @@ -0,0 +1,5 @@ +# Single positional arg: a branch name. It runs `git checkout -b`, so the new +# branch is typically a fresh name -- but offer existing branch names as a base +# to type from. Only suggest inside a git repo. +complete -c c2-worktree -x -n 'git rev-parse --is-inside-work-tree 2>/dev/null' \ + -a '(git for-each-ref --format="%(refname:short)" refs/heads refs/remotes 2>/dev/null | string replace -r "^origin/" "")' diff --git a/common/.config/fish/completions/h.fish b/common/.config/fish/completions/h.fish new file mode 100644 index 0000000..700542c --- /dev/null +++ b/common/.config/fish/completions/h.fish @@ -0,0 +1,3 @@ +# `h ...` shows a command's --help via bat, so complete it like a +# command line: first token = a command, rest = that command's own args. +complete -c h -x -a '(__fish_complete_subcommand)' diff --git a/.config/fish/completions/pipx.fish b/common/.config/fish/completions/pipx.fish similarity index 100% rename from .config/fish/completions/pipx.fish rename to common/.config/fish/completions/pipx.fish diff --git a/common/.config/fish/conf.d/fish-ssh-agent.fish b/common/.config/fish/conf.d/fish-ssh-agent.fish new file mode 100644 index 0000000..719087a --- /dev/null +++ b/common/.config/fish/conf.d/fish-ssh-agent.fish @@ -0,0 +1,7 @@ +if test -z "$SSH_ENV" + set -xg SSH_ENV $HOME/.ssh/environment +end + +if not __ssh_agent_is_started + __ssh_agent_start +end diff --git a/.config/fish/conf.d/fish_frozen_key_bindings.fish b/common/.config/fish/conf.d/fish_frozen_key_bindings.fish similarity index 100% rename from .config/fish/conf.d/fish_frozen_key_bindings.fish rename to common/.config/fish/conf.d/fish_frozen_key_bindings.fish diff --git a/.config/fish/conf.d/fish_frozen_theme.fish b/common/.config/fish/conf.d/fish_frozen_theme.fish similarity index 100% rename from .config/fish/conf.d/fish_frozen_theme.fish rename to common/.config/fish/conf.d/fish_frozen_theme.fish diff --git a/common/.config/fish/conf.d/fnm.fish b/common/.config/fish/conf.d/fnm.fish new file mode 100644 index 0000000..542288d --- /dev/null +++ b/common/.config/fish/conf.d/fnm.fish @@ -0,0 +1,7 @@ +# fnm — single init for all hosts (config.fish must NOT source fnm again: +# every `fnm env | source` mints a fresh multishell dir onto PATH). +# fnm may be pacman-installed (on PATH) or self-installed under ~/.local/share/fnm. +test -d "$HOME/.local/share/fnm"; and fish_add_path -g "$HOME/.local/share/fnm" +if command -q fnm + fnm env --use-on-cd --version-file-strategy local --shell fish | source +end diff --git a/common/.config/fish/config.fish b/common/.config/fish/config.fish new file mode 100644 index 0000000..7db692e --- /dev/null +++ b/common/.config/fish/config.fish @@ -0,0 +1,113 @@ +## Shared fish config (all hosts). Host-specific bits live in host.fish (sourced at the end). +## Secrets (e.g. DUSKADIY_API_KEY) go in conf.d/secrets.fish, which is gitignored. + +set -gx EDITOR nvim +set -gx PAGER less +set -gx QT_FONT_DPI 96 +set -gx PI_SKIP_VERSION_CHECK 1 +set -gx OPENCODE_CONFIG "$HOME/.config/opencode/openai-gpt.jsonc" +set -gx MANPAGER "nvim +Man!" +# fzf's built-in walker is the one search tool that ignores .ignore files, so point it at +# fd, which honours them. fd still hides dotfiles by default everywhere else — this only +# changes what fzf sees inside trees that opt in (e.g. ~/.dots/.ignore). +set -gx FZF_DEFAULT_COMMAND 'fd --type f --strip-cwd-prefix' +set -gx NEWT_COLORS 'root=black,black;window=black,black;border=white,black;listbox=white,black;label=blue,black;checkbox=red,black;title=green,black;button=white,red;actsellistbox=white,red;actlistbox=white,gray;compactbutton=white,gray;actcheckbox=white,blue;entry=lightgray,black;textbox=blue,black' # themes nmtui +# set -gx BAT_THEME "Catppuccin Mocha" + +# Cursor / greeting +set -g fish_greeting +set -g fish_cursor_insert line +set -g fish_cursor_default block +set -g fish_cursor_visual underscore + +# Misc +alias exti=exit +alias dmz="cat ~/.config/fish/dmz.txt" +alias nmatrix="neo-matrix -DS 3" +alias grep="grep --color=auto" +alias fzf="fzf --preview 'bat --color=always {}'" +alias fzff="fzf --multi --preview 'bat --style=numbers --color=always {}' | xargs -n 1 nvim" +abbr scrcpyz 'scrcpy -wSK -m 1920 --window-borderless --always-on-top --power-off-on-close --no-audio' + +# Git +alias gti="git" +alias gs="git status -s" +alias gca="git add -p . && git commit" +alias gd="git diff --word-diff" +alias gl="git log --graph --show-signature" +alias glog="git log --all --decorate --oneline --color --graph" +alias gla="git log --all --decorate --oneline" +alias gls="serie" +alias gm="git merge" + +# List directory +alias ls='eza --icons --group-directories-first' +alias l="ls -l" +alias la="ls -a" +alias lla="ls -la" +alias lt="ls --tree" + +# Dotfiles + notes +alias cdots="cd ~/.dots/" +alias cpnotes="cd ~/sync/PersonalNotes/" +alias cnotes="cd ~/sync/CojaDuska/Notes/" +abbr notes "cnotes && nvim" +abbr dots "cdots && nvim" +abbr dotsync "cdots && ./bin/dotsync" +abbr pnotes "cpnotes && nvim" +abbr todo "cpnotes && nvim ToDoNext.md" +abbr aliases "bat ~/.config/fish/config.fish" + +# Handy +abbr cat "bat -p" +abbr untar "tar -xf" +abbr unarchive "atool -x" +abbr copy wl-copy +abbr img "kitten icat" +abbr fm yazi +abbr lg lazygit +abbr gcommit "git diff HEAD | aichat -r commit" +abbr aichatdel "rm ~/.config/aichat/sessions/*.yaml" +abbr mkdir "mkdir -p" +abbr faillock "sudo faillock --reset" +abbr xremaps "sudo xremap ~/.config/xremap/config.yml" +abbr pacs "sudo pacman -Syu --noconfirm" +abbr yays "yay --noconfirm --sudoloop" +abbr nmaps "sudo nmap -sn 192.168.0.0/24" +abbr scrcpyc 'scrcpy -wSK -m 1920 --window-borderless --always-on-top --power-off-on-close' +abbr scrcpys 'scrcpy -wS --power-off-on-close' +abbr tts "tt -notheme -bold -showwpm -json" +abbr tuioss "tuios --show-clock --show-keys --show-cpu --show-ram --confirm-quit" +abbr keybinds "cd ~/.dots/ && nvim gui/.config/hypr/keybindings.conf" +abbr vpnhome "sudo wg-quick up wg0" +abbr vpnkralizec "sudo wg-quick up kralizec-wg0" +abbr vpnsumadija "sudo wg-quick up sumadija-wg0" +abbr ipadd "sudo ip route add 192.168.0.234 dev wg0" +abbr aria2cs "aria2c -x 16 -k 1M" + +# Change-dir shortcuts +abbr .. "cd .." +abbr ... "cd ../.." +abbr .3 "cd ../../.." +abbr .4 "cd ../../../.." +abbr .5 "cd ../../../../.." + +# Tool init (fnm is initialized once in conf.d/fnm.fish — don't source it again here) +zoxide init --cmd cd fish | source +fish_add_path $HOME/.cargo/bin $HOME/.local/bin + +# thefuck, lazy-loaded on first use (its --alias eval costs ~170ms of shell startup) +function fuck + functions -e fuck + thefuck --alias | source + fuck $argv +end + +# pnpm +set -gx PNPM_HOME "$HOME/.local/share/pnpm" +if not string match -q -- $PNPM_HOME $PATH + set -gx PATH "$PNPM_HOME" $PATH +end + +# Per-host extras +test -f ~/.config/fish/host.fish; and source ~/.config/fish/host.fish diff --git a/.config/fish/dmz.txt b/common/.config/fish/dmz.txt similarity index 100% rename from .config/fish/dmz.txt rename to common/.config/fish/dmz.txt diff --git a/common/.config/fish/fish_plugins b/common/.config/fish/fish_plugins new file mode 100644 index 0000000..fd5963b --- /dev/null +++ b/common/.config/fish/fish_plugins @@ -0,0 +1 @@ +danhper/fish-ssh-agent diff --git a/common/.config/fish/functions/__ssh_agent_is_started.fish b/common/.config/fish/functions/__ssh_agent_is_started.fish new file mode 100644 index 0000000..a9da28a --- /dev/null +++ b/common/.config/fish/functions/__ssh_agent_is_started.fish @@ -0,0 +1,23 @@ +function __ssh_agent_is_started -d "check if ssh agent is already started" + if test -n "$SSH_CONNECTION" + # This is an SSH session + ssh-add -l > /dev/null 2>&1 + if test $status -eq 0 -o $status -eq 1 + # An SSH agent was forwarded + return 0 + end + end + + if begin; test -f "$SSH_ENV"; and test -z "$SSH_AGENT_PID"; end + source $SSH_ENV > /dev/null + end + + if test -z "$SSH_AGENT_PID" + return 1 + end + + ssh-add -l > /dev/null 2>&1 + if test $status -eq 2 + return 1 + end +end diff --git a/common/.config/fish/functions/__ssh_agent_start.fish b/common/.config/fish/functions/__ssh_agent_start.fish new file mode 100644 index 0000000..3766fe4 --- /dev/null +++ b/common/.config/fish/functions/__ssh_agent_start.fish @@ -0,0 +1,5 @@ +function __ssh_agent_start -d "start a new ssh agent" + ssh-agent -c | sed 's/^echo/#echo/' > $SSH_ENV + chmod 600 $SSH_ENV + source $SSH_ENV > /dev/null +end diff --git a/.config/fish/functions/ask.fish b/common/.config/fish/functions/ask.fish similarity index 100% rename from .config/fish/functions/ask.fish rename to common/.config/fish/functions/ask.fish diff --git a/.config/fish/functions/bashs.fish b/common/.config/fish/functions/bashs.fish similarity index 100% rename from .config/fish/functions/bashs.fish rename to common/.config/fish/functions/bashs.fish diff --git a/.config/fish/functions/c2-worktree.fish b/common/.config/fish/functions/c2-worktree.fish similarity index 51% rename from .config/fish/functions/c2-worktree.fish rename to common/.config/fish/functions/c2-worktree.fish index 132bfa8..ede91cb 100644 --- a/.config/fish/functions/c2-worktree.fish +++ b/common/.config/fish/functions/c2-worktree.fish @@ -1,12 +1,18 @@ function c2-worktree if test (count $argv) -ne 1 - echo "Usage: pkg-worktree " - echo "Example: pkg-worktree package/package-update" + echo "Usage: c2-worktree " + echo "Example: c2-worktree package/package-update" return 1 end set branch $argv[1] - set dir (basename $branch) + + set root (git rev-parse --show-toplevel) + or return 1 + set dir $root/.worktrees/(basename $branch) + + mkdir -p $root/.worktrees + or return 1 git checkout -b $branch or return 1 diff --git a/common/.config/fish/functions/clis.fish b/common/.config/fish/functions/clis.fish new file mode 100644 index 0000000..8472c9c --- /dev/null +++ b/common/.config/fish/functions/clis.fish @@ -0,0 +1,3 @@ +function clis --description 'fzf select cli' + eval (cat $HOME/.config/misc/clis.txt | command fzf --multi) +end diff --git a/common/.config/fish/functions/copy-commandline.fish b/common/.config/fish/functions/copy-commandline.fish new file mode 100644 index 0000000..4365648 --- /dev/null +++ b/common/.config/fish/functions/copy-commandline.fish @@ -0,0 +1,18 @@ +function copy-commandline + set -l cmd (commandline) + if test -n "$cmd" + if type -q pbcopy + echo -n "$cmd" | pbcopy + else if type -q xclip + echo -n "$cmd" | xclip -selection clipboard + else if type -q wl-copy + echo -n "$cmd" | wl-copy + end + + # Provide brief visual feedback in the prompt + set -l original_cmd (commandline) + commandline -r "Copied!" + sleep 0.4 + commandline -r "$original_cmd" + end +end diff --git a/common/.config/fish/functions/extract.fish b/common/.config/fish/functions/extract.fish new file mode 100644 index 0000000..aabb31d --- /dev/null +++ b/common/.config/fish/functions/extract.fish @@ -0,0 +1,23 @@ +function extract --description 'Extract any archive' + switch $argv[1] + case '*.tar' '*.tar.*' '*.tgz' '*.tbz2' '*.txz' # tar autodetects compression + tar xf $argv[1] + case '*.zip' + unzip $argv[1] + case '*.rar' + unrar x $argv[1] + case '*.7z' + 7z x $argv[1] + case '*.gz' + gunzip -k $argv[1] + case '*.xz' + unxz -k $argv[1] + case '*.zst' + unzstd $argv[1] + case '*.bz2' + bunzip2 -k $argv[1] + case '*' + echo "Unknown extension: $argv[1]" + return 1 + end +end diff --git a/common/.config/fish/functions/fish_greeting.fish b/common/.config/fish/functions/fish_greeting.fish new file mode 100644 index 0000000..20cab0e --- /dev/null +++ b/common/.config/fish/functions/fish_greeting.fish @@ -0,0 +1,5 @@ +function fish_greeting + #curl "wttr.in/Belgrade?format=%l:+%c+%t+|+%h+|+%w+|+(%%M+%m)\n" + test -r ~/.config/fish/greet.txt; and cat ~/.config/fish/greet.txt ## populated by cronjob + date +"📅 %d. %b 🕗 %H:%M - %A" +end diff --git a/.config/fish/functions/fish_prompt.fish b/common/.config/fish/functions/fish_prompt.fish similarity index 100% rename from .config/fish/functions/fish_prompt.fish rename to common/.config/fish/functions/fish_prompt.fish diff --git a/.config/fish/functions/fish_right_prompt.fish b/common/.config/fish/functions/fish_right_prompt.fish similarity index 93% rename from .config/fish/functions/fish_right_prompt.fish rename to common/.config/fish/functions/fish_right_prompt.fish index 5fd70b8..38c5b37 100644 --- a/.config/fish/functions/fish_right_prompt.fish +++ b/common/.config/fish/functions/fish_right_prompt.fish @@ -1,7 +1,7 @@ function fish_right_prompt set -l cmd_status $status if test $cmd_status -ne 0 - echo -n (set_color red)"✘ $cmd_status" + echo -n (set_color red)" $cmd_status" end if not command -sq git @@ -130,25 +130,25 @@ function fish_right_prompt echo -n (set_color white)':'(set_color -o brred)"$action" end if test $status_ahead -ne 0 - echo -n ' '(set_color brmagenta)'⬆' + echo -n ' '(set_color brmagenta)'' end if test $status_behind -ne 0 - echo -n ' '(set_color brmagenta)'⬇' + echo -n ' '(set_color brmagenta)'' end if test $status_stashed -ne 0 - echo -n ' '(set_color cyan)'✭' + echo -n ' '(set_color cyan)'' end if test $status_added -ne 0 - echo -n ' '(set_color green)'✚' + echo -n ' '(set_color green)'' end if test $status_deleted -ne 0 - echo -n ' '(set_color red)'✖' + echo -n ' '(set_color red)'' end if test $status_modified -ne 0 - echo -n ' '(set_color blue)'✱' + echo -n ' '(set_color blue)'' end if test $status_renamed -ne 0 - echo -n ' '(set_color magenta)'➜' + echo -n ' '(set_color magenta)'' end if test $status_unmerged -ne 0 echo -n ' '(set_color yellow)'═' diff --git a/.config/fish/functions/fish_user_key_bindings.fish b/common/.config/fish/functions/fish_user_key_bindings.fish similarity index 63% rename from .config/fish/functions/fish_user_key_bindings.fish rename to common/.config/fish/functions/fish_user_key_bindings.fish index 19c75b2..00e16d6 100644 --- a/.config/fish/functions/fish_user_key_bindings.fish +++ b/common/.config/fish/functions/fish_user_key_bindings.fish @@ -1,4 +1,6 @@ function fish_user_key_bindings + # free Ctrl+S from terminal flow control (XOFF) so it can be bound below + stty -ixon fish_default_key_bindings -M insert fish_vi_key_bindings --no-erase insert bind -M default ctrl-a beginning-of-line @@ -7,4 +9,7 @@ function fish_user_key_bindings bind -M insert ctrl-g 'git diff' repaint bind -M insert ctrl-o execute yazi bind -M insert ctrl-l clear-screen ls repaint + bind -M default ctrl-s copy-commandline + bind -M insert ctrl-s copy-commandline + bind -M search ctrl-s copy-commandline end diff --git a/common/.config/fish/functions/fzfh.fish b/common/.config/fish/functions/fzfh.fish new file mode 100644 index 0000000..24c8eee --- /dev/null +++ b/common/.config/fish/functions/fzfh.fish @@ -0,0 +1,7 @@ +function fzfh --description 'Search history with fzf' + history | fzf --query="$argv" --tac | read cmd + if test -n "$cmd" + echo $cmd + commandline -r $cmd + end +end diff --git a/common/.config/fish/functions/gcap.fish b/common/.config/fish/functions/gcap.fish new file mode 100644 index 0000000..85633ed --- /dev/null +++ b/common/.config/fish/functions/gcap.fish @@ -0,0 +1,5 @@ +function gcap --description 'Git add, commit with message, and push' + git add . + and git commit -m "$argv" + and git push +end diff --git a/common/.config/fish/functions/h.fish b/common/.config/fish/functions/h.fish new file mode 100644 index 0000000..aa3e2c4 --- /dev/null +++ b/common/.config/fish/functions/h.fish @@ -0,0 +1,3 @@ +function h --description "Colorized --help via bat" + $argv --help 2>&1 | bat --plain --language=help +end diff --git a/common/.config/fish/functions/lk.fish b/common/.config/fish/functions/lk.fish new file mode 100644 index 0000000..3d30037 --- /dev/null +++ b/common/.config/fish/functions/lk.fish @@ -0,0 +1,18 @@ +function lk --description 'linux knowledge' + set -l DB "$HOME/projects/dmz/soft/lk/db.rec" + set -l passes 0 + set -l count 0 + set -l query "" + + while test $passes -lt 2; and test $count -eq 0 + set query (recsel "$DB" -p aim,tag | recsel -iq "$query" -CP aim,tag | sort -u | fzf --preview="recsel \"$DB\" -e \"aim~{}\" | bat") + if test -n "$query" + set count (recsel "$DB" -q "$query" -c) + end + set passes (math $passes + 1) + end + + if test $count -eq 1 + recsel "$DB" -q "$query" | recfmt -f "$HOME/projects/dmz/soft/lk/lists.fmt" | less + end +end diff --git a/.config/fish/functions/opencodes.fish b/common/.config/fish/functions/opencodes.fish similarity index 79% rename from .config/fish/functions/opencodes.fish rename to common/.config/fish/functions/opencodes.fish index f1d35be..3b34ae4 100644 --- a/.config/fish/functions/opencodes.fish +++ b/common/.config/fish/functions/opencodes.fish @@ -1,5 +1,6 @@ -function opencodes --description 'bubblewraped opencode' - bwrap --unshare-all \ +function opencodes --description 'contained opencode' + bwrap \ + --unshare-all \ --new-session \ --clearenv \ --die-with-parent \ @@ -16,6 +17,8 @@ function opencodes --description 'bubblewraped opencode' --bind ~/.local/state/opencode ~/.local/state/opencode \ --bind ~/.local/share/opencode ~/.local/share/opencode \ --bind "$(pwd)" "$(pwd)" \ - opencode + opencode end -# strace -f -e trace=%file opencode + + + diff --git a/common/.config/fish/functions/ssh.fish b/common/.config/fish/functions/ssh.fish new file mode 100644 index 0000000..9977f95 --- /dev/null +++ b/common/.config/fish/functions/ssh.fish @@ -0,0 +1,7 @@ +function ssh + if type -q kitten + kitten ssh $argv + else + command ssh $argv + end +end diff --git a/common/.config/fish/functions/sshf.fish b/common/.config/fish/functions/sshf.fish new file mode 100644 index 0000000..2325d6a --- /dev/null +++ b/common/.config/fish/functions/sshf.fish @@ -0,0 +1,6 @@ +function sshf --description 'fzf-pick an ssh host from ~/.ssh/config' + # command fzf: bypass the fzf alias (its bat preview makes no sense for host names); + # skip `Host *` wildcard blocks and take every alias on multi-name Host lines + set -l host (awk '/^Host /{for (i = 2; i <= NF; i++) if ($i != "*") print $i}' ~/.ssh/config | command fzf) + test -n "$host"; and ssh $host +end diff --git a/common/.config/fish/functions/tuis.fish b/common/.config/fish/functions/tuis.fish new file mode 100644 index 0000000..9665608 --- /dev/null +++ b/common/.config/fish/functions/tuis.fish @@ -0,0 +1,3 @@ +function tuis --description 'fzf select tui' + eval (cat $HOME/.config/misc/tuis.txt | command fzf --multi) +end diff --git a/.config/lazygit/config.yml b/common/.config/lazygit/config.yml similarity index 98% rename from .config/lazygit/config.yml rename to common/.config/lazygit/config.yml index 47ab7f7..21d6337 100644 --- a/.config/lazygit/config.yml +++ b/common/.config/lazygit/config.yml @@ -174,18 +174,12 @@ gui: # Config relating to git git: # See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md - pagers: + diffRenderers: - # Value of the --color arg in the git diff command. Some pagers want this to be set to 'always' and some want it set to 'never' colorArg: always - # e.g. - # diff-so-fancy - # delta --dark --paging=never - # ydiff -p cat -s --wrap --width={{columnWidth}} - pager: "" # If true, Lazygit will use whatever pager is specified in `$GIT_PAGER`, `$PAGER`, or your *git config*. If the pager ends with something like ` | less` we will strip that part out, because less doesn't play nice with our rendering approach. If the custom pager uses less under the hood, that will also break rendering (hence the `--paging=never` flag for the `delta` pager). useConfig: false - # e.g. 'difft --color=always' - externalDiffCommand: "" + type: rawGit # Config relating to committing commit: # If true, pass '--signoff' flag when committing @@ -361,8 +355,8 @@ keybinding: nextMatch: "n" prevMatch: "N" startSearch: / - optionMenu: - optionMenu-alt1: '?' + optionMenu: '?' + optionMenu-alt1: select: goInto: confirm: @@ -404,6 +398,7 @@ keybinding: increaseRenameSimilarityThreshold: ) decreaseRenameSimilarityThreshold: ( openDiffTool: + newWorktree: w status: checkForUpdate: u recentRepos: @@ -442,8 +437,6 @@ keybinding: setUpstream: u fetchRemote: f sortOrder: s - worktrees: - viewWorktreeOptions: w commits: squashDown: s renameCommit: r @@ -463,6 +456,7 @@ keybinding: markCommitAsBaseForRebase: B tagCommit: T checkoutCommit: + # resetCherryPick: copyCommitAttributeToClipboard: "y" openLogMenu: openInBrowser: o diff --git a/common/.config/misc/appImages.txt b/common/.config/misc/appImages.txt new file mode 100644 index 0000000..961fcc2 --- /dev/null +++ b/common/.config/misc/appImages.txt @@ -0,0 +1,3 @@ +om +openWebUI +itTools diff --git a/common/.config/misc/clis.txt b/common/.config/misc/clis.txt new file mode 100644 index 0000000..1347b19 --- /dev/null +++ b/common/.config/misc/clis.txt @@ -0,0 +1,36 @@ +jq +fq +rg +fd +fzf +lsd +bat +zoxide +resvg +mosh +tennis +dysk +dust +scrcpy +adb +gpg +fnm +recfmt +recsel +pass +xxd +voxcii +tldr +rsync +cowsay +fortune +lolcat +glow +pake +bwrap +dog +nmap +traceroute +fuck +atool +aria2c diff --git a/common/.config/misc/tuis.txt b/common/.config/misc/tuis.txt new file mode 100644 index 0000000..54bfd56 --- /dev/null +++ b/common/.config/misc/tuis.txt @@ -0,0 +1,72 @@ +browsh +wiki-tui +wikiman +yazi +tdf +dysk +dust +ncdu +duf +systemctl-tui +btop +bandwhich +mtr +gping +bmon +profanity +nmtui +impala +bluetui +sshm +aichat +tt +gittype +toofan +pastel +hexyl +tmux +zellij +nvim +vim +tetrigo +pyradio +px7-radio +kew +cmatrix +neo-matrix +cava +pipes-rs +asciiquarium +cbonsai +tclock +hunk +gittop +lazygit +lazyjournal +lazydocker +lazyrsync +ctop +colima +dive +asciinema +posting +serie +clx +whosthere +voxcii +bookokrat +tennis +llmfit +termdraw +torlink +riptide +quien +eilmeldung +calcure +weathr +hyprmon +hulak +netwatch +gpk +circumflex +darya diff --git a/.config/nvim/.stylua.toml b/common/.config/nvim/.stylua.toml similarity index 100% rename from .config/nvim/.stylua.toml rename to common/.config/nvim/.stylua.toml diff --git a/common/.config/nvim/README.md b/common/.config/nvim/README.md new file mode 100644 index 0000000..518a989 --- /dev/null +++ b/common/.config/nvim/README.md @@ -0,0 +1,98 @@ +# Neovim Configuration Setup + +This Neovim configuration is built on top of [NvChad](https://nvchad.com/), a popular Neovim framework designed for speed and aesthetics. It uses [lazy.nvim](https://github.com/folke/lazy.nvim) as the plugin manager. + +## Core Components + +- **Framework**: [NvChad](https://nvchad.com/) (v2.5) +- **Plugin Manager**: `lazy.nvim` +- **Theme Engine**: `base46` (integrated with NvChad) +- **Keybindings**: Custom mappings are defined in `lua/mappings.lua`. +- **Options**: Base options are inherited from NvChad, with customizations in `lua/options.lua`. + +## Installed Plugins & Features + +The configuration includes several key plugins and features: + +### Development Tools +- **LSP (Language Server Protocol)**: Configured via `nvim-lspconfig`. Note that you must install the actual language servers using [Mason.nvim](https://github.com/williamboman/mason.nvim). +- **Formatting**: Uses `conform.nvim` for code formatting (configured in `lua/configs/conform.lua`). +- **Treesitter**: Provides advanced syntax highlighting and code folding (configured in `lua/plugins/init.lua`). +- **Telescope**: Powerful fuzzy finder for files, text, and LSP symbols. + +### UI & Productivity +- **Snacks.nvim**: A collection of useful utilities including: + - `explorer`: File explorer. + - `indent`: Indent guides (replaces `indent-blankline.nvim` to avoid duplication). + - `picker`: Fast picker for various tasks. + - `notifier`: Improved notification system. + - `words`: Word count and related features. +- **Render Markdown**: Enhances the appearance of Markdown files in the buffer (`lua/configs/render-markdown.lua`). + +## Maintenance & Package Management + +### Managing Plugins (lazy.nvim) +To update your installed Neovim plugins: +1. Open Neovim. +2. Run `:Lazy` to open the plugin manager UI. +3. Press `U` to check for updates and `u` to update everything. + +### Managing LSPs & Tools (Mason.nvim) +While plugins are managed by `lazy.nvim`, the actual language servers, debuggers, and formatters are managed by **Mason**. +1. Open Neovim. +2. Run `:Mason` to open the Mason UI. +3. Use the UI to install or update your desired LSPs (e.g., `ts_ls`, `html`, `cssls`), linters, or formatters. + +### Managing Syntax Highlighting (Treesitter) +To ensure all language parsers are installed for proper syntax highlighting: +1. Open Neovim. +2. Run `:TSUpdate` (or `:TSInstall ` for a specific one). + +## Custom Keybindings + +Some notable custom mappings (defined in `lua/mappings.lua`): + +| Mapping | Action | Description | +|---------|--------|-------------| +| `gd` | `vim.lsp.buf.definition` | Go to definition | +| `gD` | `Telescope lsp_definitions` | Go to definitions (Telescope) | +| `grr` | `Telescope lsp_references` | Go to references (Telescope) | +| `da` | `vim.lsp.buf.code_action()` | Code actions | +| `dh` | `vim.diagnostic.open_float()` | Show diagnostics in float | +| `dj` | `vim.diagnostic.jump` | Go to next diagnostic | +| `dk` | `vim.diagnostic.jump` | Go to previous diagnostic | +| `fr` | `Telescope resume` | Resume last Telescope search | +| `fs` | `Telescope grep_string` | Find selected string | +| `fk` | `Telescope keymaps` | Find keymaps | +| `fd` | `Telescope diagnostics` | Search diagnostics | + +**Improved Deletion/Change:** +- `x` (in Normal/Visual mode): Deletes a character without yanking it to the register. +- `c` (in Normal/Visual mode): Changes text without yanking it to the register. + +## Directory Structure + +```text +. +├── init.lua # Entry point: bootstraps lazy.nvim and loads config +├── lazy-lock.json # Lockfile for lazy.nvim +└── lua/ + ├── chadrc.lua # NvChad configuration + ├── configs/ # Plugin-specific configurations + │ ├── conform.lua + │ ├── lazy.lua + │ ├── lspconfig.lua + │ └── render-markdown.lua + ├── mappings.lua # Custom keybindings + ├── options.lua # Custom Neovim options + └── plugins/ + └── init.lua # Plugin definitions and setup +``` + +## Installation + +1. Ensure you have [Neovim](https://neovim.io/) (latest stable or nightly) installed. +2. Clone this repository to your Neovim configuration directory (usually `~/.config/nvim`). +3. Open Neovim. `lazy.nvim` will automatically bootstrap and install all required plugins. +4. **Important**: After the initial plugin installation, run `:MasonInstall ` (or use the `:Mason` UI) to install the language servers required for your workflow. +5. Restart Neovim after the initial installation completes. diff --git a/.config/nvim/init.lua b/common/.config/nvim/init.lua similarity index 100% rename from .config/nvim/init.lua rename to common/.config/nvim/init.lua diff --git a/common/.config/nvim/lazy-lock.json b/common/.config/nvim/lazy-lock.json new file mode 100644 index 0000000..a4f6a3c --- /dev/null +++ b/common/.config/nvim/lazy-lock.json @@ -0,0 +1,31 @@ +{ + "LuaSnip": { "branch": "master", "commit": "0abc8f390b278c3b4aabc4c004ac8a088b65cf24" }, + "NvChad": { "branch": "v2.5", "commit": "add44b952d631981614bbb8cfc6f7002f296dfe6" }, + "base46": { "branch": "v3.0", "commit": "267954c8663607823f03a3259bb8deb15688212f" }, + "cmp-async-path": { "branch": "main", "commit": "98185a91d49ff5dd249aebf2f7456e18063fa2a0" }, + "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" }, + "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" }, + "cmp-nvim-lua": { "branch": "main", "commit": "e3a22cb071eb9d6508a156306b102c45cd2d573d" }, + "cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" }, + "conform.nvim": { "branch": "master", "commit": "016802de402556da54c36bd7359b441266b01cdd" }, + "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" }, + "gitsigns.nvim": { "branch": "main", "commit": "d29ecf6626cf6ffb109fb04a8ecd92c46f4a1ce9" }, + "indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" }, + "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" }, + "mason.nvim": { "branch": "main", "commit": "2a6940af80375532e5e9e7c1f2fc6319a1b7a69d" }, + "menu": { "branch": "main", "commit": "7a0a4a2896b715c066cfbe320bdc048091874cc6" }, + "minty": { "branch": "main", "commit": "aafc9e8e0afe6bf57580858a2849578d8d8db9e0" }, + "nvim-autopairs": { "branch": "master", "commit": "7b9923abad60b903ece7c52940e1321d39eccc79" }, + "nvim-cmp": { "branch": "main", "commit": "2ffe79f1f021def8dd1fcd81deb16f1bb0d989f3" }, + "nvim-lspconfig": { "branch": "master", "commit": "4267d26e60323a45e18f8e7a63a964cc8f53ca4e" }, + "nvim-tree.lua": { "branch": "master", "commit": "b2aadda94b107480c48e548d6db51c6840b7b33c" }, + "nvim-treesitter": { "branch": "main", "commit": "c9f9ed6c1892f629ea399f4ee7905f2686fa13f2" }, + "nvim-web-devicons": { "branch": "master", "commit": "2ae6958df7ced50baac5035cec0c15799eedfbf7" }, + "plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" }, + "render-markdown.nvim": { "branch": "main", "commit": "f422cb5c6855f150e2ddcfaf44e7157b98b34f6a" }, + "snacks.nvim": { "branch": "main", "commit": "882c996cf28183f4d63640de0b4c02ec886d01f2" }, + "telescope.nvim": { "branch": "master", "commit": "427b576c16792edad01a92b89721d923c19ad60f" }, + "ui": { "branch": "v3.0", "commit": "fe781d1c12860d6a25d45e588fe4fdd27eb34a1a" }, + "volt": { "branch": "main", "commit": "620de1321f275ec9d80028c68d1b88b409c0c8b1" }, + "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" } +} diff --git a/.config/nvim/lua/chadrc.lua b/common/.config/nvim/lua/chadrc.lua similarity index 93% rename from .config/nvim/lua/chadrc.lua rename to common/.config/nvim/lua/chadrc.lua index 3d52eff..dbf4537 100644 --- a/.config/nvim/lua/chadrc.lua +++ b/common/.config/nvim/lua/chadrc.lua @@ -5,10 +5,11 @@ ---@type ChadrcConfig local M = { + lsp = { signature = true }, + base46 = { theme = "onedark", transparency = true, - lsp = { signature = true }, hl_override = { NvimTreeGitDirty = { fg = "#DBA55D", @@ -19,7 +20,7 @@ local M = { }, nvdash = { - load_on_startup = false, + load_on_startup = true, -- buttons = { -- { txt = " Find File", keys = "Spc f f", cmd = "Telescope find_files custom" }, -- { txt = " Recent Files", keys = "Spc f o", cmd = "Telescope oldfiles" }, @@ -35,7 +36,6 @@ local M = { order = { "treeOffset", "buffers", "tabs", "btns" }, modules = nil, bufwidth = 21, - transparency = true, }, }, diff --git a/.config/nvim/lua/configs/conform.lua b/common/.config/nvim/lua/configs/conform.lua similarity index 56% rename from .config/nvim/lua/configs/conform.lua rename to common/.config/nvim/lua/configs/conform.lua index 1d0ee88..c4834be 100644 --- a/.config/nvim/lua/configs/conform.lua +++ b/common/.config/nvim/lua/configs/conform.lua @@ -1,16 +1,10 @@ local options = { formatters_by_ft = { lua = { "stylua" }, - javascript = { "prettier" }, - javascriptreact = { "prettier" }, - typescript = { "prettier" }, - typescriptreact = { "prettier" }, - json = { "prettier" }, + css = { "prettier" }, html = { "prettier" }, - yaml = { "prettier" }, - markdown = { "prettier" }, - css = { "prettier", "stylelint" }, - scss = { "prettier", "stylelint" }, + typescript = { "prettier" }, + javascript = { "prettier" }, }, format_on_save = { diff --git a/.config/nvim/lua/configs/lazy.lua b/common/.config/nvim/lua/configs/lazy.lua similarity index 100% rename from .config/nvim/lua/configs/lazy.lua rename to common/.config/nvim/lua/configs/lazy.lua diff --git a/.config/nvim/lua/configs/lspconfig.lua b/common/.config/nvim/lua/configs/lspconfig.lua similarity index 92% rename from .config/nvim/lua/configs/lspconfig.lua rename to common/.config/nvim/lua/configs/lspconfig.lua index 744351a..bb5228c 100644 --- a/.config/nvim/lua/configs/lspconfig.lua +++ b/common/.config/nvim/lua/configs/lspconfig.lua @@ -1,6 +1,6 @@ require("nvchad.configs.lspconfig").defaults() -local servers = { "html", "cssls", "ts_ls", "eslint", "yamlls" } +local servers = { "html", "cssls", "ts_ls", "eslint" } vim.lsp.enable(servers) -- local lspconfig = require "lspconfig" diff --git a/.config/nvim/lua/configs/render-markdown.lua b/common/.config/nvim/lua/configs/render-markdown.lua similarity index 99% rename from .config/nvim/lua/configs/render-markdown.lua rename to common/.config/nvim/lua/configs/render-markdown.lua index 97c9984..9e24d04 100644 --- a/.config/nvim/lua/configs/render-markdown.lua +++ b/common/.config/nvim/lua/configs/render-markdown.lua @@ -24,7 +24,7 @@ return { -- Only intended to be used for plugin development / debugging. log_runtime = false, -- Filetypes this plugin will run on. - file_types = { "markdown", "gitcommit", "codecompanion" }, + file_types = { "markdown", "gitcommit" }, -- Takes buffer as input, if it returns true this plugin will not attach to the buffer ignore = function() return false diff --git a/.config/nvim/lua/mappings.lua b/common/.config/nvim/lua/mappings.lua similarity index 64% rename from .config/nvim/lua/mappings.lua rename to common/.config/nvim/lua/mappings.lua index 01a98c9..a5a3544 100644 --- a/.config/nvim/lua/mappings.lua +++ b/common/.config/nvim/lua/mappings.lua @@ -9,18 +9,17 @@ map("n", "gD", "Telescope lsp_definitions", { desc = "Go to Definitions map("n", "grr", "Telescope lsp_references", { desc = "Go to references" }) map("n", "da", ":lua vim.lsp.buf.code_action()", { desc = "Code actions", noremap = true, silent = true }) map("n", "dh", ":lua vim.diagnostic.open_float()", { desc = "Show diagnostics" }) -map("n", "dj", ":lua vim.diagnostic.goto_next()", { desc = "Go to next" }) -map("n", "dk", ":lua vim.diagnostic.goto_prev()", { desc = "Go to previous" }) +map("n", "dj", function() + vim.diagnostic.jump { count = 1, float = true } +end, { desc = "Go to next" }) +map("n", "dk", function() + vim.diagnostic.jump { count = -1, float = true } +end, { desc = "Go to previous" }) -- Telescope map("n", "fr", ":Telescope resume", { desc = "Resume last search" }) map("n", "fs", ":Telescope grep_string", { desc = "Find selected string" }) map("n", "fk", ":Telescope keymaps", { desc = "Find keymaps" }) map("n", "fd", ":Telescope diagnostics", { desc = "Diagnostics" }) --- CodeCompanion -map({ "n", "v" }, "ge", ":CodeCompanion", { desc = "CodeCompanion" }) -map({ "n", "v" }, "ga", ":CodeCompanionActions", { desc = "CodeCompanion actions" }) -map("n", "gc", ":CodeCompanionChat Toggle", { desc = "CodeCompanion chat" }) -map("v", "gc", ":CodeCompanionChat Add", { desc = "Send selection to CodeCompanion chat" }) -- Remaps map({ "n", "v" }, "x", '"_x', { desc = "Delete char without yanking" }) map({ "n", "v" }, "c", '"_c', { desc = "Change without yanking" }) diff --git a/.config/nvim/lua/options.lua b/common/.config/nvim/lua/options.lua similarity index 100% rename from .config/nvim/lua/options.lua rename to common/.config/nvim/lua/options.lua diff --git a/.config/nvim/lua/plugins/init.lua b/common/.config/nvim/lua/plugins/init.lua similarity index 59% rename from .config/nvim/lua/plugins/init.lua rename to common/.config/nvim/lua/plugins/init.lua index 4d15441..7048801 100644 --- a/.config/nvim/lua/plugins/init.lua +++ b/common/.config/nvim/lua/plugins/init.lua @@ -1,7 +1,7 @@ return { { "stevearc/conform.nvim", - event = "BufWritePre", + event = "BufWritePre", -- uncomment for format on save opts = require "configs.conform", }, @@ -13,24 +13,41 @@ return { }, { - "olimorris/codecompanion.nvim", - version = "^19.0.0", - lazy = false, - dependencies = { - "nvim-lua/plenary.nvim", - "nvim-treesitter/nvim-treesitter", + "nvim-treesitter/nvim-treesitter", + branch = "main", + opts = { + ensure_installed = { + "vim", + "vimdoc", + "lua", + "luadoc", + "printf", + "html", + "css", + "typescript", + "javascript", + "markdown", + "scss", + "svelte", + "typst", + "vue", + "regex", + }, }, - opts = function() - return require "configs.codecompanion" - end, }, + { "MeanderingProgrammer/render-markdown.nvim", - ft = { "markdown", "gitcommit", "codecompanion" }, + ft = { "markdown", "gitcommit" }, opts = function() return require "configs.render-markdown" end, }, + + -- snacks.indent (below) draws the indent guides — disable NvChad's indent-blankline + -- so they don't render doubled + { "lukas-reineke/indent-blankline.nvim", enabled = false }, + { "folke/snacks.nvim", priority = 1000, @@ -45,7 +62,7 @@ return { explorer = { enabled = true }, indent = { enabled = true }, input = { enabled = true }, - picker = { enabled = false }, + picker = { enabled = true }, notifier = { enabled = true }, quickfile = { enabled = true }, scope = { enabled = true }, @@ -55,25 +72,6 @@ return { }, }, - -- { - -- "jacob411/Ollama-Copilot", - -- opts = { - -- model_name = "llama3:3b", - -- stream_suggestion = false, - -- python_command = "python3", - -- filetypes = { "python", "lua", "vim", "markdown", "javascript", "typescript" }, - -- ollama_model_opts = { - -- num_predict = 40, - -- temperature = 0.1, - -- }, - -- keymaps = { - -- suggestion = "os", - -- reject = "or", - -- insert_accept = "", - -- }, - -- }, - -- }, - -- These are some examples, uncomment them if you want to see them work! -- -- { diff --git a/common/.config/opencode/openai-gpt.jsonc b/common/.config/opencode/openai-gpt.jsonc new file mode 100644 index 0000000..9161024 --- /dev/null +++ b/common/.config/opencode/openai-gpt.jsonc @@ -0,0 +1,239 @@ +{ + "$schema": "https://opencode.ai/config.json", + "plugin": [ + "opencode-openai-codex-auth" + ], + "provider": { + "openai": { + "options": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": [ + "reasoning.encrypted_content" + ], + "store": false + }, + "models": { + "gpt-5.2": { + "name": "GPT 5.2 (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "none": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.2-codex": { + "name": "GPT 5.2 Codex (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.1-codex-max": { + "name": "GPT 5.1 Codex Max (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "low": { + "reasoningEffort": "low", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.1-codex": { + "name": "GPT 5.1 Codex (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.1-codex-mini": { + "name": "GPT 5.1 Codex Mini (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.1": { + "name": "GPT 5.1 (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "none": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "low" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "high" + } + } + } + } + } + } +} diff --git a/.config/opencode/opencode.jsonc b/common/.config/opencode/opencode.json similarity index 60% rename from .config/opencode/opencode.jsonc rename to common/.config/opencode/opencode.json index 0bd670e..74f327b 100644 --- a/.config/opencode/opencode.jsonc +++ b/common/.config/opencode/opencode.json @@ -1,7 +1,7 @@ { "$schema": "https://opencode.ai/config.json", - "model": "duskadiy/gemma-4-26B-A4B-it-UD-IQ4_XS", - "small_model": "duskadiy/gemma-4-E4B-it-UD-Q8_K_XL", + "model": "llama.cpp/gemma-4-26B-A4B-it-UD-IQ4_XS", + "small_model": "llama.cpp/gemma-4-E4B-it-UD-Q8_K_XL", "instructions": ["CLAUDE.md", "AGENTS.md"], "share": "disabled", "autoupdate": "notify", @@ -49,14 +49,14 @@ "diff *": "allow", "sort *": "allow", "uniq *": "allow", - "echo *": "allow", + "echo *": "ask", "pwd": "allow", "date": "allow", "whoami": "allow", "jq *": "allow", "tsc *": "allow", - "mkdir *": "allow", - "touch *": "allow", + "mkdir *": "ask", + "touch *": "ask", "git *": "ask", "git status": "allow", @@ -136,27 +136,16 @@ "baseURL": "https://llm.duskadiy.com/api/v1" }, "models": { - "gemma-4-26B-A4B-it-UD-IQ4_XS": { - "name": "Gemma 4 26B (remote)", - "limit": { - "context": 81920, - "output": 8192 - } - }, - "DeepSeek-Coder-V2-Lite-Instruct-Q8_0": { - "name": "DeepSeek Coder V2 (remote)", - "limit": { - "context": 81920, - "output": 8192 - } - }, - "Qwen3-Coder-30B-Instruct-IQ4_XS": { - "name": "Qwen3 Coder A3B (remote)", - "limit": { - "context": 81920, - "output": 8192 - } - } + "Qwen3-Coder-30B-Instruct-UD-Q3_K_XL": { "name": "Qwen3 Coder 30B · 32k — main agent coder (remote)", "limit": { "context": 32768, "output": 4096 } }, + "Qwen3-Coder-Next-UD-IQ3_XXS": { "name": "Qwen3 Coder Next 80B · 128k — long sessions (remote)", "limit": { "context": 131072, "output": 16384 } }, + "Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS": { "name": "Qwen3.6 35B · 24k · vision — daily driver (remote)", "attachment": true, "limit": { "context": 24576, "output": 8192 } }, + "Qwen3.6-35B-A3B-Thinking": { "name": "Qwen3.6 35B Thinking · 24k · vision — hard problems (remote)", "attachment": true, "limit": { "context": 24576, "output": 8192 } }, + "Qwen3.5-9B-UD-Q6_K_XL": { "name": "Qwen3.5 9B · 32k · vision — quick tasks (remote)", "attachment": true, "limit": { "context": 32768, "output": 8192 } }, + "gemma-4-26B-A4B-it-UD-IQ4_XS": { "name": "Gemma 4 26B · 24k · vision — quality generalist (remote)", "attachment": true, "limit": { "context": 24576, "output": 4096 } }, + "gemma-4-E4B-it-UD-Q8_K_XL": { "name": "Gemma 4 E4B · 64k · vision — fast generalist, long docs (remote)", "attachment": true, "limit": { "context": 65536, "output": 16384 } }, + "GLM-4.7-Flash-UD-Q4_K_XL": { "name": "GLM-4.7 Flash · 24k — quality coder (remote)", "limit": { "context": 24576, "output": 8192 } }, + "gpt-oss-20b": { "name": "gpt-oss 20B · 64k — fast reasoning + tools (remote)", "limit": { "context": 65536, "output": 8192 } }, + "gpt-oss-20b-low": { "name": "gpt-oss 20B low · 64k — snappy answers (remote)", "limit": { "context": 65536, "output": 8192 } } } }, "llama.cpp": { @@ -166,41 +155,16 @@ "baseURL": "http://192.168.0.204:11343/v1" }, "models": { - "DeepSeek-Coder-V2-Lite-Instruct-Q8_0": { - "name": "DeepSeek Coder V2", - "limit": { - "context": 81920, - "output": 8192 - } - }, - "gemma-4-E4B-it-UD-Q8_K_XL": { - "name": "Gemma 4 E4B", - "limit": { - "context": 81920, - "output": 8192 - } - }, - "gemma-4-26B-A4B-it-UD-IQ4_XS": { - "name": "Gemma 4 26B", - "limit": { - "context": 81920, - "output": 8192 - } - }, - "Qwen3-Coder-30B-Instruct-IQ4_XS": { - "name": "Qwen3 Coder A3B", - "limit": { - "context": 81920, - "output": 8192 - } - }, - "Qwen3.6-27B-UD-IQ3_XXS.gguf": { - "name": "Qwen3.6 27B", - "limit": { - "context": 81920, - "output": 8192 - } - } + "Qwen3-Coder-Next-UD-IQ3_XXS": { "name": "Qwen3 Coder Next 80B · 128k — long sessions", "limit": { "context": 131072, "output": 16384 } }, + "Qwen3-Coder-30B-Instruct-UD-Q3_K_XL": { "name": "Qwen3 Coder 30B · 32k — main agent coder", "limit": { "context": 32768, "output": 4096 } }, + "Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS": { "name": "Qwen3.6 35B · 24k · vision — daily driver", "attachment": true, "limit": { "context": 24576, "output": 8192 } }, + "Qwen3.6-35B-A3B-Thinking": { "name": "Qwen3.6 35B Thinking · 24k · vision — hard problems", "attachment": true, "limit": { "context": 24576, "output": 8192 } }, + "Qwen3.5-9B-UD-Q6_K_XL": { "name": "Qwen3.5 9B · 32k · vision — quick tasks", "attachment": true, "limit": { "context": 32768, "output": 8192 } }, + "gemma-4-26B-A4B-it-UD-IQ4_XS": { "name": "Gemma 4 26B · 24k · vision — quality generalist", "attachment": true, "limit": { "context": 24576, "output": 4096 } }, + "gemma-4-E4B-it-UD-Q8_K_XL": { "name": "Gemma 4 E4B · 64k · vision — fast generalist, long docs", "attachment": true, "limit": { "context": 65536, "output": 16384 } }, + "GLM-4.7-Flash-UD-Q4_K_XL": { "name": "GLM-4.7 Flash · 24k — quality coder", "limit": { "context": 24576, "output": 8192 } }, + "gpt-oss-20b": { "name": "gpt-oss 20B · 64k — fast reasoning + tools", "limit": { "context": 65536, "output": 8192 } }, + "gpt-oss-20b-low": { "name": "gpt-oss 20B low · 64k — snappy answers", "limit": { "context": 65536, "output": 8192 } } } } }, @@ -235,7 +199,7 @@ "description": "Reviews code for best practices and potential issues", "prompt": "You are a code reviewer. Focus on security, performance, and maintainability.", "mode": "subagent", - "model": "llama.cpp/DeepSeek-Coder-V2-Lite-Instruct-Q8_0", + "model": "llama.cpp/GLM-4.7-Flash-UD-Q4_K_XL", "permission": { "edit": "deny" } @@ -244,7 +208,7 @@ "description": "Runs and interprets test suites", "prompt": "You are a test runner. Focus on identifying failing tests and providing clear reproduction steps.", "mode": "subagent", - "model": "llama.cpp/DeepSeek-Coder-V2-Lite-Instruct-Q8_0", + "model": "llama.cpp/GLM-4.7-Flash-UD-Q4_K_XL", "permission": { "edit": "deny" } @@ -269,7 +233,7 @@ "description": "Scans for security vulnerabilities and hardcoded secrets", "prompt": "You are a security auditor. Focus on OWASP principles, secret detection, and dependency vulnerabilities.", "mode": "subagent", - "model": "llama.cpp/DeepSeek-Coder-V2-Lite-Instruct-Q8_0", + "model": "llama.cpp/GLM-4.7-Flash-UD-Q4_K_XL", "permission": { "edit": "deny" } diff --git a/common/.config/opencode/tui.json b/common/.config/opencode/tui.json new file mode 100644 index 0000000..f05004f --- /dev/null +++ b/common/.config/opencode/tui.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://opencode.ai/tui.json", + "theme": "lucent-orng" +} diff --git a/common/.config/presenterm/config.yaml b/common/.config/presenterm/config.yaml new file mode 100644 index 0000000..75d3086 --- /dev/null +++ b/common/.config/presenterm/config.yaml @@ -0,0 +1,106 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/mfontanini/presenterm/master/config-file-schema.json +defaults: + # override the terminal font size when in windows or when using sixel. + terminal_font_size: 16 + + # the theme to use by default in every presentation unless overridden. + theme: dark + + # the image protocol to use. + image_protocol: kitty-local + +typst: + # the pixels per inch when rendering latex/typst formulas. + ppi: 300 + +mermaid: + # the scale parameter passed to the mermaid CLI (mmdc). + scale: 1 + +options: + # whether slides are automatically terminated when a slide title is found. + implicit_slide_ends: false + + # the prefix to use for commands. + command_prefix: "" + + # show all lists incrementally, by implicitly adding pauses in between elements. + incremental_lists: false + + # this option tells presenterm you don't care about extra parameters in + # presentation's front matter. This can be useful if you're trying to load a + # presentation made for another tool + strict_front_matter_parsing: true + + # whether to treat a thematic break as a slide end. + end_slide_shorthand: false + +snippet: + exec: + # enable code snippet execution. Use at your own risk! + enable: true + + exec_replace: + # enable code snippet automatic execution + replacing the snippet with its output. Use at your own risk! + enable: true + + render: + # the number of threads to use when rendering `+render` code snippets. + threads: 2 + +speaker_notes: + # The endpoint to listen for speaker note events. + listen_address: "127.0.0.1:59418" + + # The endpoint to publish speaker note events. + publish_address: "127.0.0.1:59418" + + # Whether to always publish speaker notes even when `--publish-speaker-notes` is not set. + always_publish: false + +bindings: + # the keys that cause the presentation to move forwards. + next: ["l", "j", "", "", "", " "] + + # the keys that cause the presentation to move forwards fast. + next_fast: ["n"] + + # the keys that cause the presentation to move backwards. + previous: ["h", "k", "", "", ""] + + # the keys that cause the presentation to move backwards fast + previous_fast: ["p"] + + # the key binding to jump to the first slide. + first_slide: ["gg"] + + # the key binding to jump to the last slide. + last_slide: ["G"] + + # the key binding to jump to a specific slide. + go_to_slide: ["G"] + + # the key binding to execute a piece of shell code. + execute_code: [""] + + # the key binding to reload the presentation. + reload: [""] + + # the key binding to toggle the slide index modal. + toggle_slide_index: [""] + + # the key binding to toggle the key bindings modal. + toggle_bindings: ["?"] + + # the key binding to close the currently open modal. + close_modal: [""] + + # the key binding to close the application. + exit: ["", "q"] + + # the key binding to suspend the application. + suspend: [""] + + # the key binding to skip all pauses in the current slide. + skip_pauses: ["s"] diff --git a/common/.config/pyradio/README.md b/common/.config/pyradio/README.md new file mode 100644 index 0000000..e0e06ec --- /dev/null +++ b/common/.config/pyradio/README.md @@ -0,0 +1,45 @@ +# PyRadio Setup + +This repository contains a custom configuration and station list for [PyRadio](https://github.com/coderholic/pyradio), a terminal-based radio player. + +## Kitty Music Session + +To get a full-screen, immersive music player experience, you can use a custom [Kitty terminal session](https://sw.kovidgoyal.net/kitty/sessions/). + +The configuration file `music.conf` (found in your kitty config directory) sets up a layout that includes: +- A `pyradio` terminal. +- A `cava` terminal for audio visualization. +- A `pipes-rs` terminal for some visual flair. + +### Launching the Session + +Use keybind `$mainMod + R` or run the following command to launch the music session: + +```bash +kitty --session ~/.config/kitty/music.conf +``` + +## Setup + +1. **Install PyRadio**: Ensure you have `pyradio` installed on your system. +2. **Configuration**: + * Copy the `config` file to your PyRadio configuration directory (usually `~/.config/pyradio/config`). + * Copy the `stations.csv` file to your PyRadio directory. +3. **Using Custom Stations**: + * PyRadio can be configured to use a custom stations file. Ensure your `config` points to the `stations.csv` provided here if you want to use this specific collection of stations. + +## Station Sources + +The stations in `stations.csv` are curated from various sources, often utilizing the [Radio Browser](https://www.radio-browser.info/) API. + +### About Radio Browser +[Radio Browser](https://www.radio-browser.info/) is a community-driven database of internet radio stations. It is a free, open-source project that provides a massive API for accessing thousands of radio streams worldwide. It's a great resource for discovering new music and finding niche genres that might not be available on mainstream services. + +## Alternatives to PyRadio + +If you are looking for other ways to listen to internet radio in the terminal or via other interfaces, consider these alternatives: + +* **[Web Radio Clients](https://www.radio-browser.info/users)**: Many users of Radio Browser utilize various web-based players or custom scripts that interface directly with the Radio Browser API. +* **[MPV (Command Line)](https://mpv.io/)**: Terminal player pyradio uses as a backend `mpv ` +* **[VLC (Command Line)](https://www.videolan.org/vlc/)**: You can play many radio streams directly from the terminal using `cvlc --play-and-exit `. + diff --git a/.config/pyradio/config b/common/.config/pyradio/config similarity index 100% rename from .config/pyradio/config rename to common/.config/pyradio/config diff --git a/.config/pyradio/stations.csv b/common/.config/pyradio/stations.csv similarity index 88% rename from .config/pyradio/stations.csv rename to common/.config/pyradio/stations.csv index f711beb..8d5b9bd 100644 --- a/.config/pyradio/stations.csv +++ b/common/.config/pyradio/stations.csv @@ -1,6 +1,4 @@ Ambient Psychill [1.FM],http://strm112.1.fm/ambientpsy_mobile_mp3,, -Goa Psychill [SomaFM],http://ice3.somafm.com/suburbsofgoa-128-mp3,, -Chilli Zet,https://ch.cdn.eurozet.pl/chi-net.mp3,, Chilltrax,http://server1.chilltrax.com:9000/,, Chillout [psyradio*fm],http://streamer.psyradio.org:8020/;listen.mp3,, Chillout [Psyndora],http://cast.magicstreams.gr:9125/stream,, @@ -9,16 +7,11 @@ Groove Salad Classic (Early 2000s Ambient),https://somafm.com/gsclassic.pls,, Ambient and Experimental [n5MD],https://somafm.com/n5md.pls,, Suomisaundi [Caprice],http://79.120.77.11:8004/suomisaundi,, Vaporwaves [SomaFM],https://somafm.com/vaporwaves.pls,, -Cliq Hop [SomaFM],http://somafm.com/startstream=cliqhop.pls,, -Lounge (Illinois Street) [SomaFM],http://somafm.com/illstreet.pls,, -Space [SomaFM],http://ice.somafm.com/spacestation,, Space Music [Synphaera],https://somafm.com/synphaera.pls,, -Deep Space [SomaFM], http://ice.somafm.com/deepspaceone,, Downtempo (Secret Agent) [SomaFM],http://somafm.com/secretagent.pls,, Dub Step (Beyond) [SomaFM],http://somafm.com/dubstep.pls,, -Dub Techno [Schizoid], http://94.130.113.214:8000/dubtechno,, +Dub Techno [Schizoid], http://94.130.113.214:8000/dubtechno,nocon, Dubplate.fm,https://sc2.dubplate.fm/radio/8030/dnb/uhifi,, -MansionNET,https://radio.inthemansion.com/listen/mansionnet/radio.mp3,, Liquid funk [Caprice], http://79.111.119.111:9115/,, Liquid funk,https://radiorecord.hostingradio.ru/liquidfunk96.aacp,, Liqui Radio,https://stream-162.zeno.fm/ug59eq099yzuv?zs=zaqVusVSQuWMN3O5gnh8xA,, diff --git a/common/.config/tmux/scripts/cpu.sh b/common/.config/tmux/scripts/cpu.sh new file mode 100755 index 0000000..fecbd3e --- /dev/null +++ b/common/.config/tmux/scripts/cpu.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# CPU utilisation (%) averaged over the interval since the PREVIOUS status refresh: +# /proc/stat totals persist in a state file between runs (same pattern as net.sh), +# so the value covers the whole status-interval. (The old version slept 0.3s and +# sampled just that window — a 6% duty cycle that aliased bursty loads.) No sleep, +# ~5ms. First run (no state yet) shows the since-boot average. Glyph = Waybar's +# cpu module icon (U+F035B). +printf '\U000f035b ' +rt="${XDG_RUNTIME_DIR:-/tmp}" +st="$rt/tmux_cpu_stat" # previous totals: tot idle + +read -r _ u n s i w rest < /proc/stat +idle=$((i + w)); tot=$((u + n + s + i + w)); for v in $rest; do tot=$((tot + v)); done + +p_tot=0; p_idle=0 +[ -r "$st" ] && read -r p_tot p_idle < "$st" +[ "$p_tot" -gt "$tot" ] && { p_tot=0; p_idle=0; } # stale state from a previous boot +printf '%s %s\n' "$tot" "$idle" > "$st" + +dt=$((tot - p_tot)); di=$((idle - p_idle)) +if [ "$dt" -le 0 ]; then printf -- '--%%'; else printf '%d%%' $(((100 * (dt - di) + dt / 2) / dt)); fi diff --git a/common/.config/tmux/scripts/mem.sh b/common/.config/tmux/scripts/mem.sh new file mode 100755 index 0000000..a702820 --- /dev/null +++ b/common/.config/tmux/scripts/mem.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# RAM utilisation (%) — used = MemTotal - MemAvailable, from /proc/meminfo. +# Called from tmux status-right via #(); cached per status-interval. +printf '\U000f0f86 ' # Waybar's memory glyph (U+F0F86), then the percentage +awk '/^MemTotal:/{t=$2} /^MemAvailable:/{a=$2} END{if(t>0)printf "%d%%",(t-a)*100/t; else printf "--%%"}' /proc/meminfo diff --git a/common/.config/tmux/scripts/net.sh b/common/.config/tmux/scripts/net.sh new file mode 100755 index 0000000..2b7a833 --- /dev/null +++ b/common/.config/tmux/scripts/net.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# Network throughput from /proc/net/dev (sum of all non-lo interfaces), averaged +# over the interval between status refreshes — no blocking sleep. +# +# Usage: net.sh dl -> " 1.2M" (download icon U+F019 + rate) +# net.sh ul -> " 45K" (upload icon U+F093 + rate) +# +# tmux calls both from status-right. A short (<3s) cache lets the two calls of a +# single refresh share one computation, so the up/down figures are consistent and +# only one of them re-samples the counters per refresh. +which=${1:-dl} +rt="${XDG_RUNTIME_DIR:-/tmp}" +ctr="$rt/tmux_net_ctr" # previous counters: rx tx epoch.ns +cache="$rt/tmux_net_cache" # computed rates: dl_rate ul_rate epoch_s + +emit() { # emit + printf "$1 %s" "$2" +} + +# Reuse a fresh cache so dl and ul agree and we only sample once per refresh. +fresh_emit() { # print from the cache and exit — if the cache is fresh enough + [ -r "$cache" ] || return 1 + read -r cdl cul cts < "$cache" + { [ -n "$cts" ] && [ "$(( $(date +%s) - cts ))" -lt 3 ]; } || return 1 + if [ "$which" = ul ]; then emit '\U0000f093' "$cul"; else emit '\U0000f019' "$cdl"; fi + exit 0 +} + +fresh_emit + +# tmux launches the dl and ul jobs CONCURRENTLY each refresh, so without a lock +# both miss the stale cache and compute independently — worst case the loser reads +# the counters milliseconds after the winner rewrote them and shows a ~0 rate. +# Serialize: the loser waits ~30ms on the lock, then hits the winner's fresh cache. +exec 9>"$rt/tmux_net_lock" +flock 9 +fresh_emit + +now=$(date +%s.%N) +read -r rx tx < <(awk 'NR>2{gsub(/:/," "); if($1!="lo"){r+=$2; t+=$10}} END{print r+0, t+0}' /proc/net/dev) + +drx=0; dtx=0; dt=1 +if [ -r "$ctr" ]; then + read -r prx ptx pnow < "$ctr" + dt=$(awk -v a="$now" -v b="$pnow" 'BEGIN{d=a-b; print (d>0.05)?d:1}') + drx=$(( rx - prx )); dtx=$(( tx - ptx )) + [ "$drx" -lt 0 ] && drx=0 + [ "$dtx" -lt 0 ] && dtx=0 +fi +printf '%s %s %s\n' "$rx" "$tx" "$now" > "$ctr" + +human() { awk -v b="$1" 'BEGIN{ split("B K M G T", u, " "); i=1; + while (b >= 1024 && i < 5) { b /= 1024; i++ } + if (i == 1) printf "%d%s", b, u[i]; else printf "%.1f%s", b, u[i] }'; } + +dl=$(human "$(awk -v x="$drx" -v d="$dt" 'BEGIN{printf "%d", x/d}')") +ul=$(human "$(awk -v x="$dtx" -v d="$dt" 'BEGIN{printf "%d", x/d}')") +printf '%s %s %s\n' "$dl" "$ul" "$(date +%s)" > "$cache" + +if [ "$which" = ul ]; then emit '\U0000f093' "$ul"; else emit '\U0000f019' "$dl"; fi diff --git a/common/.config/tmux/scripts/temp.sh b/common/.config/tmux/scripts/temp.sh new file mode 100755 index 0000000..fc190ce --- /dev/null +++ b/common/.config/tmux/scripts/temp.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# CPU temperature (°C) for the tmux status bar (thermometer glyph U+F050F). +# Sensor hunt, most→least specific: a CPU-ish thermal zone (x86_pkg_temp/…), +# else a CPU hwmon chip (coretemp/k10temp/…), else the hottest remaining zone +# (acpitz can be a bogus constant on desktops, hence last). Prints NOTHING when +# no sensor exists (VMs/containers) so the segment simply disappears — which is +# also why the trailing spacing lives in here, not in status-right. +cpu_zone=""; any_zone=""; hw="" + +for d in /sys/class/thermal/thermal_zone*/; do + [ -r "${d}temp" ] && [ -r "${d}type" ] || continue + t=$(<"${d}temp") + case $t in ''|*[!0-9]*) continue ;; esac + ty=$(<"${d}type"); ty=${ty,,} + case $ty in *x86_pkg_temp*|*coretemp*|*k10temp*|*tctl*|*cpu*) + if [ -z "$cpu_zone" ] || [ "$t" -gt "$cpu_zone" ]; then cpu_zone=$t; fi ;; + esac + if [ -z "$any_zone" ] || [ "$t" -gt "$any_zone" ]; then any_zone=$t; fi +done + +if [ -z "$cpu_zone" ]; then + for d in /sys/class/hwmon/hwmon*/; do + [ -r "${d}name" ] || continue + case $(<"${d}name") in coretemp|k10temp|zenpower|cpu_thermal) + for f in "${d}"temp*_input; do + [ -r "$f" ] || continue + t=$(<"$f") + case $t in ''|*[!0-9]*) continue ;; esac + if [ -z "$hw" ] || [ "$t" -gt "$hw" ]; then hw=$t; fi + done ;; + esac + done +fi + +t=${cpu_zone:-${hw:-$any_zone}} +[ -n "$t" ] || exit 0 +printf '\U000f050f %d° ' $(( (t + 500) / 1000 )) diff --git a/common/.config/tmux/tmux.conf b/common/.config/tmux/tmux.conf new file mode 100644 index 0000000..604b107 --- /dev/null +++ b/common/.config/tmux/tmux.conf @@ -0,0 +1,78 @@ +# Forward modified keys (Shift/Ctrl/Alt+Enter) to apps like pi-coding-agent. +# Without this, tmux collapses them to a plain Enter. Requires tmux 3.5+ for csi-u. +set -g extended-keys on +set -g extended-keys-format csi-u + +# Number windows (and panes) from 1 instead of 0 — easier to reach on the +# keyboard. renumber-windows keeps them gap-free when a window is closed. +set -g base-index 1 +setw -g pane-base-index 1 +set -g renumber-windows on + +# --- Session persistence (survives reboot) --------------------------------- +# resurrect: save/restore sessions, windows, panes, layout and working dirs +# (prefix + Ctrl-s to save, prefix + Ctrl-r to restore). +# continuum: auto-saves every 15 min and auto-restores when the tmux server +# starts, so sessions come back after a reboot. Both via tpm. +set -g @plugin 'tmux-plugins/tpm' +set -g @plugin 'tmux-plugins/tmux-resurrect' +set -g @plugin 'tmux-plugins/tmux-continuum' + +# Also capture pane scrollback and restore running programs (nvim, …). +# nvim 'session' strategy restores via Session.vim when present, else plain nvim. +set -g @resurrect-capture-pane-contents 'on' +set -g @resurrect-strategy-nvim 'session' + +# Auto-restore the last saved environment on tmux server start. +set -g @continuum-restore 'on' + +# Auto-start a detached tmux server at login: continuum writes and enables a +# systemd --user unit (~/.config/systemd/user/tmux.service). With the restore +# option above, sessions are already back and waiting after a reboot+login. +set -g @continuum-boot 'on' + +# --- Status bar (Catppuccin Mocha, flat) ----------------------------------- +# Palette: bg #1e1e2e · surface #313244 · divider #45475a · muted #6c7086/#7f849c +# text #cdd6f4 · subtext #a6adc8 · accent(mauve) #cba6f7 +set -g status on +set -g status-position bottom # (also tmux's default) +set -g status-interval 5 # refresh clock + CPU every 5s +set -g status-justify left +set -g status-style "bg=default,fg=#cdd6f4" # transparent: no bar background +set -g status-left-length 40 +set -g status-right-length 100 + +# Left: session name (mauve/bold) + thin divider. +set -g status-left " #[fg=#cba6f7,bold]#S #[fg=#45475a]│ " + +# Middle: flat window list — inactive muted, active mauve/bold, 2-space gaps. +set -g window-status-separator " " +setw -g window-status-format "#[fg=#7f849c]#I #[fg=#6c7086]#W" +setw -g window-status-current-format "#[fg=#cba6f7,bold]#I #[fg=#cdd6f4,bold]#W" +setw -g window-status-activity-style "fg=#f9e2af" + +# Right: net dl/ul · cpu · mem │ date │ clock. continuum prepends its invisible +# save hook here (this block sits above the tpm `run` line, so the hook survives +# every reload). Metrics are scripts/*.sh, symlinked into ~/.config/tmux/scripts/. +set -g status-right "#[fg=#a6e3a1]#(~/.config/tmux/scripts/net.sh dl) #[fg=#f9e2af]#(~/.config/tmux/scripts/net.sh ul) #[fg=#89b4fa]#(~/.config/tmux/scripts/cpu.sh) #[fg=#94e2d5]#(~/.config/tmux/scripts/mem.sh) #[fg=#fab387]#(~/.config/tmux/scripts/temp.sh)#[fg=#45475a]│ #[fg=#a6adc8]%a %b %-d #[fg=#45475a]│ #[fg=#cba6f7,bold]%H:%M " + +# Two rows: [0] drawn top-border rule · [1] the bar content. The rule doubles as +# the separator from the pane content above; tmux status height is whole rows, so +# one row above the content is the smallest gap that still shows a border. +# Full-width run of ─ (yes|head|paste avoids %, backslashes and nested parens so +# tmux's strftime/quote/#() parsing leave it alone); tmux truncates it to width. +# status-format[1] is tmux 3.7's default content template (status-left/windows/right). +set -g status 2 +set -g status-format[0] "#[fg=#6c7086,bg=default]#(yes ─ | head -n 400 | paste -sd '')" +set -g status-format[1] "#[align=left range=left #{E:status-left-style}]#[push-default]#{T;=/#{status-left-length}:status-left}#[pop-default]#[norange default]#[list=on align=#{status-justify}]#[list=left-marker]<#[list=right-marker]>#[list=on]#{W:#[range=window|#{window_index} #{E:window-status-style}#{?#{&&:#{window_last_flag},#{!=:#{E:window-status-last-style},default}}, #{E:window-status-last-style},}#{?#{&&:#{window_bell_flag},#{!=:#{E:window-status-bell-style},default}}, #{E:window-status-bell-style},#{?#{&&:#{||:#{window_activity_flag},#{window_silence_flag}},#{!=:#{E:window-status-activity-style},default}}, #{E:window-status-activity-style},}}]#[push-default]#{T:window-status-format}#[pop-default]#[norange default]#{?loop_last_flag,,#{E:window-status-separator}},#[range=window|#{window_index} list=focus #{?#{!=:#{E:window-status-current-style},default},#{E:window-status-current-style},#{E:window-status-style}}#{?#{&&:#{window_last_flag},#{!=:#{E:window-status-last-style},default}}, #{E:window-status-last-style},}#{?#{&&:#{window_bell_flag},#{!=:#{E:window-status-bell-style},default}}, #{E:window-status-bell-style},#{?#{&&:#{||:#{window_activity_flag},#{window_silence_flag}},#{!=:#{E:window-status-activity-style},default}}, #{E:window-status-activity-style},}}]#[push-default]#{T:window-status-current-format}#[pop-default]#[norange list=on default]#{?loop_last_flag,,#{E:window-status-separator}}}#[nolist align=right range=right #{E:status-right-style}]#[push-default]#{T;=/#{status-right-length}:status-right}#[pop-default]#[norange default]" + +# Round out the theme so no default green leaks into borders/messages/modes. +set -g pane-border-style "fg=#313244" +set -g pane-active-border-style "fg=#cba6f7" +set -g message-style "bg=#313244,fg=#cdd6f4" +set -g message-command-style "bg=#313244,fg=#cdd6f4" +set -g mode-style "bg=#45475a,fg=#cdd6f4" +set -g clock-mode-colour "#cba6f7" + +# Initialize tpm — KEEP THIS LINE AT THE VERY BOTTOM of tmux.conf. +run '~/.config/tmux/plugins/tpm/tpm' diff --git a/.config/tuios/config.toml b/common/.config/tuios/config.toml similarity index 99% rename from .config/tuios/config.toml rename to common/.config/tuios/config.toml index b3630c7..e875091 100644 --- a/.config/tuios/config.toml +++ b/common/.config/tuios/config.toml @@ -1,7 +1,7 @@ # TUIOS Configuration File # This file allows you to customize appearance and keybindings # -# Configuration location: /home/coja/.config/tuios/config.toml +# Configuration location: ~/.config/tuios/config.toml # Documentation: https://github.com/Gaurav-Gosain/tuios # For keybindings documentation, run: tuios keybinds list diff --git a/common/.inputrc b/common/.inputrc new file mode 100644 index 0000000..a76b643 --- /dev/null +++ b/common/.inputrc @@ -0,0 +1,47 @@ +# Vi mode +set editing-mode vi +set show-mode-in-prompt on +set vi-ins-mode-string "\1\e[32m\2[I] \1\e[0m\2" +set vi-cmd-mode-string "\1\e[31m\2[N] \1\e[0m\2" + +# exclude .files +set match-hidden-files off + +set show-all-if-ambiguous on +set show-all-if-unmodified on +set colored-stats on +set colored-completion-prefix on +set mark-symlinked-directories on +set completion-ignore-case on + +# Ignore duplicates in history where supported +set skip-completed-text on + +# ----- insert mode: keep the emacs-style defaults ----- +# (no \e-prefixed binds here — they would delay ESC switching to normal mode) +set keymap vi-insert +"\C-a": beginning-of-line +"\C-e": end-of-line +"\C-b": backward-char +"\C-f": forward-char +"\C-p": previous-history +"\C-n": next-history +"\C-r": reverse-search-history +"\C-k": kill-line +"\C-u": unix-line-discard +"\C-w": unix-word-rubout + +# custom binds +"\C- ": shell-expand-line +"\C-x": glob-expand-word +"\C-g": "git diff\n" +"\C-y": "| ccze\n" +"\C-o": "\C-u yazi\n\C-u" +"\C-l": "\C-u clear -x && ls\n" + +# ----- normal (command) mode ----- +set keymap vi-command +"\C-l": clear-screen +"\C-a": beginning-of-line +"\C-e": end-of-line +"\C-r": reverse-search-history diff --git a/common/.pi/CLAUDE.md b/common/.pi/CLAUDE.md new file mode 100644 index 0000000..4786cf4 --- /dev/null +++ b/common/.pi/CLAUDE.md @@ -0,0 +1,191 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +This is **not application source** — it's the on-disk configuration tree for `pi`, the TUI coding agent from [earendil-works/pi](https://github.com/earendil-works/pi) (binary at `/usr/bin/pi`). It lives inside the user's dots repo (`~/.dots`) and is surfaced into `$HOME` via GNU Stow's per-file symlinks (`--no-folding`), e.g. `~/.pi/agent/extensions/notify.ts -> ../../../.dots/common/.pi/agent/extensions/notify.ts`. **Editing** an existing file edits the live config directly — no deploy step. But **adding a new file** (e.g. a new extension) is not surfaced until you re-stow: run `./install.sh` (or `stow --no-folding -R -d ~/.dots -t ~ common`) to create its symlink, on each machine. + +Because it's JSON + markdown config, there are no tests/lint/build. The only useful "validation" is JSON well-formedness, e.g. `jq . settings.json` or `python -m json.tool < settings.json`. + +## Config directory — single source of truth + +**All live config is `.pi/agent/`** (surfaced as `~/.pi/agent`). The pi coding agent reads `$PI_CODING_AGENT_DIR`, which defaults to `~/.pi/agent` and is not overridden here; per pi's docs, settings/auth/trust/sessions/extensions/AGENTS.md all resolve under that one directory. Nothing reads `~/.pi/*` at the top level or `~/.config/pi`. + +> History: there used to be three drifted copies of this config (`.pi/*` top-level, `.pi/agent/`, and `~/.config/pi/`). The two unread mirrors were deleted and the dangling `~/.config/pi` stow symlink removed, leaving `.pi/agent/` as the only copy. Don't reintroduce mirrors — edit `.pi/agent/` directly. + +## Layout of `.pi/agent/` (the live config) + +- `settings.json` — runtime settings: `defaultProvider`/`defaultModel`/`defaultThinkingLevel`, `theme`, `enabledModels` (glob allowlist for the Ctrl+P model picker), `compaction` (auto-summarize long sessions), `retry`, HTTP timeouts, `npmCommand` (pinned to `fnm exec --using=22 -- npm`), `packages` (installed extension packages, currently `npm:pi-vim`) and their config blocks (`piVim` — see Vim input editor). +- `models.json` — the provider + model catalog (see below). +- `auth.json` — maps each provider to its credential. Values are **references, not secrets** (`$DUSKADIY_API_KEY`, `no-key-required`); pi resolves `$VAR` from the environment. **Gitignored** — the tracked copy is `auth.json.example`; a new host needs `cp auth.json.example auth.json` before pi can resolve a provider (stow links whatever is present, tracked or not, so the live file keeps working here). +- `prompts/*.md` — custom slash commands (pi "prompt templates"). `/commit` and `/review` are defined here. Format: YAML frontmatter (`description`, `argument-hint`) + body, with `${1:-default}` positional-arg substitution. +- `themes/*.json` — color themes following the schema at `earendil-works/pi .../theme/theme-schema.json`: a `vars` palette referenced by semantic `colors` keys, plus an `export` block for HTML session export. +- `sessions/` — runtime session transcripts (`.jsonl`), **gitignored**. One subdir per project cwd; each line is an event (`session`, `model_change`, `message`, …). +- `extensions/*/index.ts` — auto-discovered TypeScript extensions, loaded via [jiti](https://github.com/unjs/jiti) (no build step). `import type` from `@earendil-works/*` is erased at runtime; value imports resolve against pi's own bundled packages, so a vendored extension needs no `node_modules`. Editor TS "cannot find module" warnings on these imports are therefore expected noise. +- `npm/` — extension **packages** installed by `pi install` (currently `pi-vim`), **gitignored**; `settings.json > packages` is the tracked source of truth. On a fresh host run `pi install npm:pi-vim` once — plus pi-vim's manual peer-dep install (see Vim input editor), which `pi install` does not do — then `pi update npm:pi-vim` to bump. A package needs no re-stow — unlike a new file under `extensions/`. +- `keybindings.json` — key remaps. A user entry **replaces** the default keys for that action (it does not merge). Action ids and defaults are listed in `/opt/pi-coding-agent/docs/keybindings.md`. + +## Plan mode (Shift+Tab) + +`extensions/plan-mode/` is pi's bundled plan-mode example (pi has no built-in plan mode), **vendored here and rebound from its upstream `Ctrl+Alt+P` to Shift+Tab**. In plan mode it disables `edit`/`write` and restricts `bash` to a read-only allowlist (footer shows `⏸ plan`); `/plan` also toggles it, `--plan` starts in it. The rebind is two coupled edits — keep them together: + +- `extensions/plan-mode/index.ts` (~line 157): `pi.registerShortcut("shift+tab", …)`, and the upstream `import { Key } … }` is removed. This file is a **local fork** of `/opt/pi-coding-agent/examples/extensions/plan-mode/`; on a pi upgrade, re-pull from there and re-apply these two edits. +- `keybindings.json` moves `app.thinking.cycle` off Shift+Tab to `ctrl+shift+t`, so the toggle fires deterministically (otherwise it collides with the built-in thinking-cycle binding). + +After editing an extension or `keybindings.json`, run `/reload` in pi to apply without restarting. + +## Status bar (custom footer) + +`extensions/statusbar.ts` replaces pi's default footer via `ctx.ui.setFooter()`. It +mirrors the Claude Code status line (`~/.config/claude/statusline.py`) but swaps the +emoji for **Nerd Font (Material Design) glyphs** — the same family already used in +tmux (waybar cpu/mem/net glyphs) and nvim — so it renders natively in kitty +(CaskaydiaCove Nerd Font Mono). Colors come from the active theme, not raw ANSI. + +Icon legend (each glyph echoes the Claude emoji it stands in for): + +| Glyph | Codepoint | Segment | ~ Claude | +|---|---|---|---| +| `󰉋` | `U+F024B` nf-md-folder | cwd (`~`-collapsed) | 📁 | +| `󰘬` | `U+F062C` nf-md-source_branch | git branch | 🌿 | +| `󰚩` | `U+F06A9` nf-md-robot | model id + `· thinking` | 🤖 | +| `󰈚` | `U+F021A` nf-md-text_box | context-window % used (+ tokens) | 📝 | +| `󰓅` | `U+F04C5` nf-md-speedometer | last response's decode throughput (t/s) | — | +| `󰀪` | `U+F002A` nf-md-alert_outline | context-budget warning widget (above editor, ≥80%) | — | +| `↑ ↓` | — | session input / output tokens | 💰 | + +Two extras beyond the footer line: a themed "breathing" pulse **working-indicator** +(the streaming spinner), and a **context-budget warning widget** above the editor +that appears once the window is ≥80% full (`warning`, then `error` ≥90%) nudging a +`/compact`. Both are reset when the footer is toggled off. + +Segments render left→right and truncate at the terminal edge. git only shows inside +a git repo; context, t/s and tokens only appear after the first response (t/s is +`usage.output ÷ (message_end − first streamed token)`, so prompt-eval time is +excluded). Extension statuses (e.g. plan-mode's `⏸ plan`) are preserved at the far +left. The Claude bar's system row (RAM/CPU/temp/disk) is intentionally omitted — +that data isn't in the footer API, and the tmux bar below pi already shows +cpu/mem/net. `/statusbar` toggles it off (restores the built-in footer) and back on; +`/reload` picks up edits to this file (a *new* extension needs a re-stow first — see +top). + +## Vim input editor + +The modal (vim-like) input editor is the npm package +**[`pi-vim`](https://github.com/lajarre/pi-vim)** (pinned by `settings.json > packages`, +installed into the gitignored `.pi/agent/npm/`, configured under `settings.json > piVim`; +v0.14.1 at adoption). It replaced the hand-rolled local fork on 2026-08-05; the fork was parked +as `extensions/vim-editor.ts.disabled` and **deleted 2026-08-09** once the package had proven +itself in daily use. `git show c6566af^:common/.pi/agent/extensions/vim-editor.ts` brings it back +if it is ever wanted. +**That swap is the whole point: no more re-pull-and-re-apply on every pi upgrade** — use +`pi update npm:pi-vim`, and `pi remove npm:pi-vim` to back out. + +⚠️ **This pi build needs pi-vim's peer dep installed by hand.** `pi` here is the AUR +`pi-coding-agent`: a Bun-compiled ELF at `/opt/pi-coding-agent/pi` with the +`@earendil-works/*` packages *embedded* as virtual modules, not present on disk +(`/opt/pi-coding-agent/node_modules/` holds only `@mariozechner`). pi-vim's +`clipboard-mirror.ts` calls `import.meta.resolve("@earendil-works/pi-coding-agent")` at +**module top level** — it bakes that URL into the source of a spawned clipboard helper — +so after a bare `pi install npm:pi-vim` the resolve throws and the whole extension fails +to load: `Cannot find module '@earendil-works/pi-coding-agent'`. No setting dodges it; +`clipboardMirror: "never"` can't, because the call runs at import time. Fix, once per host: + +```fish +cd ~/.pi/agent/npm +fnm exec --using=22 -- npm install --save-exact @earendil-works/pi-coding-agent@0.83.0 # match `pi --version` +``` + +pi's own `pi install` deliberately does *not* pull peers (it would duplicate the runtime), +so this is manual, costs ~170 MB inside the gitignored `npm/`, and wants re-pinning after a +pi upgrade (hence `--save-exact`: npm's default `^` range would let a later `npm install` +drift the peer off `pi --version` on its own). The static +`CustomEditor`/`SettingsManager`/`matchesKey` imports still bind to pi's embedded copy — +verified in the TUI — so the on-disk copy stays inert apart from the spawned helper. Upstream has no issue filed for this (checked 2026-08-05); the proper fix is +making that resolve lazy. `@burneikis/pi-vim` and `pi-vimmode` use no `import.meta.resolve` +and need no peer install, if this ever gets annoying. + +Five modes — INSERT, NORMAL, VISUAL, V-LINE, EX. The active one renders as a word label +(` NORMAL `) at the **bottom-right** of the editor border, doubling as a pending-command +display: `3d2w`, `ci"`, `25gg` appear as you type them (` NORMAL 3d2w_ `). That's a +**position change from the fork**, which put a single `[N]` tag bottom-*left*. + +- **motions** — `hjkl` · `w`/`b`/`e` + `W`/`B`/`E` · `0`/`^`/`$` · `{`/`}` paragraph · + `gg`/`G` with counts (`25gg`) · `f`/`F`/`t`/`T` + `;` repeat · `%` matching pair +- **operators** — `d`/`c`/`y` + any motion · `dd`/`cc`/`yy`/`Y` · `x`/`X`/`s`/`S`/`C`/`D` · + `o`/`O` open line · `r` replace char · `J`/`gJ` join · `p`/`P` put +- **text objects** — `iw`/`aw` · `iW`/`aW` · `i"`/`a"` · `i(`/`a(` · `i[`/`a[` · `i{`/`a{` +- **undo/repeat** — `u` and `Ctrl+R`, scoped to vim changes · `.` repeats the last edit +- **EX** — `:` dispatches real pi commands (`:model`, `:tree`) and shells out via + `:!git status`. pi exposes no command-dispatch API, so it re-submits the line as if + typed, snapshotting and restoring the prompt around it. +- cursor shape follows the mode (bar in INSERT, block elsewhere) via DECSCUSR. + +Upstream implements no search (`/`, `?`, `n`, `N`), no macros (`q`/`@`) and no visual-block. +The fork had none of those either, so nothing regressed; `pi-vimmode` has them but is +rougher (5★/18 open issues vs 75★/2 at the time of choosing). + +### Config (`settings.json > piVim`) + +- `modeColors` — theme tokens, **ported from the fork**: `insert: success` (green), + `normal: accent` (mauve), `visual: warning`. ⚠️ the fork's header comment claimed visual + was "peach", but `warning` resolves to **yellow** `#f9e2af` in catppuccin-mocha — the port + keeps what actually rendered and hands the *peach* (`bashMode`) to the new EX mode. +- `borderSync` all `host`, `labelSync` all `mode` — also the upstream defaults, but pinned + explicitly so an upstream default change can't start repainting the input border. Matches + the fork: only the label is tinted, the border is left to the host. +- `clipboardMirror: "yank"` — only an explicit `y` reaches the **OS** clipboard. Upstream's + default `"all"` mirrors deletes too, so every `dd`/`x` would clobber the Wayland clipboard; + `"never"` is exact fork parity (pi's kill-ring only). Writes go through + `@mariozechner/clipboard` in a spawned helper, not a direct `wl-copy`. +- `exCommand.piDispatch: true` — the `:` bridge above. `copyInputToClipboard: false`: it + copies the composed prompt out, an exfiltration path, so upstream only honors it from the + user-global file (never project settings) — leave it off. +- `modeChange` (unset) — would run a shell command on every INSERT/NORMAL transition. + +`/vim` still toggles the editor, now from **`extensions/vim-toggle.ts`** (pi-vim registers no +commands of its own): off drops the editor component for the rest of the session, on calls +`ctx.reload()` — the same flow as `/reload` — which re-runs discovery and reinstalls it. +No clash with `statusbar.ts`: pi-vim draws its label inside its own editor component, not +through `setFooter()`. + +## Compaction tuning (small local windows) + +`settings.json > compaction` is set for the small local context windows (24k–128k; +several presets are only 24k). `reserveTokens: 6144` (headroom for the +response; auto-compaction fires at `contextTokens > contextWindow − reserveTokens`) +and `keepRecentTokens: 6000` (kept verbatim, not summarized) — both well below pi's +16384/20000 defaults so short windows aren't dominated by the reserve or thrash into +repeated compaction. These are **global** (pi has no per-model compaction): on the +128k model you could raise `keepRecentTokens` for richer retained context; if you +see responses truncate near a full window, raise `reserveTokens` toward the models' +`maxTokens` (8192). All model `contextWindow`s are verified to match the server +`ctx-size` in `fl/.config/llamacpp/config.ini`. + +## Providers and models + +Two OpenAI-compatible providers are configured, both serving the same catalog of small local/self-hosted models (Qwen3-Coder-30B, Gemma 4, GLM-4.7-Flash, etc.). All have `cost: 0`: + +- **`duskadiy`** — remote, `https://llm.duskadiy.com/api/v1`, key `$DUSKADIY_API_KEY`. +- **`localcpp`** — LAN llama.cpp server at `http://192.168.0.204:11343/v1`, no key. + +⚠️ **`duskadiy` is parked out of the model picker** (2026-08-05): `settings.json > enabledModels` lists only `localcpp/*`. pi resolves `auth.json`'s `$DUSKADIY_API_KEY` from the environment, and this host has no `~/.config/fish/conf.d/secrets.fish`, so the credential resolves empty, pi drops the whole provider, and the `duskadiy/*` glob then matched nothing — printing `Warning: No models match pattern "duskadiy/*"` on every single start. Its box is down independently of the missing key: `llm.duskadiy.com` still resolves (`24.135.113.16`) but TCP 443 times out, as does the apex. Provider, catalog and auth entry are all left **intact** — re-add `"duskadiy/*"` to `enabledModels` once the key is back on the host and the box answers. + +`defaultProvider` stays `duskadiy` on purpose. With the provider dropped pi falls back to the same model id under `localcpp` (the two catalogs are mirrors), so startup resolves fine today — and when duskadiy returns it is the provider that works **off**-LAN, unlike `localcpp`'s `192.168.0.204`. Don't "simplify" it to `localcpp` without weighing that. + +When adding/editing a model, keep `id` exactly matching the server's model id (the section name in `fl/.config/llamacpp/config.ini`), and set `contextWindow` to match the `ctx-size` the model is actually loaded with server-side. + +## Secrets + +`$DUSKADIY_API_KEY` is the only real secret. It is defined in `.config/fish/conf.d/secrets.fish` (gitignored) and referenced — never inlined — in tracked config. Keep it that way: tracked files (`auth.json`, `models.json`) must contain `$DUSKADIY_API_KEY`, not the literal token. + +`auth.json` is **no longer tracked** (2026-08-08, ahead of making the repo public): pi's interactive `/login` rewrites it with the **literal** key, so a tracked copy was one stray `/login` away from committing a real token. `auth.json.example` carries the same `$VAR` references. Every historical version of the old tracked file was audited and only ever contained references — nothing leaked. Installed-package and runtime artifacts (`.pi/agent/npm/`, `git/`, `trust.json`, `sessions/`) are gitignored. + +## Running pi + +- `pi` — interactive TUI. `pi -p ""` — non-interactive, print and exit. +- `pi -c` / `pi -r` — continue / pick a session to resume. +- `pi config` — TUI to enable/disable discovered resources. `pi --list-models [search]` — list available models. +- `--provider` / `--model` / `--thinking` override the `settings.json` defaults per-run. +- pi also auto-discovers `CLAUDE.md`/`AGENTS.md` as context files (disable with `-nc`) — i.e. pi reads this very file too, not just Claude Code. diff --git a/common/.pi/README.md b/common/.pi/README.md new file mode 100644 index 0000000..40eb0a3 --- /dev/null +++ b/common/.pi/README.md @@ -0,0 +1,160 @@ +# pi config (`~/.pi`) + +On-disk configuration for **[pi](https://github.com/earendil-works/pi)**, a TUI coding +agent (binary `/usr/bin/pi`). This tree lives in the dots repo and is surfaced into +`$HOME` by GNU Stow, so `~/.pi/agent/...` symlinks back here — **editing a file here +edits the live config**, no deploy step. + + +## Install / how it's surfaced + +pi reads everything under `$PI_CODING_AGENT_DIR` (default `~/.pi/agent`). Stow +symlinks each file individually (`--no-folding`): + +``` +~/.pi/agent/settings.json -> ~/.dots/common/.pi/agent/settings.json +~/.pi/agent/extensions/statusbar.ts -> ~/.dots/common/.pi/agent/extensions/statusbar.ts +... +``` + +- **Editing** an existing tracked file = editing live config immediately. +- **Adding** a new file (e.g. a new extension) is *not* live until you re-stow — + run `./install.sh` (or `stow --no-folding -R -d ~/.dots -t ~ common`) once to + create the symlink. +- After editing an extension, prompt, theme, or `keybindings.json`, run `/reload` + inside pi to apply without restarting. + +## What's in here + +| Path | Purpose | +|---|---| +| `agent/settings.json` | runtime settings (default model, theme, compaction, retry, timeouts) | +| `agent/models.json` | provider + model catalog (two OpenAI-compatible providers) | +| `agent/auth.json` | provider → credential map (references like `$DUSKADIY_API_KEY`, never literals). **Untracked** — copy `auth.json.example` on a new host | +| `agent/keybindings.json` | key remaps (each entry **replaces** the default for that action) | +| `agent/AGENTS.md` | global system instructions injected into every session | +| `agent/prompts/*.md` | custom `/slash` commands (prompt templates) | +| `agent/themes/*.json` | custom color themes | +| `agent/extensions/**` | auto-discovered TypeScript extensions (loaded via jiti, no build) | + +Runtime artifacts (`sessions/`, `npm/`, `git/`, `trust.json`) are gitignored. + +--- + +## Addons vs. stock pi + +Stock pi ships with **no extensions enabled, two built-in themes (`dark`/`light`), +default keybindings, and default settings**. Everything below is a deviation added +here. Each is independent — pull the ones you want and delete the rest. + +### Extensions + +pi **auto-discovers** every `.ts` under `extensions/`. So the universal "remove" +step is: **delete the file (or its folder) and `/reload`.** Toggle-able ones also +have a slash command to disable them for the current session without deleting. + +| Extension | What it adds | Origin | Remove | +|---|---|---|---| +| `statusbar.ts` | Replaces the footer with a Claude-Code-style status line (cwd, git branch, model, context %, decode t/s, token counts) in Nerd Font glyphs; adds a streaming pulse + a ≥80%-context warning widget | **custom** | delete file → default footer returns; or `/statusbar` to toggle off for the session | +| `vim-editor.ts` | Modal (vim-like) input editor with `[I]/[N]/[V]` indicator, hjkl/word/line motions, counts, yank/paste via the kill-ring | **local fork** of pi's bundled `examples/extensions/modal-editor.ts` | delete file → default editor; or `/vim` to toggle | +| `plan-mode/` | Read-only "plan" mode (disables edit/write, restricts bash) bound to **Shift+Tab**; `/plan` toggles, `--plan` starts in it | **vendored** copy of pi's bundled `examples/extensions/plan-mode/`, rebound from upstream `Ctrl+Alt+P` | delete the folder **and** revert the `keybindings.json` `app.thinking.cycle` remap (see below) | +| `notify.ts` | Native terminal notification (OSC 777/99, Windows toast) when the agent goes idle | **verbatim** from pi's `examples/extensions/notify.ts` | delete file | +| `questionnaire.ts` | Interactive multi-question overlay the model can call as a tool | **near-verbatim** from pi's `examples/extensions/questionnaire.ts` (one-line fix) | delete file | + +> The bundled examples live at `/opt/pi-coding-agent/examples/extensions/` — that's +> where `notify`/`questionnaire`/`modal-editor`/`plan-mode` came from, and where to +> re-pull the two forks after a pi upgrade before re-applying the local edits (the +> forks' own headers document those edits). + +### Theme + +`themes/catppuccin-mocha.json` is a **custom** theme (stock pi only bundles `dark` +and `light`). `settings.json` sets `"theme": "catppuccin-mocha"`. + +**Remove:** delete the file and set `"theme": "dark"` (or `"light"`) in +`settings.json`. + +### Custom slash commands (prompt templates) + +`prompts/commit.md` and `prompts/review.md` add `/commit` (writes a Conventional +Commits message for the staged diff, never commits) and `/review` (reviews the +working diff). Both are **custom**. Format is YAML frontmatter + body with +`${1:-default}` positional-arg substitution. + +**Remove:** delete the file(s). Stock pi has no `/commit` or `/review`. + +### Keybindings + +`keybindings.json` overrides three actions. **A user entry replaces the default keys +for that action — it does not merge.** Action ids and their stock defaults are in +`/opt/pi-coding-agent/docs/keybindings.md`. + +```jsonc +{ + "app.thinking.cycle": ["ctrl+shift+t"], // moved OFF Shift+Tab so plan-mode can bind it + "tui.editor.cursorUp": ["up", "ctrl+p"], // adds Ctrl+P as up (Emacs-style) + "app.model.cycleForward": ["alt+p"] // Alt+P cycles models +} +``` + +The `app.thinking.cycle` remap is **coupled to `plan-mode`**: plan-mode binds +Shift+Tab, so the thinking-cycle default (also Shift+Tab) is moved aside to avoid a +collision. If you drop plan-mode, drop that line too. + +**Remove:** delete the whole file to restore all stock keybindings, or delete +individual lines to restore just those. + +### `settings.json` tunings + +Beyond pointing at local models, these values deviate from pi's defaults: + +| Key | Value here | Stock default | Why | +|---|---|---|---| +| `compaction.reserveTokens` | `6144` | 16384 | small local windows (24k–128k) — don't let the response reserve dominate a short window | +| `compaction.keepRecentTokens` | `6000` | 20000 | keep less verbatim so short windows don't thrash into repeated compaction | +| `npmCommand` | `fnm exec --using=22 -- npm` | `npm` | pin extension npm installs to Node 22 via fnm | +| `retry` / `httpIdleTimeoutMs` | long (1h provider timeout, 10m idle) | shorter | local models can be slow to first token | +| `defaultThinkingLevel` | `off` | — | most local models here are non-reasoning | + +**Remove:** delete each key to fall back to pi's default (or drop the whole +`compaction`/`retry` block). + +### Providers & models + +`models.json` + `auth.json` define **two OpenAI-compatible providers** serving the +same catalog of small self-hosted models (Qwen3-Coder-30B, Gemma 4, GLM-4.7-Flash, +gpt-oss-20b, …), all `cost: 0`: + +- **`localcpp`** — LAN llama.cpp server, `http://192.168.0.204:11343/v1`, no key. +- **`duskadiy`** — remote, `https://llm.duskadiy.com/api/v1`, key `$DUSKADIY_API_KEY`. + +This is the part you'd **replace**, not just delete, to point pi at your own backend: +edit `models.json` (baseUrl + model `id`s, keeping each `id` exactly matching your +server's model id and `contextWindow` matching its loaded `ctx-size`) and set the +matching credential in `auth.json`. `settings.json > enabledModels` is a glob +allowlist for the Ctrl+P picker; `defaultProvider`/`defaultModel` pick the startup +model. + +> **Secret hygiene:** `$DUSKADIY_API_KEY` lives in +> `.config/fish/conf.d/secrets.fish` (gitignored) and is only *referenced* in tracked +> config. `auth.json` is **not tracked** (gitignored as of 2026-08-08): pi's interactive +> `/login` rewrites it with the **literal** key, and a tracked copy would be one stray +> `/login` away from committing a real token. Bootstrap a new host with +> `cp auth.json.example auth.json`. For bash its `.bash_profile` since it's not tracked by +> git, stow not applied. + +--- + +## Replicating just one piece + +- **Just the status bar:** copy `agent/extensions/statusbar.ts` into your + `~/.pi/agent/extensions/`, re-stow/restart, `/reload`. Colors follow your active + theme automatically. Needs a Nerd Font terminal for the glyphs. +- **Just vim input:** copy `agent/extensions/vim-editor.ts`, `/reload`, `/vim`. +- **Just plan mode:** copy `agent/extensions/plan-mode/` **and** add the + `app.thinking.cycle` remap to your `keybindings.json` (else Shift+Tab collides). +- **Just the theme:** copy `agent/themes/catppuccin-mocha.json`, set + `"theme": "catppuccin-mocha"`. +- **Just the slash commands:** copy `agent/prompts/commit.md` / `review.md`. + +Validate any JSON edit with `jq . agent/settings.json` (there's no build/test step). diff --git a/common/.pi/agent/AGENTS.md b/common/.pi/agent/AGENTS.md new file mode 100644 index 0000000..bdd9320 --- /dev/null +++ b/common/.pi/agent/AGENTS.md @@ -0,0 +1,22 @@ +# Global agent instructions + +You are a coding assistant running on small, locally-hosted models. Be precise and economical with tokens. + +## Working style +- Act with tools instead of describing what you would do. Keep prose short. +- Do the task that was asked. Don't add unrequested changes, refactors, or files. +- When done, stop. A one- or two-line summary is enough; no recaps of obvious steps. + +## Files & edits +- Read a file before you edit it. Never guess at file paths, function names, or APIs — verify first. +- Use the edit tool for changes. Make minimal, targeted diffs; never paste an entire file back to the user. +- Match the surrounding code's style, naming, and imports. + +## Shell +- Run one command at a time and check its output before the next. +- Prefer `rg` and `fd` for search. Use read-only commands when exploring. +- Never run destructive or system-changing commands (`rm -rf`, `sudo`, package installs, force-push) unless explicitly asked. + +## Honesty +- If you're unsure, say so and check rather than inventing an answer. +- Report failures plainly with the actual error; don't claim success you didn't verify. diff --git a/.pi/agent/auth.json b/common/.pi/agent/auth.json.example similarity index 100% rename from .pi/agent/auth.json rename to common/.pi/agent/auth.json.example diff --git a/common/.pi/agent/extensions/notify.ts b/common/.pi/agent/extensions/notify.ts new file mode 100644 index 0000000..f7b1f81 --- /dev/null +++ b/common/.pi/agent/extensions/notify.ts @@ -0,0 +1,55 @@ +/** + * Pi Notify Extension + * + * Sends a native terminal notification when Pi agent is done and waiting for input. + * Supports multiple terminal protocols: + * - OSC 777: Ghostty, iTerm2, WezTerm, rxvt-unicode + * - OSC 99: Kitty + * - Windows toast: Windows Terminal (WSL) + */ + +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; + +function windowsToastScript(title: string, body: string): string { + const type = "Windows.UI.Notifications"; + const mgr = `[${type}.ToastNotificationManager, ${type}, ContentType = WindowsRuntime]`; + const template = `[${type}.ToastTemplateType]::ToastText01`; + const toast = `[${type}.ToastNotification]::new($xml)`; + return [ + `${mgr} > $null`, + `$xml = [${type}.ToastNotificationManager]::GetTemplateContent(${template})`, + `$xml.GetElementsByTagName('text')[0].AppendChild($xml.CreateTextNode('${body}')) > $null`, + `[${type}.ToastNotificationManager]::CreateToastNotifier('${title}').Show(${toast})`, + ].join("; "); +} + +function notifyOSC777(title: string, body: string): void { + process.stdout.write(`\x1b]777;notify;${title};${body}\x07`); +} + +function notifyOSC99(title: string, body: string): void { + // Kitty OSC 99: i=notification id, d=0 means not done yet, p=body for second part + process.stdout.write(`\x1b]99;i=1:d=0;${title}\x1b\\`); + process.stdout.write(`\x1b]99;i=1:p=body;${body}\x1b\\`); +} + +function notifyWindows(title: string, body: string): void { + const { execFile } = require("child_process"); + execFile("powershell.exe", ["-NoProfile", "-Command", windowsToastScript(title, body)]); +} + +function notify(title: string, body: string): void { + if (process.env.WT_SESSION) { + notifyWindows(title, body); + } else if (process.env.KITTY_WINDOW_ID) { + notifyOSC99(title, body); + } else { + notifyOSC777(title, body); + } +} + +export default function (pi: ExtensionAPI) { + pi.on("agent_end", async () => { + notify("Pi", "Ready for input"); + }); +} diff --git a/common/.pi/agent/extensions/plan-mode/README.md b/common/.pi/agent/extensions/plan-mode/README.md new file mode 100644 index 0000000..e0c30d8 --- /dev/null +++ b/common/.pi/agent/extensions/plan-mode/README.md @@ -0,0 +1,66 @@ +# Plan Mode Extension + +Read-only exploration mode for safe code analysis. + +## Features + +- **Built-in write tools disabled**: Disables edit/write while preserving other active tools +- **Bash allowlist**: Only read-only bash commands are allowed +- **Plan extraction**: Extracts numbered steps from `Plan:` sections +- **Progress tracking**: Widget shows completion status during execution +- **[DONE:n] markers**: Explicit step completion tracking +- **Session persistence**: State survives session resume + +## Commands + +- `/plan` - Toggle plan mode +- `/todos` - Show current plan progress +- `Shift+Tab` - Toggle plan mode (shortcut; this fork rebinds it from upstream's Ctrl+Alt+P) + +## Usage + +1. Enable plan mode with `/plan` or `--plan` flag +2. Ask the agent to analyze code and create a plan +3. The agent should output a numbered plan under a `Plan:` header: + +``` +Plan: +1. First step description +2. Second step description +3. Third step description +``` + +4. Choose "Execute the plan" when prompted +5. During execution, the agent marks steps complete with `[DONE:n]` tags +6. Progress widget shows completion status + +## How It Works + +### Plan Mode (Read-Only) +- Built-in edit/write tools disabled +- Other active tools remain available +- Bash commands filtered through allowlist +- Agent creates a plan without making changes + +### Execution Mode +- Full tool access restored +- Agent executes steps in order +- `[DONE:n]` markers track completion +- Widget shows progress + +### Command Allowlist + +Safe commands (allowed): +- File inspection: `cat`, `head`, `tail`, `less`, `more` +- Search: `grep`, `find`, `rg`, `fd` +- Directory: `ls`, `pwd`, `tree` +- Git read: `git status`, `git log`, `git diff`, `git branch` +- Package info: `npm list`, `npm outdated`, `yarn info` +- System info: `uname`, `whoami`, `date`, `uptime` + +Blocked commands: +- File modification: `rm`, `mv`, `cp`, `mkdir`, `touch` +- Git write: `git add`, `git commit`, `git push` +- Package install: `npm install`, `yarn add`, `pip install` +- System: `sudo`, `kill`, `reboot` +- Editors: `vim`, `nano`, `code` diff --git a/common/.pi/agent/extensions/plan-mode/index.ts b/common/.pi/agent/extensions/plan-mode/index.ts new file mode 100644 index 0000000..df1af64 --- /dev/null +++ b/common/.pi/agent/extensions/plan-mode/index.ts @@ -0,0 +1,388 @@ +/** + * Plan Mode Extension + * + * Read-only exploration mode for safe code analysis. + * When enabled, built-in write tools are disabled. + * + * Features: + * - /plan command or Shift+Tab to toggle + * - Bash restricted to allowlisted read-only commands + * - Extracts numbered plan steps from "Plan:" sections + * - [DONE:n] markers to complete steps during execution + * - Progress tracking widget during execution + */ + +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { AssistantMessage, TextContent } from "@earendil-works/pi-ai"; +import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent"; +import { extractTodoItems, isSafeCommand, markCompletedSteps, type TodoItem } from "./utils.ts"; + +// Tools +const PLAN_MODE_TOOLS = ["read", "bash", "grep", "find", "ls", "questionnaire"]; +const NORMAL_MODE_TOOLS = ["read", "bash", "edit", "write"]; +const PLAN_MODE_DISABLED_TOOLS = new Set(["edit", "write"]); +const PLAN_MANAGED_TOOLS = new Set([...PLAN_MODE_TOOLS, ...NORMAL_MODE_TOOLS]); + +interface PlanModeState { + enabled: boolean; + todos?: TodoItem[]; + executing?: boolean; + toolsBeforePlanMode?: string[]; +} + +// Type guard for assistant messages +function isAssistantMessage(m: AgentMessage): m is AssistantMessage { + return m.role === "assistant" && Array.isArray(m.content); +} + +// Extract text content from an assistant message +function getTextContent(message: AssistantMessage): string { + return message.content + .filter((block): block is TextContent => block.type === "text") + .map((block) => block.text) + .join("\n"); +} + +export default function planModeExtension(pi: ExtensionAPI): void { + let planModeEnabled = false; + let executionMode = false; + let todoItems: TodoItem[] = []; + let toolsBeforePlanMode: string[] | undefined; + + pi.registerFlag("plan", { + description: "Start in plan mode (read-only exploration)", + type: "boolean", + default: false, + }); + + function updateStatus(ctx: ExtensionContext): void { + // Footer status + if (executionMode && todoItems.length > 0) { + const completed = todoItems.filter((t) => t.completed).length; + ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("accent", `📋 ${completed}/${todoItems.length}`)); + } else if (planModeEnabled) { + ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("warning", "⏸ plan")); + } else { + ctx.ui.setStatus("plan-mode", undefined); + } + + // Widget showing todo list + if (executionMode && todoItems.length > 0) { + const lines = todoItems.map((item) => { + if (item.completed) { + return ( + ctx.ui.theme.fg("success", "☑ ") + ctx.ui.theme.fg("muted", ctx.ui.theme.strikethrough(item.text)) + ); + } + return `${ctx.ui.theme.fg("muted", "☐ ")}${item.text}`; + }); + ctx.ui.setWidget("plan-todos", lines); + } else { + ctx.ui.setWidget("plan-todos", undefined); + } + } + + function uniqueToolNames(toolNames: string[]): string[] { + return [...new Set(toolNames)]; + } + + function getPlanModeTools(activeToolNames: string[]): string[] { + return uniqueToolNames([ + ...activeToolNames.filter((name) => !PLAN_MODE_DISABLED_TOOLS.has(name)), + ...PLAN_MODE_TOOLS, + ]); + } + + function getNormalModeTools(activeToolNames: string[]): string[] { + return uniqueToolNames([ + ...NORMAL_MODE_TOOLS, + ...activeToolNames.filter((name) => !PLAN_MANAGED_TOOLS.has(name)), + ]); + } + + function enablePlanModeTools(): void { + if (toolsBeforePlanMode === undefined) { + toolsBeforePlanMode = pi.getActiveTools(); + } + pi.setActiveTools(getPlanModeTools(toolsBeforePlanMode)); + } + + function restoreNormalModeTools(): void { + pi.setActiveTools(toolsBeforePlanMode ?? getNormalModeTools(pi.getActiveTools())); + toolsBeforePlanMode = undefined; + } + + function persistState(): void { + pi.appendEntry("plan-mode", { + enabled: planModeEnabled, + todos: todoItems, + executing: executionMode, + toolsBeforePlanMode, + }); + } + + function togglePlanMode(ctx: ExtensionContext): void { + planModeEnabled = !planModeEnabled; + executionMode = false; + todoItems = []; + + if (planModeEnabled) { + enablePlanModeTools(); + ctx.ui.notify("Plan mode enabled. Built-in write tools disabled."); + } else { + restoreNormalModeTools(); + ctx.ui.notify("Plan mode disabled. Full access restored."); + } + updateStatus(ctx); + persistState(); + } + + pi.registerCommand("plan", { + description: "Toggle plan mode (read-only exploration)", + handler: async (_args, ctx) => togglePlanMode(ctx), + }); + + pi.registerCommand("todos", { + description: "Show current plan todo list", + handler: async (_args, ctx) => { + if (todoItems.length === 0) { + ctx.ui.notify("No todos. Create a plan first with /plan", "info"); + return; + } + const list = todoItems.map((item, i) => `${i + 1}. ${item.completed ? "✓" : "○"} ${item.text}`).join("\n"); + ctx.ui.notify(`Plan Progress:\n${list}`, "info"); + }, + }); + + pi.registerShortcut("shift+tab", { + description: "Toggle plan mode", + handler: async (ctx) => togglePlanMode(ctx), + }); + + // Block destructive bash commands in plan mode + pi.on("tool_call", async (event) => { + if (!planModeEnabled || event.toolName !== "bash") return; + + const command = event.input.command as string; + if (!isSafeCommand(command)) { + return { + block: true, + reason: `Plan mode: command blocked (not allowlisted). Use /plan to disable plan mode first.\nCommand: ${command}`, + }; + } + }); + + // Filter out stale plan mode context when not in plan mode + pi.on("context", async (event) => { + if (planModeEnabled) return; + + return { + messages: event.messages.filter((m) => { + const msg = m as AgentMessage & { customType?: string }; + if (msg.customType === "plan-mode-context") return false; + if (msg.role !== "user") return true; + + const content = msg.content; + if (typeof content === "string") { + return !content.includes("[PLAN MODE ACTIVE]"); + } + if (Array.isArray(content)) { + return !content.some( + (c) => c.type === "text" && (c as TextContent).text?.includes("[PLAN MODE ACTIVE]"), + ); + } + return true; + }), + }; + }); + + // Inject plan/execution context before agent starts + pi.on("before_agent_start", async () => { + if (planModeEnabled) { + return { + message: { + customType: "plan-mode-context", + content: `[PLAN MODE ACTIVE] +You are in plan mode - a read-only exploration mode for safe code analysis. + +Restrictions: +- Built-in edit and write tools are disabled +- Other currently active tools remain available +- Bash is restricted to an allowlist of read-only commands + +Ask clarifying questions using the questionnaire tool. + +Create a detailed numbered plan under a "Plan:" header: + +Plan: +1. First step description +2. Second step description +... + +Do NOT attempt to make changes - just describe what you would do.`, + display: false, + }, + }; + } + + if (executionMode && todoItems.length > 0) { + const remaining = todoItems.filter((t) => !t.completed); + const todoList = remaining.map((t) => `${t.step}. ${t.text}`).join("\n"); + return { + message: { + customType: "plan-execution-context", + content: `[EXECUTING PLAN - Full tool access enabled] + +Remaining steps: +${todoList} + +Execute each step in order. +After completing a step, include a [DONE:n] tag in your response.`, + display: false, + }, + }; + } + }); + + // Track progress after each turn + pi.on("turn_end", async (event, ctx) => { + if (!executionMode || todoItems.length === 0) return; + if (!isAssistantMessage(event.message)) return; + + const text = getTextContent(event.message); + if (markCompletedSteps(text, todoItems) > 0) { + updateStatus(ctx); + } + persistState(); + }); + + // Handle plan completion and plan mode UI + pi.on("agent_end", async (event, ctx) => { + // Check if execution is complete + if (executionMode && todoItems.length > 0) { + if (todoItems.every((t) => t.completed)) { + const completedList = todoItems.map((t) => `~~${t.text}~~`).join("\n"); + pi.sendMessage( + { customType: "plan-complete", content: `**Plan Complete!** ✓\n\n${completedList}`, display: true }, + { triggerTurn: false }, + ); + executionMode = false; + todoItems = []; + updateStatus(ctx); + persistState(); // Save cleared state so resume doesn't restore old execution mode + } + return; + } + + if (!planModeEnabled || !ctx.hasUI) return; + + // Extract todos from last assistant message + const lastAssistant = [...event.messages].reverse().find(isAssistantMessage); + if (lastAssistant) { + const extracted = extractTodoItems(getTextContent(lastAssistant)); + if (extracted.length > 0) { + todoItems = extracted; + } + } + + if (todoItems.length === 0) return; + persistState(); + + // Show plan steps and prompt for next action + const todoListText = todoItems.map((t, i) => `${i + 1}. ☐ ${t.text}`).join("\n"); + const planTodoListMessage = { + customType: "plan-todo-list", + content: `**Plan Steps (${todoItems.length}):**\n\n${todoListText}`, + display: true, + }; + + const choice = await ctx.ui.select("Plan mode - what next?", [ + "Execute the plan (track progress)", + "Stay in plan mode", + "Refine the plan", + ]); + + if (choice?.startsWith("Execute")) { + const firstTodoItem = todoItems[0]; + if (!firstTodoItem) return; + + planModeEnabled = false; + executionMode = true; + restoreNormalModeTools(); + updateStatus(ctx); + persistState(); + + const remainingList = todoItems.map((t) => `${t.step}. ${t.text}`).join("\n"); + const execMessage = `Execute the plan. + +Remaining steps: +${remainingList} + +Start with: ${firstTodoItem.text} +After completing a step, include a [DONE:n] tag in your response.`; + pi.sendMessage(planTodoListMessage, { deliverAs: "followUp" }); + pi.sendMessage( + { customType: "plan-mode-execute", content: execMessage, display: true }, + { triggerTurn: true, deliverAs: "followUp" }, + ); + } else if (choice === "Refine the plan") { + const refinement = await ctx.ui.editor("Refine the plan:", ""); + if (refinement?.trim()) { + pi.sendMessage(planTodoListMessage, { deliverAs: "followUp" }); + pi.sendUserMessage(refinement.trim(), { deliverAs: "followUp" }); + } + } + }); + + // Restore state on session start/resume + pi.on("session_start", async (_event, ctx) => { + if (pi.getFlag("plan") === true) { + planModeEnabled = true; + } + + const entries = ctx.sessionManager.getEntries(); + + // Restore persisted state + const planModeEntry = entries + .filter((e: { type: string; customType?: string }) => e.type === "custom" && e.customType === "plan-mode") + .pop() as { data?: PlanModeState } | undefined; + + if (planModeEntry?.data) { + planModeEnabled = planModeEntry.data.enabled ?? planModeEnabled; + todoItems = planModeEntry.data.todos ?? todoItems; + executionMode = planModeEntry.data.executing ?? executionMode; + toolsBeforePlanMode = planModeEntry.data.toolsBeforePlanMode ?? toolsBeforePlanMode; + } + + // On resume: re-scan messages to rebuild completion state + // Only scan messages AFTER the last "plan-mode-execute" to avoid picking up [DONE:n] from previous plans + const isResume = planModeEntry !== undefined; + if (isResume && executionMode && todoItems.length > 0) { + // Find the index of the last plan-mode-execute entry (marks when current execution started) + let executeIndex = -1; + for (let i = entries.length - 1; i >= 0; i--) { + const entry = entries[i] as { type: string; customType?: string }; + if (entry.customType === "plan-mode-execute") { + executeIndex = i; + break; + } + } + + // Only scan messages after the execute marker + const messages: AssistantMessage[] = []; + for (let i = executeIndex + 1; i < entries.length; i++) { + const entry = entries[i]; + if (entry.type === "message" && "message" in entry && isAssistantMessage(entry.message as AgentMessage)) { + messages.push(entry.message as AssistantMessage); + } + } + const allText = messages.map(getTextContent).join("\n"); + markCompletedSteps(allText, todoItems); + } + + if (planModeEnabled) { + enablePlanModeTools(); + } + updateStatus(ctx); + }); +} diff --git a/common/.pi/agent/extensions/plan-mode/utils.ts b/common/.pi/agent/extensions/plan-mode/utils.ts new file mode 100644 index 0000000..62123f9 --- /dev/null +++ b/common/.pi/agent/extensions/plan-mode/utils.ts @@ -0,0 +1,168 @@ +/** + * Pure utility functions for plan mode. + * Extracted for testability. + */ + +// Destructive commands blocked in plan mode +const DESTRUCTIVE_PATTERNS = [ + /\brm\b/i, + /\brmdir\b/i, + /\bmv\b/i, + /\bcp\b/i, + /\bmkdir\b/i, + /\btouch\b/i, + /\bchmod\b/i, + /\bchown\b/i, + /\bchgrp\b/i, + /\bln\b/i, + /\btee\b/i, + /\btruncate\b/i, + /\bdd\b/i, + /\bshred\b/i, + /(^|[^<])>(?!>)/, + />>/, + /\bnpm\s+(install|uninstall|update|ci|link|publish)/i, + /\byarn\s+(add|remove|install|publish)/i, + /\bpnpm\s+(add|remove|install|publish)/i, + /\bpip\s+(install|uninstall)/i, + /\bapt(-get)?\s+(install|remove|purge|update|upgrade)/i, + /\bbrew\s+(install|uninstall|upgrade)/i, + /\bgit\s+(add|commit|push|pull|merge|rebase|reset|checkout|branch\s+-[dD]|stash|cherry-pick|revert|tag|init|clone)/i, + /\bsudo\b/i, + /\bsu\b/i, + /\bkill\b/i, + /\bpkill\b/i, + /\bkillall\b/i, + /\breboot\b/i, + /\bshutdown\b/i, + /\bsystemctl\s+(start|stop|restart|enable|disable)/i, + /\bservice\s+\S+\s+(start|stop|restart)/i, + /\b(vim?|nano|emacs|code|subl)\b/i, +]; + +// Safe read-only commands allowed in plan mode +const SAFE_PATTERNS = [ + /^\s*cat\b/, + /^\s*head\b/, + /^\s*tail\b/, + /^\s*less\b/, + /^\s*more\b/, + /^\s*grep\b/, + /^\s*find\b/, + /^\s*ls\b/, + /^\s*pwd\b/, + /^\s*echo\b/, + /^\s*printf\b/, + /^\s*wc\b/, + /^\s*sort\b/, + /^\s*uniq\b/, + /^\s*diff\b/, + /^\s*file\b/, + /^\s*stat\b/, + /^\s*du\b/, + /^\s*df\b/, + /^\s*tree\b/, + /^\s*which\b/, + /^\s*whereis\b/, + /^\s*type\b/, + /^\s*env\b/, + /^\s*printenv\b/, + /^\s*uname\b/, + /^\s*whoami\b/, + /^\s*id\b/, + /^\s*date\b/, + /^\s*cal\b/, + /^\s*uptime\b/, + /^\s*ps\b/, + /^\s*top\b/, + /^\s*htop\b/, + /^\s*free\b/, + /^\s*git\s+(status|log|diff|show|branch|remote|config\s+--get)/i, + /^\s*git\s+ls-/i, + /^\s*npm\s+(list|ls|view|info|search|outdated|audit)/i, + /^\s*yarn\s+(list|info|why|audit)/i, + /^\s*node\s+--version/i, + /^\s*python\s+--version/i, + /^\s*curl\s/i, + /^\s*wget\s+-O\s*-/i, + /^\s*jq\b/, + /^\s*sed\s+-n/i, + /^\s*awk\b/, + /^\s*rg\b/, + /^\s*fd\b/, + /^\s*bat\b/, + /^\s*eza\b/, +]; + +export function isSafeCommand(command: string): boolean { + const isDestructive = DESTRUCTIVE_PATTERNS.some((p) => p.test(command)); + const isSafe = SAFE_PATTERNS.some((p) => p.test(command)); + return !isDestructive && isSafe; +} + +export interface TodoItem { + step: number; + text: string; + completed: boolean; +} + +export function cleanStepText(text: string): string { + let cleaned = text + .replace(/\*{1,2}([^*]+)\*{1,2}/g, "$1") // Remove bold/italic + .replace(/`([^`]+)`/g, "$1") // Remove code + .replace( + /^(Use|Run|Execute|Create|Write|Read|Check|Verify|Update|Modify|Add|Remove|Delete|Install)\s+(the\s+)?/i, + "", + ) + .replace(/\s+/g, " ") + .trim(); + + if (cleaned.length > 0) { + cleaned = cleaned.charAt(0).toUpperCase() + cleaned.slice(1); + } + if (cleaned.length > 50) { + cleaned = `${cleaned.slice(0, 47)}...`; + } + return cleaned; +} + +export function extractTodoItems(message: string): TodoItem[] { + const items: TodoItem[] = []; + const headerMatch = message.match(/\*{0,2}Plan:\*{0,2}\s*\n/i); + if (!headerMatch) return items; + + const planSection = message.slice(message.indexOf(headerMatch[0]) + headerMatch[0].length); + const numberedPattern = /^\s*(\d+)[.)]\s+\*{0,2}([^*\n]+)/gm; + + for (const match of planSection.matchAll(numberedPattern)) { + const text = match[2] + .trim() + .replace(/\*{1,2}$/, "") + .trim(); + if (text.length > 5 && !text.startsWith("`") && !text.startsWith("/") && !text.startsWith("-")) { + const cleaned = cleanStepText(text); + if (cleaned.length > 3) { + items.push({ step: items.length + 1, text: cleaned, completed: false }); + } + } + } + return items; +} + +export function extractDoneSteps(message: string): number[] { + const steps: number[] = []; + for (const match of message.matchAll(/\[DONE:(\d+)\]/gi)) { + const step = Number(match[1]); + if (Number.isFinite(step)) steps.push(step); + } + return steps; +} + +export function markCompletedSteps(text: string, items: TodoItem[]): number { + const doneSteps = extractDoneSteps(text); + for (const step of doneSteps) { + const item = items.find((t) => t.step === step); + if (item) item.completed = true; + } + return doneSteps.length; +} diff --git a/common/.pi/agent/extensions/questionnaire.ts b/common/.pi/agent/extensions/questionnaire.ts new file mode 100644 index 0000000..9b2f307 --- /dev/null +++ b/common/.pi/agent/extensions/questionnaire.ts @@ -0,0 +1,449 @@ +/** + * Questionnaire Tool - Unified tool for asking single or multiple questions + * + * Single question: simple options list + * Multiple questions: tab bar navigation between questions + */ + +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; +import { + Editor, + type EditorTheme, + Key, + matchesKey, + Text, + visibleWidth, + wrapTextWithAnsi, +} from "@earendil-works/pi-tui"; +import { Type } from "typebox"; + +// Types +interface QuestionOption { + value: string; + label: string; + description?: string; +} + +type RenderOption = QuestionOption & { isOther?: boolean }; + +interface Question { + id: string; + label: string; + prompt: string; + options: QuestionOption[]; + allowOther: boolean; +} + +interface Answer { + id: string; + value: string; + label: string; + wasCustom: boolean; + index?: number; +} + +interface QuestionnaireResult { + questions: Question[]; + answers: Answer[]; + cancelled: boolean; +} + +// Schema +const QuestionOptionSchema = Type.Object({ + value: Type.String({ description: "The value returned when selected" }), + label: Type.String({ description: "Display label for the option" }), + description: Type.Optional(Type.String({ description: "Optional description shown below label" })), +}); + +const QuestionSchema = Type.Object({ + id: Type.String({ description: "Unique identifier for this question" }), + label: Type.Optional( + Type.String({ + description: "Short contextual label for tab bar, e.g. 'Scope', 'Priority' (defaults to Q1, Q2)", + }), + ), + prompt: Type.String({ description: "The full question text to display" }), + options: Type.Array(QuestionOptionSchema, { description: "Available options to choose from" }), + allowOther: Type.Optional(Type.Boolean({ description: "Allow 'Type something' option (default: true)" })), +}); + +const QuestionnaireParams = Type.Object({ + questions: Type.Array(QuestionSchema, { description: "Questions to ask the user" }), +}); + +function errorResult( + message: string, + questions: Question[] = [], +): { content: { type: "text"; text: string }[]; details: QuestionnaireResult } { + return { + content: [{ type: "text", text: message }], + details: { questions, answers: [], cancelled: true }, + }; +} + +export default function questionnaire(pi: ExtensionAPI) { + pi.registerTool({ + name: "questionnaire", + label: "Questionnaire", + description: + "Ask the user one or more questions. Use for clarifying requirements, getting preferences, or confirming decisions. For single questions, shows a simple option list. For multiple questions, shows a tab-based interface.", + parameters: QuestionnaireParams, + + async execute(_toolCallId, params, _signal, _onUpdate, ctx) { + if (ctx.mode !== "tui") { + return errorResult("Error: UI not available (running in non-interactive mode)"); + } + if (params.questions.length === 0) { + return errorResult("Error: No questions provided"); + } + + // Normalize questions with defaults + const questions: Question[] = params.questions.map((q, i) => ({ + ...q, + label: q.label || `Q${i + 1}`, + allowOther: q.allowOther !== false, + })); + + const isMulti = questions.length > 1; + const totalTabs = questions.length + 1; // questions + Submit + + const result = await ctx.ui.custom((tui, theme, _kb, done) => { + // State + let currentTab = 0; + let optionIndex = 0; + let inputMode = false; + let inputQuestionId: string | null = null; + let cachedLines: string[] | undefined; + const answers = new Map(); + + // Editor for "Type something" option + const editorTheme: EditorTheme = { + borderColor: (s) => theme.fg("accent", s), + selectList: { + selectedPrefix: (t) => theme.fg("accent", t), + selectedText: (t) => theme.fg("accent", t), + description: (t) => theme.fg("muted", t), + scrollInfo: (t) => theme.fg("dim", t), + noMatch: (t) => theme.fg("warning", t), + }, + }; + const editor = new Editor(tui, editorTheme); + + // Helpers + function refresh() { + cachedLines = undefined; + tui.requestRender(); + } + + function submit(cancelled: boolean) { + done({ questions, answers: Array.from(answers.values()), cancelled }); + } + + function currentQuestion(): Question | undefined { + return questions[currentTab]; + } + + function currentOptions(): RenderOption[] { + const q = currentQuestion(); + if (!q) return []; + const opts: RenderOption[] = [...q.options]; + if (q.allowOther) { + opts.push({ value: "__other__", label: "Type something.", isOther: true }); + } + return opts; + } + + function allAnswered(): boolean { + return questions.every((q) => answers.has(q.id)); + } + + function advanceAfterAnswer() { + if (!isMulti) { + submit(false); + return; + } + if (currentTab < questions.length - 1) { + currentTab++; + } else { + currentTab = questions.length; // Submit tab + } + optionIndex = 0; + refresh(); + } + + function saveAnswer(questionId: string, value: string, label: string, wasCustom: boolean, index?: number) { + answers.set(questionId, { id: questionId, value, label, wasCustom, index }); + } + + // Editor submit callback + editor.onSubmit = (value) => { + if (!inputQuestionId) return; + const trimmed = value.trim() || "(no response)"; + saveAnswer(inputQuestionId, trimmed, trimmed, true); + inputMode = false; + inputQuestionId = null; + editor.setText(""); + advanceAfterAnswer(); + }; + + function handleInput(data: string) { + // Input mode: route to editor + if (inputMode) { + if (matchesKey(data, Key.escape)) { + inputMode = false; + inputQuestionId = null; + editor.setText(""); + refresh(); + return; + } + editor.handleInput(data); + refresh(); + return; + } + + const q = currentQuestion(); + const opts = currentOptions(); + + // Tab navigation (multi-question only) + if (isMulti) { + if (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) { + currentTab = (currentTab + 1) % totalTabs; + optionIndex = 0; + refresh(); + return; + } + if (matchesKey(data, Key.shift("tab")) || matchesKey(data, Key.left)) { + currentTab = (currentTab - 1 + totalTabs) % totalTabs; + optionIndex = 0; + refresh(); + return; + } + } + + // Submit tab + if (currentTab === questions.length) { + if (matchesKey(data, Key.enter) && allAnswered()) { + submit(false); + } else if (matchesKey(data, Key.escape)) { + submit(true); + } + return; + } + + // Option navigation + if (matchesKey(data, Key.up)) { + optionIndex = Math.max(0, optionIndex - 1); + refresh(); + return; + } + if (matchesKey(data, Key.down)) { + optionIndex = Math.min(opts.length - 1, optionIndex + 1); + refresh(); + return; + } + + // Select option + if (matchesKey(data, Key.enter) && q) { + const opt = opts[optionIndex]; + if (!opt) return; // empty options with allowOther=false + if (opt.isOther) { + inputMode = true; + inputQuestionId = q.id; + editor.setText(""); + refresh(); + return; + } + saveAnswer(q.id, opt.value, opt.label, false, optionIndex + 1); + advanceAfterAnswer(); + return; + } + + // Cancel + if (matchesKey(data, Key.escape)) { + submit(true); + } + } + + function render(width: number): string[] { + if (cachedLines) return cachedLines; + + const lines: string[] = []; + const renderWidth = Math.max(1, width); + const q = currentQuestion(); + const opts = currentOptions(); + + function addWrapped(text: string) { + lines.push(...wrapTextWithAnsi(text, renderWidth)); + } + + function addWrappedWithPrefix(prefix: string, text: string) { + const prefixWidth = visibleWidth(prefix); + if (prefixWidth >= renderWidth) { + addWrapped(prefix + text); + return; + } + const wrapped = wrapTextWithAnsi(text, renderWidth - prefixWidth); + const continuationPrefix = " ".repeat(prefixWidth); + for (let i = 0; i < wrapped.length; i++) { + lines.push(`${i === 0 ? prefix : continuationPrefix}${wrapped[i]}`); + } + } + + lines.push(theme.fg("accent", "─".repeat(renderWidth))); + + // Tab bar (multi-question only) + if (isMulti) { + const tabs: string[] = ["← "]; + for (let i = 0; i < questions.length; i++) { + const isActive = i === currentTab; + const isAnswered = answers.has(questions[i].id); + const lbl = questions[i].label; + const box = isAnswered ? "■" : "□"; + const color = isAnswered ? "success" : "muted"; + const text = ` ${box} ${lbl} `; + const styled = isActive ? theme.bg("selectedBg", theme.fg("text", text)) : theme.fg(color, text); + tabs.push(`${styled} `); + } + const canSubmit = allAnswered(); + const isSubmitTab = currentTab === questions.length; + const submitText = " ✓ Submit "; + const submitStyled = isSubmitTab + ? theme.bg("selectedBg", theme.fg("text", submitText)) + : theme.fg(canSubmit ? "success" : "dim", submitText); + tabs.push(`${submitStyled} →`); + addWrappedWithPrefix(" ", tabs.join("")); + lines.push(""); + } + + // Helper to render options list + function renderOptions() { + for (let i = 0; i < opts.length; i++) { + const opt = opts[i]; + const selected = i === optionIndex; + const isOther = opt.isOther === true; + const prefix = selected ? theme.fg("accent", "> ") : " "; + const label = `${i + 1}. ${opt.label}${isOther && inputMode ? " ✎" : ""}`; + const color = selected || (isOther && inputMode) ? "accent" : "text"; + + addWrappedWithPrefix(prefix, theme.fg(color, label)); + if (opt.description) { + addWrappedWithPrefix(" ", theme.fg("muted", opt.description)); + } + } + } + + // Content + if (inputMode && q) { + addWrappedWithPrefix(" ", theme.fg("text", q.prompt)); + lines.push(""); + // Show options for reference + renderOptions(); + lines.push(""); + addWrappedWithPrefix(" ", theme.fg("muted", "Your answer:")); + for (const line of editor.render(Math.max(1, renderWidth - 2))) { + lines.push(` ${line}`); + } + lines.push(""); + addWrappedWithPrefix(" ", theme.fg("dim", "Enter to submit • Esc to cancel")); + } else if (currentTab === questions.length) { + addWrappedWithPrefix(" ", theme.fg("accent", theme.bold("Ready to submit"))); + lines.push(""); + for (const question of questions) { + const answer = answers.get(question.id); + if (answer) { + const prefix = answer.wasCustom ? "(wrote) " : ""; + const summary = `${theme.fg("muted", `${question.label}: `)}${theme.fg("text", prefix + answer.label)}`; + addWrappedWithPrefix(" ", summary); + } + } + lines.push(""); + if (allAnswered()) { + addWrappedWithPrefix(" ", theme.fg("success", "Press Enter to submit")); + } else { + const missing = questions + .filter((q) => !answers.has(q.id)) + .map((q) => q.label) + .join(", "); + addWrappedWithPrefix(" ", theme.fg("warning", `Unanswered: ${missing}`)); + } + } else if (q) { + addWrappedWithPrefix(" ", theme.fg("text", q.prompt)); + lines.push(""); + renderOptions(); + } + + lines.push(""); + if (!inputMode) { + const help = isMulti + ? "Tab/←→ navigate • ↑↓ select • Enter confirm • Esc cancel" + : "↑↓ navigate • Enter select • Esc cancel"; + addWrappedWithPrefix(" ", theme.fg("dim", help)); + } + lines.push(theme.fg("accent", "─".repeat(renderWidth))); + + cachedLines = lines; + return lines; + } + + return { + render, + invalidate: () => { + cachedLines = undefined; + }, + handleInput, + }; + }); + + if (result.cancelled) { + return { + content: [{ type: "text", text: "User cancelled the questionnaire" }], + details: result, + }; + } + + const answerLines = result.answers.map((a) => { + const qLabel = questions.find((q) => q.id === a.id)?.label || a.id; + if (a.wasCustom) { + return `${qLabel}: user wrote: ${a.label}`; + } + return `${qLabel}: user selected: ${a.index}. ${a.label}`; + }); + + return { + content: [{ type: "text", text: answerLines.join("\n") }], + details: result, + }; + }, + + renderCall(args, theme, _context) { + const qs = (args.questions as Question[]) || []; + const count = qs.length; + const labels = qs.map((q) => q.label || q.id).join(", "); + let text = theme.fg("toolTitle", theme.bold("questionnaire ")); + text += theme.fg("muted", `${count} question${count !== 1 ? "s" : ""}`); + if (labels) { + text += theme.fg("dim", ` (${labels})`); + } + return new Text(text, 0, 0); + }, + + renderResult(result, _options, theme, _context) { + const details = result.details as QuestionnaireResult | undefined; + if (!details) { + const text = result.content[0]; + return new Text(text?.type === "text" ? text.text : "", 0, 0); + } + if (details.cancelled) { + return new Text(theme.fg("warning", "Cancelled"), 0, 0); + } + const lines = details.answers.map((a) => { + if (a.wasCustom) { + return `${theme.fg("success", "✓ ")}${theme.fg("accent", a.id)}: ${theme.fg("muted", "(wrote) ")}${a.label}`; + } + const display = a.index ? `${a.index}. ${a.label}` : a.label; + return `${theme.fg("success", "✓ ")}${theme.fg("accent", a.id)}: ${display}`; + }); + return new Text(lines.join("\n"), 0, 0); + }, + }); +} diff --git a/common/.pi/agent/extensions/statusbar.ts b/common/.pi/agent/extensions/statusbar.ts new file mode 100644 index 0000000..d1e75cc --- /dev/null +++ b/common/.pi/agent/extensions/statusbar.ts @@ -0,0 +1,257 @@ +/** + * Pi status bar (custom footer) + * + * Replaces pi's default footer via `ctx.ui.setFooter()`. It mirrors the layout + * and *meaning* of the Claude Code status line (`~/.config/claude/statusline.py`), + * but swaps the emoji for Nerd Font glyphs — the same Material Design set already + * used across tmux (waybar cpu/mem/net glyphs) and nvim — so it reads natively in + * kitty (CaskaydiaCove Nerd Font Mono). Colors come from the active pi theme + * (catppuccin-mocha), not hard-coded ANSI. + * + * Segments render left → right and truncate at the terminal edge, so the most + * useful info stays visible on a narrow pane. Each icon echoes the Claude emoji + * it replaces: + * + * 󰉋 dir cwd, ~-collapsed (Claude 📁) U+F024B nf-md-folder + * 󰘬 git current branch (only inside a repo) (Claude 🌿) U+F062C nf-md-source_branch + * 󰚩 model model id + · thinking level (Claude 🤖) U+F06A9 nf-md-robot + * 󰈚 context % of context window used (+ tokens) (Claude 📝) U+F021A nf-md-text_box + * 󰓅 t/s last response's decode throughput U+F04C5 nf-md-speedometer + * ↑ ↓ tokens session input / output tokens (Claude 💰) plain arrows + * + * git, context, t/s and tokens only appear once there is data for them (i.e. after + * the first response, and git only inside a git repo). Extension statuses (e.g. + * plan-mode's "⏸ plan") are preserved at the far left. The Claude bar's system row + * (RAM/CPU/temp/disk) is intentionally omitted — that data isn't in the footer API, + * and the tmux bar below pi already shows cpu/mem/net. + * + * Two extras beyond the footer line: + * - a themed pulse working-indicator (the spinner shown while pi streams), and + * - a 󰀪 context-budget warning widget above the editor once the window passes + * 80% full, nudging a /compact before things get truncated. + * + * Restore the built-in footer (and reset the indicator/widget) with `/statusbar`. + */ + +import type { AssistantMessage } from "@earendil-works/pi-ai"; +import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent"; +import { truncateToWidth } from "@earendil-works/pi-tui"; + +// Nerd Font (Material Design) glyphs — verified to render in the user's font. +const ICON = { + dir: "\u{F024B}", // 󰉋 nf-md-folder + git: "\u{F062C}", // 󰘬 nf-md-source_branch + model: "\u{F06A9}", // 󰚩 nf-md-robot + context: "\u{F021A}", // 󰈚 nf-md-text_box + tps: "\u{F04C5}", // 󰓅 nf-md-speedometer + warn: "\u{F002A}", // 󰀪 nf-md-alert_outline (same glyph nvim uses for warnings) + up: "↑", // ↑ + down: "↓", // ↓ +}; + +// Widget key for the context-budget warning shown above the editor. +const CTX_WIDGET = "statusbar-context-warning"; +// Show the warning once the context window is this full (%). +const CTX_WARN_AT = 80; + +// Current thinking level, tracked via thinking_level_select (footer render has no +// direct access to it). Defaults to the settings.json default of "off". +let thinkingLevel = "off"; + +// Decode throughput (tokens/sec) of the most recent assistant response, measured +// from the first streamed token to message end so prompt-eval time is excluded. +let lastTps = 0; +let genFirstTokenAt = 0; +let sawToken = false; + +// Set from inside the footer factory so events can trigger a re-render. +let requestRender: (() => void) | undefined; + +function collapseHome(p: string): string { + const home = process.env.HOME || ""; + if (p === home) return "~"; + if (home && p.startsWith(`${home}/`)) return `~${p.slice(home.length)}`; + return p; +} + +function fmtTokens(n: number): string { + return n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`; +} + +/** Cumulative session tokens, plus the last request's input tokens (~ current + * context-window fill). */ +function tokenStats(ctx: ExtensionContext): { + input: number; + output: number; + lastInput: number; +} { + let input = 0; + let output = 0; + let lastInput = 0; + for (const entry of ctx.sessionManager.getBranch()) { + if (entry.type === "message" && entry.message.role === "assistant") { + const usage = (entry.message as AssistantMessage).usage; + if (!usage) continue; // aborted stream: no usage — a throwing render kills the TUI + input += usage.input; + output += usage.output; + lastInput = usage.input; + } + } + return { input, output, lastInput }; +} + +/** Show/hide the above-editor warning when the context window is nearly full. */ +function updateContextWidget(ctx: ExtensionContext): void { + if (ctx.mode !== "tui") return; + const contextWindow = ctx.model?.contextWindow; + const { lastInput } = tokenStats(ctx); + if (!contextWindow || lastInput <= 0) { + ctx.ui.setWidget(CTX_WIDGET, undefined); + return; + } + const pct = (lastInput / contextWindow) * 100; + if (pct < CTX_WARN_AT) { + ctx.ui.setWidget(CTX_WIDGET, undefined); + return; + } + const th = ctx.ui.theme; + const col = pct >= 90 ? "error" : "warning"; + const line = + th.fg(col, `${ICON.warn} context ${pct.toFixed(0)}%`) + + th.fg("dim", ` (${fmtTokens(lastInput)}/${fmtTokens(contextWindow)}) — /compact soon`); + ctx.ui.setWidget(CTX_WIDGET, [line]); +} + +/** A gentle catppuccin "breathing" pulse for the streaming working-indicator. */ +function pulseIndicator(ctx: ExtensionContext) { + const th = ctx.ui.theme; + return { + frames: [th.fg("dim", "·"), th.fg("muted", "•"), th.fg("accent", "●"), th.fg("muted", "•")], + intervalMs: 120, + }; +} + +export default function (pi: ExtensionAPI) { + let enabled = true; + + pi.on("thinking_level_select", async (event) => { + thinkingLevel = event.level; + requestRender?.(); + }); + + pi.on("model_select", async () => { + requestRender?.(); + }); + + // --- t/s timing: bracket each assistant response and clock its generation --- + pi.on("message_start", async (event) => { + if (event.message?.role !== "assistant") return; + sawToken = false; + genFirstTokenAt = 0; + }); + + pi.on("message_update", async (event) => { + if (event.message?.role !== "assistant") return; + if (!sawToken) { + sawToken = true; + genFirstTokenAt = Date.now(); + } + }); + + pi.on("message_end", async (event, ctx) => { + if (event.message?.role !== "assistant") return; + const output = (event.message as AssistantMessage).usage?.output ?? 0; + const secs = genFirstTokenAt ? (Date.now() - genFirstTokenAt) / 1000 : 0; + if (secs > 0 && output > 0) lastTps = output / secs; + requestRender?.(); + if (enabled) updateContextWidget(ctx); + }); + + const install = (ctx: ExtensionContext) => { + if (ctx.mode !== "tui") return; + ctx.ui.setWorkingIndicator(pulseIndicator(ctx)); + updateContextWidget(ctx); + ctx.ui.setFooter((tui, theme, footerData) => { + const sep = theme.fg("dim", " │ "); + requestRender = () => tui.requestRender(); + const unsub = footerData.onBranchChange(() => tui.requestRender()); + return { + dispose: unsub, + invalidate() {}, + render(width: number): string[] { + const parts: string[] = []; + + // Preserve extension statuses (plan-mode "⏸ plan", etc.) up front. + for (const status of footerData.getExtensionStatuses().values()) { + if (status) parts.push(status); + } + + // 󰉋 directory + parts.push(theme.fg("accent", ICON.dir) + " " + theme.fg("text", collapseHome(ctx.cwd))); + + // 󰘬 git branch (only inside a repo) + const branch = footerData.getGitBranch(); + if (branch) parts.push(theme.fg("accent", ICON.git) + " " + theme.fg("success", branch)); + + // 󰚩 model (+ · thinking level) + if (ctx.model?.id) { + let model = theme.fg("accent", ICON.model) + " " + theme.fg("text", ctx.model.id); + if (thinkingLevel && thinkingLevel !== "off") { + model += theme.fg("dim", ` · ${thinkingLevel}`); + } + parts.push(model); + } + + // 󰈚 context window usage (% when the window is known, else raw tokens) + const { input, output, lastInput } = tokenStats(ctx); + if (lastInput > 0) { + const contextWindow = ctx.model?.contextWindow; + let seg = theme.fg("accent", ICON.context) + " "; + if (contextWindow) { + const pct = Math.min(100, (lastInput / contextWindow) * 100); + const col = pct < 50 ? "success" : pct < 80 ? "warning" : "error"; + seg += theme.fg(col, `${pct.toFixed(0)}%`) + theme.fg("dim", ` (${fmtTokens(lastInput)})`); + } else { + seg += theme.fg("text", fmtTokens(lastInput)); + } + parts.push(seg); + } + + // 󰓅 tokens/sec of the last response + if (lastTps > 0) { + parts.push( + theme.fg("accent", ICON.tps) + " " + theme.fg("text", lastTps.toFixed(1)) + theme.fg("dim", " t/s"), + ); + } + + // ↑ ↓ cumulative session tokens (Claude's 💰 slot; local models are $0) + if (input || output) { + parts.push(theme.fg("dim", `${ICON.up}${fmtTokens(input)} ${ICON.down}${fmtTokens(output)}`)); + } + + return [truncateToWidth(parts.join(sep), width)]; + }, + }; + }); + }; + + pi.on("session_start", async (_event, ctx) => { + if (enabled) install(ctx); + }); + + pi.registerCommand("statusbar", { + description: "Toggle the custom pi status bar footer", + handler: async (_args, ctx) => { + enabled = !enabled; + if (enabled) { + install(ctx); + ctx.ui.notify("Custom status bar enabled", "info"); + } else { + ctx.ui.setFooter(undefined); + ctx.ui.setWorkingIndicator(); // restore pi's default spinner + ctx.ui.setWidget(CTX_WIDGET, undefined); + ctx.ui.notify("Default footer restored", "info"); + } + }, + }); +} diff --git a/common/.pi/agent/extensions/vim-toggle.ts b/common/.pi/agent/extensions/vim-toggle.ts new file mode 100644 index 0000000..cf7f704 --- /dev/null +++ b/common/.pi/agent/extensions/vim-toggle.ts @@ -0,0 +1,44 @@ +/** + * `/vim` — toggle the modal input editor off/on + * + * The modal editor itself is now the **npm package `pi-vim`** (declared in + * `settings.json > packages`, configured under `settings.json > piVim`). That + * package registers no commands, so this file restores the `/vim` toggle the + * local `vim-editor.ts` fork used to provide — the fork is kept beside it as + * `vim-editor.ts.disabled` (see ../../CLAUDE.md). + * + * OFF — drop the custom editor component, which puts pi's stock editor back for + * the rest of the session (handy for a big paste, or any key the modal + * layer swallows). + * ON — `ctx.reload()`, the same flow as `/reload`, re-runs extension discovery + * and so re-installs pi-vim's editor. That reloads statusbar/plan-mode too + * and re-instantiates *this* file, which is why turning it back on needs no + * bookkeeping: the fresh instance starts at `enabled = true` again. + * + * Nothing is installed on `session_start` — pi-vim already does that, so this + * file only ever reacts to the command and never races it for the editor slot. + */ + +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; + +export default function (pi: ExtensionAPI) { + // pi-vim installs its editor at session_start, so we start out enabled. + let enabled = true; + + pi.registerCommand("vim", { + description: "Toggle the vim (modal) input editor", + handler: async (_args, ctx) => { + if (ctx.mode !== "tui") return; // editor components are TUI-only + if (enabled) { + ctx.ui.setEditorComponent(undefined); + enabled = false; + ctx.ui.notify("Default editor restored — /vim to re-enable", "info"); + return; + } + // Notify *before* awaiting: after the reload this call frame belongs to + // the pre-reload instance, and its UI handle is on the way out. + ctx.ui.notify("Vim editor enabled", "info"); + await ctx.reload(); + }, + }); +} diff --git a/common/.pi/agent/keybindings.json b/common/.pi/agent/keybindings.json new file mode 100644 index 0000000..f6903b9 --- /dev/null +++ b/common/.pi/agent/keybindings.json @@ -0,0 +1,5 @@ +{ + "app.thinking.cycle": ["ctrl+shift+t"], + "tui.editor.cursorUp": ["up", "ctrl+p"], + "app.model.cycleForward": ["alt+p"] +} diff --git a/common/.pi/agent/models.json b/common/.pi/agent/models.json new file mode 100644 index 0000000..c7c43c7 --- /dev/null +++ b/common/.pi/agent/models.json @@ -0,0 +1,214 @@ +{ + "providers": { + "duskadiy": { + "baseUrl": "https://llm.duskadiy.com/api/v1", + "api": "openai-completions", + "apiKey": "$DUSKADIY_API_KEY", + "compat": { + "supportsDeveloperRole": false, + "supportsReasoningEffort": false, + "maxTokensField": "max_tokens" + }, + "models": [ + { + "id": "gemma-4-E4B-it-UD-Q8_K_XL", + "name": "Gemma 4 E4B · 64k · vision — fast generalist, long docs", + "reasoning": false, + "input": ["text", "image"], + "contextWindow": 65536, + "maxTokens": 16384, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gemma-4-26B-A4B-it-UD-IQ4_XS", + "name": "Gemma 4 26B · 24k · vision — quality generalist", + "reasoning": false, + "input": ["text", "image"], + "contextWindow": 24576, + "maxTokens": 4096, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3-Coder-30B-Instruct-UD-Q3_K_XL", + "name": "Qwen3 Coder 30B · 32k — main agent coder", + "reasoning": false, + "input": ["text"], + "contextWindow": 32768, + "maxTokens": 4096, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3-Coder-Next-UD-IQ3_XXS", + "name": "Qwen3 Coder Next 80B · 128k — long sessions", + "reasoning": false, + "input": ["text"], + "contextWindow": 131072, + "maxTokens": 16384, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS", + "name": "Qwen3.6 35B · 24k · vision — daily driver", + "reasoning": false, + "input": ["text", "image"], + "contextWindow": 24576, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3.6-35B-A3B-Thinking", + "name": "Qwen3.6 35B Thinking · 24k · vision — hard problems", + "reasoning": true, + "compat": { "thinkingFormat": "qwen-chat-template" }, + "input": ["text", "image"], + "contextWindow": 24576, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3.5-9B-UD-Q6_K_XL", + "name": "Qwen3.5 9B · 32k · vision — quick tasks", + "reasoning": true, + "compat": { "thinkingFormat": "qwen-chat-template" }, + "input": ["text", "image"], + "contextWindow": 32768, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "GLM-4.7-Flash-UD-Q4_K_XL", + "name": "GLM-4.7 Flash · 24k — quality coder", + "reasoning": true, + "compat": { "thinkingFormat": "qwen-chat-template" }, + "input": ["text"], + "contextWindow": 24576, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-oss-20b", + "name": "gpt-oss 20B · 64k — fast reasoning + tools", + "reasoning": true, + "input": ["text"], + "contextWindow": 65536, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-oss-20b-low", + "name": "gpt-oss 20B low · 64k — snappy answers", + "reasoning": true, + "input": ["text"], + "contextWindow": 65536, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + } + ] + }, + "localcpp": { + "baseUrl": "http://192.168.0.204:11343/v1", + "api": "openai-completions", + "apiKey": "no-key-required", + "compat": { + "supportsDeveloperRole": false, + "supportsReasoningEffort": false, + "maxTokensField": "max_tokens" + }, + "models": [ + { + "id": "gemma-4-E4B-it-UD-Q8_K_XL", + "name": "Gemma 4 E4B · 64k · vision — fast generalist, long docs", + "reasoning": false, + "input": ["text", "image"], + "contextWindow": 65536, + "maxTokens": 16384, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gemma-4-26B-A4B-it-UD-IQ4_XS", + "name": "Gemma 4 26B · 24k · vision — quality generalist", + "reasoning": false, + "input": ["text", "image"], + "contextWindow": 24576, + "maxTokens": 4096, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3-Coder-30B-Instruct-UD-Q3_K_XL", + "name": "Qwen3 Coder 30B · 32k — main agent coder", + "reasoning": false, + "input": ["text"], + "contextWindow": 32768, + "maxTokens": 4096, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3-Coder-Next-UD-IQ3_XXS", + "name": "Qwen3 Coder Next 80B · 128k — long sessions", + "reasoning": false, + "input": ["text"], + "contextWindow": 131072, + "maxTokens": 16384, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS", + "name": "Qwen3.6 35B · 24k · vision — daily driver", + "reasoning": false, + "input": ["text", "image"], + "contextWindow": 24576, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3.6-35B-A3B-Thinking", + "name": "Qwen3.6 35B Thinking · 24k · vision — hard problems", + "reasoning": true, + "compat": { "thinkingFormat": "qwen-chat-template" }, + "input": ["text", "image"], + "contextWindow": 24576, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "Qwen3.5-9B-UD-Q6_K_XL", + "name": "Qwen3.5 9B · 32k · vision — quick tasks", + "reasoning": true, + "compat": { "thinkingFormat": "qwen-chat-template" }, + "input": ["text", "image"], + "contextWindow": 32768, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "GLM-4.7-Flash-UD-Q4_K_XL", + "name": "GLM-4.7 Flash · 24k — quality coder", + "reasoning": true, + "compat": { "thinkingFormat": "qwen-chat-template" }, + "input": ["text"], + "contextWindow": 24576, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-oss-20b", + "name": "gpt-oss 20B · 64k — fast reasoning + tools", + "reasoning": true, + "input": ["text"], + "contextWindow": 65536, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + }, + { + "id": "gpt-oss-20b-low", + "name": "gpt-oss 20B low · 64k — snappy answers", + "reasoning": true, + "input": ["text"], + "contextWindow": 65536, + "maxTokens": 8192, + "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } + } + ] + } + } +} diff --git a/.pi/agent/prompts/commit.md b/common/.pi/agent/prompts/commit.md similarity index 100% rename from .pi/agent/prompts/commit.md rename to common/.pi/agent/prompts/commit.md diff --git a/.pi/agent/prompts/review.md b/common/.pi/agent/prompts/review.md similarity index 100% rename from .pi/agent/prompts/review.md rename to common/.pi/agent/prompts/review.md diff --git a/common/.pi/agent/settings.json b/common/.pi/agent/settings.json new file mode 100644 index 0000000..5818613 --- /dev/null +++ b/common/.pi/agent/settings.json @@ -0,0 +1,64 @@ +{ + "defaultProvider": "duskadiy", + "defaultModel": "gemma-4-26B-A4B-it-UD-IQ4_XS", + "defaultThinkingLevel": "off", + "theme": "catppuccin-mocha", + "quietStartup": false, + "defaultProjectTrust": "ask", + "enableInstallTelemetry": false, + "enabledModels": [ + "duskadiy/*", + "localcpp/*" + ], + "compaction": { + "enabled": true, + "reserveTokens": 6144, + "keepRecentTokens": 6000 + }, + "retry": { + "enabled": true, + "maxRetries": 3, + "baseDelayMs": 2000, + "provider": { + "timeoutMs": 3600000 + } + }, + "httpIdleTimeoutMs": 600000, + "npmCommand": [ + "fnm", + "exec", + "--using=22", + "--", + "npm" + ], + "showHardwareCursor": true, + "piVim": { + "clipboardMirror": "yank", + "exCommand": { + "piDispatch": true, + "copyInputToClipboard": false + }, + "modeColors": { + "insert": "success", + "normal": "accent", + "visual": "warning", + "ex": "bashMode" + }, + "borderSync": { + "insert": "host", + "normal": "host", + "visual": "host", + "ex": "host" + }, + "labelSync": { + "insert": "mode", + "normal": "mode", + "visual": "mode", + "ex": "mode" + } + }, + "lastChangelogVersion": "0.83.0", + "packages": [ + "npm:pi-vim" + ] +} \ No newline at end of file diff --git a/.pi/agent/themes/catppuccin-mocha.json b/common/.pi/agent/themes/catppuccin-mocha.json similarity index 100% rename from .pi/agent/themes/catppuccin-mocha.json rename to common/.pi/agent/themes/catppuccin-mocha.json diff --git a/fl/.config/evremap/evremap.service b/fl/.config/evremap/evremap.service new file mode 100644 index 0000000..987fc11 --- /dev/null +++ b/fl/.config/evremap/evremap.service @@ -0,0 +1,14 @@ +[Unit] +Description=Evremap Service +After=multi-user.target + +[Service] +WorkingDirectory=/ +# Config path must stay absolute: this is a system unit running as root, so ~ or $HOME would resolve to /root. +ExecStart=bash -c "/usr/bin/evremap remap /home/anon/.config/evremap/evremap.toml -d 0" +Restart=always +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target diff --git a/fl/.config/evremap/evremap.toml b/fl/.config/evremap/evremap.toml new file mode 100644 index 0000000..fded262 --- /dev/null +++ b/fl/.config/evremap/evremap.toml @@ -0,0 +1,60 @@ +# The name of the device to remap. +# Run `sudo evremap list-devices` to see the devices available +# on your system. +device_name = "Logitech ERGO K860" + +# If you have multiple devices with the same name, you can optionally +# specify the `phys` value that is printed by the `list-devices` subcommand +# phys = "usb-0000:07:00.3-2.1.1/input0" + +# Configure CAPSLOCK as a Dual Role key. +# Holding it produces LEFTCTRL, but tapping it +# will produce ESC. +# Both `tap` and `hold` can expand to multiple output keys. +[[dual_role]] +input = "KEY_CAPSLOCK" +hold = ["KEY_LEFTCTRL"] +tap = ["KEY_ESC"] + +# Turn RIGHT ALT into a vim-arrow layer. +# We first neuter it: instead of ever emitting KEY_RIGHTALT (which apps treat +# as "activate menu" on a bare tap), it becomes KEY_F13 -- a real keycode that +# is unbound in every app, so tapping or holding right alt alone does nothing. +# The chords below then turn F13 + h/j/k/l into the arrow keys. +[[dual_role]] +input = "KEY_RIGHTALT" +hold = ["KEY_F13"] +tap = ["KEY_F13"] + +[[remap]] +input = ["KEY_F13", "KEY_H"] +output = ["KEY_LEFT"] + +[[remap]] +input = ["KEY_F13", "KEY_J"] +output = ["KEY_DOWN"] + +[[remap]] +input = ["KEY_F13", "KEY_K"] +output = ["KEY_UP"] + +[[remap]] +input = ["KEY_F13", "KEY_L"] +output = ["KEY_RIGHT"] + +[[remap]] +input = ["KEY_F13", "KEY_U"] +output = ["KEY_PAGEUP"] + +[[remap]] +input = ["KEY_F13", "KEY_D"] +output = ["KEY_PAGEDOWN"] + +# Word-wise motion: emit Ctrl+Arrow so it jumps by word. +[[remap]] +input = ["KEY_F13", "KEY_B"] +output = ["KEY_LEFTCTRL", "KEY_LEFT"] + +[[remap]] +input = ["KEY_F13", "KEY_W"] +output = ["KEY_LEFTCTRL", "KEY_RIGHT"] diff --git a/fl/.config/fish/host.fish b/fl/.config/fish/host.fish new file mode 100644 index 0000000..aaf5454 --- /dev/null +++ b/fl/.config/fish/host.fish @@ -0,0 +1,2 @@ +# fl runs the llama.cpp router as a systemd service (llama.service) — no shell abbr needed. +# Presets: ~/.config/llamacpp/config.ini; see ~/.config/llamacpp/README.md. diff --git a/.config/hypr/animations.conf b/fl/.config/hypr/animations.conf similarity index 100% rename from .config/hypr/animations.conf rename to fl/.config/hypr/animations.conf diff --git a/.config/hypr/hyprland.conf b/fl/.config/hypr/hyprland.conf similarity index 94% rename from .config/hypr/hyprland.conf rename to fl/.config/hypr/hyprland.conf index 7086fef..f6f36ee 100644 --- a/.config/hypr/hyprland.conf +++ b/fl/.config/hypr/hyprland.conf @@ -38,10 +38,8 @@ exec-once = wl-paste --type text --watch cliphist store # clipboard store text d exec-once = wl-paste --type image --watch cliphist store # clipboard store image data exec-once = $scrPath/swwwallpaper.sh # start wallpaper daemon exec-once = $scrPath/batterynotify.sh # battery notification -exec-once = xremap --watch ~/.config/xremap/config.yml -exec-once = hypridle - - +# (fl remaps keys with evremap (see .config/evremap/) — the old xremap exec-once was dead, no config here) +exec-once = swayidle -w timeout 180 'swaylock --timestr %H:%M & brightnessctl -s & brightnessctl set 15' resume 'brightnessctl -r' # █▀▀ █▄░█ █░█ # ██▄ █░▀█ ▀▄▀ @@ -112,7 +110,7 @@ device { # See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ dwindle { - # pseudotile = yes + pseudotile = yes preserve_split = yes } diff --git a/.config/hypr/keybindings.conf b/fl/.config/hypr/keybindings.conf similarity index 95% rename from .config/hypr/keybindings.conf rename to fl/.config/hypr/keybindings.conf index b8aef2e..4c0d632 100644 --- a/.config/hypr/keybindings.conf +++ b/fl/.config/hypr/keybindings.conf @@ -1,3 +1,4 @@ + # █▄▀ █▀▀ █▄█ █▄▄ █ █▄░█ █▀▄ █ █▄░█ █▀▀ █▀ # █░█ ██▄ ░█░ █▄█ █ █░▀█ █▄▀ █ █░▀█ █▄█ ▄█ @@ -13,7 +14,10 @@ $file = dolphin $browser = librewolf $yt = freetube + $wm=Window Management +$d=[$wm] + # Window/Session actions bind = $mainMod, Q, exec, $scrPath/dontkillsteam.sh # close focused window @@ -22,7 +26,7 @@ bind = $mainMod, Delete, exit, # kill hyprland session bind = $mainMod, W, togglefloating, # toggle the window between focus and float bind = $mainMod, G, togglegroup, # toggle the window between focus and group bind = Alt, Return, fullscreen, # toggle the window between focus and fullscreen -bind = $mainMod, L, exec, hyprlock --immediate #& sleep 0.3 && hyprctl dispatch dpms off +bind = $mainMod, L, exec, swaylock --timestr %H:%M # launch lock screen bind = Ctrl+Alt, W, exec, killall waybar || waybar # toggle waybar # bind = $mainMod+Shift, F, exec, $scrPath/windowpin.sh # toggle pin on focused window # bind = $mainMod, Backspace, exec, $scrPath/logoutlaunch.sh # launch logout menu @@ -36,18 +40,14 @@ bind = $mainMod+Ctrl, T, exec, tilix # launch tilix terminal emulator bind = $mainMod, E, exec, $file # launch file manager bind = $mainMod, F, exec, $yt # launch yt alternative bind = $mainMod, B, exec, $browser # launch web browser -bind = $mainMod + Shift, B, exec, chromium -bind = $mainMod + Ctrl, B, exec, thorium-browser bind = $mainMod, G, exec, gajim # launch messanger bind = $mainMod, K, exec, keepassxc # launch password manager -bind = $mainMod, Escape, exec, dunstctl close bindd = $mainMod, N, Neovim, exec, $term -e nvim bindd = $mainMod, R, $d pyradio, exec, kitty --session ~/.config/kitty/music.conf -bindd = $mainMod, I, $d c2, exec, kitty --session ~/.config/kitty/c2.conf -bindd = $mainMod, O, $d runners, exec, kitty --session ~/.config/kitty/runners.conf bindd = Ctrl+Shift, Escape, Btop, exec, kitty -e btop bindd = Ctrl+Alt, Delete, Btop, exec, kitty -e btop # bind = Ctrl+Shift, Escape, exec, $scrPath/sysmonlaunch.sh # launch system monitor (htop/btop or fallback to top) +# bind = $mainMod+Shift, B, exec, $browser, title: ^(LibreWolf Private Browsing)$ # launch private web browser # Rofi menus @@ -87,8 +87,8 @@ bind = , Print, exec, $scrPath/screenshot.sh p # all monitors screenshot capture # Custom scripts bind = $mainMod+Alt, G, exec, $scrPath/gamemode.sh # disable hypr effects for gamemode -# bind = $mainMod+Alt, Right, exec, $scrPath/swwwallpaper.sh -n # next wallpaper -# bind = $mainMod+Alt, Left, exec, $scrPath/swwwallpaper.sh -p # previous wallpaper +bind = $mainMod+Alt, Right, exec, $scrPath/swwwallpaper.sh -n # next wallpaper +bind = $mainMod+Alt, Left, exec, $scrPath/swwwallpaper.sh -p # previous wallpaper # bind = $mainMod+Alt, Up, exec, $scrPath/wbarconfgen.sh n # next waybar mode # bind = $mainMod+Alt, Down, exec, $scrPath/wbarconfgen.sh p # previous waybar mode bind = $mainMod+Shift, R, exec, pkill -x rofi || $scrPath/wallbashtoggle.sh -m # launch wallbash mode select menu @@ -175,7 +175,7 @@ bind = $mainMod+Alt, S, movetoworkspacesilent, special bind = $mainMod, S, togglespecialworkspace, # Toggle focused window split -bindd = $mainMod, J, $d toggle split, layoutmsg, togglesplit +bind = $mainMod, J, togglesplit $d=[$ws|Navigation|Relative workspace] bindd = $mainMod Control Shift, Right, $d change active workspace forwards , workspace, r+1 diff --git a/fl/.config/hypr/monitors.conf b/fl/.config/hypr/monitors.conf new file mode 100644 index 0000000..8da85ed --- /dev/null +++ b/fl/.config/hypr/monitors.conf @@ -0,0 +1,62 @@ + +# █▀▄▀█ █▀█ █▄░█ █ ▀█▀ █▀█ █▀█ █▀ +# █░▀░█ █▄█ █░▀█ █ ░█░ █▄█ █▀▄ ▄█ + +# Set your monitor configuration here +# See https://wiki.hyprland.org/Configuring/Monitors/ +# For a sample file, please refer to https://github.com/prasanthrangan/hyprdots/blob/main/Configs/.config/hypr/monitors.t2 + +$mainScreen = eDP-1 +$leftScreen = DP-2 +$rightScreen = HDMI-A-1 + +monitor = $mainScreen, 1366x768@60.14, 0x0, 1 # main screen +monitor = $leftScreen, 1920x1080@60, -1920x0, 1 # left +monitor = $rightScreen, 1920x1080@60,1366x0, 1 # right + +# monitor=HDMI-A-1,1920x1080@60,-1366x0,1 # hdmi on left +# monitor=HDMI-A-1,1920x1080@60,0x-768,1 # hdmi on top +# monitor=DP-2,640x480@59.94Hz,0x0,1 # vga + +# monitor = HDMI-A-1, 1366x768@60.00Hz, auto , 1, mirror, eDP-1 #mirror lapscreen +# monitor = eDP-1, 1920x1080@60.00Hz, auto , 1, mirror, HDMI-A-1 #main hdmi, lapscreen mirrors + +# monitor = , preferred, auto, 1, mirror, DP-2 # dmz projector +# monitor = eDP-1, 1920x1080@60, auto , 1, mirror, DP-2 #mirror vga test + +#Main Monitor +workspace = 1, monitor:$mainScreen, default:true +workspace = 2, monitor:$mainScreen +workspace = 3, monitor:$mainScreen +workspace = 4, monitor:$mainScreen +workspace = 5, monitor:$mainScreen +workspace = 6, monitor:$mainScreen +workspace = 7, monitor:$mainScreen +workspace = 8, monitor:$mainScreen +workspace = 9, monitor:$mainScreen +workspace = 10,monitor:$mainScreen + +# Second Monitor +workspace = 11, monitor:$rightScreen, default:true +workspace = 12, monitor:$rightScreen +workspace = 13, monitor:$rightScreen +workspace = 14, monitor:$rightScreen +workspace = 15, monitor:$rightScreen +workspace = 16, monitor:$rightScreen +workspace = 17, monitor:$rightScreen +workspace = 18, monitor:$rightScreen +workspace = 19, monitor:$rightScreen +workspace = 20, monitor:$rightScreen + +# Third Monitor +workspace = 21, monitor:$leftScreen, default:true +workspace = 22, monitor:$leftScreen +workspace = 23, monitor:$leftScreen +workspace = 24, monitor:$leftScreen +workspace = 25, monitor:$leftScreen +workspace = 26, monitor:$leftScreen +workspace = 27, monitor:$leftScreen +workspace = 28, monitor:$leftScreen +workspace = 29, monitor:$leftScreen +workspace = 30, monitor:$leftScreen + diff --git a/.config/hypr/userprefs.conf b/fl/.config/hypr/userprefs.conf similarity index 61% rename from .config/hypr/userprefs.conf rename to fl/.config/hypr/userprefs.conf index 94787b3..9ad1750 100644 --- a/.config/hypr/userprefs.conf +++ b/fl/.config/hypr/userprefs.conf @@ -30,6 +30,8 @@ bind = $mainMod+Alt, XF86MonBrightnessUp, exec, hyprshade off # disable blue lig # █░░ ▄▀█ █░█ █▄░█ █▀▀ █░█ # █▄▄ █▀█ █▄█ █░▀█ █▄▄ █▀█ +# exec-once = swayidle -w timeout 600 'swaylock' timeout 900 'hyprctl dispatch dpms off' resume 'hyprctl dispatch dpms on' # lock after 10 mins, sleep after 15 mins // install swayidle +# exec-once = swayidle -w timeout 1200 'swaylock; hyprctl dispatch dpms off' resume 'hyprctl dispatch dpms on' timeout 1800 'systemctl suspend' # lock and sleep after 20 mins, suspend after 30 mins // install swayidle # exec-once = libinput-gestures // install libinput-gestures # exec-once = swayosd-server # enable swayosd service # exec-once = `pkexec swayosd-libinput-backend` # swayosd service for keyboard input (requires to be run in a subshell) @@ -37,24 +39,24 @@ bind = $mainMod+Alt, XF86MonBrightnessUp, exec, hyprshade off # disable blue lig # █░█░█ █ █▄░█ █▀▄ █▀█ █░█░█   █▀█ █░█ █░░ █▀▀ █▀ # ▀▄▀▄▀ █ █░▀█ █▄▀ █▄█ ▀▄▀▄▀   █▀▄ █▄█ █▄▄ ██▄ ▄█ -windowrule = opacity 0.90 0.90, match:class ^(neovide)$ -windowrule = opacity 0.90 0.90, match:class ^(firefox)$ -windowrule = opacity 0.80 0.80, match:class ^(betterbird)$ -windowrule = opacity 0.80 0.80, match:class ^(org.qbittorrent.qBittorrent)$ -windowrule = opacity 0.80 0.80, match:class ^(org.gajim.Gajim)$ -windowrule = opacity 0.80 0.80, match:class ^(FreeTube)$ -windowrule = opacity 0.80 0.80, match:class ^(librewolf)$ -windowrule = opacity 0.80 0.60, match:class ^(obsidian)$ -windowrule = opacity 0.80 0.80, match:class ^(VSCodium)$ -windowrule = opacity 0.80 0.80, match:class ^(codium-url-handler)$ -windowrule = opacity 0.80 0.80, match:class ^(code-oss)$ -windowrule = opacity 0.80 0.80, match:class ^(Code)$ -windowrule = opacity 0.80 0.80, match:class ^(kitty)$ -windowrule = opacity 0.70 0.70, match:class ^(chromium)$ -windowrule = opacity 0.70 0.70, match:class ^(tilix)$ -windowrule = opacity 0.70 0.70, match:class ^(rustdesk)$ -windowrule = opacity 0.70 0.70, match:class ^(Ferdium)$ -windowrule = opacity 0.70 0.70, match:class ^(org.keepassxc.KeePassXC)$ +windowrulev2 = opacity 0.90 0.90,class:^(neovide)$ +windowrulev2 = opacity 0.90 0.90,class:^(firefox)$ +windowrulev2 = opacity 0.80 0.80,class:^(betterbird)$ +windowrulev2 = opacity 0.80 0.80,class:^(org.qbittorrent.qBittorrent)$ +windowrulev2 = opacity 0.80 0.80,class:^(org.gajim.Gajim)$ +windowrulev2 = opacity 0.80 0.80,class:^(Chromium)$ +windowrulev2 = opacity 0.80 0.80,class:^(FreeTube)$ +windowrulev2 = opacity 0.80 0.80,class:^(LibreWolf)$ +windowrulev2 = opacity 0.80 0.60,class:^(obsidian)$ +windowrulev2 = opacity 0.80 0.80,class:^(VSCodium)$ +windowrulev2 = opacity 0.80 0.80,class:^(codium-url-handler)$ +windowrulev2 = opacity 0.80 0.80,class:^(code-oss)$ +windowrulev2 = opacity 0.80 0.80,class:^(Code)$ +windowrulev2 = opacity 0.80 0.80,class:^(kitty)$ +windowrulev2 = opacity 0.70 0.70,class:^(tilix)$ +windowrulev2 = opacity 0.70 0.70,class:^(rustdesk)$ +windowrulev2 = opacity 0.70 0.70,class:^(Ferdium)$ +windowrulev2 = opacity 0.70 0.70,class:^(org.keepassxc.KeePassXC)$ # █░█ █▀▄▀█ # ▀▄▀ █░▀░█ diff --git a/fl/.config/hypr/windowrules.conf b/fl/.config/hypr/windowrules.conf new file mode 100644 index 0000000..f3f112c --- /dev/null +++ b/fl/.config/hypr/windowrules.conf @@ -0,0 +1,91 @@ + +# █░█░█ █ █▄░█ █▀▄ █▀█ █░█░█   █▀█ █░█ █░░ █▀▀ █▀ +# ▀▄▀▄▀ █ █░▀█ █▄▀ █▄█ ▀▄▀▄▀   █▀▄ █▄█ █▄▄ ██▄ ▄█ + +# See https://wiki.hyprland.org/Configuring/Window-Rules/ +# hyprctl clients to get names + +windowrulev2 = opacity 0.80 0.80,class:^(code-url-handler)$ +windowrulev2 = opacity 0.80 0.80,class:^(code-insiders-url-handler)$ +windowrulev2 = opacity 0.80 0.80,class:^(org.kde.dolphin)$ +windowrulev2 = opacity 0.80 0.80,class:^(org.kde.ark)$ +windowrulev2 = opacity 0.80 0.80,class:^(nwg-look)$ +windowrulev2 = opacity 0.80 0.80,class:^(qt5ct)$ +windowrulev2 = opacity 0.80 0.80,class:^(qt6ct)$ +windowrulev2 = opacity 0.80 0.80,class:^(kvantummanager)$ +windowrulev2 = opacity 0.80 0.70,class:^(org.pulseaudio.pavucontrol)$ +windowrulev2 = opacity 0.80 0.70,class:^(lagrange)$ +windowrulev2 = opacity 0.60 0.60,class:^(com.github.wwmm.easyeffects)$ +windowrulev2 = opacity 0.80 0.70,class:^(blueman-manager)$ +windowrulev2 = opacity 0.80 0.70,class:^(nm-applet)$ +windowrulev2 = opacity 0.80 0.70,class:^(nm-connection-editor)$ +windowrulev2 = opacity 0.80 0.70,class:^(org.kde.polkit-kde-authentication-agent-1)$ +windowrulev2 = opacity 0.80 0.70,class:^(polkit-gnome-authentication-agent-1)$ +windowrulev2 = opacity 0.80 0.70,class:^(org.freedesktop.impl.portal.desktop.gtk)$ +windowrulev2 = opacity 0.80 0.70,class:^(org.freedesktop.impl.portal.desktop.hyprland)$ +windowrulev2 = opacity 0.70 0.70,class:^([Ss]team)$ + +windowrulev2 = opacity 0.90 0.90,class:^(com.github.rafostar.Clapper)$ # Clapper-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(com.github.tchx84.Flatseal)$ # Flatseal-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(hu.kramo.Cartridges)$ # Cartridges-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(com.obsproject.Studio)$ # Obs-Qt +windowrulev2 = opacity 0.80 0.80,class:^(gnome-boxes)$ # Boxes-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(discord)$ # Discord-Electron +windowrulev2 = opacity 0.80 0.80,class:^(WebCord)$ # WebCord-Electron +windowrulev2 = opacity 0.80 0.80,class:^(ArmCord)$ # ArmCord-Electron +windowrulev2 = opacity 0.80 0.80,class:^(app.drey.Warp)$ # Warp-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(net.davidotek.pupgui2)$ # ProtonUp-Qt +windowrulev2 = opacity 0.80 0.80,class:^(yad)$ # Protontricks-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(Signal)$ # Signal-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(io.github.alainm23.planify)$ # planify-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(io.gitlab.theevilskeleton.Upscaler)$ # Upscaler-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(io.gitlab.adhami3310.Impression)$ # Impression-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(io.missioncenter.MissionCenter)$ # MissionCenter-Gtk +windowrulev2 = opacity 0.80 0.80,class:^(io.github.flattool.Warehouse)$ # Warehouse-Gtk + +windowrulev2 = float,class:^(org.kde.dolphin)$,title:^(Progress Dialog — Dolphin)$ +windowrulev2 = float,class:^(org.kde.dolphin)$,title:^(Copying — Dolphin)$ +windowrulev2 = float,title:^(About Mozilla Firefox)$ +windowrulev2 = float,class:^(firefox)$,title:^(Picture-in-Picture)$ +windowrulev2 = float,class:^(firefox)$,title:^(Library)$ +windowrulev2 = float,class:^(kitty)$,title:^(top)$ +windowrulev2 = float,class:^(kitty)$,title:^(btop)$ +windowrulev2 = float,class:^(kitty)$,title:^(htop)$ +windowrulev2 = float,class:^(vlc)$ +windowrulev2 = float,class:^(kvantummanager)$ +windowrulev2 = float,class:^(qt5ct)$ +windowrulev2 = float,class:^(qt6ct)$ +windowrulev2 = float,class:^(nwg-look)$ +windowrulev2 = float,class:^(org.kde.ark)$ +windowrulev2 = float,class:^(org.pulseaudio.pavucontrol)$ +windowrulev2 = float,class:^(blueman-manager)$ +windowrulev2 = float,class:^(nm-applet)$ +windowrulev2 = float,class:^(nm-connection-editor)$ +windowrulev2 = float,class:^(org.kde.polkit-kde-authentication-agent-1)$ + +windowrulev2 = float,class:^(Signal)$ # Signal-Gtk +windowrulev2 = float,class:^(com.github.rafostar.Clapper)$ # Clapper-Gtk +windowrulev2 = float,class:^(app.drey.Warp)$ # Warp-Gtk +windowrulev2 = float,class:^(net.davidotek.pupgui2)$ # ProtonUp-Qt +windowrulev2 = float,class:^(yad)$ # Protontricks-Gtk +windowrulev2 = float,class:^(eog)$ # Imageviewer-Gtk +windowrulev2 = float,class:^(io.github.alainm23.planify)$ # planify-Gtk +windowrulev2 = float,class:^(io.gitlab.theevilskeleton.Upscaler)$ # Upscaler-Gtk +windowrulev2 = float,class:^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gkk +windowrulev2 = float,class:^(io.gitlab.adhami3310.Impression)$ # Impression-Gtk +windowrulev2 = float,class:^(io.missioncenter.MissionCenter)$ # MissionCenter-Gtk + + +# █░░ ▄▀█ █▄█ █▀▀ █▀█   █▀█ █░█ █░░ █▀▀ █▀ +# █▄▄ █▀█ ░█░ ██▄ █▀▄   █▀▄ █▄█ █▄▄ ██▄ ▄█ + +layerrule = blur,rofi +layerrule = ignorezero,rofi +layerrule = blur,notifications +layerrule = ignorezero,notifications +layerrule = blur,swaync-notification-window +layerrule = ignorezero,swaync-notification-window +layerrule = blur,swaync-control-center +layerrule = ignorezero,swaync-control-center +layerrule = blur,logout_dialog diff --git a/fl/.config/kglobalshortcutsrc b/fl/.config/kglobalshortcutsrc new file mode 100644 index 0000000..3971edf --- /dev/null +++ b/fl/.config/kglobalshortcutsrc @@ -0,0 +1,344 @@ +[ActivityManager] +_k_friendly_name=Activity Manager +switch-to-activity-a8fc2925-fa8d-449a-a9be-48ffe253c900=Meta+Shift+Tab,none,Switch to activity "Work" +switch-to-activity-c0551195-8905-4f5f-9be3-86124cedc90e=Meta+Tab,none,Switch to activity "Personal" + +[KDE Keyboard Layout Switcher] +Switch keyboard layout to English (US)=none,none,Switch keyboard layout to English (US) +Switch keyboard layout to Serbian (Cyrillic, ZE and ZHE swapped)=none,none,Switch keyboard layout to Serbian (Cyrillic\\, ZE and ZHE swapped) +Switch keyboard layout to Serbian (Latin, with guillemets)=none,none,Switch keyboard layout to Serbian (Latin\\, with guillemets) +Switch to Last-Used Keyboard Layout=none,Meta+Alt+L,Switch to Last-Used Keyboard Layout +Switch to Next Keyboard Layout=none,Meta+Alt+K,Switch to Next Keyboard Layout +_k_friendly_name=Keyboard Layout Switcher + +[kaccess] +Toggle Screen Reader On and Off=Meta+Alt+S,Meta+Alt+S,Toggle Screen Reader On and Off +_k_friendly_name=Accessibility + +[kmix] +_k_friendly_name=KMix +decrease_microphone_volume=Microphone Volume Down,Microphone Volume Down,Decrease Microphone Volume +decrease_volume=Volume Down,Volume Down,Decrease Volume +decrease_volume_small=Shift+Volume Down,Shift+Volume Down,Decrease Volume by 1% +increase_microphone_volume=Microphone Volume Up,Microphone Volume Up,Increase Microphone Volume +increase_volume=Volume Up,Volume Up,Increase Volume +increase_volume_small=Shift+Volume Up,Shift+Volume Up,Increase Volume by 1% +mic_mute=Microphone Mute\tMeta+Volume Mute,Microphone Mute\tMeta+Volume Mute,Mute Microphone +mute=Volume Mute,Volume Mute,Mute +push_to_talk=none,none,Push to talk + +[ksmserver] +Halt Without Confirmation=none,none,Shut Down Without Confirmation +Lock Session=Meta+L,Screensaver\tMeta+L,Lock Session +Log Out=Ctrl+Alt+Del,Ctrl+Alt+Del,Show Logout Screen +Log Out Without Confirmation=Meta+Del,none,Log Out Without Confirmation +LogOut=none,none,Log Out +Reboot=none,none,Reboot +Reboot Without Confirmation=none,none,Reboot Without Confirmation +Shut Down=none,none,Shut Down +_k_friendly_name=Session Management + +[kwin] +Activate Window Demanding Attention=Meta+Ctrl+A,Meta+Ctrl+A,Activate Window Demanding Attention +Cycle Overview=none,none,Cycle through Overview and Grid View +Cycle Overview Opposite=none,none,Cycle through Grid View and Overview +Decrease Opacity=none,none,Decrease Opacity of Active Window by 5% +Edit Tiles=none,Meta+T,Toggle Tiles Editor +Expose=Ctrl+F9\tMeta+F9,Ctrl+F9\tMeta+F9,Toggle Present Windows (Current desktop) +ExposeAll=Launch (C)\tCtrl+F10\tMeta+F10,Launch (C)\tCtrl+F10\tMeta+F10,Toggle Present Windows (All desktops) +ExposeClass=Ctrl+F7\tMeta+F7,Ctrl+F7\tMeta+F7,Toggle Present Windows (Window class) +ExposeClassCurrentDesktop=none,none,Toggle Present Windows (Window class on current desktop) +Grid View=none,Meta+G,Toggle Grid View +Increase Opacity=none,none,Increase Opacity of Active Window by 5% +Kill Window=Meta+Ctrl+Esc,Meta+Ctrl+Esc,Kill Window +Move Tablet to Next LogicalOutput=none,none,Move the tablet to the next output +Move Tablet to Next Output=none,none,Move the tablet to the next output +MoveMouseToCenter=Meta+F6,Meta+F6,Move Mouse to Center +MoveMouseToFocus=Meta+F5,Meta+F5,Move Mouse to Focus +MoveZoomDown=none,none,Move Zoomed Area Downwards +MoveZoomLeft=none,none,Move Zoomed Area to Left +MoveZoomRight=none,none,Move Zoomed Area to Right +MoveZoomUp=none,none,Move Zoomed Area Upwards +Overview=none,Meta+W,Toggle Overview +Setup Window Shortcut=none,none,Setup Window Shortcut +Show Desktop=Meta+D,Meta+D,Peek at Desktop +Switch One Desktop Down=none,Meta+Ctrl+Down,Switch One Desktop Down +Switch One Desktop Up=Meta+Ctrl+Shift+Up,Meta+Ctrl+Up,Switch One Desktop Up +Switch One Desktop to the Left=none,Meta+Ctrl+Left,Switch One Desktop to the Left +Switch One Desktop to the Right=none,Meta+Ctrl+Right,Switch One Desktop to the Right +Switch Window Down=Meta+Shift+K\tMeta+Shift+Down\tMeta+Ctrl+Down\tMeta+Ctrl+J\tCtrl+Alt+Down\tCtrl+Alt+J,Meta+Alt+Down,Switch to Window Below +Switch Window Left=Meta+Shift+H\tMeta+Shift+Left\tMeta+Ctrl+H\tCtrl+Alt+Left\tCtrl+Alt+H,Meta+Alt+Left,Switch to Window to the Left +Switch Window Right=Meta+Shift+L\tMeta+Shift+Right\tMeta+Ctrl+L\tCtrl+Alt+Right\tCtrl+Alt+L,Meta+Alt+Right,Switch to Window to the Right +Switch Window Up=Meta+Shift+J\tMeta+Shift+Up\tMeta+Ctrl+Up\tMeta+Ctrl+K\tCtrl+Alt+Up\tCtrl+Alt+K,Meta+Alt+Up,Switch to Window Above +Switch to Desktop 1=Ctrl+F1\tMeta+1\tMeta+Ctrl+Left,Ctrl+F1\tMeta+F1,Switch to Desktop 1 +Switch to Desktop 10=Meta+0,none,Switch to Desktop 10 +Switch to Desktop 11=none,none,Switch to Desktop 11 +Switch to Desktop 12=none,none,Switch to Desktop 12 +Switch to Desktop 13=none,none,Switch to Desktop 13 +Switch to Desktop 14=none,none,Switch to Desktop 14 +Switch to Desktop 15=none,none,Switch to Desktop 15 +Switch to Desktop 16=none,none,Switch to Desktop 16 +Switch to Desktop 17=none,none,Switch to Desktop 17 +Switch to Desktop 18=none,none,Switch to Desktop 18 +Switch to Desktop 19=none,none,Switch to Desktop 19 +Switch to Desktop 2=Ctrl+F2\tMeta+2\tMeta+Ctrl+Right,Ctrl+F2\tMeta+F2,Switch to Desktop 2 +Switch to Desktop 20=none,none,Switch to Desktop 20 +Switch to Desktop 21=none,none,Switch to Desktop 21 +Switch to Desktop 22=none,none,Switch to Desktop 22 +Switch to Desktop 23=none,none,Switch to Desktop 23 +Switch to Desktop 24=none,none,Switch to Desktop 24 +Switch to Desktop 25=none,none,Switch to Desktop 25 +Switch to Desktop 3=Ctrl+F3\tMeta+3,Ctrl+F3\tMeta+F3,Switch to Desktop 3 +Switch to Desktop 4=Ctrl+F4\tMeta+4,Ctrl+F4\tMeta+F4,Switch to Desktop 4 +Switch to Desktop 5=Meta+5,none,Switch to Desktop 5 +Switch to Desktop 6=Meta+6,none,Switch to Desktop 6 +Switch to Desktop 7=Meta+7,none,Switch to Desktop 7 +Switch to Desktop 8=Meta+8,none,Switch to Desktop 8 +Switch to Desktop 9=Meta+9,none,Switch to Desktop 9 +Switch to Next Desktop=Meta+Ctrl+Shift+Right\tMeta+Ctrl+Shift+L,none,Switch to Next Desktop +Switch to Next Screen=none,none,Switch to Next Screen +Switch to Previous Desktop=Meta+Ctrl+Shift+Left\tMeta+Ctrl+Shift+H,none,Switch to Previous Desktop +Switch to Previous Screen=none,none,Switch to Previous Screen +Switch to Screen 0=none,none,Switch to Screen 0 +Switch to Screen 1=none,none,Switch to Screen 1 +Switch to Screen 2=none,none,Switch to Screen 2 +Switch to Screen 3=none,none,Switch to Screen 3 +Switch to Screen 4=none,none,Switch to Screen 4 +Switch to Screen 5=none,none,Switch to Screen 5 +Switch to Screen 6=none,none,Switch to Screen 6 +Switch to Screen 7=none,none,Switch to Screen 7 +Switch to Screen Above=none,none,Switch to Screen Above +Switch to Screen Below=none,none,Switch to Screen Below +Switch to Screen to the Left=none,none,Switch to Screen to the Left +Switch to Screen to the Right=none,none,Switch to Screen to the Right +Toggle Night Color=none,none,Suspend/Resume Night Light +Toggle Window Raise/Lower=none,none,Toggle Window Raise/Lower +ToggleMouseClick=Meta+*,Meta+*,Toggle Mouse Click Animation +Walk Through Windows=Alt+Tab,Alt+Tab\tMeta+Tab,Walk Through Windows +Walk Through Windows (Reverse)=Alt+Shift+Backtab,Alt+Shift+Tab\tMeta+Shift+Tab,Walk Through Windows (Reverse) +Walk Through Windows Alternative=none,none,Walk Through Windows Alternative +Walk Through Windows Alternative (Reverse)=none,none,Walk Through Windows Alternative (Reverse) +Walk Through Windows of Current Application=Alt+`,Alt+`\tMeta+`,Walk Through Windows of Current Application +Walk Through Windows of Current Application (Reverse)=Alt+~,Alt+~\tMeta+~,Walk Through Windows of Current Application (Reverse) +Walk Through Windows of Current Application Alternative=none,none,Walk Through Windows of Current Application Alternative +Walk Through Windows of Current Application Alternative (Reverse)=none,none,Walk Through Windows of Current Application Alternative (Reverse) +Window Above Other Windows=none,none,Keep Window Above Others +Window Below Other Windows=none,none,Keep Window Below Others +Window Close=Alt+F4\tMeta+Q,Alt+F4,Close Window +Window Custom Quick Tile Bottom=none,none,Custom Quick Tile Window to the Bottom +Window Custom Quick Tile Left=none,none,Custom Quick Tile Window to the Left +Window Custom Quick Tile Right=none,none,Custom Quick Tile Window to the Right +Window Custom Quick Tile Top=none,none,Custom Quick Tile Window to the Top +Window Fullscreen=Alt+Return\tShift+F11,none,Make Window Fullscreen +Window Grow Horizontal=none,none,Expand Window Horizontally +Window Grow Vertical=none,none,Expand Window Vertically +Window Lower=none,none,Lower Window +Window Maximize=Meta+Return\tMeta+PgUp,Meta+PgUp,Maximize Window +Window Maximize Horizontal=none,none,Maximize Window Horizontally +Window Maximize Vertical=none,none,Maximize Window Vertically +Window Minimize=Meta+Esc\tMeta+PgDown,Meta+PgDown,Minimize Window +Window Move=Meta+Z,none,Move Window +Window Move Center=none,none,Move Window to the Center +Window No Border=none,none,Toggle Window Titlebar and Frame +Window On All Desktops=Meta+Shift+F,none,Keep Window on All Desktops +Window One Desktop Down=none,Meta+Ctrl+Shift+Down,Window One Desktop Down +Window One Desktop Up=none,Meta+Ctrl+Shift+Up,Window One Desktop Up +Window One Desktop to the Left=none,Meta+Ctrl+Shift+Left,Window One Desktop to the Left +Window One Desktop to the Right=none,Meta+Ctrl+Shift+Right,Window One Desktop to the Right +Window One Screen Down=none,none,Move Window One Screen Down +Window One Screen Up=none,none,Move Window One Screen Up +Window One Screen to the Left=none,none,Move Window One Screen to the Left +Window One Screen to the Right=none,none,Move Window One Screen to the Right +Window Operations Menu=Alt+F3,Alt+F3,Window Menu +Window Pack Down=Meta+Alt+Down,none,Move Window Down +Window Pack Left=Meta+Alt+Left,none,Move Window Left +Window Pack Right=Meta+Alt+Right,none,Move Window Right +Window Pack Up=Meta+Alt+Up,none,Move Window Up +Window Quick Tile Bottom=Meta+Down\tMeta+Alt+\\,\tMeta+Alt+K,Meta+Down,Quick Tile Window to the Bottom +Window Quick Tile Bottom Left=none,none,Quick Tile Window to the Bottom Left +Window Quick Tile Bottom Right=none,none,Quick Tile Window to the Bottom Right +Window Quick Tile Left=Meta+Left\tMeta+Alt+H,Meta+Left,Quick Tile Window to the Left +Window Quick Tile Right=Meta+Right\tMeta+Alt+L,Meta+Right,Quick Tile Window to the Right +Window Quick Tile Top=Meta+Up\tMeta+Alt+J,Meta+Up,Quick Tile Window to the Top +Window Quick Tile Top Left=none,none,Quick Tile Window to the Top Left +Window Quick Tile Top Right=none,none,Quick Tile Window to the Top Right +Window Raise=none,none,Raise Window +Window Resize=Meta+X,none,Resize Window +Window Restore=Meta+Backspace,Meta+Backspace,Restore Window +Window Shrink Horizontal=none,none,Shrink Window Horizontally +Window Shrink Vertical=none,none,Shrink Window Vertically +Window to Desktop 1=Meta+Ctrl+1,none,Window to Desktop 1 +Window to Desktop 10=Meta+Ctrl+0,none,Window to Desktop 10 +Window to Desktop 11=none,none,Window to Desktop 11 +Window to Desktop 12=none,none,Window to Desktop 12 +Window to Desktop 13=none,none,Window to Desktop 13 +Window to Desktop 14=none,none,Window to Desktop 14 +Window to Desktop 15=none,none,Window to Desktop 15 +Window to Desktop 16=none,none,Window to Desktop 16 +Window to Desktop 17=none,none,Window to Desktop 17 +Window to Desktop 18=none,none,Window to Desktop 18 +Window to Desktop 19=none,none,Window to Desktop 19 +Window to Desktop 2=Meta+Ctrl+2,none,Window to Desktop 2 +Window to Desktop 20=none,none,Window to Desktop 20 +Window to Desktop 21=none,none,Window to Desktop 21 +Window to Desktop 22=none,none,Window to Desktop 22 +Window to Desktop 23=none,none,Window to Desktop 23 +Window to Desktop 24=none,none,Window to Desktop 24 +Window to Desktop 25=none,none,Window to Desktop 25 +Window to Desktop 3=Meta+Ctrl+3,none,Window to Desktop 3 +Window to Desktop 4=Meta+Ctrl+4,none,Window to Desktop 4 +Window to Desktop 5=Meta+Ctrl+5,none,Window to Desktop 5 +Window to Desktop 6=Meta+Ctrl+6,none,Window to Desktop 6 +Window to Desktop 7=Meta+Ctrl+7,none,Window to Desktop 7 +Window to Desktop 8=Meta+Ctrl+8,none,Window to Desktop 8 +Window to Desktop 9=Meta+Ctrl+9,none,Window to Desktop 9 +Window to Next Desktop=Meta+Ctrl+Alt+Right\tMeta+Ctrl+Alt+L,none,Window to Next Desktop +Window to Next Screen=none,Meta+Shift+Right,Move Window to Next Screen +Window to Previous Desktop=Meta+Ctrl+Alt+Left\tMeta+Ctrl+Alt+H,none,Window to Previous Desktop +Window to Previous Screen=none,Meta+Shift+Left,Move Window to Previous Screen +Window to Screen 0=none,none,Move Window to Screen 0 +Window to Screen 1=none,none,Move Window to Screen 1 +Window to Screen 2=none,none,Move Window to Screen 2 +Window to Screen 3=none,none,Move Window to Screen 3 +Window to Screen 4=none,none,Move Window to Screen 4 +Window to Screen 5=none,none,Move Window to Screen 5 +Window to Screen 6=none,none,Move Window to Screen 6 +Window to Screen 7=none,none,Move Window to Screen 7 +_k_friendly_name=KWin +disableInputCapture=Meta+Shift+Esc,Meta+Shift+Esc,Disable Active Input Capture +view_actual_size=Meta+),Meta+0,Zoom to Actual Size +view_zoom_in=Meta++,Meta++\tMeta+=,Zoom In +view_zoom_out=Meta+_,Meta+-,Zoom Out + +[mediacontrol] +_k_friendly_name=Media Controller +mediavolumedown=none,none,Media volume down +mediavolumeup=none,none,Media volume up +nextmedia=Media Next,Media Next,Media playback next +pausemedia=Media Pause,Media Pause,Pause media playback +playmedia=none,none,Play media playback +playpausemedia=Media Play,Media Play,Play/Pause media playback +previousmedia=Media Previous,Media Previous,Media playback previous +seekbackwardmedia=Media Rewind,Media Rewind,Media playback seek backward 5s +seekbackwardmedialong=none,none,Media playback seek backward 30s +seekforwardmedia=Media Fast Forward,Media Fast Forward,Media playback seek forward 5s +seekforwardmedialong=none,none,Media playback seek forward 30s +stopmedia=Media Stop,Media Stop,Stop media playback + +[org_kde_powerdevil] +Decrease Keyboard Brightness=Keyboard Brightness Down,Keyboard Brightness Down,Decrease Keyboard Brightness +Decrease Screen Brightness=Monitor Brightness Down,Monitor Brightness Down,Decrease Screen Brightness +Decrease Screen Brightness Small=Shift+Monitor Brightness Down,Shift+Monitor Brightness Down,Decrease Screen Brightness by 1% +Hibernate=Hibernate,Hibernate,Hibernate +Increase Keyboard Brightness=Keyboard Brightness Up,Keyboard Brightness Up,Increase Keyboard Brightness +Increase Screen Brightness=Monitor Brightness Up,Monitor Brightness Up,Increase Screen Brightness +Increase Screen Brightness Small=Shift+Monitor Brightness Up,Shift+Monitor Brightness Up,Increase Screen Brightness by 1% +PowerDown=Power Down,Power Down,Power Down +PowerOff=Power Off,Power Off,Power Off +Sleep=Sleep,Sleep,Suspend +Toggle Keyboard Backlight=Keyboard Light On/Off,Keyboard Light On/Off,Toggle Keyboard Backlight +Turn Off Screen=none,none,Turn Off Screen +_k_friendly_name=Power Management +powerProfile=Battery,Battery\tMeta+B,Switch Power Profile + +[plasmashell] +Slideshow Wallpaper Next Image=Meta+Ctrl+Alt+Shift+Right,none,Next Wallpaper Image +_k_friendly_name=plasmashell +activate application launcher=Meta,Meta\tAlt+F1,Activate Application Launcher +activate task manager entry 1=none,Meta+1,Activate Task Manager Entry 1 +activate task manager entry 10=none,none,Activate Task Manager Entry 10 +activate task manager entry 2=none,Meta+2,Activate Task Manager Entry 2 +activate task manager entry 3=none,Meta+3,Activate Task Manager Entry 3 +activate task manager entry 4=none,Meta+4,Activate Task Manager Entry 4 +activate task manager entry 5=none,Meta+5,Activate Task Manager Entry 5 +activate task manager entry 6=none,Meta+6,Activate Task Manager Entry 6 +activate task manager entry 7=none,Meta+7,Activate Task Manager Entry 7 +activate task manager entry 8=none,Meta+8,Activate Task Manager Entry 8 +activate task manager entry 9=none,Meta+9,Activate Task Manager Entry 9 +activate widget 47=none,none,Activate System Monitor Widget +activate widget 61=none,none,Activate Activity Pager Widget +activate widget 69=none,none,Activate System Monitor Widget +activate widget 70=none,none,Activate System Monitor Widget +clear history=none,none,Clear Notification History +clear-history=none,none,Clear Clipboard History +clipboard_action=Meta+Ctrl+X,Meta+Ctrl+X,Automatic Action Popup Menu +cycle-panels=Meta+Alt+P,Meta+Alt+P,Move keyboard focus between panels +cycleNextAction=none,none,Next History Item +cyclePrevAction=none,none,Previous History Item +edit_clipboard=none,none,Edit Contents… +manage activities=none,Meta+Q,Show Activity Switcher +next activity=none,none,Walk through activities +previous activity=Meta+Shift+A,none,Walk through activities (Reverse) +repeat_action=Meta+Ctrl+R,none,Manually Invoke Action on Current Clipboard +show dashboard=Ctrl+F12,Ctrl+F12,Show Desktop +show-barcode=none,none,Show Barcode… +show-on-mouse-pos=Meta+V,Meta+V,Show Clipboard Items at Mouse Position +switch to next activity=none,none,Switch to Next Activity +switch to previous activity=none,none,Switch to Previous Activity +toggle do not disturb=none,none,Toggle do not disturb + +[services][Alacritty.desktop] +_launch=Meta+Shift+T + +[services][FreeTube.desktop] +_launch=Meta+F + +[services][betterbird.desktop] +_launch=Meta+M + +[services][chromium.desktop] +_launch=Meta+Shift+B + +[services][com.mitchellh.ghostty.desktop] +_launch=none + +[services][ferdium.desktop] +_launch=Meta+Y + +[services][kitty.desktop] +_launch=Ctrl+Alt+T\tMeta+T + +[services][librewolf.desktop] +_launch=Meta+B + +[services][net.local.kitty-2.desktop] +_launch=Meta+I + +[services][net.local.kitty-3.desktop] +_launch=Meta+O + +[services][net.local.kitty.desktop] +_launch=Meta+R + +[services][nvim.desktop] +_launch=Meta+C + +[services][org.gajim.Gajim.desktop] +StartChat=Meta+G + +[services][org.kde.dolphin.desktop] +_launch=Meta+E + +[services][org.kde.krunner.desktop] +_launch=Search\tAlt+Space\tAlt+F2\tCtrl+Alt+S\tMeta+A\tMeta+S + +[services][org.kde.plasma-systemmonitor.desktop] +_launch=Ctrl+Shift+Esc + +[services][org.kde.spectacle.desktop] +RecordRegion=Meta+Shift+R +RecordWindow=none +RectangularRegionScreenShot=Meta+P +_launch=Print\tMeta+Shift+P\tMeta+Shift+S + +[services][org.keepassxc.KeePassXC.desktop] +_launch=Meta+K + +[services][st.desktop] +_launch=Meta+Ctrl+T + +[services][systemsettings.desktop] +_launch=\tTools + +[services][thorium-browser.desktop] +_launch=Meta+Ctrl+B diff --git a/fl/.config/llamacpp/README.md b/fl/.config/llamacpp/README.md new file mode 100644 index 0000000..5d9657c --- /dev/null +++ b/fl/.config/llamacpp/README.md @@ -0,0 +1,166 @@ +# Local LLM server — setup & usage + +llama.cpp in **router mode** serving ~10 model presets from one 16 GB GPU. +Last full tune: **2026-07-16** (decode numbers measured then via `bench.py`, unless a preset comment in `config.ini` says otherwise). +Last full clean sweep: **2026-08-08 03:22** — all 11 presets loaded and ran green (baseline 2.0 GB) after the week's coder-race/KV-fix/gemma-sidecar round. + +- **Box**: AMD RX 7600 XT 16 GB (ROCm, ~288 GB/s) + Ryzen 5600X (6 cores) + 48 GB RAM. + ⚠ The GPU **also drives the display** — see [VRAM safety](#vram-safety-the-golden-rules). +- **Endpoints**: LAN `http://$SERVER:11343/v1` · remote `https:///api/v1` + (own reverse proxy to the same server; requires an API key. Only the LAN endpoint is keyless). + `$SERVER` = the box's LAN address. +- **Files here**: `config.ini` (model presets — the section names ARE the API model ids), + `bench.py` (safety-first benchmark), `bench-results.md` / `bench-history.md` (ledgers, generated). + +## How it runs + +systemd unit **`llama.service`** (on the server, under a dedicated service user) runs a script wrapping: + +``` +llama-server --port 11343 --host 0.0.0.0 --models-max 1 --models-preset ~/.config/llamacpp/config.ini +``` + +- Router mode: each requested model loads on demand in a child process. +- **`--models-max 1` is deliberate and load-bearing**: at most one model resident; requesting + another LRU-evicts the current one *first*. Anything ≥2 lets two ~13 GB models stack → + VRAM overcommit → GTT spill → **whole-PC freeze**. Do not raise it casually. + (`sleep-idle-seconds` per preset is the second line of defense.) +- Models live in `~/software/models/` (paths in `config.ini` must stay absolute — llama-server + does not expand `~` or env vars in preset values). mmproj/draft files must match the exact + filename in the preset (watch for trailing spaces when renaming downloads!). + +**Deploying config changes**: this dir is stowed into `~/.config/llamacpp/` as per-file +symlinks, so on the server `dotsync` (pull; re-stows only if files were added/removed) then +`sudo systemctl restart llama.service` — the router reads presets once at startup. +After ANY preset change: restart + `./bench.py -m ` and check the guards. + +## VRAM safety (the golden rules) + +ROCm does not OOM cleanly — an overcommitted model spills into GTT (system RAM), +starves the desktop and freezes the PC (reboot). Hard-won rules: + +1. Keep every preset's benched **VRAM free ≥ 2.5 GB** (idle desktop uses ~1.3 of 17.2). +2. File size > VRAM is **fine** for MoE — `n-cpu-moe N` keeps the first N layers' experts + in system RAM. It is the main tuning knob: higher = safer/slower, lower = faster. +3. Bench with a **clean baseline** (close the browser): a dirty baseline both skews the + margins and costs ~3–4 t/s of decode (GPU compute contention — measured 2026-07-08). +4. Bench after every change; config-edit without restart = numbers from the *old* config. + +## Model roster (reference numbers = clean night sweeps, 2026-07-16) + +| model id | decode t/s | ctx | extras | role | +|---|---|---|---|---| +| `gemma-4-E4B-it-UD-Q8_K_XL` | **57.3** | **96k** | vision, MTP, prefill 185 t/s | fast small generalist, long docs, images (ctx 96k ✓ 08-08, n_ctx_train 131072) | +| `Qwen3.6-35B-A3B-Thinking` | 38.8 | 24k | MTP, vision, reasoning | hard problems, slow-but-smart answers | +| `gpt-oss-20b` | 38.4 | **64k** | reasoning, tools | fast reasoning + tool use · fast long-context (37 t/s vs Coder-Next 16; 131k ruled out) | +| `gpt-oss-20b-low` | 37.2 | **64k** | reasoning LOW, TTFT 0.8 s | same model, snappy answers (no long preamble) | +| `Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS` | 35.9 | 24k | MTP, vision, thinking OFF | **daily driver** — compact instant answers | +| `gemma-4-26B-A4B-it-UD-IQ4_XS` | 33.9 | 24k | vision, MTP | quality generalist + best vision | +| `Qwen3.5-9B-UD-Q6_K_XL` | 32.1 | 32k | vision, MTP, reasoning | small Qwen, quick tasks | +| `Qwen3-Coder-30B-Instruct-UD-Q3_K_XL` | **~30** clean / 27.0 evening @moe12 | 32k | — | **main agent coder** — won the 2026-08-06 quant race; moe12 verified evening 08-07 + clean 08-08 | +| `GLM-4.7-Flash-UD-Q4_K_XL` | **21.5** @moe22 | 24k | reasoning | quality coder (opencode subagents); KV fix + moe22 ✓ verified clean 08-08 | +| `Qwen3-Coder-Next-UD-IQ3_XXS` | 16.0 | **128k** | 80B-A3B | long-session coder (128k ctx) | +| `Qwen3-Embedding-0.6B` | 33 (CPU) | 8k | CPU-only, `/v1/embeddings` | RAG/search embedder (not a chat model) | + +Expected run-to-run spread: MTP models swing ±15% with draft **acceptance rate** (content- +dependent); CPU-heavy presets (Coder-Next, embedder) dip under daytime CPU contention. +Treat clean night runs as the reference; don't retune on daytime deltas. + +## Which model, when + +- **Agent coding loops** (edit/test cycles): `Qwen3-Coder-30B` — best speed/quality balance at 32k. +- **Hard code, reviews, tricky bugs**: `GLM-4.7-Flash` — strongest 30B-class coder, slightly slower. +- **Marathon sessions / huge conversation history**: `Qwen3-Coder-Next` — 128k ctx at only + 6 GB VRAM (hybrid attention). Caveat: ~33 t/s prefill means it's for *growing* sessions + (`cache-reuse` makes turns incremental), **not** for cold-dumping 100k tokens. +- **Everyday questions**: `Qwen3.6-35B-A3B` — no reasoning preamble, 36 t/s. +- **Hard reasoning**: `35B-Thinking` (quality) or `gpt-oss-20b` (speed + tool use). + Their multi-second TTFT is the reasoning phase streaming first — not a slow load. + For quick interactive gpt-oss answers use `gpt-oss-20b-low` (reasoning effort low). +- **Images**: `gemma-4-26B` for quality, `gemma-4-E4B` or `Qwen3.5-9B` for speed, + `Qwen3.6-35B` when you want the daily driver to see the screenshot. +- **Long one-shot documents**: `gemma-4-E4B` — 185 t/s prefill eats 64k in ~6 min + (Coder-Next would take ~30 min to prefill the same). +- **RAG embeddings**: point Open WebUI etc. at `Qwen3-Embedding-0.6B`. Runs on CPU by + design — 0 VRAM, can never contribute to an overcommit. + +## Speech-to-text (separate service) + +STT deliberately does **not** live in this router — it is a CPU-only whisper.cpp service on port +11345, documented in `../whisper/README.md`. Two reasons: `models-max 1` would make every +transcription LRU-evict the resident chat model, and llama.cpp decodes audio via miniaudio +(wav/mp3/flac only) while browsers record opus. + +The router *does* answer `POST /v1/audio/transcriptions` — it rewrites the request into a chat +completion — so `gemma-4-E4B-it-UD-Q8_K_XL` (audio-capable via its mmproj) transcribes uploaded +wav/mp3/flac with no config change. Keep that as the fallback; mind the eviction. + +## Text-to-speech (separate service) + +TTS is **not servable from this router at all** — unlike STT, there is no fallback. `llama-server` +has `--model-vocoder` / `--tts-use-guide-tokens`, but exposes **no `/v1/audio/speech` route**: +the router 404s on it exactly like an undefined path, and `grep -rn "audio/speech"` over the +b10216 tree returns nothing — the only audio route registered is `/v1/audio/transcriptions` (STT). +Worse, `--model-vocoder` is accepted by the server and then **silently ignored**: it is registered +in the arg table and documented, but no server code path consumes it. `--help` listing it is not +evidence it works. `llama-tts` is a CLI. (All verified 2026-08-09.) + +So TTS is a CPU-only Kokoro-82M service on port **11347**, documented in `../tts/README.md` +(repo-side only — not installed on fl yet). The `models-max 1` argument applies even harder there +than for STT: TTS fires on *every* response, so a resident preset would evict the chat model every +single turn. + +## Client wiring + +The same catalog is mirrored in every client — when adding/removing a preset, update all: + +- `common/.config/opencode/opencode.json` (both providers + agent model overrides) +- `common/.pi/agent/models.json` (both providers; `contextWindow` = server `ctx-size`) +- `common/.config/aichat/config.yaml` (both clients) + +Rule: client model **id = config.ini section name**, client context ≤ server `ctx-size`. +The embedder is deliberately absent from chat clients. +2026-08-06: the coder id changed `…-IQ4_XS` → `…-UD-Q3_K_XL` in all six client files +(common + lw overlays) after the quant race; OWUI picks the new id up automatically from +the router, but chats/presets saved against the old id need re-picking. + +## Tuning cheat-sheet + +- **`n-cpu-moe`** (MoE only): experts→CPU. The speed/VRAM dial. Measured curve is gentle — + tune in steps of 2–4 layers, re-bench, keep free ≥2.5 GB. +- **`ctx-size`**: KV cost varies wildly by arch — Coder-Next 64k→128k cost 0.5 GB (hybrid + attn); dense Qwens pay ~1 GB per 16k. Raise only after a bench shows the headroom. +- **Quants**: dense = bandwidth-bound, quant size sets speed directly (9B: Q8→Q6 = +87% + with MTP). MoE = buy quality with a bigger quant, pay in CPU offload (both coders run + 4-bit now; 3-bit was only ~2 t/s faster). Coders want ≥4-bit; chat tolerates UD 3-bit. +- **MTP / spec decode** (`spec-type draft-mtp`): ~1.5–2× decode. Qwen3.5/3.6 embed the + head in the *MTP-repo* GGUF (same filename as plain repo — size is the tell); + gemma-4 uses a separate `mtp-*.gguf` draft file. **No MTP possible yet for**: + GLM-4.7-Flash (conversion drops the head — expected ~1.5× when llama.cpp lands it), + Qwen3-Coder-Next (Qwen3-Next head exists upstream, no GGUF ships it), gpt-oss and + Qwen3-Coder-30B (no head exists). Recheck releases occasionally. +- **KV cache**: `q8_0` K / `q4_0` V everywhere except gpt-oss (attention-sink issues with + quantized KV — keep defaults there). + +## Benchmarking + +``` +./bench.py # full sweep (add -x Qwen3-Embedding-0.6B — it can't chat) +./bench.py -m id1,id2 # just the changed presets +./bench.py -n 512 --ctx 16000 # longer gen + long-context decode column +``` + +⚠ **Benches need the server to themselves**: with `models-max 1`, a request for another +model arriving mid-load LRU **force-kills the loading instance** → fake "failed to load" +rows (and a dirty baseline). Close chat clients / OWUI before sweeping. + +Safety-first: force-unloads resident models via the router API (`POST /models/unload`), +verifies a clean card with rocm-smi before every load, records a ⚠ row and skips +generation when a model lands too tight (free < 1.5 GB or GTT ballooning). +Results: `bench-results.md` (latest) + `bench-history.md` (append-only; diff runs there). + +## Watch list + +- **GLM MTP in llama.cpp** — the single biggest pending win (primary coder ~22 → ~30+). +- **Coder-Next 256k**: if the load log's `n_ctx_train` says 262144, ctx can likely double + again for ~1 GB (KV measured nearly flat). diff --git a/fl/.config/llamacpp/bench.py b/fl/.config/llamacpp/bench.py new file mode 100755 index 0000000..0c8b910 --- /dev/null +++ b/fl/.config/llamacpp/bench.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python3 +""" +llama.cpp router benchmark → writes a results ledger for later tuning. + +SAFETY-FIRST: this GPU also drives the display — VRAM overcommit spills to GTT and can +freeze the whole PC. The script therefore: + - records the BASELINE VRAM before touching anything (leftover model / browser skews everything) + - ensures a CLEAN card before EVERY load (incl. the first): resident models are force-unloaded + via the router API (POST /models/unload), then VRAM is verified with rocm-smi — the poll + covers driver reclaim lag, older builds without the endpoint (sleep-idle fallback), and + desktop apps the router doesn't own. Aborts if the card never drains. + - checks free VRAM right after a model mounts; too tight (--min-free-gb) or GTT ballooning + (--max-gtt-gb, measured against a PER-MODEL baseline — GTT reclaims slowly) → records a + ⚠ row and skips generating; the sweep continues after the drain-wait + - refuses to sweep multiple models blind (no rocm-smi) unless --no-vram-ok + +Per model it records decode/prefill t/s, TTFT, VRAM used/free, and the preset's knobs +(ctx, n-cpu-moe, spec-type) parsed from config.ini. + +Output: bench-results.md (latest run, overwritten) + bench-history.md (every run, appended) +— both next to this script. Run ON the GPU box. Stdlib only. + + ./bench.py # all models the server lists + ./bench.py -m id1,id2 # only these + ./bench.py -x Qwen3-Coder-Next-UD-IQ3_XXS # skip some (e.g. not downloaded yet) + ./bench.py -n 512 --ctx 16000 # longer gen + a long-context decode column +Compare a change: edit config.ini → reload server → re-run → diff runs in bench-history.md. +""" +import argparse, json, os, re, subprocess, sys, time, urllib.request, urllib.error +from datetime import datetime + +HERE = os.path.dirname(os.path.abspath(__file__)) +_smi_warned = False + + +def rocm_mem(kind="vram"): + """(used_bytes, total_bytes) from rocm-smi for 'vram' or 'gtt'; (None, None) if unavailable.""" + global _smi_warned + try: + out = subprocess.run(["rocm-smi", "--showmeminfo", kind], + capture_output=True, text=True, timeout=15).stdout + except Exception: + out = "" + def grab(pat): + m = re.search(pat + r"\s*:?\s*(\d+)", out) + return int(m.group(1)) if m else None + label = "VRAM" if kind == "vram" else "GTT" + used = grab(label + r" Total Used Memory \(B\)") or grab(r"Used Memory \(B\)") + total = grab(label + r" Total Memory \(B\)") + if kind == "vram" and total is None and not _smi_warned: + _smi_warned = True + print(" (rocm-smi not readable here — VRAM columns and freeze guards are OFF)", file=sys.stderr) + return used, total + + +def with_retries(fn, retries, wait, label=""): + """Retry on 5xx/connection errors (model still mounting). A deterministic 'failed to + load' from the router is NOT retried — that's a broken preset, not a slow mount.""" + last = None + for i in range(retries + 1): + try: + return fn() + except urllib.error.HTTPError as e: + if e.code < 500: # 4xx = real client error + raise + body = "" + try: + body = e.read().decode("utf-8", "ignore") + except Exception: + pass + if "failed to load" in body: # deterministic → don't burn retries + raise RuntimeError("model failed to load — check `journalctl -u llama.service`") from None + last = e + except (urllib.error.URLError, ConnectionError, TimeoutError) as e: + last = e + if i < retries: + print(f" … {label} not ready ({last}); waiting {wait}s (try {i + 1}/{retries})", file=sys.stderr) + time.sleep(wait) + raise last + + +def _open(url, key, payload=None, timeout=600): + data = json.dumps(payload).encode() if payload is not None else None + hdrs = {"Authorization": f"Bearer {key}"} + if data: + hdrs["Content-Type"] = "application/json" + return urllib.request.urlopen(urllib.request.Request(url, data=data, headers=hdrs), timeout=timeout) + + +def list_models(base, key): + d = json.load(_open(base.rstrip("/") + "/models", key)) + return [m["id"] for m in d.get("data", [])] + + +def force_unload(base, key): + """Ask the router to unload every resident model (POST /models/unload on the ROOT api, + not /v1). Harmless no-op on builds without the endpoint — callers fall back to polling.""" + root = re.sub(r"/v1/?$", "", base.rstrip("/")) + try: + d = json.load(_open(root + "/models", key, timeout=30)) + except Exception as e: + print(f" (router /models not readable: {e} — relying on sleep-idle drain)", file=sys.stderr) + return + for m in d.get("data", []): + if m.get("status", "loaded") != "loaded": # builds without 'status': try them all + continue + try: + _open(root + "/models/unload", key, {"model": m["id"]}, timeout=30).read() + except Exception: + pass # not loaded / endpoint missing — poll covers it + + +def parse_config(path): + """{preset: {ctx, ncpumoe, spec}} from the router config.ini.""" + sections, sec = {}, None + try: + lines = open(path, encoding="utf-8").read().splitlines() + except OSError: + return {} + for ln in lines: + s = ln.strip() + if s.startswith("[") and s.endswith("]"): + sec = s[1:-1]; sections[sec] = {} + elif sec and "=" in s and not s.startswith("#"): + k, v = s.split("=", 1) + sections[sec][k.strip()] = v.split("#", 1)[0].strip() + return {sec: {"ctx": kv.get("ctx-size", "-"), + "ncpumoe": kv.get("n-cpu-moe", "-"), + "spec": kv.get("spec-type", "-") or "-"} + for sec, kv in sections.items()} + + +def run(base, key, model, prompt, n, temp, timeout): + payload = {"model": model, "messages": [{"role": "user", "content": prompt}], + "max_tokens": n, "temperature": temp, "stream": True, + "stream_options": {"include_usage": True}, + # llama.cpp extension: makes the server embed its own timings in the stream → + # the table shows the SAME predicted_per_second as the llama.cpp web UI + # (without it we fall back to wall-clock estimates, which read lower). + "timings_per_token": True} + t0 = time.perf_counter(); ttft = None; ntok = 0; usage = None; timings = None + for raw in _open(base.rstrip("/") + "/chat/completions", key, payload, timeout): + line = raw.decode("utf-8", "ignore").strip() + if not line.startswith("data:"): + continue + body = line[5:].strip() + if body == "[DONE]": + break + try: + c = json.loads(body) + except ValueError: + continue + ch = c.get("choices") or [{}] + if ch and ch[0].get("delta", {}).get("content"): + if ttft is None: + ttft = time.perf_counter() - t0 + ntok += 1 + if c.get("usage"): + usage = c["usage"] + if c.get("timings"): + timings = c["timings"] + total = time.perf_counter() - t0 + comp = (usage or {}).get("completion_tokens") or ntok + ptok = (usage or {}).get("prompt_tokens") + gen_s = total - (ttft or total) + tg = comp / gen_s if gen_s > 0 else 0.0 + pp = (ptok / ttft) if (ptok and ttft) else None + if timings: # llama.cpp's own numbers are authoritative when the router forwards them + tg = timings.get("predicted_per_second", tg) + pp = timings.get("prompt_per_second", pp) + ptok = timings.get("prompt_n", ptok) + return {"tg": tg, "pp": pp, "ttft": ttft or 0.0, "ptok": ptok or 0} + + +def wait_drain(drain_gb, window_s, label="", base=None, key=None): + """Force-unload resident models via the router API, then poll until VRAM used drops + below drain_gb (card is clean) or the window expires.""" + used, _ = rocm_mem() + if base and used is not None and used > drain_gb * 1e9: + force_unload(base, key) + end = time.time() + window_s + announced = False + while used is not None and used > drain_gb * 1e9 and time.time() < end: + if not announced: + print(f" … waiting for VRAM to drain before {label} ({gb(used)} used)", file=sys.stderr) + announced = True + time.sleep(5) + used, _ = rocm_mem() + return used + + +def gb(b): + return f"{b / 1e9:.1f} GB" if b else "-" + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("-u", "--url", default="http://192.168.0.204:11343/v1") + ap.add_argument("-k", "--key", default="no-key-required") + ap.add_argument("-m", "--models", default="all", help="comma-separated ids, or 'all'") + ap.add_argument("-x", "--skip", default="", help="comma-separated ids to skip") + ap.add_argument("-n", "--tokens", type=int, default=256) + ap.add_argument("-t", "--temp", type=float, default=0.3) + ap.add_argument("--ctx", type=int, default=0, help="also record decode at ~this many prompt tokens") + ap.add_argument("--timeout", type=int, default=600, help="per-request stall timeout (s)") + ap.add_argument("--config", default=os.path.join(HERE, "config.ini")) + ap.add_argument("--out", default=os.path.join(HERE, "bench-results.md")) + ap.add_argument("--history", default=os.path.join(HERE, "bench-history.md")) + ap.add_argument("--retries", type=int, default=4, help="retries while a model is still mounting") + ap.add_argument("--retry-wait", type=int, default=15, help="seconds between retries") + ap.add_argument("--drain-gb", type=float, default=4.0, + help="VRAM considered 'clean' below this (raise it if your desktop alone uses more)") + ap.add_argument("--min-free-gb", type=float, default=1.5, + help="SAFETY: stop the sweep if a loaded model leaves less VRAM free than this") + ap.add_argument("--max-gtt-gb", type=float, default=2.0, + help="SAFETY: flag a model if GTT grows more than this over baseline (= VRAM spilling to RAM)") + ap.add_argument("--unload-wait", type=int, default=360, + help="after a too-tight model, wait up to this long for it to unload before continuing " + "(sleep-idle-seconds keeps models resident up to 300s)") + ap.add_argument("--no-vram-ok", action="store_true", + help="allow a multi-model sweep even though rocm-smi (and thus the freeze guards) is unavailable") + a = ap.parse_args() + + models = list_models(a.url, a.key) if a.models == "all" else [m.strip() for m in a.models.split(",")] + skip = {s.strip() for s in a.skip.split(",") if s.strip()} + models = [m for m in models if m not in skip] + cfg = parse_config(a.config) + + used0, total_vram = rocm_mem("vram") + if total_vram is None and len(models) > 1 and not a.no_vram_ok: + sys.exit("ABORT: rocm-smi is not readable here, so the VRAM/GTT freeze guards can't work.\n" + "Run this ON the GPU box, bench a single model with -m , or pass --no-vram-ok to accept the risk.") + if used0 is not None and used0 > a.drain_gb * 1e9: + print(f"⚠ baseline VRAM used is already {gb(used0)} — a leftover model (sleep-idle keeps them " + f"up to 5 min) or desktop apps (browser!). Results/safety margins will be skewed; " + f"ideally close GPU apps or wait, then re-run.", file=sys.stderr) + + task = "Write a Python function that merges two sorted lists, with a short docstring and one example." + filler = ("The quick brown fox jumps over the lazy dog. " * max(1, a.ctx // 9)) if a.ctx else None + + cols = ["model", "cfg ctx", "n-cpu-moe", "spec", "decode t/s", "prefill t/s", "TTFT s", "VRAM used", "VRAM free"] + if a.ctx: + cols.append(f"decode@{a.ctx // 1000}k") + + latest = open(a.out, "w", encoding="utf-8") + hist = open(a.history, "a", encoding="utf-8") + meta = (f"_Run {datetime.now():%Y-%m-%d %H:%M} · server `{a.url}` · VRAM total {gb(total_vram)} · " + f"baseline used {gb(used0)} · gen {a.tokens} tok · guards: free≥{a.min_free_gb} GB, GTT+≤{a.max_gtt_gb} GB_") + + def emit(line, both=True): + print(line) + latest.write(line + "\n"); latest.flush() + if both: + hist.write(line + "\n"); hist.flush() + + latest.write("# llama.cpp benchmark results (latest run)\n\n") + hist.write(f"\n## run {datetime.now():%Y-%m-%d %H:%M}\n\n") + emit(meta + "\n") + emit("| " + " | ".join(cols) + " |") + emit("|" + "|".join(["---"] * len(cols)) + "|") + + for m in models: + c = cfg.get(m, {}) + base_cells = [m, c.get("ctx", "-"), c.get("ncpumoe", "-"), c.get("spec", "-")] + # A CLEAN card before EVERY load (incl. the first): force-unload via the router API, + # then verify with rocm-smi — double residency = bogus numbers or a freeze. + u = wait_drain(a.drain_gb, a.unload_wait, m, a.url, a.key) + if u is not None and u > a.drain_gb * 1e9: + emit("") + emit(f"_⚠ aborted before {m}: VRAM still {gb(u)} used after {a.unload_wait}s — something " + f"won't unload (leftover model / GPU apps). If that's your normal desktop, raise --drain-gb._") + print(f"\n⚠ aborted: card not clean ({gb(u)} used).", file=sys.stderr) + break + gtt_pre, _ = rocm_mem("gtt") # per-model GTT baseline — GTT reclaims slowly across swaps + try: + # warmup — retries wait out the mount; 'failed to load' aborts immediately + with_retries(lambda: run(a.url, a.key, m, "hi", 8, a.temp, a.timeout), a.retries, a.retry_wait, m) + used, _ = rocm_mem("vram") + gtt, _ = rocm_mem("gtt") + free = (total_vram - used) if (total_vram and used) else None + gtt_delta = (gtt - gtt_pre) if (gtt is not None and gtt_pre is not None) else None + reason = None + if free is not None and free < a.min_free_gb * 1e9: + reason = f"only {gb(free)} VRAM free" + elif gtt_delta is not None and gtt_delta > a.max_gtt_gb * 1e9: + reason = f"GTT grew {gb(gtt_delta)} = spilling to system RAM" + if reason: + # Too tight — do NOT generate (allocates more). Record the row; the sweep continues + # once this model has fully unloaded (see below). + hint = ("raise n-cpu-moe" if c.get("ncpumoe", "-") != "-" + else "lower ctx / smaller quant / add n-cpu-moe if MoE") + cells = base_cells + [f"⚠ {reason} — SKIPPED ({hint})", "", "", gb(used), gb(free)] + if a.ctx: + cells.append("") + else: + r = with_retries(lambda: run(a.url, a.key, m, task, a.tokens, a.temp, a.timeout), + a.retries, a.retry_wait, m) + cells = base_cells + [f"{r['tg']:.1f}", (f"{r['pp']:.0f}" if r['pp'] else "-"), + f"{r['ttft']:.2f}", gb(used), gb(free)] + if a.ctx: + rc = with_retries(lambda: run(a.url, a.key, m, filler + "\n\n" + task, a.tokens, a.temp, a.timeout), + a.retries, a.retry_wait, m) + cells.append(f"{rc['tg']:.1f}") + except Exception as e: + err = f"HTTP {e.code} (check server log)" if isinstance(e, urllib.error.HTTPError) else f"ERROR {e}" + cells = base_cells + [err] + [""] * (len(cols) - len(base_cells) - 1) + emit("| " + " | ".join(str(x) for x in cells) + " |") + # (no post-row wait needed: the pre-load drain above protects the next iteration, + # including after ⚠-tight rows where generation was skipped) + + latest.write("\n_Tuning hints: **VRAM free** = headroom to raise `ctx-size` or lower `n-cpu-moe` " + "(more experts on GPU → faster). Low decode t/s on an offloaded MoE → lower `n-cpu-moe` " + "if free allows. High **baseline used** invalidates the run. For spec/MTP models, check " + "the server log's acceptance rate._\n") + latest.close(); hist.close() + print(f"\nwrote {a.out} (+ appended {a.history})") + + +if __name__ == "__main__": + main() diff --git a/fl/.config/llamacpp/config.ini b/fl/.config/llamacpp/config.ini new file mode 100644 index 0000000..debfa67 --- /dev/null +++ b/fl/.config/llamacpp/config.ini @@ -0,0 +1,457 @@ +# llama.cpp model config — AMD RX 7600 XT (ROCm) · 16 GB (17.16 GB total) +# ═════════════════════════════════════════════════════════════════════════════ +# ⚠⚠ VRAM SAFETY — READ THIS. This GPU ALSO DRIVES THE DISPLAY. If a model asks for +# more VRAM than is free, ROCm does NOT OOM cleanly — it spills into system RAM +# (GTT), which starves the desktop and FREEZES THE WHOLE PC (reboot required). +# → TARGET ≥ ~2.5 GB free (≤ ~14.5 GB used). HARD FLOOR 1.5 GB (bench.py guard stops there). +# Idle desktop uses ~1.3 GB of 17.16 — but a browser/leftover model can hold 1-3 GB more, +# so measure with a CLEAN baseline (bench.py prints "baseline used"; close GPU apps first; +# sleep-idle keeps a model resident up to 5 min after last use). +# → Verify with bench.py → ledger: bench-results.md (latest) + bench-history.md (all runs). +# → If a model is tight: LOWER ctx-size, or RAISE n-cpu-moe (MoE), or use a smaller quant. +# The values below are deliberately CONSERVATIVE after freeze incidents — raise ctx +# only after bench.py confirms the headroom. +# ═════════════════════════════════════════════════════════════════════════════ +# Speed notes: decode is memory-bandwidth-bound (~288 GB/s). Prefer MoE (few active +# params). For MoE that doesn't fit, n-cpu-moe offloads expert FFNs to CPU (48 GB RAM) — +# this is FASTER than letting VRAM spill to GTT, and frees VRAM. threads = 6 (5600X cores). +# Runtime: `sudo rocm-smi --setperflevel high`; `watch -n1 rocm-smi --showmeminfo vram`. + +# ───────────────────────────────────────────────────────────────────────────── +# Coding models +# ───────────────────────────────────────────────────────────────────────────── + +[Qwen3-Coder-Next-UD-IQ3_XXS] +# ⭐ Big-context coder: Qwen3-Coder-Next 80B-A3B (3B active), Qwen3-Next hybrid attn → cheap KV. +# Weights ~30 GB: ALL experts on CPU (n-cpu-moe 99), only attention+KV on GPU → low VRAM, safe. +# Needs a CURRENT llama.cpp build (Qwen3-Next arch). ⬇ DOWNLOAD unsloth/Qwen3-Coder-Next-GGUF:UD-IQ3_XXS. +# Froze the PC before at n-cpu-moe 48 / ctx 131072 (overcommit) — keep it conservative, verify, then raise ctx. +model = /home/anon/software/models/Qwen3-Coder-Next-UD-IQ3_XXS.gguf +ctx-size = 131072 # ✓ VERIFIED @131072/moe99 (2026-07-16): 15.7 t/s, 6.0 GB used / 11.2 free — + # full 128k at a comfortable margin. Settled. +n-gpu-layers = 99 +n-cpu-moe = 99 # ALL experts on CPU — settled AGAIN 2026-08-08 after a two-round + # experiment, CLOSED: GPU experts barely move this model. + # moe56 (04:11) = NO-OP: 6.0 used ≈ moe99's 5.8, all numbers flat → + # only ~48 MoE layers exist; moe ≥ 48 ≡ all-on-CPU. (Also: the old + # "moe48/131k froze the PC" lore was ctx-KV overcommit in a pre- + # quantized-KV config, not expert placement.) + # moe40 (04:37) = 8 layers on GPU, 10.2 used / 6.9 free: prefill + # 33→36 (+9%), decode 16.4 (noise) — 4.2 GB for a rounding error. + # The shallow curve says don't chase it: cache-reuse already makes + # growing sessions incremental, and the 6 GB footprint IS the niche. + # MEASURED (07-16): moe99 = 14.3-16.4 t/s vs moe36@65536 = 16.9. +threads = 6 +# no-mmap TRIED AND REVERTED 2026-07-18: llama.cpp's log hint suggested it (CPU experts + +# mmap = page-fault churn), but reading 26 GB fully resident exceeds the router's load +# patience → "failed to load". Retry only if the router ever gets a load-timeout knob. +flash-attn = on +cache-type-k = q8_0 +cache-type-v = q4_0 +batch-size = 2048 +ubatch-size = 256 # small compute buffer = less VRAM +cache-reuse = 256 +defrag-thold = 0.1 +temp = 0.7 # unsloth default 1.0/top-p0.95/top-k40/min-p0.01 — lower = more deterministic +top-p = 0.95 +top-k = 40 +min-p = 0.01 +jinja = on +sleep-idle-seconds = 300 + +# ─── RETIRED 2026-07-16: [DeepSeek-Coder-V2-Lite-Instruct-Q8_0] ────────────────────────── +# Mid-2024 model, outclassed by GLM-4.7-Flash + both Qwen coders. Bench kept skipping it +# (~15.3 GB fixed footprint; measured 2026-07-03 that ctx/moe knobs barely move it — only fix +# was a Q4_K_M requant, not worth it as the 4th-best coder). Removed from opencode/pi/aichat. +# File can be deleted from the models dir: DeepSeek-Coder-V2-Lite-Instruct-Q5_K_M.gguf + +# ─── RETIRED 2026-08-06: [Qwen3-Coder-30B-Instruct-IQ4_XS] ─────────────────────────────── +# Lost the 2026-08-06 quant race to UD-Q3_K_XL@moe10 (31.3 vs 23.0 t/s, +36%, three +# consistent sweeps; it also failed twice to load on ~3.2 GB evening baselines at moe20 = +# 14.1-14.3 used). Clients rewired to Qwen3-Coder-30B-Instruct-UD-Q3_K_XL same day — the +# extended quality trial was deliberately skipped (user call; revert path = git, one commit). +# ⚠ OWUI chats/presets saved against this id need re-picking after the restart. +# File can be deleted from the models dir: Qwen3-Coder-30B-A3B-Instruct-IQ4_XS.gguf +# Reference numbers: moe24 = 21.1 t/s, 11.9/5.3 · moe20 = 23.2 (2026-07-16), 22.0-23.0 (2026-08-05/06) + +[Qwen3-Coder-30B-Instruct-UD-Q3_K_XL] +# ⭐ MAIN AGENT CODER — took the slot 2026-08-06 after winning the quant race: 31.3 t/s @moe10 +# vs IQ4_XS@moe20 = 23.0 (+36%, three consistent sweeps); Q3_K_S dominated (tombstone below). +# Same 32k ctx and sampling as the retired IQ4_XS — clients only needed the id swap. +# ⚠ QUALITY TRIAL SKIPPED (user call, 2026-08-06): Q3-on-MoE damage looks like wrong API +# signatures / off-by-one logic, NOT word salad — if opencode sessions smell off, the +# IQ4_XS preset + client wiring are one `git revert` away (and the .gguf may still be on +# disk). UD protects attn/shared tensors; the experts are still 3-bit. +# RACE NUMBERS: moe24 = 22.2 · moe16 = 25.1 (07-16) / 25.5 / 26.3 · moe10 = 31.3 (15.0 used / +# 2.1 free @3.1 GB baseline; replicate 30.9 @1.8 free under a 13.9 leftover-model baseline). +model = /home/anon/software/models/Qwen3-Coder-30B-A3B-Instruct-UD-Q3_K_XL.gguf +ctx-size = 32768 +n-gpu-layers = 99 +n-cpu-moe = 12 # ✓ VERIFIED @moe12 (2026-08-07, 2.9 GB EVENING baseline): 27.0 t/s, + # 14.2 used / 3.0 free — loaded fine in exactly the regime that killed + # IQ4_XS@moe20 twice; the insurance works. Cost vs moe10 (31.3 clean race) + # was ~4 t/s, more than the ~1 estimated — evening compute contention + # likely eats 2-3 of that; confirmed: CLEAN (2026-08-08 02:45, 1.9 + # baseline) = 29.5 t/s, 13.2 used / 4.0 free — evening cost was indeed + # contention. Settled: moe12 = 29.5 clean / 27.0 evening, always loads. +threads = 6 +flash-attn = on +cache-type-k = q8_0 +cache-type-v = q4_0 +batch-size = 2048 +ubatch-size = 1024 # ✓ SETTLED 2026-08-08: prefill 74 → 89-92 t/s (+24%), decode unchanged, + # buffer +0.5 GB. FINAL GATE PASSED (14:43 sweep, 3.0 GB dirty baseline = + # evening-class): loaded and ran at 14.6 used / 2.6 free. If a worse + # evening (3.3+) ever fails the load: revert to 512, or pay one moe + # step (12→13). +cache-reuse = 256 +defrag-thold = 0.1 +temp = 0.15 +top-p = 0.8 +top-k = 20 +min-p = 0 +repeat-penalty = 1.05 +jinja = on +sleep-idle-seconds = 300 + +# ─── RETIRED 2026-08-06: [Qwen3-Coder-30B-Instruct-Q3_K_S] ─────────────────────────────── +# Dominated in the quant race: 30.9 t/s @moe8 (15.2 used / 2.0 free, replicate 30.9) = a tie +# with UD-Q3_K_XL@moe10 (31.3) at equal headroom, from a strictly worse quant (plain Q3_K_S, +# no UD-protected attn/shared tensors). No axis where it wins. Was never client-wired. +# File can be deleted from the models dir: Qwen3-Coder-30B-A3B-Instruct-Q3_K_S.gguf + +# ───────────────────────────────────────────────────────────────────────────── +# General Qwen models (35B-A3B is fast MoE; the dense 27Bs are inherently VRAM-tight) +# ───────────────────────────────────────────────────────────────────────────── + +[Qwen3.6-35B-A3B-MTP-UD-IQ3_XXS] +# ⭐ Fast NON-THINKING daily driver: Qwen3.6-35B-A3B (MoE, 3B active) — the "bigger Qwen3 +# Instruct". Compact precise answers, no reasoning preamble. (Alias kept for clients.) +# File is flat + named without "MTP" (unsloth MTP-repo naming). If the load log complains +# there is no MTP head in this file, comment the two spec- lines out (plain repo file). +model = /home/anon/software/models/Qwen3.6-35B-A3B-UD-IQ3_XXS.gguf +mmproj = /home/anon/software/models/mmproj-Qwen3.6-35B-A3B-F16.gguf # vision ✓ VERIFIED 2026-07-18: + # 13.9 used / 3.3 free (2.8 dirty baseline), decode unchanged at 36-37 t/s. +spec-type = draft-mtp +spec-draft-n-max = 2 +reasoning-budget = 0 # hard-disable thinking → instant compact answers. Delete to allow /think. + # ⚠ if startup says "option 'reasoning_budget' not recognized" → remove this line. +ctx-size = 24576 # raised from 16384 (bench @16384: 4.8 GB free) — bench to confirm ≥2.5 free +n-gpu-layers = 99 +n-cpu-moe = 16 # MEASURED @24: 19.8 t/s (MTP working!), 10.6 used / 6.6 free → pull experts onto GPU +threads = 6 +flash-attn = on +cache-type-k = q8_0 +cache-type-v = q4_0 +batch-size = 2048 +ubatch-size = 512 +cache-reuse = 256 +defrag-thold = 0.1 +temp = 0.7 +top-p = 0.8 +top-k = 20 +min-p = 0 +repeat-penalty = 1.05 +jinja = on +sleep-idle-seconds = 120 + +[Qwen3.6-35B-A3B-Thinking] +# Same GGUF as above with reasoning ENABLED (no reasoning-budget line) — fills the "thinking +# model" slot at zero disk cost. Qwen thinking-mode sampling: temp 0.6 / top-p 0.95, no +# repeat-penalty. Slower per answer (reasoning preamble) — use for hard problems. +model = /home/anon/software/models/Qwen3.6-35B-A3B-UD-IQ3_XXS.gguf +mmproj = /home/anon/software/models/mmproj-Qwen3.6-35B-A3B-F16.gguf # vision ✓ VERIFIED 2026-07-18: + # 13.9 used / 3.3 free (2.8 dirty baseline), decode unchanged at 36-37 t/s. +spec-type = draft-mtp +spec-draft-n-max = 2 +ctx-size = 24576 # thinking eats ctx — same footprint as the non-thinking preset +n-gpu-layers = 99 +n-cpu-moe = 16 +threads = 6 +flash-attn = on +cache-type-k = q8_0 +cache-type-v = q4_0 +batch-size = 2048 +ubatch-size = 512 +cache-reuse = 256 +defrag-thold = 0.1 +temp = 0.6 +top-p = 0.95 +top-k = 20 +min-p = 0 +jinja = on +sleep-idle-seconds = 120 + +# ─── RETIRED 2026-07-03: [Qwen3.6-27B-UD-IQ3_XXS] ──────────────────────────────────────── +# The 27B files (plain AND MTP) were deleted from the models dir — preset removed so the +# router doesn't advertise a dead model. To bring it back: re-download (MTP-repo files are +# named WITHOUT "MTP"), restore the preset, re-add the id to opencode/pi/aichat lists. + +# ─── RETIRED 2026-07-16: [Qwen3.5-27B-UD-Q3_K_XL] ──────────────────────────────────────── +# Dense 27B: 16.2 GB used / 0.9 free even @8192 (under the 1.5 floor) and ~10 t/s ceiling on +# this card. Role covered by Qwen3.6-35B-A3B (MoE, 2× faster). Removed from opencode/pi/aichat. +# File can be deleted from the models dir: Qwen3.5-27B-UD-Q3_K_XL.gguf + +[Qwen3.5-9B-UD-Q6_K_XL] +# Small fast Qwen + MTP. Replaces Qwen3.5-9B-Q8_0 (retired 2026-07-16: Q8 was quality-overkill +# and bandwidth-slow at 17.2 t/s — delete Qwen3.5-9B-Q8_0.gguf; also delete the interim +# Qwen3.5-4B-UD-Q6_K_XL.gguf, superseded by this). +# ⬇ FILE MUST COME FROM unsloth/Qwen3.5-9B-MTP-GGUF (8.99 GB — MTP head embedded, filename +# identical to the plain repo!). If the load log complains there is no MTP head in this +# file, it's the plain-repo file — comment the two spec- lines out. +# ✓ VERIFIED (2026-07-16): 32.2 t/s (MTP working — nearly 2× the Q8's 17.2), prefill 83, +# 12.4 used / 4.8 free @32768. TTFT ~3.7 s = the model thinking (reasoning streams first). +model = /home/anon/software/models/Qwen3.5-9B-UD-Q6_K_XL.gguf +mmproj = /home/anon/software/models/Qwen3.5-9B-MTP-mmproj-F16.gguf # vision (from the MTP repo) +spec-type = draft-mtp +spec-draft-n-max = 3 # ⚗→✓ KEPT (04:11): 33.8 t/s vs 32.0-33.1 @n=2 — small real win. + # Revert to 2 if a future sweep drops below 32. +ctx-size = 32768 # raise toward 65536 once bench confirms free ≥2.5 GB +n-gpu-layers = 99 +flash-attn = on +cache-type-k = q8_0 +cache-type-v = q4_0 # q4 V to save VRAM +batch-size = 2048 +ubatch-size = 512 +cache-reuse = 256 +defrag-thold = 0.1 +temp = 0.7 +top-p = 0.8 +top-k = 20 +min-p = 0 +repeat-penalty = 1.05 +jinja = on +sleep-idle-seconds = 60 + +# ─── RETIRED 2026-07-16: [Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ3_M] ────────────── +# Dense: 10 t/s, 2.0 GB free (under the 2.5 target) and unfixable — bandwidth-bound, smaller +# quant would cost the quality that justified it. Removed from opencode/pi/aichat. +# File can be deleted from the models dir: Qwen3.6-27B-Uncensored-HauhauCS-Aggressive-IQ3_M.gguf + +# ───────────────────────────────────────────────────────────────────────────── +# Gemma models +# ───────────────────────────────────────────────────────────────────────────── + +[gemma-4-26B-A4B-it-UD-IQ4_XS] +# MoE (4B active) + MTP + vision. ~13.5 GB weights → tight. Projector on CPU (no-mmproj-offload). +# From unsloth/gemma-4-26B-A4B-it-qat-GGUF: mmproj-F16.gguf + mtp-gemma-4-26B-A4B-it.gguf. +# ⬇ SOURCES — local name ← upstream (PLAIN repo unsloth/gemma-4-26B-A4B-it-GGUF; the qat +# repo has NO IQ4_XS, its only main is qat-UD-Q4_K_XL 14.25 GB — see decision note below): +# gemma-4-26B-A4B-it-UD-IQ4_XS.gguf ← gemma-4-26B-A4B-it-UD-IQ4_XS.gguf (13.6 GB) +# ✓ current: the 2026-08-05 dl has the Jul 17 template +# mmproj-gemma-4-26B-A4B-it-F16.gguf ← mmproj-F16.gguf (1.19 GB) +# mtp-gemma-4-26B-A4B-it.gguf ← MTP/mtp-gemma-4-26B-A4B-it-Q8_0.gguf (462 MB) +# Keep LOCAL names stable; re-fetch the upstream names above when repos update. Pair main +# and drafter from the SAME repo — the two repos' drafters are different extractions. +# ✓ FIXED 2026-08-08 03:15: matched plain-repo sidecars cured the "Invalid input batch." +# 500s — root cause CONFIRMED as the cross-repo drafter mismatch (old qat-repo mtp under +# the new plain main). Re-benched 32.8 t/s @moe13, 12.1 used / 5.0 free, MTP ✓ — same +# speed as pre-update (32.7), template fix now in place. The *.gguf.bak sidecars are +# deletable once vision + opencode tool-calling are smoke-tested. +# (QAT quality upgrade = separate decision: main → qat-UD-Q4_K_XL, +0.7 GB, moe 13→~16, and +# a section/client id rename round. Not part of this fix.) +# ⚠ vision + MTP together is heavy — if it OOMs/freezes, add n-cpu-moe or drop MTP (keep vision). +model = /home/anon/software/models/gemma-4-26B-A4B-it-UD-IQ4_XS.gguf +mmproj = /home/anon/software/models/mmproj-gemma-4-26B-A4B-it-F16.gguf +model-draft = /home/anon/software/models/mtp-gemma-4-26B-A4B-it.gguf +spec-type = draft-mtp +spec-draft-n-max = 2 +reasoning-budget = 0 # added 2026-08-09: the Jul-17 official template makes gemma-4 DELIBERATE + # on hard prompts (probe: 700 tok = 100% reasoning_content, content + # EMPTY, finish=length) — that ate pi's 4096 maxTokens cap and returned + # truncated nothing. Same fix as the 35B daily driver. ⚠ if startup says + # option not recognized, remove; if it loads but a hard probe still emits + # reasoning_content, the gemma template ignores the budget → remove and + # raise client maxTokens to 8192 instead. +ctx-size = 24576 # MEASURED @16384/moe16: 29.6 t/s, 10.7 used / 6.5 free → sliding-window KV is + # cheap, take the ctx back (clients already assume 24576) + # n_ctx_train = 262144 (08-08 load log) — huge ctx headroom exists if a + # long-context vision need ever appears; bench stepwise, free ≥ 2.5. +n-gpu-layers = 99 +n-cpu-moe = 13 # MEASURED @16: 26.4 t/s, 11.9 used / 5.2 free → pull ~3 expert layers onto GPU for speed +threads = 6 +flash-attn = on +jinja = on # OpenAI-style tool calling (opencode build/plan default to this preset) +cache-type-k = q8_0 +cache-type-v = q4_0 +batch-size = 2048 +ubatch-size = 512 +cache-reuse = 256 +defrag-thold = 0.1 +no-mmproj-offload = true # vision encoder on CPU → frees ~1 GB +temp = 1.0 +top-p = 0.95 +top-k = 64 +sleep-idle-seconds = 60 + +[gemma-4-E4B-it-UD-Q8_K_XL] +# ~5-7 GB — small/fast, lots of headroom, safe at big ctx. +# Vision: the HF repo ships mmproj-{BF16,F16,F32}.gguf — use F16 (990 MB), saved AS the exact +# path below (rename it; if the path doesn't exist the model fails to load / vision silently +# missing depending on router). Verify: startup log shows the mmproj load, and an image request +# answers instead of erroring "image input is not supported". +# NOTE: repo got Google's chat-template update ~2026-05 — if the main gguf predates that, re-download it too. +model = /home/anon/software/models/gemma-4-E4B-it-UD-Q8_K_XL.gguf +mmproj = /home/anon/software/models/mmproj-gemma-4-E4B-it.gguf +model-draft = /home/anon/software/models/mtp-gemma-4-E4B-it.gguf # ✓ MEASURED (2026-07-18): 57.3 t/s — + # +50% over 38.4 without the draft, 10.5 used / 6.6 free. Fastest model on the box. +spec-type = draft-mtp +spec-draft-n-max = 2 +reasoning-budget = 0 # added 2026-08-09, same reason as the 26B: gemma-4 deliberates on hard + # prompts since the Jul-17 template (this is what caused the 20-second + # aichat -e "think" preambles on 2026-08-05). Fast models answer NOW. + # ⚠ same caveats as the 26B — remove if not recognized / not honored. +ctx-size = 98304 # ⚗→✓ CLOSED 2026-08-08: 58.2-60.4 t/s, 10.3-10.6 used / 6.5-6.9 free + # @98304, and n_ctx_train = 131072 confirmed (upstream config.json + # max_position_embeddings) — 98304 is inside training range, no RoPE + # risk. 131072 is the hard ceiling if ever needed (~+0.5-1 GB KV). +n-gpu-layers = 99 +flash-attn = on +jinja = on # OpenAI-style tool calling (doc-writer agent defaults to this preset) +cache-type-k = q8_0 +cache-type-v = q8_0 +batch-size = 2048 +ubatch-size = 1024 +cache-reuse = 256 +defrag-thold = 0.1 +temp = 1.0 +top-p = 0.95 +top-k = 64 +sleep-idle-seconds = 30 + +# ───────────────────────────────────────────────────────────────────────────── +# GLM +# ───────────────────────────────────────────────────────────────────────────── + +[GLM-4.7-Flash-UD-Q4_K_XL] +# 30B-A3B MoE, primary coder. Upgraded UD-Q3_K_XL (~14 GB) → UD-Q4_K_XL (17.5 GB, unsloth's +# recommended quant) 2026-07-16, benched clean same day → the old GLM-4.7-Flash-UD-Q3_K_XL.gguf +# can be deleted. +# Tuning trajectory: Q3@moe9 = 28.2 t/s → Q4@moe24 = 17.9 (measured 2026-07-16) → now moe18, +# lower stepwise toward 16 while bench keeps free ≥2.5 GB. +model = /home/anon/software/models/GLM-4.7-Flash-UD-Q4_K_XL.gguf +ctx-size = 24576 # MEASURED @16384/moe24 (Q3): 9.5 GB used, 7.6 free + # n_ctx_train = 202752 (08-08 load log) — MLA KV is compact, ctx could + # go far higher if opencode subagent sessions ever outgrow 24k. +n-gpu-layers = 99 +n-cpu-moe = 22 # lowered 24→22 on 2026-08-08: both step-down gates passed post-KV-fix — + # clean sweep @24 = 20.5 t/s, 12.0 used / 5.2 free (02:45, 1.9 baseline); + # elevated-baseline load @24 = 18.7, 13.0/4.2 (@2.9). moe22 est ~21.5 + # clean / ~19.5-20 evening, ~12.7 used clean / ~14.0 evening → inside the + # coder-proven evening envelope (14.2 loads). ✓ CLEAN VERIFIED (02:58, + # 2.0 baseline): 21.5 t/s, 12.8 used / 4.4 free — est was 21.5, exact. + # Evening observation pending; revert to 24 if an evening bench shows + # free < 2.5. moe20 stays gated (~14.4-14.7 evening = borderline vs the + # coder's 15.2 evening death). + # (08-05/07 "failures" were the mixed-KV rejection below, not VRAM.) + # Trajectory: Q3@moe9 = 28.2 → Q4@moe24 = 17.9 (07-16) → 18.7/20.5 post-KV-fix. +threads = 6 +flash-attn = on +cache-type-k = q8_0 +cache-type-v = q8_0 # was q4_0 — BROKE 2026-08-08: a llama.cpp upgrade (the one deprecating + # defrag-thold) enforces same-type K/V for this model (deepseek2/MLA + # conversion; MLA KV is compact, mixed types unsupported). Every load + # since failed at context creation regardless of VRAM. Only THIS preset + # was affected — the other k-q8/v-q4 presets are regular-attention archs. + # If q8/q8 still refuses: delete both cache-type lines (f16 default; MLA + # KV is small enough that the cost is minor at 24576 ctx). +batch-size = 2048 +ubatch-size = 512 +cache-reuse = 256 +defrag-thold = 0.1 +# MTP: GLM-4.7-Flash HAS an MTP head upstream (num_nextn_predict_layers: 1) but the GGUF +# conversion (deepseek2 arch) drops those tensors and llama.cpp draft-mtp only supports +# Qwen3.5/3.6 + Gemma 4 so far (PRs #22673/#23398). No mtp-*.gguf exists to download — +# recheck when llama.cpp lands GLM MTP support. +temp = 0.6 # unsloth rec: 0.7/top-p 1.0 (tool-calling) or 1.0/0.95 (general); no repeat-penalty +top-p = 0.95 +min-p = 0.01 +jinja = on +reasoning-preserve = true # ⚗ option ACCEPTED by the build (loaded + ran 20.9 t/s, 04:11 sweep). + # BEHAVIOR flag — judge multi-turn subagent quality in opencode over the + # next days; remove if reasoning bleed ever pollutes final answers. +sleep-idle-seconds = 60 + +# ───────────────────────────────────────────────────────────────────────────── +# Utility & candidates +# ───────────────────────────────────────────────────────────────────────────── + +[Qwen3-Embedding-0.6B] +# Embeddings-only, for RAG/semantic search (OWUI etc. via /v1/embeddings) — NOT a chat model, +# so it is deliberately absent from the opencode/pi/aichat chat lists. +# Qwen3-Embedding requires last-token pooling. ~0.7 GB — tiny, loads fast. +# (Q8_0 is effectively lossless for a 0.6B embedder — the f16 file is redundant, delete it.) +# CPU-ONLY on purpose: a 0.6B embeds fast on 6 cores, and 0 VRAM means it can never stack +# with a chat model into an overcommit (and never suffers/causes eviction pressure). +model = /home/anon/software/models/Qwen3-Embedding-0.6B-Q8_0.gguf +embeddings = true +pooling = last +ctx-size = 8192 +n-gpu-layers = 0 +flash-attn = on +batch-size = 8192 # embed long chunks in one pass +ubatch-size = 1024 +threads = 6 +sleep-idle-seconds = 60 + +[gpt-oss-20b] +# OpenAI gpt-oss-20b — MoE 21B / 3.6B active, reasoning + tool use (enabled 2026-07-16, NOT +# BENCHED yet). OpenAI post-trained it in MXFP4, so there is exactly ONE sensible GGUF: +# ggml-org/gpt-oss-20b-GGUF → gpt-oss-20b-mxfp4.gguf (~12.1 GB). Requants are never better — +# the old gpt-oss-20b-Q3_K_M.gguf can be deleted. +# Harmony chat template → jinja required. Default KV types kept (gpt-oss attention sinks have +# had issues with quantized KV — verify before adding cache-type lines). +model = /home/anon/software/models/gpt-oss-20b-mxfp4.gguf +ctx-size = 65536 # ⚗→✓ VERIFIED @65536 (04:11): 37.3/37.4 t/s unchanged, 14.0 used / 3.2 + # free — the fast long-context option is real (37 t/s vs Coder-Next 16). + # 131072 is OFF the table: 32k→65k cost +1.2 GB (KV slope steepens past + # the sliding window) → 131k would land under 2.5 free. Client caps + # raised 32k→64k on 2026-08-08. +n-gpu-layers = 99 +n-cpu-moe = 4 # ✓ SETTLED. MEASURED @6: 31.3 t/s · @4 (2026-07-16): 38.1 t/s, 12.7 used / + # 4.5 free — fastest model in the lineup. TTFT (~3-7 s) is the model THINKING + # (harmony reasoning streams as reasoning_content first) — not a slow load. +threads = 6 +flash-attn = on +batch-size = 2048 +ubatch-size = 512 +cache-reuse = 256 +defrag-thold = 0.1 +temp = 1.0 +top-p = 1.0 +jinja = on +sleep-idle-seconds = 120 + +[gpt-oss-20b-low] +# Same GGUF, reasoning effort LOW — zero disk cost (like the 35B-Thinking alias, inverted). +# Cuts the multi-second thinking preamble for light/interactive use; keep [gpt-oss-20b] +# (default = medium effort) for hard problems. ⚠ if startup says the option is not +# recognized, the llama.cpp build predates chat-template-kwargs — comment that line out. +model = /home/anon/software/models/gpt-oss-20b-mxfp4.gguf +chat-template-kwargs = {"reasoning_effort":"low"} +ctx-size = 65536 # ⚗→✓ VERIFIED @65536 (04:11): 37.3/37.4 t/s unchanged, 14.0 used / 3.2 + # free — the fast long-context option is real (37 t/s vs Coder-Next 16). + # 131072 is OFF the table: 32k→65k cost +1.2 GB (KV slope steepens past + # the sliding window) → 131k would land under 2.5 free. Client caps + # raised 32k→64k on 2026-08-08. +n-gpu-layers = 99 +n-cpu-moe = 4 +threads = 6 +flash-attn = on +batch-size = 2048 +ubatch-size = 512 +cache-reuse = 256 +defrag-thold = 0.1 +temp = 1.0 +top-p = 1.0 +jinja = on +sleep-idle-seconds = 60 + +# ───────────────────────────────────────────────────────────────────────────── +# Notes +# ───────────────────────────────────────────────────────────────────────────── +# n-cpu-moe N : offload the first N MoE layers' experts to CPU. Higher = less VRAM (safer) but +# slower. On this display-driving GPU, a bit of offload is FAR better than a freeze. +# Only applies to MoE models (dense models ignore it — reduce ctx instead). +# override-tensor : finer control, e.g. `blk\.(1[2-9]|[2-9][0-9])\.ffn_.*_exps=CPU`. +# After ANY change: reload the server and run bench.py — keep every "VRAM free" ≥ ~2.5 GB. diff --git a/fl/.config/whisper/README.md b/fl/.config/whisper/README.md new file mode 100644 index 0000000..87a3dcc --- /dev/null +++ b/fl/.config/whisper/README.md @@ -0,0 +1,193 @@ +# Speech-to-text — setup & usage + +whisper.cpp serving Whisper models over an **OpenAI-compatible** transcription endpoint, so Open WebUI's +mic button works against local hardware. Added **2026-08-08**. + +- **Box**: same fl server as the LLM router — but this runs **entirely on CPU** (Ryzen 5600X, 6 cores). +- **Files here**: `whisper@.service` (one template unit) + one `.env` per model. + Models live with the GGUFs in `~/software/models/`. + +| instance | endpoint | model | speed / use | +|---|---|---|---| +| **`whisper@turbo`** | `http://$SERVER:11345/v1` | `ggml-large-v3-turbo-q5_0` (574 MB) | **default** — best accuracy, multilingual, ~15 s per request. This is what OWUI points at. | +| `whisper@small` | `http://$SERVER:11346/v1` | `ggml-small.en-q5_1` (181 MB) | English-only, ~5x lighter. Point a client here when latency beats accuracy. | + +Both run at once — together they're well under 1 GB of RAM and **zero VRAM**, so there is no reason to +stop one to use the other. Adding a third is a new `.env` file plus `systemctl enable --now whisper@`. + +## How it runs + +Template unit **`whisper@.service`** (a dedicated service user), from `/usr/bin/whisper-server` (Arch `extra/whisper-cpp`). +Each instance reads `~/.config/whisper/.env`: + +``` +WHISPER_MODEL=/home//software/models/ggml-large-v3-turbo-q5_0.bin +WHISPER_PORT=11345 +WHISPER_LANG=auto +``` + +which expands to: + +``` +whisper-server --model $WHISPER_MODEL \ + --host 0.0.0.0 --port $WHISPER_PORT \ + --inference-path /v1/audio/transcriptions \ + --convert --tmp-dir /run/whisper- \ + --no-gpu --threads 6 --language $WHISPER_LANG +``` + +It is a template rather than N copied units on purpose: the `--tmp-dir` bug below had to be found once +and fixed once, and that only stays true while there is exactly one `ExecStart` in the repo. + +| flag | why | +|---|---| +| `--no-gpu` | **Load-bearing.** 0 VRAM — see below. | +| `--convert` | ffmpeg transcodes webm/opus/ogg → 16 kHz wav. Browsers record opus; whisper.cpp reads wav. **Requires ffmpeg on the box.** | +| `--inference-path` | `--request-path` defaults to empty, so this alone puts the route at `/v1/audio/transcriptions` — exactly where OWUI posts. | +| `--tmp-dir` | **Required.** Defaults to `"."`, i.e. the CWD — which is `/` here, and unwritable. Without it every request returns `FFmpeg conversion failed.`, plain wav included. `RuntimeDirectory=whisper-%i` creates and owns `/run/whisper-`. | +| `--language` | `auto` for multilingual models; `en` for `.en` models, where detection is pointless. | +| `--threads 6` | all cores; `Nice=5` yields to the router's CPU-offloaded experts. | +| ports `11345`/`11346` | clear of `11343` (router), **`11344` (the standalone embedding llama-server)** and `11434` (Ollama). Router *children* take random ephemeral ports (32768+), so low ports are safe from them — but run `ss -ltnp` before claiming one. | + +Keyless on the LAN, same posture as the router. If this ever leaves the trusted LAN it needs a proxy +with auth in front — whisper-server has no `--api-key`. + +**Deploying changes**: `/etc/systemd/system/whisper@.service` is a symlink to the file in the repo, so a +`git pull` edit is on disk immediately — but systemd caches unit files, so it still needs a reload. +(Copying into `/etc` instead would work, but then every repo edit silently drifts.) On the server: + +``` +dotsync # nudges you if this dir changed +sudo systemctl daemon-reload +sudo systemctl restart whisper@turbo whisper@small +``` + +Editing only an `.env` needs no `daemon-reload` — just restart that instance. + +First install (stow does not install units): + +``` +yay -S whisper-cpp ffmpeg +curl -L -o ~/software/models/ggml-large-v3-turbo-q5_0.bin \ + https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo-q5_0.bin +curl -L -o ~/software/models/ggml-small.en-q5_1.bin \ + https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en-q5_1.bin + +# Point systemd at the REAL file in the repo, NOT the stowed symlink: stow already made +# ~/.config/whisper/whisper@.service a symlink, and systemctl rejects those with +# "Failed to link unit: Refusing to operate on linked unit file" +sudo systemctl link (readlink -f ~/.config/whisper/whisper@.service) # fish +sudo systemctl enable --now whisper@turbo whisper@small +``` + +## Why this is NOT a llama.cpp router preset + +The router *does* expose `POST /v1/audio/transcriptions` (it rewrites the request into a chat completion +with the prompt "Transcribe audio to text"), and `gemma-4-E4B` genuinely accepts audio input. Tempting — +but two reasons it stays a separate service. Don't re-litigate this without re-checking both: + +1. **`--models-max 1` eviction.** Every transcription would LRU-evict the resident chat model, and the + next chat message would reload it (~17.5 GB for GLM) with a cold prompt cache. Fine for a one-off + file; unusable for talking to a model. +2. **Format.** llama.cpp decodes audio via miniaudio — **wav/mp3/flac only**. OWUI's mic records + `audio/webm; codecs=opus` and its own transcode-to-mp3 step depends on ffprobe being usable inside + the OWUI AppImage; when that fails it silently forwards the opus file. `--convert` here makes the + question moot. + +Third reason, softer: an LLM asked to transcribe can paraphrase or answer the audio instead of +transcribing it, and the `gemma-4-E4B` preset samples at temp 1.0. Whisper is purpose-built and boring. + +**Still available as a fallback** for wav/mp3/flac (no config change needed), if these services are down: + +``` +curl -F file=@clip.wav -F model=gemma-4-E4B-it-UD-Q8_K_XL \ + http://$SERVER:11343/v1/audio/transcriptions +``` + +## Open WebUI wiring + +Admin Settings → **Audio** → Speech-to-Text: + +| field | value | +|---|---| +| STT Engine | `OpenAI` | +| Base URL | `http://$SERVER:11345/v1` — no trailing path, OWUI appends `/audio/transcriptions`. Swap `11345`→`11346` for the fast instance. | +| API Key | any non-empty string (`none`) — ignored by whisper-server, but OWUI needs it set | +| STT Model | `whisper-1` — ignored by whisper-server; must not be blank | +| Request format | leave default (`multipart`) | + +OWUI sends multipart `model` + optional `language` + `file`; whisper-server reads the fields it knows +and ignores the rest. Note it joins segments with newlines, so long dictations arrive in the composer +with line breaks mid-sentence. + +## Verifying + +``` +systemctl status whisper@turbo whisper@small +rocm-smi --showmeminfo vram # must be UNCHANGED vs before the services started + +curl -F file=@sample.wav http://$SERVER:11345/v1/audio/transcriptions # → {"text": "…"} +ffmpeg -i sample.wav -c:a libopus sample.webm # the case that actually matters: +curl -F file=@sample.webm http://$SERVER:11345/v1/audio/transcriptions # proves --convert +curl -F file=@sample.webm -F model=whisper-1 -F language=en \ + http://$SERVER:11346/v1/audio/transcriptions # OWUI's exact shape +``` + +Then: with a chat model loaded, transcribe and confirm `http://$SERVER:11343/v1/models` still +shows it `ready`/`sleeping` — **not** `unloaded`. That is the whole point of the separate service. + +**Measured 2026-08-08** (6 threads, CPU, 11 s JFK sample, `--convert` in the path): + +| model | per request | verdict | +|---|---|---| +| `large-v3-turbo-q5_0` (574 MB) | **~15.2 s** | accurate; too slow to dictate into comfortably | +| `small.en-q5_1` (181 MB) | **~2.2 s** | **~7x faster.** Word-for-word identical to turbo on the JFK sample — only punctuation differs (`And so my fellow Americans` vs turbo's `And so, my fellow Americans,`). | + +Runs were tight: small.en 2.16 / 2.16 / 2.20 s over three passes, turbo 15.1-15.2 s over three. +The webm/opus path costs nothing extra (2.19 s with `--convert` + OWUI's field shape). + +**On this evidence turbo is hard to justify as the default** — 13 seconds per utterance buys a comma on +clean audio. Where it should still win is names, jargon, accents and non-English speech, which the JFK +sample cannot show. If small.en holds up on real dictation, point OWUI at `:11346` and demote turbo to +the second instance (or just swap the ports in the two `.env` files). + +The cost is **flat** for both models: whisper pads every request to a 30 s window, so a 3 s clip costs +what a 30 s one does (turbo 15.1 s vs 15.2 s; small.en 2.08 s vs 2.20 s). It is a fixed toll per +utterance, not a rate — which is why the model is the only thing that moves it. +Flash-attention is already on by default in whisper-server, and the `parakeet-*` binaries in the package +have no server integration, so **model size is the only real dial**. + +A/B any candidate without touching a service: + +``` +curl -LO https://github.com/ggml-org/whisper.cpp/raw/master/samples/jfk.wav # known text +time whisper-cli -m ~/software/models/ggml-small.en-q5_1.bin -f jfk.wav -t 6 -nt +time whisper-cli -m ~/software/models/ggml-base.en-q5_1.bin -f jfk.wav -t 6 -nt +``` + +## Troubleshooting + +- **Every request returns `{"error":"FFmpeg conversion failed."}`, wav included** → the temp dir is not + writable by the service user. Check `--tmp-dir /run/whisper-` is in ExecStart and that + `RuntimeDirectory=` created it (`ls -ld /run/whisper-turbo`). ffmpeg itself is fine — the startup log + says `ffmpeg is available.` either way. +- **`Failed to load environment files`** → the instance has no `.env` (`whisper@foo` needs `foo.env`), + or the repo was not re-stowed after adding it (`install.sh` links new files; `dotsync` does it for you). +- **webm/opus uploads fail, wav works** → ffmpeg missing or `--convert` dropped. `ffmpeg -version` on + the box; the journal logs the conversion command on each non-wav request. +- **VRAM moved when transcribing** → `--no-gpu` lost, or the package was rebuilt with a GPU backend. + Fix immediately; this is the freeze scenario. +- **Empty / hallucinated text on near-silence** → whisper's classic failure. Try VAD: + download `ggml-silero-v5.1.2.bin` and add `--vad --vad-model …` to the template. +- **Still too slow** → next step down is `ggml-base.en-q5_1.bin` (56 MB, ~4x lighter again) at a real + cost in accuracy on names and jargon. New `.env`, or edit `small.env`. +- **OWUI: "Server Connection Error"** → base URL has a trailing `/audio/transcriptions` (it appends that + itself), or the port is not reachable from the client host. + +## Watch list + +- `whisper-cpp` also ships **`parakeet-cli`** — NVIDIA Parakeet is much faster than Whisper on CPU for + English. No server front-end upstream yet (`examples/` has `parakeet-cli` but no server path); worth + rechecking, it would be the real fix for the 15 s toll. +- **TTS** (the other half of voice chat) is unconfigured — OWUI can drive its own, or llama.cpp can + serve OuteTTS via `--model-vocoder`. Separate job. diff --git a/fl/.config/whisper/small.env b/fl/.config/whisper/small.env new file mode 100644 index 0000000..60cf1e5 --- /dev/null +++ b/fl/.config/whisper/small.env @@ -0,0 +1,6 @@ +# FAST instance (port 11346) — English-only, ~5x lighter than turbo. +# Same API; point a client at :11346 instead of :11345 when latency matters more than accuracy. +# For a multilingual equivalent use ggml-small-q5_1.bin and set WHISPER_LANG=auto. +WHISPER_MODEL=/home/anon/software/models/ggml-small.en-q5_1.bin +WHISPER_PORT=11346 +WHISPER_LANG=en diff --git a/fl/.config/whisper/turbo.env b/fl/.config/whisper/turbo.env new file mode 100644 index 0000000..38537a2 --- /dev/null +++ b/fl/.config/whisper/turbo.env @@ -0,0 +1,6 @@ +# DEFAULT instance (port 11345) — the one Open WebUI points at. +# Best accuracy, multilingual. Measured 2026-08-08: ~15.1 s per request, FLAT (whisper pads every +# clip to a 30 s window, so a 3 s utterance costs the same as a 30 s one). +WHISPER_MODEL=/home/anon/software/models/ggml-large-v3-turbo-q5_0.bin +WHISPER_PORT=11345 +WHISPER_LANG=auto diff --git a/fl/.config/whisper/whisper@.service b/fl/.config/whisper/whisper@.service new file mode 100644 index 0000000..c121dde --- /dev/null +++ b/fl/.config/whisper/whisper@.service @@ -0,0 +1,48 @@ +[Unit] +Description=whisper.cpp STT server "%i" (CPU-only) — OpenAI-compatible /v1/audio/transcriptions +Documentation=file:///home/anon/.config/whisper/README.md +After=network-online.target +Wants=network-online.target + +[Service] +User=anon +Group=anon +WorkingDirectory=/ +# TEMPLATE UNIT — one file, N instances. `systemctl start whisper@small` reads its model/port/language +# from ~/.config/whisper/small.env. Deliberately not N copies of this unit: the --tmp-dir bug below had +# to be found once and fixed once, and that only holds if there is exactly one ExecStart in the repo. +EnvironmentFile=/home/anon/.config/whisper/%i.env +# whisper-server's --tmp-dir defaults to "." — with WorkingDirectory=/ that means it tries to write +# uploads and ffmpeg output into the filesystem root, and every request dies with +# {"error":"FFmpeg conversion failed."} (yes, even plain wav: --convert pipes everything to ffmpeg) +# RuntimeDirectory= creates /run/whisper-, owned by this user, wiped on stop. Per-instance so +# two servers can never collide on a temp filename. +RuntimeDirectory=whisper-%i +RuntimeDirectoryMode=0700 +# +# ⚠⚠ --no-gpu IS LOAD-BEARING. The GPU on this box drives the display AND holds a 13-15 GB chat model; +# ROCm does not OOM cleanly, it spills to GTT and freezes the whole PC. Whisper is CPU-only BY +# DESIGN (same rule as the Qwen3-Embedding preset): 0 VRAM means it can never stack into an +# overcommit, and it never evicts / gets evicted by the router. Do not remove. +# --convert shells out to ffmpeg (webm/opus/ogg -> 16 kHz wav). This is the whole reason the Open WebUI +# mic button works — the browser records opus, whisper.cpp reads wav. +# --inference-path puts the route exactly where OWUI posts (--request-path defaults to empty), so a +# client's base URL is http://192.168.0.204:/v1 . +ExecStart=/usr/bin/whisper-server \ + --model ${WHISPER_MODEL} \ + --host 0.0.0.0 --port ${WHISPER_PORT} \ + --inference-path /v1/audio/transcriptions \ + --convert \ + --tmp-dir /run/whisper-%i \ + --no-gpu \ + --threads 6 \ + --language ${WHISPER_LANG} +Restart=always +RestartSec=2 +# Yield to the llama.cpp router: its CPU-offloaded MoE experts (n-cpu-moe) want the same 6 cores. +Nice=5 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target diff --git a/.config/alacritty/alacritty.toml b/gui/.config/alacritty/alacritty.toml similarity index 63% rename from .config/alacritty/alacritty.toml rename to gui/.config/alacritty/alacritty.toml index b6fe766..3213a2e 100644 --- a/.config/alacritty/alacritty.toml +++ b/gui/.config/alacritty/alacritty.toml @@ -1,36 +1,44 @@ +[general] +import = ["theme.toml"] [window] opacity = 0.7 -padding.x = 30 -padding.y = 20 +padding.x = 25 +padding.y = 25 decorations = "Full" decorations_theme_variant = "Dark" [font] -size = 10.0 +size = 12.0 [font.bold] -family = "CaskaydiaCove Nerd Font Mono" +family = "CaskaydiaCove Nerd Font Mono" style = "Bold" [font.bold_italic] -family = "CaskaydiaCove Nerd Font Mono" +family = "CaskaydiaCove Nerd Font Mono" style = "Bold Italic" [font.italic] -family = "CaskaydiaCove Nerd Font Mono" +family = "CaskaydiaCove Nerd Font Mono" style = "Italic" [font.normal] -family = "CaskaydiaCove Nerd Font Mono" +family = "CaskaydiaCove Nerd Font Mono" style = "Regular" # family = "Source Code Pro" # family = "Liberation Mono" -# family = "Source Code Pro" +[cursor] +blink_interval = 400 +blink_timeout = 15 + +[cursor.style] +shape = "Block" +blinking = "On" [keyboard] @@ -39,8 +47,4 @@ bindings = [ { key = "K", mods = "Control|Shift", action = "ScrollLineUp" }, { key = "D", mods = "Control|Shift", action = "ScrollPageDown" }, { key = "U", mods = "Control|Shift", action = "ScrollPageUp" }, - # { key = "A", mods = "Control", action = "ScrollLineUpScrollLineUp" }, ] - - - diff --git a/gui/.config/alacritty/theme.toml b/gui/.config/alacritty/theme.toml new file mode 100644 index 0000000..1d958bd --- /dev/null +++ b/gui/.config/alacritty/theme.toml @@ -0,0 +1,33 @@ +# Synthwave '84 — matches ../kitty/theme.conf + +[colors.primary] +background = "#262335" +foreground = "#ffffff" + +[colors.cursor] +text = "#262335" +cursor = "#f97e72" + +[colors.selection] +text = "#262335" +background = "#f97e72" + +[colors.normal] +black = "#232530" +red = "#fe4450" +green = "#72f1b8" +yellow = "#ff7edb" +blue = "#03edf9" +magenta = "#fede5d" +cyan = "#03edf9" +white = "#ffffff" + +[colors.bright] +black = "#232530" +red = "#fe4450" +green = "#72f1b8" +yellow = "#ff7edb" +blue = "#03edf9" +magenta = "#f3e70f" +cyan = "#03edf9" +white = "#ffffff" diff --git a/gui/.config/fish/conf.d/waybar.fish b/gui/.config/fish/conf.d/waybar.fish new file mode 100644 index 0000000..029ff1e --- /dev/null +++ b/gui/.config/fish/conf.d/waybar.fish @@ -0,0 +1,8 @@ +## gui package — waybar helpers. Lives in conf.d/ (fish sources every conf.d/*.fish), +## NOT in host.fish: each host package ships its own host.fish, which stow deploys last +## with --override — a gui/host.fish would be shadowed on every host that has one. + +# Reload the custom waybar (kill + relaunch, detached so it survives this shell). +# Same invocation as userprefs.conf exec-once / the Ctrl+Alt+W bind. Killing waybar +# also stops its cava child (cava-waybar.sh is a child of the bar). +abbr wbar 'killall waybar; setsid -f waybar -c ~/.config/waybar/layouts/custom.jsonc -s ~/.config/waybar/layouts/custom.css' diff --git a/gui/.config/hypr/animations.conf b/gui/.config/hypr/animations.conf new file mode 100644 index 0000000..bdb8598 --- /dev/null +++ b/gui/.config/hypr/animations.conf @@ -0,0 +1,12 @@ +# ▄▀█ █▄░█ █ █▀▄▀█ ▄▀█ ▀█▀ █ █▀█ █▄░█ +# █▀█ █░▀█ █ █░▀░█ █▀█ ░█░ █ █▄█ █░▀█ +# +# See https://wiki.hypr.land/Configuring/Animations/ +# Edit or add presets in ./animations/, then run 'animations.sh --select' to switch. +# (noanim layerrules for hyprpicker/selection live in userprefs.conf) + +$ANIMATION=theme +# Absolute, not ./ — HyDE re-sources $ANIMATION_PATH from ~/.local/share/hyde/hyprland.conf, +# where a relative path resolves against that directory instead of this one. +$ANIMATION_PATH=~/.config/hypr/animations/theme.conf +source = $ANIMATION_PATH diff --git a/.config/hypr/animations/classic.conf b/gui/.config/hypr/animations/classic.conf similarity index 100% rename from .config/hypr/animations/classic.conf rename to gui/.config/hypr/animations/classic.conf diff --git a/.config/hypr/animations/diablo-1.conf b/gui/.config/hypr/animations/diablo-1.conf similarity index 100% rename from .config/hypr/animations/diablo-1.conf rename to gui/.config/hypr/animations/diablo-1.conf diff --git a/.config/hypr/animations/diablo-2.conf b/gui/.config/hypr/animations/diablo-2.conf similarity index 100% rename from .config/hypr/animations/diablo-2.conf rename to gui/.config/hypr/animations/diablo-2.conf diff --git a/.config/hypr/animations/disable.conf b/gui/.config/hypr/animations/disable.conf similarity index 100% rename from .config/hypr/animations/disable.conf rename to gui/.config/hypr/animations/disable.conf diff --git a/.config/hypr/animations/dynamic.conf b/gui/.config/hypr/animations/dynamic.conf similarity index 100% rename from .config/hypr/animations/dynamic.conf rename to gui/.config/hypr/animations/dynamic.conf diff --git a/.config/hypr/animations/end4.conf b/gui/.config/hypr/animations/end4.conf similarity index 100% rename from .config/hypr/animations/end4.conf rename to gui/.config/hypr/animations/end4.conf diff --git a/.config/hypr/animations/fast.conf b/gui/.config/hypr/animations/fast.conf similarity index 100% rename from .config/hypr/animations/fast.conf rename to gui/.config/hypr/animations/fast.conf diff --git a/.config/hypr/animations/high.conf b/gui/.config/hypr/animations/high.conf similarity index 100% rename from .config/hypr/animations/high.conf rename to gui/.config/hypr/animations/high.conf diff --git a/.config/hypr/animations/ja.conf b/gui/.config/hypr/animations/ja.conf similarity index 100% rename from .config/hypr/animations/ja.conf rename to gui/.config/hypr/animations/ja.conf diff --git a/.config/hypr/animations/me-1.conf b/gui/.config/hypr/animations/me-1.conf similarity index 100% rename from .config/hypr/animations/me-1.conf rename to gui/.config/hypr/animations/me-1.conf diff --git a/.config/hypr/animations/me-2.conf b/gui/.config/hypr/animations/me-2.conf similarity index 100% rename from .config/hypr/animations/me-2.conf rename to gui/.config/hypr/animations/me-2.conf diff --git a/.config/hypr/animations/minimal-1.conf b/gui/.config/hypr/animations/minimal-1.conf similarity index 100% rename from .config/hypr/animations/minimal-1.conf rename to gui/.config/hypr/animations/minimal-1.conf diff --git a/.config/hypr/animations/minimal-2.conf b/gui/.config/hypr/animations/minimal-2.conf similarity index 100% rename from .config/hypr/animations/minimal-2.conf rename to gui/.config/hypr/animations/minimal-2.conf diff --git a/.config/hypr/animations/moving.conf b/gui/.config/hypr/animations/moving.conf similarity index 100% rename from .config/hypr/animations/moving.conf rename to gui/.config/hypr/animations/moving.conf diff --git a/.config/hypr/animations/optimized.conf b/gui/.config/hypr/animations/optimized.conf similarity index 100% rename from .config/hypr/animations/optimized.conf rename to gui/.config/hypr/animations/optimized.conf diff --git a/.config/hypr/animations/standard.conf b/gui/.config/hypr/animations/standard.conf similarity index 100% rename from .config/hypr/animations/standard.conf rename to gui/.config/hypr/animations/standard.conf diff --git a/gui/.config/hypr/animations/theme.conf b/gui/.config/hypr/animations/theme.conf new file mode 100644 index 0000000..ab4a157 --- /dev/null +++ b/gui/.config/hypr/animations/theme.conf @@ -0,0 +1 @@ +source = ./HyDE.conf \ No newline at end of file diff --git a/.config/hypr/animations/vertical.conf b/gui/.config/hypr/animations/vertical.conf similarity index 100% rename from .config/hypr/animations/vertical.conf rename to gui/.config/hypr/animations/vertical.conf diff --git a/gui/.config/hypr/battery.sh b/gui/.config/hypr/battery.sh new file mode 100755 index 0000000..6a86692 --- /dev/null +++ b/gui/.config/hypr/battery.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Battery readout for the hyprlock label. Prints nothing on machines without a +# battery (wm desktop), so the label just stays empty there. +for bat in /sys/class/power_supply/BAT*; do + [ -r "$bat/capacity" ] || continue + cap=$(<"$bat/capacity") + glyph="󰁹" + [ "$(cat "$bat/status" 2>/dev/null)" = "Charging" ] && glyph="󰂄" + printf '%s %s%%' "$glyph" "$cap" + exit 0 +done diff --git a/gui/.config/hypr/host.conf b/gui/.config/hypr/host.conf new file mode 100644 index 0000000..0c871ad --- /dev/null +++ b/gui/.config/hypr/host.conf @@ -0,0 +1,4 @@ +# Default (empty) per-host extras — fallback shared by all GUI hosts. +# Real host.conf comes from the per-host overlay (e.g. wm/ sources nvidia.conf here). +# Present so `source = ./host.conf` in hyprland.conf always resolves, even mid-install +# before the host overlay is stowed (avoids the transient "source globbing error"). diff --git a/.config/hypr/hyde.conf b/gui/.config/hypr/hyde.conf similarity index 100% rename from .config/hypr/hyde.conf rename to gui/.config/hypr/hyde.conf diff --git a/gui/.config/hypr/hypridle.conf b/gui/.config/hypr/hypridle.conf new file mode 100644 index 0000000..b1466e7 --- /dev/null +++ b/gui/.config/hypr/hypridle.conf @@ -0,0 +1,50 @@ +#!   ░▒▒▒░░░▓▓          ___________ +#! ░░▒▒▒░░░░░▓▓        //___________/ +#! ░░▒▒▒░░░░░▓▓     _   _ _    _ _____ +#! ░░▒▒░░░░░▓▓▓▓▓▓ | | | | |  | |  __/ +#! ░▒▒░░░░▓▓   ▓▓ | |_| | |_/ /| |___ +#!  ░▒▒░░▓▓   ▓▓   \__  |____/ |____/ +#!    ░▒▓▓   ▓▓  //____/ + +# $LOCKSCREEN = hyprlock # preferred LOCKSCREEN e.g swaylock +$LOCKSCREEN = lockscreen.sh # Calls $LOCKSCREEN set from hyprland + +general { + lock_cmd = $LOCKSCREEN + unlock_cmd = #notify-send "unlock!" # same as above, but unlock + before_sleep_cmd = $LOCKSCREEN # command ran before sleep + after_sleep_cmd = # notify-send "Awake!" # command ran after sleep + ignore_dbus_inhibit = 0 +} + +# Dims the display +listener { + timeout = 120 +on-timeout = { brightnessctl -s && brightnessctl s 1% ;} + on-resume = brightnessctl -r +} + +# Lock it first before dpms off so that screen won't show for a moment after wake up. +listener { + timeout = 240 + on-timeout = $LOCKSCREEN +} + +# DPMS off +listener { + timeout = 2300 + on-timeout = hyprctl dispatch dpms off #do not turn off display while media is playing + on-resume = hyprctl dispatch dpms on +} + +# Suspend +# listener { +# timeout = 500 +# on-timeout = systemctl suspend +# } + +# hyprlang noerror true +# Source anything from this path if you want to add your own listener +# source command actually do not exist yet +source = ~/.config/hypridle/* +# hyprlang noerror false diff --git a/gui/.config/hypr/hyprland.conf b/gui/.config/hypr/hyprland.conf new file mode 100644 index 0000000..328e8ab --- /dev/null +++ b/gui/.config/hypr/hyprland.conf @@ -0,0 +1,117 @@ +$HYDE_HYPRLAND=set #! Do not remove - HyDE marker to prevent file overwrite + +#!   ░▒▒▒░░░▓▓          ___________ +#! ░░▒▒▒░░░░░▓▓        //___________/ +#! ░░▒▒▒░░░░░▓▓     _   _ _    _ _____ +#! ░░▒▒░░░░░▓▓▓▓▓▓ | | | | |  | |  __/ +#! ░▒▒░░░░▓▓   ▓▓ | |_| | |_/ /| |___ +#!  ░▒▒░░▓▓   ▓▓   \__  |____/ |____/ +#!    ░▒▓▓   ▓▓  //____/ + +#?  HyDE's Hyprland Configuration  +# First, let's understand the structure of the configuration file +# It is divided into 3 sections: Boilerplate, Overrides, and Users +#* 1. Boilerplate +# - This section is for the default configuration of HyDE +# - It is recommended to not change this section +# - Filepath: $XDG_DATA_HOME/share/hyde/hyprland.conf +#* 2. Overrides +# - This section is for overriding the default configuration of HyDE +# - Change this section if you intended to: +# - Change the startup and env variables +# - Stop a app/service from starting +# - Override HyDE specific variables +# - Filepath: $XDG_CONFIG_HOME/hypr/hyde.conf +#* 3. Users +# - This section is for user configuration +# - It is recommended to change this section to your liking +# - Filepath: ./keybindings.conf, ./windowrules.conf, ./monitors.conf, ./userprefs.conf + + + +#?  Boilerplate  +source = ~/.local/share/hyde/hyprland.conf + + +#?  Overrides  +# Overriding startup and env variables +# To avoid breaking and conflicts, HyDE will use variables to store the env and startup variables +# See ~/.config/hypr/hyde.conf for overriding the default env and startup variables +#* startup: +# - The startup variables are the variables that are set when the shell starts +# - $start.BAR=waybar will launch waybar on startup (default waybar) +# - $start.BAR= will unset the variable +# - $start.BAR=other_bar will launch other_bar on startup, overriding the default (waybar) +#* env: +# - The env variables are the variables that are set when the shell starts +# - $env.QT_QPA_PLATFORMTHEME= will unset the variable +# - $env.QT_QPA_PLATFORMTHEME=qt6ct will set the variable to qt6ct +# for updated configuration, see the https://github.com/HyDE-Project/HyDE/blob/master/Configs/.config/hypr/hyde.conf + + +#?  Users  +# These files are for user configuration +# Edit the files to your liking +# Refer to https://wiki.hyprland.org +source = ./keybindings.conf # Keyboard shortcuts +source = ./windowrules.conf # Window rules +source = ./monitors.conf # Monitor configuration (from per-host overlay) +source = ./userprefs.conf # Personal hyprland configuration +source = ./host.conf # Per-host extras: nvidia, etc. (from per-host overlay) + + + +#!▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ + +#? █▀█ █▀▀ █▀▀ █▀▀ █▀█ █▀▀ █▄░█ █▀▀ █▀▀ +#? █▀▄ ██▄ █▀░ ██▄ █▀▄ ██▄ █░▀█ █▄▄ ██▄ + +#!▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ + + +#?  Reference  +# Configurations below are for reference ONLY +# They are not essential for HyDE to work but can be used to enhance QOL +# Copy the config line and paste it in the Users section to use it +# This is to prevent bloating users configuration files when not needed + +# // █▀▀ █▄░█ █░█ +# // ██▄ █░▀█ ▀▄▀ + +# env = MANGOHUD,1 +# env = MANGOHUD_DLSYM,1 + + +# // █▄▀ █▀▀ █▄█ █▄▄ █ █▄░█ █▀▄ █ █▄░█ █▀▀ █▀ +# // █░█ ██▄ ░█░ █▄█ █ █░▀█ █▄▀ █ █░▀█ █▄█ ▄█ + +# bind = Ctrl+Alt+Shift, R, pass, ^(com\.obsproject\.Studio)$ # toggle obs screen recording // install obs flatpak +# bind = $mainMod+Shift, G, exec, pkill -x rofi || $scrPath/gamelauncher.sh # launch steam game launcher // install steam +# bind = $mainMod, Period, exec, emote # launch emoji selector // install emote +# bind = $mainMod+Alt, XF86MonBrightnessDown, exec, hyprshade on blue-light-filter # enable blue light filter // install hyprshade +# bind = $mainMod+Alt, XF86MonBrightnessUp, exec, hyprshade off # disable blue light filter // install hyprshade + + +# // █░░ ▄▀█ █░█ █▄░█ █▀▀ █░█ +# // █▄▄ █▀█ █▄█ █░▀█ █▄▄ █▀█ + +# exec-once = swayidle -w timeout 600 'swaylock' timeout 900 'hyprctl dispatch dpms off' resume 'hyprctl dispatch dpms on' # lock after 10 mins, sleep after 15 mins // install swayidle +# exec-once = swayidle -w timeout 1200 'swaylock; hyprctl dispatch dpms off' resume 'hyprctl dispatch dpms on' timeout 1800 'systemctl suspend' # lock and sleep after 20 mins, suspend after 30 mins // install swayidle + +# exec-once = libinput-gestures // install libinput-gestures + +# exec-once = swayosd-server # enable swayosd service +# exec-once = `pkexec swayosd-libinput-backend` # swayosd service for keyboard input (requires to be run in a subshell) + +# // █░█░█ █ █▄░█ █▀▄ █▀█ █░█░█   █▀█ █░█ █░░ █▀▀ █▀ +# // ▀▄▀▄▀ █ █░▀█ █▄▀ █▄█ ▀▄▀▄▀   █▀▄ █▄█ █▄▄ ██▄ ▄█ + + + +# // █░█ █▀▄▀█ +# // ▀▄▀ █░▀░█ + +# bind = Ctrl+Alt_L, V, submap, passthrough +# submap = passthrough +# bind = Ctrl+Alt_L, V, submap, reset +# submap = reset diff --git a/gui/.config/hypr/hyprlock-host.conf b/gui/.config/hypr/hyprlock-host.conf new file mode 100644 index 0000000..8d6f522 --- /dev/null +++ b/gui/.config/hypr/hyprlock-host.conf @@ -0,0 +1,9 @@ +# Default (empty) per-host hyprlock sizes — fallback shared by all GUI hosts. +# The real one comes from the per-host overlay (see lw/.config/hypr/hyprlock-host.conf). +# Present so `source = ~/.config/hypr/hyprlock-host.conf` in hyprlock.conf always resolves, +# even mid-install before the host overlay is stowed. A missing `source` target is a hard +# parse error in hyprlang — for hyprlock that means the lock screen itself fails, so this +# file must never be deleted. +# +# gui's defaults are tuned for wm (3072x1728 logical). To override, redefine any of: +# $fsClock $fsDate $fsMeta $fsSmall $fsIcon $logoSize diff --git a/gui/.config/hypr/hyprlock.conf b/gui/.config/hypr/hyprlock.conf new file mode 100644 index 0000000..e9c6832 --- /dev/null +++ b/gui/.config/hypr/hyprlock.conf @@ -0,0 +1,195 @@ +#! █░█ █▄█ █▀█ █▀█ █░░ █▀█ █▀▀ █▄▀ +#! █▀█ ░█░ █▀▀ █▀▄ █▄▄ █▄█ █▄▄ █░█ + +#* Hyprlock Configuration File + +# Everything is anchored to the CENTRE (only the two bottom-corner labels use valign), in this +# order top to bottom: uptime, greeting, logo, input field, clock, date. The offsets spread the +# content out to the top and bottom edges. Centre-anchoring is deliberate: an earlier pass used +# TOP/BOTTOM valign bands and silently reordered the clock and date, putting the date above the +# clock and the clock half off the bottom edge — don't reintroduce that. +# Every `position` and `size` is a percentage (hyprlock accepts them: both are LAYOUTCONFIG +# values), so the arrangement scales instead of being the fixed pixel offsets it once was. +# The percentages are the old pixel offsets expressed against a 1080-tall screen. +# +# `font_size` and `image:size` are plain ints in hyprlock 0.9.6 and cannot take a +# percentage, so they can't scale on their own. They're hoisted into the variables below: +# retune those few numbers for a very different screen (they are tuned for wm, 3072x1728 logical), +# or `source` a per-host file that redefines them, as kitty.conf does with host.conf. +# +# Coordinates: +x is right, +y is UP — so centre-anchored, a positive y sits above centre. +# (For the two bottom-corner labels, valign = bottom means positive y moves up from it.) + +$fontColor = rgba(255,255,255,0.5) +$fontFamily = SourceCodeVF ExtraBold +$timeFontFamily = Cantarell ExtraBold + +# Type scale — tuned for wm's 3072x1728 logical screen (this file is shared with lw — +# see ToDo.md). Since these can't be +# percentages, they are the one thing to retune on a very different display: they are +# roughly clock 13%, date 2.5%, meta 1.5% of screen height. +$fsClock = 220 +$fsDate = 44 +$fsMeta = 26 +$fsSmall = 24 +$fsIcon = 40 +$logoSize = 280 + +# Vertical positions — % of screen height from centre, + is up. Order top to bottom: uptime, +# greet, logo, input, clock, date. Deliberately pushed OUT to the edges: uptime/greet ride near +# the top, clock/date near the bottom, leaving logo + input as the only things near the middle. +# Anchored to centre rather than valign top/bottom on purpose — an earlier pass used bands and +# silently reordered the clock and date (see the header). Percentages scale, and because each +# host's font sizes are set to the same *proportion* of its screen (clock ~12.8% of height on +# both), one set of offsets gives both hosts the same layout — hence no per-host override here +# by default. Add $y* to hyprlock-host.conf if a host ever needs to differ. +$yUptime = 45% +$yGreet = 41% +$yLogo = 12% +$yInput = -5% +$yClock = -30% +$yDate = -42% + +# Per-host override for the six sizes above. They are ints, not percentages, so they are the +# one thing that cannot scale itself — a host with a different panel redefines whichever it +# needs here, and everything below picks up the new value (hyprlang expands in file order). +# Shipped empty in gui/ so this always resolves even mid-install, before a host overlay is +# stowed — same idiom, and same reason, as hypr/host.conf. +source = ~/.config/hypr/hyprlock-host.conf + +general { + ignore_empty_input = true + text_trim = true +} +# Dropped as of hyprlock 0.9.6 — it rejected all four as "config option does not exist" +# and carried on without them, so they never took effect: +# no_fade_in / fade_in_time -> fade-in is CLI-only now: lockscreen.sh --no-fade-in +# grade, disable_loading_bar -> no equivalent + +background { + monitor = + path = ~/.config/hypr/hyprlock/assets/background.jpg + # Heavier blur than you'd expect: the wallpaper has "open source" set across its + # middle, which otherwise reads as text competing with the clock sitting on top of it. + blur_passes = 3 + blur_size = 6 + contrast = 0.7 + brightness = 0.38 + vibrancy = 0.15 + vibrancy_darkness = 0.5 +} + + +# // ▀█▀ █▀█ █▀█ +# // ░█░ █▄█ █▀▀ + +# uptime +label { + monitor = + text = cmd[update:5000] echo "$(uptime -p)" + color = $fontColor + font_family = $fontFamily + font_size = $fsMeta + position = 0, $yUptime + halign = center + valign = center +} + +# greeting / weather +label { + monitor = + text = cmd[update:300000] echo "$(cat ~/.config/fish/greet.txt)" + color = $fontColor + font_family = $fontFamily + font_size = $fsSmall + position = 0, $yGreet + halign = center + valign = center +} + + +# // █▀▄▀█ █ █▀▄ █▀▄ █░░ █▀▀ +# // █░▀░█ █ █▄▀ █▄▀ █▄▄ ██▄ + +image { + monitor = + path = ~/.config/hypr/hyprlock/assets/logo.jpg + size = $logoSize + position = 0, $yLogo + halign = center + valign = center + reload_time = -1 + reload_cmd = +} + +input-field { + monitor = + size = 20%, 5% + position = 0, $yInput + halign = center + valign = center + outline_thickness = 3 + dots_size = 0.08 + dots_spacing = 0.2 + dots_center = true + outer_color = rgba(0,0,0,0.1) + inner_color = $fontColor + fade_on_empty = true +} + + +# // █▄▄ █▀█ ▀█▀ ▀█▀ █▀█ █▀▄▀█ +# // █▄█ █▄█ ░█░ ░█░ █▄█ █░▀░█ + +# date — sits BELOW the clock (-28% vs the clock's -17%), as it did before the band rewrite. +label { + monitor = + text = cmd[update:1000] echo "$(LC_TIME=en_US.UTF-8 date +'%A, %B %d')" + color = $fontColor + font_family = $fontFamily + font_size = $fsDate + position = 0, $yDate + halign = center + valign = center +} + +# clock — 17% below centre, directly under the input field, with the date below it. Centre- +# anchored, so y is the box's CENTRE and the glyphs grow both ways from it; budget for that +# when changing $fsClock, which is by far the tallest text here. +label { + monitor = + text = cmd[update:1000] echo "$(date +"%H:%M")" + color = $fontColor + font_family = $timeFontFamily + font_size = $fsClock + position = 0, $yClock + halign = center + valign = center + shadow_passes = 1 + shadow_size = 1 + shadow_color = rgba(0, 0, 0, 0.5) +} + +# power +label { + monitor = + text = cmd[] echo "" + color = $fontColor + font_size = $fsIcon + onclick = systemctl poweroff + position = 3%, 3% + halign = left + valign = bottom +} + +# battery (prints nothing on a desktop) +label { + monitor = + text = cmd[update:1000] echo -e "$(~/.config/hypr/battery.sh)" + color = $fontColor + font_family = $fontFamily + font_size = $fsSmall + position = -3%, 3% + halign = right + valign = bottom +} diff --git a/.config/hypr/hyprlock/Anurati.conf b/gui/.config/hypr/hyprlock/Anurati.conf similarity index 100% rename from .config/hypr/hyprlock/Anurati.conf rename to gui/.config/hypr/hyprlock/Anurati.conf diff --git a/.config/hypr/hyprlock/Arfan on Clouds.conf b/gui/.config/hypr/hyprlock/Arfan on Clouds.conf similarity index 100% rename from .config/hypr/hyprlock/Arfan on Clouds.conf rename to gui/.config/hypr/hyprlock/Arfan on Clouds.conf diff --git a/.config/hypr/hyprlock/IBM Plex.conf b/gui/.config/hypr/hyprlock/IBM Plex.conf similarity index 100% rename from .config/hypr/hyprlock/IBM Plex.conf rename to gui/.config/hypr/hyprlock/IBM Plex.conf diff --git a/.config/hypr/hyprlock/SF Pro.conf b/gui/.config/hypr/hyprlock/SF Pro.conf similarity index 100% rename from .config/hypr/hyprlock/SF Pro.conf rename to gui/.config/hypr/hyprlock/SF Pro.conf diff --git a/gui/.config/hypr/hyprlock/assets/background.jpg b/gui/.config/hypr/hyprlock/assets/background.jpg new file mode 100644 index 0000000..fdcb473 Binary files /dev/null and b/gui/.config/hypr/hyprlock/assets/background.jpg differ diff --git a/gui/.config/hypr/hyprlock/assets/logo.jpg b/gui/.config/hypr/hyprlock/assets/logo.jpg new file mode 100644 index 0000000..788b72a Binary files /dev/null and b/gui/.config/hypr/hyprlock/assets/logo.jpg differ diff --git a/gui/.config/hypr/hyprlock/theme.conf b/gui/.config/hypr/hyprlock/theme.conf new file mode 100644 index 0000000..ab4a157 --- /dev/null +++ b/gui/.config/hypr/hyprlock/theme.conf @@ -0,0 +1 @@ +source = ./HyDE.conf \ No newline at end of file diff --git a/gui/.config/hypr/keybindings.conf b/gui/.config/hypr/keybindings.conf new file mode 100644 index 0000000..e2a6207 --- /dev/null +++ b/gui/.config/hypr/keybindings.conf @@ -0,0 +1,265 @@ +## █▄▀ █▀▀ █▄█ █▄▄ █ █▄░█ █▀▄ █ █▄░█ █▀▀ █▀ +## █░█ ██▄ ░█░ █▄█ █ █░▀█ █▄▀ █ █░▀█ █▄█ ▄█ + +# see https://wiki.hyprland.org/configuring/keywords/ for more +# example binds, see https://wiki.hyprland.org/configuring/binds/ for more + +# Grouping of binds for easier management +# $d=[Group Name|Subgroup Name1|Subgroup Name2|...] +# '$d' is a variable that is used to group binds together (or use another variable) +# This is only for organization purposes and is not a defined hyprland variable +# What we did here is to modify the Description of the binds to include the group name +# The $d will be parsed as a separate key to be use for a GUI or something pretty +# [Main|Subgroup1|Subgroup2|...] +# Main - The main groupname +# Subgroup1.. - The subgroup names can be use to avoid repeating the same description + + +#*  Variables  +# Default if commented out +# $mainMod = Super # super / meta / windows key +# Assign apps +# $TERMINAL = kitty +# $EDITOR = code +# $EXPLORER = dolphin +# $BROWSER = firefox + +$wm=Window Management +$d=[$wm] +bindd = $mainMod, Q, $d close focused window, exec, $scrPath/dontkillsteam.sh +bindd = Alt, F4, $d close focused window, exec, $scrPath/dontkillsteam.sh +bindd = $mainMod, Delete, $d kill hyprland session, exit +bindd = $mainMod, W, $d toggle float, togglefloating, # +bindd = $mainMod Shift, G, $d toggle group, togglegroup +bindd = Shift, F11, $d toggle fullscreen, fullscreen +bindd = Alt, Return, $d toggle fullscreen, fullscreen +bindd = $mainMod, L, $d lock screen, exec, lockscreen.sh +bindd = $mainMod Shift, F, $d toggle pin on focused window, exec, $scrPath/window.pin.sh +bindd = Control Alt, Delete, $d logout menu, exec, $scrPath/logoutlaunch.sh +bindd = Control Alt, W, $d toggle waybar, exec, killall waybar || waybar -c ~/.config/waybar/layouts/custom.jsonc -s ~/.config/waybar/layouts/custom.css +# bindd = Control Alt, W, $d toggle waybar, exec, hyde-shell waybar --hide +# bindd = Alt_R, Control_R, W, $d toggle waybar and reload config, exec, killall waybar || (env reload_flag=1 $scrPath/wbarconfgen.sh) # was throwing error +# bindd = ALT_R, Control_R,toggle waybar, exec, killall waybar || waybar # toggle waybar without reloading, this is faster + +$d=[$wm|Group Navigation] +bindd = $mainMod Control, H, $d change active group backwards , changegroupactive, b +bindd = $mainMod Control, L, $d change active group forwards , changegroupactive, f + +$d=[$wm|Change focus] +bindd = $mainMod, Left, $d focus left, movefocus, l +bindd = $mainMod, Right, $d focus right , movefocus, r +bindd = $mainMod, Up, $d focus up , movefocus, u +bindd = $mainMod, Down , $d focus down, movefocus, d +bindd = $mainMod Control, Left, $d focus left, movefocus, l +bindd = $mainMod Control, Right, $d focus right , movefocus, r +bindd = $mainMod Control, Up, $d focus up , movefocus, u +bindd = $mainMod Control, Down, $d focus down, movefocus, d +# Super+Ctrl h/l intentionally share a combo with Group Navigation above: hyprland fires +# every bind matching a combo, and changegroupactive is a no-op outside a group. +bindd = $mainMod Control, h, $d focus left, movefocus, l +bindd = $mainMod Control, l, $d focus right , movefocus, r +bindd = $mainMod Control, k, $d focus up , movefocus, u +bindd = $mainMod Control, j , $d focus down, movefocus, d +bindd = Alt Control, Left, $d focus left, movefocus, l +bindd = Alt Control, Right, $d focus right , movefocus, r +bindd = Alt Control, Up, $d focus up , movefocus, u +bindd = Alt Control, Down, $d focus down, movefocus, d +bindd = Alt Control, h, $d focus left, movefocus, l +bindd = Alt Control, l, $d focus right , movefocus, r +bindd = Alt Control, k, $d focus up , movefocus, u +bindd = Alt Control, j , $d focus down, movefocus, d +bindd = Alt, Tab, $d focus , movefocus, d + +$d=[$wm|Resize Active Window] +# Resize windows +bindde = $mainMod Shift, Right, $d resize window right , resizeactive, 30 0 +bindde = $mainMod Shift, Left, $d resize window left, resizeactive, -30 0 +bindde = $mainMod Shift, Up, $d resize window up, resizeactive, 0 -30 +bindde = $mainMod Shift, Down, $d resize window down, resizeactive, 0 30 +bindde = $mainMod Shift, l, $d resize window right , resizeactive, 30 0 +bindde = $mainMod Shift, h, $d resize window left, resizeactive, -30 0 +bindde = $mainMod Shift, k, $d resize window up, resizeactive, 0 -30 +bindde = $mainMod Shift, j, $d resize window down, resizeactive, 0 30 + +# Move active window around current workspace with mainMod + Shift + Control [←→↑↓] +$d=[$wm|Move active window across workspace] +$moveactivewindow=grep -q "true" <<< $(hyprctl activewindow -j | jq -r .floating) && hyprctl dispatch moveactive +bindde = $mainMod Alt, left, Move activewindow to the left, exec, $moveactivewindow -30 0 || hyprctl dispatch swapwindow l +bindde = $mainMod Alt, right, Move activewindow to the right, exec, $moveactivewindow 30 0 || hyprctl dispatch swapwindow r +bindde = $mainMod Alt, up, Move activewindow up, exec, $moveactivewindow 0 -30 || hyprctl dispatch swapwindow u +bindde = $mainMod Alt, down, Move activewindow down, exec, $moveactivewindow 0 30 || hyprctl dispatch swapwindow d +bindde = $mainMod Alt, h, Move activewindow to the left, exec, $moveactivewindow -30 0 || hyprctl dispatch swapwindow l +bindde = $mainMod Alt, l, Move activewindow to the right, exec, $moveactivewindow 30 0 || hyprctl dispatch swapwindow r +bindde = $mainMod Alt, k, Move activewindow up, exec, $moveactivewindow 0 -30 || hyprctl dispatch swapwindow u +bindde = $mainMod Alt, j, Move activewindow down, exec, $moveactivewindow 0 30 || hyprctl dispatch swapwindow d +# (Super+Alt+Ctrl h/l belong to "move window to relative workspace" below; the movewindow +# variant duplicated the swapwindow block above and double-fired with those binds) + +# Move/Resize focused window +$d=[$wm|Move & Resize with mouse] +binddm = $mainMod, mouse:272, $d hold to move window, movewindow +binddm = $mainMod, mouse:273, $d hold to resize window, resizewindow +binddm = $mainMod, Z, $d hold to move window , movewindow +binddm = $mainMod, X, $d hold to resize window, resizewindow + +# Toggle focused window split +$d=[$wm] +bindd = $mainMod, J, $d toggle split, layoutmsg, togglesplit + +$l=Launcher +$d=[$l|Apps] +bindd = $mainMod, T, $d terminal emulator , exec, $TERMINAL +bindd = $mainMod Shift, T, $d terminal emulator , exec, alacritty +bindd = Control Alt, T, $d terminal emulator , exec, $TERMINAL +# bindd = Control Shift, Alt, T, $d terminal emulator , exec, tilix # launch tilix terminal emulator - using for ssh +bindd = $mainMod Control, T, $d terminal emulator, exec, tilix # launch tilix terminal emulator +# bindd = $mainMod, R, $d pyradio, exec, $TERMINAL pyradio #radio +bindd = $mainMod, I, $d work, exec, kitty --session ~/.config/kitty/c2.conf +bindd = $mainMod, O, $d work, exec, kitty --session ~/.config/kitty/runners.conf +bindd = $mainMod, R, $d pyradio, exec, kitty --session ~/.config/kitty/music.conf +bindd = $mainMod, E, $d file explorer , exec, $EXPLORER +bindd = $mainMod, C, $d neovim, exec, $TERMINAL nvim #editor +bindd = $mainMod, B, $d web browser , exec, $BROWSER +bindd = $mainMod Shift, B, $d web browser , exec, librewolf +bindd = $mainMod Control, B, $d web browser , exec, firefox +bindd = $mainMod, Y, $d ferdium, exec, ferdium +bindd = Control Shift, Escape, $d system monitor , exec, $scrPath/sysmonlaunch.sh +bindd = $mainMod, F, $d freetube , exec, freetube # youtube alternative +bindd = $mainMod, K, $d password manager , exec, keepassxc # launch password manager +bindd = $mainMod, G, $d xmpp, exec, gajim # launch messanger +bindd = $mainMod, M, $d betterbird, exec, betterbird + +# bindd = $mainMod, M, exec, $notes # launch notes app +# bindd = Ctrl+Shift, Escape, exec, $scrPath/sysmonlaunch.sh # launch system monitor (htop/btop or fallback to top) + +$d=[$l|Rofi menus] +$rofi-launch=$scrPath/rofilaunch.sh +bindd = $mainMod, A, $d application finder , exec, pkill -x rofi || $rofi-launch d +bindd = $mainMod, TAB, $d window switcher , exec, pkill -x rofi || $rofi-launch w +bindd = $mainMod Shift, E, $d file finder , exec, pkill -x rofi || $rofi-launch f +bindd = $mainMod, slash, $d keybindings hint, exec, pkill -x rofi || $scrPath/keybinds_hint.sh c # launch keybinds hint +bindd = $mainMod, comma, $d emoji picker , exec, pkill -x rofi || $scrPath/emoji-picker.sh # launch emoji picker +bindd = $mainMod, period, $d glyph picker , exec, pkill -x rofi || $scrPath/glyph-picker.sh # launch glyph picker +bindd = $mainMod, V, $d clipboard , exec, pkill -x rofi || $scrPath/cliphist.sh -c # launch clipboard, +bindd = $mainMod Shift, V, $d clipboard manager , exec, pkill -x rofi || $scrPath/cliphist.sh # launch clipboard Manager +bindd = $mainMod Shift, A, $d select rofi launcher , exec, pkill -x rofi || $scrPath/rofiselect.sh # launch select menu + + +$hc=Hardware Controls +$d=[$hc|Audio] +binddl = , F10, $d toggle mute output , exec, $scrPath/volumecontrol.sh -o m # toggle audio mute +binddl = , XF86AudioMute,$d toggle mute output, exec, $scrPath/volumecontrol.sh -o m # toggle audio mute +# binddel = , F11, $d decrease volume , exec, $scrPath/volumecontrol.sh -o d # decrease volume +# binddel = , F12, $d increase volume , exec, $scrPath/volumecontrol.sh -o i # increase volume +binddl = , XF86AudioMicMute,$d un/mute microphone , exec, $scrPath/volumecontrol.sh -i m # toggle microphone mute +binddel = , XF86AudioLowerVolume, $d decrease volume , exec, $scrPath/volumecontrol.sh -o d # decrease volume +binddel = , XF86AudioRaiseVolume, $d increase volume , exec, $scrPath/volumecontrol.sh -o i # increase volume + +$d=[$hc|Media] +binddl = , XF86AudioPlay,$d play media, exec, playerctl play-pause # toggle between media play and pause +binddl = , XF86AudioPause,$d pause media, exec, playerctl play-pause # toggle between media play and pause +binddl = , XF86AudioNext,$d next media , exec, playerctl next # media next +binddl = , XF86AudioPrev,$d previous media , exec, playerctl previous # media previous + +$d=[$hc|Brightness] +binddel = , XF86MonBrightnessUp, $d increase brightness , exec, $scrPath/brightnesscontrol.sh i # increase brightness +binddel = , XF86MonBrightnessDown, $d decrease brightness , exec, $scrPath/brightnesscontrol.sh d # decrease brightness + + +$ut=Utilities +$d=[$ut] +# binddl = $mainMod, K, $d toggle keyboard layout , exec, $scrPath/keyboardswitch.sh # switch keyboard layout +bindd = $mainMod Alt, G, $d game mode , exec, $scrPath/gamemode.sh # disable hypr effects for gamemode + +$d=[$ut|Screen Capture] +bindd = $mainMod Shift, P, $d color picker, exec, hyprpicker -an # Pick color (Hex) >> clipboard# +bindd = $mainMod, P, $d snip screen , exec, $scrPath/screenshot.sh s # partial screenshot capture +bindd = $mainMod Control, P, $d freeze and snip screen, exec, $scrPath/screenshot.sh sf # partial screenshot capture (frozen screen) +binddl = $mainMod Alt, P, $d print monitor , exec, $scrPath/screenshot.sh m # monitor screenshot capture +binddl = , Print, $d print all monitors , exec, $scrPath/screenshot.sh p # all monitors screenshot capture + +# TODO Make a main rofi menu for these selectors +$rice=Theming and Wallpaper +$d=[$rice] +bindd = $mainMod Alt Control Shift, Right, $d next global wallpaper , exec, $scrPath/wallpaper.sh -Gn # next global wallpaper +bindd = $mainMod Alt Control Shift, Left, $d previous global wallpaper , exec, $scrPath/wallpaper.sh -Gp # previous global wallpaper +bindd = $mainMod Shift, W, $d select a global wallpaper , exec, pkill -x rofi || $scrPath/wallpaper.sh -SG # launch wallpaper select menu +# bindd = $mainMod Alt, Up, $d next waybar layout , exec, $scrPath/wbarconfgen.sh n # next waybar mode +# bindd = $mainMod Alt, Down, $d previous waybar layout , exec, $scrPath/wbarconfgen.sh p # previous waybar mode +bindd = $mainMod Shift, R, $d reload hyprland + waybar, exec, hyprctl reload; killall waybar; setsid -f waybar -c ~/.config/waybar/layouts/custom.jsonc -s ~/.config/waybar/layouts/custom.css +# bindd = $mainMod Shift, R, $d wallbash mode selector , exec, pkill -x rofi || $scrPath/wallbashtoggle.sh -m # (HyDE default; needs framework) +# bindd = $mainMod Shift, T, $d select a theme, exec, pkill -x rofi || $scrPath/themeselect.sh # launch theme select menu + + + +$ws=Workspaces +$d=[$ws|Navigation] +bindd = $mainMod, 1, $d navigate to workspace 1 , workspace, r~1 +bindd = $mainMod, 2, $d navigate to workspace 2 , workspace, r~2 +bindd = $mainMod, 3, $d navigate to workspace 3 , workspace, r~3 +bindd = $mainMod, 4, $d navigate to workspace 4 , workspace, r~4 +bindd = $mainMod, 5, $d navigate to workspace 5 , workspace, r~5 +bindd = $mainMod, 6, $d navigate to workspace 6 , workspace, r~6 +bindd = $mainMod, 7, $d navigate to workspace 7 , workspace, r~7 +bindd = $mainMod, 8, $d navigate to workspace 8 , workspace, r~8 +bindd = $mainMod, 9, $d navigate to workspace 9 , workspace, r~9 +bindd = $mainMod, 0, $d navigate to workspace 10 , workspace, r~10 + +$d=[$ws|Navigation|Relative workspace] +bindd = $mainMod Control Shift, Right, $d change active workspace forwards , workspace, r+1 +bindd = $mainMod Control Shift, Left, $d change active workspace backwards , workspace, r-1 +bindd = $mainMod Control Shift, L, $d change active workspace forwards , workspace, r+1 +bindd = $mainMod Control Shift, H, $d change active workspace backwards , workspace, r-1 + +$d=[$ws|Navigation] +# E for "empty" — Super+Ctrl+Down is taken by movefocus above +bindd = $mainMod Control, E, $d navigate to the nearest empty workspace , workspace, empty + +# Move focused window to a workspace +$d=[$ws|Move window to workspace] +bindd = $mainMod Control, 1, $d move to workspace 1 , movetoworkspace, r~1 +bindd = $mainMod Control, 2, $d move to workspace 2 , movetoworkspace, r~2 +bindd = $mainMod Control, 3, $d move to workspace 3 , movetoworkspace, r~3 +bindd = $mainMod Control, 4, $d move to workspace 4 , movetoworkspace, r~4 +bindd = $mainMod Control, 5, $d move to workspace 5 , movetoworkspace, r~5 +bindd = $mainMod Control, 6, $d move to workspace 6 , movetoworkspace, r~6 +bindd = $mainMod Control, 7, $d move to workspace 7 , movetoworkspace, r~7 +bindd = $mainMod Control, 8, $d move to workspace 8 , movetoworkspace, r~8 +bindd = $mainMod Control, 9, $d move to workspace 9 , movetoworkspace, r~9 +bindd = $mainMod Control, 0, $d move to workspace 10 , movetoworkspace, r~10 + +# Move focused window to a relative workspace +$d=[$ws] +bindd = $mainMod Control Alt, Right, $d move window to next relative workspace , movetoworkspace, r+1 +bindd = $mainMod Control Alt, Left, $d move window to previous relative workspace , movetoworkspace, r-1 +bindd = $mainMod Control Alt, l, $d move window to next relative workspace , movetoworkspace, r+1 +bindd = $mainMod Control Alt, h, $d move window to previous relative workspace , movetoworkspace, r-1 + +# Scroll through existing workspaces +$d=[$ws|Navigation] +bindd = $mainMod, mouse_down, $d next workspace, workspace, e+1 +bindd = $mainMod, mouse_up, $d previous workspace, workspace, e-1 + + +# Move/Switch to special workspace (scratchpad) +$d=[$ws|Navigation|Special workspace] +bindd = $mainMod Shift, S, $d move to scratchpad , movetoworkspace, special +bindd = $mainMod Alt, S, $d move to scratchpad (silent) , movetoworkspacesilent, special +bindd = $mainMod, S, $d toggle scratchpad , togglespecialworkspace + +# Move focused window to a workspace silently +$d=[$ws|Navigation|Move window silently] +bindd = $mainMod Alt, 1, $d move to workspace 1 (silent), movetoworkspacesilent, r~1 +bindd = $mainMod Alt, 2, $d move to workspace 2 (silent), movetoworkspacesilent, r~2 +bindd = $mainMod Alt, 3, $d move to workspace 3 (silent), movetoworkspacesilent, r~3 +bindd = $mainMod Alt, 4, $d move to workspace 4 (silent), movetoworkspacesilent, r~4 +bindd = $mainMod Alt, 5, $d move to workspace 5 (silent), movetoworkspacesilent, r~5 +bindd = $mainMod Alt, 6, $d move to workspace 6 (silent), movetoworkspacesilent, r~6 +bindd = $mainMod Alt, 7, $d move to workspace 7 (silent), movetoworkspacesilent, r~7 +bindd = $mainMod Alt, 8, $d move to workspace 8 (silent), movetoworkspacesilent, r~8 +bindd = $mainMod Alt, 9, $d move to workspace 9 (silent), movetoworkspacesilent, r~9 +bindd = $mainMod Alt, 0, $d move to workspace 10 (silent), movetoworkspacesilent, r~10 + + + +$d=#! unset the group name diff --git a/gui/.config/hypr/monitors.conf b/gui/.config/hypr/monitors.conf new file mode 100644 index 0000000..89727e0 --- /dev/null +++ b/gui/.config/hypr/monitors.conf @@ -0,0 +1,6 @@ +# Default monitor config (fallback shared by all GUI hosts). +# Per-host overlays (lw/, wm/, fl/, …) override this with their own monitors.conf via +# stow --override. It exists here so `source = ./monitors.conf` in hyprland.conf always +# resolves — even mid-install, before the host overlay is stowed (avoids the transient +# "source globbing error: found no match"). +monitor = , preferred, auto, 1 diff --git a/gui/.config/hypr/userprefs.conf b/gui/.config/hypr/userprefs.conf new file mode 100644 index 0000000..85cb09a --- /dev/null +++ b/gui/.config/hypr/userprefs.conf @@ -0,0 +1,43 @@ + +# █░█ █▀ █▀▀ █▀█   █▀█ █▀█ █▀▀ █▀▀ █▀ +# █▄█ ▄█ ██▄ █▀▄   █▀▀ █▀▄ ██▄ █▀░ ▄█ + +# Set your personal hyprland configuration here + + +# BAR BLUR +# +# The blur behind the bars only ever came from HyDE's *generated* `hypr/themes/theme.conf`, +# which this repo deliberately does not carry (HyDE writes it per host) — so without this +# block the bars have no blur behind them. That generated file also still ships the pre-0.45 +# form (`layerrule = blur,waybar`), which current hyprland rejects; HyDE wraps the source in +# `hyprlang noerror true`, so it fails silently either way. Re-declared here in the current +# `,match:namespace ` syntax, matching windowrules.conf. `layerrule` is additive. +# Keep comments on their own lines: a trailing one is parsed as part of the value. +# (`cavabar` is the standalone cava bar from waybar/layouts/custom.jsonc.) +layerrule = blur true,match:namespace waybar +layerrule = ignore_alpha 0,match:namespace waybar +layerrule = blur true,match:namespace cavabar +layerrule = ignore_alpha 0,match:namespace cavabar + + + +# // █░░ ▄▀█ █░█ █▄░█ █▀▀ █░█ +# // █▄▄ █▀█ █▄█ █░▀█ █▄▄ █▀█ + +# (xremap lives in lw's host.conf — it's the only host with an xremap config; wm uses evremap) +env = SSH_AUTH_SOCK,$XDG_RUNTIME_DIR/ssh-agent.socket # ssh on start +env = ELECTRON_OZONE_PLATFORM_HINT,x11 # Disable after hyprland fixes electron flickering +exec-once = waybar -c ~/.config/waybar/layouts/custom.jsonc -s ~/.config/waybar/layouts/custom.css + +# (No static `noanim` layerrule: current Hyprland rejects it in the match: syntax, and +# grimblast already suppresses selection animation at runtime via `hyprctl keyword layerrule`.) + + +# I know there is like two 'solutions' already, so have third one - (tested GTX 1060, 555.58.02 drivers) - run flickery apps with --disable-gpu-compositing. This makes apps slightly less smooth (GPU still used tho), but fixes the issue. I think this is better then to disable damage_tracking, which works too. + +# render { +# explicit_sync = 1 +# explicit_sync_kms = 1 +# direct_scanout = no +# } diff --git a/gui/.config/hypr/windowrules.conf b/gui/.config/hypr/windowrules.conf new file mode 100644 index 0000000..aba683f --- /dev/null +++ b/gui/.config/hypr/windowrules.conf @@ -0,0 +1,145 @@ + +# █░█░█ █ █▄░█ █▀▄ █▀█ █░█░█   █▀█ █░█ █░░ █▀▀ █▀ +# ▀▄▀▄▀ █ █░▀█ █▄▀ █▄█ ▀▄▀▄▀   █▀▄ █▄█ █▄▄ ██▄ ▄█ + +# See https://wiki.hypr.land/Configuring/Window-Rules/ + +#? ------- WINDOWRULES_HYPRLAND_V_0_53 COMPATIBLE ---------------- +# This Config is compatible with windowrules changes in Hyprland v0.53+ +# Any future breaking changes disables this block and notifies the user to update. +# User template files will be provided in $XDG_DATA_HOME/hyde/templates/hypr/windowrules.conf for manual update. + +# hyprlang if WINDOWRULES_HYPRLAND_V_0_53 + +# idle_inhibit rules +windowrule = idle_inhibit fullscreen, match:class ^(.*celluloid.*)$|^(.*mpv.*)$|^(.*vlc.*)$ +windowrule = idle_inhibit fullscreen, match:class ^(.*[Ss]potify.*)$ +windowrule = idle_inhibit fullscreen, match:class ^(.*LibreWolf.*)$|^(.*floorp.*)$|^(.*brave-browser.*)$|^(.*firefox.*)$|^(.*chromium.*)$|^(.*zen.*)$|^(.*vivaldi.*)$ + +# Picture-in-Picture +windowrule { + name = hyde_picture_in_picture + match:title = ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ + tag = +picture-in-picture + tag = +hyde_picture_in_picture + float = true + keep_aspect_ratio = true + move = (monitor_w*0.73) (monitor_h*0.72) + size = (monitor_w*0.25) (monitor_h*0.25) + pin = true +} + +windowrule = opacity 0.90 $& 0.90 $& 1,match:class ^(firefox)$ +windowrule = opacity 0.90 $& 0.90 $& 1,match:class ^(zen)$ +windowrule = opacity 0.90 $& 0.90 $& 1,match:class ^(brave-browser)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(code-oss)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^([Cc]ode)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(code-url-handler)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(code-insiders-url-handler)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(kitty)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(org.kde.dolphin)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(org.kde.ark)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(nwg-look)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(qt5ct)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(qt6ct)$ +windowrule = opacity 0.80 $& 0.80 $& 1,match:class ^(kvantummanager)$ +windowrule = opacity 0.80 $& 0.70 $& 1,match:class ^(org.pulseaudio.pavucontrol)$ +windowrule = opacity 0.80 $& 0.70 $& 1,match:class ^(blueman-manager)$ +windowrule = opacity 0.80 $& 0.70 $& 1,match:class ^(nm-applet)$ +windowrule = opacity 0.80 $& 0.70 $& 1,match:class ^(nm-connection-editor)$ +windowrule = opacity 0.80 $& 0.70 $& 1,match:class ^(hyprpolkitagent)$ +windowrule = opacity 0.80 $& 0.70 $& 1,match:class ^(org.freedesktop.impl.portal.desktop.gtk)$ +windowrule = opacity 0.80 $& 0.70 $& 1,match:class ^(org.freedesktop.impl.portal.desktop.hyprland)$ +windowrule = opacity 0.70 $& 0.70 $& 1,match:class ^([Ss]team)$ +windowrule = opacity 0.70 $& 0.70 $& 1,match:class ^(steamwebhelper)$ +windowrule = opacity 0.70 $& 0.70 $& 1,match:class ^([Ss]potify)$ +windowrule = opacity 0.70 $& 0.70 $& 1,match:initial_title ^(Spotify Free)$ +windowrule = opacity 0.70 $& 0.70 $& 1,match:initial_title ^(Spotify Premium)$ +windowrule = opacity 1.00 $& 1.00 $& 1,match:class ^(blender)$ + +windowrule = opacity 0.90 0.90,match:class ^(com.github.rafostar.Clapper)$ # Clapper-Gtk +windowrule = opacity 0.80 0.80,match:class ^(com.github.tchx84.Flatseal)$ # Flatseal-Gtk +windowrule = opacity 0.80 0.80,match:class ^(hu.kramo.Cartridges)$ # Cartridges-Gtk +windowrule = opacity 0.80 0.80,match:class ^(com.obsproject.Studio)$ # Obs-Qt +windowrule = opacity 0.80 0.80,match:class ^(gnome-boxes)$ # Boxes-Gtk +windowrule = opacity 0.80 0.80,match:class ^(vesktop)$ # Vesktop +windowrule = opacity 0.80 0.80,match:class ^(discord)$ # Discord-Electron +windowrule = opacity 0.80 0.80,match:class ^(WebCord)$ # WebCord-Electron +windowrule = opacity 0.80 0.80,match:class ^(ArmCord)$ # ArmCord-Electron +windowrule = opacity 0.80 0.80,match:class ^(app.drey.Warp)$ # Warp-Gtk +windowrule = opacity 0.80 0.80,match:class ^(net.davidotek.pupgui2)$ # ProtonUp-Qt +windowrule = opacity 0.80 0.80,match:class ^(yad)$ # Protontricks-Gtk +windowrule = opacity 0.80 0.80,match:class ^(Signal)$ # Signal-Gtk +windowrule = opacity 0.80 0.80,match:class ^(io.github.alainm23.planify)$ # planify-Gtk +windowrule = opacity 0.80 0.80,match:class ^(io.gitlab.theevilskeleton.Upscaler)$ # Upscaler-Gtk +windowrule = opacity 0.80 0.80,match:class ^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gtk +windowrule = opacity 0.80 0.80,match:class ^(io.gitlab.adhami3310.Impression)$ # Impression-Gtk +windowrule = opacity 0.80 0.80,match:class ^(io.missioncenter.MissionCenter)$ # MissionCenter-Gtk +windowrule = opacity 0.80 0.80,match:class ^(io.github.flattool.Warehouse)$ # Warehouse-Gtk + +windowrule = float true,match:class ^(Signal)$ # Signal-Gtk +windowrule = float true,match:class ^(com.github.rafostar.Clapper)$ # Clapper-Gtk +windowrule = float true,match:class ^(app.drey.Warp)$ # Warp-Gtk +windowrule = float true,match:class ^(net.davidotek.pupgui2)$ # ProtonUp-Qt +windowrule = float true,match:class ^(yad)$ # Protontricks-Gtk +windowrule = float true,match:class ^(eog)$ # Imageviewer-Gtk +windowrule = float true,match:class ^(io.github.alainm23.planify)$ # planify-Gtk +windowrule = float true,match:class ^(io.gitlab.theevilskeleton.Upscaler)$ # Upscaler-Gtk +windowrule = float true,match:class ^(com.github.unrud.VideoDownloader)$ # VideoDownloader-Gkk +windowrule = float true,match:class ^(io.gitlab.adhami3310.Impression)$ # Impression-Gtk +windowrule = float true,match:class ^(io.missioncenter.MissionCenter)$ # MissionCenter-Gtk +windowrule = float true,match:title ^(Friends List)$ # Steam Friends List +windowrule = float true,match:title ^(Steam Settings)$ # Steam Settings +windowrule = float true,match:initial_title ^(Image Editor)$,match:class ^(blender)$ # Blender Render +windowrule = size (monitor_w*0.5) (monitor_h*0.5),match:initial_title ^(Image Editor)$,match:class ^(blender)$ +windowrule = float true, match:initial_title ^(Ghidra: NO ACTIVE PROJECT) #Ghidra Project manager + +# workaround for jetbrains IDEs dropdowns/popups cause flickering +windowrule = no_initial_focus true,match:class ^(.*jetbrains.*)$,match:title ^(win[0-9]+)$ + +# █░░ ▄▀█ █▄█ █▀▀ █▀█   █▀█ █░█ █░░ █▀▀ █▀ +# █▄▄ █▀█ ░█░ ██▄ █▀▄   █▀▄ █▄█ █▄▄ ██▄ ▄█ + +layerrule = blur true,match:namespace rofi +layerrule = ignore_alpha 0,match:namespace rofi +layerrule = blur true,match:namespace notifications +layerrule = ignore_alpha 0,match:namespace notifications +layerrule = blur true,match:namespace swaync-notification-window +layerrule = ignore_alpha 0,match:namespace swaync-notification-window +layerrule = blur true,match:namespace swaync-control-center +layerrule = ignore_alpha 0,match:namespace swaync-control-center +layerrule = blur true,match:namespace logout_dialog + +# █▀▄ █▀█ ▀█▀ █▀ █▀▀ █░█ █▀ ▀█▀ █▀█ █▀▄▀█ +# █▄▀ █▄█ ░█░ ▄█ █▄▄ █▄█ ▄█ ░█░ █▄█ █░▀░█ +# dots-only rules preserved from the legacy windowrules.conf (converted to v0.53 syntax) + +# float utility / settings / dialog windows +windowrule = float true,match:class ^(org.kde.dolphin)$,match:title ^(Progress Dialog — Dolphin)$ +windowrule = float true,match:class ^(org.kde.dolphin)$,match:title ^(Copying — Dolphin)$ +windowrule = float true,match:title ^(About Mozilla Firefox)$ +windowrule = float true,match:class ^(firefox)$,match:title ^(Library)$ +windowrule = float true,match:class ^(kitty)$,match:title ^(top)$ +windowrule = float true,match:class ^(kitty)$,match:title ^(btop)$ +windowrule = float true,match:class ^(kitty)$,match:title ^(htop)$ +windowrule = float true,match:class ^(vlc)$ +windowrule = float true,match:class ^(kvantummanager)$ +windowrule = float true,match:class ^(qt5ct)$ +windowrule = float true,match:class ^(qt6ct)$ +windowrule = float true,match:class ^(nwg-look)$ +windowrule = float true,match:class ^(org.kde.ark)$ +windowrule = float true,match:class ^(org.pulseaudio.pavucontrol)$ +windowrule = float true,match:class ^(blueman-manager)$ +windowrule = float true,match:class ^(nm-applet)$ +windowrule = float true,match:class ^(nm-connection-editor)$ +windowrule = float true,match:class ^(org.kde.polkit-kde-authentication-agent-1)$ +windowrule = float true,match:class ^(xdg-desktop-portal-gtk)$ + +# common file-dialog modals +windowrule = float true,match:title ^(Open)$ +windowrule = float true,match:title ^(Choose Files)$ +windowrule = float true,match:title ^(Save As)$ +windowrule = float true,match:title ^(Confirm to replace files)$ +windowrule = float true,match:title ^(File Operation Progress)$ + +# hyprlang endif diff --git a/gui/.config/kitty/host.conf b/gui/.config/kitty/host.conf new file mode 100644 index 0000000..88f4454 --- /dev/null +++ b/gui/.config/kitty/host.conf @@ -0,0 +1,2 @@ +# Per-host kitty overrides. Empty by default — a host overlay (e.g. lw/) replaces this file +# to tweak font_size/padding/opacity for that machine. kitty.conf includes it LAST. diff --git a/.config/kitty/kitty.conf b/gui/.config/kitty/kitty.conf similarity index 54% rename from .config/kitty/kitty.conf rename to gui/.config/kitty/kitty.conf index 3d1de9c..ae30935 100644 --- a/.config/kitty/kitty.conf +++ b/gui/.config/kitty/kitty.conf @@ -1,16 +1,21 @@ include theme.conf -font_size 9.0 +font_size 12.0 font_family CaskaydiaCove Nerd Font Mono bold_font auto italic_font auto bold_italic_font auto -window_padding_width 25 +window_padding_width 25 enable_audio_bell no +cursor_trail 1 +cursor_blink_interval 0.4 ease-in-out tab_bar_style powerline tab_powerline_style slanted confirm_os_window_close 0 -background_opacity 0.70 +#background_opacity 0.60 #hide_window_decorations yes + +# Per-host overrides (font_size, padding, opacity…) — included last so it wins. +include host.conf diff --git a/gui/.config/kitty/music.conf b/gui/.config/kitty/music.conf new file mode 100644 index 0000000..e4b71ec --- /dev/null +++ b/gui/.config/kitty/music.conf @@ -0,0 +1,20 @@ +# run with `kitty --session ~/.config/kitty/music.conf` + +# os_window_size 220c 64c +# create a new tab +new_tab music +layout splits + +# set the working directory for windows in the current tab +cd ~ + +# Splits are sized with --bias (a percentage of the space being split) rather +# than `resize_window taller/wider N`: that takes N *cells*, so on a small window +# N swamps the available space and the other pane collapses to nothing -- cava +# was disappearing. --bias keeps the same proportions at any window size. +# Tune: --bias here is the share the *new* pane takes; cava gets the remainder. +launch --var window=first cava +# create additional terminals +launch --location=hsplit --bias 80 pyradio +focus +launch --location=vsplit --bias 65 pipes-rs diff --git a/.config/kitty/theme.conf b/gui/.config/kitty/theme.conf similarity index 83% rename from .config/kitty/theme.conf rename to gui/.config/kitty/theme.conf index 4511d8d..737c0d6 100644 --- a/.config/kitty/theme.conf +++ b/gui/.config/kitty/theme.conf @@ -28,7 +28,9 @@ mark3_background #614D85 # black color0 #232530 -color8 #232530 +# bright-black = dim/secondary text (menus, comments); was #232530 ≈ background so it +# rendered invisible. Lifted to the theme's muted lavender for legible dim text. +color8 #848bbd # red color1 #fe4450 diff --git a/gui/.config/mpv/input.conf b/gui/.config/mpv/input.conf new file mode 100644 index 0000000..1c0abe1 --- /dev/null +++ b/gui/.config/mpv/input.conf @@ -0,0 +1,65 @@ +# Volume +9 add volume -5 +0 add volume 5 + +UP add volume 2 +DOWN add volume -2 + +k add volume 2 +j add volume -2 + +# Seek backward / forward +LEFT seek -5 +RIGHT seek 5 + +l seek 5 +h seek -5 + +# Bigger jumps +Shift+LEFT seek -30 +Shift+RIGHT seek 30 + +# Precise nudge (Shift+h/l): 1s exact seek +H seek -1 exact +L seek 1 exact + +# Playback speed += add speed 0.1 +- add speed -0.1 +BS set speed 1.0 # backspace resets to normal + +# Subtitle sync +z add sub-delay -0.1 # subs earlier +Z add sub-delay 0.1 # subs later + +# Chapters +> add chapter 1 +< add chapter -1 + +# A-B loop (moved off L to avoid the precise-nudge conflict) +b ab-loop + +# Audio sync (mirrors the z/Z subtitle-delay pair) +x add audio-delay -0.1 # audio earlier +X add audio-delay 0.1 # audio later + +# Playlist nav (< / > are used for chapters) +[ playlist-prev +] playlist-next + +# Track cycling +a cycle audio # next audio track +n cycle sub # next subtitle track +v cycle sub-visibility # (default) toggle subs on/off + +# Loop current file (distinct from the b A-B loop) +r cycle-values loop-file inf no + +# Screenshots +s screenshot # (default) with subs +S screenshot video # clean frame: no subs/OSD + +# Info overlays +i script-binding stats/display-stats-toggle # (default) stats HUD +o show-progress # flash progress/OSD + diff --git a/.config/mpv/mpv.conf b/gui/.config/mpv/mpv.conf similarity index 100% rename from .config/mpv/mpv.conf rename to gui/.config/mpv/mpv.conf diff --git a/gui/.config/waybar/cava-waybar.sh b/gui/.config/waybar/cava-waybar.sh new file mode 100755 index 0000000..486e5c3 --- /dev/null +++ b/gui/.config/waybar/cava-waybar.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Self-contained waybar cava module: stream `cava` raw output as Unicode bar chars. +# No HyDE dependency — just needs `cava` installed. Usage: cava-waybar.sh [bars] +# bars = number of columns (even; ~screen_px / (font-size * 0.6)); default 94. +# +# waybar's custom/cava module uses restart-interval, so this script is relaunched +# every time it exits. Without cleanup each relaunch would leak an orphaned `cava` +# child and they'd pile up. We enforce a single instance: track cava's own PID, +# kill it on exit, and reap any stale instance left by a previous (crashed) run. +bars="${1:-94}" +chars="▁▂▃▄▅▆▇█" # cava ascii value 0..7 -> these + +conf="$(mktemp)" +pidfile="${XDG_RUNTIME_DIR:-/tmp}/cava-waybar.pid" + +cava_pid="" +cleanup() { + [[ -n "$cava_pid" ]] && kill "$cava_pid" 2>/dev/null + rm -f "$conf" "$pidfile" +} +trap cleanup EXIT INT TERM + +# Reap a stale cava from a previous run that didn't clean up (e.g. hard kill). +if [[ -f "$pidfile" ]]; then + old="$(cat "$pidfile" 2>/dev/null)" + [[ "$old" =~ ^[0-9]+$ ]] && kill "$old" 2>/dev/null +fi + +cat >"$conf" < >(sed -u "$prog") & +cava_pid=$! +echo "$cava_pid" >"$pidfile" +wait "$cava_pid" diff --git a/gui/.config/waybar/cava.width b/gui/.config/waybar/cava.width new file mode 100644 index 0000000..6529ff8 --- /dev/null +++ b/gui/.config/waybar/cava.width @@ -0,0 +1 @@ +98 diff --git a/.config/waybar/config.jsonc b/gui/.config/waybar/config.jsonc similarity index 68% rename from .config/waybar/config.jsonc rename to gui/.config/waybar/config.jsonc index da06eb3..e987932 100644 --- a/.config/waybar/config.jsonc +++ b/gui/.config/waybar/config.jsonc @@ -1,11 +1,11 @@ -// --// waybar config generated by wbarconfgen.sh //-- // +// --// standalone two-bar cava rig — hand-maintained, NOT HyDE-generated (see README "Waybar") //-- // +[ { // sourced from header module // "layer": "top", "position": "top", - "mod": "dock", "height": 33, "exclusive": true, "passthrough": false, @@ -17,7 +17,7 @@ "modules-left": ["custom/l_end","custom/power","custom/r_end","hyprland/workspaces", "custom/sl_end","hyprland/window","custom/sr_end","custom/l_end","custom/wallchange","custom/cliphist","custom/r_end"], "modules-center": ["wlr/taskbar"], - "modules-right": ["custom/notification","custom/l_end","backlight","pulseaudio","pulseaudio#microphone","mpris","custom/r_end","custom/l_end","memory","cpu","custom/cpuinfo","battery","custom/r_end","custom/l_end","tray","custom/r_end","custom/l_end","idle_inhibitor","clock","custom/r_end"], + "modules-right": ["custom/l_end","backlight","pulseaudio","pulseaudio#microphone","mpris","custom/r_end","custom/l_end","memory","cpu","custom/cpuinfo","battery","custom/r_end","custom/l_end","tray","custom/r_end","custom/l_end","idle_inhibitor","clock","custom/r_end"], // sourced from modules based on config.ctl // @@ -27,42 +27,44 @@ "spacing": 5 }, + // NOTE: keep in sync with modules/window.jsonc — this layout is launched standalone + // (waybar -c), so the modules/ file is NOT read here; this inline copy is what renders. "hyprland/window": { - "format": "{}", - "window-rewrite-default":"󰣇", + "format": " {}", "rotate": 0, "separate-outputs": true, "max-length": 1000, "rewrite": { - "${USER}@${set_sysname}:(.*)": "$1 ", "(.*) — Kitty": " $1", - "(.*)~": " $1", + "(.*)~": " $1", "(.*) — Mozilla Firefox": "󰈹 $1", "(.*)Mozilla Firefox": "󰈹 Firefox", "(.*) — LibreWolf": "󰈹 $1", "(.*)LibreWolf": "󰈹 LibreWolf", "(.*) — Chromium": " $1", "(.*)Chromium": " Chromium", - "(.*) - VSCodium": " $1", - "(.*)VSCodium": " VSCodium", + "(.*) - VSCodium": "󰨞 $1", + "(.*)VSCodium": "󰨞 VSCodium", "(.*)Code - OSS": "󰨞 $1", - "(.*)Code": "󰨞 Code", "(.*) - Visual Studio Code": "󰨞 $1", "(.*)Visual Studio Code": "󰨞 Code", - "(.*) — Dolphin": "$1 󰉋", - "(.*) - Obsidian v1.6.7": " $1 ", - "(.*)Ferdium": " $1", - "(.*)Freetube": " $1", + "(.*) — Dolphin": "󰉋 $1", + "(.*) - Obsidian(.*)": "󰠮 $1", + "(.*)Ferdium": "󰭻 Ferdium", + "(.*)FreeTube": " FreeTube", "(.*) - VLC media player": "󰕼 $1", "(.*)Steam": "󰓓 Steam", - "(.*)Neovide": " Nvim", - "(.*)Gajim": "󰟿 Gajim" + "(.*)Neovide": " Nvim", + "(.*)Neovim": " Nvim", + "(.*)Nvim": " Nvim", + "(.*)Gajim": "󰭹 Gajim", + "(.*)KeePassXC": " KeePassXC", + "(.*)Betterbird": "󰇮 Betterbird", + "(.*)Spotify": "󰓇 Spotify" } }, - "hyprland/workspaces": { - "window-rewrite-default":"", - "disable-scroll": true, + "window-rewrite-default":"", "all-outputs": true, "active-only": false, "on-click": "activate", @@ -70,30 +72,8 @@ "on-scroll-up": "hyprctl dispatch workspace -1", "on-scroll-down": "hyprctl dispatch workspace +1", "persistent-workspaces": {} -// "format":"{icon}", -// "format-icons": { -// "1": "", -// "2": "", -// "3": "", -// "4": "", -// "5": "", -// "6": "" -// } -// } }, - "bluetooth": { - "format": "", - "format-disabled": "", - "format-connected": " {num_connections}", - "format-connected-battery": "{icon} {num_connections}", - "format-icons": ["󰥇", "󰤾", "󰤿", "󰥀", "󰥁", "󰥂", "󰥃", "󰥄", "󰥅", "󰥆", "󰥈"], - "tooltip-format": "{controller_alias}\n{num_connections} connected", - "tooltip-format-connected": "{controller_alias}\n{num_connections} connected\n\n{device_enumerate}", - "tooltip-format-enumerate-connected": "{device_alias}", - "tooltip-format-enumerate-connected-battery": "{device_alias}\t{icon} {device_battery_percentage}%" - }, - "cpu": { "interval": 10, "format": "󰍛 {usage}%", @@ -160,11 +140,10 @@ "format": "{icon}", "rotate": 0, "format-icons": { - "activated": "󰥔", - "deactivated": "" + "activated": "󰅶", + "deactivated": "󰛊" } }, - "clock": { "format": "{:%H:%M}", "rotate": 0, @@ -212,17 +191,6 @@ "tooltip": true }, - "custom/theme": { - "format": "{}", - "rotate": 0, - "exec": "echo ; echo 󰟡 switch theme", - "on-click": "themeswitch.sh -n", - "on-click-right": "themeswitch.sh -p", - "on-click-middle": "sleep 0.1 && themeselect.sh", - "interval" : 86400, // once every day - "tooltip": true - }, - "pulseaudio": { "format": "{icon} {volume}", "rotate": 0, @@ -273,8 +241,6 @@ "interval": 1 }, -//not used atm - "battery": { "states": { "good": 95, @@ -289,35 +255,6 @@ "format-icons": ["󰂎", "󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"] }, - "custom/notifications": { - "format": "{icon} {}", - "rotate": 0, - "format-icons": { - "email-notification": "", - "chat-notification": "󱋊", - "warning-notification": "󱨪", - "error-notification": "󱨪", - "network-notification": "󱂇", - "battery-notification": "󰁺", - "update-notification": "󰚰", - "music-notification": "󰝚", - "volume-notification": "󰕿", - "notification": "", - "dnd": "", - "none": "" - }, - "return-type": "json", - "exec-if": "which dunstctl", - "exec": "notifications.py", - "on-scroll-down": "sleep 0.1 && dunstctl history-pop", - "on-click": "dunstctl set-paused toggle", - "on-click-middle": "dunstctl history-clear", - "on-click-right": "dunstctl close-all", - "interval": 1, - "tooltip": true, - "escape": true - }, - "backlight": { "device": "intel_backlight", "rotate": 0, @@ -327,7 +264,6 @@ "on-scroll-down": "brightnessctl set 1%-", "min-length": 6 }, - // modules for padding // "custom/l_end": { @@ -371,5 +307,26 @@ "interval" : "once", "tooltip": false } -} +}, + +// full-width cava visualizer layered behind the main bar // + +{ + "name": "cavabar", + "layer": "bottom", + "position": "top", + "height": 36, + "margin-top": -33, + "exclusive": false, + "passthrough": true, + "modules-center": ["custom/cava"], + + "custom/cava": { + "format": "{}", + "exec": "bash $HOME/.config/waybar/cava-waybar.sh $(cat $HOME/.config/waybar/cava.width 2>/dev/null || echo 94)", // per-host cava.width (bars, even; ~screen_px / (font-size*0.6)). gui default 94; wm overrides to 218 + "restart-interval": 1, + "hide-empty-text": true + } +} +] diff --git a/gui/.config/waybar/includes/border-radius.css b/gui/.config/waybar/includes/border-radius.css new file mode 100644 index 0000000..2310a0a --- /dev/null +++ b/gui/.config/waybar/includes/border-radius.css @@ -0,0 +1,307 @@ +/* +This is an autogenerated file. +Do not edit this file directly. + +This is modified to dynamically +change the border-radius which will +follow the border-radius of hyprland. + +To parse this file, we use PT as a unit +to represent the border-radius. +This is stored separately to avoid +issues on parsing the border-radius + +*/ + +/* ? Leaf shape */ + +window#waybar.top #leaf, +window#waybar.top #leaf button, +window#waybar.top #leaf menu, +window#waybar.top #leaf menuitem, +window#waybar.top #leaf tooltip, +window#waybar.bottom #leaf, +window#waybar.bottom #leaf button, +window#waybar.bottom #leaf menu, +window#waybar.bottom #leaf menuitem, +window#waybar.bottom #leaf tooltip { + border-radius: 10pt 0 10pt 0; +} + +window#waybar.left #leaf, +window#waybar.left #leaf button, +window#waybar.left #leaf menu, +window#waybar.left #leaf menuitem, +window#waybar.left #leaf tooltip, +window#waybar.right #leaf, +window#waybar.right #leaf button, +window#waybar.right #leaf menu, +window#waybar.right #leaf menuitem, +window#waybar.right #leaf tooltip { + border-radius: 10pt 0 10pt 0; +} + +/* ? Inverse Leaf shape */ + +window#waybar.top #leaf-inverse, +window#waybar.top #leaf-inverse button, +window#waybar.top #leaf-inverse menu, +window#waybar.top #leaf-inverse menuitem, +window#waybar.top #leaf-inverse tooltip, +window#waybar.bottom #leaf-inverse, +window#waybar.bottom #leaf-inverse button, +window#waybar.bottom #leaf-inverse menu, +window#waybar.bottom #leaf-inverse menuitem, +window#waybar.bottom #leaf-inverse tooltip { + border-radius: 0 10pt 0 10pt; +} + +window#waybar.left #leaf-inverse, +window#waybar.left #leaf-inverse button, +window#waybar.left #leaf-inverse menu, +window#waybar.left #leaf-inverse menuitem, +window#waybar.left #leaf-inverse tooltip, +window#waybar.right #leaf-inverse, +window#waybar.right #leaf-inverse button, +window#waybar.right #leaf-inverse menu, +window#waybar.right #leaf-inverse menuitem, +window#waybar.right #leaf-inverse tooltip { + border-radius: 0 10pt 0 10pt; +} + + +/* ? Full Pill shape*/ + +window#waybar.bottom #pill menuitem, +window#waybar.bottom #pill menu, +window#waybar.bottom #pill tooltip, +window#waybar.bottom #pill, +window#waybar.bottom #pill button, +window#waybar.top #pill menuitem, +window#waybar.top #pill menu, +window#waybar.top #pill tooltip, +window#waybar.top #pill, +window#waybar.top #pill button { + border-radius: 10pt 10pt 10pt 10pt; +} + +window#waybar.left #pill menuitem, +window#waybar.left #pill menu, +window#waybar.left #pill tooltip, +window#waybar.left #pill, +window#waybar.left #pill button, +window#waybar.right #pill menuitem, +window#waybar.right #pill menu, +window#waybar.right #pill tooltip, +window#waybar.right #pill, +window#waybar.right #pill button { + border-radius: 10pt 10pt 10pt 10pt; +} + + +/* ? half pill with the curve pointing dowmwards */ + + +window#waybar.top #pill-down, +window#waybar.top #pill-down button, +window#waybar.top #pill-down menu, +window#waybar.top #pill-down menuitem, +window#waybar.top #pill-down tooltip, +window#waybar.bottom #pill-down, +window#waybar.bottom #pill-down button, +window#waybar.bottom #pill-down menu, +window#waybar.bottom #pill-down menuitem, +window#waybar.bottom #pill-down tooltip { + border-radius: 0 0 10pt 10pt; +} + +window#waybar.left #pill-down, +window#waybar.left #pill-down button, +window#waybar.left #pill-down menu, +window#waybar.left #pill-down menuitem, +window#waybar.left #pill-down tooltip, +window#waybar.right #pill-down, +window#waybar.right #pill-down button, +window#waybar.right #pill-down menu, +window#waybar.right #pill-down menuitem, +window#waybar.right #pill-down tooltip { + border-radius: 0 0 10pt 10pt; +} + + +/* +? half pill with the curve pointing inwards +*/ + +window#waybar.top #pill-in, +window#waybar.top #pill-in button, +window#waybar.top #pill-in menu, +window#waybar.top #pill-in menuitem, +window#waybar.top #pill-in tooltip { + border-radius: 0 0 10pt 10pt; +} + +window#waybar.right #pill-in, +window#waybar.right #pill-in button, +window#waybar.right #pill-in menu, +window#waybar.right #pill-in menuitem, +window#waybar.right #pill-in tooltip { + border-radius: 10pt 0 0 10pt; +} + +window#waybar.bottom #pill-in, +window#waybar.bottom #pill-in button, +window#waybar.bottom #pill-in menu, +window#waybar.bottom #pill-in menuitem, +window#waybar.bottom #pill-in tooltip { + border-radius: 10pt 10pt 0 0; +} + +window#waybar.left #pill-in, +window#waybar.left #pill-in button, +window#waybar.left #pill-in menu, +window#waybar.left #pill-in menuitem, +window#waybar.left #pill-in tooltip { + border-radius: 0 10pt 10pt 0; +} + + + +/* ? half pill with the curve pointing to left */ + +window#waybar.top #pill-left, +window#waybar.top #pill-left button, +window#waybar.top #pill-left menu, +window#waybar.top #pill-left menuitem, +window#waybar.top #pill-left tooltip, +window#waybar.bottom #pill-left, +window#waybar.bottom #pill-left button, +window#waybar.bottom #pill-left menu, +window#waybar.bottom #pill-left menuitem, +window#waybar.bottom #pill-left tooltip { + border-radius: 10pt 0 0 10pt; +} + +window#waybar.left #pill-left, +window#waybar.left #pill-left button, +window#waybar.left #pill-left menu, +window#waybar.left #pill-left menuitem, +window#waybar.left #pill-left tooltip, +window#waybar.right #pill-left, +window#waybar.right #pill-left button, +window#waybar.right #pill-left menu, +window#waybar.right #pill-left menuitem, +window#waybar.right #pill-left tooltip { + border-radius: 10pt 0 0 10pt; +} + + +/* ? half pill with the curve pointing to outwards */ + + +window#waybar.top #pill-out, +window#waybar.top #pill-out button, +window#waybar.top #pill-out menu, +window#waybar.top #pill-out menuitem, +window#waybar.top #pill-out tooltip { + border-radius: 10pt 10pt 0 0; +} + +window#waybar.right #pill-out, +window#waybar.right #pill-out button, +window#waybar.right #pill-out menu, +window#waybar.right #pill-out menuitem, +window#waybar.right #pill-out tooltip { + border-radius: 0 10pt 10pt 0; +} + +window#waybar.bottom #pill-out, +window#waybar.bottom #pill-out button, +window#waybar.bottom #pill-out menu, +window#waybar.bottom #pill-out menuitem, +window#waybar.bottom #pill-out tooltip { + border-radius: 0 0 10pt 10pt; +} + +window#waybar.left #pill-out, +window#waybar.left #pill-out button, +window#waybar.left #pill-out menu, +window#waybar.left #pill-out menuitem, +window#waybar.left #pill-out tooltip { + border-radius: 10pt 0 0 10pt; +} + + +/* ============================ */ +/* ? half pill with the curve pointing to the right */ + +window#waybar.top #pill-right, +window#waybar.top #pill-right button, +window#waybar.bottom #pill-right menu, +window#waybar.bottom #pill-right menuitem, +window#waybar.bottom #pill-right tooltip, +window#waybar.bottom #pill-right, +window#waybar.bottom #pill-right button, +window#waybar.top #pill-right menu, +window#waybar.top #pill-right menuitem, +window#waybar.top #pill-right tooltip { + border-radius: 0 10pt 10pt 0; +} + +window#waybar.left #pill-right, +window#waybar.left #pill-right button, +window#waybar.right #pill-right menu, +window#waybar.right #pill-right menuitem, +window#waybar.right #pill-right tooltip, +window#waybar.right #pill-right, +window#waybar.right #pill-right button, +window#waybar.left #pill-right menu, +window#waybar.left #pill-right menuitem, +window#waybar.left #pill-right tooltip { + border-radius: 0 10pt 10pt 0; +} + + +/* ? half pill with the flat surface pointing upwards */ + +window#waybar.top #pill-up, +window#waybar.top #pill-up button, +window#waybar.top #pill-up menu, +window#waybar.top #pill-up menuitem, +window#waybar.top #pill-up tooltip, +window#waybar.bottom #pill-up, +window#waybar.bottom #pill-up button, +window#waybar.bottom #pill-up menu, +window#waybar.bottom #pill-up menuitem, +window#waybar.bottom #pill-up tooltip { + border-radius: 10pt 10pt 0 0; +} + +window#waybar.left #pill-up, +window#waybar.left #pill-up button, +window#waybar.left #pill-up menu, +window#waybar.left #pill-up menuitem, +window#waybar.left #pill-up tooltip, +window#waybar.right #pill-up, +window#waybar.right #pill-up button, +window#waybar.right #pill-up menu, +window#waybar.right #pill-up menuitem, +window#waybar.right #pill-up tooltip { + border-radius: 10pt 10pt 0 0; +} + +tooltip { + border-radius: 10pt; +} + +menu { + border-radius: 10pt; +} + +menu menuitem { + border-radius: 10pt; +} + +window.popup decoration { + border-radius: 10pt; +} \ No newline at end of file diff --git a/gui/.config/waybar/includes/global.css b/gui/.config/waybar/includes/global.css new file mode 100644 index 0000000..be3757a --- /dev/null +++ b/gui/.config/waybar/includes/global.css @@ -0,0 +1,14 @@ +/* + Dynamic Style Configuration * + This is handled by HyDE + + To generate a dynamic configuration + base on theme and user settings + +*/ + +* { + border-radius: 0em; + font-family: "JetBrainsMono Nerd Font","JetBrainsMono Nerd Font"; + font-size: 10px; +} diff --git a/gui/.config/waybar/includes/includes.json b/gui/.config/waybar/includes/includes.json new file mode 100644 index 0000000..80e3fcd --- /dev/null +++ b/gui/.config/waybar/includes/includes.json @@ -0,0 +1,120 @@ +{ + "include": [ + "/home/anon/.config/waybar/modules/backlight.jsonc", + "/home/anon/.config/waybar/modules/bluetooth.jsonc", + "/home/anon/.config/waybar/modules/cava.jsonc", + "/home/anon/.config/waybar/modules/cliphist.jsonc", + "/home/anon/.config/waybar/modules/clock.jsonc", + "/home/anon/.config/waybar/modules/cpu.jsonc", + "/home/anon/.config/waybar/modules/cpuinfo.jsonc", + "/home/anon/.config/waybar/modules/footer.jsonc", + "/home/anon/.config/waybar/modules/github_hyprdots.jsonc", + "/home/anon/.config/waybar/modules/gpuinfo.jsonc", + "/home/anon/.config/waybar/modules/header.jsonc", + "/home/anon/.config/waybar/modules/idle_inhibitor.jsonc", + "/home/anon/.config/waybar/modules/keybindhint.jsonc", + "/home/anon/.config/waybar/modules/language.jsonc", + "/home/anon/.config/waybar/modules/memory.jsonc", + "/home/anon/.config/waybar/modules/mpris.jsonc", + "/home/anon/.config/waybar/modules/network.jsonc", + "/home/anon/.config/waybar/modules/notifications.jsonc", + "/home/anon/.config/waybar/modules/power.jsonc", + "/home/anon/.config/waybar/modules/privacy.jsonc", + "/home/anon/.config/waybar/modules/pulseaudio.jsonc", + "/home/anon/.config/waybar/modules/spotify.jsonc", + "/home/anon/.config/waybar/modules/taskbar.jsonc", + "/home/anon/.config/waybar/modules/theme.jsonc", + "/home/anon/.config/waybar/modules/tray.jsonc", + "/home/anon/.config/waybar/modules/updates.jsonc", + "/home/anon/.config/waybar/modules/wallchange.jsonc", + "/home/anon/.config/waybar/modules/window.jsonc", + "/home/anon/.config/waybar/modules/workspaces.jsonc", + "/home/anon/.config/waybar/modules/battery.jsonc", + "/home/anon/.local/share/waybar/modules/image#wallpaper.json", + "/home/anon/.local/share/waybar/modules/privacy.json", + "/home/anon/.local/share/waybar/modules/tray.json", + "/home/anon/.local/share/waybar/modules/wlr-taskbar#windows.json", + "/home/anon/.local/share/waybar/modules/wlr-taskbar.json", + "/home/anon/.local/share/waybar/modules/backlight.jsonc", + "/home/anon/.local/share/waybar/modules/battery.jsonc", + "/home/anon/.local/share/waybar/modules/bluetooth.jsonc", + "/home/anon/.local/share/waybar/modules/cava.jsonc", + "/home/anon/.local/share/waybar/modules/clock.jsonc", + "/home/anon/.local/share/waybar/modules/cpu.jsonc", + "/home/anon/.local/share/waybar/modules/custom-app-launcher.jsonc", + "/home/anon/.local/share/waybar/modules/custom-cava.jsonc", + "/home/anon/.local/share/waybar/modules/custom-clipboard.jsonc", + "/home/anon/.local/share/waybar/modules/custom-cliphist.jsonc", + "/home/anon/.local/share/waybar/modules/custom-cpuinfo.jsonc", + "/home/anon/.local/share/waybar/modules/custom-display.jsonc", + "/home/anon/.local/share/waybar/modules/custom-dunst.jsonc", + "/home/anon/.local/share/waybar/modules/custom-gamemode.jsonc", + "/home/anon/.local/share/waybar/modules/custom-github_hyde.jsonc", + "/home/anon/.local/share/waybar/modules/custom-gpuinfo#amd.jsonc", + "/home/anon/.local/share/waybar/modules/custom-gpuinfo#intel.jsonc", + "/home/anon/.local/share/waybar/modules/custom-gpuinfo#nvidia.jsonc", + "/home/anon/.local/share/waybar/modules/custom-gpuinfo.jsonc", + "/home/anon/.local/share/waybar/modules/custom-hyde-menu.jsonc", + "/home/anon/.local/share/waybar/modules/custom-hyprsunset.jsonc", + "/home/anon/.local/share/waybar/modules/custom-keybindhint.jsonc", + "/home/anon/.local/share/waybar/modules/custom-mediaplayer.jsonc", + "/home/anon/.local/share/waybar/modules/custom-power.jsonc", + "/home/anon/.local/share/waybar/modules/custom-powermenu.jsonc", + "/home/anon/.local/share/waybar/modules/custom-sensorsinfo.jsonc", + "/home/anon/.local/share/waybar/modules/custom-spotify.jsonc", + "/home/anon/.local/share/waybar/modules/custom-swaync.jsonc", + "/home/anon/.local/share/waybar/modules/custom-theme.jsonc", + "/home/anon/.local/share/waybar/modules/custom-updates.jsonc", + "/home/anon/.local/share/waybar/modules/custom-wallchange.jsonc", + "/home/anon/.local/share/waybar/modules/custom-wbar.jsonc", + "/home/anon/.local/share/waybar/modules/custom-weather.jsonc", + "/home/anon/.local/share/waybar/modules/custom-workflows.jsonc", + "/home/anon/.local/share/waybar/modules/gamemode.jsonc", + "/home/anon/.local/share/waybar/modules/group-eyecare.jsonc", + "/home/anon/.local/share/waybar/modules/group-hide-tray.jsonc", + "/home/anon/.local/share/waybar/modules/group-mediaplayer.jsonc", + "/home/anon/.local/share/waybar/modules/group-volumecontrol.jsonc", + "/home/anon/.local/share/waybar/modules/hyprland-language.jsonc", + "/home/anon/.local/share/waybar/modules/hyprland-window.jsonc", + "/home/anon/.local/share/waybar/modules/hyprland-workspaces#kanji.jsonc", + "/home/anon/.local/share/waybar/modules/hyprland-workspaces#roman.jsonc", + "/home/anon/.local/share/waybar/modules/hyprland-workspaces.jsonc", + "/home/anon/.local/share/waybar/modules/idle_inhibitor.jsonc", + "/home/anon/.local/share/waybar/modules/image#profile.jsonc", + "/home/anon/.local/share/waybar/modules/image#wallpaper.jsonc", + "/home/anon/.local/share/waybar/modules/memory.jsonc", + "/home/anon/.local/share/waybar/modules/mpd.jsonc", + "/home/anon/.local/share/waybar/modules/mpris.jsonc", + "/home/anon/.local/share/waybar/modules/network#bandwidth.jsonc", + "/home/anon/.local/share/waybar/modules/network.jsonc", + "/home/anon/.local/share/waybar/modules/power-profiles-daemon.jsonc", + "/home/anon/.local/share/waybar/modules/privacy.jsonc", + "/home/anon/.local/share/waybar/modules/pulseaudio#microphone.jsonc", + "/home/anon/.local/share/waybar/modules/pulseaudio.jsonc", + "/home/anon/.local/share/waybar/modules/temperature.jsonc", + "/home/anon/.local/share/waybar/modules/tray.jsonc", + "/home/anon/.local/share/waybar/modules/wlr-taskbar#windows.jsonc", + "/home/anon/.local/share/waybar/modules/wlr-taskbar.jsonc" + ], + "image#wallpaper": { + "size": 30, + "icon-size-multiplier": 3 + }, + "privacy": { + "icon-size": 10, + "icon-size-multiplier": 1 + }, + "tray": { + "icon-size": 16, + "icon-size-multiplier": 1.6 + }, + "wlr/taskbar#windows": { + "icon-size": 16, + "icon-size-multiplier": 1.6 + }, + "wlr/taskbar": { + "icon-size": 16, + "icon-size-multiplier": 1.6 + }, + "position": "top" +} \ No newline at end of file diff --git a/.config/waybar/style.css b/gui/.config/waybar/layouts/custom.css similarity index 81% rename from .config/waybar/style.css rename to gui/.config/waybar/layouts/custom.css index 37fce40..76c42f8 100644 --- a/.config/waybar/style.css +++ b/gui/.config/waybar/layouts/custom.css @@ -7,10 +7,11 @@ min-height: 10px; } -@import "theme.css"; +@import "../theme.css"; /* HyDE/wallbash-generated colors, lives one level up */ window#waybar { - background: @bar-bg; + /* transparent so the cavabar (bottom layer, same region) shows through; module pills keep @main-bg */ + background: transparent; } tooltip { @@ -48,14 +49,14 @@ tooltip { background: @wb-act-bg; color: @wb-act-fg; animation: ws_active 20s ease-in-out 1; - transition: all 0.4s cubic-bezier(.55, -0.68, .48, 1.682); + transition: all 0.4s cubic-bezier(0.55, -0.68, 0.48, 1.682); } #workspaces button:hover { background: @wb-hvr-bg; color: @wb-hvr-fg; animation: ws_hover 20s ease-in-out 1; - transition: all 0.3s cubic-bezier(.55, -0.68, .48, 1.682); + transition: all 0.3s cubic-bezier(0.55, -0.68, 0.48, 1.682); } #taskbar button { @@ -78,7 +79,7 @@ tooltip { color: @wb-act-color; /*animation: tb_active 20s ease-in-out 1;*/ box-shadow: 0 0 1px 1px rgba(128, 128, 128, 0.4); - transition: all 0.4s cubic-bezier(.55, -0.68, .48, 1.682); + transition: all 0.4s cubic-bezier(0.55, -0.68, 0.48, 1.682); } #taskbar button:hover { @@ -86,7 +87,7 @@ tooltip { color: @wb-hvr-color; opacity: 0.8; animation: tb_hover 20s ease-in-out 1; - transition: all 0.3s cubic-bezier(.55, -0.68, .48, 1.682); + transition: all 0.3s cubic-bezier(0.55, -0.68, 0.48, 1.682); } #tray menu * { @@ -100,6 +101,7 @@ tooltip { #backlight, #battery, #bluetooth, +#custom-cava, #custom-cliphist, #clock, #custom-cpuinfo, @@ -192,3 +194,14 @@ window#waybar.empty #custom-sr_end { padding-left: 3px; } +window#waybar.cavabar { + background: transparent; +} + +window#waybar.cavabar #custom-cava { + font-size: 24px; + color: alpha(@main-fg, 0.7); + background: rgba(0, 0, 0, 0.2); + margin: 0px; + padding: 0px; +} diff --git a/gui/.config/waybar/layouts/custom.jsonc b/gui/.config/waybar/layouts/custom.jsonc new file mode 100644 index 0000000..e987932 --- /dev/null +++ b/gui/.config/waybar/layouts/custom.jsonc @@ -0,0 +1,332 @@ +// --// standalone two-bar cava rig — hand-maintained, NOT HyDE-generated (see README "Waybar") //-- // + +[ +{ +// sourced from header module // + + "layer": "top", + "position": "top", + "height": 33, + "exclusive": true, + "passthrough": false, + "gtk-layer-shell": true, + "reload_style_on_change": true, + + +// positions generated based on config.ctl // + + "modules-left": ["custom/l_end","custom/power","custom/r_end","hyprland/workspaces", "custom/sl_end","hyprland/window","custom/sr_end","custom/l_end","custom/wallchange","custom/cliphist","custom/r_end"], + "modules-center": ["wlr/taskbar"], + "modules-right": ["custom/l_end","backlight","pulseaudio","pulseaudio#microphone","mpris","custom/r_end","custom/l_end","memory","cpu","custom/cpuinfo","battery","custom/r_end","custom/l_end","tray","custom/r_end","custom/l_end","idle_inhibitor","clock","custom/r_end"], + +// sourced from modules based on config.ctl // + + "tray": { + "icon-size": 19, + "rotate": 0, + "spacing": 5 + }, + + // NOTE: keep in sync with modules/window.jsonc — this layout is launched standalone + // (waybar -c), so the modules/ file is NOT read here; this inline copy is what renders. + "hyprland/window": { + "format": " {}", + "rotate": 0, + "separate-outputs": true, + "max-length": 1000, + "rewrite": { + "(.*) — Kitty": " $1", + "(.*)~": " $1", + "(.*) — Mozilla Firefox": "󰈹 $1", + "(.*)Mozilla Firefox": "󰈹 Firefox", + "(.*) — LibreWolf": "󰈹 $1", + "(.*)LibreWolf": "󰈹 LibreWolf", + "(.*) — Chromium": " $1", + "(.*)Chromium": " Chromium", + "(.*) - VSCodium": "󰨞 $1", + "(.*)VSCodium": "󰨞 VSCodium", + "(.*)Code - OSS": "󰨞 $1", + "(.*) - Visual Studio Code": "󰨞 $1", + "(.*)Visual Studio Code": "󰨞 Code", + "(.*) — Dolphin": "󰉋 $1", + "(.*) - Obsidian(.*)": "󰠮 $1", + "(.*)Ferdium": "󰭻 Ferdium", + "(.*)FreeTube": " FreeTube", + "(.*) - VLC media player": "󰕼 $1", + "(.*)Steam": "󰓓 Steam", + "(.*)Neovide": " Nvim", + "(.*)Neovim": " Nvim", + "(.*)Nvim": " Nvim", + "(.*)Gajim": "󰭹 Gajim", + "(.*)KeePassXC": " KeePassXC", + "(.*)Betterbird": "󰇮 Betterbird", + "(.*)Spotify": "󰓇 Spotify" + } + }, + "hyprland/workspaces": { + "window-rewrite-default":"", + "all-outputs": true, + "active-only": false, + "on-click": "activate", + "disable-scroll": false, + "on-scroll-up": "hyprctl dispatch workspace -1", + "on-scroll-down": "hyprctl dispatch workspace +1", + "persistent-workspaces": {} + }, + + "cpu": { + "interval": 10, + "format": "󰍛 {usage}%", + "format-alt": "{icon0}{icon1}{icon2}{icon3}", + "format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"] + }, + + "custom/cpuinfo": { + "exec": " cpuinfo.sh", + "return-type": "json", + "format": "{}", + "rotate": 0, + "interval": 5, // once every 5 seconds + "tooltip": true, + "max-length": 1000 + }, + + "memory": { + "states": { + "c": 90, // critical + "h": 60, // high + "m": 30 // medium + }, + "interval": 30, + "rotate": 0, + "format": "󰾆 {used:0.1f}GB", + "format-m": "󰾅 {used:0.1f}GB", + "format-h": "󰓅 {used:0.1f}GB", + "format-c": " {used:0.1f}GB", + "format-alt": "󰾆 {percentage}%", + "max-length": 10, + "tooltip": true, + "tooltip-format": "󰾆 {percentage}%\n {used:0.1f}GB/{total:0.1f}GB" + }, + + "custom/power": { + "format": "{}", + "rotate": 0, + "exec": "echo ; echo  logout", + "on-click": "logoutlaunch.sh 2", + "on-click-right": "logoutlaunch.sh 1", + "interval" : 86400, // once every day + "tooltip": true + }, + + "wlr/taskbar": { + "format": "{icon}", + "rotate": 0, + "icon-size": 19, + "icon-theme": "Tela-circle-dracula", + "spacing": 0, + "tooltip-format": "{title}", + "on-click": "activate", + "on-click-middle": "close", + "ignore-list": [ + "Alacritty" + ], + "app_ids-mapping": { + "firefoxdeveloperedition": "firefox-developer-edition" + } + }, + + "idle_inhibitor": { + "format": "{icon}", + "rotate": 0, + "format-icons": { + "activated": "󰅶", + "deactivated": "󰛊" + } + }, + "clock": { + "format": "{:%H:%M}", + "rotate": 0, + "format-alt": "{:%R 󰃭 %d·%m·%y}", + "tooltip-format": "{calendar}", + "calendar": { + "mode": "month", + "mode-mon-col": 3, + "on-scroll": 1, + "on-click-right": "mode", + "format": { + "months": "{}", + "weekdays": "{}", + "today": "{}" + } + }, + "actions": { + "on-click-right": "mode", + "on-click-forward": "tz_up", + "on-click-backward": "tz_down", + "on-scroll-up": "shift_up", + "on-scroll-down": "shift_down" + } + }, + + "custom/cliphist": { + "format": "{}", + "rotate": 0, + "exec": "echo ; echo 󰅇 clipboard history", + "on-click": "sleep 0.1 && cliphist.sh c", + "on-click-right": "sleep 0.1 && cliphist.sh d", + "on-click-middle": "sleep 0.1 && cliphist.sh w", + "interval" : 86400, // once every day + "tooltip": true + }, + + "custom/wallchange": { + "format": "{}", + "rotate": 0, + "exec": "echo ; echo 󰆊 switch wallpaper", + "on-click": "swwwallpaper.sh -n", + "on-click-right": "swwwallpaper.sh -p", + "on-click-middle": "sleep 0.1 && swwwallselect.sh", + "interval" : 86400, // once every day + "tooltip": true + }, + + "pulseaudio": { + "format": "{icon} {volume}", + "rotate": 0, + "format-muted": "婢", + "on-click": "pavucontrol -t 3", + "on-click-right": "volumecontrol.sh -s ''", + "on-click-middle": "volumecontrol.sh -o m", + "on-scroll-up": "volumecontrol.sh -o i", + "on-scroll-down": "volumecontrol.sh -o d", + "tooltip-format": "{icon} {desc} // {volume}%", + "scroll-step": 5, + "format-icons": { + "headphone": "", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + "default": ["", "", ""] + } + }, + + "pulseaudio#microphone": { + "format": "{format_source}", + "rotate": 0, + "format-source": "", + "format-source-muted": "", + "on-click": "pavucontrol -t 4", + "on-click-middle": "volumecontrol.sh -i m", + "on-scroll-up": "volumecontrol.sh -i i", + "on-scroll-down": "volumecontrol.sh -i d", + "tooltip-format": "{format_source} {source_desc} // {source_volume}%", + "scroll-step": 5 + }, + + "mpris": { + "format": "{player_icon} {dynamic}", + "rotate": 0, + "format-paused": "{status_icon} {dynamic}", + "player-icons": { + "default": "▶", + "mpv": "🎵" + }, + "status-icons": { + "paused": "" + }, + "max-length": 1000, + "interval": 1 + }, + + "battery": { + "states": { + "good": 95, + "warning": 30, + "critical": 20 + }, + "format": "{icon} {capacity}%", + "rotate": 0, + "format-charging": " {capacity}%", + "format-plugged": " {capacity}%", + "format-alt": "{time} {icon}", + "format-icons": ["󰂎", "󰁺", "󰁻", "󰁼", "󰁽", "󰁾", "󰁿", "󰂀", "󰂁", "󰂂", "󰁹"] + }, + + "backlight": { + "device": "intel_backlight", + "rotate": 0, + "format": "{icon} {percent}%", + "format-icons": ["", "", "", "", "", "", "", "", ""], + "on-scroll-up": "brightnessctl set 1%+", + "on-scroll-down": "brightnessctl set 1%-", + "min-length": 6 + }, +// modules for padding // + + "custom/l_end": { + "format": " ", + "interval" : "once", + "tooltip": false + }, + + "custom/r_end": { + "format": " ", + "interval" : "once", + "tooltip": false + }, + + "custom/sl_end": { + "format": " ", + "interval" : "once", + "tooltip": false + }, + + "custom/sr_end": { + "format": " ", + "interval" : "once", + "tooltip": false + }, + + "custom/rl_end": { + "format": " ", + "interval" : "once", + "tooltip": false + }, + + "custom/rr_end": { + "format": " ", + "interval" : "once", + "tooltip": false + }, + + "custom/padd": { + "format": " ", + "interval" : "once", + "tooltip": false + } +}, + +// full-width cava visualizer layered behind the main bar // + +{ + "name": "cavabar", + "layer": "bottom", + "position": "top", + "height": 36, + "margin-top": -33, + "exclusive": false, + "passthrough": true, + "modules-center": ["custom/cava"], + + "custom/cava": { + "format": "{}", + "exec": "bash $HOME/.config/waybar/cava-waybar.sh $(cat $HOME/.config/waybar/cava.width 2>/dev/null || echo 94)", // per-host cava.width (bars, even; ~screen_px / (font-size*0.6)). gui default 94; wm overrides to 218 + "restart-interval": 1, + "hide-empty-text": true + } +} +] + diff --git a/.config/waybar/modules/backlight.jsonc b/gui/.config/waybar/modules/backlight.jsonc similarity index 100% rename from .config/waybar/modules/backlight.jsonc rename to gui/.config/waybar/modules/backlight.jsonc diff --git a/.config/waybar/modules/battery.jsonc b/gui/.config/waybar/modules/battery.jsonc similarity index 100% rename from .config/waybar/modules/battery.jsonc rename to gui/.config/waybar/modules/battery.jsonc diff --git a/.config/waybar/modules/bluetooth.jsonc b/gui/.config/waybar/modules/bluetooth.jsonc similarity index 100% rename from .config/waybar/modules/bluetooth.jsonc rename to gui/.config/waybar/modules/bluetooth.jsonc diff --git a/gui/.config/waybar/modules/cava.jsonc b/gui/.config/waybar/modules/cava.jsonc new file mode 100644 index 0000000..cd08adc --- /dev/null +++ b/gui/.config/waybar/modules/cava.jsonc @@ -0,0 +1,24 @@ +{ + "cava": { + // "cava_config": "$XDG_CONFIG_HOME/cava/cava.conf", + "framerate": 30, + "autosens": 1, + "sensitivity": 100, + "bars": 14, + "lower_cutoff_freq": 50, + "higher_cutoff_freq": 10000, + "method": "pulse", + "source": "auto", + "stereo": true, + "reverse": false, + "bar_delimiter": 0, + "monstercat": false, + "waves": false, + "noise_reduction": 0.77, + "input_delay": 2, + "format-icons": ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"], + "actions": { + "on-click-right": "mode" + } + } +} diff --git a/.config/waybar/modules/cliphist.jsonc b/gui/.config/waybar/modules/cliphist.jsonc similarity index 100% rename from .config/waybar/modules/cliphist.jsonc rename to gui/.config/waybar/modules/cliphist.jsonc diff --git a/.config/waybar/modules/clock.jsonc b/gui/.config/waybar/modules/clock.jsonc similarity index 100% rename from .config/waybar/modules/clock.jsonc rename to gui/.config/waybar/modules/clock.jsonc diff --git a/.config/waybar/modules/cpu.jsonc b/gui/.config/waybar/modules/cpu.jsonc similarity index 100% rename from .config/waybar/modules/cpu.jsonc rename to gui/.config/waybar/modules/cpu.jsonc diff --git a/.config/waybar/modules/cpuinfo.jsonc b/gui/.config/waybar/modules/cpuinfo.jsonc similarity index 100% rename from .config/waybar/modules/cpuinfo.jsonc rename to gui/.config/waybar/modules/cpuinfo.jsonc diff --git a/.config/waybar/modules/footer.jsonc b/gui/.config/waybar/modules/footer.jsonc similarity index 100% rename from .config/waybar/modules/footer.jsonc rename to gui/.config/waybar/modules/footer.jsonc diff --git a/gui/.config/waybar/modules/github_hyprdots.jsonc b/gui/.config/waybar/modules/github_hyprdots.jsonc new file mode 100644 index 0000000..465af06 --- /dev/null +++ b/gui/.config/waybar/modules/github_hyprdots.jsonc @@ -0,0 +1,8 @@ +{ + "custom/github_hyprdots": { + "format": " ", + "rotate": 0, + "tooltip-format": "  Hyprdots repository", + "on-click": "xdg-open https://github.com/prasanthrangan/hyprdots" + } +} diff --git a/.config/waybar/modules/gpuinfo.jsonc b/gui/.config/waybar/modules/gpuinfo.jsonc similarity index 100% rename from .config/waybar/modules/gpuinfo.jsonc rename to gui/.config/waybar/modules/gpuinfo.jsonc diff --git a/.config/waybar/modules/header.jsonc b/gui/.config/waybar/modules/header.jsonc similarity index 100% rename from .config/waybar/modules/header.jsonc rename to gui/.config/waybar/modules/header.jsonc diff --git a/.config/waybar/modules/idle_inhibitor.jsonc b/gui/.config/waybar/modules/idle_inhibitor.jsonc similarity index 64% rename from .config/waybar/modules/idle_inhibitor.jsonc rename to gui/.config/waybar/modules/idle_inhibitor.jsonc index d109a95..5963c20 100644 --- a/.config/waybar/modules/idle_inhibitor.jsonc +++ b/gui/.config/waybar/modules/idle_inhibitor.jsonc @@ -2,8 +2,8 @@ "format": "{icon}", "rotate": ${r_deg}, "format-icons": { - "activated": "󰥔", - "deactivated": "" + "activated": "󰅶", + "deactivated": "󰛊" } }, diff --git a/.config/waybar/modules/keybindhint.jsonc b/gui/.config/waybar/modules/keybindhint.jsonc similarity index 100% rename from .config/waybar/modules/keybindhint.jsonc rename to gui/.config/waybar/modules/keybindhint.jsonc diff --git a/.config/waybar/modules/language.jsonc b/gui/.config/waybar/modules/language.jsonc similarity index 100% rename from .config/waybar/modules/language.jsonc rename to gui/.config/waybar/modules/language.jsonc diff --git a/.config/waybar/modules/memory.jsonc b/gui/.config/waybar/modules/memory.jsonc similarity index 100% rename from .config/waybar/modules/memory.jsonc rename to gui/.config/waybar/modules/memory.jsonc diff --git a/.config/waybar/modules/mpris.jsonc b/gui/.config/waybar/modules/mpris.jsonc similarity index 100% rename from .config/waybar/modules/mpris.jsonc rename to gui/.config/waybar/modules/mpris.jsonc diff --git a/.config/waybar/modules/network.jsonc b/gui/.config/waybar/modules/network.jsonc similarity index 100% rename from .config/waybar/modules/network.jsonc rename to gui/.config/waybar/modules/network.jsonc diff --git a/.config/waybar/modules/notifications.jsonc b/gui/.config/waybar/modules/notifications.jsonc similarity index 100% rename from .config/waybar/modules/notifications.jsonc rename to gui/.config/waybar/modules/notifications.jsonc diff --git a/.config/waybar/modules/power.jsonc b/gui/.config/waybar/modules/power.jsonc similarity index 100% rename from .config/waybar/modules/power.jsonc rename to gui/.config/waybar/modules/power.jsonc diff --git a/gui/.config/waybar/modules/privacy.jsonc b/gui/.config/waybar/modules/privacy.jsonc new file mode 100644 index 0000000..0510e9a --- /dev/null +++ b/gui/.config/waybar/modules/privacy.jsonc @@ -0,0 +1,19 @@ +{ + "privacy": { + "icon-size": 18, + "icon-spacing": 5, + "transition-duration": 250, + "modules": [ + { + "type": "screenshare", + "tooltip": true, + "tooltip-icon-size": 24 + }, + { + "type": "audio-in", + "tooltip": true, + "tooltip-icon-size": 24 + } + ] + } +} diff --git a/.config/waybar/modules/pulseaudio.jsonc b/gui/.config/waybar/modules/pulseaudio.jsonc similarity index 100% rename from .config/waybar/modules/pulseaudio.jsonc rename to gui/.config/waybar/modules/pulseaudio.jsonc diff --git a/.config/waybar/modules/spotify.jsonc b/gui/.config/waybar/modules/spotify.jsonc similarity index 100% rename from .config/waybar/modules/spotify.jsonc rename to gui/.config/waybar/modules/spotify.jsonc diff --git a/.config/waybar/modules/style.css b/gui/.config/waybar/modules/style.css similarity index 100% rename from .config/waybar/modules/style.css rename to gui/.config/waybar/modules/style.css diff --git a/.config/waybar/modules/taskbar.jsonc b/gui/.config/waybar/modules/taskbar.jsonc similarity index 100% rename from .config/waybar/modules/taskbar.jsonc rename to gui/.config/waybar/modules/taskbar.jsonc diff --git a/.config/waybar/modules/theme.jsonc b/gui/.config/waybar/modules/theme.jsonc similarity index 100% rename from .config/waybar/modules/theme.jsonc rename to gui/.config/waybar/modules/theme.jsonc diff --git a/.config/waybar/modules/tray.jsonc b/gui/.config/waybar/modules/tray.jsonc similarity index 100% rename from .config/waybar/modules/tray.jsonc rename to gui/.config/waybar/modules/tray.jsonc diff --git a/.config/waybar/modules/updates.jsonc b/gui/.config/waybar/modules/updates.jsonc similarity index 100% rename from .config/waybar/modules/updates.jsonc rename to gui/.config/waybar/modules/updates.jsonc diff --git a/.config/waybar/modules/wallchange.jsonc b/gui/.config/waybar/modules/wallchange.jsonc similarity index 100% rename from .config/waybar/modules/wallchange.jsonc rename to gui/.config/waybar/modules/wallchange.jsonc diff --git a/gui/.config/waybar/modules/window.jsonc b/gui/.config/waybar/modules/window.jsonc new file mode 100644 index 0000000..b48d0e7 --- /dev/null +++ b/gui/.config/waybar/modules/window.jsonc @@ -0,0 +1,35 @@ +{ + "hyprland/window": { + "format": " {}", + "rotate": 0, + "separate-outputs": true, + "max-length": 1000, + "rewrite": { + "(.*) — Kitty": " $1", + "(.*)~": " $1", + "(.*) — Mozilla Firefox": "󰈹 $1", + "(.*)Mozilla Firefox": "󰈹 Firefox", + "(.*) — LibreWolf": "󰈹 $1", + "(.*)LibreWolf": "󰈹 LibreWolf", + "(.*) — Chromium": " $1", + "(.*)Chromium": " Chromium", + "(.*) - VSCodium": " $1", + "(.*)VSCodium": " VSCodium", + "(.*)Code - OSS": "󰨞 $1", + "(.*) - Visual Studio Code": "󰨞 $1", + "(.*)Visual Studio Code": "󰨞 Code", + "(.*) — Dolphin": "󰉋 $1", + "(.*) - Obsidian(.*)": "󰠮 $1", + "(.*)Ferdium": "󰭻 Ferdium", + "(.*)FreeTube": " FreeTube", + "(.*)Freetube": " FreeTube", + "(.*) - VLC media player": "󰕼 $1", + "(.*)Steam": "󰓓 Steam", + "(.*)Neovide": " Nvim", + "(.*)Gajim": "󰭹 Gajim", + "(.*)KeePassXC": " KeePassXC", + "(.*)Betterbird": " Betterbird", + "(.*)Spotify": "󰓇 Spotify" + } + } +} diff --git a/.config/waybar/modules/workspaces.jsonc b/gui/.config/waybar/modules/workspaces.jsonc similarity index 100% rename from .config/waybar/modules/workspaces.jsonc rename to gui/.config/waybar/modules/workspaces.jsonc diff --git a/.config/waybar/theme.css b/gui/.config/waybar/theme.css similarity index 66% rename from .config/waybar/theme.css rename to gui/.config/waybar/theme.css index c3ad237..9aeacd7 100644 --- a/.config/waybar/theme.css +++ b/gui/.config/waybar/theme.css @@ -1,6 +1,6 @@ -@define-color bar-bg rgba(0, 0, 0, 0); +@define-color bar-bg rgba(0, 0, 0, 0.1); -@define-color main-bg rgba(0,0,0,0.4); +@define-color main-bg rgba(0, 0, 0, 0.1); @define-color main-fg #cdd6f4; @define-color wb-act-bg #a6adc8; diff --git a/.config/zellij/config.kdl b/gui/.config/zellij/config.kdl similarity index 98% rename from .config/zellij/config.kdl rename to gui/.config/zellij/config.kdl index 84f2dd1..f2ecf85 100644 --- a/.config/zellij/config.kdl +++ b/gui/.config/zellij/config.kdl @@ -1,5 +1,5 @@ // -// THIS FILE WAS AUTOGENERATED BY ZELLIJ, THE PREVIOUS FILE AT THIS LOCATION WAS COPIED TO: /home/coja/.config/zellij/config.kdl.bak +// THIS FILE WAS AUTOGENERATED BY ZELLIJ, THE PREVIOUS FILE AT THIS LOCATION WAS COPIED TO: ~/.config/zellij/config.kdl.bak // keybinds clear-defaults=true { @@ -10,7 +10,7 @@ keybinds clear-defaults=true { bind "h" "left" { MoveFocus "left"; } bind "j" "down" { MoveFocus "down"; } bind "k" "up" { MoveFocus "up"; } - bind "j" "right" { MoveFocus "right"; } + bind "l" "right" { MoveFocus "right"; } bind "c" { SwitchToMode "renamepane"; PaneNameInput 0; } bind "d" { NewPane "down"; SwitchToMode "normal"; } bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } @@ -31,7 +31,7 @@ keybinds clear-defaults=true { bind "h" "left" { GoToPreviousTab; } bind "j" "down" { GoToNextTab; } bind "k" "up" { GoToPreviousTab; } - bind "j" "right" { GoToNextTab; } + bind "l" "right" { GoToNextTab; } bind "1" { GoToTab 1; SwitchToMode "normal"; } bind "2" { GoToTab 2; SwitchToMode "normal"; } bind "3" { GoToTab 3; SwitchToMode "normal"; } @@ -59,7 +59,7 @@ keybinds clear-defaults=true { bind "h" "left" { Resize "Increase left"; } bind "j" "down" { Resize "Increase down"; } bind "k" "up" { Resize "Increase up"; } - bind "j" "right" { Resize "Increase right"; } + bind "l" "right" { Resize "Increase right"; } bind "+" { Resize "Increase"; } bind "-" { Resize "Decrease"; } bind "=" { Resize "Increase"; } @@ -77,7 +77,7 @@ keybinds clear-defaults=true { bind "h" "left" { MovePane "left"; } bind "j" "down" { MovePane "down"; } bind "k" "up" { MovePane "up"; } - bind "j" "right" { MovePane "right"; } + bind "l" "right" { MovePane "right"; } bind "h" { MovePane "left"; } bind "Alt h" { SwitchToMode "normal"; } bind "j" { MovePane "down"; } @@ -314,7 +314,7 @@ load_plugins { // Choose the theme that is specified in the themes section. // Default: default // -// theme "dracula" +theme "gruvbox-dark" // defined in themes/gruvbox-dark.kdl // Choose the base input mode of zellij. // Default: normal diff --git a/.config/zellij/guvbox-dark.kdl b/gui/.config/zellij/themes/gruvbox-dark.kdl similarity index 100% rename from .config/zellij/guvbox-dark.kdl rename to gui/.config/zellij/themes/gruvbox-dark.kdl diff --git a/gui/.librewolf/librewolf.overrides.cfg b/gui/.librewolf/librewolf.overrides.cfg new file mode 100644 index 0000000..59021ee --- /dev/null +++ b/gui/.librewolf/librewolf.overrides.cfg @@ -0,0 +1,105 @@ +// ============================================================================ +// librewolf.overrides.cfg — personal overrides for LibreWolf's hardened defaults +// ---------------------------------------------------------------------------- +// Deployed by ~/.dots (GNU Stow, `gui` package) to ~/.librewolf/librewolf.overrides.cfg +// It is read AFTER the packaged librewolf.cfg, so anything set here WINS and is +// re-applied on every launch (this is why about:config edits to hardened prefs +// don't otherwise stick). +// +// Syntax is Mozilla AutoConfig (JS-ish), NOT prefs.js. Available functions: +// defaultPref("x", v) set the DEFAULT (still changeable in about:config, <- preferred +// reapplied each start; runtime changes persist per profile) +// pref("x", v) set the current value +// lockPref("x", v) set AND lock (greyed-out / uneditable in about:config) +// clearPref("x") reset to the built-in default +// Comments: // line or /* block */. Changes take effect after a restart. +// +// Everything below uses defaultPref so you can still toggle live in about:config. +// Trim to taste — the "privacy trade-offs" section is commented out on purpose; +// uncomment only what you actually need. +// ============================================================================ + + +// ===[ Quality-of-life (safe, low-risk) ]===================================== + +// Restore my previous windows & tabs on startup (1 = homepage, 3 = restore session). +defaultPref("browser.startup.page", 3); + +// Keep search-in-address-bar working (also required for DuckDuckGo !bangs & keyword +// searches to resolve from the URL bar). +defaultPref("keyword.enabled", true); + +// Middle-click / hold autoscroll. +defaultPref("general.autoScroll", true); + +// Remember form-field entries (search boxes, etc.). +defaultPref("browser.formfill.enable", true); + +// Smooth-ish spell check in text areas. +defaultPref("layout.spellcheckDefault", 1); + + +// ===[ Stay logged in across restarts ]======================================= +// LibreWolf wipes cookies + history on shutdown, so you're logged out of +// everything every time you close it. This block keeps sessions/history. +// >>> Comment this whole block out to restore LibreWolf's clear-on-exit privacy. <<< + +// Master switch for the "clear on shutdown" sweep. +defaultPref("privacy.sanitize.sanitizeOnShutdown", false); + +// Belt-and-suspenders granular keys (names differ across Firefox versions; setting +// unknown ones is harmless). Old-style: +defaultPref("privacy.clearOnShutdown.cookies", false); +defaultPref("privacy.clearOnShutdown.history", false); +defaultPref("privacy.clearOnShutdown.sessions", false); +defaultPref("privacy.clearOnShutdown.offlineApps", false); +// Firefox 128+ (_v2) keys: +defaultPref("privacy.clearOnShutdown_v2.cookiesAndStorage", false); +defaultPref("privacy.clearOnShutdown_v2.historyFormDataAndDownloads", false); + +// Don't downgrade all cookies to session-only (LibreWolf sets 2 = delete on close; +// 0 = keep until they expire). May be a no-op on newer builds where it's deprecated. +defaultPref("network.cookie.lifetimePolicy", 0); + + +// ===[ Downloads ]============================================================ +// true = save straight to the download dir; false = ask every time. Pick one. +defaultPref("browser.download.useDownloadDir", true); +// defaultPref("browser.download.dir", "/home/YOURUSER/Downloads"); // must be absolute — ~ is not expanded +// defaultPref("browser.download.folderList", 2); // 2 = use the custom dir above + + +// ===[ Privacy / security trade-offs — OPT IN by uncommenting ]================ +// Each of these RELAXES a LibreWolf hardening default. Enable only if you need it. + +// -- resistFingerprinting (RFP) -- +// RFP is LibreWolf's strongest anti-fingerprinting measure but it also forces a +// light theme, spoofs your timezone, adds letterbox bars, and standardises the +// window size. Turn it fully off: +// defaultPref("privacy.resistFingerprinting", false); +// ...or keep RFP but drop just the grey letterbox bars around pages: +// defaultPref("privacy.resistFingerprinting.letterboxing", false); + +// -- WebGL -- (LibreWolf disables it; needed for maps, some games, 3D canvas) +// defaultPref("webgl.disabled", false); + +// -- DRM / EME -- (needed for Netflix, Spotify Web, Amazon Video, etc.) +// defaultPref("media.eme.enabled", true); +// defaultPref("media.gmp-widevine.enabled", true); + +// -- Firefox Sync -- (LibreWolf disables the account UI) +// defaultPref("identity.fxaccounts.enabled", true); + +// -- Built-in password manager -- (LibreWolf disables saving logins) +// defaultPref("signon.rememberSignons", true); + +// -- Search suggestions from your default engine as you type -- +// defaultPref("browser.search.suggest.enabled", true); +// defaultPref("browser.urlbar.suggest.searches", true); + + +// ===[ Personal tweaks — add your own below ]================================= +// e.g. compact UI density: +// defaultPref("browser.uidensity", 1); // 0 normal, 1 compact, 2 touch +// e.g. open new tab to a blank page: +// defaultPref("browser.newtabpage.enabled", false); diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..e605500 --- /dev/null +++ b/install.sh @@ -0,0 +1,189 @@ +#!/usr/bin/env bash +# Deploy the unified dots for this machine using GNU Stow. +# +# ./install.sh [host] host defaults to $(hostname -s) +# ./install.sh -n [host] dry-run (preview, no changes) +# ./install.sh -a [host] first run on a machine that still has real config files: +# --adopt them into the repo, then review `git diff` and +# `git restore .` to keep the repo (merged) versions. +# ./install.sh -D [host] revert: unstow this host's symlinks (repo + real files untouched) +# ./install.sh -h show all options +# +# Layering (later wins): common -> gui -> +# - common : shared everywhere +# - gui : GUI configs (skipped on headless machines — see the marker below) +# - : per-machine overrides (top-level package), stowed last with --override +# All packages live at the repo root so they share one stow dir — required for +# --override to let a host file take over a common/gui file of the same path. +# --no-folding: every file is symlinked individually (dirs stay real) so a host can +# override a single file in a shared dir without shadowing the rest. +# +# Idempotent: re-run any time. Adding/removing files in the repo needs a re-run +# (editing an existing file does not — symlinks point straight at the repo file). +set -uo pipefail + +# --- headless (terminal-only, no GUI) machines skip the gui package --- +# A machine is "headless" if this marker file exists. It's machine-local (not tracked by the repo) +# and hostname-independent, so the same terminal-only setup replicates across any number of machines +# — run once on each tty-only box: touch ~/.config/dots-headless +HEADLESS_MARKER="${XDG_CONFIG_HOME:-$HOME/.config}/dots-headless" + +usage() { + cat <<'EOF' +Deploy this machine's dotfiles with GNU Stow. + +Usage: ./install.sh [options] [host] + + host profile to deploy (default: `hostname -s`) + -n, --dry-run preview stow actions; change nothing + -a, --adopt first run on a machine with existing real config files: adopt + them into the repo, then review `git diff` and `git restore .` + -D, --unstow revert: remove this host's stow symlinks (repo + real files kept) + -h, --help show this help and exit + +Layering (later wins): common -> gui -> . A machine with ~/.config/dots-headless +skips gui (and has any stray gui unstowed). Run from inside the repo (~/.dots). +EOF +} + +# --- args --- +DRY="" +ADOPT="" +UNSTOW=0 +ARGS=() +for a in "$@"; do + case "$a" in + -n|--dry-run) DRY="-n" ;; + -a|--adopt) ADOPT="--adopt" ;; + -D|--unstow|--delete) UNSTOW=1 ;; + -h|--help) usage; exit 0 ;; + -*) echo "unknown option: $a" >&2; usage >&2; exit 2 ;; + *) ARGS+=("$a") ;; + esac +done +HOST="${ARGS[0]:-$(hostname -s)}" + +DOTS="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" +command -v stow >/dev/null 2>&1 || { echo "error: GNU stow is not installed (pacman -S stow)" >&2; exit 1; } + +gui=true +[ -e "$HEADLESS_MARKER" ] && gui=false + +echo "host=$HOST dots=$DOTS gui=$gui${DRY:+ (dry-run)}" + +# --- revert mode: unstow this host's packages (reverse of deploy), leave repo + real files intact --- +if [ "$UNSTOW" -eq 1 ]; then + verb="unstowed"; [ -n "$DRY" ] && verb="would unstow" + for p in "$HOST" gui common; do + [ -d "$DOTS/$p" ] || continue + if stow $DRY -d "$DOTS" -t "$HOME" -D "$p" 2>/dev/null; then + echo " $verb $p" + fi + # prune the now-empty dirs this package created under ~/.config (empty only — never real files) + if [ -z "$DRY" ] && [ -d "$DOTS/$p/.config" ]; then + for d in "$DOTS/$p"/.config/*/; do + t="$HOME/.config/$(basename "$d")" + [ -d "$t" ] && find "$t" -type d -empty -delete 2>/dev/null || true + done + fi + done + if [ -n "$DRY" ]; then + echo "(dry-run) nothing changed." + else + echo "reverted — removed this host's stow symlinks. ~/.dots and any real files are untouched." + echo "note: ~/.claude/* links (from link.sh) are separate; remove by hand if you want those gone too." + fi + exit 0 +fi + +# 0. self-heal symlinks that stow would reject ("not owned by stow"), which otherwise aborts all of +# common. Two stale-link classes a prior link.sh/heal can leave behind: +# - hooks/skills folded into an ABSOLUTE dir-symlink (they must be real dirs) — drop it entirely. +# - a file item re-linked with an ABSOLUTE target instead of stow's relative one — drop it. +# Only ever removes a *symlink* here (never a real file/dir), so nothing is lost; stow then +# recreates the correct relative link. A drifted *real* file is left for link.sh's drift guard +# (or `-a` to adopt) to reconcile, since it may hold un-synced edits. +if [ -z "$DRY" ]; then + for d in hooks skills; do + t="$HOME/.config/claude/$d" + [ -L "$t" ] && { rm -f "$t"; echo " cleared stale folded symlink ~/.config/claude/$d"; } + done + for f in settings.json statusline.py keybindings.json CLAUDE.md; do + t="$HOME/.config/claude/$f" + case "$(readlink "$t" 2>/dev/null)" in + /*) rm -f "$t"; echo " cleared absolute symlink ~/.config/claude/$f (stow will relink)" ;; + esac + done +fi + +# 1. release the host overlay first so base restows never conflict on overridden files +if [ -d "$DOTS/$HOST" ] && [ -z "$DRY" ]; then + stow -d "$DOTS" -t "$HOME" -D "$HOST" 2>/dev/null || true +fi + +# stow one package; track failures instead of silently continuing and reporting success +FAIL=0 +stow_pkg() { # stow_pkg