volleyballsimtypes 0.0.527 → 0.0.528

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 (39) hide show
  1. package/dist/cjs/src/api/index.d.ts +3 -1
  2. package/dist/cjs/src/data/init-models.js +5 -0
  3. package/dist/cjs/src/data/models/index.d.ts +1 -0
  4. package/dist/cjs/src/data/models/index.js +1 -0
  5. package/dist/cjs/src/data/models/team-badge.d.ts +20 -0
  6. package/dist/cjs/src/data/models/team-badge.js +38 -0
  7. package/dist/cjs/src/data/models/team.d.ts +3 -1
  8. package/dist/cjs/src/data/transformers/team.js +4 -1
  9. package/dist/cjs/src/service/team/badge.d.ts +301 -0
  10. package/dist/cjs/src/service/team/badge.js +93 -0
  11. package/dist/cjs/src/service/team/index.d.ts +1 -0
  12. package/dist/cjs/src/service/team/index.js +1 -0
  13. package/dist/cjs/src/service/team/jersey-defaults.js +3 -1
  14. package/dist/cjs/src/service/team/jersey.d.ts +2 -0
  15. package/dist/cjs/src/service/team/jersey.js +14 -2
  16. package/dist/cjs/src/service/team/schemas/team.z.d.ts +98 -0
  17. package/dist/cjs/src/service/team/schemas/team.z.js +3 -1
  18. package/dist/cjs/src/service/team/team.d.ts +2 -0
  19. package/dist/cjs/src/service/team/team.js +2 -1
  20. package/dist/esm/src/api/index.d.ts +3 -1
  21. package/dist/esm/src/data/init-models.js +6 -1
  22. package/dist/esm/src/data/models/index.d.ts +1 -0
  23. package/dist/esm/src/data/models/index.js +1 -0
  24. package/dist/esm/src/data/models/team-badge.d.ts +20 -0
  25. package/dist/esm/src/data/models/team-badge.js +34 -0
  26. package/dist/esm/src/data/models/team.d.ts +3 -1
  27. package/dist/esm/src/data/transformers/team.js +4 -1
  28. package/dist/esm/src/service/team/badge.d.ts +301 -0
  29. package/dist/esm/src/service/team/badge.js +90 -0
  30. package/dist/esm/src/service/team/index.d.ts +1 -0
  31. package/dist/esm/src/service/team/index.js +1 -0
  32. package/dist/esm/src/service/team/jersey-defaults.js +3 -1
  33. package/dist/esm/src/service/team/jersey.d.ts +2 -0
  34. package/dist/esm/src/service/team/jersey.js +14 -2
  35. package/dist/esm/src/service/team/schemas/team.z.d.ts +98 -0
  36. package/dist/esm/src/service/team/schemas/team.z.js +3 -1
  37. package/dist/esm/src/service/team/team.d.ts +2 -0
  38. package/dist/esm/src/service/team/team.js +2 -1
  39. package/package.json +1 -1
@@ -0,0 +1,90 @@
1
+ import { z } from 'zod';
2
+ // 6-digit hex (#rrggbb) or 8-digit hex with alpha (#rrggbbaa), the same convention as the jersey colours (the
3
+ // 8-digit form comes from the UI's opacity-enabled colour pickers; SVG fills render both).
4
+ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/, 'INVALID_HEX_COLOR');
5
+ // The badge canvas is a fixed square coordinate space. Layer x/y are the layer's centre in canvas units and
6
+ // `size` is the layer's height in the same units; positions may overhang the canvas a little (clipped at render).
7
+ export const BADGE_CANVAS = 200;
8
+ // Curated FontAwesome icon names offered as badge layers (owner decision 2026-08-31: curated set, not a full
9
+ // catalog). The UI maps each name to its solid-icon path data (view/badge/badge-icons.ts, a Record keyed by this
10
+ // type so the two lists cannot drift); this list is what the API validates against.
11
+ export const BADGE_ICONS = [
12
+ 'star', 'crown', 'bolt', 'fire', 'heart', 'gem', 'skull', 'ghost', 'anchor', 'compass',
13
+ 'sun', 'moon', 'snowflake', 'leaf', 'seedling', 'tree', 'mountain', 'water', 'wind', 'meteor',
14
+ 'rocket', 'bell', 'flag', 'feather', 'shield-halved', 'chess-knight', 'chess-rook', 'hammer', 'crosshairs',
15
+ 'medal', 'trophy', 'award', 'volleyball', 'dragon', 'cat', 'crow', 'turtle', 'unicorn', 'horse', 'dove',
16
+ 'fish', 'paw', 'spider', 'otter', 'frog', 'dog'
17
+ ];
18
+ // Text layers offer the same 20 faces as the jersey number editor (JERSEY_FONT_ALL): five generic stacks, then
19
+ // the fifteen display families. The jersey renders its number from GENERATED DIGIT GLYPHS because it lives in a
20
+ // data-uri <img> where @font-face does not load; the badge canvas is inline SVG in the document, so it uses the
21
+ // real webfonts instead, which also means badge text is not limited to the digits those glyph sets cover.
22
+ export const BADGE_TEXT_FONTS = [
23
+ 'sans', 'wide', 'mono', 'serif', 'block',
24
+ 'permanentMarker', 'kaushanScript', 'russoOne', 'teko', 'saira', 'michroma', 'zillaSlab', 'changaOne',
25
+ 'stardosStencil', 'wallpoet', 'orbitron', 'tourney', 'bebasNeue', 'anton', 'jersey25'
26
+ ];
27
+ // Basic geometric shapes (VolleyballSimUI/public/shapes), extracted from the two source sets 2026-08-31. The
28
+ // crest sets are positional (shield-a01, ribbon-07), but these are named, so the vocabulary is spelled out
29
+ // rather than pattern-matched: this list IS the allow list, and every entry has a file of the same name.
30
+ // `-outline` is the hollow cut, whose interior is a real hole in the path (so layers underneath show through,
31
+ // no transparent fill slot needed). Order is the picker order, grouped by family.
32
+ // The rounded-corner cut of every shape, plus the split square and the pie, were dropped by the owner
33
+ // 2026-08-31; only the sharp cuts ship.
34
+ export const BADGE_SHAPES = [
35
+ 'star5-solid', 'star5-slim-solid', 'star6-solid', 'cross-solid',
36
+ 'star5-outline', 'star5-slim-outline', 'star6-outline', 'cross-outline',
37
+ 'pentagon-solid', 'hexagon-solid', 'octagon-solid',
38
+ 'pentagon-outline', 'hexagon-outline', 'octagon-outline',
39
+ 'square-solid', 'triangle-solid', 'diamond-solid',
40
+ 'square-outline', 'triangle-outline', 'diamond-outline',
41
+ 'rectangle-solid', 'burst-solid', 'trapezoid-solid',
42
+ 'rectangle-outline', 'burst-outline', 'trapezoid-outline',
43
+ 'circle-solid', 'heptagon-solid', 'nonagon-solid', 'ellipse-solid', 'parallelogram-solid',
44
+ 'heart-solid', 'moon-solid', 'semicircle-solid', 'triangle-right-solid', 'triangle-tall-solid',
45
+ 'trapezoid-tall-solid', 'kite-solid'
46
+ ];
47
+ // The extracted vector sets in VolleyballSimUI/public/shields + public/ribbons + public/shapes:
48
+ // shield-a01..a17 / shield-b01..b16, each -solid|-bordered|-outline; ribbon-01..ribbon-49; and shape-<name>
49
+ // for every name in BADGE_SHAPES.
50
+ const CREST_ID = /^(shield-(a(0[1-9]|1[0-7])|b(0[1-9]|1[0-6]))-(solid|bordered|outline)|ribbon-(0[1-9]|[1-4][0-9]))$/;
51
+ const BASIC_ID = new RegExp(`^shape-(${BADGE_SHAPES.join('|')})$`);
52
+ const shapeId = z.string().refine(v => CREST_ID.test(v) || BASIC_ID.test(v), 'INVALID_BADGE_SHAPE');
53
+ const layerBase = {
54
+ x: z.number().min(-100).max(300),
55
+ y: z.number().min(-100).max(300),
56
+ size: z.number().min(4).max(400),
57
+ rotation: z.number().min(-180).max(180)
58
+ };
59
+ // One colour per DISTINCT fill in the source art (solid shields 1, bordered 3, ribbons up to 4, basic shapes 1),
60
+ // applied in the order the fills first appear in the file. Extra entries are ignored at render; missing ones
61
+ // fall back to the source colour.
62
+ const shapeLayer = z.object({
63
+ type: z.literal('shape'),
64
+ shape: shapeId,
65
+ colors: z.array(hexColor).min(1).max(6),
66
+ ...layerBase
67
+ });
68
+ const iconLayer = z.object({
69
+ type: z.literal('icon'),
70
+ icon: z.enum(BADGE_ICONS),
71
+ color: hexColor,
72
+ ...layerBase
73
+ });
74
+ const textLayer = z.object({
75
+ type: z.literal('text'),
76
+ text: z.string().min(1).max(24),
77
+ font: z.enum(BADGE_TEXT_FONTS),
78
+ color: hexColor,
79
+ ...layerBase
80
+ });
81
+ export const BadgeLayerSchema = z.discriminatedUnion('type', [shapeLayer, iconLayer, textLayer]);
82
+ // The most layers one badge may stack. Not a technical ceiling (25 layers is under 3 KB of jsonb and a trivial
83
+ // number of SVG paths): past roughly a dozen you cannot tell the elements apart at the size a crest is shown,
84
+ // and the one-step-per-click reordering gets tedious. Exported so the editor gates on the SAME number the save
85
+ // validates against, rather than keeping its own copy that can drift.
86
+ export const BADGE_MAX_LAYERS = 25;
87
+ // The full team badge: an ordered stack of layers (index 0 renders first, i.e. at the back).
88
+ export const BadgeSchema = z.object({
89
+ layers: z.array(BadgeLayerSchema).min(1).max(BADGE_MAX_LAYERS)
90
+ });
@@ -1,6 +1,7 @@
1
1
  export * from './team';
2
2
  export * from './jersey';
3
3
  export * from './jersey-defaults';
4
+ export * from './badge';
4
5
  export * from './team-generator';
5
6
  export * from './formation';
6
7
  export * from './tactics';
@@ -1,6 +1,7 @@
1
1
  export * from './team';
2
2
  export * from './jersey';
3
3
  export * from './jersey-defaults';
4
+ export * from './badge';
4
5
  export * from './team-generator';
5
6
  export * from './formation';
6
7
  export * from './tactics';
@@ -70,6 +70,8 @@ export function jerseyFromTeamId(teamId) {
70
70
  numberSize: 46,
71
71
  brand: 'none',
72
72
  brandColor1: '#ffffff',
73
- brandColor2: '#1e293b'
73
+ brandColor2: '#1e293b',
74
+ brandSize: 29,
75
+ badgeSize: 48
74
76
  };
75
77
  }
@@ -67,6 +67,8 @@ export declare const JerseySchema: z.ZodObject<{
67
67
  }>>;
68
68
  brandColor1: z.ZodDefault<z.ZodString>;
69
69
  brandColor2: z.ZodDefault<z.ZodString>;
70
+ brandSize: z.ZodDefault<z.ZodNumber>;
71
+ badgeSize: z.ZodDefault<z.ZodNumber>;
70
72
  }, z.core.$strip>;
71
73
  export type JerseyConfig = z.infer<typeof JerseySchema>;
72
74
  export type JerseyClothing = JerseyConfig['clothing'];
@@ -26,10 +26,18 @@ export const JerseySchema = z.object({
26
26
  // configs saved before it stay valid.
27
27
  brand: z.enum(['none', 'sigma', 'person-soccer', 'feather-pointed', 'paw-claws', 'narwhal', 'horseshoe', 'spider-black-widow', 'wreath-laurel', 'omega', 'trillium']).default('none'),
28
28
  brandColor1: hexColor.default('#ffffff'),
29
- brandColor2: hexColor.default('#1e293b')
29
+ brandColor2: hexColor.default('#1e293b'),
30
+ // Chest-element heights in avatar-svg units, on the y=297 chest line. The brand sits at x=90 and the team badge
31
+ // at x=191, with the (non-editable) champion badge between them at x=140.5 spanning 116.5..164.5, so either one
32
+ // starts touching it past about 52. The range allows more than that on purpose: most teams have no champion
33
+ // badge, and the editor preview shows the overlap for the ones that do. Defaulted to the sizes both were fixed
34
+ // at before, so configs saved earlier stay valid and look unchanged.
35
+ brandSize: z.number().min(16).max(64).default(29),
36
+ badgeSize: z.number().min(16).max(64).default(48)
30
37
  });
31
38
  // The alternate (libero) kit stores only the fields the premium editor allows changing. Locked fields (clothing,
32
- // font, brand icon, numberSpacing, numberSize) are always inherited from the main kit at render time.
39
+ // font, brand icon, numberSpacing, numberSize, brandSize, badgeSize) are always inherited from the main kit at
40
+ // render time.
33
41
  export const AlternateJerseySchema = z.object({
34
42
  pattern: JerseySchema.shape.pattern,
35
43
  colors: JerseySchema.shape.colors,
@@ -120,6 +128,8 @@ export function computeComplementaryJersey(main) {
120
128
  brand: main.brand,
121
129
  numberSpacing: main.numberSpacing,
122
130
  numberSize: main.numberSize,
131
+ brandSize: main.brandSize,
132
+ badgeSize: main.badgeSize,
123
133
  // Editable fields: complementary-derived
124
134
  pattern: main.pattern,
125
135
  colors: compColors,
@@ -139,6 +149,8 @@ export function mergeAlternateJersey(main, alt) {
139
149
  brand: main.brand,
140
150
  numberSpacing: main.numberSpacing,
141
151
  numberSize: main.numberSize,
152
+ brandSize: main.brandSize,
153
+ badgeSize: main.badgeSize,
142
154
  pattern: alt.pattern,
143
155
  colors: alt.colors,
144
156
  numberColor: alt.numberColor,
@@ -413,6 +413,8 @@ export declare const TeamInputSchema: z.ZodObject<{
413
413
  }>>;
414
414
  brandColor1: z.ZodDefault<z.ZodString>;
415
415
  brandColor2: z.ZodDefault<z.ZodString>;
416
+ brandSize: z.ZodDefault<z.ZodNumber>;
417
+ badgeSize: z.ZodDefault<z.ZodNumber>;
416
418
  }, z.core.$strip>>;
417
419
  alternateJerseyConfig: z.ZodOptional<z.ZodObject<{
418
420
  pattern: z.ZodEnum<{
@@ -438,5 +440,101 @@ export declare const TeamInputSchema: z.ZodObject<{
438
440
  brandColor1: z.ZodDefault<z.ZodString>;
439
441
  brandColor2: z.ZodDefault<z.ZodString>;
440
442
  }, z.core.$strip>>;
443
+ badgeConfig: z.ZodOptional<z.ZodObject<{
444
+ layers: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
445
+ x: z.ZodNumber;
446
+ y: z.ZodNumber;
447
+ size: z.ZodNumber;
448
+ rotation: z.ZodNumber;
449
+ type: z.ZodLiteral<"shape">;
450
+ shape: z.ZodString;
451
+ colors: z.ZodArray<z.ZodString>;
452
+ }, z.core.$strip>, z.ZodObject<{
453
+ x: z.ZodNumber;
454
+ y: z.ZodNumber;
455
+ size: z.ZodNumber;
456
+ rotation: z.ZodNumber;
457
+ type: z.ZodLiteral<"icon">;
458
+ icon: z.ZodEnum<{
459
+ star: "star";
460
+ crown: "crown";
461
+ bolt: "bolt";
462
+ fire: "fire";
463
+ heart: "heart";
464
+ gem: "gem";
465
+ skull: "skull";
466
+ ghost: "ghost";
467
+ anchor: "anchor";
468
+ compass: "compass";
469
+ sun: "sun";
470
+ moon: "moon";
471
+ snowflake: "snowflake";
472
+ leaf: "leaf";
473
+ seedling: "seedling";
474
+ tree: "tree";
475
+ mountain: "mountain";
476
+ water: "water";
477
+ wind: "wind";
478
+ meteor: "meteor";
479
+ rocket: "rocket";
480
+ bell: "bell";
481
+ flag: "flag";
482
+ feather: "feather";
483
+ "shield-halved": "shield-halved";
484
+ "chess-knight": "chess-knight";
485
+ "chess-rook": "chess-rook";
486
+ hammer: "hammer";
487
+ crosshairs: "crosshairs";
488
+ medal: "medal";
489
+ trophy: "trophy";
490
+ award: "award";
491
+ volleyball: "volleyball";
492
+ dragon: "dragon";
493
+ cat: "cat";
494
+ crow: "crow";
495
+ turtle: "turtle";
496
+ unicorn: "unicorn";
497
+ horse: "horse";
498
+ dove: "dove";
499
+ fish: "fish";
500
+ paw: "paw";
501
+ spider: "spider";
502
+ otter: "otter";
503
+ frog: "frog";
504
+ dog: "dog";
505
+ }>;
506
+ color: z.ZodString;
507
+ }, z.core.$strip>, z.ZodObject<{
508
+ x: z.ZodNumber;
509
+ y: z.ZodNumber;
510
+ size: z.ZodNumber;
511
+ rotation: z.ZodNumber;
512
+ type: z.ZodLiteral<"text">;
513
+ text: z.ZodString;
514
+ font: z.ZodEnum<{
515
+ block: "block";
516
+ sans: "sans";
517
+ wide: "wide";
518
+ mono: "mono";
519
+ serif: "serif";
520
+ permanentMarker: "permanentMarker";
521
+ kaushanScript: "kaushanScript";
522
+ russoOne: "russoOne";
523
+ teko: "teko";
524
+ saira: "saira";
525
+ michroma: "michroma";
526
+ zillaSlab: "zillaSlab";
527
+ changaOne: "changaOne";
528
+ stardosStencil: "stardosStencil";
529
+ wallpoet: "wallpoet";
530
+ orbitron: "orbitron";
531
+ tourney: "tourney";
532
+ bebasNeue: "bebasNeue";
533
+ anton: "anton";
534
+ jersey25: "jersey25";
535
+ }>;
536
+ color: z.ZodString;
537
+ }, z.core.$strip>], "type">>;
538
+ }, z.core.$strip>>;
441
539
  }, z.core.$strip>;
442
540
  export type TeamInput = z.infer<typeof TeamInputSchema>;
@@ -3,6 +3,7 @@ import { Player } from '../../player';
3
3
  import { Country } from '../../country';
4
4
  import { TacticsInputSchema } from './tactics.z';
5
5
  import { JerseySchema, AlternateJerseySchema } from '../jersey';
6
+ import { BadgeSchema } from '../badge';
6
7
  const playerInstanceSchema = z.custom((v) => v instanceof Player, {
7
8
  message: 'INVALID_PLAYER_INSTANCE'
8
9
  });
@@ -20,7 +21,8 @@ export const TeamInputSchema = z.object({
20
21
  legacyFlagCountry: countrySchema.optional(),
21
22
  tactics: TacticsInputSchema.optional(),
22
23
  jerseyConfig: JerseySchema.optional(),
23
- alternateJerseyConfig: AlternateJerseySchema.optional()
24
+ alternateJerseyConfig: AlternateJerseySchema.optional(),
25
+ badgeConfig: BadgeSchema.optional()
24
26
  }).superRefine((data, ctx) => {
25
27
  const ids = data.roster.map(p => p.id);
26
28
  if (new Set(ids).size !== ids.length) {
@@ -2,6 +2,7 @@ import { Player } from '../player';
2
2
  import { Country } from '../country';
3
3
  import { Tactics } from './tactics';
4
4
  import type { JerseyConfig, AlternateJerseyConfig } from './jersey';
5
+ import type { BadgeConfig } from './badge';
5
6
  export declare class Team {
6
7
  readonly id: string;
7
8
  readonly roster: Player[];
@@ -13,6 +14,7 @@ export declare class Team {
13
14
  readonly legacyFlagCountry?: Country;
14
15
  readonly jerseyConfig?: JerseyConfig;
15
16
  readonly alternateJerseyConfig?: AlternateJerseyConfig;
17
+ readonly badgeConfig?: BadgeConfig;
16
18
  private _rating;
17
19
  static create(input: unknown): Team;
18
20
  private constructor();
@@ -16,7 +16,7 @@ export class Team {
16
16
  tactics: result.data.tactics != null ? Tactics.create(result.data.tactics) : undefined
17
17
  });
18
18
  }
19
- constructor({ id, rating, name, shortName, divisionId, country, legacyFlagCountry, roster, tactics, jerseyConfig, alternateJerseyConfig }) {
19
+ constructor({ id, rating, name, shortName, divisionId, country, legacyFlagCountry, roster, tactics, jerseyConfig, alternateJerseyConfig, badgeConfig }) {
20
20
  this.id = id;
21
21
  this._rating = rating;
22
22
  this.roster = roster;
@@ -28,6 +28,7 @@ export class Team {
28
28
  this.legacyFlagCountry = legacyFlagCountry;
29
29
  this.jerseyConfig = jerseyConfig;
30
30
  this.alternateJerseyConfig = alternateJerseyConfig;
31
+ this.badgeConfig = badgeConfig;
31
32
  }
32
33
  get rating() {
33
34
  return this._rating;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volleyballsimtypes",
3
- "version": "0.0.527",
3
+ "version": "0.0.528",
4
4
  "description": "vbsim types",
5
5
  "main": "./dist/cjs/src/index.js",
6
6
  "module": "./dist/esm/src/index.js",