@datamoc/mw_games 0.5.2 → 0.5.3

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 (71) hide show
  1. package/README.md +7 -4
  2. package/dist/i18n/EditSession.d.ts +101 -0
  3. package/dist/i18n/EditSession.d.ts.map +1 -0
  4. package/dist/i18n/EditSession.js +150 -0
  5. package/dist/i18n/EditSession.js.map +1 -0
  6. package/dist/i18n/Fluent.js +9 -1
  7. package/dist/i18n/Fluent.js.map +1 -1
  8. package/dist/i18n/FormatSpec.d.ts +74 -0
  9. package/dist/i18n/FormatSpec.d.ts.map +1 -0
  10. package/dist/i18n/FormatSpec.js +302 -0
  11. package/dist/i18n/FormatSpec.js.map +1 -0
  12. package/dist/i18n/SemanticMessage.d.ts +15 -2
  13. package/dist/i18n/SemanticMessage.d.ts.map +1 -1
  14. package/dist/i18n/SemanticMessage.js +16 -2
  15. package/dist/i18n/SemanticMessage.js.map +1 -1
  16. package/dist/i18n/Validate.d.ts +26 -0
  17. package/dist/i18n/Validate.d.ts.map +1 -1
  18. package/dist/i18n/Validate.js +33 -0
  19. package/dist/i18n/Validate.js.map +1 -1
  20. package/dist/i18n/index.d.ts +32 -2
  21. package/dist/i18n/index.d.ts.map +1 -1
  22. package/dist/i18n/index.js +76 -9
  23. package/dist/i18n/index.js.map +1 -1
  24. package/dist/mw_games.global.js +42 -35
  25. package/dist/two-d/render/AnimatedSprite.d.ts +4 -0
  26. package/dist/two-d/render/AnimatedSprite.d.ts.map +1 -1
  27. package/dist/two-d/render/AnimatedSprite.js +4 -0
  28. package/dist/two-d/render/AnimatedSprite.js.map +1 -1
  29. package/dist/two-d/render/LayeredSprite.d.ts +8 -0
  30. package/dist/two-d/render/LayeredSprite.d.ts.map +1 -1
  31. package/dist/two-d/render/LayeredSprite.js +8 -0
  32. package/dist/two-d/render/LayeredSprite.js.map +1 -1
  33. package/dist/two-d/render/TileMap.d.ts +7 -0
  34. package/dist/two-d/render/TileMap.d.ts.map +1 -1
  35. package/dist/two-d/render/TileMap.js +7 -0
  36. package/dist/two-d/render/TileMap.js.map +1 -1
  37. package/dist/two-d/render/TintedSprite.d.ts +12 -0
  38. package/dist/two-d/render/TintedSprite.d.ts.map +1 -1
  39. package/dist/two-d/render/TintedSprite.js +12 -0
  40. package/dist/two-d/render/TintedSprite.js.map +1 -1
  41. package/dist/two-d/ui/Label.d.ts +14 -0
  42. package/dist/two-d/ui/Label.d.ts.map +1 -1
  43. package/dist/two-d/ui/Label.js +40 -0
  44. package/dist/two-d/ui/Label.js.map +1 -1
  45. package/dist/two-d/ui/MessageBox.d.ts +1 -1
  46. package/dist/two-d/ui/MessageBox.d.ts.map +1 -1
  47. package/dist/two-d/ui/MessageBox.js +8 -7
  48. package/dist/two-d/ui/MessageBox.js.map +1 -1
  49. package/dist/two-d/ui/NinePatch.d.ts +2 -0
  50. package/dist/two-d/ui/NinePatch.d.ts.map +1 -1
  51. package/dist/two-d/ui/NinePatch.js +2 -0
  52. package/dist/two-d/ui/NinePatch.js.map +1 -1
  53. package/dist/two-d/ui/RichLabel.d.ts +56 -0
  54. package/dist/two-d/ui/RichLabel.d.ts.map +1 -0
  55. package/dist/two-d/ui/RichLabel.js +135 -0
  56. package/dist/two-d/ui/RichLabel.js.map +1 -0
  57. package/dist/two-d/ui/index.d.ts +6 -0
  58. package/dist/two-d/ui/index.d.ts.map +1 -1
  59. package/dist/two-d/ui/index.js +3 -0
  60. package/dist/two-d/ui/index.js.map +1 -1
  61. package/dist/two-d/ui/markdown.d.ts +55 -0
  62. package/dist/two-d/ui/markdown.d.ts.map +1 -0
  63. package/dist/two-d/ui/markdown.js +145 -0
  64. package/dist/two-d/ui/markdown.js.map +1 -0
  65. package/dist/two-d/ui/reveal.d.ts +41 -0
  66. package/dist/two-d/ui/reveal.d.ts.map +1 -0
  67. package/dist/two-d/ui/reveal.js +22 -0
  68. package/dist/two-d/ui/reveal.js.map +1 -0
  69. package/dist/version.d.ts +1 -1
  70. package/dist/version.js +1 -1
  71. package/package.json +5 -1
@@ -0,0 +1,302 @@
1
+ /**
2
+ * Python f-string-style value formatting for catalog messages: `{dmg:03d}`, `{hp:.1%}`,
3
+ * `{name:>12}`. Plain `{token}` interpolation stays exactly as it was; a format spec after
4
+ * a colon picks a deterministic, locale-independent rendering (padded damage numbers, hex
5
+ * ids, fixed-precision percentages), the counterpart to `Format.ts`, whose `Intl` wrappers
6
+ * render the locale-aware way instead ("1,234" vs "1 234").
7
+ *
8
+ * Supported subset of
9
+ * https://docs.python.org/3/library/string.html#format-specification-mini-language -
10
+ * `[[fill]align][sign][z][#][0][width][,|_][.precision][type]` with types
11
+ * `b c d e E f F g G n o s x X %`, verified case by case against CPython itself
12
+ * (two-digit exponents, stripped `g` precision, `_` grouping binary/octal/hex in fours).
13
+ * Deliberately outside the subset: nested replacement fields in the spec (`{:{width}}`),
14
+ * which are left for a game to resolve before calling `t()`. Anything outside the subset
15
+ * returns `undefined` rather than guessing, and `t()` leaves that placeholder untouched
16
+ * the way it already does for a missing token.
17
+ *
18
+ * One divergence no implementation choice can close: JavaScript numbers carry no
19
+ * int/float distinction, so integer-valued floats always take the integer path (`3.0`
20
+ * renders as `3`, and takes `d` where CPython would raise for a float). Games that need
21
+ * `3.0` spell it with an explicit float type (`{v:.1f}`).
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { formatSpec, tokenizeMessage, diffPlaceholders } from '@datamoc/mw_games/i18n';
26
+ *
27
+ * formatSpec(7, '03d'); // '007'
28
+ * formatSpec(0.125, '.1%'); // '12.5%'
29
+ * formatSpec('hi', '^7'); // ' hi '
30
+ *
31
+ * tokenizeMessage('Hit for {dmg:03d}!');
32
+ * // ['Hit for ', { raw: '{dmg:03d}', token: 'dmg', debug: false, conv: undefined, spec: '03d' }, '!']
33
+ *
34
+ * diffPlaceholders('Hit for {dmg:03d}!', 'Touché pour {dmg} !');
35
+ * // { missing: [], extra: [], changed: [{ token: 'dmg', base: ':03d', target: '' }] }
36
+ * ```
37
+ */
38
+ const PLACEHOLDER_PATTERN = /\{(\w+)(=)?(?:!([sra]))?(?::([^{}]*))?\}/g;
39
+ /**
40
+ * Splits a message into literal text and `{token}`, `{token:spec}`, `{token!conv}` and
41
+ * `{token=}` placeholders - the single definition of that grammar, shared by `t()`'s own
42
+ * interpolation and by `diffPlaceholders` below, so the editor's consistency check can
43
+ * never disagree with the runtime about what counts as a placeholder.
44
+ */
45
+ export function tokenizeMessage(text) {
46
+ const parts = [];
47
+ PLACEHOLDER_PATTERN.lastIndex = 0;
48
+ let cursor = 0;
49
+ let match;
50
+ while ((match = PLACEHOLDER_PATTERN.exec(text)) !== null) {
51
+ if (match.index > cursor)
52
+ parts.push(text.slice(cursor, match.index));
53
+ //group 1 always participates; the rest may be absent at runtime
54
+ const token = match[1];
55
+ const conv = match[3];
56
+ const spec = match[4];
57
+ parts.push({ raw: match[0], token, debug: match[2] === '=', conv, spec });
58
+ cursor = match.index + match[0].length;
59
+ }
60
+ if (cursor < text.length)
61
+ parts.push(text.slice(cursor));
62
+ return parts;
63
+ }
64
+ function placeholderSignature(part) {
65
+ return `${part.debug ? '=' : ''}${part.conv ? `!${part.conv}` : ''}${part.spec === undefined ? '' : `:${part.spec}`}`;
66
+ }
67
+ /**
68
+ * Compares two message texts placeholder by placeholder - the check a translation editor
69
+ * runs between a reference string and its translation, since a dropped or reworded token
70
+ * (`{dmg:03d}` becoming `{damage}`, or vanishing entirely) reaches the player as a raw
71
+ * placeholder exactly the way a missing key would.
72
+ */
73
+ export function diffPlaceholders(baseText, targetText) {
74
+ const base = new Map();
75
+ const target = new Map();
76
+ for (const part of tokenizeMessage(baseText)) {
77
+ if (typeof part !== 'string')
78
+ base.set(part.token, placeholderSignature(part));
79
+ }
80
+ for (const part of tokenizeMessage(targetText)) {
81
+ if (typeof part !== 'string')
82
+ target.set(part.token, placeholderSignature(part));
83
+ }
84
+ return {
85
+ missing: [...base.keys()].filter((token) => !target.has(token)),
86
+ extra: [...target.keys()].filter((token) => !base.has(token)),
87
+ changed: [...base].flatMap(([token, baseSig]) => {
88
+ const targetSig = target.get(token);
89
+ return targetSig !== undefined && targetSig !== baseSig ? [{ token, base: baseSig, target: targetSig }] : [];
90
+ }),
91
+ };
92
+ }
93
+ const SPEC_PATTERN = /^(?:(.)?([<>=^]))?([+\- ])?(z)?(#)?(0)?(\d+)?([,_])?(?:\.(\d+))?([bcdeEfFgGnosxX%])?$/;
94
+ const INT_RADIX = { b: 2, o: 8, x: 16, X: 16 };
95
+ const INT_PREFIX = { b: '0b', o: '0o', x: '0x', X: '0X' };
96
+ const FINITE_FLOAT_TYPES = new Set(['f', 'F', 'e', 'E', 'g', 'G', '%', 'n']);
97
+ /** `str()` for a number: integers plain, non-integers as-is, infinities Python-spelled */
98
+ function reprNumber(value) {
99
+ if (Number.isNaN(value))
100
+ return 'nan';
101
+ if (value === Infinity)
102
+ return 'inf';
103
+ if (value === -Infinity)
104
+ return '-inf';
105
+ if (Object.is(value, -0))
106
+ return '-0.0';
107
+ return String(value);
108
+ }
109
+ function groupDigits(digits, separator, size = 3, digit = '\\d') {
110
+ return digits.replace(new RegExp(`\\B(?=(${digit}{${size}})+(?!${digit}))`, 'g'), separator);
111
+ }
112
+ /** groups the leading digit run of a decimal rendering (`1,234.5`, `1_234.5`) */
113
+ function groupDecimalRendering(text, separator) {
114
+ const match = /^(\d+)/.exec(text);
115
+ if (!match)
116
+ return text;
117
+ return groupDigits(match[1], separator) + text.slice(match[1].length);
118
+ }
119
+ function padText(text, width, fill, align) {
120
+ if (text.length >= width)
121
+ return text;
122
+ const pad = fill.repeat(width - text.length);
123
+ if (align === '<')
124
+ return text + pad;
125
+ //a centered field with an odd spare character puts it on the right, like CPython
126
+ if (align === '^')
127
+ return pad.slice(0, Math.floor(pad.length / 2)) + text + pad.slice(Math.floor(pad.length / 2));
128
+ return pad + text;
129
+ }
130
+ /** CPython pads an exponent to at least two digits (`1.23e+3` renders as `1.23e+03`) */
131
+ function padExponent(text) {
132
+ return text.replace(/([eE][+-])(\d)$/, '$10$2');
133
+ }
134
+ /**
135
+ * Significant-digits formatting with stripped zeros. CPython's `g` renders fixed while
136
+ * the exponent stays below the precision; a bare precision without a type (`{v:.4}`)
137
+ * switches to exponential one digit sooner - same shape, a tighter threshold.
138
+ */
139
+ function formatGeneral(magnitude, precision, upper, alt, fixedBelow) {
140
+ const digits = precision === 0 ? 1 : precision;
141
+ if (magnitude === 0)
142
+ return alt ? `0.${'0'.repeat(digits - 1)}` : '0';
143
+ const exponent = Math.floor(Math.log10(magnitude));
144
+ let out;
145
+ if (exponent >= -4 && exponent < (fixedBelow ?? digits)) {
146
+ out = magnitude.toFixed(digits - 1 - exponent);
147
+ if (!alt)
148
+ out = out.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
149
+ }
150
+ else {
151
+ out = padExponent(magnitude.toExponential(digits - 1));
152
+ if (!alt)
153
+ out = out.replace(/(\.\d*?)0+(e|$)/, '$1$2').replace(/\.e/i, 'e');
154
+ }
155
+ return upper ? out.toUpperCase() : out;
156
+ }
157
+ function localeSeparators(language) {
158
+ let group = ',';
159
+ let decimal = '.';
160
+ for (const part of new Intl.NumberFormat(language).formatToParts(1000.5)) {
161
+ if (part.type === 'group')
162
+ group = part.value;
163
+ if (part.type === 'decimal')
164
+ decimal = part.value;
165
+ }
166
+ return { group, decimal };
167
+ }
168
+ function formatStringSpec(value, fillChar, alignChar, zeroFlag, width, precision) {
169
+ const text = precision === undefined ? value : value.slice(0, precision);
170
+ //unlike numbers, a string keeps its left alignment behind a `0` flag (`'ab'` as
171
+ //`05` renders `'ab000'`), since zero-padding a word has no sign to sit behind
172
+ return padText(text, width, fillChar ?? (zeroFlag ? '0' : ' '), alignChar ?? '<');
173
+ }
174
+ export function formatSpec(value, spec, language = 'en') {
175
+ if (spec === '')
176
+ return typeof value === 'number' ? reprNumber(value) : value;
177
+ const parsed = SPEC_PATTERN.exec(spec);
178
+ if (!parsed)
179
+ return undefined;
180
+ const groups = parsed.slice(1);
181
+ const [fillChar, alignChar, sign, zFlag, altFlag, zeroFlag, widthDigits, grouping, precisionDigits, type] = groups;
182
+ const precision = precisionDigits === undefined ? undefined : Number(precisionDigits);
183
+ const width = widthDigits === undefined ? 0 : Number(widthDigits);
184
+ if (typeof value === 'string') {
185
+ //strings take width/alignment/fill and `.precision` truncation only - every other
186
+ //option is numeric, and CPython raises for those rather than coercing the string
187
+ if (sign || zFlag || altFlag || grouping || alignChar === '=' || (type !== undefined && type !== 's'))
188
+ return undefined;
189
+ return formatStringSpec(value, fillChar, alignChar, zeroFlag, width, precision);
190
+ }
191
+ //CPython's `s` is string-only: a number behind one raises rather than stringifying
192
+ if (type === 's')
193
+ return undefined;
194
+ if (type === 'c') {
195
+ //a code point, not a number rendered as text - numeric dressing of any kind is refused
196
+ if (sign || altFlag || zeroFlag || grouping || precision !== undefined)
197
+ return undefined;
198
+ const code = Math.trunc(value);
199
+ if (code < 0 || code > 0x10ffff)
200
+ return undefined;
201
+ return padText(String.fromCodePoint(code), width, fillChar ?? ' ', alignChar ?? '>');
202
+ }
203
+ if (!Number.isFinite(value)) {
204
+ if (type !== undefined && !FINITE_FLOAT_TYPES.has(type))
205
+ return undefined;
206
+ //CPython spells these lowercase except behind an uppercase float type
207
+ let text = Number.isNaN(value) ? 'nan' : 'inf';
208
+ if (type === 'F' || type === 'E' || type === 'G')
209
+ text = text.toUpperCase();
210
+ if (type === '%')
211
+ text += '%';
212
+ const signText = value < 0 ? '-' : sign === '+' ? '+' : sign === ' ' ? ' ' : '';
213
+ return padText(signText + text, width, fillChar ?? ' ', alignChar ?? '>');
214
+ }
215
+ let negative = value < 0 || Object.is(value, -0);
216
+ //Python 3.11's `z`: a negative zero renders as a positive one
217
+ if (zFlag && value === 0)
218
+ negative = false;
219
+ const magnitude = Math.abs(value);
220
+ const separator = grouping === ',' ? ',' : grouping === '_' ? '_' : '';
221
+ let body;
222
+ if (type === undefined) {
223
+ //no type: `str()` plus sign/grouping/width - a precision without a type reads as
224
+ //significant digits, except on an integer, where CPython raises
225
+ if (precision !== undefined) {
226
+ if (Number.isInteger(magnitude))
227
+ return undefined;
228
+ body = formatGeneral(magnitude, precision, false, !!altFlag, precision - 1);
229
+ }
230
+ else {
231
+ body = reprNumber(magnitude);
232
+ }
233
+ if (separator)
234
+ body = groupDecimalRendering(body, separator);
235
+ }
236
+ else if (type === 'd') {
237
+ if (precision !== undefined || !Number.isInteger(magnitude))
238
+ return undefined;
239
+ body = String(Math.trunc(magnitude));
240
+ if (separator)
241
+ body = groupDigits(body, separator);
242
+ }
243
+ else if (type in INT_RADIX) {
244
+ //integer-only, like `d`; `,` is refused on a non-decimal base while `_` groups
245
+ //binary, octal and hex in fours rather than threes (`dead_beef`, `1010_1010`)
246
+ if (precision !== undefined || !Number.isInteger(magnitude) || grouping === ',')
247
+ return undefined;
248
+ body = Math.trunc(magnitude).toString(INT_RADIX[type]);
249
+ if (type === 'X')
250
+ body = body.toUpperCase();
251
+ if (separator)
252
+ body = groupDigits(body, separator, 4, '[0-9a-fA-F]');
253
+ if (altFlag)
254
+ body = INT_PREFIX[type] + body;
255
+ }
256
+ else if (type === 'n') {
257
+ //locale-aware, so it shares none of the fixed separators above - and takes none
258
+ if (grouping || (precision !== undefined && Number.isInteger(magnitude)))
259
+ return undefined;
260
+ if (Number.isInteger(magnitude)) {
261
+ body = new Intl.NumberFormat(language).format(Math.trunc(magnitude));
262
+ }
263
+ else {
264
+ const { group, decimal } = localeSeparators(language);
265
+ const general = formatGeneral(magnitude, precision ?? 6, false, !!altFlag);
266
+ const mantissa = general.split('e')[0];
267
+ const exponent = general.slice(mantissa.length);
268
+ body = groupDecimalRendering(mantissa, group).replace('.', decimal) + exponent;
269
+ }
270
+ }
271
+ else if (type === 'f' || type === 'F') {
272
+ body = magnitude.toFixed(precision ?? 6);
273
+ if (separator)
274
+ body = groupDecimalRendering(body, separator);
275
+ }
276
+ else if (type === 'e' || type === 'E') {
277
+ body = padExponent(magnitude.toExponential(precision ?? 6));
278
+ if (type === 'E')
279
+ body = body.toUpperCase();
280
+ if (separator)
281
+ body = groupDecimalRendering(body, separator);
282
+ }
283
+ else if (type === 'g' || type === 'G') {
284
+ body = formatGeneral(magnitude, precision ?? 6, type === 'G', !!altFlag);
285
+ if (separator)
286
+ body = groupDecimalRendering(body, separator);
287
+ }
288
+ else {
289
+ // '%' - fixed digits of the value times a hundred, then the sign itself
290
+ const hundredths = magnitude * 100;
291
+ body = (Number.isFinite(hundredths) ? hundredths.toFixed(precision ?? 6) : 'inf') + '%';
292
+ if (separator && Number.isFinite(hundredths))
293
+ body = groupDecimalRendering(body, separator);
294
+ }
295
+ const signText = negative ? '-' : sign === '+' ? '+' : sign === ' ' ? ' ' : '';
296
+ const fill = fillChar ?? (zeroFlag && !alignChar ? '0' : ' ');
297
+ const align = alignChar ?? (zeroFlag && !alignChar ? '=' : '>');
298
+ if (align === '=')
299
+ return signText + padText(body, width - signText.length, fill, '>');
300
+ return padText(signText + body, width, fill, align);
301
+ }
302
+ //# sourceMappingURL=FormatSpec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormatSpec.js","sourceRoot":"","sources":["../../src/i18n/FormatSpec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAaH,MAAM,mBAAmB,GAAG,2CAA2C,CAAC;AAExE;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,mBAAmB,CAAC,SAAS,GAAG,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,KAA6B,CAAC;IAClC,OAAO,CAAC,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC1D,IAAI,KAAK,CAAC,KAAK,GAAG,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,gEAAgE;QAChE,MAAM,KAAK,GAAW,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAuB,CAAC;QAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAuB,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1E,MAAM,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACxC,CAAC;IACD,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAiB;IAC9C,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;AACvH,CAAC;AAWD;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,UAAkB;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9C,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;IAChF,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;IAClF,CAAC;IACD,OAAO;QACN,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/D,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC7D,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,EAAE;YAC/C,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACpC,OAAO,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9G,CAAC,CAAC;KACF,CAAC;AACH,CAAC;AACD,MAAM,YAAY,GAAG,uFAAuF,CAAC;AAE7G,MAAM,SAAS,GAA2B,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;AACvE,MAAM,UAAU,GAA2B,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;AAClF,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAE7E,0FAA0F;AAC1F,SAAS,UAAU,CAAC,KAAa;IAChC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,KAAK,KAAK,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAC;IACvC,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACxC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,WAAW,CAAC,MAAc,EAAE,SAAiB,EAAE,IAAI,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK;IAC9E,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;AAC9F,CAAC;AAED,iFAAiF;AACjF,SAAS,qBAAqB,CAAC,IAAY,EAAE,SAAiB;IAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,KAAa,EAAE,IAAY,EAAE,KAAa;IACxE,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,IAAI,GAAG,GAAG,CAAC;IACrC,iFAAiF;IACjF,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAClH,OAAO,GAAG,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,wFAAwF;AACxF,SAAS,WAAW,CAAC,IAAY;IAChC,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,SAAiB,EAAE,SAAiB,EAAE,KAAc,EAAE,GAAY,EAAE,UAAmB;IAC7G,MAAM,MAAM,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/C,IAAI,SAAS,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IACnD,IAAI,GAAW,CAAC;IAChB,IAAI,QAAQ,IAAI,CAAC,CAAC,IAAI,QAAQ,GAAG,CAAC,UAAU,IAAI,MAAM,CAAC,EAAE,CAAC;QACzD,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;SAAM,CAAC;QACP,GAAG,GAAG,WAAW,CAAC,SAAS,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,GAAG;YAAE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AACxC,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAgB;IACzC,IAAI,KAAK,GAAG,GAAG,CAAC;IAChB,IAAI,OAAO,GAAG,GAAG,CAAC;IAClB,KAAK,MAAM,IAAI,IAAI,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1E,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;YAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QAC9C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC;IACnD,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,gBAAgB,CACxB,KAAa,EACb,QAA4B,EAC5B,SAA6B,EAC7B,QAA4B,EAC5B,KAAa,EACb,SAA6B;IAE7B,MAAM,IAAI,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACzE,gFAAgF;IAChF,8EAA8E;IAC9E,OAAO,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,SAAS,IAAI,GAAG,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAsB,EAAE,IAAY,EAAE,QAAQ,GAAG,IAAI;IAC/E,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC9E,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,MAAM,GAA8B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC;IACnH,MAAM,SAAS,GAAG,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACtF,MAAM,KAAK,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAElE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,kFAAkF;QAClF,iFAAiF;QACjF,IAAI,IAAI,IAAI,KAAK,IAAI,OAAO,IAAI,QAAQ,IAAI,SAAS,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QACxH,OAAO,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACjF,CAAC;IAED,mFAAmF;IACnF,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,SAAS,CAAC;IACnC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QAClB,uFAAuF;QACvF,IAAI,IAAI,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACzF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,QAAQ;YAAE,OAAO,SAAS,CAAC;QAClD,OAAO,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,IAAI,GAAG,EAAE,SAAS,IAAI,GAAG,CAAC,CAAC;IACtF,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAC1E,sEAAsE;QACtE,IAAI,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/C,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG;YAAE,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5E,IAAI,IAAI,KAAK,GAAG;YAAE,IAAI,IAAI,GAAG,CAAC;QAC9B,MAAM,QAAQ,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,OAAO,OAAO,CAAC,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,QAAQ,IAAI,GAAG,EAAE,SAAS,IAAI,GAAG,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,QAAQ,GAAG,KAAK,GAAG,CAAC,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,8DAA8D;IAC9D,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC;QAAE,QAAQ,GAAG,KAAK,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACvE,IAAI,IAAY,CAAC;IAEjB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACxB,iFAAiF;QACjF,gEAAgE;QAChE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAC;YAClD,IAAI,GAAG,aAAa,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;QAC7E,CAAC;aAAM,CAAC;YACP,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,SAAS;YAAE,IAAI,GAAG,qBAAqB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC9D,CAAC;SAAM,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QACzB,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QAC9E,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;QACrC,IAAI,SAAS;YAAE,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACpD,CAAC;SAAM,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;QAC9B,+EAA+E;QAC/E,8EAA8E;QAC9E,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,QAAQ,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC;QAClG,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,IAAI,IAAI,KAAK,GAAG;YAAE,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5C,IAAI,SAAS;YAAE,IAAI,GAAG,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC;QACrE,IAAI,OAAO;YAAE,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7C,CAAC;SAAM,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QACzB,gFAAgF;QAChF,IAAI,QAAQ,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3F,IAAI,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;YACjC,IAAI,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;QACtE,CAAC;aAAM,CAAC;YACP,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YACtD,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,EAAE,SAAS,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YAC3E,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,GAAG,qBAAqB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC;QAChF,CAAC;IACF,CAAC;SAAM,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QACzC,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;QACzC,IAAI,SAAS;YAAE,IAAI,GAAG,qBAAqB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC9D,CAAC;SAAM,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QACzC,IAAI,GAAG,WAAW,CAAC,SAAS,CAAC,aAAa,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5D,IAAI,IAAI,KAAK,GAAG;YAAE,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5C,IAAI,SAAS;YAAE,IAAI,GAAG,qBAAqB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC9D,CAAC;SAAM,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QACzC,IAAI,GAAG,aAAa,CAAC,SAAS,EAAE,SAAS,IAAI,CAAC,EAAE,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QACzE,IAAI,SAAS;YAAE,IAAI,GAAG,qBAAqB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC9D,CAAC;SAAM,CAAC;QACP,wEAAwE;QACxE,MAAM,UAAU,GAAG,SAAS,GAAG,GAAG,CAAC;QACnC,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;QACxF,IAAI,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,IAAI,GAAG,qBAAqB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/E,MAAM,IAAI,GAAG,QAAQ,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,SAAS,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChE,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IACvF,OAAO,OAAO,CAAC,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AACrD,CAAC"}
@@ -33,8 +33,11 @@ export interface GrammaticalEntity {
33
33
  forms?: Record<string, string>;
34
34
  }
35
35
  /** Where the same semantic message ends up: a game log line, a compact HUD number, an
36
- * accessibility announcement, or a debug dump - one simulation event, several presentations. */
37
- export type MessageChannel = 'log' | 'compact' | 'accessibility' | 'debug';
36
+ * accessibility announcement, a debug dump, or the sound effect played alongside it - one
37
+ * simulation event, several presentations. The `audio` entry holds a sound path (an asset
38
+ * path `mwg/audio`'s `Sound` plays), not display text, so it resolves through `tRaw`
39
+ * rather than `t()`: no typographic spacing, no right-to-left wrapping. */
40
+ export type MessageChannel = 'log' | 'compact' | 'accessibility' | 'debug' | 'audio';
38
41
  export interface MessageFormatter {
39
42
  format(message: SemanticMessage, channel: MessageChannel): string;
40
43
  }
@@ -65,7 +68,17 @@ export interface MessageFormatter {
65
68
  * console.log(formatter.format(message, 'log')); // 'The gnoll takes 7 damage.'
66
69
  * console.log(formatter.format(message, 'compact')); // '-7 HP'
67
70
  * console.log(formatter.format(message, 'debug')); // falls back to the bare type: the key itself, if undeclared
71
+ * console.log(formatter.format(message, 'audio')); // 'sounds/sword-hit.wav', or '' when no '<type>.audio' entry exists
68
72
  * ```
73
+ *
74
+ * The `audio` channel is deliberately narrower than the text channels: it resolves only
75
+ * `<type>.audio`, never falling back to the bare type the way text does. A bare type
76
+ * holding a sentence (`'combat.damage': 'combat.damage generic'`) is not a playable path,
77
+ * so falling back to it would hand the game garbage to load. Sounds that need no
78
+ * per-channel variation are still shared - every text channel already falls back to the
79
+ * same bare type, while `audio` stays a separate entry a translator never touches and the
80
+ * base language usually owns alone (runtime lookup falls back to the base catalog, so a
81
+ * translated catalog needs no copy of it).
69
82
  */
70
83
  export declare function createCatalogFormatter(): MessageFormatter;
71
84
  //# sourceMappingURL=SemanticMessage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SemanticMessage.d.ts","sourceRoot":"","sources":["../../src/i18n/SemanticMessage.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe,CAC/B,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjE,IAAI,EAAE,KAAK,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,MAAM,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IACjC,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACxD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;gGACgG;AAChG,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,GAAG,eAAe,GAAG,OAAO,CAAC;AAE3E,MAAM,WAAW,gBAAgB;IAChC,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAAC;CAClE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAQzD"}
1
+ {"version":3,"file":"SemanticMessage.d.ts","sourceRoot":"","sources":["../../src/i18n/SemanticMessage.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe,CAC/B,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjE,IAAI,EAAE,KAAK,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,MAAM,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IACjC,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACxD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;;;2EAI2E;AAC3E,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,GAAG,eAAe,GAAG,OAAO,GAAG,OAAO,CAAC;AAErF,MAAM,WAAW,gBAAgB;IAChC,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAAC;CAClE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAYzD"}
@@ -1,4 +1,4 @@
1
- import { t, has } from './index.js';
1
+ import { t, tRaw, has } from './index.js';
2
2
  /**
3
3
  * The reference `MessageFormatter`: looks up `${type}.${channel}` in the active catalog
4
4
  * (`setBase`/`setActive`, `src/i18n/index.ts`), falling back to `${type}` alone for a message
@@ -26,13 +26,27 @@ import { t, has } from './index.js';
26
26
  * console.log(formatter.format(message, 'log')); // 'The gnoll takes 7 damage.'
27
27
  * console.log(formatter.format(message, 'compact')); // '-7 HP'
28
28
  * console.log(formatter.format(message, 'debug')); // falls back to the bare type: the key itself, if undeclared
29
+ * console.log(formatter.format(message, 'audio')); // 'sounds/sword-hit.wav', or '' when no '<type>.audio' entry exists
29
30
  * ```
31
+ *
32
+ * The `audio` channel is deliberately narrower than the text channels: it resolves only
33
+ * `<type>.audio`, never falling back to the bare type the way text does. A bare type
34
+ * holding a sentence (`'combat.damage': 'combat.damage generic'`) is not a playable path,
35
+ * so falling back to it would hand the game garbage to load. Sounds that need no
36
+ * per-channel variation are still shared - every text channel already falls back to the
37
+ * same bare type, while `audio` stays a separate entry a translator never touches and the
38
+ * base language usually owns alone (runtime lookup falls back to the base catalog, so a
39
+ * translated catalog needs no copy of it).
30
40
  */
31
41
  export function createCatalogFormatter() {
32
42
  return {
33
43
  format(message, channel) {
34
- const key = `${message.type}.${channel}`;
35
44
  const params = message.params;
45
+ if (channel === 'audio') {
46
+ const key = `${message.type}.audio`;
47
+ return has(key) ? tRaw(key, params) : '';
48
+ }
49
+ const key = `${message.type}.${channel}`;
36
50
  return has(key) ? t(key, params) : t(message.type, params);
37
51
  },
38
52
  };
@@ -1 +1 @@
1
- {"version":3,"file":"SemanticMessage.js","sourceRoot":"","sources":["../../src/i18n/SemanticMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,GAAG,EAAsB,MAAM,YAAY,CAAC;AAkDxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,sBAAsB;IACrC,OAAO;QACN,MAAM,CAAC,OAAwB,EAAE,OAAuB;YACvD,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;YACzC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAuB,CAAC;YAC/C,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5D,CAAC;KACD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"SemanticMessage.js","sourceRoot":"","sources":["../../src/i18n/SemanticMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAsB,MAAM,YAAY,CAAC;AAqD9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,UAAU,sBAAsB;IACrC,OAAO;QACN,MAAM,CAAC,OAAwB,EAAE,OAAuB;YACvD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAuB,CAAC;YAC/C,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;gBACzB,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,IAAI,QAAQ,CAAC;gBACpC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1C,CAAC;YACD,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;YACzC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5D,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -53,4 +53,30 @@ export interface CatalogIssue {
53
53
  * ```
54
54
  */
55
55
  export declare function validateCatalog(catalog: Catalog): CatalogIssue[];
56
+ export interface AudioIssue {
57
+ /** the full `<type>.audio` key */
58
+ key: string;
59
+ kind: 'empty-audio-path' | 'audio-not-a-path';
60
+ detail: string;
61
+ }
62
+ /**
63
+ * Structural problems in a catalog's sound cues: an `<type>.audio` entry holding an
64
+ * empty string (the formatter already reads that as "no sound", so it is dead weight),
65
+ * or holding plural forms/a select message instead of a plain path. A sound path is data
66
+ * a game hands to `mwg/audio`'s `Sound`, not grammar - count- or variant-dependent paths
67
+ * would silently reduce to one branch at lookup time rather than selecting one per play.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * import { validateMessageAudio } from '@datamoc/mw_games/i18n';
72
+ *
73
+ * const catalog = {
74
+ * locale: 'en', direction: 'ltr' as const,
75
+ * messages: { 'hit.audio': '', 'loot.audio': { one: 'a.wav', other: 'b.wav' } },
76
+ * };
77
+ *
78
+ * console.log(validateMessageAudio(catalog).map((issue) => issue.key)); // ['hit.audio', 'loot.audio']
79
+ * ```
80
+ */
81
+ export declare function validateMessageAudio(catalog: Catalog): AudioIssue[];
56
82
  //# sourceMappingURL=Validate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Validate.d.ts","sourceRoot":"","sources":["../../src/i18n/Validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,YAAY,CAAC;AAExD;4DAC4D;AAC5D,MAAM,WAAW,cAAc;IAC9B,2DAA2D;IAC3D,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,iEAAiE;IACjE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,cAAc,CAOlF;AAED,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,eAAe,GAAG,sBAAsB,CAAC;IAC/C,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,EAAE,CAchE"}
1
+ {"version":3,"file":"Validate.d.ts","sourceRoot":"","sources":["../../src/i18n/Validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,YAAY,CAAC;AAExD;4DAC4D;AAC5D,MAAM,WAAW,cAAc;IAC9B,2DAA2D;IAC3D,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,iEAAiE;IACjE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,cAAc,CAOlF;AAED,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,eAAe,GAAG,sBAAsB,CAAC;IAC/C,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,EAAE,CAchE;AAMD,MAAM,WAAW,UAAU;IAC1B,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,EAAE,CAanE"}
@@ -62,4 +62,37 @@ export function validateCatalog(catalog) {
62
62
  function isPluralForms(value) {
63
63
  return typeof value === 'object' && value !== null && !('format' in value);
64
64
  }
65
+ /**
66
+ * Structural problems in a catalog's sound cues: an `<type>.audio` entry holding an
67
+ * empty string (the formatter already reads that as "no sound", so it is dead weight),
68
+ * or holding plural forms/a select message instead of a plain path. A sound path is data
69
+ * a game hands to `mwg/audio`'s `Sound`, not grammar - count- or variant-dependent paths
70
+ * would silently reduce to one branch at lookup time rather than selecting one per play.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * import { validateMessageAudio } from '@datamoc/mw_games/i18n';
75
+ *
76
+ * const catalog = {
77
+ * locale: 'en', direction: 'ltr' as const,
78
+ * messages: { 'hit.audio': '', 'loot.audio': { one: 'a.wav', other: 'b.wav' } },
79
+ * };
80
+ *
81
+ * console.log(validateMessageAudio(catalog).map((issue) => issue.key)); // ['hit.audio', 'loot.audio']
82
+ * ```
83
+ */
84
+ export function validateMessageAudio(catalog) {
85
+ const issues = [];
86
+ for (const [key, value] of Object.entries(catalog.messages)) {
87
+ if (!key.endsWith('.audio'))
88
+ continue;
89
+ if (typeof value !== 'string') {
90
+ issues.push({ key, kind: 'audio-not-a-path', detail: 'an audio entry must be a plain path string, not plural forms or a select message' });
91
+ }
92
+ else if (value === '') {
93
+ issues.push({ key, kind: 'empty-audio-path', detail: 'an empty audio entry already means "no sound"; remove the key instead' });
94
+ }
95
+ }
96
+ return issues;
97
+ }
65
98
  //# sourceMappingURL=Validate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Validate.js","sourceRoot":"","sources":["../../src/i18n/Validate.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAAC,SAAkB,EAAE,KAAc;IACjE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvD,OAAO;QACN,OAAO,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;KAC9D,CAAC;AACH,CAAC;AAQD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC/C,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/B,IAAI,KAAK,KAAK,EAAE;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC,CAAC;YAC7G,SAAS;QACV,CAAC;QACD,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YACvD,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC,CAAC;QACnG,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,KAAmB;IACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC;AAC5E,CAAC"}
1
+ {"version":3,"file":"Validate.js","sourceRoot":"","sources":["../../src/i18n/Validate.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAAC,SAAkB,EAAE,KAAc;IACjE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvD,OAAO;QACN,OAAO,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;KAC9D,CAAC;AACH,CAAC;AAQD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC/C,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/B,IAAI,KAAK,KAAK,EAAE;gBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC,CAAC;YAC7G,SAAS;QACV,CAAC;QACD,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YACvD,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC,CAAC;QACnG,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,KAAmB;IACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC;AAC5E,CAAC;AASD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACpD,MAAM,MAAM,GAAiB,EAAE,CAAC;IAEhC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7D,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,SAAS;QACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,kFAAkF,EAAE,CAAC,CAAC;QAC5I,CAAC;aAAM,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,uEAAuE,EAAE,CAAC,CAAC;QACjI,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -76,9 +76,34 @@ export interface MessageParams {
76
76
  count?: number;
77
77
  [token: string]: string | number | undefined;
78
78
  }
79
+ /**
80
+ * Resolves a message by key, interpolating `{token}` placeholders from `params`, without
81
+ * any display-text decoration: no `typographic` spacing, no right-to-left isolate wrapping.
82
+ * This is the resolver for catalog values that are not shown to the player - a sound path
83
+ * behind the semantic formatter's `audio` channel, an icon id, a font name - where a
84
+ * narrow no-break space or a bidi control character would corrupt the value rather than
85
+ * improve it.
86
+ *
87
+ * A key present in neither language returns itself, the same as `t()`.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * import { setBase, tRaw } from '@datamoc/mw_games/i18n';
92
+ *
93
+ * setBase({ locale: 'fr', direction: 'ltr', messages: { 'ui.click.audio': 'sounds/hit!.wav' } });
94
+ * tRaw('ui.click.audio'); // 'sounds/hit!.wav', with no extra spacing before the '!'
95
+ * ```
96
+ */
97
+ export declare function tRaw(key: string, params?: MessageParams): string;
79
98
  /**
80
99
  * Resolves a message by key, interpolating `{token}` placeholders from `params`.
81
100
  *
101
+ * Placeholders also take Python f-string-style fitting-out: `{dmg:03d}`, `{hp:.1%}`,
102
+ * `{name:>12}`, plus `!s`/`!r`/`!a` conversions and `=` debugging (`{dmg=}` renders as
103
+ * `dmg=42`), all resolved through `formatSpec`. A placeholder whose token is missing, or
104
+ * whose spec fits neither the value nor the supported subset, is left untouched rather
105
+ * than throwing or rendering half-formatted.
106
+ *
82
107
  * A key present in neither language returns itself, which is the only case where a raw key
83
108
  * can reach the player - and it means the key was never translated anywhere, not merely
84
109
  * missing from one language.
@@ -129,8 +154,13 @@ export type { FluentOptions } from './Fluent.ts';
129
154
  export { createCatalogFormatter } from './SemanticMessage.ts';
130
155
  export type { SemanticMessage, EntityTextResolver, GrammaticalEntity, MessageChannel, MessageFormatter } from './SemanticMessage.ts';
131
156
  export { formatNumber, formatDate, formatList } from './Format.ts';
132
- export { diffCatalogKeys, validateCatalog } from './Validate.ts';
133
- export type { CatalogKeyDiff, CatalogIssue } from './Validate.ts';
157
+ import { formatSpec, tokenizeMessage, diffPlaceholders } from './FormatSpec.ts';
158
+ export { formatSpec, tokenizeMessage, diffPlaceholders };
159
+ export type { Placeholder, MessagePart, PlaceholderDiff } from './FormatSpec.ts';
160
+ export { diffCatalogKeys, validateCatalog, validateMessageAudio } from './Validate.ts';
161
+ export type { CatalogKeyDiff, CatalogIssue, AudioIssue } from './Validate.ts';
162
+ export { AUDIO_SUFFIX, copyFromBase, createEditSession, cueKeyFor, deleteTargetKey, isAudioKey, sessionCompleteness, sessionKeys, sessionRow, sessionRows, setTargetSound, setTargetText, swapSession, } from './EditSession.ts';
163
+ export type { EditRow, EditSession, RowFilter } from './EditSession.ts';
134
164
  export { messageText, levenshteinDistance, findSimilarMessages, catalogUsage, catalogCompleteness, pluralFormCoverage, mergeCatalogKeys, } from './Content.ts';
135
165
  export type { SimilarMessagePair, CatalogUsageStats, PluralCoverage } from './Content.ts';
136
166
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,6FAA6F;AAC7F,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC;AAEtC,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvE,MAAM,WAAW,aAAa;IAC7B,MAAM,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;CACvC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC;AAEhE,MAAM,WAAW,OAAO;IACvB,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACvC,4FAA4F;IAC5F,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAOD,uEAAuE;AACvE,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE9C;AAED,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAGvD;AAED,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,6CAA6C;AAC7C,wBAAgB,SAAS,IAAI,SAAS,CAErC;AAED,MAAM,WAAW,aAAa;IAC7B,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC7C;AAED;;;;;;GAMG;AACH,wBAAgB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAkB7D;AAoBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAW,GAAG,MAAM,CAYrE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAW,GAAG,MAAM,CAIjG;AAiCD,kFAAkF;AAClF,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExC;AAsBD,gFAAgF;AAChF,wBAAgB,KAAK,IAAI,IAAI,CAI5B;AAED,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAErI,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,EACN,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,6FAA6F;AAC7F,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC;AAEtC,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvE,MAAM,WAAW,aAAa;IAC7B,MAAM,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;CACvC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC;AAEhE,MAAM,WAAW,OAAO;IACvB,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACvC,4FAA4F;IAC5F,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAOD,uEAAuE;AACvE,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE9C;AAED,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAGvD;AAED,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,6CAA6C;AAC7C,wBAAgB,SAAS,IAAI,SAAS,CAErC;AAED,MAAM,WAAW,aAAa;IAC7B,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAMhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAgB7D;AAoBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAW,GAAG,MAAM,CAYrE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAW,GAAG,MAAM,CAIjG;AAiCD,kFAAkF;AAClF,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExC;AAwDD,gFAAgF;AAChF,wBAAgB,KAAK,IAAI,IAAI,CAI5B;AAED,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAErI,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAoB,MAAM,iBAAiB,CAAC;AAClG,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEjF,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACvF,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE9E,OAAO,EACN,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,UAAU,EACV,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,EACd,aAAa,EACb,WAAW,GACX,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAExE,OAAO,EACN,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}