Skip to content

feat(extensions): show urgent badges on the widget and colour-code Pylon rows - #232

Open
CameronCarlin wants to merge 8 commits into
StackOneHQ:mainfrom
CameronCarlin:feat/SOL-604-widget-glance
Open

CameronCarlin wants to merge 8 commits into
StackOneHQ:mainfrom
CameronCarlin:feat/SOL-604-widget-glance

Conversation

@CameronCarlin

@CameronCarlin CameronCarlin commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

What

This lets an extension opt in to a small chip on the widget. With "glance": true in its manifest, the pill shows that extension's header badge (its tab label, the badge text and trailing), but only while the badge's tone is warning or danger.

An extension doesn't produce anything new: it's the same badge its tab already renders. For example, the Pylon tab's "2 DUE · 12m" becomes visible without opening the panel.

  • Opt-in and additive. glance defaults to false, and hosts that predate it ignore the field.
  • Urgent only. neutral and success badges never show, so a "CLEAR" or a plain count doesn't sit on the widget all day.
  • One chip. The most severe wins, then tab order among equals, and a +N counts the rest.
  • Clicking it opens that extension's tab using the transient expand (expandFromCompact), so widget mode stays on. The expand button, by contrast, leaves widget mode for good.
  • It sheds detail instead of truncating. In sessions mode the pill leaves about 120 pt beside the gauge and headline. The chip uses ViewThatFits, so it shows "Pylon 2 DUE 12m", then "2 DUE 12m", then "2 DUE", and truncates only as a last resort. The tooltip and accessibility label always carry everything.
  • Quiet updates. Glances are published only when they change, so a scheduled refresh doesn't redraw the pill every minute. A glance survives a transient failure, like the document it comes from, and goes when its extension is removed.

Pylon 1.1.0

  • Widget. Pylon opts in with "glance": true, so the widget shows its "N DUE · 12m" or "N LATE" while a first response is due or missed.
  • Colour-coded rows. The bar is the one thing on a row an extension can colour, so it now carries the row's category:
    • First response: green, then amber, then red as the deadline nears, as before.
    • Reply owed: blue, filling toward the next-reply deadline, measured from the customer's last message.
    • With engineering: solid purple.
  • Tickets with engineering (the waiting_on_engineering state) are now their own kind of row.
    • Before this, some looked like ordinary replies owed and the rest weren't shown at all.
    • They're shown whether or not an SLA is running, and however long they've been quiet, so stalled ones get picked up. The search asks for that state explicitly, alongside the 30-day activity window.
    • They sort last, any with a reply due first, then the longest quiet.
    • They're never bannered. The exception is a first response that's still owed, which stays a first-response row with a "with engineering" label.
    • The footnote says "with engineering" too, so the colour isn't the only signal.
  • Owners who are away. If an issue's owner has their Pylon status set to out of office, the row reads "ours (Alex away)" and the issue escalates like an unowned one. That way a first response doesn't sit with somebody who isn't there. The user list is cached for an hour.
  • "close?" on sign-offs. When the customer's latest message is a short closing line, like "thanks, all good" (no question, at most 160 characters), the row shows "close?" with a grey bar and drops out of the badge's owed count. It's a hint only: nothing is closed for you.
  • The filter key is now F, not M. M is mute on StackNudge's list pages, and AGENTS.md asks for one meaning per key.

Extension tab keys

The second pass came from an audit against AGENTS.md's keyboard and testing rules:

  • A held key no longer starts work twice. Every binding spawns the extension. Before this, holding Return on a row that opens a link opened it once per autorepeat, and holding a letter re-ran that letter's action. Held keys are now swallowed. The arrows still repeat, because they only move the selection.
  • Routing is in a pure PanelController.extensionTabKeyAction, with a (for: NSEvent) form, as the Testing rules ask. AGENTS.md's list of those functions now includes it.
  • The chip's click goes through PanelNav.openGlance, so a test can drive the real entry point.

Changes

  • ExtensionManifest: an optional glance field, last and defaulted, so existing memberwise constructions are untouched.
  • ExtensionHost: ExtensionGlance, the derivation (glance(for:pane:)), and publish-on-change through a new onGlancesChanged callback. This is the same pattern as onTabsChanged.
  • PanelNav.extensionGlances, wired in PanelController, and PanelNav.openGlance for the chip's click.
  • Panel.swift: ExtensionTabKeyAction and extensionTabKeyAction, which the extension tab's key branch now calls.
  • CompactView: the chip, in both the sessions and usage layouts, plus glanceToShow as a pure function so it can be tested.
  • docs/extensions.md: the manifest field and a short "The widget" section.

Testing

  • 22 new Swift tests:

    • ExtensionManifestTests: the field is off unless asked for.
    • ExtensionHostTests: opted-in and urgent only, no document means no glance, publish only on change, survives a transient failure, removed with its extension.
    • CompactGlanceTests: severity, tab order and the +N count. A click drives openGlance with a full SettingsActions and checks three things: the transient expand happens before the mode flips, the tab opens, and widget mode isn't left.
    • ExtensionTabKeyTests: raw key codes and real NSEvents. They cover Return and numpad Enter, letters (lowercased), held keys swallowed, arrows repeating, ⌘R once and not while held, other combinations passed through to the app, a key with no character swallowed, and device flags masked.
  • The full CI set, run locally:

    • build.sh for arm64 and x86_64, with 0 compiler warnings
    • 1,491 Swift tests via make test-without-xcode
    • the extension, shell and hook suites
  • Rendering. The real CompactView was rendered offscreen with ImageRenderer for six cases: no glance, warning in sessions mode, danger with +1, usage mode, a very long badge, and light mode. That's how the first version's "Pyl… 2 D… 12m" truncation was caught and replaced with the ViewThatFits fallbacks.

  • Not tested yet: clicking the chip in a running app. I didn't replace the installed signed build with an ad-hoc one to try it.

  • Pylon: 97 offline tests (15 new), on Python 3.14 and 3.9.6. The new ones cover engineering rows, away owners, sign-off hints, and refetching a stale cached preview. A live, read-only check confirmed Pylon accepts the nested and/or filter, at its documented depth limit of 3.

Checklist

@CameronCarlin CameronCarlin changed the title feat(widget): show urgent extension badges on the widget feat(extensions): show urgent badges on the widget and colour-code Pylon rows Nov 2, 2026
Extension tab keys now route through a pure extensionTabKeyAction, per
AGENTS.md's testing rules, and a held key no longer starts work twice:
holding Return reopened links and holding a letter re-ran its action.
Arrows still repeat. The widget chip opens its tab through
PanelNav.openGlance, which uses the transient expand, and both are
covered by tests that drive real entry points and real NSEvents.
An issue whose owner is out of office is labelled "ours (X away)" and
escalates like an unowned one. A customer's short closing message
("thanks, all good") shows "close?" in grey instead of counting as
owed. The mine/everyone filter moves from m to f: m is mute on
StackNudge's list pages, and AGENTS.md asks for one meaning per key.
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.

1 participant