quick_search/config_example.toml
Jeremy Karst 658e32159a
All checks were successful
CI / linux (push) Successful in 11m53s
CI / windows-cross (push) Successful in 4m50s
CI / release (push) Successful in 13s
Some optimizations and another fix for the shortcut system.
2026-09-06 19:43:57 -04:00

257 lines
14 KiB
TOML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# QuickSearch configuration reference.
#
# The live config is auto-created at ~/.config/quicksearch/config.toml
# (Windows: %APPDATA%\quicksearch\config.toml). A config.toml placed next
# to the quicksearch binary overrides it entirely (portable mode).
# Relative paths resolve against the directory containing the config
# file, so a portable folder can be moved wholesale.
#
# Every key is optional; missing keys take the defaults shown here.
[paths]
# One or more directory roots to index. Walked in order; duplicate and
# nested roots are de-duplicated automatically. `~` expands to home.
# Adding a folder reindexes to pick it up; removing one deletes its entries
# and leaves the rest of the index alone. Order and spelling do not matter:
# "~/docs", "/home/you/docs" and "/home/you/docs/" are one folder.
indexing_paths = ["~"]
# SQLite index location. Default: ~/.local/share/quicksearch/index.sqlite
# (Windows: %LOCALAPPDATA%\quicksearch\index.sqlite — write Windows paths
# as TOML *literal* strings, single quotes, and keep the index out of a
# roaming profile; it is far too large to synchronise). The file here is
# created if missing, and replaced if it is an index from an older
# QuickSearch layout; a SQLite database belonging to some other program
# is refused with an error, and nothing is deleted.
database_path = "~/.local/share/quicksearch/index.sqlite"
[indexing]
# The indexing mode. true = automatic: filesystem watchers apply changes a
# couple of seconds after they happen, and a full reindex runs every
# reindex_interval_minutes to cover whatever the watcher missed.
# false = manual: nothing is indexed until you ask for it. The Stop and
# Return to Automatic buttons on the Manage Index tab write this value, so
# the mode you left the app in is the mode it starts in.
auto_index = true
reindex_interval_minutes = 60
# Follow symbolic links during directory walks. Applies to links pointing
# at files as well as at directories: off, a symlink is not resolved at
# all, so its target is never indexed — a target can live outside every
# root listed above. A resolved target is stored under its own real path,
# not the link's.
follow_symlinks = false
# Index hidden files and directories: dot-files everywhere, and
# additionally anything carrying the Hidden attribute on Windows (AppData,
# $RECYCLE.BIN, System Volume Information ...). The System attribute on
# its own does not count — cloud sync roots carry it purely to get a
# branded folder icon, and are indexed normally.
include_hidden = false
# Empty = extract text from every supported format. Non-empty = content
# indexing only for these extensions; other files are still listed for
# filename search. Entries are case-insensitive, leading dot optional.
# The reserved entry "(none)" whitelists files that have no extension at
# all (Makefile, README, .bashrc); a non-empty list without it skips them.
# Inside an entry, "#" starts a comment that runs to its end:
# content_extensions = ["txt", "md # docs", "# pdf — too slow", "(none)"]
# Narrowing this drops the stored text of the files it now excludes;
# widening it reindexes to extract the ones it now allows.
content_extensions = []
# Excluded from the index entirely. A pattern without a separator matches
# any single path component (so ".git" prunes whole subtrees); patterns
# containing one match full paths. Glob syntax (*, ?, [..]); a name
# pattern must match the whole name, so ignoring an extension needs the
# wildcard ("*.jpg"). Matching is case-insensitive on Windows and macOS.
# The Windows defaults add "$RECYCLE.BIN", "System Volume Information",
# "pagefile.sys", "hiberfil.sys", "swapfile.sys", "Thumbs.db" and
# "desktop.ini"; when indexing a whole Windows drive, adding 'C:\Windows'
# and 'C:\Windows\WinSxS' by hand is worthwhile. The index's own files
# are always skipped and need no pattern here.
ignore_patterns = [".git", "node_modules", "*.tmp", ".venv", "venv"]
# Walker threads per root, keyed by the exact root string from
# indexing_paths. Absent or 0 = auto (4 on local storage, 16 on network
# mounts, detected per root). Applies at the start of the next run.
# root_workers = { "/media/share" = 24 }
[processing]
# Bytes read from the start of each file for its content hash,
# `sha256(size || first hash_length bytes)`, which backs duplicate
# detection; the same bytes are also the detection window for magic-byte
# matching and the text sniff. Known limitation: files of identical size
# whose heads match are reported as duplicates when they may not be —
# verify from the Duplicates tab before deleting anything. 8192 is tuned;
# raising it to 16384 is defensible, lowering it is not. Clamped to
# 262..1048576 on load.
hash_length = 8192
# Maximum extracted text stored per file (bytes).
maximum_text_size = 262144
# Files larger than this skip text extraction entirely (bytes). Clamped to
# 1..4294967296 on load.
maximum_text_file_size = 2097152
# Files per batch during walks / inserts / extraction.
batch_size = 500
# Writer time one indexing root's turn may take before the round-robin
# moves on (milliseconds), so a root extracting large documents cannot
# leave another root's walkers parked behind it. 0 gives each turn one
# batch_size quantum and no more. Clamped to 0..10000 on load.
writer_turn_slice_ms = 100
# How large the write-ahead log (index.sqlite-wal) may grow during an
# indexing run before the indexer forces a checkpoint (bytes); left
# alone, the log grows for the whole run. Not a safety knob: on a volume
# short of space the indexer checkpoints sooner than asked and stops the
# run with an error before the disk fills (which would kill the process
# with SIGBUS through SQLite's mmap'd wal-index). 0 disables it; any
# other value below 16777216 is raised to it.
#
# Both directions cost. A checkpoint blocks indexing for its whole
# copy-back, so a low value stalls the run often. A high one is paid by
# readers instead: SQLite searches the log before every page it fetches
# from the index, so a larger log slows searches running alongside a
# run, and it lengthens recovery after a crash or a force-quit. The
# default trades toward fewer stalls; lower it if searching while
# indexing matters more than the run finishing quickly.
maximum_wal_size = 2147483648
# FTS5 tokenizer: 'trigram' (substring matching, the default; gets
# remove_diacritics 1 appended), 'unicode61', 'porter', or a full FTS5
# option string. See https://www.sqlite.org/fts5.html#tokenizers
tokenize = "trigram"
# Store extracted text (zstd-compressed) alongside the FTS index. Off:
# the index shrinks to roughly stock-Baloo size, but search loses snippet
# previews, occurrence ranking, case verification, and fuzzy full-text.
# Turning it off discards the stored text immediately; turning it on
# re-extracts, because the text of files already indexed was never kept.
store_text_for_snippets = true
[security]
# Encrypt the index with a password (SQLCipher). The password is asked
# for every time QuickSearch starts; turning this on or off deletes and
# rebuilds the index. Change it from the GUI (Settings → Security), not by
# hand: enabling protection also generates the KDF salt below.
password_protected = false
# Store the derived key in the OS keychain (Secret Service / KWallet on
# Linux, Credential Manager on Windows) and skip the startup prompt.
use_keychain = false
# When a password is set, the app writes a `salt` value here (32 hex
# digits). It is not a secret, but it is unique to your index: do not
# create or edit it by hand, and keep it if you copy this file — the
# password only unlocks the index together with its salt.
[ui]
# Zoom factor for the whole GUI: fonts, spacing, and widgets scale
# together (0.5 2.5). Ctrl +/- and Ctrl 0 adjust it temporarily at
# runtime; this value is the persistent baseline.
scale = 1.25
# Written by QuickSearch, not by you: the folders that have already shown
# the "more subfolders than the watcher can follow" warning, so restarting
# does not repeat it. Deleting it just means the warnings come back once
# each.
watch_cap_warned_roots = []
# The shortcut QuickSearch claims for itself while it is running: brings it to
# the front, switches to the Search tab and selects whatever is in the search
# box. Modifiers are Ctrl, Alt and Shift, joined to one key with "+". Leave it
# empty ("") for no shortcut. To start QuickSearch when it is closed, use
# "Set up system shortcut" on the Settings tab, or bind "quicksearch
# --toggle" in your desktop's keyboard settings yourself.
search_hotkey = "Ctrl+Shift+F"
# 'dark' or 'light'; anything other than 'light' is dark. Applied as soon
# as it is changed on the Settings tab. Following the desktop's own
# light/dark setting would need the session's D-Bus settings portal, so it
# is deliberately not offered.
color_scheme = "dark"
# Written by QuickSearch, not by you: whether the Settings tab shows the
# technical settings alongside the everyday ones. The checkbox at the top of
# that tab writes it immediately, without an Apply. Off, the tab hides the
# byte budgets, the tokenizer, the database path and the other knobs whose
# defaults suit almost every installation.
show_advanced_settings = false
# Written by QuickSearch, not by you: whether the short introduction shown
# on a brand-new installation has been dismissed. Absent means this config
# predates that introduction - an installation that upgraded into this
# version, which is not offered it. The Help tab can show it again at any
# time.
tutorial_seen = false
[search]
# Whether the Fuzzy box on the Search tab starts ticked. Off by default: the
# fuzzy passes cost noticeable time on every keystroke, and most searches do
# not need them. Tick it in the GUI whenever you want typo tolerance for a
# session, or set it here to start that way every time.
fuzzy_default = false
# Ceiling on the fuzzy stages' typo budget. The allowance grows with the
# search term, one edit per three characters, up to this value, so 2
# means "1 edit for 3-5 character terms, 2 for anything longer". 0 turns
# the fuzzy stages off. Above 3 is allowed but not recommended: matches
# become dominated by coincidence and every fuzzy pass slows down.
fuzzy_max_edits = 2
# Hard cap on results per search (the GUI's scroll list length). Clamped to
# 1..1000000 on load: at zero the cascade stops before its first pass, so
# every search returns nothing and calls the empty answer truncated.
display_limit = 1000
# Results per streamed batch (latency/overhead knob, not a page size).
results_per_page = 100
# How long the GUI waits after the last keystroke before searching (ms).
debounce_ms = 150
# Watch the visible search results and show renames, deletions and content
# changes as they happen. What a row shows is read from the file itself,
# so this works whether or not indexing is running, and the index is
# brought up to date for those files. Editing the query drops the watches.
live_results = true
# Memory the search connection keeps database pages in, in MiB, held for the
# length of a search session and released after a long idle.
#
# 0 sizes it from the index and is almost always right. Every keystroke
# rescans the whole file list, so what has to stay resident is that list:
# roughly 168 bytes per indexed file, capped at 128 MiB automatically.
#
# It matters on an *encrypted* index, which must decrypt any page the cache
# does not already hold. Measured at 600k files: 127 ms per keystroke with a
# cache too small, 34 ms once it fit. An unencrypted index reads a miss
# straight from the operating system and is given a flat 16 MiB, because
# sweeping 1 MiB to 256 MiB on one measured no faster than noise.
#
# Set it explicitly only when the automatic value is wrong for your tree —
# deeply nested folders make wider rows and want more — or when your index is
# over ~800k files, where the automatic cap lands below what it wants. An
# explicit value may exceed that cap; it is clamped to 16..1024 on load.
cache_size_mib = 0
# Which columns the Search tab shows; the right-click menu of any column
# header and Settings → Search both write here immediately, without an
# Apply. There is deliberately no 'path' key: the path is always shown,
# because it is the only column that identifies a result on its own.
[search.columns]
name = true
# The excerpt of a file's contents around the match. Rows that matched on
# their name or path show a dash there instead.
content_match = true
# Off by default: the width these take is usually better spent on the path
# and the matched text. Turning one on also makes it available to sort by;
# sorting by a column that is hidden falls back to sorting by rank.
size = false
modified = false
rank = true
# What the Duplicates tab lists. Both keys are written by the tab itself, from
# its exclusion box and its right-click menu; they are here because they are
# yours to read and edit. Neither changes what is indexed or what a search
# finds: a file left out of the duplicate listing is still in the index.
[duplicates]
# Paths left out of the listing. Same syntax and same matcher as
# indexing.ignore_patterns - a pattern without a separator matches any single
# name ("*.iso"), one with a separator matches a whole path and everything
# under it ("/home/you/Backups/*").
#
# A member whose path matches is not counted, and the group is re-priced
# around the copies that are left; a group down to one copy is not a duplicate
# of anything and is not listed at all. For a folder that is *meant* to hold
# copies, this is the key to use.
exclude_patterns = []
# Groups dismissed with "Hide this group", by content hash, lowercase hex.
# For the other case: a group that is not really a duplicate at all, because
# grouping only reads each file's size and its first processing.hash_length
# bytes.
#
# Keyed by hash, not by path, so a hidden group stays hidden when its files
# are renamed or moved, and comes back if their contents change. Changing
# processing.hash_length rebuilds the index and gives every file a new hash,
# which strands every entry here; the tab's Clear button empties the list.
hidden_groups = []