Skip to content

🔗 feat(link-styling): add per-profile underline style and hover/active colours for OSC 8 hyperlinks#650

Draft
kud wants to merge 1 commit into
gnachman:masterfrom
kud:feat/link-underline-style-picker
Draft

🔗 feat(link-styling): add per-profile underline style and hover/active colours for OSC 8 hyperlinks#650
kud wants to merge 1 commit into
gnachman:masterfrom
kud:feat/link-underline-style-picker

Conversation

@kud
Copy link
Copy Markdown
Contributor

@kud kud commented Apr 15, 2026

Overview

Add per-profile customisation for OSC 8 hyperlinks beyond colour. Previously iTerm2 only allowed users to set a single link colour per profile. This PR introduces:

  • Link underline style picker — per-profile selection of underline styles (Single / Double / Dashed / Curly / Dotted) for OSC 8 hyperlinks. Previously hardcoded to dashed-single.
  • Hover colour — optional per-profile colour applied when Cmd+hovering over a link (KEY_LINK_HOVER_COLOR, opt-in with checkbox).
  • Active colour — optional per-profile colour applied whilst Cmd+clicking a link (KEY_LINK_ACTIVE_COLOR, opt-in with checkbox).

Changes

Profile Keys & Defaults (5 new)

  • ITAddressBookMgr.h — added KEY_LINK_UNDERLINE_STYLE, KEY_LINK_HOVER_COLOR, KEY_LINK_HOVER_COLOR_ENABLED, KEY_LINK_ACTIVE_COLOR, KEY_LINK_ACTIVE_COLOR_ENABLED
  • iTermProfilePreferences.m — registered defaults, help text, type registration

Colour Map & Rendering

  • iTermColorMap.h/.m — added kColorMapLinkHover and kColorMapLinkActive (extended colour slots 2 and 3)
  • iTermColorPresets.m — included new colour keys in preset serialisation list
  • iTermAttributedStringBuilder.m — unified hover colour check across Metal and non-Metal paths

Metal Rendering

  • iTermMetalPerFrameStateConfiguration.h/.m — added underlineStyle field and hover/active colour flags
  • iTermMetalPerFrameStateRow.h/.m — per-row snapshot of active link range (mirrors existing hover tracking)
  • iTermMetalPerFrameState.m — bounds-checked underline style substitution with three-way colour priority (active > hover > default)

Session & View Logic

  • PTYSession.m/.swift — colour table wiring and terminalOnlyKeys configuration
  • PTYTextView+ARC.m / PTYTextView.m — set/clear active link range on Cmd+mouseDown/mouseUp
  • iTermTextDrawingHelper.h/.mactiveLinkRange property and activeLinkRangeOnLine: method

UI

  • ProfilesColorsPreferencesViewController.m — IBOutlets and control registration for the style picker and colour wells (xib wiring required — see Known Gaps below)

Tests

  • ModernTests/LinkColourMapAndActiveRangeTests.swift — 21 unit tests covering colour priority, range tracking, and style substitution

Validation

To test the feature:

  1. Open Profiles → Colours tab
  2. (After XIB wiring is complete) Select a link underline style from the popup
  3. Hover over a link with Cmd held — the selected underline style should appear
  4. Enable "Hover" colour and pick a colour — Cmd+hover should show that colour instead of the default
  5. Enable "Active" colour and Cmd+click (hold briefly) on a link — the active colour should appear whilst clicking

The three-way colour priority ensures: active colour (when clicking) > hover colour (when hovering) > default link colour.

Known Gaps

The Interfaces/PreferencePanel.xib changes are not included in this commit. The xib requires Xcode Interface Builder to wire:

  • One NSPopUpButton for the underline style selector
  • Two NSColorWell controls for hover and active colours
  • Two NSButton checkboxes to enable/disable hover and active colours

All IBOutlet declarations and defineControl: registrations are in place in ProfilesColorsPreferencesViewController.m — only the visual controls in the xib file are pending.

Notes

  • Underline style substitution is bounds-checked to ensure invalid values fall back to the default dashed-single style
  • The colour priority system means active colour always takes precedence over hover, preventing visual confusion during click events
  • Both hover and active colours are opt-in (off by default) to preserve existing behaviour unless explicitly enabled per profile

@kud
Copy link
Copy Markdown
Contributor Author

kud commented Apr 18, 2026

Still working on it.

Draft: not ready to present it to reviewers, but gives information that I'm working on a feature.

@gnachman
Copy link
Copy Markdown
Owner

There are a number of underlines. In addition to the ones set by SGR control sequence, they are also used for annotations, semantic history, and OSC 8 hyperlinks. There could be others I'm forgetting. Customizing colors is simple enough but customizing the shapes is riskier - you could easily end up with ambiguous cases. I believe there's a special code path for semantic history on already-underlined text that shows a mixed sold/dash underline.

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.

2 participants