@crockery/fellowship-components 0.1.0 → 0.4.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 +10 -3
- package/RELEASING.md +9 -45
- package/dist/chunks/data-source-XbksqUrw.js +9 -0
- package/dist/chunks/data-source-XbksqUrw.js.map +1 -0
- package/dist/chunks/decorate-B_ZTN9li.js.map +1 -1
- package/dist/chunks/dungeon-cast-browser-CSnu3gC5.js +705 -0
- package/dist/chunks/dungeon-cast-browser-CSnu3gC5.js.map +1 -0
- package/dist/chunks/{dungeon-map-IPN6sEDj.js → dungeon-map-CutIcCVP.js} +93 -119
- package/dist/chunks/dungeon-map-CutIcCVP.js.map +1 -0
- package/dist/chunks/{talent-calculator-Ch1Pbbba.js → talent-calculator-Dxuc2dZ8.js} +75 -105
- package/dist/chunks/talent-calculator-Dxuc2dZ8.js.map +1 -0
- package/dist/data/cast-data-source.d.ts +5 -0
- package/dist/data/cast-data-source.d.ts.map +1 -0
- package/dist/data/data-source.d.ts +7 -16
- package/dist/data/data-source.d.ts.map +1 -1
- package/dist/data/package-cast-data-source.d.ts +8 -0
- package/dist/data/package-cast-data-source.d.ts.map +1 -0
- package/dist/data/package-data-source.d.ts +3 -1
- package/dist/data/package-data-source.d.ts.map +1 -1
- package/dist/data/package-talent-data-source.d.ts +2 -0
- package/dist/data/package-talent-data-source.d.ts.map +1 -1
- package/dist/data/talent-data-source.d.ts +4 -10
- package/dist/data/talent-data-source.d.ts.map +1 -1
- package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts +36 -0
- package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts.map +1 -0
- package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts +2 -0
- package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts.map +1 -0
- package/dist/dungeon-cast-browser/index.d.ts +4 -0
- package/dist/dungeon-cast-browser/index.d.ts.map +1 -0
- package/dist/dungeon-cast-browser/render-model.d.ts +5 -0
- package/dist/dungeon-cast-browser/render-model.d.ts.map +1 -0
- package/dist/dungeon-cast-browser/types.d.ts +60 -0
- package/dist/dungeon-cast-browser/types.d.ts.map +1 -0
- package/dist/dungeon-cast-browser.js +7 -0
- package/dist/dungeon-cast-browser.js.map +1 -0
- package/dist/dungeon-map/dungeon-map.d.ts.map +1 -1
- package/dist/dungeon-map.js +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -5
- package/dist/react/dungeon-cast-browser.d.ts +12 -0
- package/dist/react/dungeon-cast-browser.d.ts.map +1 -0
- package/dist/react/dungeon-cast-browser.js +19 -0
- package/dist/react/dungeon-cast-browser.js.map +1 -0
- package/dist/react/dungeon-map.js +1 -1
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/talent-calculator.js +1 -1
- package/dist/react.js +4 -3
- package/dist/talent-calculator/talent-calculator.d.ts.map +1 -1
- package/dist/talent-calculator.js +1 -1
- package/package.json +35 -25
- package/src/dungeon-cast-browser/README.md +22 -0
- package/dist/chunks/data-source-CoIFvV72.js +0 -10
- package/dist/chunks/data-source-CoIFvV72.js.map +0 -1
- package/dist/chunks/dungeon-map-IPN6sEDj.js.map +0 -1
- package/dist/chunks/talent-calculator-Ch1Pbbba.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crockery/fellowship-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Lit web components for rendering Fellowship game data.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"web-components",
|
|
16
16
|
"lit",
|
|
17
17
|
"react",
|
|
18
|
+
"cast-browser",
|
|
18
19
|
"dungeon-map",
|
|
19
20
|
"talent-calculator",
|
|
20
21
|
"tooltip"
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
"type": "module",
|
|
24
25
|
"sideEffects": [
|
|
25
26
|
"./dist/index.js",
|
|
27
|
+
"./dist/dungeon-cast-browser.js",
|
|
26
28
|
"./dist/dungeon-map.js",
|
|
27
29
|
"./dist/talent-calculator.js",
|
|
28
30
|
"./dist/tooltip.js",
|
|
@@ -43,6 +45,11 @@
|
|
|
43
45
|
"import": "./dist/dungeon-map.js",
|
|
44
46
|
"default": "./dist/dungeon-map.js"
|
|
45
47
|
},
|
|
48
|
+
"./dungeon-cast-browser": {
|
|
49
|
+
"types": "./dist/dungeon-cast-browser/index.d.ts",
|
|
50
|
+
"import": "./dist/dungeon-cast-browser.js",
|
|
51
|
+
"default": "./dist/dungeon-cast-browser.js"
|
|
52
|
+
},
|
|
46
53
|
"./talent-calculator": {
|
|
47
54
|
"types": "./dist/talent-calculator/index.d.ts",
|
|
48
55
|
"import": "./dist/talent-calculator.js",
|
|
@@ -63,6 +70,11 @@
|
|
|
63
70
|
"import": "./dist/react/dungeon-map.js",
|
|
64
71
|
"default": "./dist/react/dungeon-map.js"
|
|
65
72
|
},
|
|
73
|
+
"./react/dungeon-cast-browser": {
|
|
74
|
+
"types": "./dist/react/dungeon-cast-browser.d.ts",
|
|
75
|
+
"import": "./dist/react/dungeon-cast-browser.js",
|
|
76
|
+
"default": "./dist/react/dungeon-cast-browser.js"
|
|
77
|
+
},
|
|
66
78
|
"./react/talent-calculator": {
|
|
67
79
|
"types": "./dist/react/talent-calculator.d.ts",
|
|
68
80
|
"import": "./dist/react/talent-calculator.js",
|
|
@@ -89,29 +101,8 @@
|
|
|
89
101
|
"engines": {
|
|
90
102
|
"node": "^20.19.0 || >=22.12.0"
|
|
91
103
|
},
|
|
92
|
-
"packageManager": "pnpm@11.3.0",
|
|
93
|
-
"scripts": {
|
|
94
|
-
"build": "vite build && tsc -p tsconfig.build.json",
|
|
95
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
96
|
-
"lint": "biome lint .",
|
|
97
|
-
"format": "biome format --write .",
|
|
98
|
-
"format:check": "biome format .",
|
|
99
|
-
"test": "pnpm test:unit && pnpm test:browser",
|
|
100
|
-
"test:unit": "vitest run --project unit",
|
|
101
|
-
"test:browser": "vitest run --project browser",
|
|
102
|
-
"test:browser:smoke": "FELLOWSHIP_TEST_BROWSERS=smoke vitest run --project browser -t '\\[smoke\\]'",
|
|
103
|
-
"test:browser:all": "FELLOWSHIP_TEST_BROWSERS=all vitest run --project browser",
|
|
104
|
-
"ladle": "ladle serve",
|
|
105
|
-
"ladle:build": "ladle build",
|
|
106
|
-
"package:lint": "publint --strict --pack npm && attw --pack . --profile esm-only",
|
|
107
|
-
"pack:check": "pnpm build && node scripts/check-build.mjs && pnpm package:lint && pnpm pack --dry-run",
|
|
108
|
-
"prepublishOnly": "pnpm build && node scripts/check-build.mjs && pnpm package:lint",
|
|
109
|
-
"changeset": "changeset",
|
|
110
|
-
"version-packages": "changeset version",
|
|
111
|
-
"release": "changeset publish"
|
|
112
|
-
},
|
|
113
104
|
"dependencies": {
|
|
114
|
-
"@crockery/fellowship-data": "^1.
|
|
105
|
+
"@crockery/fellowship-data": "^1.3.0",
|
|
115
106
|
"@lit/react": "^1.0.8",
|
|
116
107
|
"lit": "^3.3.3"
|
|
117
108
|
},
|
|
@@ -128,11 +119,11 @@
|
|
|
128
119
|
}
|
|
129
120
|
},
|
|
130
121
|
"devDependencies": {
|
|
122
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
131
123
|
"@biomejs/biome": "^2.5.3",
|
|
132
124
|
"@changesets/cli": "^2.31.0",
|
|
133
125
|
"@fontsource/ibm-plex-mono": "5.2.7",
|
|
134
126
|
"@fontsource/ibm-plex-sans-condensed": "5.2.8",
|
|
135
|
-
"@arethetypeswrong/cli": "^0.18.5",
|
|
136
127
|
"@ladle/react": "^5.1.1",
|
|
137
128
|
"@types/react": "^18.3.24",
|
|
138
129
|
"@types/react-dom": "^18.3.7",
|
|
@@ -145,5 +136,24 @@
|
|
|
145
136
|
"typescript": "^5.9.3",
|
|
146
137
|
"vite": "^8.1.4",
|
|
147
138
|
"vitest": "^4.1.10"
|
|
139
|
+
},
|
|
140
|
+
"scripts": {
|
|
141
|
+
"build": "vite build && tsc -p tsconfig.build.json",
|
|
142
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
143
|
+
"lint": "biome lint .",
|
|
144
|
+
"format": "biome format --write .",
|
|
145
|
+
"format:check": "biome format .",
|
|
146
|
+
"test": "pnpm test:unit && pnpm test:browser",
|
|
147
|
+
"test:unit": "vitest run --project unit",
|
|
148
|
+
"test:browser": "vitest run --project browser",
|
|
149
|
+
"test:browser:smoke": "FELLOWSHIP_TEST_BROWSERS=smoke vitest run --project browser -t '\\[smoke\\]'",
|
|
150
|
+
"test:browser:all": "FELLOWSHIP_TEST_BROWSERS=all vitest run --project browser",
|
|
151
|
+
"ladle": "ladle serve",
|
|
152
|
+
"ladle:build": "ladle build",
|
|
153
|
+
"package:lint": "publint --strict --pack npm && attw --pack . --profile esm-only",
|
|
154
|
+
"pack:check": "pnpm build && node scripts/check-build.mjs && pnpm package:lint && pnpm pack --dry-run",
|
|
155
|
+
"changeset": "changeset",
|
|
156
|
+
"version-packages": "changeset version",
|
|
157
|
+
"release": "changeset publish"
|
|
148
158
|
}
|
|
149
|
-
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Dungeon Cast Browser
|
|
2
|
+
|
|
3
|
+
`<fellowship-dungeon-cast-browser>` joins a Fellowship dungeon’s unique enemies with their
|
|
4
|
+
cast-bearing abilities. Enemies without an actionable Interrupt or Stop response are omitted;
|
|
5
|
+
non-actionable casts remain visible beneath otherwise eligible enemies.
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<fellowship-dungeon-cast-browser dungeon-id="Level.ID.DemonicRitual">
|
|
9
|
+
</fellowship-dungeon-cast-browser>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The component emits composed `fellowship-cast-browser-ready`,
|
|
13
|
+
`fellowship-cast-browser-error`, and `fellowship-cast-selected` events. Selection is controlled:
|
|
14
|
+
set `selectedCast` after receiving a selection event. Use `configuredCasts` to mark rules that
|
|
15
|
+
already exist, and replace `dataSource` to provide game data from another host.
|
|
16
|
+
|
|
17
|
+
Set `configuredFilter` to `"configured"` or `"unconfigured"` to narrow the visible cast rows;
|
|
18
|
+
the default is `"all"`. Set `fill` when the browser should occupy a constrained-height workspace
|
|
19
|
+
and scroll its cast list independently. Both properties are available through the React wrapper.
|
|
20
|
+
|
|
21
|
+
CSS parts include `toolbar`, `enemy-list`, `enemy`, `enemy-header`, `cast`, `cast-icon`, and
|
|
22
|
+
`cast-icon-fallback`.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
//#region src/data/data-source.ts
|
|
2
|
-
var e = class extends Error {
|
|
3
|
-
constructor(e, t, n) {
|
|
4
|
-
super(t), this.name = "FellowshipDataError", this.code = e, this.cause = n;
|
|
5
|
-
}
|
|
6
|
-
};
|
|
7
|
-
//#endregion
|
|
8
|
-
export { e as t };
|
|
9
|
-
|
|
10
|
-
//# sourceMappingURL=data-source-CoIFvV72.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-source-CoIFvV72.js","names":[],"sources":["../../src/data/data-source.ts"],"sourcesContent":["import type {\n Dungeon,\n Enemy,\n FellowshipDataManifest,\n ImageAssetRef,\n} from \"@crockery/fellowship-data\";\n\nexport interface DungeonMapData {\n readonly manifest: FellowshipDataManifest;\n readonly dungeon: Dungeon;\n readonly enemies: ReadonlyMap<string, Enemy>;\n}\n\nexport interface FellowshipDataSource {\n loadDungeon(dungeonId: string, signal?: AbortSignal): Promise<DungeonMapData>;\n resolveAssetUrl(asset: ImageAssetRef): string;\n}\n\nexport type FellowshipDataErrorCode =\n | \"aborted\"\n | \"dungeon-not-found\"\n | \"hero-not-found\"\n | \"unsupported-schema\"\n | \"data-load-failed\";\n\nexport class FellowshipDataError extends Error {\n readonly code: FellowshipDataErrorCode;\n override readonly cause?: unknown;\n\n constructor(code: FellowshipDataErrorCode, message: string, cause?: unknown) {\n super(message);\n this.name = \"FellowshipDataError\";\n this.code = code;\n this.cause = cause;\n }\n}\n"],"mappings":";AAyBA,IAAa,IAAb,cAAyC,MAAM;CAI7C,YAAY,GAA+B,GAAiB,GAAiB;EAI3E,AAHA,MAAM,CAAO,GACb,KAAK,OAAO,uBACZ,KAAK,OAAO,GACZ,KAAK,QAAQ;CACf;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dungeon-map-IPN6sEDj.js","names":["#zoom","#x","#y","#viewportWidth","#viewportHeight","#mapWidth","#mapHeight","#clampPan","#selectMap","#viewport","#commitViewport","#isInteractiveEvent","#pointers","#lastPointer","#lastPinchDistance","#lastPinchCenter","#resizeObserver","#handleResize","#abortController","#flushRendererErrors","#loadDungeon","#prepareSelectedMap","#rendererFailures","#prepareRendererContent","#prepareMapLabels","#selectedMapTabId","#viewerLabel","#handleWheel","#handleViewportKeydown","#handlePointerDown","#handlePointerMove","#handlePointerEnd","#mapLabel","#renderGroups","#renderMapSelector","#renderControls","#renderStatus","#loadToken","#renderToken","#activeSource","#setError","#drawMap","#queueRendererError","#renderEnemyAnchor","#groupKey","#renderGroup","#renderDefaultGroup","#renderDefaultEnemy","#handleMapSelectorKeydown","#pendingRendererErrors"],"sources":["../../src/data/package-data-source.ts","../../src/dungeon-map/dungeon-map.styles.ts","../../src/dungeon-map/layout.ts","../../src/dungeon-map/render-model.ts","../../src/dungeon-map/viewport-controller.ts","../../src/dungeon-map/dungeon-map.ts"],"sourcesContent":["import { getAssetUrl } from \"@crockery/fellowship-data\";\nimport type {\n Dungeon,\n Enemy,\n FellowshipDataManifest,\n ImageAssetRef,\n} from \"@crockery/fellowship-data\";\n\nimport {\n FellowshipDataError,\n type DungeonMapData,\n type FellowshipDataSource,\n} from \"./data-source.js\";\n\nexport interface PackageFellowshipDataSourceOptions {\n readonly assetBaseUrl?: string;\n}\n\ninterface PackageDataIndex {\n readonly manifest: FellowshipDataManifest;\n readonly dungeons: ReadonlyMap<string, Dungeon>;\n readonly enemies: ReadonlyMap<string, Enemy>;\n}\n\nlet packageDataPromise: Promise<PackageDataIndex> | undefined;\n\nconst throwIfAborted = (signal?: AbortSignal): void => {\n if (signal?.aborted) {\n throw new FellowshipDataError(\n \"aborted\",\n \"The dungeon data request was aborted.\",\n signal.reason,\n );\n }\n};\n\nconst loadPackageData = (): Promise<PackageDataIndex> => {\n packageDataPromise ??= Promise.all([\n import(\"@crockery/fellowship-data/dungeons\"),\n import(\"@crockery/fellowship-data/enemies\"),\n import(\"@crockery/fellowship-data/manifest\"),\n ])\n .then(([dungeonModule, enemyModule, manifestModule]) => {\n const manifest = manifestModule.default;\n\n if ((manifest as { schemaVersion: number }).schemaVersion !== 1) {\n throw new FellowshipDataError(\n \"unsupported-schema\",\n `Unsupported fellowship-data schema version: ${String(manifest.schemaVersion)}.`,\n );\n }\n\n return {\n manifest,\n dungeons: new Map(dungeonModule.default.map((dungeon) => [dungeon.id, dungeon])),\n enemies: new Map(enemyModule.default.map((enemy) => [enemy.id, enemy])),\n };\n })\n .catch((error: unknown) => {\n packageDataPromise = undefined;\n if (error instanceof FellowshipDataError) {\n throw error;\n }\n throw new FellowshipDataError(\n \"data-load-failed\",\n \"Unable to load @crockery/fellowship-data.\",\n error,\n );\n });\n\n return packageDataPromise;\n};\n\nexport const createPackageFellowshipDataSource = (\n options: PackageFellowshipDataSourceOptions = {},\n): FellowshipDataSource => ({\n async loadDungeon(dungeonId: string, signal?: AbortSignal): Promise<DungeonMapData> {\n throwIfAborted(signal);\n const data = await loadPackageData();\n throwIfAborted(signal);\n\n const dungeon = data.dungeons.get(dungeonId);\n if (!dungeon) {\n throw new FellowshipDataError(\n \"dungeon-not-found\",\n `No dungeon with ID “${dungeonId}” exists in @crockery/fellowship-data.`,\n );\n }\n\n return {\n manifest: data.manifest,\n dungeon,\n enemies: data.enemies,\n };\n },\n\n resolveAssetUrl(asset: ImageAssetRef): string {\n return options.assetBaseUrl\n ? getAssetUrl(asset, { baseUrl: options.assetBaseUrl })\n : getAssetUrl(asset);\n },\n});\n","import { css } from \"lit\";\n\nexport const dungeonMapStyles = css`\n :host {\n --fellowship-map-soot: var(--_fellowship-field-canvas);\n --fellowship-map-ink: var(--_fellowship-field-ink);\n --fellowship-map-muted-ink: var(--_fellowship-field-muted);\n --fellowship-map-border: var(--_fellowship-field-border);\n --fellowship-map-focus: var(--_fellowship-field-focus);\n --fellowship-map-rune: var(--_fellowship-field-accent);\n --fellowship-map-ember: var(--_fellowship-field-ember);\n --fellowship-map-control-surface: color-mix(\n in srgb,\n var(--_fellowship-field-surface-raised) 92%,\n transparent\n );\n --fellowship-map-enemy-size: 32px;\n --fellowship-map-radius: var(--_fellowship-field-radius);\n --fellowship-map-font-family: var(--_fellowship-field-font-family);\n --fellowship-map-tactical-font-family: var(--_fellowship-field-mono-font-family);\n\n display: block;\n inline-size: 100%;\n min-block-size: 240px;\n aspect-ratio: 2 / 1;\n container-type: inline-size;\n color: var(--fellowship-map-ink);\n font-family: var(--fellowship-map-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 .viewer,\n .viewport {\n inline-size: 100%;\n block-size: 100%;\n min-block-size: inherit;\n }\n\n .viewer {\n position: relative;\n overflow: hidden;\n border: 1px solid var(--fellowship-map-border);\n border-radius: calc(var(--fellowship-map-radius) + 2px);\n background: var(--fellowship-map-soot);\n }\n\n .viewport {\n position: relative;\n overflow: hidden;\n outline: none;\n cursor: grab;\n touch-action: none;\n }\n\n .viewport[data-panning=\"true\"] {\n cursor: grabbing;\n }\n\n .viewport:focus-visible {\n box-shadow: inset 0 0 0 2px var(--fellowship-map-focus);\n }\n\n .stage {\n position: absolute;\n inset: 0 auto auto 0;\n transform-origin: 0 0;\n will-change: transform;\n }\n\n .map-canvas,\n .overlay {\n position: absolute;\n inset: 0;\n inline-size: 100%;\n block-size: 100%;\n }\n\n .map-canvas {\n display: block;\n background: #d5ceb1;\n box-shadow: inset 0 0 0 1px rgb(0 0 0 / 10%);\n }\n\n .overlay {\n pointer-events: none;\n }\n\n .group-anchor {\n position: absolute;\n pointer-events: none;\n }\n\n .default-group,\n .custom-group-host {\n position: absolute;\n inset: 0;\n }\n\n .group-ring {\n position: absolute;\n inset: 0;\n border: calc(1px * var(--fellowship-map-inverse-scale, 1)) solid transparent;\n border-radius: 50%;\n background: transparent;\n transition:\n border-color 140ms cubic-bezier(0.23, 1, 0.32, 1),\n background-color 140ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n .group-anchor:has(.enemy-anchor:hover) .group-ring,\n .group-anchor:focus-within .group-ring {\n border-color: var(--fellowship-map-rune);\n background: color-mix(in srgb, var(--fellowship-map-rune) 8%, transparent);\n }\n\n .group-label {\n position: absolute;\n inset: 0 auto auto 50%;\n z-index: 1;\n display: inline-flex;\n align-items: center;\n min-block-size: 22px;\n padding: 0 8px 0 12px;\n border-radius: 4px;\n background: var(--fellowship-map-control-surface);\n color: var(--fellowship-map-ink);\n box-shadow: inset 0 0 0 1px var(--fellowship-map-border);\n font-family: var(--fellowship-map-tactical-font-family);\n font-size: 10px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.04em;\n line-height: 1;\n white-space: nowrap;\n transform: translate(-50%, -45%) scale(var(--fellowship-map-inverse-scale, 1));\n transform-origin: center;\n }\n\n .group-label::before {\n position: absolute;\n inset: 5px auto 5px 0;\n width: 2px;\n background: var(--fellowship-map-rune);\n content: \"\";\n }\n\n .enemy-anchor {\n position: absolute;\n z-index: 2;\n inline-size: 0;\n block-size: 0;\n pointer-events: auto;\n }\n\n .enemy-visual {\n position: absolute;\n inset: 0 auto auto 0;\n transform: translate(-50%, -50%) scale(var(--fellowship-map-inverse-scale, 1));\n transform-origin: center;\n }\n\n .enemy-marker {\n position: relative;\n display: grid;\n inline-size: var(--fellowship-map-enemy-size);\n block-size: var(--fellowship-map-enemy-size);\n place-items: center;\n overflow: hidden;\n border: 2px solid var(--fellowship-map-rune);\n border-radius: 50%;\n background: var(--_fellowship-field-surface-raised);\n color: var(--fellowship-map-ink);\n font-family: var(--fellowship-map-tactical-font-family);\n font-size: 10px;\n font-weight: 600;\n box-shadow: 0 0 0 1px rgb(0 0 0 / 68%);\n transition:\n border-color 140ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 140ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n .enemy-marker[data-boss=\"true\"] {\n inline-size: calc(var(--fellowship-map-enemy-size) + 4px);\n block-size: calc(var(--fellowship-map-enemy-size) + 4px);\n border-color: var(--fellowship-map-ember);\n }\n\n .enemy-anchor:hover .enemy-marker {\n transform: scale(1.08);\n }\n\n .enemy-image {\n inline-size: 100%;\n block-size: 100%;\n object-fit: cover;\n }\n\n .controls,\n .map-selector {\n position: absolute;\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 4px;\n border-radius: calc(var(--fellowship-map-radius) + 2px);\n background: var(--fellowship-map-control-surface);\n box-shadow: inset 0 0 0 1px var(--fellowship-map-border);\n backdrop-filter: blur(12px);\n }\n\n .map-selector {\n inset: 12px auto auto 12px;\n max-inline-size: calc(100% - 172px);\n overflow-x: auto;\n scrollbar-width: none;\n }\n\n .map-selector::-webkit-scrollbar {\n display: none;\n }\n\n .controls {\n inset: 12px 12px auto auto;\n }\n\n .control-button,\n .map-selector-button {\n position: relative;\n min-inline-size: 44px;\n min-block-size: 44px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: var(--fellowship-map-muted-ink);\n font: inherit;\n font-size: 13px;\n font-weight: 600;\n line-height: 1;\n cursor: pointer;\n transition:\n color 140ms cubic-bezier(0.23, 1, 0.32, 1),\n background-color 140ms cubic-bezier(0.23, 1, 0.32, 1),\n transform 120ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n .control-button {\n font-family: var(--fellowship-map-tactical-font-family);\n font-size: 16px;\n font-weight: 500;\n }\n\n .map-selector-button {\n padding-inline: 12px;\n letter-spacing: 0.02em;\n white-space: nowrap;\n }\n\n .map-selector-button[aria-selected=\"true\"] {\n background: rgb(255 255 255 / 6%);\n color: var(--fellowship-map-ink);\n }\n\n .map-selector-button[aria-selected=\"true\"]::after {\n position: absolute;\n inset: auto 12px 0;\n height: 2px;\n background: var(--fellowship-map-rune);\n content: \"\";\n }\n\n .control-button:focus-visible,\n .map-selector-button:focus-visible,\n .retry-button:focus-visible {\n outline: 2px solid var(--fellowship-map-focus);\n outline-offset: 2px;\n }\n\n .control-button:active,\n .map-selector-button:active,\n .retry-button:active {\n transform: scale(0.97);\n }\n\n .status {\n position: absolute;\n z-index: 30;\n inset: 0;\n display: grid;\n padding: 24px;\n place-items: center;\n background: color-mix(in srgb, var(--fellowship-map-soot) 84%, transparent);\n text-align: center;\n }\n\n .status-card {\n display: grid;\n max-inline-size: 376px;\n gap: 8px;\n justify-items: center;\n padding: 24px;\n border-radius: calc(var(--fellowship-map-radius) + 2px);\n background: var(--fellowship-map-control-surface);\n box-shadow: inset 0 0 0 1px var(--fellowship-map-border);\n }\n\n .status-mark {\n position: relative;\n display: grid;\n inline-size: 40px;\n block-size: 24px;\n place-items: center;\n color: var(--fellowship-map-rune);\n font-family: var(--fellowship-map-tactical-font-family);\n font-size: 12px;\n }\n\n .status-mark::before,\n .status-mark::after {\n position: absolute;\n width: 8px;\n height: 1px;\n background: var(--fellowship-map-border);\n content: \"\";\n }\n\n .status-mark::before {\n left: 0;\n }\n\n .status-mark::after {\n right: 0;\n }\n\n .status-title {\n margin: 4px 0 0;\n color: var(--fellowship-map-ink);\n font-size: 18px;\n font-weight: 600;\n line-height: 24px;\n text-wrap: balance;\n }\n\n .status-copy {\n margin: 0;\n color: var(--fellowship-map-muted-ink);\n font-size: 14px;\n line-height: 1.5;\n text-wrap: pretty;\n }\n\n .retry-button {\n min-block-size: 44px;\n margin-block-start: 8px;\n padding-inline: 16px;\n border: 1px solid var(--fellowship-map-border);\n border-radius: 4px;\n background: var(--_fellowship-field-inset);\n color: var(--fellowship-map-ink);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n }\n\n .empty-badge {\n position: absolute;\n z-index: 20;\n inset: auto auto 12px 12px;\n padding: 8px;\n border-radius: 4px;\n background: var(--fellowship-map-control-surface);\n color: var(--fellowship-map-muted-ink);\n box-shadow: inset 0 0 0 1px var(--fellowship-map-border);\n font-size: 12px;\n }\n\n .visually-hidden {\n position: absolute;\n inline-size: 1px;\n block-size: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n white-space: nowrap;\n }\n\n @media (hover: hover) {\n .control-button:hover,\n .map-selector-button:hover {\n background: rgb(255 255 255 / 6%);\n color: var(--fellowship-map-ink);\n }\n\n .retry-button:hover {\n border-color: color-mix(in srgb, var(--fellowship-map-rune) 48%, transparent);\n color: var(--fellowship-map-focus);\n }\n }\n\n @container (max-width: 560px) {\n .map-selector,\n .controls {\n inset-block-start: 8px;\n }\n\n .map-selector {\n inset-inline-start: 8px;\n max-inline-size: calc(100% - 164px);\n }\n\n .controls {\n inset-inline-end: 8px;\n }\n\n .map-selector-button {\n padding-inline: 8px;\n }\n\n .map-selector-button[aria-selected=\"true\"]::after {\n inset-inline: 8px;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .group-ring,\n .enemy-marker,\n .control-button,\n .map-selector-button,\n .retry-button {\n transition: none;\n }\n\n .control-button:active,\n .map-selector-button:active,\n .retry-button:active {\n transform: none;\n }\n }\n`;\n","import type { Position2D } from \"@crockery/fellowship-data\";\n\nimport type { DungeonMapEnemyModel, DungeonMapGroupModel, NormalizedRect } from \"./types.js\";\n\nconst VISUAL_PADDING_X = 0.018;\nconst VISUAL_PADDING_Y = 0.036;\nconst MIN_VISUAL_WIDTH = 0.06;\nconst MIN_VISUAL_HEIGHT = 0.12;\n\nconst clamp = (value: number, minimum: number, maximum: number): number =>\n Math.min(maximum, Math.max(minimum, value));\n\nconst calculateBounds = (enemies: readonly DungeonMapEnemyModel[]): NormalizedRect => {\n const xs = enemies.map(({ position }) => position.x);\n const ys = enemies.map(({ position }) => position.y);\n const minimumX = Math.min(...xs);\n const maximumX = Math.max(...xs);\n const minimumY = Math.min(...ys);\n const maximumY = Math.max(...ys);\n\n return {\n x: minimumX,\n y: minimumY,\n width: maximumX - minimumX,\n height: maximumY - minimumY,\n };\n};\n\nconst expandBounds = (bounds: NormalizedRect): NormalizedRect => {\n const centerX = bounds.x + bounds.width / 2;\n const centerY = bounds.y + bounds.height / 2;\n const width = Math.max(MIN_VISUAL_WIDTH, bounds.width + VISUAL_PADDING_X * 2);\n const height = Math.max(MIN_VISUAL_HEIGHT, bounds.height + VISUAL_PADDING_Y * 2);\n const x = clamp(centerX - width / 2, 0, Math.max(0, 1 - width));\n const y = clamp(centerY - height / 2, 0, Math.max(0, 1 - height));\n\n return { x, y, width: Math.min(width, 1), height: Math.min(height, 1) };\n};\n\nconst groupCenter = (enemies: readonly DungeonMapEnemyModel[]): Position2D => ({\n x: enemies.reduce((sum, enemy) => sum + enemy.position.x, 0) / enemies.length,\n y: enemies.reduce((sum, enemy) => sum + enemy.position.y, 0) / enemies.length,\n});\n\nconst groupScore = (enemies: readonly DungeonMapEnemyModel[]): number | null => {\n const scores = enemies\n .map(({ enemy }) => enemy?.dungeonScore)\n .filter((score): score is number => typeof score === \"number\");\n return scores.length > 0 ? scores.reduce((sum, score) => sum + score, 0) : null;\n};\n\nexport const createDungeonMapGroupModel = (\n id: number | null,\n enemies: readonly DungeonMapEnemyModel[],\n): DungeonMapGroupModel => {\n const enemyBounds = calculateBounds(enemies);\n return {\n id,\n enemies,\n enemyBounds,\n visualBounds: expandBounds(enemyBounds),\n center: groupCenter(enemies),\n dungeonScore: groupScore(enemies),\n };\n};\n","import { findImage } from \"@crockery/fellowship-data\";\nimport type { Dungeon, ImageAssetRef, Position2D } from \"@crockery/fellowship-data\";\n\nimport type { DungeonMapData, FellowshipDataSource } from \"../data/data-source.js\";\nimport { createDungeonMapGroupModel } from \"./layout.js\";\nimport type { DungeonMapEnemyModel, DungeonMapRenderModel, DungeonMapWarning } from \"./types.js\";\n\nexport const isValidMapPosition = (position: Position2D | null): position is Position2D =>\n position !== null &&\n Number.isFinite(position.x) &&\n Number.isFinite(position.y) &&\n position.x >= 0 &&\n position.x <= 1 &&\n position.y >= 0 &&\n position.y <= 1;\n\nexport const createDungeonMapRenderModel = (\n data: DungeonMapData,\n mapImage: ImageAssetRef,\n source: FellowshipDataSource,\n): DungeonMapRenderModel => {\n const warnings: DungeonMapWarning[] = [];\n const groups = new Map<string, { id: number | null; enemies: DungeonMapEnemyModel[] }>();\n\n data.dungeon.enemies.forEach((placement, placementIndex) => {\n if (placement.mapImageId !== mapImage.id) {\n return;\n }\n\n if (!isValidMapPosition(placement.mapPosition)) {\n warnings.push({\n code: \"invalid-map-position\",\n message: `Skipped ${placement.id}: its map position is missing or outside the map.`,\n placementIndex,\n enemyId: placement.id,\n });\n return;\n }\n\n const enemy = data.enemies.get(placement.id) ?? null;\n if (!enemy) {\n warnings.push({\n code: \"missing-enemy\",\n message: `No enemy metadata exists for ${placement.id}.`,\n placementIndex,\n enemyId: placement.id,\n });\n }\n\n const image = enemy ? findImage(enemy.images, { type: \"enemy\", size: \"200\" }) : null;\n let imageUrl: string | null = null;\n if (image) {\n try {\n imageUrl = source.resolveAssetUrl(image);\n } catch (error: unknown) {\n warnings.push({\n code: \"asset-url-error\",\n message: `Could not resolve the image URL for ${placement.id}: ${String(error)}.`,\n placementIndex,\n enemyId: placement.id,\n });\n }\n } else if (enemy) {\n warnings.push({\n code: \"missing-enemy-image\",\n message: `No 200px enemy image exists for ${placement.id}.`,\n placementIndex,\n enemyId: placement.id,\n });\n }\n\n const model: DungeonMapEnemyModel = {\n instanceId: `${data.dungeon.id}:${mapImage.id}:${placementIndex}`,\n enemy,\n placement,\n position: placement.mapPosition,\n imageUrl,\n };\n const key =\n placement.groupId === null ? `singleton:${placementIndex}` : `group:${placement.groupId}`;\n const group = groups.get(key) ?? { id: placement.groupId, enemies: [] };\n group.enemies.push(model);\n groups.set(key, group);\n });\n\n return {\n dungeon: data.dungeon,\n mapImage,\n groups: [...groups.values()].map(({ id, enemies }) => createDungeonMapGroupModel(id, enemies)),\n warnings,\n };\n};\n\nexport const humanizeMapLabel = (mapImage: ImageAssetRef, dungeon: Dungeon): string => {\n const rawName = mapImage.id.split(\":\").at(-1) ?? mapImage.id;\n const withoutPrefix = rawName.replace(/^T_(?:Map_)?/i, \"\").replace(/^map[-_]/i, \"\");\n const words = withoutPrefix\n .replaceAll(/[_-]+/g, \" \")\n .replace(/([a-z\\d])([A-Z])/g, \"$1 $2\")\n .trim();\n const dungeonName = dungeon.id.split(\".\").at(-1)?.toLowerCase();\n const parts = words.split(/\\s+/).filter((part) => part.toLowerCase() !== dungeonName);\n const label = parts.join(\" \") || words || \"Map\";\n return label.replace(/\\b[a-z]/g, (letter) => letter.toUpperCase());\n};\n","import type { ViewportState } from \"./types.js\";\n\nconst clamp = (value: number, minimum: number, maximum: number): number =>\n Math.min(maximum, Math.max(minimum, value));\n\nexport class ViewportController {\n #viewportWidth = 0;\n #viewportHeight = 0;\n #mapWidth = 0;\n #mapHeight = 0;\n #zoom = 1;\n #x = 0;\n #y = 0;\n\n maxZoom = 8;\n\n get state(): ViewportState {\n return { zoom: this.#zoom, scale: this.scale, x: this.#x, y: this.#y };\n }\n\n get scale(): number {\n return this.fitScale * this.#zoom;\n }\n\n get fitScale(): number {\n if (!this.#viewportWidth || !this.#viewportHeight || !this.#mapWidth || !this.#mapHeight) {\n return 1;\n }\n return Math.min(this.#viewportWidth / this.#mapWidth, this.#viewportHeight / this.#mapHeight);\n }\n\n setDimensions(\n viewportWidth: number,\n viewportHeight: number,\n mapWidth: number,\n mapHeight: number,\n ): ViewportState {\n const previousScale = this.scale;\n const previousCenterX = this.#mapWidth\n ? (this.#viewportWidth / 2 - this.#x) / previousScale / this.#mapWidth\n : 0.5;\n const previousCenterY = this.#mapHeight\n ? (this.#viewportHeight / 2 - this.#y) / previousScale / this.#mapHeight\n : 0.5;\n\n this.#viewportWidth = Math.max(0, viewportWidth);\n this.#viewportHeight = Math.max(0, viewportHeight);\n this.#mapWidth = Math.max(0, mapWidth);\n this.#mapHeight = Math.max(0, mapHeight);\n\n this.#x = this.#viewportWidth / 2 - previousCenterX * this.#mapWidth * this.scale;\n this.#y = this.#viewportHeight / 2 - previousCenterY * this.#mapHeight * this.scale;\n this.#clampPan();\n return this.state;\n }\n\n fit(): ViewportState {\n this.#zoom = 1;\n this.#x = (this.#viewportWidth - this.#mapWidth * this.scale) / 2;\n this.#y = (this.#viewportHeight - this.#mapHeight * this.scale) / 2;\n return this.state;\n }\n\n zoomAt(\n factor: number,\n pointX = this.#viewportWidth / 2,\n pointY = this.#viewportHeight / 2,\n ): ViewportState {\n const previousScale = this.scale;\n const mapX = (pointX - this.#x) / previousScale;\n const mapY = (pointY - this.#y) / previousScale;\n this.#zoom = clamp(this.#zoom * factor, 1, Math.max(1, this.maxZoom));\n this.#x = pointX - mapX * this.scale;\n this.#y = pointY - mapY * this.scale;\n this.#clampPan();\n return this.state;\n }\n\n panBy(deltaX: number, deltaY: number): ViewportState {\n this.#x += deltaX;\n this.#y += deltaY;\n this.#clampPan();\n return this.state;\n }\n\n #clampPan(): void {\n const scaledWidth = this.#mapWidth * this.scale;\n const scaledHeight = this.#mapHeight * this.scale;\n\n this.#x =\n scaledWidth <= this.#viewportWidth\n ? (this.#viewportWidth - scaledWidth) / 2\n : clamp(this.#x, this.#viewportWidth - scaledWidth, 0);\n this.#y =\n scaledHeight <= this.#viewportHeight\n ? (this.#viewportHeight - scaledHeight) / 2\n : clamp(this.#y, this.#viewportHeight - scaledHeight, 0);\n }\n}\n","import type { ImageAssetRef } from \"@crockery/fellowship-data\";\nimport { html, LitElement, nothing, type PropertyValues, type TemplateResult } from \"lit\";\nimport { customElement, property, query, state } from \"lit/decorators.js\";\nimport { repeat } from \"lit/directives/repeat.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\n\nimport {\n FellowshipDataError,\n type DungeonMapData,\n type FellowshipDataSource,\n} from \"../data/data-source.js\";\nimport { createPackageFellowshipDataSource } from \"../data/package-data-source.js\";\nimport { fieldManualStyles } from \"../styles/field-manual.styles.js\";\nimport { installFieldManualFonts } from \"../styles/field-manual-fonts.js\";\nimport { dungeonMapStyles } from \"./dungeon-map.styles.js\";\nimport { createDungeonMapRenderModel, humanizeMapLabel } from \"./render-model.js\";\nimport type {\n DungeonMapEnemyModel,\n DungeonMapGroupModel,\n DungeonMapRenderModel,\n FellowshipMapErrorDetail,\n FellowshipMapImageChangeDetail,\n FellowshipMapReadyDetail,\n FellowshipMapViewportChangeDetail,\n FellowshipMapWarningDetail,\n LitEnemyRenderer,\n LitGroupRenderer,\n MapLabelFormatter,\n ViewportChangeSource,\n ViewportState,\n} from \"./types.js\";\nimport { ViewportController } from \"./viewport-controller.js\";\n\ntype ComponentStatus = \"empty\" | \"loading\" | \"ready\" | \"error\";\n\ninterface PointerPosition {\n readonly x: number;\n readonly y: number;\n}\n\nconst eventOptions = { bubbles: true, composed: true } as const;\n\n/**\n * @fires fellowship-map-ready - Fired after dungeon data and the map image are ready.\n * @fires fellowship-map-error - Fired when map data, imagery, or a renderer fails.\n * @fires fellowship-map-warning - Fired for recoverable dungeon data issues.\n * @fires fellowship-map-image-change - Fired when the selected map changes.\n * @fires fellowship-map-viewport-change - Fired when zoom or pan state changes.\n */\n@customElement(\"fellowship-dungeon-map\")\nexport class FellowshipDungeonMapElement extends LitElement {\n static override styles = [fieldManualStyles, dungeonMapStyles];\n\n @property({ type: String, attribute: \"dungeon-id\" }) dungeonId = \"\";\n @property({ type: String, attribute: \"map-image-id\" }) mapImageId: string | null = null;\n @property({ type: String, attribute: \"asset-base-url\" }) assetBaseUrl: string | null = null;\n @property({ attribute: false }) dataSource: FellowshipDataSource | null = null;\n @property({ attribute: false }) renderEnemy: LitEnemyRenderer | null = null;\n @property({ attribute: false }) renderGroup: LitGroupRenderer | null = null;\n @property({ attribute: false }) mapLabelFormatter: MapLabelFormatter | null = null;\n @property({ type: Boolean, attribute: \"show-controls\" }) showControls = true;\n @property({ type: Boolean, attribute: \"show-map-selector\" }) showMapSelector = true;\n @property({ type: Number, attribute: \"max-zoom\" }) maxZoom = 8;\n\n /** Internal bridge used by the React wrapper. */\n @property({ type: Boolean, attribute: false }) externalOverlay = false;\n\n @state() private _status: ComponentStatus = \"empty\";\n @state() private _error: FellowshipMapErrorDetail | null = null;\n @state() private _data: DungeonMapData | null = null;\n @state() private _model: DungeonMapRenderModel | null = null;\n @state() private _selectedMap: ImageAssetRef | null = null;\n @state() private _viewportState: ViewportState = { zoom: 1, scale: 1, x: 0, y: 0 };\n @state() private _isPanning = false;\n @state() private _enemyRenderContent = new Map<string, TemplateResult | typeof nothing>();\n @state() private _groupRenderContent = new Map<string, TemplateResult | typeof nothing>();\n @state() private _mapLabels = new Map<string, string>();\n\n @query(\".viewport\") private _viewportElement?: HTMLElement;\n @query(\"canvas\") private _canvasElement?: HTMLCanvasElement;\n\n #activeSource: FellowshipDataSource | null = null;\n #abortController: AbortController | null = null;\n #loadToken = 0;\n #renderToken = 0;\n #resizeObserver: ResizeObserver | null = null;\n #viewport = new ViewportController();\n #pointers = new Map<number, PointerPosition>();\n #lastPointer: PointerPosition | null = null;\n #lastPinchDistance = 0;\n #lastPinchCenter: PointerPosition | null = null;\n #rendererFailures = new Set<string>();\n #pendingRendererErrors: FellowshipMapErrorDetail[] = [];\n\n override connectedCallback(): void {\n super.connectedCallback();\n installFieldManualFonts(this.ownerDocument);\n this.#resizeObserver ??= new ResizeObserver(() => this.#handleResize());\n }\n\n override disconnectedCallback(): void {\n this.#abortController?.abort();\n this.#resizeObserver?.disconnect();\n super.disconnectedCallback();\n }\n\n protected override firstUpdated(changed: PropertyValues<this>): void {\n super.firstUpdated(changed);\n const viewport = this._viewportElement;\n if (viewport) {\n this.#resizeObserver?.observe(viewport);\n }\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n super.updated(changed);\n this.#flushRendererErrors();\n\n if (changed.has(\"dungeonId\") || changed.has(\"dataSource\") || changed.has(\"assetBaseUrl\")) {\n queueMicrotask(() => {\n if (this.isConnected) {\n void this.#loadDungeon();\n }\n });\n return;\n }\n\n if (changed.has(\"mapImageId\") && this._data) {\n queueMicrotask(() => {\n if (this.isConnected) {\n void this.#prepareSelectedMap(true);\n }\n });\n }\n\n if (changed.has(\"maxZoom\") && changed.get(\"maxZoom\") !== undefined) {\n this.#viewport.maxZoom = Math.max(1, this.maxZoom);\n this.#viewport.zoomAt(1);\n queueMicrotask(() => this.#commitViewport(\"button\"));\n }\n }\n\n protected override willUpdate(changed: PropertyValues): void {\n super.willUpdate(changed);\n\n if (\n changed.has(\"renderEnemy\") ||\n changed.has(\"renderGroup\") ||\n changed.has(\"mapLabelFormatter\")\n ) {\n this.#rendererFailures.clear();\n }\n if (changed.has(\"_model\") || changed.has(\"renderEnemy\") || changed.has(\"renderGroup\")) {\n this.#prepareRendererContent();\n }\n if (changed.has(\"_data\") || changed.has(\"mapLabelFormatter\")) {\n this.#prepareMapLabels();\n }\n }\n\n fitToView(): void {\n this.#viewport.fit();\n this.#commitViewport(\"fit\");\n }\n\n zoomIn(): void {\n this.#viewport.zoomAt(1.35);\n this.#commitViewport(\"button\");\n }\n\n zoomOut(): void {\n this.#viewport.zoomAt(1 / 1.35);\n this.#commitViewport(\"button\");\n }\n\n retry(): void {\n void this.#loadDungeon();\n }\n\n get renderModel(): DungeonMapRenderModel | null {\n return this._model;\n }\n\n override render(): TemplateResult {\n const model = this._model;\n const viewport = this._viewportState;\n const selectedMapTabId = this.#selectedMapTabId;\n const stageStyle = model\n ? {\n width: `${model.mapImage.width}px`,\n height: `${model.mapImage.height}px`,\n transform: `translate3d(${viewport.x}px, ${viewport.y}px, 0) scale(${viewport.scale})`,\n \"--fellowship-map-inverse-scale\": String(1 / viewport.scale),\n }\n : {};\n\n return html`\n <section class=\"viewer\" aria-label=${this.#viewerLabel}>\n <div\n class=\"viewport\"\n part=\"viewport\"\n role=\"region\"\n aria-label=\"Interactive dungeon map\"\n tabindex=\"0\"\n aria-describedby=\"map-instructions\"\n data-panning=${String(this._isPanning)}\n @wheel=${this.#handleWheel}\n @keydown=${this.#handleViewportKeydown}\n @pointerdown=${this.#handlePointerDown}\n @pointermove=${this.#handlePointerMove}\n @pointerup=${this.#handlePointerEnd}\n @pointercancel=${this.#handlePointerEnd}\n >\n ${\n model\n ? html`\n <div\n class=\"stage\"\n id=\"map-stage\"\n role=${selectedMapTabId === nothing ? nothing : \"tabpanel\"}\n aria-labelledby=${selectedMapTabId}\n style=${styleMap(stageStyle)}\n >\n <canvas\n class=\"map-canvas\"\n part=\"map-canvas\"\n role=\"img\"\n aria-label=${`${model.dungeon.name ?? model.dungeon.id}, ${this.#mapLabel(model.mapImage)}`}\n .width=${model.mapImage.width}\n .height=${model.mapImage.height}\n ></canvas>\n <div class=\"overlay\" part=\"overlay\">\n ${\n this.externalOverlay\n ? html`<slot name=\"overlay\"></slot>`\n : this.#renderGroups(model)\n }\n </div>\n </div>\n ${this.#renderMapSelector(model)} ${this.#renderControls()}\n ${\n model.groups.length === 0 && this._status === \"ready\"\n ? html`<div class=\"empty-badge\">No enemy placements on this map</div>`\n : nothing\n }\n `\n : nothing\n }\n ${this.#renderStatus()}\n <p class=\"visually-hidden\" id=\"map-instructions\">\n Drag to pan. Use the mouse wheel, plus and minus keys, or zoom controls to zoom. Press\n zero to fit the full map.\n </p>\n </div>\n </section>\n `;\n }\n\n get #viewerLabel(): string {\n if (this._data) {\n return `${this._data.dungeon.name ?? this._data.dungeon.id} dungeon map`;\n }\n return \"Fellowship dungeon map\";\n }\n\n get #selectedMapTabId(): string | typeof nothing {\n if (\n !this.showMapSelector ||\n !this._model ||\n this._model.dungeon.mapImages.length <= 1 ||\n !this._selectedMap\n ) {\n return nothing;\n }\n const index = this._model.dungeon.mapImages.findIndex(\n (mapImage) => mapImage.id === this._selectedMap?.id,\n );\n return index >= 0 ? `map-tab-${index}` : nothing;\n }\n\n async #loadDungeon(): Promise<void> {\n const dungeonId = this.dungeonId.trim();\n const token = ++this.#loadToken;\n this.#renderToken += 1;\n this.#abortController?.abort();\n this.#abortController = new AbortController();\n this._data = null;\n this._model = null;\n this._selectedMap = null;\n this._error = null;\n this.#rendererFailures.clear();\n\n if (!dungeonId) {\n this._status = \"empty\";\n return;\n }\n\n this._status = \"loading\";\n const source =\n this.dataSource ??\n createPackageFellowshipDataSource(\n this.assetBaseUrl ? { assetBaseUrl: this.assetBaseUrl } : {},\n );\n this.#activeSource = source;\n\n try {\n const data = await source.loadDungeon(dungeonId, 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 this._data = data;\n await this.#prepareSelectedMap(true);\n } catch (error: unknown) {\n if (token !== this.#loadToken || this.#abortController.signal.aborted) {\n return;\n }\n this.#setError(error);\n }\n }\n\n async #prepareSelectedMap(resetView: boolean): Promise<void> {\n const data = this._data;\n const source = this.#activeSource;\n if (!data || !source) {\n return;\n }\n\n const mapImage = this.mapImageId\n ? data.dungeon.mapImages.find(({ id }) => id === this.mapImageId)\n : data.dungeon.mapImages[0];\n\n if (!mapImage) {\n this.#setError({\n code: \"map-not-found\",\n message: this.mapImageId\n ? `No map image with ID “${this.mapImageId}” exists for this dungeon.`\n : \"This dungeon does not include a map image.\",\n });\n return;\n }\n\n const renderToken = ++this.#renderToken;\n const model = createDungeonMapRenderModel(data, mapImage, source);\n this._model = model;\n this._selectedMap = mapImage;\n this._status = \"loading\";\n this._error = null;\n await this.updateComplete;\n\n const viewport = this._viewportElement;\n if (viewport) {\n this.#viewport.maxZoom = Math.max(1, this.maxZoom);\n this.#viewport.setDimensions(\n viewport.clientWidth,\n viewport.clientHeight,\n mapImage.width,\n mapImage.height,\n );\n if (resetView) {\n this.#viewport.fit();\n }\n this._viewportState = this.#viewport.state;\n await this.updateComplete;\n }\n\n try {\n await this.#drawMap(mapImage, source, renderToken);\n if (renderToken !== this.#renderToken) {\n return;\n }\n this._status = \"ready\";\n this.dispatchEvent(\n new CustomEvent<FellowshipMapReadyDetail>(\"fellowship-map-ready\", {\n ...eventOptions,\n detail: { model, data },\n }),\n );\n if (model.warnings.length > 0) {\n this.dispatchEvent(\n new CustomEvent<FellowshipMapWarningDetail>(\"fellowship-map-warning\", {\n ...eventOptions,\n detail: { warnings: model.warnings, model },\n }),\n );\n }\n } catch (error: unknown) {\n if (renderToken === this.#renderToken) {\n this.#setError({\n code: \"map-image-error\",\n message: `Unable to load the dungeon map image: ${String(error)}.`,\n cause: error,\n });\n }\n }\n }\n\n async #drawMap(\n mapImage: ImageAssetRef,\n source: FellowshipDataSource,\n renderToken: number,\n ): Promise<void> {\n const image = new Image();\n image.crossOrigin = \"anonymous\";\n image.src = source.resolveAssetUrl(mapImage);\n await image.decode();\n if (renderToken !== this.#renderToken) {\n return;\n }\n const canvas = this._canvasElement;\n const context = canvas?.getContext(\"2d\");\n if (!canvas || !context) {\n throw new Error(\"A 2D canvas context is unavailable.\");\n }\n context.clearRect(0, 0, canvas.width, canvas.height);\n context.drawImage(image, 0, 0, mapImage.width, mapImage.height);\n }\n\n #setError(error: unknown): void {\n const detail: FellowshipMapErrorDetail =\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<FellowshipMapErrorDetail>(\"fellowship-map-error\", {\n ...eventOptions,\n detail,\n }),\n );\n }\n\n #prepareRendererContent(): void {\n const model = this._model;\n const enemyContent = new Map<string, TemplateResult | typeof nothing>();\n const groupContent = new Map<string, TemplateResult | typeof nothing>();\n\n if (!model) {\n this._enemyRenderContent = enemyContent;\n this._groupRenderContent = groupContent;\n return;\n }\n\n if (this.renderEnemy) {\n for (const group of model.groups) {\n for (const enemy of group.enemies) {\n try {\n enemyContent.set(\n enemy.instanceId,\n this.renderEnemy({\n model: enemy,\n group,\n dungeon: model.dungeon,\n mapImage: model.mapImage,\n }),\n );\n } catch (error: unknown) {\n this.#queueRendererError(`enemy:${enemy.instanceId}`, \"enemy-renderer-error\", error);\n }\n }\n }\n }\n this._enemyRenderContent = enemyContent;\n\n if (this.renderGroup) {\n for (const group of model.groups) {\n const children = group.enemies.map((enemy) => this.#renderEnemyAnchor(group, enemy));\n const key = this.#groupKey(model, group);\n try {\n groupContent.set(\n key,\n this.renderGroup({\n group,\n children,\n dungeon: model.dungeon,\n mapImage: model.mapImage,\n }),\n );\n } catch (error: unknown) {\n this.#queueRendererError(`group:${key}`, \"group-renderer-error\", error);\n }\n }\n }\n this._groupRenderContent = groupContent;\n }\n\n #prepareMapLabels(): void {\n const labels = new Map<string, string>();\n const dungeon = this._data?.dungeon;\n if (dungeon) {\n for (const mapImage of dungeon.mapImages) {\n try {\n labels.set(\n mapImage.id,\n this.mapLabelFormatter?.(mapImage, dungeon) ?? humanizeMapLabel(mapImage, dungeon),\n );\n } catch (error: unknown) {\n this.#queueRendererError(`map-label:${mapImage.id}`, \"map-label-formatter-error\", error);\n labels.set(mapImage.id, humanizeMapLabel(mapImage, dungeon));\n }\n }\n }\n this._mapLabels = labels;\n }\n\n #renderGroups(model: DungeonMapRenderModel): unknown {\n return repeat(\n model.groups,\n (group) => this.#groupKey(model, group),\n (group) => this.#renderGroup(model, group),\n );\n }\n\n #groupKey(model: DungeonMapRenderModel, group: DungeonMapGroupModel): string {\n return `${model.mapImage.id}:${String(group.id)}:${group.enemies[0]?.instanceId ?? \"empty\"}`;\n }\n\n #renderGroup(model: DungeonMapRenderModel, group: DungeonMapGroupModel): TemplateResult {\n const bounds = group.visualBounds;\n const style = {\n left: `${bounds.x * 100}%`,\n top: `${bounds.y * 100}%`,\n width: `${bounds.width * 100}%`,\n height: `${bounds.height * 100}%`,\n };\n const key = this.#groupKey(model, group);\n const content =\n this.renderGroup && this._groupRenderContent.has(key)\n ? (this._groupRenderContent.get(key) ?? nothing)\n : this.#renderDefaultGroup(group);\n\n return html` <div class=\"group-anchor\" part=\"group\" style=${styleMap(style)}>${content}</div> `;\n }\n\n #renderDefaultGroup(group: DungeonMapGroupModel): TemplateResult {\n const label = `G${group.id ?? \"–\"}${group.dungeonScore === null ? \"\" : ` · ${group.dungeonScore}`}`;\n return html`\n <div class=\"default-group\">\n <div class=\"group-ring\" aria-hidden=\"true\"></div>\n <span class=\"group-label\">${label}</span>\n ${repeat(\n group.enemies,\n (enemy) => enemy.instanceId,\n (enemy) => this.#renderEnemyAnchor(group, enemy),\n )}\n </div>\n `;\n }\n\n #renderEnemyAnchor(group: DungeonMapGroupModel, enemy: DungeonMapEnemyModel): TemplateResult {\n const bounds = group.visualBounds;\n const style = {\n left: `${((enemy.position.x - bounds.x) / bounds.width) * 100}%`,\n top: `${((enemy.position.y - bounds.y) / bounds.height) * 100}%`,\n };\n const content =\n this.renderEnemy && this._enemyRenderContent.has(enemy.instanceId)\n ? (this._enemyRenderContent.get(enemy.instanceId) ?? nothing)\n : this.#renderDefaultEnemy(enemy);\n const name = enemy.enemy?.name ?? enemy.placement.id;\n\n return html`\n <div\n class=\"enemy-anchor\"\n part=\"enemy\"\n style=${styleMap(style)}\n role=\"img\"\n aria-label=${name}\n title=${name}\n >\n <div class=\"enemy-visual\">${content}</div>\n </div>\n `;\n }\n\n #renderDefaultEnemy(enemy: DungeonMapEnemyModel): TemplateResult {\n const name = enemy.enemy?.name ?? enemy.placement.id;\n const fallback = name\n .split(/\\s+/)\n .map((part) => part[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n return html`\n <span class=\"enemy-marker\" data-boss=${String(enemy.enemy?.isBoss === true)}>\n ${\n enemy.imageUrl\n ? html`<img class=\"enemy-image\" src=${enemy.imageUrl} alt=\"\" draggable=\"false\" />`\n : fallback\n }\n </span>\n `;\n }\n\n #renderMapSelector(model: DungeonMapRenderModel): TemplateResult | typeof nothing {\n const maps = model.dungeon.mapImages;\n if (!this.showMapSelector || maps.length <= 1) {\n return nothing;\n }\n\n return html`\n <div class=\"map-selector\" part=\"map-selector\" role=\"tablist\" aria-label=\"Dungeon maps\">\n ${repeat(\n maps,\n (mapImage) => mapImage.id,\n (mapImage, index) => html`\n <button\n class=\"map-selector-button\"\n id=${`map-tab-${index}`}\n part=\"map-selector-button\"\n type=\"button\"\n role=\"tab\"\n aria-controls=\"map-stage\"\n aria-selected=${String(mapImage.id === this._selectedMap?.id)}\n tabindex=${mapImage.id === this._selectedMap?.id ? \"0\" : \"-1\"}\n data-map-index=${String(index)}\n @click=${() => this.#selectMap(mapImage)}\n @keydown=${this.#handleMapSelectorKeydown}\n >\n ${this.#mapLabel(mapImage)}\n </button>\n `,\n )}\n </div>\n `;\n }\n\n #renderControls(): TemplateResult | typeof nothing {\n if (!this.showControls || !this._model) {\n return nothing;\n }\n return html`\n <div class=\"controls\" part=\"controls\" role=\"group\" aria-label=\"Map zoom controls\">\n <button\n class=\"control-button\"\n part=\"control-button\"\n type=\"button\"\n aria-label=\"Zoom out\"\n title=\"Zoom out\"\n @click=${this.zoomOut}\n >\n −\n </button>\n <button\n class=\"control-button\"\n part=\"control-button\"\n type=\"button\"\n aria-label=\"Fit map\"\n title=\"Fit map\"\n @click=${this.fitToView}\n >\n ◇\n </button>\n <button\n class=\"control-button\"\n part=\"control-button\"\n type=\"button\"\n aria-label=\"Zoom in\"\n title=\"Zoom in\"\n @click=${this.zoomIn}\n >\n +\n </button>\n </div>\n `;\n }\n\n #renderStatus(): TemplateResult | typeof nothing {\n if (this._status === \"ready\") {\n return nothing;\n }\n\n const loading = this._status === \"loading\";\n const empty = this._status === \"empty\";\n const title = loading ? \"Charting dungeon\" : empty ? \"Choose a dungeon\" : \"Map unavailable\";\n const copy = loading\n ? \"Loading Fellowship data and preparing the tactical layer.\"\n : empty\n ? \"Set the dungeon-id attribute or dungeonId property to render a map.\"\n : (this._error?.message ?? \"An unexpected map error occurred.\");\n\n return html`\n <div class=\"status\" part=\"status\" role=${this._status === \"error\" ? \"alert\" : \"status\"}>\n <div class=\"status-card\">\n <div class=\"status-mark\" aria-hidden=\"true\"><span>${loading ? \"…\" : \"✦\"}</span></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 #mapLabel(mapImage: ImageAssetRef): string {\n return this._mapLabels.get(mapImage.id) ?? \"Map\";\n }\n\n #selectMap(mapImage: ImageAssetRef): void {\n if (mapImage.id === this._selectedMap?.id) {\n return;\n }\n this.mapImageId = mapImage.id;\n this.dispatchEvent(\n new CustomEvent<FellowshipMapImageChangeDetail>(\"fellowship-map-image-change\", {\n ...eventOptions,\n detail: { mapImageId: mapImage.id, mapImage },\n }),\n );\n }\n\n #handleMapSelectorKeydown = (event: KeyboardEvent): void => {\n if (![\"ArrowLeft\", \"ArrowRight\", \"Home\", \"End\"].includes(event.key)) {\n return;\n }\n const maps = this._data?.dungeon.mapImages ?? [];\n const currentIndex = maps.findIndex(({ id }) => id === this._selectedMap?.id);\n const nextIndex =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? maps.length - 1\n : event.key === \"ArrowRight\"\n ? (currentIndex + 1) % maps.length\n : (currentIndex - 1 + maps.length) % maps.length;\n const nextMap = maps[nextIndex];\n if (nextMap) {\n event.preventDefault();\n this.#selectMap(nextMap);\n void this.updateComplete.then(() => {\n this.renderRoot\n .querySelector<HTMLButtonElement>(`[data-map-index=\"${nextIndex}\"]`)\n ?.focus();\n });\n }\n };\n\n #handleWheel = (event: WheelEvent): void => {\n if (!this._model || this._status !== \"ready\") {\n return;\n }\n event.preventDefault();\n const rect = this._viewportElement?.getBoundingClientRect();\n if (!rect) {\n return;\n }\n const factor = Math.exp(-event.deltaY * 0.0015);\n this.#viewport.zoomAt(factor, event.clientX - rect.left, event.clientY - rect.top);\n this.#commitViewport(\"wheel\");\n };\n\n #handleViewportKeydown = (event: KeyboardEvent): void => {\n if (event.target !== this._viewportElement || !this._model) {\n return;\n }\n const panAmount = 56;\n if (event.key === \"+\" || event.key === \"=\") {\n event.preventDefault();\n this.#viewport.zoomAt(1.35);\n } else if (event.key === \"-\") {\n event.preventDefault();\n this.#viewport.zoomAt(1 / 1.35);\n } else if (event.key === \"0\") {\n event.preventDefault();\n this.#viewport.fit();\n } else if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n this.#viewport.panBy(panAmount, 0);\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault();\n this.#viewport.panBy(-panAmount, 0);\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n this.#viewport.panBy(0, panAmount);\n } else if (event.key === \"ArrowDown\") {\n event.preventDefault();\n this.#viewport.panBy(0, -panAmount);\n } else {\n return;\n }\n this.#commitViewport(\"keyboard\");\n };\n\n #handlePointerDown = (event: PointerEvent): void => {\n if (!this._model || this.#isInteractiveEvent(event)) {\n return;\n }\n const viewport = this._viewportElement;\n const rect = viewport?.getBoundingClientRect();\n if (!viewport || !rect) {\n return;\n }\n viewport.setPointerCapture(event.pointerId);\n const point = { x: event.clientX - rect.left, y: event.clientY - rect.top };\n this.#pointers.set(event.pointerId, point);\n this._isPanning = true;\n if (this.#pointers.size === 1) {\n this.#lastPointer = point;\n } else if (this.#pointers.size === 2) {\n const [first, second] = [...this.#pointers.values()];\n if (first && second) {\n this.#lastPinchDistance = Math.hypot(second.x - first.x, second.y - first.y);\n this.#lastPinchCenter = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 };\n }\n }\n };\n\n #handlePointerMove = (event: PointerEvent): void => {\n if (!this.#pointers.has(event.pointerId)) {\n return;\n }\n const rect = this._viewportElement?.getBoundingClientRect();\n if (!rect) {\n return;\n }\n const point = { x: event.clientX - rect.left, y: event.clientY - rect.top };\n this.#pointers.set(event.pointerId, point);\n\n if (this.#pointers.size === 1 && this.#lastPointer) {\n this.#viewport.panBy(point.x - this.#lastPointer.x, point.y - this.#lastPointer.y);\n this.#lastPointer = point;\n this.#commitViewport(\"pan\");\n return;\n }\n\n if (this.#pointers.size === 2) {\n const [first, second] = [...this.#pointers.values()];\n if (!first || !second) {\n return;\n }\n const distance = Math.hypot(second.x - first.x, second.y - first.y);\n const center = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 };\n if (this.#lastPinchDistance > 0) {\n this.#viewport.zoomAt(distance / this.#lastPinchDistance, center.x, center.y);\n }\n if (this.#lastPinchCenter) {\n this.#viewport.panBy(\n center.x - this.#lastPinchCenter.x,\n center.y - this.#lastPinchCenter.y,\n );\n }\n this.#lastPinchDistance = distance;\n this.#lastPinchCenter = center;\n this.#commitViewport(\"pinch\");\n }\n };\n\n #handlePointerEnd = (event: PointerEvent): void => {\n this.#pointers.delete(event.pointerId);\n if (this.#pointers.size === 1) {\n this.#lastPointer = [...this.#pointers.values()][0] ?? null;\n } else if (this.#pointers.size === 0) {\n this.#lastPointer = null;\n this.#lastPinchCenter = null;\n this.#lastPinchDistance = 0;\n this._isPanning = false;\n }\n };\n\n #isInteractiveEvent(event: PointerEvent): boolean {\n return event\n .composedPath()\n .some(\n (node) =>\n node instanceof HTMLElement &&\n node.matches(\"button, a, input, select, textarea, [contenteditable='true']\"),\n );\n }\n\n #handleResize(): void {\n const viewport = this._viewportElement;\n const mapImage = this._selectedMap;\n if (!viewport || !mapImage) {\n return;\n }\n this.#viewport.setDimensions(\n viewport.clientWidth,\n viewport.clientHeight,\n mapImage.width,\n mapImage.height,\n );\n this.#commitViewport(\"resize\");\n }\n\n #commitViewport(source: ViewportChangeSource): void {\n this._viewportState = this.#viewport.state;\n this.dispatchEvent(\n new CustomEvent<FellowshipMapViewportChangeDetail>(\"fellowship-map-viewport-change\", {\n ...eventOptions,\n detail: { viewport: this.#viewport.state, source },\n }),\n );\n }\n\n #queueRendererError(key: string, code: string, error: unknown): void {\n if (this.#rendererFailures.has(key)) {\n return;\n }\n this.#rendererFailures.add(key);\n this.#pendingRendererErrors.push({\n code,\n message: error instanceof Error ? error.message : String(error),\n cause: error,\n });\n }\n\n #flushRendererErrors(): void {\n if (this.#pendingRendererErrors.length === 0) {\n return;\n }\n const errors = this.#pendingRendererErrors;\n this.#pendingRendererErrors = [];\n for (const detail of errors) {\n this.dispatchEvent(\n new CustomEvent<FellowshipMapErrorDetail>(\"fellowship-map-error\", {\n ...eventOptions,\n detail,\n }),\n );\n }\n }\n}\n\nexport const defineFellowshipDungeonMap = (): void => {\n if (\n typeof globalThis.customElements !== \"undefined\" &&\n !globalThis.customElements.get(\"fellowship-dungeon-map\")\n ) {\n globalThis.customElements.define(\"fellowship-dungeon-map\", FellowshipDungeonMapElement);\n }\n};\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"fellowship-dungeon-map\": FellowshipDungeonMapElement;\n }\n}\n"],"mappings":";;;;;;;;AAwBA,IAAI,GAEE,KAAkB,MAA+B;CACrD,IAAI,GAAQ,SACV,MAAM,IAAI,EACR,WACA,yCACA,EAAO,MACT;AAEJ,GAEM,WACJ,MAAuB,QAAQ,IAAI;CACjC,OAAO;CACP,OAAO;CACP,OAAO;AACT,CAAC,CAAC,CACC,MAAM,CAAC,GAAe,GAAa,OAAoB;CACtD,IAAM,IAAW,EAAe;CAEhC,IAAK,EAAuC,kBAAkB,GAC5D,MAAM,IAAI,EACR,sBACA,+CAA+C,OAAO,EAAS,aAAa,EAAE,EAChF;CAGF,OAAO;EACL;EACA,UAAU,IAAI,IAAI,EAAc,QAAQ,KAAK,MAAY,CAAC,EAAQ,IAAI,CAAO,CAAC,CAAC;EAC/E,SAAS,IAAI,IAAI,EAAY,QAAQ,KAAK,MAAU,CAAC,EAAM,IAAI,CAAK,CAAC,CAAC;CACxE;AACF,CAAC,CAAC,CACD,OAAO,MAAmB;CAKzB,MAJA,IAAqB,KAAA,GACjB,aAAiB,IACb,IAEF,IAAI,EACR,oBACA,6CACA,CACF;AACF,CAAC,GAEI,IAGI,KACX,IAA8C,CAAC,OACrB;CAC1B,MAAM,YAAY,GAAmB,GAA+C;EAClF,EAAe,CAAM;EACrB,IAAM,IAAO,MAAM,EAAgB;EACnC,EAAe,CAAM;EAErB,IAAM,IAAU,EAAK,SAAS,IAAI,CAAS;EAC3C,IAAI,CAAC,GACH,MAAM,IAAI,EACR,qBACA,uBAAuB,EAAU,uCACnC;EAGF,OAAO;GACL,UAAU,EAAK;GACf;GACA,SAAS,EAAK;EAChB;CACF;CAEA,gBAAgB,GAA8B;EAC5C,OAAO,EAAQ,eACX,EAAY,GAAO,EAAE,SAAS,EAAQ,aAAa,CAAC,IACpD,EAAY,CAAK;CACvB;AACF,ICnGa,IAAmB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GCE7B,IAAmB,MACnB,IAAmB,MACnB,IAAmB,KACnB,IAAoB,KAEpB,KAAS,GAAe,GAAiB,MAC7C,KAAK,IAAI,GAAS,KAAK,IAAI,GAAS,CAAK,CAAC,GAEtC,KAAmB,MAA6D;CACpF,IAAM,IAAK,EAAQ,KAAK,EAAE,kBAAe,EAAS,CAAC,GAC7C,IAAK,EAAQ,KAAK,EAAE,kBAAe,EAAS,CAAC,GAC7C,IAAW,KAAK,IAAI,GAAG,CAAE,GACzB,IAAW,KAAK,IAAI,GAAG,CAAE,GACzB,IAAW,KAAK,IAAI,GAAG,CAAE,GACzB,IAAW,KAAK,IAAI,GAAG,CAAE;CAE/B,OAAO;EACL,GAAG;EACH,GAAG;EACH,OAAO,IAAW;EAClB,QAAQ,IAAW;CACrB;AACF,GAEM,KAAgB,MAA2C;CAC/D,IAAM,IAAU,EAAO,IAAI,EAAO,QAAQ,GACpC,IAAU,EAAO,IAAI,EAAO,SAAS,GACrC,IAAQ,KAAK,IAAI,GAAkB,EAAO,QAAQ,IAAmB,CAAC,GACtE,IAAS,KAAK,IAAI,GAAmB,EAAO,SAAS,IAAmB,CAAC;CAI/E,OAAO;EAAE,GAHC,EAAM,IAAU,IAAQ,GAAG,GAAG,KAAK,IAAI,GAAG,IAAI,CAAK,CAGpD;EAAG,GAFF,EAAM,IAAU,IAAS,GAAG,GAAG,KAAK,IAAI,GAAG,IAAI,CAAM,CAEnD;EAAG,OAAO,KAAK,IAAI,GAAO,CAAC;EAAG,QAAQ,KAAK,IAAI,GAAQ,CAAC;CAAE;AACxE,GAEM,KAAe,OAA0D;CAC7E,GAAG,EAAQ,QAAQ,GAAK,MAAU,IAAM,EAAM,SAAS,GAAG,CAAC,IAAI,EAAQ;CACvE,GAAG,EAAQ,QAAQ,GAAK,MAAU,IAAM,EAAM,SAAS,GAAG,CAAC,IAAI,EAAQ;AACzE,IAEM,KAAc,MAA4D;CAC9E,IAAM,IAAS,EACZ,KAAK,EAAE,eAAY,GAAO,YAAY,CAAC,CACvC,QAAQ,MAA2B,OAAO,KAAU,QAAQ;CAC/D,OAAO,EAAO,SAAS,IAAI,EAAO,QAAQ,GAAK,MAAU,IAAM,GAAO,CAAC,IAAI;AAC7E,GAEa,KACX,GACA,MACyB;CACzB,IAAM,IAAc,EAAgB,CAAO;CAC3C,OAAO;EACL;EACA;EACA;EACA,cAAc,EAAa,CAAW;EACtC,QAAQ,EAAY,CAAO;EAC3B,cAAc,EAAW,CAAO;CAClC;AACF,GCzDa,KAAsB,MACjC,MAAa,QACb,OAAO,SAAS,EAAS,CAAC,KAC1B,OAAO,SAAS,EAAS,CAAC,KAC1B,EAAS,KAAK,KACd,EAAS,KAAK,KACd,EAAS,KAAK,KACd,EAAS,KAAK,GAEH,KACX,GACA,GACA,MAC0B;CAC1B,IAAM,IAAgC,CAAC,GACjC,oBAAS,IAAI,IAAoE;CA+DvF,OA7DA,EAAK,QAAQ,QAAQ,SAAS,GAAW,MAAmB;EAC1D,IAAI,EAAU,eAAe,EAAS,IACpC;EAGF,IAAI,CAAC,EAAmB,EAAU,WAAW,GAAG;GAC9C,EAAS,KAAK;IACZ,MAAM;IACN,SAAS,WAAW,EAAU,GAAG;IACjC;IACA,SAAS,EAAU;GACrB,CAAC;GACD;EACF;EAEA,IAAM,IAAQ,EAAK,QAAQ,IAAI,EAAU,EAAE,KAAK;EAChD,AAAK,KACH,EAAS,KAAK;GACZ,MAAM;GACN,SAAS,gCAAgC,EAAU,GAAG;GACtD;GACA,SAAS,EAAU;EACrB,CAAC;EAGH,IAAM,IAAQ,IAAQ,EAAU,EAAM,QAAQ;GAAE,MAAM;GAAS,MAAM;EAAM,CAAC,IAAI,MAC5E,IAA0B;EAC9B,IAAI,GACF,IAAI;GACF,IAAW,EAAO,gBAAgB,CAAK;EACzC,SAAS,GAAgB;GACvB,EAAS,KAAK;IACZ,MAAM;IACN,SAAS,uCAAuC,EAAU,GAAG,IAAI,OAAO,CAAK,EAAE;IAC/E;IACA,SAAS,EAAU;GACrB,CAAC;EACH;OACK,AAAI,KACT,EAAS,KAAK;GACZ,MAAM;GACN,SAAS,mCAAmC,EAAU,GAAG;GACzD;GACA,SAAS,EAAU;EACrB,CAAC;EAGH,IAAM,IAA8B;GAClC,YAAY,GAAG,EAAK,QAAQ,GAAG,GAAG,EAAS,GAAG,GAAG;GACjD;GACA;GACA,UAAU,EAAU;GACpB;EACF,GACM,IACJ,EAAU,YAAY,OAAO,aAAa,MAAmB,SAAS,EAAU,WAC5E,IAAQ,EAAO,IAAI,CAAG,KAAK;GAAE,IAAI,EAAU;GAAS,SAAS,CAAC;EAAE;EAEtE,AADA,EAAM,QAAQ,KAAK,CAAK,GACxB,EAAO,IAAI,GAAK,CAAK;CACvB,CAAC,GAEM;EACL,SAAS,EAAK;EACd;EACA,QAAQ,CAAC,GAAG,EAAO,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,OAAI,iBAAc,EAA2B,GAAI,CAAO,CAAC;EAC7F;CACF;AACF,GAEa,KAAoB,GAAyB,MAA6B;CAGrF,IAAM,KAFU,EAAS,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAS,GAAA,CAC5B,QAAQ,iBAAiB,EAAE,CAAC,CAAC,QAAQ,aAAa,EAClE,CAAA,CACX,WAAW,UAAU,GAAG,CAAC,CACzB,QAAQ,qBAAqB,OAAO,CAAC,CACrC,KAAK,GACF,IAAc,EAAQ,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,YAAY;CAG9D,QAFc,EAAM,MAAM,KAAK,CAAC,CAAC,QAAQ,MAAS,EAAK,YAAY,MAAM,CAC3D,CAAA,CAAM,KAAK,GAAG,KAAK,KAAS,MAAA,CAC7B,QAAQ,aAAa,MAAW,EAAO,YAAY,CAAC;AACnE,GCtGM,KAAS,GAAe,GAAiB,MAC7C,KAAK,IAAI,GAAS,KAAK,IAAI,GAAS,CAAK,CAAC,GAE/B,IAAb,MAAgC;;YACb,aACC,aACN,aACC,aACL,aACH,aACA,kBAEK;;CARV;CACA;CACA;CACA;CACA;CACA;CACA;CAIA,IAAI,QAAuB;EACzB,OAAO;GAAE,MAAM,KAAKA;GAAO,OAAO,KAAK;GAAO,GAAG,KAAKC;GAAI,GAAG,KAAKC;EAAG;CACvE;CAEA,IAAI,QAAgB;EAClB,OAAO,KAAK,WAAW,KAAKF;CAC9B;CAEA,IAAI,WAAmB;EAIrB,OAHI,CAAC,KAAKG,MAAkB,CAAC,KAAKC,MAAmB,CAAC,KAAKC,MAAa,CAAC,KAAKC,KACrE,IAEF,KAAK,IAAI,KAAKH,KAAiB,KAAKE,IAAW,KAAKD,KAAkB,KAAKE,EAAU;CAC9F;CAEA,cACE,GACA,GACA,GACA,GACe;EACf,IAAM,IAAgB,KAAK,OACrB,IAAkB,KAAKD,MACxB,KAAKF,KAAiB,IAAI,KAAKF,MAAM,IAAgB,KAAKI,KAC3D,IACE,IAAkB,KAAKC,MACxB,KAAKF,KAAkB,IAAI,KAAKF,MAAM,IAAgB,KAAKI,KAC5D;EAUJ,OARA,KAAKH,KAAiB,KAAK,IAAI,GAAG,CAAa,GAC/C,KAAKC,KAAkB,KAAK,IAAI,GAAG,CAAc,GACjD,KAAKC,KAAY,KAAK,IAAI,GAAG,CAAQ,GACrC,KAAKC,KAAa,KAAK,IAAI,GAAG,CAAS,GAEvC,KAAKL,KAAK,KAAKE,KAAiB,IAAI,IAAkB,KAAKE,KAAY,KAAK,OAC5E,KAAKH,KAAK,KAAKE,KAAkB,IAAI,IAAkB,KAAKE,KAAa,KAAK,OAC9E,KAAKC,GAAU,GACR,KAAK;CACd;CAEA,MAAqB;EAInB,OAHA,KAAKP,KAAQ,GACb,KAAKC,MAAM,KAAKE,KAAiB,KAAKE,KAAY,KAAK,SAAS,GAChE,KAAKH,MAAM,KAAKE,KAAkB,KAAKE,KAAa,KAAK,SAAS,GAC3D,KAAK;CACd;CAEA,OACE,GACA,IAAS,KAAKH,KAAiB,GAC/B,IAAS,KAAKC,KAAkB,GACjB;EACf,IAAM,IAAgB,KAAK,OACrB,KAAQ,IAAS,KAAKH,MAAM,GAC5B,KAAQ,IAAS,KAAKC,MAAM;EAKlC,OAJA,KAAKF,KAAQ,EAAM,KAAKA,KAAQ,GAAQ,GAAG,KAAK,IAAI,GAAG,KAAK,OAAO,CAAC,GACpE,KAAKC,KAAK,IAAS,IAAO,KAAK,OAC/B,KAAKC,KAAK,IAAS,IAAO,KAAK,OAC/B,KAAKK,GAAU,GACR,KAAK;CACd;CAEA,MAAM,GAAgB,GAA+B;EAInD,OAHA,KAAKN,MAAM,GACX,KAAKC,MAAM,GACX,KAAKK,GAAU,GACR,KAAK;CACd;CAEA,KAAkB;EAChB,IAAM,IAAc,KAAKF,KAAY,KAAK,OACpC,IAAe,KAAKC,KAAa,KAAK;EAM5C,AAJA,KAAKL,KACH,KAAe,KAAKE,MACf,KAAKA,KAAiB,KAAe,IACtC,EAAM,KAAKF,IAAI,KAAKE,KAAiB,GAAa,CAAC,GACzD,KAAKD,KACH,KAAgB,KAAKE,MAChB,KAAKA,KAAkB,KAAgB,IACxC,EAAM,KAAKF,IAAI,KAAKE,KAAkB,GAAc,CAAC;CAC7D;AACF,GC1DM,IAAe;CAAE,SAAS;CAAM,UAAU;AAAK,GAU9C,IAAA,cAA0C,EAAW;;gCAGO,sBACkB,0BACI,wBACb,yBACH,yBACA,+BACO,0BACN,2BACO,mBAClB,0BAGI,mBAErB,uBACe,mBACX,oBACQ,0BACF,4BACL;GAAE,MAAM;GAAG,OAAO;GAAG,GAAG;GAAG,GAAG;EAAE,qBACnD,+CACS,IAAI,IAA6C,8CACjD,IAAI,IAA6C,qCAC1D,IAAI,IAAoB,aAKT,gBACF,gBAC9B,aACE,aAC0B,gBAC7B,IAAI,EAAmB,6BACvB,IAAI,IAA6B,aACN,gBAClB,aACsB,gCACvB,IAAI,IAAY,aACiB,CAAC,cA0nBzB,MAA+B;GAC1D,IAAI,CAAC;IAAC;IAAa;IAAc;IAAQ;GAAK,CAAC,CAAC,SAAS,EAAM,GAAG,GAChE;GAEF,IAAM,IAAO,KAAK,OAAO,QAAQ,aAAa,CAAC,GACzC,IAAe,EAAK,WAAW,EAAE,YAAS,MAAO,KAAK,cAAc,EAAE,GACtE,IACJ,EAAM,QAAQ,SACV,IACA,EAAM,QAAQ,QACZ,EAAK,SAAS,IACd,EAAM,QAAQ,gBACX,IAAe,KAAK,EAAK,UACzB,IAAe,IAAI,EAAK,UAAU,EAAK,QAC5C,IAAU,EAAK;GACrB,AAAI,MACF,EAAM,eAAe,GACrB,KAAKI,GAAW,CAAO,GACvB,KAAU,eAAe,WAAW;IAClC,KAAK,WACF,cAAiC,oBAAoB,EAAU,GAAG,CAAC,EAClE,MAAM;GACZ,CAAC;EAEL,cAEgB,MAA4B;GAC1C,IAAI,CAAC,KAAK,UAAU,KAAK,YAAY,SACnC;GAEF,EAAM,eAAe;GACrB,IAAM,IAAO,KAAK,kBAAkB,sBAAsB;GAC1D,IAAI,CAAC,GACH;GAEF,IAAM,IAAS,KAAK,IAAI,CAAC,EAAM,SAAS,KAAM;GAE9C,AADA,KAAKC,GAAU,OAAO,GAAQ,EAAM,UAAU,EAAK,MAAM,EAAM,UAAU,EAAK,GAAG,GACjF,KAAKC,GAAgB,OAAO;EAC9B,cAE0B,MAA+B;GACnD,QAAM,WAAW,KAAK,oBAAoB,CAAC,KAAK,SAIpD;QAAI,EAAM,QAAQ,OAAO,EAAM,QAAQ,KAErC,AADA,EAAM,eAAe,GACrB,KAAKD,GAAU,OAAO,IAAI;SACrB,IAAI,EAAM,QAAQ,KAEvB,AADA,EAAM,eAAe,GACrB,KAAKA,GAAU,OAAO,IAAI,IAAI;SACzB,IAAI,EAAM,QAAQ,KAEvB,AADA,EAAM,eAAe,GACrB,KAAKA,GAAU,IAAI;SACd,IAAI,EAAM,QAAQ,aAEvB,AADA,EAAM,eAAe,GACrB,KAAKA,GAAU,MAAM,IAAW,CAAC;SAC5B,IAAI,EAAM,QAAQ,cAEvB,AADA,EAAM,eAAe,GACrB,KAAKA,GAAU,MAAM,KAAY,CAAC;SAC7B,IAAI,EAAM,QAAQ,WAEvB,AADA,EAAM,eAAe,GACrB,KAAKA,GAAU,MAAM,GAAG,EAAS;SAC5B,IAAI,EAAM,QAAQ,aAEvB,AADA,EAAM,eAAe,GACrB,KAAKA,GAAU,MAAM,GAAG,GAAU;SAElC;IAEF,KAAKC,GAAgB,UAAU;GAF7B;EAGJ,cAEsB,MAA8B;GAClD,IAAI,CAAC,KAAK,UAAU,KAAKC,GAAoB,CAAK,GAChD;GAEF,IAAM,IAAW,KAAK,kBAChB,IAAO,GAAU,sBAAsB;GAC7C,IAAI,CAAC,KAAY,CAAC,GAChB;GAEF,EAAS,kBAAkB,EAAM,SAAS;GAC1C,IAAM,IAAQ;IAAE,GAAG,EAAM,UAAU,EAAK;IAAM,GAAG,EAAM,UAAU,EAAK;GAAI;GAG1E,IAFA,KAAKC,GAAU,IAAI,EAAM,WAAW,CAAK,GACzC,KAAK,aAAa,IACd,KAAKA,GAAU,SAAS,GAC1B,KAAKC,KAAe;QACf,IAAI,KAAKD,GAAU,SAAS,GAAG;IACpC,IAAM,CAAC,GAAO,KAAU,CAAC,GAAG,KAAKA,GAAU,OAAO,CAAC;IACnD,AAAI,KAAS,MACX,KAAKE,KAAqB,KAAK,MAAM,EAAO,IAAI,EAAM,GAAG,EAAO,IAAI,EAAM,CAAC,GAC3E,KAAKC,KAAmB;KAAE,IAAI,EAAM,IAAI,EAAO,KAAK;KAAG,IAAI,EAAM,IAAI,EAAO,KAAK;IAAE;GAEvF;EACF,cAEsB,MAA8B;GAClD,IAAI,CAAC,KAAKH,GAAU,IAAI,EAAM,SAAS,GACrC;GAEF,IAAM,IAAO,KAAK,kBAAkB,sBAAsB;GAC1D,IAAI,CAAC,GACH;GAEF,IAAM,IAAQ;IAAE,GAAG,EAAM,UAAU,EAAK;IAAM,GAAG,EAAM,UAAU,EAAK;GAAI;GAG1E,IAFA,KAAKA,GAAU,IAAI,EAAM,WAAW,CAAK,GAErC,KAAKA,GAAU,SAAS,KAAK,KAAKC,IAAc;IAGlD,AAFA,KAAKJ,GAAU,MAAM,EAAM,IAAI,KAAKI,GAAa,GAAG,EAAM,IAAI,KAAKA,GAAa,CAAC,GACjF,KAAKA,KAAe,GACpB,KAAKH,GAAgB,KAAK;IAC1B;GACF;GAEA,IAAI,KAAKE,GAAU,SAAS,GAAG;IAC7B,IAAM,CAAC,GAAO,KAAU,CAAC,GAAG,KAAKA,GAAU,OAAO,CAAC;IACnD,IAAI,CAAC,KAAS,CAAC,GACb;IAEF,IAAM,IAAW,KAAK,MAAM,EAAO,IAAI,EAAM,GAAG,EAAO,IAAI,EAAM,CAAC,GAC5D,IAAS;KAAE,IAAI,EAAM,IAAI,EAAO,KAAK;KAAG,IAAI,EAAM,IAAI,EAAO,KAAK;IAAE;IAY1E,AAXI,KAAKE,KAAqB,KAC5B,KAAKL,GAAU,OAAO,IAAW,KAAKK,IAAoB,EAAO,GAAG,EAAO,CAAC,GAE1E,KAAKC,MACP,KAAKN,GAAU,MACb,EAAO,IAAI,KAAKM,GAAiB,GACjC,EAAO,IAAI,KAAKA,GAAiB,CACnC,GAEF,KAAKD,KAAqB,GAC1B,KAAKC,KAAmB,GACxB,KAAKL,GAAgB,OAAO;GAC9B;EACF,cAEqB,MAA8B;GAEjD,AADA,KAAKE,GAAU,OAAO,EAAM,SAAS,GACjC,KAAKA,GAAU,SAAS,IAC1B,KAAKC,KAAe,CAAC,GAAG,KAAKD,GAAU,OAAO,CAAC,CAAC,CAAC,MAAM,OAC9C,KAAKA,GAAU,SAAS,MACjC,KAAKC,KAAe,MACpB,KAAKE,KAAmB,MACxB,KAAKD,KAAqB,GAC1B,KAAK,aAAa;EAEtB;;;gBArzByB,CAAC,GAAmB,CAAgB;;CA8B7D;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,oBAAmC;EAGjC,AAFA,MAAM,kBAAkB,GACxB,EAAwB,KAAK,aAAa,GAC1C,KAAKE,OAAoB,IAAI,qBAAqB,KAAKC,GAAc,CAAC;CACxE;CAEA,uBAAsC;EAGpC,AAFA,KAAKC,IAAkB,MAAM,GAC7B,KAAKF,IAAiB,WAAW,GACjC,MAAM,qBAAqB;CAC7B;CAEA,aAAgC,GAAqC;EACnE,MAAM,aAAa,CAAO;EAC1B,IAAM,IAAW,KAAK;EACtB,AAAI,KACF,KAAKA,IAAiB,QAAQ,CAAQ;CAE1C;CAEA,QAA2B,GAAqC;EAI9D,IAHA,MAAM,QAAQ,CAAO,GACrB,KAAKG,GAAqB,GAEtB,EAAQ,IAAI,WAAW,KAAK,EAAQ,IAAI,YAAY,KAAK,EAAQ,IAAI,cAAc,GAAG;GACxF,qBAAqB;IACnB,AAAI,KAAK,eACP,KAAUC,GAAa;GAE3B,CAAC;GACD;EACF;EAUA,AARI,EAAQ,IAAI,YAAY,KAAK,KAAK,SACpC,qBAAqB;GACnB,AAAI,KAAK,eACP,KAAUC,GAAoB,EAAI;EAEtC,CAAC,GAGC,EAAQ,IAAI,SAAS,KAAK,EAAQ,IAAI,SAAS,MAAM,KAAA,MACvD,KAAKZ,GAAU,UAAU,KAAK,IAAI,GAAG,KAAK,OAAO,GACjD,KAAKA,GAAU,OAAO,CAAC,GACvB,qBAAqB,KAAKC,GAAgB,QAAQ,CAAC;CAEvD;CAEA,WAA8B,GAA+B;EAa3D,AAZA,MAAM,WAAW,CAAO,IAGtB,EAAQ,IAAI,aAAa,KACzB,EAAQ,IAAI,aAAa,KACzB,EAAQ,IAAI,mBAAmB,MAE/B,KAAKY,GAAkB,MAAM,IAE3B,EAAQ,IAAI,QAAQ,KAAK,EAAQ,IAAI,aAAa,KAAK,EAAQ,IAAI,aAAa,MAClF,KAAKC,GAAwB,IAE3B,EAAQ,IAAI,OAAO,KAAK,EAAQ,IAAI,mBAAmB,MACzD,KAAKC,GAAkB;CAE3B;CAEA,YAAkB;EAEhB,AADA,KAAKf,GAAU,IAAI,GACnB,KAAKC,GAAgB,KAAK;CAC5B;CAEA,SAAe;EAEb,AADA,KAAKD,GAAU,OAAO,IAAI,GAC1B,KAAKC,GAAgB,QAAQ;CAC/B;CAEA,UAAgB;EAEd,AADA,KAAKD,GAAU,OAAO,IAAI,IAAI,GAC9B,KAAKC,GAAgB,QAAQ;CAC/B;CAEA,QAAc;EACZ,KAAUU,GAAa;CACzB;CAEA,IAAI,cAA4C;EAC9C,OAAO,KAAK;CACd;CAEA,SAAkC;EAChC,IAAM,IAAQ,KAAK,QACb,IAAW,KAAK,gBAChB,IAAmB,KAAKK,IACxB,IAAa,IACf;GACE,OAAO,GAAG,EAAM,SAAS,MAAM;GAC/B,QAAQ,GAAG,EAAM,SAAS,OAAO;GACjC,WAAW,eAAe,EAAS,EAAE,MAAM,EAAS,EAAE,eAAe,EAAS,MAAM;GACpF,kCAAkC,OAAO,IAAI,EAAS,KAAK;EAC7D,IACA,CAAC;EAEL,OAAO,CAAI;2CAC4B,KAAKC,GAAa;;;;;;;;yBAQpC,OAAO,KAAK,UAAU,EAAE;mBAC9B,KAAKC,GAAa;qBAChB,KAAKC,GAAuB;yBACxB,KAAKC,GAAmB;yBACxB,KAAKC,GAAmB;uBAC1B,KAAKC,GAAkB;2BACnB,KAAKA,GAAkB;;YAGtC,IACI,CAAI;;;;2BAIO,MAAqB,IAAU,IAAU,WAAW;sCACzC,EAAiB;4BAC3B,EAAS,CAAU,EAAE;;;;;;mCAMd,GAAG,EAAM,QAAQ,QAAQ,EAAM,QAAQ,GAAG,IAAI,KAAKC,GAAU,EAAM,QAAQ,IAAI;+BACnF,EAAM,SAAS,MAAM;gCACpB,EAAM,SAAS,OAAO;;;wBAI9B,KAAK,kBACD,CAAI,iCACJ,KAAKC,GAAc,CAAK,EAC7B;;;oBAGH,KAAKC,GAAmB,CAAK,EAAE,GAAG,KAAKC,GAAgB,EAAE;oBAEzD,EAAM,OAAO,WAAW,KAAK,KAAK,YAAY,UAC1C,CAAI,mEACJ,EACL;oBAEH,EACL;YACC,KAAKC,GAAc,EAAE;;;;;;;;CAQ/B;CAEA,IAAIV,KAAuB;EAIzB,OAHI,KAAK,QACA,GAAG,KAAK,MAAM,QAAQ,QAAQ,KAAK,MAAM,QAAQ,GAAG,gBAEtD;CACT;CAEA,IAAID,KAA6C;EAC/C,IACE,CAAC,KAAK,mBACN,CAAC,KAAK,UACN,KAAK,OAAO,QAAQ,UAAU,UAAU,KACxC,CAAC,KAAK,cAEN,OAAO;EAET,IAAM,IAAQ,KAAK,OAAO,QAAQ,UAAU,WACzC,MAAa,EAAS,OAAO,KAAK,cAAc,EACnD;EACA,OAAO,KAAS,IAAI,WAAW,MAAU;CAC3C;CAEA,MAAML,KAA8B;EAClC,IAAM,IAAY,KAAK,UAAU,KAAK,GAChC,IAAQ,EAAE,KAAKiB;EAUrB,IATA,KAAKC,MAAgB,GACrB,KAAKpB,IAAkB,MAAM,GAC7B,KAAKA,KAAmB,IAAI,gBAAgB,GAC5C,KAAK,QAAQ,MACb,KAAK,SAAS,MACd,KAAK,eAAe,MACpB,KAAK,SAAS,MACd,KAAKI,GAAkB,MAAM,GAEzB,CAAC,GAAW;GACd,KAAK,UAAU;GACf;EACF;EAEA,KAAK,UAAU;EACf,IAAM,IACJ,KAAK,cACL,EACE,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,IAAI,CAAC,CAC7D;EACF,KAAKiB,KAAgB;EAErB,IAAI;GACF,IAAM,IAAO,MAAM,EAAO,YAAY,GAAW,KAAKrB,GAAiB,MAAM;GAC7E,IAAI,MAAU,KAAKmB,MAAc,KAAKnB,GAAiB,OAAO,SAC5D;GAEF,IAAI,EAAK,SAAS,kBAAkB,GAClC,MAAM,IAAI,EACR,sBACA,+CAA+C,OAAO,EAAK,SAAS,aAAa,EAAE,EACrF;GAGF,AADA,KAAK,QAAQ,GACb,MAAM,KAAKG,GAAoB,EAAI;EACrC,SAAS,GAAgB;GACvB,IAAI,MAAU,KAAKgB,MAAc,KAAKnB,GAAiB,OAAO,SAC5D;GAEF,KAAKsB,GAAU,CAAK;EACtB;CACF;CAEA,MAAMnB,GAAoB,GAAmC;EAC3D,IAAM,IAAO,KAAK,OACZ,IAAS,KAAKkB;EACpB,IAAI,CAAC,KAAQ,CAAC,GACZ;EAGF,IAAM,IAAW,KAAK,aAClB,EAAK,QAAQ,UAAU,MAAM,EAAE,YAAS,MAAO,KAAK,UAAU,IAC9D,EAAK,QAAQ,UAAU;EAE3B,IAAI,CAAC,GAAU;GACb,KAAKC,GAAU;IACb,MAAM;IACN,SAAS,KAAK,aACV,yBAAyB,KAAK,WAAW,8BACzC;GACN,CAAC;GACD;EACF;EAEA,IAAM,IAAc,EAAE,KAAKF,IACrB,IAAQ,EAA4B,GAAM,GAAU,CAAM;EAKhE,AAJA,KAAK,SAAS,GACd,KAAK,eAAe,GACpB,KAAK,UAAU,WACf,KAAK,SAAS,MACd,MAAM,KAAK;EAEX,IAAM,IAAW,KAAK;EACtB,AAAI,MACF,KAAK7B,GAAU,UAAU,KAAK,IAAI,GAAG,KAAK,OAAO,GACjD,KAAKA,GAAU,cACb,EAAS,aACT,EAAS,cACT,EAAS,OACT,EAAS,MACX,GACI,KACF,KAAKA,GAAU,IAAI,GAErB,KAAK,iBAAiB,KAAKA,GAAU,OACrC,MAAM,KAAK;EAGb,IAAI;GAEF,IADA,MAAM,KAAKgC,GAAS,GAAU,GAAQ,CAAW,GAC7C,MAAgB,KAAKH,IACvB;GASF,AAPA,KAAK,UAAU,SACf,KAAK,cACH,IAAI,YAAsC,wBAAwB;IAChE,GAAG;IACH,QAAQ;KAAE;KAAO;IAAK;GACxB,CAAC,CACH,GACI,EAAM,SAAS,SAAS,KAC1B,KAAK,cACH,IAAI,YAAwC,0BAA0B;IACpE,GAAG;IACH,QAAQ;KAAE,UAAU,EAAM;KAAU;IAAM;GAC5C,CAAC,CACH;EAEJ,SAAS,GAAgB;GACvB,AAAI,MAAgB,KAAKA,MACvB,KAAKE,GAAU;IACb,MAAM;IACN,SAAS,yCAAyC,OAAO,CAAK,EAAE;IAChE,OAAO;GACT,CAAC;EAEL;CACF;CAEA,MAAMC,GACJ,GACA,GACA,GACe;EACf,IAAM,IAAQ,IAAI,MAAM;EAIxB,IAHA,EAAM,cAAc,aACpB,EAAM,MAAM,EAAO,gBAAgB,CAAQ,GAC3C,MAAM,EAAM,OAAO,GACf,MAAgB,KAAKH,IACvB;EAEF,IAAM,IAAS,KAAK,gBACd,IAAU,GAAQ,WAAW,IAAI;EACvC,IAAI,CAAC,KAAU,CAAC,GACd,MAAU,MAAM,qCAAqC;EAGvD,AADA,EAAQ,UAAU,GAAG,GAAG,EAAO,OAAO,EAAO,MAAM,GACnD,EAAQ,UAAU,GAAO,GAAG,GAAG,EAAS,OAAO,EAAS,MAAM;CAChE;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,YAAsC,wBAAwB;GAChE,GAAG;GACH;EACF,CAAC,CACH;CACF;CAEA,KAAgC;EAC9B,IAAM,IAAQ,KAAK,QACb,oBAAe,IAAI,IAA6C,GAChE,oBAAe,IAAI,IAA6C;EAEtE,IAAI,CAAC,GAAO;GAEV,AADA,KAAK,sBAAsB,GAC3B,KAAK,sBAAsB;GAC3B;EACF;EAEA,IAAI,KAAK,aACP,KAAK,IAAM,KAAS,EAAM,QACxB,KAAK,IAAM,KAAS,EAAM,SACxB,IAAI;GACF,EAAa,IACX,EAAM,YACN,KAAK,YAAY;IACf,OAAO;IACP;IACA,SAAS,EAAM;IACf,UAAU,EAAM;GAClB,CAAC,CACH;EACF,SAAS,GAAgB;GACvB,KAAKI,GAAoB,SAAS,EAAM,cAAc,wBAAwB,CAAK;EACrF;EAMN,IAFA,KAAK,sBAAsB,GAEvB,KAAK,aACP,KAAK,IAAM,KAAS,EAAM,QAAQ;GAChC,IAAM,IAAW,EAAM,QAAQ,KAAK,MAAU,KAAKC,GAAmB,GAAO,CAAK,CAAC,GAC7E,IAAM,KAAKC,GAAU,GAAO,CAAK;GACvC,IAAI;IACF,EAAa,IACX,GACA,KAAK,YAAY;KACf;KACA;KACA,SAAS,EAAM;KACf,UAAU,EAAM;IAClB,CAAC,CACH;GACF,SAAS,GAAgB;IACvB,KAAKF,GAAoB,SAAS,KAAO,wBAAwB,CAAK;GACxE;EACF;EAEF,KAAK,sBAAsB;CAC7B;CAEA,KAA0B;EACxB,IAAM,oBAAS,IAAI,IAAoB,GACjC,IAAU,KAAK,OAAO;EAC5B,IAAI,GACF,KAAK,IAAM,KAAY,EAAQ,WAC7B,IAAI;GACF,EAAO,IACL,EAAS,IACT,KAAK,oBAAoB,GAAU,CAAO,KAAK,EAAiB,GAAU,CAAO,CACnF;EACF,SAAS,GAAgB;GAEvB,AADA,KAAKA,GAAoB,aAAa,EAAS,MAAM,6BAA6B,CAAK,GACvF,EAAO,IAAI,EAAS,IAAI,EAAiB,GAAU,CAAO,CAAC;EAC7D;EAGJ,KAAK,aAAa;CACpB;CAEA,GAAc,GAAuC;EACnD,OAAO,EACL,EAAM,SACL,MAAU,KAAKE,GAAU,GAAO,CAAK,IACrC,MAAU,KAAKC,GAAa,GAAO,CAAK,CAC3C;CACF;CAEA,GAAU,GAA8B,GAAqC;EAC3E,OAAO,GAAG,EAAM,SAAS,GAAG,GAAG,OAAO,EAAM,EAAE,EAAE,GAAG,EAAM,QAAQ,EAAE,EAAE,cAAc;CACrF;CAEA,GAAa,GAA8B,GAA6C;EACtF,IAAM,IAAS,EAAM,cACf,IAAQ;GACZ,MAAM,GAAG,EAAO,IAAI,IAAI;GACxB,KAAK,GAAG,EAAO,IAAI,IAAI;GACvB,OAAO,GAAG,EAAO,QAAQ,IAAI;GAC7B,QAAQ,GAAG,EAAO,SAAS,IAAI;EACjC,GACM,IAAM,KAAKD,GAAU,GAAO,CAAK,GACjC,IACJ,KAAK,eAAe,KAAK,oBAAoB,IAAI,CAAG,IAC/C,KAAK,oBAAoB,IAAI,CAAG,KAAK,IACtC,KAAKE,GAAoB,CAAK;EAEpC,OAAO,CAAI,iDAAiD,EAAS,CAAK,EAAE,GAAG,EAAQ;CACzF;CAEA,GAAoB,GAA6C;EAE/D,OAAO,CAAI;;;oCAGqB,IAJd,EAAM,MAAM,MAAM,EAAM,iBAAiB,OAAO,KAAK,MAAM,EAAM,iBAI7C;UAChC,EACA,EAAM,UACL,MAAU,EAAM,aAChB,MAAU,KAAKH,GAAmB,GAAO,CAAK,CACjD,EAAE;;;CAGR;CAEA,GAAmB,GAA6B,GAA6C;EAC3F,IAAM,IAAS,EAAM,cACf,IAAQ;GACZ,MAAM,IAAK,EAAM,SAAS,IAAI,EAAO,KAAK,EAAO,QAAS,IAAI;GAC9D,KAAK,IAAK,EAAM,SAAS,IAAI,EAAO,KAAK,EAAO,SAAU,IAAI;EAChE,GACM,IACJ,KAAK,eAAe,KAAK,oBAAoB,IAAI,EAAM,UAAU,IAC5D,KAAK,oBAAoB,IAAI,EAAM,UAAU,KAAK,IACnD,KAAKI,GAAoB,CAAK,GAC9B,IAAO,EAAM,OAAO,QAAQ,EAAM,UAAU;EAElD,OAAO,CAAI;;;;gBAIC,EAAS,CAAK,EAAE;;qBAEX,EAAK;gBACV,EAAK;;oCAEe,EAAQ;;;CAG1C;CAEA,GAAoB,GAA6C;EAE/D,IAAM,KADO,EAAM,OAAO,QAAQ,EAAM,UAAU,GAAA,CAE/C,MAAM,KAAK,CAAC,CACZ,KAAK,MAAS,EAAK,EAAE,CAAC,CACtB,KAAK,EAAE,CAAC,CACR,MAAM,GAAG,CAAC,CAAC,CACX,YAAY;EACf,OAAO,CAAI;6CAC8B,OAAO,EAAM,OAAO,WAAW,EAAI,EAAE;UAExE,EAAM,WACF,CAAI,gCAAgC,EAAM,SAAS,gCACnD,EACL;;;CAGP;CAEA,GAAmB,GAA+D;EAChF,IAAM,IAAO,EAAM,QAAQ;EAK3B,OAJI,CAAC,KAAK,mBAAmB,EAAK,UAAU,IACnC,IAGF,CAAI;;UAEL,EACA,IACC,MAAa,EAAS,KACtB,GAAU,MAAU,CAAI;;;mBAGhB,WAAW,IAAQ;;;;;8BAKR,OAAO,EAAS,OAAO,KAAK,cAAc,EAAE,EAAE;yBACnD,EAAS,OAAO,KAAK,cAAc,KAAK,MAAM,KAAK;+BAC7C,OAAO,CAAK,EAAE;6BAChB,KAAKvC,GAAW,CAAQ,EAAE;yBAC9B,KAAKwC,GAA0B;;gBAExC,KAAKhB,GAAU,CAAQ,EAAE;;WAGjC,EAAE;;;CAGR;CAEA,KAAmD;EAIjD,OAHI,CAAC,KAAK,gBAAgB,CAAC,KAAK,SACvB,IAEF,CAAI;;;;;;;;mBAQI,KAAK,QAAQ;;;;;;;;;;mBAUb,KAAK,UAAU;;;;;;;;;;mBAUf,KAAK,OAAO;;;;;;CAM7B;CAEA,KAAiD;EAC/C,IAAI,KAAK,YAAY,SACnB,OAAO;EAGT,IAAM,IAAU,KAAK,YAAY,WAC3B,IAAQ,KAAK,YAAY,SACzB,IAAQ,IAAU,qBAAqB,IAAQ,qBAAqB,mBACpE,IAAO,IACT,8DACA,IACE,wEACC,KAAK,QAAQ,WAAW;EAE/B,OAAO,CAAI;+CACgC,KAAK,YAAY,UAAU,UAAU,SAAS;;8DAE/B,IAAU,MAAM,IAAI;oCAC9C,EAAM;mCACP,EAAK;YAE5B,KAAK,YAAY,UACb,CAAI,qDAAqD,KAAK,MAAM,mBACpE,EACL;;;;CAIT;CAEA,GAAU,GAAiC;EACzC,OAAO,KAAK,WAAW,IAAI,EAAS,EAAE,KAAK;CAC7C;CAEA,GAAW,GAA+B;EACpC,EAAS,OAAO,KAAK,cAAc,OAGvC,KAAK,aAAa,EAAS,IAC3B,KAAK,cACH,IAAI,YAA4C,+BAA+B;GAC7E,GAAG;GACH,QAAQ;IAAE,YAAY,EAAS;IAAI;GAAS;EAC9C,CAAC,CACH;CACF;CAEA;CA0BA;CAcA;CAgCA;CAwBA;CAwCA;CAYA,GAAoB,GAA8B;EAChD,OAAO,EACJ,aAAa,CAAC,CACd,MACE,MACC,aAAgB,eAChB,EAAK,QAAQ,8DAA8D,CAC/E;CACJ;CAEA,KAAsB;EACpB,IAAM,IAAW,KAAK,kBAChB,IAAW,KAAK;EAClB,CAAC,KAAY,CAAC,MAGlB,KAAKvB,GAAU,cACb,EAAS,aACT,EAAS,cACT,EAAS,OACT,EAAS,MACX,GACA,KAAKC,GAAgB,QAAQ;CAC/B;CAEA,GAAgB,GAAoC;EAElD,AADA,KAAK,iBAAiB,KAAKD,GAAU,OACrC,KAAK,cACH,IAAI,YAA+C,kCAAkC;GACnF,GAAG;GACH,QAAQ;IAAE,UAAU,KAAKA,GAAU;IAAO;GAAO;EACnD,CAAC,CACH;CACF;CAEA,GAAoB,GAAa,GAAc,GAAsB;EAC/D,KAAKa,GAAkB,IAAI,CAAG,MAGlC,KAAKA,GAAkB,IAAI,CAAG,GAC9B,KAAK2B,GAAuB,KAAK;GAC/B;GACA,SAAS,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK;GAC9D,OAAO;EACT,CAAC;CACH;CAEA,KAA6B;EAC3B,IAAI,KAAKA,GAAuB,WAAW,GACzC;EAEF,IAAM,IAAS,KAAKA;EACpB,KAAKA,KAAyB,CAAC;EAC/B,KAAK,IAAM,KAAU,GACnB,KAAK,cACH,IAAI,YAAsC,wBAAwB;GAChE,GAAG;GACH;EACF,CAAC,CACH;CAEJ;AACF;GAn3BG,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAa,CAAC,CAAA,GAAA,EAAA,WAAA,aAAA,KAAA,CAAA,MAClD,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAe,CAAC,CAAA,GAAA,EAAA,WAAA,cAAA,KAAA,CAAA,MACpD,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,eAAA,KAAA,CAAA,MAC7B,EAAS,EAAE,WAAW,GAAM,CAAC,CAAA,GAAA,EAAA,WAAA,eAAA,KAAA,CAAA,MAC7B,EAAS,EAAE,WAAW,GAAM,CAAC,CAAA,GAAA,EAAA,WAAA,qBAAA,KAAA,CAAA,MAC7B,EAAS;CAAE,MAAM;CAAS,WAAW;AAAgB,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MACtD,EAAS;CAAE,MAAM;CAAS,WAAW;AAAoB,CAAC,CAAA,GAAA,EAAA,WAAA,mBAAA,KAAA,CAAA,MAC1D,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAW,CAAC,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,MAGhD,EAAS;CAAE,MAAM;CAAS,WAAW;AAAM,CAAC,CAAA,GAAA,EAAA,WAAA,mBAAA,KAAA,CAAA,MAE5C,EAAM,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,SAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,kBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,cAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,uBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,uBAAA,KAAA,CAAA,MACN,EAAM,CAAA,GAAA,EAAA,WAAA,cAAA,KAAA,CAAA,MAEN,EAAM,WAAW,CAAA,GAAA,EAAA,WAAA,oBAAA,KAAA,CAAA,MACjB,EAAM,QAAQ,CAAA,GAAA,EAAA,WAAA,kBAAA,KAAA,CAAA,UA9BhB,EAAc,wBAAwB,CAAA,GAAA,CAAA;AAy3BvC,IAAa,UAAyC;CACpD,AACS,WAAW,mBAAmB,UACrC,CAAC,WAAW,eAAe,IAAI,wBAAwB,KAEvD,WAAW,eAAe,OAAO,0BAA0B,CAA2B;AAE1F"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"talent-calculator-Ch1Pbbba.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 { getAssetUrl } from \"@crockery/fellowship-data\";\nimport type {\n FellowshipDataManifest,\n Hero,\n ImageAssetRef,\n Talent,\n} from \"@crockery/fellowship-data\";\n\nimport { FellowshipDataError } from \"./data-source.js\";\nimport type { FellowshipTalentDataSource, TalentCalculatorData } from \"./talent-data-source.js\";\n\nexport interface PackageFellowshipTalentDataSourceOptions {\n readonly assetBaseUrl?: string;\n}\n\ninterface PackageTalentIndex {\n readonly manifest: FellowshipDataManifest;\n readonly heroes: ReadonlyMap<string, Hero>;\n readonly talentsByHero: ReadonlyMap<string, readonly Talent[]>;\n}\n\nlet packageTalentDataPromise: Promise<PackageTalentIndex> | undefined;\n\nconst throwIfAborted = (signal?: AbortSignal): void => {\n if (signal?.aborted) {\n throw new FellowshipDataError(\n \"aborted\",\n \"The hero talent data request was aborted.\",\n signal.reason,\n );\n }\n};\n\nconst loadPackageTalentData = (): Promise<PackageTalentIndex> => {\n packageTalentDataPromise ??= Promise.all([\n import(\"@crockery/fellowship-data/heroes\"),\n import(\"@crockery/fellowship-data/talents\"),\n import(\"@crockery/fellowship-data/manifest\"),\n ])\n .then(([heroModule, talentModule, manifestModule]) => {\n const manifest = manifestModule.default;\n if ((manifest as { schemaVersion: number }).schemaVersion !== 1) {\n throw new FellowshipDataError(\n \"unsupported-schema\",\n `Unsupported fellowship-data schema version: ${String(manifest.schemaVersion)}.`,\n );\n }\n\n const mutableTalentsByHero = new Map<string, Talent[]>();\n for (const talent of talentModule.default) {\n const talents = mutableTalentsByHero.get(talent.parentId) ?? [];\n talents.push(talent);\n mutableTalentsByHero.set(talent.parentId, talents);\n }\n\n return {\n manifest,\n heroes: new Map(heroModule.default.map((hero) => [hero.id, hero])),\n talentsByHero: new Map(\n [...mutableTalentsByHero].map(([heroId, talents]) => [heroId, talents] as const),\n ),\n };\n })\n .catch((error: unknown) => {\n packageTalentDataPromise = undefined;\n if (error instanceof FellowshipDataError) {\n throw error;\n }\n throw new FellowshipDataError(\n \"data-load-failed\",\n \"Unable to load hero talents from @crockery/fellowship-data.\",\n error,\n );\n });\n\n return packageTalentDataPromise;\n};\n\nexport const createPackageFellowshipTalentDataSource = (\n options: PackageFellowshipTalentDataSourceOptions = {},\n): FellowshipTalentDataSource => ({\n async loadHeroTalents(heroId: string, signal?: AbortSignal): Promise<TalentCalculatorData> {\n throwIfAborted(signal);\n const data = await loadPackageTalentData();\n throwIfAborted(signal);\n\n const hero = data.heroes.get(heroId);\n if (!hero) {\n throw new FellowshipDataError(\n \"hero-not-found\",\n `No hero with ID “${heroId}” exists in @crockery/fellowship-data.`,\n );\n }\n\n return {\n manifest: data.manifest,\n hero,\n talents: data.talentsByHero.get(heroId) ?? [],\n };\n },\n\n resolveAssetUrl(asset: ImageAssetRef): string {\n return options.assetBaseUrl\n ? getAssetUrl(asset, { baseUrl: options.assetBaseUrl })\n : getAssetUrl(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":"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"}
|