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>
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>
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>
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>
The description is fetched on demand and cached, but nothing invalidated
it: notifychanneledited never carries the text, and the separate
notifychanneldescriptionchanged was ignored. Handle that event, and
refresh locally after our own edit, since the server does not notify the
invoker.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ftgetfilelist is answered with a notifyfilelist event per entry and a
closing notifyfilelistfinished, not with rows of the command's own reply
— and ts3j dispatches anything named notify* as an event, so every
listing came back empty. The entries are now collected for the request
in flight, which is what the icon chooser and the file browser read.
The icons themselves live in the repository's "icons" directory on newer
servers rather than loose in its root, so both places are read. They are
still addressed as /icon_<id> for download, upload and delete, which is
how the server resolves them regardless of where it keeps them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The official client creates and edits channels with one dialog, so
ChannelEditDialog becomes ChannelDialog with two entry points. Creating
starts from caller-chosen defaults instead of a channelinfo and sends a
channelcreate carrying only what deviates from a plain new channel —
TeamSpeak checks a create permission per property that is present.
The server row now has a context menu with "Create Channel" and "Create
Spacer" (pre-filled with the lowest free [spacerN], since channel names
must be unique), and a channel's menu offers "Create Channel" and
"Create Sub-Channel", both starting out temporary.
Verified against the test server: a temporary channel is created, and a
permanent one a guest may not create reports the server's refusal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Recovers the official client's CreateChannelDialog layout so the widget
set, labels and tab order match: name/icon/password/topic/description
above Standard, Audio, Permissions and Advanced tabs.
Core gains ChannelSettings (diffed against the original so channeledit
only carries changed properties) and ChannelAdmin (channelinfo/edit,
channel permissions, the server icon store). TeamspeakConnection now
owns the event executor so it can drain queued notify events before
reading a command's result, which channelpermlist needs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lets moderators assign or revoke server and channel groups for a
client directly from its context menu, with per-group checkboxes
gated by the client's actual TS3 permissions (i_group_needed_member_
add/remove_power vs. each group's n_member_addp/removep, including
TS3's -1 "unlimited" sentinel). A "Server Groups Dialog..." entry
shows every server group, graying out ones the client can't touch.
Group membership changes now update ClientEntry locally instead of
only logging them, so the checkboxes and dialog reflect a change
immediately instead of needing a reconnect. The server's default
channel group (e.g. "Guest") is hidden from the channel-group menu
since it's not something you assign directly.
Extract notify-event handling and the native-style server log/sound
announcements into ConnectionEventHandler, and getconnectioninfo
request/response + stats-snapshot building into ConnectionStatsCollector.
TeamspeakConnection remains the public facade (1828 -> 1037 lines); its
external API is unchanged. Pure refactor, no protocol/timing changes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The toolbar now has a right-click menu to hide/show the status bar and
a short Master Volume slider (drag or scroll) that multiplies both
voice and notification volume via new Settings.effectiveOutputVolume()/
effectiveSoundVolume() helpers.
Also splits MainFrame's toolbar, menu bar and status bar construction
into their own MainToolbar/MainMenuBar/StatusBar classes, each talking
back to MainFrame only through a Listener interface.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Native TS3 logs client joins/leaves/moves, group changes and channel edits
to the server tab; minor local-only notices (mic mute, tray) no longer
spam the log. Chat tabs scroll by mouse wheel like server tabs. The info
panel can now be moved into a persistent, generically-labelled chat tab
that tracks tree selection and collapses the panel's space entirely; a
tree rebuild no longer briefly fires a null selection that reset this.
Added "Find Client in Channel Tree" to the client context menu, and tree
selection now survives model rebuilds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Mute/deafen status now goes out through clientupdate instead of
clientedit, which silently rejected client_input_muted/output_muted
since they are runtime status, not editable client properties.
- Publish client_input_hardware so other clients see "Microphone
Disabled" instead of silence while another tab holds the capture
device; track input/output hardware flags for other clients too,
and show a distinct grey "disabled" icon instead of reusing the red
"muted" one for both the tree and the info panel.
- Implement TS3's Enable/Disable/Toggle Local Mic Mute hotkeys: they
silence capture like a real mute, but never touch the published
mute status or play a sound.
- A speaker's "talking" indicator only ever cleared when its
zero-length end-of-burst voice packet arrived; if that one UDP
packet was lost, the indicator stuck until their next burst. Add a
watchdog that clears it after 200ms of silence from that speaker
regardless.
Ports WebRTC's rnn_vad (as TS3 embeds it) to Java: LPC, pitch
estimation, spectral features and the RNN itself, feeding a
speech-probability detector that replaces the old SpeechDetector.
Also switches the volume-gate threshold from raw dBFS to
InputLevel's scale, matching TS3's own slider and range, with a
migration for settings saved under the old key.
A message-only window registers the keyboard and mouse with
RIDEV_INPUTSINK, so every key and button arrives as WM_INPUT whether or
not the client is in front. Raw input only observes, where a
WH_KEYBOARD_LL hook sits in the path of the system input queue and can
swallow a keystroke; it also reports the side buttons and both edges of
every key, which push-to-talk needs.
Keyboard codes stay in each platform's own numbering — X keycodes on
X11, set-1 scan codes with the E0/E1 escape folded into the high byte on
Windows — since that is what the input API reports and the key-naming
call expects. Bindings are per-machine either way, as TeamSpeak's own
per-OS keydefs are. Mouse buttons are unified on the X numbering, so
"Mouse 4" means the same thing on both.
The RAWINPUT decoding lives in its own class so it can be tested off
Windows; the window and its pump have not been run on Windows yet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bindings are captured system-wide rather than only while the window has
focus: X11's RECORD extension where the X server sees every key, and a
/dev/input reader as the Wayland fallback. Any key can act as a modifier,
mouse buttons included, as TS3 allows.
The action catalogue, its three categories and the "advanced actions"
split are reverse-engineered from the original client; actions this
client cannot perform are listed but greyed out. Push-to-talk becomes one
of these hotkeys, so the old focus-bound pushToTalkKey setting is gone and
the Voice Activation button edits that binding instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Other clients see the version and platform from clientinit in the info
panel, and until now that was whatever ts3j sends. The new Client Version
tab picks one instead.
Servers refuse a version they do not know with "client is modified", so
free text is not enough: the list holds genuine version/platform/signature
triples from released clients, newest first per platform. The fields stay
editable for a version whose signature is known from elsewhere, and a
refused connection now says where to change it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The client menu gains "Kick Client from Channel", "Kick Client from
Server" and "Ban Client", each asking for a reason first. Kicks use a
shared prompt that enforces the protocol's 40-character limit; the ban
dialog adds a duration, whose unit dropdown ends in "Permanent" — the
zero-length ban the server understands.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tree's top row showed the placeholder name, since nothing ever read the
real one; take it from initserver, which ts3j now keeps.
There is only ever one top-level row, so its expand control bought nothing and
cost horizontal space in a narrow view. Drop it, ignore collapse requests on
the root, and tighten the per-level indent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Track the subscription state from the notifychannelsubscribed events, draw
subscribed and unsubscribed channels apart with the pack's icons (and the
built-in glyph as a hollow cone), and offer subscribe/unsubscribe entries —
channel and family — in the channel context menu.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The toolbar gains an away toggle with a drop-down: it toggles away on the
current server with no message, while the menu carries the global actions
("Set Globally Away", "Set Globally Away Status"), the saved presets and
their editor. Presets live in ~/.ts3jclient/away.properties and are
managed in a list with add/remove and double-click renaming.
Clients in the channel tree now show their away message in brackets after
the nickname instead of their primary server group.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The server sends notifyclientupdated to the other clients only, so a
clientupdate of our own never came back to us and our row in the tree kept
its old icons until something else refreshed it. Apply the change to the
local model as we send it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Icon packs are read in TeamSpeak's own format — a zip (or unpacked folder)
of SVG/PNG art plus a settings.ini mapping icon keys to files — so the packs
of an installed client and the ones from its add-on site work unchanged.
Legacy packs without that mapping (default.zip) are resolved by their file
naming convention instead, picking the resolution closest to the drawn size.
The pack draws the tree, toolbar, menus, context menus, file browser and the
default group icons; a pack's FALLBACK option decides whether what it lacks
comes from default.zip, and anything still missing falls back to the icons
the client draws itself. Options → Design picks the pack and shows a viewer
of everything in it.
Vector art is rasterised with JSVG, and the pack search roots are shared
with the sound packs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds TeamSpeak-format sound packs: a folder of waves plus a settings.ini
mapping actions to play()/say() entries, with ${clientType} and friends
resolved per event. Packs are found in the client's own sound folder, an
installed TS3 client and a folder of the user's choosing, so the official
packs work unchanged.
Each action can be switched off or marked important; important actions are
the only ones still played while the speakers are muted, as in TS3. The new
Notifications options page lists them by category, greys out what the active
pack has no sound for, and previews on double-click.
Sounds are decoded, resampled and mixed onto a single playback line that is
only open while something plays, so overlapping events never fight over the
device.
Fires the events from the protocol layer, following TeamSpeak's own
distinctions: reason ids separate switched/moved/kicked/banned/timed out,
and visibility decides appears/disappears/stays.
Also fixes a ts3j trap in the process: a field an event never carried reads
back as an empty string, so the existing "e.get(x) != null" checks were
always true. That made a partial clientupdate (someone muting) announce a
stopped recording, and it let a nickname-only update reset another client's
mute/away flags and talk power, or a channel edit blank the channel name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The channellist command answers with a narrower field set than the events
the server pushes while connecting, so reconciling against it wiped the
icon ids those events had just delivered — leaving pre-existing channel
icons invisible while a live icon change still showed up. Treat a missing
or blank icon there as "not reported" rather than "no icon"; only the edit
event, which lists exactly what changed, may clear one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The right-aligned icon strip is positioned against the viewport rather than
the row, so the pixels a blit scroll reuses are stale and the icons leave
ghosts behind. Scroll the viewport in simple mode, and widen every repaint
request to the full visible width so a row whose label changes size cannot
leave its icons behind either.
Also stop blacklisting an icon whose download failed only because the file
transfer channel was not up yet: channel rows are painted during connect,
well before that point, so their icons would never be retried.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Channels carry a channel_icon_id just like groups do; track it on
ChannelNode from the channel list and the create/edit events, and paint it
in the same right-aligned strip that already carries a client's group
icons. Spacers keep their bare look.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Download the icons a server advertises for its groups over the file
transfer channel, cache them under the config directory, and draw them as
a badge strip on each client's row, right-aligned against the tree's
visible edge like the TeamSpeak client does. Groups without a custom icon
fall back to the bundled default set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Clients can be dragged into a channel and channels re-parented or
reordered, with an insertion line (or a row outline, for "into this
channel") showing where the drop lands.
Channel positions come from channel_order, which is the id of the
channel above rather than an index, so siblings are now ordered by
walking that chain and the neighbour links are repaired locally on
create, move and delete -- the server only announces the channel
that changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Each connection now lives in its own ServerTab with its own tree, info
and chat views plus its own mic-mute, deafen, away and commander state.
MainFrame keeps only the shell (menus, toolbar, status bar) and shows
one tab at a time; the tab bar below the toolbar appears once a second
server is open.
Incoming voice from every connection mixes into the same output — each
connection already renders one line per speaker. Capture is exclusive:
a connection only opens the microphone while it holds it, and a toolbar
button moves it to the visible tab. The mic follows a disconnect to
another connected tab, and push-to-talk drives whoever holds it.
Startup bookmarks are no longer mutually exclusive: every flagged
bookmark opens its own tab.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace the modal add/edit form with a master/detail dialog: bookmark
list on the left, the selected bookmark's settings on the right. Edits
are flushed back into the bookmark when the selection changes or the
dialog closes.
Bookmarks gain a "connect on startup" flag (only one at a time, since
the client holds a single connection); MainFrame connects to it once
the window is up.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace the chat target dropdown with a tab strip along the bottom of the
chat panel: a Server and a Channel tab plus one closable tab per private
chat, each with its own log and an unread marker.
Render logs and channel/client descriptions as HTML through a new BBCode
renderer in core, supporting b/i/u/s, color, size, url, center and lists,
with auto-linked bare URLs. Input is escaped and tag values validated, so
server-supplied text cannot inject markup or unsafe schemes.
Sender names and TeamSpeak's own client:// links open the same context menu
as the server tree (menus extracted into ClientMenu/ChannelMenu); channel://
links open the channel menu. Links are styled by CSS class: identities bold
in the author colour, other TS protocols plain, and links leaving the client
underlined so they cannot be mistaken for an identity.
Dragging a client or channel out of the tree yields the TS3 link BBCode.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bookmarks can store a channel to join on connect, given either as a
"/"-separated path or a channel id, plus an optional channel password.
"Add bookmark" offers to remember the channel you are currently in.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Manage several TeamSpeak identities like the official client instead of
using one auto-generated identity file.
- IdentityStore/IdentityEntry keep each identity as a TeamSpeak-format INI
in ~/.ts3jclient/identities, so files interchange with the TS3 client;
supports create, import, export, rename, remove and a cancellable
security-level search. A legacy identity.ini is migrated in on first load.
- Settings hold the default identity; bookmarks may pin their own, falling
back to the default when unset.
- TeamspeakConnection now connects with the identity it is handed.
- Swing: Tools -> Identities manager, plus an identity drop-down in the
connect and bookmark forms.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add file up/downloads and directory operations for channel file
repositories, reachable from a channel's right-click "Browse files".
Core (com.ts3client.net.filetransfer):
- FileTransferManager drives the TS3 file protocol on the ts3j socket.
ftinitupload/ftinitdownload results arrive out-of-band as
notifystartupload/notifystartdownload (success) or
notifystatusfiletransfer (failure) events, not as command responses,
so negotiation correlates them by clientftfid via a CompletableFuture.
The byte phase opens a raw TCP socket, sends the ftkey, then streams.
- FileTransfer: observable per-transfer handle (state, progress, cancel).
- RemoteFile: UI-facing listing DTO, keeping core free of ts3j API types.
TeamspeakConnection exposes list/mkdir/delete/rename and up/download and
manages the manager's lifecycle. Swing FileBrowserDialog provides the
browser UI with a live per-transfer progress strip.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swing desktop client (core/desktop/swing Maven modules) built on the
ts3j protocol library, included as a submodule. Native Opus voice with
voice-activation detection, push-to-talk, and audio pre-processing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>