From b95521ea994494afd20440e24038d17d4a43e6e0 Mon Sep 17 00:00:00 2001 From: Oleh Polisan Date: Fri, 26 Apr 2024 16:51:05 +0300 Subject: [PATCH] Added custom color support --- src/main/ipc.ts | 5 +- src/main/mainWindow.ts | 18 +++++-- src/preload/VesktopNative.ts | 3 +- src/renderer/components/settings/Settings.tsx | 2 + .../components/settings/TrayColorPicker.tsx | 46 ++++++++++++++++++ .../components/settings/traySetting.css | 16 ++++++ src/renderer/patches/tray.ts | 46 +++++++++++++++--- src/shared/IpcEvents.ts | 3 +- src/shared/settings.d.ts | 1 + static/deafened.png | Bin 2853 -> 0 bytes static/deafened.svg | 1 + static/idle.png | Bin 6000 -> 0 bytes static/idle.svg | 5 ++ static/muted.png | Bin 2788 -> 0 bytes static/muted.svg | 1 + static/speaking.png | Bin 3810 -> 0 bytes static/speaking.svg | 5 ++ 17 files changed, 136 insertions(+), 16 deletions(-) create mode 100644 src/renderer/components/settings/TrayColorPicker.tsx create mode 100644 src/renderer/components/settings/traySetting.css delete mode 100644 static/deafened.png create mode 100644 static/deafened.svg delete mode 100644 static/idle.png create mode 100644 static/idle.svg delete mode 100644 static/muted.png create mode 100644 static/muted.svg delete mode 100644 static/speaking.png create mode 100644 static/speaking.svg diff --git a/src/main/ipc.ts b/src/main/ipc.ts index e010f2b..cc9d069 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -18,7 +18,7 @@ import { IpcEvents } from "../shared/IpcEvents"; import { setBadgeCount } from "./appBadge"; import { autoStart } from "./autoStart"; import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants"; -import { mainWin, setTrayIcon } from "./mainWindow"; +import { getTrayIconFile, mainWin, setTrayIcon } from "./mainWindow"; import { Settings } from "./settings"; import { handle, handleSync } from "./utils/ipcWrappers"; import { PopoutWindows } from "./utils/popout"; @@ -154,4 +154,5 @@ watch( }) ); -handle(IpcEvents.SET_TRAY_ICON, (_, iconName: string) => setTrayIcon(iconName)); +handle(IpcEvents.SET_TRAY_ICON, (_, iconURI) => setTrayIcon(iconURI)); +handle(IpcEvents.GET_TRAY_ICON, (_, iconName) => getTrayIconFile(iconName)); diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index e935a11..0df9ae5 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -11,10 +11,11 @@ import { dialog, Menu, MenuItemConstructorOptions, + nativeImage, nativeTheme, Tray } from "electron"; -import { rm } from "fs/promises"; +import { readFile, rm } from "fs/promises"; import { join } from "path"; import { IpcEvents } from "shared/IpcEvents"; import { isTruthy } from "shared/utils/guards"; @@ -479,14 +480,21 @@ export async function createWindows() { initArRPC(); } -export async function setTrayIcon(iconName: string) { +export async function setTrayIcon(iconURI: string) { if (!tray) return; + if (iconURI !== "" && iconURI !== "icon") { + tray.setImage(nativeImage.createFromDataURL(iconURI)); + return; + } + tray.setImage(join(STATIC_DIR, "icon.png")); +} - const Icons = new Set(["speaking", "muted", "deafened", "idle", "icon"]); +export async function getTrayIconFile(iconName: string) { + const Icons = new Set(["speaking", "muted", "deafened", "idle"]); if (!Icons.has(iconName)) { - console.warn("setTrayIcon: Invalid icon name", iconName); iconName = "icon"; + return readFile(join(STATIC_DIR, "icon.png")); } - tray.setImage(join(STATIC_DIR, iconName + ".png")); + return readFile(join(STATIC_DIR, iconName + ".svg"), "utf8"); } diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index cd8027c..fcf9bc4 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -25,7 +25,8 @@ export const VesktopNative = { getVersion: () => sendSync(IpcEvents.GET_VERSION), setBadgeCount: (count: number) => invoke(IpcEvents.SET_BADGE_COUNT, count), supportsWindowsTransparency: () => sendSync(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY), - setTrayIcon: (iconName: string) => invoke(IpcEvents.SET_TRAY_ICON, iconName) + setTrayIcon: (iconURI: string) => invoke(IpcEvents.SET_TRAY_ICON, iconURI), + getTrayIcon: (iconName: string) => invoke(IpcEvents.GET_TRAY_ICON, iconName) }, autostart: { isEnabled: () => sendSync(IpcEvents.AUTOSTART_ENABLED), diff --git a/src/renderer/components/settings/Settings.tsx b/src/renderer/components/settings/Settings.tsx index d6de13c..944ad70 100644 --- a/src/renderer/components/settings/Settings.tsx +++ b/src/renderer/components/settings/Settings.tsx @@ -14,6 +14,7 @@ import { isMac, isWindows } from "renderer/utils"; import { AutoStartToggle } from "./AutoStartToggle"; import { DiscordBranchPicker } from "./DiscordBranchPicker"; import { NotificationBadgeToggle } from "./NotificationBadgeToggle"; +import { trayIconPicker } from "./TrayColorPicker"; import { VencordLocationPicker } from "./VencordLocationPicker"; import { WindowsTransparencyControls } from "./WindowsTransparencyControls"; @@ -75,6 +76,7 @@ const SettingsOptions: Record> defaultValue: true, invisible: () => isMac }, + trayIconPicker, { key: "minimizeToTray", title: "Minimize to tray", diff --git a/src/renderer/components/settings/TrayColorPicker.tsx b/src/renderer/components/settings/TrayColorPicker.tsx new file mode 100644 index 0000000..4e38dc8 --- /dev/null +++ b/src/renderer/components/settings/TrayColorPicker.tsx @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: GPL-3.0 + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2023 Vendicated and Vencord contributors + */ + +import "./traySetting.css"; + +import { Margins } from "@vencord/types/utils"; +import { findByCodeLazy } from "@vencord/types/webpack"; +import { Forms } from "@vencord/types/webpack/common"; +import { setCurrentState } from "renderer/patches/tray"; + +import { SettingsComponent } from "./Settings"; + +const ColorPicker = findByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); + +const presets = [ + "#3DB77F", // discord default ~ + "#F6BFAC" // Vesktop inpired +]; + +export const trayIconPicker: SettingsComponent = ({ settings }) => { + if (!settings.tray) return null; // how to disable instead of hiding? + return ( +
+
+
+ Tray Icon Color + Choose a color for your tray icon! +
+ { + const hexColor = newColor.toString(16).padStart(6, "0"); + settings.trayColor = hexColor; + setCurrentState(); + }} + showEyeDropper={false} + suggestedColors={presets} + /> +
+ +
+ ); +}; diff --git a/src/renderer/components/settings/traySetting.css b/src/renderer/components/settings/traySetting.css new file mode 100644 index 0000000..b4941e3 --- /dev/null +++ b/src/renderer/components/settings/traySetting.css @@ -0,0 +1,16 @@ +.tray-settings { + display: flex; + flex-direction: column; +} + +.tray-container { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.tray-settings-labels { + display: flex; + flex-direction: column; + justify-content: flex-start; +} \ No newline at end of file diff --git a/src/renderer/patches/tray.ts b/src/renderer/patches/tray.ts index 4d94044..c0be6f0 100644 --- a/src/renderer/patches/tray.ts +++ b/src/renderer/patches/tray.ts @@ -4,19 +4,49 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ +import { Logger } from "@vencord/types/utils"; import { findByPropsLazy, onceReady } from "@vencord/types/webpack"; import { FluxDispatcher, UserStore } from "@vencord/types/webpack/common"; const muteActions = findByPropsLazy("isSelfMute"); const deafActions = findByPropsLazy("isSelfDeaf"); -function setCurrentState() { +var inCall = false; +const logger = new Logger("VesktopTrayIcon"); + +async function changeIconColor(iconName: string) { + const pickedColor = VesktopNative.settings.get().trayColor; + + try { + var svg = await VesktopNative.app.getTrayIcon(iconName); + svg = svg.replace(/#f6bfac/gim, "#" + (pickedColor ?? "3DB77F")); + const canvas = document.createElement("canvas"); + canvas.width = 128; + canvas.height = 128; + const img = new Image(); + img.width = 128; + img.height = 128; + img.onload = () => { + const ctx = canvas.getContext("2d"); + if (ctx) { + ctx.drawImage(img, 0, 0); + const dataURL = canvas.toDataURL("image/png"); + VesktopNative.app.setTrayIcon(dataURL); + } + }; + img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`; + } catch (error) { + logger.error("Error: ", error); + } +} + +export function setCurrentState() { if (deafActions.isSelfDeaf()) { - VesktopNative.app.setTrayIcon("deafened"); + changeIconColor("deafened"); } else if (muteActions.isSelfMute()) { - VesktopNative.app.setTrayIcon("muted"); + changeIconColor("muted"); } else { - VesktopNative.app.setTrayIcon("idle"); + changeIconColor("idle"); } } @@ -26,7 +56,7 @@ onceReady.then(() => { FluxDispatcher.subscribe("SPEAKING", params => { if (params.userId === userID) { if (params.speakingFlags) { - VesktopNative.app.setTrayIcon("speaking"); + changeIconColor("speaking"); } else { setCurrentState(); } @@ -34,18 +64,20 @@ onceReady.then(() => { }); FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_DEAF", () => { - setCurrentState(); + if (inCall) setCurrentState(); }); FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_MUTE", () => { - setCurrentState(); + if (inCall) setCurrentState(); }); FluxDispatcher.subscribe("RTC_CONNECTION_STATE", params => { if (params.state === "RTC_CONNECTED") { + inCall = true; setCurrentState(); } else if (params.state === "RTC_DISCONNECTED") { VesktopNative.app.setTrayIcon("icon"); + inCall = false; } }); }); diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index 63df0d9..f68826a 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -51,5 +51,6 @@ export const enum IpcEvents { CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE", - SET_TRAY_ICON = "VCD_SET_TRAY_ICON" + SET_TRAY_ICON = "VCD_SET_TRAY_ICON", + GET_TRAY_ICON = "VCD_GET_TRAY_ICON" } diff --git a/src/shared/settings.d.ts b/src/shared/settings.d.ts index 7f6e74a..a03a766 100644 --- a/src/shared/settings.d.ts +++ b/src/shared/settings.d.ts @@ -11,6 +11,7 @@ export interface Settings { vencordDir?: string; transparencyOption?: "none" | "mica" | "tabbed" | "acrylic"; tray?: boolean; + trayColor?: string; minimizeToTray?: boolean; openLinksWithElectron?: boolean; staticTitle?: boolean; diff --git a/static/deafened.png b/static/deafened.png deleted file mode 100644 index 7dfc308b5d9996f45fbc5e023689f5d7d583b730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2853 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 D=tvK_ diff --git a/static/deafened.svg b/static/deafened.svg new file mode 100644 index 0000000..c4ef926 --- /dev/null +++ b/static/deafened.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/idle.png b/static/idle.png deleted file mode 100644 index 0a0e08aa4ca3d495259bcecbf0d9d1d9eb6057c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6000 zcmV-$7mw(PP)_%}s7{69R$67%CP80j)lb1Qc5#2_#^UfaV!12)<0MqhguX z`q&ve)|pQ0(@wP(tMfcg)q-Pj(8x=8f>kk~fuh1QiV7ksT2umgHSfti>)Suhx%cjK z&fe$leeSt0$vxkH?mcVm^;>JN{awHHU2CtkwrtbG%te^$0`xGo=n-Isqep-ljvfJK zIC=z_;ph=yhNDM-8IB$SW;iNSU4Ti!t+ysS7c5w+7#CJxZj|O?FAOjjK(JrZu4wNM zus?*bYtEiMzh=XR>QonfqM_q$CUD>8&1VlOIahFIu+NBaro;<@{@NAw`_*s?H~s{W z0S*H{0e%cT4*szM-=4SnHIGjCO-v$=c>?UY>+8Sk;8zJQfOsiz>TsNtb#KiCsD(8W zAaz|!IzJhUbZQQR z2y9W%P5Y{SUtE02CHvdm&ve1GC&2bQwhmUpp|?tYzr~W_@Y(~&Zp~>-OgE58fONVY z;Ez`iCm&nzn%C^gD8RJAv?aj4&6`gis`kAL=}&ExcV0LqUV|amd$ble#c;HCi-*f4u zhf7w;gko9|V9%ZRtVH~a#fxeo87?^WpwnJJ_F<%-Iq)de_!#mp2_fg^VoONUc1o;@ z#UjjEMrVP~5)7z~*J<`10)&4`(Def=FW=lQx5>n`Ai&PeTb3~tKPGXNpshP^s@Ma< z-;F^JhFJX>L9I_V7mb%DFvh%SYEH-wwoa$QdBIw4JTR6QXdtWjq7-hJvvS?9ikEj{ zFy#c;y=lvO<4*$%8~Wi)5e5hgKLUC{=KF>r0FttkcICZ5CZs;wF9p9sLFZtrUQSvB zDfO`04enI%TLvzA?Hwfx-c?K;0k&+Z?Ajau2I+l_wE1QNjD{f@`(dOzWw#2d1acoz zCNc;BZM;Ac+g@t1M)nG%kkt(|X%>TSoV)YTU$9}rkwV4pGNyz8d+xmN`6hnB^0}?C zO)A?lTG|@mZeV*`!{hVuLZvnHCk-+0(ICACKbP7_J!-8ES5p{FzE!? zap%3~27DKAa!W-3jb1=cMB?WYDArd_RX31X79duaS-er`^x@zPFHnbfMsfMz$}8?K zp2_$zsRY=u=^Lwp^<`i%Rf49g46q+@7t!|+aN&2>rMm%`FfWjd*p{sGB7VJKunhqK z_DkL{uyXyRjS?n}0J}EbceRP1k?CuUY4!s8{|>%craqNgdqBlWFwML`-M-9Ro9hrS z&Flt71>+ISTjs95;`7Bb>Kv0ofE~Br_bvtB2uS&3`yt><1bu^+Ld6mUfTc@3N(lnM z^bvzewtYpPM$jK)FUlc6Em7c2b1#}$PcWeb*m3)o)spT4X4P_oW&#MFjOOE#_vTmn z@zD)5Eg-YLwkLseCt{wU`-?(v{n4yyKVJY&ss+^BdK%_I%x4I( zm3@(%ryR(vL?KaKrV!6|i06dh)3GZxFXIuyjm|FA{#RDH3YVpeNWzd~{B#Hb;}tFS zWk6aKqi^A%|N93lfBVsHX5V!J?Cjh5_dv!BUmen;irhqy%a7Sd5%Bz&RL>QDmE@(U z{=gfa>1bsdjmAsh=3!V4!E%vMuKaE6hhXH-5dO&HvrDfn{%}fgrv7h^lT%$l z&3-c`r5hN&#GYkwgW>e7MJB4bud4=Dt-q~!#_iEH2e0G6H&(myvo z{2XM|lkt3ss|9}zJ`X9$%4&qq8N6;;^W*098P(DDnI-KaOT*F;!zWPIJCFJ+GV!km z@7eU+ZsplE0@&f7fQ7YRYJLD!V*IpGM++qb{Jui`poo81d+_Ou>sh&Ja5)m4@xa}9 zO*?dLXGj&ss_qB~?NgOcAq_Rh*Q@lz$k0c+RjjUb0XuKI?-EO2X)v6g>I}X`);IDk zWLI+x?ys0@EU&1$K*(|T>26?H4+!>0@s!|MM|OjD3--p^rVq;;jP7fie45mm!4`~W zh4qD;el22=X zu#J8Ca6SgHo5&A^et`A!7%R0Exzl`RFpX3;GVvY2%SJ;Q2=K%}*!>bNeDfj3fpL!l zPCPvLev315qW5Bb7LCQSCPf`sWG6R(Pi`HurIfuzz=s|6kd%BY#_&^U%e0PArAxhi z0Yj;gr-Q>yR(ye>HksyHFIIP~FE|iSc<%uG&3KA5E)THn%lDsBQT#b#fZ-CRVgFl5 zC^&;vtrv)SQF@o>b zh3ncclqsB7NmPh`1`K2?Za)zc9w=ik;(n7{kF~gFq~7Z{i8fmg4@G?-#;|eBhYwv* za+56vM-lEf`dK<8Ro!!wSiNC9#p##;yKa4G0n$6N;%p*L{+&|ZI6<*;jo~!%6CRG} zUrq9VMDp8mCGLU~FEdZnJviXcVb^kR8NvAa-CJ%up;O}>6Tp>2e+pjE7+Nb>khdoI zsWQzbM*Rv6u=_XvZYRVF<^g8S+5nuCHJPHIZ_6$vN}N6tN)uq$ty_Nw@#@jcM<1G`A4KA>OBsyG zIv{D7KK96;himHwX!DdO+6!c0YQ!K|zj2JU6G>X9vsZQUj-WIFhAMhfemZB5`E#0$ zV=~{?%UhQV2*F{=|6aCyW8#<=gAm9SgCTn8>XMn4CxGCZR0|z};XpO=Nc!5*y3WqV z_A98j{uYm7zAjvNs5eu@U?RRJI0&?eL8*K

KN0+;;!zFuZj;4T1b!i88BIWQPkz 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 eIC=z_;rMSJ7m>N8_M?&j0000 + + + + diff --git a/static/muted.png b/static/muted.png deleted file mode 100644 index e6b3a6062eb9fef862ec36b1509614ea1e544156..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2788 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 DtzZ{? diff --git a/static/muted.svg b/static/muted.svg new file mode 100644 index 0000000..fc93fb9 --- /dev/null +++ b/static/muted.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/speaking.png b/static/speaking.png deleted file mode 100644 index 6aac412cc3dfc820bcf8d4a50e4cf77fe02f181b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3810 zcmZ9Pc|6nq=IhJ%0N`xFu zm_*1?m?TG%0MKgS z(FFkD4mjfw3;=BEKVXlonq>e0P{=9!5UVSckT8!RPY@OsrgF*OH<;`Z;Hh#Y$m{Me zy+Z)-Uokc!S%>E@6!>0tZWry?_X=H*x!z`hGtx6XqJb>YZl^9N#bEKkOn zJh(28u$0m$O}8v5wN%LVa(W^qUnqVhn-|}-m?|dQbU)wy@jXAk2z0w3uURQ_92hl;Y#NSlu z4m9)a|EOI4B62$3k8^^{#%8s%%I6KS{**QUw?nYoTmbgBk%qT=8rt0mt*+(e{ar>! z=fZE-$i6CQrBA1R($>0`O z6df^MVSknLfhA?cpSuCajyCN3DuqKZQ&<_2^&rVWdW8If7dM`xKrIQp6u2uJ%OCq( z;Id?ex8rK1lGF*;+#EP|?8PEpaI&GLT5tlnVHb>_lRopRNaXa(a{+<^4|>9GLj@jf zuK5FX8&*Rdmhxueu&Cu9Wiv0>C&S{l4#WLnxcqk!`0+C8HHi@p;3dW*{&;HHR{bev zvjuBAtmH)+!FH%*1tw*6Q)roTwA-}0Lx8$Fly9cSp<08^z(2t7pg5nt%T-J4Gfe@2WZgxwTjm?jmOlmy}Z7=%T*6IdSyFT%v-o8 zjAZZ)k;9{y$(yil<2f^7FtO%i&fR4pdd6Q{F;PlYTrmQ!tb9MSeL6Kxh}HLtix0P3 z$o+}!Bx7;CU+`T8&z3*RWeN>q4da++J^6eiWsFx(kkIZ-xghT1j{`>Da;$Y?sZDHS zUQ0>-igF5?lhk%H_>iMvmQ3%lh=Z&-{R~Z%p#bx!r&LbPmn2OA8zy)e`=o{~H37Ky zNF&LdFk`VC!ipUAjEr75ds{T|FnfB{`yk8OiD#e}^RV)&314rHK>ohAjt*NU3t{ex zP^)g(G`X>k7gjZ29OAe=%7vC?Om9mzyitmUT+XR}@{kOOVd0w#+?`WGH%$-D;_eFB$M1v{!Xq@H5Qu}kL6*$)~!lT+SB=oGQn2=8H@cT>l$Z+E`ehfel_9Q z;eQbethCo`k(8Be(x$LC!eG!h8hr;31X&yHP9}aPJ-XUW*sVBPpNg!m-y%04!)|Zg>gx|LPlQQ$lta$l} zB^7Gtll%^^)Gx;=mLnK=8;;uIt?D%LQ8sr>xS89X*|2#Y^+%Y#3DrZjrqx ztc0yyBvgtfipe?nf-AyaD{$eQ5bA;Emde`9XbzmwZUuZMH_woJy9Q@lvq?@WF>gDw zNuBnCj){9ccRIgXA+g_o_+2cvJ1?YY8@nc~T*(14HmrHgbd4UdM~=vDBz7|PQPx2bLR&8Byq=!M`?!~RVx%?TY>%hwltqvkkeM<`XXPn1p#pLF* z^gR4IR8#Zc$L{qwW@Z$bM0Vv~H(?C^S$|=>-s_A%mN6u@)ZX*=!5f|1*!ZgOl%{Hk zfMr4mY6b)CcCNm$#-a)>TU81?>Og z#}$Z|F8xK-$+n}tYV78;MV7P;rQJTYxRlwR1>R2b_4AWaFWC1X1Z;(z30qKh58!<5 zx*)rSrKT57VhQqYzDtxo>3G$F=rm#j+^DtsNN~P7Ei}j-mC*iSeU$VHh8Qad*XaLi z=3f4MN9T6(-aB04S}jZ&7BzBpS^>8_2UE#&~FzSxv>aAXw={Od%Qzbs|lR(P~Lu#qP4rpYF6By=wl=Rqa zovCc3THl}T;~?+&5Z(ot!2IspHppvrZo+buSI*-zy5mjJ8xMSJv+IS zPNT#@)iWzMcNs0%$Bv#)Gr`-v27@X=<17v*UtWvwxQS1s0>Z>9I^)fXzrl)g5zJ(D zT4)@eW=p?)D>o0EQ1HQ1EGt#r3G#!k)dqerl?-T<4q@1sOwpC+@bB*3?#%keM$+_+ zOn}w8pVPx2b!cd7(z<;*%*x?y-u9RB@odcKG6y5{`xy0^4-(KJYX?(&3xm`$T?7Ve zfgByOZm}R>d4Jo(Ogf$(DIbB6QCvOQm&DN@>iX5Kgoy8VO!s*w8s80Tu`xOiJ5wRO z z@J=o6NQ*~Y;Gn&MoA8A`Jm>S zM)=AM1D#6{3j*A>FYzbQf1qG%sm)F zpAJ=LReuyvB;R|wPsHymqC%A8~XQ$;^oK8hKafN#RxWLbPKO=AY;Pl&zBH2)N;-#svZ{O_nj-HpcgaKmo z&U;lsvM@fG#b}XHk{)yWFxEKanb3To4gh9Fbf*-n8pdL{UOboAqOSJY<-_pgfYSm* z*<4xt>N>X=87xa!jGq#)II0rTNwk;*&0<W#cxaxO&@n$<8Fz-AqQUgig*yZ(lTf|#P(ALZUhTa}|Fy&{r z79m_lj6bHVs$N^IcG0K@3Bb^Tslk&Oargc!AF&$447>PY&XY#gAStY`yNEW-6ZZ@;E1&xE-}oS6%a}O)$_9z1i*O zGkUjBBW(HsT*m5k=!k!EKOgw>&jT{W4H`SdpCrB#y%Y<73_8~qv>M?ug`b2w_IV{NNUQXBdD(o(fwJdS5L_#B=FN-wBX!JS?V3oVXjI zNm~>Q^coaW9jejvjxjX+VrB}H8K|9FPdUZNfqhh<_`n0S)3xkZf^K!fmS1*rSk_8{ z&7jY8BKp~dN}KXayINg0)9oHMfuCy&I*n8%-{-jI9MUIhIBatNq5krx+IN&CJt`TY z({oYsuPvIrS)*0VPBOI_nJq=h2PKzdv2_OcBFeoyW$R}NJn&KnCLAQHb*ty2w&>^q zG;e-I^iH4gP?2*{kH}4cxg|1~fIIo?f5z?qecbH+>V$)A>U>!%000=DGB>I?;U4#Y Dx@Fp~ diff --git a/static/speaking.svg b/static/speaking.svg new file mode 100644 index 0000000..dad855d --- /dev/null +++ b/static/speaking.svg @@ -0,0 +1,5 @@ + + + + +