Skip to content

fix(powerline): detect Nerd Fonts v3 names and fonts in font subfolders - #64

Merged
axisrow merged 16 commits into
mainfrom
fix/powerline-font-detection
Oct 15, 2026
Merged

axisrow merged 16 commits into
mainfrom
fix/powerline-font-detection

Conversation

@axisrow

@axisrow axisrow commented Oct 15, 2026

Copy link
Copy Markdown
Owner

Summary

Mirror of the upstream fix PR: sirmalloc#739 (issue: sirmalloc#738, moved from #62).

checkPowerlineFonts() missed Nerd Fonts v3 file names (no spaces, e.g. JetBrainsMonoNerdFont-Regular.ttf), the short NF form, and fonts nested in subfolders. Fix: /nerd ?font/i + an NF token in the patterns, recursive depth-capped scan of the font folders (extracted into the exported fontFolderHasPowerlineFont for testing against real temp dirs).

The branch is cut from upstream/main, so the diff also carries the upstream commits the fork does not have yet (up to bed8501); the powerline change itself is the single fix(powerline): … commit.

Test plan

🤖 Generated with Claude Code

dependabot Bot and others added 12 commits October 13, 2026 14:12
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.11 to 5.0.0.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 5.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…loc#722)

Bumps [react-devtools-core](https://github.com/react/react/tree/HEAD/packages/react-devtools-core) from 7.0.1 to 8.0.0.
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/react-devtools@8.0.0/packages/react-devtools-core)

---
updated-dependencies:
- dependency-name: react-devtools-core
  dependency-version: 8.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@8207627...949feb2)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ith 6 updates (sirmalloc#733)

Bumps the dev-dependencies group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@remotion/cli](https://github.com/remotion-dev/remotion) | `4.0.532` | `4.0.534` |
| [eslint](https://github.com/eslint/eslint) | `10.11.0` | `10.12.0` |
| [globals](https://github.com/sindresorhus/globals) | `17.12.0` | `17.13.0` |
| [remotion](https://github.com/remotion-dev/remotion) | `4.0.532` | `4.0.534` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.70.1` | `8.71.1` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `5.0.0` | `5.0.3` |



Updates `@remotion/cli` from 4.0.532 to 4.0.534
- [Release notes](https://github.com/remotion-dev/remotion/releases)
- [Commits](remotion-dev/remotion@v4.0.532...v4.0.534)

Updates `eslint` from 10.11.0 to 10.12.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.11.0...v10.12.0)

Updates `globals` from 17.12.0 to 17.13.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](sindresorhus/globals@v17.12.0...v17.13.0)

Updates `remotion` from 4.0.532 to 4.0.534
- [Release notes](https://github.com/remotion-dev/remotion/releases)
- [Commits](remotion-dev/remotion@v4.0.532...v4.0.534)

Updates `typescript-eslint` from 8.70.1 to 8.71.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.71.1/packages/typescript-eslint)

Updates `vitest` from 5.0.0 to 5.0.3
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.3/packages/vitest)

---
updated-dependencies:
- dependency-name: "@remotion/cli"
  dependency-version: 4.0.534
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: eslint
  dependency-version: 10.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: globals
  dependency-version: 17.13.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: remotion
  dependency-version: 4.0.534
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: typescript-eslint
  dependency-version: 8.71.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: vitest
  dependency-version: 5.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…rmalloc#695)

Claude Code 2.1.251 added rate_limits.spend_limit to the status line payload for developers behind a Claude apps gateway. Parse it and show it with two widgets that read the payload only and never call the usage API.

Closes sirmalloc#572

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
…#710)

* fix: draw the text cursor as a bar so trailing spaces show

The label editor quotes the label so a trailing space stays visible, but
the cursor was a highlighted block: over a character, or a highlighted
space after the text when at the end. So a label ending in a space with
the cursor on it ("Weekly:" + block) looked the same as a label with no
space and the cursor past the end ("7d" + block), and "7d" read as "7d ".

Draw the cursor as a bar between characters instead. It never covers a
character, so a trailing space shows as a gap before it, and the end of
the text adds nothing that looks like a space. Custom Text shares the
cursor, so it gets the bar too.

* feat(tui): preview label edits live and blink editor cursors

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
…el clash (sirmalloc#708)

* fix(usage): call Session Usage Block Usage, ending its "Session:" label clash

Three widgets printed the label "Session: " and meant two different
things by it. Session Clock and Session Name mean the Claude Code
conversation; Session Usage meant the 5-hour usage window. On one line,
"Session: 42% | Session: 1hr 5m" reads as two facts about the same
session when they aren't.

The 5-hour window already has a name here: Block Timer and Block Reset
Timer call it the block. Session Usage becomes Block Usage, labeled
"Block: ", with the description "Shows the current 5-hour block's usage
percentage". Its type stays session-usage, so saved configs keep
working, and anyone who prefers the old label can set "Session: " back
with the label editor (e). The docs follow; past release notes keep the
name they shipped with.

* fix(usage): label Block Usage "5h: " rather than "Block: "

"Block: " is already Block Timer's label, for the block's elapsed time
("Block: 3hr 45m"), so Block Usage beside it would print two "Block:"
figures. Label it "5h: " instead, which says which window the percent
is of and pairs with Weekly Usage's "Weekly: ". The name stays Block
Usage, and a "Session: " override still sets the old label back.

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
…es whole (sirmalloc#704)

* fix(ansi): strip 8-bit color codes as well as ESC [ ones

The ANSI parser reads the 8-bit CSI (0x9b) as an escape sequence, but
stripSgrCodes only matched color codes written with ESC [. Text colored
with \x9b31m kept its colors where they should go: a global foreground
override left a preserveColors widget's own colors in place, and a
Custom Command cut by its max width never got its closing reset, so its
color ran into the separators and widgets after it.

Match both introducers. restoreBackgroundAfterResets shares the pattern,
so an 8-bit reset now gets the background re-applied after it too; its
parameter parsing skips a one-character introducer for those.

* fix(sanitize): keep 8-bit color codes and drop other 8-bit sequences whole

The terminal sanitizer dropped 8-bit C1 controls one character at a time.
For a sequence started by one, that removed only the introducer and left
the rest as text: a command printing \x9b31mred\x9b39m showed "31mred39m",
and an 8-bit hyperlink or title string showed its URL or payload.

Read each 8-bit introducer as the ESC sequence it stands for: CSI (0x9b)
as ESC [, OSC (0x9d) as ESC ], and DCS, SOS, PM and APC likewise, with
ST (0x9c) ending strings. Color codes and safe hyperlinks are kept, in
their 7-bit form, which every terminal reads and the rest of the
renderer parses; any other sequence is dropped whole. Lone 8-bit
controls that start no sequence, such as NEL or a stray ST, are still
dropped.
…irmalloc#701)

* feat(claude-status): optionally link the widget to the status page

Claude Status shows "minor" or "major" when something's wrong, but
the details are on status.claude.com, which you have to look up.
Git Branch and Git Root Dir can already link to the repo or IDE.

Press `l` on Claude Status to make the whole widget an OSC 8 hyperlink
to https://status.claude.com, the site it reads the status from:
label, status and the history strip, and an unknown "?" too, when the
page is most worth a look. It's off by default, like the other links,
and the editor row shows "status link". Terminals without OSC 8
support show the text as before.

* fix(preview): preserve enabled widget hyperlinks

---------

Co-authored-by: Matthew Breedlove <sirmalloc@gmail.com>
checkPowerlineFonts() scanned only the top level of the font folders and
required a spaced "nerd font" pattern, so common layouts were reported as
missing: Nerd Fonts v3 file names have no spaces
("JetBrainsMonoNerdFont-Regular.ttf"), the short "NF" form exists
("JetBrainsMonoNF-Regular.ttf"), and font managers nest fonts in
subfolders ("NerdFonts/", "TTF/", Debian's
/usr/share/fonts/opentype/PowerlineSymbols.otf).

Match "nerd ?font" and an "NF" token, and scan the font folders
recursively with a depth cap.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…tection

# Conflicts:
#	bun.lock
#	docs/USAGE.md
#	src/widgets/CurrentWorkingDir.tsx
#	src/widgets/CustomCommand.tsx
#	src/widgets/CustomSymbol.tsx
#	src/widgets/Link.tsx
#	src/widgets/Skills.tsx
#	src/widgets/shared/max-width.tsx
#	src/widgets/shared/speed-widget.tsx
#	src/widgets/shared/symbol-override.tsx

@axisrow axisrow left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Reviewed the powerline fix commit d621a3b: the extraction into exported fontFolderHasPowerlineFont (depth cap, hidden-entry skip, per-directory error swallowing) preserves the old semantics while adding recursion, the async path reuses the same code so no duplicated pattern list is left behind, and the new tests on real temp dirs cover v3 spaceless names, the short NF form, subfolder nesting, the Debian opentype layout, the old spaced v2 name, and the negative cases. The rest of the diff is the upstream sync the branch was cut with, as disclosed in the description; the powerline change and its test are byte-identical to upstream sirmalloc#739 head, and merge 1463381 keeps the fix intact against fork main. No required changes. Optional, non-blocking: (1) /nf(?=[-.0-9]|$)/i is case-insensitive with no left boundary, so any filename whose tail is nf (a stray *.conf inside a font folder) counts as a Nerd Font; risk is low for a heuristic check, but the v3 short form is always uppercase, so a case-sensitive /NF(?=[-.0-9]|$)/ would still match JetBrainsMonoNF-Regular.ttf while trimming that; note the spaced short form (MesloLGS NF Regular.ttf) is not matched by the lookahead either way, add \s to the lookahead if that naming matters. (2) The test helper could use dirname(filePath) instead of join(filePath, ..); recursive mkdir resolves both, dirname is just clearer. (3) Symlinked font directories are not followed (Dirent.isDirectory is false for symlinks); not a regression, and the fc-list fallback covers fontconfig-registered setups.

@axisrow axisrow left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Re-review of the updated head: since the previous review (5527921612, approved) the branch gained only two empty retrigger-CI commits (6cc6da6, e1e0a35); the tree is byte-identical (diff against the previously reviewed 1463381 is empty), so the powerline fix and its tests are unchanged. Verdict stands: the extraction into exported fontFolderHasPowerlineFont (depth cap, hidden-entry skip, per-directory error swallowing) preserves the old semantics while adding recursion, the async path reuses the same code, and the temp-dir tests cover the v3 spaceless names, the short NF form, subfolder nesting, the Debian opentype layout, the old spaced v2 name, and the negative cases. No required changes. Optional, non-blocking (carried over from the previous review): (1) /nf(?=[-.0-9]|$)/i is case-insensitive with no left boundary, so any filename whose tail is nf (a stray *.conf inside a font folder) counts as a Nerd Font; a case-sensitive /NF(?=[-.0-9]|$)/ would still match JetBrainsMonoNF-Regular.ttf while trimming that, and the spaced short form (MesloLGS NF Regular.ttf) is not matched by the lookahead either way. (2) The test helper could use dirname(filePath) instead of join(filePath, ..). (3) Symlinked font directories are not followed; not a regression, and the fc-list fallback covers fontconfig-registered setups.

@axisrow axisrow left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. This run targets the same head e1e0a35 already reviewed and approved in review 5527982924: the tree is unchanged, base a2c1161 unchanged, and no new upstream feedback has appeared on sirmalloc#739. Verdict stands. The powerline fix extracts the scan into exported fontFolderHasPowerlineFont (depth cap, hidden-entry skip, per-directory error swallowing) while preserving old semantics, the async path reuses the same code, and the temp-dir tests cover the v3 spaceless names, the short NF form, subfolder nesting, the Debian opentype layout, the old spaced v2 name, and the negative cases. No required changes. Optional, non-blocking (carried over): (1) /nf(?=[-.0-9]|$)/i is case-insensitive with no left boundary, so any filename whose tail is nf (a stray *.conf inside a font folder) counts as a Nerd Font; a case-sensitive /NF(?=[-.0-9]|$)/ would still match JetBrainsMonoNF-Regular.ttf while trimming that, and the spaced short form (MesloLGS NF Regular.ttf) is not matched by the lookahead either way. (2) The test helper could use dirname(filePath) instead of join(filePath, ..). (3) Symlinked font directories are not followed; not a regression, and the fc-list fallback covers fontconfig-registered setups.

@axisrow axisrow left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. This run targets head 5747b82, which adds only an empty retrigger-CI commit on top of the previously approved e1e0a35 (review 5527997048): the tree diff between the two is empty, base a2c1161 unchanged, so the powerline fix and its tests are byte-identical to what was already approved. Verdict stands. The fix extracts the font scan into exported fontFolderHasPowerlineFont (depth cap, hidden-entry skip, per-directory error swallowing) while preserving old semantics, the async path reuses the same code, and the temp-dir tests cover the v3 spaceless names, the short NF form, subfolder nesting, the Debian opentype layout, the old spaced v2 name, and the negative cases. No required changes. Optional, non-blocking (carried over): (1) /nf(?=[-.0-9]|$)/i is case-insensitive with no left boundary, so any filename whose tail is nf (a stray *.conf inside a font folder) counts as a Nerd Font; a case-sensitive /NF(?=[-.0-9]|$)/ would still match JetBrainsMonoNF-Regular.ttf while trimming that, and the spaced short form (MesloLGS NF Regular.ttf) is not matched by the lookahead either way. (2) The test helper could use dirname(filePath) instead of join(filePath, ..). (3) Symlinked font directories are not followed; not a regression, and the fc-list fallback covers fontconfig-registered setups.

axisrow and others added 2 commits October 15, 2026 13:45
Three merge fallouts after merging origin/main into the PR branch:

- upstream-terminal-sanitize: upstream c08a59d drops an 8-bit C1 CSI
  sequence whole (terminals consume it as one unit), so \x9b2J no
  longer leaves 2J as text; expect 'okx'.
- usage-prefetch: the fork's async providers pass a cache handle in
  every fetchUsageData call; the upstream spend-limit test now uses
  the same anyCacheMatcher() as the neighboring assertions.
- compact labels: upstream 6e148cb renamed the session usage label
  from "Session:" to "5h:", so the compact-label probe dropped the
  widget; add the '5h: ' preset.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@axisrow
axisrow merged commit e7e55c7 into main Oct 15, 2026
6 checks passed

@axisrow axisrow left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. The new commit 77af91b fixes three merge fallouts against origin/main, and each checks out against the code on this head: (1) the terminal-sanitize expectation okx matches the merged upstream c08a59d behavior, where the 8-bit CSI is consumed as one complete sequence (skipEscapeSequence sees the 0x9b introducer, 2 as a parameter byte, J as the 0x40-0x7e final byte, and drops the whole run); (2) the usage-prefetch spend-limit assertion now uses anyCacheMatcher, which is defined in the file (line 29) and matches every neighboring assertion for the fork cache-handle argument; (3) the 5h: compact preset matches the actual label the session usage widget now renders (usage-percent-widget config label 5h:), and keeping Session: alongside it is correct because SessionClock still renders that prefix. CI on this head is green (Build, Lint & Type Check, Test, both runs). The powerline fix itself is unchanged since review 5528674718. No required changes. Optional, non-blocking: 5h: -> 5h: only trims the trailing space, the point is keeping the widget covered by the compact-label probe, same as the existing Mem: -> Mem: entry; fine as is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants