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

`.break` and `.tok`'s nowrap are base.ts text utilities, and neither stays in content.ts #4786

Merged opened this

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

Merged

mason/forge:mason/4747-break-and-tok-s-nowrap-are-base-ts-text → main 8 commits fork 2 threads · 2 unresolved

  • Merged. mason/4747-break-and-tok-s-nowrap-are-base-ts-text was merged into main as d663aea.

Review

Sign in to review this change.
6 files changed +27 −8
docs/DESIGN.md −2
@@ -423,7 +423,6 @@ the same line (#4888).
423423 - `.bp-h3`
424424 - `.bp-w2`
425425 - `.bp-w3`
426 - `.break`
427426 - `.commit-body`
428427 - `.commit-head`
429428 - `.filters`
@@ -436,7 +435,6 @@ the same line (#4888).
436435 - `.repo-desc`
437436 - `.repo-title`
438437 - `.slash`
439 - `.tok`
440438 - **`style/content.ts`, apps, runs, and the crew** — run pages, their logs and checks (a pull's look is §5's **look**).
441439 - **`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
442440 - **`studio/feed_style.ts`** — the home feed's cards and its composer (the apps rail is §5's card, row and btn; an admin's tick boxes and Archive are always shown, deliberately, with no Select toggle):
docs/design/areas/text.md +5
@@ -17,6 +17,11 @@ quiet line under a card), and a page reaches for them instead of setting a colou
1717 - `.truncate` — one line that ends in an ellipsis rather than wrapping. Written by `ui.ts` (`envFacts()`'s URL) and
1818 pages such as `pages/apps.ts`, `pages/refs.ts` and `pages/explorer.ts`; styled in `style/base.ts`, capped at 34ch
1919 inside `.table` by `style/content.ts`.
20 - `.nowrap` — keeps a short run (a token count, a figure with its unit) on one line without clipping it. Written by
21 `ui.ts` (`tokenCount()`, with `.num`); styled in `style/base.ts`.
22 - `.break` — lets a long unbroken string (a URL, a hash, a path) wrap anywhere rather than overflow its box. Written by
23 `ui.ts`, `scope.ts` and pages such as `pages/backups.ts`, `pages/origins.ts` and `studio/feed.ts`; styled in
24 `style/base.ts`.
2025 - `.sr-only` — text for a screen reader only: a table's caption, a select's label, a fieldset's legend. Written by
2126 pages such as `pages/audit.ts`, `pages/pull.ts` and `pages/thread.ts`, and by `studio/app.ts` and
2227 `studio/review_act.ts`; styled in `style/base.ts`.
src/web/style/base.ts +2
@@ -166,6 +166,8 @@ export const utilities = /* css */ `
166166 .grow { flex: 1; min-width: 0; }
167167 .spacer { flex: 1; }
168168 .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
169 .nowrap { white-space: nowrap; }
170 .break { overflow-wrap: anywhere; }
169171 .stack { display: flex; flex-direction: column; gap: 12px; }
170172 .stack-tight { gap: 4px; }
171173 .bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
src/web/style/content.ts −2
@@ -150,14 +150,12 @@ a.origin-chip:hover { border-color: var(--border-strong); color: var(--fg); }
150150 font-variant-numeric: tabular-nums; letter-spacing: 0.14em;
151151 text-align: center; user-select: all; word-break: break-all;
152152 }
153 .break { overflow-wrap: anywhere; }
154153 [data-apps] { overflow-wrap: anywhere; }
155154
156155 .bar > .device-code { flex: 1 1 auto; }
157156
158157 .repo-actions { display: flex; align-items: center; gap: 6px; margin-left: auto; }
159158

Nit: removing .tok leaves two blank lines between .repo-actions and .filters. Drop one when you next touch this file.

160 .tok { white-space: nowrap; }
161159

Nit: removing .tok leaves two blank lines in a row between .repo-actions and .filters. Drop one next time this file is touched; it doesn't block.

162160 .filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
163161 .filters form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
src/web/ui.ts +1 −1
@@ -133,7 +133,7 @@ export function tokenCount(
133133 basis: TokenBasis,
134134 kind?: "prose" | "code" | "data",
135135 ): Html {
136 return html`<span class="tok" title="${tokenNote(n, basis, kind)}"
136 return html`<span class="num nowrap" title="${tokenNote(n, basis, kind)}"
137137 >${formatTokens(n, basis)}</span
138138 >`;
139139 }
test/tokens.test.ts +19 −3
@@ -24,6 +24,8 @@ import {
2424 textKind,
2525 tokenNote,
2626 } from "../src/kernel/tokens-format.ts";
27 import { css } from "../src/web/style.ts";
28 import { tokenCount } from "../src/web/ui.ts";
2729
2830 describe("the formatter", () => {
2931 test("writes counts the way storage is written", () => {
@@ -83,6 +85,20 @@ describe("the estimate", () => {
8385 });
8486 });
8587
88 describe("the markup", () => {
89 test("a count wears the base text utilities, not a class of its own", () => {
90 const markup = String(tokenCount(1200, "estimated", "code"));
91 expect(markup).toContain('class="num nowrap"');
92 expect(markup).not.toContain('class="tok"');
93 expect(css).not.toMatch(/\.tok\s*\{/);
94 });
95
96 test("the stylesheet defines .break and .nowrap once each", () => {
97 expect(css.match(/\.break\s*\{/g)).toHaveLength(1);
98 expect(css.match(/\.nowrap\s*\{/g)).toHaveLength(1);
99 });
100 });
101
86102 describe("token counts on the pages", () => {
87103 let dataDir: string;
88104 let app: App;
@@ -199,7 +215,7 @@ describe("token counts on the pages", () => {
199215 const page = await get("/alice/one/blob/main/src/a.ts", cookie);
200216 // 700 B; 700 ÷ 3.5 = 200 tokens.
201217 expect(page).toMatch(
202 /1 lines · 700 B · <span class="tok" title="About 200 tokens — an estimate at 3\.5 bytes per token for code/,
218 /1 lines · 700 B · <span class="num nowrap" title="About 200 tokens — an estimate at 3\.5 bytes per token for code/,
203219 );
204220 expect(page).toContain(">~200 tok</span");
205221 });
@@ -208,14 +224,14 @@ describe("token counts on the pages", () => {
208224 const page = await get("/alice/one/edit/main/data.json", cookie);
209225 expect(page).toContain('id="editor-size"');
210226 // 3000 B = 2.9 KB; 3000 ÷ 3.0 = 1000 tokens.
211 expect(page).toMatch(/2\.9 KB · <span class="tok"[^>]*>~1\.0k tok</);
227 expect(page).toMatch(/2\.9 KB · <span class="num nowrap"[^>]*>~1\.0k tok</);
212228 });
213229
214230 test("a run shows tokens in and out beside its cost, to the owner only", async () => {
215231 const page = await get(`/alice/one/runs/${runId}`, cookie);
216232 // 46,500 in → 47k; 800 out. Counted, so no tilde.
217233 expect(page).toMatch(
218 /id="run-tokens"\s*><span class="tok" title="46,500 tokens, as counted by Claude&#39;s tokenizer"\s*>47k tok<\/span\s*>\s*in ·/,
234 /id="run-tokens"\s*><span class="num nowrap" title="46,500 tokens, as counted by Claude&#39;s tokenizer"\s*>47k tok<\/span\s*>\s*in ·/,
219235 );
220236 expect(page).toContain(">800 tok</span");
221237 const read = await get(`/alice/one/runs/${runId}`, bobCookie);

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs