@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.
Files changed (103) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +400 -0
  3. package/dist/constants/index.d.ts +56 -0
  4. package/dist/constants/index.d.ts.map +1 -0
  5. package/dist/constants/index.js +103 -0
  6. package/dist/constants/index.js.map +1 -0
  7. package/dist/data/colors_xiv.json +3130 -0
  8. package/dist/data/locales/de.json +231 -0
  9. package/dist/data/locales/en.json +231 -0
  10. package/dist/data/locales/fr.json +231 -0
  11. package/dist/data/locales/ja.json +231 -0
  12. package/dist/data/locales/ko.json +233 -0
  13. package/dist/data/locales/zh.json +233 -0
  14. package/dist/data/presets.json +390 -0
  15. package/dist/index.d.ts +16 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +18 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/services/APIService.d.ts +246 -0
  20. package/dist/services/APIService.d.ts.map +1 -0
  21. package/dist/services/APIService.js +499 -0
  22. package/dist/services/APIService.js.map +1 -0
  23. package/dist/services/ColorService.d.ts +146 -0
  24. package/dist/services/ColorService.d.ts.map +1 -0
  25. package/dist/services/ColorService.js +209 -0
  26. package/dist/services/ColorService.js.map +1 -0
  27. package/dist/services/DyeService.d.ts +230 -0
  28. package/dist/services/DyeService.d.ts.map +1 -0
  29. package/dist/services/DyeService.js +326 -0
  30. package/dist/services/DyeService.js.map +1 -0
  31. package/dist/services/LocalizationService.d.ts +338 -0
  32. package/dist/services/LocalizationService.d.ts.map +1 -0
  33. package/dist/services/LocalizationService.js +449 -0
  34. package/dist/services/LocalizationService.js.map +1 -0
  35. package/dist/services/PaletteService.d.ts +137 -0
  36. package/dist/services/PaletteService.d.ts.map +1 -0
  37. package/dist/services/PaletteService.js +349 -0
  38. package/dist/services/PaletteService.js.map +1 -0
  39. package/dist/services/PresetService.d.ts +196 -0
  40. package/dist/services/PresetService.d.ts.map +1 -0
  41. package/dist/services/PresetService.js +261 -0
  42. package/dist/services/PresetService.js.map +1 -0
  43. package/dist/services/color/ColorAccessibility.d.ts +39 -0
  44. package/dist/services/color/ColorAccessibility.d.ts.map +1 -0
  45. package/dist/services/color/ColorAccessibility.js +71 -0
  46. package/dist/services/color/ColorAccessibility.js.map +1 -0
  47. package/dist/services/color/ColorConverter.d.ts +146 -0
  48. package/dist/services/color/ColorConverter.d.ts.map +1 -0
  49. package/dist/services/color/ColorConverter.js +393 -0
  50. package/dist/services/color/ColorConverter.js.map +1 -0
  51. package/dist/services/color/ColorManipulator.d.ts +36 -0
  52. package/dist/services/color/ColorManipulator.d.ts.map +1 -0
  53. package/dist/services/color/ColorManipulator.js +56 -0
  54. package/dist/services/color/ColorManipulator.js.map +1 -0
  55. package/dist/services/color/ColorblindnessSimulator.d.ts +35 -0
  56. package/dist/services/color/ColorblindnessSimulator.d.ts.map +1 -0
  57. package/dist/services/color/ColorblindnessSimulator.js +110 -0
  58. package/dist/services/color/ColorblindnessSimulator.js.map +1 -0
  59. package/dist/services/dye/DyeDatabase.d.ts +131 -0
  60. package/dist/services/dye/DyeDatabase.d.ts.map +1 -0
  61. package/dist/services/dye/DyeDatabase.js +367 -0
  62. package/dist/services/dye/DyeDatabase.js.map +1 -0
  63. package/dist/services/dye/DyeSearch.d.ts +55 -0
  64. package/dist/services/dye/DyeSearch.d.ts.map +1 -0
  65. package/dist/services/dye/DyeSearch.js +196 -0
  66. package/dist/services/dye/DyeSearch.js.map +1 -0
  67. package/dist/services/dye/HarmonyGenerator.d.ts +110 -0
  68. package/dist/services/dye/HarmonyGenerator.d.ts.map +1 -0
  69. package/dist/services/dye/HarmonyGenerator.js +221 -0
  70. package/dist/services/dye/HarmonyGenerator.js.map +1 -0
  71. package/dist/services/localization/LocaleLoader.d.ts +38 -0
  72. package/dist/services/localization/LocaleLoader.d.ts.map +1 -0
  73. package/dist/services/localization/LocaleLoader.js +83 -0
  74. package/dist/services/localization/LocaleLoader.js.map +1 -0
  75. package/dist/services/localization/LocaleRegistry.d.ts +73 -0
  76. package/dist/services/localization/LocaleRegistry.d.ts.map +1 -0
  77. package/dist/services/localization/LocaleRegistry.js +84 -0
  78. package/dist/services/localization/LocaleRegistry.js.map +1 -0
  79. package/dist/services/localization/TranslationProvider.d.ts +157 -0
  80. package/dist/services/localization/TranslationProvider.d.ts.map +1 -0
  81. package/dist/services/localization/TranslationProvider.js +289 -0
  82. package/dist/services/localization/TranslationProvider.js.map +1 -0
  83. package/dist/types/index.d.ts +409 -0
  84. package/dist/types/index.d.ts.map +1 -0
  85. package/dist/types/index.js +87 -0
  86. package/dist/types/index.js.map +1 -0
  87. package/dist/types/logger.d.ts +84 -0
  88. package/dist/types/logger.d.ts.map +1 -0
  89. package/dist/types/logger.js +54 -0
  90. package/dist/types/logger.js.map +1 -0
  91. package/dist/utils/index.d.ts +441 -0
  92. package/dist/utils/index.d.ts.map +1 -0
  93. package/dist/utils/index.js +577 -0
  94. package/dist/utils/index.js.map +1 -0
  95. package/dist/utils/kd-tree.d.ts +76 -0
  96. package/dist/utils/kd-tree.d.ts.map +1 -0
  97. package/dist/utils/kd-tree.js +195 -0
  98. package/dist/utils/kd-tree.js.map +1 -0
  99. package/dist/version.d.ts +11 -0
  100. package/dist/version.d.ts.map +1 -0
  101. package/dist/version.js +11 -0
  102. package/dist/version.js.map +1 -0
  103. package/package.json +84 -0
@@ -0,0 +1,261 @@
1
+ /**
2
+ * @xivdyetools/core - Preset Service
3
+ *
4
+ * Manages preset color palettes for FFXIV glamours.
5
+ * Provides themed color combinations for jobs, seasons, events, and aesthetics.
6
+ *
7
+ * @module services/PresetService
8
+ * @example
9
+ * ```typescript
10
+ * import { PresetService, presetData, DyeService, dyeDatabase } from '@xivdyetools/core';
11
+ *
12
+ * const presetService = new PresetService(presetData);
13
+ * const dyeService = new DyeService(dyeDatabase);
14
+ *
15
+ * // Get all job presets
16
+ * const jobPresets = presetService.getPresetsByCategory('jobs');
17
+ *
18
+ * // Get a preset with resolved dye objects
19
+ * const rdmPreset = presetService.getPresetWithDyes('job-rdm', dyeService);
20
+ * ```
21
+ */
22
+ /**
23
+ * Service for managing preset color palettes
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const presetService = new PresetService(presetData);
28
+ *
29
+ * // List categories
30
+ * const categories = presetService.getCategories();
31
+ *
32
+ * // Get presets by category
33
+ * const jobPresets = presetService.getPresetsByCategory('jobs');
34
+ *
35
+ * // Search presets
36
+ * const results = presetService.searchPresets('red');
37
+ * ```
38
+ */
39
+ export class PresetService {
40
+ /**
41
+ * Initialize the preset service with preset data
42
+ * @param presetData - Preset data object (import from presets.json)
43
+ */
44
+ constructor(presetData) {
45
+ this.data = presetData;
46
+ // Build lookup map for O(1) access by ID
47
+ this.presetById = new Map(this.data.palettes.map((p) => [p.id, p]));
48
+ }
49
+ // ============================================================================
50
+ // Category Operations
51
+ // ============================================================================
52
+ /**
53
+ * Get all preset categories with metadata
54
+ * @returns Array of category metadata with IDs
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const categories = presetService.getCategories();
59
+ * // [{ id: 'jobs', name: 'FFXIV Jobs', description: '...', icon: '⚔️' }, ...]
60
+ * ```
61
+ */
62
+ getCategories() {
63
+ return Object.entries(this.data.categories).map(([id, meta]) => ({
64
+ id,
65
+ ...meta,
66
+ }));
67
+ }
68
+ /**
69
+ * Get metadata for a specific category
70
+ * @param category - Category identifier
71
+ * @returns Category metadata or undefined if not found
72
+ */
73
+ getCategoryMeta(category) {
74
+ return this.data.categories[category];
75
+ }
76
+ // ============================================================================
77
+ // Preset Retrieval
78
+ // ============================================================================
79
+ /**
80
+ * Get all presets
81
+ * @returns Array of all preset palettes
82
+ */
83
+ getAllPresets() {
84
+ return [...this.data.palettes];
85
+ }
86
+ /**
87
+ * Get all presets in a specific category
88
+ * @param category - Category to filter by
89
+ * @returns Array of presets in that category
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * const jobPresets = presetService.getPresetsByCategory('jobs');
94
+ * // Returns all job-themed presets (RDM, BLM, WHM, etc.)
95
+ * ```
96
+ */
97
+ getPresetsByCategory(category) {
98
+ return this.data.palettes.filter((p) => p.category === category);
99
+ }
100
+ /**
101
+ * Get a specific preset by ID
102
+ * @param id - Preset identifier (e.g., "job-rdm")
103
+ * @returns Preset or undefined if not found
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const rdm = presetService.getPreset('job-rdm');
108
+ * if (rdm) {
109
+ * console.log(rdm.name); // "Red Mage"
110
+ * console.log(rdm.dyes); // [5738, 13115, 13117, 5729]
111
+ * }
112
+ * ```
113
+ */
114
+ getPreset(id) {
115
+ return this.presetById.get(id);
116
+ }
117
+ /**
118
+ * Get preset count by category
119
+ * @returns Map of category to preset count
120
+ */
121
+ getPresetCountByCategory() {
122
+ const counts = new Map();
123
+ for (const preset of this.data.palettes) {
124
+ counts.set(preset.category, (counts.get(preset.category) ?? 0) + 1);
125
+ }
126
+ return counts;
127
+ }
128
+ // ============================================================================
129
+ // Search Operations
130
+ // ============================================================================
131
+ /**
132
+ * Search presets by name or tags
133
+ * @param query - Search query (case-insensitive)
134
+ * @returns Array of matching presets
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * // Search by name
139
+ * const results = presetService.searchPresets('red');
140
+ * // Returns: Red Mage, Blood Red themed presets, etc.
141
+ *
142
+ * // Search by tag
143
+ * const tankResults = presetService.searchPresets('tank');
144
+ * // Returns: Paladin, Warrior, Dark Knight, Gunbreaker
145
+ * ```
146
+ */
147
+ searchPresets(query) {
148
+ const lowerQuery = query.toLowerCase().trim();
149
+ if (!lowerQuery) {
150
+ return [];
151
+ }
152
+ return this.data.palettes.filter((p) => p.name.toLowerCase().includes(lowerQuery) ||
153
+ p.description.toLowerCase().includes(lowerQuery) ||
154
+ p.tags.some((tag) => tag.toLowerCase().includes(lowerQuery)));
155
+ }
156
+ /**
157
+ * Search presets by tag (exact match)
158
+ * @param tag - Tag to search for (case-insensitive)
159
+ * @returns Array of presets with that tag
160
+ */
161
+ getPresetsByTag(tag) {
162
+ const lowerTag = tag.toLowerCase().trim();
163
+ return this.data.palettes.filter((p) => p.tags.some((t) => t.toLowerCase() === lowerTag));
164
+ }
165
+ // ============================================================================
166
+ // Random Selection
167
+ // ============================================================================
168
+ /**
169
+ * Get a random preset, optionally filtered by category
170
+ * @param category - Optional category to filter by
171
+ * @returns Random preset, or undefined if no presets exist
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * // Random from all presets
176
+ * const random = presetService.getRandomPreset();
177
+ *
178
+ * // Random job preset
179
+ * const randomJob = presetService.getRandomPreset('jobs');
180
+ * ```
181
+ */
182
+ getRandomPreset(category) {
183
+ const pool = category ? this.getPresetsByCategory(category) : this.data.palettes;
184
+ if (pool.length === 0) {
185
+ return undefined;
186
+ }
187
+ return pool[Math.floor(Math.random() * pool.length)];
188
+ }
189
+ // ============================================================================
190
+ // Dye Resolution (requires DyeService)
191
+ // ============================================================================
192
+ /**
193
+ * Get preset with resolved Dye objects
194
+ * Requires a DyeService instance to resolve dye IDs to full Dye objects
195
+ *
196
+ * @param id - Preset identifier
197
+ * @param dyeService - DyeService instance for resolving dye IDs
198
+ * @returns Resolved preset with full Dye objects, or undefined if preset not found
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * const dyeService = new DyeService(dyeDatabase);
203
+ * const resolved = presetService.getPresetWithDyes('job-rdm', dyeService);
204
+ *
205
+ * if (resolved) {
206
+ * resolved.resolvedDyes.forEach(dye => {
207
+ * if (dye) {
208
+ * console.log(`${dye.name}: ${dye.hex}`);
209
+ * }
210
+ * });
211
+ * }
212
+ * ```
213
+ */
214
+ getPresetWithDyes(id, dyeService) {
215
+ const preset = this.getPreset(id);
216
+ if (!preset) {
217
+ return undefined;
218
+ }
219
+ return {
220
+ ...preset,
221
+ resolvedDyes: preset.dyes.map((dyeId) => dyeService.getDyeById(dyeId)),
222
+ };
223
+ }
224
+ /**
225
+ * Resolve multiple presets with their Dye objects
226
+ * @param presets - Array of presets to resolve
227
+ * @param dyeService - DyeService instance for resolving dye IDs
228
+ * @returns Array of resolved presets
229
+ */
230
+ resolvePresets(presets, dyeService) {
231
+ return presets.map((preset) => ({
232
+ ...preset,
233
+ resolvedDyes: preset.dyes.map((dyeId) => dyeService.getDyeById(dyeId)),
234
+ }));
235
+ }
236
+ // ============================================================================
237
+ // Metadata
238
+ // ============================================================================
239
+ /**
240
+ * Get data version
241
+ * @returns Version string from preset data
242
+ */
243
+ getVersion() {
244
+ return this.data.version;
245
+ }
246
+ /**
247
+ * Get last update timestamp
248
+ * @returns ISO date string of last update
249
+ */
250
+ getLastUpdated() {
251
+ return this.data.lastUpdated;
252
+ }
253
+ /**
254
+ * Get total number of presets
255
+ * @returns Total preset count
256
+ */
257
+ getPresetCount() {
258
+ return this.data.palettes.length;
259
+ }
260
+ }
261
+ //# sourceMappingURL=PresetService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PresetService.js","sourceRoot":"","sources":["../../src/services/PresetService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Color Accessibility
3
+ * Per R-4: Focused class for accessibility-related color operations
4
+ * Handles WCAG contrast, luminance, and text color optimization
5
+ */
6
+ import type { HexColor } from '../../types/index.js';
7
+ /**
8
+ * Color accessibility utilities
9
+ * Per R-4: Single Responsibility - accessibility operations only
10
+ */
11
+ export declare class ColorAccessibility {
12
+ /**
13
+ * Calculate perceived luminance of a color (0-1)
14
+ * Uses relative luminance formula from WCAG
15
+ */
16
+ static getPerceivedLuminance(hex: string): number;
17
+ /**
18
+ * Calculate contrast ratio between two colors
19
+ * Returns 1 (no contrast) to 21 (maximum contrast)
20
+ */
21
+ static getContrastRatio(hex1: string, hex2: string): number;
22
+ /**
23
+ * Check if two colors meet WCAG AA contrast ratio (4.5:1 for small text, 3:1 for large)
24
+ */
25
+ static meetsWCAGAA(hex1: string, hex2: string, largeText?: boolean): boolean;
26
+ /**
27
+ * Check if two colors meet WCAG AAA contrast ratio (7:1 for small text, 4.5:1 for large)
28
+ */
29
+ static meetsWCAGAAA(hex1: string, hex2: string, largeText?: boolean): boolean;
30
+ /**
31
+ * Check if a color is light (for determining text color on background)
32
+ */
33
+ static isLightColor(hex: string): boolean;
34
+ /**
35
+ * Get optimal text color for a background color
36
+ */
37
+ static getOptimalTextColor(backgroundColor: string): HexColor;
38
+ }
39
+ //# sourceMappingURL=ColorAccessibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColorAccessibility.d.ts","sourceRoot":"","sources":["../../../src/services/color/ColorAccessibility.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAIrD;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B;;;OAGG;IACH,MAAM,CAAC,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAiBjD;;;OAGG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAU3D;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,OAAe,GAAG,OAAO;IAKnF;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,OAAe,GAAG,OAAO;IAKpF;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAKzC;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,eAAe,EAAE,MAAM,GAAG,QAAQ;CAK9D"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Color Accessibility
3
+ * Per R-4: Focused class for accessibility-related color operations
4
+ * Handles WCAG contrast, luminance, and text color optimization
5
+ */
6
+ import { createHexColor } from '../../types/index.js';
7
+ import { ColorConverter } from './ColorConverter.js';
8
+ /**
9
+ * Color accessibility utilities
10
+ * Per R-4: Single Responsibility - accessibility operations only
11
+ */
12
+ export class ColorAccessibility {
13
+ /**
14
+ * Calculate perceived luminance of a color (0-1)
15
+ * Uses relative luminance formula from WCAG
16
+ */
17
+ static getPerceivedLuminance(hex) {
18
+ const rgb = ColorConverter.hexToRgb(hex);
19
+ // Convert to sRGB (linear RGB)
20
+ const toLinear = (c) => {
21
+ const cNorm = c / 255;
22
+ return cNorm <= 0.03928 ? cNorm / 12.92 : Math.pow((cNorm + 0.055) / 1.055, 2.4);
23
+ };
24
+ const r = toLinear(rgb.r);
25
+ const g = toLinear(rgb.g);
26
+ const b = toLinear(rgb.b);
27
+ // WCAG relative luminance formula
28
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
29
+ }
30
+ /**
31
+ * Calculate contrast ratio between two colors
32
+ * Returns 1 (no contrast) to 21 (maximum contrast)
33
+ */
34
+ static getContrastRatio(hex1, hex2) {
35
+ const lum1 = this.getPerceivedLuminance(hex1);
36
+ const lum2 = this.getPerceivedLuminance(hex2);
37
+ const lighter = Math.max(lum1, lum2);
38
+ const darker = Math.min(lum1, lum2);
39
+ return (lighter + 0.05) / (darker + 0.05);
40
+ }
41
+ /**
42
+ * Check if two colors meet WCAG AA contrast ratio (4.5:1 for small text, 3:1 for large)
43
+ */
44
+ static meetsWCAGAA(hex1, hex2, largeText = false) {
45
+ const ratio = this.getContrastRatio(hex1, hex2);
46
+ return ratio >= (largeText ? 3 : 4.5);
47
+ }
48
+ /**
49
+ * Check if two colors meet WCAG AAA contrast ratio (7:1 for small text, 4.5:1 for large)
50
+ */
51
+ static meetsWCAGAAA(hex1, hex2, largeText = false) {
52
+ const ratio = this.getContrastRatio(hex1, hex2);
53
+ return ratio >= (largeText ? 4.5 : 7);
54
+ }
55
+ /**
56
+ * Check if a color is light (for determining text color on background)
57
+ */
58
+ static isLightColor(hex) {
59
+ const luminance = this.getPerceivedLuminance(hex);
60
+ return luminance > 0.5;
61
+ }
62
+ /**
63
+ * Get optimal text color for a background color
64
+ */
65
+ static getOptimalTextColor(backgroundColor) {
66
+ return this.isLightColor(backgroundColor)
67
+ ? createHexColor('#000000')
68
+ : createHexColor('#FFFFFF');
69
+ }
70
+ }
71
+ //# sourceMappingURL=ColorAccessibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColorAccessibility.js","sourceRoot":"","sources":["../../../src/services/color/ColorAccessibility.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IAC7B;;;OAGG;IACH,MAAM,CAAC,qBAAqB,CAAC,GAAW;QACtC,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAEzC,+BAA+B;QAC/B,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAU,EAAE;YACrC,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC;YACtB,OAAO,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,KAAK,EAAE,GAAG,CAAC,CAAC;QACnF,CAAC,CAAC;QAEF,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAE1B,kCAAkC;QAClC,OAAO,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAY,EAAE,IAAY;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE9C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAEpC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,IAAY,EAAE,IAAY,EAAE,YAAqB,KAAK;QACvE,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChD,OAAO,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,IAAY,EAAE,IAAY,EAAE,YAAqB,KAAK;QACxE,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChD,OAAO,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,GAAW;QAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC;QAClD,OAAO,SAAS,GAAG,GAAG,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,eAAuB;QAChD,OAAO,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC;YACvC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC;YAC3B,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;CACF"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Color Converter
3
+ * Per R-4: Focused class for color format conversions
4
+ * Handles conversions between hex, RGB, and HSV color formats
5
+ */
6
+ import type { RGB, HSV, HexColor } from '../../types/index.js';
7
+ /**
8
+ * Configuration for ColorConverter caches
9
+ */
10
+ export interface ColorConverterConfig {
11
+ cacheSize?: number;
12
+ }
13
+ /**
14
+ * Color format converter with LRU caching
15
+ * Per R-4: Single Responsibility - format conversions only
16
+ *
17
+ * Refactored for testability: Supports dependency injection of cache configuration
18
+ */
19
+ export declare class ColorConverter {
20
+ private readonly hexToRgbCache;
21
+ private readonly rgbToHexCache;
22
+ private readonly rgbToHsvCache;
23
+ private readonly hsvToRgbCache;
24
+ private readonly hexToHsvCache;
25
+ private static defaultInstance;
26
+ /**
27
+ * Constructor with optional cache configuration
28
+ * @param config Configuration for cache sizes (default: 1000 entries per cache)
29
+ */
30
+ constructor(config?: ColorConverterConfig);
31
+ /**
32
+ * Get the default singleton instance
33
+ * Per Issue #6: Returns eagerly-initialized instance to avoid race conditions
34
+ */
35
+ private static getDefault;
36
+ /**
37
+ * Clear all caches (useful for testing or memory management)
38
+ */
39
+ clearCaches(): void;
40
+ /**
41
+ * Static method: Clear all caches of the default instance
42
+ */
43
+ static clearCaches(): void;
44
+ /**
45
+ * Get cache statistics (for monitoring)
46
+ */
47
+ getCacheStats(): {
48
+ hexToRgb: number;
49
+ rgbToHex: number;
50
+ rgbToHsv: number;
51
+ hsvToRgb: number;
52
+ hexToHsv: number;
53
+ };
54
+ /**
55
+ * Static method: Get cache statistics from default instance
56
+ */
57
+ static getCacheStats(): {
58
+ hexToRgb: number;
59
+ rgbToHex: number;
60
+ rgbToHsv: number;
61
+ hsvToRgb: number;
62
+ hexToHsv: number;
63
+ };
64
+ /**
65
+ * Convert hexadecimal color to RGB
66
+ * Per P-1: Cached for performance
67
+ * @example hexToRgb("#FF0000") -> { r: 255, g: 0, b: 0 }
68
+ */
69
+ hexToRgb(hex: string): RGB;
70
+ /**
71
+ * Static method: Convert hex to RGB using default instance
72
+ */
73
+ static hexToRgb(hex: string): RGB;
74
+ /**
75
+ * Convert RGB to hexadecimal color
76
+ * Per P-1: Cached for performance
77
+ * @example rgbToHex(255, 0, 0) -> "#FF0000"
78
+ */
79
+ rgbToHex(r: number, g: number, b: number): HexColor;
80
+ /**
81
+ * Static method: Convert RGB to hex using default instance
82
+ */
83
+ static rgbToHex(r: number, g: number, b: number): HexColor;
84
+ /**
85
+ * Convert RGB to HSV
86
+ * Per P-1: Cached for performance, optimized single-pass min/max
87
+ * @example rgbToHsv(255, 0, 0) -> { h: 0, s: 100, v: 100 }
88
+ */
89
+ rgbToHsv(r: number, g: number, b: number): HSV;
90
+ /**
91
+ * Static method: Convert RGB to HSV using default instance
92
+ */
93
+ static rgbToHsv(r: number, g: number, b: number): HSV;
94
+ /**
95
+ * Normalize hue to [0, 360) range.
96
+ * CORE-BUG-001: Ensures consistent cache keys for equivalent hue values
97
+ * (e.g., h=359.9999 and h=0.0001 both produce similar cache keys after rounding)
98
+ * Also handles negative values and values >= 360.
99
+ */
100
+ private normalizeHue;
101
+ /**
102
+ * Convert HSV to RGB
103
+ * Per P-1: Cached for performance
104
+ * @example hsvToRgb(0, 100, 100) -> { r: 255, g: 0, b: 0 }
105
+ */
106
+ hsvToRgb(h: number, s: number, v: number): RGB;
107
+ /**
108
+ * Static method: Convert HSV to RGB using default instance
109
+ */
110
+ static hsvToRgb(h: number, s: number, v: number): RGB;
111
+ /**
112
+ * Convert hex to HSV
113
+ * Per P-1: Cached for performance
114
+ */
115
+ hexToHsv(hex: string): HSV;
116
+ /**
117
+ * Static method: Convert hex to HSV using default instance
118
+ */
119
+ static hexToHsv(hex: string): HSV;
120
+ /**
121
+ * Convert HSV to hex
122
+ */
123
+ hsvToHex(h: number, s: number, v: number): HexColor;
124
+ /**
125
+ * Static method: Convert HSV to hex using default instance
126
+ */
127
+ static hsvToHex(h: number, s: number, v: number): HexColor;
128
+ /**
129
+ * Normalize a hex color to #RRGGBB format
130
+ */
131
+ normalizeHex(hex: string): HexColor;
132
+ /**
133
+ * Static method: Normalize hex using default instance
134
+ */
135
+ static normalizeHex(hex: string): HexColor;
136
+ /**
137
+ * Calculate Euclidean distance between two RGB colors
138
+ * Returns 0 for identical colors, ~441.67 for white vs black
139
+ */
140
+ getColorDistance(hex1: string, hex2: string): number;
141
+ /**
142
+ * Static method: Calculate color distance using default instance
143
+ */
144
+ static getColorDistance(hex1: string, hex2: string): number;
145
+ }
146
+ //# sourceMappingURL=ColorConverter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColorConverter.d.ts","sourceRoot":"","sources":["../../../src/services/color/ColorConverter.ts"],"names":[],"mappings":"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"}