Give the Material pack every icon TeamSpeak's packs have

The rest of default_colored_2014's keys get Material counterparts:
talk power, whisper, recording, text formatting, identities, add-ons,
permissions and the like. Ping levels are drawn as signal bars, since
Material's own tell their levels apart by a hairline, and the default
group icons as lettered badges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-26 09:43:07 +00:00
parent cdf81f3258
commit e7741e8b34
124 changed files with 510 additions and 86 deletions

View File

@@ -18,6 +18,8 @@ TALKING = "#2E9BF0"
MUTED = "#D9443A"
AWAY = "#9E8A2E"
IDLE = "#888888"
YELLOW = "#C9A227"
GREEN = "#3A9D4A"
UNSUBSCRIBED = 0.5
# key: (icon, style, colour, opacity); style "o" is Material's outlined variant.
@@ -109,14 +111,175 @@ ICONS = {
"FILETRANSFER": ("swap_vert",),
"UPLOAD": ("upload",),
"DOWNLOAD": ("download",),
# Everything else TeamSpeak's own packs draw, so the pack is as complete as theirs.
"CHANNEL_YELLOW": ("chat", "o", YELLOW, UNSUBSCRIBED),
"CHANNEL_YELLOW_SUBSCRIBED": ("chat", "o", YELLOW),
"CHANNEL_COLLAPSE_ALL": ("unfold_less",),
"CHANNEL_COLLAPSE_SUB": ("unfold_less",),
"CHANNEL_EXPAND_ALL": ("unfold_more",),
"CHANNEL_EXPAND_SUB": ("unfold_more",),
"MAKE_CURRENT_CHANNEL_DEFAULT": ("push_pin",),
"MODERATED": ("shield", "o"),
"SUBSCRIBE_MODE": ("visibility", "o"),
"UNSUBSCRIBE_MODE": ("visibility_off", "o"),
"CLIENT_SHOW": ("preview",),
"CLIENT_HIDE": ("hide_source",),
"IS_TALKER": ("record_voice_over", "o"),
"TALK_POWER_REQUEST": ("back_hand",),
"TALK_POWER_CANCEL_REQUEST": ("do_not_touch",),
"TALK_POWER_GRANT": ("record_voice_over", "", GREEN),
"TALK_POWER_GRANT_NEXT": ("record_voice_over", "o", GREEN),
"TALK_POWER_REVOKE": ("voice_over_off", "", MUTED),
"TALK_POWER_REVOKE_ALL_GRANT_NEXT": ("voice_over_off", "o", MUTED),
"WHISPER": ("hearing",),
"ON_WHISPERLIST": ("hearing", "", TALKING),
"PLAYER_WHISPER": ("hearing", "", MUTED),
"WHISPERHISTORY": ("history",),
"WHISPERLISTS": ("list_alt",),
"TOGGLE_BLOCK_INCOMING_WHISPERS": ("hearing_disabled",),
"TOGGLE_SERVER_QUERY_CLIENTS": ("terminal", "o"),
"3D_SOUND": ("surround_sound",),
"3D_SOUND_ME": ("person_pin_circle",),
"3D_SOUND_OTHER": ("person_pin",),
"MUSIC": ("music_note",),
"PLAY": ("play_arrow",),
"STOP": ("stop",),
"RECORDING_START": ("fiber_manual_record", "", MUTED),
"RECORDING_STOP": ("stop_circle",),
"PING_CALCULATING": ("network_check",),
"PING_DISCONNECTED": ("wifi_off", "", IDLE),
"MESSAGES": ("forum",),
"MESSAGE_INCOMING": ("call_received",),
"MESSAGE_OUTGOING": ("call_made",),
"MESSAGE_INFO": ("info", "", TALKING),
"OFFLINE_MESSAGES": ("mail", "o"),
"NOTIFICATIONS": ("notifications",),
"EMOTICON": ("emoji_emotions", "o"),
"TEXTFORMAT": ("text_format",),
"TEXTFORMAT_BOLD": ("format_bold",),
"TEXTFORMAT_ITALIC": ("format_italic",),
"TEXTFORMAT_UNDERLINE": ("format_underlined",),
"TEXTFORMAT_FOREGROUND": ("format_color_text",),
"COPY_URL": ("link",),
"URLCATCHER": ("add_link",),
"WEBLIST": ("public",),
"WARNING_EXTERNAL_LINK": ("open_in_new",),
"SERVER_LOG": ("receipt_long",),
"COMPLAINT_LIST": ("report", "o"),
"SEND_COMPLAINT": ("flag",),
"INVITE_BUDDY": ("group_add",),
"PHONETICSNICKNAME": ("spellcheck",),
"RENAME": ("edit", "o"),
"CONNECT_NEW_TAB": ("tab",),
"DISCONNECT_ALL": ("link_off",),
"HOSTER_BUTTON": ("storage",),
"VIRTUALSERVER_EDIT": ("settings_applications",),
"TEMP_SERVER_PASSWORD": ("password",),
"TEMP_SERVER_PASSWORD_ADD": ("enhanced_encryption",),
"TOKEN": ("vpn_key",),
"TOKEN_USE": ("key",),
"SECURITY": ("security",),
"PERMISSION_OVERVIEW": ("policy",),
"PERMISSIONS_CLIENTS": ("manage_accounts",),
"PERMISSIONS_CHANNELS": ("rule",),
"PERMISSIONS_CHANNEL_CLIENT": ("switch_account",),
"IDENTITY_ADD": ("person_add_alt",),
"IDENTITY_REMOVE": ("person_remove_alt_1",),
"IDENTITY_DEFAULT": ("how_to_reg",),
"IDENTITY_IMPORT": ("file_download",),
"IDENTITY_EXPORT": ("file_upload",),
"DELETE_AVATAR_OTHER": ("no_photography",),
"BOOKMARK_ADD_FOLDER": ("create_new_folder", "o"),
"BOOKMARK_DUPLICATE": ("bookmarks",),
"BOOKMARK_SET_FOR_ALL": ("bookmark_added",),
"MYTS_ACCOUNT": ("account_circle",),
"MYTS_ACCOUNT_ONLINE": ("cloud_done",),
"MYTS_ACCOUNT_OFFLINE": ("cloud_off", "", IDLE),
"MYTS_ACCOUNT_ERROR": ("sync_problem", "", MUTED),
"SYNC": ("sync",),
"SYNC_ENABLE": ("cloud_sync",),
"SYNC_DISABLE": ("cloud_off",),
"REGISTER": ("app_registration",),
"ADDON": ("extension",),
"ADDON_BROWSER": ("travel_explore",),
"ADDON_COLLECTION": ("library_add",),
"ADDON_ICONPACK": ("category",),
"ADDON_SOUNDPACK": ("library_music",),
"ADDON_THEME": ("palette",),
"ADDON_TRANSLATION": ("translate",),
"PLUGINS": ("electrical_services",),
"SOUNDPACK": ("library_music",),
"HOTKEYS": ("keyboard",),
"GUISETUP": ("tune",),
"SWITCH_ADVANCED": ("build",),
"SWITCH_STANDARD": ("build", "o"),
"CHANGELOG": ("article",),
"CHECK_UPDATE": ("update",),
"PRESENT": ("redeem",),
"APPLY": ("check",),
"RELOAD": ("replay",),
"FIND": ("search",),
"SEARCH": ("search",),
"SEARCH_CLEAR": ("close",),
"SEARCH_HIDE": ("close",),
"FILTER_CLEAR": ("filter_alt_off",),
"SORT_BY_NAME": ("sort_by_alpha",),
"UP": ("arrow_drop_up",),
"UP_ARROW": ("arrow_drop_up",),
"DOWN": ("arrow_drop_down",),
"DOWN_ARROW": ("arrow_drop_down",),
"FILE_HOME": ("home",),
"SELECTFOLDER": ("folder_open",),
"LISTVIEW": ("view_list",),
"ICONSVIEW": ("grid_view",),
"ICONVIEWER": ("photo_library",),
"BROKEN_IMAGE": ("broken_image",),
"LOADING_IMAGE": ("hourglass_empty",),
}
# TeamSpeak's default server and channel groups, lettered as its own packs letter them.
GROUP_BADGES = {
"GROUP_100": ("C", "#D9443A"),
"GROUP_200": ("O", "#E0782E"),
"GROUP_300": ("S", "#B02E6E"),
"GROUP_500": ("Q", "#6E4CB0"),
"GROUP_600": ("V", "#2E7DB0"),
}
# Material's own signal icons tell their levels apart by an outline too thin for 16 px.
PING_LEVELS = {"PING_1": 1, "PING_2": 2, "PING_3": 3, "PING_4": 4}
def ping_bars(level):
bars = "".join(
f'<rect x="{3 + i * 5}" y="{17 - i * 4}" width="3.5" height="{4 + i * 4}" rx="1"'
+ ("" if i < level else ' fill-opacity="0.3"') + "/>"
for i in range(4))
return ('<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" width="24" height="24" '
f'viewBox="0 0 24 24">{bars}</svg>')
BADGE = (
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">'
'<rect x="2" y="2" width="20" height="20" rx="5" fill="{colour}"/>'
'<text x="12" y="17.5" text-anchor="middle" font-family="sans-serif" font-weight="bold" '
'font-size="15" fill="#FFFFFF">{letter}</text></svg>'
)
INI_HEAD = """\
[info]
name = Material
[options]
# Complete for what the client draws; the rest stays with the client's own icons.
# Covers every icon TeamSpeak's own packs do.
FALLBACK=false
[gfxfiles]
@@ -158,6 +321,14 @@ def main():
if not path.exists():
path.write_text(paint(fetch(icon, style), colour, opacity) + "\n")
lines.append(f"{key.ljust(width)} = {name}")
for key, level in PING_LEVELS.items():
name = f"ping-{level}.svg"
(OUT / name).write_text(ping_bars(level) + "\n")
lines.append(f"{key.ljust(width)} = {name}")
for key, (letter, colour) in GROUP_BADGES.items():
name = f"group-{letter.lower()}.svg"
(OUT / name).write_text(BADGE.format(letter=letter, colour=colour) + "\n")
lines.append(f"{key.ljust(width)} = {name}")
(OUT / "settings.ini").write_text(INI_HEAD + "\n".join(lines) + "\n")