Skip to content

UI corrections, and the test page that had served its purpose - #68

Merged
rexionmars merged 6 commits into
mainfrom
refactor/ui-corrections-and-feature-removal
Aug 31, 2026
Merged

UI corrections, and the test page that had served its purpose#68
rexionmars merged 6 commits into
mainfrom
refactor/ui-corrections-and-feature-removal

Conversation

@rexionmars

Copy link
Copy Markdown
Owner

Summary

Six corrections to the interface, and the removal of a screen that existed to prove a library.

  • The Globe test page goes. It was built to prove MapLibre could replace the previous map library. The library is in, the proof is done, and the surface it proved — GlobeSurface — stays and is now the studio's own map.
  • The workspace-layout switch leaves the studio. It governs how the map screen is arranged, and the studio covers the map: a control that cannot act on what is in front of the reader is worse than an absent one.
  • The wordmark clears the traffic lights by 1 cm, so the name is not read as part of the window chrome.
  • The Activity grid's idle squares were not faint — they were absent.
  • The display name could be typed into but not changed.
  • The settings entry names who is signed in, rather than labelling the account with the word "account".

The one that was not what it looked like

The idle squares turned out not to be a colour choice. Chasing it into the compiled stylesheet found <alpha-value> — Tailwind v3 syntax — reaching the output literally under v4, which does not substitute it. Fourteen utilities across the application were producing invalid CSS the parser dropped in silence.

The full correction repainted parts of the interface that had been living with the defect and looked deliberate, so it was reverted in favour of a contained fix that reaches the palette channels directly. The token defect is still there, now documented at the site, and is worth its own change with the visual diff in front of a reader.

Testing

gofmt, go vet, the Go suite under -race, tsc, the frontend suite and check:types all pass. Exercised in the window.

golangci-lint is not installed on the machine this was written on, so that part of the repository's gate has not run.

… it proved stays

The Globe was a destination and it was also the test bed. The question it was
built to answer was whether a map library could draw a planet from XYZ tiles,
and the answer arrived: MapSurface and DrawMap now open on the same adaptive
`globe` projection, so the sphere is on every surface that draws a map rather
than on one screen beside them. What the screen kept after that was a fifth
place to go, and it is retired.

WHAT IS REMOVED IS THE DESTINATION, NOT THE GLOBE. `pages/GlobeScreen.tsx`
goes, and with it the `"globe"` member of `AppScreen`, `goGlobe` on the auth
context, the `NavGroup` in `lib/navigation.ts`, the `NavItem` in the navigation
column, and in App the lazy import, the `screen === "globe"` branch, the
`globe` arm of `navigateTo` and the `openFromGlobe` handoff that flew the map
to an area pressed on the planet.

`components/globe/` STAYS, because it has a second consumer. BoardSurface
mounts `GlobeSurface` as the studio's globe editor, registered in
`lib/studioEditors.ts`, and that editor is not part of what is being retired.
The chunk is still built and is still lazy; the whiteboard is now the only
thing that pulls it.

`cameraNavigation.tsx` and `SpaceBackdrop.tsx` also stay. Both were written
for the globe and both were moved to `components/map/` when the work map took
the same projection, which is the move that made this screen redundant in the
first place.

Nothing was persisted that now names a screen that is gone: `screen` is
component state seeded to "map", not a stored preference, so no saved value
can point at the removed destination.

263 lines removed across five files, two added. tsc reports no errors, the 289
frontend tests pass, and the production build completes -- the GlobeSurface
chunk is 4.74 kB and is now reached only from BoardSurface.
…es the traffic lights

Two corrections to the same bar.

THE LAYOUT SWITCH WAS INERT UNDER THE STUDIO, not merely redundant. The board
draws its own 15rem column and the shell's navigation column fights it for the
left edge, so MapScreen pins the layout to workspace for as long as the board
is up and restores the previous mode on close. A press on the switch set
docked, that effect set workspace back on the same beat, and the outcome was
an icon that flickered over a layout that did not move. A control whose only
result is to be undone is one to withhold, so it is now gated on `!boardOpen`
-- a prop the bar already received and already read for the map telemetry.

The way out of the mode is unaffected: closing the board restores the layout
by itself, and the board's own toggle stays in the bar through `boardSlotRef`.

THE MARK KEEPS A CENTIMETRE CLEAR OF THE WINDOW CONTROLS. The bar reserved
`pl-[4.5rem]` for them, which is 72 px, and macOS puts the last of the three
traffic lights at almost exactly that -- so the logotype started where the
green button ended and read as a fourth control. Now `calc(4.5rem+1cm)`.

The unit is deliberate on both halves. The 4.5rem stays a rem because it is
the space the platform's own controls occupy, which is theirs to scale. The
gap is `1cm` because what is being asked for is a physical distance between
two marks a reader sees side by side, and CSS anchors that unit to the
reference pixel, so it does not drift if the root size is ever changed.

Both values stay unconditional. `onMac` is resolved from the runtime after
mount and starts false, so gating either on it would slide the wordmark
leftward on every launch. The consequence is unchanged from before this
commit: on Windows and Linux, where this bar draws the window controls at its
other end, the reserved strip is empty.

tsc reports no errors and the 289 frontend tests pass.
…s the icon

Two images, and the project has been careful about which is which since the
brand landed: the icon is the wordless circle, generated into the .icns and the
.ico and drawn by the title bar and the splash, because it is what the operating
system shows when it has 32 px to show it in. The mark is the hexagonal badge
carrying TERRA and EARTH OBSERVATION, which is what the README opens with and
what the LaTeX manual references.

The paragraph at the foot of settings is the project speaking about itself as a
project, to a reader who might go and look at the repository. That is the mark's
job, so the mark goes there, at 40 px, top-aligned so it does not centre itself
against a sentence that reflows to two lines and back.

COPIED TO public/ RATHER THAN REACHED FOR ACROSS THE REPOSITORY. docs/ is not
under the Vite root and serving from it would mean widening server.fs.allow for
one image. The icon is already carried twice for exactly this reason --
build/appicon.png and public/terra-logo.png are one file in two places -- so
this follows a path the project already takes rather than opening a new one.
The copy is byte for byte identical to docs/img/v4/TERRA_EO_Logo.png.

alt="" because the badge says TERRA and the sentence beside it says TERRA. A
screen reader announcing the name twice reads as two things rather than one:
the text carries the meaning and the image is the signature on it.
A year of runs is mostly days without one, so the empty square is what draws
the shape of the year. It was drawing nothing at all, and so was the first
swatch of the legend under it -- five steps rendering as four.

THE VALUE WAS NEVER THE PROBLEM. `bg-line/55` compiled to
`background-color: rgb(var(--p-line) / <alpha-value>)`, with the placeholder
still in it. That placeholder is Tailwind v3, where the engine substituted it
per utility; v4 copies the declared value through and wraps opacity around it
with color-mix, so the literal string reached the stylesheet and the parser
dropped the rule. An invalid background-color is not a wrong colour, it is no
declaration at all, which is why the cells kept the page behind them and why
nothing reported it.

Fixed here by reaching past the `line` scale to the channels it is built from:
`bg-[rgb(var(--p-line))]`, which is the narrow way around the defect. The wide
way is to drop <alpha-value> from the ten tokens in index.css that carry it,
and that is deliberately NOT this commit: it revives thirteen further utilities
across roughly thirty files -- bg-surface and bg-surface-raised among them --
repainting the navigation column, the buttons and the cards in tones that were
declared but never once rendered. That is a design pass over the app's
surfaces, to be taken on its own terms rather than smuggled in behind a
heatmap. The comment on LEVEL_CLASS records both, so the arbitrary value is not
mistaken for one that wants simplifying back into a dead utility.

TWO VALUES FOR THE IDLE STEP, because the headroom under the first filled step
is not the same in both themes. The filled steps are the accent and the accent
is already darkened for light, so one alpha lands both; the idle step is the
neutral line against the page, and the accent at 45 per cent reaches 2.32 on
the dark background but only 2.01 on the light one. A single alpha bright
enough to be worth looking at in the dark rises to 94 per cent of the first
filled step in the light, which is an idle day reading as busy as a working
one. Full line in dark and 75 per cent in light hold it at 79 per cent of the
first filled step in both.

Contrast against --background, dark then light:
  idle   1.85 / 1.59
  1 run  2.32 / 2.01
  2-3    3.24 / 2.69
  4-6    4.49 / 3.67
  7+     6.25 / 5.02

Verified in the compiled stylesheet rather than by eye: the two rules are
rgb(var(--p-line)) under [data-theme=dark] and
color-mix(in oklab, rgb(var(--p-line)) 75%, transparent) at the base.
The field accepted keystrokes and reverted to the stored name. Nothing failed
and nothing was reported, because nothing had gone wrong on the way to the
database -- the value never got that far.

THE LOOP. ProfilePage held one effect doing three jobs, with `goAuth` in its
dependency list. `goAuth` was an arrow literal written inside the context
value's useMemo in lib/auth.tsx, so it was a different function every time that
memo recomputed -- and it recomputes on `runs`, on `projects`, on `prefs`, on
`screen`. The effect called `refreshRuns`, which brings a fresh array back
across the Wails bridge; `setRuns` therefore always changed identity, the memo
recomputed, `goAuth` was new, and the effect ran again. Each pass re-seeded the
field from `user.display_name`. The revert was not occasional, it was
continuous.

TWO REPAIRS, AND NEITHER IS SUFFICIENT ALONE.

The plain destinations get one identity for the life of the provider. goMap,
goAuth, goAnalysis, goEnergy and goFlood move out of the memo into useCallback
with empty dependency lists -- setScreen is a state setter and React guarantees
its identity. A consumer is entitled to put a context callback in a dependency
array; these could not be put in one. goProfile does not join them: it reads
`user` and `screen` to decide where to land and what to record, so its identity
properly follows those.

The effect becomes three. Redirect when signed out, seed the field, refresh the
run list. The seeding one keys on `user?.display_name` -- the string -- rather
than on `user`, which is a fresh object after every save. Keyed on the object,
uploading an avatar mid-edit would still have discarded a half-typed name, with
no loop needed.

A stable callback with the three jobs still fused leaves that avatar case
standing. Split effects with an unstable callback still spin. Both.

The loop was also hammering the database: `refreshRuns` ran continuously for as
long as settings was open and now runs once, on mount.

The Go side was not at fault and is untouched. UpdateProfile in app_session.go
validates nothing that could reject an ordinary name.
The bottom entry of the navigation column read "Settings" beside the user's
avatar. The avatar already said whose account it was; the word said what the
row did, which the position and the separator above it were saying too.

It now carries the display name. Every other entry in the column names a
destination and this one names a person, which is the shape the account control
takes in the applications this column is modelled on: the row reads as "you",
and settings is what you get when you press yourself. Signed out it stays "Sign
in", because there is no name to show and the row is an invitation rather than
an identity.

TRIMMED, AND FALLING BACK TO THE DESTINATION. The name is only guarded at the
point of save, not in the database, so a blank one can reach this row -- and a
row rendering an empty string beside an avatar is a row with no label at all.

NavItem grows an optional `title`, set here to the whole name. The column is
13.5rem and the label span already truncated; a truncated name is a name you
cannot read, and the other entries are unaffected because the prop is optional
and they do not pass it.

The trade is that the word "Settings" is no longer in the column for someone
hunting for it. It remains on the title bar's equivalent control, which carries
it as a title for the same reason this row now carries a name.
@rexionmars
rexionmars merged commit d752abe into main Aug 31, 2026
2 checks passed
@rexionmars
rexionmars deleted the refactor/ui-corrections-and-feature-removal branch August 31, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant