@crockery/fellowship-components 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +7 -2
  2. package/RELEASING.md +9 -45
  3. package/dist/chunks/data-source-C-Gs7kRl.js +2 -0
  4. package/dist/chunks/decorate-B_ZTN9li.js.map +1 -1
  5. package/dist/chunks/dungeon-cast-browser-Cyh519NI.js +705 -0
  6. package/dist/chunks/dungeon-cast-browser-Cyh519NI.js.map +1 -0
  7. package/dist/chunks/{dungeon-map-IPN6sEDj.js → dungeon-map-xFaPasVr.js} +113 -138
  8. package/dist/chunks/dungeon-map-xFaPasVr.js.map +1 -0
  9. package/dist/chunks/{talent-calculator-Ch1Pbbba.js → talent-calculator-CIPNbqK5.js} +84 -114
  10. package/dist/chunks/talent-calculator-CIPNbqK5.js.map +1 -0
  11. package/dist/data/cast-data-source.d.ts +5 -0
  12. package/dist/data/cast-data-source.d.ts.map +1 -0
  13. package/dist/data/data-source.d.ts +5 -16
  14. package/dist/data/data-source.d.ts.map +1 -1
  15. package/dist/data/package-cast-data-source.d.ts +8 -0
  16. package/dist/data/package-cast-data-source.d.ts.map +1 -0
  17. package/dist/data/package-data-source.d.ts +3 -1
  18. package/dist/data/package-data-source.d.ts.map +1 -1
  19. package/dist/data/package-talent-data-source.d.ts +2 -0
  20. package/dist/data/package-talent-data-source.d.ts.map +1 -1
  21. package/dist/data/talent-data-source.d.ts +4 -10
  22. package/dist/data/talent-data-source.d.ts.map +1 -1
  23. package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts +36 -0
  24. package/dist/dungeon-cast-browser/dungeon-cast-browser.d.ts.map +1 -0
  25. package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts +2 -0
  26. package/dist/dungeon-cast-browser/dungeon-cast-browser.styles.d.ts.map +1 -0
  27. package/dist/dungeon-cast-browser/index.d.ts +4 -0
  28. package/dist/dungeon-cast-browser/index.d.ts.map +1 -0
  29. package/dist/dungeon-cast-browser/render-model.d.ts +5 -0
  30. package/dist/dungeon-cast-browser/render-model.d.ts.map +1 -0
  31. package/dist/dungeon-cast-browser/types.d.ts +60 -0
  32. package/dist/dungeon-cast-browser/types.d.ts.map +1 -0
  33. package/dist/dungeon-cast-browser.js +7 -0
  34. package/dist/dungeon-cast-browser.js.map +1 -0
  35. package/dist/dungeon-map.js +1 -1
  36. package/dist/index.d.ts +6 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +8 -5
  39. package/dist/react/dungeon-cast-browser.d.ts +12 -0
  40. package/dist/react/dungeon-cast-browser.d.ts.map +1 -0
  41. package/dist/react/dungeon-cast-browser.js +19 -0
  42. package/dist/react/dungeon-cast-browser.js.map +1 -0
  43. package/dist/react/dungeon-map.js +1 -1
  44. package/dist/react/index.d.ts +2 -0
  45. package/dist/react/index.d.ts.map +1 -1
  46. package/dist/react/talent-calculator.js +1 -1
  47. package/dist/react.js +4 -3
  48. package/dist/talent-calculator.js +1 -1
  49. package/package.json +35 -25
  50. package/src/dungeon-cast-browser/README.md +22 -0
  51. package/dist/chunks/data-source-CoIFvV72.js +0 -10
  52. package/dist/chunks/data-source-CoIFvV72.js.map +0 -1
  53. package/dist/chunks/dungeon-map-IPN6sEDj.js.map +0 -1
  54. package/dist/chunks/talent-calculator-Ch1Pbbba.js.map +0 -1
@@ -0,0 +1 @@
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,44 +1,19 @@
1
- import { t as e } from "./data-source-CoIFvV72.js";
1
+ import { t as e } from "./data-source-C-Gs7kRl.js";
2
2
  import { n as t, r as n, t as r } from "./decorate-B_ZTN9li.js";
3
- import { findImage as i, getAssetUrl as a } from "@crockery/fellowship-data";
4
- import { LitElement as o, css as s, html as c, nothing as l } from "lit";
5
- import { customElement as u, property as d, query as f, state as p } from "lit/decorators.js";
6
- import { repeat as m } from "lit/directives/repeat.js";
7
- import { styleMap as h } from "lit/directives/style-map.js";
3
+ import { createPackageFellowshipDataClient as i } from "@crockery/fellowship-data/client";
4
+ import { LitElement as a, css as o, html as s, nothing as c } from "lit";
5
+ import { customElement as l, property as u, query as d, state as f } from "lit/decorators.js";
6
+ import { repeat as p } from "lit/directives/repeat.js";
7
+ import { styleMap as m } from "lit/directives/style-map.js";
8
+ import { findImage as h } from "@crockery/fellowship-data";
8
9
  //#region src/data/package-data-source.ts
9
- var g, _ = (t) => {
10
- if (t?.aborted) throw new e("aborted", "The dungeon data request was aborted.", t.reason);
11
- }, v = () => (g ??= Promise.all([
12
- import("@crockery/fellowship-data/dungeons"),
13
- import("@crockery/fellowship-data/enemies"),
14
- import("@crockery/fellowship-data/manifest")
15
- ]).then(([t, n, r]) => {
16
- let i = r.default;
17
- if (i.schemaVersion !== 1) throw new e("unsupported-schema", `Unsupported fellowship-data schema version: ${String(i.schemaVersion)}.`);
10
+ var g = (e = {}) => {
11
+ let t = i(e);
18
12
  return {
19
- manifest: i,
20
- dungeons: new Map(t.default.map((e) => [e.id, e])),
21
- enemies: new Map(n.default.map((e) => [e.id, e]))
13
+ loadDungeon: (e, n) => t.loadDungeon(e, n),
14
+ resolveAssetUrl: (e) => t.resolveAssetUrl(e)
22
15
  };
23
- }).catch((t) => {
24
- throw g = void 0, t instanceof e ? t : new e("data-load-failed", "Unable to load @crockery/fellowship-data.", t);
25
- }), g), y = (t = {}) => ({
26
- async loadDungeon(t, n) {
27
- _(n);
28
- let r = await v();
29
- _(n);
30
- let i = r.dungeons.get(t);
31
- if (!i) throw new e("dungeon-not-found", `No dungeon with ID “${t}” exists in @crockery/fellowship-data.`);
32
- return {
33
- manifest: r.manifest,
34
- dungeon: i,
35
- enemies: r.enemies
36
- };
37
- },
38
- resolveAssetUrl(e) {
39
- return t.assetBaseUrl ? a(e, { baseUrl: t.assetBaseUrl }) : a(e);
40
- }
41
- }), b = s`
16
+ }, _ = o`
42
17
  :host {
43
18
  --fellowship-map-soot: var(--_fellowship-field-canvas);
44
19
  --fellowship-map-ink: var(--_fellowship-field-ink);
@@ -484,7 +459,7 @@ var g, _ = (t) => {
484
459
  transform: none;
485
460
  }
486
461
  }
487
- `, x = .018, S = .036, C = .06, w = .12, T = (e, t, n) => Math.min(n, Math.max(t, e)), E = (e) => {
462
+ `, v = .018, y = .036, b = .06, x = .12, S = (e, t, n) => Math.min(n, Math.max(t, e)), C = (e) => {
488
463
  let t = e.map(({ position: e }) => e.x), n = e.map(({ position: e }) => e.y), r = Math.min(...t), i = Math.max(...t), a = Math.min(...n), o = Math.max(...n);
489
464
  return {
490
465
  x: r,
@@ -492,91 +467,91 @@ var g, _ = (t) => {
492
467
  width: i - r,
493
468
  height: o - a
494
469
  };
495
- }, D = (e) => {
496
- let t = e.x + e.width / 2, n = e.y + e.height / 2, r = Math.max(C, e.width + x * 2), i = Math.max(w, e.height + S * 2);
470
+ }, w = (e) => {
471
+ let t = e.x + e.width / 2, n = e.y + e.height / 2, r = Math.max(b, e.width + v * 2), i = Math.max(x, e.height + y * 2);
497
472
  return {
498
- x: T(t - r / 2, 0, Math.max(0, 1 - r)),
499
- y: T(n - i / 2, 0, Math.max(0, 1 - i)),
473
+ x: S(t - r / 2, 0, Math.max(0, 1 - r)),
474
+ y: S(n - i / 2, 0, Math.max(0, 1 - i)),
500
475
  width: Math.min(r, 1),
501
476
  height: Math.min(i, 1)
502
477
  };
503
- }, O = (e) => ({
478
+ }, T = (e) => ({
504
479
  x: e.reduce((e, t) => e + t.position.x, 0) / e.length,
505
480
  y: e.reduce((e, t) => e + t.position.y, 0) / e.length
506
- }), k = (e) => {
481
+ }), E = (e) => {
507
482
  let t = e.map(({ enemy: e }) => e?.dungeonScore).filter((e) => typeof e == "number");
508
483
  return t.length > 0 ? t.reduce((e, t) => e + t, 0) : null;
509
- }, A = (e, t) => {
510
- let n = E(t);
484
+ }, D = (e, t) => {
485
+ let n = C(t);
511
486
  return {
512
487
  id: e,
513
488
  enemies: t,
514
489
  enemyBounds: n,
515
- visualBounds: D(n),
516
- center: O(t),
517
- dungeonScore: k(t)
490
+ visualBounds: w(n),
491
+ center: T(t),
492
+ dungeonScore: E(t)
518
493
  };
519
- }, j = (e) => e !== null && Number.isFinite(e.x) && Number.isFinite(e.y) && e.x >= 0 && e.x <= 1 && e.y >= 0 && e.y <= 1, M = (e, t, n) => {
520
- let r = [], a = /* @__PURE__ */ new Map();
521
- return e.dungeon.enemies.forEach((o, s) => {
522
- if (o.mapImageId !== t.id) return;
523
- if (!j(o.mapPosition)) {
494
+ }, O = (e) => e !== null && Number.isFinite(e.x) && Number.isFinite(e.y) && e.x >= 0 && e.x <= 1 && e.y >= 0 && e.y <= 1, k = (e, t, n) => {
495
+ let r = [], i = /* @__PURE__ */ new Map();
496
+ return e.dungeon.enemies.forEach((a, o) => {
497
+ if (a.mapImageId !== t.id) return;
498
+ if (!O(a.mapPosition)) {
524
499
  r.push({
525
500
  code: "invalid-map-position",
526
- message: `Skipped ${o.id}: its map position is missing or outside the map.`,
527
- placementIndex: s,
528
- enemyId: o.id
501
+ message: `Skipped ${a.id}: its map position is missing or outside the map.`,
502
+ placementIndex: o,
503
+ enemyId: a.id
529
504
  });
530
505
  return;
531
506
  }
532
- let c = e.enemies.get(o.id) ?? null;
533
- c || r.push({
507
+ let s = e.enemies.get(a.id) ?? null;
508
+ s || r.push({
534
509
  code: "missing-enemy",
535
- message: `No enemy metadata exists for ${o.id}.`,
536
- placementIndex: s,
537
- enemyId: o.id
510
+ message: `No enemy metadata exists for ${a.id}.`,
511
+ placementIndex: o,
512
+ enemyId: a.id
538
513
  });
539
- let l = c ? i(c.images, {
514
+ let c = s ? h(s.images, {
540
515
  type: "enemy",
541
516
  size: "200"
542
- }) : null, u = null;
543
- if (l) try {
544
- u = n.resolveAssetUrl(l);
517
+ }) : null, l = null;
518
+ if (c) try {
519
+ l = n.resolveAssetUrl(c);
545
520
  } catch (e) {
546
521
  r.push({
547
522
  code: "asset-url-error",
548
- message: `Could not resolve the image URL for ${o.id}: ${String(e)}.`,
549
- placementIndex: s,
550
- enemyId: o.id
523
+ message: `Could not resolve the image URL for ${a.id}: ${String(e)}.`,
524
+ placementIndex: o,
525
+ enemyId: a.id
551
526
  });
552
527
  }
553
- else c && r.push({
528
+ else s && r.push({
554
529
  code: "missing-enemy-image",
555
- message: `No 200px enemy image exists for ${o.id}.`,
556
- placementIndex: s,
557
- enemyId: o.id
530
+ message: `No 200px enemy image exists for ${a.id}.`,
531
+ placementIndex: o,
532
+ enemyId: a.id
558
533
  });
559
- let d = {
560
- instanceId: `${e.dungeon.id}:${t.id}:${s}`,
561
- enemy: c,
562
- placement: o,
563
- position: o.mapPosition,
564
- imageUrl: u
565
- }, f = o.groupId === null ? `singleton:${s}` : `group:${o.groupId}`, p = a.get(f) ?? {
566
- id: o.groupId,
534
+ let u = {
535
+ instanceId: `${e.dungeon.id}:${t.id}:${o}`,
536
+ enemy: s,
537
+ placement: a,
538
+ position: a.mapPosition,
539
+ imageUrl: l
540
+ }, d = a.groupId === null ? `singleton:${o}` : `group:${a.groupId}`, f = i.get(d) ?? {
541
+ id: a.groupId,
567
542
  enemies: []
568
543
  };
569
- p.enemies.push(d), a.set(f, p);
544
+ f.enemies.push(u), i.set(d, f);
570
545
  }), {
571
546
  dungeon: e.dungeon,
572
547
  mapImage: t,
573
- groups: [...a.values()].map(({ id: e, enemies: t }) => A(e, t)),
548
+ groups: [...i.values()].map(({ id: e, enemies: t }) => D(e, t)),
574
549
  warnings: r
575
550
  };
576
- }, N = (e, t) => {
551
+ }, A = (e, t) => {
577
552
  let n = (e.id.split(":").at(-1) ?? e.id).replace(/^T_(?:Map_)?/i, "").replace(/^map[-_]/i, "").replaceAll(/[_-]+/g, " ").replace(/([a-z\d])([A-Z])/g, "$1 $2").trim(), r = t.id.split(".").at(-1)?.toLowerCase();
578
553
  return (n.split(/\s+/).filter((e) => e.toLowerCase() !== r).join(" ") || n || "Map").replace(/\b[a-z]/g, (e) => e.toUpperCase());
579
- }, P = (e, t, n) => Math.min(n, Math.max(t, e)), F = class {
554
+ }, j = (e, t, n) => Math.min(n, Math.max(t, e)), M = class {
580
555
  constructor() {
581
556
  this.#e = 0, this.#t = 0, this.#n = 0, this.#r = 0, this.#i = 1, this.#a = 0, this.#o = 0, this.maxZoom = 8;
582
557
  }
@@ -610,26 +585,26 @@ var g, _ = (t) => {
610
585
  }
611
586
  zoomAt(e, t = this.#e / 2, n = this.#t / 2) {
612
587
  let r = this.scale, i = (t - this.#a) / r, a = (n - this.#o) / r;
613
- return this.#i = P(this.#i * e, 1, Math.max(1, this.maxZoom)), this.#a = t - i * this.scale, this.#o = n - a * this.scale, this.#s(), this.state;
588
+ return this.#i = j(this.#i * e, 1, Math.max(1, this.maxZoom)), this.#a = t - i * this.scale, this.#o = n - a * this.scale, this.#s(), this.state;
614
589
  }
615
590
  panBy(e, t) {
616
591
  return this.#a += e, this.#o += t, this.#s(), this.state;
617
592
  }
618
593
  #s() {
619
594
  let e = this.#n * this.scale, t = this.#r * this.scale;
620
- this.#a = e <= this.#e ? (this.#e - e) / 2 : P(this.#a, this.#e - e, 0), this.#o = t <= this.#t ? (this.#t - t) / 2 : P(this.#o, this.#t - t, 0);
595
+ this.#a = e <= this.#e ? (this.#e - e) / 2 : j(this.#a, this.#e - e, 0), this.#o = t <= this.#t ? (this.#t - t) / 2 : j(this.#o, this.#t - t, 0);
621
596
  }
622
- }, I = {
597
+ }, N = {
623
598
  bubbles: !0,
624
599
  composed: !0
625
- }, L = class extends o {
600
+ }, P = class extends a {
626
601
  constructor(...e) {
627
602
  super(...e), this.dungeonId = "", this.mapImageId = null, this.assetBaseUrl = null, this.dataSource = null, this.renderEnemy = null, this.renderGroup = null, this.mapLabelFormatter = null, this.showControls = !0, this.showMapSelector = !0, this.maxZoom = 8, this.externalOverlay = !1, this._status = "empty", this._error = null, this._data = null, this._model = null, this._selectedMap = null, this._viewportState = {
628
603
  zoom: 1,
629
604
  scale: 1,
630
605
  x: 0,
631
606
  y: 0
632
- }, this._isPanning = !1, this._enemyRenderContent = /* @__PURE__ */ new Map(), this._groupRenderContent = /* @__PURE__ */ new Map(), this._mapLabels = /* @__PURE__ */ new Map(), this.#e = null, this.#t = null, this.#n = 0, this.#r = 0, this.#i = null, this.#a = new F(), this.#o = /* @__PURE__ */ new Map(), this.#s = null, this.#c = 0, this.#l = null, this.#u = /* @__PURE__ */ new Set(), this.#d = [], this.#j = (e) => {
607
+ }, this._isPanning = !1, this._enemyRenderContent = /* @__PURE__ */ new Map(), this._groupRenderContent = /* @__PURE__ */ new Map(), this._mapLabels = /* @__PURE__ */ new Map(), this.#e = null, this.#t = null, this.#n = 0, this.#r = 0, this.#i = null, this.#a = new M(), this.#o = /* @__PURE__ */ new Map(), this.#s = null, this.#c = 0, this.#l = null, this.#u = /* @__PURE__ */ new Set(), this.#d = [], this.#j = (e) => {
633
608
  if (![
634
609
  "ArrowLeft",
635
610
  "ArrowRight",
@@ -702,7 +677,7 @@ var g, _ = (t) => {
702
677
  };
703
678
  }
704
679
  static {
705
- this.styles = [n, b];
680
+ this.styles = [n, _];
706
681
  }
707
682
  #e;
708
683
  #t;
@@ -763,7 +738,7 @@ var g, _ = (t) => {
763
738
  transform: `translate3d(${t.x}px, ${t.y}px, 0) scale(${t.scale})`,
764
739
  "--fellowship-map-inverse-scale": String(1 / t.scale)
765
740
  } : {};
766
- return c`
741
+ return s`
767
742
  <section class="viewer" aria-label=${this.#f}>
768
743
  <div
769
744
  class="viewport"
@@ -780,13 +755,13 @@ var g, _ = (t) => {
780
755
  @pointerup=${this.#I}
781
756
  @pointercancel=${this.#I}
782
757
  >
783
- ${e ? c`
758
+ ${e ? s`
784
759
  <div
785
760
  class="stage"
786
761
  id="map-stage"
787
- role=${n === l ? l : "tabpanel"}
762
+ role=${n === c ? c : "tabpanel"}
788
763
  aria-labelledby=${n}
789
- style=${h(r)}
764
+ style=${m(r)}
790
765
  >
791
766
  <canvas
792
767
  class="map-canvas"
@@ -797,12 +772,12 @@ var g, _ = (t) => {
797
772
  .height=${e.mapImage.height}
798
773
  ></canvas>
799
774
  <div class="overlay" part="overlay">
800
- ${this.externalOverlay ? c`<slot name="overlay"></slot>` : this.#b(e)}
775
+ ${this.externalOverlay ? s`<slot name="overlay"></slot>` : this.#b(e)}
801
776
  </div>
802
777
  </div>
803
778
  ${this.#E(e)} ${this.#D()}
804
- ${e.groups.length === 0 && this._status === "ready" ? c`<div class="empty-badge">No enemy placements on this map</div>` : l}
805
- ` : l}
779
+ ${e.groups.length === 0 && this._status === "ready" ? s`<div class="empty-badge">No enemy placements on this map</div>` : c}
780
+ ` : c}
806
781
  ${this.#O()}
807
782
  <p class="visually-hidden" id="map-instructions">
808
783
  Drag to pan. Use the mouse wheel, plus and minus keys, or zoom controls to zoom. Press
@@ -816,9 +791,9 @@ var g, _ = (t) => {
816
791
  return this._data ? `${this._data.dungeon.name ?? this._data.dungeon.id} dungeon map` : "Fellowship dungeon map";
817
792
  }
818
793
  get #p() {
819
- if (!this.showMapSelector || !this._model || this._model.dungeon.mapImages.length <= 1 || !this._selectedMap) return l;
794
+ if (!this.showMapSelector || !this._model || this._model.dungeon.mapImages.length <= 1 || !this._selectedMap) return c;
820
795
  let e = this._model.dungeon.mapImages.findIndex((e) => e.id === this._selectedMap?.id);
821
- return e >= 0 ? `map-tab-${e}` : l;
796
+ return e >= 0 ? `map-tab-${e}` : c;
822
797
  }
823
798
  async #m() {
824
799
  let t = this.dungeonId.trim(), n = ++this.#n;
@@ -827,7 +802,7 @@ var g, _ = (t) => {
827
802
  return;
828
803
  }
829
804
  this._status = "loading";
830
- let r = this.dataSource ?? y(this.assetBaseUrl ? { assetBaseUrl: this.assetBaseUrl } : {});
805
+ let r = this.dataSource ?? g(this.assetBaseUrl ? { assetBaseUrl: this.assetBaseUrl } : {});
831
806
  this.#e = r;
832
807
  try {
833
808
  let i = await r.loadDungeon(t, this.#t.signal);
@@ -850,20 +825,20 @@ var g, _ = (t) => {
850
825
  });
851
826
  return;
852
827
  }
853
- let i = ++this.#r, a = M(t, r, n);
828
+ let i = ++this.#r, a = k(t, r, n);
854
829
  this._model = a, this._selectedMap = r, this._status = "loading", this._error = null, await this.updateComplete;
855
830
  let o = this._viewportElement;
856
831
  o && (this.#a.maxZoom = Math.max(1, this.maxZoom), this.#a.setDimensions(o.clientWidth, o.clientHeight, r.width, r.height), e && this.#a.fit(), this._viewportState = this.#a.state, await this.updateComplete);
857
832
  try {
858
833
  if (await this.#g(r, n, i), i !== this.#r) return;
859
834
  this._status = "ready", this.dispatchEvent(new CustomEvent("fellowship-map-ready", {
860
- ...I,
835
+ ...N,
861
836
  detail: {
862
837
  model: a,
863
838
  data: t
864
839
  }
865
840
  })), a.warnings.length > 0 && this.dispatchEvent(new CustomEvent("fellowship-map-warning", {
866
- ...I,
841
+ ...N,
867
842
  detail: {
868
843
  warnings: a.warnings,
869
844
  model: a
@@ -894,7 +869,7 @@ var g, _ = (t) => {
894
869
  message: t instanceof Error ? t.message : String(t)
895
870
  };
896
871
  this._error = n, this._status = "error", this.dispatchEvent(new CustomEvent("fellowship-map-error", {
897
- ...I,
872
+ ...N,
898
873
  detail: n
899
874
  }));
900
875
  }
@@ -932,14 +907,14 @@ var g, _ = (t) => {
932
907
  #y() {
933
908
  let e = /* @__PURE__ */ new Map(), t = this._data?.dungeon;
934
909
  if (t) for (let n of t.mapImages) try {
935
- e.set(n.id, this.mapLabelFormatter?.(n, t) ?? N(n, t));
910
+ e.set(n.id, this.mapLabelFormatter?.(n, t) ?? A(n, t));
936
911
  } catch (r) {
937
- this.#B(`map-label:${n.id}`, "map-label-formatter-error", r), e.set(n.id, N(n, t));
912
+ this.#B(`map-label:${n.id}`, "map-label-formatter-error", r), e.set(n.id, A(n, t));
938
913
  }
939
914
  this._mapLabels = e;
940
915
  }
941
916
  #b(e) {
942
- return m(e.groups, (t) => this.#x(e, t), (t) => this.#S(e, t));
917
+ return p(e.groups, (t) => this.#x(e, t), (t) => this.#S(e, t));
943
918
  }
944
919
  #x(e, t) {
945
920
  return `${e.mapImage.id}:${String(t.id)}:${t.enemies[0]?.instanceId ?? "empty"}`;
@@ -950,15 +925,15 @@ var g, _ = (t) => {
950
925
  top: `${n.y * 100}%`,
951
926
  width: `${n.width * 100}%`,
952
927
  height: `${n.height * 100}%`
953
- }, i = this.#x(e, t), a = this.renderGroup && this._groupRenderContent.has(i) ? this._groupRenderContent.get(i) ?? l : this.#C(t);
954
- return c` <div class="group-anchor" part="group" style=${h(r)}>${a}</div> `;
928
+ }, i = this.#x(e, t), a = this.renderGroup && this._groupRenderContent.has(i) ? this._groupRenderContent.get(i) ?? c : this.#C(t);
929
+ return s` <div class="group-anchor" part="group" style=${m(r)}>${a}</div> `;
955
930
  }
956
931
  #C(e) {
957
- return c`
932
+ return s`
958
933
  <div class="default-group">
959
934
  <div class="group-ring" aria-hidden="true"></div>
960
935
  <span class="group-label">${`G${e.id ?? "–"}${e.dungeonScore === null ? "" : ` · ${e.dungeonScore}`}`}</span>
961
- ${m(e.enemies, (e) => e.instanceId, (t) => this.#w(e, t))}
936
+ ${p(e.enemies, (e) => e.instanceId, (t) => this.#w(e, t))}
962
937
  </div>
963
938
  `;
964
939
  }
@@ -966,12 +941,12 @@ var g, _ = (t) => {
966
941
  let n = e.visualBounds, r = {
967
942
  left: `${(t.position.x - n.x) / n.width * 100}%`,
968
943
  top: `${(t.position.y - n.y) / n.height * 100}%`
969
- }, i = this.renderEnemy && this._enemyRenderContent.has(t.instanceId) ? this._enemyRenderContent.get(t.instanceId) ?? l : this.#T(t), a = t.enemy?.name ?? t.placement.id;
970
- return c`
944
+ }, i = this.renderEnemy && this._enemyRenderContent.has(t.instanceId) ? this._enemyRenderContent.get(t.instanceId) ?? c : this.#T(t), a = t.enemy?.name ?? t.placement.id;
945
+ return s`
971
946
  <div
972
947
  class="enemy-anchor"
973
948
  part="enemy"
974
- style=${h(r)}
949
+ style=${m(r)}
975
950
  role="img"
976
951
  aria-label=${a}
977
952
  title=${a}
@@ -982,17 +957,17 @@ var g, _ = (t) => {
982
957
  }
983
958
  #T(e) {
984
959
  let t = (e.enemy?.name ?? e.placement.id).split(/\s+/).map((e) => e[0]).join("").slice(0, 2).toUpperCase();
985
- return c`
960
+ return s`
986
961
  <span class="enemy-marker" data-boss=${String(e.enemy?.isBoss === !0)}>
987
- ${e.imageUrl ? c`<img class="enemy-image" src=${e.imageUrl} alt="" draggable="false" />` : t}
962
+ ${e.imageUrl ? s`<img class="enemy-image" src=${e.imageUrl} alt="" draggable="false" />` : t}
988
963
  </span>
989
964
  `;
990
965
  }
991
966
  #E(e) {
992
967
  let t = e.dungeon.mapImages;
993
- return !this.showMapSelector || t.length <= 1 ? l : c`
968
+ return !this.showMapSelector || t.length <= 1 ? c : s`
994
969
  <div class="map-selector" part="map-selector" role="tablist" aria-label="Dungeon maps">
995
- ${m(t, (e) => e.id, (e, t) => c`
970
+ ${p(t, (e) => e.id, (e, t) => s`
996
971
  <button
997
972
  class="map-selector-button"
998
973
  id=${`map-tab-${t}`}
@@ -1013,7 +988,7 @@ var g, _ = (t) => {
1013
988
  `;
1014
989
  }
1015
990
  #D() {
1016
- return !this.showControls || !this._model ? l : c`
991
+ return !this.showControls || !this._model ? c : s`
1017
992
  <div class="controls" part="controls" role="group" aria-label="Map zoom controls">
1018
993
  <button
1019
994
  class="control-button"
@@ -1049,15 +1024,15 @@ var g, _ = (t) => {
1049
1024
  `;
1050
1025
  }
1051
1026
  #O() {
1052
- if (this._status === "ready") return l;
1027
+ if (this._status === "ready") return c;
1053
1028
  let e = this._status === "loading", t = this._status === "empty", n = e ? "Charting dungeon" : t ? "Choose a dungeon" : "Map unavailable", r = e ? "Loading Fellowship data and preparing the tactical layer." : t ? "Set the dungeon-id attribute or dungeonId property to render a map." : this._error?.message ?? "An unexpected map error occurred.";
1054
- return c`
1029
+ return s`
1055
1030
  <div class="status" part="status" role=${this._status === "error" ? "alert" : "status"}>
1056
1031
  <div class="status-card">
1057
1032
  <div class="status-mark" aria-hidden="true"><span>${e ? "…" : "✦"}</span></div>
1058
1033
  <p class="status-title">${n}</p>
1059
1034
  <p class="status-copy">${r}</p>
1060
- ${this._status === "error" ? c`<button class="retry-button" type="button" @click=${this.retry}>Retry</button>` : l}
1035
+ ${this._status === "error" ? s`<button class="retry-button" type="button" @click=${this.retry}>Retry</button>` : c}
1061
1036
  </div>
1062
1037
  </div>
1063
1038
  `;
@@ -1067,7 +1042,7 @@ var g, _ = (t) => {
1067
1042
  }
1068
1043
  #A(e) {
1069
1044
  e.id !== this._selectedMap?.id && (this.mapImageId = e.id, this.dispatchEvent(new CustomEvent("fellowship-map-image-change", {
1070
- ...I,
1045
+ ...N,
1071
1046
  detail: {
1072
1047
  mapImageId: e.id,
1073
1048
  mapImage: e
@@ -1089,7 +1064,7 @@ var g, _ = (t) => {
1089
1064
  }
1090
1065
  #z(e) {
1091
1066
  this._viewportState = this.#a.state, this.dispatchEvent(new CustomEvent("fellowship-map-viewport-change", {
1092
- ...I,
1067
+ ...N,
1093
1068
  detail: {
1094
1069
  viewport: this.#a.state,
1095
1070
  source: e
@@ -1108,37 +1083,37 @@ var g, _ = (t) => {
1108
1083
  let e = this.#d;
1109
1084
  this.#d = [];
1110
1085
  for (let t of e) this.dispatchEvent(new CustomEvent("fellowship-map-error", {
1111
- ...I,
1086
+ ...N,
1112
1087
  detail: t
1113
1088
  }));
1114
1089
  }
1115
1090
  };
1116
- r([d({
1091
+ r([u({
1117
1092
  type: String,
1118
1093
  attribute: "dungeon-id"
1119
- })], L.prototype, "dungeonId", void 0), r([d({
1094
+ })], P.prototype, "dungeonId", void 0), r([u({
1120
1095
  type: String,
1121
1096
  attribute: "map-image-id"
1122
- })], L.prototype, "mapImageId", void 0), r([d({
1097
+ })], P.prototype, "mapImageId", void 0), r([u({
1123
1098
  type: String,
1124
1099
  attribute: "asset-base-url"
1125
- })], L.prototype, "assetBaseUrl", void 0), r([d({ attribute: !1 })], L.prototype, "dataSource", void 0), r([d({ attribute: !1 })], L.prototype, "renderEnemy", void 0), r([d({ attribute: !1 })], L.prototype, "renderGroup", void 0), r([d({ attribute: !1 })], L.prototype, "mapLabelFormatter", void 0), r([d({
1100
+ })], P.prototype, "assetBaseUrl", void 0), r([u({ attribute: !1 })], P.prototype, "dataSource", void 0), r([u({ attribute: !1 })], P.prototype, "renderEnemy", void 0), r([u({ attribute: !1 })], P.prototype, "renderGroup", void 0), r([u({ attribute: !1 })], P.prototype, "mapLabelFormatter", void 0), r([u({
1126
1101
  type: Boolean,
1127
1102
  attribute: "show-controls"
1128
- })], L.prototype, "showControls", void 0), r([d({
1103
+ })], P.prototype, "showControls", void 0), r([u({
1129
1104
  type: Boolean,
1130
1105
  attribute: "show-map-selector"
1131
- })], L.prototype, "showMapSelector", void 0), r([d({
1106
+ })], P.prototype, "showMapSelector", void 0), r([u({
1132
1107
  type: Number,
1133
1108
  attribute: "max-zoom"
1134
- })], L.prototype, "maxZoom", void 0), r([d({
1109
+ })], P.prototype, "maxZoom", void 0), r([u({
1135
1110
  type: Boolean,
1136
1111
  attribute: !1
1137
- })], L.prototype, "externalOverlay", void 0), r([p()], L.prototype, "_status", void 0), r([p()], L.prototype, "_error", void 0), r([p()], L.prototype, "_data", void 0), r([p()], L.prototype, "_model", void 0), r([p()], L.prototype, "_selectedMap", void 0), r([p()], L.prototype, "_viewportState", void 0), r([p()], L.prototype, "_isPanning", void 0), r([p()], L.prototype, "_enemyRenderContent", void 0), r([p()], L.prototype, "_groupRenderContent", void 0), r([p()], L.prototype, "_mapLabels", void 0), r([f(".viewport")], L.prototype, "_viewportElement", void 0), r([f("canvas")], L.prototype, "_canvasElement", void 0), L = r([u("fellowship-dungeon-map")], L);
1138
- var R = () => {
1139
- globalThis.customElements !== void 0 && !globalThis.customElements.get("fellowship-dungeon-map") && globalThis.customElements.define("fellowship-dungeon-map", L);
1112
+ })], P.prototype, "externalOverlay", void 0), r([f()], P.prototype, "_status", void 0), r([f()], P.prototype, "_error", void 0), r([f()], P.prototype, "_data", void 0), r([f()], P.prototype, "_model", void 0), r([f()], P.prototype, "_selectedMap", void 0), r([f()], P.prototype, "_viewportState", void 0), r([f()], P.prototype, "_isPanning", void 0), r([f()], P.prototype, "_enemyRenderContent", void 0), r([f()], P.prototype, "_groupRenderContent", void 0), r([f()], P.prototype, "_mapLabels", void 0), r([d(".viewport")], P.prototype, "_viewportElement", void 0), r([d("canvas")], P.prototype, "_canvasElement", void 0), P = r([l("fellowship-dungeon-map")], P);
1113
+ var F = () => {
1114
+ globalThis.customElements !== void 0 && !globalThis.customElements.get("fellowship-dungeon-map") && globalThis.customElements.define("fellowship-dungeon-map", P);
1140
1115
  };
1141
1116
  //#endregion
1142
- export { N as a, M as i, R as n, y as o, F as r, L as t };
1117
+ export { A as a, k as i, F as n, g as o, M as r, P as t };
1143
1118
 
1144
- //# sourceMappingURL=dungeon-map-IPN6sEDj.js.map
1119
+ //# sourceMappingURL=dungeon-map-xFaPasVr.js.map