feat: add auto-generated pseudonyms and identicon avatars for users
This commit is contained in:
80
js/services/identity.js
Normal file
80
js/services/identity.js
Normal file
@@ -0,0 +1,80 @@
|
||||
const ADJECTIVES = [
|
||||
'swift','calm','bold','keen','wise','warm','bright','silent','gentle','steady',
|
||||
'clever','vivid','nimble','serene','brave','quick','subtle','sharp','lucid','agile',
|
||||
'noble','witty','mellow','fierce','stout','deft','proud','fair','grand','cool'
|
||||
]
|
||||
|
||||
const ANIMALS = [
|
||||
'fox','owl','wolf','bear','lynx','hare','deer','hawk','otter','raven',
|
||||
'crane','puma','seal','finch','cobra','bison','ibex','oriole','quail','gecko',
|
||||
'panda','tiger','eagle','badger','moose','robin','coral','falcon','heron','viper'
|
||||
]
|
||||
|
||||
function hashToBytes(input) {
|
||||
const hex = input.replace(/[^0-9a-fA-F]/g, '')
|
||||
const bytes = []
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
bytes.push(parseInt(hex.substring(i, i + 2), 16))
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
export function generatePseudonym(hash) {
|
||||
if (!hash || hash.length < 8) return 'Unknown'
|
||||
const bytes = hashToBytes(hash)
|
||||
const adj = ADJECTIVES[bytes[0] % ADJECTIVES.length]
|
||||
const animal = ANIMALS[bytes[1] % ANIMALS.length]
|
||||
const num = ((bytes[2] << 8) | bytes[3]) % 100
|
||||
const capitalAdj = adj.charAt(0).toUpperCase() + adj.slice(1)
|
||||
const capitalAnimal = animal.charAt(0).toUpperCase() + animal.slice(1)
|
||||
return `${capitalAdj}${capitalAnimal}${num}`
|
||||
}
|
||||
|
||||
export function generateAvatar(hash, size = 64) {
|
||||
if (!hash || hash.length < 16) {
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" rx="32" fill="#A8A29E"/>
|
||||
<text x="32" y="40" text-anchor="middle" font-family="system-ui" font-size="24" fill="#fff">?</text>
|
||||
</svg>`
|
||||
}
|
||||
|
||||
const bytes = hashToBytes(hash)
|
||||
|
||||
const hues = [
|
||||
174, // teal (brand)
|
||||
210, // blue
|
||||
260, // purple
|
||||
330, // pink
|
||||
25, // orange
|
||||
45, // gold
|
||||
140, // green
|
||||
190, // cyan
|
||||
]
|
||||
const hue = hues[bytes[4] % hues.length]
|
||||
const bg = `hsl(${hue}, 45%, 45%)`
|
||||
const fg = `hsl(${hue}, 30%, 85%)`
|
||||
|
||||
const grid = 5
|
||||
const cellSize = 64 / grid
|
||||
let cells = ''
|
||||
|
||||
for (let y = 0; y < grid; y++) {
|
||||
for (let x = 0; x < Math.ceil(grid / 2); x++) {
|
||||
const byteIndex = (y * 3 + x + 5) % bytes.length
|
||||
if (bytes[byteIndex] % 2 === 0) {
|
||||
const rx = x * cellSize
|
||||
const ry = y * cellSize
|
||||
cells += `<rect x="${rx}" y="${ry}" width="${cellSize}" height="${cellSize}" fill="${fg}"/>`
|
||||
const mirrorX = (grid - 1 - x) * cellSize
|
||||
if (mirrorX !== rx) {
|
||||
cells += `<rect x="${mirrorX}" y="${ry}" width="${cellSize}" height="${cellSize}" fill="${fg}"/>`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" rx="32" fill="${bg}"/>
|
||||
${cells}
|
||||
</svg>`
|
||||
}
|
||||
Reference in New Issue
Block a user