@agent-facets/brand 0.1.0

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.
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@agent-facets/brand",
3
+ "repository": {
4
+ "type": "git",
5
+ "url": "https://github.com/ex-machina-co/facets",
6
+ "directory": "packages/brand"
7
+ },
8
+ "version": "0.1.0",
9
+ "type": "module",
10
+ "exports": {
11
+ ".": "./src/index.ts"
12
+ },
13
+ "scripts": {
14
+ "types": "tsc --noEmit",
15
+ "test": "bun test"
16
+ },
17
+ "devDependencies": {
18
+ "@types/bun": "1.3.10"
19
+ },
20
+ "peerDependencies": {
21
+ "typescript": "^5"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public",
25
+ "provenance": false
26
+ }
27
+ }
@@ -0,0 +1,23 @@
1
+ import { expect, test } from 'bun:test'
2
+ import { BRAND, BRIGHTNESS, hexToRgb, PALETTE } from '../colors.ts'
3
+
4
+ test('hexToRgb parses brand purple', () => {
5
+ expect(hexToRgb(BRAND.purple)).toEqual([167, 139, 250])
6
+ })
7
+
8
+ test('hexToRgb parses brand green', () => {
9
+ expect(hexToRgb(BRAND.green)).toEqual([66, 205, 170])
10
+ })
11
+
12
+ test('hexToRgb parses brand coral', () => {
13
+ expect(hexToRgb(BRAND.coral)).toEqual([250, 139, 141])
14
+ })
15
+
16
+ test('PALETTE violet400 matches BRAND purple', () => {
17
+ expect(PALETTE.violet400).toBe(BRAND.purple)
18
+ })
19
+
20
+ test('BRIGHTNESS has dim and dimmer values', () => {
21
+ expect(BRIGHTNESS.dim).toBe('#666666')
22
+ expect(BRIGHTNESS.dimmer).toBe('#999999')
23
+ })
package/src/colors.ts ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Core brand colors — derived from the facet.svg icon.
3
+ */
4
+ export const BRAND = {
5
+ purple: '#A78BFA',
6
+ green: '#42CDAA',
7
+ coral: '#FA8B8D',
8
+ dark: '#1F1F28',
9
+ } as const
10
+
11
+ /**
12
+ * Extended palette — includes docs/marketing variants.
13
+ */
14
+ export const PALETTE = {
15
+ violet400: '#A78BFA',
16
+ violet700: '#6D28D9',
17
+ violet900: '#4C1D95',
18
+ green: '#42CDAA',
19
+ coral: '#FA8B8D',
20
+ dark: '#1F1F28',
21
+ } as const
22
+
23
+ /**
24
+ * Text brightness scale.
25
+ */
26
+ export const BRIGHTNESS = {
27
+ dim: '#666666',
28
+ dimmer: '#999999',
29
+ } as const
30
+
31
+ /**
32
+ * Parse a hex color string into an RGB tuple.
33
+ */
34
+ export function hexToRgb(hex: string): readonly [number, number, number] {
35
+ const h = hex.replace('#', '')
36
+ return [parseInt(h.substring(0, 2), 16), parseInt(h.substring(2, 4), 16), parseInt(h.substring(4, 6), 16)] as const
37
+ }
@@ -0,0 +1,48 @@
1
+ import { THEME } from './theme.ts'
2
+
3
+ /** Fixed number of gradient stops — ensures consistent animation speed regardless of text length */
4
+ const GRADIENT_STOP_COUNT = 32
5
+
6
+ function toHex(r: number, g: number, b: number): string {
7
+ return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`
8
+ }
9
+
10
+ function lerpRgb(
11
+ a: readonly [number, number, number],
12
+ b: readonly [number, number, number],
13
+ t: number,
14
+ ): [number, number, number] {
15
+ return [
16
+ Math.round(a[0] + (b[0] - a[0]) * t),
17
+ Math.round(a[1] + (b[1] - a[1]) * t),
18
+ Math.round(a[2] + (b[2] - a[2]) * t),
19
+ ]
20
+ }
21
+
22
+ /** Pre-computed gradient loop with fixed stop count for consistent animation */
23
+ function generateGradientLoop(): string[] {
24
+ const [colorA, colorB, colorC] = THEME.gradientRGB
25
+ const colors: string[] = []
26
+ for (let i = 0; i < GRADIENT_STOP_COUNT; i++) {
27
+ const segment = ((i / GRADIENT_STOP_COUNT) * 3) % 3
28
+ let rgb: [number, number, number]
29
+ if (segment < 1) {
30
+ rgb = lerpRgb(colorA, colorB, segment)
31
+ } else if (segment < 2) {
32
+ rgb = lerpRgb(colorB, colorC, segment - 1)
33
+ } else {
34
+ rgb = lerpRgb(colorC, colorA, segment - 2)
35
+ }
36
+ colors.push(toHex(...rgb))
37
+ }
38
+ return colors
39
+ }
40
+
41
+ /** Pre-computed stops — shared by all animated gradients */
42
+ export const GRADIENT_STOPS = generateGradientLoop()
43
+
44
+ /** Get the gradient colors at a given offset for animation */
45
+ export function getAnimatedGradient(offset: number): string[] {
46
+ const i = offset % GRADIENT_STOPS.length
47
+ return [...GRADIENT_STOPS.slice(i), ...GRADIENT_STOPS.slice(0, i)]
48
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { BRAND, BRIGHTNESS, hexToRgb, PALETTE } from './colors.ts'
2
+ export { GRADIENT_STOPS, getAnimatedGradient } from './gradient.ts'
3
+ export { THEME } from './theme.ts'
package/src/theme.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { BRAND, BRIGHTNESS, hexToRgb } from './colors.ts'
2
+
3
+ /**
4
+ * Semantic theme — all values are references to brand constants.
5
+ * Consumers should use THEME, not the raw color constants.
6
+ */
7
+ export const THEME = {
8
+ // Brand hierarchy
9
+ primary: BRAND.purple,
10
+ secondary: BRAND.green,
11
+ tertiary: BRAND.coral,
12
+ brand: BRAND.purple,
13
+ dark: BRAND.dark,
14
+
15
+ // Semantic aliases (can diverge from brand later)
16
+ success: BRAND.green,
17
+ warning: BRAND.coral,
18
+
19
+ // Text — structural
20
+ hint: BRIGHTNESS.dim,
21
+ keyword: BRIGHTNESS.dimmer,
22
+
23
+ // Three-color gradient: purple → teal → coral
24
+ gradient: [BRAND.purple, BRAND.green, BRAND.coral] as const,
25
+ gradientRGB: [hexToRgb(BRAND.purple), hexToRgb(BRAND.green), hexToRgb(BRAND.coral)] as const,
26
+ } as const
package/tsconfig.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "include": ["src"]
4
+ }