@xivdyetools/core 1.3.7
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/LICENSE +37 -0
- package/README.md +400 -0
- package/dist/constants/index.d.ts +56 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +103 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/data/colors_xiv.json +3130 -0
- package/dist/data/locales/de.json +231 -0
- package/dist/data/locales/en.json +231 -0
- package/dist/data/locales/fr.json +231 -0
- package/dist/data/locales/ja.json +231 -0
- package/dist/data/locales/ko.json +233 -0
- package/dist/data/locales/zh.json +233 -0
- package/dist/data/presets.json +390 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/services/APIService.d.ts +246 -0
- package/dist/services/APIService.d.ts.map +1 -0
- package/dist/services/APIService.js +499 -0
- package/dist/services/APIService.js.map +1 -0
- package/dist/services/ColorService.d.ts +146 -0
- package/dist/services/ColorService.d.ts.map +1 -0
- package/dist/services/ColorService.js +209 -0
- package/dist/services/ColorService.js.map +1 -0
- package/dist/services/DyeService.d.ts +230 -0
- package/dist/services/DyeService.d.ts.map +1 -0
- package/dist/services/DyeService.js +326 -0
- package/dist/services/DyeService.js.map +1 -0
- package/dist/services/LocalizationService.d.ts +338 -0
- package/dist/services/LocalizationService.d.ts.map +1 -0
- package/dist/services/LocalizationService.js +449 -0
- package/dist/services/LocalizationService.js.map +1 -0
- package/dist/services/PaletteService.d.ts +137 -0
- package/dist/services/PaletteService.d.ts.map +1 -0
- package/dist/services/PaletteService.js +349 -0
- package/dist/services/PaletteService.js.map +1 -0
- package/dist/services/PresetService.d.ts +196 -0
- package/dist/services/PresetService.d.ts.map +1 -0
- package/dist/services/PresetService.js +261 -0
- package/dist/services/PresetService.js.map +1 -0
- package/dist/services/color/ColorAccessibility.d.ts +39 -0
- package/dist/services/color/ColorAccessibility.d.ts.map +1 -0
- package/dist/services/color/ColorAccessibility.js +71 -0
- package/dist/services/color/ColorAccessibility.js.map +1 -0
- package/dist/services/color/ColorConverter.d.ts +146 -0
- package/dist/services/color/ColorConverter.d.ts.map +1 -0
- package/dist/services/color/ColorConverter.js +393 -0
- package/dist/services/color/ColorConverter.js.map +1 -0
- package/dist/services/color/ColorManipulator.d.ts +36 -0
- package/dist/services/color/ColorManipulator.d.ts.map +1 -0
- package/dist/services/color/ColorManipulator.js +56 -0
- package/dist/services/color/ColorManipulator.js.map +1 -0
- package/dist/services/color/ColorblindnessSimulator.d.ts +35 -0
- package/dist/services/color/ColorblindnessSimulator.d.ts.map +1 -0
- package/dist/services/color/ColorblindnessSimulator.js +110 -0
- package/dist/services/color/ColorblindnessSimulator.js.map +1 -0
- package/dist/services/dye/DyeDatabase.d.ts +131 -0
- package/dist/services/dye/DyeDatabase.d.ts.map +1 -0
- package/dist/services/dye/DyeDatabase.js +367 -0
- package/dist/services/dye/DyeDatabase.js.map +1 -0
- package/dist/services/dye/DyeSearch.d.ts +55 -0
- package/dist/services/dye/DyeSearch.d.ts.map +1 -0
- package/dist/services/dye/DyeSearch.js +196 -0
- package/dist/services/dye/DyeSearch.js.map +1 -0
- package/dist/services/dye/HarmonyGenerator.d.ts +110 -0
- package/dist/services/dye/HarmonyGenerator.d.ts.map +1 -0
- package/dist/services/dye/HarmonyGenerator.js +221 -0
- package/dist/services/dye/HarmonyGenerator.js.map +1 -0
- package/dist/services/localization/LocaleLoader.d.ts +38 -0
- package/dist/services/localization/LocaleLoader.d.ts.map +1 -0
- package/dist/services/localization/LocaleLoader.js +83 -0
- package/dist/services/localization/LocaleLoader.js.map +1 -0
- package/dist/services/localization/LocaleRegistry.d.ts +73 -0
- package/dist/services/localization/LocaleRegistry.d.ts.map +1 -0
- package/dist/services/localization/LocaleRegistry.js +84 -0
- package/dist/services/localization/LocaleRegistry.js.map +1 -0
- package/dist/services/localization/TranslationProvider.d.ts +157 -0
- package/dist/services/localization/TranslationProvider.d.ts.map +1 -0
- package/dist/services/localization/TranslationProvider.js +289 -0
- package/dist/services/localization/TranslationProvider.js.map +1 -0
- package/dist/types/index.d.ts +409 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +87 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/logger.d.ts +84 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/logger.js +54 -0
- package/dist/types/logger.js.map +1 -0
- package/dist/utils/index.d.ts +441 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +577 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/kd-tree.d.ts +76 -0
- package/dist/utils/kd-tree.d.ts.map +1 -0
- package/dist/utils/kd-tree.js +195 -0
- package/dist/utils/kd-tree.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +84 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Flash Galatine
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
FINAL FANTASY XIV © SQUARE ENIX CO., LTD. All Rights Reserved.
|
|
26
|
+
|
|
27
|
+
This project is a fan-made tool and is not affiliated with, endorsed by, or
|
|
28
|
+
sponsored by Square Enix Co., Ltd. FINAL FANTASY is a registered trademark
|
|
29
|
+
of Square Enix Holdings Co., Ltd.
|
|
30
|
+
|
|
31
|
+
Game data, including dye names, colors, and acquisition methods, are property
|
|
32
|
+
of Square Enix Co., Ltd. and are used under fair use for educational and
|
|
33
|
+
informational purposes only.
|
|
34
|
+
|
|
35
|
+
Market board data is provided by Universalis (https://universalis.app/),
|
|
36
|
+
an independent third-party service not affiliated with Square Enix.
|
|
37
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# xivdyetools-core
|
|
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.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/xivdyetools-core)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
✨ **Color Conversion** - RGB ↔ HSV ↔ Hex with full validation
|
|
12
|
+
🎨 **136 FFXIV Dyes** - Complete database with RGB/HSV/metadata
|
|
13
|
+
🎯 **Dye Matching** - Find closest dyes to any color
|
|
14
|
+
🌈 **Color Harmonies** - Triadic, complementary, analogous, and more
|
|
15
|
+
🖼️ **Palette Extraction** - K-means++ clustering for multi-color extraction from images
|
|
16
|
+
♿ **Accessibility** - Colorblindness simulation (Brettel 1997)
|
|
17
|
+
📡 **Universalis API** - Price data integration with caching
|
|
18
|
+
🔌 **Pluggable Cache** - Memory, localStorage, Redis support
|
|
19
|
+
🌍 **Environment Agnostic** - Works in Node.js, browsers, edge runtimes
|
|
20
|
+
🗣️ **6 Languages** - English, Japanese, German, French, Korean, Chinese
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install xivdyetools-core
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### Browser (with bundler)
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { ColorService, DyeService, dyeDatabase } from 'xivdyetools-core';
|
|
34
|
+
|
|
35
|
+
// Initialize services
|
|
36
|
+
const dyeService = new DyeService(dyeDatabase);
|
|
37
|
+
|
|
38
|
+
// Find closest dye to a color
|
|
39
|
+
const closestDye = dyeService.findClosestDye('#FF6B6B');
|
|
40
|
+
console.log(closestDye.name); // "Coral Pink"
|
|
41
|
+
|
|
42
|
+
// Generate color harmonies
|
|
43
|
+
const triadicDyes = dyeService.findTriadicDyes('#FF6B6B');
|
|
44
|
+
console.log(triadicDyes.map(d => d.name)); // ["Turquoise Green", "Grape Purple"]
|
|
45
|
+
|
|
46
|
+
// Color conversions
|
|
47
|
+
const rgb = ColorService.hexToRgb('#FF6B6B');
|
|
48
|
+
const hsv = ColorService.rgbToHsv(rgb.r, rgb.g, rgb.b);
|
|
49
|
+
console.log(hsv); // { h: 0, s: 58.04, v: 100 }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Node.js (Discord bot, API, CLI)
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import {
|
|
56
|
+
DyeService,
|
|
57
|
+
APIService,
|
|
58
|
+
dyeDatabase
|
|
59
|
+
} from 'xivdyetools-core';
|
|
60
|
+
import Redis from 'ioredis';
|
|
61
|
+
|
|
62
|
+
// Initialize with Redis cache (for Discord bots)
|
|
63
|
+
const redis = new Redis();
|
|
64
|
+
const cacheBackend = new RedisCacheBackend(redis);
|
|
65
|
+
const apiService = new APIService(cacheBackend);
|
|
66
|
+
const dyeService = new DyeService(dyeDatabase);
|
|
67
|
+
|
|
68
|
+
// Fetch live market prices
|
|
69
|
+
const priceData = await apiService.getPriceData(5752); // Jet Black Dye
|
|
70
|
+
console.log(`${priceData.currentMinPrice} Gil`);
|
|
71
|
+
|
|
72
|
+
// Find harmony with pricing
|
|
73
|
+
const baseDye = dyeService.findClosestDye('#000000');
|
|
74
|
+
const harmonyDyes = dyeService.findComplementaryPair(baseDye.hex);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Core Services
|
|
78
|
+
|
|
79
|
+
### ColorService
|
|
80
|
+
|
|
81
|
+
Pure color conversion and manipulation algorithms.
|
|
82
|
+
|
|
83
|
+
> **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.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { ColorService } from 'xivdyetools-core';
|
|
87
|
+
|
|
88
|
+
// Hex ↔ RGB
|
|
89
|
+
const rgb = ColorService.hexToRgb('#FF6B6B');
|
|
90
|
+
const hex = ColorService.rgbToHex(255, 107, 107);
|
|
91
|
+
|
|
92
|
+
// RGB ↔ HSV
|
|
93
|
+
const hsv = ColorService.rgbToHsv(255, 107, 107);
|
|
94
|
+
const rgbFromHsv = ColorService.hsvToRgb(0, 58.04, 100);
|
|
95
|
+
|
|
96
|
+
// Colorblindness simulation
|
|
97
|
+
const simulated = ColorService.simulateColorblindness(
|
|
98
|
+
{ r: 255, g: 0, b: 0 },
|
|
99
|
+
'deuteranopia'
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Color distance (Euclidean in RGB space)
|
|
103
|
+
const distance = ColorService.getColorDistance('#FF0000', '#00FF00');
|
|
104
|
+
|
|
105
|
+
// Color inversion
|
|
106
|
+
const inverted = ColorService.invert('#FF6B6B');
|
|
107
|
+
|
|
108
|
+
// Cache management (for memory-constrained environments)
|
|
109
|
+
ColorService.clearCaches();
|
|
110
|
+
const cacheStats = ColorService.getCacheStats();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### DyeService
|
|
114
|
+
|
|
115
|
+
FFXIV dye database management and color matching.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { DyeService, dyeDatabase } from 'xivdyetools-core';
|
|
119
|
+
|
|
120
|
+
const dyeService = new DyeService(dyeDatabase);
|
|
121
|
+
|
|
122
|
+
// Database access
|
|
123
|
+
const allDyes = dyeService.getAllDyes(); // 136 dyes
|
|
124
|
+
const dyeById = dyeService.getDyeById(5752); // Jet Black Dye
|
|
125
|
+
const categories = dyeService.getCategories(); // ['Neutral', 'Red', 'Blue', ...]
|
|
126
|
+
|
|
127
|
+
// Color matching
|
|
128
|
+
const closest = dyeService.findClosestDye('#FF6B6B');
|
|
129
|
+
const nearby = dyeService.findDyesWithinDistance('#FF6B6B', 50, 5);
|
|
130
|
+
|
|
131
|
+
// Harmony generation
|
|
132
|
+
const triadic = dyeService.findTriadicDyes('#FF6B6B');
|
|
133
|
+
const complementary = dyeService.findComplementaryPair('#FF6B6B');
|
|
134
|
+
const analogous = dyeService.findAnalogousDyes('#FF6B6B', 30);
|
|
135
|
+
const monochromatic = dyeService.findMonochromaticDyes('#FF6B6B', 6);
|
|
136
|
+
const splitComplementary = dyeService.findSplitComplementaryDyes('#FF6B6B');
|
|
137
|
+
|
|
138
|
+
// Filtering
|
|
139
|
+
const redDyes = dyeService.searchByCategory('Red');
|
|
140
|
+
const searchResults = dyeService.searchByName('black');
|
|
141
|
+
const filtered = dyeService.filterDyes({
|
|
142
|
+
category: 'Special',
|
|
143
|
+
excludeIds: [5752, 5753],
|
|
144
|
+
minPrice: 0,
|
|
145
|
+
maxPrice: 10000
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### PaletteService
|
|
150
|
+
|
|
151
|
+
Multi-color palette extraction from images using K-means++ clustering.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { PaletteService, DyeService, dyeDatabase } from 'xivdyetools-core';
|
|
155
|
+
|
|
156
|
+
const paletteService = new PaletteService();
|
|
157
|
+
const dyeService = new DyeService(dyeDatabase);
|
|
158
|
+
|
|
159
|
+
// Extract from Canvas ImageData
|
|
160
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
161
|
+
const pixels = PaletteService.pixelDataToRGBFiltered(imageData.data);
|
|
162
|
+
|
|
163
|
+
// Extract dominant colors only
|
|
164
|
+
const palette = paletteService.extractPalette(pixels, { colorCount: 4 });
|
|
165
|
+
// Returns: Array<{ color: RGB, dominance: number }>
|
|
166
|
+
|
|
167
|
+
// Extract and match to FFXIV dyes
|
|
168
|
+
const matches = paletteService.extractAndMatchPalette(pixels, dyeService, {
|
|
169
|
+
colorCount: 4,
|
|
170
|
+
maxIterations: 25,
|
|
171
|
+
convergenceThreshold: 1.0,
|
|
172
|
+
maxSamples: 10000
|
|
173
|
+
});
|
|
174
|
+
// Returns: Array<{ extracted: RGB, matchedDye: Dye, distance: number, dominance: number }>
|
|
175
|
+
|
|
176
|
+
// Helper: Convert raw pixel buffer (RGB, 3 bytes per pixel)
|
|
177
|
+
const pixelsFromBuffer = PaletteService.pixelDataToRGB(buffer);
|
|
178
|
+
|
|
179
|
+
// Helper: Convert RGBA ImageData, filtering transparent pixels
|
|
180
|
+
const pixelsFromCanvas = PaletteService.pixelDataToRGBFiltered(imageData.data);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### APIService
|
|
184
|
+
|
|
185
|
+
Universalis API integration with pluggable cache backends.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { APIService, MemoryCacheBackend } from 'xivdyetools-core';
|
|
189
|
+
|
|
190
|
+
// With memory cache (default)
|
|
191
|
+
const apiService = new APIService();
|
|
192
|
+
|
|
193
|
+
// With custom cache backend
|
|
194
|
+
const cache = new MemoryCacheBackend();
|
|
195
|
+
const apiService = new APIService(cache);
|
|
196
|
+
|
|
197
|
+
// Fetch price data
|
|
198
|
+
const priceData = await apiService.getPriceData(5752); // itemID
|
|
199
|
+
const pricesWithDC = await apiService.getPriceData(5752, undefined, 'Aether');
|
|
200
|
+
|
|
201
|
+
// Batch operations
|
|
202
|
+
const prices = await apiService.getPricesForItems([5752, 5753, 5754]);
|
|
203
|
+
|
|
204
|
+
// Cache management
|
|
205
|
+
await apiService.clearCache();
|
|
206
|
+
const stats = await apiService.getCacheStats();
|
|
207
|
+
|
|
208
|
+
// API health check
|
|
209
|
+
const { available, latency } = await apiService.getAPIStatus();
|
|
210
|
+
|
|
211
|
+
// Utility methods
|
|
212
|
+
const formatted = APIService.formatPrice(123456); // "123,456G"
|
|
213
|
+
const trend = APIService.getPriceTrend(100, 80); // { trend: 'up', ... }
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Custom Cache Backends
|
|
217
|
+
|
|
218
|
+
Implement the `ICacheBackend` interface for custom storage:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { ICacheBackend, CachedData, PriceData } from 'xivdyetools-core';
|
|
222
|
+
import Redis from 'ioredis';
|
|
223
|
+
|
|
224
|
+
class RedisCacheBackend implements ICacheBackend {
|
|
225
|
+
constructor(private redis: Redis) {}
|
|
226
|
+
|
|
227
|
+
async get(key: string): Promise<CachedData<PriceData> | null> {
|
|
228
|
+
const data = await this.redis.get(key);
|
|
229
|
+
return data ? JSON.parse(data) : null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async set(key: string, value: CachedData<PriceData>): Promise<void> {
|
|
233
|
+
await this.redis.set(key, JSON.stringify(value), 'EX', value.ttl / 1000);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async delete(key: string): Promise<void> {
|
|
237
|
+
await this.redis.del(key);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async clear(): Promise<void> {
|
|
241
|
+
await this.redis.flushdb();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async keys(): Promise<string[]> {
|
|
245
|
+
return await this.redis.keys('*');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Use with APIService
|
|
250
|
+
const redis = new Redis();
|
|
251
|
+
const cache = new RedisCacheBackend(redis);
|
|
252
|
+
const apiService = new APIService(cache);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## TypeScript Types
|
|
256
|
+
|
|
257
|
+
All services are fully typed with TypeScript:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import type {
|
|
261
|
+
Dye,
|
|
262
|
+
RGB,
|
|
263
|
+
HSV,
|
|
264
|
+
HexColor,
|
|
265
|
+
PriceData,
|
|
266
|
+
CachedData,
|
|
267
|
+
VisionType,
|
|
268
|
+
ErrorSeverity,
|
|
269
|
+
ICacheBackend
|
|
270
|
+
} from 'xivdyetools-core';
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Constants
|
|
274
|
+
|
|
275
|
+
Access color theory and API configuration constants:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import {
|
|
279
|
+
RGB_MAX,
|
|
280
|
+
HUE_MAX,
|
|
281
|
+
VISION_TYPES,
|
|
282
|
+
BRETTEL_MATRICES,
|
|
283
|
+
UNIVERSALIS_API_BASE,
|
|
284
|
+
API_CACHE_TTL
|
|
285
|
+
} from 'xivdyetools-core';
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Utilities
|
|
289
|
+
|
|
290
|
+
Helper functions for common tasks:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import {
|
|
294
|
+
clamp,
|
|
295
|
+
lerp,
|
|
296
|
+
isValidHexColor,
|
|
297
|
+
isValidRGB,
|
|
298
|
+
retry,
|
|
299
|
+
sleep,
|
|
300
|
+
generateChecksum
|
|
301
|
+
} from 'xivdyetools-core';
|
|
302
|
+
|
|
303
|
+
// Validation
|
|
304
|
+
const isValid = isValidHexColor('#FF6B6B'); // true
|
|
305
|
+
|
|
306
|
+
// Math
|
|
307
|
+
const clamped = clamp(150, 0, 100); // 100
|
|
308
|
+
const interpolated = lerp(0, 100, 0.5); // 50
|
|
309
|
+
|
|
310
|
+
// Async utilities
|
|
311
|
+
await sleep(1000); // Wait 1 second
|
|
312
|
+
const result = await retry(() => fetchData(), 3, 1000); // Retry with backoff
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Use Cases
|
|
316
|
+
|
|
317
|
+
### Discord Bot
|
|
318
|
+
```typescript
|
|
319
|
+
// Implement /harmony command
|
|
320
|
+
import { DyeService, dyeDatabase } from 'xivdyetools-core';
|
|
321
|
+
|
|
322
|
+
const dyeService = new DyeService(dyeDatabase);
|
|
323
|
+
const baseDye = dyeService.findClosestDye(userColor);
|
|
324
|
+
const harmonyDyes = dyeService.findTriadicDyes(userColor);
|
|
325
|
+
// Render color wheel, send Discord embed
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Web App
|
|
329
|
+
```typescript
|
|
330
|
+
// Color matcher tool
|
|
331
|
+
import { DyeService, dyeDatabase } from 'xivdyetools-core';
|
|
332
|
+
|
|
333
|
+
const dyeService = new DyeService(dyeDatabase);
|
|
334
|
+
const matchingDyes = dyeService.findDyesWithinDistance(imageColor, 50, 10);
|
|
335
|
+
// Display results in UI
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### CLI Tool
|
|
339
|
+
```typescript
|
|
340
|
+
// Color conversion utility
|
|
341
|
+
import { ColorService } from 'xivdyetools-core';
|
|
342
|
+
|
|
343
|
+
const hex = process.argv[2];
|
|
344
|
+
const rgb = ColorService.hexToRgb(hex);
|
|
345
|
+
console.log(`RGB: ${rgb.r}, ${rgb.g}, ${rgb.b}`);
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Requirements
|
|
349
|
+
|
|
350
|
+
- **Node.js** 18.0.0 or higher
|
|
351
|
+
- **TypeScript** 5.3 or higher (for development)
|
|
352
|
+
|
|
353
|
+
## Browser Compatibility
|
|
354
|
+
|
|
355
|
+
Works in all modern browsers with ES6 module support:
|
|
356
|
+
- Chrome/Edge 89+
|
|
357
|
+
- Firefox 88+
|
|
358
|
+
- Safari 15+
|
|
359
|
+
|
|
360
|
+
## License
|
|
361
|
+
|
|
362
|
+
MIT © 2025 Flash Galatine
|
|
363
|
+
|
|
364
|
+
See [LICENSE](./LICENSE) for full details.
|
|
365
|
+
|
|
366
|
+
## Legal Notice
|
|
367
|
+
|
|
368
|
+
**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.**
|
|
369
|
+
|
|
370
|
+
## Coming Soon
|
|
371
|
+
|
|
372
|
+
**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.
|
|
373
|
+
|
|
374
|
+
## Related Projects
|
|
375
|
+
|
|
376
|
+
- [XIV Dye Tools Web App](https://github.com/FlashGalatine/xivdyetools-web-app) - Interactive color tools for FFXIV
|
|
377
|
+
- [XIV Dye Tools Discord Worker](https://github.com/FlashGalatine/xivdyetools-discord-worker) - Cloudflare Worker Discord bot using this package
|
|
378
|
+
|
|
379
|
+
## Support
|
|
380
|
+
|
|
381
|
+
- **Issues**: [GitHub Issues](https://github.com/FlashGalatine/xivdyetools-core/issues)
|
|
382
|
+
- **NPM Package**: [xivdyetools-core](https://www.npmjs.com/package/xivdyetools-core)
|
|
383
|
+
- **Documentation**: [Full Docs](https://github.com/FlashGalatine/xivdyetools-core#readme)
|
|
384
|
+
|
|
385
|
+
## Connect With Me
|
|
386
|
+
|
|
387
|
+
**Flash Galatine** | Balmung (Crystal)
|
|
388
|
+
|
|
389
|
+
🎮 **FFXIV**: [Lodestone Character](https://na.finalfantasyxiv.com/lodestone/character/7677106/)
|
|
390
|
+
📝 **Blog**: [Project Galatine](https://blog.projectgalatine.com/)
|
|
391
|
+
💻 **GitHub**: [@FlashGalatine](https://github.com/FlashGalatine)
|
|
392
|
+
🐦 **X / Twitter**: [@AsheJunius](https://x.com/AsheJunius)
|
|
393
|
+
📺 **Twitch**: [flashgalatine](https://www.twitch.tv/flashgalatine)
|
|
394
|
+
🌐 **BlueSky**: [projectgalatine.com](https://bsky.app/profile/projectgalatine.com)
|
|
395
|
+
❤️ **Patreon**: [ProjectGalatine](https://patreon.com/ProjectGalatine)
|
|
396
|
+
💬 **Discord**: [Join Server](https://discord.gg/5VUSKTZCe5)
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
**Made with ❤️ for the FFXIV community**
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xivdyetools/core - Application Constants
|
|
3
|
+
*
|
|
4
|
+
* Centralized configuration and constant values
|
|
5
|
+
*
|
|
6
|
+
* @module constants
|
|
7
|
+
*/
|
|
8
|
+
import type { VisionType, ColorblindMatrices } from '../types/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* RGB value constraints
|
|
11
|
+
*/
|
|
12
|
+
export declare const RGB_MIN = 0;
|
|
13
|
+
export declare const RGB_MAX = 255;
|
|
14
|
+
/**
|
|
15
|
+
* HSV value constraints
|
|
16
|
+
*/
|
|
17
|
+
export declare const HUE_MIN = 0;
|
|
18
|
+
export declare const HUE_MAX = 360;
|
|
19
|
+
export declare const SATURATION_MIN = 0;
|
|
20
|
+
export declare const SATURATION_MAX = 100;
|
|
21
|
+
export declare const VALUE_MIN = 0;
|
|
22
|
+
export declare const VALUE_MAX = 100;
|
|
23
|
+
/**
|
|
24
|
+
* Color distance calculation mode
|
|
25
|
+
*/
|
|
26
|
+
export declare const COLOR_DISTANCE_MAX: number;
|
|
27
|
+
export declare const VISION_TYPES: readonly VisionType[];
|
|
28
|
+
export declare const VISION_TYPE_LABELS: Record<VisionType, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Brettel 1997 transformation matrices for colorblindness simulation
|
|
31
|
+
* These matrices transform RGB values to simulate different types of colorblindness
|
|
32
|
+
*/
|
|
33
|
+
export declare const BRETTEL_MATRICES: ColorblindMatrices;
|
|
34
|
+
/**
|
|
35
|
+
* Regex patterns for validation
|
|
36
|
+
*/
|
|
37
|
+
export declare const PATTERNS: {
|
|
38
|
+
readonly HEX_COLOR: RegExp;
|
|
39
|
+
readonly RGB_COLOR: RegExp;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Universalis API configuration
|
|
43
|
+
*/
|
|
44
|
+
export declare const UNIVERSALIS_API_BASE = "https://universalis.app/api/v2";
|
|
45
|
+
export declare const UNIVERSALIS_API_TIMEOUT = 5000;
|
|
46
|
+
export declare const UNIVERSALIS_API_RETRY_COUNT = 3;
|
|
47
|
+
export declare const UNIVERSALIS_API_RETRY_DELAY = 1000;
|
|
48
|
+
/**
|
|
49
|
+
* API caching and rate limiting
|
|
50
|
+
*/
|
|
51
|
+
export declare const API_CACHE_TTL: number;
|
|
52
|
+
export declare const API_DEBOUNCE_DELAY = 500;
|
|
53
|
+
export declare const API_CACHE_VERSION = "1.0.0";
|
|
54
|
+
export declare const API_MAX_RESPONSE_SIZE: number;
|
|
55
|
+
export declare const API_RATE_LIMIT_DELAY = 200;
|
|
56
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAMxE;;GAEG;AACH,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,OAAO,MAAM,CAAC;AAE3B;;GAEG;AACH,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,OAAO,MAAM,CAAC;AAC3B,eAAO,MAAM,cAAc,IAAI,CAAC;AAChC,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAA4C,CAAC;AAM5E,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAMpC,CAAC;AAEX,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAMzD,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,kBAqB9B,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,QAAQ;;;CAGX,CAAC;AAMX;;GAEG;AACH,eAAO,MAAM,oBAAoB,mCAAmC,CAAC;AACrE,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAC7C,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD;;GAEG;AACH,eAAO,MAAM,aAAa,QAAgB,CAAC;AAC3C,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,iBAAiB,UAAU,CAAC;AACzC,eAAO,MAAM,qBAAqB,QAAc,CAAC;AACjD,eAAO,MAAM,oBAAoB,MAAM,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xivdyetools/core - Application Constants
|
|
3
|
+
*
|
|
4
|
+
* Centralized configuration and constant values
|
|
5
|
+
*
|
|
6
|
+
* @module constants
|
|
7
|
+
*/
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Color Conversion Constraints
|
|
10
|
+
// ============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* RGB value constraints
|
|
13
|
+
*/
|
|
14
|
+
export const RGB_MIN = 0;
|
|
15
|
+
export const RGB_MAX = 255;
|
|
16
|
+
/**
|
|
17
|
+
* HSV value constraints
|
|
18
|
+
*/
|
|
19
|
+
export const HUE_MIN = 0;
|
|
20
|
+
export const HUE_MAX = 360;
|
|
21
|
+
export const SATURATION_MIN = 0;
|
|
22
|
+
export const SATURATION_MAX = 100;
|
|
23
|
+
export const VALUE_MIN = 0;
|
|
24
|
+
export const VALUE_MAX = 100;
|
|
25
|
+
/**
|
|
26
|
+
* Color distance calculation mode
|
|
27
|
+
*/
|
|
28
|
+
export const COLOR_DISTANCE_MAX = Math.sqrt(255 ** 2 + 255 ** 2 + 255 ** 2); // ~441.67
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Vision Type Configuration
|
|
31
|
+
// ============================================================================
|
|
32
|
+
export const VISION_TYPES = [
|
|
33
|
+
'normal',
|
|
34
|
+
'deuteranopia',
|
|
35
|
+
'protanopia',
|
|
36
|
+
'tritanopia',
|
|
37
|
+
'achromatopsia',
|
|
38
|
+
];
|
|
39
|
+
export const VISION_TYPE_LABELS = {
|
|
40
|
+
normal: 'Normal Vision',
|
|
41
|
+
deuteranopia: 'Deuteranopia (Red-Green Colorblindness)',
|
|
42
|
+
protanopia: 'Protanopia (Red-Green Colorblindness)',
|
|
43
|
+
tritanopia: 'Tritanopia (Blue-Yellow Colorblindness)',
|
|
44
|
+
achromatopsia: 'Achromatopsia (Total Colorblindness)',
|
|
45
|
+
};
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// Colorblindness Transformation Matrices (Brettel 1997)
|
|
48
|
+
// ============================================================================
|
|
49
|
+
/**
|
|
50
|
+
* Brettel 1997 transformation matrices for colorblindness simulation
|
|
51
|
+
* These matrices transform RGB values to simulate different types of colorblindness
|
|
52
|
+
*/
|
|
53
|
+
export const BRETTEL_MATRICES = {
|
|
54
|
+
deuteranopia: [
|
|
55
|
+
[0.625, 0.375, 0.0],
|
|
56
|
+
[0.7, 0.3, 0.0],
|
|
57
|
+
[0.0, 0.3, 0.7],
|
|
58
|
+
],
|
|
59
|
+
protanopia: [
|
|
60
|
+
[0.567, 0.433, 0.0],
|
|
61
|
+
[0.558, 0.442, 0.0],
|
|
62
|
+
[0.0, 0.242, 0.758],
|
|
63
|
+
],
|
|
64
|
+
tritanopia: [
|
|
65
|
+
[0.95, 0.05, 0.0],
|
|
66
|
+
[0.0, 0.433, 0.567],
|
|
67
|
+
[0.0, 0.475, 0.525],
|
|
68
|
+
],
|
|
69
|
+
achromatopsia: [
|
|
70
|
+
[0.299, 0.587, 0.114],
|
|
71
|
+
[0.299, 0.587, 0.114],
|
|
72
|
+
[0.299, 0.587, 0.114],
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// Regular Expressions
|
|
77
|
+
// ============================================================================
|
|
78
|
+
/**
|
|
79
|
+
* Regex patterns for validation
|
|
80
|
+
*/
|
|
81
|
+
export const PATTERNS = {
|
|
82
|
+
HEX_COLOR: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,
|
|
83
|
+
RGB_COLOR: /^rgb\(\d+,\s*\d+,\s*\d+\)$/,
|
|
84
|
+
};
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// API Configuration (Universalis)
|
|
87
|
+
// ============================================================================
|
|
88
|
+
/**
|
|
89
|
+
* Universalis API configuration
|
|
90
|
+
*/
|
|
91
|
+
export const UNIVERSALIS_API_BASE = 'https://universalis.app/api/v2';
|
|
92
|
+
export const UNIVERSALIS_API_TIMEOUT = 5000; // milliseconds
|
|
93
|
+
export const UNIVERSALIS_API_RETRY_COUNT = 3;
|
|
94
|
+
export const UNIVERSALIS_API_RETRY_DELAY = 1000; // milliseconds
|
|
95
|
+
/**
|
|
96
|
+
* API caching and rate limiting
|
|
97
|
+
*/
|
|
98
|
+
export const API_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
99
|
+
export const API_DEBOUNCE_DELAY = 500; // milliseconds
|
|
100
|
+
export const API_CACHE_VERSION = '1.0.0'; // Increment to invalidate all cached data
|
|
101
|
+
export const API_MAX_RESPONSE_SIZE = 1024 * 1024; // 1 MB maximum response size
|
|
102
|
+
export const API_RATE_LIMIT_DELAY = 200; // milliseconds between requests
|
|
103
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"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"}
|