From be5d65883bf57caabfab64d53d5937e1b5407075 Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Sun, 13 Jul 2025 18:15:48 +0200 Subject: [PATCH 01/83] feat: add xxhash-wasm library --- package.json | 3 ++- pnpm-lock.yaml | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5d7d19e..42347b1 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "pinia-plugin-persistedstate": "^4.2.0", "typescript": "^5.8.3", "vue": "^3.5.13", - "vue-router": "^4.5.1" + "vue-router": "^4.5.1", + "xxhash-wasm": "^1.1.0" }, "packageManager": "pnpm@10.11.0", "license": "MIT", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6a8cf2c..6b461e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: vue-router: specifier: ^4.5.1 version: 4.5.1(vue@3.5.13(typescript@5.8.3)) + xxhash-wasm: + specifier: ^1.1.0 + version: 1.1.0 devDependencies: '@iconify-json/lucide': specifier: ^1.2.44 @@ -4744,6 +4747,9 @@ packages: engines: {node: '>= 0.10.0'} hasBin: true + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -10146,6 +10152,8 @@ snapshots: cssfilter: 0.0.10 optional: true + xxhash-wasm@1.1.0: {} + y18n@5.0.8: {} yallist@3.1.1: {} From 9b7de48c0276f860892da58d7087be16af3f6b2d Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Sun, 13 Jul 2025 18:16:02 +0200 Subject: [PATCH 02/83] feat: add IRC colours, without a toggle for now --- components/Message.vue | 5 ++++- components/MessageArea.vue | 1 + types/props.ts | 1 + utils/generateIrcColor.ts | 12 ++++++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 utils/generateIrcColor.ts diff --git a/components/Message.vue b/components/Message.vue index 2bcdcfc..e1d3c6c 100644 --- a/components/Message.vue +++ b/components/Message.vue @@ -40,7 +40,7 @@
- + {{ author?.display_name || author?.username }} @@ -71,6 +71,7 @@ import DOMPurify from 'dompurify'; import { parse } from 'marked'; import type { MessageProps } from '~/types/props'; +import generateIrcColor from '~/utils/generateIrcColor'; const props = defineProps(); @@ -117,6 +118,8 @@ onMounted(async () => { // showHover.value = !showHover.value; //} +console.log(props.authorColor) + const menuItems = [ { name: "Reply", callback: () => { if (messageElement.value) replyToMessage(messageElement.value, props) } } ] diff --git a/components/MessageArea.vue b/components/MessageArea.vue index d59b862..cae68f1 100644 --- a/components/MessageArea.vue +++ b/components/MessageArea.vue @@ -7,6 +7,7 @@ :margin-bottom="(messages[i + 1] && messagesType[messages[i + 1].uuid] == 'normal') ?? false" :last="i == messages.length - 1" :message-id="message.uuid" :author="message.user" :me="me" :message="message" :is-reply="message.reply_to" + :author-color="`${generateIrcColor(message.user.uuid)}`" :reply-message="message.reply_to ? getReplyMessage(message.reply_to) : undefined" />
diff --git a/types/props.ts b/types/props.ts index aa6ff0c..fe2b049 100644 --- a/types/props.ts +++ b/types/props.ts @@ -9,6 +9,7 @@ export interface MessageProps { format: "12" | "24", type: "normal" | "grouped", marginBottom: boolean, + authorColor: string, last: boolean, messageId: string, replyingTo?: boolean, diff --git a/utils/generateIrcColor.ts b/utils/generateIrcColor.ts new file mode 100644 index 0000000..d2ed643 --- /dev/null +++ b/utils/generateIrcColor.ts @@ -0,0 +1,12 @@ +import xxhash from "xxhash-wasm" + +const { h64 } = await xxhash() + +export default (seed: string): string => { + const lightness = 50 + + // this should probably be cached eventually + const idHash = h64(seed) + + return `hsl(${idHash % 360n}, 100%, ${lightness}%)` +} \ No newline at end of file From dc786cd42078a89466664f43dda316b9223e175e Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Sun, 13 Jul 2025 18:17:01 +0200 Subject: [PATCH 03/83] fix: remove random console log --- components/Message.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/Message.vue b/components/Message.vue index e1d3c6c..b6afc7b 100644 --- a/components/Message.vue +++ b/components/Message.vue @@ -118,8 +118,6 @@ onMounted(async () => { // showHover.value = !showHover.value; //} -console.log(props.authorColor) - const menuItems = [ { name: "Reply", callback: () => { if (messageElement.value) replyToMessage(messageElement.value, props) } } ] From 047fe5e83395265d9ddae33831a5e23d0d9f1b8b Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Sun, 13 Jul 2025 20:47:55 +0200 Subject: [PATCH 04/83] fix: links not being clickable due to href attribute not being allowed --- components/Message.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Message.vue b/components/Message.vue index 2bcdcfc..e766ed9 100644 --- a/components/Message.vue +++ b/components/Message.vue @@ -97,7 +97,7 @@ onMounted(async () => { ], ALLOW_DATA_ATTR: false, ALLOW_SELF_CLOSE_IN_ATTR: false, - ALLOWED_ATTR: [] + ALLOWED_ATTR: ["href"] }); console.log("adding listeners") await nextTick(); From 7f1b26a59c431c06cdb6d4109103677c7046fc07 Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Sun, 13 Jul 2025 20:48:28 +0200 Subject: [PATCH 05/83] style(ui): hide scrollbar in left column --- layouts/client.vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/layouts/client.vue b/layouts/client.vue index b609c2c..3781fe9 100644 --- a/layouts/client.vue +++ b/layouts/client.vue @@ -261,6 +261,12 @@ function createDropdown() { align-items: center; gap: 1.5dvh; overflow-y: scroll; + scrollbar-width: none; + -ms-overflow-style: none; +} + +#left-column-top::-webkit-scrollbar, #left-column-bottom::-webkit-scrollbar { + display: none; } #left-column-bottom { @@ -331,4 +337,4 @@ function createDropdown() { color: var(--primary-highlighted-color) } - \ No newline at end of file + From bb06d2e0beb1f06e3c1c2d620d70c1b68597bc7a Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Sun, 13 Jul 2025 20:58:53 +0200 Subject: [PATCH 06/83] feat: start working on device list --- components/Settings/UserSettings/Devices.vue | 63 +++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/components/Settings/UserSettings/Devices.vue b/components/Settings/UserSettings/Devices.vue index 7006a12..ee471ba 100644 --- a/components/Settings/UserSettings/Devices.vue +++ b/components/Settings/UserSettings/Devices.vue @@ -1,12 +1,73 @@ \ No newline at end of file From 088c6c558b708873142c9675f7525818bd437adf Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Mon, 14 Jul 2025 01:05:47 +0200 Subject: [PATCH 07/83] fix: modals not showing properly due to imports not being updated after components folder restructuring --- components/Message.vue | 1 + components/{Modals/Modal.vue => Modal/Base.vue} | 0 components/{Modals/InviteModal.vue => Modal/Invite.vue} | 4 ++-- layouts/client.vue | 6 +++--- 4 files changed, 6 insertions(+), 5 deletions(-) rename components/{Modals/Modal.vue => Modal/Base.vue} (100%) rename components/{Modals/InviteModal.vue => Modal/Invite.vue} (95%) diff --git a/components/Message.vue b/components/Message.vue index e766ed9..7bff711 100644 --- a/components/Message.vue +++ b/components/Message.vue @@ -71,6 +71,7 @@ import DOMPurify from 'dompurify'; import { parse } from 'marked'; import type { MessageProps } from '~/types/props'; +import MessageReply from './UserInterface/MessageReply.vue'; const props = defineProps(); diff --git a/components/Modals/Modal.vue b/components/Modal/Base.vue similarity index 100% rename from components/Modals/Modal.vue rename to components/Modal/Base.vue diff --git a/components/Modals/InviteModal.vue b/components/Modal/Invite.vue similarity index 95% rename from components/Modals/InviteModal.vue rename to components/Modal/Invite.vue index 9c26bc2..232114c 100644 --- a/components/Modals/InviteModal.vue +++ b/components/Modal/Invite.vue @@ -1,5 +1,5 @@ From b6b8d10d294411ccf642ef55f8a843d0582bb114 Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Mon, 14 Jul 2025 01:07:46 +0200 Subject: [PATCH 09/83] fix: automatically scrolling to bottom of chat not working properly --- components/MessageArea.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/MessageArea.vue b/components/MessageArea.vue index d59b862..c91efe8 100644 --- a/components/MessageArea.vue +++ b/components/MessageArea.vue @@ -251,6 +251,8 @@ onMounted(async () => { if (import.meta.server) return; console.log("[MSG] messages keys:", Object.values(messages.value)); if (messagesElement.value) { + await nextTick(); + await nextTick(); scrollToBottom(messagesElement.value); let fetched = false; const amount = messages.value.length; From 015b23f4e546a0bc45481588e2073e8630e8c1f5 Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Mon, 14 Jul 2025 01:11:36 +0200 Subject: [PATCH 10/83] feat: implement image embeds --- app.vue | 1 + components/Message.vue | 35 +++++++++++++++++++++++++-- components/MessageMedia.vue | 47 +++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 components/MessageMedia.vue diff --git a/app.vue b/app.vue index c6db309..9c8fb1c 100644 --- a/app.vue +++ b/app.vue @@ -14,6 +14,7 @@ const banner = useState("banner", () => false); onMounted(() => { document.removeEventListener("contextmenu", contextMenuHandler); document.addEventListener("contextmenu", (e) => { + if (e.target instanceof Element && e.target.classList.contains("default-contextmenu")) return; contextMenuHandler(e); }); document.addEventListener("mousedown", (e) => { diff --git a/components/Message.vue b/components/Message.vue index 7bff711..82bc151 100644 --- a/components/Message.vue +++ b/components/Message.vue @@ -50,8 +50,9 @@ {{ date.toLocaleTimeString(undefined, { hour12: props.format == "12", timeStyle: "short" }) }}
-
+
+
-
+
+ @@ -71,6 +73,7 @@ import DOMPurify from 'dompurify'; import { parse } from 'marked'; import type { MessageProps } from '~/types/props'; +import MessageMedia from './MessageMedia.vue'; import MessageReply from './UserInterface/MessageReply.vue'; const props = defineProps(); @@ -86,6 +89,13 @@ console.log("[MSG] message to render:", props.message); console.log("author:", props.author); console.log("[MSG] reply message:", props.replyMessage); +const linkRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)/g; +const linkMatches = props.message.message.matchAll(linkRegex).map(link => link[0]); +const mediaLinks: string[] = []; +console.log("link matches:", linkMatches); + +const hasEmbed = ref(false); + const sanitized = ref(); onMounted(async () => { @@ -112,6 +122,27 @@ onMounted(async () => { }); console.log("added listeners"); } + + for (const link of linkMatches) { + console.log("link:", link); + try { + const res = await $fetch.raw(link); + if (res.ok && res.headers.get("content-type")?.match(/^image\/(apng|gif|jpeg|png|webp)$/)) { + console.log("link is image"); + mediaLinks.push(link); + } + if (mediaLinks.length) { + hasEmbed.value = true + setTimeout(() => { + scrollToBottom(document.getElementById("messages") as HTMLDivElement); + }, 500); + }; + } catch (error) { + console.error(error); + } +} + +console.log("media links:", mediaLinks); }); //function toggleTooltip(e: Event) { diff --git a/components/MessageMedia.vue b/components/MessageMedia.vue new file mode 100644 index 0000000..ab62387 --- /dev/null +++ b/components/MessageMedia.vue @@ -0,0 +1,47 @@ + + + + + \ No newline at end of file From 9d1eeff5825dfa559ed49a6d8e7bb2a1a3aabc3a Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Mon, 14 Jul 2025 18:44:18 +0200 Subject: [PATCH 11/83] feat: remove -ms-overflow-style CSS property from left column --- layouts/client.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/layouts/client.vue b/layouts/client.vue index 8aeaf9c..b362156 100644 --- a/layouts/client.vue +++ b/layouts/client.vue @@ -262,7 +262,6 @@ function createDropdown() { gap: 1.5dvh; overflow-y: scroll; scrollbar-width: none; - -ms-overflow-style: none; } #left-column-top::-webkit-scrollbar, #left-column-bottom::-webkit-scrollbar { From fc87bd4b6f5e16f143249f9fa66f2e109395b29c Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Mon, 14 Jul 2025 18:44:25 +0200 Subject: [PATCH 12/83] chore: remove unused temporary members list array --- layouts/client.vue | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/layouts/client.vue b/layouts/client.vue index b362156..65dd021 100644 --- a/layouts/client.vue +++ b/layouts/client.vue @@ -138,47 +138,6 @@ const options = [ const guilds: GuildResponse[] | undefined = await fetchWithApi("/me/guilds"); -//const servers = await fetchWithApi("/servers") as { uuid: string, name: string, description: string }[]; -//console.log("servers:", servers); -const members = [ - { - id: "3287484395", - displayName: "SauceyRed" - }, - { - id: "3287484395", - displayName: "SauceyRed" - }, - { - id: "3287484395", - displayName: "SauceyRed" - }, - { - id: "3287484395", - displayName: "SauceyRed" - }, - { - id: "3287484395", - displayName: "SauceyRed" - }, - { - id: "3287484395", - displayName: "SauceyRed" - }, - { - id: "3287484395", - displayName: "SauceyRed" - }, - { - id: "3287484395", - displayName: "SauceyRed" - }, - { - id: "3287484395", - displayName: "SauceyRed" - } -]; - function createDropdown() { const dropdown = h(GuildDropdown, { options }); const div = document.createElement("div"); From df741ee5d4df36bb88f320898ef2393dfa66fc4c Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Mon, 14 Jul 2025 19:24:59 +0200 Subject: [PATCH 13/83] fix: sidebar scrolling, and such --- layouts/client.vue | 117 +++++++++++---------------- public/themes/ash.css | 5 ++ public/themes/dark.css | 5 ++ public/themes/description.css | 5 ++ public/themes/light.css | 5 ++ public/themes/rainbow-capitalism.css | 5 ++ 6 files changed, 71 insertions(+), 71 deletions(-) diff --git a/layouts/client.vue b/layouts/client.vue index 8aeaf9c..e5d7907 100644 --- a/layouts/client.vue +++ b/layouts/client.vue @@ -8,27 +8,37 @@ -
+
-
+
-
- - - - -
-
+ +
+ + + + + + + + + + + + +
+ +
-
- - + +
@@ -42,6 +52,7 @@ import { ModalBase } from '#components'; import { render } from 'vue'; import GuildDropdown from '~/components/Guild/GuildDropdown.vue'; import Button from '~/components/UserInterface/Button.vue'; +import VerticalSpacer from '~/components/UserInterface/VerticalSpacer.vue'; import type { GuildResponse } from '~/types/interfaces'; const loading = useState("loading", () => false); @@ -243,68 +254,29 @@ function createDropdown() { #left-column { display: flex; flex-direction: column; - justify-content: space-between; - align-items: center; - gap: .75em; + padding-left: .25em; padding-right: .25em; padding-top: .5em; - border-right: 1px solid var(--padding-color); + background: var(--optional-sidebar-background); background-color: var(--sidebar-background-color); } -#left-column-top, #left-column-bottom { +.left-column-segment { display: flex; flex-direction: column; - justify-content: center; - align-items: center; - gap: 1.5dvh; - overflow-y: scroll; + scrollbar-width: none; - -ms-overflow-style: none; } -#left-column-top::-webkit-scrollbar, #left-column-bottom::-webkit-scrollbar { +.left-column-segment::-webkit-scrollbar { display: none; } -#left-column-bottom { - padding-top: 1dvh; - border-top: 1px solid var(--padding-color); -} - -#middle-left-column { - padding-left: 1dvw; - padding-right: 1dvw; - border-right: 1px solid var(--padding-color); -} - -#home-button { - border-bottom: 1px solid var(--padding-color); - padding-bottom: 1dvh; -} - -#servers-list { - display: flex; - flex-direction: column; - gap: 1em; - width: 3.2rem; - padding-top: .5em; -} - -#create-button { - color: var(--primary-color); - background-color: transparent; - border: none; - cursor: pointer; - font-size: 2rem; - padding: 0; - display: inline-block; -} - -#create-icon { - float: left; +#left-column-middle { + overflow-y: scroll; + flex-grow: 1; } #middle-left-column { @@ -317,24 +289,27 @@ function createDropdown() { overflow-x: hidden; } +#home-button { + height: 3em; +} + .sidebar-icon { width: 3rem; height: 3rem; - overflow-y: scroll; - overflow-x: hidden; } -#home-button { - border-bottom: 1px solid var(--padding-color); - padding-bottom: .375em; +.sidebar-bottom-buttons { + color: var(--primary-color); + background-color: transparent; + border: none; + cursor: pointer; + font-size: 2.4rem; + padding: 0; + display: inline-block; } -#settings-menu { - color: var(--primary-color) -} - -#settings-menu:hover { - color: var(--primary-highlighted-color) +.sidebar-bottom-buttons:hover { + color: var(--primary-highlighted-color); } diff --git a/public/themes/ash.css b/public/themes/ash.css index 63aaa53..3f27868 100644 --- a/public/themes/ash.css +++ b/public/themes/ash.css @@ -11,6 +11,11 @@ --chatbox-background-color: #3a3733; --padding-color: #e0e0e0; + + --sidebar-width: 3em; + --standard-radius: .5em; + --button-radius: .6em; + --pfp-radius: 100%; --primary-color: #f07028; --primary-highlighted-color: #f28f4b; diff --git a/public/themes/dark.css b/public/themes/dark.css index 3673d4c..d03f07f 100644 --- a/public/themes/dark.css +++ b/public/themes/dark.css @@ -18,4 +18,9 @@ --secondary-highlighted-color: #8f5b2c; --accent-color: #b35719; --accent-highlighted-color: #c76a2e; + + --sidebar-width: 3em; + --standard-radius: .5em; + --button-radius: .6em; + --pfp-radius: 100%; } \ No newline at end of file diff --git a/public/themes/description.css b/public/themes/description.css index 209fb36..91f6e7f 100644 --- a/public/themes/description.css +++ b/public/themes/description.css @@ -20,6 +20,11 @@ --accent-color: #ff218c80; --accent-highlighted-color: #df1b6f80; + --sidebar-width: 3em; + --standard-radius: .5em; + --button-radius: .6em; + --pfp-radius: 100%; + --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 */ diff --git a/public/themes/light.css b/public/themes/light.css index fdd4756..44e5318 100644 --- a/public/themes/light.css +++ b/public/themes/light.css @@ -11,6 +11,11 @@ --chatbox-background-color: #dfdbd6; --padding-color: #484848; + + --sidebar-width: 3em; + --standard-radius: .5em; + --button-radius: .6em; + --pfp-radius: 100%; --primary-color: #df5f0b; --primary-highlighted-color: #ef6812; diff --git a/public/themes/rainbow-capitalism.css b/public/themes/rainbow-capitalism.css index 0e146d9..0fe2287 100644 --- a/public/themes/rainbow-capitalism.css +++ b/public/themes/rainbow-capitalism.css @@ -19,6 +19,11 @@ --accent-color: #ff218c80; --accent-highlighted-color: #df1b6f80; + --sidebar-width: 3em; + --standard-radius: .5em; + --button-radius: .6em; + --pfp-radius: 100%; + /* --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); From 06de4777f96c99b4ef90fcda7408c49dc2f4fa5b Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Mon, 14 Jul 2025 19:36:16 +0200 Subject: [PATCH 14/83] feat: make sidebar size adjustable by theme --- layouts/client.vue | 14 +++----------- public/themes/ash.css | 2 +- public/themes/dark.css | 2 +- public/themes/description.css | 2 +- public/themes/light.css | 2 +- public/themes/rainbow-capitalism.css | 2 +- 6 files changed, 8 insertions(+), 16 deletions(-) diff --git a/layouts/client.vue b/layouts/client.vue index e5d7907..f76b285 100644 --- a/layouts/client.vue +++ b/layouts/client.vue @@ -21,14 +21,6 @@ - - - - - - - -
@@ -290,12 +282,12 @@ function createDropdown() { } #home-button { - height: 3em; + height: var(--sidebar-width); } .sidebar-icon { - width: 3rem; - height: 3rem; + width: var(--sidebar-width); + height: var(--sidebar-width); } .sidebar-bottom-buttons { diff --git a/public/themes/ash.css b/public/themes/ash.css index 3f27868..d1f09c8 100644 --- a/public/themes/ash.css +++ b/public/themes/ash.css @@ -12,7 +12,7 @@ --padding-color: #e0e0e0; - --sidebar-width: 3em; + --sidebar-width: 2.5em; --standard-radius: .5em; --button-radius: .6em; --pfp-radius: 100%; diff --git a/public/themes/dark.css b/public/themes/dark.css index d03f07f..6160194 100644 --- a/public/themes/dark.css +++ b/public/themes/dark.css @@ -19,7 +19,7 @@ --accent-color: #b35719; --accent-highlighted-color: #c76a2e; - --sidebar-width: 3em; + --sidebar-width: 2.5em; --standard-radius: .5em; --button-radius: .6em; --pfp-radius: 100%; diff --git a/public/themes/description.css b/public/themes/description.css index 91f6e7f..f3738a5 100644 --- a/public/themes/description.css +++ b/public/themes/description.css @@ -20,7 +20,7 @@ --accent-color: #ff218c80; --accent-highlighted-color: #df1b6f80; - --sidebar-width: 3em; + --sidebar-width: 2.5em; --standard-radius: .5em; --button-radius: .6em; --pfp-radius: 100%; diff --git a/public/themes/light.css b/public/themes/light.css index 44e5318..d11f5f5 100644 --- a/public/themes/light.css +++ b/public/themes/light.css @@ -12,7 +12,7 @@ --padding-color: #484848; - --sidebar-width: 3em; + --sidebar-width: 2.5em; --standard-radius: .5em; --button-radius: .6em; --pfp-radius: 100%; diff --git a/public/themes/rainbow-capitalism.css b/public/themes/rainbow-capitalism.css index 0fe2287..1a5197b 100644 --- a/public/themes/rainbow-capitalism.css +++ b/public/themes/rainbow-capitalism.css @@ -19,7 +19,7 @@ --accent-color: #ff218c80; --accent-highlighted-color: #df1b6f80; - --sidebar-width: 3em; + --sidebar-width: 2.5em; --standard-radius: .5em; --button-radius: .6em; --pfp-radius: 100%; From 9bfe3310ccd4334e45436a79881c417e98c360c1 Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Mon, 14 Jul 2025 19:48:35 +0200 Subject: [PATCH 15/83] fix: comply with es2020 standards --- utils/generateIrcColor.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utils/generateIrcColor.ts b/utils/generateIrcColor.ts index d2ed643..24d4946 100644 --- a/utils/generateIrcColor.ts +++ b/utils/generateIrcColor.ts @@ -1,6 +1,9 @@ import xxhash from "xxhash-wasm" -const { h64 } = await xxhash() +let h64: CallableFunction; +(async () => { + h64 = (await xxhash()).h64; +})(); export default (seed: string): string => { const lightness = 50 From 25cd9a397e5ef5a7cb9396fe5e0ad1d7ec9e3fd7 Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Mon, 14 Jul 2025 20:05:31 +0200 Subject: [PATCH 16/83] feat: implement caching for hash function --- utils/generateIrcColor.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/utils/generateIrcColor.ts b/utils/generateIrcColor.ts index 24d4946..03a30e7 100644 --- a/utils/generateIrcColor.ts +++ b/utils/generateIrcColor.ts @@ -5,11 +5,9 @@ let h64: CallableFunction; h64 = (await xxhash()).h64; })(); -export default (seed: string): string => { - const lightness = 50 - - // this should probably be cached eventually - const idHash = h64(seed) +export default (seed: string, saturation: number = 100, lightness: number = 50): string => { + const idHash = useState(`h64Hash-${seed}`, () => h64(seed)) + const hashValue: bigint = idHash.value - return `hsl(${idHash % 360n}, 100%, ${lightness}%)` + return `hsl(${hashValue % 360n}, ${saturation}%, ${lightness}%)` } \ No newline at end of file From b319a06749199d4baab0e9c085592b33ba0b6bec Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Mon, 14 Jul 2025 21:36:41 +0200 Subject: [PATCH 17/83] feat: import function from JOHN OZBAY --- utils/isCanvasBlocked.ts | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 utils/isCanvasBlocked.ts diff --git a/utils/isCanvasBlocked.ts b/utils/isCanvasBlocked.ts new file mode 100644 index 0000000..3bd191e --- /dev/null +++ b/utils/isCanvasBlocked.ts @@ -0,0 +1,50 @@ +// +// Canvas Blocker & +// Firefox privacy.resistFingerprinting Detector. +// (c) 2018 // JOHN OZBAY // CRYPT.EE +// MIT License +// +export default () => { + // create a 1px image data + var blocked = false; + var canvas = document.createElement("canvas"); + var ctx = canvas.getContext("2d"); + + // some blockers just return an undefined ctx. So let's check that first. + if (ctx) { + var imageData = ctx.createImageData(1,1); + var originalImageData = imageData.data; + + // set pixels to RGB 128 + originalImageData[0]=128; + originalImageData[1]=128; + originalImageData[2]=128; + originalImageData[3]=255; + + // set this to canvas + ctx.putImageData(imageData,1,1); + + try { + // now get the data back from canvas. + var checkData = ctx.getImageData(1, 1, 1, 1).data; + + // If this is firefox, and privacy.resistFingerprinting is enabled, + // OR a browser extension blocking the canvas, + // This will return RGB all white (255,255,255) instead of the (128,128,128) we put. + + // so let's check the R and G to see if they're 255 or 128 (matching what we've initially set) + if (originalImageData[0] !== checkData[0] && originalImageData[1] !== checkData[1]) { + blocked = true; + console.log("Canvas is blocked. Will display warning."); + } + } catch (error) { + // some extensions will return getImageData null. this is to account for that. + blocked = true; + console.log("Canvas is blocked. Will display warning."); + } + } else { + blocked = true; + console.log("Canvas is blocked. Will display warning."); + } + return blocked; +} \ No newline at end of file From f4ddcf9d8db61d4c4704514de43cbe186fde9ac8 Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Mon, 14 Jul 2025 21:37:45 +0200 Subject: [PATCH 18/83] fix: prop --- types/props.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/props.ts b/types/props.ts index fe2b049..9a8e642 100644 --- a/types/props.ts +++ b/types/props.ts @@ -3,7 +3,7 @@ import type { MessageResponse, UserResponse } from "./interfaces"; export interface MessageProps { class?: string, img?: string | null, - author?: UserResponse + author: UserResponse text: string, timestamp: number, format: "12" | "24", From f98e8c611092e747e267afd029cf87eeebf21cca Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Mon, 14 Jul 2025 21:39:00 +0200 Subject: [PATCH 19/83] feat: implement generic component --- components/Avatar.vue | 37 ++++++++++++++++++++++++++++++++++ components/User/UserEntry.vue | 8 +++++--- components/User/UserPopup.vue | 3 +-- layouts/client.vue | 2 +- utils/generateDefaultIcon.ts | 38 +++++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 components/Avatar.vue create mode 100644 utils/generateDefaultIcon.ts diff --git a/components/Avatar.vue b/components/Avatar.vue new file mode 100644 index 0000000..368316c --- /dev/null +++ b/components/Avatar.vue @@ -0,0 +1,37 @@ + + + \ No newline at end of file diff --git a/components/User/UserEntry.vue b/components/User/UserEntry.vue index b463759..b539f2c 100644 --- a/components/User/UserEntry.vue +++ b/components/User/UserEntry.vue @@ -1,8 +1,8 @@ @@ -12,6 +12,8 @@ import type { UserResponse } from '~/types/interfaces'; const props = defineProps<{ user: UserResponse }>(); + +const displayName = props.user.display_name || props.user.username \ No newline at end of file From a146eb001ae43f0e3bc4bb5f349afe3ffd3e31a8 Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Tue, 15 Jul 2025 00:16:47 +0200 Subject: [PATCH 29/83] style: manually edit the reply svg a bit --- components/Message.vue | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/components/Message.vue b/components/Message.vue index 82bc151..9c2c0c8 100644 --- a/components/Message.vue +++ b/components/Message.vue @@ -4,14 +4,10 @@ :editing.sync="props.editing" :replying-to.sync="props.replyingTo">
+ Date: Tue, 15 Jul 2025 00:25:52 +0200 Subject: [PATCH 30/83] feat: add preferred font field to themes --- app.vue | 2 +- public/themes/ash.css | 11 ++++++----- public/themes/dark.css | 1 + public/themes/description.css | 1 + public/themes/light.css | 11 ++++++----- public/themes/rainbow-capitalism.css | 3 ++- utils/generateDefaultIcon.ts | 3 ++- 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/app.vue b/app.vue index 9c8fb1c..59d006d 100644 --- a/app.vue +++ b/app.vue @@ -65,7 +65,7 @@ useHead({
@@ -259,8 +259,8 @@ function createDropdown() { display: flex; flex-direction: column; - padding-left: .25em; - padding-right: .25em; + padding-left: var(--sidebar-margin); + padding-right: var(--sidebar-margin); padding-top: .5em; background: var(--optional-sidebar-background); @@ -281,6 +281,7 @@ function createDropdown() { #left-column-middle { overflow-y: scroll; flex-grow: 1; + gap: var(--sidebar-icon-gap); } #middle-left-column { @@ -294,12 +295,16 @@ function createDropdown() { } #home-button { - height: var(--sidebar-width); + height: var(--sidebar-icon-width); +} + +.guild-icon { + border-radius: var(--guild-icon-radius); } .sidebar-icon { - width: var(--sidebar-width); - height: var(--sidebar-width); + width: var(--sidebar-icon-width); + height: var(--sidebar-icon-width); } .sidebar-bottom-buttons { diff --git a/public/themes/ash.css b/public/themes/ash.css index 7a6b7a4..94f5e85 100644 --- a/public/themes/ash.css +++ b/public/themes/ash.css @@ -22,6 +22,7 @@ --sidebar-width: 2.5em; --standard-radius: .5em; --button-radius: .6em; - --pfp-radius: 100%; + --guild-icon-radius: 20%; + --pfp-radius: 50%; --preferred-font: Arial; } \ No newline at end of file diff --git a/public/themes/dark.css b/public/themes/dark.css index 59099c7..a228889 100644 --- a/public/themes/dark.css +++ b/public/themes/dark.css @@ -19,9 +19,13 @@ --accent-color: #b35719; --accent-highlighted-color: #c76a2e; - --sidebar-width: 2.5em; + --sidebar-icon-width: 2.5em; + --sidebar-icon-gap: .25em; + --sidebar-margin: .5em; + --standard-radius: .5em; --button-radius: .6em; - --pfp-radius: 100%; + --guild-icon-radius: 15%; + --pfp-radius: 50%; --preferred-font: Arial; } \ No newline at end of file diff --git a/public/themes/description.css b/public/themes/description.css index 57ade55..e30025f 100644 --- a/public/themes/description.css +++ b/public/themes/description.css @@ -23,7 +23,7 @@ --sidebar-width: 2.5em; --standard-radius: .5em; --button-radius: .6em; - --pfp-radius: 100%; + --pfp-radius: 50%; --preferred-font: Arial; --optional-body-background: ; /* background element for the body */ diff --git a/public/themes/light.css b/public/themes/light.css index 0b24214..556b64d 100644 --- a/public/themes/light.css +++ b/public/themes/light.css @@ -22,6 +22,6 @@ --sidebar-width: 2.5em; --standard-radius: .5em; --button-radius: .6em; - --pfp-radius: 100%; + --pfp-radius: 50%; --preferred-font: Arial; } \ No newline at end of file diff --git a/public/themes/rainbow-capitalism.css b/public/themes/rainbow-capitalism.css index c1c8246..ca73e1f 100644 --- a/public/themes/rainbow-capitalism.css +++ b/public/themes/rainbow-capitalism.css @@ -22,7 +22,7 @@ --sidebar-width: 2.5em; --standard-radius: .5em; --button-radius: .6em; - --pfp-radius: 100%; + --pfp-radius: 50%; --preferred-font: Arial; /* --optional-body-background: background */ From 4ce89d980348eca29b0734ebb4c07a2334d760e0 Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Tue, 15 Jul 2025 11:24:27 +0200 Subject: [PATCH 34/83] feat: update wording of guild create/join button's alt text --- layouts/client.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layouts/client.vue b/layouts/client.vue index f76b285..0018e8c 100644 --- a/layouts/client.vue +++ b/layouts/client.vue @@ -26,7 +26,7 @@
From 07fa883a14285cac89a38b6ce8e8a60d7eadd50c Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Tue, 15 Jul 2025 11:31:11 +0200 Subject: [PATCH 35/83] feat: change Avatar id to class --- components/Avatar.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/Avatar.vue b/components/Avatar.vue index 20f91c3..b2da51b 100644 --- a/components/Avatar.vue +++ b/components/Avatar.vue @@ -1,6 +1,6 @@ \ No newline at end of file diff --git a/pages/me/index.vue b/pages/me/index.vue index e875c56..a0d0384 100644 --- a/pages/me/index.vue +++ b/pages/me/index.vue @@ -1,13 +1,56 @@ - \ No newline at end of file From ace66980bfb060fd6b0ede2ea929c8d6974c5822 Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Wed, 16 Jul 2025 10:22:04 +0200 Subject: [PATCH 44/83] feat: sort friends alphabetically --- components/Me/FriendsList.vue | 4 +++- composables/api.ts | 9 +++++++-- utils/getDisplayName.ts | 7 +++++++ 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 utils/getDisplayName.ts diff --git a/components/Me/FriendsList.vue b/components/Me/FriendsList.vue index 44c606f..6c9fded 100644 --- a/components/Me/FriendsList.vue +++ b/components/Me/FriendsList.vue @@ -26,7 +26,9 @@ \ No newline at end of file diff --git a/pages/reset-password.vue b/pages/reset-password.vue index f579fc5..83e1f1f 100644 --- a/pages/reset-password.vue +++ b/pages/reset-password.vue @@ -1,50 +1,27 @@ + + \ No newline at end of file From c295225c4303e14be722ed3659800fb2074cf9d8 Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Wed, 16 Jul 2025 23:50:41 +0200 Subject: [PATCH 59/83] chore: rename instances of cursor to pointer in createContextMenu component and ContextMenuItem interface --- components/UserInterface/ContextMenu.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/UserInterface/ContextMenu.vue b/components/UserInterface/ContextMenu.vue index 1388bbd..5342b8c 100644 --- a/components/UserInterface/ContextMenu.vue +++ b/components/UserInterface/ContextMenu.vue @@ -7,13 +7,13 @@ \ No newline at end of file diff --git a/pages/register.vue b/pages/register.vue index 708fed0..36bdb53 100644 --- a/pages/register.vue +++ b/pages/register.vue @@ -86,6 +86,7 @@ const auth = useAuth(); const loggedIn = ref(await auth.getUser()); const query = new URLSearchParams(useRoute().query as Record); +query.delete("token"); const user = await useAuth().getUser(); diff --git a/pages/reset-password.vue b/pages/reset-password.vue new file mode 100644 index 0000000..8f869c4 --- /dev/null +++ b/pages/reset-password.vue @@ -0,0 +1,56 @@ + + + + + \ No newline at end of file From 49e8c3425490fd72ca5430ee8a23759b8a970e64 Mon Sep 17 00:00:00 2001 From: JustTemmie <47639983+JustTemmie@users.noreply.github.com> Date: Fri, 18 Jul 2025 07:44:07 +0200 Subject: [PATCH 75/83] fix: make channel list and DM list width share storage name --- components/Me/DirectMessagesSidebar.vue | 2 +- pages/servers/[serverId]/channels/[channelId].vue | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/components/Me/DirectMessagesSidebar.vue b/components/Me/DirectMessagesSidebar.vue index b3d1788..b06890b 100644 --- a/components/Me/DirectMessagesSidebar.vue +++ b/components/Me/DirectMessagesSidebar.vue @@ -1,5 +1,5 @@