Le bubble custom ereditavano la slice base (centro largo) -> con repeat si
duplicavano, con stretch si allungavano troppo/storte. Ora ogni bubble ha
border-image-slice/width calcolata dalla sua immagine: il taglio cade nella
zona centrale del corpo, sulla colonna piu uniforme (colore pieno), con un
filo stirabile di ~2px e border-image-repeat: stretch. Cosi il corpo si stira
pulito e i cap restano intatti. (Le bubble con decoro su tutto il corpo, senza
una fascia piena, restano un compromesso: limite intrinseco del 9-slice.)
Ereditavano border-image-repeat: repeat dalla regola base, quindi il centro
delle nuove bubble (con grafica) si duplicava invece di allungarsi. Override
border-image-repeat: stretch per le 253-291 cosi il corpo si stira come le
originali (che avevano il centro a tinta unita).
- 39 bubble custom con relativo pointer colorato (colore campionato dal
fondo di ogni bubble) + regole CSS in Chats.css (resa in-stanza border-image
+ anteprima nel selettore)
- selezionabili via chat.styles dell'ui-config (lato server)
- soundboard: paginazione 9/pagina (griglia 3x3) con frecce + indicatore,
cosi la card non cresce a dismisura
- Catalogo Hippiehotel responsive su mobile (finestra 96vw/72vh, rail
tappabile, sotto-pannelli ristretti); duckie ripristinato come entita
separata (revert modifiche scrollbar sul suo CSS)
- BC e catalogo normale seguono entrambi il toggle tema; il duckie non
duplica piu il logo nelle pagine info_duckets
- Navigator: highlight della tab segue currentTabCode (era bloccato su
Pubblici mentre il contenuto cambiava)
- Inventario: link inventory/show/<tab> per deep-link a una scheda
- User Profile: avatar visibile e allineato a bg/stand, finestra piu
grande, bordi puliti, Created/Last login mostrano il valore, bottoni
Change Looks/Rooms/Change Badges/Add friends/Achievement funzionanti
- Amici: header non piu sovradimensionati e teste avatar inquadrate
(regole flat: quelle annidate .nitro-friends{&...} non si applicavano)
Rimpiazza il catalogo del rebuild upstream con quello originale di
Hippiehotel.nl Nitro-V3 (CatalogModernView ripristinato, ClassicView/
sub-views/CSS pre-merge). CatalogView sceglie Modern (default) o Classic
via il toggle 'stile classico'. Rimosso l'hack CatalogClassicLegacy.css.
Ripristina il look del catalogo pre-merge come CSS scoped sotto
body.catalog-skin-legacy (CatalogClassicLegacy.css), attivato dal toggle
'stile classico'. CatalogView mette/toglie la classe sul body.
Client side of the soundboard. Room owners enable it in Room Settings >
Misc (next to the YouTube TV toggle). When enabled, a soundboard icon
appears in the toolbar for everyone in the room; pressing a pad broadcasts
the sound so all occupants hear it. Incoming SoundboardPlay is played via
the HTML5 Audio API.
Also: fix FloorplanCanvasSVG to use ReactElement instead of the removed
global JSX namespace (React 19), and pair the client Dev branch with the
renderer fork that carries the custom features in CI.
How sounds are managed (works with any CMS):
Sounds are rows in the `soundboard_sounds` table:
id, name, url, enabled, sort_order
The emulator loads every row with enabled=1 (ordered by sort_order, id)
and sends the list to clients on room enter; the client plays `url`
directly, so any publicly reachable audio URL works (mp3/ogg/wav).
To add a sound from an admin/housekeeping panel of any CMS:
1. Upload the audio file to wherever the CMS stores public assets
(same approach as custom badge images).
2. INSERT a row into `soundboard_sounds` with the display name and the
public URL of the uploaded file, enabled = 1.
3. Reload the emulator soundboard (or restart) to pick it up.
Relative urls resolve against the `soundboard.url.prefix` config key
(falls back to `asset.url`); absolute urls are used as-is.
The example data has been provided in /Content-Gamedata so you could place it in /gamadata or anything you like.
Do not forget the render-config.json to update :
"login.health.method": "GET",
"login.news.url": "${asset.url}/news/news.json",
Adds a "Cards" tab to the Profile Background picker (BackgroundsView)
that selects a pattern applied to the entire user info card and the
extended profile container, in addition to the existing avatar-pad
background/stand/overlay layers.
- AvatarInfoUser/Utilities: propagate cardBackgroundId from RoomUserData.
- InfoStandWidgetUserView: stateful cardBackgroundId, applied as
.profile-card-background.card-background-{id} on the outer Column
with bg-color suppressed when active.
- UserContainerView: same class on the wrapper of the extended profile.
- BackgroundsView: 4th tab "cards" backed by cards.data config
(falls back to backgrounds.data); sends 4-id message via the
extended sendBackgroundMessage signature.
- ui-config.example: cards.data dataset (15 entries).
- BackgroundsView.css: 188 .card-background-{N} rules cloned from
background-{N} (repeat-tiled) plus 15 CSS-pattern overrides for the
provisional dataset (gradients, stripes, dots, grid, checker).
Backend (AuthHttpHandler):
- New users_remember_tokens table stores sha256 hex of the raw token
so the DB never holds a usable credential. Seed file adds the table
and a login.remember.duration.days setting (default 30).
- /api/auth/login accepts "remember": true. On success, issues a fresh
32-byte base64url token, stores the hash, returns the raw token.
- New POST /api/auth/remember: accepts the raw token, looks up by hash,
on a valid hit mints a fresh SSO ticket, rotates the token (deletes
the consumed one and issues a new one), returns both to the client.
No Turnstile - it's an automated trusted-device flow.
- /api/auth/logout also accepts rememberToken and deletes that single
row so other devices keep their tokens.
Frontend:
- LoginView: "Remember me" checkbox (key login.remember_me already in
ExternalTexts). Enabling it persists the returned rememberToken in
localStorage.nitro.remember.token.
- App.tsx: before deciding to show the login screen, try a silent POST
to /api/auth/remember with the stored token. On 200, inject the
returned ssoTicket into window.NitroConfig and proceed to the
authenticated flow; on 401, forget the token and show login.
- PurseView logout: sends the stored rememberToken in the body so the
server can delete it, and clears localStorage before reload.