@hanzogui/logo 8.1.1 → 8.2.1

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/src/LogoWords.tsx CHANGED
@@ -2,30 +2,29 @@ import type { LayoutValue } from '@hanzogui/use-element-layout'
2
2
  import React, { useEffect, useState } from 'react'
3
3
  import type { XStackProps } from '@hanzo/gui'
4
4
  import { Circle, XStack } from '@hanzo/gui'
5
+ import { WORDMARKS } from '@hanzo/logo/wordmarks'
5
6
  import { useTint } from './useTint'
6
7
 
7
- // T A M A G U I
8
- // default: white white white white red green blue
9
- // hover: yellow yellow yellow color12 red green blue
10
- const defaultColors = [
11
- 'var(--color12)',
12
- 'var(--color12)',
13
- 'var(--color12)',
14
- 'var(--color12)',
15
- 'var(--red9)',
16
- 'var(--green9)',
17
- 'var(--blue9)',
18
- ]
19
-
20
- const hoveredColors = [
21
- 'var(--yellow9)',
22
- 'var(--yellow9)',
23
- 'var(--yellow9)',
24
- 'var(--color12)',
25
- 'var(--red9)',
26
- 'var(--green9)',
27
- 'var(--blue9)',
28
- ]
8
+ /**
9
+ * The wordmark.
10
+ *
11
+ * This drew T-A-M-A-G-U-I: seven pixel-art letter polygons on a 373x41 box,
12
+ * each tinted from a seven-colour table, with the hover strip split into seven
13
+ * sections and the dot stepped by a constant fitted to those letters. The fork
14
+ * renamed the package and kept the artwork, so the site's own name was another
15
+ * company's.
16
+ *
17
+ * It renders the outlined Hanzo wordmark from @hanzo/logo now — real paths cut
18
+ * from Zen, so it needs no font loaded and cannot drift from the wordmark every
19
+ * other Hanzo surface uses.
20
+ *
21
+ * The tint interaction stays, because it is a feature rather than a piece of
22
+ * the old wordmark — but it is no longer DIMENSIONED to seven letters. The
23
+ * strip divides by however many tints there are, and the dot rides the same
24
+ * fraction, so the two agree whatever that number becomes.
25
+ */
26
+ const WORDMARK = WORDMARKS['Hanzo']
27
+ const [WM_X, WM_Y, WM_W, WM_H] = WORDMARK.box
29
28
 
30
29
  export const LogoWords: React.MemoExoticComponent<
31
30
  ({
@@ -42,6 +41,7 @@ export const LogoWords: React.MemoExoticComponent<
42
41
  const [mounted, setMounted] = React.useState<'start' | 'animate' | 'done'>('start')
43
42
 
44
43
  const { tintIndex: index } = Tint
44
+ const sections = Tint.tints.length
45
45
 
46
46
  useEffect(() => {
47
47
  const idle = window.requestIdleCallback || setTimeout
@@ -58,9 +58,9 @@ export const LogoWords: React.MemoExoticComponent<
58
58
 
59
59
  useEffect(() => {
60
60
  const handleKeyDown = (event: KeyboardEvent) => {
61
- const index = Number.parseInt(event.key, 10) - 1 // Convert key to index (0-based)
62
- if (!Number.isNaN(index) && index >= 0 && index < Tint.tints.length) {
63
- Tint.setTintIndex(index)
61
+ const i = Number.parseInt(event.key, 10) - 1 // Convert key to index (0-based)
62
+ if (!Number.isNaN(i) && i >= 0 && i < Tint.tints.length) {
63
+ Tint.setTintIndex(i)
64
64
  }
65
65
  }
66
66
 
@@ -71,16 +71,16 @@ export const LogoWords: React.MemoExoticComponent<
71
71
  }
72
72
  }, [Tint])
73
73
 
74
- const getColor = (i: number) => {
75
- if (hovered) return `var(--${Tint.tints[i]}9)`
76
- return defaultColors[i]
77
- }
74
+ const [layout, setLayout] = useState<LayoutValue>()
78
75
 
79
- const x = Math.round(
80
- index * 18.5 + (18 / 2) * (index / defaultColors.length) + 3 + (index === 6 ? -3 : 0)
81
- )
76
+ // The mark is one ink; the tint shows through the dot rather than by
77
+ // recolouring letters, which is what the seven-colour table did.
78
+ const fill = hovered ? `var(--${Tint.tints[index]}9)` : 'var(--color12)'
82
79
 
83
- const [layout, setLayout] = useState<LayoutValue>()
80
+ const width = 373 * (1 / downscale) * 0.3333333
81
+ const height = (width * WM_H) / WM_W
82
+ // the dot sits over the section the current tint owns
83
+ const dotX = Math.round((width / sections) * (index + 0.5))
84
84
 
85
85
  return (
86
86
  <XStack
@@ -98,10 +98,8 @@ export const LogoWords: React.MemoExoticComponent<
98
98
  onMouseMove={(e: MouseEvent) => {
99
99
  if (!layout) return
100
100
  const x = e.clientX - layout.pageX
101
- // Total width divided into 7 sections (one for each letter)
102
- const sectionWidth = layout.width / 7
103
- // Calculate which section we're in (0-6)
104
- const section = Math.min(6, Math.floor(x / sectionWidth))
101
+ const sectionWidth = layout.width / sections
102
+ const section = Math.min(sections - 1, Math.max(0, Math.floor(x / sectionWidth)))
105
103
  Tint.setTintIndex(section)
106
104
  }}
107
105
  {...props}
@@ -113,8 +111,7 @@ export const LogoWords: React.MemoExoticComponent<
113
111
  top={0}
114
112
  left={0}
115
113
  y={mounted === 'start' ? -30 : -4}
116
- // the last i is less wide
117
- x={x}
114
+ x={dotX}
118
115
  size={4}
119
116
  backgroundColor="$color12"
120
117
  />
@@ -122,64 +119,15 @@ export const LogoWords: React.MemoExoticComponent<
122
119
 
123
120
  <svg
124
121
  data-tauri-drag-region
125
- width={373 * (1 / downscale) * 0.3333333}
126
- height={41 * (1 / downscale) * 0.3333333}
127
- viewBox="0 0 373 41"
122
+ role="img"
123
+ aria-label="Hanzo"
124
+ width={width}
125
+ height={height}
126
+ viewBox={`${WM_X} ${-WM_Y - WM_H} ${WM_W} ${WM_H}`}
128
127
  >
129
- <polygon
130
- data-tauri-drag-region
131
- shapeRendering="crispEdges"
132
- fill={getColor(0)}
133
- points="24.3870968 40.1612903 24.3870968 8.67741935 32.2580645 8.67741935 32.2580645 0.806451613 0.774193548 0.806451613 0.774193548 8.67741935 8.64516129 8.67741935 8.64516129 40.1612903"
134
- // onMouseEnter={() => Tint.setTintIndex(0)}
135
- />
136
-
137
- <path
138
- shapeRendering="crispEdges"
139
- fill={getColor(1)}
140
- d="M87.3548387,0.806451613 L87.3548387,8.67741935 L95.2258065,8.67741935 L95.2258065,40.1612903 L79.483871,40.1612903 L79.483871,24.4193548 L71.6129032,24.4193548 L71.6129032,40.1612903 L55.8709677,40.1612903 L55.8709677,8.67741935 L63.7419355,8.67741935 L63.7419355,0.806451613 L87.3548387,0.806451613 Z M79.483871,8.67741935 L71.6129032,8.67741935 L71.6129032,16.5483871 L79.483871,16.5483871 L79.483871,8.67741935 Z"
141
- fillRule="nonzero"
142
- // onMouseEnter={() => Tint.setTintIndex(1)}
143
- />
144
-
145
- <polygon
146
- data-tauri-drag-region
147
- shapeRendering="crispEdges"
148
- fill={getColor(2)}
149
- points="130.645161 40.1612903 130.645161 22.4516129 138.516129 22.4516129 138.516129 40.1612903 154.258065 40.1612903 154.258065 0.806451613 142.451613 0.806451613 142.451613 8.67741935 126.709677 8.67741935 126.709677 0.806451613 114.903226 0.806451613 114.903226 40.1612903"
150
- // onMouseEnter={() => Tint.setTintIndex(2)}
151
- />
152
-
153
- <path
154
- fill={getColor(3)}
155
- d="M205.419355,0.806451613 L205.419355,8.67741935 L213.290323,8.67741935 L213.290323,40.1612903 L197.548387,40.1612903 L197.548387,24.4193548 L189.677419,24.4193548 L189.677419,40.1612903 L173.935484,40.1612903 L173.935484,8.67741935 L181.806452,8.67741935 L181.806452,0.806451613 L205.419355,0.806451613 Z M197.548387,8.67741935 L189.677419,8.67741935 L189.677419,16.5483871 L197.548387,16.5483871 L197.548387,8.67741935 Z"
156
- fillRule="nonzero"
157
- // onMouseEnter={() => Tint.setTintIndex(3)}
158
- />
159
-
160
- <polygon
161
- data-tauri-drag-region
162
- shapeRendering="crispEdges"
163
- fill={getColor(4)}
164
- points="264.451613 40.1612903 264.451613 32.2903226 272.322581 32.2903226 272.322581 16.5483871 256.580645 16.5483871 256.580645 32.2903226 248.709677 32.2903226 248.709677 8.67741935 272.322581 8.67741935 272.322581 0.806451613 240.83871 0.806451613 240.83871 8.67741935 232.967742 8.67741935 232.967742 32.2903226 240.83871 32.2903226 240.83871 40.1612903"
165
- // onMouseEnter={() => Tint.setTintIndex(4)}
166
- />
167
-
168
- <polygon
169
- data-tauri-drag-region
170
- shapeRendering="crispEdges"
171
- fill={getColor(5)}
172
- points="323.483871 40.1612903 323.483871 32.2903226 331.354839 32.2903226 331.354839 0.806451613 315.612903 0.806451613 315.612903 32.2903226 307.741935 32.2903226 307.741935 0.806451613 292 0.806451613 292 32.2903226 299.870968 32.2903226 299.870968 40.1612903"
173
- // onMouseEnter={() => Tint.setTintIndex(5)}
174
- />
175
-
176
- <polygon
177
- data-tauri-drag-region
178
- shapeRendering="crispEdges"
179
- fill={getColor(6)}
180
- points="372.677419 40.1612903 372.677419 0.806451613 356.935484 0.806451613 356.935484 40.1612903"
181
- // onMouseEnter={() => Tint.setTintIndex(6)}
182
- />
128
+ <g transform="scale(1,-1)">
129
+ <path d={WORDMARK.d} fill={fill} />
130
+ </g>
183
131
  </svg>
184
132
  </XStack>
185
133
  )
package/src/tints.tsx CHANGED
@@ -5,8 +5,10 @@ type ChangeHandler = (next: TintFamily) => void
5
5
 
6
6
  const listeners = new Set<ChangeHandler>()
7
7
 
8
- // T A M A G U I
9
- // Maps to logo letters - index 3 (A) is the "none" theme position
8
+ // A seasonal ramp of tint stops. These used to be one entry per letter of the
9
+ // old seven-letter wordmark, with index 3 reserved as the neutral notch; the
10
+ // wordmark is Hanzo now and every reader indexes by `tints.length`, so the
11
+ // length is just how many stops the ramp has.
10
12
  const familiesValues = {
11
13
  hanzogui: ['yellow', 'yellow', 'yellow', 'gray', 'red', 'green', 'blue'] as ThemeName[],
12
14
  xmas: ['red', 'green', 'red', 'green', 'red', 'green', 'red'] as ThemeName[],
@@ -2,6 +2,17 @@ import type { JSX } from "react/jsx-runtime";
2
2
  type GuiIconSvgProps = React.SVGProps<SVGSVGElement> & {
3
3
  color?: string;
4
4
  };
5
+ /**
6
+ * The Hanzo mark.
7
+ *
8
+ * This drew a duck — `<g id="duck">`, pixel art, `#ECD20A` — because the fork
9
+ * renamed the package and left the artwork. It was the mark in this site's
10
+ * header, footer, login, 404, every doc page and its favicon.
11
+ *
12
+ * The geometry comes from `@hanzo/logo`, never retyped: MARK_BLOCKS is the five
13
+ * body blocks of the block-H on its own 67 viewBox. `currentColor` by default so
14
+ * the mark themes with the text around it rather than pinning a brand hex.
15
+ */
5
16
  export declare const GuiIconSvg: ({ color, ...props }: GuiIconSvgProps) => JSX.Element;
6
17
  export {};
7
18
 
@@ -1,11 +1,11 @@
1
1
  {
2
- "mappings": "AAAA,cAAc,WAAW;KAEpB,kBAAkB,MAAM,SAAS,iBAAiB;CACrD;;AAGF,OAAO,cAAM,aAAc,EACzB,OACA,GAAG,SACF,oBAAkB,IAAI",
2
+ "mappings": "AAAA,cAAc,WAAW;KAGpB,kBAAkB,MAAM,SAAS,iBAAiB;CACrD;;;;;;;;;;;;;AAcF,OAAO,cAAM,aAAc,EACzB,OACA,GAAG,SACF,oBAAkB,IAAI",
3
3
  "names": [],
4
4
  "sources": [
5
5
  "src/GuiLogoSvg.tsx"
6
6
  ],
7
7
  "version": 3,
8
8
  "sourcesContent": [
9
- "import type { JSX } from 'react/jsx-runtime'\n\ntype GuiIconSvgProps = React.SVGProps<SVGSVGElement> & {\n color?: string\n}\n\nexport const GuiIconSvg = ({\n color = '#ECD20A',\n ...props\n}: GuiIconSvgProps): JSX.Element => (\n <svg width=\"160px\" height=\"160px\" viewBox=\"0 0 160 160\" {...props}>\n <g id=\"duck\" stroke=\"none\" strokeWidth=\"1\" fill=\"none\" fillRule=\"evenodd\">\n <circle id=\"Oval\" cx=\"80\" cy=\"80\" r=\"145\"></circle>\n <g id=\"Group-Copy-5\">\n <path\n d=\"M10,110 L10,100 L20,100 L20,90 L30,90 L30,80 L40,80 L40,30 L50,30 L50,10 L70,10 L70,0 L120,0 L120,10 L130,10 L130,20 L140,20 L140,40 L160,40 L160,50 L150,50 L150,60 L160,60 L160,70 L150,70 L150,80 L140,80 L140,100 L150,100 L150,110 L160,110 L160,140 L150,140 L150,150 L130,150 L130,160 L40,160 L40,150 L20,150 L20,140 L10,140 L10,130 L0,130 L0,110 L10,110 Z M90,40 L80,40 L80,50 L90,50 L90,40 Z M120,30 L110,30 L110,40 L120,40 L120,30 Z\"\n id=\"Combined-Shape\"\n fill={color}\n ></path>\n <g id=\"Group\" transform=\"translate(80, 30)\" fill=\"#000000\">\n <rect id=\"Rectangle-Copy-117\" x=\"0\" y=\"10\" width=\"10\" height=\"10\"></rect>\n <rect id=\"Rectangle-Copy-141\" x=\"30\" y=\"0\" width=\"10\" height=\"10\"></rect>\n </g>\n </g>\n </g>\n </svg>\n)\n"
9
+ "import type { JSX } from 'react/jsx-runtime'\nimport { MARK_BLOCKS } from '@hanzo/logo/logos'\n\ntype GuiIconSvgProps = React.SVGProps<SVGSVGElement> & {\n color?: string\n}\n\n/**\n * The Hanzo mark.\n *\n * This drew a duck — `<g id=\"duck\">`, pixel art, `#ECD20A` — because the fork\n * renamed the package and left the artwork. It was the mark in this site's\n * header, footer, login, 404, every doc page and its favicon.\n *\n * The geometry comes from `@hanzo/logo`, never retyped: MARK_BLOCKS is the five\n * body blocks of the block-H on its own 67 viewBox. `currentColor` by default so\n * the mark themes with the text around it rather than pinning a brand hex.\n */\nexport const GuiIconSvg = ({\n color = 'currentColor',\n ...props\n}: GuiIconSvgProps): JSX.Element => (\n <svg viewBox=\"0 0 67 67\" width=\"67px\" height=\"67px\" {...props}>\n {MARK_BLOCKS.map((d) => (\n <path key={d} d={d} fill={color} />\n ))}\n </svg>\n)\n"
10
10
  ]
11
11
  }
@@ -6,6 +6,6 @@
6
6
  ],
7
7
  "version": 3,
8
8
  "sourcesContent": [
9
- "import { YStack } from '@hanzo/gui'\nimport { GuiIconSvg } from './GuiLogoSvg'\nimport type { JSX } from 'react/jsx-runtime'\n\nexport const LogoIcon = ({\n downscale = 2,\n color,\n}: {\n downscale?: number\n color?: string\n}): JSX.Element => {\n return (\n <YStack\n render=\"span\"\n className=\"unselectable\"\n alignSelf=\"center\"\n marginVertical={-10}\n pressStyle={{\n opacity: 0.7,\n scaleX: -1,\n }}\n >\n <GuiIconSvg\n className=\"hanzogui-icon\"\n width={450 / 8 / downscale}\n height={420 / 8 / downscale}\n color={color}\n />\n </YStack>\n )\n}\n"
9
+ "import { YStack } from '@hanzo/gui'\nimport { GuiIconSvg } from './GuiLogoSvg'\nimport type { JSX } from 'react/jsx-runtime'\n\nexport const LogoIcon = ({\n downscale = 2,\n color,\n}: {\n downscale?: number\n color?: string\n}): JSX.Element => {\n return (\n <YStack\n render=\"span\"\n className=\"unselectable\"\n alignSelf=\"center\"\n marginVertical={-10}\n pressStyle={{\n opacity: 0.7,\n scaleX: -1,\n }}\n >\n <GuiIconSvg\n className=\"hanzogui-icon\"\n width={440 / 8 / downscale}\n height={440 / 8 / downscale}\n color={color}\n />\n </YStack>\n )\n}\n"
10
10
  ]
11
11
  }
@@ -1,11 +1,11 @@
1
1
  {
2
- "mappings": "AACA,OAAO,WAAoC;AAC3C,cAAc,mBAAmB;AA2BjC,OAAO,cAAM,WAAW,MAAM,qBAC3B,EACC,WACA,UACA,GAAG,SACF,cAAc;CACf;CACA;aACW",
2
+ "mappings": "AACA,OAAO,WAAoC;AAC3C,cAAc,mBAAmB;AA0BjC,OAAO,cAAM,WAAW,MAAM,qBAC3B,EACC,WACA,UACA,GAAG,SACF,cAAc;CACf;CACA;aACW",
3
3
  "names": [],
4
4
  "sources": [
5
5
  "src/LogoWords.tsx"
6
6
  ],
7
7
  "version": 3,
8
8
  "sourcesContent": [
9
- "import type { LayoutValue } from '@hanzogui/use-element-layout'\nimport React, { useEffect, useState } from 'react'\nimport type { XStackProps } from '@hanzo/gui'\nimport { Circle, XStack } from '@hanzo/gui'\nimport { useTint } from './useTint'\n\n// T A M A G U I\n// default: white white white white red green blue\n// hover: yellow yellow yellow color12 red green blue\nconst defaultColors = [\n 'var(--color12)',\n 'var(--color12)',\n 'var(--color12)',\n 'var(--color12)',\n 'var(--red9)',\n 'var(--green9)',\n 'var(--blue9)',\n]\n\nconst hoveredColors = [\n 'var(--yellow9)',\n 'var(--yellow9)',\n 'var(--yellow9)',\n 'var(--color12)',\n 'var(--red9)',\n 'var(--green9)',\n 'var(--blue9)',\n]\n\nexport const LogoWords: React.MemoExoticComponent<\n ({\n downscale,\n animated,\n ...props\n }: XStackProps & {\n downscale?: number\n animated?: boolean\n }) => import('react/jsx-runtime').JSX.Element\n> = React.memo(({ downscale = 1, animated, ...props }) => {\n const Tint = useTint()\n const [hovered, setHovered] = React.useState(false)\n const [mounted, setMounted] = React.useState<'start' | 'animate' | 'done'>('start')\n\n const { tintIndex: index } = Tint\n\n useEffect(() => {\n const idle = window.requestIdleCallback || setTimeout\n idle(() => {\n setTimeout(() => {\n setMounted('animate')\n }, 50)\n\n setTimeout(() => {\n setMounted('done')\n }, 1500)\n })\n }, [])\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n const index = Number.parseInt(event.key, 10) - 1 // Convert key to index (0-based)\n if (!Number.isNaN(index) && index >= 0 && index < Tint.tints.length) {\n Tint.setTintIndex(index)\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n }\n }, [Tint])\n\n const getColor = (i: number) => {\n if (hovered) return `var(--${Tint.tints[i]}9)`\n return defaultColors[i]\n }\n\n const x = Math.round(\n index * 18.5 + (18 / 2) * (index / defaultColors.length) + 3 + (index === 6 ? -3 : 0)\n )\n\n const [layout, setLayout] = useState<LayoutValue>()\n\n return (\n <XStack\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n paddingVertical=\"$2\"\n data-tauri-drag-region\n marginVertical=\"$-2\"\n position=\"relative\"\n className=\"logo-words\"\n onLayout={(e) => {\n setLayout(e.nativeEvent.layout as any)\n }}\n // @ts-ignore\n onMouseMove={(e: MouseEvent) => {\n if (!layout) return\n const x = e.clientX - layout.pageX\n // Total width divided into 7 sections (one for each letter)\n const sectionWidth = layout.width / 7\n // Calculate which section we're in (0-6)\n const section = Math.min(6, Math.floor(x / sectionWidth))\n Tint.setTintIndex(section)\n }}\n {...props}\n >\n {animated && (\n <Circle\n transition=\"medium\"\n position=\"absolute\"\n top={0}\n left={0}\n y={mounted === 'start' ? -30 : -4}\n // the last i is less wide\n x={x}\n size={4}\n backgroundColor=\"$color12\"\n />\n )}\n\n <svg\n data-tauri-drag-region\n width={373 * (1 / downscale) * 0.3333333}\n height={41 * (1 / downscale) * 0.3333333}\n viewBox=\"0 0 373 41\"\n >\n <polygon\n data-tauri-drag-region\n shapeRendering=\"crispEdges\"\n fill={getColor(0)}\n points=\"24.3870968 40.1612903 24.3870968 8.67741935 32.2580645 8.67741935 32.2580645 0.806451613 0.774193548 0.806451613 0.774193548 8.67741935 8.64516129 8.67741935 8.64516129 40.1612903\"\n // onMouseEnter={() => Tint.setTintIndex(0)}\n />\n\n <path\n shapeRendering=\"crispEdges\"\n fill={getColor(1)}\n d=\"M87.3548387,0.806451613 L87.3548387,8.67741935 L95.2258065,8.67741935 L95.2258065,40.1612903 L79.483871,40.1612903 L79.483871,24.4193548 L71.6129032,24.4193548 L71.6129032,40.1612903 L55.8709677,40.1612903 L55.8709677,8.67741935 L63.7419355,8.67741935 L63.7419355,0.806451613 L87.3548387,0.806451613 Z M79.483871,8.67741935 L71.6129032,8.67741935 L71.6129032,16.5483871 L79.483871,16.5483871 L79.483871,8.67741935 Z\"\n fillRule=\"nonzero\"\n // onMouseEnter={() => Tint.setTintIndex(1)}\n />\n\n <polygon\n data-tauri-drag-region\n shapeRendering=\"crispEdges\"\n fill={getColor(2)}\n points=\"130.645161 40.1612903 130.645161 22.4516129 138.516129 22.4516129 138.516129 40.1612903 154.258065 40.1612903 154.258065 0.806451613 142.451613 0.806451613 142.451613 8.67741935 126.709677 8.67741935 126.709677 0.806451613 114.903226 0.806451613 114.903226 40.1612903\"\n // onMouseEnter={() => Tint.setTintIndex(2)}\n />\n\n <path\n fill={getColor(3)}\n d=\"M205.419355,0.806451613 L205.419355,8.67741935 L213.290323,8.67741935 L213.290323,40.1612903 L197.548387,40.1612903 L197.548387,24.4193548 L189.677419,24.4193548 L189.677419,40.1612903 L173.935484,40.1612903 L173.935484,8.67741935 L181.806452,8.67741935 L181.806452,0.806451613 L205.419355,0.806451613 Z M197.548387,8.67741935 L189.677419,8.67741935 L189.677419,16.5483871 L197.548387,16.5483871 L197.548387,8.67741935 Z\"\n fillRule=\"nonzero\"\n // onMouseEnter={() => Tint.setTintIndex(3)}\n />\n\n <polygon\n data-tauri-drag-region\n shapeRendering=\"crispEdges\"\n fill={getColor(4)}\n points=\"264.451613 40.1612903 264.451613 32.2903226 272.322581 32.2903226 272.322581 16.5483871 256.580645 16.5483871 256.580645 32.2903226 248.709677 32.2903226 248.709677 8.67741935 272.322581 8.67741935 272.322581 0.806451613 240.83871 0.806451613 240.83871 8.67741935 232.967742 8.67741935 232.967742 32.2903226 240.83871 32.2903226 240.83871 40.1612903\"\n // onMouseEnter={() => Tint.setTintIndex(4)}\n />\n\n <polygon\n data-tauri-drag-region\n shapeRendering=\"crispEdges\"\n fill={getColor(5)}\n points=\"323.483871 40.1612903 323.483871 32.2903226 331.354839 32.2903226 331.354839 0.806451613 315.612903 0.806451613 315.612903 32.2903226 307.741935 32.2903226 307.741935 0.806451613 292 0.806451613 292 32.2903226 299.870968 32.2903226 299.870968 40.1612903\"\n // onMouseEnter={() => Tint.setTintIndex(5)}\n />\n\n <polygon\n data-tauri-drag-region\n shapeRendering=\"crispEdges\"\n fill={getColor(6)}\n points=\"372.677419 40.1612903 372.677419 0.806451613 356.935484 0.806451613 356.935484 40.1612903\"\n // onMouseEnter={() => Tint.setTintIndex(6)}\n />\n </svg>\n </XStack>\n )\n})\n"
9
+ "import type { LayoutValue } from '@hanzogui/use-element-layout'\nimport React, { useEffect, useState } from 'react'\nimport type { XStackProps } from '@hanzo/gui'\nimport { Circle, XStack } from '@hanzo/gui'\nimport { WORDMARKS } from '@hanzo/logo/wordmarks'\nimport { useTint } from './useTint'\n\n/**\n * The wordmark.\n *\n * This drew T-A-M-A-G-U-I: seven pixel-art letter polygons on a 373x41 box,\n * each tinted from a seven-colour table, with the hover strip split into seven\n * sections and the dot stepped by a constant fitted to those letters. The fork\n * renamed the package and kept the artwork, so the site's own name was another\n * company's.\n *\n * It renders the outlined Hanzo wordmark from @hanzo/logo now — real paths cut\n * from Zen, so it needs no font loaded and cannot drift from the wordmark every\n * other Hanzo surface uses.\n *\n * The tint interaction stays, because it is a feature rather than a piece of\n * the old wordmark — but it is no longer DIMENSIONED to seven letters. The\n * strip divides by however many tints there are, and the dot rides the same\n * fraction, so the two agree whatever that number becomes.\n */\nconst WORDMARK = WORDMARKS['Hanzo']\nconst [WM_X, WM_Y, WM_W, WM_H] = WORDMARK.box\n\nexport const LogoWords: React.MemoExoticComponent<\n ({\n downscale,\n animated,\n ...props\n }: XStackProps & {\n downscale?: number\n animated?: boolean\n }) => import('react/jsx-runtime').JSX.Element\n> = React.memo(({ downscale = 1, animated, ...props }) => {\n const Tint = useTint()\n const [hovered, setHovered] = React.useState(false)\n const [mounted, setMounted] = React.useState<'start' | 'animate' | 'done'>('start')\n\n const { tintIndex: index } = Tint\n const sections = Tint.tints.length\n\n useEffect(() => {\n const idle = window.requestIdleCallback || setTimeout\n idle(() => {\n setTimeout(() => {\n setMounted('animate')\n }, 50)\n\n setTimeout(() => {\n setMounted('done')\n }, 1500)\n })\n }, [])\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n const i = Number.parseInt(event.key, 10) - 1 // Convert key to index (0-based)\n if (!Number.isNaN(i) && i >= 0 && i < Tint.tints.length) {\n Tint.setTintIndex(i)\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n }\n }, [Tint])\n\n const [layout, setLayout] = useState<LayoutValue>()\n\n // The mark is one ink; the tint shows through the dot rather than by\n // recolouring letters, which is what the seven-colour table did.\n const fill = hovered ? `var(--${Tint.tints[index]}9)` : 'var(--color12)'\n\n const width = 373 * (1 / downscale) * 0.3333333\n const height = (width * WM_H) / WM_W\n // the dot sits over the section the current tint owns\n const dotX = Math.round((width / sections) * (index + 0.5))\n\n return (\n <XStack\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n paddingVertical=\"$2\"\n data-tauri-drag-region\n marginVertical=\"$-2\"\n position=\"relative\"\n className=\"logo-words\"\n onLayout={(e) => {\n setLayout(e.nativeEvent.layout as any)\n }}\n // @ts-ignore\n onMouseMove={(e: MouseEvent) => {\n if (!layout) return\n const x = e.clientX - layout.pageX\n const sectionWidth = layout.width / sections\n const section = Math.min(sections - 1, Math.max(0, Math.floor(x / sectionWidth)))\n Tint.setTintIndex(section)\n }}\n {...props}\n >\n {animated && (\n <Circle\n transition=\"medium\"\n position=\"absolute\"\n top={0}\n left={0}\n y={mounted === 'start' ? -30 : -4}\n x={dotX}\n size={4}\n backgroundColor=\"$color12\"\n />\n )}\n\n <svg\n data-tauri-drag-region\n role=\"img\"\n aria-label=\"Hanzo\"\n width={width}\n height={height}\n viewBox={`${WM_X} ${-WM_Y - WM_H} ${WM_W} ${WM_H}`}\n >\n <g transform=\"scale(1,-1)\">\n <path d={WORDMARK.d} fill={fill} />\n </g>\n </svg>\n </XStack>\n )\n})\n"
10
10
  ]
11
11
  }
@@ -1,11 +1,11 @@
1
1
  {
2
- "mappings": "AACA,cAAc,iBAAiB;KAE1B,iBAAiB,MAAM;AAM5B,cAAM;CACJ,UAA4E;CAC5E,MAAiE;CACjE,QAQK;CACL,WAQK;CACL,WAAgE;CAChE,OAAkE;CAClE,YAQK;;KAGF,sBAAsB;KAMtB,cAAc,OAAO,UAAS;AACnC,cAAM,UAA6B;AAEnC,YAAY,0BAA0B;AAItC,OAAO,iBAAS,YAAY;CAC1B;CACA,OAAO;CACP,UAAU;;AASZ,OAAO,iBAAS,YAAY;CAC1B;CACA,OAAO;CACP,UAAU;;AAeZ,OAAO,iBAAS,cAAc,MAAM;AAwBpC,OAAO,cAAM;AAIb,OAAO,cAAM,qBAAsB,IAAI",
2
+ "mappings": "AACA,cAAc,iBAAiB;KAE1B,iBAAiB,MAAM;AAQ5B,cAAM;CACJ,UAA4E;CAC5E,MAAiE;CACjE,QAQK;CACL,WAQK;CACL,WAAgE;CAChE,OAAkE;CAClE,YAQK;;KAGF,sBAAsB;KAMtB,cAAc,OAAO,UAAS;AACnC,cAAM,UAA6B;AAEnC,YAAY,0BAA0B;AAItC,OAAO,iBAAS,YAAY;CAC1B;CACA,OAAO;CACP,UAAU;;AASZ,OAAO,iBAAS,YAAY;CAC1B;CACA,OAAO;CACP,UAAU;;AAeZ,OAAO,iBAAS,cAAc,MAAM;AAwBpC,OAAO,cAAM;AAIb,OAAO,cAAM,qBAAsB,IAAI",
3
3
  "names": [],
4
4
  "sources": [
5
5
  "src/tints.tsx"
6
6
  ],
7
7
  "version": 3,
8
8
  "sourcesContent": [
9
- "import React from 'react'\nimport type { ThemeName } from '@hanzo/gui'\n\ntype ChangeHandler = (next: TintFamily) => void\n\nconst listeners = new Set<ChangeHandler>()\n\n// T A M A G U I\n// Maps to logo letters - index 3 (A) is the \"none\" theme position\nconst familiesValues = {\n hanzogui: ['yellow', 'yellow', 'yellow', 'gray', 'red', 'green', 'blue'] as ThemeName[],\n xmas: ['red', 'green', 'red', 'green', 'red', 'green', 'red'] as ThemeName[],\n easter: [\n 'yellow',\n 'yellow',\n 'yellow',\n 'yellow',\n 'yellow',\n 'yellow',\n 'yellow',\n ] as ThemeName[],\n halloween: [\n 'yellow',\n 'gray',\n 'yellow',\n 'gray',\n 'yellow',\n 'gray',\n 'yellow',\n ] as ThemeName[],\n valentine: ['red', 'red', 'red', 'red', 'red', 'red', 'red'] as ThemeName[],\n lunar: ['yellow', 'red', 'red', 'red', 'red', 'red', 'yellow'] as ThemeName[],\n stpatricks: [\n 'green',\n 'yellow',\n 'green',\n 'yellow',\n 'green',\n 'yellow',\n 'green',\n ] as ThemeName[],\n}\n\ntype Family = keyof typeof familiesValues\n\nconst DEFAULT_FAMILY: Family = 'hanzogui'\n\nconst familiesNames = Object.keys(familiesValues) as any as Family[]\n\ntype Families = { [key in Family]: ThemeName[] }\nconst families = familiesValues as Families\n\nexport type TintFamily = keyof typeof families\n\nlet fam: TintFamily = DEFAULT_FAMILY\n\nexport function getTints(): {\n name: string\n tints: ThemeName[]\n families: Families\n} {\n return {\n name: fam || DEFAULT_FAMILY,\n tints: families[fam] || families.hanzogui,\n families,\n }\n}\n\nexport function useTints(): {\n name: string\n tints: ThemeName[]\n families: Families\n} {\n const [val, setVal] = React.useState(getTints())\n\n React.useEffect(() => {\n return onTintFamilyChange(() => {\n React.startTransition(() => {\n setVal(getTints())\n })\n })\n }, [])\n\n return val\n}\n\nexport function setTintFamily(next: TintFamily): void {\n if (!families[next]) throw `impossible`\n fam = next\n\n // update DOM class for CSS-based styling (e.g., rainbow gradients)\n if (typeof document !== 'undefined') {\n const root = document.documentElement.classList\n // remove all season classes\n familiesNames.forEach((s) => {\n if (s !== 'hanzogui') {\n root.remove(`${s}-season`)\n }\n })\n // add the new one if not hanzogui\n if (next !== 'hanzogui') {\n root.add(`${next}-season`)\n }\n }\n\n React.startTransition(() => {\n listeners.forEach((l) => l(next))\n })\n}\n\nexport const setNextTintFamily = (): void => {\n setTintFamily(familiesNames[(familiesNames.indexOf(fam) + 1) % familiesNames.length])\n}\n\nexport const onTintFamilyChange = (cb: ChangeHandler) => {\n listeners.add(cb)\n return (): void => {\n listeners.delete(cb)\n }\n}\n"
9
+ "import React from 'react'\nimport type { ThemeName } from '@hanzo/gui'\n\ntype ChangeHandler = (next: TintFamily) => void\n\nconst listeners = new Set<ChangeHandler>()\n\n// A seasonal ramp of tint stops. These used to be one entry per letter of the\n// old seven-letter wordmark, with index 3 reserved as the neutral notch; the\n// wordmark is Hanzo now and every reader indexes by `tints.length`, so the\n// length is just how many stops the ramp has.\nconst familiesValues = {\n hanzogui: ['yellow', 'yellow', 'yellow', 'gray', 'red', 'green', 'blue'] as ThemeName[],\n xmas: ['red', 'green', 'red', 'green', 'red', 'green', 'red'] as ThemeName[],\n easter: [\n 'yellow',\n 'yellow',\n 'yellow',\n 'yellow',\n 'yellow',\n 'yellow',\n 'yellow',\n ] as ThemeName[],\n halloween: [\n 'yellow',\n 'gray',\n 'yellow',\n 'gray',\n 'yellow',\n 'gray',\n 'yellow',\n ] as ThemeName[],\n valentine: ['red', 'red', 'red', 'red', 'red', 'red', 'red'] as ThemeName[],\n lunar: ['yellow', 'red', 'red', 'red', 'red', 'red', 'yellow'] as ThemeName[],\n stpatricks: [\n 'green',\n 'yellow',\n 'green',\n 'yellow',\n 'green',\n 'yellow',\n 'green',\n ] as ThemeName[],\n}\n\ntype Family = keyof typeof familiesValues\n\nconst DEFAULT_FAMILY: Family = 'hanzogui'\n\nconst familiesNames = Object.keys(familiesValues) as any as Family[]\n\ntype Families = { [key in Family]: ThemeName[] }\nconst families = familiesValues as Families\n\nexport type TintFamily = keyof typeof families\n\nlet fam: TintFamily = DEFAULT_FAMILY\n\nexport function getTints(): {\n name: string\n tints: ThemeName[]\n families: Families\n} {\n return {\n name: fam || DEFAULT_FAMILY,\n tints: families[fam] || families.hanzogui,\n families,\n }\n}\n\nexport function useTints(): {\n name: string\n tints: ThemeName[]\n families: Families\n} {\n const [val, setVal] = React.useState(getTints())\n\n React.useEffect(() => {\n return onTintFamilyChange(() => {\n React.startTransition(() => {\n setVal(getTints())\n })\n })\n }, [])\n\n return val\n}\n\nexport function setTintFamily(next: TintFamily): void {\n if (!families[next]) throw `impossible`\n fam = next\n\n // update DOM class for CSS-based styling (e.g., rainbow gradients)\n if (typeof document !== 'undefined') {\n const root = document.documentElement.classList\n // remove all season classes\n familiesNames.forEach((s) => {\n if (s !== 'hanzogui') {\n root.remove(`${s}-season`)\n }\n })\n // add the new one if not hanzogui\n if (next !== 'hanzogui') {\n root.add(`${next}-season`)\n }\n }\n\n React.startTransition(() => {\n listeners.forEach((l) => l(next))\n })\n}\n\nexport const setNextTintFamily = (): void => {\n setTintFamily(familiesNames[(familiesNames.indexOf(fam) + 1) % familiesNames.length])\n}\n\nexport const onTintFamilyChange = (cb: ChangeHandler) => {\n listeners.add(cb)\n return (): void => {\n listeners.delete(cb)\n }\n}\n"
10
10
  ]
11
11
  }