KLIPY joins GIPHY and Tenor as a GIF provider. Set KLIPY_API_KEY to enable it, and PREFERRED_GIF_SEARCH (klipy, giphy or tenor) to pick which provider serves the picker when more than one key is set. With no preference the server tries GIPHY first, then KLIPY, then Tenor. Existing GIPHY and Tenor setups are unchanged. The picker footer credits whichever provider served the batch.
* Rework message search into a persistent overlay panel
Search results now open as an overlay on the right side over the member
list, the way Discord does it. The panel stays open across channel
switches and only closes when you close it yourself. Public channels
share one panel while each DM keeps its own.
Results paginate at 25 per page. The panel clears and asks you to
refresh when a channel or one of your roles changes, so it never shows
messages you can no longer see. A collapsed sidebar expands
automatically while the panel is open.
* Make message search global with SQLite full text search
Search now spans every channel you can see instead of just the current
one, backed by an FTS5 index that stays in sync with messages through
triggers. Results are scoped to your channel memberships on every query,
so you never see anything you don't have access to. in:#channel narrows
to one channel, from: and has: still work.
The tokenizer is admin configurable through HAVEN_SEARCH_TOKENIZER and
reconciled at boot: unicode61 by default (closest to Discord), with
porter for stemming and trigram for substring matching. Changing it
rebuilds the index once on the next restart. Results come back 25 per
page with a total count, sorted newest first.
* Add a filter picker and cross-channel results to search
Clicking the search box opens a filter popover with from:, in: and has:
pickers. Each lists what you can see (members, channels, media types),
rendered client side with a prefix filter box, and clicking one appends
the filter to the search box. A filter button next to the box toggles
the popover so you can reopen it without closing search.
Channel names are not unique, so in:#code selects a channel by its code
while in:name still matches by name. The picker fills the code form so
duplicate names are never ambiguous.
Search results now show the channel each message came from as
#name (code) above the author, and clicking a result switches to that
channel before jumping to the message so cross-channel results work.
* Add date, pinned, and audio filters plus a sort control to search
The filter popover gains before:, after: and during: date pickers, a
pinned toggle, and audio in the has: list. before/after take a boundary
date and during matches a single day. A sort row lets you order results
by newest, oldest, or most relevant, which the search engine already
supported but the UI never exposed.
All of these run server side against the message index, so they stay
scoped to the channels you can see.
* Guard against stale search results, add a loading state and recents
Each search request carries a sequence token that the server echoes
back, and the client only renders the response matching its latest
request. A slow earlier query can no longer overwrite newer results.
A spinner shows while a request is in flight.
Recent searches are kept in local storage and offered at the top of the
filter popover when the box is empty. They save on Enter, so the list
holds real searches rather than the partials typed on the way there.
* Render search results as real messages and rate limit search
Search results now render through the message formatter, so images,
uploaded video and audio, spoilers, mentions and links look the same as
they do in chat. Audio uses preload=none so nothing loads until play.
Page links are not unfurled automatically. Each link gets a Load button
that renders just that embed through the existing preview scheduler and
cache, so a page of results never fires a burst of preview fetches.
Right clicking a result opens a Copy link and Delete menu. Delete targets
the result's own channel, and the row is removed only once the server
confirms the delete. Results that head a thread show a reply count badge.
The bottom pager gains a page number input so you can jump straight to a
page instead of clicking through the arrows one fetch at a time.
Search also gets its own per account rate limit of 10 requests per 10
seconds. When it trips the client keeps its current results and shows a
toast.
The OIDC issuer URL was having its trailing slash stripped, which broke providers like Authentik that publish the issuer with one. The issuer is now left as entered, and the discovery path is built correctly either way.
Also documents OIDC_CLIENT_SECRET in .env.example and docker-compose.yml.
Reported by @jesjhoward in #12 and @RCCore in #5501.
Adds TURN_URL, TURN_SECRET, TURN_USERNAME, TURN_PASSWORD env vars.
New /api/ice-servers endpoint serves STUN + TURN config to authenticated clients.
Supports both coturn shared-secret (time-limited credentials) and static auth.
Client fetches ICE config on init and refreshes before each voice join.
Closes#37
- Relocated voice controls panel from left sidebar to right sidebar
- Buttons are now icon-only circles with hover tooltips (Mute, Deafen, Share, Music)
- Added Voice Settings gear button that toggles a slide-up panel with NS slider
- Right sidebar restructured: Voice section (pinned top) > Online Users (scrollable) > Settings (pinned bottom)
- Voice section takes priority; user list scrolls independently below
- Mobile: header voice indicator opens right sidebar instead of left
- Includes all prior changes: admin self-nerf prevention, Channel Roles tags rework, Docker audit