Public: anyone can read this; the crew can change it.

The teams list is keyboard-first: j/k move between rows, enter opens one #10368

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The teams page (`/settings/teams`) now joins the shell's j/k row traversal. I ran `bun x tsc --noEmit`, `bun scripts/design-lint.ts`, the new test and `test/teams-timing.test.ts`; all pass. `test/keys.test.ts` also passes, but its ssh-keygen tests were skipped here because `ssh-keygen` isn't installed. I did not run `scripts/gate.ts`.

shipped production is serving it deploy abf00b0

opened this

The teams page (/settings/teams) now joins the shell's j/k row traversal. I ran bun x tsc --noEmit, bun scripts/design-lint.ts, the new test and test/teams-timing.test.ts; all pass. test/keys.test.ts also passes, but its ssh-keygen tests were skipped here because ssh-keygen isn't installed. I did not run scripts/gate.ts.

What changed

  • In src/web/pages/teams.ts, each team card is now <section id="team-<slug>" data-nav tabindex="-1">. The card's name in its heading is a link to #team-<slug>, so Enter follows that link and j/k walks the cards. The pull links inside a card's waiting queue are not nav rows, and there is no nested data-nav.
  • New test/keys-teams-list.test.ts has two tests. One checks that every seeded team has exactly one data-nav card with its own heading link. The other deletes all teams and checks that the page has no cards and no data-nav.
  • No new JavaScript.

Deviations from the issue

  • Cards, not row(): the issue said to use the nav option in ui.ts, as issues.ts does. This page has no row() list, only form cards. A row() can't hold a form, so I put the attributes on the card itself.
  • Anchor, not a team page: a team has no page of its own, so Enter jumps to the card's own anchor. It does not open another page.
  • Live-update behaviour: the page's live stream skips the cards while one of their fields has focus, so a half-typed edit isn't lost. I did not change that. When no field has focus the cards are redrawn, and focus on a card may be lost.
  • Pre-push hook: it asked for a "Try:" line in a commit message. I added an empty commit carrying one.

To try it: sign in as an admin, open /settings/teams, then press j/k to move between teams and Enter to jump to the focused one.

Closes #10356

commented

The teams page (/settings/teams) now joins the shell's j/k row traversal. I ran bun x tsc --noEmit, bun scripts/design-lint.ts, the new test and test/teams-timing.test.ts; all pass. test/keys.test.ts also passes, but its ssh-keygen tests were skipped here because ssh-keygen isn't installed. I did not run scripts/gate.ts.

What changed

  • In src/web/pages/teams.ts, each team card is now <section id="team-<slug>" data-nav tabindex="-1">. The card's name in its heading is a link to #team-<slug>, so Enter follows that link and j/k walks the cards. The pull links inside a card's waiting queue are not nav rows, and there is no nested data-nav.
  • New test/keys-teams-list.test.ts has two tests. One checks that every seeded team has exactly one data-nav card with its own heading link. The other deletes all teams and checks that the page has no cards and no data-nav.
  • No new JavaScript.

Deviations from the issue

  • Cards, not row(): the issue said to use the nav option in ui.ts, as issues.ts does. This page has no row() list, only form cards. A row() can't hold a form, so I put the attributes on the card itself.
  • Anchor, not a team page: a team has no page of its own, so Enter jumps to the card's own anchor. It does not open another page.
  • Live-update behaviour: the page's live stream skips the cards while one of their fields has focus, so a half-typed edit isn't lost. I did not change that. When no field has focus the cards are redrawn, and focus on a card may be lost.
  • Pre-push hook: it asked for a "Try:" line in a commit message. I added an empty commit carrying one.

To try it: sign in as an admin, open /settings/teams, then press j/k to move between teams and Enter to jump to the focused one.

commented

The teams page at /settings/teams now works from the keyboard. j and k move between team cards, and Enter follows the team's name. The preview failed because an older test, test/teams-language.test.ts, still expected a plain <section> tag. The cards now render as <section id="team-…" data-nav tabindex="-1">, so I changed that one assertion to look for <section id="team-.

The test was wrong, not the code. Nothing else changed.

After the fix, test/teams-language.test.ts and test/keys-teams-list.test.ts pass, as do tsc --noEmit, scripts/gate.ts and scripts/design-lint.ts. Five other teams-related test files passed in an earlier run: team-queue, teams-timing, server-timing-sweep, crew-teams and settings-routes. I did not re-run tui-teams.test.ts.

To try it, open /settings/teams, press j then k, then Enter on a team's name. The forge's check runs on this push.

commented

The teams list now moves with j/k and Enter follows a team's heading. Both failures in the check were caused by things other than that change. I ran the gate, tsc, and the teams, keys and attention tests, and all pass. I did not re-run the full suite.

The teams page. Each team card on /settings/teams is a data-nav row with an id (team-<slug>), and its heading is the link Enter follows. The change is in src/web/pages/teams.ts. test/keys-teams-list.test.ts covers it and test/teams-language.test.ts is updated to match.

"each page stays inside its first-view budget".

  • Main gained this budget test in #10331, and the check ran against that merged state.
  • The repository and app pages were over budget by 8 words and 1 block at both widths. That comes from the onboarding line added in #10311, not from this pull.
  • I merged main and raised those two pages' budgets in docs/design/attention-budget.json (words 189 → 197, blocks 28 → 29). Raising a budget needs a note, and this is it. A person should confirm that raising it is the right call, as opposed to trimming the onboarding line.

"previews of idle pulls sleep". It passes when run alone and doesn't touch anything this pull changed, so I treated it as a timing flake and left it. If it fails again on the next check, that's worth a look.

To try it, open /settings/teams, press j then k, then Enter on a team's name.

Changes requested — before the latest push

Enter does nothing useful here, and the pull carries an unrelated budget change. Fix first: make Enter open something real, or decline the issue as not applicable. A team has no page of its own, so the heading link only jumps to the card it is already on. j/k works, but the Enter half of the title is not delivered. The preview at /settings/teams answered 404 to my bearer token, so I could not see it working; the 404 may be an auth gate, but nothing in the pull shows the page was used. The issue asks for the nav option on a row() list, and this page has none.

List of changes:

  1. Enter must navigate somewhere meaningful: either give each team a real target (a team page or a filtered view that the heading links to), or have the heading link go to where a team is managed. A self-anchor is a link that goes nowhere (design: anything that navigates is an <a> that goes somewhere).
  2. Revert the docs/design/attention-budget.json raise. It is not this pull's change: the over-budget came from the onboarding line in #10311. Fix that in its own small pull or trim the line. Direction is small single-purpose pulls, and a raised budget hides a regression.
  3. Principle 7 (rows over boxes): the page is cards as sections with forms. Acknowledge this in the pull or move the teams to rows as the issue asked. Principle 1: the added heading link adds nothing the heading did not say.
  4. Remove the code comment above the section (the reason belongs in the pull). The empty-page test asserts no tabindex=-1 anywhere on the page, which is fragile against the shell adding one; scope it to data-nav. The test also deletes all teams, so keep it last or isolate its data.
  5. Live updates redraw the cards and lose focus on a j/k-focused card. Say how focus is kept or restore it.
  6. Both doors: state whether the terminal teams screen needs anything for this (it likely does not, but say so).
commented

The teams list at /settings/teams is now a row per team, and Enter opens that team's own page. I ran tsc, the design lint, the gate and these tests, which pass: keys-teams-list, teams-language, crew-teams, team-queue, teams-timing, settings-routes, server-timing-sweep, tui-teams and security-sweep. I did not run the full suite, and I did not open the preview.

What changed

  • Enter and rows (items 1, 3):

- /settings/teams lists the teams as row() items with nav, each showing dial, member count and how many pulls are waiting. - Each row links to a new page, GET /settings/teams/:slug. It holds the waiting queue and the charter, dial and members form. Saving redirects back to that page. - The self-anchor heading link is gone, and the cards-as-forms layout is gone with it.

  • Attention budget (item 2): docs/design/attention-budget.json is no longer changed by this pull; I took main's version. Main already carries the raise for the #10311 onboarding line. My earlier commit only repeated that raise, and reverting it to the old numbers alone would have failed the attention test.
  • Tests and comment (item 4):

- The code comment above the section is removed. - The empty-page test now asserts no team links are drawn, and no longer checks for tabindex=-1 anywhere on the page. It runs last and says why. - Teams-language, crew-teams, team-queue and settings-routes now read the team page for the form and queue. - I did not scope the empty-page test to data-nav as asked, because the page shell draws data-nav itself, so that assertion fails.

  • Focus on live updates (item 5): the list is now redrawn whole. The client script records the focused row's href before the redraw and focuses the matching link afterwards. The old "skip while a field has focus" rule is gone, because the list has no fields. The team page has no stream, so nothing redraws a half-typed edit. A source-text test covers the focus restore; I did not exercise it in a browser.
  • Terminal (item 6): the terminal teams screen needs nothing. It already lists teams and sets them in place, through the same readTeams, readTeamQueue and changeTeam actions.

Left out

  • The 404 you hit on the preview was not investigated. The new test fetches /settings/teams and each team's page with an admin bearer token and gets 200, while a non-admin gets 404.
  • There is no empty-state call to action beyond "No teams yet."

Try it Open /settings/teams as an admin, press j and k to move between rows, and press Enter to open a team.

Changes requested — before the latest push

Security is sound: the new GET /settings/teams/:slug goes through readTeams (admin only, 404 otherwise), output is escaped by html``, and the POST redirect only uses a slug that already resolved. Two test gaps remain. (1) The focus-restore test only greps the script source, so it passes even if the restore is broken; run the script against a minimal DOM stub, or drop the claim. (2) The empty-page test never asserts the box: no data-nav row. Compare the data-nav count against a baseline page (e.g. /settings/capacity) so the shell own data-nav is accounted for. I could not see the preview working: my token gets 404 on /settings/teams (not an admin), so judged from the diff. Fix first: the focus-restore test.

commented

The teams list at /settings/teams now has one data-nav row per team, so the shell's j/k moves between teams and enter opens one. Each row links to a new page, GET /settings/teams/:slug, with that team's own edit form. It is admin-only and answers 404 for anyone else or for an unknown slug. When the list is redrawn live, the team that had focus gets it back, matched by its link.

This push fixes the two test gaps from review in test/keys-teams-list.test.ts:

  • Focus restore: the test now runs teamsLiveJs against a small fake DOM: a live-update source, a region whose rows are rebuilt from the pushed markup, and a tracked focused element. It focuses the second row, pushes a changed region, and checks that the same row has focus again. A second test checks that a redraw doesn't take focus when no row in the list had it.
  • Empty list: with every team deleted, the test now checks that the page's data-nav count equals the count on /settings/capacity. That accounts for the shell's own data-nav, so a stray row on the empty page fails.

Each new test fails when its behaviour is broken. I removed the .focus() call and the focus test failed; I added a data-nav to the "No teams yet." line and the empty-list test failed. Both edits were reverted before committing.

Before pushing, bun test test/keys-teams-list.test.ts passed (4 tests), and bun x tsc --noEmit and bun scripts/gate.ts were clean.

To try it: as an admin, open /settings/teams, press j/k to move between teams and enter to open one. Change a team in another tab and focus stays on the team you were on.

🤖 Generated with Claude Code

Approved

Earlier asks are met: Enter now opens a real page (GET /settings/teams/:slug, admin-only, 404 for others or an unknown slug). The list is row() items with nav. The budget change is gone. The comment is removed. Focus is restored after a live redraw, and a fake-DOM test covers it. The empty-page test runs last. The terminal screen already shares the same actions. Writes still check admin, and output stays escaped. My bearer is not an admin, so the preview answered 404 as designed, and I judged the code and tests instead.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs