* feat: line-spanning foreground gradients via overrideForegroundColor
Add a `gradient:<stop>,<stop>,...` form for `overrideForegroundColor` that
paints the whole status line with a continuous gradient — each visible
character is colored by its column position, so the gradient spans the line
rather than restarting per widget. Applies to standard (non-powerline) lines;
powerline separators derive their color from adjacent backgrounds, so a
foreground gradient is intentionally not applied there.
- src/utils/gradient.ts: parse hex stops (`hex:RRGGBB` / `#RRGGBB` / bare),
interpolate in OKLab for perceptually even, non-muddy blends, and map to
truecolor or the nearest ansi256 index.
- src/utils/ansi.ts: applyLineGradient walks the assembled line with the
existing escape/cluster tokenizer, so SGR styling and OSC-8 hyperlinks pass
through untouched and visible width is unchanged (flex layout unaffected).
- src/utils/renderer.ts: applied after assembly and before truncation in the
standard path. `overrideForegroundColor` accepts the new `gradient:` form
alongside the existing `hex:` / `ansi256:` / named tagged-string forms; a
gradient spec is not treated as a per-widget solid color and degrades to a
no-op at ansi16 (keeping widgets' own colors).
Tests cover spec parsing, OKLab sampling, ansi256 quantization,
width-invariance, OSC-8 passthrough, and a renderer integration case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat: add per-widget gradients + presets + TUI picker on the line-span core
Converges the two same-day gradient PRs into one coherent feature. #406
added line-spanning gradients via overrideForegroundColor (OKLab, zero-dep);
#404 (@akkaz) added gradient as a per-widget color with named presets and a
ColorMenu picker. Both created src/utils/gradient.ts and would conflict, so
this meshes them onto a single shared OKLab engine:
- gradient.ts: GRADIENT_PRESETS (akkaz's stops, gradient-string MIT;
rainbow/pastel re-expressed as multi-stop hue wheels for OKLab); unified
parseGradientSpec accepting presets, dash (RRGGBB-RRGGBB), and comma
(hex:..,..) forms; applyGradientToText for the per-widget sweep.
- colors.ts: per-widget gradient hook in applyColors; getColorAnsiCode
collapses a gradient to its first stop (powerline / ansi16 degrade).
- ColorMenu.tsx: 'g' opens a gradient picker (preset list + custom hex),
foreground-only, colorLevel >= 2.
- No new dependency (tinygradient dropped in favor of OKLab).
Precedence: overrideForegroundColor gradient (line-span) > widget.color
gradient (per-widget) > solid. Gradients self-degrade at render time, so
color-sanitize leaves them untouched at every level.
Builds on the per-widget design from #404 by @akkaz.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: akkaz <giomarco@cleversoft.it>
* refactor: polish gradient feature — fix truncation reset-leak, tighten, document
Pre-PR polish pass over the gradient feature.
Fix (correctness):
- renderStatusLine now applies the line-span gradient AFTER truncation, not
before. truncateStyledText cuts from the right and appends a raw "..." with no
trailing reset, so a gradient applied earlier had its closing \x1b[39m sliced
off, leaking the last color past the status line. Gradient codes are zero-width,
so the truncation measurement is unaffected by deferring. Regression test added.
Simplify:
- Add exported isGradientSpec(); reuse it across colors.ts and renderer.ts instead
of duplicating the 'gradient:' startsWith check. Drop the redundant prefix guard
in applyColors (parseGradientSpec already self-guards).
Docs:
- Document applyGradientToText's code-point (vs grapheme-cluster) iteration as a
known limitation, with the ZWJ/variation-selector consequence and the
circular-import reason it isn't unified with applyLineGradient yet.
- Note that hex:/# /bare stops are valid in both comma and dash parse forms.
- Note getColorAnsiCode's ansi16 branch intentionally emits a truecolor first-stop
escape (caller-degraded; never reached at a true ansi16 terminal).
- README + docs/USAGE.md: gradient color options (both scopes, presets, forms).
bun test 1433 pass / 0 fail; lint + tsc clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(gradient): preserve escape sequences in widget gradients
Keep ANSI and OSC control sequences byte-for-byte intact when applying per-widget gradients so hyperlinks and pre-styled widget output are not corrupted by inserted SGR codes.
Count and color only visible non-whitespace characters while passing escape sequences through unchanged. Add a regression covering styled OSC 8 link output.
* fix(colors): suppress gradient fallback at ansi16
Prevent stored gradient color specs from emitting 24-bit SGR codes when the active terminal color level has been reduced to Basic or No Color.
Leave gradient settings intact, but return no gradient ANSI fallback at ansi16 and cover the behavior in per-widget and raw-code regression tests.
* fix(renderer): detect truncated gradient lines visibly
Preserve truncation reporting after whole-line gradients insert SGR codes between the ellipsis characters.
Check the visible text for the truncation marker instead of the raw styled bytes, and add a regression where the rendered line no longer contains a literal contiguous ellipsis.
* feat(tui): add gradients to foreground overrides
Let Global Overrides assign a whole-line foreground gradient through the same preset/custom selector used by the color screen.
Move (g) to gradient selection, add (x) as the foreground override clear key, show gradient override values in the menu, and update usage docs for the new controls and Basic/No Color degradation behavior.
In Powerline mode, carry global gradient position continuously across widget text while leaving separators and caps under Powerline contrast rules. Surface an active foreground override warning on the Powerline Setup screen so theme masking is visible.
---------
Co-authored-by: Cameron Sjo <cameronsjo@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: akkaz <giomarco@cleversoft.it>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* feat(terminal): honor CCSTATUSLINE_WIDTH env var to override probe
Provide an explicit width override so users can bypass the TTY probe
entirely when both ancestor-walk and `tput cols` fall through.
This is the fallback case the existing probe cannot solve on its own:
Claude Code >= 2.1.139 spawns statusline/hooks without terminal access,
and in some configurations (IDE integrations, nested shells, certain
agent-mode spawn paths) no ancestor process owns a TTY either. The
ancestor walk fails, `tput cols` returns 80, and the multi-line layout
truncates regardless of the actual iTerm2/terminal width.
PR #377 (`stty -F`/`stty -f`) covers the case where an ancestor does
hold a TTY but the legacy `< /dev/tty` form errors with ENOTTY. This
patch is complementary -- it handles the case where the ancestor walk
finds no TTY at all -- and gives users a knob today while upstream
work on passing `terminalWidth` via stdin JSON (#308) lands.
Change
------
`src/utils/terminal.ts` -- `probeTerminalWidth` now reads
`CCSTATUSLINE_WIDTH` before any platform check or probe. A valid
positive integer short-circuits with that value; anything else
(missing, empty, non-numeric, zero, negative) falls through to the
existing probe logic.
Usage
-----
Set the env var on the statusLine command in `~/.claude/settings.json`:
```json
"statusLine": {
"type": "command",
"command": "CCSTATUSLINE_WIDTH=200 ccstatusline"
}
```
Tests
-----
Four new cases in `src/utils/__tests__/terminal.test.ts`:
- override short-circuits probing entirely
- non-positive override (`0`) falls back to probing
- non-numeric override falls back to probing
- override applies on Windows where probing is otherwise disabled
`bunx vitest run src/utils/__tests__/terminal.test.ts` -> 12/12 pass.
Full-suite delta vs `main`: 4 new passing tests, zero new failures
(the pre-existing 105 env-specific failures in other suites are
unchanged on both branches).
`bun run lint` -> clean.
* docs: document terminal width override
---------
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
Adds a "Integration Example: AIWatch" section to docs/USAGE.md mirroring
the existing ccusage section, plus a Related Projects link in README, per
the placement the maintainer chose in #362.
AIWatch (ai-watch.dev) monitors live status for 30+ AI APIs/apps; the
Custom Command one-liner surfaces degraded providers in the status line
and renders empty when everything's operational.
Co-authored-by: Bentley <bentley@naemomlab.com>
* feat: add date mode for block and weekly reset timers
* fix(tui): avoid reset timer timestamp keybind conflict
Use t for timestamp mode so d remains the editor delete shortcut, and hide the weekly hours-only toggle while timestamp mode is active.
---------
Co-authored-by: Fan Bot <clawbot@Fandexuniji.local>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
* feat(git-branch): support GitLab and self-hosted hosts for branch link URLs
Replace the GitHub-only parseGitHubBaseUrl helper with a forge-agnostic
buildBranchWebUrl built on the existing parseRemoteUrl + RemoteInfo
plumbing in git-remote.ts. GitBranch links now work for GitHub, GitLab,
and compatible self-hosted remotes that expose the standard
host/owner/repo path.
Uses a single /tree/<branch> suffix because GitLab redirects it to its
canonical /-/tree/<branch> form, so one format covers both forges.
parseGitHubBaseUrl and its helper were the only GitHub-specific URL
builders in hyperlink.ts; remove them and their tests now that GitBranch
is the only caller and has been migrated.
Also rename the metadata key linkToGitHub to linkToRepo to match the
naming used by GitOriginOwnerRepo / GitOriginRepo / GitUpstreamOwner.
Legacy linkToGitHub is preserved as a read-only fallback: toggling the
modifier strips both keys and writes only linkToRepo, so users who
interact with the feature get their settings quietly upgraded. Explicit
linkToRepo:false wins over legacy linkToGitHub:true.
* feat(git-pr): support GitLab merge requests via glab
Replace the GitHub-only gh-pr-cache with a forge-aware git-review-cache.
The widget now renders pull requests for GitHub (via `gh`) and merge
requests for GitLab (via `glab`), picking the CLI per-repo based on the
origin remote host:
- host contains `github` → `gh`
- host contains `gitlab` → `glab`
- unknown/self-hosted host → probe each CLI with
`gh/glab auth status --hostname <h>` and use whichever is authenticated
against that host; if neither is, stay quiet rather than fire wasted
queries
- no origin remote → try both and let the CLI resolve the repo itself
For forks where the CLI would default-resolve to the parent repo, the
fetch falls back to `--repo <origin-url>` after an empty first query so
the user's fork PR/MR is still found.
GitPr.ts now records the provider on the cache entry and renders "MR #N"
for glab and "PR #N" for gh; raw mode stays `#N` for both. Widget display
name becomes "Git PR/MR".
Also rename the widget `type` from `git-pr` to `git-review` to match the
internal `git-review-cache` name. Legacy `git-pr` configs keep rendering
via a resolver in widgets.ts, and loadSettings silently rewrites them to
`git-review` in-memory so the canonical name lands on the next save —
same pattern as the linkToGitHub → linkToRepo rewrite in the previous
commit.
* docs(usage): describe GitHub and GitLab behavior for Git widgets
Update the Git-section intro to mention both forges and the CLI-selection
rules used for self-hosted hosts, and note on the Git PR keybind line
that the widget renders "MR" for GitLab origins.
* chore(gitlab-support): trim verbose comments to match repo style
Most of this repo's TS files carry zero or a handful of one-line comments;
the verbose rationale blocks added during the GitLab work (multi-paragraph
JSDocs on buildBranchWebUrl, getProviderCandidates, fetchFromProvider, etc.
and long inline explainers in the tests) stood out. Trim them to short
one-liners where the why is genuinely non-obvious, and drop the rest.
* fix(git-review): preserve self-hosted remote ports
* fix(tui): clamp status preview to terminal width
---------
Co-authored-by: jmecham <jmecham@foundrydigital.com>
Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>