git-hash-art 0.5.0 → 0.7.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/ALGORITHM.md +115 -20
- package/CHANGELOG.md +17 -0
- package/bin/cli.js +155 -0
- package/dist/browser.js +639 -33
- package/dist/browser.js.map +1 -1
- package/dist/main.js +639 -33
- package/dist/main.js.map +1 -1
- package/dist/module.js +639 -33
- package/dist/module.js.map +1 -1
- package/package.json +4 -1
- package/src/lib/archetypes.ts +248 -0
- package/src/lib/canvas/colors.ts +116 -0
- package/src/lib/canvas/draw.ts +1 -1
- package/src/lib/canvas/shapes/index.ts +2 -0
- package/src/lib/canvas/shapes/procedural.ts +209 -0
- package/src/lib/render.ts +138 -38
package/ALGORITHM.md
CHANGED
|
@@ -9,28 +9,33 @@ Hash String
|
|
|
9
9
|
│
|
|
10
10
|
├─► Seed (mulberry32 PRNG)
|
|
11
11
|
│
|
|
12
|
-
├─►
|
|
12
|
+
├─► Archetype Selection (1 of 10 visual personalities)
|
|
13
13
|
│
|
|
14
|
-
|
|
14
|
+
├─► Color Scheme (palette mode from archetype + temperature mode + contrast enforcement)
|
|
15
|
+
│
|
|
16
|
+
└─► Rendering Pipeline (parameters overridden by archetype)
|
|
15
17
|
│
|
|
16
|
-
|
|
18
|
+
0. Archetype Override (gridSize, layers, opacity, sizes, styles)
|
|
19
|
+
1. Background Layer (7 styles: radial, linear, solid, multi-stop)
|
|
20
|
+
1a. Background Luminance → contrast enforcement threshold
|
|
17
21
|
1b. Layered Background (faint shapes + concentric rings)
|
|
18
22
|
2. Composition Mode Selection
|
|
19
23
|
2b. Symmetry Mode Selection (none / bilateral / quad)
|
|
20
24
|
3. Focal Points (rule-of-thirds biased) + Void Zones
|
|
21
25
|
4. Flow Field Initialization
|
|
22
|
-
4b. Hero Shape (large focal anchor,
|
|
23
|
-
5. Shape Layers (× N layers)
|
|
26
|
+
4b. Hero Shape (large focal anchor, archetype-controlled)
|
|
27
|
+
5. Shape Layers (× N layers, archetype-tuned)
|
|
24
28
|
│ ├─ Blend Mode (per-layer compositing)
|
|
25
|
-
│ ├─ Render Style (
|
|
29
|
+
│ ├─ Render Style (archetype-preferred + random mix)
|
|
26
30
|
│ ├─ Position (composition mode + focal bias + density check)
|
|
27
|
-
│ ├─ Shape Selection (
|
|
31
|
+
│ ├─ Shape Selection (4 categories: basic, complex, sacred, procedural)
|
|
32
|
+
│ ├─ Contrast Enforcement (ensure readability vs background)
|
|
28
33
|
│ ├─ Atmospheric Depth (desaturation on later layers)
|
|
29
34
|
│ ├─ Temperature Contrast (foreground opposite to background)
|
|
30
35
|
│ ├─ Styling (transparency, glow, gradients, color jitter)
|
|
31
36
|
│ ├─ Organic Edges (~15% watercolor bleed)
|
|
32
37
|
│ └─ Recursive Nesting (~15% of large shapes)
|
|
33
|
-
6. Flow-Line Pass (tapered brush strokes)
|
|
38
|
+
6. Flow-Line Pass (tapered brush strokes, archetype-scaled)
|
|
34
39
|
6b. Symmetry Mirroring (bilateral-x, bilateral-y, or quad)
|
|
35
40
|
7. Noise Texture Overlay
|
|
36
41
|
8. Vignette (radial edge darkening)
|
|
@@ -48,7 +53,44 @@ rng() → float in [0, 1)
|
|
|
48
53
|
|
|
49
54
|
The old approach extracted 2-char hex pairs from the hash (only ~20 unique values in a 40-char hash). Mulberry32 produces a full 32-bit uniform stream from any seed, eliminating correlation artifacts.
|
|
50
55
|
|
|
51
|
-
## 2.
|
|
56
|
+
## 2. Archetype System
|
|
57
|
+
|
|
58
|
+
Before any rendering begins, the hash deterministically selects one of 10 **visual archetypes** — fundamentally different rendering personalities that override key parameters. This is the primary mechanism for visual diversity: two hashes that select different archetypes will look like they came from entirely different generators.
|
|
59
|
+
|
|
60
|
+
Each archetype controls:
|
|
61
|
+
|
|
62
|
+
| Parameter | Effect |
|
|
63
|
+
|-----------|--------|
|
|
64
|
+
| `gridSize` | Shape density (2 = sparse, 9 = packed) |
|
|
65
|
+
| `layers` | Rendering depth (2 = flat, 5 = deep) |
|
|
66
|
+
| `baseOpacity` / `opacityReduction` | Transparency character |
|
|
67
|
+
| `minShapeSize` / `maxShapeSize` | Scale range |
|
|
68
|
+
| `backgroundStyle` | One of 7 background rendering modes |
|
|
69
|
+
| `paletteMode` | One of 7 color palette strategies |
|
|
70
|
+
| `preferredStyles` | Weighted render style selection |
|
|
71
|
+
| `flowLineMultiplier` | Flow line density (0 = none, 4 = heavy) |
|
|
72
|
+
| `heroShape` | Whether to draw a dominant focal shape |
|
|
73
|
+
| `glowMultiplier` | Glow probability scaling (0 = none, 3 = heavy) |
|
|
74
|
+
| `sizePower` | Size distribution curve (0.5 = uniform, 2.5 = many tiny) |
|
|
75
|
+
|
|
76
|
+
### The 10 Archetypes
|
|
77
|
+
|
|
78
|
+
| Archetype | Character | Background | Palette | Key Traits |
|
|
79
|
+
|-----------|-----------|------------|---------|------------|
|
|
80
|
+
| **dense-chaotic** | Packed, energetic | radial-dark | harmonious | 9×9 grid, 5 layers, heavy flow lines, low glow |
|
|
81
|
+
| **minimal-spacious** | Clean, deliberate | solid-light | duotone | 2×2 grid, 2 layers, large shapes, no glow |
|
|
82
|
+
| **organic-flow** | Natural, flowing | radial-dark | earth | Heavy flow lines (4×), watercolor style, no hero |
|
|
83
|
+
| **geometric-precision** | Technical, structured | solid-dark | high-contrast | Stroke-only/dashed/hatched styles, no flow lines |
|
|
84
|
+
| **ethereal** | Dreamy, luminous | radial-light | pastel-light | High glow (2×), watercolor, hero shape |
|
|
85
|
+
| **bold-graphic** | Poster-like, impactful | linear-diagonal | duotone | 2 layers, very large shapes, no flow lines |
|
|
86
|
+
| **neon-glow** | Electric, vibrant | solid-dark | neon | Heavy glow (3×), stroke-heavy styles, hero shape |
|
|
87
|
+
| **monochrome-ink** | Pen-and-ink, textural | solid-light | monochrome | Hatched/incomplete styles, no glow |
|
|
88
|
+
| **cosmic** | Deep space, vast | radial-dark | neon | 8×8 grid, 5 layers, heavy glow, many tiny shapes |
|
|
89
|
+
| **classic** | Balanced, familiar | radial-dark | harmonious | Preserves the original rendering look |
|
|
90
|
+
|
|
91
|
+
Archetype values serve as defaults — explicit user config always wins. The `classic` archetype preserves backward compatibility with the original rendering style.
|
|
92
|
+
|
|
93
|
+
## 3. Color Scheme
|
|
52
94
|
|
|
53
95
|
The `SacredColorScheme` class derives three harmonious palettes from the hash:
|
|
54
96
|
|
|
@@ -60,6 +102,22 @@ The `SacredColorScheme` class derives three harmonious palettes from the hash:
|
|
|
60
102
|
|
|
61
103
|
These are merged and deduplicated into a single 6-8 color palette. Background colors are darkened variants (65% and 55% brightness) of the base scheme, with optional temperature shifting.
|
|
62
104
|
|
|
105
|
+
### Palette Modes
|
|
106
|
+
|
|
107
|
+
The archetype's `paletteMode` reshapes the color palette to match the visual personality:
|
|
108
|
+
|
|
109
|
+
| Mode | Colors | Character |
|
|
110
|
+
|------|--------|-----------|
|
|
111
|
+
| **harmonious** | Full base + complementary + triadic | Rich, balanced (default) |
|
|
112
|
+
| **monochrome** | Single hue, 5 lightness steps | Elegant, focused |
|
|
113
|
+
| **duotone** | Two contrasting hues + tints | Bold, graphic |
|
|
114
|
+
| **neon** | 4 hues at full saturation | Electric, vivid |
|
|
115
|
+
| **pastel-light** | 4 hues at low saturation, high lightness | Soft, dreamy |
|
|
116
|
+
| **earth** | Warm muted naturals (browns, olives, sage) | Organic, grounded |
|
|
117
|
+
| **high-contrast** | Black + white + one accent | Technical, stark |
|
|
118
|
+
|
|
119
|
+
Each mode also provides matching background colors (e.g., neon gets near-black backgrounds, pastel-light gets warm off-whites).
|
|
120
|
+
|
|
63
121
|
### Temperature Contrast
|
|
64
122
|
|
|
65
123
|
The hash deterministically selects a **temperature mode** that creates warm/cool tension across the image:
|
|
@@ -95,9 +153,21 @@ Scheme types also vary: `analogic`, `mono`, `contrast`, `triade`, `tetrade`. The
|
|
|
95
153
|
- **`shiftTemperature(hex, target, amount)`** — shifts hue toward warm (orange) or cool (blue)
|
|
96
154
|
- **Positional blending** — shape fill color is biased by canvas position, creating smooth color flow across the image
|
|
97
155
|
|
|
98
|
-
##
|
|
156
|
+
## 4. Background
|
|
157
|
+
|
|
158
|
+
The archetype's `backgroundStyle` selects one of 7 rendering modes:
|
|
159
|
+
|
|
160
|
+
| Style | Description |
|
|
161
|
+
|-------|-------------|
|
|
162
|
+
| **radial-dark** | Radial gradient from dark center to darker edges (original default) |
|
|
163
|
+
| **radial-light** | Light off-white center fading to the base palette |
|
|
164
|
+
| **linear-horizontal** | Left-to-right gradient between two palette colors |
|
|
165
|
+
| **linear-diagonal** | Corner-to-corner gradient with color reversal |
|
|
166
|
+
| **solid-dark** | Flat dark color fill |
|
|
167
|
+
| **solid-light** | Flat warm off-white fill |
|
|
168
|
+
| **multi-stop** | 3-4 color gradient with a darkened mid-palette accent |
|
|
99
169
|
|
|
100
|
-
|
|
170
|
+
This single change has an outsized impact on visual diversity — a solid-light background with monochrome shapes looks nothing like a radial-dark background with neon glow.
|
|
101
171
|
|
|
102
172
|
### Layered Background
|
|
103
173
|
|
|
@@ -108,7 +178,7 @@ After the gradient, a second pass adds visual texture to the background:
|
|
|
108
178
|
|
|
109
179
|
This prevents the background from feeling flat and gives the image depth before the main shape layers begin.
|
|
110
180
|
|
|
111
|
-
##
|
|
181
|
+
## 5. Composition Modes
|
|
112
182
|
|
|
113
183
|
The hash deterministically selects one of five composition strategies that control how shapes are positioned on the canvas:
|
|
114
184
|
|
|
@@ -135,7 +205,7 @@ Each mode produces fundamentally different visual character from the same shape
|
|
|
135
205
|
|
|
136
206
|
Symmetry is applied after shape layers and flow lines but before post-processing (noise, vignette, connecting curves). This means the mirrored content gets the same noise texture and vignette as the original, maintaining visual consistency. Symmetrical generative art is disproportionately appealing to humans due to our innate preference for bilateral symmetry.
|
|
137
207
|
|
|
138
|
-
##
|
|
208
|
+
## 6. Focal Points & Negative Space
|
|
139
209
|
|
|
140
210
|
1-2 focal points are placed on the canvas. **70% of the time**, focal points snap to **rule-of-thirds intersection points** (with slight jitter to avoid a mechanical look), creating compositions that feel intentionally designed. The remaining 30% use free placement within the central 60% of the canvas. Every shape position is pulled toward the nearest focal point by a strength factor (30-70%).
|
|
141
211
|
|
|
@@ -156,7 +226,7 @@ Symmetry is applied after shape layers and flow lines but before post-processing
|
|
|
156
226
|
|
|
157
227
|
Before placing each shape, the renderer checks how many shapes already exist nearby. If local density exceeds ~15% of the per-layer shape count, there's a 60% chance the shape is skipped. This prevents areas from becoming an opaque blob and creates natural visual rhythm.
|
|
158
228
|
|
|
159
|
-
##
|
|
229
|
+
## 7. Shape Layers
|
|
160
230
|
|
|
161
231
|
The image is built in N layers (default: 4). Each layer has its own characteristics:
|
|
162
232
|
|
|
@@ -169,7 +239,7 @@ The image is built in N layers (default: 4). Each layer has its own characterist
|
|
|
169
239
|
| Shape weights | Early layers favor basic shapes; later layers favor complex/sacred |
|
|
170
240
|
| Per-shape opacity | Additional random jitter (50-100% of layer opacity) |
|
|
171
241
|
| Blend mode | Each layer gets a hash-derived `globalCompositeOperation` (see below) |
|
|
172
|
-
| Render style | Each layer has a dominant render style; 30% of shapes pick their own |
|
|
242
|
+
| Render style | Each layer has a dominant render style (60% from archetype preferences, 40% random); 30% of shapes pick their own |
|
|
173
243
|
| Atmospheric depth | Later layers desaturate colors by up to 30%, simulating distance |
|
|
174
244
|
|
|
175
245
|
### Blend Modes (Per-Layer Compositing)
|
|
@@ -199,13 +269,25 @@ Later layers progressively desaturate their colors (0% on layer 0, up to 30% on
|
|
|
199
269
|
|
|
200
270
|
### Shape Selection (Layer-Weighted)
|
|
201
271
|
|
|
202
|
-
Shapes are divided into
|
|
272
|
+
Shapes are divided into four categories with weights that shift across layers:
|
|
203
273
|
|
|
204
274
|
| Category | Shapes | Early layers | Late layers |
|
|
205
275
|
|----------|--------|-------------|-------------|
|
|
206
276
|
| **Basic** | circle, square, triangle, hexagon, diamond, cube | High weight | Low weight |
|
|
207
277
|
| **Complex** | star, platonic solid, fibonacci spiral, islamic pattern, celtic knot, merkaba, fractal | Medium | Medium-high |
|
|
208
278
|
| **Sacred** | mandala, flower of life, tree of life, Metatron's cube, Sri Yantra, seed of life, vesica piscis, torus, egg of life | Low | High |
|
|
279
|
+
| **Procedural** | blob, ngon, lissajous, superellipse, spirograph, waveRing, rose | Medium (always present) | Medium-high |
|
|
280
|
+
|
|
281
|
+
Procedural shapes are hash-derived — their geometry is generated from the RNG, so every hash produces unique shapes that don't exist in any other generation. See the Procedural Shapes section below for details.
|
|
282
|
+
|
|
283
|
+
### Contrast Enforcement
|
|
284
|
+
|
|
285
|
+
After color selection, every foreground color (fills, strokes, flow lines, connecting curves) is checked against the average background luminance. If the luminance difference is below the minimum threshold (0.15), the color is adjusted:
|
|
286
|
+
|
|
287
|
+
- **Light backgrounds** — foreground colors are darkened and saturated
|
|
288
|
+
- **Dark backgrounds** — foreground colors are lightened and saturated
|
|
289
|
+
|
|
290
|
+
This prevents the white-on-white and dark-on-dark readability problems that occur when light palette modes (pastel-light, high-contrast) combine with light background styles (solid-light, radial-light).
|
|
209
291
|
|
|
210
292
|
### Size Distribution
|
|
211
293
|
|
|
@@ -230,7 +312,7 @@ Each shape receives:
|
|
|
230
312
|
- Sized at 15-40% of the parent
|
|
231
313
|
- More transparent than the parent layer
|
|
232
314
|
|
|
233
|
-
##
|
|
315
|
+
## 8. Flow-Line Pass (Tapered Brush Strokes)
|
|
234
316
|
|
|
235
317
|
6-16 flowing curves are drawn across the canvas, following the hash-derived vector field:
|
|
236
318
|
|
|
@@ -246,7 +328,7 @@ The flow field is defined by:
|
|
|
246
328
|
angle(x, y) = baseAngle + sin(x/w × freq × 2π) × π/2 + cos(y/h × freq × 2π) × π/2
|
|
247
329
|
```
|
|
248
330
|
|
|
249
|
-
##
|
|
331
|
+
## 9. Noise Texture Overlay
|
|
250
332
|
|
|
251
333
|
A dedicated noise RNG (seeded separately from the main RNG to avoid affecting shape generation) renders thousands of 1px dots across the canvas:
|
|
252
334
|
|
|
@@ -255,7 +337,7 @@ A dedicated noise RNG (seeded separately from the main RNG to avoid affecting sh
|
|
|
255
337
|
- Very low opacity (1-4%)
|
|
256
338
|
- Creates subtle film-grain texture that adds organic depth
|
|
257
339
|
|
|
258
|
-
##
|
|
340
|
+
## 9b. Vignette
|
|
259
341
|
|
|
260
342
|
A radial gradient overlay darkens the edges of the canvas, drawing the viewer's eye toward the center:
|
|
261
343
|
|
|
@@ -264,7 +346,7 @@ A radial gradient overlay darkens the edges of the canvas, drawing the viewer's
|
|
|
264
346
|
- Applied after noise but before connecting curves, so the curves remain visible at edges
|
|
265
347
|
- Creates a natural "spotlight" effect that makes compositions feel more focused and photographic
|
|
266
348
|
|
|
267
|
-
##
|
|
349
|
+
## 10. Organic Connecting Curves
|
|
268
350
|
|
|
269
351
|
Quadratic bezier curves connect nearby shapes:
|
|
270
352
|
|
|
@@ -298,6 +380,19 @@ Mathematically precise sacred geometry patterns:
|
|
|
298
380
|
- **Torus** — 2D projection of a torus via line segments
|
|
299
381
|
- **Egg of Life** — 7 circles in tight hexagonal packing
|
|
300
382
|
|
|
383
|
+
### Procedural Shapes
|
|
384
|
+
Hash-derived shapes whose geometry is generated from the RNG. Every hash produces unique shapes that don't exist in any other generation:
|
|
385
|
+
|
|
386
|
+
| Shape | Algorithm | Hash Controls |
|
|
387
|
+
|-------|-----------|---------------|
|
|
388
|
+
| **Blob** | Smooth closed curve via quadratic bezier through 5-9 control points arranged around a circle | Number of lobes (5-9), radius jitter per lobe (50-100%) |
|
|
389
|
+
| **Ngon** | Irregular polygon with independent vertex displacement | Side count (3-12), vertex jitter amount (10-50%) |
|
|
390
|
+
| **Lissajous** | Parametric curve `x = sin(a*t + φ), y = sin(b*t)` | Frequency ratios a,b (1-5 each), phase offset φ |
|
|
391
|
+
| **Superellipse** | `|x|^n + |y|^n = 1` rendered parametrically | Exponent n: 0.3 (spiky astroid) → 2 (circle) → 5 (rounded rectangle) |
|
|
392
|
+
| **Spirograph** | Hypotrochoid curve `(R-r)cos(t) + d*cos((R-r)t/r)` | Inner radius ratio r (0.2-0.8), pen distance d (0.3-1.0) |
|
|
393
|
+
| **Wave Ring** | Concentric rings with sinusoidal radial displacement | Ring count (2-5), wave frequency (3-14), amplitude (5-20%) |
|
|
394
|
+
| **Rose** | Polar rose curve `r = cos(k*θ)` | Petal parameter k (2-7), producing k or 2k petals |
|
|
395
|
+
|
|
301
396
|
## Configuration
|
|
302
397
|
|
|
303
398
|
All parameters are exposed via `GenerationConfig`:
|
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,25 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
#### [0.7.0](https://github.com/gfargo/git-hash-art/compare/0.6.0...0.7.0)
|
|
8
|
+
|
|
9
|
+
- feat: procedural shape generators and contrast enforcement [`#13`](https://github.com/gfargo/git-hash-art/pull/13)
|
|
10
|
+
- feat: procedural shapes and contrast enforcement [`4f018ab`](https://github.com/gfargo/git-hash-art/commit/4f018abb4c89849954ccc941ee7e9ab468231f78)
|
|
11
|
+
- docs: update ALGORITHM.md with procedural shapes and contrast enforcement [`3e044fd`](https://github.com/gfargo/git-hash-art/commit/3e044fd940fbc0f62e3a9696fa815145cf315f67)
|
|
12
|
+
|
|
13
|
+
#### [0.6.0](https://github.com/gfargo/git-hash-art/compare/0.5.0...0.6.0)
|
|
14
|
+
|
|
15
|
+
> 19 March 2026
|
|
16
|
+
|
|
17
|
+
- feat: archetype system, palette modes, background variety, and CLI [`#12`](https://github.com/gfargo/git-hash-art/pull/12)
|
|
18
|
+
- feat: archetype system for dramatically different visual personalities [`2a1b919`](https://github.com/gfargo/git-hash-art/commit/2a1b919c51d3bfbd9d5c7a381ea5e104f81df2f6)
|
|
19
|
+
- feat: add CLI for generating art from terminal [`184372a`](https://github.com/gfargo/git-hash-art/commit/184372a584f68031bf44379f385f2e78691f98aa)
|
|
20
|
+
- docs: update ALGORITHM.md with archetype system, palette modes, and background styles [`4eccd07`](https://github.com/gfargo/git-hash-art/commit/4eccd077be9469c27caf60a0d8c463124ad7f9fa)
|
|
21
|
+
|
|
7
22
|
#### [0.5.0](https://github.com/gfargo/git-hash-art/compare/0.4.1...0.5.0)
|
|
8
23
|
|
|
24
|
+
> 19 March 2026
|
|
25
|
+
|
|
9
26
|
- feat: visual improvements — composition, color, and rendering upgrades [`#11`](https://github.com/gfargo/git-hash-art/pull/11)
|
|
10
27
|
- feat: warm/cool temperature contrast in color system [`550bc6a`](https://github.com/gfargo/git-hash-art/commit/550bc6a87929d7ebafa973079845e688a00de929)
|
|
11
28
|
- docs: update ALGORITHM.md with all new visual features [`a05de15`](https://github.com/gfargo/git-hash-art/commit/a05de1586684b91266f5a26ea7279fc601dd2202)
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require("child_process");
|
|
4
|
+
const { generateImageFromHash, saveImageToFile } = require("../dist/main.js");
|
|
5
|
+
const { PRESETS } = require("../dist/main.js");
|
|
6
|
+
|
|
7
|
+
const HELP = `
|
|
8
|
+
git-hash-art — Generate deterministic abstract art from git hashes.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
git-hash-art current [options] Generate art from HEAD commit
|
|
12
|
+
git-hash-art generate <hash> [options] Generate art from a specific hash
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--width <n> Canvas width in pixels (default: 2048)
|
|
16
|
+
--height <n> Canvas height in pixels (default: 2048)
|
|
17
|
+
--output <dir> Output directory (default: ./output)
|
|
18
|
+
--preset <name> Use a built-in preset (overrides width/height)
|
|
19
|
+
--layers <n> Number of layers (default: 4)
|
|
20
|
+
--grid <n> Grid density (default: 5)
|
|
21
|
+
--list-presets List available presets
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
npx git-hash-art current
|
|
25
|
+
npx git-hash-art current --preset instagram-square
|
|
26
|
+
npx git-hash-art generate abc123ff --width 1920 --height 1080
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
function parseArgs(argv) {
|
|
30
|
+
const args = argv.slice(2);
|
|
31
|
+
const command = args[0];
|
|
32
|
+
const parsed = { command, hash: null, options: {} };
|
|
33
|
+
|
|
34
|
+
let i = 1;
|
|
35
|
+
if (command === "generate") {
|
|
36
|
+
parsed.hash = args[1];
|
|
37
|
+
i = 2;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
while (i < args.length) {
|
|
41
|
+
const arg = args[i];
|
|
42
|
+
if (arg === "--width" && args[i + 1]) {
|
|
43
|
+
parsed.options.width = parseInt(args[i + 1], 10);
|
|
44
|
+
i += 2;
|
|
45
|
+
} else if (arg === "--height" && args[i + 1]) {
|
|
46
|
+
parsed.options.height = parseInt(args[i + 1], 10);
|
|
47
|
+
i += 2;
|
|
48
|
+
} else if (arg === "--output" && args[i + 1]) {
|
|
49
|
+
parsed.options.output = args[i + 1];
|
|
50
|
+
i += 2;
|
|
51
|
+
} else if (arg === "--preset" && args[i + 1]) {
|
|
52
|
+
parsed.options.preset = args[i + 1];
|
|
53
|
+
i += 2;
|
|
54
|
+
} else if (arg === "--layers" && args[i + 1]) {
|
|
55
|
+
parsed.options.layers = parseInt(args[i + 1], 10);
|
|
56
|
+
i += 2;
|
|
57
|
+
} else if (arg === "--grid" && args[i + 1]) {
|
|
58
|
+
parsed.options.gridSize = parseInt(args[i + 1], 10);
|
|
59
|
+
i += 2;
|
|
60
|
+
} else if (arg === "--list-presets") {
|
|
61
|
+
parsed.command = "list-presets";
|
|
62
|
+
i += 1;
|
|
63
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
64
|
+
parsed.command = "help";
|
|
65
|
+
i += 1;
|
|
66
|
+
} else {
|
|
67
|
+
i += 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return parsed;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getHeadHash() {
|
|
75
|
+
try {
|
|
76
|
+
return execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
|
|
77
|
+
} catch {
|
|
78
|
+
console.error("Error: Not a git repository or git is not installed.");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function listPresets() {
|
|
84
|
+
console.log("\nAvailable presets:\n");
|
|
85
|
+
for (const [name, config] of Object.entries(PRESETS)) {
|
|
86
|
+
console.log(` ${name.padEnd(20)} ${config.width}x${config.height}`);
|
|
87
|
+
}
|
|
88
|
+
console.log("");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function run() {
|
|
92
|
+
const { command, hash, options } = parseArgs(process.argv);
|
|
93
|
+
|
|
94
|
+
if (!command || command === "help") {
|
|
95
|
+
console.log(HELP);
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (command === "list-presets") {
|
|
100
|
+
listPresets();
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (command !== "current" && command !== "generate") {
|
|
105
|
+
console.error(`Unknown command: ${command}\n`);
|
|
106
|
+
console.log(HELP);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let gitHash;
|
|
111
|
+
if (command === "current") {
|
|
112
|
+
gitHash = getHeadHash();
|
|
113
|
+
console.log(`Using HEAD: ${gitHash}`);
|
|
114
|
+
} else {
|
|
115
|
+
if (!hash) {
|
|
116
|
+
console.error("Error: Please provide a hash.\n");
|
|
117
|
+
console.log("Usage: git-hash-art generate <hash> [options]");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
gitHash = hash;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Build config from preset + overrides
|
|
124
|
+
let config = {};
|
|
125
|
+
if (options.preset) {
|
|
126
|
+
const preset = PRESETS[options.preset];
|
|
127
|
+
if (!preset) {
|
|
128
|
+
console.error(`Unknown preset: ${options.preset}`);
|
|
129
|
+
console.log("Use --list-presets to see available presets.");
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
config = { ...preset };
|
|
133
|
+
delete config.hash;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (options.width) config.width = options.width;
|
|
137
|
+
if (options.height) config.height = options.height;
|
|
138
|
+
if (options.layers) config.layers = options.layers;
|
|
139
|
+
if (options.gridSize) config.gridSize = options.gridSize;
|
|
140
|
+
|
|
141
|
+
const outputDir = options.output || "./output";
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const buffer = generateImageFromHash(gitHash, config);
|
|
145
|
+
const w = config.width || 2048;
|
|
146
|
+
const h = config.height || 2048;
|
|
147
|
+
const outputPath = saveImageToFile(buffer, outputDir, gitHash, "", w, h);
|
|
148
|
+
console.log(`Done! Image saved to ${outputPath}`);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error("Generation failed:", error.message);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
run();
|