[Sync] adopt unified stow layout from the private repo

Mirrors the private dots tree at 900bdda: one shared base plus per-host
overlays, replacing the old flat .config/ layout (last synced 2026-06-28).

- packages: common/ gui/ wm/ lw/ fl/ + install.sh and bin/ tooling (dotsync,
  reconcile-hyde.sh)
- new README (layout, deploy order, HyDE dependency), plus ToDo.md and
  HYDE-UPDATE.md
- current HyDE waybar rig (layouts/, cava), pi agent extensions, claude/
  config, tmux, presenterm, aichat roles
- drops stale duplicates and generated cruft that should never have been
  tracked: the second top-level .pi/ copy, btop.log, zellij config.kdl.bak,
  fish_variables, nvim codecompanion.lua
- .pi/agent/auth.json is gitignored now; auth.json.example ships instead
- fl/ and wm/ hypr themes/ stay untracked (HyDE-generated per machine, per
  the root .gitignore)
This commit is contained in:
coja
2026-08-12 22:50:18 +02:00
parent f62cb40499
commit a700e23e0a
318 changed files with 12564 additions and 10772 deletions
+55
View File
@@ -0,0 +1,55 @@
# 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; no key required)
# 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
+663
View File
@@ -0,0 +1,663 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Name: One Half Dark
Author: Son A. Pham <sp@sonpham.me>
Url: https://github.com/sonph/onehalf
License: The MIT License (MIT)
A dark Sublime Text 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 Vim or iTerm.
Red: #e06c75
Green: #98c379
Yellow: #e5c07b
Blue: #61afef
Purple: #c678dd
Cyan: #56b6c2
White: #dcdfe4
Black: #282c34
Fg: #dcdfe4
Bg: #282c34
Comment: #5c6370
Gutter foreground: #919baa
Gutter background: #282c34
Selection: #474e5d
-->
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>OneHalfLight</string>
<key>semanticClass</key>
<string>theme.dark.one_half_dark</string>
<key>uuid</key>
<string></string>
<key>colorSpaceName</key>
<string>sRGB</string>
<key>author</key>
<string>Son A. Pham &lt;sp@sonpham.me&gt;</string>
<key>comment</key>
<string>A dark iTerm color scheme based on Atom&apos;s One. See github.com&#x2f;sonph&#x2f;onehalf for installation instructions, a light color scheme, and versions for other editors&#x2f;terminals such as (Neo)Vim and Sublime Text.</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#dcdfe4</string>
<key>background</key>
<string>#282c34</string>
<key>bracketsOptions</key>
<string>underline</string>
<key>caret</key>
<string>#a3b3cc</string>
<key>gutter</key>
<string>#282c34</string>
<key>gutterForeground</key>
<string>#919baa</string>
<key>invisibles</key>
<string>#5c6370</string>
<key>lineHighlight</key>
<string>#313640</string>
<key>selection</key>
<string>#474e5d</string>
<key>selectionBorder</key>
<string>#474e5d</string>
<key>tagsForeground</key>
<string></string>
<key>tagsOptions</key>
<string>stippled_underline</string>
<key>bracketContentsOptions</key>
<string>underline</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comments</string>
<key>scope</key>
<string>comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#5c6370</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Text</string>
<key>scope</key>
<string>variable.parameter.function</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Delimiters</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Operators</string>
<key>scope</key>
<string>keyword.operator</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keywords</string>
<key>scope</key>
<string>keyword</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variables</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Functions</string>
<key>scope</key>
<string>entity.name.function, meta.require, support.function.any-method</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Classes</string>
<key>scope</key>
<string>support.class, entity.name.class, entity.name.type.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Classes</string>
<key>scope</key>
<string>meta.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Methods</string>
<key>scope</key>
<string>keyword.other.special-method</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage</string>
<key>scope</key>
<string>storage</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Support</string>
<key>scope</key>
<string>support.function</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Strings, Inherited Class</string>
<key>scope</key>
<string>string</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Integers</string>
<key>scope</key>
<string>constant.numeric</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Floats</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Boolean</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Constants</string>
<key>scope</key>
<string>constant</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>HTML: Tags</string>
<key>scope</key>
<string>entity.name.tag</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>HTML: Tag attributes</string>
<key>scope</key>
<string>entity.other.attribute-name</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Attribute IDs</string>
<key>scope</key>
<string>entity.other.attribute-name.id, punctuation.definition.entity</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Selector</string>
<key>scope</key>
<string>meta.selector</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markdown: Headings</string>
<key>scope</key>
<string>markup.heading punctuation.definition.heading, entity.name.section</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markdown: Bold</string>
<key>scope</key>
<string>markup.bold, punctuation.definition.bold</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markdown: Italic</string>
<key>scope</key>
<string>markup.italic, punctuation.definition.italic</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markdown: Code</string>
<key>scope</key>
<string>markup.raw.inline</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markdown: Link Text</string>
<key>scope</key>
<string>string.other.link, punctuation.definition.string.end.markdown</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markdown: Link Url</string>
<key>scope</key>
<string>meta.link</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markdown: Lists</string>
<key>scope</key>
<string>markup.list</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markdown: Quotes</string>
<key>scope</key>
<string>markup.quote</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Java Source</string>
<key>scope</key>
<string>source.java meta.class.java meta.method.java</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Java Class Body</string>
<key>scope</key>
<string>source.java meta.class.java meta.class.body.java</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: Function Arguments</string>
<key>scope</key>
<string>source.js meta.function.js variable.parameter.function.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: New Variables</string>
<key>scope</key>
<string>source.js variable.other.readwrite.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: Variables</string>
<key>scope</key>
<string>source.js variable.other.object.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: Variables in Function Calls</string>
<key>scope</key>
<string>source.js meta.function-call.method.js variable.other.readwrite.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: New Block Variables</string>
<key>scope</key>
<string>source.js meta.block.js variable.other.readwrite.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: Block Variables</string>
<key>scope</key>
<string>source.js meta.block.js variable.other.object.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: Block Variables in Function Calls</string>
<key>scope</key>
<string>source.js meta.block.js meta.function-call.method.js variable.other.readwrite.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: Function Calls</string>
<key>scope</key>
<string>source.js meta.function-call.method.js variable.function.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: Properties</string>
<key>scope</key>
<string>source.js meta.property.object.js entity.name.function.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Javascript: Prototypes</string>
<key>scope</key>
<string>source.js support.constant.prototype.js</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Separator</string>
<key>scope</key>
<string>meta.separator</string>
<key>settings</key>
<dict>
<key>background</key>
<string></string>
<key>foreground</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Inserted</string>
<key>scope</key>
<string>markup.inserted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Deleted</string>
<key>scope</key>
<string>markup.deleted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Changed</string>
<key>scope</key>
<string>markup.changed</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Regular Expressions</string>
<key>scope</key>
<string>string.regexp</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Escape Characters</string>
<key>scope</key>
<string>constant.character.escape</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#56b6c2</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Embedded</string>
<key>scope</key>
<string>punctuation.section.embedded, variable.interpolation</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Illegal</string>
<key>scope</key>
<string>invalid.illegal</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#e06c75</string>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Broken</string>
<key>scope</key>
<string>invalid.broken</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#e5c07b</string>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Deprecated</string>
<key>scope</key>
<string>invalid.deprecated</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#e5c07b</string>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Unimplemented</string>
<key>scope</key>
<string>invalid.unimplemented</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#c678dd</string>
<key>foreground</key>
<string>#dcdfe4</string>
</dict>
</dict>
</array>
</dict>
</plist>
+9
View File
@@ -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 12 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.
+11
View File
@@ -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 14 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`).
+9
View File
@@ -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.
+11
View File
@@ -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`).
+10
View File
@@ -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.
+34
View File
@@ -0,0 +1,34 @@
# This is `bat`s configuration file. Each line either contains a comment or
# a command-line option that you want to pass to `bat` by default. You can
# run `bat --help` to get a list of all possible configuration options.
# Specify desired highlighting theme (e.g. "TwoDark"). Run `bat --list-themes`
# for a list of all available themes
# 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
# Uncomment the following line to disable automatic paging:
#--paging=never
# 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 --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"
+260
View File
@@ -0,0 +1,260 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>One Dark (NvChad base46)</string>
<key>uuid</key>
<string>3c1e2b84-9d5f-4a7c-8b2e-6f4a1d9c7e5b</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#1e222a</string>
<key>foreground</key>
<string>#abb2bf</string>
<key>caret</key>
<string>#abb2bf</string>
<key>selection</key>
<string>#3e4451</string>
<key>lineHighlight</key>
<string>#252931</string>
<key>gutterForeground</key>
<string>#545862</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comment</string>
<key>scope</key>
<string>comment, punctuation.definition.comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#6f737b</string>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>String</string>
<key>scope</key>
<string>string</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Number, constant</string>
<key>scope</key>
<string>constant.numeric, constant.language, constant.other</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Escape character</string>
<key>scope</key>
<string>constant.character.escape</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#a3b8ef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keyword, storage</string>
<key>scope</key>
<string>keyword, storage</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c882e7</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Operator</string>
<key>scope</key>
<string>keyword.operator</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#abb2bf</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function</string>
<key>scope</key>
<string>entity.name.function, support.function, meta.function-call.generic</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Class, type</string>
<key>scope</key>
<string>entity.name.class, entity.name.type, entity.name.struct, entity.name.enum, support.type, support.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e7c787</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variable</string>
<key>scope</key>
<string>variable, variable.other</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function parameter</string>
<key>scope</key>
<string>variable.parameter</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Attribute</string>
<key>scope</key>
<string>entity.other.attribute-name</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag</string>
<key>scope</key>
<string>entity.name.tag, meta.tag</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup heading</string>
<key>scope</key>
<string>markup.heading, entity.name.section</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
<key>fontStyle</key>
<string>bold</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup bold</string>
<key>scope</key>
<string>markup.bold</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>bold</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup italic</string>
<key>scope</key>
<string>markup.italic</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup link</string>
<key>scope</key>
<string>markup.underline.link, string.other.link</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup quote</string>
<key>scope</key>
<string>markup.quote</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#6f737b</string>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Diff inserted</string>
<key>scope</key>
<string>markup.inserted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Diff deleted</string>
<key>scope</key>
<string>markup.deleted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid</string>
<key>scope</key>
<string>invalid, invalid.illegal</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#ffffff</string>
<key>background</key>
<string>#be5046</string>
</dict>
</dict>
</array>
</dict>
</plist>
@@ -0,0 +1,262 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>Synthwave 84</string>
<key>uuid</key>
<string>7b2c3a10-4b6e-4d8f-9c1a-2e5f8d7a9b3c</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#262335</string>
<key>foreground</key>
<string>#ffffff</string>
<key>caret</key>
<string>#f97e72</string>
<key>selection</key>
<string>#463465</string>
<key>lineHighlight</key>
<string>#2a2139</string>
<key>gutterForeground</key>
<string>#848bbd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comment</string>
<key>scope</key>
<string>comment, punctuation.definition.comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#848bbd</string>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>String</string>
<key>scope</key>
<string>string</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#ff8b39</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Number, constant</string>
<key>scope</key>
<string>constant.numeric, constant.language, constant.other</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#f97e72</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Escape character</string>
<key>scope</key>
<string>constant.character.escape</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#36f9f6</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keyword, storage</string>
<key>scope</key>
<string>keyword, storage, keyword.operator</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#fede5d</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage type</string>
<key>scope</key>
<string>storage.type, storage.modifier</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#fede5d</string>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function</string>
<key>scope</key>
<string>entity.name.function, support.function, meta.function-call.generic</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#36f9f6</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Class, type</string>
<key>scope</key>
<string>entity.name.class, entity.name.type, entity.name.struct, entity.name.enum, support.type, support.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#fe4450</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variable</string>
<key>scope</key>
<string>variable, variable.other</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#ff7edb</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function parameter</string>
<key>scope</key>
<string>variable.parameter</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#ff7edb</string>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Attribute</string>
<key>scope</key>
<string>entity.other.attribute-name</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#fede5d</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag</string>
<key>scope</key>
<string>entity.name.tag, meta.tag</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#72f1b8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup heading</string>
<key>scope</key>
<string>markup.heading, entity.name.section</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#fe4450</string>
<key>fontStyle</key>
<string>bold</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup bold</string>
<key>scope</key>
<string>markup.bold</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>bold</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup italic</string>
<key>scope</key>
<string>markup.italic</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup link</string>
<key>scope</key>
<string>markup.underline.link, string.other.link</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#36f9f6</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Markup quote</string>
<key>scope</key>
<string>markup.quote</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#848bbd</string>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Diff inserted</string>
<key>scope</key>
<string>markup.inserted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#72f1b8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Diff deleted</string>
<key>scope</key>
<string>markup.deleted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#fe4450</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid</string>
<key>scope</key>
<string>invalid, invalid.illegal</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#ffffff</string>
<key>background</key>
<string>#fe4450</string>
</dict>
</dict>
</array>
</dict>
</plist>
+286
View File
@@ -0,0 +1,286 @@
#? Config file for btop v.1.4.7
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
color_theme = "Default"
#* If the theme set background should be shown, set to False if you want terminal background transparency.
theme_background = false
#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false.
truecolor = true
#* Set to true to force tty mode regardless if a real tty has been detected or not.
#* Will force 16-color mode and TTY theme, set all graph symbols to "tty" and swap out other non tty friendly symbols.
force_tty = false
#* Option to disable presets. Either the default preset, custom presets, or all presets.
#* "Off" All presets are enabled.
#* "Default" preset is disabled.#* "Custom" presets are disabled.#* "All" presets are disabled.
disable_presets = "Off"
#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.
#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box.
#* Use whitespace " " as separator between different presets.
#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty"
presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"
#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists.
#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift.
vim_keys = true
#* Disable all mouse events.
disable_mouse = false
#* Rounded corners on boxes, is ignored if TTY mode is ON.
rounded_corners = true
#* Use terminal synchronized output sequences to reduce flickering on supported terminals.
terminal_sync = true
#* Default symbols to use for graph creation, "braille", "block" or "tty".
#* "braille" offers the highest resolution but might not be included in all fonts.
#* "block" has half the resolution of braille but uses more common characters.
#* "tty" uses only 3 different symbols but will work with most fonts and should work in a real TTY.
#* Note that "tty" only has half the horizontal resolution of the other two, so will show a shorter historical view.
graph_symbol = "braille"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_cpu = "default"
# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty".
graph_symbol_gpu = "default"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_mem = "default"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_net = "default"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_proc = "default"
#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace.
shown_boxes = "proc cpu mem net"
#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs.
update_ms = 2000
#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct",
#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly.
proc_sorting = "cpu lazy"
#* Reverse sorting order, True or False.
proc_reversed = false
#* Show processes as a tree.
proc_tree = true
#* Use the cpu graph colors in the process list.
proc_colors = true
#* Use a darkening gradient in the process list.
proc_gradient = true
#* If process cpu usage should be of the core it's running on or usage of the total available cpu power.
proc_per_core = false
#* Show process memory as bytes instead of percent.
proc_mem_bytes = true
#* Show cpu graph for each process.
proc_cpu_graphs = true
#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate)
proc_info_smaps = false
#* Show proc box on left side of screen instead of right.
proc_left = false
#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop).
proc_filter_kernel = false
#* Should the process list follow the selected process when detailed view is open.
proc_follow_detailed = true
#* In tree-view, always accumulate child process resources in the parent process.
proc_aggregate = false
#* Should cpu and memory usage display be preserved for dead processes when paused.
keep_dead_proc_usage = false
#* Sets the CPU stat shown in upper half of the CPU graph, "total" is always available.
#* Select from a list of detected attributes from the options menu.
cpu_graph_upper = "Auto"
#* Sets the CPU stat shown in lower half of the CPU graph, "total" is always available.
#* Select from a list of detected attributes from the options menu.
cpu_graph_lower = "Auto"
#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off".
show_gpu_info = "Auto"
#* Toggles if the lower CPU graph should be inverted.
cpu_invert_lower = true
#* Set to True to completely disable the lower CPU graph.
cpu_single_graph = false
#* Show cpu box at bottom of screen instead of top.
cpu_bottom = false
#* Shows the system uptime in the CPU box.
show_uptime = true
#* Shows the CPU package current power consumption in watts. Requires running `make setcap` or `make setuid` or running with sudo.
show_cpu_watts = true
#* Show cpu temperature.
check_temp = true
#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors.
cpu_sensor = "Auto"
#* Show temperatures for cpu cores also if check_temp is True and sensors has been found.
show_coretemp = true
#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core.
#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine.
#* Format "x:y" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries.
#* Example: "4:0 5:1 6:3"
cpu_core_map = ""
#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine".
temp_scale = "celsius"
#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024.
base_10_sizes = false
#* Show CPU frequency.
show_cpu_freq = true
#* How to calculate CPU frequency, available values: "first", "range", "lowest", "highest" and "average".
freq_mode = "first"
#* Draw a clock at top of screen, formatting according to strftime, empty string to disable.
#* Special formatting: /host = hostname | /user = username | /uptime = system uptime
clock_format = "%X"
#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort.
background_update = true
#* Custom cpu model name, empty string to disable.
custom_cpu_name = ""
#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace " ".
#* Only disks matching the filter will be shown. Prepend exclude= to only show disks not matching the filter. Examples: disk_filter="/boot /home/user", disks_filter="exclude=/boot /home/user"
disks_filter = ""
#* Show graphs instead of meters for memory values.
mem_graphs = true
#* Show mem box below net box instead of above.
mem_below_net = false
#* Count ZFS ARC in cached and available memory.
zfs_arc_cached = true
#* If swap memory should be shown in memory box.
show_swap = true
#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk.
swap_disk = true
#* If mem box should be split to also show disks info.
show_disks = true
#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar.
only_physical = true
#* Read disks list from /etc/fstab. This also disables only_physical.
use_fstab = true
#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool)
zfs_hide_datasets = false
#* Set to true to show available disk space for privileged users.
disk_free_priv = false
#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view.
show_io_stat = true
#* Toggles io mode for disks, showing big graphs for disk read/write speeds.
io_mode = false
#* Set to True to show combined read/write io graphs in io mode.
io_graph_combined = false
#* Set the top speed for the io graphs in MiB/s (100 by default), use format "mountpoint:speed" separate disks with whitespace " ".
#* Example: "/mnt/media:100 /:20 /boot:1".
io_graph_speeds = ""
#* Swap the positions of the upload and download speed graphs. When true, upload will be on top.
swap_upload_download = false
#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False.
net_download = 100
net_upload = 100
#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest.
net_auto = true
#* Sync the auto scaling for download and upload to whichever currently has the highest scale.
net_sync = true
#* Starts with the Network Interface specified here.
net_iface = ""
#* "True" shows bitrates in base 10 (Kbps, Mbps). "False" shows bitrates in binary sizes (Kibps, Mibps, etc.). "Auto" uses base_10_sizes.
base_10_bitrate = "Auto"
#* Show battery stats in top right if battery is present.
show_battery = true
#* Which battery to use if multiple are present. "Auto" for auto detection.
selected_battery = "Auto"
#* Show power stats of battery next to charge indicator.
show_battery_watts = true
#* Set loglevel for "~/.local/state/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG".
#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info.
log_level = "WARNING"
#* Automatically save current settings to config file on exit.
save_config_on_exit = false
#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards.
nvml_measure_pcie_speeds = true
#* Measure PCIe throughput on AMD cards, may impact performance on certain cards.
rsmi_measure_pcie_speeds = true
#* Horizontally mirror the GPU graph.
gpu_mirror_graph = true
#* Set which GPU vendors to show. Available values are "nvidia amd intel apple"
shown_gpus = "nvidia amd intel"
#* Custom gpu0 model name, empty string to disable.
custom_gpu_name0 = ""
#* Custom gpu1 model name, empty string to disable.
custom_gpu_name1 = ""
#* Custom gpu2 model name, empty string to disable.
custom_gpu_name2 = ""
#* Custom gpu3 model name, empty string to disable.
custom_gpu_name3 = ""
#* Custom gpu4 model name, empty string to disable.
custom_gpu_name4 = ""
#* Custom gpu5 model name, empty string to disable.
custom_gpu_name5 = ""
+309
View File
@@ -0,0 +1,309 @@
## Configuration file for CAVA.
# Remove the ; to change parameters.
[general]
# Smoothing mode. Can be 'normal', 'scientific' or 'waves'. DEPRECATED as of 0.6.0
# mode = scientific # deprecated since cava 0.6.0, ignored
# Accepts only non-negative values.
framerate = 60
# 'autosens' will attempt to decrease sensitivity if the bars peak. 1 = on, 0 = off
# new as of 0.6.0 autosens of low values (dynamic range)
# 'overshoot' allows bars to overshoot (in % of terminal height) without initiating autosens. DEPRECATED as of 0.6.0
autosens = 1
; overshoot = 20
# Manual sensitivity in %. If autosens is enabled, this will only be the initial value.
# 200 means double height. Accepts only non-negative values.
; sensitivity = 100
# The number of bars (0-512). 0 sets it to auto (fill up console).
# Bars' width and space between bars in number of characters.
; bars = 0
; bar_width = 2
; bar_spacing = 1
# bar_height is only used for output in "noritake" format
; bar_height = 32
# For SDL width and space between bars is in pixels, defaults are:
; bar_width = 20
; bar_spacing = 5
# sdl_glsl have these default values, they are only used to calculate max number of bars.
; bar_width = 1
; bar_spacing = 0
# ceter bars in terminal, if there is space.
; center_align = 1
# max height of bars in terminal, in percent of terminal height.
; max_height = 100
# Lower and higher cutoff frequencies for lowest and highest bars
# the bandwidth of the visualizer.
# Note: there is a minimum total bandwidth of 43Mhz x number of bars.
# Cava will automatically increase the higher cutoff if a too low band is specified.
; lower_cutoff_freq = 50
; higher_cutoff_freq = 10000
# Seconds with no input before cava goes to sleep mode. Cava will not perform FFT or drawing and
# only check for input once per second. Cava will wake up once input is detected. 0 = disable.
; sleep_timer = 0
[input]
# Audio capturing method. Possible methods are: 'fifo', 'portaudio', 'pipewire', 'alsa', 'pulse', 'sndio', 'oss', 'jack' or 'shmem'
# Defaults to 'oss', 'pipewire', 'sndio', 'jack', 'pulse', 'alsa', 'portaudio' or 'fifo', in that order, dependent on what support cava was built with.
# On Mac it defaults to 'portaudio' or 'fifo'
# On windows this is automatic and no input settings are needed.
#
# All input methods uses the same config variable 'source'
# to define where it should get the audio.
#
# For pulseaudio and pipewire 'source' will be the source. Default: 'auto', which uses the monitor source of the default sink
# (all pulseaudio sinks(outputs) have 'monitor' sources(inputs) associated with them).
#
# For pipewire 'source' will be the object name or object.serial of the device to capture from.
# Both input and output devices are supported. To capture the monitor source of a sink node, append '.monitor' to the sink's object name.
#
# For alsa 'source' will be the capture device.
# For fifo 'source' will be the path to fifo-file.
# For shmem 'source' will be /squeezelite-AA:BB:CC:DD:EE:FF where 'AA:BB:CC:DD:EE:FF' will be squeezelite's MAC address
#
# For sndio 'source' will be a raw recording audio descriptor or a monitoring sub-device, e.g. 'rsnd/2' or 'snd/1'. Default: 'default'.
# README.md contains further information on how to setup CAVA for sndio.
#
# For oss 'source' will be the path to a audio device, e.g. '/dev/dsp2'. Default: '/dev/dsp', i.e. the default audio device.
# README.md contains further information on how to setup CAVA for OSS on FreeBSD.
#
# For jack 'source' will be the name of the JACK server to connect to, e.g. 'foobar'. Default: 'default'.
# README.md contains further information on how to setup CAVA for JACK.
#
; method = pulse
; source = auto
; method = pipewire
; source = auto
; method = alsa
; source = hw:Loopback,1
; method = fifo
; source = /tmp/mpd.fifo
; method = shmem
; source = /squeezelite-AA:BB:CC:DD:EE:FF
; method = portaudio
; source = auto
; method = sndio
; source = default
; method = oss
; source = /dev/dsp
; method = jack
; source = default
# The options 'sample_rate', 'sample_bits', 'channels' and 'autoconnect' can be configured for some input methods:
# sample_rate: fifo, pipewire, sndio, oss
# sample_bits: fifo, pipewire, sndio, oss
# channels: sndio, oss, jack
# autoconnect: jack
# Other methods ignore these settings.
#
# For 'sndio' and 'oss' they are only preferred values, i.e. if the values are not supported
# by the chosen audio device, the device will use other supported values instead.
# Example: 48000, 32 and 2, but the device only supports 44100, 16 and 1, then it
# will use 44100, 16 and 1.
#
; sample_rate = 44100
; sample_bits = 16
; channels = 2
; autoconnect = 2
[output]
# Output method. Can be 'ncurses', 'noncurses', 'raw', 'noritake', 'sdl'
# or 'sdl_glsl'.
# 'noncurses' (default) uses a buffer and cursor movements to only print
# changes from frame to frame in the terminal. Uses less resources and is less
# prone to tearing (vsync issues) than 'ncurses'.
#
# 'raw' is an 8 or 16 bit (configurable via the 'bit_format' option) data
# stream of the bar heights that can be used to send to other applications.
# 'raw' defaults to 1024 bars stereo (512 bars mono), which can be adjusted in the 'bars' option above.
#
# 'noritake' outputs a bitmap in the format expected by a Noritake VFD display
# in graphic mode. It only support the 3000 series graphical VFDs for now.
#
# 'sdl' uses the Simple DirectMedia Layer to render in a graphical context.
# 'sdl_glsl' uses SDL to create an OpenGL context. Write your own shaders or
# use one of the predefined ones.
; method = noncurses
# Orientation of the visualization. Can be 'bottom', 'top', 'left', 'right' or
# 'horizontal'. Default is 'bottom'. 'left and 'right' are only supported on sdl
# and ncruses output. 'horizontal' (bars go up and down from center) is only supported
# on noncurses output.
# Note: many fonts have weird or missing glyphs for characters used in orientations
# other than 'bottom', which can make output not look right.
; orientation = bottom
# Visual channels. Can be 'stereo' or 'mono'.
# 'stereo' mirrors both channels with low frequencies in center.
# 'mono' outputs left to right lowest to highest frequencies.
# 'mono_option' set mono to either take input from 'left', 'right' or 'average'.
# set 'reverse' to 1 to display frequencies the other way around.
; channels = stereo
; mono_option = average
; reverse = 0
# Raw output target.
# On Linux, a fifo will be created if target does not exist.
# On Windows, a named pipe will be created if target does not exist.
; raw_target = /dev/stdout
# Raw data format. Can be 'binary' or 'ascii'.
; data_format = binary
# Binary bit format, can be '8bit' (0-255) or '16bit' (0-65530).
; bit_format = 16bit
# Ascii max value. In 'ascii' mode range will run from 0 to value specified here
; ascii_max_range = 1000
# Ascii delimiters. In ascii format each bar and frame is separated by a delimiters.
# Use decimal value in ascii table (i.e. 59 = ';' and 10 = '\n' (line feed)).
; bar_delimiter = 59
; frame_delimiter = 10
# sdl window size and position. -1,-1 is centered.
; sdl_width = 1024
; sdl_height = 512
; sdl_x = -1
; sdl_y= -1
; sdl_full_screen = 0
# set label on bars on the x-axis. Can be 'frequency' or 'none'. Default: 'none'
# 'frequency' displays the lower cut off frequency of the bar above.
# Only supported on ncurses and noncurses output.
; xaxis = none
# enable synchronized sync. 1 = on, 0 = off
# removes flickering in alacritty terminal emulator.
# defaults to off since the behaviour in other terminal emulators is unknown
; synchronized_sync = 0
# Shaders for sdl_glsl, located in $HOME/.config/cava/shaders
; vertex_shader = pass_through.vert
; fragment_shader = bar_spectrum.frag
; for glsl output mode, keep rendering even if no audio
; continuous_rendering = 0
# disable console blank (screen saver) in tty
# (Not supported on FreeBSD)
; disable_blanking = 0
# show a flat bar at the bottom of the screen when idle, 1 = on, 0 = off
; show_idle_bar_heads = 1
# show waveform instead of frequency spectrum, 1 = on, 0 = off
; waveform = 0
[color]
# Colors can be one of seven predefined: black, blue, cyan, green, magenta, red, white, yellow.
# Or defined by hex code '#xxxxxx' (hex code must be within ''). User defined colors requires
# a terminal that can change color definitions such as Gnome-terminal or rxvt.
# default is to keep current terminal color
; background = default
; foreground = default
# SDL and sdl_glsl only support hex code colors, these are the default:
; background = '#111111'
; foreground = '#D34b11'
# ; foreground = '#D34b11'
# Gradient mode, only hex defined colors are supported,
# background must also be defined in hex or remain commented out. 1 = on, 0 = off.
# You can define as many as 8 different colors. They range from bottom to top of screen
gradient = 1
gradient_color_1 = '#d34b11'
gradient_color_2 = '#cc5933'
gradient_color_3 = '#cc8033'
gradient_color_4 = '#cca633'
gradient_color_5 = '#cccc33'
gradient_color_6 = '#a6cc33'
gradient_color_7 = '#80cc33'
gradient_color_8 = '#59cc33'
# Horizontal is only supported on noncurses output.
# Only one color will be calculated per bar.
; horizontal_gradient = 1
; horizontal_gradient_color_1 = '#c45161'
; horizontal_gradient_color_2 = '#e094a0'
; horizontal_gradient_color_3 = '#f2b6c0'
; horizontal_gradient_color_4 = '#f2dde1'
; horizontal_gradient_color_5 = '#cbc7d8'
; horizontal_gradient_color_6 = '#8db7d2'
; horizontal_gradient_color_7 = '#5e62a9'
; horizontal_gradient_color_8 = '#434279'
# If both vertical and horizontal gradient is enabled, vertical will be blended in this direction.
# Can be 'up', 'down', 'left' or 'right'. 'up' means the vertical gradient will be blended in from
# bottom to top. I.e. the bottom will be only the horizontal
# and top will be only the color of the vertical gradient.
; blend_direction = 'up'
# use theme file instead of defining colors in this file
# themes are located in $HOME/.config/cava/themes
; theme = 'none'
[smoothing]
# Percentage value for integral smoothing. Takes values from 0 - 100.
# Higher values means smoother, but less precise. 0 to disable.
# DEPRECATED as of 0.8.0, use noise_reduction instead
; integral = 77
# Disables or enables the so-called "Monstercat smoothing" with or without "waves". Set to 0 to disable.
; monstercat = 0
; waves = 0
# Set gravity percentage for "drop off". Higher values means bars will drop faster.
# Accepts only non-negative values. 50 means half gravity, 200 means double. Set to 0 to disable "drop off".
# DEPRECATED as of 0.8.0, use noise_reduction instead
; gravity = 100
# In bar height, bars that would have been lower that this will not be drawn.
# DEPRECATED as of 0.8.0
; ignore = 0
# Noise reduction, int 0 - 100. default 77
# the raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth
# 100 will be very slow and smooth, 0 will be fast but noisy.
; noise_reduction = 77
[eq]
# This one is tricky. You can have as much keys as you want.
# Remember to uncomment more than one key! More keys = more precision.
# Look at readme.md on github for further explanations and examples.
; 1 = 1 # bass
; 2 = 1
; 3 = 1 # midtone
; 4 = 1
; 5 = 1 # treble
@@ -0,0 +1,79 @@
#version 330
in vec2 fragCoord;
out vec4 fragColor;
// bar values. defaults to left channels first (low to high), then right (high to low).
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform int bar_width; // bar width (configurable), not used here
uniform int bar_spacing; // space bewteen bars (configurable)
uniform vec3 u_resolution; // window resolution
//colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
uniform vec3 bg_color; // background color
uniform vec3 fg_color; // foreground color
uniform int gradient_count;
uniform vec3 gradient_colors[8]; // gradient colors
vec3 normalize_C(float y,vec3 col_1, vec3 col_2, float y_min, float y_max)
{
//create color based on fraction of this color and next color
float yr = (y - y_min) / (y_max - y_min);
return col_1 * (1.0 - yr) + col_2 * yr;
}
void main()
{
// find which bar to use based on where we are on the x axis
float x = u_resolution.x * fragCoord.x;
int bar = int(bars_count * fragCoord.x);
//calculate a bar size
float bar_size = u_resolution.x / bars_count;
//the y coordinate and bar values are the same
float y = bars[bar];
// make sure there is a thin line at bottom
if (y * u_resolution.y < 1.0)
{
y = 1.0 / u_resolution.y;
}
//draw the bar up to current height
if (y > fragCoord.y)
{
//make some space between bars basen on settings
if (x > (bar + 1) * (bar_size) - bar_spacing)
{
fragColor = vec4(bg_color,1.0);
}
else
{
if (gradient_count == 0)
{
fragColor = vec4(fg_color,1.0);
}
else
{
//find which color in the configured gradient we are at
int color = int((gradient_count - 1) * fragCoord.y);
//find where on y this and next color is supposed to be
float y_min = color / (gradient_count - 1.0);
float y_max = (color + 1.0) / (gradient_count - 1.0);
//make color
fragColor = vec4(normalize_C(fragCoord.y, gradient_colors[color], gradient_colors[color + 1], y_min, y_max), 1.0);
}
}
}
else
{
fragColor = vec4(bg_color,1.0);
}
}
+117
View File
@@ -0,0 +1,117 @@
#version 330
// this shader was stolen from shadertoy user ChunderFPV
#define SCALE 8.0
#define PI radians(180.0)
#define TAU (PI * 2.0)
#define CS(a) vec2(cos(a), sin(a))
#define PT(u, r) smoothstep(0.0, r, r - length(u))
in vec2 fragCoord;
out vec4 fragColor;
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform float shader_time; // shader execution time s
uniform int bar_width; // bar width (configurable), not used here
uniform int bar_spacing; // space bewteen bars (configurable)
uniform vec3 u_resolution; // window resolution
// colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
uniform vec3 bg_color; // background color
uniform vec3 fg_color; // foreground color
uniform int gradient_count;
uniform vec3 gradient_colors[8]; // gradient colors
// gradient map ( color, equation, time, width, shadow, reciprocal )
vec3 gm(vec3 c, float n, float t, float w, float d, bool i) {
float g = min(abs(n), 1.0 / abs(n));
float s = abs(sin(n * PI - t));
if (i)
s = min(s, abs(sin(PI / n + t)));
return (1.0 - pow(abs(s), w)) * c * pow(g, d) * 6.0;
}
// denominator spiral, use 1/n for numerator
// ( screen xy, spiral exponent, decimal, line width, hardness, rotation )
float ds(vec2 u, float e, float n, float w, float h, float ro) {
float ur = length(u); // unit radius
float sr = pow(ur, e); // spiral radius
float a = round(sr) * n * TAU; // arc
vec2 xy = CS(a + ro) * ur; // xy coords
float l = PT(u - xy, w); // line
float s = mod(sr + 0.5, 1.0); // gradient smooth
s = min(s, 1.0 - s); // darken filter
return l * s * h;
}
void main() {
float t = shader_time / PI * 2.0;
vec4 m = vec4(0, 0, 0, 0); // iMouse;
m.xy = m.xy * 2.0 / u_resolution.xy - 1.0; // ±1x, ±1y
if (m.z > 0.0)
t += m.y * SCALE; // move time with mouse y
float z = (m.z > 0.0) ? pow(1.0 - abs(m.y), sign(m.y)) : 1.0; // zoom (+)
float e = (m.z > 0.0) ? pow(1.0 - abs(m.x), -sign(m.x))
: 1.0; // screen exponent (+)
float se = (m.z > 0.0) ? e * -sign(m.y) : 1.0; // spiral exponent
vec3 bg = vec3(0); // black background
float aa = 3.0; // anti-aliasing
for (float j = 0.0; j < aa; j++)
for (float k = 0.0; k < aa; k++) {
vec3 c = vec3(0);
vec2 o = vec2(j, k) / aa;
vec2 uv = (fragCoord * u_resolution.xy - 0.5 * u_resolution.xy + o) /
u_resolution.y * SCALE * z; // apply cartesian, scale and zoom
if (m.z > 0.0)
uv =
exp(log(abs(uv)) * e) * sign(uv); // warp screen space with exponent
float px = length(fwidth(uv)); // pixel width
float x = uv.x; // every pixel on x
float y = uv.y; // every pixel on y
float l = length(uv); // hypot of xy: sqrt(x*x+y*y)
float mc = (x * x + y * y - 1.0) / y; // metallic circle at xy
float g = min(abs(mc), 1.0 / abs(mc)); // gradient
vec3 gold = vec3(1.0, 0.6, 0.0) * g * l;
vec3 blue = vec3(0.3, 0.5, 0.9) * (1.0 - g);
vec3 rgb = max(gold, blue);
float w = 0.1; // line width
float d = 0.4; // shadow depth
c = max(c, gm(rgb, mc, -t, w * bars[0], d, false)); // metallic
c = max(c, gm(rgb, abs(y / x) * sign(y), -t, w * bars[1], d,
false)); // tangent
c = max(c, gm(rgb, (x * x) / (y * y) * sign(y), -t, w * bars[2], d,
false)); // sqrt cotangent
c = max(c, gm(rgb, (x * x) + (y * y), t, w * bars[3], d,
true)); // sqrt circles
c += rgb * ds(uv, se, t / TAU, px * 2.0 * bars[4], 2.0, 0.0); // spiral 1a
c += rgb * ds(uv, se, t / TAU, px * 2.0 * bars[5], 2.0, PI); // spiral 1b
c +=
rgb * ds(uv, -se, t / TAU, px * 2.0 * bars[6], 2.0, 0.0); // spiral 2a
c += rgb * ds(uv, -se, t / TAU, px * 2.0 * bars[7], 2.0, PI); // spiral 2b
c = max(c, 0.0); // clear negative color
c += pow(max(1.0 - l, 0.0), 3.0 / z); // center glow
if (m.z > 0.0) // display grid on click
{
vec2 xyg = abs(fract(uv + 0.5) - 0.5) / px; // xy grid
c.gb += 0.2 * (1.0 - min(min(xyg.x, xyg.y), 1.0));
}
bg += c;
}
bg /= aa * aa;
bg *= sqrt(bg) * 1.5;
fragColor = vec4(bg, 1.0);
}
@@ -0,0 +1,34 @@
#version 330
in vec2 fragCoord;
out vec4 fragColor;
// bar values. defaults to left channels first (low to high), then right (high to low).
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform vec3 u_resolution; // window resolution, not used here
//colors, configurable in cava config file
uniform vec3 bg_color; // background color(r,g,b) (0.0 - 1.0), not used here
uniform vec3 fg_color; // foreground color, not used here
void main()
{
// find which bar to use based on where we are on the x axis
int bar = int(bars_count * fragCoord.x);
float bar_y = 1.0 - abs((fragCoord.y - 0.5)) * 2.0;
float y = (bars[bar]) * bar_y;
float bar_x = (fragCoord.x - float(bar) / float(bars_count)) * bars_count;
float bar_r = 1.0 - abs((bar_x - 0.5)) * 2;
bar_r = bar_r * bar_r * 2;
// set color
fragColor.r = fg_color.x * y * bar_r;
fragColor.g = fg_color.y * y * bar_r;
fragColor.b = fg_color.z * y * bar_r;
}
@@ -0,0 +1,14 @@
#version 330
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
// Output data ; will be interpolated for each fragment.
out vec2 fragCoord;
void main()
{
gl_Position = vec4(vertexPosition_modelspace,1);
fragCoord = (vertexPosition_modelspace.xy+vec2(1,1))/2.0;
}
@@ -0,0 +1,53 @@
#version 330
in vec2 fragCoord;
out vec4 fragColor;
// bar values. defaults to left channels first (low to high), then right (high
// to low).
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform int bar_width; // bar width (configurable), not used here
uniform int bar_spacing; // space bewteen bars (configurable)
uniform vec3 u_resolution; // window resolution
// colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
uniform vec3 bg_color; // background color
uniform vec3 fg_color; // foreground color
uniform int gradient_count;
uniform vec3 gradient_colors[8]; // gradient colors
uniform sampler2D inputTexture; // Texture from the first render pass
vec3 normalize_C(float y, vec3 col_1, vec3 col_2, float y_min, float y_max) {
// create color based on fraction of this color and next color
float yr = (y - y_min) / (y_max - y_min);
return col_1 * (1.0 - yr) + col_2 * yr;
}
void main() {
// find which bar to use based on where we are on the y axis
int bar = int(bars_count * fragCoord.y);
float y = bars[bar];
float band_size = 1.0 / float(bars_count);
float current_band_min = bar * band_size;
float current_band_max = (bar + 1) * band_size;
int hist_length = 512;
float win_size = 1.0 / hist_length;
if (fragCoord.x > 1.0 - win_size) {
if (fragCoord.y > current_band_min && fragCoord.y < current_band_max) {
fragColor = vec4(fg_color * y, 1.0);
}
} else {
vec2 offsetCoord = fragCoord;
offsetCoord.x += float(win_size);
fragColor = texture(inputTexture, offsetCoord);
}
}
@@ -0,0 +1,112 @@
#version 330
// Emulate the "line style" spectrum analyzer from Winamp 2.
// Try this config for a demonstration:
/*
[general]
bar_width = 2
bar_spacing = 0
higher_cutoff_freq = 22000
[output]
method = sdl_glsl
channels = mono
fragment_shader = winamp_line_style_spectrum.frag
[color]
background = '#000000'
gradient = 1
gradient_color_1 = '#319C08'
gradient_color_2 = '#29CE10'
gradient_color_3 = '#BDDE29'
gradient_color_4 = '#DEA518'
gradient_color_5 = '#D66600'
gradient_color_6 = '#CE2910'
[smoothing]
noise_reduction = 10
*/
in vec2 fragCoord;
out vec4 fragColor;
// bar values. defaults to left channels first (low to high), then right (high to low).
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform int bar_width; // bar width (configurable), not used here
uniform int bar_spacing; // space bewteen bars (configurable)
uniform vec3 u_resolution; // window resolution
//colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
uniform vec3 bg_color; // background color
uniform vec3 fg_color; // foreground color
uniform int gradient_count;
uniform vec3 gradient_colors[8]; // gradient colors
vec3 normalize_C(float y,vec3 col_1, vec3 col_2, float y_min, float y_max)
{
//create color based on fraction of this color and next color
float yr = (y - y_min) / (y_max - y_min);
return col_1 * (1.0 - yr) + col_2 * yr;
}
void main()
{
// find which bar to use based on where we are on the x axis
float x = u_resolution.x * fragCoord.x;
int bar = int(bars_count * fragCoord.x);
//calculate a bar size
float bar_size = u_resolution.x / bars_count;
//the y coordinate is stretched by 4X to resemble Winamp
float y = min(bars[bar] * 4.0, 1.0);
// make sure there is a thin line at bottom
if (y * u_resolution.y < 1.0)
{
y = 1.0 / u_resolution.y;
}
vec4 bar_color;
if (gradient_count == 0)
{
bar_color = vec4(fg_color,1.0);
}
else
{
//find color in the configured gradient for the top of the bar
int color = int((gradient_count - 1) * y);
//find where on y this and next color is supposed to be
float y_min = float(color) / (gradient_count - 1.0);
float y_max = float(color + 1) / (gradient_count - 1.0);
//make a solid color for the entire bar
bar_color = vec4(normalize_C(y, gradient_colors[color], gradient_colors[color + 1], y_min, y_max), 1.0);
}
//draw the bar up to current height
if (y > fragCoord.y)
{
//make some space between bars based on settings
if (x > (bar + 1) * (bar_size) - bar_spacing)
{
fragColor = vec4(bg_color,1.0);
}
else
{
fragColor = bar_color;
}
}
else
{
fragColor = vec4(bg_color,1.0);
}
}
+15
View File
@@ -0,0 +1,15 @@
[color]
background = '#001e26'
foreground = '#708183'
gradient = 1
gradient_color_1 = '#268bd2'
gradient_color_2 = '#6c71c4'
gradient_color_3 = '#cb4b16'
horizontal_gradient = 1
horizontal_gradient_color_1 = '#586e75'
horizontal_gradient_color_2 = '#b58900'
horizontal_gradient_color_3 = '#839496'
blend_direction = 'up'
+10
View File
@@ -0,0 +1,10 @@
[color]
horizontal_gradient = 1
horizontal_gradient_color_1 = '#c45161'
horizontal_gradient_color_2 = '#e094a0'
horizontal_gradient_color_3 = '#f2b6c0'
horizontal_gradient_color_4 = '#f2dde1'
horizontal_gradient_color_5 = '#cbc7d8'
horizontal_gradient_color_6 = '#8db7d2'
horizontal_gradient_color_7 = '#5e62a9'
horizontal_gradient_color_8 = '#434279'
+21
View File
@@ -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.
+91
View File
@@ -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/<name>/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/`.
+76
View File
@@ -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%` | `5080%` | `≥ 80%` (⚠compact) |
| 5h usage | `< 50%` | `5080%` | `≥ 80%` |
| Disk | `< 75%` | `7590%` | `≥ 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.
+149
View File
@@ -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()
+36
View File
@@ -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"
}
}
]
}
+97
View File
@@ -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
+79
View File
@@ -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
}
@@ -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 < <path from step 5>`. 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.
@@ -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.
@@ -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 `<git-dir>/pr-loop-state.json` (find `<git-dir>` 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 <N> — PR #<num>: 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/<login> --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 — <reviewer> hit its usage limit; nothing left to address,
I won't re-check again.` or `⏹ Stopped — hit safety cap (<which>); 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 < <that path>`.
- 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 `<git-dir>/pr-loop-state.json`. Then schedule:
- **normal cycle:** `ScheduleWakeup(delaySeconds=600, prompt="/pr-loop", reason="recheck PR
#<num> 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.
@@ -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 <base> HEAD`..`HEAD`.
2. **Gather context.** Read the full diff for that range (`git diff <merge-base>...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.
+517
View File
@@ -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()
+108
View File
@@ -0,0 +1,108 @@
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
"logo": {
"source": "$(find \"${XDG_CONFIG_HOME:-$HOME/.config}/fastfetch/pngs/\" -name \"*.png\" | shuf -n 1)",
"height": 18
},
"display": {
"separator": " : "
},
"modules": [
{
"type": "custom",
"format": "\u001b[36m "
},
{
"type": "custom",
"format": "┌──────────────────────────────────────────┐"
},
{
"type": "os",
"key": "  OS",
"keyColor": "red"
},
{
"type": "kernel",
"key": "  Kernel",
"keyColor": "red"
},
{
"type": "packages",
"key": "  Packages",
"keyColor": "green"
},
{
"type": "display",
"key": "  Display",
"keyColor": "green"
},
{
"type": "wm",
"key": "  WM",
"keyColor": "yellow"
},
{
"type": "terminal",
"key": "  Terminal",
"keyColor": "yellow"
},
{
"type": "custom",
"format": "└──────────────────────────────────────────┘"
},
"break",
{
"type": "title",
"key": " "
},
{
"type": "custom",
"format": "┌──────────────────────────────────────────┐"
},
{
"type": "cpu",
"format": "{1}",
"key": "  CPU",
"keyColor": "blue"
},
{
"type": "gpu",
"format": "{2}",
"key": "  GPU",
"keyColor": "blue"
},
{
"type": "gpu",
"format": "{3}",
"key": "  GPU Driver",
"keyColor": "magenta"
},
{
"type": "memory",
"key": " ﬙ Memory",
"keyColor": "magenta"
},
{
"type": "command",
"key": " 󱦟 OS Age ",
"keyColor": "31",
"text": "birth_install=$(stat -c %W /); current=$(date +%s); time_progression=$((current - birth_install)); days_difference=$((time_progression / 86400)); echo $days_difference days"
},
{
"type": "uptime",
"key": " 󱫐 Uptime ",
"keyColor": "red"
},
{
"type": "custom",
"format": "└──────────────────────────────────────────┘"
},
{
"type": "colors",
"paddingLeft": 2,
"symbol": "circle"
},
"break"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -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/" "")'
+3
View File
@@ -0,0 +1,3 @@
# `h <cmd> ...` 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)'
+17
View File
@@ -0,0 +1,17 @@
function __fish_pipx_complete
set -x _ARGCOMPLETE 1
set -x _ARGCOMPLETE_DFS \t
set -x _ARGCOMPLETE_IFS \n
set -x _ARGCOMPLETE_SUPPRESS_SPACE 1
set -x _ARGCOMPLETE_SHELL fish
set -x COMP_LINE (commandline -p)
set -x COMP_POINT (string length (commandline -cp))
set -x COMP_TYPE
if set -q _ARC_DEBUG
pipx 8>&1 9>&2 1>&9 2>&1
else
pipx 8>&1 9>&2 1>/dev/null 2>&1
end
end
complete --command pipx -f -a '(__fish_pipx_complete)'
@@ -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
@@ -0,0 +1,3 @@
set --global fish_key_bindings fish_vi_key_bindings
set --erase --universal fish_key_bindings
@@ -0,0 +1,38 @@
set --global fish_color_autosuggestion 4D5566
set --global fish_color_cancel -r
set --global fish_color_command 39BAE6
set --global fish_color_comment 626A73
set --global fish_color_cwd 59C2FF
set --global fish_color_cwd_root red
set --global fish_color_end F29668
set --global fish_color_error FF3333
set --global fish_color_escape 95E6CB
set --global fish_color_history_current --bold
set --global fish_color_host normal
set --global fish_color_host_remote yellow
set --global fish_color_keyword 39BAE6
set --global fish_color_match F07178
set --global fish_color_normal B3B1AD
set --global fish_color_operator E6B450
set --global fish_color_option B3B1AD
set --global fish_color_param B3B1AD
set --global fish_color_quote C2D94C
set --global fish_color_redirection FFEE99
set --global fish_color_search_match --background=E6B450
set --global fish_color_selection --background=E6B450
set --global fish_color_status red
set --global fish_color_user brgreen
set --global fish_color_valid_path --underline
set --global fish_pager_color_background
set --global fish_pager_color_completion normal
set --global fish_pager_color_description B3A06D yellow
set --global fish_pager_color_prefix normal --bold --underline
set --global fish_pager_color_progress brwhite --background=cyan
set --global fish_pager_color_secondary_background
set --global fish_pager_color_secondary_completion
set --global fish_pager_color_secondary_description
set --global fish_pager_color_secondary_prefix
set --global fish_pager_color_selected_background --background=E6B450
set --global fish_pager_color_selected_completion
set --global fish_pager_color_selected_description
set --global fish_pager_color_selected_prefix
+7
View File
@@ -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
+115
View File
@@ -0,0 +1,115 @@
## 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 wnotes "cd ~/sync/Wingman/notes && nvim"
abbr wtodo "cd ~/sync/Wingman/notes/wm-client/ && nvim todo.md"
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
+30
View File
@@ -0,0 +1,30 @@
                          .dO00Od'                          
                         d00000000d.                        
                        d0000000000k                        
                        x0000000000O                        
                         k000000000                         
                           ;0000: Okc                       
                                    kkc                     
                                      kkc                   
                                        dkl                 
                                          okl               
                                            lOl             
                                              cOo.          
 .lxkOOkxl.              .lxkOOkxl.             ;OoxkOOkxl. 
'O00000000O,            'O00000000O,            '000000000O,
O0000000000OddddddddddddO00000000000ddddddddddddO00000000000
c0000000000c            :0000000000l            :0000000000l
 .00000000.              .00000000'             '000000000' 
                             00               'd0.          
                             00             ,d0.            
                             00           ,x0               
                             00         :x0                 
                             00       cxO                   
                             00     ckO                     
                           .c00l' ckk                       
                         ck000000O0                         
                        c0000000000o                        
                        O00000000000                        
                        .0000000000.                        
                          ,000000:                          
                             :c                             
+1
View File
@@ -0,0 +1 @@
danhper/fish-ssh-agent
@@ -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
@@ -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
+3
View File
@@ -0,0 +1,3 @@
function ask --description 'answer from cht.sh'
curl -s https://cht.sh/$(string join '+' $argv[1..])
end
+3
View File
@@ -0,0 +1,3 @@
function bashs --description 'raw bash command'
bash -c "$argv"
end
@@ -0,0 +1,30 @@
function c2-worktree
if test (count $argv) -ne 1
echo "Usage: c2-worktree <branch-name>"
echo "Example: c2-worktree package/package-update"
return 1
end
set branch $argv[1]
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
git checkout master
or return 1
git worktree add $dir $branch
or return 1
cd $dir
or return 1
pnpm i
end
+3
View File
@@ -0,0 +1,3 @@
function clis --description 'fzf select cli'
eval (cat $HOME/.config/misc/clis.txt | command fzf --multi)
end
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,14 @@
function fish_prompt
if test -n "$SSH_TTY"
echo -n (set_color brred)"$USER"(set_color white)'@'(set_color yellow)(prompt_hostname)' '
end
echo -n (set_color blue)(prompt_pwd)' '
set_color -o
if fish_is_root_user
echo -n (set_color red)'# '
end
echo -n (set_color red)''(set_color yellow)''(set_color green)' '
set_color normal
end
@@ -0,0 +1,161 @@
function fish_right_prompt
set -l cmd_status $status
if test $cmd_status -ne 0
echo -n (set_color red)"$cmd_status"
end
if not command -sq git
set_color normal
return
end
# Get the git directory for later use.
# Return if not inside a Git repository work tree.
if not set -l git_dir (command git rev-parse --git-dir 2>/dev/null)
set_color normal
return
end
# Get the current action ("merge", "rebase", etc.)
# and if there's one get the current commit hash too.
set -l commit ''
if set -l action (fish_print_git_action "$git_dir")
set commit (command git rev-parse HEAD 2> /dev/null | string sub -l 7)
end
# Get either the branch name or a branch descriptor.
set -l branch_detached 0
if not set -l branch (command git symbolic-ref --short HEAD 2>/dev/null)
set branch_detached 1
set branch (command git describe --contains --all HEAD 2>/dev/null)
end
# Get the commit difference counts between local and remote.
command git rev-list --count --left-right 'HEAD...@{upstream}' 2>/dev/null \
| read -d \t -l status_ahead status_behind
if test $status -ne 0
set status_ahead 0
set status_behind 0
end
# Get the stash status.
# (git stash list) is very slow. => Avoid using it.
set -l status_stashed 0
if test -f "$git_dir/refs/stash"
set status_stashed 1
else if test -r "$git_dir/commondir"
read -l commondir <"$git_dir/commondir"
if test -f "$commondir/refs/stash"
set status_stashed 1
end
end
# git-status' porcelain v1 format starts with 2 letters on each line:
# The first letter (X) denotes the index state.
# The second letter (Y) denotes the working directory state.
#
# The following table presents the possible combinations:
# * The underscore character denotes whitespace.
# * The cell values stand for the following file states:
# a: added
# d: deleted
# m: modified
# r: renamed
# u: unmerged
# t: untracked
# * Cells with more than one letter signify that both states
# are simultaneously the case. This is possible since the git index
# and working directory operate independently of each other.
# * Cells which are empty are unhandled by this code.
# * T (= type change) is undocumented.
# See Git v1.7.8.2 release notes for more information.
#
# \ Y→
# X \
# ↓ | A | C | D | M | R | T | U | X | B | ? | _
# ----+----+----+----+----+----+----+----+----+----+----+----
# A | u | | ad | am | r | am | u | | | | a
# C | | | ad | am | r | am | u | | | | a
# D | | | u | am | r | am | u | | | | a
# M | | | ad | am | r | am | u | | | | a
# R | r | r | rd | rm | r | rm | ur | r | r | r | r
# T | | | ad | am | r | am | u | | | | a
# U | u | u | u | um | ur | um | u | u | u | u | u
# X | | | | m | r | m | u | | | |
# B | | | | m | r | m | u | | | |
# ? | | | | m | r | m | u | | | t |
# _ | | | d | m | r | m | u | | | |
set -l porcelain_status (command git status --porcelain 2>/dev/null | string sub -l2)
set -l status_added 0
if string match -qr '[ACDMT][ MT]|[ACMT]D' $porcelain_status
set status_added 1
end
set -l status_deleted 0
if string match -qr '[ ACMRT]D' $porcelain_status
set status_deleted 1
end
set -l status_modified 0
if string match -qr '[MT]$' $porcelain_status
set status_modified 1
end
set -l status_renamed 0
if string match -qe R $porcelain_status
set status_renamed 1
end
set -l status_unmerged 0
if string match -qr 'AA|DD|U' $porcelain_status
set status_unmerged 1
end
set -l status_untracked 0
if string match -qe '\?\?' $porcelain_status
set status_untracked 1
end
set_color -o
if test -n "$branch"
if test $branch_detached -ne 0
set_color brmagenta
else
set_color green
end
echo -n " $branch"
end
if test -n "$commit"
echo -n ' '(set_color yellow)"$commit"
end
if test -n "$action"
set_color normal
echo -n (set_color white)':'(set_color -o brred)"$action"
end
if test $status_ahead -ne 0
echo -n ' '(set_color brmagenta)''
end
if test $status_behind -ne 0
echo -n ' '(set_color brmagenta)''
end
if test $status_stashed -ne 0
echo -n ' '(set_color cyan)''
end
if test $status_added -ne 0
echo -n ' '(set_color green)''
end
if test $status_deleted -ne 0
echo -n ' '(set_color red)''
end
if test $status_modified -ne 0
echo -n ' '(set_color blue)''
end
if test $status_renamed -ne 0
echo -n ' '(set_color magenta)''
end
if test $status_unmerged -ne 0
echo -n ' '(set_color yellow)'═'
end
if test $status_untracked -ne 0
echo -n ' '(set_color white)'◼'
end
set_color normal
end
@@ -0,0 +1,15 @@
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
bind -M default ctrl-e end-of-line
bind -M insert ctrl-space accept-autosuggestion
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
+7
View File
@@ -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
+5
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
function h --description "Colorized --help via bat"
$argv --help 2>&1 | bat --plain --language=help
end
+18
View File
@@ -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
@@ -0,0 +1,24 @@
function opencodes --description 'contained opencode'
bwrap \
--unshare-all \
--new-session \
--clearenv \
--die-with-parent \
--share-net \
--dev /dev \
--proc /proc \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--ro-bind /bin /bin \
--ro-bind /etc /etc \
--ro-bind ~/.config/opencode ~/.config/opencode \
--tmpfs /tmp/node-compile-cache \
--bind ~/.local/state/opencode ~/.local/state/opencode \
--bind ~/.local/share/opencode ~/.local/share/opencode \
--bind "$(pwd)" "$(pwd)" \
opencode
end
+7
View File
@@ -0,0 +1,7 @@
function ssh
if type -q kitten
kitten ssh $argv
else
command ssh $argv
end
end
+6
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
function tuis --description 'fzf select tui'
eval (cat $HOME/.config/misc/tuis.txt | command fzf --multi)
end
+483
View File
@@ -0,0 +1,483 @@
# Config relating to the Lazygit UI
gui:
# The number of lines you scroll by when scrolling the main window
scrollHeight: 2
# If true, allow scrolling past the bottom of the content in the main window
scrollPastBottom: true
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#scroll-off-margin
scrollOffMargin: 2
# One of: 'margin' (default) | 'jump'
scrollOffBehavior: margin
# If true, capture mouse events.
# When mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.
mouseEvents: true
# If true, do not show a warning when discarding changes in the staging view.
skipDiscardChangeWarning: false
# If true, do not show warning when applying/popping the stash
skipStashWarning: false
# If true, do not show a warning when attempting to commit without any staged files; instead stage all unstaged files.
skipNoStagedFilesWarning: false
# If true, do not show a warning when rewording a commit via an external editor
skipRewordInEditorWarning: false
# Fraction of the total screen width to use for the left side section. You may want to pick a small number (e.g. 0.2) if you're using a narrow screen, so that you can see more of the main section.
# Number from 0 to 1.0.
sidePanelWidth: 0.3333
# If true, increase the height of the focused side window; creating an accordion effect.
expandFocusedSidePanel: true
# The weight of the expanded side panel, relative to the other panels. 2 means
# twice as tall as the other panels. Only relevant if `expandFocusedSidePanel` is true.
expandedSidePanelWeight: 3
# Sometimes the main window is split in two (e.g. when the selected file has both staged and unstaged changes). This setting controls how the two sections are split.
# Options are:
# - 'horizontal': split the window horizontally
# - 'vertical': split the window vertically
# - 'flexible': (default) split the window horizontally if the window is wide enough, otherwise split vertically
mainPanelSplitMode: flexible
# How the window is split when in half screen mode (i.e. after hitting '+' once).
# Possible values:
# - 'left': split the window horizontally (side panel on the left, main view on the right)
# - 'top': split the window vertically (side panel on top, main view below)
enlargedSideViewLocation: left
# If true, wrap lines in the staging view to the width of the view. This
# makes it much easier to work with diffs that have long lines, e.g.
# paragraphs of markdown text.
wrapLinesInStagingView: true
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
language: auto
# Format used when displaying time e.g. commit time.
# Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
timeFormat: 02 Jan 06
# Format used when displaying time if the time is less than 24 hours ago.
# Uses Go's time format syntax: https://pkg.go.dev/time#Time.Format
shortTimeFormat: 3:04PM
# Config relating to colors and styles.
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#color-attributes
theme:
# Border color of focused window
activeBorderColor:
- green
- bold
# Border color of non-focused windows
inactiveBorderColor:
- default
# Border color of focused window when searching in that window
searchingActiveBorderColor:
- cyan
- bold
# Color of keybindings help text in the bottom line
optionsTextColor:
- blue
# Background color of selected line.
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#highlighting-the-selected-line
selectedRangeBgColor:
- "#444444"
selectedLineBgColor:
- "#444444"
# Background color of selected line when view doesn't have focus.
inactiveViewSelectedLineBgColor:
- bold
# Foreground color of copied commit
cherryPickedCommitFgColor:
- blue
# Background color of copied commit
cherryPickedCommitBgColor:
- cyan
# Foreground color of marked base commit (for rebase)
markedBaseCommitFgColor:
- blue
# Background color of marked base commit (for rebase)
markedBaseCommitBgColor:
- yellow
# Color for file with unstaged changes
unstagedChangesColor:
- red
# Default text color
defaultFgColor:
- default
# Config relating to the commit length indicator
commitLength:
# If true, show an indicator of commit message length
show: true
# If true, show the '5 of 20' footer at the bottom of list views
showListFooter: true
# If true, display the files in the file views as a tree. If false, display the files as a flat list.
# This can be toggled from within Lazygit with the '~' key, but that will not change the default.
showFileTree: true
# If true, show the number of lines changed per file in the Files view
showNumstatInFilesView: false
# If true, show a random tip in the command log when Lazygit starts
showRandomTip: true
# If true, show the command log
showCommandLog: true
# If true, show the bottom line that contains keybinding info and useful buttons. If false, this line will be hidden except to display a loader for an in-progress action.
showBottomLine: true
# If true, show jump-to-window keybindings in window titles.
showPanelJumps: true
# Deprecated: use nerdFontsVersion instead
showIcons: false
# Nerd fonts version to use.
# One of: '2' | '3' | empty string (default)
# If empty, do not show icons.
nerdFontsVersion: ""
# If true (default), file icons are shown in the file views. Only relevant if NerdFontsVersion is not empty.
showFileIcons: true
# Length of author name in (non-expanded) commits view. 2 means show initials only.
commitAuthorShortLength: 2
# Length of author name in expanded commits view. 2 means show initials only.
commitAuthorLongLength: 17
# Length of commit hash in commits view. 0 shows '*' if NF icons aren't on.
commitHashLength: 8
# If true, show commit hashes alongside branch names in the branches view.
showBranchCommitHash: false
# Whether to show the divergence from the base branch in the branches view.
# One of: 'none' | 'onlyArrow' | 'arrowAndNumber'
showDivergenceFromBaseBranch: none
# Height of the command log view
commandLogSize: 8
# Whether to split the main window when viewing file changes.
# One of: 'auto' | 'always'
# If 'auto', only split the main window when a file has both staged and unstaged changes
splitDiff: auto
# Default size for focused window. Window size can be changed from within Lazygit with '+' and '_' (but this won't change the default).
# One of: 'normal' (default) | 'half' | 'full'
screenMode: normal
# Window border style.
# One of 'rounded' (default) | 'single' | 'double' | 'hidden'
border: rounded
# If true, show a seriously epic explosion animation when nuking the working tree.
animateExplosion: true
# Whether to stack UI components on top of each other.
# One of 'auto' (default) | 'always' | 'never'
portraitMode: auto
# How things are filtered when typing '/'.
# One of 'substring' (default) | 'fuzzy'
filterMode: substring
# Config relating to the spinner.
spinner:
# The frames of the spinner animation.
frames:
- '|'
- /
- '-'
- \
# The "speed" of the spinner in milliseconds.
rate: 50
# Status panel view.
# One of 'dashboard' (default) | 'allBranchesLog'
statusPanelView: dashboard
# If true, jump to the Files panel after popping a stash
switchToFilesAfterStashPop: true
# If true, jump to the Files panel after applying a stash
switchToFilesAfterStashApply: true
# If true, when using the panel jump keys (default 1 through 5) and target panel is already active, go to next tab instead
switchTabsWithPanelJumpKeys: false
# Config relating to git
git:
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
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
# 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
type: rawGit
# Config relating to committing
commit:
# If true, pass '--signoff' flag when committing
signOff: false
# Automatic WYSIWYG wrapping of the commit message as you type
autoWrapCommitMessage: true
# If autoWrapCommitMessage is true, the width to wrap to
autoWrapWidth: 72
# Config relating to merging
merging:
# If true, run merges in a subprocess so that if a commit message is required, Lazygit will not hang
# Only applicable to unix users.
manualCommit: false
# Extra args passed to `git merge`, e.g. --no-ff
args: ""
# The commit message to use for a squash merge commit. Can contain "{{selectedRef}}" and "{{currentBranch}}" placeholders.
squashMergeMessage: Squash merge {{selectedRef}} into {{currentBranch}}
# list of branches that are considered 'main' branches, used when displaying commits
mainBranches:
- master
- main
# Prefix to use when skipping hooks. E.g. if set to 'WIP', then pre-commit hooks will be skipped when the commit message starts with 'WIP'
skipHookPrefix: WIP
# If true, periodically fetch from remote
autoFetch: true
# If true, periodically refresh files and submodules
autoRefresh: true
# If true, pass the --all arg to git fetch
fetchAll: true
# If true, lazygit will automatically stage files that used to have merge
# conflicts but no longer do; and it will also ask you if you want to
# continue a merge or rebase if you've resolved all conflicts. If false, it
# won't do either of these things.
autoStageResolvedConflicts: true
# Command used when displaying the current branch git log in the main window
branchLogCmd: git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --
# If true, do not spawn a separate process when using GPG
overrideGpg: false
# If true, do not allow force pushes
disableForcePushing: false
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-commit-message-prefix
commitPrefix:
- # pattern to match on. E.g. for 'feature/AB-123' to match on the AB-123 use "^\\w+\\/(\\w+-\\w+).*"
pattern: ""
# Replace directive. E.g. for 'feature/AB-123' to start the commit message with 'AB-123 ' use "[$1] "
replace: ""
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#predefined-branch-name-prefix
branchPrefix: ""
# If true, parse emoji strings in commit messages e.g. render :rocket: as 🚀
# (This should really be under 'gui', not 'git')
parseEmoji: false
# Config for showing the log in the commits view
log:
# One of: 'date-order' | 'author-date-order' | 'topo-order' | 'default'
# 'topo-order' makes it easier to read the git log graph, but commits may not
# appear chronologically. See https://git-scm.com/docs/
#
# Deprecated: Configure this with `Log menu -> Commit sort order` (<c-l> in the commits window by default).
order: topo-order
# This determines whether the git graph is rendered in the commits panel
# One of 'always' | 'never' | 'when-maximised'
#
# Deprecated: Configure this with `Log menu -> Show git graph` (<c-l> in the commits window by default).
showGraph: always
# displays the whole git graph by default in the commits view (equivalent to passing the `--all` argument to `git log`)
showWholeGraph: false
# When copying commit hashes to the clipboard, truncate them to this
# length. Set to 40 to disable truncation.
truncateCopiedCommitHashesTo: 12
allBranchesLogCmds:
- git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium
# Periodic update checks
update:
# One of: 'prompt' (default) | 'background' | 'never'
method: prompt
# Period in days between update checks
days: 14
# Background refreshes
refresher:
# File/submodule refresh interval in seconds.
# Auto-refresh can be disabled via option 'git.autoRefresh'.
refreshInterval: 10
# Re-fetch interval in seconds.
# Auto-fetch can be disabled via option 'git.autoFetch'.
fetchInterval: 60
# If true, show a confirmation popup before quitting Lazygit
confirmOnQuit: false
# If true, exit Lazygit when the user presses escape in a context where there is nothing to cancel/close
quitOnTopLevelReturn: false
# Config relating to things outside of Lazygit like how files are opened, copying to clipboard, etc
os:
# Command for editing a file. Should contain "{{filename}}".
edit: ""
# Command for editing a file at a given line number. Should contain
# "{{filename}}", and may optionally contain "{{line}}".
editAtLine: ""
# Same as EditAtLine, except that the command needs to wait until the
# window is closed.
editAtLineAndWait: ""
# For opening a directory in an editor
openDirInEditor: ""
# A built-in preset that sets all of the above settings. Supported presets
# are defined in the getPreset function in editor_presets.go.
editPreset: ""
# Command for opening a file, as if the file is double-clicked. Should
# contain "{{filename}}", but doesn't support "{{line}}".
open: ""
# Command for opening a link. Should contain "{{link}}".
openLink: ""
# EditCommand is the command for editing a file.
# Deprecated: use Edit instead. Note that semantics are different:
# EditCommand is just the command itself, whereas Edit contains a
# "{{filename}}" variable.
editCommand: ""
# EditCommandTemplate is the command template for editing a file
# Deprecated: use EditAtLine instead.
editCommandTemplate: ""
# OpenCommand is the command for opening a file
# Deprecated: use Open instead.
openCommand: ""
# OpenLinkCommand is the command for opening a link
# Deprecated: use OpenLink instead.
openLinkCommand: ""
# CopyToClipboardCmd is the command for copying to clipboard.
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
copyToClipboardCmd: ""
# ReadFromClipboardCmd is the command for reading the clipboard.
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
readFromClipboardCmd: ""
# If true, don't display introductory popups upon opening Lazygit.
disableStartupPopups: false
# What to do when opening Lazygit outside of a git repo.
# - 'prompt': (default) ask whether to initialize a new repo or open in the most recent repo
# - 'create': initialize a new repo
# - 'skip': open most recent repo
# - 'quit': exit Lazygit
notARepository: prompt
# If true, display a confirmation when subprocess terminates. This allows you to view the output of the subprocess before returning to Lazygit.
promptToReturnFromSubprocess: true
# Keybindings
keybinding:
universal:
quit: q
quit-alt1: <c-c>
return: <esc>
quitWithoutChangingDirectory: Q
togglePanel: <tab>
prevItem: <up>
nextItem: <down>
prevItem-alt: k
nextItem-alt: j
prevPage: ','
nextPage: .
scrollLeft: H
scrollRight: L
gotoTop: <
gotoBottom: '>'
toggleRangeSelect: v
rangeSelectDown: <s-down>
rangeSelectUp: <s-up>
prevBlock: <left>
nextBlock: <right>
prevBlock-alt: h
nextBlock-alt: l
nextBlock-alt2: <tab>
prevBlock-alt2: <backtab>
jumpToBlock:
- "1"
- "2"
- "3"
- "4"
- "5"
nextMatch: "n"
prevMatch: "N"
startSearch: /
optionMenu: '?'
optionMenu-alt1: <disabled>
select: <space>
goInto: <enter>
confirm: <enter>
confirmInEditor: <a-enter>
remove: d
new: "n"
edit: e
openFile: o
scrollUpMain: <pgup>
scrollDownMain: <pgdown>
scrollUpMain-alt1: K
scrollDownMain-alt1: J
scrollUpMain-alt2: <c-u>
scrollDownMain-alt2: <c-d>
executeShellCommand: ':'
createRebaseOptionsMenu: m
# 'Files' appended for legacy reasons
pushFiles: P
# 'Files' appended for legacy reasons
pullFiles: p
refresh: R
createPatchOptionsMenu: <c-p>
nextTab: ']'
prevTab: '['
nextScreenMode: +
prevScreenMode: _
undo: z
redo: <c-z>
filteringMenu: <c-s>
diffingMenu: W
diffingMenu-alt: <c-e>
copyToClipboard: <c-o>
openRecentRepos: <c-r>
submitEditorText: <enter>
extrasMenu: '@'
toggleWhitespaceInDiffView: <c-w>
increaseContextInDiffView: '}'
decreaseContextInDiffView: '{'
increaseRenameSimilarityThreshold: )
decreaseRenameSimilarityThreshold: (
openDiffTool: <c-t>
newWorktree: w
status:
checkForUpdate: u
recentRepos: <enter>
allBranchesLogGraph: a
files:
commitChanges: c
commitChangesWithoutHook: w
amendLastCommit: A
commitChangesWithEditor: C
findBaseCommitForFixup: <c-f>
confirmDiscard: x
ignoreFile: i
refreshFiles: r
stashAllChanges: s
viewStashOptions: S
toggleStagedAll: a
viewResetOptions: D
fetch: f
toggleTreeView: '`'
openMergeOptions: M
openStatusFilter: <c-b>
copyFileInfoToClipboard: "y"
branches:
createPullRequest: o
viewPullRequestOptions: O
copyPullRequestURL: <c-y>
checkoutBranchByName: c
forceCheckoutBranch: F
rebaseBranch: r
renameBranch: R
mergeIntoCurrentBranch: M
viewGitFlowOptions: i
fastForward: f
createTag: T
pushTag: P
setUpstream: u
fetchRemote: f
sortOrder: s
commits:
squashDown: s
renameCommit: r
renameCommitWithEditor: R
viewResetOptions: g
markCommitAsFixup: f
createFixupCommit: F
squashAboveCommits: S
moveDownCommit: <c-j>
moveUpCommit: <c-k>
amendToCommit: A
resetCommitAuthor: a
pickCommit: p
revertCommit: t
cherryPickCopy: C
pasteCommits: V
markCommitAsBaseForRebase: B
tagCommit: T
checkoutCommit: <space>
# resetCherryPick: <c-R>
copyCommitAttributeToClipboard: "y"
openLogMenu: <c-l>
openInBrowser: o
viewBisectOptions: b
startInteractiveRebase: i
amendAttribute:
resetAuthor: a
setAuthor: A
addCoAuthor: c
stash:
popStash: g
renameStash: r
commitFiles:
checkoutCommitFile: c
main:
toggleSelectHunk: a
pickBothHunks: b
editSelectHunk: E
submodules:
init: i
update: u
bulkMenu: b
commitMessage:
commitMenu: <c-o>
+3
View File
@@ -0,0 +1,3 @@
om
openWebUI
itTools
+36
View File
@@ -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
+72
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
column_width = 120
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferDouble"
call_parentheses = "None"
+98
View File
@@ -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 <language>` 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) |
| `<leader>da` | `vim.lsp.buf.code_action()` | Code actions |
| `<leader>dh` | `vim.diagnostic.open_float()` | Show diagnostics in float |
| `<leader>dj` | `vim.diagnostic.jump` | Go to next diagnostic |
| `<leader>dk` | `vim.diagnostic.jump` | Go to previous diagnostic |
| `<leader>fr` | `Telescope resume` | Resume last Telescope search |
| `<leader>fs` | `Telescope grep_string` | Find selected string |
| `<leader>fk` | `Telescope keymaps` | Find keymaps |
| `<leader>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 <server_name>` (or use the `:Mason` UI) to install the language servers required for your workflow.
5. Restart Neovim after the initial installation completes.
+38
View File
@@ -0,0 +1,38 @@
vim.g.base46_cache = vim.fn.stdpath "data" .. "/base46/"
vim.g.mapleader = " "
vim.opt.relativenumber = true
-- bootstrap lazy and all plugins
local lazypath = vim.fn.stdpath "data" .. "/lazy/lazy.nvim"
if not vim.uv.fs_stat(lazypath) then
local repo = "https://github.com/folke/lazy.nvim.git"
vim.fn.system { "git", "clone", "--filter=blob:none", repo, "--branch=stable", lazypath }
end
vim.opt.rtp:prepend(lazypath)
local lazy_config = require "configs.lazy"
-- load plugins
require("lazy").setup({
{
"NvChad/NvChad",
lazy = false,
branch = "v2.5",
import = "nvchad.plugins",
},
{ import = "plugins" },
}, lazy_config)
-- load theme
dofile(vim.g.base46_cache .. "defaults")
dofile(vim.g.base46_cache .. "statusline")
require "options"
require "nvchad.autocmds"
vim.schedule(function()
require "mappings"
end)
+31
View File
@@ -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" }
}
+57
View File
@@ -0,0 +1,57 @@
-- This file needs to have same structure as nvconfig.lua
-- https://github.com/NvChad/ui/blob/v3.0/lua/nvconfig.lua
-- Please read that file to know all available options :(
---@type ChadrcConfig
local M = {
lsp = { signature = true },
base46 = {
theme = "onedark",
transparency = true,
hl_override = {
NvimTreeGitDirty = {
fg = "#DBA55D",
},
},
-- Comment = { italic = true },
-- ["@comment"] = { italic = true },
},
nvdash = {
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" },
-- -- more... check nvconfig.lua file for full list of buttons
-- },
},
ui = {
-- lazyload it when there are 1+ buffers
tabufline = {
enabled = true,
lazyload = true,
order = { "treeOffset", "buffers", "tabs", "btns" },
modules = nil,
bufwidth = 21,
},
},
term = {
base46_colors = true,
winopts = { number = false },
sizes = { sp = 0.5, vsp = 0.2, ["bo sp"] = 0.3, ["bo vsp"] = 0.2 },
float = {
relative = "editor",
row = 0.05,
col = 0.05,
width = 0.9,
height = 0.8,
border = "single",
},
},
}
return M
@@ -0,0 +1,17 @@
local options = {
formatters_by_ft = {
lua = { "stylua" },
css = { "prettier" },
html = { "prettier" },
typescript = { "prettier" },
javascript = { "prettier" },
},
format_on_save = {
-- These options will be passed to conform.format()
timeout_ms = 500,
lsp_format = "fallback",
},
}
return options
+47
View File
@@ -0,0 +1,47 @@
return {
defaults = { lazy = true },
install = { colorscheme = { "nvchad" } },
ui = {
icons = {
ft = "",
lazy = "󰂠 ",
loaded = "",
not_loaded = "",
},
},
performance = {
rtp = {
disabled_plugins = {
"2html_plugin",
"tohtml",
"getscript",
"getscriptPlugin",
"gzip",
"logipat",
"netrw",
"netrwPlugin",
"netrwSettings",
"netrwFileHandlers",
"matchit",
"tar",
"tarPlugin",
"rrhelper",
"spellfile_plugin",
"vimball",
"vimballPlugin",
"zip",
"zipPlugin",
"tutor",
"rplugin",
"syntax",
"synmenu",
"optwin",
"compiler",
"bugreport",
"ftplugin",
},
},
},
}
@@ -0,0 +1,28 @@
require("nvchad.configs.lspconfig").defaults()
local servers = { "html", "cssls", "ts_ls", "eslint" }
vim.lsp.enable(servers)
-- local lspconfig = require "lspconfig"
-- local nvlsp = require "nvchad.configs.lspconfig"
--
-- -- lsps with default config
-- for _, lsp in ipairs(servers) do
-- lspconfig[lsp].setup {
-- on_attach = nvlsp.on_attach,
-- on_init = nvlsp.on_init,
-- capabilities = nvlsp.capabilities,
-- }
-- end
--
-- -- configuring single server, example: typescript
-- lspconfig.ts_ls.setup {
-- filetypes = { 'typescript', 'javascript', 'javascriptreact', 'typescriptreact', 'vue', 'markdown' },
-- }
--
-- -- configuring single server, example: typescript
-- -- lspconfig.ts_ls.setup {
-- -- on_attach = nvlsp.on_attach,
-- -- on_init = nvlsp.on_init,
-- -- capabilities = nvlsp.capabilities,
-- -- }
@@ -0,0 +1,851 @@
return {
-- Whether markdown should be rendered by default.
enabled = true,
-- Vim modes that will show a rendered view of the markdown file, :h mode(), for all enabled
-- components. Individual components can be enabled for other modes. Remaining modes will be
-- unaffected by this plugin.
render_modes = { "i", "n", "c", "t" },
-- Maximum file size (in MB) that this plugin will attempt to render.
-- Any file larger than this will effectively be ignored.
max_file_size = 10.0,
-- Milliseconds that must pass before updating marks, updates occur.
-- within the context of the visible window, not the entire buffer.
debounce = 100,
-- Pre configured settings that will attempt to mimic various target user experiences.
-- Any user provided settings will take precedence.
-- | obsidian | mimic Obsidian UI |
-- | lazy | will attempt to stay up to date with LazyVim configuration |
-- | none | does nothing |
preset = "none",
-- The level of logs to write to file: vim.fn.stdpath('state') .. '/render-markdown.log'.
-- Only intended to be used for plugin development / debugging.
log_level = "error",
-- Print runtime of main update method.
-- Only intended to be used for plugin development / debugging.
log_runtime = false,
-- Filetypes this plugin will run on.
file_types = { "markdown", "gitcommit" },
-- Takes buffer as input, if it returns true this plugin will not attach to the buffer
ignore = function()
return false
end,
-- Additional events that will trigger this plugin's render loop.
change_events = {},
injections = {
-- Out of the box language injections for known filetypes that allow markdown to be interpreted
-- in specified locations, see :h treesitter-language-injections.
-- Set enabled to false in order to disable.
gitcommit = {
enabled = true,
query = [[
((message) @injection.content
(#set! injection.combined)
(#set! injection.include-children)
(#set! injection.language "markdown"))
]],
},
},
patterns = {
-- Highlight patterns to disable for filetypes, i.e. lines concealed around code blocks
markdown = {
disable = true,
directives = {
{ id = 17, name = "conceal_lines" },
{ id = 18, name = "conceal_lines" },
},
},
},
anti_conceal = {
-- This enables hiding any added text on the line the cursor is on.
enabled = true,
-- Modes to disable anti conceal feature.
disabled_modes = false,
-- Number of lines above cursor to show.
above = 0,
-- Number of lines below cursor to show.
below = 0,
-- Which elements to always show, ignoring anti conceal behavior. Values can either be
-- booleans to fix the behavior or string lists representing modes where anti conceal
-- behavior will be ignored. Valid values are:
-- bullet
-- callout
-- check_icon, check_scope
-- code_background, code_border, code_language
-- dash
-- head_background, head_border, head_icon
-- indent
-- link
-- quote
-- sign
-- table_border
-- virtual_lines
ignore = {
code_background = true,
indent = true,
sign = true,
virtual_lines = true,
},
},
padding = {
-- Highlight to use when adding whitespace, should match background.
highlight = "Normal",
},
latex = {
-- Turn on / off latex rendering.
enabled = true,
-- Additional modes to render latex.
render_modes = false,
-- Executable used to convert latex formula to rendered unicode.
converter = "latex2text",
-- Highlight for latex blocks.
highlight = "RenderMarkdownMath",
-- Determines where latex formula is rendered relative to block.
-- | above | above latex block |
-- | below | below latex block |
position = "above",
-- Number of empty lines above latex blocks.
top_pad = 0,
-- Number of empty lines below latex blocks.
bottom_pad = 0,
},
on = {
-- Called when plugin initially attaches to a buffer.
attach = function() end,
-- Called before adding marks to the buffer for the first time.
initial = function() end,
-- Called after plugin renders a buffer.
render = function() end,
-- Called after plugin clears a buffer.
clear = function() end,
},
completions = {
-- Settings for blink.cmp completions source
blink = { enabled = false },
-- Settings for coq_nvim completions source
coq = { enabled = false },
-- Settings for in-process language server completions
lsp = { enabled = false },
filter = {
callout = function()
-- example to exclude obsidian callouts
-- return value.category ~= 'obsidian'
return true
end,
checkbox = function()
return true
end,
},
},
heading = {
-- Useful context to have when evaluating values.
-- | level | the number of '#' in the heading marker |
-- | sections | for each level how deeply nested the heading is |
-- Turn on / off heading icon & background rendering.
enabled = true,
-- Additional modes to render headings.
render_modes = false,
-- Turn on / off atx heading rendering.
atx = true,
-- Turn on / off setext heading rendering.
setext = true,
-- Turn on / off any sign column related rendering.
sign = true,
-- Replaces '#+' of 'atx_h._marker'.
-- Output is evaluated depending on the type.
-- | function | `value(context)` |
-- | string[] | `cycle(value, context.level)` |
icons = { "󰲡 ", "󰲣 ", "󰲥 ", "󰲧 ", "󰲩 ", "󰲫 " },
-- Determines how icons fill the available space.
-- | right | '#'s are concealed and icon is appended to right side |
-- | inline | '#'s are concealed and icon is inlined on left side |
-- | overlay | icon is left padded with spaces and inserted on left hiding any additional '#' |
position = "overlay",
-- Added to the sign column if enabled.
-- Output is evaluated by `cycle(value, context.level)`.
signs = { "󰫎 " },
-- Width of the heading background.
-- | block | width of the heading text |
-- | full | full width of the window |
-- Can also be a list of the above values evaluated by `clamp(value, context.level)`.
width = "full",
-- Amount of margin to add to the left of headings.
-- Margin available space is computed after accounting for padding.
-- If a float < 1 is provided it is treated as a percentage of available window space.
-- Can also be a list of numbers evaluated by `clamp(value, context.level)`.
left_margin = 0,
-- Amount of padding to add to the left of headings.
-- Output is evaluated using the same logic as 'left_margin'.
left_pad = 0,
-- Amount of padding to add to the right of headings when width is 'block'.
-- Output is evaluated using the same logic as 'left_margin'.
right_pad = 0,
-- Minimum width to use for headings when width is 'block'.
-- Can also be a list of integers evaluated by `clamp(value, context.level)`.
min_width = 0,
-- Determines if a border is added above and below headings.
-- Can also be a list of booleans evaluated by `clamp(value, context.level)`.
border = false,
-- Always use virtual lines for heading borders instead of attempting to use empty lines.
border_virtual = false,
-- Highlight the start of the border using the foreground highlight.
border_prefix = false,
-- Used above heading for border.
above = "",
-- Used below heading for border.
below = "",
-- Highlight for the heading icon and extends through the entire line.
-- Output is evaluated by `clamp(value, context.level)`.
backgrounds = {
"RenderMarkdownH1Bg",
"RenderMarkdownH2Bg",
"RenderMarkdownH3Bg",
"RenderMarkdownH4Bg",
"RenderMarkdownH5Bg",
"RenderMarkdownH6Bg",
},
-- Highlight for the heading and sign icons.
-- Output is evaluated using the same logic as 'backgrounds'.
foregrounds = {
"RenderMarkdownH1",
"RenderMarkdownH2",
"RenderMarkdownH3",
"RenderMarkdownH4",
"RenderMarkdownH5",
"RenderMarkdownH6",
},
-- Define custom heading patterns which allow you to override various properties based on
-- the contents of a heading.
-- The key is for healthcheck and to allow users to change its values, value type below.
-- | pattern | matched against the heading text @see :h lua-patterns |
-- | icon | optional override for the icon |
-- | background | optional override for the background |
-- | foreground | optional override for the foreground |
custom = {},
},
paragraph = {
-- Useful context to have when evaluating values.
-- | text | text value of the node |
-- Turn on / off paragraph rendering.
enabled = true,
-- Additional modes to render paragraphs.
render_modes = false,
-- Amount of margin to add to the left of paragraphs.
-- If a float < 1 is provided it is treated as a percentage of available window space.
-- Output is evaluated depending on the type.
-- | function | `value(context)` |
-- | number | `value` |
left_margin = 0,
-- Amount of padding to add to the first line of each paragraph.
-- Output is evaluated using the same logic as 'left_margin'.
indent = 0,
-- Minimum width to use for paragraphs.
min_width = 0,
},
code = {
-- Turn on / off code block & inline code rendering.
enabled = true,
-- Additional modes to render code blocks.
render_modes = false,
-- Turn on / off any sign column related rendering.
sign = true,
-- Determines how code blocks & inline code are rendered.
-- | none | disables all rendering |
-- | normal | background highlighting + padding |
-- | language | language heading with icon + sign column |
-- | full | normal + language |
style = "full",
-- Whether to conceal nodes at the top and bottom of code blocks.
conceal_delimiters = true,
-- Turn on / off any language heading related rendering.
language = true,
-- Determines where language icon is rendered.
-- | right | right side of code block |
-- | left | left side of code block |
position = "left",
-- Whether to include the language icon above code blocks.
language_icon = true,
-- Whether to include the language name above code blocks.
language_name = true,
-- Whether to include the language info above code blocks.
language_info = true,
-- Amount of padding to add around the language.
-- If a float < 1 is provided it is treated as a percentage of available window space.
language_pad = 0,
-- A list of language names for which background highlighting will be disabled.
-- Likely because that language has background highlights itself.
-- Use a boolean to make behavior apply to all languages.
-- Borders above & below blocks will continue to be rendered.
disable_background = { "diff" },
-- Width of the code block background.
-- | block | width of the code block |
-- | full | full width of the window |
width = "full",
-- Amount of margin to add to the left of code blocks.
-- If a float < 1 is provided it is treated as a percentage of available window space.
-- Margin available space is computed after accounting for padding.
left_margin = 0,
-- Amount of padding to add to the left of code blocks.
-- If a float < 1 is provided it is treated as a percentage of available window space.
left_pad = 0,
-- Amount of padding to add to the right of code blocks when width is 'block'.
-- If a float < 1 is provided it is treated as a percentage of available window space.
right_pad = 0,
-- Minimum width to use for code blocks when width is 'block'.
min_width = 0,
-- Determines how the top / bottom of code block are rendered.
-- | none | do not render a border |
-- | thick | use the same highlight as the code body |
-- | thin | when lines are empty overlay the above & below icons |
-- | hide | conceal lines unless language name or icon is added |
border = "hide",
-- Used above code blocks to fill remaining space around language.
language_border = "",
-- Added to the left of language.
language_left = "",
-- Added to the right of language.
language_right = "",
-- Used above code blocks for thin border.
above = "",
-- Used below code blocks for thin border.
below = "",
-- Icon to add to the left of inline code.
inline_left = "",
-- Icon to add to the right of inline code.
inline_right = "",
-- Padding to add to the left & right of inline code.
inline_pad = 0,
-- Highlight for code blocks.
highlight = "RenderMarkdownCode",
-- Highlight for code info section, after the language.
highlight_info = "RenderMarkdownCodeInfo",
-- Highlight for language, overrides icon provider value.
highlight_language = nil,
-- Highlight for border, use false to add no highlight.
highlight_border = "RenderMarkdownCodeBorder",
-- Highlight for language, used if icon provider does not have a value.
highlight_fallback = "RenderMarkdownCodeFallback",
-- Highlight for inline code.
highlight_inline = "RenderMarkdownCodeInline",
},
dash = {
-- Turn on / off thematic break rendering.
enabled = true,
-- Additional modes to render dash.
render_modes = false,
-- Replaces '---'|'***'|'___'|'* * *' of 'thematic_break'.
-- The icon gets repeated across the window's width.
icon = "",
-- Width of the generated line.
-- | <number> | a hard coded width value |
-- | full | full width of the window |
-- If a float < 1 is provided it is treated as a percentage of available window space.
width = "full",
-- Amount of margin to add to the left of dash.
-- If a float < 1 is provided it is treated as a percentage of available window space.
left_margin = 0,
-- Highlight for the whole line generated from the icon.
highlight = "RenderMarkdownDash",
},
document = {
-- Turn on / off document rendering.
enabled = true,
-- Additional modes to render document.
render_modes = false,
-- Ability to conceal arbitrary ranges of text based on lua patterns, @see :h lua-patterns.
-- Relies entirely on user to set patterns that handle their edge cases.
conceal = {
-- Matched ranges will be concealed using character level conceal.
char_patterns = {},
-- Matched ranges will be concealed using line level conceal.
line_patterns = {},
},
},
bullet = {
-- Useful context to have when evaluating values.
-- | level | how deeply nested the list is, 1-indexed |
-- | index | how far down the item is at that level, 1-indexed |
-- | value | text value of the marker node |
-- Turn on / off list bullet rendering
enabled = true,
-- Additional modes to render list bullets
render_modes = false,
-- Replaces '-'|'+'|'*' of 'list_item'.
-- If the item is a 'checkbox' a conceal is used to hide the bullet instead.
-- Output is evaluated depending on the type.
-- | function | `value(context)` |
-- | string | `value` |
-- | string[] | `cycle(value, context.level)` |
-- | string[][] | `clamp(cycle(value, context.level), context.index)` |
icons = { "", "", "", "" },
-- Replaces 'n.'|'n)' of 'list_item'.
-- Output is evaluated using the same logic as 'icons'.
ordered_icons = function(ctx)
local value = vim.trim(ctx.value)
local index = tonumber(value:sub(1, #value - 1))
return ("%d."):format(index > 1 and index or ctx.index)
end,
-- Padding to add to the left of bullet point.
-- Output is evaluated depending on the type.
-- | function | `value(context)` |
-- | integer | `value` |
left_pad = 0,
-- Padding to add to the right of bullet point.
-- Output is evaluated using the same logic as 'left_pad'.
right_pad = 0,
-- Highlight for the bullet icon.
-- Output is evaluated using the same logic as 'icons'.
highlight = "RenderMarkdownBullet",
-- Highlight for item associated with the bullet point.
-- Output is evaluated using the same logic as 'icons'.
scope_highlight = {},
},
checkbox = {
-- Checkboxes are a special instance of a 'list_item' that start with a 'shortcut_link'.
-- There are two special states for unchecked & checked defined in the markdown grammar.
-- Turn on / off checkbox state rendering.
enabled = true,
-- Additional modes to render checkboxes.
render_modes = false,
-- Render the bullet point before the checkbox.
bullet = false,
-- Padding to add to the right of checkboxes.
right_pad = 1,
unchecked = {
-- Replaces '[ ]' of 'task_list_marker_unchecked'.
icon = "󰄱 ",
-- Highlight for the unchecked icon.
highlight = "RenderMarkdownUnchecked",
-- Highlight for item associated with unchecked checkbox.
scope_highlight = nil,
},
checked = {
-- Replaces '[x]' of 'task_list_marker_checked'.
icon = "󰱒 ",
-- Highlight for the checked icon.
highlight = "RenderMarkdownChecked",
-- Highlight for item associated with checked checkbox.
scope_highlight = nil,
},
-- Define custom checkbox states, more involved, not part of the markdown grammar.
-- As a result this requires neovim >= 0.10.0 since it relies on 'inline' extmarks.
-- The key is for healthcheck and to allow users to change its values, value type below.
-- | raw | matched against the raw text of a 'shortcut_link' |
-- | rendered | replaces the 'raw' value when rendering |
-- | highlight | highlight for the 'rendered' icon |
-- | scope_highlight | optional highlight for item associated with custom checkbox |
-- stylua: ignore
custom = {
todo = { raw = '[-]', rendered = '󰥔 ', highlight = 'RenderMarkdownTodo', scope_highlight = nil },
},
},
quote = {
-- Turn on / off block quote & callout rendering.
enabled = true,
-- Additional modes to render quotes.
render_modes = false,
-- Replaces '>' of 'block_quote'.
icon = "",
-- Whether to repeat icon on wrapped lines. Requires neovim >= 0.10. This will obscure text
-- if incorrectly configured with :h 'showbreak', :h 'breakindent' and :h 'breakindentopt'.
-- A combination of these that is likely to work follows.
-- | showbreak | ' ' (2 spaces) |
-- | breakindent | true |
-- | breakindentopt | '' (empty string) |
-- These are not validated by this plugin. If you want to avoid adding these to your main
-- configuration then set them in win_options for this plugin.
repeat_linebreak = false,
-- Highlight for the quote icon.
-- If a list is provided output is evaluated by `cycle(value, level)`.
highlight = {
"RenderMarkdownQuote1",
"RenderMarkdownQuote2",
"RenderMarkdownQuote3",
"RenderMarkdownQuote4",
"RenderMarkdownQuote5",
"RenderMarkdownQuote6",
},
},
pipe_table = {
-- Turn on / off pipe table rendering.
enabled = true,
-- Additional modes to render pipe tables.
render_modes = false,
-- Pre configured settings largely for setting table border easier.
-- | heavy | use thicker border characters |
-- | double | use double line border characters |
-- | round | use round border corners |
-- | none | does nothing |
preset = "none",
-- Determines how the table as a whole is rendered.
-- | none | disables all rendering |
-- | normal | applies the 'cell' style rendering to each row of the table |
-- | full | normal + a top & bottom line that fill out the table when lengths match |
style = "full",
-- Determines how individual cells of a table are rendered.
-- | overlay | writes completely over the table, removing conceal behavior and highlights |
-- | raw | replaces only the '|' characters in each row, leaving the cells unmodified |
-- | padded | raw + cells are padded to maximum visual width for each column |
-- | trimmed | padded except empty space is subtracted from visual width calculation |
cell = "padded",
-- Amount of space to put between cell contents and border.
padding = 1,
-- Minimum column width to use for padded or trimmed cell.
min_width = 0,
-- Characters used to replace table border.
-- Correspond to top(3), delimiter(3), bottom(3), vertical, & horizontal.
-- stylua: ignore
border = {
'', '', '',
'', '', '',
'', '', '',
'', '',
},
-- Always use virtual lines for table borders instead of attempting to use empty lines.
-- Will be automatically enabled if indentation module is enabled.
border_virtual = false,
-- Gets placed in delimiter row for each column, position is based on alignment.
alignment_indicator = "",
-- Highlight for table heading, delimiter, and the line above.
head = "RenderMarkdownTableHead",
-- Highlight for everything else, main table rows and the line below.
row = "RenderMarkdownTableRow",
-- Highlight for inline padding used to add back concealed space.
filler = "RenderMarkdownTableFill",
},
callout = {
-- Callouts are a special instance of a 'block_quote' that start with a 'shortcut_link'.
-- The key is for healthcheck and to allow users to change its values, value type below.
-- | raw | matched against the raw text of a 'shortcut_link', case insensitive |
-- | rendered | replaces the 'raw' value when rendering |
-- | highlight | highlight for the 'rendered' text and quote markers |
-- | quote_icon | optional override for quote.icon value for individual callout |
-- | category | optional metadata useful for filtering |
note = {
raw = "[!NOTE]",
rendered = "󰋽 Note",
highlight = "RenderMarkdownInfo",
category = "github",
},
tip = {
raw = "[!TIP]",
rendered = "󰌶 Tip",
highlight = "RenderMarkdownSuccess",
category = "github",
},
important = {
raw = "[!IMPORTANT]",
rendered = "󰅾 Important",
highlight = "RenderMarkdownHint",
category = "github",
},
warning = {
raw = "[!WARNING]",
rendered = "󰀪 Warning",
highlight = "RenderMarkdownWarn",
category = "github",
},
caution = {
raw = "[!CAUTION]",
rendered = "󰳦 Caution",
highlight = "RenderMarkdownError",
category = "github",
},
-- Obsidian: https://help.obsidian.md/Editing+and+formatting/Callouts
abstract = {
raw = "[!ABSTRACT]",
rendered = "󰨸 Abstract",
highlight = "RenderMarkdownInfo",
category = "obsidian",
},
summary = {
raw = "[!SUMMARY]",
rendered = "󰨸 Summary",
highlight = "RenderMarkdownInfo",
category = "obsidian",
},
tldr = {
raw = "[!TLDR]",
rendered = "󰨸 Tldr",
highlight = "RenderMarkdownInfo",
category = "obsidian",
},
info = {
raw = "[!INFO]",
rendered = "󰋽 Info",
highlight = "RenderMarkdownInfo",
category = "obsidian",
},
todo = {
raw = "[!TODO]",
rendered = "󰗡 Todo",
highlight = "RenderMarkdownInfo",
category = "obsidian",
},
hint = {
raw = "[!HINT]",
rendered = "󰌶 Hint",
highlight = "RenderMarkdownSuccess",
category = "obsidian",
},
success = {
raw = "[!SUCCESS]",
rendered = "󰄬 Success",
highlight = "RenderMarkdownSuccess",
category = "obsidian",
},
check = {
raw = "[!CHECK]",
rendered = "󰄬 Check",
highlight = "RenderMarkdownSuccess",
category = "obsidian",
},
done = {
raw = "[!DONE]",
rendered = "󰄬 Done",
highlight = "RenderMarkdownSuccess",
category = "obsidian",
},
question = {
raw = "[!QUESTION]",
rendered = "󰘥 Question",
highlight = "RenderMarkdownWarn",
category = "obsidian",
},
help = {
raw = "[!HELP]",
rendered = "󰘥 Help",
highlight = "RenderMarkdownWarn",
category = "obsidian",
},
faq = {
raw = "[!FAQ]",
rendered = "󰘥 Faq",
highlight = "RenderMarkdownWarn",
category = "obsidian",
},
attention = {
raw = "[!ATTENTION]",
rendered = "󰀪 Attention",
highlight = "RenderMarkdownWarn",
category = "obsidian",
},
failure = {
raw = "[!FAILURE]",
rendered = "󰅖 Failure",
highlight = "RenderMarkdownError",
category = "obsidian",
},
fail = {
raw = "[!FAIL]",
rendered = "󰅖 Fail",
highlight = "RenderMarkdownError",
category = "obsidian",
},
missing = {
raw = "[!MISSING]",
rendered = "󰅖 Missing",
highlight = "RenderMarkdownError",
category = "obsidian",
},
danger = {
raw = "[!DANGER]",
rendered = "󱐌 Danger",
highlight = "RenderMarkdownError",
category = "obsidian",
},
error = {
raw = "[!ERROR]",
rendered = "󱐌 Error",
highlight = "RenderMarkdownError",
category = "obsidian",
},
bug = {
raw = "[!BUG]",
rendered = "󰨰 Bug",
highlight = "RenderMarkdownError",
category = "obsidian",
},
example = {
raw = "[!EXAMPLE]",
rendered = "󰉹 Example",
highlight = "RenderMarkdownHint",
category = "obsidian",
},
quote = {
raw = "[!QUOTE]",
rendered = "󱆨 Quote",
highlight = "RenderMarkdownQuote",
category = "obsidian",
},
cite = {
raw = "[!CITE]",
rendered = "󱆨 Cite",
highlight = "RenderMarkdownQuote",
category = "obsidian",
},
},
link = {
-- Turn on / off inline link icon rendering.
enabled = true,
-- Additional modes to render links.
render_modes = false,
-- How to handle footnote links, start with a '^'.
footnote = {
-- Turn on / off footnote rendering.
enabled = true,
-- Replace value with superscript equivalent.
superscript = true,
-- Added before link content.
prefix = "",
-- Added after link content.
suffix = "",
},
-- Inlined with 'image' elements.
image = "󰥶 ",
-- Inlined with 'email_autolink' elements.
email = "󰀓 ",
-- Fallback icon for 'inline_link' and 'uri_autolink' elements.
hyperlink = "󰌹 ",
-- Applies to the inlined icon as a fallback.
highlight = "RenderMarkdownLink",
-- Applies to WikiLink elements.
wiki = {
icon = "󱗖 ",
body = function()
return nil
end,
highlight = "RenderMarkdownWikiLink",
},
-- Define custom destination patterns so icons can quickly inform you of what a link
-- contains. Applies to 'inline_link', 'uri_autolink', and wikilink nodes. When multiple
-- patterns match a link the one with the longer pattern is used.
-- The key is for healthcheck and to allow users to change its values, value type below.
-- | pattern | matched against the destination text |
-- | icon | gets inlined before the link text |
-- | kind | optional determines how pattern is checked |
-- | | pattern | @see :h lua-patterns, is the default if not set |
-- | | suffix | @see :h vim.endswith() |
-- | priority | optional used when multiple match, uses pattern length if empty |
-- | highlight | optional highlight for 'icon', uses fallback highlight if empty |
custom = {
web = { pattern = "^http", icon = "󰖟 " },
discord = { pattern = "discord%.com", icon = "󰙯 " },
github = { pattern = "github%.com", icon = "󰊤 " },
gitlab = { pattern = "gitlab%.com", icon = "󰮠 " },
google = { pattern = "google%.com", icon = "󰊭 " },
neovim = { pattern = "neovim%.io", icon = "" },
reddit = { pattern = "reddit%.com", icon = "󰑍 " },
stackoverflow = { pattern = "stackoverflow%.com", icon = "󰓌 " },
wikipedia = { pattern = "wikipedia%.org", icon = "󰖬 " },
youtube = { pattern = "youtube%.com", icon = "󰗃 " },
},
},
sign = {
-- Turn on / off sign rendering.
enabled = true,
-- Applies to background of sign text.
highlight = "RenderMarkdownSign",
},
inline_highlight = {
-- Mimics Obsidian inline highlights when content is surrounded by double equals.
-- The equals on both ends are concealed and the inner content is highlighted.
-- Turn on / off inline highlight rendering.
enabled = true,
-- Additional modes to render inline highlights.
render_modes = false,
-- Applies to background of surrounded text.
highlight = "RenderMarkdownInlineHighlight",
},
indent = {
-- Mimic org-indent-mode behavior by indenting everything under a heading based on the
-- level of the heading. Indenting starts from level 2 headings onward by default.
-- Turn on / off org-indent-mode.
enabled = false,
-- Additional modes to render indents.
render_modes = false,
-- Amount of additional padding added for each heading level.
per_level = 2,
-- Heading levels <= this value will not be indented.
-- Use 0 to begin indenting from the very first level.
skip_level = 1,
-- Do not indent heading titles, only the body.
skip_heading = false,
-- Prefix added when indenting, one per level.
icon = "",
-- Priority to assign to extmarks.
priority = 0,
-- Applied to icon.
highlight = "RenderMarkdownIndent",
},
html = {
-- Turn on / off all HTML rendering.
enabled = true,
-- Additional modes to render HTML.
render_modes = false,
comment = {
-- Turn on / off HTML comment concealing.
conceal = true,
-- Optional text to inline before the concealed comment.
text = nil,
-- Highlight for the inlined text.
highlight = "RenderMarkdownHtmlComment",
},
-- HTML tags whose start and end will be hidden and icon shown.
-- The key is matched against the tag name, value type below.
-- | icon | gets inlined at the start |
-- | highlight | highlight for the icon |
tag = {},
},
win_options = {
-- Window options to use that change between rendered and raw view.
-- @see :h 'conceallevel'
conceallevel = {
-- Used when not being rendered, get user setting.
default = vim.o.conceallevel,
-- Used when being rendered, concealed text is completely hidden.
rendered = 3,
},
-- @see :h 'concealcursor'
concealcursor = {
-- Used when not being rendered, get user setting.
default = vim.o.concealcursor,
-- Used when being rendered, show concealed text in all modes.
rendered = "",
},
},
overrides = {
-- More granular configuration mechanism, allows different aspects of buffers to have their own
-- behavior. Values default to the top level configuration if no override is provided. Supports
-- the following fields:
-- enabled, max_file_size, debounce, render_modes, anti_conceal, padding, heading, paragraph,
-- code, dash, bullet, checkbox, quote, pipe_table, callout, link, sign, indent, latex, html,
-- win_options
-- Override for different buflisted values, @see :h 'buflisted'.
buflisted = {},
-- Override for different buftype values, @see :h 'buftype'.
buftype = {
nofile = {
render_modes = true,
padding = { highlight = "NormalFloat" },
sign = { enabled = false },
},
},
-- Override for different filetype values, @see :h 'filetype'.
filetype = {},
},
custom_handlers = {
-- Mapping from treesitter language to user defined handlers.
-- @see [Custom Handlers](doc/custom-handlers.md)
},
}
+25
View File
@@ -0,0 +1,25 @@
require "nvchad.mappings"
-- add yours here
local map = vim.keymap.set
map("n", "gd", vim.lsp.buf.definition, { desc = "Go to definition" })
map("n", "gD", "<cmd>Telescope lsp_definitions<CR>", { desc = "Go to Definitions" })
map("n", "grr", "<cmd>Telescope lsp_references<CR>", { desc = "Go to references" })
map("n", "<leader>da", ":lua vim.lsp.buf.code_action()<cr>", { desc = "Code actions", noremap = true, silent = true })
map("n", "<leader>dh", ":lua vim.diagnostic.open_float()<CR>", { desc = "Show diagnostics" })
map("n", "<leader>dj", function()
vim.diagnostic.jump { count = 1, float = true }
end, { desc = "Go to next" })
map("n", "<leader>dk", function()
vim.diagnostic.jump { count = -1, float = true }
end, { desc = "Go to previous" })
-- Telescope
map("n", "<leader>fr", ":Telescope resume<CR>", { desc = "Resume last search" })
map("n", "<leader>fs", ":Telescope grep_string<CR>", { desc = "Find selected string" })
map("n", "<leader>fk", ":Telescope keymaps<CR>", { desc = "Find keymaps" })
map("n", "<leader>fd", ":Telescope diagnostics<CR>", { desc = "Diagnostics" })
-- Remaps
map({ "n", "v" }, "x", '"_x', { desc = "Delete char without yanking" })
map({ "n", "v" }, "c", '"_c', { desc = "Change without yanking" })
+6
View File
@@ -0,0 +1,6 @@
require "nvchad.options"
-- add yours here!
-- local o = vim.o
-- o.cursorlineopt ='both' -- to enable cursorline!
+87
View File
@@ -0,0 +1,87 @@
return {
{
"stevearc/conform.nvim",
event = "BufWritePre", -- uncomment for format on save
opts = require "configs.conform",
},
{
"neovim/nvim-lspconfig",
config = function()
require "configs.lspconfig"
end,
},
{
"nvim-treesitter/nvim-treesitter",
branch = "main",
opts = {
ensure_installed = {
"vim",
"vimdoc",
"lua",
"luadoc",
"printf",
"html",
"css",
"typescript",
"javascript",
"markdown",
"scss",
"svelte",
"typst",
"vue",
"regex",
},
},
},
{
"MeanderingProgrammer/render-markdown.nvim",
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,
lazy = false,
---@type snacks.Config
opts = {
-- your configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
bigfile = { enabled = false },
dashboard = { enabled = false },
explorer = { enabled = true },
indent = { enabled = true },
input = { enabled = true },
picker = { enabled = true },
notifier = { enabled = true },
quickfile = { enabled = true },
scope = { enabled = true },
scroll = { enabled = true },
statuscolumn = { enabled = true },
words = { enabled = true },
},
},
-- These are some examples, uncomment them if you want to see them work!
--
-- {
-- "nvim-treesitter/nvim-treesitter",
-- opts = {
-- ensure_installed = {
-- "vim", "lua", "vimdoc",
-- "html", "css"
-- },
-- },
-- },
--
}
+239
View File
@@ -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"
}
}
}
}
}
}
}
+242
View File
@@ -0,0 +1,242 @@
{
"$schema": "https://opencode.ai/config.json",
"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",
"disabled_providers": [
"gemini",
"anthropic",
"groq",
"openrouter",
"opencode"
],
"permission": {
"edit": "deny",
"webfetch": "ask",
"websearch": "ask",
"external_directory": "ask",
"read": {
"*": "allow",
"**/.env*": "deny",
"**/*.env": "deny",
"**/*.pem": "deny",
"**/*.key": "deny",
"**/*.p12": "deny",
"**/*.pfx": "deny",
"**/*.keystore": "deny",
"**/id_rsa*": "deny",
"**/.ssh/**": "deny",
"**/.aws/**": "deny",
"**/.npmrc": "deny",
"**/.netrc": "deny"
},
"bash": {
"*": "ask",
"ls *": "allow",
"cat *": "allow",
"grep *": "allow",
"rg *": "allow",
"find *": "allow",
"fd *": "allow",
"which *": "allow",
"file *": "allow",
"stat *": "allow",
"wc *": "allow",
"tree *": "allow",
"diff *": "allow",
"sort *": "allow",
"uniq *": "allow",
"echo *": "ask",
"pwd": "allow",
"date": "allow",
"whoami": "allow",
"jq *": "allow",
"tsc *": "allow",
"mkdir *": "ask",
"touch *": "ask",
"git *": "ask",
"git status": "allow",
"git status *": "allow",
"git diff": "allow",
"git diff *": "allow",
"git log": "allow",
"git log *": "allow",
"git show *": "allow",
"git branch": "allow",
"git remote -v": "allow",
"npm *": "ask",
"pnpm *": "ask",
"npx *": "ask",
"npm list": "allow",
"npm help": "allow",
"npm version": "allow",
"pnpm list": "allow",
"pnpm help": "allow",
"npx --version": "allow",
"curl *": "ask",
"wget *": "ask",
"rm *": "deny",
"rmdir *": "deny",
"dd *": "deny",
"mkfs*": "deny",
"shred *": "deny",
"chmod -R *": "deny",
"sudo *": "deny",
"doas *": "deny",
"npm install*": "deny",
"npm i": "deny",
"npm i *": "deny",
"npm ci*": "deny",
"pnpm install*": "deny",
"pnpm i": "deny",
"pnpm i *": "deny",
"pnpm add*": "deny",
"yarn add*": "deny",
"yarn install*": "deny",
"pip install*": "deny",
"pip3 install*": "deny",
"pipx install*": "deny",
"cargo install*": "deny",
"go install*": "deny",
"gem install*": "deny",
"brew install*": "deny",
"pacman *": "deny",
"yay *": "deny",
"apt *": "deny",
"apt-get *": "deny",
"dnf *": "deny",
"cat .env*": "deny",
"cat *.env": "deny",
"cat *.pem": "deny",
"cat *.key": "deny",
"cat *id_rsa*": "deny",
"cat *secret*": "deny",
"cat *credential*": "deny",
"cat ~/.ssh/*": "deny",
"cat ~/.aws/*": "deny",
"grep .env*": "deny",
"grep * .env*": "deny"
}
},
"provider": {
"duskadiy": {
"npm": "@ai-sdk/openai-compatible",
"name": "DuskaDIY",
"options": {
"baseURL": "https://llm.duskadiy.com/api/v1"
},
"models": {
"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": {
"npm": "@ai-sdk/openai-compatible",
"name": "llamacpp",
"options": {
"baseURL": "http://192.168.0.204:11343/v1"
},
"models": {
"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 } }
}
}
},
"agent": {
"plan": {
"mode": "primary",
"model": "llama.cpp/gemma-4-26B-A4B-it-UD-IQ4_XS",
"permission": {
"edit": "deny"
}
},
"build": {
"mode": "primary",
"model": "llama.cpp/gemma-4-26B-A4B-it-UD-IQ4_XS",
"permission": {
"edit": {
"*": "allow",
"**/.env*": "deny",
"**/*.env": "deny",
"**/*.pem": "deny",
"**/*.key": "deny",
"**/id_rsa*": "deny",
"**/.ssh/**": "deny",
"**/.aws/**": "deny",
"**/.git/**": "deny",
"**/.npmrc": "deny",
"**/.netrc": "deny"
}
}
},
"code-reviewer": {
"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/GLM-4.7-Flash-UD-Q4_K_XL",
"permission": {
"edit": "deny"
}
},
"test-runner": {
"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/GLM-4.7-Flash-UD-Q4_K_XL",
"permission": {
"edit": "deny"
}
},
"doc-writer": {
"description": "Maintains project documentation and docstrings",
"prompt": "You are a technical writer. Focus on clarity, accuracy, and keeping documentation in sync with code changes.",
"mode": "subagent",
"model": "llama.cpp/gemma-4-26B-A4B-it-UD-IQ4_XS",
"permission": {
"edit": {
"*": "deny",
"**/*.md": "allow",
"**/*.mdx": "allow",
"**/*.markdown": "allow",
"**/*.txt": "allow",
"**/*.rst": "allow"
}
}
},
"security-auditor": {
"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/GLM-4.7-Flash-UD-Q4_K_XL",
"permission": {
"edit": "deny"
}
}
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"$schema": "https://opencode.ai/tui.json",
"theme": "lucent-orng"
}
+106
View File
@@ -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", "<right>", "<page_down>", "<down>", " "]
# 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", "<left>", "<page_up>", "<up>"]
# 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: ["<number>G"]
# the key binding to execute a piece of shell code.
execute_code: ["<c-e>"]
# the key binding to reload the presentation.
reload: ["<c-r>"]
# the key binding to toggle the slide index modal.
toggle_slide_index: ["<c-p>"]
# the key binding to toggle the key bindings modal.
toggle_bindings: ["?"]
# the key binding to close the currently open modal.
close_modal: ["<esc>"]
# the key binding to close the application.
exit: ["<c-c>", "q"]
# the key binding to suspend the application.
suspend: ["<c-z>"]
# the key binding to skip all pauses in the current slide.
skip_pauses: ["s"]
+45
View File
@@ -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 <stream_url>`
* **[VLC (Command Line)](https://www.videolan.org/vlc/)**: You can play many radio streams directly from the terminal using `cvlc --play-and-exit <stream_url>`.
+6
View File
@@ -0,0 +1,6 @@
# PyRadio User Configuration File
# To get a full list of options execute: pyradio -pc
# or examine the file: /usr/lib/python3.13/site-packages/pyradio/config
#
theme = blue-by-boxer
use_transparency = True
+61
View File
@@ -0,0 +1,61 @@
Ambient Psychill [1.FM],http://strm112.1.fm/ambientpsy_mobile_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,,
Chillout (Groove Salad) [SomaFM],https://hls.somafm.com/hls/groovesalad/320k/program.m3u8,,
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,,
Space Music [Synphaera],https://somafm.com/synphaera.pls,,
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,nocon,
Dubplate.fm,https://sc2.dubplate.fm/radio/8030/dnb/uhifi,,
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,,
Liquid DnB,http://95.47.244.172:8000/live,,
Acid Techno,https://radiorecord.hostingradio.ru/techno96.aacp,,
Dark Techno [Caprice],http://79.111.14.76:8000/darktechno,,
Deep Techno/House [1.FM],http://strm112.1.fm/deeptech_mobile_mp3,,
90s90s Techno,http://streams.90s90s.de/techno/mp3-192/radiode/,,
The Trip: [SomaFM],https://somafm.com/thetrip.pls,,
ToXoRs minimalRADIO,http://95.216.245.239:8000/stream/1/,,
Minimalradio.de,https://azurafallback.de:8010/320.mp3,,
Minimal [Technolovers],https://stream.technolovers.fm/minimal,,
Suomisaundi / Freeform Psytrance / Spugedelic Trance [Caprice],http://79.120.77.11:8000/suomisaundi,,
Psychedelic Secret,https://solid48.streamupsolutions.com/proxy/bglsokon?mp=/=stream,,
Full-on psytrance [Caprice],http://79.111.14.76:9019/,,
Magnathospere Morning,https://listen7.myradio24.com/magnatos,,
Psytrance [Psyndora],https://cast.magicstreams.gr/sc/psyndora/stream,,
Hirschmilch Psytrance,https://hirschmilch.de:7001/psytrance.mp3,,
Bluffphonica,http://bluffphonica.stream.laut.fm/bluffphonica?pl=m3u&amp;t302=2024-09-17_23-09-40&amp;uuid=ce6a1b0b-1654-4d08-afbc-31b028f8f23b,,
BOM Psytrance [1.FM],http://strm112.1.fm/psytrance_mobile_mp3,,
TL_Psytrance,https://stream.technolovers.fm/psytrance,,
Record Goa Psy,https://radiorecord.hostingradio.ru/goa96.aacp,,
Psytrance 24/7 [DMT FM],https://dc1.serverse.com/proxy/ywycfrxn/live,,
Trance [RadiOzora],http://trance.out.airtime.pro:8000/trance_a,,
Uplifting trance [Technolovers],https://0nlineradio.stream13.radiohost.de/technolovers-uplifting-trance,,
Uplift [RadioRecord],https://radiorecord.hostingradio.ru/uplift96.aacp,,
Alternative [BAGeL],https://ais-sa3.cdnstream1.com/2606_128.aac,,
Celtic (ThistleRadio) [SomaFM],http://somafm.com/thistle.pls,,
Commodore 64 Remixes [Slay],http://www.slayradio.org/tune_in.php/128kbps/listen.m3u,,
Covers [SomaFM],http://somafm.com/covers.pls,,
Indie Pop [SomaFM],http://somafm.com/indiepop130.pls,,
Jazz (Sonic Universe) [SomaFM],http://somafm.com/startstream=sonicuniverse.pls,,
Pop (PopTron) [SomaFM],http://somafm.com/poptron.pls,,
Pop/Rock/Urban (Frequence 3 - Paris),http://streams.frequence3.net/hd-mp3.m3u,,
Progressive (Tags Trance Trip) [SomaFM],http://somafm.com/tagstrance.pls,,
Public Radio (NPR National Public Radio Stream),https://npr-ice.streamguys1.com/live.mp3,,
Heavyweight Reggae,https://somafm.com/reggae.pls,,
Rock (Digitalis) [SomaFM],http://somafm.com/digitalis.pls,,
Metal Detector,https://somafm.com/metal.pls,,
JazzGroove,https://www.internet-radio.com/servers/tools/playlistgenerator/?u=http://199.180.72.2:8015/listen.pls?sid=1&t=.pls,,
Main Mix [Paradise],http://stream.radioparadise.com/aac-128,,
Mellow Mix [Paradise] ,http://stream.radioparadise.com/mellow-128,,
Rock Mix [Paradise],http://stream.radioparadise.com/rock-128,,
Eclectic Mix [Paradise],http://stream.radioparadise.com/eclectic-128,,
Echoes of Bluemars,http://streams.echoesofbluemars.org:8000/bluemars.m3u,,
Cryosleep [Echoes of Bluemars] ,http://streams.echoesofbluemars.org:8000/cryosleep.m3u,,
Voices from Within [Echoes of Bluemars],http://streams.echoesofbluemars.org:8000/voicesfromwithin.m3u,,
1 Ambient Psychill [1.FM] http://strm112.1.fm/ambientpsy_mobile_mp3
2 Chilltrax http://server1.chilltrax.com:9000/
3 Chillout [psyradio*fm] http://streamer.psyradio.org:8020/;listen.mp3
4 Chillout [Psyndora] http://cast.magicstreams.gr:9125/stream
5 Chillout (Groove Salad) [SomaFM] https://hls.somafm.com/hls/groovesalad/320k/program.m3u8
6 Groove Salad Classic (Early 2000s Ambient) https://somafm.com/gsclassic.pls
7 Ambient and Experimental [n5MD] https://somafm.com/n5md.pls
8 Suomisaundi [Caprice] http://79.120.77.11:8004/suomisaundi
9 Vaporwaves [SomaFM] https://somafm.com/vaporwaves.pls
10 Space Music [Synphaera] https://somafm.com/synphaera.pls
11 Downtempo (Secret Agent) [SomaFM] http://somafm.com/secretagent.pls
12 Dub Step (Beyond) [SomaFM] http://somafm.com/dubstep.pls
13 Dub Techno [Schizoid] http://94.130.113.214:8000/dubtechno nocon
14 Dubplate.fm https://sc2.dubplate.fm/radio/8030/dnb/uhifi
15 Liquid funk [Caprice] http://79.111.119.111:9115/
16 Liquid funk https://radiorecord.hostingradio.ru/liquidfunk96.aacp
17 Liqui Radio https://stream-162.zeno.fm/ug59eq099yzuv?zs=zaqVusVSQuWMN3O5gnh8xA
18 Liquid DnB http://95.47.244.172:8000/live
19 Acid Techno https://radiorecord.hostingradio.ru/techno96.aacp
20 Dark Techno [Caprice] http://79.111.14.76:8000/darktechno
21 Deep Techno/House [1.FM] http://strm112.1.fm/deeptech_mobile_mp3
22 90s90s Techno http://streams.90s90s.de/techno/mp3-192/radiode/
23 The Trip: [SomaFM] https://somafm.com/thetrip.pls
24 ToXoRs minimalRADIO http://95.216.245.239:8000/stream/1/
25 Minimalradio.de https://azurafallback.de:8010/320.mp3
26 Minimal [Technolovers] https://stream.technolovers.fm/minimal
27 Suomisaundi / Freeform Psytrance / Spugedelic Trance [Caprice] http://79.120.77.11:8000/suomisaundi
28 Psychedelic Secret https://solid48.streamupsolutions.com/proxy/bglsokon?mp=/=stream
29 Full-on psytrance [Caprice] http://79.111.14.76:9019/
30 Magnathospere Morning https://listen7.myradio24.com/magnatos
31 Psytrance [Psyndora] https://cast.magicstreams.gr/sc/psyndora/stream
32 Hirschmilch Psytrance https://hirschmilch.de:7001/psytrance.mp3
33 Bluffphonica http://bluffphonica.stream.laut.fm/bluffphonica?pl=m3u&amp;t302=2024-09-17_23-09-40&amp;uuid=ce6a1b0b-1654-4d08-afbc-31b028f8f23b
34 BOM Psytrance [1.FM] http://strm112.1.fm/psytrance_mobile_mp3
35 TL_Psytrance https://stream.technolovers.fm/psytrance
36 Record Goa Psy https://radiorecord.hostingradio.ru/goa96.aacp
37 Psytrance 24/7 [DMT FM] https://dc1.serverse.com/proxy/ywycfrxn/live
38 Trance [RadiOzora] http://trance.out.airtime.pro:8000/trance_a
39 Uplifting trance [Technolovers] https://0nlineradio.stream13.radiohost.de/technolovers-uplifting-trance
40 Uplift [RadioRecord] https://radiorecord.hostingradio.ru/uplift96.aacp
41 Alternative [BAGeL] https://ais-sa3.cdnstream1.com/2606_128.aac
42 Celtic (ThistleRadio) [SomaFM] http://somafm.com/thistle.pls
43 Commodore 64 Remixes [Slay] http://www.slayradio.org/tune_in.php/128kbps/listen.m3u
44 Covers [SomaFM] http://somafm.com/covers.pls
45 Indie Pop [SomaFM] http://somafm.com/indiepop130.pls
46 Jazz (Sonic Universe) [SomaFM] http://somafm.com/startstream=sonicuniverse.pls
47 Pop (PopTron) [SomaFM] http://somafm.com/poptron.pls
48 Pop/Rock/Urban (Frequence 3 - Paris) http://streams.frequence3.net/hd-mp3.m3u
49 Progressive (Tags Trance Trip) [SomaFM] http://somafm.com/tagstrance.pls
50 Public Radio (NPR National Public Radio Stream) https://npr-ice.streamguys1.com/live.mp3
51 Heavyweight Reggae https://somafm.com/reggae.pls
52 Rock (Digitalis) [SomaFM] http://somafm.com/digitalis.pls
53 Metal Detector https://somafm.com/metal.pls
54 JazzGroove https://www.internet-radio.com/servers/tools/playlistgenerator/?u=http://199.180.72.2:8015/listen.pls?sid=1&t=.pls
55 Main Mix [Paradise] http://stream.radioparadise.com/aac-128
56 Mellow Mix [Paradise] http://stream.radioparadise.com/mellow-128
57 Rock Mix [Paradise] http://stream.radioparadise.com/rock-128
58 Eclectic Mix [Paradise] http://stream.radioparadise.com/eclectic-128
59 Echoes of Bluemars http://streams.echoesofbluemars.org:8000/bluemars.m3u
60 Cryosleep [Echoes of Bluemars] http://streams.echoesofbluemars.org:8000/cryosleep.m3u
61 Voices from Within [Echoes of Bluemars] http://streams.echoesofbluemars.org:8000/voicesfromwithin.m3u
+21
View File
@@ -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

Some files were not shown because too many files have changed in this diff Show More