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 01/16] 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 02/16] 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 03/16] 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 04/16] 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 diff --git a/components/Message.vue b/components/Message.vue index 166f99d..ce726fb 100644 --- a/components/Message.vue +++ b/components/Message.vue @@ -225,7 +225,6 @@ function getDayDifference(date1: Date, date2: Date) { .message-author-avatar { width: 100%; - border-radius: 50%; } .left-column { diff --git a/components/User/UserEntry.vue b/components/User/UserEntry.vue index b539f2c..1817816 100644 --- a/components/User/UserEntry.vue +++ b/components/User/UserEntry.vue @@ -37,6 +37,5 @@ const displayName = props.user.display_name || props.user.username .user-avatar { width: 2.3em; height: 2.3em; - border-radius: 50%; } diff --git a/layouts/client.vue b/layouts/client.vue index 112440d..2ed9e8c 100644 --- a/layouts/client.vue +++ b/layouts/client.vue @@ -19,16 +19,16 @@
@@ -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 07fa883a14285cac89a38b6ce8e8a60d7eadd50c Mon Sep 17 00:00:00 2001 From: SauceyRed Date: Tue, 15 Jul 2025 11:31:11 +0200 Subject: [PATCH 15/16] 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 @@