Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
274 commits
Select commit Hold shift + click to select a range
9a40eec
feat(chat): fold long messages by input device, escape code block fal…
matheusfillipe Oct 3, 2026
67637a1
feat(composer): one suggestion list for members, emojis and slash com…
matheusfillipe Oct 3, 2026
bf1c246
feat(sidebar): pick the channel kind from the first prefix typed
matheusfillipe Oct 6, 2026
34ecb61
feat(notices): show the motd in server notices and on the welcome page
matheusfillipe Oct 6, 2026
a7471c3
feat(chat): compact links with their address in a hover card
matheusfillipe Oct 6, 2026
6072fb6
feat(bots): one workflow line per bot run that opens its card
matheusfillipe Oct 7, 2026
869fe82
feat(sidebar): user panel with a status menu, taller message bar
matheusfillipe Oct 7, 2026
6736160
chore(tauri): update to 2.12, point dev hot reload at the device host
matheusfillipe Oct 7, 2026
c0715d2
feat(sidebar): one in-place row for add channel and message someone
matheusfillipe Oct 7, 2026
0b694cf
feat(mobile): android pip, ios keyboard and foreground events, backgr…
matheusfillipe Oct 7, 2026
979fa30
chore(i18n): translate mobile, voice, media and settings strings
matheusfillipe Oct 7, 2026
9b00f48
fix(voice): rejoin on drops, keep mute, listen-only without a mic
matheusfillipe Oct 7, 2026
ac566d9
fix(media): working uploads, camera mic only while recording, voice m…
matheusfillipe Oct 7, 2026
bdd4032
fix(connection): no toast for a dropped live socket, never reopen cal…
matheusfillipe Oct 7, 2026
65dba82
fix(chat): whisper headers, verb menu labels, readable topic hint
matheusfillipe Oct 7, 2026
6427d48
fix(mobile): keyboard keeps header and input in view, safe areas on s…
matheusfillipe Oct 7, 2026
d158499
style: real theme colors, one primary button style
matheusfillipe Oct 7, 2026
12cca00
chore(i18n): translate call layout, join refusals and pm menu
matheusfillipe Oct 8, 2026
78897e0
fix(voice): stay in the channel on hang up, retry throttled joins
matheusfillipe Oct 8, 2026
e84bf44
feat(mobile): one control layer over phone calls, fullscreen, fold vi…
matheusfillipe Oct 8, 2026
7ef1a59
feat(chat): pms work like channels and every chat can be reordered
matheusfillipe Oct 8, 2026
44d0f6b
fix(mobile): no gap under the app, keyboard steps aside for sheets
matheusfillipe Oct 8, 2026
8993adc
ci: wait for the tauri 2.12 options file on ios, install only platfor…
matheusfillipe Oct 10, 2026
a9ae2b5
chore(i18n): translate camera, server notices and toasts
matheusfillipe Oct 9, 2026
33d8544
feat(notices): server errors go to server notices with a badge
matheusfillipe Oct 9, 2026
a4100c6
feat(media): youtube plays in the installed apps through the web origin
matheusfillipe Oct 9, 2026
53b0b7a
feat(settings): dialogs become pages, profile pane, autosave, one emb…
matheusfillipe Oct 9, 2026
581541e
test(voice): give the mocked store a ui state so leaving a call reads it
matheusfillipe Oct 15, 2026
3f4ae0a
fix(notices): keep server notices silent and mark plain notices with …
matheusfillipe Oct 16, 2026
e450431
fix(user-panel): show our nick on the selected server
matheusfillipe Oct 16, 2026
e6e329d
fix(irc): send NICK after SASL so a persistent session gets its nick …
matheusfillipe Oct 16, 2026
5f6ab67
fix(chat): mount a newly opened chat's message list on the first frame
matheusfillipe Oct 16, 2026
a6db13b
feat(history): keep read history on the device, fill offline gaps fro…
matheusfillipe Oct 9, 2026
3086247
fix(discover): cards fit the column width
matheusfillipe Oct 9, 2026
0b18188
refactor(history): one chathistory request helper, bouncer disconnect…
matheusfillipe Oct 9, 2026
cf57514
feat(search): search saved messages and jump to any message
matheusfillipe Oct 9, 2026
6f60805
fix(search): fetch missed messages for gaps while searching, mark a g…
matheusfillipe Oct 10, 2026
9ea090f
test(history): build the full older database layout in upgrade tests
matheusfillipe Oct 10, 2026
45b96af
fix(desktop): mac freeze from duplicate media probes, mobile swipe ba…
matheusfillipe Oct 10, 2026
13fe2ce
feat(discover): plugins page mockup
matheusfillipe Oct 10, 2026
6884907
style: frosted glass for menus, dialogs, sheets and the palette
matheusfillipe Oct 10, 2026
ca7d71d
feat(mobile): phone pages stack with swipe back, outlined search result
matheusfillipe Oct 10, 2026
93c7cc7
fix: share location map, chat stays at the bottom around the keyboard…
matheusfillipe Oct 10, 2026
999c96c
fix(mobile): drag the location map on touch, still page on ios keyboa…
matheusfillipe Oct 10, 2026
14291cd
fix(web): reload into the nickname prompt after disconnecting in sing…
matheusfillipe Oct 10, 2026
f42d80c
fix(tauri): try ipv4 first and give each address its own connect timeout
matheusfillipe Oct 11, 2026
1e5bd85
feat: suggest nicknames in the invite dialog
matheusfillipe Oct 11, 2026
d2a5434
fix(history): show saved private messages on servers that keep only c…
matheusfillipe Oct 11, 2026
648c40e
fix: show and select a server as soon as it is added
matheusfillipe Oct 11, 2026
a2be87a
fix(mobile): enable send with only an attachment
matheusfillipe Oct 11, 2026
61a89da
fix(history): drop saved messages that were redacted while we were away
matheusfillipe Oct 11, 2026
16c47df
fix: keep the reply when sending files
matheusfillipe Oct 11, 2026
aa5ce98
fix(desktop): ask for screen recording before picking a region, open …
matheusfillipe Oct 11, 2026
f85a9c2
fix(metadata): set our own metadata again after the app restarts
matheusfillipe Oct 11, 2026
4128514
fix: leave sentence punctuation out of links
matheusfillipe Oct 11, 2026
77af7ef
fix(metadata): follow our metadata when another of our clients change…
matheusfillipe Oct 11, 2026
578eaef
docs: never send long-lived credentials anywhere but the ircd
matheusfillipe Oct 11, 2026
ebd9f6a
fix(metadata): read our own metadata from the server only
matheusfillipe Oct 11, 2026
daed194
fix: fill members from WHO with metadata we already fetched
matheusfillipe Oct 11, 2026
a7f58e2
fix: stop firefox recordings that have no audio track
matheusfillipe Oct 11, 2026
23102a7
refactor(notices): drop the unused server notices link from toasts
matheusfillipe Oct 16, 2026
235c4b8
feat: use wide screens on settings and list pages
matheusfillipe Oct 11, 2026
99a3e6e
feat(chat): infinite scroll with skeletons, one bottom arrow and a ne…
matheusfillipe Oct 12, 2026
bcf416e
feat(read-marker): mark only what the user saw and clear unread acros…
matheusfillipe Oct 12, 2026
cf37a25
fix(irc): record our account from RPL_LOGGEDIN and RPL_LOGGEDOUT
matheusfillipe Oct 12, 2026
6061bfc
fix(connection): ping on wake and reconnect a socket that went dead
matheusfillipe Oct 12, 2026
d2ba662
feat(history): save older channel pages to disk and show them from there
matheusfillipe Oct 12, 2026
df977e8
fix(links): ask before opening links outside a link wrapper, includin…
matheusfillipe Oct 12, 2026
4858879
feat(history): open chats at the read marker with CHATHISTORY AROUND …
matheusfillipe Oct 12, 2026
7ed3bc5
feat(read-marker): derive unread from the read marker and follow new …
matheusfillipe Oct 12, 2026
70a76af
fix(chat): follow new messages while away until the new messages line…
matheusfillipe Oct 12, 2026
2c08b49
fix(bot-tools): finish workflows whose final answer arrives as a mult…
matheusfillipe Oct 12, 2026
7944bef
fix(chat): jump to the bottom when sending while scrolled up
matheusfillipe Oct 12, 2026
fdb07f7
fix(history): keep replayed history out of live state and confirm del…
matheusfillipe Oct 12, 2026
e4746ab
fix(focus): count clicks and keys as the app being in front and widen…
matheusfillipe Oct 13, 2026
e837627
fix(dm): show our own uploaded file once in private messages
matheusfillipe Oct 13, 2026
f6864ba
fix(irc): close the old socket when a server reconnects
matheusfillipe Oct 13, 2026
8a79d1a
fix(away): turn our own away dot green in channels when we come back
matheusfillipe Oct 13, 2026
2eda6e4
feat(servers): keep a second account on the same network as its own s…
matheusfillipe Oct 13, 2026
f09543f
fix(settings): send only what the user changes in channel, server and…
matheusfillipe Oct 13, 2026
18fed06
feat(discover): show network user and channel counts from the server …
matheusfillipe Oct 13, 2026
7fc2613
feat(chat): stack uploads, bot workflows and audio in one dock above …
matheusfillipe Oct 13, 2026
717803c
test(app): give the store mock a setState for the focus listeners
matheusfillipe Oct 13, 2026
e9214ea
fix(join): prefix bare names with the server's channel type and drop …
matheusfillipe Oct 13, 2026
d473b14
fix(chat): move upload progress to the top strip and bring back the o…
matheusfillipe Oct 13, 2026
57faffb
fix(chat): show real upload progress bars without spinners and size t…
matheusfillipe Oct 13, 2026
a435d63
fix(join): keep saved channels when an autojoin is refused and match …
matheusfillipe Oct 13, 2026
ec1b2dd
fix(links): open external links from the app itself on macOS
matheusfillipe Oct 14, 2026
e95e6de
docs(agents): add real server behaviour, platform, testing and PR rul…
matheusfillipe Oct 14, 2026
b0b2779
chore(docs): lint only tracked markdown files
matheusfillipe Oct 14, 2026
7d53b41
fix(history): stamp one receive time per line so saved history replay…
matheusfillipe Oct 14, 2026
0b130b8
docs(agents): add the CI gate, channel prefix and missing field rules
matheusfillipe Oct 14, 2026
9f3b5f6
fix(channels): detect channels from the server's CHANTYPES everywhere
matheusfillipe Oct 14, 2026
57a8b0e
fix(read-marker): mark a chat read when the user sends to it and keep…
matheusfillipe Oct 14, 2026
5f20591
fix(ios): vendor swift-rs with the Xcode 27 fix so iOS release builds…
matheusfillipe Oct 15, 2026
370ede8
fix(chat): fill a loading chat with skeleton rows and load older rows…
matheusfillipe Oct 16, 2026
6ad53dc
fix(chat): page past ignored users, ask for the read marker page once…
matheusfillipe Oct 16, 2026
bd4fc5b
fix(history): page past typing notices to the start of history and ma…
matheusfillipe Oct 16, 2026
6d63f9b
fix(chat): place the unread line once late history lands and keep the…
matheusfillipe Oct 16, 2026
a1da2eb
fix(chat): open a chat whose history never arrives and drop stale his…
matheusfillipe Oct 16, 2026
91157d8
fix(read-marker): send MARKREAD only while logged in to an account
matheusfillipe Oct 16, 2026
660c4d1
fix(chat): keep the visible rows in place when the top skeleton goes
matheusfillipe Oct 16, 2026
f610369
feat(notifications): show native notifications with tap routing and k…
matheusfillipe Oct 15, 2026
7c90fe0
feat(notifications): check for messages in the background on Android …
matheusfillipe Oct 15, 2026
a5f0dac
fix(notifications): route taps on iOS, read all history since the las…
matheusfillipe Oct 15, 2026
9a07fb1
fix(notifications): show the Obby crystal as the Android notification…
matheusfillipe Oct 15, 2026
23fef0b
feat(notifications): ask for notifications once when the app first opens
matheusfillipe Oct 15, 2026
056f70c
fix(persistence): ask for the persistence state once registration is …
matheusfillipe Oct 15, 2026
589075f
refactor(notifications): isolate the history poll as the fallback for…
matheusfillipe Oct 15, 2026
e55f0bb
fix(notifications): keep the poll through offline launches and only o…
matheusfillipe Oct 15, 2026
9a0af07
fix(notifications): poll web push servers too since the app webviews …
matheusfillipe Oct 15, 2026
5d5c3c0
fix(notifications): post android notifications through our plugin so …
matheusfillipe Oct 16, 2026
2eed31f
fix(notifications): show ios banners for notifications posted while t…
matheusfillipe Oct 16, 2026
0baf871
fix(notifications): land a tapped notification on its message instead…
matheusfillipe Oct 16, 2026
1e4591b
feat(webpush): soju.im/webpush client support
ValwareIRC May 20, 2026
4016c81
fix(notifications): configure the history poll once per change and sk…
matheusfillipe Oct 16, 2026
0a2446d
feat(webpush): add Enable Notifications toggle that drives push regis…
ValwareIRC May 20, 2026
b16b9ee
fix(notifications): jump to a tapped message once the page is visible
matheusfillipe Oct 16, 2026
a5900cd
feat(webpush): show channel + network in push notifications
ValwareIRC May 21, 2026
b7bdacd
fix(notifications): keep notifications on when iOS answers a prompt l…
matheusfillipe Oct 16, 2026
4a06ee3
feat(webpush): drive push from the notifications switch and share tit…
matheusfillipe Oct 15, 2026
8484016
fix(notifications): stop replaying old android taps and bound the ios…
matheusfillipe Oct 16, 2026
bfeb291
fix(webpush): route a push tap to the message time the server sent
matheusfillipe Oct 15, 2026
66ab5d1
fix(persistence): ask about persistence only while logged in to an ac…
matheusfillipe Oct 16, 2026
f186844
fix(webpush): give each server its own push registration so taps, key…
matheusfillipe Oct 16, 2026
edbc262
fix(notifications): alert once for a message android already notified
matheusfillipe Oct 16, 2026
22483d1
feat(notifications): show in settings how each server's notifications…
matheusfillipe Oct 16, 2026
7af3774
fix(webpush): subscribe as soon as the user allows notifications
matheusfillipe Oct 16, 2026
4c18cc8
feat(android): deliver notifications while closed through UnifiedPush…
matheusfillipe Oct 16, 2026
0f6226f
fix(webpush): drop stale push registrations and keep endpoints that c…
matheusfillipe Oct 16, 2026
d174b84
fix(webpush): filter channel pushes like live notifications and show …
matheusfillipe Oct 16, 2026
3380625
fix(voice): show voice channels with a mic icon and their bare name
matheusfillipe Oct 17, 2026
33a6b91
chore: merge stack/calls into stack/media
matheusfillipe Oct 17, 2026
434d4e4
chore: merge stack/media into stack/sidebar
matheusfillipe Oct 17, 2026
9c522ca
chore: merge stack/sidebar into stack/mobile
matheusfillipe Oct 17, 2026
f357885
chore: merge stack/mobile into stack/pages
matheusfillipe Oct 17, 2026
6639361
chore: merge stack/pages into stack/search
matheusfillipe Oct 17, 2026
6763884
chore: merge stack/search into stack/polish
matheusfillipe Oct 17, 2026
e023de8
chore: merge stack/polish into stack/wide-pages
matheusfillipe Oct 17, 2026
b95fda6
chore: merge stack/wide-pages into stack/infinite-scroll
matheusfillipe Oct 17, 2026
8155ee0
chore: merge stack/notifications into feature/webpush
matheusfillipe Oct 17, 2026
5b1a932
chore: merge stack/infinite-scroll into stack/notifications
matheusfillipe Oct 17, 2026
8138968
fix(ui): bots in the side pane, member menu on any click, a gap-centr…
matheusfillipe Oct 17, 2026
87fc8d4
fix(metadata): keep our whole profile when one key changes and show a…
matheusfillipe Oct 17, 2026
f03814e
fix(notifications): stay quiet only for the chat in front of you and …
matheusfillipe Oct 17, 2026
9295067
fix(mobile): close the keyboard for message actions and swipe side pa…
matheusfillipe Oct 17, 2026
2253503
chore: merge feature/webpush into stack/qa-round
matheusfillipe Oct 17, 2026
11de45f
fix(mobile): swipe a bottom sheet down to close it
matheusfillipe Oct 17, 2026
dd7c774
fix(chat): fit the completion list above the composer on phones with …
matheusfillipe Oct 17, 2026
098d756
fix(mobile): swipe down to close every bottom sheet, menus and dialog…
matheusfillipe Oct 17, 2026
60bc85d
feat(bots): type command arguments inline in the composer
matheusfillipe Oct 17, 2026
db393a7
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 17, 2026
75adc48
fix(media): stop probing a host after it blocks both requests
matheusfillipe Oct 17, 2026
209f5fc
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 17, 2026
eba00d6
fix(notifications): name the muted bell when it is what turns notific…
matheusfillipe Oct 18, 2026
6342621
fix(notifications): show the closed-app status only on mobile and ref…
matheusfillipe Oct 18, 2026
007856e
fix(notifications): pick the notifications source from one control an…
matheusfillipe Oct 18, 2026
5fbd4cf
fix(notifications): keep every source choosable and open ntfy's store…
matheusfillipe Oct 18, 2026
c66cf2c
Revert "fix(notifications): keep every source choosable and open ntfy…
matheusfillipe Oct 18, 2026
42c72cd
fix(notifications): keep every notifications source choosable
matheusfillipe Oct 18, 2026
534f291
fix(notifications): jump to the tapped message, fetching its page fro…
matheusfillipe Oct 18, 2026
813bcaa
fix(read-marker): store a start marker for channels the server has no…
matheusfillipe Oct 18, 2026
03bc93a
fix(notifications): name the muted bell when it is what turns notific…
matheusfillipe Oct 19, 2026
1c7b578
fix(notifications): show the closed-app status only on mobile and ref…
matheusfillipe Oct 19, 2026
6266fcc
fix(notifications): pick the notifications source from one control an…
matheusfillipe Oct 19, 2026
28e95ec
fix(notifications): keep every notifications source choosable
matheusfillipe Oct 19, 2026
fdb66a0
fix(notifications): jump to the tapped message, fetching its page fro…
matheusfillipe Oct 19, 2026
1ba78b1
fix(read-marker): store a start marker for channels the server has no…
matheusfillipe Oct 19, 2026
335793b
chore(i18n): translate the notification settings strings
matheusfillipe Oct 19, 2026
a929119
chore: merge feature/webpush into stack/qa-round
matheusfillipe Oct 19, 2026
063fe43
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 19, 2026
209a9d0
fix(read-marker): send the start marker only for logged-in users
matheusfillipe Oct 19, 2026
b7fef1c
chore: merge feature/webpush into stack/qa-round
matheusfillipe Oct 19, 2026
0b17f10
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 19, 2026
b7cb969
feat(bots): show every command argument inline with Tab to move and c…
matheusfillipe Oct 19, 2026
b7527dd
feat(bots): open the argument fields when a bot command is typed and …
matheusfillipe Oct 19, 2026
291bc2b
fix(notices): keep a gap under the last server notice
matheusfillipe Oct 19, 2026
ae4c235
feat(docker): read the network settings from the container environment
matheusfillipe Oct 19, 2026
bb6cc42
fix(welcome): scroll a long motd, give it more width and hide the emp…
matheusfillipe Oct 19, 2026
8f5317e
fix(sidebar): tighten the gap above channel folders
matheusfillipe Oct 19, 2026
c1e61db
chore: merge stack/sidebar into stack/mobile
matheusfillipe Oct 19, 2026
4fc6a6a
chore: merge stack/mobile into stack/pages
matheusfillipe Oct 19, 2026
073311d
chore: merge stack/pages into stack/search
matheusfillipe Oct 19, 2026
f7b5f5c
chore: merge stack/search into stack/polish
matheusfillipe Oct 19, 2026
de12f85
chore: merge stack/polish into stack/wide-pages
matheusfillipe Oct 19, 2026
a779966
chore: merge stack/wide-pages into stack/infinite-scroll
matheusfillipe Oct 19, 2026
b9c8492
chore: merge stack/infinite-scroll into stack/notifications
matheusfillipe Oct 19, 2026
190dab0
chore: merge stack/notifications into feature/webpush
matheusfillipe Oct 19, 2026
300247e
chore: merge feature/webpush into stack/qa-round
matheusfillipe Oct 19, 2026
2a412c5
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 19, 2026
6d7ca49
fix(bots): mark optional arguments clearly and give the active field …
matheusfillipe Oct 19, 2026
fc7de31
fix(chat): keep the read all chip compact with a full size tap area
matheusfillipe Oct 19, 2026
763acf0
fix(media): close an open image with the back button on android and i…
matheusfillipe Oct 19, 2026
ecee9c5
chore: merge stack/sidebar into stack/mobile
matheusfillipe Oct 19, 2026
d9cbbf5
chore: merge stack/mobile into stack/pages
matheusfillipe Oct 19, 2026
9398bee
chore: merge stack/pages into stack/search
matheusfillipe Oct 19, 2026
9afac2a
chore: merge stack/search into stack/polish
matheusfillipe Oct 19, 2026
85b4d19
chore: merge stack/polish into stack/wide-pages
matheusfillipe Oct 19, 2026
c68a344
chore: merge stack/wide-pages into stack/infinite-scroll
matheusfillipe Oct 19, 2026
076121a
chore: merge stack/infinite-scroll into stack/notifications
matheusfillipe Oct 19, 2026
86fb3bd
chore: merge stack/notifications into feature/webpush
matheusfillipe Oct 19, 2026
737063c
chore: merge feature/webpush into stack/qa-round
matheusfillipe Oct 19, 2026
9bc09b9
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 19, 2026
809616a
feat(mobile): close sheets with the browser back gesture and ask befo…
matheusfillipe Oct 19, 2026
e713375
chore: merge stack/pages into stack/search
matheusfillipe Oct 19, 2026
de576c0
chore: merge stack/search into stack/polish
matheusfillipe Oct 19, 2026
e06cab6
chore: merge stack/polish into stack/wide-pages
matheusfillipe Oct 19, 2026
2852903
chore: merge stack/wide-pages into stack/infinite-scroll
matheusfillipe Oct 19, 2026
0339d98
chore: merge stack/infinite-scroll into stack/notifications
matheusfillipe Oct 19, 2026
a96fd68
chore: merge stack/notifications into feature/webpush
matheusfillipe Oct 19, 2026
648ee10
chore: merge feature/webpush into stack/qa-round
matheusfillipe Oct 19, 2026
7cbc308
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 19, 2026
54837b9
chore: merge main into stack/fixes
matheusfillipe Oct 20, 2026
5389d55
chore: merge stack/fixes into stack/calls
matheusfillipe Oct 20, 2026
b81bbc6
chore: merge stack/calls into stack/media
matheusfillipe Oct 20, 2026
46df832
chore: merge stack/media into stack/sidebar
matheusfillipe Oct 20, 2026
6ea1a6c
chore: merge stack/sidebar into stack/mobile
matheusfillipe Oct 20, 2026
9140928
chore: merge stack/mobile into stack/pages
matheusfillipe Oct 20, 2026
a6cbfcf
chore: merge stack/pages into stack/search
matheusfillipe Oct 20, 2026
3c8b2d3
chore: merge stack/search into stack/polish
matheusfillipe Oct 20, 2026
9a665f5
chore: merge stack/polish into stack/wide-pages
matheusfillipe Oct 20, 2026
6eb837d
chore: merge stack/wide-pages into stack/infinite-scroll
matheusfillipe Oct 20, 2026
527dfd0
chore: merge stack/infinite-scroll into stack/notifications
matheusfillipe Oct 20, 2026
513bdc1
chore: merge stack/notifications into feature/webpush
matheusfillipe Oct 20, 2026
06ed0a7
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 20, 2026
3c001d9
chore: merge feature/webpush into stack/qa-round
matheusfillipe Oct 20, 2026
2c4ff63
feat(bots): start a command from its ran chip as if it were typed
matheusfillipe Oct 20, 2026
9295c7b
chore: merge main into stack/fixes
matheusfillipe Oct 20, 2026
3fe18f4
chore: merge stack/fixes into stack/calls
matheusfillipe Oct 20, 2026
00c6910
chore: merge stack/calls into stack/media
matheusfillipe Oct 20, 2026
b74b180
chore: merge stack/media into stack/sidebar
matheusfillipe Oct 20, 2026
cf73e2e
chore: merge stack/sidebar into stack/mobile
matheusfillipe Oct 20, 2026
07d935d
chore: merge stack/mobile into stack/pages
matheusfillipe Oct 20, 2026
3814edb
chore: merge stack/pages into stack/search
matheusfillipe Oct 20, 2026
d00fb7d
chore: merge stack/search into stack/polish
matheusfillipe Oct 20, 2026
9371f2f
chore: merge stack/polish into stack/wide-pages
matheusfillipe Oct 20, 2026
62ce1d1
chore: merge stack/wide-pages into stack/infinite-scroll
matheusfillipe Oct 20, 2026
d7ead7a
chore: merge stack/infinite-scroll into stack/notifications
matheusfillipe Oct 20, 2026
c2af813
chore: merge stack/notifications into feature/webpush
matheusfillipe Oct 20, 2026
b0cc70a
chore: merge feature/webpush into stack/qa-round
matheusfillipe Oct 20, 2026
26591f0
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 20, 2026
a7afa7d
fix(webpush): register after the server welcome, recover a stuck subs…
matheusfillipe Oct 21, 2026
afb7823
fix(notices): file dropped connections as notes so they do not raise …
matheusfillipe Oct 21, 2026
cfc9688
chore: merge feature/webpush into stack/qa-round
matheusfillipe Oct 21, 2026
82adc04
chore: merge stack/qa-round into stack/command-args
matheusfillipe Oct 21, 2026
fb4568e
fix(notifications): show page notifications through the push worker s…
matheusfillipe Oct 23, 2026
6c49777
fix(commands): run a bare server command on the server and rank bots …
matheusfillipe Oct 23, 2026
ea112f7
fix(messages): order our own lines by the server's echo time
matheusfillipe Oct 23, 2026
92602f4
fix(i18n): drop merge markers left in two translations
matheusfillipe Oct 23, 2026
7abce3b
chore: merge main into stack/command-args
matheusfillipe Oct 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
./src-tauri/target/*
node_modules
dist
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ RUN npm run build

FROM nginx:alpine
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
COPY docker/pwa /etc/obby/pwa
COPY --chmod=755 docker/40-obby-config.sh /docker-entrypoint.d/
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80

Expand Down
46 changes: 46 additions & 0 deletions docker/40-obby-config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/sh
# Writes the network settings from the container environment into the page,
# so one image serves any network. Unset variables keep the build-time values.
set -eu

html=/usr/share/nginx/html
templates=/etc/obby/pwa

json_escape() {
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
}

entries=""
for key in \
VITE_DEFAULT_IRC_SERVER VITE_DEFAULT_IRC_SERVER_NAME VITE_DEFAULT_IRC_CHANNELS \
VITE_HIDE_SERVER_LIST VITE_DEFAULT_OAUTH_PROVIDER_LABEL VITE_DEFAULT_OAUTH_ISSUER \
VITE_DEFAULT_OAUTH_CLIENT_ID VITE_DEFAULT_OAUTH_SCOPES VITE_DEFAULT_OAUTH_REDIRECT_URI \
VITE_DEFAULT_OAUTH_TOKEN_KIND VITE_DEFAULT_OAUTH_SERVER_PROVIDER \
VITE_DEFAULT_OAUTH_AUTHORIZE_URL VITE_DEFAULT_OAUTH_TOKEN_URL; do
eval "value=\${$key-}"
[ -n "$value" ] || continue
case "$key" in
VITE_HIDE_SERVER_LIST)
[ "$value" = "true" ] && json=true || json=false ;;
VITE_DEFAULT_IRC_CHANNELS)
json="[$(json_escape "$value" | tr -d "'" | sed 's/ *, */","/g; s/^ */"/; s/ *$/"/')]" ;;
*)
json="\"$(json_escape "$value")\"" ;;
esac
entries="$entries\"$key\":$json,"
done
printf 'globalThis.__OBBY_CONFIG__ = {%s};\n' "${entries%,}" > "$html/config.js"

if [ "${VITE_HIDE_SERVER_LIST-}" = "true" ] && ! grep -q 'rel="manifest"' "$html/index.html"; then
name="${VITE_DEFAULT_IRC_SERVER_NAME:-Obby}"
short=$(printf '%s' "$name" | cut -c1-12)
json_name=$(json_escape "$name" | sed 's/[&|]/\\&/g')
json_short=$(json_escape "$short" | sed 's/[&|]/\\&/g')
html_short=$(printf '%s' "$short" | sed 's/"/\"/g; s/[&|]/\\&/g')
sed "s|__NETWORK_NAME__|$json_name|g; s|__SHORT_NAME__|$json_short|g" \
"$templates/manifest.webmanifest" > "$html/manifest.webmanifest"
sed "s|__SHORT_NAME__|$html_short|g" "$templates/head.html" > /tmp/obby-head.html
awk '/<\/head>/ { while ((getline line < "/tmp/obby-head.html") > 0) print line } { print }' \
"$html/index.html" > /tmp/obby-index.html
cat /tmp/obby-index.html > "$html/index.html"
fi
5 changes: 5 additions & 0 deletions docker/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ server {
try_files $uri $uri/ /index.html;
}

location = /config.js {
root /usr/share/nginx/html;
add_header Cache-Control "no-cache";
}

# Cache static assets
location /assets {
root /usr/share/nginx/html;
Expand Down
6 changes: 6 additions & 0 deletions docker/pwa/head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="apple-touch-icon" href="/pwa/icon-192.png" />
<meta name="apple-mobile-web-app-title" content="__SHORT_NAME__" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="mobile-web-app-capable" content="yes" />
31 changes: 31 additions & 0 deletions docker/pwa/manifest.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "__NETWORK_NAME__",
"short_name": "__SHORT_NAME__",
"description": "__NETWORK_NAME__ chat",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "any",
"theme_color": "#202225",
"background_color": "#202225",
"icons": [
{
"src": "/pwa/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/icon-512-maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<body>
<div id="root"></div>
<script src="/config.js"></script>
<script type="module" src="./src/main.tsx"></script>
</body>

Expand Down
1 change: 1 addition & 0 deletions public/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
globalThis.__OBBY_CONFIG__ = {};
53 changes: 53 additions & 0 deletions src/components/chat/CommandArgHint.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useLingui } from "@lingui/react/macro";
import type React from "react";
import { useIsTouchInput } from "../../hooks/useIsTouchInput";
import type { CommandArgError } from "../../lib/commandInvocation";
import type { BotCommandOption } from "../../types";
import { CloseButton } from "../ui/CloseButton";

export const COMMAND_DETAIL_ID = "command-args-detail";

export const CommandArgHint: React.FC<{
param: BotCommandOption;
fallback?: string;
error?: CommandArgError;
onCancel: () => void;
ref?: React.Ref<HTMLDivElement>;
}> = ({ param, fallback, error, onCancel, ref }) => {
const { t } = useLingui();
const isTouch = useIsTouchInput();
const errorText = (e: CommandArgError) => {
if (e.kind === "required") return t`Fill in ${e.name}`;
if (e.kind === "int") return t`Enter a whole number for ${e.name}`;
return t`Enter a number for ${e.name}`;
};
const detail = error ? errorText(error) : (param.description ?? fallback);

return (
<div
ref={ref}
className="flex min-w-0 items-center rounded-t-lg border-b border-white/[0.06] bg-discord-dark-100 px-3 text-xs"
>
<span
id={COMMAND_DETAIL_ID}
aria-live="polite"
className="min-w-0 flex-1 break-words py-1"
>
<span className="mr-2 font-semibold text-discord-text-normal">
{param.name}
</span>
{!param.required && (
<span className="mr-2 rounded border border-dashed border-white/25 px-1 text-discord-text-muted">
{t`optional`}
</span>
)}
<span
className={error ? "text-discord-red" : "text-discord-text-muted"}
>
{detail}
</span>
</span>
{isTouch && <CloseButton label={t`Cancel command`} onClick={onCancel} />}
</div>
);
};
37 changes: 37 additions & 0 deletions src/components/chat/CommandChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type React from "react";
import { useIsTouchInput } from "../../hooks/useIsTouchInput";
import { keepInputFocus } from "../../lib/focus";

export const CommandChip: React.FC<{ name: string }> = ({ name }) => (
<span className="mr-1 flex h-[52px] shrink-0 items-center">
<span className="rounded bg-discord-primary/25 px-2 py-0.5 font-mono text-sm text-discord-text-link">
/{name}
</span>
</span>
);

export const CommandArgPill: React.FC<{
label: string;
value: string;
optional: boolean;
order: number;
onSelect: () => void;
}> = ({ label, value, optional, order, onSelect }) => {
const isTouch = useIsTouchInput();
const tone = value
? "border-transparent text-discord-text-normal"
: optional
? "border-dashed border-white/25 text-discord-text-muted"
: "border-transparent text-discord-text-muted";
return (
<button
type="button"
style={{ order }}
onMouseDown={keepInputFocus}
onClick={onSelect}
className={`mr-1 max-w-full whitespace-pre-wrap break-words rounded border bg-white/[0.06] px-1.5 text-left leading-[22px] hover:bg-white/10 ${isTouch ? "my-1 min-h-11 min-w-11 py-[11px]" : "my-[11px] py-1"} ${tone}`}
>
{value || label}
</button>
);
};
56 changes: 42 additions & 14 deletions src/components/chat/completionProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { useEffect, useMemo, useState } from "react";
import type { CompletionProvider } from "../../hooks/useCompletion";
import { botPriority } from "../../lib/botPriority";
import { getClientCommands } from "../../lib/clientCommands";
import type { ResolvedShortcode } from "../../lib/customEmoji";
import type { BotCommandOption, Channel, Server, User } from "../../types";
Expand Down Expand Up @@ -194,6 +195,29 @@ export function getActiveSlashQuery(
return beforeCursor.slice(1).toLowerCase();
}

// `/name ` or `/name@bot ` with the caret after the space names the bot
// command the user wants to fill in. A bare name goes to the first bot listed,
// since slashSuggestions lists bots in the order they answer.
export function typedBotCommand(
inputValue: string,
cursorPosition: number,
suggestions: readonly SlashSuggestion[],
): (SlashSuggestion & { source: { kind: "bot" } }) | null {
const match = /^\/([^\s/@]+)(?:@(\S+))? $/.exec(inputValue);
if (!match || cursorPosition !== inputValue.length) return null;
const name = match[1].toLowerCase();
const bot = match[2]?.toLowerCase();
const named = suggestions.filter((s) => s.name.toLowerCase() === name);
if (named.some((s) => s.source.kind === "client")) return null;
if (!bot && named.some((s) => s.source.kind === "server")) return null;
const bots = named.filter(
(s): s is SlashSuggestion & { source: { kind: "bot" } } =>
s.source.kind === "bot" &&
(!bot || s.source.botNick.toLowerCase() === bot),
);
return bots[0] ?? null;
}

function formatOptions(options: BotCommandOption[] | undefined): string {
if (!options || options.length === 0) return "";
return options
Expand Down Expand Up @@ -224,9 +248,9 @@ function sourceLabel(source: SlashSuggestionSource): {
}
}

// Client commands come first because they own their names, then bot schemas,
// then the server's cmdslist minus anything already listed. Two bots may both
// offer a name, and we list both so the pick fills `/cmd@bot` unambiguously.
// Client commands come first because they own their names, then the server's
// cmdslist, then bot schemas by botPriority. A bare `/cmd` runs the first of
// these, so a bot behind it is picked as `/cmd@bot`.
export function slashSuggestions(
server: Server | undefined,
channel: Channel | undefined,
Expand All @@ -245,11 +269,20 @@ export function slashSuggestions(
seen.add(c.name.toLowerCase());
}

for (const name of server?.cmdsAvailable ?? []) {
if (seen.has(name.toLowerCase())) continue;
suggestions.push({ name, source: { kind: "server" } });
}

if (server?.botCommands) {
const channelNicks = new Set(
channel?.users.map((u) => u.username.toLowerCase()) ?? [],
);
for (const [botNick, list] of Object.entries(server.botCommands)) {
const byPriority = Object.entries(server.botCommands).sort(
([a], [b]) =>
botPriority(server.bots?.[a]) - botPriority(server.bots?.[b]),
);
for (const [botNick, list] of byPriority) {
const scope = server.bots?.[botNick]?.scope ?? "channel";
if (scope === "channel" && !channelNicks.has(botNick.toLowerCase()))
continue;
Expand All @@ -260,21 +293,12 @@ export function slashSuggestions(
options: c.options,
source: { kind: "bot", botNick, scope },
});
seen.add(c.name.toLowerCase());
}
}
}

for (const name of server?.cmdsAvailable ?? []) {
if (seen.has(name.toLowerCase())) continue;
suggestions.push({ name, source: { kind: "server" } });
seen.add(name.toLowerCase());
}
return suggestions;
}

// A bot command that declares options goes to `onPickWithOptions` so its
// values are entered through typed form fields.
export function slashProvider(
commands: readonly SlashSuggestion[],
onPickWithOptions: (suggestion: SlashSuggestion) => void,
Expand Down Expand Up @@ -307,7 +331,11 @@ export function slashProvider(
</span>
)}
<span
className="row-meta ml-auto shrink-0 [[data-compact]_&]:order-last"
className={`ml-auto shrink-0 [[data-compact]_&]:order-last ${
cmd.source.kind === "server"
? "rounded px-1.5 text-[11px] font-semibold uppercase bg-discord-primary text-white"
: "row-meta"
}`}
title={source.title}
>
{source.label}
Expand Down
Loading
Loading