Skip to content

feat(github-actions-grafana-jump)!: page-based jump links - every entry is just a name and a URL - #45

Merged
nsheaps merged 3 commits into
mainfrom
n8bot/page-based-jump-links
Aug 8, 2026
Merged

feat(github-actions-grafana-jump)!: page-based jump links - every entry is just a name and a URL#45
nsheaps merged 3 commits into
mainfrom
n8bot/page-based-jump-links

Conversation

@nsheaps-oura

@nsheaps-oura nsheaps-oura commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

What this changes

Two things, together, because they only make sense together.

1. Every config entry is now just a link. A name you word however you like, and a URL you write yourself with {{field}} placeholders in it. The type: dashboard / type: trace split is gone, along with the top-level baseUrl and the two URL builders those types fed. A Grafana dashboard link and a Tempo trace link are now both plain URLs — you paste the URL you already have and swap the parts that vary for placeholders.

2. The config is organized by page instead of by guesswork. It used to infer where a link belonged from which fields its URL happened to use. Now the top level is a list of pages, each with its own links, and a link only shows up on the page kind it's listed under.

pages:
  - page: pr
    links:
      - name: CI for this PR
        url: https://grafana.example.com/d/ci/ci?var-pr={{prNumber}}
  - page: branchList
    links:
      - name: CI
        url: https://grafana.example.com/d/ci/ci?var-branch={{branch}}

Where the links actually show up

flowchart TD
    A[Page loads or DOM mutates] --> B{Which kind of page?}
    B -->|PR list, branch list| C[One small link per row,<br/>revealed on hover or focus,<br/>scoped to that row]
    B -->|Has a known toolbar| D{Toolbar found?}
    B -->|No toolbar entry| F[Floating button,<br/>bottom right]
    D -->|yes| E[Primer-styled buttons<br/>in GitHub's own toolbar]
    D -->|no| F
Loading

Previously this was a single hardcoded special case: pull request pages got an inline button, and everything else floated. Now each page kind has its own toolbar selector, and the floating button is the fallback rather than the norm. The old PR selector [class^="gh-header-actions"] was dead — that header is [class^="prc-PageHeader-Actions-"] now.

New page kinds

Six pages had no representation at all before: the repo home page, a file view, the pull request list, the branch list, the Actions tab itself, and a single job within a run. That last one used to be folded into the run page, which is why jobId was an optional field on it; they're separate kinds now.

The two list pages work differently from the rest — instead of one button for the page, each row gets its own link pointing at that row's own PR or branch, revealed on hover or keyboard focus. That's the same mechanism Primer uses for its own trailing row actions (:hover, :focus-within on the row), and it fades rather than hides so the links stay in the tab order.

Also in here

  • Fixed: GitHub's React header throws the injected container away. It re-renders without the URL changing, and the buttons never came back (seen live on a workflow run page). Placement is now re-checked on every DOM mutation, and the container's contents carry a signature so re-rendering identical content is a no-op — otherwise the write would itself be a mutation and loop.
  • Fixed: the stale comment above CONTAINER_ID still describing the old "PR gets inline, everything else floats" rule.
  • The docs can't drift. The page/field reference at the top of .github/jump-links.config.yaml is generated from the code, and a test asserts the file matches. Two more drift tests: every declared page kind has to be reachable from resolveJumpContext(), and this repo's own checked-in config has to still parse with all its links surviving the placeholder gate.
  • Storage key bumped to jumpLinksConfig.v2. Nothing reads v1, so an existing personal config is left alone rather than half-migrated into a shape it can't express.

Breaking

Configs in the old targets: format aren't read any more. Repo configs need rewriting in the pages: format; personal configs need re-entering through the config panel. Renaming the package away from its "Grafana" branding is deliberately not in here — that's its own change.

How this was verified

Code-correct: typecheck clean, 58 unit tests passing, build script runs, oxlint clean for this package (the warnings it prints are pre-existing, in other packages).

Live on real github.com pages, with the compiled build injected into the page and a fake config — repo home, file view, PR list, branch list, one workflow's runs, and a run. Buttons landed in the right toolbar with the right text and the right filled-in URLs; per-row hover links appeared on only the hovered row with that row's own PR number / branch name. Not verified live: the pull request page itself (its toolbar selector is unexercised), and the Actions tab's own landing page. A userscript manager is installed in the browser I used, but it runs the released version and I couldn't repoint it at this build, so all of the above was injected by hand rather than loaded as a userscript.

One thing to know before merging: yarn install --immutable already fails on main - the root package.json pins typescript at ~6.0.3 while yarn.lock resolves ~7.0.2. That predates this branch and nothing here touches either file, but it will show up in CI.

Co-Authored-By: Claude noreply@anthropic.com

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

Userscript Version Preview

Preview only — versions and CHANGELOGs are bumped automatically on merge to main, not in this PR. Manual bumps to a higher version are preserved.

Package Base New Action
github-actions-grafana-jump 0.2.6 0.2.7 will-bump

nsheaps-oura and others added 3 commits August 7, 2026 22:51
Replaces the old `targets:` list (each entry a `type: dashboard` or
`type: trace` with a `baseUrl` and a set of var-name mappings) with a
`pages:` list, one entry per kind of GitHub page, each holding plain
name + URL links.

The header now documents every supported page kind, which GitHub page
each name means, and exactly which `{{field}}` placeholders that page
provides. That field list is generated by pageFieldsReference() in the
userscript source and asserted against this file by a test, so it can't
drift from the code.

Co-Authored-By: Claude <noreply@anthropic.com>
…k, organized by page

Every config entry is now just a name and a URL you write yourself. The
`type: dashboard` / `type: trace` distinction, the top-level `baseUrl`,
and the Grafana/Tempo URL builders they fed are all gone - a Grafana
dashboard or a Tempo trace link is now simply a full URL with
`{{field}}` placeholders in it, same as any other link.

The config is also reorganized around pages instead of inferring where a
link belongs from which fields its URL happens to use. The top level is a
list of pages, each with its own links, and a link only ever shows up on
the page kind it's declared under.

Alongside that:

- Adds six page kinds that had no representation before: the repo home
  page, a file view, the pull request list, the branch list, the Actions
  tab itself, and a single job within a run (previously folded into the
  run page, which made `jobId` an optional field on it).
- Replaces the hardcoded pull-request-only inline placement, and the
  "float unless this is a PR" rule around it, with a per-page-kind
  toolbar selector table. Each page's buttons go in that page's own
  GitHub toolbar, styled as Primer buttons; the floating button remains
  the fallback for a page with no toolbar entry or whose toolbar isn't
  found. The old `[class^="gh-header-actions"]` PR selector was dead -
  that header is now `[class^="prc-PageHeader-Actions-"]`.
- The two list pages instead get a small link per row, revealed on hover
  or keyboard focus, scoped to that row's own PR number or branch name -
  the same mechanism Primer uses for its own trailing row actions.
- Keeps the injected container in place across GitHub's own React
  re-renders, which discard it without the URL changing (seen on a
  workflow run page). Placement is re-checked on every DOM mutation, and
  the container's contents carry a signature so re-rendering identical
  content is a no-op rather than a mutation that triggers another pass.
- Bumps the stored-config key to `jumpLinksConfig.v2`; nothing reads the
  v1 value, so an existing personal config is left alone rather than
  half-migrated into a shape it can't express.

The package name and its "Grafana" branding are deliberately untouched
here; renaming it is its own change.

BREAKING CHANGE: personal and repo configs in the old `targets:` format
are no longer read. Repo configs need rewriting in the new `pages:`
format, and personal configs need re-entering through the config panel.

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

Replaces the dashboard/trace URL-builder tests with tests for what the
script actually does now: the new config parse/normalize/serialize path,
the six new page-kind URL parsers, per-page field availability, and the
placeholder gate that hides a link whose URL wants a field its page
doesn't provide.

Three of these exist to catch drift rather than to test logic:

- every page kind declared in JUMP_PAGE_KINDS is reachable from
  resolveJumpContext(), so adding a kind without a parser fails
- the generated field reference matches each page's actual fields
- this repo's own checked-in .github/jump-links.config.yaml still parses,
  and each page's links all survive the placeholder gate

Co-Authored-By: Claude <noreply@anthropic.com>
@nsheaps
nsheaps force-pushed the n8bot/page-based-jump-links branch from a6b972f to afeb7b7 Compare August 8, 2026 02:51
@nsheaps
nsheaps merged commit 7e4096d into main Aug 8, 2026
5 checks passed
@nsheaps
nsheaps deleted the n8bot/page-based-jump-links branch August 8, 2026 02:52
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