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

A run page's jump, live and steer controls draw §5's bar and btn, and .run-jump, .run-live, .run-steer leave the markup #4367

Merged opened this

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

Merged

scout/forge:scout/4263-a-run-page-s-jump-live-and-steer-control → main 4 commits fork

  • Merged. scout/4263-a-run-page-s-jump-live-and-steer-control was merged into main as a07d4b8.

Review

Sign in to review this change.
4 files changed +45 −16
docs/DESIGN.md +1 −1
@@ -398,7 +398,7 @@ gone, so this list only shrinks.
398398 - **`style/content.ts`, code view** — the source view and the syntax-highlight token classes `highlight-html.ts` builds as `tok-${kind}`: `.blob-view` `.ln` `.tok-attribute` `.tok-comment` `.tok-constant` `.tok-decorator` `.tok-deleted` `.tok-emphasis` `.tok-function` `.tok-heading` `.tok-inserted` `.tok-keyword` `.tok-link` `.tok-meta` `.tok-number` `.tok-operator` `.tok-plain` `.tok-property` `.tok-punctuation` `.tok-regex` `.tok-string` `.tok-tag` `.tok-type` `.tok-variable`
399399 - **`style/content.ts`, prose (markdown)** — table cell alignment, built by `markdown.ts` as `ta-${align}`: `.ta-c` `.ta-r`
400400 - **`style/content.ts`, diff viewer** — the diff table: its gutters, line kinds, file head and inline review comments: `.add` `.del` `.dh` `.diff` `.diff-body` `.diff-head` `.diff-note` `.diff-path` `.diff-stat` `.diff-stub` `.diff-summary` `.diff-table` `.dl` `.dl-review` `.dn` `.dn-comment` `.nonl` `.rc-hidden` `.rc-resolve` `.rc-resolved` `.wd`
401 - **`style/content.ts`, apps, runs, and the crew** — run pages, their logs and checks (a pull's look is §5's **look**): `.ln-forge` `.ln-pulse` `.ln-say` `.ln-tool` `.ln-you` `.log-tail-row` `.pinned-failures` `.pinned-slot` `.run-jump` `.run-live` `.run-steer` `.txt` `.who`
401 - **`style/content.ts`, apps, runs, and the crew** — run pages, their logs and checks (a pull's look is §5's **look**): `.ln-forge` `.ln-pulse` `.ln-say` `.ln-tool` `.ln-you` `.log-tail-row` `.pinned-failures` `.pinned-slot` `.txt` `.who`
402402 - **`style/content.ts`, search results** — search results, the inbox's ask cards: `.ask-acts` `.ask-card` `.ask-group` `.ask-kind` `.ask-meta` `.ask-more-acts` `.ask-pick` `.ask-pick-hit` `.ask-short` `.ask-waiting` `.live-toggle`
403403 - **`pages/board.ts`** — the board itself is §5's **board**; the spend tables `spend.ts` draws wear §5's `.table` and `.stack`, no class of their own
404404 - **`pages/map.ts`** — the map of what waits for what: `.map-ask` `.map-breath` `.map-canvas` `.map-card` `.map-counts` `.map-dot` `.map-edge` `.map-edge-crit` `.map-edge-done` `.map-empty` `.map-list` `.map-main` `.map-meta` `.map-node` `.map-pillars` `.map-rule` `.map-svg` `.map-title` `.map-tool` `.map-tools` `.map-who` `.map-who-i` `.map-who-n` `.map-word` `.map-wrap`
src/web/studio/run.ts +11 −4
@@ -332,7 +332,7 @@ export async function runView(
332332 },
333333 )}
334334 ${vitalsBlock(run, logHidden, looks)}
335 <div class="run-live" id="run-live" data-state="${run.state}">
335 <div class="mt" id="run-live" data-state="${run.state}">
336336 ${when(
337337 // The head above is what makes \`liveRegionsJs\` open the one
338338 // /events connection this log and its finish ride on.
@@ -363,7 +363,14 @@ export async function runView(
363363 }
364364 : {}),
365365 pulse: run.state === "running",
366 extra: html`<button type="button" class="run-jump" id="run-jump" hidden>
366 extra: html`<button
367 type="button"
368 class="btn"
369 data-size="sm"
370 data-variant="primary"
371 id="run-jump"
372 hidden
373 >
367374 newer below
368375 </button>`,
369376 }))}
@@ -375,7 +382,7 @@ export async function runView(
375382 writer && run.kind === "agent" && issue !== undefined,
376383 () =>
377384 html`<form
378 class="run-steer"
385 class="card stack"
379386 id="run-steer"
380387 method="post"
381388 action="${base}/runs/${run.id}/steer"
@@ -386,7 +393,7 @@ export async function runView(
386393 >Say something to ${who}</label
387394 >
388395 <textarea
389 class="input"
396 class="input break"
390397 id="steer-text"
391398 name="text"
392399 rows="1"
src/web/style/content.ts +9 −11
@@ -737,13 +737,13 @@ form.inline { display: inline; margin-left: auto; }
737737 .pinned-failures pre { margin: 4px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--fs-min); }
738738 /* A run, live: a transcript with a speaker column, bounded so the page
739739 head stays reachable, and a composer that sits under it. */
740 .run-live { position: relative; margin-top: 12px; }
740 #run-live { position: relative; }
741741 .run-log {
742742 margin: 0; padding: 10px 14px 8px; max-height: calc(100dvh - 300px); min-height: 12em; overflow: auto;
743743 background: var(--muted); border: 1px solid var(--border); border-radius: var(--radius);
744744 font-size: 14px; line-height: 1.5; overflow-wrap: anywhere; tab-size: 4;
745745 }
746 .run-live:has(.run-steer) .run-log { border-radius: var(--radius) var(--radius) 0 0; }
746 #run-live:has(#run-steer) .run-log { border-radius: var(--radius) var(--radius) 0 0; }
747747 .run-log .ln { display: grid; grid-template-columns: 64px minmax(0, 1fr); gap: 10px; padding: 2px 0; }
748748 .run-log .who { color: var(--faint); font-size: 12.5px; font-weight: 600; padding-top: 2px; user-select: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
749749 .run-log .txt { white-space: pre-wrap; }
@@ -762,17 +762,15 @@ form.inline { display: inline; margin-left: auto; }
762762 .run-log .ln-pulse i:nth-child(2) { animation-delay: .2s; }
763763 .run-log .ln-pulse i:nth-child(3) { animation-delay: .4s; }
764764 @keyframes run-pulse { 0%, 80%, 100% { opacity: .25; } 40% { opacity: 1; } }
765 .run-jump {
766 position: sticky; bottom: 6px; float: right; margin-top: 6px; padding: 4px 10px; font: inherit; font-size: 12.5px;
767 color: var(--bg); background: var(--fg); border: 0; border-radius: 999px; cursor: pointer; box-shadow: var(--shadow-2);
765 #run-jump {
766 position: sticky; bottom: 6px; float: right; margin-top: 6px; border-radius: 999px; box-shadow: var(--shadow-2);
768767 }
769 .run-steer {
770 position: sticky; bottom: 0; display: grid; gap: 6px; padding: 10px 12px; background: var(--bg);
771 border: 1px solid var(--border); border-top: 0; border-radius: 0 0 var(--radius) var(--radius);
768 #run-steer {
769 position: sticky; bottom: 0; gap: 6px; padding: 10px 12px; background: var(--bg);
770 border-top: 0; border-radius: 0 0 var(--radius) var(--radius); box-shadow: none;
772771 }
773 .run-steer textarea { min-height: 38px; max-height: 160px; resize: none; }
774 .run-steer .bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
775 .run-steer .bar .muted { flex: 1 1 200px; }
772 #run-steer textarea { min-height: 38px; max-height: 160px; resize: none; }
773 #run-steer .bar .muted { flex: 1 1 200px; }
776774 /* A tail beside a running row (studio/app.ts): the same log, a peek at it
777775 — fixed height so nothing around it shifts as lines arrive, and it
778776 scrolls itself rather than the page. */
test/studio-runs.test.ts +24
@@ -517,6 +517,17 @@ describe("working: a run's own page in the studio", () => {
517517 expect(page).toContain('data-who="scout"');
518518 expect(page).toContain('id="run-steer"');
519519 expect(page).toContain("Steer scout");
520 // Jump, live and steer draw §5's btn, bar and card (#4263).
521 for (const old of ["run-jump", "run-live", "run-steer"]) {
522 expect(page).not.toMatch(new RegExp(`class="[^"]*\\b${old}\\b`));
523 }
524 expect(page).toContain("newer below");
525 expect(page).toMatch(/<button[^>]*class="btn"[^>]*id="run-jump"/);
526 expect(page).toMatch(
527 /<form[^>]*class="card stack"[^>]*id="run-steer"[^>]*action="\/alice\/engine\/runs\/\w+\/steer"/s,
528 );
529 expect(page).toContain('name="text"');
530 expect(page).toContain('formaction="/alice/engine/runs/');
520531 expect(page).toContain('id="run-stop"');
521532 // The transcript is on the page, and there is no second reading of it.
522533 expect(page).toContain('id="run-log"');
@@ -2295,3 +2306,16 @@ describe("a deploy that just landed is news on the front door", () => {
22952306 * as `canWrite` today — that every surface carrying money, the crew or the
22962307 * decision ledger must ask before it computes or renders one of those.
22972308 */
2309
2310 describe("run page controls leave the old classes behind (#4263)", () => {
2311 test("no run-jump, run-live or run-steer class selector is defined, and [hidden] still beats .btn", async () => {
2312 const { css } = await import("../src/web/style.ts");
2313 for (const old of ["run-jump", "run-live", "run-steer"]) {
2314 expect(css).not.toMatch(new RegExp(`\\.${old}(?![\\w-])`));
2315 }
2316 // The layout is kept under the ids the client script already uses.
2317 expect(css).toMatch(/#run-steer \{[^}]*position: sticky/);
2318 expect(css).toMatch(/#run-jump \{[^}]*position: sticky/);
2319 expect(css).toMatch(/\[hidden\] \{ display: none !important; \}/);
2320 });
2321 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs