@aceshooting/lyra-ui 17.0.0 → 18.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +254 -0
  2. package/README.md +2 -2
  3. package/custom-elements.json +1 -1
  4. package/dist/cli/migration-contract.json +1 -1
  5. package/dist/components/conversation/streaming-text/streaming-text-core.class.d.ts +22 -0
  6. package/dist/components/conversation/streaming-text/streaming-text-core.class.js +1 -0
  7. package/dist/components/conversation/streaming-text/streaming-text.class.d.ts +22 -0
  8. package/dist/components/conversation/streaming-text/streaming-text.class.js +1 -0
  9. package/dist/components/conversation/thread-list/thread-list.class.js +1 -1
  10. package/dist/components/data/table/table.styles.js +1 -1
  11. package/dist/components/forms/combobox/combobox.class.d.ts +12 -2
  12. package/dist/components/forms/combobox/combobox.class.js +2 -2
  13. package/dist/components/forms/combobox/combobox.styles.js +1 -1
  14. package/dist/components/forms/select/select.class.d.ts +31 -5
  15. package/dist/components/forms/select/select.class.js +2 -2
  16. package/dist/components/layout/filter-bar/filter-bar.class.d.ts +68 -6
  17. package/dist/components/layout/filter-bar/filter-bar.class.js +2 -2
  18. package/dist/components/layout/reorder-list/reorder-list.class.js +1 -1
  19. package/dist/components/layout/virtual-list/virtual-list.class.d.ts +107 -2
  20. package/dist/components/layout/virtual-list/virtual-list.class.js +12 -7
  21. package/dist/components/media/map/map.class.d.ts +138 -12
  22. package/dist/components/media/map/map.class.js +25 -20
  23. package/dist/components/media/map/map.styles.js +1 -1
  24. package/dist/custom-elements-jsx.d.ts +1 -1
  25. package/dist/events.d.ts +7 -1
  26. package/dist/internal/package-metadata.d.ts +1 -1
  27. package/dist/internal/package-metadata.js +1 -1
  28. package/dist/lyra.d.ts +1 -1
  29. package/dist/lyra.js +1 -1
  30. package/dist/svelte.d.ts +1 -1
  31. package/dist/testing/lyra-tag-event-map.js +1 -1
  32. package/dist/translations/ar/retrieval.js +1 -1
  33. package/dist/translations/ar/shared.js +1 -1
  34. package/dist/translations/de/retrieval.js +1 -1
  35. package/dist/translations/de/shared.js +1 -1
  36. package/dist/translations/es/retrieval.js +1 -1
  37. package/dist/translations/es/shared.js +1 -1
  38. package/dist/translations/fa/retrieval.js +1 -1
  39. package/dist/translations/fa/shared.js +1 -1
  40. package/dist/translations/fr/retrieval.js +1 -1
  41. package/dist/translations/fr/shared.js +1 -1
  42. package/dist/translations/he/retrieval.js +1 -1
  43. package/dist/translations/he/shared.js +1 -1
  44. package/dist/translations/it/retrieval.js +1 -1
  45. package/dist/translations/it/shared.js +1 -1
  46. package/dist/translations/ja/retrieval.js +1 -1
  47. package/dist/translations/ja/shared.js +1 -1
  48. package/dist/translations/pt-BR/retrieval.js +1 -1
  49. package/dist/translations/pt-BR/shared.js +1 -1
  50. package/dist/translations/ro/agent-tools.d.ts +1 -0
  51. package/dist/translations/ro/agent-tools.js +1 -0
  52. package/dist/translations/ro/charts.d.ts +1 -0
  53. package/dist/translations/ro/charts.js +1 -0
  54. package/dist/translations/ro/conversation.d.ts +1 -0
  55. package/dist/translations/ro/conversation.js +1 -0
  56. package/dist/translations/ro/data.d.ts +1 -0
  57. package/dist/translations/ro/data.js +1 -0
  58. package/dist/translations/ro/forms.d.ts +1 -0
  59. package/dist/translations/ro/forms.js +1 -0
  60. package/dist/translations/ro/layout.d.ts +1 -0
  61. package/dist/translations/ro/layout.js +1 -0
  62. package/dist/translations/ro/media.d.ts +1 -0
  63. package/dist/translations/ro/media.js +1 -0
  64. package/dist/translations/ro/overlays.d.ts +1 -0
  65. package/dist/translations/ro/overlays.js +1 -0
  66. package/dist/translations/ro/retrieval.d.ts +1 -0
  67. package/dist/translations/ro/retrieval.js +1 -0
  68. package/dist/translations/ro/shared.d.ts +1 -0
  69. package/dist/translations/ro/shared.js +1 -0
  70. package/dist/translations/ro/utility.d.ts +1 -0
  71. package/dist/translations/ro/utility.js +1 -0
  72. package/dist/translations/ro/viewers.d.ts +1 -0
  73. package/dist/translations/ro/viewers.js +5 -0
  74. package/dist/translations/ro.d.ts +1 -0
  75. package/dist/translations/ro.js +1 -0
  76. package/dist/translations/ru/retrieval.js +1 -1
  77. package/dist/translations/ru/shared.js +1 -1
  78. package/dist/translations/zh-CN/retrieval.js +1 -1
  79. package/dist/translations/zh-CN/shared.js +1 -1
  80. package/dist/vue.d.ts +1 -1
  81. package/llms/components/lr-button.md +5 -4
  82. package/llms/components/lr-card.md +2 -1
  83. package/llms/components/lr-combobox.md +7 -0
  84. package/llms/components/lr-filter-bar.md +86 -3
  85. package/llms/components/lr-icon-button.md +2 -1
  86. package/llms/components/lr-map.md +63 -6
  87. package/llms/components/lr-option.md +7 -0
  88. package/llms/components/lr-select.md +19 -2
  89. package/llms/components/lr-streaming-text-core.md +7 -3
  90. package/llms/components/lr-streaming-text.md +7 -2
  91. package/llms/components/lr-virtual-list.md +53 -0
  92. package/llms/shared.md +6 -3
  93. package/llms-full.txt +273 -22
  94. package/package.json +28 -2
  95. package/registrations.json +1 -1
  96. package/vscode-css-data.json +1 -1
  97. package/vscode-html-data.json +1 -1
  98. package/web-types.json +1 -1
package/llms-full.txt CHANGED
@@ -1821,8 +1821,8 @@ silently defeats a registered catalog — omit it, or pass `undefined`.
1821
1821
 
1822
1822
  ### Ready-made catalogs: `@aceshooting/lyra-ui/translations/<locale>.js`
1823
1823
 
1824
- Eleven full catalogs ship with the package — **`ar`, `de`, `es`, `fa`, `fr`, `he`, `it`, `ja`,
1825
- `pt-BR`, `ru`, `zh-CN`** — each covering every key in `LYRA_DEFAULT_STRINGS`. They are
1824
+ Twelve full catalogs ship with the package — **`ar`, `de`, `es`, `fa`, `fr`, `he`, `it`, `ja`,
1825
+ `pt-BR`, `ro`, `ru`, `zh-CN`** — each covering every key in `LYRA_DEFAULT_STRINGS`. They are
1826
1826
  **side-effect-only modules**: import one bare, read nothing from it, and it calls
1827
1827
  `registerLyraLocale()` for you.
1828
1828
 
@@ -1833,11 +1833,14 @@ import "@aceshooting/lyra-ui/translations/fa.js"; // fa-IR falls back to this ba
1833
1833
  import "@aceshooting/lyra-ui/translations/he.js"; // he-IL falls back to this base catalog
1834
1834
  import "@aceshooting/lyra-ui/translations/it.js"; // Italian
1835
1835
  import "@aceshooting/lyra-ui/translations/pt-BR.js"; // also serves pt and pt-PT
1836
+ import "@aceshooting/lyra-ui/translations/ro.js"; // Romanian
1836
1837
  import "@aceshooting/lyra-ui/translations/zh-CN.js"; // also serves zh, zh-Hans and zh-Hans-CN
1837
1838
  ```
1838
1839
 
1839
1840
  Persian and Hebrew use CLDR plural categories (`fa`: `one`/`other`; `he`:
1840
- `one`/`two`/`other`); Italian uses a non-default set too (`it`: `one`/`many`/`other`). `ar`, `fa`
1841
+ `one`/`two`/`other`); Italian uses a non-default set too (`it`: `one`/`many`/`other`), and so does
1842
+ Romanian (`ro`: `one`/`few`/`other`, where `few` covers `0` and `2`-`19` and `other` is the
1843
+ `de`-requiring form from `20` upward). `ar`, `fa`
1841
1844
  and `he` declare `dir: 'rtl'`, so `getLyraLocaleDirection()`
1842
1845
  answers for them (and for `ar-EG`, `fa-IR`, `he-IL`) — but locale selection still does not _force_
1843
1846
  writing direction: set `dir="rtl"` on the page or an ancestor yourself.
@@ -4928,6 +4931,13 @@ An async `source` row can carry the same two fields (`start`, `end`) alongside i
4928
4931
  default; an explicit instance value always wins. There is deliberately no `hoist` alias here:
4929
4932
  unlike on `lr-select`, where it is Shoelace's established spelling, it would be a boolean
4930
4933
  defaulting to `true`, so its attribute could only ever express the value the control already has
4934
+ - `sync?: PlaceSync` (reflected) — copies the trigger's width, height, or both onto the listbox,
4935
+ spelled the same as on `lr-dropdown`/`lr-popup`/`lr-popover`. Unset (the default), the listbox
4936
+ sizes to its own content, clamped between `--lr-size-12rem` and `--lr-size-28rem`, exactly as
4937
+ before. `sync="width"` drops that content-based clamp (keeping only the outer viewport/
4938
+ available-space ceiling) so a full-width trigger with short option labels gets a listbox that
4939
+ aligns to its own edges instead of floating narrower in the middle. Like `placement`, a change
4940
+ takes effect the next time the listbox opens
4931
4941
  - `clearable: boolean = false` (reflected) — displays the clear button while there is something to
4932
4942
  clear on **either** axis this control owns: a committed selection, or _visible_ filter text. See
4933
4943
  "the clear button covers two axes" below
@@ -5557,7 +5567,17 @@ exactly like the multi-option case, until the trigger is actually activated.
5557
5567
  - `getUnknownLabel?: (value: string) => string` (attribute: false) — renders the label for a
5558
5568
  committed value that matches no option, everywhere it appears (trigger, `multiple` tag, synthetic
5559
5569
  row). `getTag` cannot serve this case: it is handed a matched option and there is none. A blank
5560
- return falls back to the raw value
5570
+ return falls back to the raw value. Not consulted while `loading` suppresses that same value —
5571
+ see below
5572
+ - `loading: boolean = false` (reflected) — whether a committed value's real label may still be
5573
+ pending because its `<lr-option>` catalog hasn't arrived yet (e.g. an async fetch still in
5574
+ flight). While `true`, a committed value that currently matches no option renders the localized
5575
+ `loading` placeholder in the trigger label or the relevant `multiple` tag instead of the raw
5576
+ value, with no `notInCatalog`/`[part='unknown-value']` badge and no synthetic
5577
+ `showUnknownOption` listbox row — "not yet resolved" is a different state from "known to be
5578
+ missing". A value already matching a live option is unaffected. Never mutates
5579
+ `value`/`selectedOptions` itself, and does not itself disable the trigger — pair it with
5580
+ `disabled` when the control should also be non-interactive while pending
5561
5581
  - `filled: boolean = false` (reflected) — Shoelace alias for the filled trigger treatment
5562
5582
  - `autofocus: boolean = false` / `title: string = ''` — forwarded to the internal trigger
5563
5583
  - `multiple: boolean = false` (reflected) — several options selectable at once; see "Multi-select"
@@ -5609,7 +5629,14 @@ from a removed option, or a programmatic assignment with a typo) still commits
5609
5629
  stays fully reachable through `value`/`selectedOptions` — but renders a dashed/italic
5610
5630
  `[part='unknown-value']` badge next to the trigger label, or on the relevant `multiple`-mode tag,
5611
5631
  instead of an unexplained bare label, mirroring `<lr-model-select>`'s synthetic "not in catalog"
5612
- stale-value row — see `--lr-select-unknown-value-border-style`/`-color` below.
5632
+ stale-value row — see `--lr-select-unknown-value-border-style`/`-color` below. Set `loading` while
5633
+ that same value's own `<lr-option>` catalog simply hasn't arrived yet (unlike `<lr-combobox>`,
5634
+ `<lr-select>` has no async `source` of its own, so this is consumer-driven rather than automatic):
5635
+ a still-unmatched value then renders the localized `loading` placeholder instead of the raw value,
5636
+ with no `unknown-value` badge and no synthetic `showUnknownOption` row, since it is not yet known
5637
+ to be missing. Once the matching option mounts, the real label renders on the next render with no
5638
+ `value`/`selectedOptions` re-assignment needed, whether or not `loading` is also flipped back to
5639
+ `false`.
5613
5640
 
5614
5641
  **Methods:** `focus(options?)`, `blur()`, and `click()` forward to the internal trigger button.
5615
5642
  `show()` and `hide()` return `Promise<void>` and resolve after `lr-after-show`/`lr-after-hide` once
@@ -6909,10 +6936,11 @@ box no matter what tier or override is in play.
6909
6936
  For toggle buttons, host `aria-pressed="true|false|mixed"` reaches the focused native button —
6910
6937
  BUTTONS only. A link button (`href` set) never receives it, because `role="link"` does not support
6911
6938
  `aria-pressed`; remove `href` and the same host attribute starts reaching the `<button>` that
6912
- replaces the anchor. Navigation can use the global
6913
- `aria-current="page|step|location|date|time|true|false"`, which does reach the anchor. Both states
6914
- update reactively, including native `ariaPressed`/`ariaCurrent` property assignments, attribute
6915
- removal and `href` changes. Empty or unsupported tokens are omitted. The native button/link role
6939
+ replaces the anchor. That link carve-out arrived in **16.0.0** — before it, `aria-pressed` was
6940
+ forwarded onto the anchor as well, asserting a state `role="link"` does not have. Navigation can
6941
+ use the global `aria-current="page|step|location|date|time|true|false"`, which does reach the
6942
+ anchor. Both states update reactively, including native `ariaPressed`/`ariaCurrent` property
6943
+ assignments, attribute removal and `href` changes. Empty or unsupported tokens are omitted. The native button/link role
6916
6944
  remains unchanged for an enabled control: use pressed state for button toggles and current state
6917
6945
  for navigation links. A DISABLED link button drops `href` (so it genuinely cannot navigate) and
6918
6946
  therefore also drops the anchor's implicit role, so it renders an explicit `role="link"` — without
@@ -6992,7 +7020,8 @@ button whose purpose isn't generic.
6992
7020
  Host `aria-haspopup` and `aria-expanded` values are forwarded reactively to the shadow-internal
6993
7021
  native button. `aria-pressed` (`true`, `false`, `mixed`) supports icon-only toggle actions such as
6994
7022
  mute, favorite, or pin, and reaches the `<button>` rendering only — a link icon-button (`href` set)
6995
- never receives it, since `role="link"` has no pressed state; `aria-current` (`page`, `step`,
7023
+ never receives it, since `role="link"` has no pressed state (that carve-out arrived in **16.0.0**;
7024
+ earlier versions forwarded it onto the anchor too); `aria-current` (`page`, `step`,
6996
7025
  `location`, `date`, `time`, `true`, `false`) is global and supports current-item icon buttons such
6997
7026
  as an active nav/pagination target in both renderings. Both follow attribute
6998
7027
  changes, removal, and button/link replacement without changing the native role of an enabled
@@ -18109,6 +18138,17 @@ list's `base` scroll container exposes horizontal scrolling for that explicit op
18109
18138
  number `56` and fixes every row to that many pixels. Property callers assign a number, not a
18110
18139
  numeric string. Anything else (non-numeric, zero, negative, non-finite) safely canonicalizes to
18111
18140
  `'auto'` rather than throwing.
18141
+ - `rowProjection: 'shadow' | 'light' = 'shadow'` (attribute `row-projection`) — where
18142
+ `renderItem`'s output is instantiated. `'shadow'` (default) stamps it inside this component's own
18143
+ shadow root, so only inherited custom properties and the public row parts reach it. `'light'`
18144
+ renders the windowed rows into the host's own light DOM instead, assigned into the shadow viewport
18145
+ through internal named slots, so ordinary document CSS styles a virtualized row exactly as it
18146
+ styles the same row unvirtualized. The component keeps owning windowing, measurement, spacer
18147
+ sizing, `scrollToIndex()`, the external-scroller mode and the ARIA contract either way, and
18148
+ positioning stays on the shadow-side `[part="row"]` wrapper that document CSS cannot select — so
18149
+ consumer styles can never break windowing. Any other value canonicalizes to `'shadow'`. Left
18150
+ unset, the rendered output is byte-identical to before and the host's light DOM stays empty.
18151
+ See **Light-DOM row projection** below for the trade-offs it carries.
18112
18152
  - `itemRole: 'listitem' | 'row' = 'listitem'` (attribute `item-role`) — `'listitem'` (default)
18113
18153
  preserves the plain `role="list"`/`role="listitem"` mapping with `aria-setsize`/`aria-posinset`.
18114
18154
  `'row'` additionally maps `[part="base"]` to `role="rowgroup"`, `[part="spacer"]` to
@@ -18363,6 +18403,48 @@ default estimate with sparse `ResizeObserver` measurements for rows that have ac
18363
18403
  decides: groups render before the rows, so an active row wins while (and only while) it needs to,
18364
18404
  which is right — a group header is a non-interactive `pointer-events: none` label.
18365
18405
 
18406
+ ### Light-DOM row projection
18407
+
18408
+ `rowProjection="light"` exists for one shape: an application whose list rows are already styled by
18409
+ its own global stylesheet, and which therefore could not adopt virtualization without rehoming a
18410
+ dozen descendant rules per row into a new custom element or a growing set of custom properties. In
18411
+ projection mode the windowed rows render into the host's own light DOM, so ordinary document CSS
18412
+ reaches row content directly.
18413
+
18414
+ Positioning, measurement and semantics stay where they were. The `[part="row"]` wrapper remains in
18415
+ the shadow root and keeps `position: absolute`, the per-frame `transform`, `role`, `aria-setsize`/
18416
+ `aria-posinset` (or `aria-rowindex`) and the `ResizeObserver` box — document CSS cannot select it,
18417
+ so consumer styles can never break windowing. The whole part vocabulary (`base`, `spacer`, `row`,
18418
+ `group`, `sticky-group`) keeps matching in both modes, and `row-height="auto"` still measures
18419
+ projected content because the light row is an ordinary in-flow child of that wrapper.
18420
+
18421
+ `projectedRows: HTMLElement[]` returns the projected light-DOM row wrappers in item order, and is
18422
+ empty outside projection mode. The exported type is `LyraVirtualListRowProjection`; the reserved
18423
+ attributes marking library-owned light-DOM nodes are exported as `VIRTUAL_LIST_ROW_ATTRIBUTE`
18424
+ (`data-lr-virtual-list-row`) and `VIRTUAL_LIST_STICKY_ATTRIBUTE` (`data-lr-virtual-list-sticky`).
18425
+
18426
+ **Known gotchas, all inherent to handing the cascade back to the consumer:**
18427
+
18428
+ - **One component-owned wrapper sits between the host and your markup.** A slot cannot assign a text
18429
+ node or a multi-root fragment by attribute, so each row's content lives inside a wrapper carrying
18430
+ `data-lr-virtual-list-row`. Descendant selectors (`lr-virtual-list .row-title`) port unchanged;
18431
+ child combinators (`lr-virtual-list > .row`), `:nth-child`, `:first-child` and sibling combinators
18432
+ written against the unvirtualized markup do not. `:nth-child` on the wrappers reflects the current
18433
+ *window*, not the item index.
18434
+ - **`closest('[part="row"]')` stops resolving.** A delegated listener on the host now sees an
18435
+ un-retargeted `event.target` inside the light DOM. Use `closest('[data-lr-virtual-list-row]')`.
18436
+ - **The document cascade now reaches row content**, including resets and element-level rules that
18437
+ previously could not, so a projected row can look different from the same row in shadow mode.
18438
+ - **Per-row light-DOM state does not survive a disconnect/reconnect.** Disconnect removes the
18439
+ projected rows completely (no rows, no markers, no anchor left behind), so a reparenting move
18440
+ rebuilds them. Scroll position, measurements and the window are unaffected — they live in
18441
+ component state, not in the rows.
18442
+ - **Projection activates one task after hydration.** A server render has no DOM to project into, so
18443
+ the first window is shadow-rendered, hydration matches the server markup, and the rows then swap
18444
+ into the light DOM on the next task.
18445
+ - **A row taken out of flow collapses its wrapper.** `position: fixed`/`absolute` or
18446
+ `display: none` on your own row leaves nothing for the wrapper to measure.
18447
+
18366
18448
  ---
18367
18449
 
18368
18450
  ## `lr-app-rail`
@@ -19600,7 +19682,8 @@ to `<wa-card>`'s contract, staying slot-compatible with `lr-result-card` where t
19600
19682
  - `aria-pressed` and `aria-current` (attributes only) — forwarded reactively onto the native
19601
19683
  control the card actually renders, the same mechanism `<lr-button>` and `<lr-icon-button>` use.
19602
19684
  `aria-pressed` accepts `'true' | 'false' | 'mixed'` and reaches the `activation-button` only —
19603
- `link` has no pressed state, so a linked card never receives it. The global `aria-current`
19685
+ `link` has no pressed state, so a linked card never receives it the same **16.0.0** carve-out
19686
+ `<lr-button>` and `<lr-icon-button>` took. The global `aria-current`
19604
19687
  accepts `'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false'` and reaches both the
19605
19688
  activation button and the stretched link. Anything outside those sets is dropped rather than
19606
19689
  passed through, so a typo never reaches the accessibility tree. This is what lets a single-select
@@ -20374,7 +20457,10 @@ Dashboard filter row that composes Lyra inputs and removable chips, with reset a
20374
20457
  Choice option entries must expose string `value` and `label` data fields; malformed entries are
20375
20458
  omitted independently, while supplied empty strings remain valid. A custom definition requires its
20376
20459
  adapter and a callable `render`; a rejected definition does not reserve its filter ID. Valid
20377
- siblings remain available. Exceptions thrown by an admitted trusted renderer still propagate.
20460
+ siblings remain available. Exceptions thrown by an admitted trusted renderer still propagate. A
20461
+ chip-only definition requires neither options nor an adapter — a stable filter ID and a label are
20462
+ the whole schema, since a malformed `formatValue`/`isEmpty` still has a correct fallback and so is
20463
+ guarded where it is used rather than rejected outright.
20378
20464
 
20379
20465
  **Lean registration entry.** `components/layout/filter-bar/filter-bar.js` (the default entry)
20380
20466
  eagerly imports every composed control this bar could possibly render — `<lr-select>`,
@@ -20395,6 +20481,7 @@ the filter `type`s actually declared:
20395
20481
  | `'checkbox-menu'` | `components/overlays/overlay/dropdown.js` **and** `components/layout/menu/dropdown-item.js` |
20396
20482
  | `'date'` / `'date-range'` | `components/forms/date-picker/date-input.js` |
20397
20483
  | `'text'` | `components/forms/input/input.js` |
20484
+ | `'chip'` | none — renders no control |
20398
20485
 
20399
20486
  Two more are unconditional regardless of which filter `type`s are declared: `<lr-button>` renders
20400
20487
  the reset action on every bar, and `<lr-chip>`/`<lr-chip-group>` render the active-filter row
@@ -20575,7 +20662,9 @@ same-named counterparts (with `combobox`'s `multiple` opting into a multi-value
20575
20662
  `'date'`/`'date-range'` both map to `<lr-date-input>` (single vs. `mode="range"`), and `'text'` maps
20576
20663
  to `<lr-input>` for an open-ended free-text query rather than a closed choice set. A `'text'`
20577
20664
  filter's value is the raw query string, verbatim, and its chip shows exactly that string — the same
20578
- text the user typed, not a truncated or normalized form.
20665
+ text the user typed, not a truncated or normalized form. `'chip'` is the one type that renders no
20666
+ control at all (see **Chip-only filters** below): its value belongs to a widget elsewhere on the
20667
+ page, so the bar renders only its active-filter chip and gives it no toolbar cell.
20579
20668
 
20580
20669
  Every built-in (non-`'custom'`) filter definition additionally accepts optional `size: LyraSize`,
20581
20670
  `icon: unknown` and `labelVisibility: 'visible' | 'hidden'` fields, and every one whose composed
@@ -20791,12 +20880,89 @@ const filters: LyraFilterBarFilterDefinition[] = [
20791
20880
  The custom renderer returns a Lit `TemplateResult`; the filter bar places it in its
20792
20881
  `filter-control` part and re-renders it whenever the controlled value or validation state changes.
20793
20882
 
20883
+ ### Chip-only filters
20884
+
20885
+ Use `type: 'chip'` when the value is already owned by a widget elsewhere on the page — a calendar
20886
+ heatmap cell, a map selection, a chart brush — and the bar's job is only to *show* that the filter
20887
+ is applied and to let the user take it off. Unlike `type: 'custom'`, which still renders a control
20888
+ inside the toolbar, a chip-only filter renders **no control and no toolbar cell at all**: no `field`
20889
+ wrapper is emitted for it, so `lr-filter-bar::part(field)` and `::part(field-<filterId>)` never
20890
+ match one and a bar whose filters are *all* chip-only shows no empty column — its `controls` row
20891
+ still holds the reset button (the "clear all" action such a bar needs), the `end` slot, and the
20892
+ loading spinner, exactly like a bar with no filters at all.
20893
+
20894
+ Everything else is unchanged from any other filter type. The value lives in `value` under its own
20895
+ filter ID, rides every `lr-input`/`lr-reset` detail, counts toward `hasActiveFilters` (so it enables
20896
+ the reset button) and toward `invalidFilterIds` when the definition is `required`, renders a
20897
+ removable active-filter chip subject to `activeFiltersDisplay`, and is cleared both by removing that
20898
+ chip and by `reset()`. A `required` chip-only filter is honoured in **bookkeeping only**: it joins
20899
+ `invalidFilterIds`, fails `checkValidity()` and moves `lr-validity-change`, but renders no inline
20900
+ error, because the bar renders no element of its own for it — the owning widget is responsible for
20901
+ its own error affordance. The inherited `placeholder` is inert here for the same reason it is for
20902
+ `type: 'custom'`: there is no field to place it in.
20903
+
20904
+ A chip-only definition adds three optional fields of its own:
20905
+
20906
+ ```ts
20907
+ type: 'chip';
20908
+ formatValue?: (value: LyraFilterBarFieldValue, locale: string) => string;
20909
+ clearValue?: LyraFilterBarFieldValue;
20910
+ isEmpty?: (value: LyraFilterBarFieldValue) => boolean;
20911
+ ```
20912
+
20913
+ `formatValue` produces the chip's text, and its `locale` argument is the bar's `effectiveLocale` —
20914
+ the same locale every built-in type's own chip formatting and a custom adapter's `formatValue`
20915
+ already receive, and the reason a chip-only value (normally an already-formatted string such as a
20916
+ localized date) can be localized by the caller. That output is caller data, so — like a filter's
20917
+ own label — the bar never routes it through its own localization. Omitted, the fallback ladder is
20918
+ the one a custom adapter's omitted `formatValue` uses: a string array renders as a localized
20919
+ conjunction list, anything else renders verbatim through `String(value)`, and an unset value renders
20920
+ as the empty string. Verbatim is exact: a chip-only value is never run through the date branch that
20921
+ localizes a `'date'`/`'date-range'` chip, so an ISO day is not silently reformatted and a value
20922
+ containing a slash is not mangled.
20923
+
20924
+ `clearValue` is what a chip removal (and `clearFilter()`) writes, defaulting to the empty string —
20925
+ what every non-multi built-in type writes. Declare an empty array for an array-valued chip-only
20926
+ filter. `isEmpty` overrides the built-in emptiness rule (absent, `false`, the empty string and the
20927
+ empty array are empty; everything else is set). **A domain sentinel must pair the two**: declaring
20928
+ a sentinel clear value without a matching `isEmpty` leaves the bar reading the "cleared" value as
20929
+ still set, so it keeps rendering a chip for it — the identical pairing a custom adapter's own
20930
+ `clearValue`/`isEmpty` documents. With the pair declared, the sentinel is never stored in `value`
20931
+ (cleared keys are omitted) and an absent key reads back as the sentinel for the owning widget.
20932
+
20933
+ ```ts
20934
+ const filters: LyraFilterBarFilterDefinition[] = [
20935
+ { filterId: "query", label: "Query", type: "text" },
20936
+ {
20937
+ filterId: "day",
20938
+ label: "Day",
20939
+ type: "chip",
20940
+ formatValue: (value, locale) =>
20941
+ new Intl.DateTimeFormat(locale, {
20942
+ dateStyle: "medium",
20943
+ timeZone: "UTC",
20944
+ }).format(new Date(`${String(value)}T00:00:00Z`)),
20945
+ },
20946
+ ];
20947
+
20948
+ // The calendar heatmap beside the bar owns the value; the bar only shows and removes it.
20949
+ heatmap.addEventListener("app-select-day", (event) => {
20950
+ const { isoDate } = (event as CustomEvent<{ isoDate: string }>).detail;
20951
+ bar.value = { ...bar.value, day: isoDate };
20952
+ });
20953
+ bar.addEventListener("lr-input", (event) => {
20954
+ const { value } = (event as LyraFilterBarInputEvent).detail;
20955
+ heatmap.selectedDay = (value["day"] as string | undefined) ?? "";
20956
+ });
20957
+ ```
20958
+
20794
20959
  **TypeScript:** `LyraFilterBar<Defs extends readonly LyraFilterBarFilterDefinition[] =
20795
20960
  readonly LyraFilterBarFilterDefinition[]>` — `value` and the `lr-input`/`lr-reset` detail `value`
20796
20961
  narrow to a record keyed per `filterId`, whose value type follows that filter's own definition (a
20797
20962
  `'select'`, a non-`multiple` `'combobox'`, `'text'`, `'date'`, and `'date-range'` narrow to
20798
20963
  `string`; a `'checkbox-menu'` and a `multiple: true` `'combobox'` narrow to `readonly string[]`; a
20799
- `'custom'` filter keeps the full unconstrained field value). Declare the schema with `as const
20964
+ `'custom'` filter keeps the full unconstrained field value, and so does a `'chip'` filter, whose
20965
+ value is owned by a widget this component never renders). Declare the schema with `as const
20800
20966
  satisfies readonly LyraFilterBarFilterDefinition[]` and type the element as
20801
20967
  `LyraFilterBar<typeof FILTERS>` to pick it up. Types only; the runtime is unchanged, and an untyped
20802
20968
  `<lr-filter-bar>` keeps today's `LyraFilterBarValue` (`Readonly<Record<string, string | readonly
@@ -21218,6 +21384,19 @@ These named interfaces and helper signatures are available to typed integrations
21218
21384
  readonly defaultValue?: string | readonly string[] | boolean;
21219
21385
  }`
21220
21386
  Import: `@aceshooting/lyra-ui/components/layout/filter-bar/filter-bar.class.js`.
21387
+ `LyraFilterBarChipDefinition extends LyraFilterBarDefinitionBase {
21388
+ readonly type: 'chip';
21389
+ readonly formatValue?: (value: LyraFilterBarFieldValue, locale: string) => string;
21390
+ readonly clearValue?: LyraFilterBarFieldValue;
21391
+ readonly isEmpty?: (value: LyraFilterBarFieldValue) => boolean;
21392
+ // Inherited from LyraFilterBarDefinitionBase.
21393
+ readonly filterId: string;
21394
+ readonly label: string;
21395
+ readonly placeholder?: string;
21396
+ readonly required?: boolean;
21397
+ readonly defaultValue?: string | readonly string[] | boolean;
21398
+ }`
21399
+ Import: `@aceshooting/lyra-ui/components/layout/filter-bar/filter-bar.class.js`.
21221
21400
  `LyraFilterBarCustomControlAdapter {
21222
21401
  readonly valueFromEvent: (event: Event) => LyraFilterBarFieldValue;
21223
21402
  readonly clearValue: LyraFilterBarFieldValue;
@@ -27027,10 +27206,18 @@ LyraMapLegendPattern }`, where `LyraMapLegendPattern` is `'solid' | 'diagonal' |
27027
27206
  'crosshatch'`. Pattern is required so color is never the sole category cue. At most 100 valid
27028
27207
  rows, 256 characters per label, and 8,192 aggregate label characters are retained; colors are
27029
27208
  bounded before validation. The overlay scrolls within the map allocation.
27209
+ A row may also carry its own `value`: the category key that row stands for — the same string a
27210
+ `point.colors`/`point.icons` entry matches against `point.field`/`point.iconField`. It is
27211
+ trimmed, bounded to 256 characters (sliced, never ellipsized, so a bounded key still matches) and
27212
+ **retained** in the canonical readback; a non-string, empty or whitespace-only key leaves no
27213
+ `value` property on the frozen row at all. It is what makes a row operable under
27214
+ `legendInteractive`, and it does not count toward the label budget.
27030
27215
  A row may also carry `icon`, deliberately the same record a `point.icons` entry uses — hand the
27031
27216
  legend the very icon object its point layer renders and the key shows the symbol drawn on the
27032
- map instead of describing it in colour alone. The point layer's category key (`value`) is
27033
- accepted so a pass-through needs no reshaping, and is left out of the canonical readback;
27217
+ map instead of describing it in colour alone. The **icon record's own** `value` is a separate
27218
+ field from the row-level one above: it is accepted so a pass-through needs no reshaping, and it
27219
+ is still left out of the canonical readback. A row's key is never derived from its glyph's, which
27220
+ would silently make a row interactive that the author never marked;
27034
27221
  `path`, `viewBox`, `mode`, `strokeWidth`, `lineCap` and `lineJoin` keep their point-icon meaning
27035
27222
  and their point-icon defaults, and the same validation applies (path data only, at most 8,192
27036
27223
  characters, positive `viewBox` dimensions). An unusable record is dropped and that row keeps
@@ -27039,6 +27226,22 @@ LyraMapLegendPattern }`, where `LyraMapLegendPattern` is `'solid' | 'diagonal' |
27039
27226
  omittedCount, truncatedLabelCount, truncated }` result for the latest assignment. A truncated
27040
27227
  projection renders a localized visible `1–N of M items` summary rather than silently claiming
27041
27228
  the bounded rows are complete.
27229
+ - `legendInteractive: boolean = false` (attribute `legend-interactive`, reflected) — opt-in: turns
27230
+ every legend row that carries a `value` into a keyboard-operable visibility toggle, and leaves a
27231
+ row without one inert. Unset, the legend renders exactly the read-only key it rendered before —
27232
+ no `button`, no extra attribute, and no extra MapLibre paint key. Each toggle is an independently
27233
+ tabbable native `button`, so Enter and Space are the platform's own activation and there is no
27234
+ roving tabindex: a 100-row interactive legend contributes 100 tab stops, exactly as a 100-series
27235
+ `lr-chart` legend does. Each interactive row also grows to the shared `--lr-icon-button-size`
27236
+ hit-area floor (WCAG 2.5.8), which the panel's existing `max-block-size` and scrolling contain.
27237
+ - `hiddenCategories: readonly string[] = []` (attribute: false) — the complete controlled set of
27238
+ muted category keys, mirroring `lr-chart`'s `hiddenDatasets`. Clone-owned and frozen; non-string,
27239
+ empty, whitespace-only and duplicate entries are dropped (first occurrence wins, matching
27240
+ `point.colors`), and at most 100 keys are retained. Honoured on the **first** render and the
27241
+ first MapLibre paint, not only after a user toggle, so a host can restore a saved selection. An
27242
+ empty array deliberately means every category is visible. It is controlled public state and
27243
+ survives a disconnect/reconnect. A hidden key that matches no category is harmless. Assigning it
27244
+ programmatically emits nothing.
27042
27245
  - `choropleth?: LyraMapChoroplethLayer` (attribute: false) — `LyraMapChoroplethLayer { sourceId:
27043
27246
  string; geojson: GeoJSON.FeatureCollection; field: string; stops: [number, string][]; interpolation?:
27044
27247
  'linear' | 'logarithmic' | 'step'; stepBaseColor?: string }` (interpolated
@@ -27319,7 +27522,8 @@ payload beside the map.
27319
27522
  `LyraMapHeatmapZoomValue`, `LyraMapLineOptions`, `LyraMapPointOptions`, `LyraMapPointRadiusOptions`,
27320
27523
  `LyraMapPointRadiusInterpolation`, `LyraMapPointIcon`, `LyraMapPointIconMode`,
27321
27524
  `LyraMapPointIconLineCap`, `LyraMapPointIconLineJoin`, `LyraMapMarker`, `LyraMapMarkerActivationDetail`,
27322
- `LyraMapMarkerActivationSource`, `LyraMapStyleSpecification`, and `LyraMapInstance`.
27525
+ `LyraMapMarkerActivationSource`, `LyraMapLegendToggleDetail`, `LyraMapStyleSpecification`, and
27526
+ `LyraMapInstance`.
27323
27527
  The former `LegendEntry`, `ChoroplethLayer`, `GeoJsonDataLayer`, and `MapMarker` names are removed
27324
27528
  in v9 rather than retained as aliases.
27325
27529
 
@@ -27335,7 +27539,17 @@ shared `maplibre-gl` import without constructing a map or allocating a WebGL con
27335
27539
  an element.
27336
27540
 
27337
27541
  **Events:** `lr-map-load` (fired once, after the underlying map's own `'load'`),
27338
- `lr-map-marker-activate` (non-cancelable; frozen `LyraMapMarkerActivationDetail { id, lngLat,
27542
+ `lr-map-legend-toggle` (**cancelable**; frozen `LyraMapLegendToggleDetail { value, visible,
27543
+ hiddenCategories }` — the activated row's category key, its proposed visibility, and the complete
27544
+ proposed hidden set in the order it would be committed, with the array detached and frozen so a
27545
+ listener cannot mutate the component's state through it). `preventDefault()` is a genuine veto, not
27546
+ a notification: `hiddenCategories` is not written, the row's `aria-pressed` does not change, the
27547
+ MapLibre paint is untouched, and nothing is announced — which is exactly what a host needs to own
27548
+ the set and assign its own value instead. There is deliberately no second, confirmation event: the
27549
+ committed state is `hiddenCategories`, which the host already observes, so a paired before/after
27550
+ vocabulary would be permanent public surface nobody asked for. The event is a DOM-interaction
27551
+ proposal only, so a programmatic `hiddenCategories` assignment reconciles without emitting it.
27552
+ Also `lr-map-marker-activate` (non-cancelable; frozen `LyraMapMarkerActivationDetail { id, lngLat,
27339
27553
  marker, source }`; `id` is the trimmed explicit identity or `undefined`, `marker` is the accepted
27340
27554
  declarative snapshot, and `source` is `'pointer' | 'keyboard'`), and `lr-map-click`
27341
27555
  (frozen `detail: { readonly lngLat: readonly [lng, lat], readonly feature?, readonly origin?,
@@ -27359,7 +27573,8 @@ markup as described above.
27359
27573
  stays positioned with the map instead of floating beside it. Supplying it opens the panel even
27360
27574
  when `legend` and `legendGradient` are both empty.
27361
27575
 
27362
- **CSS parts:** `base`, `container`, `legend`, `legend-swatch`, `legend-gradient`, `legend-lo`,
27576
+ **CSS parts:** `base`, `container`, `legend`, `legend-swatch`, `legend-toggle`,
27577
+ `legend-toggle-hidden`, `legend-gradient`, `legend-lo`,
27363
27578
  `legend-hi`, `legend-limit`, `marker`, `popup`,
27364
27579
  `popup-content`, `popup-close-button`, `attribution`, `attribution-toggle`, `navigation`,
27365
27580
  `zoom-in`, `zoom-out`, `compass`, `scale`, `error`.
@@ -27384,6 +27599,18 @@ visible label carries its meaning. That association is advertised only while it
27384
27599
  the optional `maplibre-gl` peer is still loading, and after any failure, there is no map
27385
27600
  container in the tree, so `legend` withholds `aria-controls` rather than leaving a dangling
27386
27601
  idref.
27602
+ Under `legendInteractive`, a row carrying a `value` wraps its swatch and label in a
27603
+ `legend-toggle` `button` with `aria-pressed` rendered as the literal `"true"`/`"false"` — never
27604
+ omitted, because a missing attribute reports "not a toggle button" rather than "unpressed". The
27605
+ button's accessible name is its own visible label (caller-supplied data, so deliberately not
27606
+ localized), scoped by the legend's own localized group name; no `aria-label` restates the state,
27607
+ which would make assistive tech announce it twice. A hidden row's button additionally carries the
27608
+ `legend-toggle-hidden` token — state lives in the part name, so `::part(legend-toggle-hidden)` is a
27609
+ reachable hook — and dims only its `aria-hidden` swatch while re-colouring the label through the
27610
+ quiet text token, so the label keeps AA contrast rather than fading with the whole button. In
27611
+ forced colors the hidden row falls back to `line-through`, which survives a system-color collapse.
27612
+ Each activation is announced through the shared light-DOM polite live region, using the same
27613
+ `legendTypeShown`/`legendTypeHidden` strings `lr-graph-legend` announces with.
27387
27614
  `legend-limit` is the localized bounded-projection summary. The five peer-chrome parts project
27388
27615
  stable Lyra names onto MapLibre-generated DOM without erasing peer-supplied part tokens;
27389
27616
  `marker` retains a 24px minimum target in both axes even when a peer/custom marker has no intrinsic
@@ -27415,6 +27642,15 @@ integration; there is no declarative controls property.
27415
27642
  - `--lr-map-choropleth-fill-opacity` (default `0.75`) — fill opacity for the declarative
27416
27643
  `choropleth` layer and polygon fills in every `dataLayers` entry. It intentionally inherits from
27417
27644
  an ancestor, so one scoped declaration rethemes every nested map without setting each host.
27645
+ - `--lr-map-hidden-category-opacity` (default `0.15`) — opacity a category listed in
27646
+ `hiddenCategories` is muted to in the rendered MapLibre paint: `circle-opacity` and
27647
+ `circle-stroke-opacity` on the points layer and `icon-opacity` on the point-icon symbol layer.
27648
+ Like the fill-opacity token it is read from the resolved cascade on every paint, because MapLibre
27649
+ draws to a WebGL canvas the CSS cascade never reaches, and it inherits from an ancestor. A
27650
+ `kind: 'heatmap'` entry is out of scope: a density surface has no per-category field to mute.
27651
+ - `--lr-map-legend-hidden-swatch-opacity` (default `0.5`) — opacity of a hidden interactive legend
27652
+ row's decorative swatch. Only the `aria-hidden` swatch dims; the label re-colours through
27653
+ `--lr-color-text-quiet` instead, so it never drops below 4.5:1.
27418
27654
  - `--lr-map-popup-close-button-hover-bg` (default `var(--lr-color-brand-quiet)`) and
27419
27655
  `--lr-map-popup-close-button-hover-color` (default `var(--lr-color-brand)`) — hover background
27420
27656
  and foreground of `popup-close-button`.
@@ -30141,6 +30377,12 @@ These named interfaces and helper signatures are available to typed integrations
30141
30377
  readonly source: LyraMapMarkerActivationSource;
30142
30378
  }`
30143
30379
  Import: `@aceshooting/lyra-ui/components/media/map/map.class.js`.
30380
+ `LyraMapLegendToggleDetail {
30381
+ readonly value: string;
30382
+ readonly visible: boolean;
30383
+ readonly hiddenCategories: readonly string[];
30384
+ }`
30385
+ Import: `@aceshooting/lyra-ui/components/media/map/map.class.js`.
30144
30386
  `LyraMapStyleSpecification {
30145
30387
  readonly version: 8;
30146
30388
  readonly sources: Readonly<Record<string, unknown>>;
@@ -36100,7 +36342,12 @@ never see that update happen. See `<lr-thinking-panel>`'s own reference at `llms
36100
36342
 
36101
36343
  **Slots:** none — content renders from `content`, not a slot.
36102
36344
 
36103
- **CSS parts:** `base`, `cursor` (only rendered while `streaming` is `true`)
36345
+ **CSS parts:** `base`, `cursor` (only rendered while `streaming` is `true`), plus every part
36346
+ `<lr-markdown>` documents forwarded verbatim (no aliasing — none collides with `base`/`cursor`)
36347
+ from the composed `<lr-markdown>` in Markdown mode via `exportparts`: `content`, `heading`,
36348
+ `paragraph`, `list`, `code-block`, `inline-code`, `link`, `table`, `blockquote`, `img`, `math`. A
36349
+ host-level `lr-streaming-text::part(link)`/`::part(img)` rule reaches the rendered `<a>`/`<img>`
36350
+ exactly as the same rule does applied directly to `<lr-markdown>`.
36104
36351
 
36105
36352
  **Themeable custom properties:** `--lr-inline-cursor-width` (default
36106
36353
  `var(--lr-size-0-125rem)`, the shared inline cursor width), `--lr-inline-cursor-height` (default
@@ -36214,8 +36461,12 @@ share one implementation.
36214
36461
 
36215
36462
  **Slots:** none — content renders from `content`, not a slot.
36216
36463
 
36217
- **CSS parts:** `base`, `cursor` (only rendered while `streaming` is `true`) identical to
36218
- `<lr-streaming-text>`'s own.
36464
+ **CSS parts:** `base`, `cursor` (only rendered while `streaming` is `true`), plus every part
36465
+ `<lr-markdown-core>` documents forwarded verbatim from the composed `<lr-markdown-core>` in
36466
+ Markdown mode via `exportparts`: `content`, `heading`, `paragraph`, `list`, `code-block`,
36467
+ `inline-code`, `link`, `table`, `blockquote`, `img`, `math` — the identical forwarded list
36468
+ `<lr-streaming-text>` documents, since `<lr-markdown>` and `<lr-markdown-core>` share the same
36469
+ documented part vocabulary.
36219
36470
 
36220
36471
  **Themeable custom properties:** `--lr-inline-cursor-width` (default `var(--lr-size-0-125rem)`) and
36221
36472
  `--lr-inline-cursor-height` (default `var(--lr-size-1em)`) — the same shared inline-cursor tokens
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aceshooting/lyra-ui",
3
- "version": "17.0.0",
3
+ "version": "18.0.0",
4
4
  "description": "Free, independent Lit web components: an open-source alternative to Shoelace and Web Awesome with accessible forms, dashboards, charts, and agent UI.",
5
5
  "keywords": [
6
6
  "web-components",
@@ -1803,6 +1803,19 @@
1803
1803
  "./dist/translations/pt-BR/shared.js",
1804
1804
  "./dist/translations/pt-BR/utility.js",
1805
1805
  "./dist/translations/pt-BR/viewers.js",
1806
+ "./dist/translations/ro.js",
1807
+ "./dist/translations/ro/agent-tools.js",
1808
+ "./dist/translations/ro/charts.js",
1809
+ "./dist/translations/ro/conversation.js",
1810
+ "./dist/translations/ro/data.js",
1811
+ "./dist/translations/ro/forms.js",
1812
+ "./dist/translations/ro/layout.js",
1813
+ "./dist/translations/ro/media.js",
1814
+ "./dist/translations/ro/overlays.js",
1815
+ "./dist/translations/ro/retrieval.js",
1816
+ "./dist/translations/ro/shared.js",
1817
+ "./dist/translations/ro/utility.js",
1818
+ "./dist/translations/ro/viewers.js",
1806
1819
  "./dist/translations/ru.js",
1807
1820
  "./dist/translations/ru/agent-tools.js",
1808
1821
  "./dist/translations/ru/charts.js",
@@ -2559,6 +2572,19 @@
2559
2572
  "./src/translations/pt-BR/shared.ts",
2560
2573
  "./src/translations/pt-BR/utility.ts",
2561
2574
  "./src/translations/pt-BR/viewers.ts",
2575
+ "./src/translations/ro.ts",
2576
+ "./src/translations/ro/agent-tools.ts",
2577
+ "./src/translations/ro/charts.ts",
2578
+ "./src/translations/ro/conversation.ts",
2579
+ "./src/translations/ro/data.ts",
2580
+ "./src/translations/ro/forms.ts",
2581
+ "./src/translations/ro/layout.ts",
2582
+ "./src/translations/ro/media.ts",
2583
+ "./src/translations/ro/overlays.ts",
2584
+ "./src/translations/ro/retrieval.ts",
2585
+ "./src/translations/ro/shared.ts",
2586
+ "./src/translations/ro/utility.ts",
2587
+ "./src/translations/ro/viewers.ts",
2562
2588
  "./src/translations/ru.ts",
2563
2589
  "./src/translations/ru/agent-tools.ts",
2564
2590
  "./src/translations/ru/charts.ts",
@@ -2767,7 +2793,7 @@
2767
2793
  "shiki": "^4.4.3",
2768
2794
  "svelte": "^5.57.0",
2769
2795
  "typescript": "^7.0.2",
2770
- "vue": "^3.5.42",
2796
+ "vue": "^3.5.43",
2771
2797
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
2772
2798
  },
2773
2799
  "customElements": "custom-elements.json",