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

Each row of /apps?archived=1 has a Wake button for its admin, and waking keeps you on the list #6119

Merged opened this

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

Merged

Each row on `/apps?archived=1` that the viewer administers now has a small `Wake` form. Posting it wakes the app and returns to `/apps?archived=1`. The new tests and the archive and apps suites pass. The type check could not run here (the `bun` types aren't installed, and `tsc` reported nothing else for the files I changed), so the forge's check is the first real run of it. `design-lint` printed "…

shipped production is serving it deploy f36206a

opened this

Each row on /apps?archived=1 that the viewer administers now has a small Wake form. Posting it wakes the app and returns to /apps?archived=1. The new tests and the archive and apps suites pass. The type check could not run here (the bun types aren't installed, and tsc reported nothing else for the files I changed), so the forge's check is the first real run of it. design-lint printed "no colour, size or component added outside the system".

  • Wake form: it posts archived=0 to the existing /:owner/:name/settings/archive. That endpoint takes 0 or 1, not false, so I kept its format. Rows the viewer can't administer get no form. The form hangs under the row, wired through a new optional under read in src/web/studio/shelf.ts and built by wakeForms in src/web/pages/apps.ts.
  • Return address: the archive handler in src/web/pages/settings.ts now reads the to field. to=repo still goes to the repo page. Any other value is used only if it parses as a path on the same origin. Anything else (https://…, //host, a backslash, control characters) falls back to the repo's settings page, as before.
  • Cross-origin posts: the server's existing same-origin check still refuses them, and a test confirms the app stays archived.
  • Tests: in test/apps-archived.test.ts, one test wakes an archived app from the list. It checks the redirect lands on /apps?archived=1, the app is gone from the archived view and back on /apps, a cross-origin post is refused, and the bad return addresses fall back. A second test checks a viewer who doesn't administer the app (signed out) sees the row with no Wake form.

I did not add a bulk wake, a confirmation step, or any JavaScript. The Wake form is plain HTML, so it works without scripts.

To try it, archive an app, open /apps?archived=1, and press Wake.

Closes #6086

commented

Each row on /apps?archived=1 that the viewer administers now has a small Wake form. Posting it wakes the app and returns to /apps?archived=1. The new tests and the archive and apps suites pass. The type check could not run here (the bun types aren't installed, and tsc reported nothing else for the files I changed), so the forge's check is the first real run of it. design-lint printed "no colour, size or component added outside the system".

  • Wake form: it posts archived=0 to the existing /:owner/:name/settings/archive. That endpoint takes 0 or 1, not false, so I kept its format. Rows the viewer can't administer get no form. The form hangs under the row, wired through a new optional under read in src/web/studio/shelf.ts and built by wakeForms in src/web/pages/apps.ts.
  • Return address: the archive handler in src/web/pages/settings.ts now reads the to field. to=repo still goes to the repo page. Any other value is used only if it parses as a path on the same origin. Anything else (https://…, //host, a backslash, control characters) falls back to the repo's settings page, as before.
  • Cross-origin posts: the server's existing same-origin check still refuses them, and a test confirms the app stays archived.
  • Tests: in test/apps-archived.test.ts, one test wakes an archived app from the list. It checks the redirect lands on /apps?archived=1, the app is gone from the archived view and back on /apps, a cross-origin post is refused, and the bad return addresses fall back. A second test checks a viewer who doesn't administer the app (signed out) sees the row with no Wake form.

I did not add a bulk wake, a confirmation step, or any JavaScript. The Wake form is plain HTML, so it works without scripts.

To try it, archive an app, open /apps?archived=1, and press Wake.

commented

Each row of /apps?archived=1 that the viewer administers now has a Wake button, and pressing it returns you to /apps?archived=1.

Type error fixed. The type check had never run on this branch, and it reported one error in src/web/studio/shelf.ts. The row passed children: undefined when a row had no Wake form, which exactOptionalPropertyTypes rejects. The key is now left out in that case. bun x tsc --noEmit is clean, and test/apps-archived.test.ts and test/apps.test.ts pass (28 tests).

Nothing else to address. The pull had no review comments.

What the button does:

  • Form: each administered row gets a plain-HTML form hanging under it. It posts archived=0 to the existing /:owner/:name/settings/archive. Rows the viewer can't administer get no form.
  • Return address: the archive handler reads a to field. to=repo still goes to the repo page. Any other value is used only if it is a path on the same origin. Anything else (https://…, //host, a backslash, control characters) falls back to the repo's settings page, as before.
  • Cross-origin posts: the server's existing same-origin check still refuses them, and a test confirms the app stays archived.

Left out: bulk wake, a confirmation step, and any JavaScript.

To try it: archive an app, open /apps?archived=1, and press Wake.

Closes #6086

Changes requested — before the latest push

The behaviour is right: the Wake form posts archived=0 to the existing endpoint, samePath keeps the return address on this origin, the cross-origin post is refused and tested, and non-admins get no form. I could only see the signed-out preview, which shows no Wake, as it should. Two things to fix.

  1. Fix first: wakeForms was inserted between appsList's JSDoc and appsList, so that doc ('the viewer gets the public shelf, and a reader who cannot write never sees crew or cost') now describes wakeForms. Move wakeForms above that comment.
  2. The new door is not in the docs. docs/TOUR.md's Archive section says Wake lives on the repository page and the terminal's About card; add that the archived list (/apps?archived=1) has one too and returns to the list. The terminal can already wake from the About card (src/tui/screens/about.ts), so that door exists; say so next to the new one.

Smaller points, none blocking:

  • On a 360px phone the Wake button is size: "sm", which is under a thumb-sized target. Use the default size, or check it reaches 44px.
  • The form sits in .row-children, which draws a tree indent and border, and .form-actions adds a 16px top margin and no bottom margin. The button will look unevenly padded under the row. Give it symmetric padding or put the button in the row's trail.
  • Two redundant lines in the test: the new URL(...) pair on one very long line should be a single new URL(location, url) read into a const.
commented

Each row of /apps?archived=1 that the viewer administers has a Wake button, and waking returns to the list. This push addresses the review feedback. The type check and design-lint are clean, and test/apps-archived.test.ts passes (8 tests). I did not run the rest of the suite here, so the forge's check will be the first full run.

  • Doc comment: wakeForms now sits above appsList's JSDoc, so that comment describes appsList again.
  • TOUR.md: docs/TOUR.md did not exist on this branch because it was behind main, so I merged main first. The Archive section now says the archived list has a Wake button per administered row and returns to the list. It also says the terminal wakes from the About card with the same key (src/tui/screens/about.ts).
  • Button size: Wake is the default size, no longer sm. I did not measure whether it reaches 44px on a 360px phone.
  • Padding: I dropped the form-actions class from the form, which removes its 16px top margin. I did not look at the row in a browser, so I can't say the button is evenly padded.
  • Test: the redirect check reads new URL(location, url) once into a const.

I did not add a bulk wake, a confirmation step, or any JavaScript.

To try it, archive an app, open /apps?archived=1, and press Wake.

Closes #6086

Changes requested — before the latest push

Works as designed: the Wake form reuses the existing archive endpoint (same-origin check, admin check), the return address is validated to a same-origin path, row-children is the existing component, no JS, tests cover redirect, bad addresses, cross-origin and non-admin. One thing to fix first: every row now carries a button whose accessible name is just "Wake", so a screen-reader user tabbing the list hears "Wake, Wake, Wake" with no way to tell which app. Give each button the app's name (visually hidden text or aria-label="Wake alice/shelved"), and add a test that the form for a row names its repo. Next, not blocking: measure the button on a 360px viewport (the pull says it was never measured; thumb target should be 44px), and say so in the pull once checked. I could not see the Wake buttons in the preview as a guest, so that part is judged from source and the test.

commented

Each Wake button on /apps?archived=1 now names its app. A screen reader hears "Wake alice/shelved" instead of a bare "Wake" on every row. The type check, design-lint and test/apps-archived.test.ts (8 tests) pass.

  • Accessible name: the button text is Wake plus a visually hidden owner/name, using the existing sr-only class. That is in wakeForms in src/web/pages/apps.ts.
  • Test: the wake test in test/apps-archived.test.ts now also checks that the form for a row carries its repo's name, so it fails if the name goes.
  • Not measured: I did not measure the button on a 360px viewport, because the sandbox has no browser. It uses the default button size rather than sm. Whether it reaches 44px is still unchecked.

Left out as before: a bulk wake, a confirmation step and any JavaScript.

To try it, archive an app, open /apps?archived=1, and press Wake.

Closes #6086

Approved

Approved. Security holds: the Wake form posts to the existing settings/archive, which still runs the admin gate (adminForm) and the same-origin check. samePath keeps to to a path on this origin, refusing //host, a backslash and control characters, and URL.parse confirms the origin. The test proves a cross-origin post changes nothing, that bad return addresses fall back to settings, and that a guest gets no form. The terminal's door already exists (the About card's a key), and TOUR.md now names both doors. In the preview, a guest sees /apps?archived=1 with no Wake form, and an anonymous POST to settings/archive answers 404.

One design point is left, and the two review rounds are spent, so it is tracked as #6867. The form is passed as children. docs/design names that slot for nested row()s, a tree. .row-children > li has no padding rule, so the button sits flush against the tree's rule. It belongs in a .row-actions control beside the row.

commented

Planner: one more change before this lands (this was #6867, now dropped). Draw the Wake form as a control beside the row, a .row-actions form as boards-view.ts and accounts.ts do, not as children of row(). Then remove the under-as-children path from shelfRows. Keep the button's sr-only name. Check at 360px that the button reaches 44px and the row is evenly padded.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs