@l.x/mycelium 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,202 @@
1
+ /* CSS Custom Properties */
2
+
3
+ /* Light mode is the default */
4
+ :root {
5
+ color-scheme: light;
6
+ --font-basel: "Basel Grotesk";
7
+
8
+ /* Light mode shadows */
9
+ --shadow-short: 0px 1px 6px 2px rgba(0, 0, 0, 0.03), 0px 1px 2px 0px rgba(0, 0, 0, 0.02);
10
+ --shadow-medium: 0px 6px 12px -3px rgba(19, 19, 19, 0.04), 0px 2px 5px -2px rgba(19, 19, 19, 0.03);
11
+ --shadow-large: 0px 10px 20px -5px rgba(19, 19, 19, 0.05), 0px 4px 12px -3px rgba(19, 19, 19, 0.04);
12
+
13
+ /* shadcn compatibility */
14
+ --radius: 0.625rem;
15
+ --background: oklch(1 0 0);
16
+ --foreground: oklch(0.145 0 0);
17
+ --card: oklch(1 0 0);
18
+ --card-foreground: oklch(0.145 0 0);
19
+ --popover: oklch(1 0 0);
20
+ --popover-foreground: oklch(0.145 0 0);
21
+ --primary: oklch(0.205 0 0);
22
+ --primary-foreground: oklch(0.985 0 0);
23
+ --secondary: oklch(0.97 0 0);
24
+ --secondary-foreground: oklch(0.205 0 0);
25
+ --muted: oklch(0.97 0 0);
26
+ --muted-foreground: oklch(0.556 0 0);
27
+ --accent: oklch(0.97 0 0);
28
+ --accent-foreground: oklch(0.205 0 0);
29
+ --destructive: oklch(0.577 0.245 27.325);
30
+ --border: oklch(0.922 0 0);
31
+ --input: oklch(0.922 0 0);
32
+ --ring: oklch(0.708 0 0);
33
+ --chart-1: oklch(0.646 0.222 41.116);
34
+ --chart-2: oklch(0.6 0.118 184.704);
35
+ --chart-3: oklch(0.398 0.07 227.392);
36
+ --chart-4: oklch(0.828 0.189 84.429);
37
+ --chart-5: oklch(0.769 0.188 70.08);
38
+ --sidebar: oklch(0.985 0 0);
39
+ --sidebar-foreground: oklch(0.145 0 0);
40
+ --sidebar-primary: oklch(0.205 0 0);
41
+ --sidebar-primary-foreground: oklch(0.985 0 0);
42
+ --sidebar-accent: oklch(0.97 0 0);
43
+ --sidebar-accent-foreground: oklch(0.205 0 0);
44
+ --sidebar-border: oklch(0.922 0 0);
45
+ --sidebar-ring: oklch(0.708 0 0);
46
+
47
+ /* Mycelium semantic colors - light mode (auto-switching) */
48
+ --surface1: var(--color-surface1-light);
49
+ --surface2: var(--color-surface2-light);
50
+ --surface3: var(--color-surface3-light);
51
+ --surface4: var(--color-surface4-light);
52
+ --surface5: var(--color-surface5-light);
53
+ --neutral1: var(--color-neutral1-light);
54
+ --neutral2: var(--color-neutral2-light);
55
+ --neutral3: var(--color-neutral3-light);
56
+ --accent1: var(--color-accent1-light);
57
+ --accent2: var(--color-accent2-light);
58
+
59
+ /* Semantic status colors - auto-switching light/dark */
60
+ --success: var(--color-success-light);
61
+ --warning: var(--color-gold-200);
62
+ --critical: var(--color-critical-light);
63
+
64
+ /* Unicon avatar colors - auto-switching */
65
+ --unicon-0: var(--color-unicon-0-light);
66
+ --unicon-1: var(--color-unicon-1-light);
67
+ --unicon-2: var(--color-unicon-2-light);
68
+ --unicon-3: var(--color-unicon-3-light);
69
+ --unicon-4: var(--color-unicon-4-light);
70
+ --unicon-5: var(--color-unicon-5-light);
71
+ --unicon-6: var(--color-unicon-6-light);
72
+ --unicon-7: var(--color-unicon-7-light);
73
+ --unicon-8: var(--color-unicon-8-light);
74
+ --unicon-9: var(--color-unicon-9-light);
75
+ --unicon-bg-opacity: 0.12;
76
+ }
77
+
78
+ /* Dark mode applies when .dark class is present */
79
+ .dark {
80
+ color-scheme: dark;
81
+
82
+ /* Dark mode shadows */
83
+ --shadow-short: 0px 1px 3px 0px rgba(0, 0, 0, 0.12), 0px 1px 2px 0px rgba(0, 0, 0, 0.24);
84
+ --shadow-medium: 0px 10px 15px -3px rgba(19, 19, 19, 0.54), 0px 4px 6px -2px rgba(19, 19, 19, 0.4);
85
+ --shadow-large: 0px 16px 24px -6px rgba(0, 0, 0, 0.6), 0px 8px 12px -4px rgba(0, 0, 0, 0.48);
86
+
87
+ /* shadcn compatibility */
88
+ --background: oklch(0.145 0 0);
89
+ --foreground: oklch(0.985 0 0);
90
+ --card: oklch(0.205 0 0);
91
+ --card-foreground: oklch(0.985 0 0);
92
+ --popover: oklch(0.205 0 0);
93
+ --popover-foreground: oklch(0.985 0 0);
94
+ --primary: oklch(0.922 0 0);
95
+ --primary-foreground: oklch(0.205 0 0);
96
+ --secondary: oklch(0.269 0 0);
97
+ --secondary-foreground: oklch(0.985 0 0);
98
+ --muted: oklch(0.269 0 0);
99
+ --muted-foreground: oklch(0.708 0 0);
100
+ --accent: oklch(0.269 0 0);
101
+ --accent-foreground: oklch(0.985 0 0);
102
+ --destructive: oklch(0.704 0.191 22.216);
103
+ --border: oklch(1 0 0 / 10%);
104
+ --input: oklch(1 0 0 / 15%);
105
+ --ring: oklch(0.556 0 0);
106
+ --chart-1: oklch(0.488 0.243 264.376);
107
+ --chart-2: oklch(0.696 0.17 162.48);
108
+ --chart-3: oklch(0.769 0.188 70.08);
109
+ --chart-4: oklch(0.627 0.265 303.9);
110
+ --chart-5: oklch(0.645 0.246 16.439);
111
+ --sidebar: oklch(0.205 0 0);
112
+ --sidebar-foreground: oklch(0.985 0 0);
113
+ --sidebar-primary: oklch(0.488 0.243 264.376);
114
+ --sidebar-primary-foreground: oklch(0.985 0 0);
115
+ --sidebar-accent: oklch(0.269 0 0);
116
+ --sidebar-accent-foreground: oklch(0.985 0 0);
117
+ --sidebar-border: oklch(1 0 0 / 10%);
118
+ --sidebar-ring: oklch(0.556 0 0);
119
+
120
+ /* Mycelium semantic colors - dark mode (auto-switching) */
121
+ --surface1: var(--color-surface1-dark);
122
+ --surface2: var(--color-surface2-dark);
123
+ --surface3: var(--color-surface3-dark);
124
+ --surface4: var(--color-surface4-dark);
125
+ --surface5: var(--color-surface5-dark);
126
+ --neutral1: var(--color-neutral1-dark);
127
+ --neutral2: var(--color-neutral2-dark);
128
+ --neutral3: var(--color-neutral3-dark);
129
+ --accent1: var(--color-accent1-dark);
130
+ --accent2: var(--color-accent2-dark);
131
+ /* Status colors - dark mode */
132
+ --success: var(--color-success-dark);
133
+ --critical: var(--color-critical-dark);
134
+
135
+ /* Unicon avatar colors - dark mode */
136
+ --unicon-0: var(--color-unicon-0-dark);
137
+ --unicon-1: var(--color-unicon-1-dark);
138
+ --unicon-2: var(--color-unicon-2-dark);
139
+ --unicon-3: var(--color-unicon-3-dark);
140
+ --unicon-4: var(--color-unicon-4-dark);
141
+ --unicon-5: var(--color-unicon-5-dark);
142
+ --unicon-6: var(--color-unicon-6-dark);
143
+ --unicon-7: var(--color-unicon-7-dark);
144
+ --unicon-8: var(--color-unicon-8-dark);
145
+ --unicon-9: var(--color-unicon-9-dark);
146
+ --unicon-bg-opacity: 0.16;
147
+ }
148
+
149
+ /* shadcn theme inline block for Tailwind v4 */
150
+ @theme inline {
151
+ --radius-sm: calc(var(--radius) - 4px);
152
+ --radius-md: calc(var(--radius) - 2px);
153
+ --radius-lg: var(--radius);
154
+ --radius-xl: calc(var(--radius) + 4px);
155
+ --color-background: var(--background);
156
+ --color-foreground: var(--foreground);
157
+ --color-card: var(--card);
158
+ --color-card-foreground: var(--card-foreground);
159
+ --color-popover: var(--popover);
160
+ --color-popover-foreground: var(--popover-foreground);
161
+ --color-primary: var(--primary);
162
+ --color-primary-foreground: var(--primary-foreground);
163
+ --color-secondary: var(--secondary);
164
+ --color-secondary-foreground: var(--secondary-foreground);
165
+ --color-muted: var(--muted);
166
+ --color-muted-foreground: var(--muted-foreground);
167
+ --color-accent: var(--accent);
168
+ --color-accent-foreground: var(--accent-foreground);
169
+ --color-destructive: var(--destructive);
170
+ --color-border: var(--border);
171
+ --color-input: var(--input);
172
+ --color-ring: var(--ring);
173
+ --color-chart-1: var(--chart-1);
174
+ --color-chart-2: var(--chart-2);
175
+ --color-chart-3: var(--chart-3);
176
+ --color-chart-4: var(--chart-4);
177
+ --color-chart-5: var(--chart-5);
178
+ --color-sidebar: var(--sidebar);
179
+ --color-sidebar-foreground: var(--sidebar-foreground);
180
+ --color-sidebar-primary: var(--sidebar-primary);
181
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
182
+ --color-sidebar-accent: var(--sidebar-accent);
183
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
184
+ --color-sidebar-border: var(--sidebar-border);
185
+ --color-sidebar-ring: var(--sidebar-ring);
186
+
187
+ /* Mycelium semantic colors - auto-switching Tailwind utilities */
188
+ --color-surface1: var(--surface1);
189
+ --color-surface2: var(--surface2);
190
+ --color-surface3: var(--surface3);
191
+ --color-surface4: var(--surface4);
192
+ --color-surface5: var(--surface5);
193
+ --color-neutral1: var(--neutral1);
194
+ --color-neutral2: var(--neutral2);
195
+ --color-neutral3: var(--neutral3);
196
+ --color-accent1: var(--accent1);
197
+ --color-accent2: var(--accent2);
198
+ /* Status colors: semantic tokens reference raw palette from theme.css */
199
+ --color-success: var(--success);
200
+ --color-warning: var(--warning);
201
+ --color-critical: var(--critical);
202
+ }
Binary file
Binary file
Binary file
Binary file
package/fonts.css ADDED
@@ -0,0 +1,4 @@
1
+ /* Basel Grotesk Fonts */
2
+ /* Usage: @import "@universe/mycelium/fonts"; */
3
+
4
+ @import "./css/fonts.css";
package/package.json CHANGED
@@ -1 +1,50 @@
1
- {"name":"@l.x/mycelium","version":"1.0.2","description":"LX Exchange - mycelium","main":"index.js","dependencies":{"@luxexchange/mycelium":"1.0.2"}}
1
+ {
2
+ "name": "@l.x/mycelium",
3
+ "version": "1.0.3",
4
+ "private": false,
5
+ "main": "src/index.ts",
6
+ "sideEffects": [
7
+ "*.css"
8
+ ],
9
+ "exports": {
10
+ ".": "./src/index.ts",
11
+ "./tailwind": "./tailwind.css",
12
+ "./fonts": "./fonts.css",
13
+ "./cn": {
14
+ "types": "./src/cn.d.ts",
15
+ "default": "./src/index.ts"
16
+ }
17
+ },
18
+ "dependencies": {
19
+ "clsx": "2.1.1",
20
+ "tailwind-merge": "3.3.1"
21
+ },
22
+ "peerDependencies": {
23
+ "react": ">=18.0.0",
24
+ "tailwindcss": ">=4.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "22.13.1",
28
+ "@typescript/native-preview": "7.0.0-dev.20260311.1",
29
+ "@luxfi/eslint-config": "workspace:^",
30
+ "depcheck": "1.4.7",
31
+ "eslint": "8.57.1",
32
+ "react": "19.0.3",
33
+ "tailwindcss": "4.1.16",
34
+ "typescript": "5.8.3"
35
+ },
36
+ "scripts": {
37
+ "typecheck": "nx typecheck @l.x/mycelium",
38
+ "typecheck:tsgo": "nx typecheck:tsgo @l.x/mycelium",
39
+ "lint": "nx lint @l.x/mycelium",
40
+ "lint:fix": "nx lint:fix @l.x/mycelium",
41
+ "lint:biome": "nx lint:biome @l.x/mycelium",
42
+ "lint:biome:fix": "nx lint:biome:fix @l.x/mycelium",
43
+ "lint:eslint": "nx lint:eslint @l.x/mycelium",
44
+ "lint:eslint:fix": "nx lint:eslint:fix @l.x/mycelium",
45
+ "check:deps:usage": "nx check:deps:usage @l.x/mycelium"
46
+ },
47
+ "nx": {
48
+ "includedScripts": []
49
+ }
50
+ }
package/project.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@l.x/mycelium",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "pkgs/mycelium/src",
5
+ "projectType": "library",
6
+ "tags": ["scope:mycelium", "type:lib"],
7
+ "targets": {
8
+ "typecheck": {},
9
+ "typecheck:tsgo": {},
10
+ "lint:biome": {},
11
+ "lint:biome:fix": {},
12
+ "lint:eslint": {},
13
+ "lint:eslint:fix": {},
14
+ "lint": {},
15
+ "lint:fix": {},
16
+ "check:deps:usage": {}
17
+ }
18
+ }
package/src/cn.ts ADDED
@@ -0,0 +1,75 @@
1
+ import { type ClassValue, clsx } from 'clsx'
2
+ import { extendTailwindMerge } from 'tailwind-merge'
3
+ import { typographyClasses } from './types'
4
+
5
+ /** Custom color classes for tailwind-merge conflict resolution */
6
+ const colorClasses = [
7
+ 'foreground',
8
+ 'background',
9
+ 'neutral1',
10
+ 'neutral1-light',
11
+ 'neutral1-dark',
12
+ 'neutral2',
13
+ 'neutral2-light',
14
+ 'neutral2-dark',
15
+ 'neutral3',
16
+ 'neutral3-light',
17
+ 'neutral3-dark',
18
+ 'surface1',
19
+ 'surface1-light',
20
+ 'surface1-dark',
21
+ 'surface2',
22
+ 'surface2-light',
23
+ 'surface2-dark',
24
+ 'surface3',
25
+ 'surface3-light',
26
+ 'surface3-dark',
27
+ 'surface4',
28
+ 'surface4-light',
29
+ 'surface4-dark',
30
+ 'surface5',
31
+ 'surface5-light',
32
+ 'surface5-dark',
33
+ 'accent1',
34
+ 'accent1-light',
35
+ 'accent1-dark',
36
+ 'accent2',
37
+ 'accent2-light',
38
+ 'accent2-dark',
39
+ 'success',
40
+ 'warning',
41
+ 'critical',
42
+ 'destructive',
43
+ 'muted-foreground',
44
+ 'card-foreground',
45
+ 'popover-foreground',
46
+ 'primary-foreground',
47
+ 'secondary-foreground',
48
+ 'destructive-foreground',
49
+ ] as const
50
+
51
+ /**
52
+ * Extended tailwind-merge configuration for Mycelium classes.
53
+ * - Typography: ensures cn('text-sm', 'text-body-1') correctly resolves to 'text-body-1'
54
+ * - Colors: ensures cn('text-foreground', 'text-critical') correctly resolves to 'text-critical'
55
+ */
56
+ const customTwMerge = extendTailwindMerge({
57
+ extend: {
58
+ classGroups: {
59
+ 'font-size': [...typographyClasses],
60
+ 'text-color': colorClasses.map((c) => `text-${c}`),
61
+ },
62
+ },
63
+ })
64
+
65
+ /**
66
+ * Merge class names with Tailwind CSS conflict resolution.
67
+ *
68
+ * @example
69
+ * cn('text-sm', 'text-body-1') // => 'text-body-1'
70
+ * cn('bg-red-500', isActive && 'bg-blue-500') // => 'bg-blue-500' if isActive
71
+ * cn('p-4', className) // Merge with external className prop
72
+ */
73
+ export function cn(...inputs: ClassValue[]): string {
74
+ return customTwMerge(clsx(inputs))
75
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ // Mycelium - Lx's Tailwind Design System
2
+ // Main JS exports
3
+
4
+ export { cn } from './cn'
5
+ export * from './types'
6
+ export { COLOR_COUNT, UNICON_COLORS } from './unicon/colors'
7
+ export { hashString } from './unicon/hash'
8
+ export { Unicon, type UniconProps } from './unicon/Unicon'
package/src/types.ts ADDED
@@ -0,0 +1,49 @@
1
+ /** Typography class names for tailwind-merge conflict resolution */
2
+ export const typographyClasses = [
3
+ 'text-body-1',
4
+ 'text-body-2',
5
+ 'text-body-3',
6
+ 'text-body-4',
7
+ 'text-heading-1',
8
+ 'text-heading-2',
9
+ 'text-heading-3',
10
+ 'text-subheading-1',
11
+ 'text-subheading-2',
12
+ 'text-button-1',
13
+ 'text-button-2',
14
+ 'text-button-3',
15
+ 'text-button-4',
16
+ ] as const
17
+
18
+ /** Mycelium typography class type */
19
+ export type TypographyClass = (typeof typographyClasses)[number]
20
+
21
+ /** Mycelium color tokens */
22
+ export type ColorToken =
23
+ | 'white'
24
+ | 'black'
25
+ | 'background'
26
+ | 'neutral1'
27
+ | 'neutral2'
28
+ | 'neutral3'
29
+ | 'surface1'
30
+ | 'surface2'
31
+ | 'surface3'
32
+ | 'surface4'
33
+ | 'surface5'
34
+ | 'accent1'
35
+ | 'accent2'
36
+ | 'accent3'
37
+ | 'accent4'
38
+ | 'success'
39
+ | 'critical'
40
+ | 'warning'
41
+
42
+ /** Mycelium screen breakpoints */
43
+ export type Breakpoint = 'xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'xxxl' | 'h-short' | 'h-mid'
44
+
45
+ /** Mycelium border radius tokens */
46
+ export type BorderRadius = 'none' | '4' | '6' | '8' | '12' | '16' | '20' | '24' | '28' | '32' | 'full'
47
+
48
+ /** Mycelium box shadow tokens */
49
+ export type BoxShadow = 'short' | 'medium' | 'large'
@@ -0,0 +1,84 @@
1
+ import React, { useMemo } from 'react'
2
+
3
+ import { cn } from '../cn'
4
+ import { COLOR_COUNT } from './colors'
5
+ import { hashString } from './hash'
6
+ import { type IconPaths, Icons } from './icons'
7
+
8
+ export interface UniconProps {
9
+ /** Any string for deterministic avatar generation */
10
+ input: string
11
+ /** Size in pixels (default: 32) */
12
+ size?: number
13
+ /** Additional CSS classes */
14
+ className?: string
15
+ /** Custom icon to render instead of the default generated icon. Will be colored with the computed unicon color. */
16
+ icon?: React.ReactNode
17
+ /** When true, removes the background circle and scales the shape to fill the full container. */
18
+ bare?: boolean
19
+ }
20
+
21
+ /**
22
+ * Deterministic avatar component that generates a unique visual identity
23
+ * based on any input string (e.g., wallet address, username, email).
24
+ *
25
+ * Colors automatically adapt to light/dark mode via CSS variables.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <Unicon input="0x1234..." size={48} />
30
+ * <Unicon input="user@example.com" size={32} className="border border-neutral1" />
31
+ * ```
32
+ */
33
+ export function Unicon({ input, size = 32, className, icon, bare }: UniconProps): React.ReactElement {
34
+ const { colorVar, paths } = useMemo(() => {
35
+ const hash = hashString(input)
36
+ const iconKeys = Object.keys(Icons)
37
+
38
+ // Use BigInt modulo for both to avoid precision loss when converting to Number
39
+ const colorIndex = Number(hash % BigInt(COLOR_COUNT))
40
+ const iconIndex = Number(hash % BigInt(iconKeys.length))
41
+
42
+ return {
43
+ colorVar: `var(--unicon-${colorIndex})`,
44
+ paths: Icons[iconKeys[iconIndex] as keyof typeof Icons] as IconPaths,
45
+ }
46
+ }, [input])
47
+
48
+ const scale = bare ? size / 48 : (size / 48 / 1.5) * 0.9
49
+ const scaledSize = 48 * scale
50
+ const translate = (size - scaledSize) / 2
51
+
52
+ // For custom icons, center them at ~40% of the container size
53
+ const iconSize = Math.round(size * 0.4)
54
+ const iconOffset = (size - iconSize) / 2
55
+
56
+ return (
57
+ <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className={cn('shrink-0', className)}>
58
+ {!bare && <circle cx={size / 2} cy={size / 2} r={size / 2} fill={colorVar} opacity="var(--unicon-bg-opacity)" />}
59
+ {icon ? (
60
+ <foreignObject x={iconOffset} y={iconOffset} width={iconSize} height={iconSize}>
61
+ {/* biome-ignore lint/correctness/noRestrictedElements: div required inside SVG foreignObject */}
62
+ <div
63
+ style={{
64
+ width: '100%',
65
+ height: '100%',
66
+ display: 'flex',
67
+ alignItems: 'center',
68
+ justifyContent: 'center',
69
+ color: colorVar,
70
+ }}
71
+ >
72
+ {icon}
73
+ </div>
74
+ </foreignObject>
75
+ ) : (
76
+ <g transform={`translate(${translate}, ${translate}) scale(${scale})`}>
77
+ {paths.map((d, i) => (
78
+ <path key={i} d={d} fill={colorVar} clipRule="evenodd" fillRule="evenodd" />
79
+ ))}
80
+ </g>
81
+ )}
82
+ </svg>
83
+ )
84
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Unicon color definitions
3
+ * Light and dark mode color pairs for avatar generation
4
+ */
5
+
6
+ export const UNICON_COLORS = {
7
+ light: [
8
+ '#F50DB4', // 0 - pink
9
+ '#FFBF17', // 1 - yellow
10
+ '#FF8934', // 2 - orange
11
+ '#85754A', // 3 - brown
12
+ '#0C8911', // 4 - green
13
+ '#78E744', // 5 - lime
14
+ '#00C3A0', // 6 - teal
15
+ '#23A3FF', // 7 - blue
16
+ '#4981FF', // 8 - indigo
17
+ '#4300B0', // 9 - purple
18
+ ],
19
+ dark: [
20
+ '#FC74FE', // 0 - pink
21
+ '#FFF612', // 1 - yellow
22
+ '#FF4D00', // 2 - orange
23
+ '#996F01', // 3 - brown
24
+ '#21C95E', // 4 - green
25
+ '#B1F13C', // 5 - lime
26
+ '#5CFE9D', // 6 - teal
27
+ '#3ADCFF', // 7 - blue
28
+ '#0047FF', // 8 - indigo
29
+ '#9E62FF', // 9 - purple
30
+ ],
31
+ } as const
32
+
33
+ export const COLOR_COUNT = 10
@@ -0,0 +1,20 @@
1
+ // biome-ignore-all lint/suspicious/noBitwiseOperators: Hash function requires bitwise ops
2
+
3
+ /**
4
+ * cyrb53 - fast, well-distributed 53-bit hash
5
+ * No dependencies, excellent distribution for avatar generation
6
+ */
7
+ export function hashString(str: string, seed = 0): bigint {
8
+ let h1 = 0xdeadbeef ^ seed
9
+ let h2 = 0x41c6ce57 ^ seed
10
+ for (let i = 0; i < str.length; i++) {
11
+ const ch = str.charCodeAt(i)
12
+ h1 = Math.imul(h1 ^ ch, 2654435761)
13
+ h2 = Math.imul(h2 ^ ch, 1597334677)
14
+ }
15
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507)
16
+ h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909)
17
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507)
18
+ h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909)
19
+ return BigInt(h2 >>> 0) * BigInt(0x100000000) + BigInt(h1 >>> 0)
20
+ }