@crockery/fellowship-components 1.0.0 → 2.0.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.
package/README.md CHANGED
@@ -136,11 +136,13 @@ pnpm --filter @crockery/fellowship-components build
136
136
  pnpm --filter @crockery/fellowship-components pack:check
137
137
  ```
138
138
 
139
- Current Chromium, Firefox, and Safari/WebKit are supported. Add a Changeset with
140
- `pnpm changeset` from the monorepo root for every user-facing change. Merging
141
- the generated version pull request publishes through trusted OIDC publishing.
142
- The private repository means npm provenance attestations are unavailable.
143
- Maintainers should follow [`RELEASING.md`](RELEASING.md).
139
+ Browser tests currently use headless Chromium as the package's deterministic
140
+ browser harness. Cross-browser compatibility is not yet a supported contract.
141
+ Add a Changeset with `pnpm changeset` from the monorepo root for every
142
+ user-facing change. Merging the generated version pull request publishes
143
+ through trusted OIDC publishing. The private repository means npm provenance
144
+ attestations are unavailable. Maintainers should follow
145
+ [`RELEASING.md`](RELEASING.md).
144
146
 
145
147
  ## License
146
148
 
@@ -4,12 +4,66 @@ import { LitElement as i, css as a, html as o, nothing as s } from "lit";
4
4
  import { customElement as c, property as l, query as u, state as d } from "lit/decorators.js";
5
5
  import { repeat as f } from "lit/directives/repeat.js";
6
6
  import { findImage as p } from "@crockery/fellowship-data";
7
- import { calculateGearPlannerBuild as m, getValidGearConfiguration as h, getValidGearItems as g, normalizeGearPlannerBuild as _, validateGearPlannerBuild as v } from "@crockery/fellowship-data/gear-planner";
7
+ import { calculateGearPlannerBuild as m, getMaxGearPlannerAppliedTempers as h, getValidGearConfiguration as g, getValidGearItems as _, normalizeGearPlannerBuild as v, resolveGearPlannerItemImage as y, resolveGearPlannerItemName as b, validateGearPlannerBuild as x } from "@crockery/fellowship-data/gear-planner";
8
+ //#region src/gear-planner/rarity-thumbnail.styles.ts
9
+ var S = a`
10
+ [data-rarity="Common"] {
11
+ --_fellowship-rarity-start: var(--fellowship-rarity-common-start, #545456);
12
+ --_fellowship-rarity-end: var(--fellowship-rarity-common-end, #909291);
13
+ --_fellowship-rarity-border: var(--fellowship-rarity-common-border, #b8bab9);
14
+ }
15
+
16
+ [data-rarity="Uncommon"] {
17
+ --_fellowship-rarity-start: var(--fellowship-rarity-uncommon-start, #235824);
18
+ --_fellowship-rarity-end: var(--fellowship-rarity-uncommon-end, #509650);
19
+ --_fellowship-rarity-border: var(--fellowship-rarity-uncommon-border, #77bd74);
20
+ }
21
+
22
+ [data-rarity="Rare"] {
23
+ --_fellowship-rarity-start: var(--fellowship-rarity-rare-start, #253358);
24
+ --_fellowship-rarity-end: var(--fellowship-rarity-rare-end, #475b80);
25
+ --_fellowship-rarity-border: var(--fellowship-rarity-rare-border, #718bb7);
26
+ }
27
+
28
+ [data-rarity="Epic"] {
29
+ --_fellowship-rarity-start: var(--fellowship-rarity-epic-start, #402858);
30
+ --_fellowship-rarity-end: var(--fellowship-rarity-epic-end, #6b4b8e);
31
+ --_fellowship-rarity-border: var(--fellowship-rarity-epic-border, #9b72c3);
32
+ }
33
+
34
+ [data-rarity="Champion"] {
35
+ --_fellowship-rarity-start: var(--fellowship-rarity-champion-start, #8a6222);
36
+ --_fellowship-rarity-end: var(--fellowship-rarity-champion-end, #c49d3f);
37
+ --_fellowship-rarity-border: var(--fellowship-rarity-champion-border, #e7c96e);
38
+ }
39
+
40
+ [data-rarity="Heroic"] {
41
+ --_fellowship-rarity-start: var(--fellowship-rarity-heroic-start, #7b3538);
42
+ --_fellowship-rarity-end: var(--fellowship-rarity-heroic-end, #c64d42);
43
+ --_fellowship-rarity-border: var(--fellowship-rarity-heroic-border, #e37b6f);
44
+ }
45
+
46
+ [data-rarity="Legendary"] {
47
+ --_fellowship-rarity-start: var(--fellowship-rarity-legendary-start, #8f4a2f);
48
+ --_fellowship-rarity-end: var(--fellowship-rarity-legendary-end, #d07c44);
49
+ --_fellowship-rarity-border: var(--fellowship-rarity-legendary-border, #efa36c);
50
+ }
51
+
52
+ [data-rarity] {
53
+ border-color: var(--_fellowship-rarity-border);
54
+ background: linear-gradient(
55
+ 145deg,
56
+ var(--_fellowship-rarity-start),
57
+ var(--_fellowship-rarity-end)
58
+ );
59
+ }
60
+ `;
61
+ //#endregion
8
62
  //#region src/gear-planner/gear-position.ts
9
63
  r();
10
- var y = class extends i {
64
+ var C = class extends i {
11
65
  constructor(...e) {
12
- super(...e), this.position = null, this.item = null, this.itemBuild = null, this.selected = !1, this.imageUrl = null, this.tooltipDetails = [], this.#e = (e) => {
66
+ super(...e), this.position = null, this.item = null, this.itemBuild = null, this.itemName = null, this.selected = !1, this.imageUrl = null, this.tooltipDetails = [], this.#e = (e) => {
13
67
  this.dispatchEvent(new CustomEvent("fellowship-gear-position-select", {
14
68
  bubbles: !0,
15
69
  composed: !0,
@@ -23,7 +77,7 @@ var y = class extends i {
23
77
  };
24
78
  }
25
79
  static {
26
- this.styles = a`
80
+ this.styles = [a`
27
81
  * {
28
82
  box-sizing: border-box;
29
83
  }
@@ -140,18 +194,18 @@ var y = class extends i {
140
194
  transition: none;
141
195
  }
142
196
  }
143
- `;
197
+ `, S];
144
198
  }
145
199
  render() {
146
200
  if (!this.position) return o``;
147
- let e = this.item?.item.name ?? this.item?.item.id ?? "Choose item", t = this.itemBuild ? `${e}. Level ${this.itemBuild.itemLevel}, ${this.itemBuild.rarity}.` : `${this.position.label}. Empty.`, n = o`
201
+ let e = this.itemName ?? this.item?.item.name ?? this.item?.item.id ?? "Choose item", t = this.itemBuild ? `${e}. Level ${this.itemBuild.itemLevel}, ${this.itemBuild.rarity}.` : `${this.position.label}. Empty.`, n = o`
148
202
  <button
149
203
  type="button"
150
204
  aria-pressed=${String(this.selected)}
151
205
  aria-label=${`${this.position.label}: ${t}`}
152
206
  @click=${this.#e}
153
207
  >
154
- <span class="image">
208
+ <span class="image" data-rarity=${this.itemBuild?.rarity ?? s}>
155
209
  ${this.imageUrl ? o`<img src=${this.imageUrl} alt="" @error=${this.#t} />` : this.position.label.slice(0, 2)}
156
210
  </span>
157
211
  <span class="copy">
@@ -180,13 +234,13 @@ var y = class extends i {
180
234
  #e;
181
235
  #t;
182
236
  };
183
- e([l({ attribute: !1 })], y.prototype, "position", void 0), e([l({ attribute: !1 })], y.prototype, "item", void 0), e([l({ attribute: !1 })], y.prototype, "itemBuild", void 0), e([l({
237
+ e([l({ attribute: !1 })], C.prototype, "position", void 0), e([l({ attribute: !1 })], C.prototype, "item", void 0), e([l({ attribute: !1 })], C.prototype, "itemBuild", void 0), e([l({ type: String })], C.prototype, "itemName", void 0), e([l({
184
238
  type: Boolean,
185
239
  reflect: !0
186
- })], y.prototype, "selected", void 0), e([l({ type: String })], y.prototype, "imageUrl", void 0), e([l({ attribute: !1 })], y.prototype, "tooltipDetails", void 0), y = e([c("fellowship-gear-position")], y);
187
- var b = () => {
188
- customElements.get("fellowship-gear-position") || customElements.define("fellowship-gear-position", y);
189
- }, x = a`
240
+ })], C.prototype, "selected", void 0), e([l({ type: String })], C.prototype, "imageUrl", void 0), e([l({ attribute: !1 })], C.prototype, "tooltipDetails", void 0), C = e([c("fellowship-gear-position")], C);
241
+ var w = () => {
242
+ customElements.get("fellowship-gear-position") || customElements.define("fellowship-gear-position", C);
243
+ }, T = a`
190
244
  :host {
191
245
  display: block;
192
246
  min-width: 0;
@@ -366,6 +420,13 @@ var b = () => {
366
420
  display: block;
367
421
  }
368
422
 
423
+ .control-hint {
424
+ display: block;
425
+ margin-top: 3px;
426
+ font-size: 9px;
427
+ text-transform: uppercase;
428
+ }
429
+
369
430
  fieldset {
370
431
  min-width: 0;
371
432
  margin: 4px 0 0;
@@ -431,23 +492,31 @@ var b = () => {
431
492
  grid-row: auto;
432
493
  }
433
494
  }
434
- `, S = class extends i {
495
+ `, E = class extends i {
435
496
  constructor(...e) {
436
497
  super(...e), this.data = null, this.build = null, this.positionId = null, this.resolveAssetUrl = null, this._query = "", this.#g = (e) => {
437
498
  this._query = e.currentTarget.value;
438
499
  };
439
500
  }
440
501
  static {
441
- this.styles = [n, x];
502
+ this.styles = [
503
+ n,
504
+ T,
505
+ S
506
+ ];
442
507
  }
443
508
  render() {
444
509
  if (!this.data || !this.build || !this.positionId) return o`<div class="empty">Select a gear position to begin.</div>`;
445
510
  let e = this.data.positions.find(({ id: e }) => e === this.positionId), t = this.build.positions.find(({ positionId: e }) => e === this.positionId);
446
511
  if (!e) return o`<div class="empty">This gear position is unavailable.</div>`;
447
- let n = t?.item ?? null, r = n ? this.data.itemById.get(n.itemId) : null, i = n && r ? h(this.data, this.build, this.positionId, n.itemId) : null, a = g(this.data, this.build, this.positionId).filter((e) => {
448
- let t = this._query.trim().toLocaleLowerCase();
449
- return !t || (e.item.name ?? e.item.id).toLocaleLowerCase().includes(t);
450
- });
512
+ let n = t?.item ?? null, r = n ? this.data.itemById.get(n.itemId) : null, i = n && r ? g(this.data, this.build, this.positionId, n.itemId) : null, a = this.data.heroById.get(this.build.heroId), s = this._query.trim().toLocaleLowerCase(), c = a ? _(this.data, this.build, this.positionId).map((e) => {
513
+ let t = e.validRarities[0] ?? "Common";
514
+ return {
515
+ item: e,
516
+ name: b(e, a),
517
+ rarity: t
518
+ };
519
+ }).filter(({ name: e }) => !s || e.toLocaleLowerCase().includes(s)) : [];
451
520
  return o`
452
521
  <header class="workbench-header">
453
522
  <div>
@@ -467,20 +536,22 @@ var b = () => {
467
536
  />
468
537
  </label>
469
538
  <div class="item-list" aria-label=${`Valid items for ${e.label}`}>
470
- ${a.length === 0 ? o`<p class="empty-search">No valid items match this search.</p>` : f(a, (e) => e.item.id, (e) => o`
539
+ ${c.length === 0 ? o`<p class="empty-search">No valid items match this search.</p>` : f(c, (e) => e.item.item.id, (e) => o`
471
540
  <button
472
541
  type="button"
473
542
  class="item-choice"
474
- aria-pressed=${String(e.item.id === n?.itemId)}
475
- ?disabled=${e.validItemLevels.length === 0 || e.validRarities.length === 0}
476
- @click=${() => this.#l(e.item.id)}
543
+ aria-pressed=${String(e.item.item.id === n?.itemId)}
544
+ ?disabled=${e.item.validItemLevels.length === 0 || e.item.validRarities.length === 0}
545
+ @click=${() => this.#l(e.item.item.id)}
477
546
  >
478
547
  <span class="choice-mark" aria-hidden="true"></span>
479
- <span class="choice-image">
480
- ${this.#o(e.item.images, e.item.name ?? e.item.id)}
548
+ <span class="choice-image" data-rarity=${e.rarity}>
549
+ ${this.#o(e.item, a, e.rarity, e.name)}
481
550
  </span>
482
- <span>${e.item.name ?? e.item.id}</span>
483
- <small>${e.maxEquipped === 1 ? "Unique" : e.itemType}</small>
551
+ <span>${e.name}</span>
552
+ <small>
553
+ ${e.item.maxEquipped === 1 ? "Unique" : e.item.itemType}
554
+ </small>
484
555
  </button>
485
556
  `)}
486
557
  </div>
@@ -488,7 +559,7 @@ var b = () => {
488
559
  `;
489
560
  }
490
561
  #e(e, t) {
491
- let n = t.slots.filter((e) => e.rollGroupId === null);
562
+ let n = t.slots.filter((e) => e.rollGroupId === null), r = h(e.itemLevel, e.rarity);
492
563
  return o`
493
564
  <section class="configuration" aria-label="Item configuration">
494
565
  ${this.#a(t)}
@@ -505,6 +576,20 @@ var b = () => {
505
576
  ${f(t.item.validItemLevels, (e) => e, (e) => o`<option value=${e}>${e}</option>`)}
506
577
  </select>
507
578
  </label>
579
+ <label>
580
+ <span>Applied tempers</span>
581
+ <input
582
+ type="number"
583
+ min="0"
584
+ max=${r}
585
+ .value=${String(e.appliedTempers)}
586
+ @change=${(t) => this.#h({
587
+ ...e,
588
+ appliedTempers: Math.max(0, Math.min(r, Math.round(Number(t.currentTarget.value))))
589
+ }, "tempers")}
590
+ />
591
+ <small class="control-hint">Maximum ${r}</small>
592
+ </label>
508
593
  <label>
509
594
  <span>Rarity</span>
510
595
  <select
@@ -640,21 +725,18 @@ var b = () => {
640
725
  </aside>
641
726
  `;
642
727
  }
643
- #o(e, t) {
644
- let n = p(e, {
645
- size: "200",
646
- type: "hero-item"
647
- });
648
- if (!n || !this.resolveAssetUrl) return t.slice(0, 2);
649
- let r;
728
+ #o(e, t, n, r) {
729
+ let i = y(e, t, n, "200");
730
+ if (!i || !this.resolveAssetUrl) return r.slice(0, 2);
731
+ let a;
650
732
  try {
651
- r = this.resolveAssetUrl(n);
733
+ a = this.resolveAssetUrl(i);
652
734
  } catch {
653
- return t.slice(0, 2);
735
+ return r.slice(0, 2);
654
736
  }
655
737
  return o`
656
738
  <img
657
- src=${r}
739
+ src=${a}
658
740
  alt=""
659
741
  @error=${(e) => {
660
742
  e.currentTarget.hidden = !0;
@@ -680,6 +762,7 @@ var b = () => {
680
762
  itemId: e,
681
763
  itemLevel: n,
682
764
  rarity: r,
765
+ appliedTempers: 0,
683
766
  rolledModifiers: [],
684
767
  gems: [],
685
768
  traits: [],
@@ -768,10 +851,10 @@ var b = () => {
768
851
  }
769
852
  #g;
770
853
  };
771
- e([l({ attribute: !1 })], S.prototype, "data", void 0), e([l({ attribute: !1 })], S.prototype, "build", void 0), e([l({ type: String })], S.prototype, "positionId", void 0), e([l({ attribute: !1 })], S.prototype, "resolveAssetUrl", void 0), e([d()], S.prototype, "_query", void 0), S = e([c("fellowship-item-workbench")], S);
772
- var C = () => {
773
- customElements.get("fellowship-item-workbench") || customElements.define("fellowship-item-workbench", S);
774
- }, w = a`
854
+ e([l({ attribute: !1 })], E.prototype, "data", void 0), e([l({ attribute: !1 })], E.prototype, "build", void 0), e([l({ type: String })], E.prototype, "positionId", void 0), e([l({ attribute: !1 })], E.prototype, "resolveAssetUrl", void 0), e([d()], E.prototype, "_query", void 0), E = e([c("fellowship-item-workbench")], E);
855
+ var D = () => {
856
+ customElements.get("fellowship-item-workbench") || customElements.define("fellowship-item-workbench", E);
857
+ }, O = a`
775
858
  :host {
776
859
  display: block;
777
860
  min-width: 0;
@@ -813,6 +896,34 @@ var C = () => {
813
896
  text-align: left;
814
897
  }
815
898
 
899
+ .talent-points > span {
900
+ display: block;
901
+ margin-bottom: 4px;
902
+ font-size: 10px;
903
+ font-weight: 500;
904
+ letter-spacing: 0.08em;
905
+ text-transform: uppercase;
906
+ }
907
+
908
+ .talent-points {
909
+ grid-column: 1 / -1;
910
+ }
911
+
912
+ .talent-points input {
913
+ width: 100%;
914
+ min-height: 44px;
915
+ padding-inline: 10px;
916
+ border: 2px solid var(--_fellowship-poster-plum);
917
+ border-radius: var(--_fellowship-poster-radius);
918
+ background: var(--_fellowship-poster-white);
919
+ color: var(--_fellowship-poster-plum);
920
+ }
921
+
922
+ .talent-points input:focus-visible {
923
+ outline: 4px solid var(--_fellowship-poster-focus);
924
+ outline-offset: 2px;
925
+ }
926
+
816
927
  .hero-image {
817
928
  display: grid;
818
929
  place-items: center;
@@ -1061,10 +1172,14 @@ var C = () => {
1061
1172
  }
1062
1173
 
1063
1174
  .hero {
1064
- grid-template-columns: 92px minmax(0, max-content);
1175
+ grid-template-columns: 92px minmax(0, max-content) minmax(150px, 180px);
1065
1176
  margin-bottom: 20px;
1066
1177
  }
1067
1178
 
1179
+ .talent-points {
1180
+ grid-column: auto;
1181
+ }
1182
+
1068
1183
  .hero-image {
1069
1184
  width: 92px;
1070
1185
  height: 92px;
@@ -1081,7 +1196,7 @@ var C = () => {
1081
1196
  minmax(142px, 0.64fr)
1082
1197
  minmax(390px, 1.9fr)
1083
1198
  minmax(142px, 0.64fr)
1084
- minmax(190px, 0.86fr);
1199
+ minmax(220px, 1fr);
1085
1200
  align-items: start;
1086
1201
  gap: 12px;
1087
1202
  }
@@ -1199,6 +1314,10 @@ var C = () => {
1199
1314
  margin-bottom: 10px;
1200
1315
  }
1201
1316
 
1317
+ .talent-points {
1318
+ grid-column: 1 / -1;
1319
+ }
1320
+
1202
1321
  .hero-image {
1203
1322
  width: 52px;
1204
1323
  height: 52px;
@@ -1231,39 +1350,30 @@ var C = () => {
1231
1350
  animation-iteration-count: 1 !important;
1232
1351
  }
1233
1352
  }
1234
- `, T = [
1235
- "head",
1236
- "shoulders",
1237
- "chest",
1238
- "hands",
1239
- "waist",
1240
- "ring-1",
1241
- "trinket-1"
1242
- ], E = [
1243
- "necklace",
1244
- "back",
1245
- "wrists",
1246
- "feet",
1247
- "ring-2",
1248
- "trinket-2",
1249
- "weapon"
1250
- ], D = "button:not([disabled]), input:not([disabled]), select:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])", O = (e) => {
1353
+ `, k = "button:not([disabled]), input:not([disabled]), select:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])", A = (e) => {
1251
1354
  let t = [];
1252
- for (let n of e.children) n instanceof HTMLElement && n.matches(D) && t.push(n), n.shadowRoot && t.push(...O(n.shadowRoot)), t.push(...O(n));
1355
+ for (let n of e.children) n instanceof HTMLElement && n.matches(k) && t.push(n), n.shadowRoot && t.push(...A(n.shadowRoot)), t.push(...A(n));
1253
1356
  return t;
1254
- }, k = (e) => {
1357
+ }, j = (e) => {
1255
1358
  let t = e.activeElement;
1256
1359
  for (; t?.shadowRoot?.activeElement;) t = t.shadowRoot.activeElement;
1257
1360
  return t;
1258
1361
  };
1259
- b(), C();
1260
- var A = class extends i {
1362
+ w(), D();
1363
+ var M = class extends i {
1261
1364
  constructor(...e) {
1262
- super(...e), this.data = null, this.build = null, this.loading = !1, this.error = null, this.resolveAssetUrl = null, this._selectedPositionId = null, this._tab = "gear", this._sheetOpen = !1, this._compact = !1, this._announcement = "", this._calculation = null, this.#e = null, this.#t = null, this.#n = null, this.#r = null, this.#i = null, this.#a = !1, this.#o = /* @__PURE__ */ new Set(), this.#p = (e) => {
1365
+ super(...e), this.data = null, this.build = null, this.loading = !1, this.error = null, this.resolveAssetUrl = null, this._selectedPositionId = null, this._tab = "gear", this._sheetOpen = !1, this._compact = !1, this._announcement = "", this._calculation = null, this._leftPositionIds = [], this._rightPositionIds = [], this._mobilePositionIds = [], this.#e = null, this.#t = null, this.#n = null, this.#r = null, this.#i = null, this.#a = !1, this.#o = /* @__PURE__ */ new Set(), this.#m = (e) => {
1366
+ if (!this.data || !this.build) return;
1367
+ let t = Math.max(0, Math.min(this.data.maxAllocatedTalentPoints, Math.round(Number(e.currentTarget.value))));
1368
+ this.#v({
1369
+ ...this.build,
1370
+ allocatedTalentPoints: t
1371
+ }, "talent-points", null);
1372
+ }, this.#h = (e) => {
1263
1373
  e.stopPropagation(), this._selectedPositionId = e.detail.positionId, this.#i = e.detail.trigger, this._compact && (this._sheetOpen = !0, this.#r !== null && window.clearTimeout(this.#r), this.#r = window.setTimeout(() => {
1264
1374
  this.#r = null, this.shadowRoot?.querySelector(".workbench-panel")?.focus({ preventScroll: !0 });
1265
1375
  }, 0));
1266
- }, this.#m = (e) => {
1376
+ }, this.#g = (e) => {
1267
1377
  if (e.stopPropagation(), !this.data || !this.build || !this._selectedPositionId) return;
1268
1378
  let t = {
1269
1379
  ...this.build,
@@ -1271,10 +1381,10 @@ var A = class extends i {
1271
1381
  ...t,
1272
1382
  item: e.detail.item
1273
1383
  } : t)
1274
- }, n = _(this.data, t), r = v(this.data, n.build);
1384
+ }, n = v(this.data, t), r = x(this.data, n.build);
1275
1385
  if (!r.valid || !r.build) {
1276
1386
  let e = r.issues.find(({ severity: e }) => e === "error");
1277
- this.#_({
1387
+ this.#y({
1278
1388
  code: e?.code ?? "invalid-change",
1279
1389
  message: e?.message ?? "That item choice is not valid.",
1280
1390
  recoverable: !0
@@ -1282,29 +1392,29 @@ var A = class extends i {
1282
1392
  return;
1283
1393
  }
1284
1394
  let i = e.detail.item.rolledModifiers.length + e.detail.item.gems.length + e.detail.item.traits.length + e.detail.item.blessings.length - (r.build.positions.find(({ positionId: e }) => e === this._selectedPositionId)?.item?.rolledModifiers.length ?? 0) - (r.build.positions.find(({ positionId: e }) => e === this._selectedPositionId)?.item?.gems.length ?? 0) - (r.build.positions.find(({ positionId: e }) => e === this._selectedPositionId)?.item?.traits.length ?? 0) - (r.build.positions.find(({ positionId: e }) => e === this._selectedPositionId)?.item?.blessings.length ?? 0);
1285
- this._announcement = i > 0 ? `${i} incompatible item choice${i === 1 ? " was" : "s were"} reset.` : "Build updated.", this.#g(r.build, e.detail.source, this._selectedPositionId);
1286
- }, this.#v = () => {
1395
+ this._announcement = i > 0 ? `${i} incompatible item choice${i === 1 ? " was" : "s were"} reset.` : "Build updated.", this.#v(r.build, e.detail.source, this._selectedPositionId);
1396
+ }, this.#b = () => {
1287
1397
  this._sheetOpen = !1, this.updateComplete.then(() => {
1288
1398
  this.#n !== null && window.cancelAnimationFrame(this.#n), this.#n = window.requestAnimationFrame(() => {
1289
1399
  this.#n = null, this.#i?.focus({ preventScroll: !0 });
1290
1400
  });
1291
1401
  });
1292
- }, this.#y = (e) => {
1293
- e.key !== "Escape" || !this._compact || !this._sheetOpen || (e.preventDefault(), this.#v());
1294
- }, this.#b = (e) => {
1402
+ }, this.#x = (e) => {
1403
+ e.key !== "Escape" || !this._compact || !this._sheetOpen || (e.preventDefault(), this.#b());
1404
+ }, this.#S = (e) => {
1295
1405
  if (!this._compact || !this._sheetOpen) return;
1296
1406
  if (e.key === "Escape") {
1297
- e.preventDefault(), this.#v();
1407
+ e.preventDefault(), this.#b();
1298
1408
  return;
1299
1409
  }
1300
1410
  if (e.key !== "Tab" || !this._workbenchPanel) return;
1301
- let t = O(this._workbenchPanel), n = t[0], r = t.at(-1), i = k(this.shadowRoot);
1411
+ let t = A(this._workbenchPanel), n = t[0], r = t.at(-1), i = j(this.shadowRoot);
1302
1412
  if (!n || !r) {
1303
1413
  e.preventDefault();
1304
1414
  return;
1305
1415
  }
1306
1416
  e.shiftKey && (i === n || i === this._workbenchPanel) ? (e.preventDefault(), r.focus()) : !e.shiftKey && i === r && (e.preventDefault(), n.focus());
1307
- }, this.#x = (e) => {
1417
+ }, this.#C = (e) => {
1308
1418
  if (!["ArrowLeft", "ArrowRight"].includes(e.key)) return;
1309
1419
  e.preventDefault();
1310
1420
  let t = [
@@ -1315,12 +1425,12 @@ var A = class extends i {
1315
1425
  this._tab = t[(r + n + t.length) % t.length] ?? "gear", this.updateComplete.then(() => {
1316
1426
  (this.shadowRoot?.querySelector(".tab[aria-selected=\"true\"]"))?.focus();
1317
1427
  });
1318
- }, this.#C = (e) => {
1428
+ }, this.#T = (e) => {
1319
1429
  e.currentTarget.hidden = !0;
1320
1430
  };
1321
1431
  }
1322
1432
  static {
1323
- this.styles = [n, w];
1433
+ this.styles = [n, O];
1324
1434
  }
1325
1435
  #e;
1326
1436
  #t;
@@ -1335,19 +1445,23 @@ var A = class extends i {
1335
1445
  this.#t !== null && window.cancelAnimationFrame(this.#t), this.#t = window.requestAnimationFrame(() => {
1336
1446
  this.#t = null, e !== this._compact && (this._compact = e, e || (this._sheetOpen = !1));
1337
1447
  });
1338
- }), this.#e.observe(this), this.ownerDocument.addEventListener("keydown", this.#y);
1448
+ }), this.#e.observe(this), this.ownerDocument.addEventListener("keydown", this.#x);
1339
1449
  }
1340
1450
  disconnectedCallback() {
1341
- this.#e?.disconnect(), this.ownerDocument.removeEventListener("keydown", this.#y), this.#t !== null && (window.cancelAnimationFrame(this.#t), this.#t = null), this.#n !== null && (window.cancelAnimationFrame(this.#n), this.#n = null), this.#r !== null && (window.clearTimeout(this.#r), this.#r = null), super.disconnectedCallback();
1451
+ this.#e?.disconnect(), this.ownerDocument.removeEventListener("keydown", this.#x), this.#t !== null && (window.cancelAnimationFrame(this.#t), this.#t = null), this.#n !== null && (window.cancelAnimationFrame(this.#n), this.#n = null), this.#r !== null && (window.clearTimeout(this.#r), this.#r = null), super.disconnectedCallback();
1342
1452
  }
1343
1453
  willUpdate(e) {
1344
- super.willUpdate(e), (e.has("data") || e.has("build")) && (this._calculation = this.data && this.build ? m(this.data, this.build) : null, this.data && this._selectedPositionId && !this.data.positions.some(({ id: e }) => e === this._selectedPositionId) && (this._selectedPositionId = null));
1454
+ if (super.willUpdate(e), e.has("data") || e.has("build")) {
1455
+ this._calculation = this.data && this.build ? m(this.data, this.build) : null, this.data && this._selectedPositionId && !this.data.positions.some(({ id: e }) => e === this._selectedPositionId) && (this._selectedPositionId = null);
1456
+ let e = [...this.data?.positions ?? []].sort((e, t) => e.order - t.order);
1457
+ this._leftPositionIds = e.slice(0, 7).map(({ id: e }) => e), this._rightPositionIds = e.slice(7).map(({ id: e }) => e), this._mobilePositionIds = e.map(({ id: e }) => e);
1458
+ }
1345
1459
  }
1346
1460
  updated(e) {
1347
1461
  if (super.updated(e), e.has("data") || e.has("build") || e.has("resolveAssetUrl")) {
1348
1462
  (e.has("data") || e.has("resolveAssetUrl")) && this.#o.clear();
1349
1463
  let t = !!(this.data && this.build && !this.resolveAssetUrl);
1350
- t && !this.#a ? (this.#a = !0, this.#_({
1464
+ t && !this.#a ? (this.#a = !0, this.#y({
1351
1465
  code: "asset-resolver-missing",
1352
1466
  message: "A resolveAssetUrl callback is required to render Fellowship images.",
1353
1467
  recoverable: !0
@@ -1390,18 +1504,28 @@ var A = class extends i {
1390
1504
  let t = p(e.hero.images, {
1391
1505
  size: "200",
1392
1506
  type: "hero"
1393
- }), n = t ? this.#S(t) : null;
1507
+ }), n = t ? this.#w(t) : null;
1394
1508
  return o`
1395
1509
  <div class="container">
1396
1510
  <section class="planner" aria-label="Fellership Gear Planner">
1397
1511
  <header class="hero">
1398
1512
  <span class="hero-image">
1399
- ${n ? o`<img src=${n} alt="" @error=${this.#C} />` : (e.hero.name ?? e.hero.id).slice(0, 2)}
1513
+ ${n ? o`<img src=${n} alt="" @error=${this.#T} />` : (e.hero.name ?? e.hero.id).slice(0, 2)}
1400
1514
  </span>
1401
1515
  <span>
1402
1516
  <span class="hero-label">Current hero</span>
1403
1517
  <h1>${e.hero.name ?? e.hero.id}</h1>
1404
1518
  </span>
1519
+ <label class="talent-points">
1520
+ <span>Allocated talent points</span>
1521
+ <input
1522
+ type="number"
1523
+ min="0"
1524
+ max=${this.data.maxAllocatedTalentPoints}
1525
+ .value=${String(this.build.allocatedTalentPoints)}
1526
+ @change=${this.#m}
1527
+ />
1528
+ </label>
1405
1529
  </header>
1406
1530
 
1407
1531
  ${this.#s()}
@@ -1419,11 +1543,11 @@ var A = class extends i {
1419
1543
  >
1420
1544
  <div class="slot-grid">
1421
1545
  <div class="slot-rail left-rail">
1422
- ${this.#c(this._compact ? this.data.positions.map(({ id: e }) => e) : T)}
1546
+ ${this.#c(this._compact ? this._mobilePositionIds : this._leftPositionIds)}
1423
1547
  </div>
1424
1548
  ${this._compact ? s : o`
1425
1549
  <div class="slot-rail right-rail">
1426
- ${this.#c(E)}
1550
+ ${this.#c(this._rightPositionIds)}
1427
1551
  </div>
1428
1552
  `}
1429
1553
  </div>
@@ -1445,11 +1569,11 @@ var A = class extends i {
1445
1569
  #s() {
1446
1570
  return o`
1447
1571
  <div class="tabs" role="tablist" aria-label="Planner workspace">
1448
- ${[
1572
+ ${f([
1449
1573
  ["traits", "Traits & gems"],
1450
1574
  ["gear", "Gear"],
1451
1575
  ["bonuses", "Bonuses"]
1452
- ].map(([e, t]) => o`
1576
+ ], ([e]) => e, ([e, t]) => o`
1453
1577
  <button
1454
1578
  class="tab"
1455
1579
  type="button"
@@ -1459,7 +1583,7 @@ var A = class extends i {
1459
1583
  @click=${() => {
1460
1584
  this._tab = e;
1461
1585
  }}
1462
- @keydown=${this.#x}
1586
+ @keydown=${this.#C}
1463
1587
  >
1464
1588
  ${t}
1465
1589
  </button>
@@ -1468,21 +1592,21 @@ var A = class extends i {
1468
1592
  `;
1469
1593
  }
1470
1594
  #c(e) {
1471
- return !this.data || !this.build ? o`` : o`
1595
+ if (!this.data || !this.build) return o``;
1596
+ let t = this.data.heroById.get(this.build.heroId) ?? null;
1597
+ return o`
1472
1598
  ${f(e, (e) => e, (e) => {
1473
- let t = this.data?.positions.find(({ id: t }) => t === e) ?? null, n = this.build?.positions.find((t) => t.positionId === e) ?? null, r = n?.item ? this.data?.itemById.get(n.item.itemId) ?? null : null, i = r ? p(r.item.images, {
1474
- size: "200",
1475
- type: "hero-item"
1476
- }) : null;
1599
+ let n = this.data?.positions.find(({ id: t }) => t === e) ?? null, r = this.build?.positions.find((t) => t.positionId === e) ?? null, i = r?.item ? this.data?.itemById.get(r.item.itemId) ?? null : null, a = i && t && r?.item ? y(i, t, r.item.rarity, "200") : null;
1477
1600
  return o`
1478
1601
  <fellowship-gear-position
1479
- .position=${t}
1480
- .item=${r}
1481
- .itemBuild=${n?.item ?? null}
1602
+ .position=${n}
1603
+ .item=${i}
1604
+ .itemBuild=${r?.item ?? null}
1605
+ .itemName=${i && t ? b(i, t) : null}
1482
1606
  .selected=${e === this._selectedPositionId}
1483
- .imageUrl=${i ? this.#S(i) : null}
1484
- .tooltipDetails=${this.#u(e, n?.item ?? null)}
1485
- @fellowship-gear-position-select=${this.#p}
1607
+ .imageUrl=${a ? this.#w(a) : null}
1608
+ .tooltipDetails=${this.#u(e, r?.item ?? null)}
1609
+ @fellowship-gear-position-select=${this.#h}
1486
1610
  ></fellowship-gear-position>
1487
1611
  `;
1488
1612
  })}
@@ -1497,12 +1621,12 @@ var A = class extends i {
1497
1621
  aria-modal=${this._compact ? "true" : s}
1498
1622
  aria-label="Item workbench"
1499
1623
  tabindex=${this._compact ? "-1" : s}
1500
- @keydown=${this.#b}
1501
- @fellowship-workbench-change=${this.#m}
1624
+ @keydown=${this.#S}
1625
+ @fellowship-workbench-change=${this.#g}
1502
1626
  >
1503
1627
  <header class="sheet-header">
1504
1628
  <strong>Configure item</strong>
1505
- <button class="sheet-close" type="button" @click=${this.#v}>Close</button>
1629
+ <button class="sheet-close" type="button" @click=${this.#b}>Close</button>
1506
1630
  </header>
1507
1631
  <fellowship-item-workbench
1508
1632
  .data=${this.data}
@@ -1553,7 +1677,7 @@ var A = class extends i {
1553
1677
  <h2>Traits & gems</h2>
1554
1678
  <h3>Active traits</h3>
1555
1679
  <ul class="summary-list">
1556
- ${e.size === 0 ? o`<li class="summary-row"><span>No traits selected</span></li>` : [...e].map(([e, t]) => o`
1680
+ ${e.size === 0 ? o`<li class="summary-row"><span>No traits selected</span></li>` : f([...e], ([e]) => e, ([e, t]) => o`
1557
1681
  <li class="summary-row">
1558
1682
  <span>${this.data?.traitById.get(e)?.name ?? e}</span>
1559
1683
  <strong>R${t}</strong>
@@ -1562,7 +1686,7 @@ var A = class extends i {
1562
1686
  </ul>
1563
1687
  <h3>Socket gems</h3>
1564
1688
  <ul class="summary-list">
1565
- ${t.size === 0 ? o`<li class="summary-row"><span>No gems socketed</span></li>` : [...t].map(([e, t]) => o`
1689
+ ${t.size === 0 ? o`<li class="summary-row"><span>No gems socketed</span></li>` : f([...t], ([e]) => e, ([e, t]) => o`
1566
1690
  <li class="summary-row">
1567
1691
  <span>${e} × ${t.count}</span>
1568
1692
  <strong>${t.power}</strong>
@@ -1582,10 +1706,10 @@ var A = class extends i {
1582
1706
  }
1583
1707
  return o`
1584
1708
  <span class="section-label">Build output</span>
1585
- <h2>Bonuses & totals</h2>
1709
+ <h2>Bonuses & stats</h2>
1586
1710
  <h3>Blessings</h3>
1587
1711
  <ul class="summary-list">
1588
- ${e.size === 0 ? o`<li class="summary-row"><span>No blessings selected</span></li>` : [...e].map(([e, t]) => o`
1712
+ ${e.size === 0 ? o`<li class="summary-row"><span>No blessings selected</span></li>` : f([...e], ([e]) => e, ([e, t]) => o`
1589
1713
  <li class="summary-row">
1590
1714
  <span>${this.data?.blessingById.get(e)?.name ?? e}</span>
1591
1715
  <strong>R${t}</strong>
@@ -1594,7 +1718,7 @@ var A = class extends i {
1594
1718
  </ul>
1595
1719
  <h3>Set bonuses</h3>
1596
1720
  <ul class="summary-list">
1597
- ${t.size === 0 ? o`<li class="summary-row"><span>No set pieces equipped</span></li>` : [...t].map(([e, t]) => {
1721
+ ${t.size === 0 ? o`<li class="summary-row"><span>No set pieces equipped</span></li>` : f([...t], ([e]) => e, ([e, t]) => {
1598
1722
  let n = this.data?.setBonuses.find(({ id: t }) => t === e);
1599
1723
  return o`
1600
1724
  <li class="summary-row">
@@ -1604,20 +1728,29 @@ var A = class extends i {
1604
1728
  `;
1605
1729
  })}
1606
1730
  </ul>
1607
- <h3>Exact totals</h3>
1608
- <ul class="breakdown">
1609
- ${Object.keys(this._calculation.finalTotals).length === 0 ? o`<li class="total-row"><span>No exact totals yet</span></li>` : Object.entries(this._calculation.finalTotals).map(([e, t]) => o`
1610
- <li class="total-row"><span>${e}</span><strong>${t}</strong></li>
1611
- `)}
1612
- </ul>
1731
+ <div class="hero-sheet" aria-label="Hero stats">
1732
+ ${f(this._calculation.heroSheet.sections, (e) => e.id, (e) => o`
1733
+ <section class="hero-sheet-section">
1734
+ <h3>${e.label}</h3>
1735
+ <ul class="breakdown">
1736
+ ${f(e.rows, (e) => e.id, (e) => o`
1737
+ <li class="total-row">
1738
+ <span>${e.label}</span>
1739
+ <strong>${this.#p(e)}</strong>
1740
+ </li>
1741
+ `)}
1742
+ </ul>
1743
+ </section>
1744
+ `)}
1745
+ </div>
1613
1746
  ${this._calculation.conditionalMechanics.length > 0 ? o`
1614
1747
  <h3>Conditional</h3>
1615
- ${this._calculation.conditionalMechanics.map((e) => o`
1748
+ ${f(this._calculation.conditionalMechanics, (e) => e.id, (e) => o`
1616
1749
  <label class="condition">
1617
1750
  <input
1618
1751
  type="checkbox"
1619
1752
  .checked=${e.applied}
1620
- @change=${() => this.#h(e.id)}
1753
+ @change=${() => this.#_(e.id)}
1621
1754
  />
1622
1755
  <span>${e.sourceName} · Apply</span>
1623
1756
  </label>
@@ -1625,7 +1758,7 @@ var A = class extends i {
1625
1758
  ` : s}
1626
1759
  <h3>Unsupported</h3>
1627
1760
  <ul class="breakdown">
1628
- ${this._calculation.unsupportedContributions.length === 0 ? o`<li class="unsupported-row"><span>None</span></li>` : this._calculation.unsupportedContributions.map((e) => o`
1761
+ ${this._calculation.unsupportedContributions.length === 0 ? o`<li class="unsupported-row"><span>None</span></li>` : f(this._calculation.unsupportedContributions, (e) => e.id, (e) => o`
1629
1762
  <li class="unsupported-row">
1630
1763
  <span>${e.sourceName}</span>
1631
1764
  <span class="unsupported-code">${e.reasonCode.replaceAll("-", " ")}</span>
@@ -1634,17 +1767,25 @@ var A = class extends i {
1634
1767
  </ul>
1635
1768
  `;
1636
1769
  }
1637
- #p;
1770
+ #p(e) {
1771
+ return new Intl.NumberFormat("en-US", {
1772
+ style: e.displayKind === "percentage" ? "percent" : "decimal",
1773
+ minimumFractionDigits: e.decimals,
1774
+ maximumFractionDigits: e.decimals
1775
+ }).format(e.value);
1776
+ }
1638
1777
  #m;
1639
- #h(e) {
1778
+ #h;
1779
+ #g;
1780
+ #_(e) {
1640
1781
  if (!this.build) return;
1641
1782
  let t = new Set(this.build.disabledConditionalContributionIds);
1642
- t.has(e) ? t.delete(e) : t.add(e), this.#g({
1783
+ t.has(e) ? t.delete(e) : t.add(e), this.#v({
1643
1784
  ...this.build,
1644
1785
  disabledConditionalContributionIds: [...t].sort()
1645
1786
  }, "conditional", null);
1646
1787
  }
1647
- #g(e, t, n) {
1788
+ #v(e, t, n) {
1648
1789
  this.dispatchEvent(new CustomEvent("fellowship-gear-build-change", {
1649
1790
  bubbles: !0,
1650
1791
  composed: !0,
@@ -1655,7 +1796,7 @@ var A = class extends i {
1655
1796
  }
1656
1797
  }));
1657
1798
  }
1658
- #_(e) {
1799
+ #y(e) {
1659
1800
  this.dispatchEvent(new CustomEvent("fellowship-gear-planner-error", {
1660
1801
  bubbles: !0,
1661
1802
  composed: !0,
@@ -1665,17 +1806,17 @@ var A = class extends i {
1665
1806
  }
1666
1807
  }));
1667
1808
  }
1668
- #v;
1669
- #y;
1670
1809
  #b;
1671
1810
  #x;
1672
- #S(e) {
1811
+ #S;
1812
+ #C;
1813
+ #w(e) {
1673
1814
  if (!this.resolveAssetUrl) return null;
1674
1815
  try {
1675
1816
  return this.resolveAssetUrl(e);
1676
1817
  } catch (t) {
1677
1818
  return this.#o.has(e.path) || (this.#o.add(e.path), queueMicrotask(() => {
1678
- this.isConnected && this.#_({
1819
+ this.isConnected && this.#y({
1679
1820
  code: "asset-url-error",
1680
1821
  message: `Could not resolve ${e.path}: ${String(t)}.`,
1681
1822
  recoverable: !0
@@ -1683,14 +1824,14 @@ var A = class extends i {
1683
1824
  })), null;
1684
1825
  }
1685
1826
  }
1686
- #C;
1827
+ #T;
1687
1828
  };
1688
- e([l({ attribute: !1 })], A.prototype, "data", void 0), e([l({ attribute: !1 })], A.prototype, "build", void 0), e([l({
1829
+ e([l({ attribute: !1 })], M.prototype, "data", void 0), e([l({ attribute: !1 })], M.prototype, "build", void 0), e([l({
1689
1830
  type: Boolean,
1690
1831
  reflect: !0
1691
- })], A.prototype, "loading", void 0), e([l({ attribute: !1 })], A.prototype, "error", void 0), e([l({ attribute: !1 })], A.prototype, "resolveAssetUrl", void 0), e([d()], A.prototype, "_selectedPositionId", void 0), e([d()], A.prototype, "_tab", void 0), e([d()], A.prototype, "_sheetOpen", void 0), e([d()], A.prototype, "_compact", void 0), e([d()], A.prototype, "_announcement", void 0), e([d()], A.prototype, "_calculation", void 0), e([u(".workbench-panel")], A.prototype, "_workbenchPanel", void 0), A = e([c("fellowship-gear-planner")], A);
1692
- var j = () => {
1693
- customElements.get("fellowship-gear-planner") || customElements.define("fellowship-gear-planner", A);
1832
+ })], M.prototype, "loading", void 0), e([l({ attribute: !1 })], M.prototype, "error", void 0), e([l({ attribute: !1 })], M.prototype, "resolveAssetUrl", void 0), e([d()], M.prototype, "_selectedPositionId", void 0), e([d()], M.prototype, "_tab", void 0), e([d()], M.prototype, "_sheetOpen", void 0), e([d()], M.prototype, "_compact", void 0), e([d()], M.prototype, "_announcement", void 0), e([d()], M.prototype, "_calculation", void 0), e([u(".workbench-panel")], M.prototype, "_workbenchPanel", void 0), M = e([c("fellowship-gear-planner")], M);
1833
+ var N = () => {
1834
+ customElements.get("fellowship-gear-planner") || customElements.define("fellowship-gear-planner", M);
1694
1835
  };
1695
1836
  //#endregion
1696
- export { j as n, A as t };
1837
+ export { N as n, M as t };
@@ -21,6 +21,9 @@ export declare class FellowshipGearPlannerElement extends LitElement {
21
21
  private _compact;
22
22
  private _announcement;
23
23
  private _calculation;
24
+ private _leftPositionIds;
25
+ private _rightPositionIds;
26
+ private _mobilePositionIds;
24
27
  private _workbenchPanel?;
25
28
  connectedCallback(): void;
26
29
  disconnectedCallback(): void;
@@ -2,10 +2,11 @@ import type { GearPlannerItem, GearPlannerItemBuild, GearPlannerPosition } from
2
2
  import { LitElement, type TemplateResult } from "lit";
3
3
  export declare class FellowshipGearPositionElement extends LitElement {
4
4
  #private;
5
- static styles: import("lit").CSSResult;
5
+ static styles: import("lit").CSSResult[];
6
6
  position: GearPlannerPosition | null;
7
7
  item: GearPlannerItem | null;
8
8
  itemBuild: GearPlannerItemBuild | null;
9
+ itemName: string | null;
9
10
  selected: boolean;
10
11
  imageUrl: string | null;
11
12
  tooltipDetails: readonly string[];
@@ -0,0 +1 @@
1
+ export declare const rarityThumbnailStyles: import("lit").CSSResult;
@@ -4,7 +4,7 @@ export interface GearPlannerPresentationError {
4
4
  readonly message: string;
5
5
  readonly recoverable?: boolean;
6
6
  }
7
- export type GearPlannerChangeSource = "item" | "item-level" | "rarity" | "modifier" | "gem" | "trait" | "blessing" | "conditional";
7
+ export type GearPlannerChangeSource = "item" | "item-level" | "rarity" | "tempers" | "talent-points" | "modifier" | "gem" | "trait" | "blessing" | "conditional";
8
8
  export interface GearPlannerBuildChangeDetail {
9
9
  readonly build: GearPlannerBuild;
10
10
  readonly source: GearPlannerChangeSource;
@@ -1,4 +1,4 @@
1
- import { n as e, t } from "./chunks/gear-planner-Bgt77Ovf.js";
1
+ import { n as e, t } from "./chunks/gear-planner-C-ZTX8S2.js";
2
2
  //#region src/gear-planner/index.ts
3
3
  e();
4
4
  //#endregion
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import "./dungeon-cast-browser.js";
7
7
  import { n as C, o as w, r as T, s as E, t as D } from "./chunks/tooltip-C3SSJw-t.js";
8
8
  import "./talent-calculator.js";
9
9
  import "./tooltip.js";
10
- import { n as O, t as k } from "./chunks/gear-planner-Bgt77Ovf.js";
10
+ import { n as O, t as k } from "./chunks/gear-planner-C-ZTX8S2.js";
11
11
  import "./gear-planner.js";
12
12
  import { createPackageFellowshipDataClient as A } from "@crockery/fellowship-data/client";
13
13
  export { e as FellowshipDataError, d as FellowshipDungeonCastBrowserElement, o as FellowshipDungeonMapElement, k as FellowshipGearPlannerElement, x as FellowshipTalentCalculatorElement, D as FellowshipTooltipElement, h as MAX_TALENT_POINTS, a as ViewportController, T as calculateTooltipPosition, f as canSelectTalent, u as castIdentityKey, c as createDungeonCastBrowserModel, n as createDungeonMapRenderModel, s as createPackageFellowshipCastDataSource, A as createPackageFellowshipDataClient, i as createPackageFellowshipDataSource, m as createPackageFellowshipTalentDataSource, g as createTalentCalculatorRenderModel, v as createTalentCalculatorSelectionState, l as defineFellowshipDungeonCastBrowser, r as defineFellowshipDungeonMap, O as defineFellowshipGearPlanner, _ as defineFellowshipTalentCalculator, C as defineFellowshipTooltip, t as humanizeMapLabel, b as normalizeSelectedTalentIds, p as normalizeTalentPoints, w as resolveFellowshipRichText, E as stripFellowshipRichText, S as talentRowLabel, y as talentSlotName };
@@ -1,4 +1,4 @@
1
- import { n as e, t } from "../chunks/gear-planner-Bgt77Ovf.js";
1
+ import { n as e, t } from "../chunks/gear-planner-C-ZTX8S2.js";
2
2
  import { createComponent as n } from "@lit/react";
3
3
  import r, { forwardRef as i } from "react";
4
4
  import { jsx as a } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crockery/fellowship-components",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Lit web components for rendering Fellowship game data.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -112,7 +112,7 @@
112
112
  "dependencies": {
113
113
  "@lit/react": "^1.0.8",
114
114
  "lit": "^3.3.3",
115
- "@crockery/fellowship-data": "^3.0.0"
115
+ "@crockery/fellowship-data": "^4.0.0"
116
116
  },
117
117
  "peerDependencies": {
118
118
  "react": ">=18 <20",
@@ -155,8 +155,6 @@
155
155
  "test": "pnpm test:unit && pnpm test:browser",
156
156
  "test:unit": "vitest run --project unit",
157
157
  "test:browser": "vitest run --project browser",
158
- "test:browser:smoke": "FELLOWSHIP_TEST_BROWSERS=smoke vitest run --project browser -t '\\[smoke\\]'",
159
- "test:browser:all": "FELLOWSHIP_TEST_BROWSERS=all vitest run --project browser",
160
158
  "ladle": "ladle serve",
161
159
  "ladle:build": "ladle build",
162
160
  "package:lint": "publint --strict --pack npm && attw --pack . --profile esm-only",
@@ -37,7 +37,7 @@ At 1200px and above, the component renders summary and slot rails around a
37
37
  persistent workbench. Below 1200px it uses Traits & gems, Gear, and Bonuses tabs,
38
38
  with configuration in a full-height focus-contained editor. Item choices are
39
39
  filtered by the Data package, all changes are immediate, and unsupported
40
- mechanics remain visibly excluded from exact totals.
40
+ mechanics remain visibly separated from the generated in-game hero sheet.
41
41
 
42
42
  The planner uses the public `--fellowship-poster-*` Dungeon Poster tokens and
43
43
  bundled Syne/DM Mono faces. It maintains 44px controls, visible focus, reduced