@crockery/fellowship-components 0.1.0 → 0.3.0

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 (54) hide show
  1. package/README.md +7 -2
  2. package/RELEASING.md +9 -45
  3. package/dist/chunks/data-source-C-Gs7kRl.js +2 -0
  4. package/dist/chunks/decorate-B_ZTN9li.js.map +1 -1
  5. package/dist/chunks/dungeon-cast-browser-Cyh519NI.js +705 -0
  6. package/dist/chunks/dungeon-cast-browser-Cyh519NI.js.map +1 -0
  7. package/dist/chunks/{dungeon-map-IPN6sEDj.js → dungeon-map-xFaPasVr.js} +113 -138
  8. package/dist/chunks/dungeon-map-xFaPasVr.js.map +1 -0
  9. package/dist/chunks/{talent-calculator-Ch1Pbbba.js → talent-calculator-CIPNbqK5.js} +84 -114
  10. package/dist/chunks/talent-calculator-CIPNbqK5.js.map +1 -0
  11. package/dist/data/cast-data-source.d.ts +5 -0
  12. package/dist/data/cast-data-source.d.ts.map +1 -0
  13. package/dist/data/data-source.d.ts +5 -16
  14. package/dist/data/data-source.d.ts.map +1 -1
  15. package/dist/data/package-cast-data-source.d.ts +8 -0
  16. package/dist/data/package-cast-data-source.d.ts.map +1 -0
  17. package/dist/data/package-data-source.d.ts +3 -1
  18. package/dist/data/package-data-source.d.ts.map +1 -1
  19. package/dist/data/package-talent-data-source.d.ts +2 -0
  20. package/dist/data/package-talent-data-source.d.ts.map +1 -1
  21. package/dist/data/talent-data-source.d.ts +4 -10
  22. package/dist/data/talent-data-source.d.ts.map +1 -1
  23. package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts +36 -0
  24. package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts.map +1 -0
  25. package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts +2 -0
  26. package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts.map +1 -0
  27. package/dist/dungeon-cast-browser/index.d.ts +4 -0
  28. package/dist/dungeon-cast-browser/index.d.ts.map +1 -0
  29. package/dist/dungeon-cast-browser/render-model.d.ts +5 -0
  30. package/dist/dungeon-cast-browser/render-model.d.ts.map +1 -0
  31. package/dist/dungeon-cast-browser/types.d.ts +60 -0
  32. package/dist/dungeon-cast-browser/types.d.ts.map +1 -0
  33. package/dist/dungeon-cast-browser.js +7 -0
  34. package/dist/dungeon-cast-browser.js.map +1 -0
  35. package/dist/dungeon-map.js +1 -1
  36. package/dist/index.d.ts +6 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +8 -5
  39. package/dist/react/dungeon-cast-browser.d.ts +12 -0
  40. package/dist/react/dungeon-cast-browser.d.ts.map +1 -0
  41. package/dist/react/dungeon-cast-browser.js +19 -0
  42. package/dist/react/dungeon-cast-browser.js.map +1 -0
  43. package/dist/react/dungeon-map.js +1 -1
  44. package/dist/react/index.d.ts +2 -0
  45. package/dist/react/index.d.ts.map +1 -1
  46. package/dist/react/talent-calculator.js +1 -1
  47. package/dist/react.js +4 -3
  48. package/dist/talent-calculator.js +1 -1
  49. package/package.json +35 -25
  50. package/src/dungeon-cast-browser/README.md +22 -0
  51. package/dist/chunks/data-source-CoIFvV72.js +0 -10
  52. package/dist/chunks/data-source-CoIFvV72.js.map +0 -1
  53. package/dist/chunks/dungeon-map-IPN6sEDj.js.map +0 -1
  54. package/dist/chunks/talent-calculator-Ch1Pbbba.js.map +0 -1
@@ -1,115 +1,85 @@
1
- import { t as e } from "./data-source-CoIFvV72.js";
1
+ import { t as e } from "./data-source-C-Gs7kRl.js";
2
2
  import { n as t, r as n, t as r } from "./decorate-B_ZTN9li.js";
3
3
  import { a as i, n as a } from "./tooltip-DoQVSnOn.js";
4
- import { findImage as o, getAssetUrl as s } from "@crockery/fellowship-data";
5
- import { LitElement as c, css as l, html as u, nothing as d } from "lit";
6
- import { customElement as f, property as p, state as m } from "lit/decorators.js";
7
- import { repeat as h } from "lit/directives/repeat.js";
4
+ import { createPackageFellowshipDataClient as o } from "@crockery/fellowship-data/client";
5
+ import { LitElement as s, css as c, html as l, nothing as u } from "lit";
6
+ import { customElement as d, property as f, state as p } from "lit/decorators.js";
7
+ import { repeat as m } from "lit/directives/repeat.js";
8
+ import { findImage as h } from "@crockery/fellowship-data";
8
9
  //#region src/data/package-talent-data-source.ts
9
- var g, _ = (t) => {
10
- if (t?.aborted) throw new e("aborted", "The hero talent data request was aborted.", t.reason);
11
- }, v = () => (g ??= Promise.all([
12
- import("@crockery/fellowship-data/heroes"),
13
- import("@crockery/fellowship-data/talents"),
14
- import("@crockery/fellowship-data/manifest")
15
- ]).then(([t, n, r]) => {
16
- let i = r.default;
17
- if (i.schemaVersion !== 1) throw new e("unsupported-schema", `Unsupported fellowship-data schema version: ${String(i.schemaVersion)}.`);
18
- let a = /* @__PURE__ */ new Map();
19
- for (let e of n.default) {
20
- let t = a.get(e.parentId) ?? [];
21
- t.push(e), a.set(e.parentId, t);
22
- }
10
+ var g = (e = {}) => {
11
+ let t = o(e);
23
12
  return {
24
- manifest: i,
25
- heroes: new Map(t.default.map((e) => [e.id, e])),
26
- talentsByHero: new Map([...a].map(([e, t]) => [e, t]))
13
+ loadHeroTalents: (e, n) => t.loadHeroTalents(e, n),
14
+ resolveAssetUrl: (e) => t.resolveAssetUrl(e)
27
15
  };
28
- }).catch((t) => {
29
- throw g = void 0, t instanceof e ? t : new e("data-load-failed", "Unable to load hero talents from @crockery/fellowship-data.", t);
30
- }), g), y = (t = {}) => ({
31
- async loadHeroTalents(t, n) {
32
- _(n);
33
- let r = await v();
34
- _(n);
35
- let i = r.heroes.get(t);
36
- if (!i) throw new e("hero-not-found", `No hero with ID “${t}” exists in @crockery/fellowship-data.`);
37
- return {
38
- manifest: r.manifest,
39
- hero: i,
40
- talents: r.talentsByHero.get(t) ?? []
41
- };
42
- },
43
- resolveAssetUrl(e) {
44
- return t.assetBaseUrl ? s(e, { baseUrl: t.assetBaseUrl }) : s(e);
45
- }
46
- }), b = [
16
+ }, _ = [
47
17
  "I",
48
18
  "II",
49
19
  "III",
50
20
  "IV",
51
21
  "V",
52
22
  "VI"
53
- ], x = (e) => b[e - 1] ?? String(e), S = (e, t, n) => ({
23
+ ], v = (e) => _[e - 1] ?? String(e), y = (e, t, n) => ({
54
24
  code: e,
55
25
  message: n,
56
26
  talentId: t.id
57
- }), C = (e, t) => {
27
+ }), b = (e, t) => {
58
28
  let n = [], r = /* @__PURE__ */ new Set(), i = [];
59
29
  for (let a of e.talents) {
60
30
  if (a.row === null && a.column === null) continue;
61
31
  if (!Number.isInteger(a.row) || !Number.isInteger(a.column) || a.row === null || a.column === null || a.row < 1 || a.row > 6 || a.column < 1 || a.column > 3) {
62
- n.push(S("invalid-position", a, `Talent “${a.id}” has an invalid row or column and was skipped.`));
32
+ n.push(y("invalid-position", a, `Talent “${a.id}” has an invalid row or column and was skipped.`));
63
33
  continue;
64
34
  }
65
35
  let e = `${a.row}:${a.column}`;
66
36
  if (r.has(e)) {
67
- n.push(S("duplicate-position", a, `Talent “${a.id}” duplicates row ${a.row}, column ${a.column} and was skipped.`));
37
+ n.push(y("duplicate-position", a, `Talent “${a.id}” duplicates row ${a.row}, column ${a.column} and was skipped.`));
68
38
  continue;
69
39
  }
70
40
  r.add(e);
71
- let s = Number.isInteger(a.pointCost) && (a.pointCost ?? 0) > 0 ? a.pointCost : null;
72
- s === null && n.push(S("invalid-cost", a, `Talent “${a.id}” has an invalid point cost.`)), a.name || n.push(S("missing-name", a, `Talent “${a.id}” has no display name.`)), a.description || n.push(S("missing-description", a, `Talent “${a.id}” has no description.`));
73
- let c = o(a.images, {
41
+ let o = Number.isInteger(a.pointCost) && (a.pointCost ?? 0) > 0 ? a.pointCost : null;
42
+ o === null && n.push(y("invalid-cost", a, `Talent “${a.id}” has an invalid point cost.`)), a.name || n.push(y("missing-name", a, `Talent “${a.id}” has no display name.`)), a.description || n.push(y("missing-description", a, `Talent “${a.id}” has no description.`));
43
+ let s = h(a.images, {
74
44
  type: "talent",
75
45
  size: "200"
76
- }) ?? o(a.images, {
46
+ }) ?? h(a.images, {
77
47
  type: "talent",
78
48
  size: "full"
79
- }), l = null;
80
- if (!c) n.push(S("missing-image", a, `Talent “${a.id}” has no icon.`));
49
+ }), c = null;
50
+ if (!s) n.push(y("missing-image", a, `Talent “${a.id}” has no icon.`));
81
51
  else try {
82
- l = t.resolveAssetUrl(c);
52
+ c = t.resolveAssetUrl(s);
83
53
  } catch (e) {
84
- n.push(S("asset-url-error", a, `The icon URL for talent “${a.id}” could not be resolved: ${String(e)}.`));
54
+ n.push(y("asset-url-error", a, `The icon URL for talent “${a.id}” could not be resolved: ${String(e)}.`));
85
55
  }
86
56
  i.push({
87
57
  talent: a,
88
58
  row: a.row,
89
59
  column: a.column,
90
- pointCost: s,
91
- imageUrl: l
60
+ pointCost: o,
61
+ imageUrl: c
92
62
  });
93
63
  }
94
64
  i.sort((e, t) => e.row - t.row || e.column - t.column);
95
- let a = new Map(i.map((e) => [e.talent.id, e])), s = [...new Set(i.map(({ row: e }) => e))].map((e) => ({
65
+ let a = new Map(i.map((e) => [e.talent.id, e])), o = [...new Set(i.map(({ row: e }) => e))].map((e) => ({
96
66
  row: e,
97
- label: x(e),
67
+ label: v(e),
98
68
  talents: i.filter((t) => t.row === e)
99
69
  }));
100
70
  return {
101
71
  hero: e.hero,
102
- rows: s,
72
+ rows: o,
103
73
  talentById: a,
104
74
  warnings: n
105
75
  };
106
- }, w = 14, T = (e) => Math.min(14, Math.max(0, Math.round(Number.isFinite(e) ? e : 14))), E = (e, t) => {
76
+ }, x = 14, S = (e) => Math.min(14, Math.max(0, Math.round(Number.isFinite(e) ? e : 14))), C = (e, t) => {
107
77
  let n = new Set(t), r = [...e.talentById.values()].filter(({ talent: e }) => n.has(e.id)).map(({ talent: e }) => e.id), i = new Set(r);
108
78
  return {
109
79
  selectedTalentIds: r,
110
80
  removedTalentIds: [...new Set(t)].filter((e) => !i.has(e))
111
81
  };
112
- }, D = (e, t, n) => {
82
+ }, w = (e, t, n) => {
113
83
  let r = new Set(t), i = [...e.talentById.values()].reduce((e, t) => r.has(t.talent.id) && t.pointCost !== null ? e + t.pointCost : e, 0), a = n - i;
114
84
  return {
115
85
  selectedTalentIds: [...e.talentById.values()].filter(({ talent: e }) => r.has(e.id)).map(({ talent: e }) => e.id),
@@ -117,7 +87,7 @@ var g, _ = (t) => {
117
87
  remainingPoints: a,
118
88
  overBudget: a < 0
119
89
  };
120
- }, O = (e, t) => e !== null && !t.overBudget && e <= t.remainingPoints, k = l`
90
+ }, T = (e, t) => e !== null && !t.overBudget && e <= t.remainingPoints, E = c`
121
91
  :host {
122
92
  --fellowship-talent-background: var(--_fellowship-field-canvas);
123
93
  --fellowship-talent-surface: var(--_fellowship-field-surface);
@@ -665,20 +635,20 @@ var g, _ = (t) => {
665
635
  transform: none;
666
636
  }
667
637
  }
668
- `, A = {
638
+ `, D = {
669
639
  bubbles: !0,
670
640
  composed: !0
671
- }, j = (e, t) => e.length !== t?.length || e.some((e, n) => e !== t[n]), M = (e) => `talent:${e}`, N = (e) => ({
641
+ }, O = (e, t) => e.length !== t?.length || e.some((e, n) => e !== t[n]), k = (e) => `talent:${e}`, A = (e) => ({
672
642
  selectedTalentIds: [],
673
643
  spentPoints: 0,
674
644
  remainingPoints: e,
675
645
  overBudget: !1
676
- }), P = class extends c {
646
+ }), j = class extends s {
677
647
  constructor(...e) {
678
- super(...e), this.heroId = "", this.talentPoints = 14, this.selectedTalentIds = [], this.assetBaseUrl = null, this.dataSource = null, this.renderTalent = null, this.showPointControls = !0, this.showTooltips = !0, this._status = "empty", this._error = null, this._model = null, this._failedImages = /* @__PURE__ */ new Set(), this._announcement = "", this._renderSelectionState = N(14), this._talentPresentation = /* @__PURE__ */ new Map(), this._talentRenderContent = /* @__PURE__ */ new Map(), this.#e = null, this.#t = 0, this.#n = null, this.#r = !1, this.#i = /* @__PURE__ */ new Set(), this.#a = [];
648
+ super(...e), this.heroId = "", this.talentPoints = 14, this.selectedTalentIds = [], this.assetBaseUrl = null, this.dataSource = null, this.renderTalent = null, this.showPointControls = !0, this.showTooltips = !0, this._status = "empty", this._error = null, this._model = null, this._failedImages = /* @__PURE__ */ new Set(), this._announcement = "", this._renderSelectionState = A(14), this._talentPresentation = /* @__PURE__ */ new Map(), this._talentRenderContent = /* @__PURE__ */ new Map(), this.#e = null, this.#t = 0, this.#n = null, this.#r = !1, this.#i = /* @__PURE__ */ new Set(), this.#a = [];
679
649
  }
680
650
  static {
681
- this.styles = [n, k];
651
+ this.styles = [n, E];
682
652
  }
683
653
  #e;
684
654
  #t;
@@ -700,7 +670,7 @@ var g, _ = (t) => {
700
670
  return;
701
671
  }
702
672
  if (e.has("talentPoints")) {
703
- let t = T(this.talentPoints);
673
+ let t = S(this.talentPoints);
704
674
  if (t !== this.talentPoints) {
705
675
  this.#r = !0, this.talentPoints = t, this.#v("normalization");
706
676
  return;
@@ -712,20 +682,20 @@ var g, _ = (t) => {
712
682
  willUpdate(e) {
713
683
  super.willUpdate(e);
714
684
  let t = e.has("_model") || e.has("selectedTalentIds") || e.has("talentPoints");
715
- t && (this._renderSelectionState = this._model ? D(this._model, this.selectedTalentIds, this.talentPoints) : N(this.talentPoints)), e.has("renderTalent") && this.#i.clear(), (t || e.has("renderTalent")) && this.#l(this._renderSelectionState);
685
+ t && (this._renderSelectionState = this._model ? w(this._model, this.selectedTalentIds, this.talentPoints) : A(this.talentPoints)), e.has("renderTalent") && this.#i.clear(), (t || e.has("renderTalent")) && this.#l(this._renderSelectionState);
716
686
  }
717
687
  get renderModel() {
718
688
  return this._model;
719
689
  }
720
690
  get selectionState() {
721
- return this._model ? D(this._model, this.selectedTalentIds, this.talentPoints) : N(this.talentPoints);
691
+ return this._model ? w(this._model, this.selectedTalentIds, this.talentPoints) : A(this.talentPoints);
722
692
  }
723
693
  reset() {
724
694
  this.selectedTalentIds.length !== 0 && (this.selectedTalentIds = [], this.#_("reset"));
725
695
  }
726
696
  selectTalent(e) {
727
697
  let t = this._model?.talentById.get(e), n = this.selectionState;
728
- return !t || n.selectedTalentIds.includes(e) || !O(t.pointCost, n) ? !1 : (this.selectedTalentIds = [...n.selectedTalentIds, e], this.#_("api"), !0);
698
+ return !t || n.selectedTalentIds.includes(e) || !T(t.pointCost, n) ? !1 : (this.selectedTalentIds = [...n.selectedTalentIds, e], this.#_("api"), !0);
729
699
  }
730
700
  deselectTalent(e) {
731
701
  return this.selectionState.selectedTalentIds.includes(e) ? (this.selectedTalentIds = this.selectionState.selectedTalentIds.filter((t) => t !== e), this.#_("api"), !0) : !1;
@@ -735,7 +705,7 @@ var g, _ = (t) => {
735
705
  }
736
706
  render() {
737
707
  let e = this._renderSelectionState;
738
- return u`
708
+ return l`
739
709
  <section
740
710
  class="container"
741
711
  part="container"
@@ -749,7 +719,7 @@ var g, _ = (t) => {
749
719
  `;
750
720
  }
751
721
  #o(e) {
752
- return u`
722
+ return l`
753
723
  <header class="header" part="header">
754
724
  <h2 class="heading" part="heading">Talent points</h2>
755
725
  <div class="points-cluster">
@@ -764,7 +734,7 @@ var g, _ = (t) => {
764
734
  <span class="points-separator" aria-hidden="true">/</span>
765
735
  <span class="points-total" part="points-total">${this.talentPoints}</span>
766
736
  </div>
767
- ${this.showPointControls ? u`
737
+ ${this.showPointControls ? l`
768
738
  <div
769
739
  class="point-controls"
770
740
  part="point-controls"
@@ -794,7 +764,7 @@ var g, _ = (t) => {
794
764
  +
795
765
  </button>
796
766
  </div>
797
- ` : d}
767
+ ` : u}
798
768
  <button
799
769
  class="clear-all"
800
770
  part="clear-all"
@@ -809,7 +779,7 @@ var g, _ = (t) => {
809
779
  `;
810
780
  }
811
781
  #s(e) {
812
- return e.rows.length === 0 ? u`
782
+ return e.rows.length === 0 ? l`
813
783
  <div class="status" part="empty-state" role="status">
814
784
  <div class="status-card">
815
785
  <div class="status-mark" aria-hidden="true">◇</div>
@@ -819,14 +789,14 @@ var g, _ = (t) => {
819
789
  </p>
820
790
  </div>
821
791
  </div>
822
- ` : u`
792
+ ` : l`
823
793
  <div class="grid" part="grid" role="group" aria-label="Talent tiers">
824
- ${h(e.rows, (e) => e.row, (e) => u`
794
+ ${m(e.rows, (e) => e.row, (e) => l`
825
795
  <fieldset class="row" part="row">
826
796
  <legend class="visually-hidden">Talent tier ${e.label}</legend>
827
797
  <span class="row-label" part="row-label" aria-hidden="true">${e.label}</span>
828
798
  <div class="row-talents">
829
- ${h(e.talents, (e) => e.talent.id, (e) => this.#c(e))}
799
+ ${m(e.talents, (e) => e.talent.id, (e) => this.#c(e))}
830
800
  </div>
831
801
  </fieldset>
832
802
  `)}
@@ -835,8 +805,8 @@ var g, _ = (t) => {
835
805
  }
836
806
  #c(e) {
837
807
  let t = this._talentPresentation.get(e.talent.id);
838
- if (!t) return d;
839
- let { context: n, name: r, descriptionId: i, affordability: a, description: o } = t, { selected: s, disabled: c } = n, l = u`
808
+ if (!t) return u;
809
+ let { context: n, name: r, descriptionId: i, affordability: a, description: o } = t, { selected: s, disabled: c } = n, d = l`
840
810
  <button
841
811
  class="talent"
842
812
  part="talent"
@@ -851,22 +821,22 @@ var g, _ = (t) => {
851
821
  <span class="talent-cost" part="talent-cost">${e.pointCost ?? "–"}</span>
852
822
  </button>
853
823
  `;
854
- return u`
855
- ${this.showTooltips && (e.talent.description || c) ? u`
824
+ return l`
825
+ ${this.showTooltips && (e.talent.description || c) ? l`
856
826
  <fellowship-tooltip
857
827
  .heading=${r}
858
828
  .text=${e.talent.description ?? a}
859
829
  >
860
- ${l}
830
+ ${d}
861
831
  </fellowship-tooltip>
862
- ` : l}
832
+ ` : d}
863
833
  <span class="visually-hidden" id=${i}>${o}</span>
864
834
  `;
865
835
  }
866
836
  #l(e) {
867
837
  let t = this.renderTalent, n = this._model, r = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map();
868
838
  if (n) for (let o of n.talentById.values()) {
869
- let s = e.selectedTalentIds.includes(o.talent.id), c = O(o.pointCost, e), l = !s && !c, u = o.talent.name ?? o.talent.id, d = o.pointCost === null ? "This talent has no valid point cost." : l ? `Requires ${o.pointCost} talent points; ${Math.max(0, e.remainingPoints)} remaining.` : `${o.pointCost} talent ${o.pointCost === 1 ? "point" : "points"}.`, f = {
839
+ let s = e.selectedTalentIds.includes(o.talent.id), c = T(o.pointCost, e), l = !s && !c, u = o.talent.name ?? o.talent.id, d = o.pointCost === null ? "This talent has no valid point cost." : l ? `Requires ${o.pointCost} talent points; ${Math.max(0, e.remainingPoints)} remaining.` : `${o.pointCost} talent ${o.pointCost === 1 ? "point" : "points"}.`, f = {
870
840
  talent: o,
871
841
  hero: n.hero,
872
842
  selected: s,
@@ -895,17 +865,17 @@ var g, _ = (t) => {
895
865
  this._talentPresentation = a, this._talentRenderContent = r;
896
866
  }
897
867
  #u(e) {
898
- let t = e.talent, n = t.talent.name ?? t.talent.id, r = this.renderTalent && this._talentRenderContent.has(t.talent.id) ? this._talentRenderContent.get(t.talent.id) : u`
899
- <slot name=${M(t.talent.id)}>
868
+ let t = e.talent, n = t.talent.name ?? t.talent.id, r = this.renderTalent && this._talentRenderContent.has(t.talent.id) ? this._talentRenderContent.get(t.talent.id) : l`
869
+ <slot name=${k(t.talent.id)}>
900
870
  <span class="talent-name" part="talent-name" title=${n}>${n}</span>
901
871
  </slot>
902
872
  `;
903
- return u`
873
+ return l`
904
874
  ${this.#d(t, n)} ${r}
905
875
  `;
906
876
  }
907
877
  #d(e, t) {
908
- return e.imageUrl && !this._failedImages.has(e.talent.id) ? u`
878
+ return e.imageUrl && !this._failedImages.has(e.talent.id) ? l`
909
879
  <img
910
880
  class="talent-icon"
911
881
  part="talent-icon"
@@ -914,11 +884,11 @@ var g, _ = (t) => {
914
884
  draggable="false"
915
885
  @error=${() => this.#b(e)}
916
886
  />
917
- ` : u`<span class="talent-icon-fallback" part="talent-icon-fallback">${t.split(/\s+/).map((e) => e[0]).join("").slice(0, 2).toUpperCase()}</span>`;
887
+ ` : l`<span class="talent-icon-fallback" part="talent-icon-fallback">${t.split(/\s+/).map((e) => e[0]).join("").slice(0, 2).toUpperCase()}</span>`;
918
888
  }
919
889
  #f() {
920
890
  let e = this._status === "loading", t = this._status === "empty", n = e ? "Reading talent codex" : t ? "Choose a hero" : "Talents unavailable", r = e ? "Loading the hero and their available talents." : t ? "Set the hero-id attribute or heroId property to build a talent loadout." : this._error?.message ?? "An unexpected talent data error occurred.";
921
- return u`
891
+ return l`
922
892
  <div
923
893
  class="status"
924
894
  part=${this._status === "error" ? "error-state" : "status"}
@@ -928,7 +898,7 @@ var g, _ = (t) => {
928
898
  <div class="status-mark" aria-hidden="true">${e ? "…" : "✦"}</div>
929
899
  <p class="status-title">${n}</p>
930
900
  <p class="status-copy">${r}</p>
931
- ${this._status === "error" ? u`<button class="retry-button" type="button" @click=${this.retry}>Retry</button>` : d}
901
+ ${this._status === "error" ? l`<button class="retry-button" type="button" @click=${this.retry}>Retry</button>` : u}
932
902
  </div>
933
903
  </div>
934
904
  `;
@@ -940,18 +910,18 @@ var g, _ = (t) => {
940
910
  return;
941
911
  }
942
912
  this._status = "loading";
943
- let r = this.dataSource ?? y(this.assetBaseUrl ? { assetBaseUrl: this.assetBaseUrl } : {});
913
+ let r = this.dataSource ?? g(this.assetBaseUrl ? { assetBaseUrl: this.assetBaseUrl } : {});
944
914
  try {
945
915
  let i = await r.loadHeroTalents(t, this.#e.signal);
946
916
  if (n !== this.#t || this.#e.signal.aborted) return;
947
917
  if (i.manifest.schemaVersion !== 1) throw new e("unsupported-schema", `Unsupported fellowship-data schema version: ${String(i.manifest.schemaVersion)}.`);
948
- let a = C(i, r);
918
+ let a = b(i, r);
949
919
  this._model = a, this._status = "ready";
950
920
  let o = this.#m(!1);
951
921
  await this.updateComplete;
952
922
  let s = this.selectionState;
953
923
  this.dispatchEvent(new CustomEvent("fellowship-talents-ready", {
954
- ...A,
924
+ ...D,
955
925
  detail: {
956
926
  data: i,
957
927
  model: a,
@@ -965,7 +935,7 @@ var g, _ = (t) => {
965
935
  }
966
936
  #m(e) {
967
937
  if (!this._model) return !1;
968
- let t = E(this._model, this.selectedTalentIds);
938
+ let t = C(this._model, this.selectedTalentIds);
969
939
  return t.selectedTalentIds.length !== this.selectedTalentIds.length || t.selectedTalentIds.some((e, t) => e !== this.selectedTalentIds[t]) ? (this.selectedTalentIds = t.selectedTalentIds, t.removedTalentIds.length > 0 && this.#x([{
970
940
  code: "invalid-selection",
971
941
  message: `Removed unavailable talent selections: ${t.removedTalentIds.join(", ")}.`,
@@ -984,13 +954,13 @@ var g, _ = (t) => {
984
954
  this.selectedTalentIds = [...this.selectionState.selectedTalentIds, e.talent.id], this.#_("user");
985
955
  }
986
956
  #g(e, t) {
987
- let n = T(e), r = this.selectionState;
957
+ let n = S(e), r = this.selectionState;
988
958
  n === this.talentPoints || n < r.spentPoints || (this.talentPoints = n, this.#r = !0, this.#v(t));
989
959
  }
990
960
  #_(e) {
991
- let t = this._model ? D(this._model, this.selectedTalentIds, this.talentPoints) : N(this.talentPoints);
961
+ let t = this._model ? w(this._model, this.selectedTalentIds, this.talentPoints) : A(this.talentPoints);
992
962
  this.dispatchEvent(new CustomEvent("fellowship-talent-selection-change", {
993
- ...A,
963
+ ...D,
994
964
  detail: {
995
965
  ...t,
996
966
  source: e
@@ -1000,7 +970,7 @@ var g, _ = (t) => {
1000
970
  #v(e) {
1001
971
  let t = this.selectionState;
1002
972
  this.dispatchEvent(new CustomEvent("fellowship-talent-points-change", {
1003
- ...A,
973
+ ...D,
1004
974
  detail: {
1005
975
  ...t,
1006
976
  talentPoints: this.talentPoints,
@@ -1031,7 +1001,7 @@ var g, _ = (t) => {
1031
1001
  }
1032
1002
  #x(e) {
1033
1003
  this.dispatchEvent(new CustomEvent("fellowship-talents-warning", {
1034
- ...A,
1004
+ ...D,
1035
1005
  detail: {
1036
1006
  warnings: e,
1037
1007
  model: this._model
@@ -1053,34 +1023,34 @@ var g, _ = (t) => {
1053
1023
  message: t instanceof Error ? t.message : String(t)
1054
1024
  };
1055
1025
  this._error = n, this._status = "error", this.dispatchEvent(new CustomEvent("fellowship-talents-error", {
1056
- ...A,
1026
+ ...D,
1057
1027
  detail: n
1058
1028
  }));
1059
1029
  }
1060
1030
  };
1061
- r([p({
1031
+ r([f({
1062
1032
  type: String,
1063
1033
  attribute: "hero-id"
1064
- })], P.prototype, "heroId", void 0), r([p({
1034
+ })], j.prototype, "heroId", void 0), r([f({
1065
1035
  type: Number,
1066
1036
  attribute: "talent-points"
1067
- })], P.prototype, "talentPoints", void 0), r([p({
1037
+ })], j.prototype, "talentPoints", void 0), r([f({
1068
1038
  attribute: !1,
1069
- hasChanged: j
1070
- })], P.prototype, "selectedTalentIds", void 0), r([p({
1039
+ hasChanged: O
1040
+ })], j.prototype, "selectedTalentIds", void 0), r([f({
1071
1041
  type: String,
1072
1042
  attribute: "asset-base-url"
1073
- })], P.prototype, "assetBaseUrl", void 0), r([p({ attribute: !1 })], P.prototype, "dataSource", void 0), r([p({ attribute: !1 })], P.prototype, "renderTalent", void 0), r([p({
1043
+ })], j.prototype, "assetBaseUrl", void 0), r([f({ attribute: !1 })], j.prototype, "dataSource", void 0), r([f({ attribute: !1 })], j.prototype, "renderTalent", void 0), r([f({
1074
1044
  type: Boolean,
1075
1045
  attribute: "show-point-controls"
1076
- })], P.prototype, "showPointControls", void 0), r([p({
1046
+ })], j.prototype, "showPointControls", void 0), r([f({
1077
1047
  type: Boolean,
1078
1048
  attribute: "show-tooltips"
1079
- })], P.prototype, "showTooltips", void 0), r([m()], P.prototype, "_status", void 0), r([m()], P.prototype, "_error", void 0), r([m()], P.prototype, "_model", void 0), r([m()], P.prototype, "_failedImages", void 0), r([m()], P.prototype, "_announcement", void 0), r([m()], P.prototype, "_renderSelectionState", void 0), r([m()], P.prototype, "_talentPresentation", void 0), r([m()], P.prototype, "_talentRenderContent", void 0), P = r([f("fellowship-talent-calculator")], P);
1080
- var F = () => {
1081
- a(), customElements.get("fellowship-talent-calculator") || customElements.define("fellowship-talent-calculator", P);
1049
+ })], j.prototype, "showTooltips", void 0), r([p()], j.prototype, "_status", void 0), r([p()], j.prototype, "_error", void 0), r([p()], j.prototype, "_model", void 0), r([p()], j.prototype, "_failedImages", void 0), r([p()], j.prototype, "_announcement", void 0), r([p()], j.prototype, "_renderSelectionState", void 0), r([p()], j.prototype, "_talentPresentation", void 0), r([p()], j.prototype, "_talentRenderContent", void 0), j = r([d("fellowship-talent-calculator")], j);
1050
+ var M = () => {
1051
+ a(), customElements.get("fellowship-talent-calculator") || customElements.define("fellowship-talent-calculator", j);
1082
1052
  };
1083
1053
  //#endregion
1084
- export { O as a, T as c, y as d, w as i, C as l, F as n, D as o, M as r, E as s, P as t, x as u };
1054
+ export { T as a, S as c, g as d, x as i, b as l, M as n, w as o, k as r, C as s, j as t, v as u };
1085
1055
 
1086
- //# sourceMappingURL=talent-calculator-Ch1Pbbba.js.map
1056
+ //# sourceMappingURL=talent-calculator-CIPNbqK5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"talent-calculator-CIPNbqK5.js","names":["#abortController","#flushRendererWarnings","#loadHeroTalents","#skipNextPropertyPointEvent","#emitPointsChange","#normalizeSelections","#reportOverBudget","#rendererFailures","#prepareTalentRenderContent","#emitSelectionChange","#renderHeader","#renderGrid","#renderStatus","#setTalentPoints","#renderTalent","#handleTalentActivation","#renderTalentContent","#pendingRendererWarnings","#renderDefaultTalentIcon","#handleImageError","#loadToken","#lastOverBudgetSignature","#reportWarnings","#setError"],"sources":["../../src/data/package-talent-data-source.ts","../../src/talent-calculator/render-model.ts","../../src/talent-calculator/selection-state.ts","../../src/talent-calculator/talent-calculator.styles.ts","../../src/talent-calculator/talent-calculator.ts"],"sourcesContent":["import { createPackageFellowshipDataClient } from \"@crockery/fellowship-data/client\";\n\nimport type { FellowshipTalentDataSource } from \"./talent-data-source.js\";\n\n/** @deprecated Create a package Fellowship data client instead. */\nexport interface PackageFellowshipTalentDataSourceOptions {\n readonly assetBaseUrl?: string;\n}\n\n/** @deprecated Use `createPackageFellowshipDataClient` from `@crockery/fellowship-data/client`. */\nexport const createPackageFellowshipTalentDataSource = (\n options: PackageFellowshipTalentDataSourceOptions = {},\n): FellowshipTalentDataSource => {\n const client = createPackageFellowshipDataClient(options);\n return {\n loadHeroTalents: (heroId, signal) => client.loadHeroTalents(heroId, signal),\n resolveAssetUrl: (asset) => client.resolveAssetUrl(asset),\n };\n};\n","import { findImage } from \"@crockery/fellowship-data\";\nimport type { Talent } from \"@crockery/fellowship-data\";\n\nimport type {\n FellowshipTalentDataSource,\n TalentCalculatorData,\n} from \"../data/talent-data-source.js\";\nimport type {\n TalentCalculatorRenderModel,\n TalentCalculatorTalentModel,\n TalentCalculatorWarning,\n} from \"./types.js\";\n\nconst romanNumerals = [\"I\", \"II\", \"III\", \"IV\", \"V\", \"VI\"] as const;\n\nexport const talentRowLabel = (row: number): string => romanNumerals[row - 1] ?? String(row);\n\nconst warning = (\n code: TalentCalculatorWarning[\"code\"],\n talent: Talent,\n message: string,\n): TalentCalculatorWarning => ({ code, message, talentId: talent.id });\n\nexport const createTalentCalculatorRenderModel = (\n data: TalentCalculatorData,\n source: FellowshipTalentDataSource,\n): TalentCalculatorRenderModel => {\n const warnings: TalentCalculatorWarning[] = [];\n const occupied = new Set<string>();\n const models: TalentCalculatorTalentModel[] = [];\n\n for (const talent of data.talents) {\n if (talent.row === null && talent.column === null) {\n continue;\n }\n if (\n !Number.isInteger(talent.row) ||\n !Number.isInteger(talent.column) ||\n talent.row === null ||\n talent.column === null ||\n talent.row < 1 ||\n talent.row > 6 ||\n talent.column < 1 ||\n talent.column > 3\n ) {\n warnings.push(\n warning(\n \"invalid-position\",\n talent,\n `Talent “${talent.id}” has an invalid row or column and was skipped.`,\n ),\n );\n continue;\n }\n\n const cell = `${talent.row}:${talent.column}`;\n if (occupied.has(cell)) {\n warnings.push(\n warning(\n \"duplicate-position\",\n talent,\n `Talent “${talent.id}” duplicates row ${talent.row}, column ${talent.column} and was skipped.`,\n ),\n );\n continue;\n }\n occupied.add(cell);\n\n const pointCost =\n Number.isInteger(talent.pointCost) && (talent.pointCost ?? 0) > 0 ? talent.pointCost : null;\n if (pointCost === null) {\n warnings.push(\n warning(\"invalid-cost\", talent, `Talent “${talent.id}” has an invalid point cost.`),\n );\n }\n if (!talent.name) {\n warnings.push(warning(\"missing-name\", talent, `Talent “${talent.id}” has no display name.`));\n }\n if (!talent.description) {\n warnings.push(\n warning(\"missing-description\", talent, `Talent “${talent.id}” has no description.`),\n );\n }\n\n const image =\n findImage(talent.images, { type: \"talent\", size: \"200\" }) ??\n findImage(talent.images, { type: \"talent\", size: \"full\" });\n let imageUrl: string | null = null;\n if (!image) {\n warnings.push(warning(\"missing-image\", talent, `Talent “${talent.id}” has no icon.`));\n } else {\n try {\n imageUrl = source.resolveAssetUrl(image);\n } catch (error: unknown) {\n warnings.push(\n warning(\n \"asset-url-error\",\n talent,\n `The icon URL for talent “${talent.id}” could not be resolved: ${String(error)}.`,\n ),\n );\n }\n }\n\n models.push({\n talent,\n row: talent.row,\n column: talent.column,\n pointCost,\n imageUrl,\n });\n }\n\n models.sort((a, b) => a.row - b.row || a.column - b.column);\n const talentById = new Map(models.map((model) => [model.talent.id, model]));\n const rows = [...new Set(models.map(({ row }) => row))].map((row) => ({\n row,\n label: talentRowLabel(row),\n talents: models.filter((talent) => talent.row === row),\n }));\n\n return { hero: data.hero, rows, talentById, warnings };\n};\n","import type { TalentCalculatorRenderModel, TalentCalculatorSelectionState } from \"./types.js\";\n\nexport const MAX_TALENT_POINTS = 14;\n\nexport const normalizeTalentPoints = (value: number): number =>\n Math.min(\n MAX_TALENT_POINTS,\n Math.max(0, Math.round(Number.isFinite(value) ? value : MAX_TALENT_POINTS)),\n );\n\nexport const normalizeSelectedTalentIds = (\n model: TalentCalculatorRenderModel,\n selectedTalentIds: readonly string[],\n): {\n readonly selectedTalentIds: readonly string[];\n readonly removedTalentIds: readonly string[];\n} => {\n const requested = new Set(selectedTalentIds);\n const selected = [...model.talentById.values()]\n .filter(({ talent }) => requested.has(talent.id))\n .map(({ talent }) => talent.id);\n const selectedSet = new Set(selected);\n const removed = [...new Set(selectedTalentIds)].filter((id) => !selectedSet.has(id));\n return { selectedTalentIds: selected, removedTalentIds: removed };\n};\n\nexport const createTalentCalculatorSelectionState = (\n model: TalentCalculatorRenderModel,\n selectedTalentIds: readonly string[],\n talentPoints: number,\n): TalentCalculatorSelectionState => {\n const selected = new Set(selectedTalentIds);\n const spentPoints = [...model.talentById.values()].reduce(\n (total, talent) =>\n selected.has(talent.talent.id) && talent.pointCost !== null\n ? total + talent.pointCost\n : total,\n 0,\n );\n const remainingPoints = talentPoints - spentPoints;\n return {\n selectedTalentIds: [...model.talentById.values()]\n .filter(({ talent }) => selected.has(talent.id))\n .map(({ talent }) => talent.id),\n spentPoints,\n remainingPoints,\n overBudget: remainingPoints < 0,\n };\n};\n\nexport const canSelectTalent = (\n pointCost: number | null,\n state: TalentCalculatorSelectionState,\n): boolean => pointCost !== null && !state.overBudget && pointCost <= state.remainingPoints;\n","import { css } from \"lit\";\n\nexport const talentCalculatorStyles = css`\n :host {\n --fellowship-talent-background: var(--_fellowship-field-canvas);\n --fellowship-talent-surface: var(--_fellowship-field-surface);\n --fellowship-talent-card-background: var(--_fellowship-field-surface);\n --fellowship-talent-card-selected-background: color-mix(\n in srgb,\n var(--_fellowship-field-surface-raised) 88%,\n var(--_fellowship-field-accent)\n );\n --fellowship-talent-foreground: var(--_fellowship-field-ink);\n --fellowship-talent-muted: var(--_fellowship-field-muted);\n --fellowship-talent-disabled: var(--_fellowship-field-tertiary);\n --fellowship-talent-border: var(--_fellowship-field-border);\n --fellowship-talent-selection: var(--_fellowship-field-accent);\n --fellowship-talent-focus: var(--_fellowship-field-focus);\n --fellowship-talent-points: var(--_fellowship-field-brass);\n --fellowship-talent-danger: var(--_fellowship-field-ember);\n --fellowship-talent-control-surface: var(--_fellowship-field-inset);\n --fellowship-talent-icon-size: 56px;\n --fellowship-talent-font-family: var(--_fellowship-field-font-family);\n --fellowship-talent-tactical-font-family: var(--_fellowship-field-mono-font-family);\n --fellowship-talent-radius: var(--_fellowship-field-radius);\n\n display: block;\n min-width: 0;\n container-type: inline-size;\n color: var(--fellowship-talent-foreground);\n font-family: var(--fellowship-talent-font-family);\n -webkit-font-smoothing: antialiased;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n button {\n font: inherit;\n }\n\n .container {\n min-width: 0;\n padding: 16px;\n border-radius: calc(var(--fellowship-talent-radius) + 2px);\n background: var(--fellowship-talent-background);\n }\n\n .header {\n min-height: 72px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 24px;\n padding: 8px 8px 24px;\n }\n\n .heading {\n margin: 0;\n color: var(--fellowship-talent-muted);\n font-size: 12px;\n font-weight: 600;\n line-height: 16px;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n }\n\n .points-cluster,\n .point-controls {\n display: flex;\n align-items: center;\n }\n\n .points-cluster {\n gap: 16px;\n }\n\n .points {\n display: flex;\n align-items: baseline;\n gap: 4px;\n color: var(--fellowship-talent-foreground);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 32px;\n font-weight: 600;\n line-height: 40px;\n letter-spacing: -0.04em;\n font-variant-numeric: tabular-nums;\n }\n\n .points-spent {\n color: var(--fellowship-talent-points);\n }\n\n .points-separator,\n .points-total {\n color: var(--_fellowship-field-tertiary);\n }\n\n .points-separator {\n font-size: 18px;\n font-weight: 500;\n }\n\n .points[data-invalid=\"true\"],\n .points[data-invalid=\"true\"] .points-spent {\n color: var(--fellowship-talent-danger);\n }\n\n .point-controls {\n gap: 4px;\n padding: 2px;\n border-radius: calc(var(--fellowship-talent-radius) + 2px);\n background: var(--fellowship-talent-control-surface);\n box-shadow: inset 0 0 0 1px var(--fellowship-talent-border);\n }\n\n .point-control {\n width: 40px;\n height: 40px;\n display: inline-grid;\n place-items: center;\n padding: 0;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: var(--fellowship-talent-muted);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 18px;\n font-weight: 500;\n line-height: 1;\n cursor: pointer;\n transition:\n background-color 120ms cubic-bezier(0.23, 1, 0.32, 1),\n color 120ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 120ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n .point-control:active:not(:disabled) {\n transform: scale(0.97);\n }\n\n .point-control:focus-visible,\n .clear-all:focus-visible,\n .talent:focus-visible,\n .retry-button:focus-visible {\n outline: 2px solid var(--fellowship-talent-focus);\n outline-offset: 2px;\n }\n\n .point-control:disabled {\n color: var(--fellowship-talent-disabled);\n cursor: not-allowed;\n opacity: 0.48;\n }\n\n .clear-all {\n min-height: 44px;\n padding: 0 8px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: var(--fellowship-talent-muted);\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n letter-spacing: 0.04em;\n white-space: nowrap;\n cursor: pointer;\n transition:\n background-color 120ms cubic-bezier(0.23, 1, 0.32, 1),\n color 120ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 120ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n .clear-all:active:not(:disabled) {\n transform: scale(0.97);\n }\n\n .clear-all:disabled {\n color: var(--fellowship-talent-disabled);\n cursor: not-allowed;\n opacity: 0.42;\n }\n\n .grid {\n display: grid;\n gap: 16px;\n padding: 8px 0 0;\n }\n\n .row {\n min-width: 0;\n min-inline-size: 0;\n width: 100%;\n display: grid;\n grid-template-columns: 48px minmax(0, 1fr);\n align-items: stretch;\n gap: 8px;\n margin: 0;\n padding: 0;\n border: 0;\n }\n\n .row-label {\n position: relative;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 16px;\n color: var(--fellowship-talent-disabled);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 11px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n letter-spacing: 0.08em;\n }\n\n .row-label::before {\n position: absolute;\n inset: 0 0 0 auto;\n width: 1px;\n background: var(--fellowship-talent-border);\n content: \"\";\n }\n\n .row-label::after {\n position: absolute;\n top: 23px;\n right: -3px;\n width: 7px;\n height: 1px;\n background: var(--fellowship-talent-selection);\n content: \"\";\n }\n\n .row-talents {\n min-width: 0;\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 8px;\n }\n\n fellowship-tooltip {\n display: block;\n min-width: 0;\n }\n\n .talent {\n position: relative;\n width: 100%;\n min-width: 0;\n min-height: 88px;\n display: grid;\n grid-template-columns: var(--fellowship-talent-icon-size) minmax(0, 1fr);\n align-items: center;\n gap: 16px;\n padding: 16px 36px 16px 16px;\n overflow: hidden;\n border: 1px solid transparent;\n border-radius: var(--fellowship-talent-radius);\n background: var(--fellowship-talent-card-background);\n color: var(--fellowship-talent-foreground);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fellowship-talent-border) 64%, transparent);\n text-align: left;\n cursor: pointer;\n transition:\n background-color 140ms cubic-bezier(0.23, 1, 0.32, 1),\n box-shadow 140ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 120ms cubic-bezier(0.23, 1, 0.32, 1),\n opacity 140ms linear;\n }\n\n .talent::before {\n position: absolute;\n top: 16px;\n left: 0;\n width: 2px;\n height: 24px;\n background: transparent;\n content: \"\";\n }\n\n .talent:active:not([aria-disabled=\"true\"]) {\n transform: scale(0.99);\n }\n\n .talent[aria-pressed=\"true\"] {\n background: var(--fellowship-talent-card-selected-background);\n box-shadow: inset 0 0 0 1px\n color-mix(in srgb, var(--fellowship-talent-selection) 32%, transparent);\n }\n\n .talent[aria-pressed=\"true\"]::before {\n background: var(--fellowship-talent-selection);\n }\n\n .talent[aria-disabled=\"true\"] {\n color: var(--fellowship-talent-disabled);\n cursor: not-allowed;\n opacity: 0.46;\n }\n\n .talent-icon,\n .talent-icon-fallback {\n width: var(--fellowship-talent-icon-size);\n height: var(--fellowship-talent-icon-size);\n border-radius: calc(var(--fellowship-talent-radius) - 2px);\n box-shadow: inset 0 0 0 1px var(--fellowship-talent-border);\n }\n\n .talent-icon {\n display: block;\n object-fit: cover;\n }\n\n .talent-icon-fallback {\n display: grid;\n place-items: center;\n background: var(--fellowship-talent-control-surface);\n color: var(--fellowship-talent-muted);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 13px;\n font-weight: 600;\n letter-spacing: 0.08em;\n }\n\n .talent[aria-pressed=\"true\"] .talent-icon,\n .talent[aria-pressed=\"true\"] .talent-icon-fallback {\n box-shadow:\n inset 0 0 0 1px var(--fellowship-talent-border),\n 0 0 0 1px color-mix(in srgb, var(--fellowship-talent-selection) 60%, transparent);\n }\n\n .talent-name {\n min-width: 0;\n overflow: hidden;\n color: currentColor;\n font-size: 16px;\n font-weight: 600;\n line-height: 24px;\n text-overflow: ellipsis;\n text-wrap: balance;\n white-space: nowrap;\n }\n\n .talent-cost {\n position: absolute;\n top: 12px;\n right: 12px;\n color: var(--fellowship-talent-muted);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 11px;\n font-weight: 600;\n line-height: 16px;\n font-variant-numeric: tabular-nums;\n }\n\n .talent[aria-pressed=\"true\"] .talent-cost {\n color: var(--fellowship-talent-focus);\n }\n\n .status {\n min-height: 256px;\n display: grid;\n place-items: center;\n padding: 32px;\n color: var(--fellowship-talent-muted);\n text-align: center;\n }\n\n .status-card {\n max-width: 480px;\n }\n\n .status-mark {\n display: flex;\n width: 40px;\n height: 24px;\n align-items: center;\n justify-content: space-between;\n margin: 0 auto 16px;\n color: var(--fellowship-talent-points);\n font-family: var(--fellowship-talent-tactical-font-family);\n font-size: 16px;\n }\n\n .status-mark::before,\n .status-mark::after {\n width: 8px;\n height: 1px;\n background: var(--fellowship-talent-border);\n content: \"\";\n }\n\n .status-title {\n margin: 0;\n color: var(--fellowship-talent-foreground);\n font-size: 18px;\n font-weight: 600;\n line-height: 24px;\n text-wrap: balance;\n }\n\n .status-copy {\n margin: 8px 0 0;\n font-size: 14px;\n line-height: 24px;\n text-wrap: pretty;\n }\n\n .retry-button {\n min-height: 44px;\n margin-top: 24px;\n padding: 0 16px;\n border: 1px solid var(--fellowship-talent-border);\n border-radius: 4px;\n background: var(--fellowship-talent-control-surface);\n color: var(--fellowship-talent-foreground);\n font-weight: 600;\n cursor: pointer;\n }\n\n .visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n @media (hover: hover) {\n .point-control:hover:not(:disabled) {\n background: var(--fellowship-talent-surface);\n color: var(--fellowship-talent-foreground);\n }\n\n .clear-all:hover:not(:disabled) {\n background: color-mix(in srgb, var(--fellowship-talent-danger) 8%, transparent);\n color: var(--fellowship-talent-danger);\n }\n\n .talent:hover:not([aria-disabled=\"true\"]) {\n background: var(--_fellowship-field-surface-raised);\n box-shadow: inset 0 0 0 1px var(--fellowship-talent-border);\n }\n }\n\n @container (max-width: 719px) {\n .container {\n padding: 16px 12px;\n }\n\n .header {\n min-height: 80px;\n grid-template-columns: 1fr;\n display: grid;\n gap: 8px;\n padding: 4px 4px 20px;\n }\n\n .heading {\n font-size: 11px;\n }\n\n .points-cluster {\n width: 100%;\n flex-wrap: wrap;\n justify-content: flex-start;\n gap: 8px;\n }\n\n .points {\n font-size: 28px;\n line-height: 36px;\n }\n\n .grid {\n gap: 24px;\n }\n\n .row {\n grid-template-columns: 1fr;\n gap: 8px;\n }\n\n .row-label {\n min-height: 24px;\n align-items: center;\n justify-content: flex-start;\n padding: 0 0 0 16px;\n text-align: left;\n }\n\n .row-label::before {\n inset: 12px auto auto 0;\n width: 8px;\n height: 1px;\n }\n\n .row-label::after {\n top: 9px;\n right: auto;\n left: 0;\n width: 1px;\n height: 7px;\n }\n\n .row-talents {\n grid-template-columns: 1fr;\n gap: 8px;\n }\n\n .talent {\n --fellowship-talent-icon-size: 52px;\n\n min-height: 80px;\n padding-block: 12px;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .point-control,\n .clear-all,\n .talent {\n transition:\n background-color 100ms linear,\n box-shadow 100ms linear,\n color 100ms linear,\n opacity 100ms linear;\n }\n\n .point-control:active:not(:disabled),\n .clear-all:active:not(:disabled),\n .talent:active:not([aria-disabled=\"true\"]) {\n transform: none;\n }\n }\n`;\n","import { html, LitElement, nothing, type PropertyValues, type TemplateResult } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\n\nimport { FellowshipDataError } from \"../data/data-source.js\";\nimport { createPackageFellowshipTalentDataSource } from \"../data/package-talent-data-source.js\";\nimport type { FellowshipTalentDataSource } from \"../data/talent-data-source.js\";\nimport { fieldManualStyles } from \"../styles/field-manual.styles.js\";\nimport { installFieldManualFonts } from \"../styles/field-manual-fonts.js\";\nimport { stripFellowshipRichText } from \"../tooltip/rich-text.js\";\nimport { defineFellowshipTooltip } from \"../tooltip/tooltip.js\";\nimport { createTalentCalculatorRenderModel } from \"./render-model.js\";\nimport {\n canSelectTalent,\n createTalentCalculatorSelectionState,\n MAX_TALENT_POINTS,\n normalizeSelectedTalentIds,\n normalizeTalentPoints,\n} from \"./selection-state.js\";\nimport { talentCalculatorStyles } from \"./talent-calculator.styles.js\";\nimport type {\n FellowshipTalentsErrorDetail,\n FellowshipTalentsReadyDetail,\n FellowshipTalentsWarningDetail,\n LitTalentRenderer,\n TalentCalculatorRenderModel,\n TalentCalculatorSelectionState,\n TalentCalculatorTalentModel,\n TalentCalculatorTalentRenderContext,\n TalentCalculatorWarning,\n TalentPointsChangeDetail,\n TalentPointsChangeSource,\n TalentSelectionChangeDetail,\n TalentSelectionChangeSource,\n} from \"./types.js\";\n\ntype ComponentStatus = \"empty\" | \"loading\" | \"ready\" | \"error\";\n\ninterface TalentPresentation {\n readonly context: TalentCalculatorTalentRenderContext;\n readonly name: string;\n readonly descriptionId: string;\n readonly affordability: string;\n readonly description: string;\n}\n\nconst eventOptions = { bubbles: true, composed: true } as const;\n\nconst stringArrayChanged = (\n value: readonly string[],\n previous: readonly string[] | undefined,\n): boolean =>\n value.length !== previous?.length || value.some((item, index) => item !== previous[index]);\n\nexport const talentSlotName = (talentId: string): string => `talent:${talentId}`;\n\nconst fallbackSelectionState = (talentPoints: number): TalentCalculatorSelectionState => ({\n selectedTalentIds: [],\n spentPoints: 0,\n remainingPoints: talentPoints,\n overBudget: false,\n});\n\nexport { MAX_TALENT_POINTS };\n\n/**\n * @fires fellowship-talents-ready - Fired after talent data is loaded and rendered.\n * @fires fellowship-talents-error - Fired when talent data cannot be loaded.\n * @fires fellowship-talents-warning - Fired for recoverable data and renderer issues.\n * @fires fellowship-talent-selection-change - Fired when selected talents change.\n * @fires fellowship-talent-points-change - Fired when the point budget changes.\n */\n@customElement(\"fellowship-talent-calculator\")\nexport class FellowshipTalentCalculatorElement extends LitElement {\n static override styles = [fieldManualStyles, talentCalculatorStyles];\n\n @property({ type: String, attribute: \"hero-id\" }) heroId = \"\";\n @property({ type: Number, attribute: \"talent-points\" }) talentPoints = MAX_TALENT_POINTS;\n @property({ attribute: false, hasChanged: stringArrayChanged })\n selectedTalentIds: readonly string[] = [];\n @property({ type: String, attribute: \"asset-base-url\" }) assetBaseUrl: string | null = null;\n @property({ attribute: false }) dataSource: FellowshipTalentDataSource | null = null;\n @property({ attribute: false }) renderTalent: LitTalentRenderer | null = null;\n @property({ type: Boolean, attribute: \"show-point-controls\" }) showPointControls = true;\n @property({ type: Boolean, attribute: \"show-tooltips\" }) showTooltips = true;\n\n @state() private _status: ComponentStatus = \"empty\";\n @state() private _error: FellowshipTalentsErrorDetail | null = null;\n @state() private _model: TalentCalculatorRenderModel | null = null;\n @state() private _failedImages = new Set<string>();\n @state() private _announcement = \"\";\n @state() private _renderSelectionState = fallbackSelectionState(MAX_TALENT_POINTS);\n @state() private _talentPresentation = new Map<string, TalentPresentation>();\n @state() private _talentRenderContent = new Map<string, unknown>();\n\n #abortController: AbortController | null = null;\n #loadToken = 0;\n #lastOverBudgetSignature: string | null = null;\n #skipNextPropertyPointEvent = false;\n #rendererFailures = new Set<string>();\n #pendingRendererWarnings: TalentCalculatorWarning[] = [];\n\n override connectedCallback(): void {\n super.connectedCallback();\n installFieldManualFonts(this.ownerDocument);\n }\n\n override disconnectedCallback(): void {\n this.#abortController?.abort();\n super.disconnectedCallback();\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n super.updated(changed);\n this.#flushRendererWarnings();\n\n if (changed.has(\"heroId\") || changed.has(\"dataSource\") || changed.has(\"assetBaseUrl\")) {\n queueMicrotask(() => {\n if (this.isConnected) {\n void this.#loadHeroTalents();\n }\n });\n return;\n }\n\n if (changed.has(\"talentPoints\")) {\n const normalized = normalizeTalentPoints(this.talentPoints);\n if (normalized !== this.talentPoints) {\n this.#skipNextPropertyPointEvent = true;\n this.talentPoints = normalized;\n this.#emitPointsChange(\"normalization\");\n return;\n }\n if (this.#skipNextPropertyPointEvent) {\n this.#skipNextPropertyPointEvent = false;\n } else if (changed.get(\"talentPoints\") !== undefined) {\n this.#emitPointsChange(\"property\");\n }\n }\n\n if (changed.has(\"selectedTalentIds\") && this._model) {\n this.#normalizeSelections(true);\n }\n\n this.#reportOverBudget();\n }\n\n protected override willUpdate(changed: PropertyValues): void {\n super.willUpdate(changed);\n\n const selectionChanged =\n changed.has(\"_model\") || changed.has(\"selectedTalentIds\") || changed.has(\"talentPoints\");\n if (selectionChanged) {\n this._renderSelectionState = this._model\n ? createTalentCalculatorSelectionState(\n this._model,\n this.selectedTalentIds,\n this.talentPoints,\n )\n : fallbackSelectionState(this.talentPoints);\n }\n\n if (changed.has(\"renderTalent\")) {\n this.#rendererFailures.clear();\n }\n if (selectionChanged || changed.has(\"renderTalent\")) {\n this.#prepareTalentRenderContent(this._renderSelectionState);\n }\n }\n\n get renderModel(): TalentCalculatorRenderModel | null {\n return this._model;\n }\n\n get selectionState(): TalentCalculatorSelectionState {\n return this._model\n ? createTalentCalculatorSelectionState(this._model, this.selectedTalentIds, this.talentPoints)\n : fallbackSelectionState(this.talentPoints);\n }\n\n reset(): void {\n if (this.selectedTalentIds.length === 0) {\n return;\n }\n this.selectedTalentIds = [];\n this.#emitSelectionChange(\"reset\");\n }\n\n selectTalent(talentId: string): boolean {\n const model = this._model?.talentById.get(talentId);\n const state = this.selectionState;\n if (\n !model ||\n state.selectedTalentIds.includes(talentId) ||\n !canSelectTalent(model.pointCost, state)\n ) {\n return false;\n }\n this.selectedTalentIds = [...state.selectedTalentIds, talentId];\n this.#emitSelectionChange(\"api\");\n return true;\n }\n\n deselectTalent(talentId: string): boolean {\n if (!this.selectionState.selectedTalentIds.includes(talentId)) {\n return false;\n }\n this.selectedTalentIds = this.selectionState.selectedTalentIds.filter((id) => id !== talentId);\n this.#emitSelectionChange(\"api\");\n return true;\n }\n\n retry(): void {\n void this.#loadHeroTalents();\n }\n\n override render(): TemplateResult {\n const state = this._renderSelectionState;\n const label = this._model?.hero.name ?? this._model?.hero.id ?? \"Fellowship hero\";\n return html`\n <section\n class=\"container\"\n part=\"container\"\n aria-label=${`${label} talent calculator`}\n aria-invalid=${String(state.overBudget)}\n >\n ${this.#renderHeader(state)}\n ${\n this._status === \"ready\" && this._model\n ? this.#renderGrid(this._model)\n : this.#renderStatus()\n }\n <p class=\"visually-hidden\" role=\"status\" aria-live=\"polite\">${this._announcement}</p>\n </section>\n `;\n }\n\n #renderHeader(state: TalentCalculatorSelectionState): TemplateResult {\n return html`\n <header class=\"header\" part=\"header\">\n <h2 class=\"heading\" part=\"heading\">Talent points</h2>\n <div class=\"points-cluster\">\n <div\n class=\"points\"\n part=\"points\"\n data-invalid=${String(state.overBudget)}\n aria-label=${`${state.spentPoints} of ${this.talentPoints} talent points spent`}\n aria-live=\"polite\"\n >\n <span class=\"points-spent\" part=\"points-spent\">${state.spentPoints}</span>\n <span class=\"points-separator\" aria-hidden=\"true\">/</span>\n <span class=\"points-total\" part=\"points-total\">${this.talentPoints}</span>\n </div>\n ${\n this.showPointControls\n ? html`\n <div\n class=\"point-controls\"\n part=\"point-controls\"\n role=\"group\"\n aria-label=\"Talent points\"\n >\n <button\n class=\"point-control\"\n part=\"point-control\"\n type=\"button\"\n aria-label=\"Decrease available talent points\"\n title=\"Decrease available talent points\"\n ?disabled=${this.talentPoints <= state.spentPoints}\n @click=${() => this.#setTalentPoints(this.talentPoints - 1, \"decrement\")}\n >\n −\n </button>\n <button\n class=\"point-control\"\n part=\"point-control\"\n type=\"button\"\n aria-label=\"Increase available talent points\"\n title=\"Increase available talent points\"\n ?disabled=${this.talentPoints >= MAX_TALENT_POINTS}\n @click=${() => this.#setTalentPoints(this.talentPoints + 1, \"increment\")}\n >\n +\n </button>\n </div>\n `\n : nothing\n }\n <button\n class=\"clear-all\"\n part=\"clear-all\"\n type=\"button\"\n ?disabled=${state.selectedTalentIds.length === 0}\n @click=${this.reset}\n >\n Clear All\n </button>\n </div>\n </header>\n `;\n }\n\n #renderGrid(model: TalentCalculatorRenderModel): TemplateResult {\n if (model.rows.length === 0) {\n return html`\n <div class=\"status\" part=\"empty-state\" role=\"status\">\n <div class=\"status-card\">\n <div class=\"status-mark\" aria-hidden=\"true\">◇</div>\n <p class=\"status-title\">No active talents</p>\n <p class=\"status-copy\">\n ${model.hero.name ?? model.hero.id} has no positioned talents.\n </p>\n </div>\n </div>\n `;\n }\n\n return html`\n <div class=\"grid\" part=\"grid\" role=\"group\" aria-label=\"Talent tiers\">\n ${repeat(\n model.rows,\n (row) => row.row,\n (row) => html`\n <fieldset class=\"row\" part=\"row\">\n <legend class=\"visually-hidden\">Talent tier ${row.label}</legend>\n <span class=\"row-label\" part=\"row-label\" aria-hidden=\"true\">${row.label}</span>\n <div class=\"row-talents\">\n ${repeat(\n row.talents,\n (talent) => talent.talent.id,\n (talent) => this.#renderTalent(talent),\n )}\n </div>\n </fieldset>\n `,\n )}\n </div>\n `;\n }\n\n #renderTalent(talent: TalentCalculatorTalentModel): TemplateResult | typeof nothing {\n const presentation = this._talentPresentation.get(talent.talent.id);\n if (!presentation) {\n return nothing;\n }\n const { context, name, descriptionId, affordability, description } = presentation;\n const { selected, disabled } = context;\n const card = html`\n <button\n class=\"talent\"\n part=\"talent\"\n type=\"button\"\n aria-label=${`${name}, ${affordability}`}\n aria-describedby=${descriptionId}\n aria-pressed=${String(selected)}\n aria-disabled=${String(disabled)}\n @click=${() => this.#handleTalentActivation(talent, selected, disabled)}\n >\n ${this.#renderTalentContent(context)}\n <span class=\"talent-cost\" part=\"talent-cost\">${talent.pointCost ?? \"–\"}</span>\n </button>\n `;\n\n return html`\n ${\n this.showTooltips && (talent.talent.description || disabled)\n ? html`\n <fellowship-tooltip\n .heading=${name}\n .text=${talent.talent.description ?? affordability}\n >\n ${card}\n </fellowship-tooltip>\n `\n : card\n }\n <span class=\"visually-hidden\" id=${descriptionId}>${description}</span>\n `;\n }\n\n #prepareTalentRenderContent(state: TalentCalculatorSelectionState): void {\n const renderer = this.renderTalent;\n const model = this._model;\n const content = new Map<string, unknown>();\n const presentations = new Map<string, TalentPresentation>();\n\n if (model) {\n for (const talent of model.talentById.values()) {\n const selected = state.selectedTalentIds.includes(talent.talent.id);\n const affordable = canSelectTalent(talent.pointCost, state);\n const disabled = !selected && !affordable;\n const name = talent.talent.name ?? talent.talent.id;\n const affordability =\n talent.pointCost === null\n ? \"This talent has no valid point cost.\"\n : disabled\n ? `Requires ${talent.pointCost} talent points; ${Math.max(0, state.remainingPoints)} remaining.`\n : `${talent.pointCost} talent ${talent.pointCost === 1 ? \"point\" : \"points\"}.`;\n const context: TalentCalculatorTalentRenderContext = {\n talent,\n hero: model.hero,\n selected,\n affordable,\n disabled,\n spentPoints: state.spentPoints,\n remainingPoints: state.remainingPoints,\n talentPoints: this.talentPoints,\n };\n presentations.set(talent.talent.id, {\n context,\n name,\n descriptionId: `talent-description-${talent.row}-${talent.column}`,\n affordability,\n description: [stripFellowshipRichText(talent.talent.description ?? \"\"), affordability]\n .filter(Boolean)\n .join(\" \"),\n });\n if (!renderer) {\n continue;\n }\n try {\n content.set(talent.talent.id, renderer(context));\n } catch (error: unknown) {\n if (!this.#rendererFailures.has(talent.talent.id)) {\n this.#rendererFailures.add(talent.talent.id);\n this.#pendingRendererWarnings.push({\n code: \"renderer-error\",\n message: `The talent renderer failed for “${talent.talent.id}”: ${String(error)}.`,\n talentId: talent.talent.id,\n });\n }\n }\n }\n }\n\n this._talentPresentation = presentations;\n this._talentRenderContent = content;\n }\n\n #renderTalentContent(context: TalentCalculatorTalentRenderContext): unknown {\n const talent = context.talent;\n const name = talent.talent.name ?? talent.talent.id;\n const content =\n this.renderTalent && this._talentRenderContent.has(talent.talent.id)\n ? this._talentRenderContent.get(talent.talent.id)\n : html`\n <slot name=${talentSlotName(talent.talent.id)}>\n <span class=\"talent-name\" part=\"talent-name\" title=${name}>${name}</span>\n </slot>\n `;\n\n return html`\n ${this.#renderDefaultTalentIcon(talent, name)} ${content}\n `;\n }\n\n #renderDefaultTalentIcon(talent: TalentCalculatorTalentModel, name: string): TemplateResult {\n if (talent.imageUrl && !this._failedImages.has(talent.talent.id)) {\n return html`\n <img\n class=\"talent-icon\"\n part=\"talent-icon\"\n src=${talent.imageUrl}\n alt=\"\"\n draggable=\"false\"\n @error=${() => this.#handleImageError(talent)}\n />\n `;\n }\n const rune = name\n .split(/\\s+/)\n .map((part) => part[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n return html`<span class=\"talent-icon-fallback\" part=\"talent-icon-fallback\">${rune}</span>`;\n }\n\n #renderStatus(): TemplateResult {\n const loading = this._status === \"loading\";\n const empty = this._status === \"empty\";\n const title = loading\n ? \"Reading talent codex\"\n : empty\n ? \"Choose a hero\"\n : \"Talents unavailable\";\n const copy = loading\n ? \"Loading the hero and their available talents.\"\n : empty\n ? \"Set the hero-id attribute or heroId property to build a talent loadout.\"\n : (this._error?.message ?? \"An unexpected talent data error occurred.\");\n return html`\n <div\n class=\"status\"\n part=${this._status === \"error\" ? \"error-state\" : \"status\"}\n role=${this._status === \"error\" ? \"alert\" : \"status\"}\n >\n <div class=\"status-card\">\n <div class=\"status-mark\" aria-hidden=\"true\">${loading ? \"…\" : \"✦\"}</div>\n <p class=\"status-title\">${title}</p>\n <p class=\"status-copy\">${copy}</p>\n ${\n this._status === \"error\"\n ? html`<button class=\"retry-button\" type=\"button\" @click=${this.retry}>Retry</button>`\n : nothing\n }\n </div>\n </div>\n `;\n }\n\n async #loadHeroTalents(): Promise<void> {\n const heroId = this.heroId.trim();\n const token = ++this.#loadToken;\n this.#abortController?.abort();\n this.#abortController = new AbortController();\n this._model = null;\n this._error = null;\n this._failedImages = new Set();\n this.#rendererFailures.clear();\n this.#lastOverBudgetSignature = null;\n\n if (!heroId) {\n this._status = \"empty\";\n return;\n }\n\n this._status = \"loading\";\n const source =\n this.dataSource ??\n createPackageFellowshipTalentDataSource(\n this.assetBaseUrl ? { assetBaseUrl: this.assetBaseUrl } : {},\n );\n\n try {\n const data = await source.loadHeroTalents(heroId, this.#abortController.signal);\n if (token !== this.#loadToken || this.#abortController.signal.aborted) {\n return;\n }\n if (data.manifest.schemaVersion !== 1) {\n throw new FellowshipDataError(\n \"unsupported-schema\",\n `Unsupported fellowship-data schema version: ${String(data.manifest.schemaVersion)}.`,\n );\n }\n const model = createTalentCalculatorRenderModel(data, source);\n this._model = model;\n this._status = \"ready\";\n const normalized = this.#normalizeSelections(false);\n await this.updateComplete;\n const selection = this.selectionState;\n this.dispatchEvent(\n new CustomEvent<FellowshipTalentsReadyDetail>(\"fellowship-talents-ready\", {\n ...eventOptions,\n detail: { data, model, selection },\n }),\n );\n if (model.warnings.length > 0) {\n this.#reportWarnings(model.warnings);\n }\n if (normalized) {\n this.#emitSelectionChange(\"normalization\");\n }\n this.#reportOverBudget();\n } catch (error: unknown) {\n if (token !== this.#loadToken || this.#abortController.signal.aborted) {\n return;\n }\n this.#setError(error);\n }\n }\n\n #normalizeSelections(emit: boolean): boolean {\n if (!this._model) {\n return false;\n }\n const normalized = normalizeSelectedTalentIds(this._model, this.selectedTalentIds);\n const changed =\n normalized.selectedTalentIds.length !== this.selectedTalentIds.length ||\n normalized.selectedTalentIds.some((id, index) => id !== this.selectedTalentIds[index]);\n if (!changed) {\n return false;\n }\n this.selectedTalentIds = normalized.selectedTalentIds;\n if (normalized.removedTalentIds.length > 0) {\n this.#reportWarnings([\n {\n code: \"invalid-selection\",\n message: `Removed unavailable talent selections: ${normalized.removedTalentIds.join(\", \")}.`,\n talentId: null,\n },\n ]);\n }\n if (emit) {\n this.#emitSelectionChange(\"normalization\");\n }\n return true;\n }\n\n #handleTalentActivation(\n talent: TalentCalculatorTalentModel,\n selected: boolean,\n disabled: boolean,\n ): void {\n if (selected) {\n this.selectedTalentIds = this.selectionState.selectedTalentIds.filter(\n (id) => id !== talent.talent.id,\n );\n this.#emitSelectionChange(\"user\");\n return;\n }\n if (disabled) {\n this._announcement = this.selectionState.overBudget\n ? \"The current build exceeds the available talent points. Deselect a talent first.\"\n : `Not enough points for ${talent.talent.name ?? talent.talent.id}.`;\n return;\n }\n this.selectedTalentIds = [...this.selectionState.selectedTalentIds, talent.talent.id];\n this.#emitSelectionChange(\"user\");\n }\n\n #setTalentPoints(value: number, source: TalentPointsChangeSource): void {\n const normalized = normalizeTalentPoints(value);\n const state = this.selectionState;\n if (normalized === this.talentPoints || normalized < state.spentPoints) {\n return;\n }\n this.talentPoints = normalized;\n this.#skipNextPropertyPointEvent = true;\n this.#emitPointsChange(source);\n }\n\n #emitSelectionChange(source: TalentSelectionChangeSource): void {\n const state = this._model\n ? createTalentCalculatorSelectionState(this._model, this.selectedTalentIds, this.talentPoints)\n : fallbackSelectionState(this.talentPoints);\n this.dispatchEvent(\n new CustomEvent<TalentSelectionChangeDetail>(\"fellowship-talent-selection-change\", {\n ...eventOptions,\n detail: { ...state, source },\n }),\n );\n }\n\n #emitPointsChange(source: TalentPointsChangeSource): void {\n const state = this.selectionState;\n this.dispatchEvent(\n new CustomEvent<TalentPointsChangeDetail>(\"fellowship-talent-points-change\", {\n ...eventOptions,\n detail: { ...state, talentPoints: this.talentPoints, source },\n }),\n );\n }\n\n #reportOverBudget(): void {\n const state = this.selectionState;\n if (!state.overBudget) {\n this.#lastOverBudgetSignature = null;\n this.removeAttribute(\"aria-invalid\");\n return;\n }\n this.setAttribute(\"aria-invalid\", \"true\");\n const signature = `${this.talentPoints}:${state.selectedTalentIds.join(\",\")}`;\n if (signature === this.#lastOverBudgetSignature) {\n return;\n }\n this.#lastOverBudgetSignature = signature;\n this.#reportWarnings([\n {\n code: \"over-budget\",\n message: `The selected talents cost ${state.spentPoints} points, exceeding the ${this.talentPoints}-point budget.`,\n talentId: null,\n },\n ]);\n }\n\n #handleImageError(talent: TalentCalculatorTalentModel): void {\n if (this._failedImages.has(talent.talent.id)) {\n return;\n }\n this._failedImages = new Set(this._failedImages).add(talent.talent.id);\n this.#reportWarnings([\n {\n code: \"asset-image-error\",\n message: `The icon for talent “${talent.talent.id}” could not be loaded.`,\n talentId: talent.talent.id,\n },\n ]);\n }\n\n #reportWarnings(warnings: readonly TalentCalculatorWarning[]): void {\n this.dispatchEvent(\n new CustomEvent<FellowshipTalentsWarningDetail>(\"fellowship-talents-warning\", {\n ...eventOptions,\n detail: { warnings, model: this._model },\n }),\n );\n }\n\n #flushRendererWarnings(): void {\n if (this.#pendingRendererWarnings.length === 0) {\n return;\n }\n const warnings = this.#pendingRendererWarnings;\n this.#pendingRendererWarnings = [];\n this.#reportWarnings(warnings);\n }\n\n #setError(error: unknown): void {\n const detail: FellowshipTalentsErrorDetail =\n typeof error === \"object\" && error !== null && \"code\" in error && \"message\" in error\n ? {\n code: String(error.code),\n message: String(error.message),\n ...(error instanceof FellowshipDataError && error.cause !== undefined\n ? { cause: error.cause }\n : {}),\n }\n : { code: \"unknown\", message: error instanceof Error ? error.message : String(error) };\n this._error = detail;\n this._status = \"error\";\n this.dispatchEvent(\n new CustomEvent<FellowshipTalentsErrorDetail>(\"fellowship-talents-error\", {\n ...eventOptions,\n detail,\n }),\n );\n }\n}\n\nexport const defineFellowshipTalentCalculator = (): void => {\n defineFellowshipTooltip();\n if (!customElements.get(\"fellowship-talent-calculator\")) {\n customElements.define(\"fellowship-talent-calculator\", FellowshipTalentCalculatorElement);\n }\n};\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"fellowship-talent-calculator\": FellowshipTalentCalculatorElement;\n }\n}\n"],"mappings":";;;;;;;;;AAUA,IAAa,KACX,IAAoD,CAAC,MACtB;CAC/B,IAAM,IAAS,EAAkC,CAAO;CACxD,OAAO;EACL,kBAAkB,GAAQ,MAAW,EAAO,gBAAgB,GAAQ,CAAM;EAC1E,kBAAkB,MAAU,EAAO,gBAAgB,CAAK;CAC1D;AACF,GCLM,IAAgB;CAAC;CAAK;CAAM;CAAO;CAAM;CAAK;AAAI,GAE3C,KAAkB,MAAwB,EAAc,IAAM,MAAM,OAAO,CAAG,GAErF,KACJ,GACA,GACA,OAC6B;CAAE;CAAM;CAAS,UAAU,EAAO;AAAG,IAEvD,KACX,GACA,MACgC;CAChC,IAAM,IAAsC,CAAC,GACvC,oBAAW,IAAI,IAAY,GAC3B,IAAwC,CAAC;CAE/C,KAAK,IAAM,KAAU,EAAK,SAAS;EACjC,IAAI,EAAO,QAAQ,QAAQ,EAAO,WAAW,MAC3C;EAEF,IACE,CAAC,OAAO,UAAU,EAAO,GAAG,KAC5B,CAAC,OAAO,UAAU,EAAO,MAAM,KAC/B,EAAO,QAAQ,QACf,EAAO,WAAW,QAClB,EAAO,MAAM,KACb,EAAO,MAAM,KACb,EAAO,SAAS,KAChB,EAAO,SAAS,GAChB;GACA,EAAS,KACP,EACE,oBACA,GACA,WAAW,EAAO,GAAG,gDACvB,CACF;GACA;EACF;EAEA,IAAM,IAAO,GAAG,EAAO,IAAI,GAAG,EAAO;EACrC,IAAI,EAAS,IAAI,CAAI,GAAG;GACtB,EAAS,KACP,EACE,sBACA,GACA,WAAW,EAAO,GAAG,mBAAmB,EAAO,IAAI,WAAW,EAAO,OAAO,kBAC9E,CACF;GACA;EACF;EACA,EAAS,IAAI,CAAI;EAEjB,IAAM,IACJ,OAAO,UAAU,EAAO,SAAS,MAAM,EAAO,aAAa,KAAK,IAAI,EAAO,YAAY;EASzF,AARI,MAAc,QAChB,EAAS,KACP,EAAQ,gBAAgB,GAAQ,WAAW,EAAO,GAAG,6BAA6B,CACpF,GAEG,EAAO,QACV,EAAS,KAAK,EAAQ,gBAAgB,GAAQ,WAAW,EAAO,GAAG,uBAAuB,CAAC,GAExF,EAAO,eACV,EAAS,KACP,EAAQ,uBAAuB,GAAQ,WAAW,EAAO,GAAG,sBAAsB,CACpF;EAGF,IAAM,IACJ,EAAU,EAAO,QAAQ;GAAE,MAAM;GAAU,MAAM;EAAM,CAAC,KACxD,EAAU,EAAO,QAAQ;GAAE,MAAM;GAAU,MAAM;EAAO,CAAC,GACvD,IAA0B;EAC9B,IAAI,CAAC,GACH,EAAS,KAAK,EAAQ,iBAAiB,GAAQ,WAAW,EAAO,GAAG,eAAe,CAAC;OAEpF,IAAI;GACF,IAAW,EAAO,gBAAgB,CAAK;EACzC,SAAS,GAAgB;GACvB,EAAS,KACP,EACE,mBACA,GACA,4BAA4B,EAAO,GAAG,2BAA2B,OAAO,CAAK,EAAE,EACjF,CACF;EACF;EAGF,EAAO,KAAK;GACV;GACA,KAAK,EAAO;GACZ,QAAQ,EAAO;GACf;GACA;EACF,CAAC;CACH;CAEA,EAAO,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM;CAC1D,IAAM,IAAa,IAAI,IAAI,EAAO,KAAK,MAAU,CAAC,EAAM,OAAO,IAAI,CAAK,CAAC,CAAC,GACpE,IAAO,CAAC,GAAG,IAAI,IAAI,EAAO,KAAK,EAAE,aAAU,CAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAS;EACpE;EACA,OAAO,EAAe,CAAG;EACzB,SAAS,EAAO,QAAQ,MAAW,EAAO,QAAQ,CAAG;CACvD,EAAE;CAEF,OAAO;EAAE,MAAM,EAAK;EAAM;EAAM;EAAY;CAAS;AACvD,GCxHa,IAAoB,IAEpB,KAAyB,MACpC,KAAK,IAAA,IAEH,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,SAAS,CAAK,IAAI,IAAA,EAAyB,CAAC,CAC5E,GAEW,KACX,GACA,MAIG;CACH,IAAM,IAAY,IAAI,IAAI,CAAiB,GACrC,IAAW,CAAC,GAAG,EAAM,WAAW,OAAO,CAAC,CAAC,CAC5C,QAAQ,EAAE,gBAAa,EAAU,IAAI,EAAO,EAAE,CAAC,CAAC,CAChD,KAAK,EAAE,gBAAa,EAAO,EAAE,GAC1B,IAAc,IAAI,IAAI,CAAQ;CAEpC,OAAO;EAAE,mBAAmB;EAAU,kBADtB,CAAC,GAAG,IAAI,IAAI,CAAiB,CAAC,CAAC,CAAC,QAAQ,MAAO,CAAC,EAAY,IAAI,CAAE,CAC1B;CAAQ;AAClE,GAEa,KACX,GACA,GACA,MACmC;CACnC,IAAM,IAAW,IAAI,IAAI,CAAiB,GACpC,IAAc,CAAC,GAAG,EAAM,WAAW,OAAO,CAAC,CAAC,CAAC,QAChD,GAAO,MACN,EAAS,IAAI,EAAO,OAAO,EAAE,KAAK,EAAO,cAAc,OACnD,IAAQ,EAAO,YACf,GACN,CACF,GACM,IAAkB,IAAe;CACvC,OAAO;EACL,mBAAmB,CAAC,GAAG,EAAM,WAAW,OAAO,CAAC,CAAC,CAC9C,QAAQ,EAAE,gBAAa,EAAS,IAAI,EAAO,EAAE,CAAC,CAAC,CAC/C,KAAK,EAAE,gBAAa,EAAO,EAAE;EAChC;EACA;EACA,YAAY,IAAkB;CAChC;AACF,GAEa,KACX,GACA,MACY,MAAc,QAAQ,CAAC,EAAM,cAAc,KAAa,EAAM,iBCnD/D,IAAyB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GC4CnC,IAAe;CAAE,SAAS;CAAM,UAAU;AAAK,GAE/C,KACJ,GACA,MAEA,EAAM,WAAW,GAAU,UAAU,EAAM,MAAM,GAAM,MAAU,MAAS,EAAS,EAAM,GAE9E,KAAkB,MAA6B,UAAU,KAEhE,KAA0B,OAA0D;CACxF,mBAAmB,CAAC;CACpB,aAAa;CACb,iBAAiB;CACjB,YAAY;AACd,IAYO,IAAA,cAAgD,EAAW;;6BAGL,qDAGpB,CAAC,uBAC+C,wBACP,0BACP,+BACU,wBACX,mBAE5B,uBACmB,oBACD,2CAC7B,IAAI,IAAY,wBAChB,iCACQ,EAAA,EAAwC,8CAC1C,IAAI,IAAgC,+CACnC,IAAI,IAAqB,aAEtB,gBAC9B,aAC6B,gBACZ,8BACV,IAAI,IAAY,aACkB,CAAC;;;gBA1B9B,CAAC,GAAmB,CAAsB;;CAqBnE;CACA;CACA;CACA;CACA;CACA;CAEA,oBAAmC;EAEjC,AADA,MAAM,kBAAkB,GACxB,EAAwB,KAAK,aAAa;CAC5C;CAEA,uBAAsC;EAEpC,AADA,KAAKA,IAAkB,MAAM,GAC7B,MAAM,qBAAqB;CAC7B;CAEA,QAA2B,GAAqC;EAI9D,IAHA,MAAM,QAAQ,CAAO,GACrB,KAAKC,GAAuB,GAExB,EAAQ,IAAI,QAAQ,KAAK,EAAQ,IAAI,YAAY,KAAK,EAAQ,IAAI,cAAc,GAAG;GACrF,qBAAqB;IACnB,AAAI,KAAK,eACP,KAAUC,GAAiB;GAE/B,CAAC;GACD;EACF;EAEA,IAAI,EAAQ,IAAI,cAAc,GAAG;GAC/B,IAAM,IAAa,EAAsB,KAAK,YAAY;GAC1D,IAAI,MAAe,KAAK,cAAc;IAGpC,AAFA,KAAKC,KAA8B,IACnC,KAAK,eAAe,GACpB,KAAKC,GAAkB,eAAe;IACtC;GACF;GACA,AAAI,KAAKD,KACP,KAAKA,KAA8B,KAC1B,EAAQ,IAAI,cAAc,MAAM,KAAA,KACzC,KAAKC,GAAkB,UAAU;EAErC;EAMA,AAJI,EAAQ,IAAI,mBAAmB,KAAK,KAAK,UAC3C,KAAKC,GAAqB,EAAI,GAGhC,KAAKC,GAAkB;CACzB;CAEA,WAA8B,GAA+B;EAC3D,MAAM,WAAW,CAAO;EAExB,IAAM,IACJ,EAAQ,IAAI,QAAQ,KAAK,EAAQ,IAAI,mBAAmB,KAAK,EAAQ,IAAI,cAAc;EAczF,AAbI,MACF,KAAK,wBAAwB,KAAK,SAC9B,EACE,KAAK,QACL,KAAK,mBACL,KAAK,YACP,IACA,EAAuB,KAAK,YAAY,IAG1C,EAAQ,IAAI,cAAc,KAC5B,KAAKC,GAAkB,MAAM,IAE3B,KAAoB,EAAQ,IAAI,cAAc,MAChD,KAAKC,GAA4B,KAAK,qBAAqB;CAE/D;CAEA,IAAI,cAAkD;EACpD,OAAO,KAAK;CACd;CAEA,IAAI,iBAAiD;EACnD,OAAO,KAAK,SACR,EAAqC,KAAK,QAAQ,KAAK,mBAAmB,KAAK,YAAY,IAC3F,EAAuB,KAAK,YAAY;CAC9C;CAEA,QAAc;EACR,KAAK,kBAAkB,WAAW,MAGtC,KAAK,oBAAoB,CAAC,GAC1B,KAAKC,GAAqB,OAAO;CACnC;CAEA,aAAa,GAA2B;EACtC,IAAM,IAAQ,KAAK,QAAQ,WAAW,IAAI,CAAQ,GAC5C,IAAQ,KAAK;EAUnB,OARE,CAAC,KACD,EAAM,kBAAkB,SAAS,CAAQ,KACzC,CAAC,EAAgB,EAAM,WAAW,CAAK,IAEhC,MAET,KAAK,oBAAoB,CAAC,GAAG,EAAM,mBAAmB,CAAQ,GAC9D,KAAKA,GAAqB,KAAK,GACxB;CACT;CAEA,eAAe,GAA2B;EAMxC,OALK,KAAK,eAAe,kBAAkB,SAAS,CAAQ,KAG5D,KAAK,oBAAoB,KAAK,eAAe,kBAAkB,QAAQ,MAAO,MAAO,CAAQ,GAC7F,KAAKA,GAAqB,KAAK,GACxB,MAJE;CAKX;CAEA,QAAc;EACZ,KAAUP,GAAiB;CAC7B;CAEA,SAAkC;EAChC,IAAM,IAAQ,KAAK;EAEnB,OAAO,CAAI;;;;qBAIM,GALH,KAAK,QAAQ,KAAK,QAAQ,KAAK,QAAQ,KAAK,MAAM,kBAKtC,oBAAoB;uBAC3B,OAAO,EAAM,UAAU,EAAE;;UAEtC,KAAKQ,GAAc,CAAK,EAAE;UAE1B,KAAK,YAAY,WAAW,KAAK,SAC7B,KAAKC,GAAY,KAAK,MAAM,IAC5B,KAAKC,GAAc,EACxB;sEAC6D,KAAK,cAAc;;;CAGvF;CAEA,GAAc,GAAuD;EACnE,OAAO,CAAI;;;;;;;2BAOY,OAAO,EAAM,UAAU,EAAE;yBAC3B,GAAG,EAAM,YAAY,MAAM,KAAK,aAAa,sBAAsB;;;6DAG/B,EAAM,YAAY;;6DAElB,KAAK,aAAa;;YAGnE,KAAK,oBACD,CAAI;;;;;;;;;;;;;kCAac,KAAK,gBAAgB,EAAM,YAAY;qCACpC,KAAKC,GAAiB,KAAK,eAAe,GAAG,WAAW,EAAE;;;;;;;;;;kCAU7D,KAAK,gBAAA,GAAkC;qCACpC,KAAKA,GAAiB,KAAK,eAAe,GAAG,WAAW,EAAE;;;;;oBAM/E,EACL;;;;;wBAKa,EAAM,kBAAkB,WAAW,EAAE;qBACxC,KAAK,MAAM;;;;;;;CAO9B;CAEA,GAAY,GAAoD;EAe9D,OAdI,EAAM,KAAK,WAAW,IACjB,CAAI;;;;;;gBAMD,EAAM,KAAK,QAAQ,EAAM,KAAK,GAAG;;;;UAOtC,CAAI;;UAEL,EACA,EAAM,OACL,MAAQ,EAAI,MACZ,MAAQ,CAAI;;4DAEqC,EAAI,MAAM;4EACM,EAAI,MAAM;;kBAEpE,EACA,EAAI,UACH,MAAW,EAAO,OAAO,KACzB,MAAW,KAAKC,GAAc,CAAM,CACvC,EAAE;;;WAIV,EAAE;;;CAGR;CAEA,GAAc,GAAsE;EAClF,IAAM,IAAe,KAAK,oBAAoB,IAAI,EAAO,OAAO,EAAE;EAClE,IAAI,CAAC,GACH,OAAO;EAET,IAAM,EAAE,YAAS,SAAM,kBAAe,kBAAe,mBAAgB,GAC/D,EAAE,aAAU,gBAAa,GACzB,IAAO,CAAI;;;;;qBAKA,GAAG,EAAK,IAAI,IAAgB;2BACtB,EAAc;uBAClB,OAAO,CAAQ,EAAE;wBAChB,OAAO,CAAQ,EAAE;uBAClB,KAAKC,GAAwB,GAAQ,GAAU,CAAQ,EAAE;;UAEtE,KAAKC,GAAqB,CAAO,EAAE;uDACU,EAAO,aAAa,IAAI;;;EAI3E,OAAO,CAAI;QAEP,KAAK,iBAAiB,EAAO,OAAO,eAAe,KAC/C,CAAI;;2BAEW,EAAK;wBACR,EAAO,OAAO,eAAe,EAAc;;kBAEjD,EAAK;;gBAGX,EACL;yCACkC,EAAc,GAAG,EAAY;;CAEpE;CAEA,GAA4B,GAA6C;EACvE,IAAM,IAAW,KAAK,cAChB,IAAQ,KAAK,QACb,oBAAU,IAAI,IAAqB,GACnC,oBAAgB,IAAI,IAAgC;EAE1D,IAAI,GACF,KAAK,IAAM,KAAU,EAAM,WAAW,OAAO,GAAG;GAC9C,IAAM,IAAW,EAAM,kBAAkB,SAAS,EAAO,OAAO,EAAE,GAC5D,IAAa,EAAgB,EAAO,WAAW,CAAK,GACpD,IAAW,CAAC,KAAY,CAAC,GACzB,IAAO,EAAO,OAAO,QAAQ,EAAO,OAAO,IAC3C,IACJ,EAAO,cAAc,OACjB,yCACA,IACE,YAAY,EAAO,UAAU,kBAAkB,KAAK,IAAI,GAAG,EAAM,eAAe,EAAE,eAClF,GAAG,EAAO,UAAU,UAAU,EAAO,cAAc,IAAI,UAAU,SAAS,IAC5E,IAA+C;IACnD;IACA,MAAM,EAAM;IACZ;IACA;IACA;IACA,aAAa,EAAM;IACnB,iBAAiB,EAAM;IACvB,cAAc,KAAK;GACrB;GACA,MAAc,IAAI,EAAO,OAAO,IAAI;IAClC;IACA;IACA,eAAe,sBAAsB,EAAO,IAAI,GAAG,EAAO;IAC1D;IACA,aAAa,CAAC,EAAwB,EAAO,OAAO,eAAe,EAAE,GAAG,CAAa,CAAC,CACnF,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;GACb,CAAC,GACI,GAGL,IAAI;IACF,EAAQ,IAAI,EAAO,OAAO,IAAI,EAAS,CAAO,CAAC;GACjD,SAAS,GAAgB;IACvB,AAAK,KAAKT,GAAkB,IAAI,EAAO,OAAO,EAAE,MAC9C,KAAKA,GAAkB,IAAI,EAAO,OAAO,EAAE,GAC3C,KAAKU,GAAyB,KAAK;KACjC,MAAM;KACN,SAAS,mCAAmC,EAAO,OAAO,GAAG,KAAK,OAAO,CAAK,EAAE;KAChF,UAAU,EAAO,OAAO;IAC1B,CAAC;GAEL;EACF;EAIF,AADA,KAAK,sBAAsB,GAC3B,KAAK,uBAAuB;CAC9B;CAEA,GAAqB,GAAuD;EAC1E,IAAM,IAAS,EAAQ,QACjB,IAAO,EAAO,OAAO,QAAQ,EAAO,OAAO,IAC3C,IACJ,KAAK,gBAAgB,KAAK,qBAAqB,IAAI,EAAO,OAAO,EAAE,IAC/D,KAAK,qBAAqB,IAAI,EAAO,OAAO,EAAE,IAC9C,CAAI;yBACW,EAAe,EAAO,OAAO,EAAE,EAAE;mEACS,EAAK,GAAG,EAAK;;;EAI5E,OAAO,CAAI;QACP,KAAKC,GAAyB,GAAQ,CAAI,EAAE,GAAG,EAAQ;;CAE7D;CAEA,GAAyB,GAAqC,GAA8B;EAmB1F,OAlBI,EAAO,YAAY,CAAC,KAAK,cAAc,IAAI,EAAO,OAAO,EAAE,IACtD,CAAI;;;;gBAID,EAAO,SAAS;;;yBAGP,KAAKC,GAAkB,CAAM,EAAE;;UAU7C,CAAI,kEANE,EACV,MAAM,KAAK,CAAC,CACZ,KAAK,MAAS,EAAK,EAAE,CAAC,CACtB,KAAK,EAAE,CAAC,CACR,MAAM,GAAG,CAAC,CAAC,CACX,YAC0E,EAAK;CACpF;CAEA,KAAgC;EAC9B,IAAM,IAAU,KAAK,YAAY,WAC3B,IAAQ,KAAK,YAAY,SACzB,IAAQ,IACV,yBACA,IACE,kBACA,uBACA,IAAO,IACT,kDACA,IACE,4EACC,KAAK,QAAQ,WAAW;EAC/B,OAAO,CAAI;;;eAGA,KAAK,YAAY,UAAU,gBAAgB,SAAS;eACpD,KAAK,YAAY,UAAU,UAAU,SAAS;;;wDAGL,IAAU,MAAM,IAAI;oCACxC,EAAM;mCACP,EAAK;YAE5B,KAAK,YAAY,UACb,CAAI,qDAAqD,KAAK,MAAM,mBACpE,EACL;;;;CAIT;CAEA,MAAMjB,KAAkC;EACtC,IAAM,IAAS,KAAK,OAAO,KAAK,GAC1B,IAAQ,EAAE,KAAKkB;EASrB,IARA,KAAKpB,IAAkB,MAAM,GAC7B,KAAKA,KAAmB,IAAI,gBAAgB,GAC5C,KAAK,SAAS,MACd,KAAK,SAAS,MACd,KAAK,gCAAgB,IAAI,IAAI,GAC7B,KAAKO,GAAkB,MAAM,GAC7B,KAAKc,KAA2B,MAE5B,CAAC,GAAQ;GACX,KAAK,UAAU;GACf;EACF;EAEA,KAAK,UAAU;EACf,IAAM,IACJ,KAAK,cACL,EACE,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,IAAI,CAAC,CAC7D;EAEF,IAAI;GACF,IAAM,IAAO,MAAM,EAAO,gBAAgB,GAAQ,KAAKrB,GAAiB,MAAM;GAC9E,IAAI,MAAU,KAAKoB,MAAc,KAAKpB,GAAiB,OAAO,SAC5D;GAEF,IAAI,EAAK,SAAS,kBAAkB,GAClC,MAAM,IAAI,EACR,sBACA,+CAA+C,OAAO,EAAK,SAAS,aAAa,EAAE,EACrF;GAEF,IAAM,IAAQ,EAAkC,GAAM,CAAM;GAE5D,AADA,KAAK,SAAS,GACd,KAAK,UAAU;GACf,IAAM,IAAa,KAAKK,GAAqB,EAAK;GAClD,MAAM,KAAK;GACX,IAAM,IAAY,KAAK;GAavB,AAZA,KAAK,cACH,IAAI,YAA0C,4BAA4B;IACxE,GAAG;IACH,QAAQ;KAAE;KAAM;KAAO;IAAU;GACnC,CAAC,CACH,GACI,EAAM,SAAS,SAAS,KAC1B,KAAKiB,GAAgB,EAAM,QAAQ,GAEjC,KACF,KAAKb,GAAqB,eAAe,GAE3C,KAAKH,GAAkB;EACzB,SAAS,GAAgB;GACvB,IAAI,MAAU,KAAKc,MAAc,KAAKpB,GAAiB,OAAO,SAC5D;GAEF,KAAKuB,GAAU,CAAK;EACtB;CACF;CAEA,GAAqB,GAAwB;EAC3C,IAAI,CAAC,KAAK,QACR,OAAO;EAET,IAAM,IAAa,EAA2B,KAAK,QAAQ,KAAK,iBAAiB;EAoBjF,OAlBE,EAAW,kBAAkB,WAAW,KAAK,kBAAkB,UAC/D,EAAW,kBAAkB,MAAM,GAAI,MAAU,MAAO,KAAK,kBAAkB,EAAM,KAIvF,KAAK,oBAAoB,EAAW,mBAChC,EAAW,iBAAiB,SAAS,KACvC,KAAKD,GAAgB,CACnB;GACE,MAAM;GACN,SAAS,0CAA0C,EAAW,iBAAiB,KAAK,IAAI,EAAE;GAC1F,UAAU;EACZ,CACF,CAAC,GAEC,KACF,KAAKb,GAAqB,eAAe,GAEpC,MAfE;CAgBX;CAEA,GACE,GACA,GACA,GACM;EACN,IAAI,GAAU;GAIZ,AAHA,KAAK,oBAAoB,KAAK,eAAe,kBAAkB,QAC5D,MAAO,MAAO,EAAO,OAAO,EAC/B,GACA,KAAKA,GAAqB,MAAM;GAChC;EACF;EACA,IAAI,GAAU;GACZ,KAAK,gBAAgB,KAAK,eAAe,aACrC,oFACA,yBAAyB,EAAO,OAAO,QAAQ,EAAO,OAAO,GAAG;GACpE;EACF;EAEA,AADA,KAAK,oBAAoB,CAAC,GAAG,KAAK,eAAe,mBAAmB,EAAO,OAAO,EAAE,GACpF,KAAKA,GAAqB,MAAM;CAClC;CAEA,GAAiB,GAAe,GAAwC;EACtE,IAAM,IAAa,EAAsB,CAAK,GACxC,IAAQ,KAAK;EACf,MAAe,KAAK,gBAAgB,IAAa,EAAM,gBAG3D,KAAK,eAAe,GACpB,KAAKN,KAA8B,IACnC,KAAKC,GAAkB,CAAM;CAC/B;CAEA,GAAqB,GAA2C;EAC9D,IAAM,IAAQ,KAAK,SACf,EAAqC,KAAK,QAAQ,KAAK,mBAAmB,KAAK,YAAY,IAC3F,EAAuB,KAAK,YAAY;EAC5C,KAAK,cACH,IAAI,YAAyC,sCAAsC;GACjF,GAAG;GACH,QAAQ;IAAE,GAAG;IAAO;GAAO;EAC7B,CAAC,CACH;CACF;CAEA,GAAkB,GAAwC;EACxD,IAAM,IAAQ,KAAK;EACnB,KAAK,cACH,IAAI,YAAsC,mCAAmC;GAC3E,GAAG;GACH,QAAQ;IAAE,GAAG;IAAO,cAAc,KAAK;IAAc;GAAO;EAC9D,CAAC,CACH;CACF;CAEA,KAA0B;EACxB,IAAM,IAAQ,KAAK;EACnB,IAAI,CAAC,EAAM,YAAY;GAErB,AADA,KAAKiB,KAA2B,MAChC,KAAK,gBAAgB,cAAc;GACnC;EACF;EACA,KAAK,aAAa,gBAAgB,MAAM;EACxC,IAAM,IAAY,GAAG,KAAK,aAAa,GAAG,EAAM,kBAAkB,KAAK,GAAG;EACtE,MAAc,KAAKA,OAGvB,KAAKA,KAA2B,GAChC,KAAKC,GAAgB,CACnB;GACE,MAAM;GACN,SAAS,6BAA6B,EAAM,YAAY,yBAAyB,KAAK,aAAa;GACnG,UAAU;EACZ,CACF,CAAC;CACH;CAEA,GAAkB,GAA2C;EACvD,KAAK,cAAc,IAAI,EAAO,OAAO,EAAE,MAG3C,KAAK,gBAAgB,IAAI,IAAI,KAAK,aAAa,CAAC,CAAC,IAAI,EAAO,OAAO,EAAE,GACrE,KAAKA,GAAgB,CACnB;GACE,MAAM;GACN,SAAS,wBAAwB,EAAO,OAAO,GAAG;GAClD,UAAU,EAAO,OAAO;EAC1B,CACF,CAAC;CACH;CAEA,GAAgB,GAAoD;EAClE,KAAK,cACH,IAAI,YAA4C,8BAA8B;GAC5E,GAAG;GACH,QAAQ;IAAE;IAAU,OAAO,KAAK;GAAO;EACzC,CAAC,CACH;CACF;CAEA,KAA+B;EAC7B,IAAI,KAAKL,GAAyB,WAAW,GAC3C;EAEF,IAAM,IAAW,KAAKA;EAEtB,AADA,KAAKA,KAA2B,CAAC,GACjC,KAAKK,GAAgB,CAAQ;CAC/B;CAEA,GAAU,GAAsB;EAC9B,IAAM,IACJ,OAAO,KAAU,YAAY,KAAkB,UAAU,KAAS,aAAa,IAC3E;GACE,MAAM,OAAO,EAAM,IAAI;GACvB,SAAS,OAAO,EAAM,OAAO;GAC7B,GAAI,aAAiB,KAAuB,EAAM,UAAU,KAAA,IACxD,EAAE,OAAO,EAAM,MAAM,IACrB,CAAC;EACP,IACA;GAAE,MAAM;GAAW,SAAS,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK;EAAE;EAGzF,AAFA,KAAK,SAAS,GACd,KAAK,UAAU,SACf,KAAK,cACH,IAAI,YAA0C,4BAA4B;GACxE,GAAG;GACH;EACF,CAAC,CACH;CACF;AACF;GA5oBG,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAU,CAAC,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,MAC/C,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAgB,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MACrD,EAAS;CAAE,WAAW;CAAO,YAAY;AAAmB,CAAC,CAAA,GAAA,EAAA,WAAA,qBAAA,KAAA,CAAA,MAE7D,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAiB,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MACtD,EAAS,EAAE,WAAW,GAAM,CAAC,CAAA,GAAA,EAAA,WAAA,cAAA,KAAA,CAAA,MAC7B,EAAS,EAAE,WAAW,GAAM,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MAC7B,EAAS;CAAE,MAAM;CAAS,WAAW;AAAsB,CAAC,CAAA,GAAA,EAAA,WAAA,qBAAA,KAAA,CAAA,MAC5D,EAAS;CAAE,MAAM;CAAS,WAAW;AAAgB,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MAEtD,EAAM,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,iBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,iBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,yBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,uBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,wBAAA,KAAA,CAAA,UArBR,EAAc,8BAA8B,CAAA,GAAA,CAAA;AAkpB7C,IAAa,UAA+C;CAE1D,AADA,EAAwB,GACnB,eAAe,IAAI,8BAA8B,KACpD,eAAe,OAAO,gCAAgC,CAAiC;AAE3F"}
@@ -0,0 +1,5 @@
1
+ export type { DungeonCastData } from "@crockery/fellowship-data/client";
2
+ import type { FellowshipDataClient } from "@crockery/fellowship-data/client";
3
+ /** @deprecated Use the corresponding `FellowshipDataClient` methods. */
4
+ export type FellowshipCastDataSource = Pick<FellowshipDataClient, "loadDungeonCasts" | "resolveAssetUrl">;
5
+ //# sourceMappingURL=cast-data-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast-data-source.d.ts","sourceRoot":"","sources":["../../src/data/cast-data-source.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAExE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAE7E,wEAAwE;AACxE,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,oBAAoB,EACpB,kBAAkB,GAAG,iBAAiB,CACvC,CAAC"}