Redesign the Compute tab - #272
Merged
Merged
Conversation
The tab stated everything at one volume: three metered tiles whose captions repeated what their numbers already said, a flat run list where every row read "Modal · succeeded", and a kernel sitting flat on the panel under a boxed callout explaining how kernels work. 3a treats it as an instrument. Free memory — the figure you actually want before starting a run — leads at display size over its unit and ceiling in small mono, beside a twenty-sample sparkline of memory pressure with the newest few in the accent, so the reading carries direction as well as a value. Cores stay discrete because they are countable. The tabs are set in mono capitals in the same voice as those labels, so the strip and the navigation read as one apparatus rather than a readout with tabs bolted underneath. Runs become a numbered ledger under a single RECENT RUNS header, each row an index, the name with its target beneath it, then an uppercase status in a fixed column, then the age. The status is stated on every row rather than only where something is wrong: in a ledger it is a column, and a column with holes is harder to scan than one always filled, because the eye tracks a fixed position instead of hunting for which rows chose to speak. The kernel moves into its own inset card — it is a distinct object with its own controls — with its metrics set as a printed record, label and value joined by a dotted leader. The sparkline is the one genuinely new capability. The compute route reports a point in time and the strip kept nothing, so it now holds a bounded twenty-sample buffer. A poll that failed contributes nothing at all: repeating the last reading would draw a flat stretch that looks like measured calm, and inserting a zero would draw a cliff that never happened. Short of a full window the bars are right-aligned against an empty left, so a freshly opened panel reads as not-yet-measured rather than as a climb from nothing. Every colour reaches the surface through a semantic token — the app ships sixteen themes, and the terracotta, the instrument blue and the status colours all resolve per theme. Verified against a running build: the ledger, the sparkline filling from seven bars to twenty, and every colour moving between light and dark. The logic worth testing is pure and tested apart from the components: host-instruments covers the reading, the bounded sample buffer and the histogram's alignment; run-ledger covers the numbering, the tones and the uppercasing. The tile model they replace is deleted rather than left behind.
…h button The 3a override set the header to two grid columns while the markup still had three children, so the language badge and the name were pushed to opposite ends of the row, the subtitle right-aligned under a title that was itself in the trailing column, and the owner pill wrapped onto a full-width row. Place all three children explicitly: the language becomes an eyebrow at row 1, the title sits beneath it, and the owner pill takes the trailing column. The lifecycle pill gets justify-self so it stops stretching the width of the card, which is a single-column grid. Also removes the panel's refresh control. It was disabled for the duration of every 2.5s poll, and the :disabled opacity rule made it flicker roughly twice a second. The panel already refreshes on that interval and on visibilitychange, so the button asked the user to trigger what was happening anyway; the "Synced Ns ago" line beside the title carries the same information honestly.
The card rendered its whole record at once — an eight-row ledger, the environment block, three controls and an identity list — so a session with more than one runtime became a stack of full records, and the question the list is actually for (which kernels exist, is any of them busy) was buried. 3a collapses it to a head: the eyebrow and name on the left, uptime and a lifecycle pill on the right, and a two-figure usage strip that survives the collapse because "is this runtime in my way" is what a collapsed list still has to answer. The head is the toggle rather than the caret alone — this pane is read on a tablet, where a 9px caret is well under a reliable thumb target. The usage strip draws the kernel's own memory against the host's total and counts the cores it is turning, so both figures need a host reading. That is already on the host strip's poll above, so it is lifted from there through the surface rather than giving every card its own /notebook/compute request; the ceiling a plate shows then always agrees with the headline beside it. Anything unmeasured stays "—" rather than being filled in with a zero that would read as a measurement. Also fixes text overlapping in the environment block. The base rule gives the value `justify-self: end`, which sizes a grid item to its content rather than its column, so `text-overflow: ellipsis` never fired and a working directory longer than the pane overflowed leftward across its own label. Stretching the value to the column is what makes it clip. Two pieces of the old card go with the redesign: the standing paragraph under the controls, whose every sentence is already on the button it describes as a title, and the owner pill for a kernel owned by the session being read, which said only what the panel's heading already says. The pill still appears for a runtime owned by some other session, where nothing else on the card carries it.
"Unavailable" is three times the width of the figure it replaces and says nothing the lifecycle pill beside it does not already say.
The eyebrow sat beside the title group rather than inside it, so the head had three flex children and space-between spread them across the row — which centred the kernel's name instead of setting it under its eyebrow.
…rols on one row The headline read free memory while the histogram beside it plots the fraction in use, so the number counted down as the bars climbed and the two instruments appeared to disagree about the same machine. Both now measure the same thing. The figure is derived by subtraction rather than reported, and a body that carries a total without an available degrades to "—" instead of guessing. The control row was a two-column grid, which stranded Stop on a line of its own — and Stop is the control that discards work, so it read as an afterthought rather than the one to be careful with.
…e jobs total live Three defects with the same shape: something that changes on its own was only ever read when something else happened to change. Uptime froze. The kernel object is reconciled in place, so it does not change while a runtime simply keeps running — nothing re-evaluated the label and the head sat at "2s" for as long as the kernel lived. The card now drives it from its own one-second clock, which exists only while there is something to count. The jobs total beside the tab label froze at whatever it read when the user switched tabs: the surface's poll was gated on the kernels tab, so a project with eleven runs kept reading ten while the eleventh was still going. The jobs panel now reports its own count off the poll it already runs, and the surface polls only when that panel is not mounted — one request for one number. A run that is still moving now sweeps a gradient along the foot of its row. The status column already names it, but a word does not read as motion, and this is the only row in the ledger whose value will change while it is being read. The sweep uses the same warning tone as the status text, so the row says one thing in two ways, and it degrades to a static bar under prefers-reduced-motion. Also splits the network state onto its own line under the environment header. Joined to the sandbox by a middot it made a label long enough to wrap inside its own pill, and it buried the fact on this card that decides what a run can reach. An open network carries the warning tone; a blocked one stays grey, rather than spending attention on the safe and far more common case.
Both numbers on a collapsed plate were live and neither could ever change. Memory was rendered in GB to one decimal because the host ceiling beside it is tens of GB. A Python kernel holds tens of MB, so every kernel that was not a training run read "0.0" for its entire life. It now uses the same adaptive helper the opened ledger does — "16 MB" against "/ 16.4 GB" — so the head and the row beneath it state one measurement in one unit. CPU was converted from the percentage the backend measures into a core count, which is a resolution most kernels never reach: single-threaded Python lives between 0 and 100%, so it read "0 of 8" no matter how hard it was working. The conversion was also internally inconsistent — at 24% the figure rounded to zero cores while the segment bar ceiled to one, so the number and the bar disagreed. The head now states utilisation as the ledger already did, and the segments keep counting cores, which is what makes multi-core work legible at a glance. The regression test drives the reconciled store rather than the helper: these figures hang off a nested `resources` object, and what failed here was never the arithmetic but whether a changed reading could reach the rendered head.
…the opened plate CPU was stated in the unit the backend measures — percent of one core over the kernel's process group — which reads 300 for a kernel across three cores and raises the fair question of how a percentage exceeds 100. It is now divided by the host's core count, so it is a share of the whole machine and cannot. The trade is resolution: a single-threaded Python kernel, which is most of them, tops out at 12.5% of an eight-core box. Hence the decimal, and hence the segments below it keeping the core count that a small share hides. The opened ledger takes the same value rather than reading the field a second time — computing it there independently would print 187.5% beside 23.4% for one kernel. Only the placeholder differs: a lone "—" among three "Unavailable" siblings reads as a different kind of absence rather than the same one. Where there is no core count the share is not stated at all, since passing a per-core figure off as a machine one is wrong by a factor of eight, silently. Also gives the opened body its vertical rhythm. The card is gap: 0 so the divider under the head can run the full width of the plate, and the blocks below carried only horizontal margins — so they stacked flush and the whole record read as one dense paragraph of figures.
…chine Dividing by the host's core count capped the figure at 100%, but it turned a statement about this kernel into one about the machine it happens to be on: the same kernel doing the same work read 23.4% on an eight-core box and 93.8% on a two-core one. The card now states what the backend measures — percent of one core over the kernel's process group, the top(1) convention — so a kernel across two cores reads about 200% and the number means the same thing wherever it is read. It also restores resolution the normalising cost: a single-threaded Python kernel, which is most of them, could never read above 12.5% of an eight-core host however hard it was working. The segments below carry the machine's scale, one per core, so a figure over 100% is legible rather than surprising, and the unit is stated in the group's title instead of being left to be inferred. The figure no longer depends on the host reading at all, so a capacity body that arrives without its cpu section now costs the segments' accuracy and not the kernel's own number.
…f the machine" This reverts commit c0da1aed. A figure above 100% reads as a bug to anyone who has not met the top(1) convention, and that cost outweighs the precision the per-core figure bought. The kernel's CPU is stated as a share of the whole machine again, so it stays between 0 and 100%. The known trade stands: a single-threaded Python kernel cannot read above 12.5% on an eight-core host. The segments below carry what the small figure hides — 23.4% says little, two of eight segments says "two cores". Rounds the share once and derives both the figure and the lit segments from it, which the reverted version did not: reading each independently let a kernel at 0.2% of one core light a whole segment while the figure beside it read 0.0%.
The strip states the machine and every kernel plate states that kernel, and nothing on the surface said which was which. The two are easy to confuse now that both carry a memory figure and a CPU one — and a reader who took the strip's "6.7 GB USED" for their kernel's own would be out by three orders of magnitude. Set in the same mono capitals as the instrument labels beneath it and in the faint tone, so it names the block without competing with the figure it introduces. It is the section's accessible name too, replacing "Host capacity", which was never shown to anyone reading the screen.
origin/main's compute surface names no font at all: PR #269 put the whole app on one sans face at html/body and stripped the mono treatment out. This branch still reached for --font-code in fifteen places, so the surface was the only part of the app in a second face. Dropping the declaration rather than naming --font-family-sans, so these inherit whatever the app is set to and cannot drift from it later. Mono was also supplying tabular figures for free. Inter's are proportional, so the uptime — which now ticks once a second — and the ledger's right-aligned values would reflow a pixel or two on every poll. The three places that show changing numbers ask for tabular-nums explicitly. The capitals and letter-spacing stay: that is the ledger's structure rather than its typeface, and it is what the approved 3a renders show.
origin/main's compute surface uses text-transform nowhere and keeps letter-spacing only as negative tracking on one heading; it renders the kernel state as plain lowercase text. This branch had eight uppercase transforms and twelve letter-spacings, so the surface spoke in a different voice from the rest of the app. Removing the transform alone would not have been enough: several labels were shouted in the source rather than by CSS, and would have kept shouting. The eyebrow, the host strip's Cores label, the memory unit and ceiling, the jobs ledger heading, and the run status and target all now read in sentence case at their source. The ledger's target reads as the server names it — "This computer", "Modal" — rather than being upper-cased on the way to the screen.
…it it The sentence-case pass removed the uppercase and tracking declarations from this surface, but the base rule in atlas.css sets them on `.kernel-card__metric span`, and the cascade resolves per property — so the base declarations kept winning and the ten ledger labels kept shouting. This is exactly why origin/main writes `letter-spacing: 0; text-transform: none` out by name rather than deleting its own declarations. Same fix here, so the branch now matches it in effect and not only in source. The test that was supposed to guard this asserted the stylesheet does not CONTAIN "text-transform: uppercase", which was true the whole time the screen was uppercase — absence of a declaration is not absence of the effect. It now asserts the base rule still sets it and that this file turns it off by name, so the guard fails if either half of that pair moves.
3a pinned the cores gauge to 126px against a memory gauge that took the remaining space. That is close to even in a narrow pane, but this one is read wide, where it left the memory track nearly twice the length of the core segments — two instruments of different sizes rather than one strip. Both are now equal halves. Also removes the .kernel-card__control-note rule, which styles a paragraph this branch deleted along with the standing note under the controls.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The surface moved to the app's sans face by editing stylesheets, but this panel styles inline — so its rules were missed, and the ledger's labels stayed mono while the kernel plate's equivalents beside them did not. The section heading, the ordinal, the target, the status word and the age now use the app's face. Code-shaped content keeps mono, which is what origin/main does too: the command, the log, the working directory, run ids, artifact paths, sha256 digests and the reproducibility values. The ordinal loses mono's fixed advance with this change, so it asks for tabular figures to keep its column straight; the age already did. The test pins both halves by name, since the failure mode here is not that a rule is wrong but that a panel is forgotten.
KB (KB-syntheticsciences)
requested review from
Aayam Bansal (aayambansal) and
Ishaan Gangwani (ishaan1124)
August 7, 2026 15:08
… light themes The lifecycle pill set the status word in the status colour on a 12% tint of that same colour. On a dark ground the tint darkens it and the hue reads clearly; on a light one the tint is near-white and the hue has nothing to separate from. Resolving all shipped themes and measuring the pair gives the scale of it: 1.0:1 on vesper/light — the word and its ground at identical luminance — 1.3 on shadesofpurple, 1.6 on dracula, nord and monokai, and under AA on nearly every light variant. Dark variants were fine throughout, which is why building against one dark palette never showed it. The word is now set in the neutral text colour and the status colour moves to a dot inside the pill, where legibility does not depend on contrast with the text. The tinted ground and the coloured ring still carry the tone. Across 90 readings — every theme, both variants, all three tinted states — the worst case is 3.8:1 and nothing falls below AA-large. Two alternatives were measured and rejected: the design system's own surface-*-weak / text-on-*-base pairs fix warning alone (its on-colour is neutral, which is the whole reason it passes) and leave success and critical where they were.
Member
|
KB (@KB-syntheticsciences) can you ensure that there is minimal consistent typography & like it's not all caps or all lowercase and it is uniform all around! Also, please make sure that the text is not cluttered (too close together).... Check out Claude Science implementation - added screenshot in the openscience fixes docuement! |
Member
|
otherwise, looks good :) |
… the last labels The surface rendered eleven distinct sizes — 9.5, 10, 10.5, 11, 11.5, 12, 13, 13.5, 14, 15, 17 — several of them near-duplicates doing the same job: three for small labels, three for values, three for sub-headings. Differences that small do not read as hierarchy, only as inconsistency. They collapse to 10 label · 12 body · 14 sub-heading · 17 object · 44 display. Casing was mixed on the same card: "Lifecycle" and "Python environment" beside "ready", "bubblewrap sandbox" and "network disabled". The three helpers still returning lowercase now return sentence case, which is where the difference belongs — the acronyms (RAM, CPU, GPU, VRAM) and the code-shaped strings (paths, ids, digests) are deliberately left alone. kernelStateLabel now capitalises the states it used to pass through raw, so "running" and "crashed" match "Ready" and "Not started" rather than only the two that had explicit cases.
Gaps were 1, 3, 4, 5, 6, 7, 9, 10, 12, 16 and 20 with no rule behind which was used where, so grouping read as crowding: a ledger row sat 9px from the next while the block below it sat 16px away, close enough that neither separation said anything. Text now sits on 8 within a group, 12 between rows, 20 between blocks. The values left off that scale are icon-sized and deliberate — 6 between a dot and its word, which reads as pairing rather than separation, and 3 and 4 for the core segments and gauge strips. The plate's own padding is square at 16 rather than 16/16/14, and its head breathes at 16 instead of 12.
Stacked, the unit and ceiling spent three lines saying what one says, and put a column of 10px type against a 44px numeral — the densest corner of the surface. They now read as one phrase beside the figure, in a single muted tone, which is how the Claude Code compute panel sets the same information. Two tones inside one line read as an accident rather than a hierarchy, so the ceiling matches the unit rather than sitting a step quieter.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Redesigns the Compute tab: a host instrument strip, a collapsible kernel plate,
and a numbered run ledger. Everything is inside
src/atlas/, apart from threehelpers added to
notebook/runtime.tswhose only consumer isKernelCard.tsx.What changes for a user
Pytile beside the nameKernel 01 · Pythoneyebrow above itFixes that are not cosmetic
Kernels was selected, so a project with 11 runs kept reading 10. The panel now
reports its own total off the poll it already runs.
justify-self: endsizes agrid item to its content, so
text-overflow: ellipsisnever fired and a longworking directory painted across its own label.
re-evaluated between polls; memory was drawn on a GB scale where a 16 MB
kernel reads
0.0forever.Design decisions worth a second opinion
measures the
topconvention, where a kernel across two cores reads 200%. Thatis divided by the host's core count here so it never exceeds 100%, at the cost
of resolution — a single-threaded kernel tops out at 12.5% on an 8-core box.
The segments below carry the core count that a small share hides.
case, with mono kept only for code-shaped content: commands, log output,
paths, run ids, digests and version strings. That is the same line the rest of
the app draws, and it is why the mono font picker in Settings still applies
where it should.
New modules
host-instruments.ts,kernel-plate.tsandrun-ledger.tshold the arithmeticas pure functions, each with its own tests, so the figures can be asserted
without mounting a component or standing up a server.
host-tiles.tsisreplaced by
host-instruments.ts.Testing
controls end to end, the ticking stopwatch, the jobs ledger and its row
detail, the two gauges at equal width, and no element on the surface rendering
in capitals.
One unrelated stale test, for information
ProjectsWorkbench > locks the screen to the compact sizing contractassertshome-workbench.csscontainsmin-height: 48px; #269 changed that bar to52pxwithout updating the assertion. CI does not exercise it and this branchdoes not touch the file, so nothing here is blocked by it — noted only so it is
not a surprise when running the suite locally.