diff --git a/frontend-vue/bun.lockb b/frontend-vue/bun.lockb
index 7999c8e..d136a17 100755
Binary files a/frontend-vue/bun.lockb and b/frontend-vue/bun.lockb differ
diff --git a/frontend-vue/package.json b/frontend-vue/package.json
index fb4f9ca..367eef7 100644
--- a/frontend-vue/package.json
+++ b/frontend-vue/package.json
@@ -14,7 +14,11 @@
"format": "prettier --write src/"
},
"dependencies": {
+ "jdenticon": "^3.3.0",
+ "node-vibrant": "^4.0.3",
"pinia": "^3.0.3",
+ "sass": "^1.89.2",
+ "sass-loader": "^16.0.5",
"vue": "^3.5.17",
"vue-router": "^4.5.1"
},
@@ -31,6 +35,7 @@
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.29.0",
"eslint-plugin-vue": "~10.2.0",
+ "fibers": "^5.0.3",
"jiti": "^2.4.2",
"jsdom": "^26.1.0",
"npm-run-all2": "^8.0.4",
diff --git a/frontend-vue/src/App.vue b/frontend-vue/src/App.vue
index 7905b05..e35255f 100644
--- a/frontend-vue/src/App.vue
+++ b/frontend-vue/src/App.vue
@@ -1,22 +1,11 @@
- {{props.user.username}}
-
-
+
{{props.rawText}}
+ +{{props.rawText}}
+ diff --git a/frontend-vue/src/router/index.ts b/frontend-vue/src/router/index.ts index 3e49915..fa57489 100644 --- a/frontend-vue/src/router/index.ts +++ b/frontend-vue/src/router/index.ts @@ -17,6 +17,11 @@ const router = createRouter({ // which is lazy-loaded when the route is visited. component: () => import('../views/AboutView.vue'), }, + { + path: '/testing/note', + name: 'note-testing', + component: () => import('../views/NoteTestView.vue'), + }, ], }) diff --git a/frontend-vue/src/stores/media.ts b/frontend-vue/src/stores/media.ts new file mode 100644 index 0000000..5a78894 --- /dev/null +++ b/frontend-vue/src/stores/media.ts @@ -0,0 +1,10 @@ +export interface MediaMetadata { + id: string; + ownedBy: string; + remote: boolean; + url: string; + mediaType: string; + name: string; + alt: string; + blurred: boolean; +} diff --git a/frontend-vue/src/stores/userdata.ts b/frontend-vue/src/stores/userdata.ts new file mode 100644 index 0000000..95d0397 --- /dev/null +++ b/frontend-vue/src/stores/userdata.ts @@ -0,0 +1,10 @@ +import type { MediaMetadata } from '@/stores/media.ts' + +export interface User { + username: string + displayName: string + description: string + profilePicture?: MediaMetadata + bannerPicture?: MediaMetadata + backgroundPicture?: MediaMetadata +} diff --git a/frontend-vue/src/utils/identiheart.ts b/frontend-vue/src/utils/identiheart.ts new file mode 100644 index 0000000..9df1ec8 --- /dev/null +++ b/frontend-vue/src/utils/identiheart.ts @@ -0,0 +1,492 @@ +class Crusher { + hash(s: any): number { + return String(s).split("").reduce(function(a, b) { + a = ((a << 5) - a) + b.charCodeAt(0); + return a & a + }, 0); + } + isDOMElement(o:any): boolean { + return ( + typeof HTMLElement === "object" ? o instanceof HTMLElement : + o && typeof o === "object" && true && o.nodeType === 1 && typeof o.nodeName==="string" + ); + } +} + +enum BlockType { + ONE = 1, + TWO = 2, + THREE = 3, +} + +class Block { + canvas: HTMLCanvasElement; + context: CanvasRenderingContext2D; + type: BlockType; + primary: string; + accent: string; + cellSize: number; + margin: number; + scale: number; + pos: Position; + hash: number; + + constructor( + c: HTMLCanvasElement, + ctx: CanvasRenderingContext2D, + type: BlockType, + primary: string, + accent: string, + hash: number, + cellSize: number, + margin: number, + scale: number, + pos: Position, + ) { + this.canvas = c; + this.context = ctx; + this.type = type; + this.primary = primary; + this.accent = accent; + this.hash = hash; + this.cellSize = cellSize; + this.margin = margin; + this.scale = scale; + this.pos = pos; + } + + offset() { + this.context.save(); + this.context.translate(0.6 * this.scale, -0.6 * this.scale); + } + + resetOffset() { + this.context.restore(); + } + + makePath(hash: number, offset: number) { + const mod = Math.abs(hash + offset) % 4; + + switch(mod) { + case 0: + // top + this.context.beginPath(); + this.context.moveTo(this.pos.x, this.pos.y); + this.context.lineTo(this.pos.x + this.cellSize, this.pos.y); + this.context.lineTo(this.pos.x + this.cellSize, this.pos.y + this.cellSize); + this.context.closePath(); + break; + case 1: + // right + this.context.beginPath(); + this.context.moveTo(this.pos.x + this.cellSize, this.pos.y); + this.context.lineTo(this.pos.x + this.cellSize, this.pos.y + this.cellSize); + this.context.lineTo(this.pos.x, this.pos.y + this.cellSize); + this.context.closePath(); + break; + case 2: + // bottom + this.context.beginPath(); + this.context.moveTo(this.pos.x, this.pos.y); + this.context.lineTo(this.pos.x, this.pos.y + this.cellSize); + this.context.lineTo(this.pos.x + this.cellSize, this.pos.y + this.cellSize); + this.context.closePath(); + break; + case 3: + // left + this.context.beginPath(); + this.context.moveTo(this.pos.x, this.pos.y); + this.context.lineTo(this.pos.x + this.cellSize, this.pos.y); + this.context.lineTo(this.pos.x, this.pos.y + this.cellSize); + this.context.closePath(); + break; + default: + // top + this.context.beginPath(); + this.context.moveTo(this.pos.x, this.pos.y); + this.context.lineTo(this.pos.x + this.cellSize, this.pos.y); + this.context.lineTo(this.pos.x, this.pos.y + this.cellSize); + this.context.closePath(); + } + } + + draw() { + this.offset(); + + if (this.type === BlockType.ONE) { + this.makePath(this.hash, this.hash % 3); + this.context.fillStyle = this.primary; + this.context.fill(); + + this.makePath(this.hash, this.hash % 5); + this.context.fillStyle = this.accent; + this.context.fill(); + } else if (this.type === BlockType.TWO) { + this.makePath(this.hash, this.hash % 4); + this.context.fillStyle = this.accent; + this.context.fill(); + + this.makePath(this.hash, this.hash % 3); + this.context.fillStyle = this.primary; + this.context.fill(); + } else { + this.makePath(this.hash, this.hash % 7); + this.context.fillStyle = this.accent; + this.context.fill(); + + this.makePath(this.hash, this.hash % 8); + this.context.fillStyle = this.primary; + this.context.fill(); + } + + this.resetOffset(); + } +} + +class Position { + x: number; + y: number; + constructor(x: number, y: number) { + this.x = x; + this.y = y; + } +} + +class Shape { + canvas: HTMLCanvasElement; + context: CanvasRenderingContext2D; + hash: number; + primary: string; + accent: string; + pos: Position; + scale: number; + cellSize: number; + strokeColor: string; + + constructor( + c: HTMLCanvasElement, + ctx: CanvasRenderingContext2D, + hash: number, + primary: string, + accent: string, + pos: Position, + scale: number, + cellSize: number, + strokeColor: string + ) { + this.canvas = c; + this.context = ctx; + this.hash = hash; + this.primary = primary; + this.accent = accent; + this.pos = pos; + this.scale = scale; + this.cellSize = cellSize; + this.strokeColor = strokeColor; + } + + getColor() { + return [this.primary, this.accent][Math.abs(this.hash % 2)]; + } + + makePath() { + const mod = Math.abs(this.hash + 1) % 4; + + switch(mod) { + case 0: + // square + this.context.beginPath(); + this.context.moveTo(this.pos.x, this.pos.y); + this.context.lineTo(this.pos.x + (this.cellSize / 2), this.pos.y); + this.context.lineTo(this.pos.x + (this.cellSize / 2), this.pos.y - (this.cellSize / 2)); + this.context.lineTo(this.pos.x, this.pos.y - (this.cellSize / 2)); + this.context.closePath(); + break; + case 1: + //circle + this.context.beginPath(); + this.context.arc( + this.pos.x + (this.cellSize / Math.PI) - 5, + this.pos.y - (this.cellSize / Math.PI) + 5, + this.cellSize / 3, + 0, + Math.PI * 2, + true + ); + break; + case 2: + // triangle + this.context.beginPath(); + this.context.moveTo(this.pos.x, this.pos.y); + this.context.lineTo(this.pos.x + (this.cellSize * 0.65), this.pos.y); + this.context.lineTo(this.pos.x, this.pos.y - (this.cellSize * 0.65)); + this.context.closePath(); + break; + case 3: + // oval + this.context.beginPath(); + this.context.moveTo(this.pos.x - (this.cellSize * 0.2), this.pos.y + (this.cellSize * 0.2)); + this.context.quadraticCurveTo(this.pos.x + (this.cellSize * 0.4), this.pos.y, this.pos.x + (this.cellSize * 0.5), this.pos.y - (this.cellSize * 0.5)); + this.context.moveTo(this.pos.x + (this.cellSize * 0.5), this.pos.y - (this.cellSize * 0.5)); + this.context.quadraticCurveTo(this.pos.x , this.pos.y - (this.cellSize * 0.4), this.pos.x - (this.cellSize * 0.2), this.pos.y + (this.cellSize * 0.2)); + break; + default: + //square + this.context.beginPath(); + this.context.moveTo(this.pos.x, this.pos.y); + this.context.lineTo(this.pos.x + (this.cellSize / 2), this.pos.y); + this.context.lineTo(this.pos.x + (this.cellSize / 2), this.pos.y - (this.cellSize / 2)); + this.context.lineTo(this.pos.x, this.pos.y - (this.cellSize / 2)); + this.context.closePath(); + } + } + + draw(hasStroke: boolean, strokeWeight: number) { + const color = this.getColor(); + this.context.globalCompositeOperation = "source-over"; + + this.makePath(); + this.context.fillStyle = color; + this.context.strokeStyle = this.strokeColor; + this.context.lineWidth = this.scale * ((4/5 * strokeWeight) / this.canvas.width); + this.context.lineJoin = "round"; + this.context.lineCap = "round"; + this.context.fill(); + + if (hasStroke) { + this.context.stroke(); + } + } +} + + +/** + * @name IdentiHeart + * @author Schlipak + * @copyright Apache license 2015 Guillaume de Matos + */ +export class Identiheart { + canvas: HTMLCanvasElement + context: CanvasRenderingContext2D + margin: number + crusher: Crusher + palette = [ + '#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', + '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', + '#FFEB3B', '#FFC107', '#FF9800', '#FF5722', '#795548', '#607D8B' + ]; + + primary: string = "#F44336"; + accent: string = "#E91E63"; + scale: number; + cellSize: number; + hash: number = 0; + blocks: Block[] = []; + shape?: Shape; + hasStroke: boolean = true; + strokeWeight = 500; + strokeColor = "#000000"; + compositeOperation: GlobalCompositeOperation = 'multiply'; + + constructor(c: HTMLCanvasElement, margin?: number, scale?: number, ctx?: CanvasRenderingContext2D) { + this.canvas = c; + this.context = ctx ?? c.getContext("2d")!; + this.margin = margin || 5; + this.scale = scale || 20; + this.crusher = new Crusher(); + this.cellSize = (this.canvas.width / 2) - (this.margin * this.scale); + } + + setUsername(username: string) { + this.hash = this.crusher.hash(username); + return this; + } + + setPalette(palette:string[]) { + if (palette.length < 2) { + throw new Error('Palette must be more than two elements long'); + } + + this.palette = palette; + return this; + } + + setHasStroke(b: boolean) { + this.hasStroke = b; + return this; + } + + setStrokeWeight(weight: number) { + this.strokeWeight = weight; + return this; + } + + setStrokeColor(color: string) { + this.strokeColor = color; + return this; + } + + setCompositeOperation(operation: GlobalCompositeOperation) { + this.compositeOperation = operation; + return this; + } + + setCanvas(canvas: HTMLCanvasElement) { + this.canvas = canvas; + this.context = canvas.getContext('2d')!; + return this; + } + + init() { + // Purge the block array + this.blocks = new Array