11 KiB
AGENTS.md
What this is
SvelteKit 5 (runes mode) static site that assembles Steam launch commands from composable "program classes" (gamescope, MangoHud, Proton, obs-gamecapture, gamemode). User flips toggles in a tabbed UI; the app renders one flat launch command string like:
gamescope -f -- %command% mangohud waitforexecandrun %command%
Rendered by adapter-static (prerender = true in src/routes/+page.ts).
Core model (src/lib/)
meow.ts— the engine.ArgEvaluatableinterface (enabled(),envs(),binary(),prefix(),suffix(),priority(),arguments());ArgEvalevaluates components: filterenabled(), sort bypriority()(lowest = outermost wrapper), envs asVAR=VALspace-joined, each program renderedprefix binary args suffix(empty parts dropped), args sorted byprioand renderedarg val(bareargwhen val empty), then%command%appended.programs.ts— data types +Programclass implementingArgEvaluatablefrom aProgramConfig. Key fields:enabled,name,binary,prefix,suffix,priority,envs,args,options(toggleable sub-flags),envSeparators(per-var merge separators), optionconflicts(see below).presets.ts— re-export index only; real factories live per-program insrc/lib/preset/(see file map).preset/helpers.ts— builders shared by presets:envToggle(label, env, val, category?),group(program, labels)(pairwise conflict keys). (Mutually-exclusive flag choices like-Ffilter use ONE option with anargs[].valuesdropdown — do NOT reintroduce one-option-per-value.)
Semantics to keep in mind
- Env vars with the same name merge into ONE var: values dedupe, joined by the
var's separator from
envSeparators(default,). conflictskeys are'programname'(program-level) or'program:Option label'(option-level). Only enabled vs enabled conflict; conflicts are displayed as a non-blocking warning banner.- Env merge destructuring:
varis a reserved word in TS — use[varName, vals]in destructures. - Args are space-separated (
-W 1920), neverarg=val.
File map
| file | role |
|---|---|
src/lib/meow.ts |
evaluation engine (see above) |
src/lib/programs.ts |
types + Program adapter + findConflicts() |
src/lib/presets.ts |
re-exports the eight preset factories |
src/lib/preset/helpers.ts |
shared option builders |
src/lib/preset/gamescope.ts |
gamescope args (fullscreen, filter/scale dropdowns, HDR, VR, …) |
src/lib/preset/proton-ge.ts |
GloriousEggroll Proton env toggles |
src/lib/preset/proton-cachyos.ts |
CachyOS Proton env toggles (largest set) |
src/lib/preset/obs.ts |
obs-gamecapture CLI |
src/lib/preset/gamemode.ts |
gamemoderun wrapper (priority 5 = outermost) |
src/lib/preset/mangohud.ts |
MANGOHUD env options + MANGOHUD_CONFIG separator merge |
src/lib/preset/mesa.ts |
Mesa Vulkan layers env toggles (anti-lag, overlay layer, fps-monitor) |
src/lib/preset/prime.ts |
prime-run wrapper for NVIDIA PRIME offload |
src/lib/ProgramForm.svelte |
per-program editor: enabled checkbox, options list, Advanced <details> (name/binary/prefix/suffix/priority, env vars, args, env separators) |
src/lib/decode.ts |
applyCommand(cmd, programs) — enable options/programs a pasted command references (only ever enables, never disables) |
src/routes/+page.svelte |
tabs + pre.result command preview + conflict banner; localStorage persistence; decode textarea |
launch_args_env_vars.md |
reference: gamescope args, Proton env-var tables, OBS, gamemode |
steam-tinker-launcher.md |
reference: STL v14 docs (gamescope CLI catalog §3.1–3.6, mangohud, proton, dxvk, wine) |
Conventions
- New programs are preset factory functions in
src/lib/preset/<name>.tsreturning aProgramConfig, re-exported frompresets.ts, imported into+page.svelte'sprogramsarray. - Every option carries a
category: string; ProgramForm groups options under category headings (keep categories consistent within a preset). - Every option should also carry a
desc: string; it appears as a hover tooltip (1 s delay, CSS-only) next to the option row. - Default new presets to
enabled: falseso the default output stays stable. - Preset files ≤ ~140 lines; split if they grow (that's why the directory
exists). Shared builders go in
helpers.ts. +page.svelteuses PURE TAB indentation — never build edit oldText with spaces.- Mutual exclusivity is expressed via
conflicts(options) orgroup()(helpers), never via extra app state. user-select: noneon tabs/labels to stop double-click selection; inputs andpre.resultstay selectable.
Adding options & programs (agent playbook)
Every task in this repo ends with the verification loop below; a change is not done until all three pass.
Add an option to an existing program
-
Edit
src/lib/preset/<name>.ts— the program's factory, insideoptions: [...]. Mine the flag/env catalogs fromlaunch_args_env_vars.mdandsteam-tinker-launcher.md. -
Pick the builder (from
./helpers):-
Env toggle (checkbox sets
VAR=1):envToggle('Label', 'ENV_VAR', '1', 'Category')— pass a custom value as the 3rd arg for non-1values. -
Flag with a value (gets an editable input in the UI while enabled):
{ label: 'Mouse sensitivity', enabled: false, args: [{ arg: '-s', val: '1.0', prio: 0 }], category: 'Window' } -
Flag with a fixed value set (gets a
<select>dropdown instead of a text input): addvalues: ['auto', 'drm', 'wayland']to the arg. -
Flag whose value must match a regex (input turns red + banner error while invalid): add
pattern: NUMorpattern: NUM_DEC(exported from./helpers) or a custom regex source string. -
Bare flag: same shape with
val: ''(checkbox only, no input). -
Mutually exclusive flag with a fixed value set (e.g.
-F fsrvs-F nis): ONE option withvalueson the arg:{ label: 'Filter', enabled: false, args: [{ arg: '-F', val: 'fsr', prio: 0, values: ['none', 'linear', 'fsr', 'nis'] }], category: 'Filter' }. Do NOT create one option per value.
-
-
categoryis required — reuse an existing one from that preset or add a new one (it becomes a tab). Keep categories consistent within a preset. -
Mutual exclusivity:
conflicts: ['other-program']orconflicts: ['same-program:Other option label'];group()generates these automatically. -
Presets only supply defaults — the form binds arg
valto an input while the option is enabled, so users can override values.
Add a new program
- Create
src/lib/preset/<name>.tsexportingexport function <name>(): ProgramConfig(model onsrc/lib/preset/obs.ts— the smallest real example). - Required config:
name,enabled(defaultfalseso output stays stable),binary,prefix(wrapper like'gamemoderun'),suffix('waitforexecandrun'for proton,'--'for gamescope-style),priority,envs: [],args: [],options: [...]. - Re-export: add
export { <name> } from './preset/<name>';tosrc/lib/presets.ts. - Import it in
src/routes/+page.svelteand append to theprograms = $state([...])array. - Keep the file ≤ ~140 lines; use
envToggle()/group()for builders and repetitive options.
Priority semantics: components sort ascending by priority() — the outermost
wrapper has the LOWEST number (gamemoderun=5, gamescope=10, proton=20,
mangohud=30).
Verification (mandatory before yielding)
pnpm exec prettier --write <edited files> # lint runs prettier --check
pnpm check # svelte-check — authoritative type check
pnpm lint # prettier --check + eslint
pnpm build # adapter-static build
All four must pass. pnpm check is the authority — see the known quirk below.
Known traps for agents:
- pi-lens/LSP diagnostics on
.sveltefiles are stale all session — they falsely reportenabled/category/envSeparatorsas missing fromProgramConfigon every edit. They are noise: the fields verifiably exist inprograms.ts. Do NOT chase them; rely onpnpm check(returns 0 errors every time). The.tsfiles' diagnostics are accurate. +page.svelteuses pure tabs — editoldTextmust use tabs, not spaces. If the edit tool reports content-drift, re-read the file verbatim before retrying.- This file (AGENTS.md) is auto-reformatted by prettier/markdownlint after
edits — always re-read it before further edits; it also uses real em-dashes
(
—). varis a reserved word in TS destructuring — use[varName, vals].- A stale
vite devserver can serve corrupted modules (missing exports). Fix: kill allviteprocesses and restart — neverpkill -fwith a pattern that also appears in your own command line (it kills your shell). - Do not add options the docs mark as per-user paths or STL-internal settings (Vortex/MO2/winetricks/Conty…); README.md is sv boilerplate, ignore it.
Reference docs to mine for new options: launch_args_env_vars.md and
steam-tinker-launcher.md (README.md is sv boilerplate, ignore it).