@xivdyetools/core 2.7.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 +7 -61
  64. package/dist/services/APIService.d.ts.map +1 -1
  65. package/dist/services/APIService.js +43 -154
  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 +16 -68
  70. package/dist/services/CharacterColorService.js.map +1 -1
  71. package/dist/services/ColorService.d.ts +55 -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 +10 -103
  76. package/dist/services/DyeService.d.ts.map +1 -1
  77. package/dist/services/DyeService.js +13 -156
  78. package/dist/services/DyeService.js.map +1 -1
  79. package/dist/services/LocalizationService.d.ts +1 -33
  80. package/dist/services/LocalizationService.d.ts.map +1 -1
  81. package/dist/services/LocalizationService.js +0 -44
  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 +2 -21
  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 +107 -35
  104. package/dist/services/color/ColorConverter.d.ts.map +1 -1
  105. package/dist/services/color/ColorConverter.js +159 -48
  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 +14 -0
  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 +53 -1
  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 +0 -21
  120. package/dist/services/dye/DyeDatabase.d.ts.map +1 -1
  121. package/dist/services/dye/DyeDatabase.js +78 -103
  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 +31 -67
  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 +24 -35
  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 +35 -122
  143. package/dist/utils/index.d.ts.map +1 -1
  144. package/dist/utils/index.js +45 -422
  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 +19 -10
  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
@@ -1 +1 @@
1
- {"version":3,"file":"PresetService.d.ts","sourceRoot":"","sources":["../../src/services/PresetService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,GAAG,EACJ,MAAM,oBAAoB,CAAC;AAE5B;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,+DAA+D;IAC/D,YAAY,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC;CAC9B;AAED;;;GAGG;AACH,UAAU,WAAW;IACnB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,UAAU,CAA6B;IAE/C;;;OAGG;gBACS,UAAU,EAAE,UAAU;IAUlC;;;;;;;;;OASG;IACH,aAAa,IAAI,CAAC,YAAY,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE;IAOlD;;;;OAIG;IACH,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,YAAY,GAAG,SAAS;IAQnE;;;OAGG;IACH,aAAa,IAAI,aAAa,EAAE;IAIhC;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,QAAQ,EAAE,cAAc,GAAG,aAAa,EAAE;IAI/D;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIhD;;;OAGG;IACH,wBAAwB,IAAI,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC;IAYvD;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,EAAE;IAc7C;;;;OAIG;IACH,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,EAAE;IAS7C;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,SAAS;IAcrE;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS;IAYlF;;;;;OAKG;IACH,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,UAAU,EAAE,WAAW,GAAG,cAAc,EAAE;IAWnF;;;OAGG;IACH,UAAU,IAAI,MAAM;IAIpB;;;OAGG;IACH,cAAc,IAAI,MAAM;IAIxB;;;OAGG;IACH,cAAc,IAAI,MAAM;CAGzB"}
1
+ {"version":3,"file":"PresetService.d.ts","sourceRoot":"","sources":["../../src/services/PresetService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,GAAG,EACJ,MAAM,oBAAoB,CAAC;AAE5B;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,+DAA+D;IAC/D,YAAY,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC;CAC9B;AAED;;;GAGG;AACH,UAAU,WAAW;IACnB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC;IACnC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,UAAU,CAA6B;IAE/C;;;OAGG;gBACS,UAAU,EAAE,UAAU;IAUlC;;;;;;;;;OASG;IACH,aAAa,IAAI,CAAC,YAAY,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE;IAOlD;;;;OAIG;IACH,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,YAAY,GAAG,SAAS;IAQnE;;;OAGG;IACH,aAAa,IAAI,aAAa,EAAE;IAIhC;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,QAAQ,EAAE,cAAc,GAAG,aAAa,EAAE;IAI/D;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAQhD;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,WAAW,GAAG,aAAa,EAAE;IA4BvE;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,SAAS;IAcrE;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS;CAYnF"}
@@ -116,17 +116,6 @@ export class PresetService {
116
116
  getPreset(id) {
117
117
  return this.presetById.get(id);
118
118
  }
119
- /**
120
- * Get preset count by category
121
- * @returns Map of category to preset count
122
- */
123
- getPresetCountByCategory() {
124
- const counts = new Map();
125
- for (const preset of this.data.palettes) {
126
- counts.set(preset.category, (counts.get(preset.category) ?? 0) + 1);
127
- }
128
- return counts;
129
- }
130
119
  // ============================================================================
131
120
  // Search Operations
132
121
  // ============================================================================
@@ -146,23 +135,22 @@ export class PresetService {
146
135
  * // Returns: Paladin, Warrior, Dark Knight, Gunbreaker
147
136
  * ```
148
137
  */
149
- searchPresets(query) {
138
+ searchPresets(query, dyeService) {
150
139
  const lowerQuery = query.toLowerCase().trim();
151
140
  if (!lowerQuery) {
152
141
  return [];
153
142
  }
143
+ // With a dye service, a palette also matches on the names of the dyes it
144
+ // contains — "search presets, dyes, tags" is only true if dyes count.
145
+ const matchesDye = (p) => {
146
+ if (!dyeService)
147
+ return false;
148
+ return p.dyes.some((stainId) => dyeService.getByStainId(stainId)?.name.toLowerCase().includes(lowerQuery));
149
+ };
154
150
  return this.data.palettes.filter((p) => p.name.toLowerCase().includes(lowerQuery) ||
155
151
  p.description.toLowerCase().includes(lowerQuery) ||
156
- p.tags.some((tag) => tag.toLowerCase().includes(lowerQuery)));
157
- }
158
- /**
159
- * Search presets by tag (exact match)
160
- * @param tag - Tag to search for (case-insensitive)
161
- * @returns Array of presets with that tag
162
- */
163
- getPresetsByTag(tag) {
164
- const lowerTag = tag.toLowerCase().trim();
165
- return this.data.palettes.filter((p) => p.tags.some((t) => t.toLowerCase() === lowerTag));
152
+ p.tags.some((tag) => tag.toLowerCase().includes(lowerQuery)) ||
153
+ matchesDye(p));
166
154
  }
167
155
  // ============================================================================
168
156
  // Random Selection
@@ -220,44 +208,9 @@ export class PresetService {
220
208
  }
221
209
  return {
222
210
  ...preset,
223
- resolvedDyes: preset.dyes.map((dyeId) => dyeService.getDyeById(dyeId)),
211
+ // 5.0: presets.json 2.0.0 stores stainIDs (never derived from ranges)
212
+ resolvedDyes: preset.dyes.map((stainId) => dyeService.getByStainId(stainId)),
224
213
  };
225
214
  }
226
- /**
227
- * Resolve multiple presets with their Dye objects
228
- * @param presets - Array of presets to resolve
229
- * @param dyeService - DyeService instance for resolving dye IDs
230
- * @returns Array of resolved presets
231
- */
232
- resolvePresets(presets, dyeService) {
233
- return presets.map((preset) => ({
234
- ...preset,
235
- resolvedDyes: preset.dyes.map((dyeId) => dyeService.getDyeById(dyeId)),
236
- }));
237
- }
238
- // ============================================================================
239
- // Metadata
240
- // ============================================================================
241
- /**
242
- * Get data version
243
- * @returns Version string from preset data
244
- */
245
- getVersion() {
246
- return this.data.version;
247
- }
248
- /**
249
- * Get last update timestamp
250
- * @returns ISO date string of last update
251
- */
252
- getLastUpdated() {
253
- return this.data.lastUpdated;
254
- }
255
- /**
256
- * Get total number of presets
257
- * @returns Total preset count
258
- */
259
- getPresetCount() {
260
- return this.data.palettes.length;
261
- }
262
215
  }
263
216
  //# sourceMappingURL=PresetService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PresetService.js","sourceRoot":"","sources":["../../src/services/PresetService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA4BH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,aAAa;IAChB,IAAI,CAAa;IACjB,UAAU,CAA6B;IAE/C;;;OAGG;IACH,YAAY,UAAsB;QAChC,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,yCAAyC;QACzC,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,+EAA+E;IAC/E,sBAAsB;IACtB,+EAA+E;IAE/E;;;;;;;;;OASG;IACH,aAAa;QACX,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/D,EAAE;YACF,GAAG,IAAI;SACR,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,QAAwB;QACtC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,+EAA+E;IAC/E,mBAAmB;IACnB,+EAA+E;IAE/E;;;OAGG;IACH,aAAa;QACX,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,QAAwB;QAC3C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,EAAU;QAClB,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED;;;OAGG;IACH,wBAAwB;QACtB,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;QACjD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,+EAA+E;IAC/E,oBAAoB;IACpB,+EAA+E;IAE/E;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,KAAa;QACzB,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;YACzC,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;YAChD,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAC/D,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,eAAe,CAAC,GAAW;QACzB,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC;IAC5F,CAAC;IAED,+EAA+E;IAC/E,mBAAmB;IACnB,+EAA+E;IAE/E;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,QAAyB;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;QAEjF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,+EAA+E;IAC/E,uCAAuC;IACvC,+EAA+E;IAE/E;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,iBAAiB,CAAC,EAAU,EAAE,UAAuB;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO;YACL,GAAG,MAAM;YACT,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;SACvE,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,cAAc,CAAC,OAAwB,EAAE,UAAuB;QAC9D,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC9B,GAAG,MAAM;YACT,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;SACvE,CAAC,CAAC,CAAC;IACN,CAAC;IAED,+EAA+E;IAC/E,WAAW;IACX,+EAA+E;IAE/E;;;OAGG;IACH,UAAU;QACR,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,cAAc;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,cAAc;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IACnC,CAAC;CACF"}
1
+ {"version":3,"file":"PresetService.js","sourceRoot":"","sources":["../../src/services/PresetService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `.chara` equipment identity — the model keys a character file carries and
3
+ * how they pack into the Item sheet's `ModelMain` / `ModelSub` values.
4
+ *
5
+ * Measured rules (docs/research/chara-equipment-resolution, 47-file corpus,
6
+ * resolved live against XIVAPI v2 — 273/273 gear keys, 41/41 weapons):
7
+ * - The Item sheet stores a model reference as one `uint64`, four
8
+ * little-endian 16-bit lanes (SaintCoinach `Quad`). The `.chara` fields are
9
+ * those lanes: armour/accessories `ModelMain = base | variant << 16`,
10
+ * weapons `ModelMain = set | base << 16 | variant << 32`.
11
+ * - The slot is a mandatory second key: one gear *set* shares a `ModelMain`
12
+ * across head/body/hands/legs/feet, so a search must also filter on the
13
+ * `EquipSlotCategory` boolean column (rings: `FingerL` / `FingerR`).
14
+ * - `ModelBase == 0` (weapons: `ModelSet == 0` too) means the slot is empty.
15
+ * - Off-hands resolve THROUGH the main hand — see the resolver on api-worker.
16
+ *
17
+ * Nothing here touches the network; these are the pure packing helpers the
18
+ * parser, the api-worker resolver and the web-app client all share.
19
+ */
20
+ import type { CharaGearSlotId } from './chara-parser.js';
21
+ /** A worn piece's model identity, as the `.chara` file spells it. */
22
+ export interface CharaGearModel {
23
+ slot: CharaGearSlotId;
24
+ /** Weapon slots only — the `w0634` set number. Absent on armour/accessories. */
25
+ set?: number;
26
+ /** Armour: the `e0361` set. Weapons: the `b0019` body. */
27
+ base: number;
28
+ /** Material variant */
29
+ variant: number;
30
+ }
31
+ /** The two slots whose model is a weapon triple rather than a gear pair. */
32
+ export declare const CHARA_WEAPON_SLOTS: readonly CharaGearSlotId[];
33
+ export declare function isCharaWeaponSlot(slot: CharaGearSlotId): boolean;
34
+ /**
35
+ * `.chara` slot → `EquipSlotCategory` boolean column. This is the mandatory
36
+ * second search key: `+EquipSlotCategory.<field>=1`.
37
+ */
38
+ export declare const CHARA_SLOT_SEARCH_FIELD: Record<CharaGearSlotId, string>;
39
+ /**
40
+ * Armour / accessory `ModelMain`: `base | variant << 16`. Computed with
41
+ * arithmetic rather than `<<` so a variant above 0x7FFF cannot overflow the
42
+ * signed 32-bit shift — the value is an unsigned 32-bit lane pair.
43
+ *
44
+ * Beech Mask of Casting: `gearModelKey(361, 5) === 328041` (Item#18085).
45
+ */
46
+ export declare function gearModelKey(base: number, variant: number): number;
47
+ /**
48
+ * Weapon `ModelMain`: `set | base << 16 | variant << 32`. Exceeds 2^32, so it
49
+ * is a BigInt — serialise with `String()` for JSON / cache keys.
50
+ *
51
+ * Runaway Bow: `weaponModelKey(634, 19, 1) === 4296213114n` (Item#49486);
52
+ * its quiver `{698, 149, 1}` is that item's `ModelSub`.
53
+ */
54
+ export declare function weaponModelKey(set: number, base: number, variant: number): bigint;
55
+ /**
56
+ * The packed key as a decimal string — the form used on the wire and in
57
+ * cache keys (weapon values do not fit a JS number exactly past 2^53 only in
58
+ * theory, but a BigInt never round-trips JSON, so everything is a string).
59
+ */
60
+ export declare function charaModelKey(model: CharaGearModel): string;
61
+ /**
62
+ * The human label for a model with no Item row: `set·base·variant` for a
63
+ * weapon, `base·variant` otherwise (the design's "MODEL 9903·1").
64
+ */
65
+ export declare function formatCharaModelLabel(model: CharaGearModel): string;
66
+ /** True when the record describes a worn piece rather than an empty slot. */
67
+ export declare function isWornCharaModel(slot: CharaGearSlotId, set: number, base: number): boolean;
68
+ //# sourceMappingURL=chara-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chara-models.d.ts","sourceRoot":"","sources":["../../../src/services/chara/chara-models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,eAAe,CAAC;IACtB,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAA4B,CAAC;AAEtF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAEhE;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAanE,CAAC;AAIF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAI3D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAInE;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,OAAO,CAET"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `.chara` equipment identity — the model keys a character file carries and
3
+ * how they pack into the Item sheet's `ModelMain` / `ModelSub` values.
4
+ *
5
+ * Measured rules (docs/research/chara-equipment-resolution, 47-file corpus,
6
+ * resolved live against XIVAPI v2 — 273/273 gear keys, 41/41 weapons):
7
+ * - The Item sheet stores a model reference as one `uint64`, four
8
+ * little-endian 16-bit lanes (SaintCoinach `Quad`). The `.chara` fields are
9
+ * those lanes: armour/accessories `ModelMain = base | variant << 16`,
10
+ * weapons `ModelMain = set | base << 16 | variant << 32`.
11
+ * - The slot is a mandatory second key: one gear *set* shares a `ModelMain`
12
+ * across head/body/hands/legs/feet, so a search must also filter on the
13
+ * `EquipSlotCategory` boolean column (rings: `FingerL` / `FingerR`).
14
+ * - `ModelBase == 0` (weapons: `ModelSet == 0` too) means the slot is empty.
15
+ * - Off-hands resolve THROUGH the main hand — see the resolver on api-worker.
16
+ *
17
+ * Nothing here touches the network; these are the pure packing helpers the
18
+ * parser, the api-worker resolver and the web-app client all share.
19
+ */
20
+ /** The two slots whose model is a weapon triple rather than a gear pair. */
21
+ export const CHARA_WEAPON_SLOTS = ['MainHand', 'OffHand'];
22
+ export function isCharaWeaponSlot(slot) {
23
+ return CHARA_WEAPON_SLOTS.includes(slot);
24
+ }
25
+ /**
26
+ * `.chara` slot → `EquipSlotCategory` boolean column. This is the mandatory
27
+ * second search key: `+EquipSlotCategory.<field>=1`.
28
+ */
29
+ export const CHARA_SLOT_SEARCH_FIELD = {
30
+ MainHand: 'MainHand',
31
+ OffHand: 'OffHand',
32
+ HeadGear: 'Head',
33
+ Body: 'Body',
34
+ Hands: 'Gloves',
35
+ Legs: 'Legs',
36
+ Feet: 'Feet',
37
+ Ears: 'Ears',
38
+ Neck: 'Neck',
39
+ Wrists: 'Wrists',
40
+ LeftRing: 'FingerL',
41
+ RightRing: 'FingerR',
42
+ };
43
+ const LANE = 65536;
44
+ /**
45
+ * Armour / accessory `ModelMain`: `base | variant << 16`. Computed with
46
+ * arithmetic rather than `<<` so a variant above 0x7FFF cannot overflow the
47
+ * signed 32-bit shift — the value is an unsigned 32-bit lane pair.
48
+ *
49
+ * Beech Mask of Casting: `gearModelKey(361, 5) === 328041` (Item#18085).
50
+ */
51
+ export function gearModelKey(base, variant) {
52
+ return base + variant * LANE;
53
+ }
54
+ /**
55
+ * Weapon `ModelMain`: `set | base << 16 | variant << 32`. Exceeds 2^32, so it
56
+ * is a BigInt — serialise with `String()` for JSON / cache keys.
57
+ *
58
+ * Runaway Bow: `weaponModelKey(634, 19, 1) === 4296213114n` (Item#49486);
59
+ * its quiver `{698, 149, 1}` is that item's `ModelSub`.
60
+ */
61
+ export function weaponModelKey(set, base, variant) {
62
+ return BigInt(set) + (BigInt(base) << 16n) + (BigInt(variant) << 32n);
63
+ }
64
+ /**
65
+ * The packed key as a decimal string — the form used on the wire and in
66
+ * cache keys (weapon values do not fit a JS number exactly past 2^53 only in
67
+ * theory, but a BigInt never round-trips JSON, so everything is a string).
68
+ */
69
+ export function charaModelKey(model) {
70
+ return isCharaWeaponSlot(model.slot)
71
+ ? String(weaponModelKey(model.set ?? 0, model.base, model.variant))
72
+ : String(gearModelKey(model.base, model.variant));
73
+ }
74
+ /**
75
+ * The human label for a model with no Item row: `set·base·variant` for a
76
+ * weapon, `base·variant` otherwise (the design's "MODEL 9903·1").
77
+ */
78
+ export function formatCharaModelLabel(model) {
79
+ return isCharaWeaponSlot(model.slot)
80
+ ? `${model.set ?? 0}·${model.base}·${model.variant}`
81
+ : `${model.base}·${model.variant}`;
82
+ }
83
+ /** True when the record describes a worn piece rather than an empty slot. */
84
+ export function isWornCharaModel(slot, set, base) {
85
+ return isCharaWeaponSlot(slot) ? set > 0 || base > 0 : base > 0;
86
+ }
87
+ //# sourceMappingURL=chara-models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chara-models.js","sourceRoot":"","sources":["../../../src/services/chara/chara-models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAeH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,kBAAkB,GAA+B,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AAEtF,MAAM,UAAU,iBAAiB,CAAC,IAAqB;IACrD,OAAO,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAoC;IACtE,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,MAAM;IAChB,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE,SAAS;CACrB,CAAC;AAEF,MAAM,IAAI,GAAG,KAAK,CAAC;AAEnB;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,OAAe;IACxD,OAAO,IAAI,GAAG,OAAO,GAAG,IAAI,CAAC;AAC/B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,IAAY,EAAE,OAAe;IACvE,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;AACxE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAqB;IACjD,OAAO,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC;QAClC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACnE,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAqB;IACzD,OAAO,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC;QAClC,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE;QACpD,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;AACvC,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAC9B,IAAqB,EACrB,GAAW,EACX,IAAY;IAEZ,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * `.chara` character-file parser — the 5.0 Swatch Matcher import.
3
+ *
4
+ * Rules measured across 112 sample files (design register, Swatch Matcher
5
+ * 10A, confirmed):
6
+ * - **Parse by key presence, never the declared `TypeName`** — producers
7
+ * (Anamnesis versions, Ktisis) write different key sets. `TypeName` is
8
+ * captured only to *show* the producer.
9
+ * - **`REyeColor` holds the LEFT eye and `LEyeColor` the right** — crossed
10
+ * against the extended floats (`LeftEyeColor`/`RightEyeColor`); trust the
11
+ * extended naming. Never "fix" this by swapping — 16% of samples are
12
+ * heterochromia and a swap corrupts them silently.
13
+ * - **Extended floats are linear RGB** — gamma-encode before use or every
14
+ * colour is wrong-but-plausible.
15
+ * - **Flags gate live-looking data**: `EnableHighlights: false` inerts the
16
+ * highlight index (54% of samples, 47 of those with a live-looking index);
17
+ * `FacePaint: 0` inerts `FacePaintColor` (only 0 is load-bearing — values
18
+ * ≥128 exist, do not range-validate `FacePaint` itself).
19
+ * - **`LipsToneFurPattern` is a fur-pattern enum on Hrothgar**, not a colour.
20
+ * - **`MouthColor` alpha is a continuous opacity**: `0` = no lip; an absent
21
+ * key ≠ 0 (Ktisis omits it and the index stays valid).
22
+ * - **`IsExtendedAppearanceValid` is unreliable** (declared in 16 of 96
23
+ * float-bearing files): the floats count as live only when it is present
24
+ * and true — a missing flag means the index wins, and the UI says so.
25
+ * - **Gear dyes are stain IDs** (`DyeId`/`DyeId2` per slot, 0 = undyed; 35%
26
+ * of dyed channels are the second one).
27
+ * - **Gear models are emitted beside the dyes** (`gearModels`): the
28
+ * `ModelBase`/`ModelVariant` pair (weapons add `ModelSet`) of every WORN
29
+ * slot — `ModelBase == 0` (weapons: `ModelSet == 0` too) is an empty slot
30
+ * and is skipped. `null` hand records (one Anamnesis file) are empty.
31
+ * `Glasses` is `{ GlassesId }` or, on Brio-era Ktisis, a bare integer —
32
+ * both are accepted; 0 = no facewear. Resolution to item names happens
33
+ * off-device (api-worker) — the parser only carries the keys.
34
+ * - **Never read `Base64Image`.**
35
+ * - An unrecognised tribe/race/gender fails loudly (name got vs expected),
36
+ * never an empty palette.
37
+ */
38
+ import type { Gender, Race, RGB, SubRace } from '@xivdyetools/types';
39
+ import { type CharaGearModel } from './chara-models.js';
40
+ /** Colour slots a `.chara` file can populate. */
41
+ export type CharaSlotId = 'leftEye' | 'rightEye' | 'hair' | 'highlights' | 'skin' | 'limbal' | 'lip' | 'facePaint';
42
+ /** Equipment slots that can carry dye channels. */
43
+ export type CharaGearSlotId = 'MainHand' | 'OffHand' | 'HeadGear' | 'Body' | 'Hands' | 'Legs' | 'Feet' | 'Ears' | 'Neck' | 'Wrists' | 'LeftRing' | 'RightRing';
44
+ export type CharaSlotInertReason = 'highlightsDisabled' | 'facePaintNone' | 'furPattern' | 'noLip';
45
+ export interface CharaColorSlotRaw {
46
+ slot: CharaSlotId;
47
+ /** Raw palette index; null = key absent from the file */
48
+ index: number | null;
49
+ /** False when a flag gates the index off (see inertReason) */
50
+ indexActive: boolean;
51
+ inertReason?: CharaSlotInertReason;
52
+ /** Extended float colour, gamma-encoded to sRGB; null = key absent */
53
+ float: RGB | null;
54
+ /** The raw linear-RGB float triple as stored in the file */
55
+ floatLinear: [number, number, number] | null;
56
+ /** Lip only: continuous opacity. null = key absent (absent ≠ 0) */
57
+ alpha: number | null;
58
+ }
59
+ export interface CharaGearDye {
60
+ slot: CharaGearSlotId;
61
+ /** Which dye channel (1 = DyeId, 2 = DyeId2) */
62
+ channel: 1 | 2;
63
+ /** Stain ID as stored (1-254); 0/undyed channels are not emitted */
64
+ stainId: number;
65
+ }
66
+ export interface ParsedCharaFile {
67
+ /** Declared producer (`TypeName`), shown in UI — never used for parsing */
68
+ producer: string | null;
69
+ race: Race | null;
70
+ tribe: SubRace | null;
71
+ /** The tribe string exactly as the file spelled it */
72
+ tribeRaw: string | null;
73
+ gender: Gender | null;
74
+ /** Ktisis nickname — the default name for exported palettes */
75
+ nickname: string | null;
76
+ /** True only when IsExtendedAppearanceValid is present AND true */
77
+ extendedValid: boolean;
78
+ /** Whether the IsExtendedAppearanceValid key was present at all */
79
+ extendedDeclared: boolean;
80
+ slots: CharaColorSlotRaw[];
81
+ /** Dyed gear channels only (stainId > 0), in file slot order */
82
+ gearDyes: CharaGearDye[];
83
+ /** Worn pieces only (model base > 0), in file slot order — undyed pieces included */
84
+ gearModels: CharaGearModel[];
85
+ /** Facewear `Glasses` sheet row; null when none / absent */
86
+ glassesId: number | null;
87
+ }
88
+ /**
89
+ * Parse a `.chara` file's text content. Structural problems (invalid JSON,
90
+ * unrecognised tribe/race/gender, malformed colour values) throw AppError
91
+ * naming field and value; per-slot range problems are left to the resolver,
92
+ * which reports them as loud slot states.
93
+ */
94
+ export declare function parseCharaFile(text: string): ParsedCharaFile;
95
+ //# sourceMappingURL=chara-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chara-parser.d.ts","sourceRoot":"","sources":["../../../src/services/chara/chara-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGrE,OAAO,EAAuC,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE7F,iDAAiD;AACjD,MAAM,MAAM,WAAW,GACrB,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,WAAW,CAAC;AAE3F,mDAAmD;AACnD,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,SAAS,GACT,UAAU,GACV,MAAM,GACN,OAAO,GACP,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,GACN,QAAQ,GACR,UAAU,GACV,WAAW,CAAC;AAEhB,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,GAAG,eAAe,GAAG,YAAY,GAAG,OAAO,CAAC;AAEnG,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,yDAAyD;IACzD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,8DAA8D;IAC9D,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,sEAAsE;IACtE,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;IAClB,4DAA4D;IAC5D,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC7C,mEAAmE;IACnE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,eAAe,CAAC;IACtB,gDAAgD;IAChD,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;IACf,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,mEAAmE;IACnE,aAAa,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,gEAAgE;IAChE,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,qFAAqF;IACrF,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,4DAA4D;IAC5D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAuKD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAiI5D"}