Skip to content

Profile: heads by name, item pictures, and a page worth looking at (#116) - #122

Merged
CaYatur merged 2 commits into
mainfrom
feat/profile-polish
Jul 28, 2026
Merged

Profile: heads by name, item pictures, and a page worth looking at (#116)#122
CaYatur merged 2 commits into
mainfrom
feat/profile-polish

Conversation

@CaYatur

@CaYatur CaYatur commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Profile: heads by name, item pictures, and a page worth looking at (#116)

The head was always going to be broken. avatarUrl asked for
/avatars/<uuid>, and the uuid MSMS holds comes from usercache.json — which
on an offline-mode server is the derived offline id, a v3 uuid over
OfflinePlayer:<name> that no skin service has ever seen. So every head
rendered as a broken-image icon on exactly the kind of server this app is most
used on.

Resolved by name now. A name-keyed service does the Mojang lookup itself and
falls back to Steve, which is a face rather than a broken image, and it is the
only thing that can work on a cracked server. One helper for the profile head
and the live map's markers, so the two cannot point at different third parties.

That also removes the uuid from the public map payload — it was there to key the
avatar lookup and nothing else ever asked for it. Heads now require the name, so
turning heads on publishes it even when names are off: a recognisable face
identifies a player exactly as well as their name, and claiming otherwise would
have been a lie the setting told.

Items are pictures. Each slot draws the item's texture at item size,
pixelated, with the item's readable name underneath as the fallback — and the
fallback is not politeness, it is the only thing between an unreachable third
party and a grid of broken images. An offline LAN server is a normal place to
run this. Ids come out of player NBT, which a modded or hand-edited item can put
anything into, so anything that is not a plain namespaced id gets no URL at all.

The page is a profile now. A 96px head with the name and presence beside it;
health and food as bars against their known maximum, because a bare "20" says
nothing about whether that is full; coordinates as separate axis chips; the
inventory as a real slot grid. And the signed-in name in the header is an
account chip with a border, a hover state and a round head, rather than an image
followed by loose text next to a styled button.

Asserted: a uuid cannot reach the avatar URL, a name that is not one becomes
Steve without escaping the path, sizes are clamped, and every bad item id
produces no URL and still has a readable label.

The right long-term answer for item and block textures is to extract them from
the client jar Mojang publishes — no third party, works offline. That pairs with
the block-colour work, which needs the same textures, and is tracked there.

)

**The head was always going to be broken.** `avatarUrl` asked for
`/avatars/<uuid>`, and the uuid MSMS holds comes from `usercache.json` — which
on an offline-mode server is the derived offline id, a v3 uuid over
`OfflinePlayer:<name>` that no skin service has ever seen. So every head
rendered as a broken-image icon on exactly the kind of server this app is most
used on.

Resolved by name now. A name-keyed service does the Mojang lookup itself and
falls back to Steve, which is a face rather than a broken image, and it is the
only thing that can work on a cracked server. One helper for the profile head
and the live map's markers, so the two cannot point at different third parties.

That also removes the uuid from the public map payload — it was there to key the
avatar lookup and nothing else ever asked for it. Heads now require the name, so
turning heads on publishes it even when names are off: a recognisable face
identifies a player exactly as well as their name, and claiming otherwise would
have been a lie the setting told.

**Items are pictures.** Each slot draws the item's texture at item size,
pixelated, with the item's readable name underneath as the fallback — and the
fallback is not politeness, it is the only thing between an unreachable third
party and a grid of broken images. An offline LAN server is a normal place to
run this. Ids come out of player NBT, which a modded or hand-edited item can put
anything into, so anything that is not a plain namespaced id gets no URL at all.

**The page is a profile now.** A 96px head with the name and presence beside it;
health and food as bars against their known maximum, because a bare "20" says
nothing about whether that is full; coordinates as separate axis chips; the
inventory as a real slot grid. And the signed-in name in the header is an
account chip with a border, a hover state and a round head, rather than an image
followed by loose text next to a styled button.

Asserted: a uuid cannot reach the avatar URL, a name that is not one becomes
Steve without escaping the path, sizes are clamped, and every bad item id
produces no URL and still has a readable label.

The right long-term answer for item and block textures is to extract them from
the client jar Mojang publishes — no third party, works offline. That pairs with
the block-colour work, which needs the same textures, and is tracked there.
Copilot AI review requested due to automatic review settings July 28, 2026 23:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…he bundler

Both pages get these helpers by embedding fn.toString() into their inline
script, and itemIconUrl called itemIconId. That works only while the bundler
leaves the callee's name alone — with minify on, the page's copy references an
identifier that does not exist there, throws a ReferenceError at runtime, and
nothing in the build notices, because the TypeScript it was compiled from is
still valid.

itemIconUrl repeats its own id validation now rather than depending on a name it
does not control.

The guard matters more than the fix: the smoke calls the PAGE's copies of
avatarUrl and itemIconUrl and compares them to the shared ones, so any embedded
helper that stops working in the page is caught whatever the cause. The pattern
had no test at all until now — every use of it was trusted because it happened
to work.
@CaYatur

CaYatur commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Self-review

A stringified function that called another function

Both pages get these helpers by embedding fn.toString() into their inline
script. itemIconUrl called itemIconId, and I declared both — which works
only for as long as the bundler leaves the callee's name alone.

The moment build.minify is on, or esbuild renames for any other reason, the
page's copy of itemIconUrl references an identifier that does not exist there.
It throws a ReferenceError at runtime and nothing in the build notices,
because the TypeScript it was compiled from is still perfectly valid: typecheck
passes, the build succeeds, the page is served, and the profile breaks in the
browser.

itemIconUrl is self-contained now — it repeats the eight characters of id
validation rather than depending on a name surviving a transform it has no
control over.

The general guard matters more than the one fix: the smoke now calls the
page's copies of avatarUrl and itemIconUrl and compares them against the
shared ones, so any embedded helper that stops working in the page is caught
whatever the cause. This existing pattern (pickSiteLang.toString(), and now
four more) had no test at all — every use of it was trusted because it happened
to work.

Proved failable by simulating exactly the transform that would break it —
renaming the callee inside the embedded copy:
FAIL - exception: ReferenceError: itemIconId_renamed is not defined.

Verified

Twelve gates green.

@CaYatur
CaYatur merged commit 7a22d45 into main Jul 28, 2026
1 check passed
@CaYatur
CaYatur deleted the feat/profile-polish branch July 28, 2026 23: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.

2 participants