diff --git a/.gitignore b/.gitignore index e77cadb..c086b2d 100644 --- a/.gitignore +++ b/.gitignore @@ -299,3 +299,4 @@ AGENTS/ docs/devlogs # ──────────────────────────────────────────────────────── /.cache_ggshield +user-dots diff --git a/README.md b/README.md index a73e2e2..9d50965 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,10 @@ test -f "$__fish_user_dots_path/local.fish" and source "$__fish_user_dots_path/local.fish" ``` +For convenience, a git-ignored `user-dots` symlink in the fish config directory is pointed at `$__fish_user_dots_path` on startup, so the overlay can be browsed from `~/.config/fish/`. It is recreated if missing and repointed if the path variable changes. Creation is a C2 startup side-effect (`__fish_config_op_autoexec`); it only ever manages a symlink and never clobbers a real file or directory at that path. + +To opt out, set `__fish_user_dots_symlink` to a falsy value (or toggle **Dots link** off on the **Paths** page of `config-settings`). Disabling it stops the symlink being generated and removes any existing one immediately — honoured regardless of the C2 master switch. + `fish_variables` (which fish auto-manages and may contain universal variable state) is excluded from this repo via `.gitignore`. --- diff --git a/config.fish b/config.fish index 058e85c..5561769 100644 --- a/config.fish +++ b/config.fish @@ -226,6 +226,13 @@ if status is-interactive # Resolve user-dots path. Customize via: set -U __fish_user_dots_path /your/path set -q __fish_user_dots_path or set -l __fish_user_dots_path "$XDG_CONFIG_HOME/.user-dots/fish" + # ────────────────────── user-dots convenience symlink ─────────────────── + # Keep $__fish_config_dir/user-dots tracking the resolved path so it can be + # browsed from the fish config dir. Git-ignored. Controlled by the + # __fish_user_dots_symlink toggle (config-settings → Paths); creation is a + # C2 side-effect, removal-on-opt-out is honoured regardless. See the helper. + __fish_user_dots_link + # ─────────────────────── Source machine-local config ──────────────────── # Sources local.fish if it exists. That file handles sourcing its own # secrets.fish companion when needed. diff --git a/docs/fish-config.index b/docs/fish-config.index index 9fc1e8e..f47089b 100644 --- a/docs/fish-config.index +++ b/docs/fish-config.index @@ -216,6 +216,8 @@ install-methods=## Install Methods customization=# 7. CUSTOMIZATION customize=# 7. CUSTOMIZATION local=## Machine-local Configuration +user-dots=## Machine-local Configuration +dots-link=## Machine-local Configuration secrets=## Secrets and API Keys overrides=## Overriding Configuration Variables universal=## Fish Universal Variables diff --git a/docs/fish-config.md b/docs/fish-config.md index 16619aa..00b7e06 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -1435,8 +1435,9 @@ Add -i (interactive confirmation) to destructive commands: Sponge — sponge history-scrubbing settings: delay, successful exit codes, purge-only-on-exit, allow-previously-successful, and extra sensitive variable-name tokens - Paths — scrollback log directory, scrollback max files, and the - user-dots path + Paths — scrollback log directory, scrollback max files, the + user-dots path, and the user-dots convenience symlink toggle + (Dots link) Toggle rows use ← → (or h/l) along an OFF ← DEFAULT → ON scale; DEFAULT erases the variable so the master switch / built-in default applies. Value @@ -1666,6 +1667,14 @@ custom location with: Typical uses: additional PATH entries, local aliases, hostname-specific env vars, work-specific tool configs. +For convenience, a git-ignored `user-dots` symlink in the fish config +directory tracks `$__fish_user_dots_path` so the overlay can be browsed from +`~/.config/fish/`. It is created if missing and repointed if the path changes. +Opt out by setting `__fish_user_dots_symlink` to a falsy value, or toggling +"Dots link" off on the config-settings Paths page — this stops generation and +removes any existing link. It only ever manages a symlink and never clobbers a +real file or directory at that path. + ## Secrets and API Keys $__fish_user_dots_path/secrets.fish @@ -1823,10 +1832,18 @@ __fish_config_op_autoexec prevents all of them. Python venv activation On every cd Sources .venv/bin/activate.fish WakaTime command hook On every command Reports to WakaTime API Auto-pull fast-forward On entering a repo Background ff-only git pull + user-dots symlink Every startup Links $__fish_config_dir/user-dots + to $__fish_user_dots_path When C2 is disabled: no Fisher install, no theme application, no paru/yay -wrapper generation, no automatic venv activation, no WakaTime reporting, and -no auto-pull (the PWD handler is never registered). +wrapper generation, no automatic venv activation, no WakaTime reporting, +no auto-pull (the PWD handler is never registered), and the user-dots +convenience symlink is not created. The symlink is git-ignored and only ever +managed as a symlink — a real file or directory at that path is left untouched. +The symlink has its own opt-out independent of C2: set __fish_user_dots_symlink +to a falsy value (or toggle "Dots link" off on the config-settings Paths page) +to stop generating it and remove any existing link — honoured even when C2 is +enabled. Managed by the __fish_user_dots_link helper. The first-run completion marker (__fish_config_first_run_complete) is still set so the init does not re-run on subsequent shells. diff --git a/functions/__config_settings_draw_value.fish b/functions/__config_settings_draw_value.fish index 27e015d..5b2b505 100644 --- a/functions/__config_settings_draw_value.fish +++ b/functions/__config_settings_draw_value.fish @@ -57,10 +57,10 @@ function __config_settings_draw_value else set title "Path Settings" set active_idx 3 - set vars __fish_scrollback_history_dir __fish_scrollback_history_max_files __fish_user_dots_path - set labels "Log dir" "Log max" "Dots path" - set types path int path - set hints "~/.terminal_history" 100 "(default)" + set vars __fish_scrollback_history_dir __fish_scrollback_history_max_files __fish_user_dots_path __fish_user_dots_symlink + set labels "Log dir" "Log max" "Dots path" "Dots link" + set types path int path bool + set hints "~/.terminal_history" 100 "(default)" on end set -l nrows (count $vars) diff --git a/functions/__fish_user_dots_link.fish b/functions/__fish_user_dots_link.fish new file mode 100644 index 0000000..6873671 --- /dev/null +++ b/functions/__fish_user_dots_link.fish @@ -0,0 +1,53 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# SYNOPSIS +# __fish_user_dots_link +# +# DESCRIPTION +# Manages the git-ignored `user-dots` convenience symlink in the fish config +# directory ($__fish_config_dir/user-dots), pointing it at the resolved +# $__fish_user_dots_path so the private overlay can be browsed from +# ~/.config/fish/. +# +# Behaviour is controlled by the __fish_user_dots_symlink toggle: +# - explicit falsy (0/false/…) — remove our symlink if present and stop. +# Honoured regardless of C2, so opting out always cleans up. +# - unset or truthy (default) — create the link if missing and repoint it +# if the target changed. This is a C2 startup side-effect, so it only +# creates when __fish_config_op_autoexec is enabled. +# +# Only ever manages a symlink; a real file or directory at that path is left +# untouched. +# +# ARGUMENTS +# (none) +# +# RETURNS +# 0 Always +# +# EXAMPLE +# __fish_user_dots_link +function __fish_user_dots_link --description 'Manage the user-dots convenience symlink' + set -l link "$__fish_config_dir/user-dots" + set -q __fish_user_dots_path + or set -l __fish_user_dots_path "$XDG_CONFIG_HOME/.user-dots/fish" + + # Explicit opt-out: remove our symlink (never a real file/dir) and stop. + __fish_variable_check __fish_user_dots_symlink + if test $status -eq 1 + test -L "$link"; and rm -f "$link" + return 0 + end + + # Enabled: creation is a C2 startup side-effect. + __fish_config_op_enabled __fish_config_op_autoexec; or return 0 + + test -d "$__fish_user_dots_path"; or return 0 + if test -L "$link" + test (readlink "$link") != "$__fish_user_dots_path" + and ln -sfn "$__fish_user_dots_path" "$link" + else if not test -e "$link" + ln -s "$__fish_user_dots_path" "$link" + end +end diff --git a/functions/config-settings.fish b/functions/config-settings.fish index 6e76229..9e7ec13 100644 --- a/functions/config-settings.fish +++ b/functions/config-settings.fish @@ -80,9 +80,9 @@ function config-settings --description 'Interactive TUI for managing fish config set -l sponge_vars sponge_delay sponge_purge_only_on_exit sponge_allow_previously_successful sponge_successful_exit_codes __fish_sponge_extra_sensitive set -l sponge_types int bool bool list list set -l sponge_labels Delay "Purge@exit" "Allow prev" "OK codes" "Extra secret" - set -l paths_vars __fish_scrollback_history_dir __fish_scrollback_history_max_files __fish_user_dots_path - set -l paths_types path int path - set -l paths_labels "Log dir" "Log max" "Dots path" + set -l paths_vars __fish_scrollback_history_dir __fish_scrollback_history_max_files __fish_user_dots_path __fish_user_dots_symlink + set -l paths_types path int path bool + set -l paths_labels "Log dir" "Log max" "Dots path" "Dots link" # Reset/blank-edit target for each value row. A non-empty entry is written # verbatim (sponge reads sponge_delay / sponge_successful_exit_codes with no @@ -91,10 +91,10 @@ function config-settings --description 'Interactive TUI for managing fish config # extra-sensitive list all tolerate being unset). Bool rows are not reset # through this path — they are a 2-state true/false with no unset state. set -l sponge_defaults 2 '' '' 0 '' - set -l paths_defaults '' '' '' + set -l paths_defaults '' '' '' '' # Rows per page index 0..3 - set -l page_rows 7 7 5 3 + set -l page_rows 7 7 5 4 set -l cur_page 0 # 0=Universal 1=Session 2=Sponge 3=Paths set -l cur_row 0 @@ -162,12 +162,21 @@ function config-settings --description 'Interactive TUI for managing fish config set -l next_val on test "$cur_val" = off; and set next_val DEFAULT __config_settings_apply $varname $scope $next_val - else if test $cur_page -eq 2 - # Sponge bool rows are 2-state (true/false) with no unset - # state — sponge reads them with no fallback. → sets true. + else + # Value pages: bool rows are 2-state (true/false). → sets + # true. Sponge reads its bools with no fallback; the Paths + # "Dots link" bool drives __fish_user_dots_link on change. + set -l v_vars $sponge_vars + set -l v_types $sponge_types + if test $cur_page -eq 3 + set v_vars $paths_vars + set v_types $paths_types + end set -l ridx (math $cur_row + 1) - if test "$sponge_types[$ridx]" = bool - set -U $sponge_vars[$ridx] true 2>/dev/null + if test "$v_types[$ridx]" = bool + set -U $v_vars[$ridx] true 2>/dev/null + test "$v_vars[$ridx]" = __fish_user_dots_symlink + and __fish_user_dots_link end end case left h @@ -196,6 +205,8 @@ function config-settings --description 'Interactive TUI for managing fish config set -l vtype $v_types[$ridx] if test "$vtype" = bool set -U $varname false 2>/dev/null + test "$varname" = __fish_user_dots_symlink + and __fish_user_dots_link else __config_settings_set_value $varname $vtype "$v_defaults[$ridx]" end