Commit graph

507 commits

Author SHA1 Message Date
42de9cc491 Update dependency react-i18next to v17.0.11 2026-07-28 02:01:59 +00:00
aed817a105
CBAN window: the join button switches the active view to the suggested channel 2026-07-27 01:03:16 +00:00
431432feb5
Show a window when a JOIN is refused by a network CBAN (926) 2026-07-27 00:56:44 +00:00
2eab043a33
Point games plugin at GamesServ (live service nick; GameServ is reserved) 2026-07-21 23:28:32 +00:00
5d90c54092
Externalize theme-init so the app CSP needs no inline-script hash 2026-07-21 23:12:59 +00:00
f9a1ac016b
Don't re-negotiate caps or SASL on a post-registration CAP NEW 2026-07-21 17:57:45 +00:00
9b2b66f827
surface +draft/reply and +draft/channel-context on service notices 2026-07-21 07:47:05 +00:00
d567627f5f
show +draft/channel-context on service notices 2026-07-21 07:40:01 +00:00
b31aceab6e
yomirc: show sender channel-status prefix in message lines 2026-07-18 04:15:53 +00:00
c4724a7236
sasl: abort connection on auth failure 2026-07-17 00:17:53 +00:00
ee9f830c7b
Set the Tchatou connect-screen headline in French brand voice
The connect hero read the generic 'Le tchat français, / en direct.'
Give the Tchatou deployment its own warmer French headline via the
branding config (leaves Orbit's shared i18n defaults untouched):
'Soyez au cœur des échanges, là où tout commence.'
2026-07-12 21:04:52 +00:00
80a7ed71ea
Keep user text colours readable against the theme background
mIRC colour codes include near-black, dark blue and grey that vanish on a
dark theme (and white/pale that vanish on light). When a coloured run has
no explicit background, clamp its lightness toward the readable side —
preserving hue and saturation, so a dark blue reads as a lighter blue.
Darkness is taken from the live --bg, cached per theme.
2026-07-12 19:34:00 +00:00
bd58c7a8d5
Keep messages aligned when a user has no avatar
Removing the avatar slot entirely for pictureless users left them
flush-left while avatar-havers stayed indented, so mixed channels looked
ragged and continuation lines lost their timestamp. Always reserve the
avatar column: photo when there is one, hover timestamp on continuation
lines, otherwise an empty spacer — no bright fallback bubble, but every
row lines up again.
2026-07-12 19:22:26 +00:00
1b1b0dfcf3
Defer off-screen message history past the switch (tail-first render)
Mounting a full 500-line buffer is the bulk of network-switch latency. On
a buffer switch we now render only the last ~120 messages (extended to
keep the unread divider in view), then fill the rest of the buffer on the
next idle tick — off the interaction's critical path. After the fill the
list is fully rendered exactly as before, so scrolling/auto-follow are
unchanged; this never drops rows while you read. The fill prepends older
rows, so the scroll effect preserves the reading position (or stays pinned
to the bottom).
2026-07-12 19:11:55 +00:00
aa5238ded7
Cut network-switch INP: stop the sidebar re-rendering per message
Three targeted wins for the slow network-tab click:
- Sidebar subscribed to the whole buffers map, so it re-rendered and
  re-mapped every conversation row on each incoming line. Subscribe to
  order only and derive channel-vs-DM from the name.
- content-visibility on the conversation rows (matching the member list)
  so switching a network only lays out the on-screen rows.
- skip the whole-buffer unread scan for the active network's own tab,
  whose badge is never shown.
2026-07-12 18:39:30 +00:00
61dfa77da5
Keep the access-mode prefix in the nicklist for every theme
Reverting the modern-theme prefix removal: the @/+/~ prefix on nicks is
core IRC and belongs in the member list regardless of theme. The in-chat
op marker stays; this just restores the nicklist prefix everywhere.
2026-07-12 18:33:13 +00:00
322505a5b5
Add a Monospace messages toggle in Appearance settings
Message text goes back to the proportional UI font by default; the new
switch renders it in a fixed-width font (via html[data-mono]) so ASCII
art, tables and box-drawing line up on demand. Persisted per user and
translated across all locales.
2026-07-12 18:26:10 +00:00
e2abcce3d9
Show a role-coloured access marker before op/voiced nicks in chat
You can now spot ops (and voiced/owners) while reading the conversation:
each sender's strongest channel prefix (@ ~ & % +) is shown before their
nick, coloured by role to match the member list. Factor the prefix→role
map into a shared lib so the member list and the message list can't drift.
2026-07-12 18:22:48 +00:00
19d8d33358
Drop the glued @ nick prefix in the modern member list
Members are already grouped under a role header (Op, Voice, …) and the
role is colour-coded, so prefixing the nick with @/+/~ was redundant
clutter — an op now reads as "Aline", matching the message list. yomIRC
keeps the classic mIRC-style "@nick". Removes the now-unused modern
per-role prefix colours.
2026-07-12 18:17:33 +00:00
04e3956484
Render message text in monospace so ASCII art lines up
The message body used the proportional UI font, so ASCII art, tables and
box-drawing never aligned. It already preserves whitespace (pre-wrap); it
just needed a fixed-width font. Give .line the monospace stack (yomIRC was
already mono) and trim the size a touch since mono renders wider.
2026-07-12 18:06:27 +00:00
432f5379db
Hide the avatar bubble for users without a real picture
The modern layout showed a bright per-nick gradient bubble for everyone
without an uploaded avatar — a column of color-shifting blobs that's hard
on the eyes. Resolve the avatar url in the row and only render the bubble
(and its rail) when there's an actual picture; otherwise the message runs
full-width. Registered users with a photo are unchanged.
2026-07-12 17:59:25 +00:00
d1e96fb97d
Keep auto-scroll alive when the message buffer is capped
The follow effect keyed on message count, but the buffer is capped at 500
so in a busy channel the count stops changing and the effect stopped
firing entirely — auto-scroll silently died exactly in the channels that
need it. Key it on the newest message id as well, which keeps changing.
2026-07-12 17:37:25 +00:00
c690f39e9d
Restore scroll position when returning to a backgrounded tab
A backgrounded browser tab freezes rAF and reports stale layout while
messages keep arriving, so the bottom-follow drifted and there was no way
back to the newest line. On the tab becoming visible again we re-pin to
the bottom if following, and the jump-to-newest button now stays in sync
as lines arrive (not only on scroll).
2026-07-12 17:24:41 +00:00
85ee7cc5ba
Only follow new messages when pinned to the very bottom
Tighten the auto-scroll band from 140px to 64px so scrolling up even a
little to read the last line no longer gets yanked back down when a new
message arrives; the jump-to-newest button covers the rest.
2026-07-12 17:09:46 +00:00
adfeaf77fa
Don't yank the view down when reading the last line in a busy channel
Auto-follow now engages only while pinned to the very bottom (64px), not
anywhere within 140px of it — so sitting on the newest message while
people type no longer snaps you back. The read-marker still clears across
the wider band.
2026-07-12 17:08:26 +00:00
7d90842fba
Smoother scrolling in busy channels
Three fixes for fast channels: the day-separator no longer runs Intl date
formatting for every message on every render (one cheap day key instead),
scroll handling is coalesced to one layout read per frame so a Mac
trackpad's rapid events don't cause jank, and the floating button now
appears whenever you're scrolled up and snaps straight to the newest line
— so a busy channel can't strand you away from the bottom.
2026-07-12 17:06:51 +00:00
e84e2ba321
Fold consecutive join/part/quit into one line with coloured nicks
A busy channel no longer shows one status line per presence event: a run
of joins and parts collapses into a single compact line (nicks coloured,
joins and leaves netted, long lists truncated with a +N hover). The
classic mIRC theme and the server console keep one line per event.
2026-07-12 16:47:53 +00:00
3417ecdfd3
Adapt link-preview cards to their column with a container query
Make each message body a size container and stack the link card (image over
text) when that column, not the viewport, is too narrow for the side-by-side
thumbnail. One rule covers a wide desktop column, a phone, and a member-list
squeezed column alike; browsers without container queries keep the horizontal
card as before.
2026-07-11 11:24:05 +00:00
db899053b7
Animate channel switches with a scoped view transition
Wrap sidebar room and DM selection in document.startViewTransition so the
message region cross-fades to the newly chosen channel. The root is opted out,
so only .messages morphs while the sidebar, topbar and composer stay instant.
Falls back to a plain switch where the API is unavailable or the user prefers
reduced motion.
2026-07-11 11:01:00 +00:00
4845da2a66
Derive accent tint via color-mix + add prefers-contrast
color-mix() derives --accent-soft from --accent so the soft tint tracks a
config.branding.accent override instead of a fixed hex. Add a
prefers-contrast: more query — heavier text (Hanken/Geist are variable
fonts, so weight tracks the wght axis) and underlined links.
2026-07-11 03:59:24 +00:00
86e9fd1445
Make Friends an anchored panel like radio and games
Friends opened as a centred modal; convert it to a nav-panel that pops up
anchored above its footer tab, matching the radio/games window via a shared
.opanel card (soft shadow, borderless input + accent button, rise entrance).
It joins the 'orbit:panel' exclusion, so opening it closes the others and
vice versa. Removes the old FriendsModal.
2026-07-11 03:28:43 +00:00
d246c526b2
Fix sandboxed panel closing itself on open
The exclusion compared the incoming id against the guest's DECLARED plugin
name ('orbit-radio'), but panel.opened emits the HOST id
('sbx:orbit-radio.js'), so a sandboxed panel closed itself the instant it
opened. The init message already carries the host name — read it and compare
against that.
2026-07-11 03:20:39 +00:00
e4d8ad3200
Close other panels when one opens (mutual exclusion)
Opening the games or radio panel left the other open — they stacked. Add a
shared 'orbit:panel' bus event: a panel announces itself on open and every
other panel closes. Sandboxed panels (radio) join through the SDK — a
null-capability panel.opened RPC emits the event host-side with the id fixed
to the plugin name, and the panel() helper closes on any other id. The games
panel emits and listens on the same event.
2026-07-11 03:16:34 +00:00
29791decfc
Remove the Rooms tab from the footer nav
The footer's Rooms/Salons tab only opened the explore modal — the same
thing the sidebar's "Explore channels" button already does (and that one
refreshes the channel list first). Drop the redundant tab; the footer is
now Home · Friends · Games · Radio. Also removes the now-unused compass
icon and the dead nav.tabRooms label from all ten locales.
2026-07-11 03:00:07 +00:00
7923faa3f7
Match the games panel styling to the radio window
Compared side by side, the games panel used outlined, bordered buttons and
a lighter shadow while the radio window uses the SDK's borderless atoms
(.obx-*). Restyle the games panel to the same language: borderless soft
buttons that fill with the accent when active, a plain muted close (obx-x),
12px card padding, and the radio card's exact two-layer shadow. Same theme
now, not just similar.
2026-07-11 02:49:51 +00:00
4922491275
Open the games panel from its tab, like the radio window
The panel was pinned to a fixed bottom-right corner, detached from the
Games footer tab — unlike the radio panel, which pops up anchored above
its own tab. Capture the tab's rect on click and position the panel
centred above it, clamped to the viewport. Now it opens from the tab it
belongs to instead of the corner.
2026-07-11 02:25:00 +00:00
a5e2c8b17a
Give panels and menus a soft shadow and a radio-style open
The popovers (games panel, notify / pin / overflow menus, status menu) had
a flat fade and a single-layer shadow, unlike the radio window. Route them
through the app's two-layer --shadow-pop token and a fade + rise entrance.
Also define @keyframes rise, which was referenced app-wide (modals, emoji
and colour pickers, settings, login) but never declared — so those
entrance animations were silently no-ops and now play. The games panel
matches the radio card: 16px radius, soft shadow, rise on open.
2026-07-11 02:12:14 +00:00
4f95f0ea66
Theme the games panel instead of hardcoded dark colours
The panel referenced its own token names (--bg2/--tx/--tx-dim/--ac/--card)
that don't exist in the app, so it always fell back to hardcoded dark
values — mismatched on the light and Night-orange/green themes. Alias
those tokens to the app's theme tokens (--bg / --ink / --muted / --accent
/ --bg-soft-2) at the panel root, fix the active white-overlay fills that
vanished on light backgrounds (rows, input, toast), map the accept action
to --online, and soften the shadows. The panel now follows the active
theme; the game-piece colours (X/O, chess, Connect-4) stay as-is.
2026-07-11 01:59:55 +00:00
a6769d3a54
Move Games from the topbar into the footer nav
Games is now a "Jeux" tab in the footer alongside Accueil / Salons / Amis
— icon over label, accent when its panel is open, an alert dot when it's
your move or a challenge is pending — a native bottom-nav home for it. The
panel still opens from the persistent 'overlay' slot, and the new-game /
challenge ("invite to game") flow lives inside it (plus the profile Défier
action). Drops the now-unused topbar-plugin-in-overflow machinery; the
clock stays in the desktop header.
2026-07-11 01:48:18 +00:00
c28a28645f
Hide the clock from the mobile overflow menu
The clock is a passive HH:MM display (a sandboxed .sbx-slot), so it doesn't
belong in the action menu — and it's redundant on phones, where the OS
status bar already shows the time. Drop sandboxed topbar plugins from the
⋮ overflow; the bar copy is already hidden on mobile, so the clock is now
gone on phones while staying in the desktop header.
2026-07-11 01:07:13 +00:00
231be23f0d
Add an 'overlay' plugin slot; move Games into the mobile ⋮ menu
A topbar plugin's popover was rendered as a child of its launcher button,
so the button had to stay mounted for the popover to survive — which is
why the games button couldn't move off the crowded mobile header. Add a
persistent 'overlay' UI slot at the app root and render the games panel
there, decoupling it from the button. The launcher is now stateless, so
on mobile it drops into the ⋮ overflow as a labelled "Jeux" row (freeing
the header for the channel name + topic) while its panel stays anchored.
Desktop keeps the icon in the bar.
2026-07-11 00:54:36 +00:00
e36d3a1d34
Keep the games button in the mobile topbar
The games panel is a fixed overlay rendered as a child of its button, so
the button has to stay mounted for the panel to survive — it can't move
into the ephemeral overflow menu, where it would unmount (and the panel
vanish) the moment the menu closes. Now that search / manage / leave live
in the overflow, the bar has room, so show the games plugin button inline
on mobile instead of hiding it. Reachable again, and the title and topic
still fit.
2026-07-11 00:28:18 +00:00
7c074d7a8b
Rework the mobile topbar into a native header
On phones the channel header crammed every button into one row: the title
sat behind the games-plugin button and the topic was pushed off screen.
Collapse the one-shot actions (search, manage, leave) into a "⋮" overflow
menu, hide topbar plugin items on mobile, and keep only the stateful notify
and pin toggles, the member count and the overflow inline. The channel
glyph, name and topic now have room to show as a proper two-line header.
2026-07-11 00:11:56 +00:00
d817db9255
Theme the topbar toolbar icons to match the footer set
The channel header's search, notify, pin, manage and close buttons — plus
the app-wide search-field magnifiers and the menu toggle — used OS emoji
glyphs. Extend the shared Icon component with search, close, menu, bell,
bell-off, pin and sliders, and use it across the topbar, the notify and
pin menus, and the explore / switcher / side-search fields. The icons
inherit each button's colour and get the same press and focus states as
the footer, so the whole chrome now reads as one icon family.
2026-07-10 23:51:15 +00:00
e82b4b7f01
Replace footer emoji glyphs with a themed icon set
The account bar's tabs and settings button used OS emoji (home, compass,
people, gear), which render off-theme and differ across platforms. Add a
shared stroke-icon component — 24×24, currentColor, the same 1.9 stroke as
the composer glyphs — and use it for Accueil, Salons, Amis and Settings.
The icons inherit each button's colour, so the active tab now reads as a
soft accent pill, and the buttons gain a press dip and focus ring so the
bar feels native on desktop, not just touch.
2026-07-10 22:57:37 +00:00
dbb45e4d17
Load only the active UI locale on first paint
All ten translations (~400 KB of JSON) were bundled into the entry
chunk, nearly all of it never used in a session. Ship only French — the
default and fallback — and fetch the other nine as separate chunks on
demand (language switch or a config-pinned default). The entry chunk
drops from 653 KB to 361 KB (200 KB to 118 KB gzipped), cutting the
parse/execute work that was delaying the largest paint.
2026-07-10 22:26:24 +00:00
d5ddbbff4f
Coalesce inbound messages into batched renders
Each IRC line arrives as its own WebSocket frame (text.ircv3.net), so
the store rendered once per line. Joining several busy channels at once
— NAMES plus a WHO reply per member — fired a render for every line and
stalled the main thread, which also inflated LCP. Buffer messages onto a
0ms timer so a whole burst drains in one render; under load the timers
back up and each drain swallows more lines. A timer rather than rAF keeps
unread counts and mention notifications live in a backgrounded tab.
2026-07-10 22:14:24 +00:00
14cf38afa8
Fix Turnstile ref write during render and the gtag lint error
Turnstile refreshed its callback ref in the render body; move it to an
effect. The gtag shim legitimately pushes the live arguments object, so
scope a disable to that line with a reason. Lint is now error-clean.
2026-07-10 21:49:55 +00:00
9b21adb9cc
Derive prop-synced state during render, not in effects
The nick field, channel +k/+l inputs and avatar hook re-synced from
props by calling setState inside an effect, which triggers an extra
render pass each time. Track the previous prop and adjust during
render instead — one pass, and the avatar now resets on account switch.
2026-07-10 21:48:08 +00:00
7ea1fd3e87
Add @/ path alias, replace deep relative imports
Set up an @/* -> src/* alias (tsconfig paths + vite resolve) and
convert every ../../-and-deeper import to it. Same-dir ./ and
one-level ../ stay relative. Build-time only, so the bundle is
unchanged; the deep imports were just hard to read and broke on
file moves.
2026-07-10 21:30:32 +00:00