@crockery/fellowship-components 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +12 -10
  2. package/RELEASING.md +15 -14
  3. package/dist/chunks/data-source-XbksqUrw.js +7 -0
  4. package/dist/chunks/decorate-B_ZTN9li.js +0 -2
  5. package/dist/chunks/{dungeon-cast-browser-Cyh519NI.js → dungeon-cast-browser-CSnu3gC5.js} +54 -56
  6. package/dist/chunks/{dungeon-map-xFaPasVr.js → dungeon-map-CutIcCVP.js} +89 -92
  7. package/dist/chunks/{talent-calculator-CIPNbqK5.js → talent-calculator-Dxuc2dZ8.js} +88 -90
  8. package/dist/chunks/tooltip-DoQVSnOn.js +0 -2
  9. package/dist/data/cast-data-source.d.ts +0 -1
  10. package/dist/data/data-source.d.ts +3 -2
  11. package/dist/data/package-cast-data-source.d.ts +0 -1
  12. package/dist/data/package-data-source.d.ts +0 -1
  13. package/dist/data/package-talent-data-source.d.ts +0 -1
  14. package/dist/data/talent-data-source.d.ts +0 -1
  15. package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts +0 -1
  16. package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts +0 -1
  17. package/dist/dungeon-cast-browser/index.d.ts +0 -1
  18. package/dist/dungeon-cast-browser/render-model.d.ts +0 -1
  19. package/dist/dungeon-cast-browser/types.d.ts +0 -1
  20. package/dist/dungeon-cast-browser.js +1 -3
  21. package/dist/dungeon-map/dungeon-map.d.ts +0 -1
  22. package/dist/dungeon-map/dungeon-map.styles.d.ts +0 -1
  23. package/dist/dungeon-map/index.d.ts +0 -1
  24. package/dist/dungeon-map/layout.d.ts +0 -1
  25. package/dist/dungeon-map/render-model.d.ts +0 -1
  26. package/dist/dungeon-map/types.d.ts +0 -1
  27. package/dist/dungeon-map/viewport-controller.d.ts +0 -1
  28. package/dist/dungeon-map.js +1 -3
  29. package/dist/index.d.ts +0 -1
  30. package/dist/index.js +4 -4
  31. package/dist/react/dungeon-cast-browser.d.ts +0 -1
  32. package/dist/react/dungeon-cast-browser.js +1 -3
  33. package/dist/react/dungeon-map.d.ts +0 -1
  34. package/dist/react/dungeon-map.js +1 -3
  35. package/dist/react/index.d.ts +0 -1
  36. package/dist/react/talent-calculator.d.ts +0 -1
  37. package/dist/react/talent-calculator.js +1 -3
  38. package/dist/react/tooltip.d.ts +0 -1
  39. package/dist/react/tooltip.js +0 -2
  40. package/dist/styles/field-manual-fonts.d.ts +0 -1
  41. package/dist/styles/field-manual.styles.d.ts +0 -1
  42. package/dist/talent-calculator/index.d.ts +0 -1
  43. package/dist/talent-calculator/render-model.d.ts +0 -1
  44. package/dist/talent-calculator/selection-state.d.ts +0 -1
  45. package/dist/talent-calculator/talent-calculator.d.ts +0 -1
  46. package/dist/talent-calculator/talent-calculator.styles.d.ts +0 -1
  47. package/dist/talent-calculator/types.d.ts +0 -1
  48. package/dist/talent-calculator.js +1 -3
  49. package/dist/tooltip/index.d.ts +0 -1
  50. package/dist/tooltip/positioning.d.ts +0 -1
  51. package/dist/tooltip/rich-text.d.ts +0 -1
  52. package/dist/tooltip/tooltip.d.ts +0 -1
  53. package/dist/tooltip/tooltip.styles.d.ts +0 -1
  54. package/dist/tooltip/types.d.ts +0 -1
  55. package/dist/tooltip.js +0 -2
  56. package/package.json +8 -15
  57. package/dist/chunks/data-source-C-Gs7kRl.js +0 -2
  58. package/dist/chunks/decorate-B_ZTN9li.js.map +0 -1
  59. package/dist/chunks/dungeon-cast-browser-Cyh519NI.js.map +0 -1
  60. package/dist/chunks/dungeon-map-xFaPasVr.js.map +0 -1
  61. package/dist/chunks/talent-calculator-CIPNbqK5.js.map +0 -1
  62. package/dist/chunks/tooltip-DoQVSnOn.js.map +0 -1
  63. package/dist/data/cast-data-source.d.ts.map +0 -1
  64. package/dist/data/data-source.d.ts.map +0 -1
  65. package/dist/data/package-cast-data-source.d.ts.map +0 -1
  66. package/dist/data/package-data-source.d.ts.map +0 -1
  67. package/dist/data/package-talent-data-source.d.ts.map +0 -1
  68. package/dist/data/talent-data-source.d.ts.map +0 -1
  69. package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts.map +0 -1
  70. package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts.map +0 -1
  71. package/dist/dungeon-cast-browser/index.d.ts.map +0 -1
  72. package/dist/dungeon-cast-browser/render-model.d.ts.map +0 -1
  73. package/dist/dungeon-cast-browser/types.d.ts.map +0 -1
  74. package/dist/dungeon-cast-browser.js.map +0 -1
  75. package/dist/dungeon-map/dungeon-map.d.ts.map +0 -1
  76. package/dist/dungeon-map/dungeon-map.styles.d.ts.map +0 -1
  77. package/dist/dungeon-map/index.d.ts.map +0 -1
  78. package/dist/dungeon-map/layout.d.ts.map +0 -1
  79. package/dist/dungeon-map/render-model.d.ts.map +0 -1
  80. package/dist/dungeon-map/types.d.ts.map +0 -1
  81. package/dist/dungeon-map/viewport-controller.d.ts.map +0 -1
  82. package/dist/dungeon-map.js.map +0 -1
  83. package/dist/index.d.ts.map +0 -1
  84. package/dist/react/dungeon-cast-browser.d.ts.map +0 -1
  85. package/dist/react/dungeon-cast-browser.js.map +0 -1
  86. package/dist/react/dungeon-map.d.ts.map +0 -1
  87. package/dist/react/dungeon-map.js.map +0 -1
  88. package/dist/react/index.d.ts.map +0 -1
  89. package/dist/react/talent-calculator.d.ts.map +0 -1
  90. package/dist/react/talent-calculator.js.map +0 -1
  91. package/dist/react/tooltip.d.ts.map +0 -1
  92. package/dist/react/tooltip.js.map +0 -1
  93. package/dist/styles/field-manual-fonts.d.ts.map +0 -1
  94. package/dist/styles/field-manual.styles.d.ts.map +0 -1
  95. package/dist/talent-calculator/index.d.ts.map +0 -1
  96. package/dist/talent-calculator/render-model.d.ts.map +0 -1
  97. package/dist/talent-calculator/selection-state.d.ts.map +0 -1
  98. package/dist/talent-calculator/talent-calculator.d.ts.map +0 -1
  99. package/dist/talent-calculator/talent-calculator.styles.d.ts.map +0 -1
  100. package/dist/talent-calculator/types.d.ts.map +0 -1
  101. package/dist/talent-calculator.js.map +0 -1
  102. package/dist/tooltip/index.d.ts.map +0 -1
  103. package/dist/tooltip/positioning.d.ts.map +0 -1
  104. package/dist/tooltip/rich-text.d.ts.map +0 -1
  105. package/dist/tooltip/tooltip.d.ts.map +0 -1
  106. package/dist/tooltip/tooltip.styles.d.ts.map +0 -1
  107. package/dist/tooltip/types.d.ts.map +0 -1
  108. package/dist/tooltip.js.map +0 -1
@@ -9,4 +9,3 @@ export declare const DungeonCastBrowser: import("@lit/react").ReactWebComponent<
9
9
  }>;
10
10
  export type DungeonCastBrowserProps = ComponentProps<typeof DungeonCastBrowser>;
11
11
  export default DungeonCastBrowser;
12
- //# sourceMappingURL=dungeon-cast-browser.d.ts.map
@@ -1,4 +1,4 @@
1
- import { n as e, t } from "../chunks/dungeon-cast-browser-Cyh519NI.js";
1
+ import { n as e, t } from "../chunks/dungeon-cast-browser-CSnu3gC5.js";
2
2
  import { createComponent as n } from "@lit/react";
3
3
  import r from "react";
4
4
  //#region src/react/dungeon-cast-browser.tsx
@@ -15,5 +15,3 @@ var i = n({
15
15
  });
16
16
  //#endregion
17
17
  export { i as DungeonCastBrowser, i as default };
18
-
19
- //# sourceMappingURL=dungeon-cast-browser.js.map
@@ -19,4 +19,3 @@ export interface DungeonMapViewerProps extends Omit<NativeDungeonMapProps, "chil
19
19
  }
20
20
  export declare const DungeonMapViewer: React.ForwardRefExoticComponent<Omit<DungeonMapViewerProps, "ref"> & React.RefAttributes<FellowshipDungeonMapElement>>;
21
21
  export default DungeonMapViewer;
22
- //# sourceMappingURL=dungeon-map.d.ts.map
@@ -1,4 +1,4 @@
1
- import { n as e, t } from "../chunks/dungeon-map-xFaPasVr.js";
1
+ import { n as e, t } from "../chunks/dungeon-map-CutIcCVP.js";
2
2
  import { createComponent as n } from "@lit/react";
3
3
  import r, { forwardRef as i, useCallback as a, useEffect as o, useState as s } from "react";
4
4
  import { jsx as c, jsxs as l } from "react/jsx-runtime";
@@ -176,5 +176,3 @@ var u = n({
176
176
  _.displayName = "DungeonMapViewer";
177
177
  //#endregion
178
178
  export { _ as DungeonMapViewer, _ as default };
179
-
180
- //# sourceMappingURL=dungeon-map.js.map
@@ -6,4 +6,3 @@ export { TalentCalculator } from "./talent-calculator.js";
6
6
  export type { ReactTalentRenderer, TalentCalculatorProps } from "./talent-calculator.js";
7
7
  export { Tooltip } from "./tooltip.js";
8
8
  export type { TooltipProps } from "./tooltip.js";
9
- //# sourceMappingURL=index.d.ts.map
@@ -19,4 +19,3 @@ export interface TalentCalculatorProps extends Omit<NativeTalentCalculatorProps,
19
19
  }
20
20
  export declare const TalentCalculator: React.ForwardRefExoticComponent<Omit<TalentCalculatorProps, "ref"> & React.RefAttributes<FellowshipTalentCalculatorElement>>;
21
21
  export default TalentCalculator;
22
- //# sourceMappingURL=talent-calculator.d.ts.map
@@ -1,4 +1,4 @@
1
- import { a as e, n as t, o as n, r, t as i } from "../chunks/talent-calculator-CIPNbqK5.js";
1
+ import { a as e, n as t, o as n, r, t as i } from "../chunks/talent-calculator-Dxuc2dZ8.js";
2
2
  import { createComponent as a } from "@lit/react";
3
3
  import o, { forwardRef as s, useCallback as c, useEffect as l, useMemo as u, useState as d } from "react";
4
4
  import { jsx as f } from "react/jsx-runtime";
@@ -67,5 +67,3 @@ var p = a({
67
67
  m.displayName = "TalentCalculator";
68
68
  //#endregion
69
69
  export { m as TalentCalculator, m as default };
70
-
71
- //# sourceMappingURL=talent-calculator.js.map
@@ -12,4 +12,3 @@ export interface TooltipProps extends Omit<NativeTooltipProps, "children" | "con
12
12
  }
13
13
  export declare const Tooltip: React.ForwardRefExoticComponent<Omit<TooltipProps, "ref"> & React.RefAttributes<FellowshipTooltipElement>>;
14
14
  export default Tooltip;
15
- //# sourceMappingURL=tooltip.d.ts.map
@@ -22,5 +22,3 @@ var s = n({
22
22
  c.displayName = "Tooltip";
23
23
  //#endregion
24
24
  export { c as Tooltip, c as default };
25
-
26
- //# sourceMappingURL=tooltip.js.map
@@ -1,3 +1,2 @@
1
1
  /** Makes the bundled field-manual faces available to a component's owner document. */
2
2
  export declare const installFieldManualFonts: (document: Document) => void;
3
- //# sourceMappingURL=field-manual-fonts.d.ts.map
@@ -1,3 +1,2 @@
1
1
  /** Shared internal aliases for the public Expedition Field Manual theme tokens. */
2
2
  export declare const fieldManualStyles: import("lit").CSSResult;
3
- //# sourceMappingURL=field-manual.styles.d.ts.map
@@ -2,4 +2,3 @@ export { createTalentCalculatorRenderModel, talentRowLabel } from "./render-mode
2
2
  export { canSelectTalent, createTalentCalculatorSelectionState, MAX_TALENT_POINTS, normalizeSelectedTalentIds, normalizeTalentPoints, } from "./selection-state.js";
3
3
  export { FellowshipTalentCalculatorElement, defineFellowshipTalentCalculator, talentSlotName, } from "./talent-calculator.js";
4
4
  export type { FellowshipTalentCalculatorProperties, FellowshipTalentsErrorDetail, FellowshipTalentsErrorEvent, FellowshipTalentsReadyDetail, FellowshipTalentsReadyEvent, FellowshipTalentsWarningDetail, FellowshipTalentsWarningEvent, LitTalentRenderer, TalentCalculatorRenderModel, TalentCalculatorRowModel, TalentCalculatorSelectionState, TalentCalculatorTalentModel, TalentCalculatorTalentRenderContext, TalentCalculatorWarning, TalentCalculatorWarningCode, TalentPointsChangeDetail, TalentPointsChangeEvent, TalentPointsChangeSource, TalentSelectionChangeDetail, TalentSelectionChangeEvent, TalentSelectionChangeSource, } from "./types.js";
5
- //# sourceMappingURL=index.d.ts.map
@@ -2,4 +2,3 @@ import type { FellowshipTalentDataSource, TalentCalculatorData } from "../data/t
2
2
  import type { TalentCalculatorRenderModel } from "./types.js";
3
3
  export declare const talentRowLabel: (row: number) => string;
4
4
  export declare const createTalentCalculatorRenderModel: (data: TalentCalculatorData, source: FellowshipTalentDataSource) => TalentCalculatorRenderModel;
5
- //# sourceMappingURL=render-model.d.ts.map
@@ -7,4 +7,3 @@ export declare const normalizeSelectedTalentIds: (model: TalentCalculatorRenderM
7
7
  };
8
8
  export declare const createTalentCalculatorSelectionState: (model: TalentCalculatorRenderModel, selectedTalentIds: readonly string[], talentPoints: number) => TalentCalculatorSelectionState;
9
9
  export declare const canSelectTalent: (pointCost: number | null, state: TalentCalculatorSelectionState) => boolean;
10
- //# sourceMappingURL=selection-state.d.ts.map
@@ -48,4 +48,3 @@ declare global {
48
48
  "fellowship-talent-calculator": FellowshipTalentCalculatorElement;
49
49
  }
50
50
  }
51
- //# sourceMappingURL=talent-calculator.d.ts.map
@@ -1,2 +1 @@
1
1
  export declare const talentCalculatorStyles: import("lit").CSSResult;
2
- //# sourceMappingURL=talent-calculator.styles.d.ts.map
@@ -89,4 +89,3 @@ declare global {
89
89
  "fellowship-talent-points-change": TalentPointsChangeEvent;
90
90
  }
91
91
  }
92
- //# sourceMappingURL=types.d.ts.map
@@ -1,7 +1,5 @@
1
- import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "./chunks/talent-calculator-CIPNbqK5.js";
1
+ import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "./chunks/talent-calculator-Dxuc2dZ8.js";
2
2
  //#region src/talent-calculator/index.ts
3
3
  i();
4
4
  //#endregion
5
5
  export { c as FellowshipTalentCalculatorElement, n as MAX_TALENT_POINTS, e as canSelectTalent, r as createTalentCalculatorRenderModel, a as createTalentCalculatorSelectionState, i as defineFellowshipTalentCalculator, s as normalizeSelectedTalentIds, t as normalizeTalentPoints, l as talentRowLabel, o as talentSlotName };
6
-
7
- //# sourceMappingURL=talent-calculator.js.map
@@ -3,4 +3,3 @@ export type { TooltipPosition, TooltipPositionOptions, TooltipRect } from "./pos
3
3
  export { resolveFellowshipRichText, stripFellowshipRichText } from "./rich-text.js";
4
4
  export { FellowshipTooltipElement, defineFellowshipTooltip } from "./tooltip.js";
5
5
  export type { FellowshipRichTextDocument, FellowshipRichTextSegment, FellowshipTextTone, FellowshipTooltipPlacement, FellowshipTooltipToggleDetail, FellowshipTooltipToggleEvent, ResolvedTooltipPlacement, } from "./types.js";
6
- //# sourceMappingURL=index.d.ts.map
@@ -20,4 +20,3 @@ export interface TooltipPositionOptions {
20
20
  readonly margin?: number;
21
21
  }
22
22
  export declare const calculateTooltipPosition: (trigger: TooltipRect, tooltip: Pick<TooltipRect, "width" | "height">, options: TooltipPositionOptions) => TooltipPosition;
23
- //# sourceMappingURL=positioning.d.ts.map
@@ -1,4 +1,3 @@
1
1
  import type { FellowshipRichTextDocument } from "./types.js";
2
2
  export declare const resolveFellowshipRichText: (value: string) => FellowshipRichTextDocument;
3
3
  export declare const stripFellowshipRichText: (value: string) => string;
4
- //# sourceMappingURL=rich-text.d.ts.map
@@ -30,4 +30,3 @@ declare global {
30
30
  "fellowship-tooltip": FellowshipTooltipElement;
31
31
  }
32
32
  }
33
- //# sourceMappingURL=tooltip.d.ts.map
@@ -1,2 +1 @@
1
1
  export declare const tooltipStyles: import("lit").CSSResult;
2
- //# sourceMappingURL=tooltip.styles.d.ts.map
@@ -16,4 +16,3 @@ declare global {
16
16
  "fellowship-tooltip-toggle": FellowshipTooltipToggleEvent;
17
17
  }
18
18
  }
19
- //# sourceMappingURL=types.d.ts.map
package/dist/tooltip.js CHANGED
@@ -3,5 +3,3 @@ import { a as e, i as t, n, r, t as i } from "./chunks/tooltip-DoQVSnOn.js";
3
3
  n();
4
4
  //#endregion
5
5
  export { i as FellowshipTooltipElement, r as calculateTooltipPosition, n as defineFellowshipTooltip, t as resolveFellowshipRichText, e as stripFellowshipRichText };
6
-
7
- //# sourceMappingURL=tooltip.js.map
package/package.json CHANGED
@@ -1,15 +1,12 @@
1
1
  {
2
2
  "name": "@crockery/fellowship-components",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Lit web components for rendering Fellowship game data.",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/Crockery/fellowship-components.git"
7
+ "url": "git+https://github.com/Crockery/fellowscript.git",
8
+ "directory": "packages/fellowship-components"
8
9
  },
9
- "bugs": {
10
- "url": "https://github.com/Crockery/fellowship-components/issues"
11
- },
12
- "homepage": "https://github.com/Crockery/fellowship-components#readme",
13
10
  "keywords": [
14
11
  "fellowship",
15
12
  "web-components",
@@ -99,12 +96,12 @@
99
96
  "access": "public"
100
97
  },
101
98
  "engines": {
102
- "node": "^20.19.0 || >=22.12.0"
99
+ "node": ">=24"
103
100
  },
104
101
  "dependencies": {
105
- "@crockery/fellowship-data": "^1.3.0",
106
102
  "@lit/react": "^1.0.8",
107
- "lit": "^3.3.3"
103
+ "lit": "^3.3.3",
104
+ "@crockery/fellowship-data": "^2.0.0"
108
105
  },
109
106
  "peerDependencies": {
110
107
  "react": ">=18 <20",
@@ -120,8 +117,7 @@
120
117
  },
121
118
  "devDependencies": {
122
119
  "@arethetypeswrong/cli": "^0.18.5",
123
- "@biomejs/biome": "^2.5.3",
124
- "@changesets/cli": "^2.31.0",
120
+ "@biomejs/biome": "2.5.3",
125
121
  "@fontsource/ibm-plex-mono": "5.2.7",
126
122
  "@fontsource/ibm-plex-sans-condensed": "5.2.8",
127
123
  "@ladle/react": "^5.1.1",
@@ -151,9 +147,6 @@
151
147
  "ladle": "ladle serve",
152
148
  "ladle:build": "ladle build",
153
149
  "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"
150
+ "pack:check": "pnpm build && node scripts/check-build.mjs && node scripts/check-package-files.mjs && pnpm package:lint"
158
151
  }
159
152
  }
@@ -1,2 +0,0 @@
1
- import { FellowshipDataError as e } from "@crockery/fellowship-data/client";
2
- export { e as t };
@@ -1 +0,0 @@
1
- {"version":3,"file":"decorate-B_ZTN9li.js","names":[],"sources":["../../src/styles/field-manual.styles.ts","../../node_modules/.pnpm/@fontsource+ibm-plex-mono@5.2.7/node_modules/@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-500-normal.woff2?fellowship-font-url","../../node_modules/.pnpm/@fontsource+ibm-plex-mono@5.2.7/node_modules/@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-600-normal.woff2?fellowship-font-url","../../node_modules/.pnpm/@fontsource+ibm-plex-sans-condensed@5.2.8/node_modules/@fontsource/ibm-plex-sans-condensed/files/ibm-plex-sans-condensed-latin-400-normal.woff2?fellowship-font-url","../../node_modules/.pnpm/@fontsource+ibm-plex-sans-condensed@5.2.8/node_modules/@fontsource/ibm-plex-sans-condensed/files/ibm-plex-sans-condensed-latin-500-normal.woff2?fellowship-font-url","../../node_modules/.pnpm/@fontsource+ibm-plex-sans-condensed@5.2.8/node_modules/@fontsource/ibm-plex-sans-condensed/files/ibm-plex-sans-condensed-latin-600-normal.woff2?fellowship-font-url","../../node_modules/.pnpm/@fontsource+ibm-plex-sans-condensed@5.2.8/node_modules/@fontsource/ibm-plex-sans-condensed/files/ibm-plex-sans-condensed-latin-700-normal.woff2?fellowship-font-url","../../src/styles/field-manual-fonts.ts"],"sourcesContent":["import { css } from \"lit\";\n\n/** Shared internal aliases for the public Expedition Field Manual theme tokens. */\nexport const fieldManualStyles = css`\n :host {\n --_fellowship-field-canvas: var(--fellowship-field-canvas, #0b0f13);\n --_fellowship-field-surface: var(--fellowship-field-surface, #10161c);\n --_fellowship-field-surface-raised: var(--fellowship-field-surface-raised, #151c23);\n --_fellowship-field-inset: var(--fellowship-field-inset, #080c10);\n --_fellowship-field-ink: var(--fellowship-field-ink, #f1eee5);\n --_fellowship-field-muted: var(--fellowship-field-muted, #b7b4aa);\n --_fellowship-field-tertiary: var(--fellowship-field-tertiary, #858a8e);\n --_fellowship-field-border: var(\n --fellowship-field-border,\n rgb(241 238 229 / 10%)\n );\n --_fellowship-field-border-strong: var(\n --fellowship-field-border-strong,\n rgb(241 238 229 / 18%)\n );\n --_fellowship-field-accent: var(--fellowship-field-accent, #69c4d4);\n --_fellowship-field-focus: var(--fellowship-field-focus, #a8e6ee);\n --_fellowship-field-ember: var(--fellowship-field-ember, #d9784c);\n --_fellowship-field-brass: var(--fellowship-field-brass, #c1a36a);\n --_fellowship-field-font-family: var(\n --fellowship-field-font-family,\n \"Fellowship Field\",\n \"Arial Narrow\",\n sans-serif\n );\n --_fellowship-field-mono-font-family: var(\n --fellowship-field-mono-font-family,\n \"Fellowship Tactical\",\n ui-monospace,\n monospace\n );\n --_fellowship-field-radius: var(--fellowship-field-radius, 6px);\n }\n`;\n","export default import.meta.ROLLUP_FILE_URL_FKNdLzKnlftW3t_gzo81yw;","export default import.meta.ROLLUP_FILE_URL_VRAku6fjghkApIISiBWPzg;","export default import.meta.ROLLUP_FILE_URL_8HSUfh7cJHNFxCkInNzNXw;","export default import.meta.ROLLUP_FILE_URL_WPGYjwIlzWVNM1bYhOc83w;","export default import.meta.ROLLUP_FILE_URL_P4DPfwIRieI8P6q8r4JZRw;","export default import.meta.ROLLUP_FILE_URL_cn7IAAGY294pWGc7MpElVg;","import mono500Url from \"@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-500-normal.woff2?url\";\nimport mono600Url from \"@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-600-normal.woff2?url\";\nimport field400Url from \"@fontsource/ibm-plex-sans-condensed/files/ibm-plex-sans-condensed-latin-400-normal.woff2?url\";\nimport field500Url from \"@fontsource/ibm-plex-sans-condensed/files/ibm-plex-sans-condensed-latin-500-normal.woff2?url\";\nimport field600Url from \"@fontsource/ibm-plex-sans-condensed/files/ibm-plex-sans-condensed-latin-600-normal.woff2?url\";\nimport field700Url from \"@fontsource/ibm-plex-sans-condensed/files/ibm-plex-sans-condensed-latin-700-normal.woff2?url\";\n\ninterface FieldManualFontAsset {\n readonly family: \"Fellowship Field\" | \"Fellowship Tactical\";\n readonly weight: \"400\" | \"500\" | \"600\" | \"700\";\n readonly url: string;\n}\n\nconst fieldManualFontAssets: readonly FieldManualFontAsset[] = [\n { family: \"Fellowship Field\", weight: \"400\", url: field400Url },\n { family: \"Fellowship Field\", weight: \"500\", url: field500Url },\n { family: \"Fellowship Field\", weight: \"600\", url: field600Url },\n { family: \"Fellowship Field\", weight: \"700\", url: field700Url },\n { family: \"Fellowship Tactical\", weight: \"500\", url: mono500Url },\n { family: \"Fellowship Tactical\", weight: \"600\", url: mono600Url },\n];\n\nconst installedDocuments = new WeakSet<Document>();\n\n/** Makes the bundled field-manual faces available to a component's owner document. */\nexport const installFieldManualFonts = (document: Document): void => {\n if (installedDocuments.has(document)) {\n return;\n }\n\n const FontFaceConstructor = document.defaultView?.FontFace;\n if (!FontFaceConstructor || !document.fonts) {\n return;\n }\n\n const faces = fieldManualFontAssets.map(\n ({ family, weight, url }) =>\n new FontFaceConstructor(family, `url(${JSON.stringify(url)}) format(\"woff2\")`, {\n display: \"swap\",\n style: \"normal\",\n weight,\n }),\n );\n for (const face of faces) {\n document.fonts.add(face);\n }\n installedDocuments.add(document);\n};\n"],"x_google_ignoreList":[1,2,3,4,5,6],"mappings":";;AAGA,IAAa,IAAoB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GCHpC,IAAA,IAAA,IAAA,2DAAA,OAAA,KAAA,GAAA,CAAA,CAAA,MCAA,IAAA,IAAA,IAAA,2DAAA,OAAA,KAAA,GAAA,CAAA,CAAA,MCAA,IAAA,IAAA,IAAA,qEAAA,OAAA,KAAA,GAAA,CAAA,CAAA,MCAA,IAAA,IAAA,IAAA,qEAAA,OAAA,KAAA,GAAA,CAAA,CAAA,MCAA,IAAA,IAAA,IAAA,qEAAA,OAAA,KAAA,GAAA,CAAA,CAAA,MCAA,IAAA,IAAA,IAAA,qEAAA,OAAA,KAAA,GAAA,CAAA,CAAA,MCaM,IAAyD;CAC7D;EAAE,QAAQ;EAAoB,QAAQ;EAAO,KAAK;CAAY;CAC9D;EAAE,QAAQ;EAAoB,QAAQ;EAAO,KAAK;CAAY;CAC9D;EAAE,QAAQ;EAAoB,QAAQ;EAAO,KAAK;CAAY;CAC9D;EAAE,QAAQ;EAAoB,QAAQ;EAAO,KAAK;CAAY;CAC9D;EAAE,QAAQ;EAAuB,QAAQ;EAAO,KAAK;CAAW;CAChE;EAAE,QAAQ;EAAuB,QAAQ;EAAO,KAAK;CAAW;AAClE,GAEM,oBAAqB,IAAI,QAAkB,GAGpC,KAA2B,MAA6B;CACnE,IAAI,EAAmB,IAAI,CAAQ,GACjC;CAGF,IAAM,IAAsB,EAAS,aAAa;CAClD,IAAI,CAAC,KAAuB,CAAC,EAAS,OACpC;CAGF,IAAM,IAAQ,EAAsB,KACjC,EAAE,WAAQ,WAAQ,aACjB,IAAI,EAAoB,GAAQ,OAAO,KAAK,UAAU,CAAG,EAAE,oBAAoB;EAC7E,SAAS;EACT,OAAO;EACP;CACF,CAAC,CACL;CACA,KAAK,IAAM,KAAQ,GACjB,EAAS,MAAM,IAAI,CAAI;CAEzB,EAAmB,IAAI,CAAQ;AACjC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"dungeon-cast-browser-Cyh519NI.js","names":["#abortController","#loadDungeonCasts","#renderStatus","#visibleEnemies","#handleSearch","#renderEnemy","#renderEmptySearch","#loadToken","#sameIdentity","#renderCast","#selectCast","#handleCastKeydown","#handleAbilityImageError","#statusCard"],"sources":["../../src/data/package-cast-data-source.ts","../../src/dungeon-cast-browser/dungeon-cast-browser.styles.ts","../../src/dungeon-cast-browser/render-model.ts","../../src/dungeon-cast-browser/dungeon-cast-browser.ts"],"sourcesContent":["import { createPackageFellowshipDataClient } from \"@crockery/fellowship-data/client\";\n\nimport type { FellowshipCastDataSource } from \"./cast-data-source.js\";\n\n/** @deprecated Create a package Fellowship data client instead. */\nexport interface PackageFellowshipCastDataSourceOptions {\n readonly assetBaseUrl?: string;\n}\n\n/** @deprecated Use `createPackageFellowshipDataClient` from `@crockery/fellowship-data/client`. */\nexport const createPackageFellowshipCastDataSource = (\n options: PackageFellowshipCastDataSourceOptions = {},\n): FellowshipCastDataSource => {\n const client = createPackageFellowshipDataClient(options);\n return {\n loadDungeonCasts: (dungeonId, signal) => client.loadDungeonCasts(dungeonId, signal),\n resolveAssetUrl: (asset) => client.resolveAssetUrl(asset),\n };\n};\n","import { css } from \"lit\";\n\nexport const dungeonCastBrowserStyles = css`\n :host {\n --fellowship-cast-canvas: var(--_fellowship-field-canvas);\n --fellowship-cast-surface: var(--_fellowship-field-surface);\n --fellowship-cast-raised: var(--_fellowship-field-surface-raised);\n --fellowship-cast-ink: var(--_fellowship-field-ink);\n --fellowship-cast-muted: var(--_fellowship-field-muted);\n --fellowship-cast-tertiary: var(--_fellowship-field-tertiary);\n --fellowship-cast-border: var(--_fellowship-field-border);\n --fellowship-cast-border-strong: var(--_fellowship-field-border-strong);\n --fellowship-cast-interrupt: var(--_fellowship-field-accent);\n --fellowship-cast-stop: var(--_fellowship-field-brass);\n --fellowship-cast-focus: var(--_fellowship-field-focus);\n --fellowship-cast-font-family: var(--_fellowship-field-font-family);\n --fellowship-cast-tactical-font-family: var(--_fellowship-field-mono-font-family);\n\n display: block;\n inline-size: 100%;\n min-block-size: 320px;\n color: var(--fellowship-cast-ink);\n font-family: var(--fellowship-cast-font-family);\n container-type: inline-size;\n -webkit-font-smoothing: antialiased;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n :host([fill]) {\n min-block-size: 0;\n block-size: 100%;\n overflow: hidden;\n }\n\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n .browser {\n min-block-size: inherit;\n overflow: hidden;\n border: 1px solid var(--fellowship-cast-border);\n border-radius: calc(var(--_fellowship-field-radius) + 2px);\n background: var(--fellowship-cast-canvas);\n }\n\n :host([fill]) .browser {\n display: grid;\n grid-template-rows: auto minmax(0, 1fr);\n min-block-size: 0;\n block-size: 100%;\n }\n\n .toolbar {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 12px;\n align-items: end;\n padding: 16px;\n border-block-end: 1px solid var(--fellowship-cast-border);\n background: var(--fellowship-cast-surface);\n }\n\n .eyebrow,\n .summary,\n .enemy-id,\n .ability-id,\n .capability,\n .marker {\n font-family: var(--fellowship-cast-tactical-font-family);\n }\n\n .eyebrow {\n margin: 0 0 4px;\n color: var(--fellowship-cast-interrupt);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n }\n\n h2 {\n margin: 0;\n font-size: clamp(18px, 3cqi, 24px);\n font-weight: 600;\n letter-spacing: 0.01em;\n line-height: 1.08;\n }\n\n .summary {\n margin: 6px 0 0;\n color: var(--fellowship-cast-tertiary);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.04em;\n }\n\n .search-label {\n display: grid;\n gap: 4px;\n min-inline-size: min(240px, 38cqi);\n color: var(--fellowship-cast-muted);\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.04em;\n }\n\n .search-input {\n min-block-size: 44px;\n inline-size: 100%;\n border: 1px solid var(--fellowship-cast-border-strong);\n border-radius: var(--_fellowship-field-radius);\n background: var(--_fellowship-field-inset);\n color: var(--fellowship-cast-ink);\n font: inherit;\n font-size: 14px;\n padding-inline: 12px;\n }\n\n .search-input::placeholder {\n color: var(--fellowship-cast-tertiary);\n }\n\n .search-input:focus-visible,\n .cast-row:focus-visible,\n .retry-button:focus-visible {\n outline: 2px solid var(--fellowship-cast-focus);\n outline-offset: 2px;\n }\n\n .enemy-list {\n display: grid;\n }\n\n :host([fill]) .enemy-list {\n min-block-size: 0;\n align-content: start;\n overflow-y: auto;\n overscroll-behavior: contain;\n }\n\n .enemy {\n min-inline-size: 0;\n border-block-end: 1px solid var(--fellowship-cast-border);\n }\n\n .enemy:last-child {\n border-block-end: 0;\n }\n\n .enemy-header {\n display: grid;\n grid-template-columns: 4px minmax(0, 1fr) auto;\n gap: 12px;\n align-items: center;\n min-block-size: 52px;\n padding: 8px 16px;\n background: color-mix(in srgb, var(--fellowship-cast-raised) 72%, transparent);\n }\n\n .enemy-rule {\n align-self: stretch;\n background: var(--fellowship-cast-interrupt);\n }\n\n .enemy-name {\n overflow: hidden;\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n line-height: 1.15;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .enemy-id {\n overflow: hidden;\n margin: 3px 0 0;\n color: var(--fellowship-cast-tertiary);\n font-size: 10px;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .enemy-count {\n color: var(--fellowship-cast-muted);\n font-family: var(--fellowship-cast-tactical-font-family);\n font-size: 10px;\n font-variant-numeric: tabular-nums;\n }\n\n .cast-list {\n display: grid;\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n .cast-list > li + li {\n border-block-start: 1px solid var(--fellowship-cast-border);\n }\n\n .cast-row {\n position: relative;\n display: grid;\n grid-template-columns: 40px minmax(0, 1fr) auto auto;\n gap: 12px;\n align-items: center;\n inline-size: 100%;\n min-block-size: 64px;\n padding: 8px 16px 8px 28px;\n border: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: start;\n cursor: pointer;\n transition: background-color 140ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n .cast-row:not(:disabled):hover {\n background: color-mix(in srgb, var(--fellowship-cast-interrupt) 7%, transparent);\n }\n\n .cast-row[aria-pressed=\"true\"] {\n background: color-mix(in srgb, var(--fellowship-cast-interrupt) 11%, transparent);\n }\n\n .cast-row[aria-pressed=\"true\"]::before {\n position: absolute;\n inset: 12px auto 12px 0;\n inline-size: 3px;\n background: var(--fellowship-cast-interrupt);\n content: \"\";\n }\n\n .cast-row:disabled {\n color: var(--fellowship-cast-tertiary);\n cursor: not-allowed;\n }\n\n .ability-image,\n .ability-fallback {\n inline-size: 40px;\n block-size: 40px;\n border: 1px solid var(--fellowship-cast-border-strong);\n border-radius: 4px;\n background: var(--fellowship-cast-raised);\n }\n\n .ability-image {\n display: block;\n object-fit: cover;\n }\n\n .ability-fallback {\n display: grid;\n place-items: center;\n color: var(--fellowship-cast-muted);\n font-family: var(--fellowship-cast-tactical-font-family);\n font-size: 11px;\n font-weight: 600;\n }\n\n .ability-image[hidden],\n .ability-fallback[hidden] {\n display: none;\n }\n\n .ability-copy {\n min-inline-size: 0;\n }\n\n .ability-name {\n display: block;\n overflow: hidden;\n font-size: 14px;\n font-weight: 600;\n line-height: 1.15;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .ability-id {\n display: block;\n overflow: hidden;\n margin-block-start: 4px;\n color: var(--fellowship-cast-tertiary);\n font-size: 10px;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .capability {\n min-inline-size: 102px;\n padding: 5px 8px;\n border: 1px solid currentColor;\n border-radius: 999px;\n color: var(--fellowship-cast-interrupt);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.04em;\n text-align: center;\n white-space: nowrap;\n }\n\n .capability[data-kind=\"varies\"] {\n border-style: dashed;\n }\n\n .capability[data-kind=\"stop\"] {\n color: var(--fellowship-cast-stop);\n }\n\n .capability[data-kind=\"none\"] {\n color: var(--fellowship-cast-tertiary);\n }\n\n .marker {\n min-inline-size: 72px;\n color: var(--fellowship-cast-stop);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-align: end;\n text-transform: uppercase;\n }\n\n .marker[data-empty=\"true\"] {\n visibility: hidden;\n }\n\n .status {\n display: grid;\n min-block-size: 280px;\n padding: 32px;\n place-items: center;\n text-align: center;\n }\n\n :host([fill]) .status {\n min-block-size: 0;\n }\n\n .status-card {\n display: grid;\n max-inline-size: 360px;\n gap: 8px;\n justify-items: center;\n }\n\n .status-mark {\n inline-size: 32px;\n block-size: 4px;\n margin-block-end: 8px;\n background: var(--fellowship-cast-interrupt);\n }\n\n .status-title {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n }\n\n .status-copy {\n margin: 0;\n color: var(--fellowship-cast-muted);\n font-size: 14px;\n line-height: 1.45;\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-cast-interrupt);\n border-radius: var(--_fellowship-field-radius);\n background: transparent;\n color: var(--fellowship-cast-ink);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n }\n\n @container (max-width: 560px) {\n .toolbar {\n grid-template-columns: 1fr;\n }\n\n .search-label {\n min-inline-size: 0;\n }\n\n .cast-row {\n grid-template-columns: 40px minmax(0, 1fr) auto;\n padding-inline: 16px;\n }\n\n .marker {\n position: absolute;\n inset: 5px 8px auto auto;\n min-inline-size: 0;\n font-size: 8px;\n }\n\n .ability-id,\n .enemy-id {\n display: none;\n }\n\n .capability {\n min-inline-size: 0;\n padding-inline: 6px;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .cast-row {\n transition: none;\n }\n }\n`;\n","import type { Ability, ImageAssetRef } from \"@crockery/fellowship-data\";\n\nimport type { DungeonCastData, FellowshipCastDataSource } from \"../data/cast-data-source.js\";\nimport type {\n CastCapabilityLabel,\n DungeonCastBrowserModel,\n DungeonCastEnemyModel,\n DungeonCastModel,\n FellowshipCastIdentity,\n} from \"./types.js\";\n\nexport const castIdentityKey = ({ enemyId, abilityId }: FellowshipCastIdentity): string =>\n `${enemyId}\\u0000${abilityId}`;\n\nconst abilityImage = (ability: Ability): ImageAssetRef | null =>\n ability.images.find(({ size }) => size === \"200\") ?? ability.images[0] ?? null;\n\nconst capabilityLabel = (\n canInterrupt: boolean,\n interruptVaries: boolean,\n canStop: boolean,\n): CastCapabilityLabel => {\n if (interruptVaries) return \"Interrupt varies\";\n if (canInterrupt) return \"Interrupt\";\n if (canStop) return \"Stop\";\n return \"No response\";\n};\n\nexport const createDungeonCastBrowserModel = (\n data: DungeonCastData,\n source: FellowshipCastDataSource,\n): DungeonCastBrowserModel => {\n const seenEnemyIds = new Set<string>();\n const enemies: DungeonCastEnemyModel[] = [];\n\n for (const placement of data.dungeon.enemies) {\n if (seenEnemyIds.has(placement.id)) continue;\n seenEnemyIds.add(placement.id);\n\n const enemy = data.enemies.get(placement.id);\n if (!enemy) continue;\n\n const casts: DungeonCastModel[] = (data.abilitiesByEnemy.get(enemy.id) ?? [])\n .filter((ability) => ability.castInterruptibility !== undefined)\n .map((ability) => {\n const canInterrupt = ability.castInterruptibility !== \"never\";\n const interruptVaries = ability.castInterruptibility === \"sometimes\";\n const canStop = enemy.canBeControlled;\n const image = abilityImage(ability);\n let imageUrl: string | null = null;\n if (image) {\n try {\n imageUrl = source.resolveAssetUrl(image);\n } catch {\n imageUrl = null;\n }\n }\n return {\n identity: { enemyId: enemy.id, abilityId: ability.id },\n ability,\n enemy,\n canInterrupt,\n interruptVaries,\n canStop,\n actionable: canInterrupt || canStop,\n capabilityLabel: capabilityLabel(canInterrupt, interruptVaries, canStop),\n imageUrl,\n };\n });\n\n if (casts.some(({ actionable }) => actionable)) enemies.push({ enemy, casts });\n }\n\n return {\n dungeon: data.dungeon,\n enemies,\n castCount: enemies.reduce((total, enemy) => total + enemy.casts.length, 0),\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 type { FellowshipCastDataSource } from \"../data/cast-data-source.js\";\nimport { FellowshipDataError } from \"../data/data-source.js\";\nimport { createPackageFellowshipCastDataSource } from \"../data/package-cast-data-source.js\";\nimport { installFieldManualFonts } from \"../styles/field-manual-fonts.js\";\nimport { fieldManualStyles } from \"../styles/field-manual.styles.js\";\nimport { dungeonCastBrowserStyles } from \"./dungeon-cast-browser.styles.js\";\nimport { castIdentityKey, createDungeonCastBrowserModel } from \"./render-model.js\";\nimport type {\n ConfiguredCastFilter,\n DungeonCastBrowserModel,\n DungeonCastEnemyModel,\n DungeonCastModel,\n FellowshipCastBrowserErrorDetail,\n FellowshipCastBrowserReadyDetail,\n FellowshipCastIdentity,\n FellowshipCastSelectedDetail,\n} from \"./types.js\";\n\ntype ComponentStatus = \"empty\" | \"loading\" | \"ready\" | \"error\";\n\nconst eventOptions = { bubbles: true, composed: true } as const;\n\n/**\n * @fires fellowship-cast-browser-ready - Fired after dungeon casts have been loaded and joined.\n * @fires fellowship-cast-browser-error - Fired when dungeon cast data cannot be loaded.\n * @fires fellowship-cast-selected - Fired when a user selects an actionable cast.\n */\n@customElement(\"fellowship-dungeon-cast-browser\")\nexport class FellowshipDungeonCastBrowserElement extends LitElement {\n static override styles = [fieldManualStyles, dungeonCastBrowserStyles];\n\n @property({ type: String, attribute: \"dungeon-id\" }) dungeonId = \"\";\n @property({ attribute: false }) selectedCast: FellowshipCastIdentity | null = null;\n @property({ attribute: false }) configuredCasts: readonly FellowshipCastIdentity[] = [];\n @property({ type: String, attribute: \"configured-filter\" })\n configuredFilter: ConfiguredCastFilter = \"all\";\n @property({ type: Boolean, reflect: true }) fill = false;\n @property({ type: String, attribute: \"asset-base-url\" }) assetBaseUrl: string | null = null;\n @property({ attribute: false }) dataSource: FellowshipCastDataSource | null = null;\n\n @state() private _status: ComponentStatus = \"empty\";\n @state() private _error: FellowshipCastBrowserErrorDetail | null = null;\n @state() private _model: DungeonCastBrowserModel | null = null;\n @state() private _query = \"\";\n\n #abortController: AbortController | null = null;\n #loadToken = 0;\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 if (changed.has(\"dungeonId\") || changed.has(\"dataSource\") || changed.has(\"assetBaseUrl\")) {\n queueMicrotask(() => {\n if (this.isConnected) void this.#loadDungeonCasts();\n });\n }\n }\n\n retry(): void {\n void this.#loadDungeonCasts();\n }\n\n get renderModel(): DungeonCastBrowserModel | null {\n return this._model;\n }\n\n override render(): TemplateResult {\n if (this._status !== \"ready\" || !this._model) {\n return html`<section class=\"browser\" aria-label=\"Dungeon cast browser\">${this.#renderStatus()}</section>`;\n }\n\n const visibleEnemies = this.#visibleEnemies(this._model);\n return html`\n <section class=\"browser\" aria-label=${`${this._model.dungeon.name ?? this._model.dungeon.id} casts`}>\n <header class=\"toolbar\" part=\"toolbar\">\n <div>\n <p class=\"eyebrow\">Dungeon cast index</p>\n <h2>${this._model.dungeon.name ?? this._model.dungeon.id}</h2>\n <p class=\"summary\">\n ${this._model.enemies.length} enemies · ${this._model.castCount} cast-bearing abilities\n </p>\n </div>\n <label class=\"search-label\">\n Search casts\n <input\n class=\"search-input\"\n type=\"search\"\n placeholder=\"Enemy or ability\"\n .value=${this._query}\n @input=${this.#handleSearch}\n />\n </label>\n </header>\n ${\n visibleEnemies.length > 0\n ? html`<div class=\"enemy-list\" part=\"enemy-list\">${repeat(\n visibleEnemies,\n ({ enemy }) => enemy.id,\n (enemy) => this.#renderEnemy(enemy),\n )}</div>`\n : this.#renderEmptySearch()\n }\n </section>\n `;\n }\n\n async #loadDungeonCasts(): Promise<void> {\n const dungeonId = this.dungeonId.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._query = \"\";\n\n if (!dungeonId) {\n this._status = \"empty\";\n return;\n }\n\n this._status = \"loading\";\n const source =\n this.dataSource ??\n createPackageFellowshipCastDataSource(\n this.assetBaseUrl ? { assetBaseUrl: this.assetBaseUrl } : {},\n );\n\n try {\n const data = await source.loadDungeonCasts(dungeonId, this.#abortController.signal);\n if (token !== this.#loadToken || this.#abortController.signal.aborted) return;\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 = createDungeonCastBrowserModel(data, source);\n this._model = model;\n this._status = \"ready\";\n this.dispatchEvent(\n new CustomEvent<FellowshipCastBrowserReadyDetail>(\"fellowship-cast-browser-ready\", {\n ...eventOptions,\n detail: { model, data },\n }),\n );\n } catch (error: unknown) {\n if (token !== this.#loadToken || this.#abortController.signal.aborted) return;\n const detail: FellowshipCastBrowserErrorDetail = {\n code:\n error instanceof FellowshipDataError\n ? error.code\n : ((error as { code?: string }).code ?? \"data-load-failed\"),\n message: error instanceof Error ? error.message : String(error),\n ...(error instanceof Error && error.cause !== undefined ? { cause: error.cause } : {}),\n };\n this._error = detail;\n this._status = \"error\";\n this.dispatchEvent(\n new CustomEvent<FellowshipCastBrowserErrorDetail>(\"fellowship-cast-browser-error\", {\n ...eventOptions,\n detail,\n }),\n );\n }\n }\n\n #visibleEnemies(model: DungeonCastBrowserModel): readonly DungeonCastEnemyModel[] {\n const query = this._query.trim().toLocaleLowerCase();\n const configuredFilter =\n this.configuredFilter === \"configured\" || this.configuredFilter === \"unconfigured\"\n ? this.configuredFilter\n : \"all\";\n return model.enemies.flatMap((enemy) => {\n const enemyMatches = `${enemy.enemy.name ?? \"\"} ${enemy.enemy.id}`\n .toLocaleLowerCase()\n .includes(query);\n const casts = enemy.casts.filter((cast) => {\n const queryMatches =\n !query ||\n enemyMatches ||\n `${cast.ability.name ?? \"\"} ${cast.ability.id}`.toLocaleLowerCase().includes(query);\n if (!queryMatches) return false;\n const configured = this.configuredCasts.some((identity) =>\n this.#sameIdentity(identity, cast.identity),\n );\n return (\n configuredFilter === \"all\" ||\n (configuredFilter === \"configured\" && configured) ||\n (configuredFilter === \"unconfigured\" && !configured)\n );\n });\n return casts.length > 0 ? [{ ...enemy, casts }] : [];\n });\n }\n\n #renderEnemy(model: DungeonCastEnemyModel): TemplateResult {\n return html`\n <article class=\"enemy\" part=\"enemy\">\n <header class=\"enemy-header\" part=\"enemy-header\">\n <span class=\"enemy-rule\" aria-hidden=\"true\"></span>\n <div>\n <h3 class=\"enemy-name\">${model.enemy.name ?? model.enemy.id}</h3>\n <p class=\"enemy-id\">${model.enemy.id}</p>\n </div>\n <span class=\"enemy-count\">${model.casts.length} ${model.casts.length === 1 ? \"cast\" : \"casts\"}</span>\n </header>\n <ul class=\"cast-list\">\n ${repeat(\n model.casts,\n ({ identity }) => castIdentityKey(identity),\n (cast) => html`<li>${this.#renderCast(cast)}</li>`,\n )}\n </ul>\n </article>\n `;\n }\n\n #renderCast(cast: DungeonCastModel): TemplateResult {\n const selected = this.#sameIdentity(this.selectedCast, cast.identity);\n const configured = this.configuredCasts.some((identity) =>\n this.#sameIdentity(identity, cast.identity),\n );\n const capabilityKind = cast.interruptVaries\n ? \"varies\"\n : cast.canInterrupt\n ? \"interrupt\"\n : cast.canStop\n ? \"stop\"\n : \"none\";\n const fallback = (cast.ability.name ?? cast.ability.id)\n .split(/\\s+/)\n .map((part) => part[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n\n return html`\n <button\n class=\"cast-row\"\n part=\"cast\"\n type=\"button\"\n ?disabled=${!cast.actionable}\n aria-pressed=${String(selected)}\n data-configured=${String(configured)}\n @click=${() => this.#selectCast(cast)}\n @keydown=${this.#handleCastKeydown}\n >\n ${\n cast.imageUrl\n ? html`<img\n class=\"ability-image\"\n part=\"cast-icon\"\n src=${cast.imageUrl}\n alt=\"\"\n @error=${this.#handleAbilityImageError}\n /><span\n class=\"ability-fallback\"\n part=\"cast-icon-fallback\"\n aria-hidden=\"true\"\n hidden\n >${fallback}</span\n >`\n : html`<span class=\"ability-fallback\" part=\"cast-icon-fallback\" aria-hidden=\"true\">${fallback}</span>`\n }\n <span class=\"ability-copy\">\n <span class=\"ability-name\">${cast.ability.name ?? cast.ability.id}</span>\n <span class=\"ability-id\">${cast.ability.id}</span>\n </span>\n <span class=\"capability\" data-kind=${capabilityKind}>${cast.capabilityLabel}</span>\n <span class=\"marker\" data-empty=${String(!configured)}>${configured ? \"Configured\" : nothing}</span>\n </button>\n `;\n }\n\n #renderStatus(): TemplateResult {\n if (this._status === \"loading\") {\n return this.#statusCard(\n \"Loading cast index\",\n \"Joining dungeon enemies with their cast data.\",\n );\n }\n if (this._status === \"error\") {\n return html`\n <div class=\"status\" role=\"alert\">\n <div class=\"status-card\">\n <span class=\"status-mark\" aria-hidden=\"true\"></span>\n <h2 class=\"status-title\">Cast data unavailable</h2>\n <p class=\"status-copy\">${this._error?.message ?? \"The dungeon cast index could not be loaded.\"}</p>\n <button class=\"retry-button\" type=\"button\" @click=${this.retry}>Retry</button>\n </div>\n </div>\n `;\n }\n if (this._status === \"ready\") {\n return this.#statusCard(\n \"No actionable casts\",\n \"This dungeon has no enemies with casts that can be interrupted or stopped.\",\n );\n }\n return this.#statusCard(\"Select a dungeon\", \"Choose a dungeon to inspect enemy casts.\");\n }\n\n #renderEmptySearch(): TemplateResult {\n return this.#statusCard(\n \"No matching casts\",\n `No enemy or ability matches “${this._query.trim()}”.`,\n );\n }\n\n #statusCard(title: string, copy: string): TemplateResult {\n return html`\n <div class=\"status\">\n <div class=\"status-card\">\n <span class=\"status-mark\" aria-hidden=\"true\"></span>\n <h2 class=\"status-title\">${title}</h2>\n <p class=\"status-copy\">${copy}</p>\n </div>\n </div>\n `;\n }\n\n #sameIdentity(left: FellowshipCastIdentity | null, right: FellowshipCastIdentity): boolean {\n return left?.enemyId === right.enemyId && left.abilityId === right.abilityId;\n }\n\n #selectCast(cast: DungeonCastModel): void {\n if (!cast.actionable) return;\n this.dispatchEvent(\n new CustomEvent<FellowshipCastSelectedDetail>(\"fellowship-cast-selected\", {\n ...eventOptions,\n detail: { cast },\n }),\n );\n }\n\n #handleSearch = (event: InputEvent): void => {\n this._query = (event.currentTarget as HTMLInputElement).value;\n };\n\n #handleAbilityImageError = (event: Event): void => {\n const image = event.currentTarget as HTMLImageElement;\n image.hidden = true;\n const fallback = image.nextElementSibling;\n if (fallback instanceof HTMLElement) fallback.hidden = false;\n };\n\n #handleCastKeydown = (event: KeyboardEvent): void => {\n const movement =\n event.key === \"ArrowDown\" || event.key === \"ArrowRight\"\n ? 1\n : event.key === \"ArrowUp\" || event.key === \"ArrowLeft\"\n ? -1\n : 0;\n if (movement === 0 && event.key !== \"Home\" && event.key !== \"End\") return;\n const buttons = [\n ...this.renderRoot.querySelectorAll<HTMLButtonElement>(\".cast-row:not(:disabled)\"),\n ];\n if (buttons.length === 0) return;\n const current = buttons.indexOf(event.currentTarget as HTMLButtonElement);\n const nextIndex =\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? buttons.length - 1\n : (current + movement + buttons.length) % buttons.length;\n buttons[nextIndex]?.focus();\n event.preventDefault();\n };\n}\n\nexport const defineFellowshipDungeonCastBrowser = (): void => {\n if (\n typeof globalThis.customElements !== \"undefined\" &&\n !globalThis.customElements.get(\"fellowship-dungeon-cast-browser\")\n ) {\n globalThis.customElements.define(\n \"fellowship-dungeon-cast-browser\",\n FellowshipDungeonCastBrowserElement,\n );\n }\n};\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"fellowship-dungeon-cast-browser\": FellowshipDungeonCastBrowserElement;\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"dungeon-map-xFaPasVr.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 { createPackageFellowshipDataClient } from \"@crockery/fellowship-data/client\";\n\nimport type { FellowshipDataSource } from \"./data-source.js\";\n\n/** @deprecated Create a package Fellowship data client instead. */\nexport interface PackageFellowshipDataSourceOptions {\n readonly assetBaseUrl?: string;\n}\n\n/** @deprecated Use `createPackageFellowshipDataClient` from `@crockery/fellowship-data/client`. */\nexport const createPackageFellowshipDataSource = (\n options: PackageFellowshipDataSourceOptions = {},\n): FellowshipDataSource => {\n const client = createPackageFellowshipDataClient(options);\n return {\n loadDungeon: (dungeonId, signal) => client.loadDungeon(dungeonId, signal),\n resolveAssetUrl: (asset) => client.resolveAssetUrl(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":"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"}