Commit Graph

154 Commits

Author SHA1 Message Date
3c389de36b Resolve the client's data directories in one place
Stores read their paths from AppDirs instead of user.home, so a platform
without a home directory can point them elsewhere at startup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 07:52:32 +00:00
1d61f458ae Process the microphone like TS3, and tell it about key presses
The capture path now runs the ported WebRTC chain in place of the
home-made noise, typing and gain stages, which are removed.

- As in TS3, the speech detector judges the raw microphone signal, while
  the level meter and volume gate see the processed one.
- Noise removal takes TS3's four levels (6, 12, 18 or 21 dB); a stored
  0..1 level falls back to TS3's default of 12 dB.
- Every key press from the global input hook reaches the connected
  microphones and the microphone test, so typing attenuation engages
  while the user types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 20:47:04 +00:00
de5f7da236 Port TS3's WebRTC capture processing to Java
TS3 pre-processes the microphone with WebRTC's audio processing module.
AudioProcessor ports the stages it enables, in APM's order, per 10 ms at
48 kHz:

- The high-pass filter APM forces on with noise suppression, then the
  three-band split and WebRTC's noise suppressor at TS3's four levels.
- The transient suppressor behind "Typing attenuation", which only acts
  while it is told about key presses.
- AGC2 adaptive digital, WebRTC's successor to the AGC1 that TS3 runs.

The tests compare against 16-bit outputs of upstream APM builds
(webrtc-audio-processing 2.1, and 1.3 for the transient suppressor).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 20:46:46 +00:00
ee0f15fb3c Stop tripping the server's flood protection with query commands
channelinfo, clientinfo, channellist and clientlist are query commands
that count heavily against anti-flood; clicking through the tree got
every command refused within seconds. Use what the official client does:

- Channel descriptions via channelgetdescription, whose answer arrives as
  an invoker-less channel edit.
- Client version and platform via clientgetvariables, answered as a
  client update.
- Connection info takes version and platform from the model and idle
  time from notifyconnectioninfo instead of polling clientinfo.
- Drop the channellist/clientlist re-read after connecting: the events
  during connect already carry everything, and more (groups, unique ids,
  away state).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 18:32:05 +00:00
b01ed43d60 Find clients and channels in the server tree
Ctrl+F in the tree opens a find bar below it that washes matching rows,
steps the selection through them and can prune the tree down to the
matches and the channels leading to them. ServerTreePanel becomes a
panel holding the scroll pane and the bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:09:45 +00:00
d05c04afb4 Find in chat with Ctrl+F
A find bar above the conversations highlights matches and steps through
them, or filters the selected tab down to matching messages. New lines
no longer pull the view away while it is open. Ctrl+F opens it from
anywhere the tree doesn't claim the shortcut.

Note tabs are now found by their scroll pane like other tabs, so sending
checks for a target instead of assuming every tab has one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:09:45 +00:00
a4057cd499 Add text queries and whole-log chat search
TextQuery holds a find bar's text and options (match case, whole word,
regex) and matches plain text; HtmlText strips a log entry down to it.
ChatLogStore can now search a whole log rather than only the history
loaded into a chat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:09:45 +00:00
249430efad Hide query clients from the log and name server groups correctly
- Query clients were already left out of the tree, but their joins, moves
  and ping-timeout drops were still logged and could play sounds; TS3 keeps
  them out of sight entirely.
- The name field of notifyservergroupclientadded/deleted is the client's
  nickname, not the group's, so "added to server group" named the wrong
  thing. Resolve the group by id instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:08:18 +00:00
3f65e95afd Fix channel reordering and keep the channel tree in sync
- Sort a channel among its current siblings with channeledit channel_order,
  as the official client does; the server refuses channelmove within the
  same parent ("already member of channel").
- Relink siblings when a channel_order edit arrives: the server never
  re-announces the neighbours, so the tree drifted out of order after any
  reorder.
- Apply password, permanence and max-clients flags from channel list,
  create and edit events, and let an emptied topic clear the old one.
- Log moves like TS3: "..., new parent channel is X".
- Play identical sounds that start within 250 ms only once, so bursts
  (a channel deleted with its subchannels) don't stack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:06:53 +00:00
4de3ad4cab Make the talking-while-muted beep optional
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 14:25:58 +00:00
03ea2c86c3 Give the window its own identity in Linux task bars
AWT names the X11 window class after the thread that first touched the
toolkit, so the client shared java-awt-EventDispatchThread with every other
Swing app. Set it to ts3j-client instead (the jar's manifest opens the AWT
internals this needs), and install a matching .desktop launcher and icon in
the user's data directory: without a launcher claiming the class, Plasma falls
back to the process's command line and files the window under Java.

The window icon is now offered at several sizes instead of 16 px only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 14:21:10 +00:00
f27cc40755 Build ts3j from source as part of the client build
The client resolved ts3j 1.0.3 from ~/.m2, so a machine that pulled new
ts3j sources without re-running mvn install silently shipped the old
library - here one without the packet generation fix, which fails
connects to busy servers with "mac check in EAX failed".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 12:43:56 +00:00
5fc2405d4a Update ts3j: resend like the official client; clean up failed connects
- ts3j: native resend timing, INIT1 resends, one clientinitiv per handshake,
  non-blocking disconnect.
- Ignore errors from a connect attempt that has been superseded or cancelled.
- Show the whole cause chain in network error messages.
- -Dts3j.debug=true enables ts3j's protocol logging.
- Tests run with a throwaway user.home instead of the real profile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 04:58:21 +00:00
c743efc3b6 Keep the window responsive while the channel tree loads
Connecting to a server with ~560 channels froze the UI for over two
seconds: every model event queued its own full tree rebuild, and each
rebuild expanded row by row with Swing's variable-height layout cache,
which measures every row on each expansion.

Coalesce model-change bursts into one rebuild and switch the tree to the
large-model layout (it already has a fixed row height), which only
measures rows on screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 23:21:52 +00:00
3187608579 Update ts3j: survive packet loss during the handshake; allow 30 s to connect
Picks up the ts3j fixes for "mac check in EAX failed" disconnects on lossy
links (retransmitted handshake packets now decrypt) and for the handshake
stalling when clientek's ACK is lost, plus tolerating an empty
client_servergroups.

The connect timeout also covers downloading the channel list, which on a
big server over mobile data can take longer than the previous 10 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 23:21:52 +00:00
9d996f48a2 Scale the interface fractionally on Linux
Java2D on X11 (and so under XWayland) truncates its scale to a whole
number, drawing 150 % at 100 %. On Linux, pin Java2D to 1 and hand the
full factor to FlatLaf, taken from XSETTINGS Xft/DPI, KWin's Xwayland
scale or GDK_SCALE, or chosen in Options → Design.

The client's own sizes now go through UIScale: borders, gaps, dialog and
row sizes, icons (rendered at the scaled size, so they stay crisp),
custom-painted components and font sizes in the HTML views.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 22:33:27 +00:00
1fddae3f0b Always rebuild the swing jar before shading it
Without forceCreation, maven-jar-plugin kept the previous shaded
ts3-client.jar when the module's classes were unchanged, and the shade then
used it as input: the stale ts3j classes inside it won over the freshly
installed ts3j, so ts3j fixes never reached the built client without a clean.

Also updates ts3j for the empty server-group parsing fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:30:48 +00:00
33411fcd13 Update ts3j: fix EAX MAC failures on busy servers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:09:09 +00:00
2a73900dcc Show client avatars and let the user set their own
Avatars are fetched from the server's file repository as /avatar_<id>,
verified against client_flag_avatar and cached on disk; animated GIFs play in
the info panel. Self > Set avatar... uploads or removes our own picture within
the server's size limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:03:00 +00:00
dbb425403a Import bookmarks and identities from the TeamSpeak 3 client
The official client keeps what a myTeamSpeak account synchronises as protobuf
Item_Data blobs in the ProtobufItems table of settings.db. Decode them, adopt
them on first run when we have no identity yet, and offer a re-import from
Manage Bookmarks and Identities. The settings.db reader moves from contacts to
a new teamspeak package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:02:52 +00:00
bab89f9b50 Add the chat log store missing from earlier commits
TeamspeakConnection and ChatPanel already use com.ts3client.chatlog, but the
package itself was never committed, so the tree did not build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:02:40 +00:00
f409f500fd Add the contact list (friends, blocked, neutral)
Mirrors the TeamSpeak 3 client's Contacts: clients can be filed as friend,
blocked or neutral from their context menu or by dragging them into the
Contacts window, given a custom and phonetic nickname, shown under either
name or both, and per contact muted automatically, ignored in server/channel
or private chat and pokes, have their away message hidden and whispers
allowed or denied. Friends are green and blocked clients red in the tree
(optional), new contacts start from per-type defaults ("Set Defaults"), and
the global whisper policy lives in Options -> Contacts. "Change Volume..."
adds a per-client dB gain that a contact entry remembers.

Contacts are stored in ~/.ts3jclient/contacts.txt using the exact record
format the official client keeps in the Contacts table of its settings.db
(field names and codes recovered from the binary), so entries move between
the two verbatim. A small read-only pure-Java SQLite reader imports the
official client's list on first run and on demand, without a driver.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 07:50:58 +00:00
22e0304f25 Add the Tools > Ban List window
Reimplements the official client's ban list: a searchable table of the
server's bans with Add / Remove / Edit, an own-ban filter and highlight,
choosable columns, and the Add/Edit dialog whose IP and name patterns
can be read as IPv4/IPv6 wildcards, fixed strings or regular expressions
(translated to the regexes banadd takes exactly as the official client
does). Editing is add-then-delete, since the protocol has no banedit.

The ban list is read from the notifybanlist burst that answers banlist,
like channelpermlist. The menu item is only shown where the server grants
b_client_ban_list.

Also fixes ServerModel numbering permissions from 0 instead of 1, which
had every selfPermissionValue() lookup reading the neighbouring
permission — including the group-assignment menus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 23:00:18 +00:00
636090d075 Start overlapping notification sounds without delay
The sound mixer rendered as fast as the playback line accepted, running a
full device buffer ahead of real time, so a sound fired while another
played could only join the mix behind all of that queued audio, and each
further one landed later still. Pace the mixer against the clock so it
never commits more than a few frames ahead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:54:02 +00:00
ad62f5fc92 Update connection and chat handling 2026-09-06 19:14:42 +00:00
62d5af7cf9 Smooth incoming voice under jitter 2026-09-06 13:43:11 +00:00
a01c598fe6 Remember how tall the user made the info panel
Kept as a share of the tree/info column, so it survives new tabs, a resized
window and the next session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 08:11:29 +00:00
02533b543d Sit the info panel's link in the viewport's corner
It overlapped the scroll bar; the viewport is what the scroll bars have
already been taken out of. The layered pane also has to report a size now,
or the split pane above it has nothing to size the panel from.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 08:11:29 +00:00
5a00015824 Float the info panel's "Show in chat tab" link in its corner
As a real label over the text, it stays reachable however long a description
runs, instead of trailing off the end of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:46:42 +00:00
ffe98be649 Trim the info panel's rows
A channel's permanent/temporary type is not worth a row, and a client's
platform reads better on the version line: Version: 3.6.2 … on Linux.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:46:36 +00:00
65f3777673 Let a chat tab grow to fit its name
Its header was pinned to the width it was built with, clipping longer names.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:27:17 +00:00
9101542f0a Switch chat tabs by clicking them, and tighten their padding
A custom tab component swallows the press, so the tabbed pane never selected
the tab itself; the reorder listener already on every tab part now does it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:04:26 +00:00
a4a8b39410 Paint the Away button as flat as its neighbours
Inside its panel it was drawn as a filled button, which read as half-active.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:58:08 +00:00
f480262c39 Drop the chat's Send button
Enter sends; the button only ate width.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:57:40 +00:00
8e3e9401d2 Name the server and channel chat tabs after where we are
Both carry an icon now, as the private chats do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:57:38 +00:00
966f3fe9b1 Log our own channel switches in the server tab
Every other client's move was logged; ours went by silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:57:23 +00:00
b85d1e5076 Connect a left-clicked bookmark on the current tab
Middle-click still opens the server in a new tab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:55:29 +00:00
50b71eb12e Leave the microphone alone when muting the speakers
Deafening silenced capture too, so coming back needed two clicks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 06:53:59 +00:00
98719d717e Darken the dark theme, and hover a tree row like a menu
The client's own look-and-feel defaults now live beside LookAndFeelManager as
FlatLaf properties files, which FlatLaf loads on top of each theme's own,
instead of a handful of UIManager.put calls.

The dark theme is built on three surfaces keyed to the toolbar's #282828: the
window chrome, the tab strips and menu bar one step below it at #232323, and
the content the client shows — channel tree, chat log, text fields, lists — at
#1e1e1e. Selections are #364b6f, and channel names in the tree are white; the
chat lines about server events keep the blue they had, which channel names no
longer carry. Everything FlatLaf derives (borders, scroll bars, hover states)
follows from those.

A read-only editor pane is a chat log or an information view here, not a
disabled input, so it keeps the content background rather than FlatLaf's
inactive one; and since neither a text view nor a tree fills its viewport, the
space below the last line takes their background instead of the window's.

The row under the pointer is now washed in the same colour a hovered menu is,
rather than outlined. FlatLaf states that colour as a shift of the surface
beneath it, so it is resolved against the window background exactly as the
menu bar resolves it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:07:19 +00:00
35f10ed14c Paint the client with FlatLaf, in a light and a dark theme
The look-and-feel is FlatLaf now, chosen light or dark on the new Design
tab and remembered in the settings. Theme keeps only the colours TeamSpeak
gives meaning to (talking, away, muted, channel and server names) and takes
its surfaces and fonts from the look-and-feel, so a switch recolours the
whole window, chat logs included: the HTML panes share one themed stylesheet
and re-parse their content when the theme changes.

Also fixes the channel tree not scrolling: the tree listened for the mouse
wheel to re-check its hover row, and AWT only forwards a wheel event to the
enclosing scroll pane when the component under the pointer has no wheel
listener of its own. The hover row now follows the viewport instead, which
also covers scrollbar drags and keyboard scrolling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:06:55 +00:00
d952ffd856 Put each client limit's count beside its "Limited" choice
The counts sat on a line of their own below the choices they belong to,
which also made the Advanced tab the tallest of the four and so decided
how much of the dialog the tab strip took. Each box now keeps its
natural height, and the free space collects below them.

Advanced went from 250px to 220px of preferred height, leaving the tab
strip about a third of the dialog rather than half.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 20:11:40 +00:00
acfe25db15 Show our own name in bold, in the row and in its editor
The renderer asks the panel which id is ours rather than reading
ClientEntry.self: our own row usually arrives among the enter-view events
of the connect handshake, before the connection knows its own client id,
so that flag was left false. It is now stamped once the id is known,
which also fixes the "(you)" marker in the info panel.

The inline editor matches, and sits 2px further left so its text lines up
with the label it replaces rather than with its own border.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:59:55 +00:00
08e6c51e90 Leave a talking client's name alone, and rename from the whole row
Bolding the name of whoever is speaking re-measures the row while the
label keeps its old width, so a nickname spent every utterance clipped to
an ellipsis. Talking already shows in the client's icon, which is where
TeamSpeak shows it too, so the name keeps its normal weight and colour.

Renaming now starts from the tree's own double-click handling, which
works a row by its whole line, instead of Swing's, which only sees clicks
that land on the label.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:50:03 +00:00
acd438dd14 Don't send a rename that changes nothing
Closing the inline editor without touching the name asked the server to
rename us to the name we already hold, which it refuses with "nickname
is already in use".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:43:00 +00:00
6e65797f25 Rename yourself by double-clicking your own tree row
The row turns into a text field holding the current nickname, selected so
typing replaces it; Enter commits, Escape leaves the name alone. Only our
own row edits — every other double click keeps its meaning, and ours no
longer opens a private chat with ourselves.

The editor keeps the row's own icon (the inherited one knows only the
renderer's default leaf icon) and selects the text on a short delay,
after the release that opened it has been turned into a caret placement.
The tree nodes only mirror the connection's model, so the typed name is
sent to the server rather than written into the node; the row follows
when the rename is reported back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:38:49 +00:00
fa99cadc44 Fill the selected tree row across its whole width
The cell renderer paints the selection only behind the label, which now
looks short next to the full-width hover outline and the full-line hit
area. The tree fills the selected rows itself, in the renderer's own
selection colour so the two meet seamlessly, which means it can no
longer clear its background as an opaque component would.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:16:06 +00:00
9ba0982f25 Work tree rows by their whole line, and outline the hovered one
Swing hit-tests a tree row only as far as its label, so a click past the
end of the name did nothing. Rows are now found by their vertical band
alone, which covers selection, the context menus, double-click join and
middle-click info; the expand handle to the left of a label still works
the row without selecting it.

The hovered row is outlined across that same full width, so what a click
would act on is visible before making it. Spacers, which nothing can be
done with, are skipped, and the outline gives way to the drag-and-drop
indicator while a drag is in progress.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 18:56:17 +00:00
275de77c8b Bold the channel references in the log, as the client ones are
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 18:24:05 +00:00
189620df7f Link the clients and channels named in server log lines
Log lines now name clients and channels with TeamSpeak's own client:// and
channel:// links, the same ones a dragged-in reference produces, so the
chat log's existing handling turns them into the context menus.

The log is rendered with a links-only pass rather than the full BBCode
renderer: its lines are assembled by the client itself, so nothing else in
them should be interpreted — a channel called "[b]VIP" stays literal.
Channels we no longer have (a deleted one) are still named in plain text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 18:11:14 +00:00
cb05b097a6 Add "Change Description" to the client menu
Offered for our own client as well as others, with TS3's edit icon. The
description is read back from the server before prompting, since the
model only carries one for clients whose info has been looked at.

Note that client_description is not a clientupdate property even for
ourselves — the server only takes it through clientedit — and that our
own change is not echoed back to us, so the local entry is updated here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 18:04:35 +00:00