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
Check · Review · Land · Live done — Landed and live in production.
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
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=0to the existing/:owner/:name/settings/archive. That endpoint takes0or1, notfalse, so I kept its format. Rows the viewer can't administer get no form. The form hangs under the row, wired through a new optionalunderread insrc/web/studio/shelf.tsand built bywakeFormsinsrc/web/pages/apps.ts. - Return address: the archive handler in
src/web/pages/settings.tsnow reads thetofield.to=repostill 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.
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=0to the existing/:owner/:name/settings/archive. Rows the viewer can't administer get no form. - Return address: the archive handler reads a
tofield.to=repostill 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
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.
- Fix first:
wakeFormswas inserted betweenappsList's JSDoc andappsList, so that doc ('the viewer gets the public shelf, and a reader who cannot write never sees crew or cost') now describeswakeForms. MovewakeFormsabove that comment. - 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-actionsadds 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'strail. - Two redundant lines in the test: the
new URL(...)pair on one very long line should be a singlenew URL(location, url)read into a const.
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:
wakeFormsnow sits aboveappsList's JSDoc, so that comment describesappsListagain. - TOUR.md:
docs/TOUR.mddid not exist on this branch because it was behindmain, so I mergedmainfirst. 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-actionsclass 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
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.
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
Wakeplus a visually hiddenowner/name, using the existingsr-onlyclass. That is inwakeFormsinsrc/web/pages/apps.ts. - Test: the wake test in
test/apps-archived.test.tsnow 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. 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.
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.
Each row on
/apps?archived=1that the viewer administers now has a smallWakeform. 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 (thebuntypes aren't installed, andtscreported nothing else for the files I changed), so the forge's check is the first real run of it.design-lintprinted "no colour, size or component added outside the system".archived=0to the existing/:owner/:name/settings/archive. That endpoint takes0or1, notfalse, so I kept its format. Rows the viewer can't administer get no form. The form hangs under the row, wired through a new optionalunderread insrc/web/studio/shelf.tsand built bywakeFormsinsrc/web/pages/apps.ts.src/web/pages/settings.tsnow reads thetofield.to=repostill 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.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 pressWake.Closes #6086