@xivdyetools/core 2.6.0 → 4.0.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.
Files changed (156) hide show
  1. package/README.md +85 -67
  2. package/dist/blending/blending.d.ts +22 -0
  3. package/dist/blending/blending.d.ts.map +1 -0
  4. package/dist/blending/blending.js +129 -0
  5. package/dist/blending/blending.js.map +1 -0
  6. package/dist/blending/conversions.d.ts +53 -0
  7. package/dist/blending/conversions.d.ts.map +1 -0
  8. package/dist/blending/conversions.js +256 -0
  9. package/dist/blending/conversions.js.map +1 -0
  10. package/dist/blending/index.d.ts +8 -0
  11. package/dist/blending/index.d.ts.map +1 -0
  12. package/dist/blending/index.js +3 -0
  13. package/dist/blending/index.js.map +1 -0
  14. package/dist/blending/types.d.ts +36 -0
  15. package/dist/blending/types.d.ts.map +1 -0
  16. package/dist/blending/types.js +21 -0
  17. package/dist/blending/types.js.map +1 -0
  18. package/dist/config/band-calibration.d.ts +68 -0
  19. package/dist/config/band-calibration.d.ts.map +1 -0
  20. package/dist/config/band-calibration.js +196 -0
  21. package/dist/config/band-calibration.js.map +1 -0
  22. package/dist/config/band-vocabulary.d.ts +82 -0
  23. package/dist/config/band-vocabulary.d.ts.map +1 -0
  24. package/dist/config/band-vocabulary.js +117 -0
  25. package/dist/config/band-vocabulary.js.map +1 -0
  26. package/dist/config/consolidated-ids.js +2 -2
  27. package/dist/config/consolidated-ids.js.map +1 -1
  28. package/dist/config/dye-vocabulary.d.ts +45 -0
  29. package/dist/config/dye-vocabulary.d.ts.map +1 -0
  30. package/dist/config/dye-vocabulary.js +58 -0
  31. package/dist/config/dye-vocabulary.js.map +1 -0
  32. package/dist/config/facewear.d.ts +24 -0
  33. package/dist/config/facewear.d.ts.map +1 -0
  34. package/dist/config/facewear.js +35 -0
  35. package/dist/config/facewear.js.map +1 -0
  36. package/dist/config/learn-links.d.ts +70 -0
  37. package/dist/config/learn-links.d.ts.map +1 -0
  38. package/dist/config/learn-links.js +171 -0
  39. package/dist/config/learn-links.js.map +1 -0
  40. package/dist/config/product-links.d.ts +44 -0
  41. package/dist/config/product-links.d.ts.map +1 -0
  42. package/dist/config/product-links.js +44 -0
  43. package/dist/config/product-links.js.map +1 -0
  44. package/dist/constants/index.d.ts +14 -1
  45. package/dist/constants/index.d.ts.map +1 -1
  46. package/dist/constants/index.js +38 -22
  47. package/dist/constants/index.js.map +1 -1
  48. package/dist/data/character_colors/race_specific/hair_colors.json +1 -1
  49. package/dist/data/character_colors/race_specific/skin_colors.json +1 -1
  50. package/dist/data/dyes.json +1127 -0
  51. package/dist/data/facewear_colors.json +57 -0
  52. package/dist/data/locales/de.json +4 -50
  53. package/dist/data/locales/en.json +4 -50
  54. package/dist/data/locales/fr.json +4 -50
  55. package/dist/data/locales/ja.json +4 -50
  56. package/dist/data/locales/ko.json +4 -50
  57. package/dist/data/locales/zh.json +4 -50
  58. package/dist/data/presets.json +59 -301
  59. package/dist/index.d.ts +156 -17
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +114 -10
  62. package/dist/index.js.map +1 -1
  63. package/dist/services/APIService.d.ts +39 -62
  64. package/dist/services/APIService.d.ts.map +1 -1
  65. package/dist/services/APIService.js +156 -150
  66. package/dist/services/APIService.js.map +1 -1
  67. package/dist/services/CharacterColorService.d.ts +4 -36
  68. package/dist/services/CharacterColorService.d.ts.map +1 -1
  69. package/dist/services/CharacterColorService.js +57 -79
  70. package/dist/services/CharacterColorService.js.map +1 -1
  71. package/dist/services/ColorService.d.ts +57 -68
  72. package/dist/services/ColorService.d.ts.map +1 -1
  73. package/dist/services/ColorService.js +86 -94
  74. package/dist/services/ColorService.js.map +1 -1
  75. package/dist/services/DyeService.d.ts +11 -104
  76. package/dist/services/DyeService.d.ts.map +1 -1
  77. package/dist/services/DyeService.js +19 -157
  78. package/dist/services/DyeService.js.map +1 -1
  79. package/dist/services/LocalizationService.d.ts +48 -59
  80. package/dist/services/LocalizationService.d.ts.map +1 -1
  81. package/dist/services/LocalizationService.js +86 -95
  82. package/dist/services/LocalizationService.js.map +1 -1
  83. package/dist/services/PaletteService.d.ts +8 -9
  84. package/dist/services/PaletteService.d.ts.map +1 -1
  85. package/dist/services/PaletteService.js +10 -22
  86. package/dist/services/PaletteService.js.map +1 -1
  87. package/dist/services/PresetService.d.ts +2 -34
  88. package/dist/services/PresetService.d.ts.map +1 -1
  89. package/dist/services/PresetService.js +12 -59
  90. package/dist/services/PresetService.js.map +1 -1
  91. package/dist/services/chara/chara-models.d.ts +68 -0
  92. package/dist/services/chara/chara-models.d.ts.map +1 -0
  93. package/dist/services/chara/chara-models.js +87 -0
  94. package/dist/services/chara/chara-models.js.map +1 -0
  95. package/dist/services/chara/chara-parser.d.ts +95 -0
  96. package/dist/services/chara/chara-parser.d.ts.map +1 -0
  97. package/dist/services/chara/chara-parser.js +299 -0
  98. package/dist/services/chara/chara-parser.js.map +1 -0
  99. package/dist/services/chara/chara-resolver.d.ts +103 -0
  100. package/dist/services/chara/chara-resolver.d.ts.map +1 -0
  101. package/dist/services/chara/chara-resolver.js +233 -0
  102. package/dist/services/chara/chara-resolver.js.map +1 -0
  103. package/dist/services/color/ColorConverter.d.ts +112 -35
  104. package/dist/services/color/ColorConverter.d.ts.map +1 -1
  105. package/dist/services/color/ColorConverter.js +205 -89
  106. package/dist/services/color/ColorConverter.js.map +1 -1
  107. package/dist/services/color/ColorManipulator.d.ts +11 -0
  108. package/dist/services/color/ColorManipulator.d.ts.map +1 -1
  109. package/dist/services/color/ColorManipulator.js +24 -9
  110. package/dist/services/color/ColorManipulator.js.map +1 -1
  111. package/dist/services/color/ColorblindnessSimulator.d.ts +17 -0
  112. package/dist/services/color/ColorblindnessSimulator.d.ts.map +1 -1
  113. package/dist/services/color/ColorblindnessSimulator.js +57 -3
  114. package/dist/services/color/ColorblindnessSimulator.js.map +1 -1
  115. package/dist/services/color/SpectralMixer.d.ts +0 -34
  116. package/dist/services/color/SpectralMixer.d.ts.map +1 -1
  117. package/dist/services/color/SpectralMixer.js +0 -73
  118. package/dist/services/color/SpectralMixer.js.map +1 -1
  119. package/dist/services/dye/DyeDatabase.d.ts +5 -21
  120. package/dist/services/dye/DyeDatabase.d.ts.map +1 -1
  121. package/dist/services/dye/DyeDatabase.js +98 -130
  122. package/dist/services/dye/DyeDatabase.js.map +1 -1
  123. package/dist/services/dye/DyeSearch.d.ts +17 -31
  124. package/dist/services/dye/DyeSearch.d.ts.map +1 -1
  125. package/dist/services/dye/DyeSearch.js +45 -141
  126. package/dist/services/dye/DyeSearch.js.map +1 -1
  127. package/dist/services/dye/HarmonyGenerator.d.ts +11 -18
  128. package/dist/services/dye/HarmonyGenerator.d.ts.map +1 -1
  129. package/dist/services/dye/HarmonyGenerator.js +33 -38
  130. package/dist/services/dye/HarmonyGenerator.js.map +1 -1
  131. package/dist/services/localization/LocaleLoader.d.ts.map +1 -1
  132. package/dist/services/localization/LocaleLoader.js +0 -1
  133. package/dist/services/localization/LocaleLoader.js.map +1 -1
  134. package/dist/services/localization/TranslationProvider.d.ts +1 -42
  135. package/dist/services/localization/TranslationProvider.d.ts.map +1 -1
  136. package/dist/services/localization/TranslationProvider.js +4 -82
  137. package/dist/services/localization/TranslationProvider.js.map +1 -1
  138. package/dist/types/index.d.ts +39 -52
  139. package/dist/types/index.d.ts.map +1 -1
  140. package/dist/types/index.js +50 -10
  141. package/dist/types/index.js.map +1 -1
  142. package/dist/utils/index.d.ts +36 -123
  143. package/dist/utils/index.d.ts.map +1 -1
  144. package/dist/utils/index.js +54 -413
  145. package/dist/utils/index.js.map +1 -1
  146. package/dist/utils/kd-tree.d.ts +0 -4
  147. package/dist/utils/kd-tree.d.ts.map +1 -1
  148. package/dist/utils/kd-tree.js +0 -6
  149. package/dist/utils/kd-tree.js.map +1 -1
  150. package/package.json +24 -15
  151. package/dist/data/character_colors/index.json +0 -40
  152. package/dist/data/colors_xiv.json +0 -3538
  153. package/dist/version.d.ts +0 -11
  154. package/dist/version.d.ts.map +0 -1
  155. package/dist/version.js +0 -11
  156. package/dist/version.js.map +0 -1
package/README.md CHANGED
@@ -1,16 +1,16 @@
1
- # xivdyetools-core
1
+ # @xivdyetools/core
2
2
 
3
- > Core color algorithms and dye database for XIV Dye Tools - Environment-agnostic TypeScript library for FFXIV dye color matching, harmony generation, and accessibility checking.
3
+ > Core color algorithms and dye database for XIV Dye Tools environment-agnostic TypeScript library for FFXIV dye color matching, harmony generation, and accessibility checking.
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/xivdyetools-core)](https://www.npmjs.com/package/xivdyetools-core)
5
+ [![npm version](https://img.shields.io/npm/v/@xivdyetools/core)](https://www.npmjs.com/package/@xivdyetools/core)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.3%2B-blue)](https://www.typescriptlang.org/)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9%2B-blue)](https://www.typescriptlang.org/)
8
8
 
9
9
  ## Features
10
10
 
11
- ✨ **Color Conversion** - RGB ↔ HSV ↔ Hex ↔ LAB ↔ RYB ↔ OKLAB ↔ OKLCH ↔ LCH ↔ HSL
12
- 🎨 **136 FFXIV Dyes** - Complete database with RGB/HSV/metadata
13
- 🖌️ **Advanced Color Mixing** - RYB, OKLAB, HSL, and Spectral (Kubelka-Munk)
11
+ ✨ **Color Conversion** - RGB ↔ HSV ↔ Hex ↔ LAB ↔ RYB ↔ OKLAB ↔ OKLCH ↔ LCH ↔ HSL ↔ CMYK
12
+ 🎨 **125 FFXIV Dyes** - Complete database (schema v2, stainID-keyed) plus 11 Facewear colors
13
+ 🖌️ **Advanced Color Mixing** - RYB, OKLAB, HSL, and Spectral (Kubelka-Munk); six blending modes via `@xivdyetools/core/blending`
14
14
  🔬 **Spectral.js Integration** - Physics-based paint mixing (Blue + Yellow = Green!)
15
15
  🎯 **Dye Matching** - Find closest dyes to any color
16
16
  🌈 **Color Harmonies** - Triadic, complementary, analogous, and more
@@ -24,7 +24,7 @@
24
24
  ## Installation
25
25
 
26
26
  ```bash
27
- npm install xivdyetools-core
27
+ npm install @xivdyetools/core
28
28
  ```
29
29
 
30
30
  ## Quick Start
@@ -32,7 +32,7 @@ npm install xivdyetools-core
32
32
  ### Browser (with bundler)
33
33
 
34
34
  ```typescript
35
- import { ColorService, DyeService, dyeDatabase } from 'xivdyetools-core';
35
+ import { ColorService, DyeService, dyeDatabase } from '@xivdyetools/core';
36
36
 
37
37
  // Initialize services
38
38
  const dyeService = new DyeService(dyeDatabase);
@@ -57,18 +57,22 @@ console.log(hsv); // { h: 0, s: 58.04, v: 100 }
57
57
  import {
58
58
  DyeService,
59
59
  APIService,
60
- dyeDatabase
61
- } from 'xivdyetools-core';
60
+ dyeDatabase,
61
+ getMarketItemID
62
+ } from '@xivdyetools/core';
62
63
  import Redis from 'ioredis';
64
+ // RedisCacheBackend is your own ICacheBackend implementation — see "Custom Cache Backends" below
63
65
 
64
66
  // Initialize with Redis cache (for Discord bots)
65
67
  const redis = new Redis();
66
68
  const cacheBackend = new RedisCacheBackend(redis);
67
- const apiService = new APIService(cacheBackend);
69
+ const apiService = new APIService({ cacheBackend });
68
70
  const dyeService = new DyeService(dyeDatabase);
69
71
 
70
- // Fetch live market prices
71
- const priceData = await apiService.getPriceData(5752); // Jet Black Dye
72
+ // Fetch live market prices.
73
+ // NOTE: since Patch 7.5, most dyes share a consolidated market itemID —
74
+ // use getMarketItemID(dye) rather than a hard-coded legacy itemID.
75
+ const priceData = await apiService.getPriceData(getMarketItemID(jetBlack));
72
76
  console.log(`${priceData.currentMinPrice} Gil`);
73
77
 
74
78
  // Find harmony with pricing
@@ -85,7 +89,7 @@ Pure color conversion and manipulation algorithms.
85
89
  > **Memory Note**: ColorService uses LRU caches (5 caches × 1000 entries each = up to 5000 cached entries) for performance optimization. For long-running applications or memory-constrained environments, call `ColorService.clearCaches()` periodically to free memory. Each cache entry is approximately 50-100 bytes, so maximum memory usage is ~500KB.
86
90
 
87
91
  ```typescript
88
- import { ColorService } from 'xivdyetools-core';
92
+ import { ColorService } from '@xivdyetools/core';
89
93
 
90
94
  // Hex ↔ RGB
91
95
  const rgb = ColorService.hexToRgb('#FF6B6B');
@@ -107,7 +111,8 @@ const distance = ColorService.getColorDistance('#FF0000', '#00FF00');
107
111
  // LAB color space and DeltaE (perceptual color difference)
108
112
  const lab = ColorService.hexToLab('#FF6B6B');
109
113
  const deltaE = ColorService.getDeltaE('#FF0000', '#FF6B6B'); // CIE76 by default
110
- const deltaE2000 = ColorService.getDeltaE('#FF0000', '#FF6B6B', 'cie2000'); // CIEDE2000
114
+ const deltaE2000 = ColorService.getDeltaE('#FF0000', '#FF6B6B', 'ciede2000'); // CIEDE2000
115
+ // 'cie2000' is accepted as a legacy alias of 'ciede2000' and returns the same value
111
116
 
112
117
  // Color inversion
113
118
  const inverted = ColorService.invert('#FF6B6B');
@@ -132,19 +137,21 @@ const hex = ColorService.rybToHex(255, 255, 0); // Red+Yellow = Orange
132
137
  FFXIV dye database management and color matching.
133
138
 
134
139
  ```typescript
135
- import { DyeService, dyeDatabase } from 'xivdyetools-core';
140
+ import { DyeService, dyeDatabase } from '@xivdyetools/core';
136
141
 
137
142
  const dyeService = new DyeService(dyeDatabase);
138
143
 
139
144
  // Database access
140
- const allDyes = dyeService.getAllDyes(); // 136 dyes
141
- const dyeById = dyeService.getDyeById(5752); // By itemID - Jet Black Dye
142
- const dyeByStain = dyeService.getByStainId(1); // By stainID - Snow White
145
+ const allDyes = dyeService.getAllDyes(); // 125 dyes
146
+ const dyeById = dyeService.getDyeById(13115); // By itemID (= Dye.id) - Jet Black; prefer getByStainId(102)
147
+ const dyeByStain = dyeService.getByStainId(1); // By stainID (canonical key) - Snow White
143
148
  const categories = dyeService.getCategories(); // ['Neutral', 'Red', 'Blue', ...]
144
149
 
145
- // Color matching
150
+ // Color matching — matchingMethod is one of the 5.0 suite:
151
+ // 'ciede2000' (default) | 'oklab' | 'cie76' | 'redmean' | 'rgb' | 'distinguish'
146
152
  const closest = dyeService.findClosestDye('#FF6B6B');
147
- const nearby = dyeService.findDyesWithinDistance('#FF6B6B', 50, 5);
153
+ const closestOk = dyeService.findClosestDye('#FF6B6B', { matchingMethod: 'oklab' });
154
+ const nearby = dyeService.findDyesWithinDistance('#FF6B6B', { maxDistance: 50, limit: 5 });
148
155
 
149
156
  // Harmony generation (default: fast hue-based matching)
150
157
  const triadic = dyeService.findTriadicDyes('#FF6B6B');
@@ -156,10 +163,10 @@ const splitComplementary = dyeService.findSplitComplementaryDyes('#FF6B6B');
156
163
  // DeltaE-based harmony (perceptually accurate matching)
157
164
  const triadicDeltaE = dyeService.findTriadicDyes('#FF6B6B', {
158
165
  algorithm: 'deltaE',
159
- deltaEFormula: 'cie2000', // or 'cie76' (faster, default)
166
+ deltaEFormula: 'ciede2000', // or 'cie76' (faster, default); 'cie2000' is a legacy alias
160
167
  });
161
168
 
162
- // Color space selection for hue rotation (v1.16.0+)
169
+ // Color space selection for hue rotation
163
170
  // OKLCH produces more perceptually balanced harmonies
164
171
  const triadicOklch = dyeService.findTriadicDyes('#FF6B6B', { colorSpace: 'oklch' });
165
172
  const compLch = dyeService.findComplementaryPair('#FF6B6B', { colorSpace: 'lch' });
@@ -181,7 +188,7 @@ const filtered = dyeService.filterDyes({
181
188
  Multi-color palette extraction from images using K-means++ clustering.
182
189
 
183
190
  ```typescript
184
- import { PaletteService, DyeService, dyeDatabase } from 'xivdyetools-core';
191
+ import { PaletteService, DyeService, dyeDatabase } from '@xivdyetools/core';
185
192
 
186
193
  const paletteService = new PaletteService();
187
194
  const dyeService = new DyeService(dyeDatabase);
@@ -203,9 +210,6 @@ const matches = paletteService.extractAndMatchPalette(pixels, dyeService, {
203
210
  });
204
211
  // Returns: Array<{ extracted: RGB, matchedDye: Dye, distance: number, dominance: number }>
205
212
 
206
- // Helper: Convert raw pixel buffer (RGB, 3 bytes per pixel)
207
- const pixelsFromBuffer = PaletteService.pixelDataToRGB(buffer);
208
-
209
213
  // Helper: Convert RGBA ImageData, filtering transparent pixels
210
214
  const pixelsFromCanvas = PaletteService.pixelDataToRGBFiltered(imageData.data);
211
215
  ```
@@ -215,32 +219,30 @@ const pixelsFromCanvas = PaletteService.pixelDataToRGBFiltered(imageData.data);
215
219
  Universalis API integration with pluggable cache backends.
216
220
 
217
221
  ```typescript
218
- import { APIService, MemoryCacheBackend } from 'xivdyetools-core';
222
+ import { APIService, MemoryCacheBackend } from '@xivdyetools/core';
219
223
 
220
224
  // With memory cache (default)
221
225
  const apiService = new APIService();
222
226
 
223
227
  // With custom cache backend
224
228
  const cache = new MemoryCacheBackend();
225
- const apiService = new APIService(cache);
229
+ const apiService = new APIService({ cacheBackend: cache });
226
230
 
227
231
  // Fetch price data
228
232
  const priceData = await apiService.getPriceData(5752); // itemID
229
233
  const pricesWithDC = await apiService.getPriceData(5752, undefined, 'Aether');
230
234
 
231
- // Batch operations
232
- const prices = await apiService.getPricesForItems([5752, 5753, 5754]);
235
+ // Batch operations (per data center)
236
+ const prices = await apiService.getPricesForDataCenter([5752, 5753, 5754], 'Aether');
233
237
 
234
238
  // Cache management
235
239
  await apiService.clearCache();
236
- const stats = await apiService.getCacheStats();
237
240
 
238
241
  // API health check
239
242
  const { available, latency } = await apiService.getAPIStatus();
240
243
 
241
244
  // Utility methods
242
245
  const formatted = APIService.formatPrice(123456); // "123,456G"
243
- const trend = APIService.getPriceTrend(100, 80); // { trend: 'up', ... }
244
246
  ```
245
247
 
246
248
  ## Custom Cache Backends
@@ -248,7 +250,7 @@ const trend = APIService.getPriceTrend(100, 80); // { trend: 'up', ... }
248
250
  Implement the `ICacheBackend` interface for custom storage:
249
251
 
250
252
  ```typescript
251
- import { ICacheBackend, CachedData, PriceData } from 'xivdyetools-core';
253
+ import { ICacheBackend, CachedData, PriceData } from '@xivdyetools/core';
252
254
  import Redis from 'ioredis';
253
255
 
254
256
  class RedisCacheBackend implements ICacheBackend {
@@ -279,7 +281,7 @@ class RedisCacheBackend implements ICacheBackend {
279
281
  // Use with APIService
280
282
  const redis = new Redis();
281
283
  const cache = new RedisCacheBackend(redis);
282
- const apiService = new APIService(cache);
284
+ const apiService = new APIService({ cacheBackend: cache });
283
285
  ```
284
286
 
285
287
  ## TypeScript Types
@@ -303,7 +305,7 @@ import type {
303
305
  HarmonyMatchingAlgorithm,
304
306
  HarmonyColorSpace,
305
307
  DeltaEFormula
306
- } from 'xivdyetools-core';
308
+ } from '@xivdyetools/core';
307
309
  ```
308
310
 
309
311
  ## Constants
@@ -314,11 +316,10 @@ Access color theory and API configuration constants:
314
316
  import {
315
317
  RGB_MAX,
316
318
  HUE_MAX,
317
- VISION_TYPES,
318
319
  BRETTEL_MATRICES,
319
320
  UNIVERSALIS_API_BASE,
320
321
  API_CACHE_TTL
321
- } from 'xivdyetools-core';
322
+ } from '@xivdyetools/core';
322
323
  ```
323
324
 
324
325
  ## Utilities
@@ -328,20 +329,18 @@ Helper functions for common tasks:
328
329
  ```typescript
329
330
  import {
330
331
  clamp,
331
- lerp,
332
332
  isValidHexColor,
333
333
  isValidRGB,
334
334
  retry,
335
335
  sleep,
336
336
  generateChecksum
337
- } from 'xivdyetools-core';
337
+ } from '@xivdyetools/core';
338
338
 
339
339
  // Validation
340
340
  const isValid = isValidHexColor('#FF6B6B'); // true
341
341
 
342
342
  // Math
343
343
  const clamped = clamp(150, 0, 100); // 100
344
- const interpolated = lerp(0, 100, 0.5); // 50
345
344
 
346
345
  // Async utilities
347
346
  await sleep(1000); // Wait 1 second
@@ -353,7 +352,7 @@ const result = await retry(() => fetchData(), 3, 1000); // Retry with backoff
353
352
  ### Discord Bot
354
353
  ```typescript
355
354
  // Implement /harmony command
356
- import { DyeService, dyeDatabase } from 'xivdyetools-core';
355
+ import { DyeService, dyeDatabase } from '@xivdyetools/core';
357
356
 
358
357
  const dyeService = new DyeService(dyeDatabase);
359
358
  const baseDye = dyeService.findClosestDye(userColor);
@@ -364,27 +363,35 @@ const harmonyDyes = dyeService.findTriadicDyes(userColor);
364
363
  ### Web App
365
364
  ```typescript
366
365
  // Color matcher tool
367
- import { DyeService, dyeDatabase } from 'xivdyetools-core';
366
+ import { DyeService, dyeDatabase } from '@xivdyetools/core';
368
367
 
369
368
  const dyeService = new DyeService(dyeDatabase);
370
- const matchingDyes = dyeService.findDyesWithinDistance(imageColor, 50, 10);
369
+ const matchingDyes = dyeService.findDyesWithinDistance(imageColor, { maxDistance: 50, limit: 10 });
371
370
  // Display results in UI
372
371
  ```
373
372
 
374
373
  ### CLI Tool
375
374
  ```typescript
376
375
  // Color conversion utility
377
- import { ColorService } from 'xivdyetools-core';
376
+ import { ColorService } from '@xivdyetools/core';
378
377
 
379
378
  const hex = process.argv[2];
380
379
  const rgb = ColorService.hexToRgb(hex);
381
380
  console.log(`RGB: ${rgb.r}, ${rgb.g}, ${rgb.b}`);
382
381
  ```
383
382
 
383
+ ## Dye Database Composition
384
+
385
+ The database is **125 standard dyes** (`src/data/dyes.json`, schema v2) keyed by `stainID`. Seven fields are stored per entry — `stainID`, `name`, `hex`, `category`, `acquisition`, `consolidationType`, `legacyItemID` — and everything else (`rgb` / `hsv` / `lab`, `cost` / `currency`, the `is*` flags) is **derived** at `DyeDatabase.initialize()`. The runtime `Dye` object still has its full shape, and `Dye.itemID` is always a `number`.
386
+
387
+ The **11 Facewear colors are not dyes.** They live separately in `facewear_colors.json` / the `facewearColors` export as `FacewearColor` objects (string slug `id`, `name`, `hex`) and are excluded from the k-d tree, since they are not market-tradeable.
388
+
389
+ Since **Patch 7.5**, 105 of the 125 dyes share three consolidated market itemIDs (Type-A `52254`, Type-B `52255`, Type-C `52256`). Use `getMarketItemID(dye)` for any market-board lookup — a hard-coded legacy itemID will not price correctly.
390
+
384
391
  ## Requirements
385
392
 
386
- - **Node.js** 18.0.0 or higher
387
- - **TypeScript** 5.3 or higher (for development)
393
+ - **Node.js** 22.0.0 or higher
394
+ - **TypeScript** 5.9 or higher (for development)
388
395
 
389
396
  ## Browser Compatibility
390
397
 
@@ -393,44 +400,55 @@ Works in all modern browsers with ES6 module support:
393
400
  - Firefox 88+
394
401
  - Safari 15+
395
402
 
396
- ## License
397
-
398
- MIT © 2025-2026 Flash Galatine
399
-
400
- See [LICENSE](./LICENSE) for full details.
401
-
402
- ## Legal Notice
403
-
404
- **This is a fan-made tool and is not affiliated with or endorsed by Square Enix Co., Ltd. FINAL FANTASY is a registered trademark of Square Enix Holdings Co., Ltd.**
403
+ ## Related Projects
405
404
 
406
- ## Coming Soon
405
+ This package lives in the [xivdyetools monorepo](https://github.com/FlashGalatine/xivdyetools). Its direct consumers:
407
406
 
408
- **Budget-Aware Dye Suggestions** - Find affordable alternatives to expensive dyes based on current market prices. See [specification](../xivdyetools-docs/BUDGET_AWARE_SUGGESTIONS.md) for details.
407
+ - [`apps/web-app`](../../apps/web-app/) — interactive color tools for FFXIV
408
+ - [`apps/discord-worker`](../../apps/discord-worker/) — Cloudflare Worker Discord bot
409
+ - [`apps/api-worker`](../../apps/api-worker/) — public REST API
410
+ - [`apps/og-worker`](../../apps/og-worker/) — OpenGraph cards (stateless localization trio)
411
+ - [`apps/stoat-worker`](../../apps/stoat-worker/) — Revolt bot (parked)
412
+ - [`@xivdyetools/svg`](../svg/), [`@xivdyetools/bot-logic`](../bot-logic/)
409
413
 
410
- ## Related Projects
414
+ ## Support
411
415
 
412
- - [XIV Dye Tools Web App](https://github.com/FlashGalatine/xivdyetools-web-app) - Interactive color tools for FFXIV
413
- - [XIV Dye Tools Discord Worker](https://github.com/FlashGalatine/xivdyetools-discord-worker) - Cloudflare Worker Discord bot using this package
416
+ - **Issues**: [GitHub Issues](https://github.com/FlashGalatine/xivdyetools/issues)
417
+ - **NPM Package**: [@xivdyetools/core](https://www.npmjs.com/package/@xivdyetools/core)
418
+ - **API Docs**: [developers.xivdyetools.app](https://developers.xivdyetools.app)
414
419
 
415
- ## Support
420
+ ## Credits & Acknowledgements
416
421
 
417
- - **Issues**: [GitHub Issues](https://github.com/FlashGalatine/xivdyetools-core/issues)
418
- - **NPM Package**: [xivdyetools-core](https://www.npmjs.com/package/xivdyetools-core)
419
- - **Documentation**: [Full Docs](https://github.com/FlashGalatine/xivdyetools-core#readme)
422
+ - **[XIVAPI](https://xivapi.com/)** — dye names in English, Japanese, German, and French. Korean and Chinese names are manually sourced; XIVAPI does not serve them.
423
+ - **[Universalis](https://universalis.app/)** (MIT) — market board price data consumed by `APIService`.
424
+ - **[spectral.js](https://github.com/rvanwijnen/spectral.js)** (MIT) — Kubelka-Munk spectral paint mixing.
425
+ - Color-vision deficiency simulation uses the matrices from **Brettel, Viénot & Mollon (1997)**, *"Computerized simulation of color appearance for dichromats"*, JOSA A 14(10).
426
+ - Perceptual color difference uses **CIE76** and **CIEDE2000** as published by the International Commission on Illumination.
420
427
 
421
428
  ## Connect With Me
422
429
 
423
430
  **Flash Galatine** | Midgardsormr (Aether)
424
431
 
425
432
  🎮 **FFXIV**: [Lodestone Character](https://na.finalfantasyxiv.com/lodestone/character/7677106/)
426
- 📝 **Blog**: [Project Galatine](https://blog.projectgalatine.com/)
427
433
  💻 **GitHub**: [@FlashGalatine](https://github.com/FlashGalatine)
434
+ 🐦 **X/Twitter**: [@AsheJunius](https://x.com/AsheJunius)
428
435
  📺 **Twitch**: [flashgalatine](https://www.twitch.tv/flashgalatine)
429
436
  🌐 **BlueSky**: [projectgalatine.com](https://bsky.app/profile/projectgalatine.com)
430
437
  ❤️ **Patreon**: [ProjectGalatine](https://patreon.com/ProjectGalatine)
431
438
  ☕ **Ko-Fi**: [flashgalatine](https://ko-fi.com/flashgalatine)
432
439
  💬 **Discord**: [Join Server](https://discord.gg/5VUSKTZCe5)
433
440
 
441
+ ## License
442
+
443
+ MIT © 2025-2026 Flash Galatine — see [LICENSE](./LICENSE).
444
+
445
+ ## Legal Notice
446
+
447
+ **FINAL FANTASY is a registered trademark of Square Enix Holdings Co., Ltd.**
448
+ **FINAL FANTASY XIV © SQUARE ENIX CO., LTD.**
449
+
450
+ XIV Dye Tools is an unofficial fan project and is **not affiliated with, endorsed by, or sponsored by Square Enix Co., Ltd.** All FINAL FANTASY XIV content, including dye names and color values, is the property of Square Enix.
451
+
434
452
  ---
435
453
 
436
454
  **Made with ❤️ for the FFXIV community**
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Color Blending — Six algorithms for mixing two colors.
3
+ *
4
+ * Blending Modes:
5
+ * - RGB: Simple additive channel averaging
6
+ * - LAB: Perceptually uniform CIELAB blending
7
+ * - OKLAB: Modern perceptual (fixes LAB blue→purple issue)
8
+ * - RYB: Traditional artist's color wheel
9
+ * - HSL: Hue-Saturation-Lightness interpolation
10
+ * - Spectral: Kubelka-Munk physics simulation
11
+ */
12
+ import type { BlendResult, BlendingMode } from './types.js';
13
+ /**
14
+ * Blend two colors using the specified blending mode.
15
+ *
16
+ * @param hex1 - First color hex code (with or without #)
17
+ * @param hex2 - Second color hex code (with or without #)
18
+ * @param mode - Blending algorithm to use
19
+ * @param ratio - 0.0 = all hex1, 0.5 = equal mix, 1.0 = all hex2
20
+ */
21
+ export declare function blendColors(hex1: string, hex2: string, mode: BlendingMode, ratio?: number): BlendResult;
22
+ //# sourceMappingURL=blending.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blending.d.ts","sourceRoot":"","sources":["../../src/blending/blending.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAiB,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAsB3E;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,YAAY,EAClB,KAAK,GAAE,MAAY,GAClB,WAAW,CAmCb"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Color Blending — Six algorithms for mixing two colors.
3
+ *
4
+ * Blending Modes:
5
+ * - RGB: Simple additive channel averaging
6
+ * - LAB: Perceptually uniform CIELAB blending
7
+ * - OKLAB: Modern perceptual (fixes LAB blue→purple issue)
8
+ * - RYB: Traditional artist's color wheel
9
+ * - HSL: Hue-Saturation-Lightness interpolation
10
+ * - Spectral: Kubelka-Munk physics simulation
11
+ */
12
+ import { rgbToLab, labToRgb, rgbToOklab, oklabToRgb, rgbToRyb, rybToRgb, rgbToHsl, hslToRgb, rgbToReflectance, reflectanceToRgb, reflectanceToKS, ksToReflectance, rgbToHex, hexToRgb, } from './conversions.js';
13
+ // ============================================================================
14
+ // Public API
15
+ // ============================================================================
16
+ /**
17
+ * Blend two colors using the specified blending mode.
18
+ *
19
+ * @param hex1 - First color hex code (with or without #)
20
+ * @param hex2 - Second color hex code (with or without #)
21
+ * @param mode - Blending algorithm to use
22
+ * @param ratio - 0.0 = all hex1, 0.5 = equal mix, 1.0 = all hex2
23
+ */
24
+ export function blendColors(hex1, hex2, mode, ratio = 0.5) {
25
+ const h1 = hex1.startsWith('#') ? hex1 : `#${hex1}`;
26
+ const h2 = hex2.startsWith('#') ? hex2 : `#${hex2}`;
27
+ const t = Math.max(0, Math.min(1, ratio));
28
+ // REFACTOR-005: local parser — no more dependency on all of core
29
+ const rgb1 = hexToRgb(h1);
30
+ const rgb2 = hexToRgb(h2);
31
+ let blendedRgb;
32
+ switch (mode) {
33
+ case 'rgb':
34
+ blendedRgb = blendRGB(rgb1, rgb2, t);
35
+ break;
36
+ case 'lab':
37
+ blendedRgb = blendLAB(rgb1, rgb2, t);
38
+ break;
39
+ case 'oklab':
40
+ blendedRgb = blendOKLAB(rgb1, rgb2, t);
41
+ break;
42
+ case 'ryb':
43
+ blendedRgb = blendRYB(rgb1, rgb2, t);
44
+ break;
45
+ case 'hsl':
46
+ blendedRgb = blendHSL(rgb1, rgb2, t);
47
+ break;
48
+ case 'spectral':
49
+ blendedRgb = blendSpectral(rgb1, rgb2, t);
50
+ break;
51
+ default:
52
+ blendedRgb = blendRGB(rgb1, rgb2, t);
53
+ }
54
+ return { hex: rgbToHex(blendedRgb), rgb: blendedRgb };
55
+ }
56
+ // ============================================================================
57
+ // Blend Implementations
58
+ // ============================================================================
59
+ function blendRGB(rgb1, rgb2, t) {
60
+ return {
61
+ r: Math.round(rgb1.r * (1 - t) + rgb2.r * t),
62
+ g: Math.round(rgb1.g * (1 - t) + rgb2.g * t),
63
+ b: Math.round(rgb1.b * (1 - t) + rgb2.b * t),
64
+ };
65
+ }
66
+ function blendLAB(rgb1, rgb2, t) {
67
+ const lab1 = rgbToLab(rgb1);
68
+ const lab2 = rgbToLab(rgb2);
69
+ const blended = {
70
+ l: lab1.l * (1 - t) + lab2.l * t,
71
+ a: lab1.a * (1 - t) + lab2.a * t,
72
+ b: lab1.b * (1 - t) + lab2.b * t,
73
+ };
74
+ return labToRgb(blended);
75
+ }
76
+ function blendOKLAB(rgb1, rgb2, t) {
77
+ const ok1 = rgbToOklab(rgb1);
78
+ const ok2 = rgbToOklab(rgb2);
79
+ return oklabToRgb({
80
+ L: ok1.L * (1 - t) + ok2.L * t,
81
+ a: ok1.a * (1 - t) + ok2.a * t,
82
+ b: ok1.b * (1 - t) + ok2.b * t,
83
+ });
84
+ }
85
+ function blendRYB(rgb1, rgb2, t) {
86
+ const ryb1 = rgbToRyb(rgb1);
87
+ const ryb2 = rgbToRyb(rgb2);
88
+ return rybToRgb({
89
+ r: ryb1.r * (1 - t) + ryb2.r * t,
90
+ y: ryb1.y * (1 - t) + ryb2.y * t,
91
+ b: ryb1.b * (1 - t) + ryb2.b * t,
92
+ });
93
+ }
94
+ function blendHSL(rgb1, rgb2, t) {
95
+ const hsl1 = rgbToHsl(rgb1);
96
+ const hsl2 = rgbToHsl(rgb2);
97
+ // Shortest-arc hue interpolation
98
+ let hueDiff = hsl2.h - hsl1.h;
99
+ if (hueDiff > 180)
100
+ hueDiff -= 360;
101
+ if (hueDiff < -180)
102
+ hueDiff += 360;
103
+ let blendedH = hsl1.h + hueDiff * t;
104
+ if (blendedH < 0)
105
+ blendedH += 360;
106
+ if (blendedH >= 360)
107
+ blendedH -= 360;
108
+ const blended = {
109
+ h: blendedH,
110
+ s: hsl1.s * (1 - t) + hsl2.s * t,
111
+ l: hsl1.l * (1 - t) + hsl2.l * t,
112
+ };
113
+ return hslToRgb(blended);
114
+ }
115
+ function blendSpectral(rgb1, rgb2, t) {
116
+ const ref1 = rgbToReflectance(rgb1);
117
+ const ref2 = rgbToReflectance(rgb2);
118
+ return reflectanceToRgb({
119
+ r: kubelkaMunkMix(ref1.r, ref2.r, t),
120
+ g: kubelkaMunkMix(ref1.g, ref2.g, t),
121
+ b: kubelkaMunkMix(ref1.b, ref2.b, t),
122
+ });
123
+ }
124
+ function kubelkaMunkMix(r1, r2, t) {
125
+ const ks1 = reflectanceToKS(r1);
126
+ const ks2 = reflectanceToKS(r2);
127
+ return ksToReflectance(ks1 * (1 - t) + ks2 * t);
128
+ }
129
+ //# sourceMappingURL=blending.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blending.js","sourceRoot":"","sources":["../../src/blending/blending.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { RGB, LAB, HSL } from './types.js';
2
+ /**
3
+ * RGB to CIELAB conversion.
4
+ * Exported for use in SVG generators that display LAB values.
5
+ */
6
+ export declare function rgbToLab(rgb: RGB): LAB;
7
+ export declare function labToRgb(lab: LAB): RGB;
8
+ export declare function rgbToOklab(rgb: RGB): {
9
+ L: number;
10
+ a: number;
11
+ b: number;
12
+ };
13
+ export declare function oklabToRgb(oklab: {
14
+ L: number;
15
+ a: number;
16
+ b: number;
17
+ }): RGB;
18
+ export declare function rgbToRyb(rgb: RGB): {
19
+ r: number;
20
+ y: number;
21
+ b: number;
22
+ };
23
+ export declare function rybToRgb(ryb: {
24
+ r: number;
25
+ y: number;
26
+ b: number;
27
+ }): RGB;
28
+ export declare function rgbToHsl(rgb: RGB): HSL;
29
+ export declare function hslToRgb(hsl: HSL): RGB;
30
+ export declare function rgbToReflectance(rgb: RGB): {
31
+ r: number;
32
+ g: number;
33
+ b: number;
34
+ };
35
+ export declare function reflectanceToRgb(ref: {
36
+ r: number;
37
+ g: number;
38
+ b: number;
39
+ }): RGB;
40
+ export declare function reflectanceToKS(r: number): number;
41
+ export declare function ksToReflectance(ks: number): number;
42
+ /**
43
+ * Parse a hex color string into RGB channels.
44
+ *
45
+ * REFACTOR-005 (2026-07-18 audit): local implementation replacing the sole use
46
+ * of `@xivdyetools/core`'s ColorService — the entire core package (dye
47
+ * database, k-d tree, i18n) was a dependency for these ~10 lines. Matches
48
+ * core's strict behavior: throws on malformed input; accepts #RGB and #RRGGBB
49
+ * (leading # optional).
50
+ */
51
+ export declare function hexToRgb(hex: string): RGB;
52
+ export declare function rgbToHex(rgb: RGB): string;
53
+ //# sourceMappingURL=conversions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversions.d.ts","sourceRoot":"","sources":["../../src/blending/conversions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAOhD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAyBtC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CA8BtC;AAMD,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAwBxE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CAuB1E;AAMD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAwBtE;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CA0BtE;AAMD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CA8BtC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CA2BtC;AAMD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAE9E;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CAM9E;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAGlD;AAMD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAiBzC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAGzC"}