@pie-players/pie-players-shared 1.0.0-beta.6 → 1.0.0-beta.8

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 (37) hide show
  1. package/README.md +3 -1
  2. package/dist/i18n/catalogs.d.ts +20 -13
  3. package/dist/i18n/catalogs.js +22 -23
  4. package/dist/i18n/content-language.d.ts +46 -4
  5. package/dist/i18n/content-language.js +83 -5
  6. package/dist/i18n/index.d.ts +3 -2
  7. package/dist/i18n/index.js +2 -2
  8. package/dist/i18n/language-tags.d.ts +73 -4
  9. package/dist/i18n/language-tags.js +176 -6
  10. package/dist/i18n/messages/en-US.d.ts +23 -0
  11. package/dist/i18n/messages/en-US.js +25 -0
  12. package/dist/i18n/provider.d.ts +3 -6
  13. package/dist/i18n/provider.js +5 -15
  14. package/dist/i18n/types.d.ts +6 -1
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/loaders/ElementLoader.d.ts +1 -1
  18. package/dist/loaders/element-loader.d.ts +4 -1
  19. package/dist/loaders/element-loader.js +74 -21
  20. package/dist/loaders/iife-adapter.d.ts +7 -0
  21. package/dist/loaders/iife-adapter.js +1 -1
  22. package/dist/security/private-hosts.d.ts +27 -0
  23. package/dist/security/private-hosts.js +85 -0
  24. package/dist/security/validate-style-url.js +2 -2
  25. package/dist/tools/term-lookup.d.ts +15 -0
  26. package/dist/tools/term-lookup.js +16 -0
  27. package/dist/types/index.d.ts +47 -27
  28. package/dist/types/interchange.d.ts +116 -0
  29. package/dist/types/interchange.js +1 -0
  30. package/dist/ui/focus-trap.js +3 -1
  31. package/dist/ui/fontawesome.d.ts +51 -0
  32. package/dist/ui/fontawesome.js +122 -0
  33. package/dist/ui/ime.d.ts +6 -0
  34. package/dist/ui/ime.js +10 -0
  35. package/package.json +15 -6
  36. package/dist/i18n/messages/nl-NL.d.ts +0 -636
  37. package/dist/i18n/messages/nl-NL.js +0 -636
@@ -10,8 +10,10 @@
10
10
  */
11
11
  import type { FormativeDeliveryPolicy, FormativeItemPolicy, FormativeSectionSlice } from "../formative/types.js";
12
12
  import type { TimedMediaSectionData, TimedMediaSectionSessionSlice } from "../timed-media/types.js";
13
+ import type { AdaptationLink, CurriculumStandard, LanguagePolicy, SkillLevel } from "./interchange.js";
13
14
  export type { FormativeDeliveryPolicy, FormativeItemPolicy };
14
15
  export type { TimedMediaSectionData };
16
+ export type { AdaptationLink, ContentSubstitution, CurriculumStandard, LanguagePolicy, ServedContentIdentity, SkillLevel, } from "./interchange.js";
15
17
  export interface BaseEntity {
16
18
  id?: string;
17
19
  createdAt?: Date;
@@ -32,12 +34,6 @@ export interface VersionEntity extends BaseEntity {
32
34
  signature?: string;
33
35
  version: SemVer;
34
36
  }
35
- export interface SearchMetaData {
36
- [key: string]: any;
37
- }
38
- export interface SearchMetaDataEntity extends BaseEntity {
39
- searchMetaData?: SearchMetaData;
40
- }
41
37
  export interface ConfigElements {
42
38
  [key: string]: string;
43
39
  }
@@ -69,16 +65,36 @@ export interface ConfigEntity {
69
65
  export interface ConfigContainerEntity extends BaseEntity {
70
66
  config: ConfigEntity;
71
67
  }
72
- export interface PassageEntity extends VersionEntity, ConfigContainerEntity, SearchMetaDataEntity {
68
+ export interface PassageEntity extends VersionEntity, ConfigContainerEntity {
73
69
  name: string;
70
+ /**
71
+ * BCP 47 tag of the content's language (QTI `xml:lang`). The players render
72
+ * and score it under this language; absent, the section's `env.locale` holds.
73
+ */
74
+ language?: string;
75
+ /** The source a translated version was adapted from. No player reads it. */
76
+ isAdaptationOf?: AdaptationLink;
74
77
  /** QTI/APIP-style accessibility catalogs owned by this passage content. */
75
78
  accessibilityCatalogs?: AccessibilityCatalog[];
79
+ /**
80
+ * Standards and skills the content is aligned to. Absent means no data; `[]`
81
+ * means the writer states that none applies. No player reads it.
82
+ */
83
+ curriculumStandards?: CurriculumStandard[];
84
+ /** DOK and Bloom's classifications, on the same terms. No player reads it. */
85
+ skillLevels?: SkillLevel[];
76
86
  retired?: boolean;
77
87
  published?: boolean;
78
- highlights?: Record<string, string[]>;
79
88
  }
80
- export interface ItemEntity extends VersionEntity, ConfigContainerEntity, SearchMetaDataEntity {
89
+ export interface ItemEntity extends VersionEntity, ConfigContainerEntity {
81
90
  name?: string;
91
+ /**
92
+ * BCP 47 tag of the content's language (QTI `xml:lang`). The players render
93
+ * and score it under this language; absent, the section's `env.locale` holds.
94
+ */
95
+ language?: string;
96
+ /** The source a translated version was adapted from. No player reads it. */
97
+ isAdaptationOf?: AdaptationLink;
82
98
  /**
83
99
  * `null` as well as absent, because importers write it: JSON has no
84
100
  * `undefined`, so an item transformed from another format carries an
@@ -87,10 +103,16 @@ export interface ItemEntity extends VersionEntity, ConfigContainerEntity, Search
87
103
  passage?: string | PassageEntity | null;
88
104
  /** QTI/APIP-style accessibility catalogs owned by the item root. */
89
105
  accessibilityCatalogs?: AccessibilityCatalog[];
106
+ /**
107
+ * Standards and skills the content is aligned to. Absent means no data; `[]`
108
+ * means the writer states that none applies. No player reads it.
109
+ */
110
+ curriculumStandards?: CurriculumStandard[];
111
+ /** DOK and Bloom's classifications, on the same terms. No player reads it. */
112
+ skillLevels?: SkillLevel[];
90
113
  retired?: boolean;
91
114
  published?: boolean;
92
115
  configuration?: any;
93
- highlights?: Record<string, string[]>;
94
116
  }
95
117
  /**
96
118
  * `baseId` and `version` are optional on an entity a section references. They
@@ -109,20 +131,6 @@ export type ReferencedPassageEntity = Omit<WithOptionalVersion<PassageEntity>, "
109
131
  export type ReferencedItemEntity = Omit<WithOptionalVersion<ItemEntity>, "passage"> & {
110
132
  passage?: string | ReferencedPassageEntity | null;
111
133
  };
112
- /**
113
- * Metadata specifically for interpretation by clients, typically containing
114
- * options that are relevant for the user interface. This is not indexed for search.
115
- */
116
- export interface SettingsMetaData {
117
- [key: string]: any;
118
- }
119
- /**
120
- * Objects that contain metadata in the settings property, which is NOT indexed for search
121
- * but typically only relevant for particular client cases.
122
- */
123
- export interface SettingsMetaDataEntity {
124
- settings?: SettingsMetaData;
125
- }
126
134
  export type RubricBlockView = "author" | "candidate" | "proctor" | "scorer" | "testConstructor" | "tutor";
127
135
  export interface RubricBlock {
128
136
  identifier?: string;
@@ -150,7 +158,7 @@ export interface RubricBlock {
150
158
  * References an item within a section. The item property contains the resolved
151
159
  * ItemEntity with its PIE config.
152
160
  */
153
- export interface AssessmentItemRef extends SearchMetaDataEntity {
161
+ export interface AssessmentItemRef extends BaseEntity {
154
162
  id?: string;
155
163
  identifier: string;
156
164
  title?: string;
@@ -175,7 +183,7 @@ export interface AssessmentItemRef extends SearchMetaDataEntity {
175
183
  */
176
184
  formative?: FormativeItemPolicy;
177
185
  }
178
- export interface AssessmentSection extends SearchMetaDataEntity, SettingsMetaDataEntity {
186
+ export interface AssessmentSection extends BaseEntity {
179
187
  id?: string;
180
188
  identifier: string;
181
189
  title?: string;
@@ -484,7 +492,7 @@ export interface PersonalNeedsProfile {
484
492
  */
485
493
  toolParameters?: ToolParameters;
486
494
  }
487
- export interface AssessmentEntity extends BaseEntity, SearchMetaDataEntity {
495
+ export interface AssessmentEntity extends BaseEntity {
488
496
  name?: string;
489
497
  title?: string;
490
498
  identifier?: string;
@@ -499,6 +507,11 @@ export interface AssessmentEntity extends BaseEntity, SearchMetaDataEntity {
499
507
  accessibilityCatalogs?: AccessibilityCatalog[];
500
508
  /** QTI 3.0: Personal Needs Profile (PNP 3.0) */
501
509
  personalNeedsProfile?: PersonalNeedsProfile;
510
+ /**
511
+ * The languages the assessment is offered in, read by a backend that
512
+ * resolves language variants. No player reads it.
513
+ */
514
+ languagePolicy?: LanguagePolicy;
502
515
  /**
503
516
  * QTI 3.0: testParts structure (authoritative for QTI format).
504
517
  */
@@ -548,6 +561,13 @@ export interface ToolParameterMap {
548
561
  signLanguage: {
549
562
  signLang?: string;
550
563
  };
564
+ /**
565
+ * `language` is the BCP 47 content language the learner reads items in. A
566
+ * host passes it to delivery as `requestedLanguages`.
567
+ */
568
+ itemTranslation: {
569
+ language?: string;
570
+ };
551
571
  /**
552
572
  * `rate` multiplies every playback speed, 1 being the provider's normal
553
573
  * speed; `pitch` runs 0–2 with 1 normal; `volume` runs 0–1.
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Interchange types: backends and authoring tools exchange them, and no player
3
+ * reads them. Their tier is listed on the content-model page.
4
+ *
5
+ * Language variants (PIE-1393): no player reads `AdaptationLink` or
6
+ * `LanguagePolicy`.
7
+ * A language variant is its own item or passage version that declares
8
+ * `language` and, when it is a translation, `isAdaptationOf`. Its family is
9
+ * `isAdaptationOf.baseId ?? baseId`.
10
+ */
11
+ import type { SemVer } from "./index.js";
12
+ /** The source a translated item or passage was adapted from. */
13
+ export interface AdaptationLink {
14
+ baseId: string;
15
+ /** The source version translated. Absent when provenance is unknown. */
16
+ version?: SemVer;
17
+ }
18
+ /**
19
+ * The languages an assessment offers and what a backend serves when a family
20
+ * has no variant in the requested language.
21
+ */
22
+ export interface LanguagePolicy {
23
+ /** BCP 47 tags the assessment may be served in. */
24
+ languages: string[];
25
+ /**
26
+ * `"default"` serves an item whose family lacks the requested language in
27
+ * the closest match for the platform default, `en-US` (so `en` or `en-GB`),
28
+ * then its content that declares no language, then its source. Fallback is
29
+ * per item; items sharing a passage fall back together. `"none"` fails the
30
+ * load.
31
+ */
32
+ fallback: "default" | "none";
33
+ /**
34
+ * `"closest"`, the default, serves the nearest variant in the requested
35
+ * language, so `es-MX` reaches `es` or `es-ES`; `"exact"` serves only an
36
+ * equal tag and leaves anything else to `fallback`.
37
+ */
38
+ matching?: "closest" | "exact";
39
+ /**
40
+ * `"serve"` serves a variant whose source has a newer release and records
41
+ * it as stale; `"require-current"` treats that variant as missing.
42
+ */
43
+ staleness: "serve" | "require-current";
44
+ }
45
+ /**
46
+ * Why the served content differs from what was requested: `"fallback"`, not
47
+ * in a requested language; `"variety"`, in another variety of a requested
48
+ * language, such as `es-ES` for `es-MX`; `"stale"`, a translation of an older
49
+ * source release.
50
+ */
51
+ export type ContentSubstitution = "fallback" | "stale" | "variety";
52
+ /** The content version a backend served, and the language it served it in. */
53
+ export interface ServedContentIdentity {
54
+ baseId: string;
55
+ version?: SemVer;
56
+ /** The family key, when the served version belongs to a variant family. */
57
+ family?: string;
58
+ /** BCP 47 tag of the served content. */
59
+ servedLanguage?: string;
60
+ substitution?: ContentSubstitution;
61
+ }
62
+ /**
63
+ * One curriculum standard, skill or competency the content is aligned to
64
+ * (PIE-1381). QTI 3 Curriculum Standards Metadata (CSM 1.1), one entry per
65
+ * `labelledGUID`. Content records direct links only; a framework provider
66
+ * resolves parents, crosswalks and successors on read.
67
+ */
68
+ export interface CurriculumStandard {
69
+ /**
70
+ * CSM `curriculumStandardsMetadata@providerId`: who issued `guid`, from the
71
+ * provider-id registry on the content-model page, or a host value. Absent
72
+ * means `"AB"` (Academic Benchmarks).
73
+ */
74
+ providerId?: string;
75
+ /**
76
+ * CSM `labelledGUID/GUID`, as the provider issued it. Stored with its case
77
+ * and compared case-insensitively.
78
+ */
79
+ guid: string;
80
+ /** CSM `labelledGUID/caseItemURI`, for a CASE framework item. */
81
+ caseItemURI?: string;
82
+ /** CSM `labelledGUID/label`: a display code. Never a key. */
83
+ label?: string;
84
+ /** LOM classification purpose. Absent means `"educational objective"`. */
85
+ purpose?: "educational objective" | "prerequisite" | (string & {});
86
+ /** The record's main standard for this provider. At most one per `providerId`. */
87
+ primary?: boolean;
88
+ /** How the link was made. */
89
+ provenance?: "authored" | "derived" | "imported";
90
+ /**
91
+ * For a derived link: the link it was generated from. An absent
92
+ * `providerId` means `"AB"`.
93
+ */
94
+ derivedFrom?: {
95
+ providerId?: string;
96
+ guid: string;
97
+ };
98
+ }
99
+ /**
100
+ * A cognitive-complexity classification of the content (PIE-1381). LOM
101
+ * classification purpose "skill level".
102
+ */
103
+ export interface SkillLevel {
104
+ /**
105
+ * LOM `taxonPath/source`: `"webb-dok"`, `"blooms-revised"`,
106
+ * `"blooms-original"`, or a host taxonomy.
107
+ */
108
+ source: string;
109
+ /**
110
+ * LOM `taxon/id`: `"1"` to `"4"` for `webb-dok`; `"remember"` to `"create"`
111
+ * for `blooms-revised`.
112
+ */
113
+ id: string;
114
+ /** PIE extension: the author's justification. */
115
+ rationale?: string;
116
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { collectFocusable } from "./first-focusable.js";
2
+ import { isComposingKeyEvent } from "./ime.js";
2
3
  /**
3
4
  * Shadow-aware: a trapped panel almost always hosts a custom element rendering into
4
5
  * `shadow: "open"`, and a flat `querySelectorAll` would collect only the panel's own
@@ -44,7 +45,8 @@ export function createFocusTrap(container, options = {}) {
44
45
  const wrap = options.wrap ?? true;
45
46
  const onKeydown = (event) => {
46
47
  if (event.key === "Escape") {
47
- if (!options.onEscape)
48
+ // An Escape that cancels an IME composition belongs to the IME.
49
+ if (!options.onEscape || isComposingKeyEvent(event))
48
50
  return;
49
51
  // Claimed, so a page-wide Escape listener does not also act on it.
50
52
  event.preventDefault();
@@ -0,0 +1,51 @@
1
+ /**
2
+ * FontAwesome for `<nds-icon-button>`, which renders its glyph as
3
+ * `<i class="fa-light fa-${name}">` and ships no font of its own.
4
+ *
5
+ * FontAwesome comes from the host page when it links any FA stylesheet.
6
+ * Otherwise `ensureFontAwesome` adds FA Free from the ESM CDN the item player's
7
+ * `loaderOptions` name, jsDelivr by default. Pro is never fetched from here:
8
+ * ui.renaissance.com serves its font files to Renaissance origins only, so a
9
+ * host that licenses it links it itself, as section-demos does through its dev
10
+ * proxy.
11
+ *
12
+ * `fa-light` is a weight only Pro carries. Free has no `.fa-light` rule and its
13
+ * Regular font lacks most toolbar glyphs, so without Pro `applyNdsGlyphVariant`
14
+ * renders every glyph in Solid, the one Free weight that has them all.
15
+ *
16
+ * Roboto comes from the vendored button: its connectedCallback links
17
+ * ui.renaissance.com's Roboto unless the page links a stylesheet whose URL
18
+ * contains `Roboto`.
19
+ */
20
+ export type NdsGlyphVariant = "fa-light" | "fa-regular" | "fa-solid";
21
+ /**
22
+ * Adds FA Free to `<head>` unless the page already links a FontAwesome
23
+ * stylesheet. Appending Free after a host's Pro would override Pro's
24
+ * `.fa-regular` / `.fa-solid` font-family rules page-wide.
25
+ *
26
+ * A stylesheet that fails to load stays in `<head>` marked
27
+ * `data-pie-load-failed`: no later call requests it again, and no shadow root
28
+ * copies it.
29
+ */
30
+ export declare function ensureFontAwesome(loaderOptions: {
31
+ esmCdnUrl?: unknown;
32
+ esmCdnProvider?: unknown;
33
+ } | null | undefined): void;
34
+ /**
35
+ * Copies the page's FontAwesome `<link>`s into the shadow root `node` renders
36
+ * in, since `<head>` stylesheets do not cross a shadow boundary. Does nothing
37
+ * when `node` is in the document tree, and runs once per shadow root.
38
+ *
39
+ * A stylesheet still loading is copied once it loads, so the copy comes from
40
+ * the cache; one that fails is not copied, since every copy would request it
41
+ * again. Chromium gives a link a `sheet` even when its load fails, so a failed
42
+ * one is known by its mark.
43
+ */
44
+ export declare function installFontAwesomeInShadow(node: Node): void;
45
+ /**
46
+ * Renders the `fa-light` glyphs inside `button` in `preferred` when the page
47
+ * links Pro, and in Solid otherwise. Re-applies whenever Lit renders or
48
+ * rewrites the icon's class; the class edit removes `fa-light`, so the
49
+ * callback it triggers is a no-op. Returns the observer's disconnect.
50
+ */
51
+ export declare function applyNdsGlyphVariant(button: HTMLElement, preferred: NdsGlyphVariant): () => void;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * FontAwesome for `<nds-icon-button>`, which renders its glyph as
3
+ * `<i class="fa-light fa-${name}">` and ships no font of its own.
4
+ *
5
+ * FontAwesome comes from the host page when it links any FA stylesheet.
6
+ * Otherwise `ensureFontAwesome` adds FA Free from the ESM CDN the item player's
7
+ * `loaderOptions` name, jsDelivr by default. Pro is never fetched from here:
8
+ * ui.renaissance.com serves its font files to Renaissance origins only, so a
9
+ * host that licenses it links it itself, as section-demos does through its dev
10
+ * proxy.
11
+ *
12
+ * `fa-light` is a weight only Pro carries. Free has no `.fa-light` rule and its
13
+ * Regular font lacks most toolbar glyphs, so without Pro `applyNdsGlyphVariant`
14
+ * renders every glyph in Solid, the one Free weight that has them all.
15
+ *
16
+ * Roboto comes from the vendored button: its connectedCallback links
17
+ * ui.renaissance.com's Roboto unless the page links a stylesheet whose URL
18
+ * contains `Roboto`.
19
+ */
20
+ import { resolveEsmPackageFileUrl } from "../loaders/esm-adapter.js";
21
+ /** The one FA Free build the players add; `<package>@<version>/<path>`. */
22
+ const FONTAWESOME_FREE_FILE = "@fortawesome/fontawesome-free@6.5.2/css/all.min.css";
23
+ // Matches any FA stylesheet the host page links: `fontawesome.min.css`,
24
+ // `font-awesome.css`, a `/_fa-pro/` proxy, `fontawesome-free@…`.
25
+ const FA_HREF_PATTERN = /font.?awesome|fa-?pro/i;
26
+ const FA_FREE_HREF_PATTERN = /fontawesome-free/i;
27
+ const documentStylesheetLinks = () => Array.from(document.querySelectorAll('link[rel="stylesheet"][href]'));
28
+ const documentFontAwesomeLinks = () => documentStylesheetLinks().filter((link) => FA_HREF_PATTERN.test(link.href));
29
+ /** Whether the page links a FontAwesome build other than Free. */
30
+ const pageLinksFontAwesomePro = () => documentFontAwesomeLinks().some((link) => !FA_FREE_HREF_PATTERN.test(link.href));
31
+ /**
32
+ * Adds FA Free to `<head>` unless the page already links a FontAwesome
33
+ * stylesheet. Appending Free after a host's Pro would override Pro's
34
+ * `.fa-regular` / `.fa-solid` font-family rules page-wide.
35
+ *
36
+ * A stylesheet that fails to load stays in `<head>` marked
37
+ * `data-pie-load-failed`: no later call requests it again, and no shadow root
38
+ * copies it.
39
+ */
40
+ export function ensureFontAwesome(loaderOptions) {
41
+ if (typeof document === "undefined")
42
+ return;
43
+ if (documentFontAwesomeLinks().length > 0)
44
+ return;
45
+ const href = resolveEsmPackageFileUrl(loaderOptions, FONTAWESOME_FREE_FILE);
46
+ const link = document.createElement("link");
47
+ link.rel = "stylesheet";
48
+ link.href = href;
49
+ link.addEventListener("error", () => {
50
+ link.dataset.pieLoadFailed = "";
51
+ }, { once: true });
52
+ document.head.appendChild(link);
53
+ }
54
+ const mirroredShadowRoots = new WeakSet();
55
+ /**
56
+ * Copies the page's FontAwesome `<link>`s into the shadow root `node` renders
57
+ * in, since `<head>` stylesheets do not cross a shadow boundary. Does nothing
58
+ * when `node` is in the document tree, and runs once per shadow root.
59
+ *
60
+ * A stylesheet still loading is copied once it loads, so the copy comes from
61
+ * the cache; one that fails is not copied, since every copy would request it
62
+ * again. Chromium gives a link a `sheet` even when its load fails, so a failed
63
+ * one is known by its mark.
64
+ */
65
+ export function installFontAwesomeInShadow(node) {
66
+ const shadow = node.getRootNode();
67
+ if (!(shadow instanceof ShadowRoot) || mirroredShadowRoots.has(shadow)) {
68
+ return;
69
+ }
70
+ mirroredShadowRoots.add(shadow);
71
+ const appendLink = (href) => {
72
+ const present = Array.from(shadow.querySelectorAll('link[rel="stylesheet"]')).some((link) => link.href === href);
73
+ if (!href || present)
74
+ return;
75
+ const link = document.createElement("link");
76
+ link.rel = "stylesheet";
77
+ link.href = href;
78
+ shadow.appendChild(link);
79
+ };
80
+ for (const link of documentFontAwesomeLinks()) {
81
+ if ("pieLoadFailed" in link.dataset)
82
+ continue;
83
+ if (link.sheet) {
84
+ appendLink(link.href);
85
+ continue;
86
+ }
87
+ const settle = (event) => {
88
+ link.removeEventListener("load", settle);
89
+ link.removeEventListener("error", settle);
90
+ if (event.type === "load")
91
+ appendLink(link.href);
92
+ };
93
+ link.addEventListener("load", settle);
94
+ link.addEventListener("error", settle);
95
+ }
96
+ }
97
+ /**
98
+ * Renders the `fa-light` glyphs inside `button` in `preferred` when the page
99
+ * links Pro, and in Solid otherwise. Re-applies whenever Lit renders or
100
+ * rewrites the icon's class; the class edit removes `fa-light`, so the
101
+ * callback it triggers is a no-op. Returns the observer's disconnect.
102
+ */
103
+ export function applyNdsGlyphVariant(button, preferred) {
104
+ const variant = pageLinksFontAwesomePro() ? preferred : "fa-solid";
105
+ if (variant === "fa-light")
106
+ return () => { };
107
+ const apply = () => {
108
+ for (const icon of button.querySelectorAll("i.fa-light")) {
109
+ icon.classList.remove("fa-light");
110
+ icon.classList.add(variant);
111
+ }
112
+ };
113
+ apply();
114
+ const observer = new MutationObserver(apply);
115
+ observer.observe(button, {
116
+ childList: true,
117
+ subtree: true,
118
+ attributes: true,
119
+ attributeFilter: ["class"],
120
+ });
121
+ return () => observer.disconnect();
122
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Whether a keydown belongs to an IME composition, so it must not act as a
3
+ * shortcut. Safari reports `isComposing` false on the Enter that commits a
4
+ * composition and marks it only with `keyCode` 229.
5
+ */
6
+ export declare function isComposingKeyEvent(event: Pick<KeyboardEvent, "isComposing" | "keyCode">): boolean;
package/dist/ui/ime.js ADDED
@@ -0,0 +1,10 @@
1
+ /** The legacy `keyCode` a browser reports for a keydown the IME consumes. */
2
+ const IME_PROCESS_KEY_CODE = 229;
3
+ /**
4
+ * Whether a keydown belongs to an IME composition, so it must not act as a
5
+ * shortcut. Safari reports `isComposing` false on the Enter that commits a
6
+ * composition and marks it only with `keyCode` 229.
7
+ */
8
+ export function isComposingKeyEvent(event) {
9
+ return event.isComposing || event.keyCode === IME_PROCESS_KEY_CODE;
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-players/pie-players-shared",
3
- "version": "1.0.0-beta.6",
3
+ "version": "1.0.0-beta.8",
4
4
  "type": "module",
5
5
  "description": "Shared runtime + UI utilities for PIE players",
6
6
  "license": "MIT",
@@ -29,6 +29,10 @@
29
29
  "types": "./dist/security/index.d.ts",
30
30
  "import": "./dist/security/index.js"
31
31
  },
32
+ "./security/private-hosts": {
33
+ "types": "./dist/security/private-hosts.d.ts",
34
+ "import": "./dist/security/private-hosts.js"
35
+ },
32
36
  "./object": {
33
37
  "types": "./dist/object/index.d.ts",
34
38
  "import": "./dist/object/index.js"
@@ -97,6 +101,10 @@
97
101
  "types": "./dist/ui/content-styles.d.ts",
98
102
  "import": "./dist/ui/content-styles.js"
99
103
  },
104
+ "./ui/fontawesome": {
105
+ "types": "./dist/ui/fontawesome.d.ts",
106
+ "import": "./dist/ui/fontawesome.js"
107
+ },
100
108
  "./tools/term-lookup": {
101
109
  "types": "./dist/tools/term-lookup.d.ts",
102
110
  "import": "./dist/tools/term-lookup.js"
@@ -106,9 +114,10 @@
106
114
  "dist"
107
115
  ],
108
116
  "dependencies": {
109
- "@pie-element/shared-utils": "0.1.4",
117
+ "@pie-element/shared-utils": "0.1.6",
110
118
  "@pie-lib/math-rendering-module": "6.0.0",
111
119
  "dompurify": "^3.4.17",
120
+ "ipaddr.js": "^2.5.0",
112
121
  "semver": "^7.8.5"
113
122
  },
114
123
  "scripts": {
@@ -122,15 +131,15 @@
122
131
  "scan-hardcoded": "bun run src/i18n/scripts/scan-hardcoded.ts"
123
132
  },
124
133
  "devDependencies": {
125
- "@biomejs/biome": "^2.5.15",
126
- "@happy-dom/global-registrator": "^20.14.5",
127
- "@pie-element/shared-math-rendering-mathjax": "0.1.7",
134
+ "@biomejs/biome": "^2.6.4",
135
+ "@happy-dom/global-registrator": "^21.0.4",
136
+ "@pie-element/shared-math-rendering-mathjax": "0.1.8",
128
137
  "@playwright/test": "^1.64.1",
129
138
  "@types/semver": "^7.8.0",
130
139
  "es-module-shims": "^2.8.4",
131
140
  "esbuild": "^0.28.2",
132
141
  "glob": "^13.0.0",
133
- "svelte": "^5.57.2",
142
+ "svelte": "^5.57.3",
134
143
  "svelte-check": "^4.7.6",
135
144
  "typescript": "^5.9.3"
136
145
  },