diff --git a/app.config.ts b/app.config.ts
deleted file mode 100644
index 11f7f7c..0000000
--- a/app.config.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export default defineAppConfig({
- title: "Gorb",
- buildTimeString: new Date().toISOString(),
- gitHash: process.env.GIT_SHORT_REV || "N/A"
-})
diff --git a/app.vue b/app.vue
index a0a0ed6..cb5ec34 100644
--- a/app.vue
+++ b/app.vue
@@ -33,6 +33,7 @@ body {
font-family: Arial, Helvetica, sans-serif;
box-sizing: border-box;
color: var(--text-color);
+ background: var(--optional-body-background);
background-color: var(--chat-background-color);
margin: 0;
}
diff --git a/components/Button.vue b/components/Button.vue
index fb44cf2..66706de 100644
--- a/components/Button.vue
+++ b/components/Button.vue
@@ -21,7 +21,7 @@ const props = defineProps<{
background-color: var(--primary-color);
color: var(--text-color);
- padding: 0.7dvh 1.2dvw;
+ padding: 0.4em 0.75em;
font-size: 1.1em;
transition: background-color 0.2s;
diff --git a/components/Channel.vue b/components/Channel.vue
index 0f48323..98f210e 100644
--- a/components/Channel.vue
+++ b/components/Channel.vue
@@ -23,14 +23,14 @@ const isCurrentChannel = props.uuid == props.currentUuid;
.channel-list-link {
text-decoration: none;
color: inherit;
- padding-left: .5dvw;
- padding-right: .5dvw;
+ padding-left: .25em;
+ padding-right: .25em;
}
.channel-list-link-container {
text-align: left;
display: flex;
- height: 4dvh;
+ height: 1.5em;
white-space: nowrap;
align-items: center;
}
diff --git a/components/CropPopup.vue b/components/CropPopup.vue
new file mode 100644
index 0000000..12c3a0b
--- /dev/null
+++ b/components/CropPopup.vue
@@ -0,0 +1,93 @@
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
diff --git a/components/MemberEntry.vue b/components/MemberEntry.vue
index 3eed3c5..ed4cd48 100644
--- a/components/MemberEntry.vue
+++ b/components/MemberEntry.vue
@@ -2,8 +2,8 @@
-
{{ props.member.user.display_name ?? props.member.user.username }}
-
+
{{ props.member.user.display_name || props.member.user.username }}
+
@@ -32,9 +32,4 @@ const hidePopup = () => {
.member-item {
position: relative; /* Set the position to relative for absolute positioning of the popup */
}
-
-.profile-popup {
- position: absolute; /* Use absolute positioning */
- left: -100px; /* Adjust this value to position the popup to the left */
-}
diff --git a/components/Message.vue b/components/Message.vue
index e8a91b5..eb073ac 100644
--- a/components/Message.vue
+++ b/components/Message.vue
@@ -60,7 +60,16 @@ const sanitized = ref();
onMounted(async () => {
const parsed = await parse(props.text, { gfm: true });
- sanitized.value = DOMPurify.sanitize(parsed, { ALLOWED_TAGS: ["strong", "em", "br", "blockquote", "code", "ul", "ol", "li", "a", "h1", "h2", "h3", "h4", "h5", "h6"] });
+ sanitized.value = DOMPurify.sanitize(parsed, {
+ ALLOWED_TAGS: [
+ "strong", "em", "br", "blockquote",
+ "code", "ul", "ol", "li", "a", "h1",
+ "h2", "h3", "h4", "h5", "h6"
+ ],
+ ALLOW_DATA_ATTR: false,
+ ALLOW_SELF_CLOSE_IN_ATTR: false,
+ ALLOWED_ATTR: []
+ });
console.log("adding listeners")
await nextTick();
messageElement.value?.addEventListener("mouseenter", (e: Event) => {
@@ -99,6 +108,7 @@ function getDayDifference(date1: Date, date2: Date) {
align-items: center;
column-gap: 1dvw;
width: 100%;
+ overflow-wrap: anywhere;
}
.message:hover {
@@ -178,12 +188,12 @@ function getDayDifference(date1: Date, date2: Date) {
diff --git a/components/MessageArea.vue b/components/MessageArea.vue
index 18b9ac5..59e5e06 100644
--- a/components/MessageArea.vue
+++ b/components/MessageArea.vue
@@ -9,11 +9,28 @@
@@ -106,11 +123,38 @@ function pushMessage(message: MessageResponse) {
messages.value.push(message);
}
+function handleTextboxKeyDown(event: KeyboardEvent) {
+ if (event.key === "Enter" && event.shiftKey && messageTextboxInput.value) {
+ // this enters a newline, due to not preventing default
+ } else if (event.key === "Enter") {
+ event.preventDefault()
+ sendMessage(event)
+ }
+
+ adjustTextboxHeight()
+}
+
+function handleTextboxInput() {
+ if (messageTextboxInput.value) {
+ messageInput.value = messageTextboxInput.value.innerText;
+ }
+
+ adjustTextboxHeight()
+}
+
+// this technically uses pixel units, but it's still set using dynamic units
+function adjustTextboxHeight() {
+ if (messageTextboxInput.value && messageTextboxDisplay.value) {
+ messageTextboxInput.value.style.height = "auto"
+ messageTextboxInput.value.style.height = `${messageTextboxInput.value.scrollHeight}px`
+ }
+}
+
const messages = ref([]);
-
-const messageInput = ref();
-
+const messageInput = ref("");
const messagesElement = ref();
+const messageTextboxInput = ref();
+const messageTextboxDisplay = ref();
if (messagesRes) messages.value = messagesRes;
@@ -158,14 +202,21 @@ if (accessToken && apiBase) {
function sendMessage(e: Event) {
e.preventDefault();
- const message = {
- message: messageInput.value
- }
- console.log("message:", message);
- if (message.message) {
+ if (messageInput.value && messageInput.value.trim() !== "") {
+ const message = {
+ message: messageInput.value.trim().replace(/\n/g, "
") // trim, and replace \n with
+ }
+
+ console.log("message:", message);
ws.send(JSON.stringify(message));
- messageInput.value = "";
- console.log("MESSAGE SENT!!!");
+
+ // reset input field
+ messageInput.value = ""
+ if (messageTextboxInput.value) {
+ messageTextboxInput.value.innerText = ""
+ }
+
+ adjustTextboxHeight()
}
}
@@ -241,38 +292,64 @@ router.beforeEach((to, from, next) => {
\ No newline at end of file
+.theme-preview-container {
+ margin: .5em;
+ width: 5em;
+ height: 5em;
+}
+
+.theme-preview {
+ width: 5em;
+ height: 5em;
+ border-radius: 100%;
+ border: .1em solid var(--primary-color);
+
+ display: inline-block;
+ text-align: center;
+ align-content: center;
+ cursor: pointer;
+}
+
+.theme-title {
+ font-size: .8em;
+ line-height: 5em; /* same height as the parent to centre it vertically */
+}
+
diff --git a/components/Settings/UserSettings/Account.vue b/components/Settings/UserSettings/Account.vue
index 85589e7..b2ac87a 100644
--- a/components/Settings/UserSettings/Account.vue
+++ b/components/Settings/UserSettings/Account.vue
@@ -43,6 +43,14 @@ async function changeEmail() {
body: formData
})
+ const apiBase = useCookie("api_base").value;
+
+ if (apiBase) {
+ const stats = await useApi().fetchInstanceStats(apiBase);
+ if (stats.email_verification_required) {
+ return window.location.reload();
+ }
+ }
alert('success!!')
} catch (error: any) {
if (error?.response?.status !== 200) {
@@ -70,13 +78,6 @@ async function deleteAccount() {
\ No newline at end of file
diff --git a/components/Settings/UserSettings/Profile.vue b/components/Settings/UserSettings/Profile.vue
index ed7da0a..04d1e30 100644
--- a/components/Settings/UserSettings/Profile.vue
+++ b/components/Settings/UserSettings/Profile.vue
@@ -20,15 +20,25 @@
-
+
+
+
\ No newline at end of file
diff --git a/components/UserPopup.vue b/components/UserPopup.vue
index 3a65cf0..a3a15cb 100644
--- a/components/UserPopup.vue
+++ b/components/UserPopup.vue
@@ -59,12 +59,14 @@ const props = defineProps<{
width: 96px;
height: 96px;
border: 5px solid #4b3018;
+ background-color: var(--secondary-color);
border-radius: 100%;
position: absolute;
left: 16px;
top: 16px;
}
+
#display-name {
margin-top: 60px;
margin-bottom: 0;
diff --git a/composables/api.ts b/composables/api.ts
index 603f71f..d2ca7a0 100644
--- a/composables/api.ts
+++ b/composables/api.ts
@@ -1,4 +1,4 @@
-import type { ChannelResponse, GuildMemberResponse, GuildResponse, MessageResponse } from "~/types/interfaces";
+import type { ChannelResponse, GuildMemberResponse, GuildResponse, MessageResponse, StatsResponse } from "~/types/interfaces";
export const useApi = () => {
async function fetchGuilds(): Promise {
@@ -41,6 +41,15 @@ export const useApi = () => {
return await fetchWithApi(`/channels/${channelId}/messages/${messageId}`);
}
+ async function fetchInstanceStats(apiBase: string): Promise {
+ return await $fetch(`${apiBase}/stats`, { method: "GET" });
+ }
+
+ async function sendVerificationEmail(): Promise {
+ const email = useAuth().user.value?.email;
+ await fetchWithApi("/auth/verify-email", { method: "POST", body: { email } });
+ }
+
return {
fetchGuilds,
fetchGuild,
@@ -51,6 +60,8 @@ export const useApi = () => {
fetchUsers,
fetchUser,
fetchMessages,
- fetchMessage
+ fetchMessage,
+ fetchInstanceStats,
+ sendVerificationEmail
}
}
diff --git a/composables/auth.ts b/composables/auth.ts
index b23a67b..ebe27de 100644
--- a/composables/auth.ts
+++ b/composables/auth.ts
@@ -7,6 +7,7 @@ export const useAuth = () => {
async function clearAuth() {
accessToken.value = null;
user.value = null;
+ await navigateTo("/login");
}
async function register(username: string, email: string, password: string) {
diff --git a/layouts/auth.vue b/layouts/auth.vue
index b7d5c5e..0c4ad41 100644
--- a/layouts/auth.vue
+++ b/layouts/auth.vue
@@ -20,30 +20,7 @@
- Instance URL is set to {{ instanceUrl }}
-
-
- You're logged in!
-
-
-
-
-
-
-
-
-
-
+ Instance URL is set to
{{ instanceUrl }}
@@ -51,7 +28,6 @@
\ No newline at end of file
diff --git a/pages/servers/[serverId]/channels/[channelId].vue b/pages/servers/[serverId]/channels/[channelId].vue
index 699b9a3..4873094 100644
--- a/pages/servers/[serverId]/channels/[channelId].vue
+++ b/pages/servers/[serverId]/channels/[channelId].vue
@@ -83,34 +83,36 @@ function handleMemberClick(member: GuildMemberResponse) {
\ No newline at end of file
diff --git a/pages/verify-email.vue b/pages/verify-email.vue
index cae02ea..f160a1d 100644
--- a/pages/verify-email.vue
+++ b/pages/verify-email.vue
@@ -15,6 +15,12 @@ const token = useRoute().query.token;
try {
const res = await fetchWithApi("/auth/verify-email", { query: { token } });
console.log("hi");
+ const query = useRoute().query;
+ if (query.redirect_to) {
+ await navigateTo(`/?redirect_to=${query.redirect_to}`);
+ } else {
+ await navigateTo("/");
+ }
} catch (error) {
console.error("Error verifying email:", error);
errorMessage.value = error;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c815868..bd6c885 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,6 +23,9 @@ importers:
'@tauri-apps/plugin-http':
specifier: ~2.5.0
version: 2.5.0
+ cropperjs:
+ specifier: ^2.0.0
+ version: 2.0.0
dompurify:
specifier: ^3.2.6
version: 3.2.6
@@ -211,6 +214,39 @@ packages:
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
engines: {node: '>=0.1.90'}
+ '@cropper/element-canvas@2.0.0':
+ resolution: {integrity: sha512-GPtGJgSm92crJhhhwUsaMw3rz2KfJWWSz7kRAlufFEV/EHTP5+6r6/Z1BCGRna830i+Avqbm435XLOtA7PVJwA==}
+
+ '@cropper/element-crosshair@2.0.0':
+ resolution: {integrity: sha512-KfPfyrdeFvUC31Ws7ATtcalWWSaMtrC6bMoCipZhqbUOE7wZoL4ecDSL6BUOZxPa74awZUqfzirCDjHvheBfyw==}
+
+ '@cropper/element-grid@2.0.0':
+ resolution: {integrity: sha512-i78SQ0IJTLFveKX6P7svkfMYVdgHrQ8ZmmEw8keFy9n1ZVbK+SK0UHK5FNMRNI/gtVhKJOGEnK/zeyjUdj4Iyw==}
+
+ '@cropper/element-handle@2.0.0':
+ resolution: {integrity: sha512-ZJvW+0MkK9E8xYymGdoruaQn2kwjSHFpNSWinjyq6csuVQiCPxlX5ovAEDldmZ9MWePPtWEi3vLKQOo2Yb0T8g==}
+
+ '@cropper/element-image@2.0.0':
+ resolution: {integrity: sha512-9BxiTS/aHRmrjopaFQb9mQQXmx4ruhYHGkDZMVz24AXpMFjUY6OpqrWse/WjzD9tfhMFvEdu17b3VAekcAgpeg==}
+
+ '@cropper/element-selection@2.0.0':
+ resolution: {integrity: sha512-ensNnbIfJsJ8bhbJTH/RXtk2URFvTOO4TvfRk461n2FPEC588D7rwBmUJxQg74IiTi4y1JbCI+6j+4LyzYBLCQ==}
+
+ '@cropper/element-shade@2.0.0':
+ resolution: {integrity: sha512-jv/2bbNZnhU4W+T4G0c8ADocLIZvQFTXgCf2RFDNhI5UVxurzWBnDdb8Mx8LnVplnkTqO+xUmHZYve0CwgWo+Q==}
+
+ '@cropper/element-viewer@2.0.0':
+ resolution: {integrity: sha512-zY+3VRN5TvpM8twlphYtXw0tzJL2VgzeK7ufhL1BixVqOdRxwP13TprYIhqwGt9EW/SyJZUiaIu396T89kRX8A==}
+
+ '@cropper/element@2.0.0':
+ resolution: {integrity: sha512-lsthn0nQq73GExUE7Mg/ss6Q3RXADGDv055hxoLFwvl/wGHgy6ZkYlfLZ/VmgBHC6jDK5IgPBFnqrPqlXWSGBA==}
+
+ '@cropper/elements@2.0.0':
+ resolution: {integrity: sha512-PQkPo1nUjxLFUQuHYu+6atfHxpX9B41Xribao6wpvmvmNIFML6LQdNqqWYb6LyM7ujsu71CZdBiMT5oetjJVoQ==}
+
+ '@cropper/utils@2.0.0':
+ resolution: {integrity: sha512-cprLYr+7kK3faGgoOsTW9gIn5sefDr2KwOmgyjzIXk+8PLpW8FgFKEg5FoWfRD5zMAmkCBuX6rGKDK3VdUEGrg==}
+
'@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
@@ -1997,6 +2033,9 @@ packages:
resolution: {integrity: sha512-onMB0OkDjkXunhdW9htFjEhqrD54+M94i6ackoUkjHKbRnXdyEyKRelp4nJ1kAz32+s27jP1FsebpJCVl0BsvA==}
engines: {node: '>=18.0'}
+ cropperjs@2.0.0:
+ resolution: {integrity: sha512-TO2j0Qre01kPHbow4FuTrbdEB4jTmGRySxW49jyEIqlJZuEBfrvCTT0vC3eRB2WBXudDfKi1Onako6DKWKxeAQ==}
+
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -5083,6 +5122,72 @@ snapshots:
'@colors/colors@1.6.0': {}
+ '@cropper/element-canvas@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-crosshair@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-grid@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-handle@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-image@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-selection@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/element-image': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-shade@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/element-selection': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element-viewer@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/element-image': 2.0.0
+ '@cropper/element-selection': 2.0.0
+ '@cropper/utils': 2.0.0
+
+ '@cropper/element@2.0.0':
+ dependencies:
+ '@cropper/utils': 2.0.0
+
+ '@cropper/elements@2.0.0':
+ dependencies:
+ '@cropper/element': 2.0.0
+ '@cropper/element-canvas': 2.0.0
+ '@cropper/element-crosshair': 2.0.0
+ '@cropper/element-grid': 2.0.0
+ '@cropper/element-handle': 2.0.0
+ '@cropper/element-image': 2.0.0
+ '@cropper/element-selection': 2.0.0
+ '@cropper/element-shade': 2.0.0
+ '@cropper/element-viewer': 2.0.0
+
+ '@cropper/utils@2.0.0': {}
+
'@dabh/diagnostics@2.0.3':
dependencies:
colorspace: 1.1.4
@@ -7079,6 +7184,11 @@ snapshots:
croner@9.0.0: {}
+ cropperjs@2.0.0:
+ dependencies:
+ '@cropper/elements': 2.0.0
+ '@cropper/utils': 2.0.0
+
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
diff --git a/public/themes/ash.css b/public/themes/ash.css
new file mode 100644
index 0000000..66923af
--- /dev/null
+++ b/public/themes/ash.css
@@ -0,0 +1,20 @@
+:root {
+ --text-color: #f0e5e0;
+ --secondary-text-color: #e8e0db;
+
+ --chat-background-color: #2f2e2d;
+ --chat-highlighted-background-color: #3f3b38;
+ --sidebar-background-color: #3e3a37;
+ --sidebar-highlighted-background-color: #46423b;
+ --topbar-background-color: #3a3733;
+ --chatbox-background-color: #3a3733;
+
+ --padding-color: #e0e0e0;
+
+ --primary-color: #f07028;
+ --primary-highlighted-color: #f28f4b;
+ --secondary-color: #683820;
+ --secondary-highlighted-color: #885830;
+ --accent-color: #a04b24;
+ --accent-highlighted-color: #b86038;
+}
\ No newline at end of file
diff --git a/public/themes/ash.json b/public/themes/ash.json
new file mode 100644
index 0000000..d5d2a59
--- /dev/null
+++ b/public/themes/ash.json
@@ -0,0 +1,6 @@
+{
+ "displayName": "Ash",
+ "previewGradient": "45deg, #2f2e2d, #46423b",
+ "complementaryColor": "white",
+ "themeUrl": "ash.css"
+}
\ No newline at end of file
diff --git a/public/themes/dark.css b/public/themes/dark.css
index 8245724..af94112 100644
--- a/public/themes/dark.css
+++ b/public/themes/dark.css
@@ -7,6 +7,7 @@
--sidebar-background-color: #2e2a27;
--sidebar-highlighted-background-color: #36322b;
--topbar-background-color: #2a2723;
+ --chatbox-background-color: #1a1713;
--padding-color: #484848;
diff --git a/public/themes/dark.json b/public/themes/dark.json
new file mode 100644
index 0000000..4731d43
--- /dev/null
+++ b/public/themes/dark.json
@@ -0,0 +1,6 @@
+{
+ "displayName": "Dark",
+ "previewGradient": "45deg, #1f1e1d, #36322b",
+ "complementaryColor": "white",
+ "themeUrl": "dark.css"
+}
\ No newline at end of file
diff --git a/public/themes/default-themes.json b/public/themes/default-themes.json
deleted file mode 100644
index fb9478d..0000000
--- a/public/themes/default-themes.json
+++ /dev/null
@@ -1,4 +0,0 @@
-[
- "dark.css",
- "light.css"
-]
\ No newline at end of file
diff --git a/public/themes/description.css b/public/themes/description.css
new file mode 100644
index 0000000..4b07f11
--- /dev/null
+++ b/public/themes/description.css
@@ -0,0 +1,29 @@
+/* this is not a real theme, but rather a template for themes */
+:root {
+ --text-color: #161518;
+ --secondary-text-color: #2b2930;
+
+ --chat-background-color: #80808000;
+ --chat-highlighted-background-color: #ffffff20;
+ --sidebar-background-color: #80808000;
+ --sidebar-highlighted-background-color: #ffffff20;
+ --topbar-background-color: #80808000;
+ --chatbox-background-color: #80808000;
+
+ --padding-color: #80808000;
+
+ --primary-color: #21b1ff80;
+ --primary-highlighted-color: #18a0df80;
+ --secondary-color: #ffd80080;
+ --secondary-highlighted-color: #dfb80080;
+ --accent-color: #ff218c80;
+ --accent-highlighted-color: #df1b6f80;
+
+ --optional-body-background: ; /* background element for the body */
+ --optional-chat-background: ; /* background element for the chat box */
+ --optional-topbar-background: ; /* background element for the topbar */
+ --optional-sidebar-background: ; /* background element for left server sidebar */
+ --optional-channel-list-background: ; /* background element for channel list and settings list */
+ --optional-member-list-background: ; /* background element for member list */
+ --optional-message-box-background: ; /* background element for message box */
+}
\ No newline at end of file
diff --git a/public/themes/light.css b/public/themes/light.css
index cb51d8c..b9b7b86 100644
--- a/public/themes/light.css
+++ b/public/themes/light.css
@@ -7,6 +7,7 @@
--sidebar-background-color: #dbd8d4;
--sidebar-highlighted-background-color: #d4d0ca;
--topbar-background-color: #dfdbd6;
+ --chatbox-background-color: #dfdbd6;
--padding-color: #484848;
diff --git a/public/themes/light.json b/public/themes/light.json
new file mode 100644
index 0000000..b95c78b
--- /dev/null
+++ b/public/themes/light.json
@@ -0,0 +1,6 @@
+{
+ "displayName": "Light",
+ "previewGradient": "45deg, #f0ebe8, #d4d0ca",
+ "complementaryColor": "black",
+ "themeUrl": "light.css"
+}
\ No newline at end of file
diff --git a/public/themes/rainbow-capitalism.css b/public/themes/rainbow-capitalism.css
new file mode 100644
index 0000000..32f7449
--- /dev/null
+++ b/public/themes/rainbow-capitalism.css
@@ -0,0 +1,28 @@
+:root {
+ --text-color: #161518;
+ --secondary-text-color: #2b2930;
+
+ --chat-background-color: #80808000;
+ --chat-highlighted-background-color: #ffffff20;
+ --sidebar-background-color: #80808000;
+ --sidebar-highlighted-background-color: #ffffff20;
+ --topbar-background-color: #80808000;
+ --chatbox-background-color: #80808040;
+
+ --padding-color: #80808000;
+
+ --primary-color: #21b1ff80;
+ --primary-highlighted-color: #18a0df80;
+ --secondary-color: #ffd80080;
+ --secondary-highlighted-color: #dfb80080;
+ --accent-color: #ff218c80;
+ --accent-highlighted-color: #df1b6f80;
+
+ /* --optional-body-background: background */
+ --optional-body-background: linear-gradient(45deg, #ed222480, #ed222480, #ed222480, #ed222480, #ed222480, #ed222480, #f35b2280, #f9962180, #f5c11e80, #f1eb1b80, #f1eb1b80, #f1eb1b80, #63c72080, #0c9b4980, #21878d80, #3954a580, #61379b80, #93288e80);
+ --optional-topbar-background: linear-gradient(-12.5deg, cyan, pink, white, pink, cyan);
+ --optional-sidebar-background: linear-gradient(90deg, #55cdfcd0, #f7a8b8d0, #ffffffd0, #f7a8b8d0, #55cdfcd0);
+ --optional-channel-list-background: linear-gradient(82deg, #d52c00b0, #e29688b0, #ffffffb0, #d27fa4b0, #a20062b0);
+ --optional-member-list-background: linear-gradient(3deg, #ff0080, #c8259d, #8c4799, #442e9f, #0032a0);
+ --optional-message-box-background: linear-gradient(3deg, #ff0080, #c8259d, #8c4799, #442e9f, #0032a0);
+}
\ No newline at end of file
diff --git a/public/themes/rainbow-capitalism.json b/public/themes/rainbow-capitalism.json
new file mode 100644
index 0000000..e110ea2
--- /dev/null
+++ b/public/themes/rainbow-capitalism.json
@@ -0,0 +1,6 @@
+{
+ "displayName": "Woke",
+ "previewGradient": "45deg, #ed2224, #ed2224, #f35b22, #f99621, #f5c11e, #f1eb1b 27%, #f1eb1b, #f1eb1b 33%, #63c720, #0c9b49, #21878d, #3954a5, #61379b, #93288e, #93288e",
+ "complementaryColor": "white",
+ "themeUrl": "rainbow-capitalism.css"
+}
\ No newline at end of file