@pie-players/pie-assessment-toolkit 0.3.61 → 0.3.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attempt/AssessmentSession.js +0 -1
- package/dist/attempt/TestSession.js +0 -1
- package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -1
- package/dist/components/ItemToolBar.custom-element.js +1 -10106
- package/dist/components/PieAssessmentToolkit.custom-element.js +48 -29930
- package/dist/components/SectionToolBar.custom-element.js +1 -14372
- package/dist/components/chunks/ItemToolBar-843902tp.js +51 -0
- package/dist/components/chunks/ItemToolBar-84nv78dy.js +22 -0
- package/dist/components/item-toolbar-element.js +0 -1
- package/dist/components/pie-assessment-toolkit-element.js +0 -1
- package/dist/components/section-toolbar-element.js +0 -1
- package/dist/context/assessment-toolkit-context.js +0 -1
- package/dist/context/runtime-context-consumer.js +0 -1
- package/dist/index.js +0 -1
- package/dist/item-loader.js +0 -1
- package/dist/policy/core/PolicySource.js +0 -1
- package/dist/policy/core/ToolPolicyEngine.js +0 -1
- package/dist/policy/core/compose-decision.js +0 -1
- package/dist/policy/core/decision-types.js +0 -1
- package/dist/policy/core/engine-context.js +0 -1
- package/dist/policy/core/pnp-policy-inputs.js +0 -1
- package/dist/policy/core/policy-source-tag.js +0 -1
- package/dist/policy/core/provenance.js +0 -1
- package/dist/policy/engine.js +0 -1
- package/dist/policy/internal.js +0 -1
- package/dist/policy/sources/PnpPolicySource.js +0 -1
- package/dist/runtime/RuntimeRegistry.js +0 -1
- package/dist/runtime/SectionRuntimeEngine.js +0 -1
- package/dist/runtime/adapter/SectionEngineAdapter.js +0 -1
- package/dist/runtime/adapter/coordinator-bridge.js +0 -1
- package/dist/runtime/adapter/dom-event-bridge.js +0 -1
- package/dist/runtime/adapter/framework-error-bridge.js +0 -1
- package/dist/runtime/adapter/instrumentation-bridge.js +0 -1
- package/dist/runtime/adapter/subscriber-fanout.js +0 -1
- package/dist/runtime/catalog-registration.js +0 -1
- package/dist/runtime/core/SectionEngineCore.js +0 -1
- package/dist/runtime/core/cohort.js +0 -1
- package/dist/runtime/core/engine-input.js +0 -1
- package/dist/runtime/core/engine-output.js +0 -1
- package/dist/runtime/core/engine-readiness.js +0 -1
- package/dist/runtime/core/engine-resolver.js +0 -1
- package/dist/runtime/core/engine-stage-derivation.js +0 -1
- package/dist/runtime/core/engine-state.js +0 -1
- package/dist/runtime/core/engine-transition.js +0 -1
- package/dist/runtime/engine-context.js +0 -1
- package/dist/runtime/engine.js +0 -1
- package/dist/runtime/internal.js +0 -1
- package/dist/runtime/registration-events.js +0 -1
- package/dist/runtime/runtime-id.js +0 -1
- package/dist/runtime/section-runtime-engine-host-context.js +0 -1
- package/dist/runtime/session-event-emitter-policy.js +0 -1
- package/dist/runtime/stage-emit-gate.js +0 -1
- package/dist/runtime/tool-host-contract.js +0 -1
- package/dist/services/AccessibilityCatalogResolver.js +0 -1
- package/dist/services/ContextVariableStore.js +0 -1
- package/dist/services/ElementToolStateStore.js +0 -1
- package/dist/services/HighlightCoordinator.js +35 -3
- package/dist/services/I18nService.js +0 -1
- package/dist/services/RangeSerializer.js +0 -1
- package/dist/services/SSMLExtractor.js +0 -1
- package/dist/services/TTSService.js +0 -1
- package/dist/services/ThemeProvider.js +0 -1
- package/dist/services/ToolConfigResolver.js +0 -1
- package/dist/services/ToolCoordinator.js +0 -1
- package/dist/services/ToolRegistry.js +0 -1
- package/dist/services/ToolkitCoordinator.js +0 -1
- package/dist/services/create-tools-config.js +0 -1
- package/dist/services/createDefaultToolRegistry.js +0 -1
- package/dist/services/defaultPersonalNeedsProfile.js +0 -1
- package/dist/services/framework-error-bus.js +0 -1
- package/dist/services/framework-error.js +0 -1
- package/dist/services/interfaces.js +0 -1
- package/dist/services/pnp-standard-features.js +0 -1
- package/dist/services/section-controller-types.js +0 -1
- package/dist/services/tool-config-defaults.js +0 -1
- package/dist/services/tool-config-validation.js +0 -1
- package/dist/services/tool-context.js +0 -1
- package/dist/services/tool-instance-id.js +0 -1
- package/dist/services/tool-providers/DesmosToolProvider.js +0 -1
- package/dist/services/tool-providers/TTSToolProvider.js +0 -1
- package/dist/services/tool-providers/ToolProviderApi.js +0 -1
- package/dist/services/tool-providers/ToolProviderRegistry.js +0 -1
- package/dist/services/tool-providers/index.js +0 -1
- package/dist/services/toolbar-items.js +0 -1
- package/dist/services/tools-config-normalizer.js +0 -1
- package/dist/services/tts/browser-provider.js +0 -1
- package/dist/services/tts/catalog-span-alignment.js +0 -1
- package/dist/services/tts/generated-speech/assemble-plan.js +0 -1
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js +0 -1
- package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js +0 -1
- package/dist/services/tts/generated-speech/dom/types.js +0 -1
- package/dist/services/tts/generated-speech/index.js +0 -1
- package/dist/services/tts/generated-speech/math-speech-cache.js +0 -1
- package/dist/services/tts/generated-speech/types.js +0 -1
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +0 -1
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +0 -1
- package/dist/services/tts/highlight-pipeline/highlight-policy.js +0 -1
- package/dist/services/tts/highlight-pipeline/index.js +0 -1
- package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js +0 -1
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +0 -1
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +0 -1
- package/dist/services/tts/highlight-pipeline/readable-region.js +0 -1
- package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js +0 -1
- package/dist/services/tts/highlight-pipeline/types.js +0 -1
- package/dist/services/tts/highlight-pipeline/visible-map-range.js +0 -1
- package/dist/services/tts/highlight-target-resolver.js +0 -1
- package/dist/services/tts/math-alignment/index.js +0 -1
- package/dist/services/tts/math-alignment/mathml-tokenizer.js +0 -1
- package/dist/services/tts/math-alignment/range-resolver.js +0 -1
- package/dist/services/tts/math-alignment/sequence-aligner.js +0 -1
- package/dist/services/tts/math-alignment/speech-tokenizer.js +0 -1
- package/dist/services/tts/math-alignment/types.js +0 -1
- package/dist/services/tts/math-aware-text-processing.js +0 -1
- package/dist/services/tts/math-speech.js +0 -1
- package/dist/services/tts/mathml-sanitization.js +0 -1
- package/dist/services/tts/ssml/spoken-text.js +0 -1
- package/dist/services/tts/text-processing.js +0 -1
- package/dist/services/tts/text-segmentation.js +0 -1
- package/dist/services/tts/visual-line-ranges.js +0 -1
- package/dist/services/tts-control-events.js +0 -1
- package/dist/services/tts-runtime-config.js +0 -1
- package/dist/tools/calculators/desmos-provider.js +0 -1
- package/dist/tools/client.js +0 -1
- package/dist/tools/index.js +0 -1
- package/dist/tools/library-loader.js +0 -1
- package/dist/tools/registrations/accessibility-tools.js +0 -1
- package/dist/tools/registrations/calculator.js +0 -1
- package/dist/tools/registrations/interaction-tools.js +0 -1
- package/dist/tools/registrations/measurement-tools.js +0 -1
- package/dist/tools/registrations/subject-specific-tools.js +0 -1
- package/dist/tools/registrations/toolbar-registration-helpers.js +0 -1
- package/dist/tools/registrations/tts.js +0 -1
- package/dist/tools/response-discovery.js +0 -1
- package/dist/tools/tool-tag-map.js +0 -1
- package/dist/tools/types.js +0 -1
- package/dist/tools/variant-resolver.js +0 -1
- package/dist/utils/logger.js +0 -1
- package/package.json +13 -11
- package/dist/attempt/AssessmentSession.js.map +0 -1
- package/dist/attempt/TestSession.js.map +0 -1
- package/dist/attempt/adapters/activity-to-test-attempt-session.js.map +0 -1
- package/dist/components/item-toolbar-element.js.map +0 -1
- package/dist/components/pie-assessment-toolkit-element.js.map +0 -1
- package/dist/components/section-toolbar-element.js.map +0 -1
- package/dist/context/assessment-toolkit-context.js.map +0 -1
- package/dist/context/runtime-context-consumer.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/item-loader.js.map +0 -1
- package/dist/policy/core/PolicySource.js.map +0 -1
- package/dist/policy/core/ToolPolicyEngine.js.map +0 -1
- package/dist/policy/core/compose-decision.js.map +0 -1
- package/dist/policy/core/decision-types.js.map +0 -1
- package/dist/policy/core/engine-context.js.map +0 -1
- package/dist/policy/core/pnp-policy-inputs.js.map +0 -1
- package/dist/policy/core/policy-source-tag.js.map +0 -1
- package/dist/policy/core/provenance.js.map +0 -1
- package/dist/policy/engine.js.map +0 -1
- package/dist/policy/internal.js.map +0 -1
- package/dist/policy/sources/PnpPolicySource.js.map +0 -1
- package/dist/runtime/RuntimeRegistry.js.map +0 -1
- package/dist/runtime/SectionRuntimeEngine.js.map +0 -1
- package/dist/runtime/adapter/SectionEngineAdapter.js.map +0 -1
- package/dist/runtime/adapter/coordinator-bridge.js.map +0 -1
- package/dist/runtime/adapter/dom-event-bridge.js.map +0 -1
- package/dist/runtime/adapter/framework-error-bridge.js.map +0 -1
- package/dist/runtime/adapter/instrumentation-bridge.js.map +0 -1
- package/dist/runtime/adapter/subscriber-fanout.js.map +0 -1
- package/dist/runtime/catalog-registration.js.map +0 -1
- package/dist/runtime/core/SectionEngineCore.js.map +0 -1
- package/dist/runtime/core/cohort.js.map +0 -1
- package/dist/runtime/core/engine-input.js.map +0 -1
- package/dist/runtime/core/engine-output.js.map +0 -1
- package/dist/runtime/core/engine-readiness.js.map +0 -1
- package/dist/runtime/core/engine-resolver.js.map +0 -1
- package/dist/runtime/core/engine-stage-derivation.js.map +0 -1
- package/dist/runtime/core/engine-state.js.map +0 -1
- package/dist/runtime/core/engine-transition.js.map +0 -1
- package/dist/runtime/engine-context.js.map +0 -1
- package/dist/runtime/engine.js.map +0 -1
- package/dist/runtime/internal.js.map +0 -1
- package/dist/runtime/registration-events.js.map +0 -1
- package/dist/runtime/runtime-id.js.map +0 -1
- package/dist/runtime/section-runtime-engine-host-context.js.map +0 -1
- package/dist/runtime/session-event-emitter-policy.js.map +0 -1
- package/dist/runtime/stage-emit-gate.js.map +0 -1
- package/dist/runtime/tool-host-contract.js.map +0 -1
- package/dist/services/AccessibilityCatalogResolver.js.map +0 -1
- package/dist/services/ContextVariableStore.js.map +0 -1
- package/dist/services/ElementToolStateStore.js.map +0 -1
- package/dist/services/HighlightCoordinator.js.map +0 -1
- package/dist/services/I18nService.js.map +0 -1
- package/dist/services/RangeSerializer.js.map +0 -1
- package/dist/services/SSMLExtractor.js.map +0 -1
- package/dist/services/TTSService.js.map +0 -1
- package/dist/services/ThemeProvider.js.map +0 -1
- package/dist/services/ToolConfigResolver.js.map +0 -1
- package/dist/services/ToolCoordinator.js.map +0 -1
- package/dist/services/ToolRegistry.js.map +0 -1
- package/dist/services/ToolkitCoordinator.js.map +0 -1
- package/dist/services/create-tools-config.js.map +0 -1
- package/dist/services/createDefaultToolRegistry.js.map +0 -1
- package/dist/services/defaultPersonalNeedsProfile.js.map +0 -1
- package/dist/services/framework-error-bus.js.map +0 -1
- package/dist/services/framework-error.js.map +0 -1
- package/dist/services/interfaces.js.map +0 -1
- package/dist/services/pnp-standard-features.js.map +0 -1
- package/dist/services/section-controller-types.js.map +0 -1
- package/dist/services/tool-config-defaults.js.map +0 -1
- package/dist/services/tool-config-validation.js.map +0 -1
- package/dist/services/tool-context.js.map +0 -1
- package/dist/services/tool-instance-id.js.map +0 -1
- package/dist/services/tool-providers/DesmosToolProvider.js.map +0 -1
- package/dist/services/tool-providers/TTSToolProvider.js.map +0 -1
- package/dist/services/tool-providers/ToolProviderApi.js.map +0 -1
- package/dist/services/tool-providers/ToolProviderRegistry.js.map +0 -1
- package/dist/services/tool-providers/index.js.map +0 -1
- package/dist/services/toolbar-items.js.map +0 -1
- package/dist/services/tools-config-normalizer.js.map +0 -1
- package/dist/services/tts/browser-provider.js.map +0 -1
- package/dist/services/tts/catalog-span-alignment.js.map +0 -1
- package/dist/services/tts/generated-speech/assemble-plan.js.map +0 -1
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js.map +0 -1
- package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js.map +0 -1
- package/dist/services/tts/generated-speech/dom/types.js.map +0 -1
- package/dist/services/tts/generated-speech/index.js.map +0 -1
- package/dist/services/tts/generated-speech/math-speech-cache.js.map +0 -1
- package/dist/services/tts/generated-speech/types.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/highlight-policy.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/index.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/readable-region.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/types.js.map +0 -1
- package/dist/services/tts/highlight-pipeline/visible-map-range.js.map +0 -1
- package/dist/services/tts/highlight-target-resolver.js.map +0 -1
- package/dist/services/tts/math-alignment/index.js.map +0 -1
- package/dist/services/tts/math-alignment/mathml-tokenizer.js.map +0 -1
- package/dist/services/tts/math-alignment/range-resolver.js.map +0 -1
- package/dist/services/tts/math-alignment/sequence-aligner.js.map +0 -1
- package/dist/services/tts/math-alignment/speech-tokenizer.js.map +0 -1
- package/dist/services/tts/math-alignment/types.js.map +0 -1
- package/dist/services/tts/math-aware-text-processing.js.map +0 -1
- package/dist/services/tts/math-speech.js.map +0 -1
- package/dist/services/tts/mathml-sanitization.js.map +0 -1
- package/dist/services/tts/ssml/spoken-text.js.map +0 -1
- package/dist/services/tts/text-processing.js.map +0 -1
- package/dist/services/tts/text-segmentation.js.map +0 -1
- package/dist/services/tts/visual-line-ranges.js.map +0 -1
- package/dist/services/tts-control-events.js.map +0 -1
- package/dist/services/tts-runtime-config.js.map +0 -1
- package/dist/tools/calculators/desmos-provider.js.map +0 -1
- package/dist/tools/client.js.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/library-loader.js.map +0 -1
- package/dist/tools/registrations/accessibility-tools.js.map +0 -1
- package/dist/tools/registrations/calculator.js.map +0 -1
- package/dist/tools/registrations/interaction-tools.js.map +0 -1
- package/dist/tools/registrations/measurement-tools.js.map +0 -1
- package/dist/tools/registrations/subject-specific-tools.js.map +0 -1
- package/dist/tools/registrations/toolbar-registration-helpers.js.map +0 -1
- package/dist/tools/registrations/tts.js.map +0 -1
- package/dist/tools/response-discovery.js.map +0 -1
- package/dist/tools/tool-tag-map.js.map +0 -1
- package/dist/tools/types.js.map +0 -1
- package/dist/tools/variant-resolver.js.map +0 -1
- package/dist/utils/logger.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AccessibilityCatalogResolver.js","sourceRoot":"","sources":["../../src/services/AccessibilityCatalogResolver.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tAccessibilityCatalog,\n\tCatalogCard,\n} from \"@pie-players/pie-players-shared/types\";\nimport { sanitizeSsmlString } from \"./SSMLExtractor.js\";\n\nexport type CatalogOwnerKind = \"global\" | \"passage\" | \"itemModel\";\n\nexport interface CatalogOwnerContext {\n\townerKind: CatalogOwnerKind;\n\tassessmentId?: string;\n\tsectionId?: string;\n\tcanonicalItemId?: string;\n\titemId?: string;\n\tpassageId?: string;\n\tmodelId?: string;\n}\n\nexport type CatalogLookupContext = CatalogOwnerContext;\n\n/**\n * Supported accessibility catalog types from QTI 3.0 / APIP\n */\nexport type CatalogType =\n\t| \"spoken\" // Text-to-speech scripts\n\t| \"sign-language\" // Video URLs for signed content\n\t| \"braille\" // Braille transcriptions\n\t| \"tactile\" // Tactile graphics descriptions\n\t| \"simplified-language\" // Plain language alternatives\n\t| \"audio-description\" // Extended audio descriptions\n\t| \"extended-description\" // Extended text descriptions\n\t| string; // Support custom types\n\n/**\n * Lookup options for catalog resolution\n */\nexport interface CatalogLookupOptions {\n\t/** Catalog type (e.g., 'spoken', 'sign-language') */\n\ttype: CatalogType;\n\t/** Language code (e.g., 'en-US', 'es-ES') */\n\tlanguage?: string;\n\t/** Fallback to default language if requested language not found */\n\tuseFallback?: boolean;\n\t/** Scope used to resolve local catalog idrefs for rendered content */\n\tcontext?: CatalogLookupContext;\n}\n\n/**\n * Resolved catalog result\n */\nexport interface ResolvedCatalog {\n\t/** The catalog identifier */\n\tcatalogId: string;\n\t/** The catalog type */\n\ttype: CatalogType;\n\t/** The language code */\n\tlanguage?: string;\n\t/** The content (HTML, URL, or plain text) */\n\tcontent: string;\n\t/** Source of the catalog (assessment or item) */\n\tsource: \"assessment\" | \"item\";\n}\n\n/**\n * Statistics about available catalogs\n */\nexport interface CatalogStatistics {\n\t/** Total number of catalogs */\n\ttotalCatalogs: number;\n\t/** Number of assessment-level catalogs */\n\tassessmentCatalogs: number;\n\t/** Number of item-level catalogs */\n\titemCatalogs: number;\n\t/** Catalog types available */\n\tavailableTypes: Set<CatalogType>;\n\t/** Languages available */\n\tavailableLanguages: Set<string>;\n}\n\n/**\n * Accessibility Catalog Resolver Service\n *\n * Manages QTI 3.0 accessibility catalogs at both assessment and item levels.\n * Provides lookup and resolution services for alternative content representations.\n *\n * @example\n * ```typescript\n * // Initialize with assessment-level catalogs\n * const resolver = new AccessibilityCatalogResolver(assessment.accessibilityCatalogs);\n *\n * // Add item-level catalogs when rendering item\n * resolver.addItemCatalogs(item.accessibilityCatalogs);\n *\n * // Resolve catalog by identifier\n * const spokenContent = resolver.getAlternative('intro-passage', {\n * type: 'spoken',\n * language: 'en-US'\n * });\n *\n * // Check if catalog exists\n * if (resolver.hasCatalog('math-problem-1')) {\n * const braille = resolver.getAlternative('math-problem-1', { type: 'braille' });\n * }\n * ```\n */\nexport class AccessibilityCatalogResolver {\n\tprivate assessmentCatalogs: Map<string, AccessibilityCatalog> = new Map();\n\tprivate itemCatalogs: Map<string, AccessibilityCatalog> = new Map();\n\tprivate scopedCatalogs = new Map<\n\t\tstring,\n\t\t{\n\t\t\tcontext: CatalogOwnerContext;\n\t\t\tcatalogs: Map<string, AccessibilityCatalog>;\n\t\t}\n\t>();\n\t// Matches the language the extractor tags cards with and the TTSService\n\t// passes on lookup (\"en-US\"), so the default-language fallback rung in\n\t// findMatchingCard is actually reachable for the common case.\n\tprivate defaultLanguage: string = \"en-US\";\n\t// Egress sanitization cache. Catalogs are sanitized at registration, but that\n\t// is a no-op when indexing runs without a DOM (SSR). Sanitizing again as\n\t// spoken content leaves the resolver guarantees no raw author SSML reaches a\n\t// provider regardless of where indexing happened; the cache keeps it to one\n\t// pass per unique string (sanitizeSsmlString is idempotent).\n\tprivate sanitizedSpokenCache = new Map<string, string>();\n\n\tconstructor(\n\t\tassessmentCatalogs?: AccessibilityCatalog[],\n\t\tdefaultLanguage: string = \"en-US\",\n\t) {\n\t\tthis.defaultLanguage = defaultLanguage;\n\t\tthis.indexCatalogs(assessmentCatalogs ?? [], \"assessment\");\n\t}\n\n\t/**\n\t * Set the default language for fallback resolution\n\t */\n\tsetDefaultLanguage(language: string): void {\n\t\tthis.defaultLanguage = language;\n\t}\n\n\t/**\n\t * Get the default language\n\t */\n\tgetDefaultLanguage(): string {\n\t\treturn this.defaultLanguage;\n\t}\n\n\t/**\n\t * Index catalogs into the appropriate map\n\t */\n\tprivate indexCatalogs(\n\t\tcatalogs: AccessibilityCatalog[],\n\t\tsource: \"assessment\" | \"item\",\n\t): void {\n\t\tconst targetMap =\n\t\t\tsource === \"assessment\" ? this.assessmentCatalogs : this.itemCatalogs;\n\n\t\tfor (const catalog of this.sanitizeCatalogs(catalogs)) {\n\t\t\tif (targetMap.has(catalog.identifier)) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[AccessibilityCatalogResolver] Duplicate ${source} catalog \"${catalog.identifier}\" ignored`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttargetMap.set(catalog.identifier, catalog);\n\t\t}\n\t}\n\n\tregisterCatalogs(\n\t\tcontext: CatalogOwnerContext,\n\t\tcatalogs?: AccessibilityCatalog[],\n\t): () => void {\n\t\tif (!catalogs || catalogs.length === 0) return () => {};\n\t\tconst key = this.getOwnerKey(context);\n\t\tconst existing = this.scopedCatalogs.get(key);\n\t\tconst scoped =\n\t\t\texisting?.catalogs ?? new Map<string, AccessibilityCatalog>();\n\t\tconst insertedIds: string[] = [];\n\t\tfor (const catalog of this.sanitizeCatalogs(catalogs)) {\n\t\t\tif (scoped.has(catalog.identifier)) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[AccessibilityCatalogResolver] Duplicate scoped catalog \"${catalog.identifier}\" ignored for ${key}`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tscoped.set(catalog.identifier, catalog);\n\t\t\tinsertedIds.push(catalog.identifier);\n\t\t}\n\t\tif (!existing) {\n\t\t\tthis.scopedCatalogs.set(key, {\n\t\t\t\tcontext: { ...context },\n\t\t\t\tcatalogs: scoped,\n\t\t\t});\n\t\t}\n\t\treturn () => {\n\t\t\tconst current = this.scopedCatalogs.get(key);\n\t\t\tif (current?.catalogs !== scoped) return;\n\t\t\tfor (const insertedId of insertedIds) {\n\t\t\t\tcurrent.catalogs.delete(insertedId);\n\t\t\t}\n\t\t\tif (current.catalogs.size === 0) {\n\t\t\t\tthis.scopedCatalogs.delete(key);\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Add item-level catalogs (called when rendering a new item)\n\t */\n\taddItemCatalogs(catalogs?: AccessibilityCatalog[]): void {\n\t\tif (!catalogs || catalogs.length === 0) return;\n\t\tthis.indexCatalogs(catalogs, \"item\");\n\t}\n\n\t/**\n\t * Clear item-level catalogs (called when leaving an item)\n\t */\n\tclearItemCatalogs(): void {\n\t\tthis.itemCatalogs.clear();\n\t}\n\n\t/**\n\t * Check if a catalog exists (checks both assessment and item levels)\n\t */\n\thasCatalog(catalogId: string): boolean {\n\t\treturn (\n\t\t\tthis.itemCatalogs.has(catalogId) ||\n\t\t\tthis.assessmentCatalogs.has(catalogId) ||\n\t\t\tArray.from(this.scopedCatalogs.values()).some((owner) =>\n\t\t\t\towner.catalogs.has(catalogId),\n\t\t\t)\n\t\t);\n\t}\n\n\t/**\n\t * Get alternative content for a catalog identifier\n\t *\n\t * Priority: Item-level catalogs take precedence over assessment-level\n\t */\n\tgetAlternative(\n\t\tcatalogId: string,\n\t\toptions: CatalogLookupOptions,\n\t): ResolvedCatalog | null {\n\t\tconst scopedCatalog = options.context\n\t\t\t? this.scopedCatalogs\n\t\t\t\t\t.get(this.getOwnerKey(options.context))\n\t\t\t\t\t?.catalogs.get(catalogId)\n\t\t\t: null;\n\t\tif (scopedCatalog) {\n\t\t\tconst card = this.findMatchingCard(scopedCatalog, options);\n\t\t\tif (card) return this.resolveCard(catalogId, card, \"item\");\n\t\t}\n\t\tif (options.context) {\n\t\t\tconst candidates = this.findScopedCandidates(catalogId, options.context);\n\t\t\tif (candidates.length === 1) {\n\t\t\t\tconst card = this.findMatchingCard(candidates[0], options);\n\t\t\t\tif (card) return this.resolveCard(catalogId, card, \"item\");\n\t\t\t} else if (candidates.length > 1) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[AccessibilityCatalogResolver] Ambiguous scoped catalog \"${catalogId}\" for owner context`,\n\t\t\t\t\toptions.context,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// Check item-level first (higher precedence)\n\t\tconst itemCatalog = this.itemCatalogs.get(catalogId);\n\t\tif (itemCatalog) {\n\t\t\tconst card = this.findMatchingCard(itemCatalog, options);\n\t\t\tif (card) return this.resolveCard(catalogId, card, \"item\");\n\t\t}\n\n\t\t// Fallback to assessment-level\n\t\tconst assessmentCatalog = this.assessmentCatalogs.get(catalogId);\n\t\tif (assessmentCatalog) {\n\t\t\tconst card = this.findMatchingCard(assessmentCatalog, options);\n\t\t\tif (card) return this.resolveCard(catalogId, card, \"assessment\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate resolveCard(\n\t\tcatalogId: string,\n\t\tcard: CatalogCard,\n\t\tsource: ResolvedCatalog[\"source\"],\n\t): ResolvedCatalog {\n\t\treturn {\n\t\t\tcatalogId,\n\t\t\ttype: card.catalog,\n\t\t\tlanguage: card.language,\n\t\t\tcontent:\n\t\t\t\tcard.catalog === \"spoken\"\n\t\t\t\t\t? this.ensureSpokenSanitized(card.content)\n\t\t\t\t\t: card.content,\n\t\t\tsource,\n\t\t};\n\t}\n\n\tprivate ensureSpokenSanitized(content: string): string {\n\t\tconst cached = this.sanitizedSpokenCache.get(content);\n\t\tif (cached !== undefined) return cached;\n\t\tconst sanitized = sanitizeSsmlString(content);\n\t\tthis.sanitizedSpokenCache.set(content, sanitized);\n\t\treturn sanitized;\n\t}\n\n\tprivate getOwnerKey(context: CatalogOwnerContext): string {\n\t\treturn [\n\t\t\tcontext.ownerKind,\n\t\t\tcontext.assessmentId || \"\",\n\t\t\tcontext.sectionId || \"\",\n\t\t\tcontext.canonicalItemId || \"\",\n\t\t\tcontext.itemId || \"\",\n\t\t\tcontext.passageId || \"\",\n\t\t\tcontext.modelId || \"\",\n\t\t].join(\"|\");\n\t}\n\n\t// Flattened [id, catalog] pairs for every context-scoped owner. The primary\n\t// runtime path registers catalogs as scoped, so enumeration APIs must include\n\t// these or they silently under-report what TTS content exists.\n\tprivate scopedCatalogEntries(): Array<[string, AccessibilityCatalog]> {\n\t\tconst entries: Array<[string, AccessibilityCatalog]> = [];\n\t\tfor (const owner of this.scopedCatalogs.values()) {\n\t\t\tfor (const entry of owner.catalogs.entries()) {\n\t\t\t\tentries.push(entry);\n\t\t\t}\n\t\t}\n\t\treturn entries;\n\t}\n\n\tprivate findScopedCandidates(\n\t\tcatalogId: string,\n\t\tcontext: CatalogOwnerContext,\n\t): AccessibilityCatalog[] {\n\t\treturn Array.from(this.scopedCatalogs.values())\n\t\t\t.filter((owner) => this.isCompatibleOwnerContext(owner.context, context))\n\t\t\t.map((owner) => owner.catalogs.get(catalogId))\n\t\t\t.filter((catalog): catalog is AccessibilityCatalog => Boolean(catalog));\n\t}\n\n\tprivate isCompatibleOwnerContext(\n\t\tregistered: CatalogOwnerContext,\n\t\tlookup: CatalogOwnerContext,\n\t): boolean {\n\t\tif (registered.ownerKind !== lookup.ownerKind) return false;\n\t\tif (\n\t\t\tlookup.assessmentId &&\n\t\t\tregistered.assessmentId !== lookup.assessmentId\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\tif (lookup.sectionId && registered.sectionId !== lookup.sectionId) {\n\t\t\treturn false;\n\t\t}\n\t\tif (\n\t\t\tlookup.canonicalItemId &&\n\t\t\tregistered.canonicalItemId !== lookup.canonicalItemId\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\tif (lookup.itemId && registered.itemId !== lookup.itemId) {\n\t\t\treturn false;\n\t\t}\n\t\tif (lookup.passageId && registered.passageId !== lookup.passageId) {\n\t\t\treturn false;\n\t\t}\n\t\tif (lookup.modelId && registered.modelId !== lookup.modelId) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate sanitizeCatalogs(\n\t\tcatalogs: AccessibilityCatalog[],\n\t): AccessibilityCatalog[] {\n\t\treturn catalogs.map((catalog) => ({\n\t\t\t...catalog,\n\t\t\tcards: catalog.cards.map((card) => ({\n\t\t\t\t...card,\n\t\t\t\tcontent:\n\t\t\t\t\tcard.catalog === \"spoken\"\n\t\t\t\t\t\t? sanitizeSsmlString(card.content)\n\t\t\t\t\t\t: card.content,\n\t\t\t})),\n\t\t}));\n\t}\n\n\t/**\n\t * Find a matching catalog card based on lookup options\n\t */\n\tprivate findMatchingCard(\n\t\tcatalog: AccessibilityCatalog,\n\t\toptions: CatalogLookupOptions,\n\t): CatalogCard | null {\n\t\tconst { type, language, useFallback = true } = options;\n\n\t\t// Try exact match (type + language)\n\t\tif (language) {\n\t\t\tconst exactMatch = catalog.cards.find(\n\t\t\t\t(card) => card.catalog === type && card.language === language,\n\t\t\t);\n\t\t\tif (exactMatch) return exactMatch;\n\t\t}\n\n\t\t// Try type match with default language (if fallback enabled)\n\t\tif (useFallback) {\n\t\t\tconst defaultMatch = catalog.cards.find(\n\t\t\t\t(card) =>\n\t\t\t\t\tcard.catalog === type && card.language === this.defaultLanguage,\n\t\t\t);\n\t\t\tif (defaultMatch) return defaultMatch;\n\n\t\t\t// Try type match without language constraint\n\t\t\tconst typeMatch = catalog.cards.find((card) => card.catalog === type);\n\t\t\tif (typeMatch) return typeMatch;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get all available alternatives for a catalog identifier\n\t */\n\tgetAllAlternatives(catalogId: string): ResolvedCatalog[] {\n\t\tconst results: ResolvedCatalog[] = [];\n\n\t\t// Add item-level alternatives\n\t\tconst itemCatalog = this.itemCatalogs.get(catalogId);\n\t\tif (itemCatalog) {\n\t\t\tfor (const card of itemCatalog.cards) {\n\t\t\t\tresults.push({\n\t\t\t\t\tcatalogId,\n\t\t\t\t\ttype: card.catalog,\n\t\t\t\t\tlanguage: card.language,\n\t\t\t\t\tcontent: card.content,\n\t\t\t\t\tsource: \"item\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Add assessment-level alternatives (if not already provided by item)\n\t\tconst assessmentCatalog = this.assessmentCatalogs.get(catalogId);\n\t\tif (assessmentCatalog) {\n\t\t\tfor (const card of assessmentCatalog.cards) {\n\t\t\t\t// Only add if not already provided by item catalog\n\t\t\t\tconst exists = results.some(\n\t\t\t\t\t(r) => r.type === card.catalog && r.language === card.language,\n\t\t\t\t);\n\t\t\t\tif (!exists) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tcatalogId,\n\t\t\t\t\t\ttype: card.catalog,\n\t\t\t\t\t\tlanguage: card.language,\n\t\t\t\t\t\tcontent: card.content,\n\t\t\t\t\t\tsource: \"assessment\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add scoped (context-registered) alternatives. These resolve as \"item\"\n\t\t// in getAlternative, so report them the same way here.\n\t\tfor (const [id, catalog] of this.scopedCatalogEntries()) {\n\t\t\tif (id !== catalogId) continue;\n\t\t\tfor (const card of catalog.cards) {\n\t\t\t\tconst exists = results.some(\n\t\t\t\t\t(r) => r.type === card.catalog && r.language === card.language,\n\t\t\t\t);\n\t\t\t\tif (!exists) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tcatalogId,\n\t\t\t\t\t\ttype: card.catalog,\n\t\t\t\t\t\tlanguage: card.language,\n\t\t\t\t\t\tcontent: card.content,\n\t\t\t\t\t\tsource: \"item\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Get all catalog identifiers available (both assessment and item)\n\t */\n\tgetAllCatalogIds(): string[] {\n\t\tconst scopedIds = this.scopedCatalogEntries().map(([id]) => id);\n\t\treturn Array.from(\n\t\t\tnew Set([\n\t\t\t\t...this.itemCatalogs.keys(),\n\t\t\t\t...this.assessmentCatalogs.keys(),\n\t\t\t\t...scopedIds,\n\t\t\t]),\n\t\t);\n\t}\n\n\t/**\n\t * Get statistics about available catalogs\n\t */\n\tgetStatistics(): CatalogStatistics {\n\t\tconst allTypes = new Set<CatalogType>();\n\t\tconst allLanguages = new Set<string>();\n\n\t\t// Collect from assessment catalogs\n\t\tfor (const catalog of this.assessmentCatalogs.values()) {\n\t\t\tfor (const card of catalog.cards) {\n\t\t\t\tallTypes.add(card.catalog);\n\t\t\t\tif (card.language) allLanguages.add(card.language);\n\t\t\t}\n\t\t}\n\n\t\t// Collect from item catalogs\n\t\tfor (const catalog of this.itemCatalogs.values()) {\n\t\t\tfor (const card of catalog.cards) {\n\t\t\t\tallTypes.add(card.catalog);\n\t\t\t\tif (card.language) allLanguages.add(card.language);\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\ttotalCatalogs:\n\t\t\t\tthis.assessmentCatalogs.size +\n\t\t\t\tthis.itemCatalogs.size +\n\t\t\t\tArray.from(this.scopedCatalogs.values()).reduce(\n\t\t\t\t\t(total, owner) => total + owner.catalogs.size,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\tassessmentCatalogs: this.assessmentCatalogs.size,\n\t\t\titemCatalogs:\n\t\t\t\tthis.itemCatalogs.size +\n\t\t\t\tArray.from(this.scopedCatalogs.values()).reduce(\n\t\t\t\t\t(total, owner) => total + owner.catalogs.size,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\tavailableTypes: allTypes,\n\t\t\tavailableLanguages: allLanguages,\n\t\t};\n\t}\n\n\t/**\n\t * Check if a specific catalog type is available for a given catalog ID\n\t */\n\thasAlternativeType(catalogId: string, type: CatalogType): boolean {\n\t\tconst alternatives = this.getAllAlternatives(catalogId);\n\t\treturn alternatives.some((alt) => alt.type === type);\n\t}\n\n\t/**\n\t * Get all catalog IDs that have a specific type of alternative\n\t */\n\tgetCatalogsByType(type: CatalogType): string[] {\n\t\tconst catalogIds = new Set<string>();\n\n\t\tfor (const [id, catalog] of this.assessmentCatalogs.entries()) {\n\t\t\tif (catalog.cards.some((card) => card.catalog === type)) {\n\t\t\t\tcatalogIds.add(id);\n\t\t\t}\n\t\t}\n\n\t\tfor (const [id, catalog] of this.itemCatalogs.entries()) {\n\t\t\tif (catalog.cards.some((card) => card.catalog === type)) {\n\t\t\t\tcatalogIds.add(id);\n\t\t\t}\n\t\t}\n\n\t\tfor (const [id, catalog] of this.scopedCatalogEntries()) {\n\t\t\tif (catalog.cards.some((card) => card.catalog === type)) {\n\t\t\t\tcatalogIds.add(id);\n\t\t\t}\n\t\t}\n\n\t\treturn Array.from(catalogIds);\n\t}\n\n\t/**\n\t * Reset all catalogs (both assessment and item)\n\t */\n\treset(): void {\n\t\tthis.assessmentCatalogs.clear();\n\t\tthis.itemCatalogs.clear();\n\t\tthis.scopedCatalogs.clear();\n\t\tthis.sanitizedSpokenCache.clear();\n\t}\n\n\t/**\n\t * Destroy and cleanup\n\t */\n\tdestroy(): void {\n\t\tthis.reset();\n\t}\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ContextVariableStore.js","sourceRoot":"","sources":["../../src/services/ContextVariableStore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Context Variable Store\n *\n * Standalone service for managing QTI 3.0 context variables.\n * Context variables are global assessment-level variables that can be\n * shared across items.\n *\n * Can be used independently by any assessment player without requiring\n * the full AssessmentPlayer.\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type { ContextDeclaration } from \"@pie-players/pie-players-shared/types\";\n\n/**\n * Standalone service for managing QTI 3.0 context variables.\n *\n * Context variables are global assessment-level variables that can be\n * shared across items. Can be used independently by any assessment player.\n *\n * @example\n * ```typescript\n * // Initialize with declarations\n * const store = new ContextVariableStore(declarations);\n *\n * // Get/set variables\n * const seed = store.get('RANDOM_SEED');\n * store.set('DIFFICULTY_LEVEL', 'hard');\n *\n * // Pass to items\n * const context = store.toObject();\n * await renderItem(item, session, context);\n *\n * // Persist in session\n * session.contextVariables = store.toObject();\n * ```\n */\nexport class ContextVariableStore {\n\tprivate variables: Map<string, any> = new Map();\n\tprivate declarations: Map<string, ContextDeclaration> = new Map();\n\n\t/**\n\t * Initialize store with context declarations from assessment\n\t *\n\t * @param declarations QTI 3.0 context declarations\n\t */\n\tconstructor(declarations?: ContextDeclaration[]) {\n\t\tif (declarations) {\n\t\t\tthis.initialize(declarations);\n\t\t}\n\t}\n\n\t/**\n\t * Initialize context variables from declarations\n\t *\n\t * Sets each variable to its defaultValue or type-appropriate default.\n\t */\n\tinitialize(declarations: ContextDeclaration[]): void {\n\t\tthis.declarations.clear();\n\t\tthis.variables.clear();\n\n\t\tfor (const decl of declarations) {\n\t\t\tthis.declarations.set(decl.identifier, decl);\n\n\t\t\tconst value =\n\t\t\t\tdecl.defaultValue ??\n\t\t\t\tthis.getDefaultValueForType(decl.baseType, decl.cardinality);\n\n\t\t\tthis.variables.set(decl.identifier, value);\n\t\t}\n\n\t\tconsole.debug(\n\t\t\t`ContextVariableStore: Initialized ${declarations.length} context variables:`,\n\t\t\tArray.from(this.variables.keys()),\n\t\t);\n\t}\n\n\t/**\n\t * Get context variable value\n\t *\n\t * @param identifier Variable identifier\n\t * @returns Variable value or undefined if not found\n\t */\n\tget(identifier: string): any {\n\t\treturn this.variables.get(identifier);\n\t}\n\n\t/**\n\t * Set context variable value\n\t *\n\t * @param identifier Variable identifier\n\t * @param value New value\n\t */\n\tset(identifier: string, value: any): void {\n\t\tif (!this.declarations.has(identifier)) {\n\t\t\tconsole.warn(\n\t\t\t\t`ContextVariableStore: Setting undeclared context variable: ${identifier}`,\n\t\t\t\t\"Consider declaring it in contextDeclarations\",\n\t\t\t);\n\t\t}\n\n\t\t// Type validation (optional but recommended)\n\t\tconst decl = this.declarations.get(identifier);\n\t\tif (decl) {\n\t\t\tthis.validateValue(decl, value);\n\t\t}\n\n\t\tthis.variables.set(identifier, value);\n\t}\n\n\t/**\n\t * Check if variable exists\n\t */\n\thas(identifier: string): boolean {\n\t\treturn this.variables.has(identifier);\n\t}\n\n\t/**\n\t * Get all variable names\n\t */\n\tgetIdentifiers(): string[] {\n\t\treturn Array.from(this.variables.keys());\n\t}\n\n\t/**\n\t * Get all variables as object (for passing to PIE elements)\n\t *\n\t * @returns Plain object with all context variables\n\t */\n\ttoObject(): Record<string, any> {\n\t\treturn Object.fromEntries(this.variables);\n\t}\n\n\t/**\n\t * Load state from serialized object (for session restore)\n\t *\n\t * @param state Object containing context variable values\n\t */\n\tfromObject(state: Record<string, any>): void {\n\t\tfor (const [key, value] of Object.entries(state)) {\n\t\t\tthis.variables.set(key, value);\n\t\t}\n\t}\n\n\t/**\n\t * Clear all variables (reset to defaults)\n\t */\n\treset(): void {\n\t\tfor (const [identifier, decl] of this.declarations) {\n\t\t\tconst value =\n\t\t\t\tdecl.defaultValue ??\n\t\t\t\tthis.getDefaultValueForType(decl.baseType, decl.cardinality);\n\t\t\tthis.variables.set(identifier, value);\n\t\t}\n\n\t\tconsole.debug(\"ContextVariableStore: Reset to default values\");\n\t}\n\n\t/**\n\t * Get declaration for variable\n\t *\n\t * @param identifier Variable identifier\n\t * @returns Declaration or undefined if not found\n\t */\n\tgetDeclaration(identifier: string): ContextDeclaration | undefined {\n\t\treturn this.declarations.get(identifier);\n\t}\n\n\t/**\n\t * Get all declarations\n\t */\n\tgetDeclarations(): ContextDeclaration[] {\n\t\treturn Array.from(this.declarations.values());\n\t}\n\n\t/**\n\t * Get default value for QTI base type\n\t *\n\t * @private\n\t */\n\tprivate getDefaultValueForType(baseType: string, cardinality: string): any {\n\t\tif (cardinality === \"multiple\" || cardinality === \"ordered\") {\n\t\t\treturn [];\n\t\t}\n\n\t\tif (cardinality === \"record\") {\n\t\t\treturn {};\n\t\t}\n\n\t\t// Single cardinality defaults\n\t\tswitch (baseType) {\n\t\t\tcase \"boolean\":\n\t\t\t\treturn false;\n\t\t\tcase \"integer\":\n\t\t\t\treturn 0;\n\t\t\tcase \"float\":\n\t\t\t\treturn 0.0;\n\t\t\tcase \"string\":\n\t\t\tcase \"identifier\":\n\t\t\tcase \"uri\":\n\t\t\t\treturn \"\";\n\t\t\tcase \"point\":\n\t\t\t\treturn [0, 0];\n\t\t\tcase \"pair\":\n\t\t\t\treturn [\"\", \"\"];\n\t\t\tcase \"directedPair\":\n\t\t\t\treturn [\"\", \"\"];\n\t\t\tcase \"duration\":\n\t\t\t\treturn 0;\n\t\t\tcase \"file\":\n\t\t\t\treturn null;\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Validate value matches declared type (basic validation)\n\t *\n\t * @private\n\t */\n\tprivate validateValue(decl: ContextDeclaration, value: any): void {\n\t\t// Basic type checking - can be extended\n\t\tif (decl.baseType === \"integer\" && !Number.isInteger(value)) {\n\t\t\tconsole.warn(\n\t\t\t\t`ContextVariableStore: Variable ${decl.identifier} expects integer, got:`,\n\t\t\t\ttypeof value,\n\t\t\t);\n\t\t}\n\n\t\tif (decl.baseType === \"float\" && typeof value !== \"number\") {\n\t\t\tconsole.warn(\n\t\t\t\t`ContextVariableStore: Variable ${decl.identifier} expects float, got:`,\n\t\t\t\ttypeof value,\n\t\t\t);\n\t\t}\n\n\t\tif (decl.baseType === \"boolean\" && typeof value !== \"boolean\") {\n\t\t\tconsole.warn(\n\t\t\t\t`ContextVariableStore: Variable ${decl.identifier} expects boolean, got:`,\n\t\t\t\ttypeof value,\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\t(decl.cardinality === \"multiple\" || decl.cardinality === \"ordered\") &&\n\t\t\t!Array.isArray(value)\n\t\t) {\n\t\t\tconsole.warn(\n\t\t\t\t`ContextVariableStore: Variable ${decl.identifier} expects array (cardinality: ${decl.cardinality}), got:`,\n\t\t\t\ttypeof value,\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElementToolStateStore.js","sourceRoot":"","sources":["../../src/services/ElementToolStateStore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ElementToolStateStore - Manages element-level ephemeral tool state\n *\n * Provides centralized storage for UI tool state (answer eliminations, flags, etc.)\n * that should NOT be sent to the server for scoring. Each element is uniquely identified\n * across the entire assessment using composite keys.\n *\n * Key features:\n * - Element-level granularity (not item-level)\n * - Composite keys for global uniqueness: `assessmentId:sectionId:itemId:elementId`\n * - Cross-section state persistence\n * - Reactive subscriptions\n * - Optional persistence callback integration\n * - Generic solution for any tool\n */\n\nexport interface ElementIdComponents {\n\tassessmentId: string;\n\tsectionId: string;\n\titemId: string;\n\telementId: string;\n}\n\nexport class ElementToolStateStore {\n\tprivate elementStates = new Map<string, Map<string, any>>();\n\tprivate listeners = new Set<(state: Map<string, Map<string, any>>) => void>();\n\tprivate onStateChange:\n\t\t| ((state: Record<string, Record<string, any>>) => void)\n\t\t| null = null;\n\n\t/**\n\t * Generates a globally unique element ID by combining assessment, section, item, and element IDs.\n\t * Format: `${assessmentId}:${sectionId}:${itemId}:${elementId}`\n\t *\n\t * @example\n\t * getGlobalElementId('demo-assessment', 'section-1', 'q1', 'mc1')\n\t * // Returns: \"demo-assessment:section-1:q1:mc1\"\n\t */\n\tgetGlobalElementId(\n\t\tassessmentId: string,\n\t\tsectionId: string,\n\t\titemId: string,\n\t\telementId: string,\n\t): string {\n\t\treturn `${assessmentId}:${sectionId}:${itemId}:${elementId}`;\n\t}\n\n\t/**\n\t * Parses a global element ID into its component parts.\n\t *\n\t * @example\n\t * parseGlobalElementId('demo-assessment:section-1:q1:mc1')\n\t * // Returns: { assessmentId: 'demo-assessment', sectionId: 'section-1', itemId: 'q1', elementId: 'mc1' }\n\t */\n\tparseGlobalElementId(globalElementId: string): ElementIdComponents | null {\n\t\tconst parts = globalElementId.split(\":\");\n\t\tif (parts.length !== 4) return null;\n\t\treturn {\n\t\t\tassessmentId: parts[0],\n\t\t\tsectionId: parts[1],\n\t\t\titemId: parts[2],\n\t\t\telementId: parts[3],\n\t\t};\n\t}\n\n\t/**\n\t * Sets tool state for a specific element.\n\t * Notifies all subscribers and triggers persistence callback.\n\t *\n\t * @param globalElementId Composite key identifying the element\n\t * @param toolId Tool identifier (e.g., 'answerEliminator', 'flagging')\n\t * @param state Tool-specific state object\n\t */\n\tsetState(globalElementId: string, toolId: string, state: any): void {\n\t\tlet elementState = this.elementStates.get(globalElementId);\n\t\tif (!elementState) {\n\t\t\telementState = new Map();\n\t\t\tthis.elementStates.set(globalElementId, elementState);\n\t\t}\n\t\telementState.set(toolId, state);\n\t\tthis._notifyListeners();\n\t\tthis._notifyStateChange();\n\t}\n\n\t/**\n\t * Gets tool state for a specific element.\n\t *\n\t * @param globalElementId Composite key identifying the element\n\t * @param toolId Tool identifier\n\t * @returns Tool state or undefined if not found\n\t */\n\tgetState(globalElementId: string, toolId: string): any | undefined {\n\t\treturn this.elementStates.get(globalElementId)?.get(toolId);\n\t}\n\n\t/**\n\t * Gets all tool states for a specific element.\n\t *\n\t * @param globalElementId Composite key identifying the element\n\t * @returns Object with tool states keyed by toolId\n\t */\n\tgetElementState(globalElementId: string): Record<string, any> {\n\t\tconst elementState = this.elementStates.get(globalElementId);\n\t\tif (!elementState) return {};\n\t\treturn Object.fromEntries(elementState.entries());\n\t}\n\n\t/**\n\t * Gets all tool states for all elements.\n\t * Used for persistence and debugging.\n\t *\n\t * @returns Nested object: { globalElementId: { toolId: state } }\n\t */\n\tgetAllState(): Record<string, Record<string, any>> {\n\t\tconst result: Record<string, Record<string, any>> = {};\n\t\tfor (const [globalElementId, toolStates] of this.elementStates.entries()) {\n\t\t\tresult[globalElementId] = Object.fromEntries(toolStates.entries());\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Subscribes to state changes.\n\t * Callback is invoked whenever any element's tool state changes.\n\t *\n\t * @param callback Function to call on state changes\n\t * @returns Unsubscribe function\n\t */\n\tsubscribe(\n\t\tcallback: (state: Map<string, Map<string, any>>) => void,\n\t): () => void {\n\t\tthis.listeners.add(callback);\n\t\treturn () => this.listeners.delete(callback);\n\t}\n\n\tprivate _notifyListeners(): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(this.elementStates);\n\t\t}\n\t}\n\n\t/**\n\t * Sets a callback to be invoked on state changes for persistence integration.\n\t * Used by demo/app layer to persist tool state to localStorage or server.\n\t *\n\t * @param callback Function to call with serialized state on changes\n\t */\n\tsetOnStateChange(\n\t\tcallback: (state: Record<string, Record<string, any>>) => void,\n\t): void {\n\t\tthis.onStateChange = callback;\n\t}\n\n\tprivate _notifyStateChange(): void {\n\t\tif (this.onStateChange) {\n\t\t\tthis.onStateChange(this.getAllState());\n\t\t}\n\t}\n\n\t/**\n\t * Loads tool state from serialized format.\n\t * Used to restore state from localStorage or server.\n\t *\n\t * @param state Serialized state object\n\t */\n\tloadState(state: Record<string, Record<string, any>>): void {\n\t\tthis.elementStates.clear();\n\t\tfor (const [globalElementId, toolStates] of Object.entries(state)) {\n\t\t\tconst elementState = new Map(Object.entries(toolStates));\n\t\t\tthis.elementStates.set(globalElementId, elementState);\n\t\t}\n\t\tthis._notifyListeners();\n\t}\n\n\t/**\n\t * Clears all tool state for a specific element.\n\t *\n\t * @param globalElementId Composite key identifying the element\n\t */\n\tclearElement(globalElementId: string): void {\n\t\tthis.elementStates.delete(globalElementId);\n\t\tthis._notifyListeners();\n\t\tthis._notifyStateChange();\n\t}\n\n\t/**\n\t * Clears state for a specific tool across all elements.\n\t * Useful when disabling a tool globally.\n\t *\n\t * @param toolId Tool identifier\n\t */\n\tclearTool(toolId: string): void {\n\t\tfor (const elementState of this.elementStates.values()) {\n\t\t\telementState.delete(toolId);\n\t\t}\n\t\tthis._notifyListeners();\n\t\tthis._notifyStateChange();\n\t}\n\n\t/**\n\t * Clears all tool state for a specific section.\n\t * Useful when unmounting/disposing a section.\n\t *\n\t * @param assessmentId Assessment identifier\n\t * @param sectionId Section identifier\n\t */\n\tclearSection(assessmentId: string, sectionId: string): void {\n\t\tconst prefix = `${assessmentId}:${sectionId}:`;\n\t\tconst keysToDelete: string[] = [];\n\t\tfor (const key of this.elementStates.keys()) {\n\t\t\tif (key.startsWith(prefix)) {\n\t\t\t\tkeysToDelete.push(key);\n\t\t\t}\n\t\t}\n\t\tfor (const key of keysToDelete) {\n\t\t\tthis.elementStates.delete(key);\n\t\t}\n\t\tthis._notifyListeners();\n\t\tthis._notifyStateChange();\n\t}\n\n\t/**\n\t * Clears all tool state.\n\t * Useful when switching between assessments or demos.\n\t */\n\tclearAll(): void {\n\t\tthis.elementStates.clear();\n\t\tthis._notifyListeners();\n\t\tthis._notifyStateChange();\n\t}\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"HighlightCoordinator.js","sourceRoot":"","sources":["../../src/services/HighlightCoordinator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * HighlightCoordinator\n *\n * Manages separate highlight layers for TTS (temporary) and annotations (persistent).\n * Uses CSS Custom Highlight API - zero DOM mutation, preserves accessibility.\n *\n * Features:\n * - Separate layers prevent TTS clearing from removing student annotations\n * - CSS Custom Highlight API (no DOM mutation)\n * - Full accessibility support (screen readers see original text)\n * - Configurable colors and styles\n * - Annotation persistence via RangeSerializer\n *\n * Part of PIE Assessment Toolkit.\n *\n * Browser Support:\n * - Chrome/Edge 105+\n * - Safari 17.2+\n * - Firefox: Behind flag (not recommended for production)\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API\n */\n\nimport type { HighlightCoordinatorApi } from \"./interfaces.js\";\nimport { RangeSerializer, type SerializedRange } from \"./RangeSerializer.js\";\n\n/**\n * Highlight types\n */\nexport enum HighlightType {\n\tTTS_WORD = \"tts-word\",\n\tTTS_SENTENCE = \"tts-sentence\",\n\tANNOTATION = \"annotation\",\n}\n\n/**\n * Highlight colors for annotations\n */\nexport enum HighlightColor {\n\tYELLOW = \"yellow\",\n\tGREEN = \"green\",\n\tBLUE = \"blue\",\n\tPINK = \"pink\",\n\tORANGE = \"orange\",\n\tUNDERLINE = \"underline\",\n}\n\n/**\n * Annotation data\n */\nexport interface Annotation {\n\tid: string;\n\trange: Range;\n\ttype: HighlightType.ANNOTATION;\n\tcolor: HighlightColor;\n\ttimestamp: number;\n}\n\n/**\n * Configuration for HighlightCoordinator\n * Currently empty but allows future extension without breaking changes\n */\nexport type HighlightCoordinatorConfig = Record<string, never>;\n\nexport class HighlightCoordinator implements HighlightCoordinatorApi {\n\tprivate config: HighlightCoordinatorConfig;\n\tprivate ttsWordHighlight: Highlight | null = null;\n\tprivate ttsSentenceHighlight: Highlight | null = null;\n\tprivate ttsWordElementHighlights = new Set<Element>();\n\tprivate ttsSentenceElementHighlights = new Set<Element>();\n\tprivate annotations = new Map<string, Annotation>();\n\t// Shared highlights per color (one Highlight object per color, contains all ranges)\n\tprivate colorHighlights = new Map<HighlightColor, Highlight>();\n\tprivate nextAnnotationId = 1;\n\tprivate supported = false;\n\tprivate rangeSerializer: RangeSerializer;\n\tprivate themeObserver: MutationObserver | null = null;\n\tprivate explicitTTSColorOverride: { color: string; opacity: number } | null =\n\t\tnull;\n\n\tconstructor(config: HighlightCoordinatorConfig = {}) {\n\t\tthis.config = config;\n\t\tthis.rangeSerializer = new RangeSerializer();\n\n\t\t// SSR guard\n\t\tif (typeof CSS === \"undefined\" || !(\"highlights\" in CSS)) {\n\t\t\tconsole.warn(\n\t\t\t\t\"CSS Custom Highlight API not supported (SSR or unsupported browser)\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.supported = true;\n\t\tthis.initializeHighlights();\n\t\tthis.registerStyles();\n\t\tthis.applyAdaptiveTTSStyle();\n\t\tthis.setupThemeObservation();\n\t}\n\n\tprivate setupThemeObservation(): void {\n\t\tif (typeof document === \"undefined\") return;\n\t\tif (typeof MutationObserver === \"undefined\") return;\n\n\t\tconst refresh = () => this.applyAdaptiveTTSStyle();\n\t\tthis.themeObserver = new MutationObserver(refresh);\n\n\t\tthis.themeObserver.observe(document.documentElement, {\n\t\t\tattributes: true,\n\t\t\tattributeFilter: [\"style\", \"data-theme\", \"data-color-scheme\", \"class\"],\n\t\t});\n\n\t\tfor (const host of document.querySelectorAll(\"pie-theme\")) {\n\t\t\tthis.themeObserver.observe(host, {\n\t\t\t\tattributes: true,\n\t\t\t\tattributeFilter: [\n\t\t\t\t\t\"theme\",\n\t\t\t\t\t\"scheme\",\n\t\t\t\t\t\"provider\",\n\t\t\t\t\t\"variables\",\n\t\t\t\t\t\"style\",\n\t\t\t\t\t\"data-theme\",\n\t\t\t\t\t\"data-color-scheme\",\n\t\t\t\t],\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate parseColor(\n\t\tinput: string | null | undefined,\n\t): [number, number, number] | null {\n\t\tif (!input) return null;\n\t\tconst value = input.trim();\n\t\tif (!value) return null;\n\n\t\tconst hexMatch = /^#([a-f\\d]{3}|[a-f\\d]{6})$/i.exec(value);\n\t\tif (hexMatch) {\n\t\t\tconst hex = hexMatch[1];\n\t\t\tif (hex.length === 3) {\n\t\t\t\treturn [\n\t\t\t\t\tparseInt(hex[0] + hex[0], 16),\n\t\t\t\t\tparseInt(hex[1] + hex[1], 16),\n\t\t\t\t\tparseInt(hex[2] + hex[2], 16),\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn [\n\t\t\t\tparseInt(hex.slice(0, 2), 16),\n\t\t\t\tparseInt(hex.slice(2, 4), 16),\n\t\t\t\tparseInt(hex.slice(4, 6), 16),\n\t\t\t];\n\t\t}\n\n\t\tconst rgbMatch = /^rgba?\\((.+)\\)$/i.exec(value);\n\t\tif (rgbMatch) {\n\t\t\tconst normalized = rgbMatch[1].replace(/\\//g, \",\");\n\t\t\tconst parts = normalized\n\t\t\t\t.split(/[,\\s]+/)\n\t\t\t\t.map((part) => part.trim())\n\t\t\t\t.filter(Boolean);\n\t\t\tconst r = Number(parts[0]);\n\t\t\tconst g = Number(parts[1]);\n\t\t\tconst b = Number(parts[2]);\n\t\t\tif (Number.isFinite(r) && Number.isFinite(g) && Number.isFinite(b)) {\n\t\t\t\treturn [r, g, b];\n\t\t\t}\n\t\t}\n\n\t\t// Browser parser fallback (covers formats like oklch()).\n\t\tif (typeof document !== \"undefined\") {\n\t\t\tconst parserEl = document.createElement(\"span\");\n\t\t\tparserEl.style.color = value;\n\t\t\tif (typeof parserEl.style.color === \"string\" && parserEl.style.color) {\n\t\t\t\tdocument.body?.appendChild(parserEl);\n\t\t\t\tconst resolved =\n\t\t\t\t\ttypeof getComputedStyle === \"function\"\n\t\t\t\t\t\t? getComputedStyle(parserEl).color\n\t\t\t\t\t\t: \"\";\n\t\t\t\tparserEl.remove();\n\t\t\t\tconst normalizedResolved = resolved.trim();\n\t\t\t\t// Guard against recursive loops when computed style returns the same\n\t\t\t\t// unresolved function syntax (observed with certain color formats).\n\t\t\t\tif (normalizedResolved && normalizedResolved !== value) {\n\t\t\t\t\treturn this.parseColor(resolved);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate relativeLuminance([r, g, b]: [number, number, number]): number {\n\t\tconst toLinear = (channel: number) => {\n\t\t\tconst n = channel / 255;\n\t\t\treturn n <= 0.03928 ? n / 12.92 : ((n + 0.055) / 1.055) ** 2.4;\n\t\t};\n\t\tconst lr = toLinear(r);\n\t\tconst lg = toLinear(g);\n\t\tconst lb = toLinear(b);\n\t\treturn 0.2126 * lr + 0.7152 * lg + 0.0722 * lb;\n\t}\n\n\tprivate contrastRatio(\n\t\ta: [number, number, number],\n\t\tb: [number, number, number],\n\t): number {\n\t\tconst la = this.relativeLuminance(a);\n\t\tconst lb = this.relativeLuminance(b);\n\t\tconst lighter = Math.max(la, lb);\n\t\tconst darker = Math.min(la, lb);\n\t\treturn (lighter + 0.05) / (darker + 0.05);\n\t}\n\n\tprivate blend(\n\t\tfg: [number, number, number],\n\t\tbg: [number, number, number],\n\t\talpha: number,\n\t): [number, number, number] {\n\t\treturn [\n\t\t\tMath.round(fg[0] * alpha + bg[0] * (1 - alpha)),\n\t\t\tMath.round(fg[1] * alpha + bg[1] * (1 - alpha)),\n\t\t\tMath.round(fg[2] * alpha + bg[2] * (1 - alpha)),\n\t\t];\n\t}\n\n\tprivate resolveAdaptiveTTSStyle(sourceEl?: Element | null): {\n\t\twordHighlight: string;\n\t\tsentenceHighlight: string;\n\t\twordUnderline: string;\n\t\twordShadow: string;\n\t} {\n\t\tconst fallbackMissing: [number, number, number] = [255, 235, 59];\n\t\tconst fallbackText: [number, number, number] = [17, 24, 39];\n\t\tconst fallbackBackground: [number, number, number] = [255, 255, 255];\n\n\t\tconst target =\n\t\t\tsourceEl ||\n\t\t\t(typeof document !== \"undefined\" ? document.documentElement : null);\n\t\tconst computed =\n\t\t\ttarget && typeof getComputedStyle === \"function\"\n\t\t\t\t? getComputedStyle(target)\n\t\t\t\t: null;\n\n\t\tconst background =\n\t\t\tthis.parseColor(\n\t\t\t\tcomputed?.getPropertyValue(\"--pie-background\") ||\n\t\t\t\t\tcomputed?.backgroundColor,\n\t\t\t) || fallbackBackground;\n\t\tconst text =\n\t\t\tthis.parseColor(computed?.getPropertyValue(\"--pie-text\")) || fallbackText;\n\t\tconst accent = this.explicitTTSColorOverride\n\t\t\t? this.parseColor(this.explicitTTSColorOverride.color) || fallbackMissing\n\t\t\t: this.parseColor(computed?.getPropertyValue(\"--pie-missing\")) ||\n\t\t\t\tfallbackMissing;\n\n\t\tconst opacityCandidates = this.explicitTTSColorOverride\n\t\t\t? [Math.max(0.3, Math.min(0.95, this.explicitTTSColorOverride.opacity))]\n\t\t\t: [0.8, 0.72, 0.68, 0.62, 0.56, 0.5];\n\n\t\tlet selectedOpacity = opacityCandidates[opacityCandidates.length - 1];\n\t\tlet bestScore = -Infinity;\n\n\t\tfor (const opacity of opacityCandidates) {\n\t\t\tconst blended = this.blend(accent, background, opacity);\n\t\t\tconst backgroundDelta = this.contrastRatio(blended, background);\n\t\t\tconst textContrast = this.contrastRatio(blended, text);\n\t\t\tconst score = backgroundDelta * 1.2 + textContrast * 0.8;\n\t\t\tif (backgroundDelta >= 1.25 && textContrast >= 2.4) {\n\t\t\t\tselectedOpacity = opacity;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (score > bestScore) {\n\t\t\t\tbestScore = score;\n\t\t\t\tselectedOpacity = opacity;\n\t\t\t}\n\t\t}\n\n\t\tconst sentenceOpacity = Math.max(\n\t\t\t0.24,\n\t\t\tMath.min(0.85, selectedOpacity * 0.55),\n\t\t);\n\t\tconst underlineOpacity = Math.max(\n\t\t\t0.55,\n\t\t\tMath.min(0.95, selectedOpacity + 0.2),\n\t\t);\n\t\tconst shadowOpacity = Math.max(0.22, Math.min(0.6, selectedOpacity * 0.45));\n\n\t\tconst underlineColor = text;\n\t\tconst underlineBlend = this.blend(\n\t\t\tunderlineColor,\n\t\t\tbackground,\n\t\t\tunderlineOpacity,\n\t\t);\n\t\tconst underlineDelta = this.contrastRatio(underlineBlend, background);\n\t\tconst fallbackUnderline: [number, number, number] =\n\t\t\tthis.relativeLuminance(background) > 0.45 ? [0, 0, 0] : [255, 255, 255];\n\t\tconst finalUnderline =\n\t\t\tunderlineDelta >= 1.35 ? underlineColor : fallbackUnderline;\n\n\t\tconst wordHighlight = `rgba(${accent[0]}, ${accent[1]}, ${accent[2]}, ${selectedOpacity})`;\n\t\tconst sentenceHighlight = `rgba(${accent[0]}, ${accent[1]}, ${accent[2]}, ${sentenceOpacity})`;\n\t\tconst wordUnderline = `rgba(${finalUnderline[0]}, ${finalUnderline[1]}, ${finalUnderline[2]}, ${underlineOpacity})`;\n\t\tconst wordShadow = `rgba(${finalUnderline[0]}, ${finalUnderline[1]}, ${finalUnderline[2]}, ${shadowOpacity})`;\n\n\t\treturn {\n\t\t\twordHighlight,\n\t\t\tsentenceHighlight,\n\t\t\twordUnderline,\n\t\t\twordShadow,\n\t\t};\n\t}\n\n\tprivate applyAdaptiveTTSStyle(sourceEl?: Element | null): void {\n\t\tif (typeof document === \"undefined\") return;\n\t\tconst vars = this.resolveAdaptiveTTSStyle(sourceEl);\n\t\tdocument.documentElement.style.setProperty(\n\t\t\t\"--pie-tts-word-highlight\",\n\t\t\tvars.wordHighlight,\n\t\t);\n\t\tdocument.documentElement.style.setProperty(\n\t\t\t\"--pie-tts-sentence-highlight\",\n\t\t\tvars.sentenceHighlight,\n\t\t);\n\t\tdocument.documentElement.style.setProperty(\n\t\t\t\"--pie-tts-line-highlight\",\n\t\t\tvars.sentenceHighlight,\n\t\t);\n\t\tdocument.documentElement.style.setProperty(\n\t\t\t\"--pie-tts-word-underline\",\n\t\t\tvars.wordUnderline,\n\t\t);\n\t\tdocument.documentElement.style.setProperty(\n\t\t\t\"--pie-tts-word-shadow\",\n\t\t\tvars.wordShadow,\n\t\t);\n\t}\n\n\t/**\n\t * Initialize CSS Custom Highlights\n\t */\n\tprivate initializeHighlights(): void {\n\t\tif (!this.supported) return;\n\n\t\t// Create highlight registries for TTS\n\t\tthis.ttsWordHighlight = new Highlight();\n\t\tthis.ttsSentenceHighlight = new Highlight();\n\n\t\t// Register TTS highlights with CSS\n\t\tCSS.highlights.set(\"tts-word\", this.ttsWordHighlight);\n\t\tCSS.highlights.set(\"tts-sentence\", this.ttsSentenceHighlight);\n\n\t\t// Create shared highlights for each annotation color\n\t\tfor (const color of Object.values(HighlightColor)) {\n\t\t\tconst highlight = new Highlight();\n\t\t\tthis.colorHighlights.set(color, highlight);\n\t\t\tCSS.highlights.set(`annotation-${color}`, highlight);\n\t\t}\n\t}\n\n\t/**\n\t * Register CSS styles for highlights\n\t */\n\tprivate registerStyles(): void {\n\t\tif (!this.supported) return;\n\t\tif (typeof document === \"undefined\") return; // SSR guard\n\n\t\t// Check if styles already exist\n\t\tif (document.getElementById(\"pie-highlight-styles\")) return;\n\n\t\tconst style = document.createElement(\"style\");\n\t\tstyle.id = \"pie-highlight-styles\";\n\t\tstyle.textContent = `\n /* TTS highlights - temporary */\n ::highlight(tts-word) {\n background-color: var(--pie-tts-word-highlight, color-mix(in srgb, var(--pie-missing, #ffeb3b) 68%, transparent));\n text-decoration: underline 2px solid var(--pie-tts-word-underline, color-mix(in srgb, var(--pie-text, #111827) 70%, transparent));\n text-underline-offset: 2px;\n text-shadow: 0 0 1px var(--pie-tts-word-shadow, color-mix(in srgb, var(--pie-text, #111827) 35%, transparent));\n color: inherit;\n }\n\n /* tts-sentence registry id: coarse read-along band (visual line boxes in layout) */\n ::highlight(tts-sentence) {\n background-color: var(--pie-tts-line-highlight, var(--pie-tts-sentence-highlight, color-mix(in srgb, var(--pie-missing, #ffeb3b) 38%, transparent)));\n color: inherit;\n }\n\n [data-pie-tts-sentence-element=\"true\"] {\n background-color: var(--pie-tts-line-highlight, var(--pie-tts-sentence-highlight, color-mix(in srgb, var(--pie-missing, #ffeb3b) 38%, transparent)));\n border-radius: 0.12em;\n box-decoration-break: clone;\n -webkit-box-decoration-break: clone;\n }\n\n [data-pie-tts-word-element=\"true\"] {\n background-color: var(--pie-tts-word-highlight, color-mix(in srgb, var(--pie-missing, #ffeb3b) 68%, transparent));\n text-decoration: underline 2px solid var(--pie-tts-word-underline, color-mix(in srgb, var(--pie-text, #111827) 70%, transparent));\n text-underline-offset: 2px;\n text-shadow: 0 0 1px var(--pie-tts-word-shadow, color-mix(in srgb, var(--pie-text, #111827) 35%, transparent));\n border-bottom: 2px solid var(--pie-tts-word-underline, color-mix(in srgb, var(--pie-text, #111827) 70%, transparent));\n padding-bottom: 1px;\n border-radius: 0.12em;\n box-decoration-break: clone;\n -webkit-box-decoration-break: clone;\n }\n\n /* Annotation highlights - persistent */\n ::highlight(annotation-yellow) {\n background-color: var(--pie-annotation-yellow-highlight, rgba(253, 233, 149, 0.5));\n color: inherit;\n }\n\n ::highlight(annotation-green) {\n background-color: var(--pie-annotation-green-highlight, rgba(166, 225, 197, 0.5));\n color: inherit;\n }\n\n ::highlight(annotation-blue) {\n background-color: var(--pie-annotation-blue-highlight, rgba(167, 224, 246, 0.5));\n color: inherit;\n }\n\n ::highlight(annotation-pink) {\n background-color: var(--pie-annotation-pink-highlight, rgba(255, 159, 174, 0.5));\n color: inherit;\n }\n\n ::highlight(annotation-orange) {\n background-color: var(--pie-annotation-orange-highlight, rgba(255, 165, 0, 0.5));\n color: inherit;\n }\n\n ::highlight(annotation-underline) {\n background-color: transparent;\n text-decoration: underline 2px solid var(--pie-primary, #0066cc);\n text-underline-offset: 2px;\n color: inherit;\n }\n\n /* The blocks below cover annotation swatches, print, and reduced motion.\n They deliberately do NOT retune the TTS layers: applyAdaptiveTTSStyle()\n writes the pie-tts custom properties inline on documentElement on every\n paint and on theme change, so a media query that only varies a var()\n fallback for those can never take effect. Annotation colours have no such\n adaptive path -- they are fixed swatches a student chose -- so a media\n query is the only way to adjust them, and each keeps its var() so a host\n override still wins.\n\n The names above are spelled without their leading dashes on purpose.\n check-theme-tokens scans comments, and its token pattern stops at the\n first non-alphanumeric character, so writing the wildcard form reads as\n consumption of a shorter token that is not in the registry and fails the\n check. Spell property names in full, or omit the dashes. */\n\n @media (prefers-color-scheme: dark) {\n ::highlight(annotation-yellow) {\n background-color: var(--pie-annotation-yellow-highlight, rgba(139, 117, 0, 0.6));\n }\n ::highlight(annotation-green) {\n background-color: var(--pie-annotation-green-highlight, rgba(45, 92, 63, 0.6));\n }\n ::highlight(annotation-blue) {\n background-color: var(--pie-annotation-blue-highlight, rgba(0, 102, 170, 0.6));\n }\n ::highlight(annotation-pink) {\n background-color: var(--pie-annotation-pink-highlight, rgba(139, 51, 74, 0.6));\n }\n ::highlight(annotation-orange) {\n background-color: var(--pie-annotation-orange-highlight, rgba(154, 99, 0, 0.6));\n }\n ::highlight(annotation-underline) {\n text-decoration-color: var(--pie-primary, #4da6ff);\n }\n }\n\n /* WCAG 2.2 SC 1.4.11 non-text contrast: a highlight is the only indication\n that text is annotated, so the swatches saturate and the underline\n thickens when the user asks for more contrast.\n\n The value is \"more\", not \"high\". The stylesheet this was recovered from\n used prefers-contrast: high, which is not a valid value for the feature\n -- the keywords are no-preference, more, less, custom. An invalid query\n evaluates to \"not all\", so that block could never have matched in any\n browser even had the file been loaded. Verified with matchMedia under\n emulation: \"high\" stays false where \"more\" flips true. */\n @media (prefers-contrast: more) {\n ::highlight(annotation-yellow) {\n background-color: var(--pie-annotation-yellow-highlight, rgba(255, 255, 0, 0.7));\n }\n ::highlight(annotation-green) {\n background-color: var(--pie-annotation-green-highlight, rgba(0, 255, 127, 0.7));\n }\n ::highlight(annotation-blue) {\n background-color: var(--pie-annotation-blue-highlight, rgba(0, 191, 255, 0.7));\n }\n ::highlight(annotation-pink) {\n background-color: var(--pie-annotation-pink-highlight, rgba(255, 105, 180, 0.7));\n }\n ::highlight(annotation-orange) {\n background-color: var(--pie-annotation-orange-highlight, rgba(255, 165, 0, 0.85));\n }\n ::highlight(annotation-underline) {\n text-decoration-thickness: 3px;\n }\n }\n\n @media print {\n /* TTS highlighting is a transient read-along cue, not content. */\n ::highlight(tts-word),\n ::highlight(tts-sentence),\n [data-pie-tts-word-element=\"true\"],\n [data-pie-tts-sentence-element=\"true\"] {\n background-color: transparent;\n text-decoration: none;\n text-shadow: none;\n border-bottom: none;\n }\n\n /* Annotations are student work and must survive printing, but a fill\n that reads fine on screen can print as an illegible wash, so each\n becomes an underline that keeps its colour coding. */\n ::highlight(annotation-yellow),\n ::highlight(annotation-green),\n ::highlight(annotation-blue),\n ::highlight(annotation-pink),\n ::highlight(annotation-orange) {\n background-color: transparent;\n border-bottom: 2px solid currentColor;\n }\n ::highlight(annotation-yellow) { border-bottom-color: #ffeb3b; }\n ::highlight(annotation-green) { border-bottom-color: #a6e1c5; }\n ::highlight(annotation-blue) { border-bottom-color: #a7e0f6; }\n ::highlight(annotation-pink) { border-bottom-color: #ff9fae; }\n ::highlight(annotation-orange) { border-bottom-color: #ffa500; }\n ::highlight(annotation-underline) {\n text-decoration-color: #000;\n border-bottom: none;\n }\n }\n\n /* text-shadow is a direct property rather than a var(), so unlike the\n other TTS rules this one is not overridden by the adaptive path. */\n @media (prefers-reduced-motion: reduce) {\n ::highlight(tts-word),\n [data-pie-tts-word-element=\"true\"] {\n text-shadow: none;\n }\n }\n `;\n\t\tdocument.head.appendChild(style);\n\t}\n\n\t/**\n\t * Highlight a word for TTS (temporary)\n\t *\n\t * @param textNode Text node containing the word\n\t * @param startOffset Start position in text node\n\t * @param endOffset End position in text node\n\t */\n\thighlightTTSWord(\n\t\ttextNode: Text,\n\t\tstartOffset: number,\n\t\tendOffset: number,\n\t): void {\n\t\tif (!this.ttsWordHighlight) return;\n\t\tthis.applyAdaptiveTTSStyle(textNode.parentElement);\n\n\t\t// Clear previous word highlight\n\t\tthis.clearTTSWord();\n\n\t\t// Create range for word\n\t\tconst range = document.createRange();\n\t\trange.setStart(textNode, startOffset);\n\t\trange.setEnd(textNode, endOffset);\n\n\t\t// Add to highlight\n\t\tthis.ttsWordHighlight.add(range);\n\t}\n\n\t/**\n\t * Highlight a single element as the active TTS word (temporary).\n\t *\n\t * Unlike {@link highlightTTSWord} (which paints a CSS range over a text node)\n\t * this marks the element itself via `data-pie-tts-word-element`. It is used\n\t * for atomic targets that have no direct text node to range over — most\n\t * notably MathJax CHTML tokens (e.g. `<mjx-mi><mjx-c/></mjx-mi>`, whose glyph\n\t * lives in a font-driven pseudo-element a CSS range cannot paint) and\n\t * whole-expression fallbacks.\n\t *\n\t * The supplied element is painted exactly; we deliberately do NOT walk up to\n\t * a containing `<math>` / `<mjx-container>`. That escalation is what made\n\t * MathJax-rendered math highlight as a full block while native MathML tracked\n\t * per-token: a resolved single token must paint as a token, and only a\n\t * genuine expression fallback (where the element already is the container)\n\t * should paint the whole expression.\n\t */\n\thighlightTTSWordElement(element: Element): void {\n\t\tif (!this.ttsWordHighlight) return;\n\t\tthis.applyAdaptiveTTSStyle(element);\n\n\t\t// Clear previous word highlight (CSS range and any element attributes)\n\t\tthis.clearTTSWord();\n\n\t\telement.setAttribute(\"data-pie-tts-word-element\", \"true\");\n\t\tthis.ttsWordElementHighlights.add(element);\n\t}\n\n\t/**\n\t * Highlight the coarse TTS read-along band (temporary).\n\t * Usually one Range per visual line; CSS layer `tts-sentence` for compatibility.\n\t *\n\t * @param ranges Line (or sentence) ranges to paint with `--pie-tts-line-highlight`\n\t */\n\thighlightTTSSentence(ranges: Range[]): void {\n\t\tif (!this.ttsSentenceHighlight) return;\n\t\tconst source = ranges[0]?.startContainer;\n\t\tconst sourceElement =\n\t\t\tsource?.nodeType === Node.ELEMENT_NODE\n\t\t\t\t? (source as Element)\n\t\t\t\t: source?.parentElement;\n\t\tthis.applyAdaptiveTTSStyle(sourceElement);\n\n\t\t// Clear previous sentence highlight\n\t\tthis.clearTTSSentence();\n\n\t\t// Add all ranges\n\t\tfor (const range of ranges) {\n\t\t\tthis.ttsSentenceHighlight.add(range);\n\t\t}\n\t\tthis.highlightTTSElementFallbacks(ranges);\n\t}\n\n\t/**\n\t * Highlight sentence/block elements for TTS (background layer).\n\t *\n\t * @param elements Visible elements to paint with `--pie-tts-line-highlight`\n\t */\n\thighlightTTSSentenceElements(elements: Element[]): void {\n\t\tif (!this.ttsSentenceHighlight) return;\n\t\tthis.applyAdaptiveTTSStyle(elements[0] || null);\n\n\t\tthis.clearTTSSentence();\n\n\t\tfor (const element of elements) {\n\t\t\telement.setAttribute(\"data-pie-tts-sentence-element\", \"true\");\n\t\t\tthis.ttsSentenceElementHighlights.add(element);\n\t\t}\n\t}\n\n\t// The read-along (sentence) band legitimately covers whole equations and\n\t// replaced elements, so it escalates to them.\n\tprivate static readonly SENTENCE_FALLBACK_SELECTOR =\n\t\t\"math, mjx-container, svg, img, canvas, [role='img']\";\n\n\t// The word layer must NOT escalate a multi-node range to the enclosing\n\t// `<math>` / `<mjx-container>`: that is what made an equation flash as a\n\t// whole block whenever a spoken word's visible range crossed several math\n\t// glyph nodes. Math is tracked per token (or held / region) by the highlight\n\t// pipeline instead. Replaced elements (svg/img/canvas) still need the element\n\t// fallback because a CSS range cannot paint them.\n\tprivate static readonly WORD_FALLBACK_SELECTOR =\n\t\t\"svg, img, canvas, [role='img']\";\n\n\tprivate highlightTTSElementFallbacks(ranges: Range[]): void {\n\t\tif (typeof Element === \"undefined\") return;\n\t\tthis.highlightElementFallbacks(\n\t\t\tranges,\n\t\t\t\"data-pie-tts-sentence-element\",\n\t\t\tthis.ttsSentenceElementHighlights,\n\t\t\tHighlightCoordinator.SENTENCE_FALLBACK_SELECTOR,\n\t\t);\n\t}\n\n\tprivate highlightTTSWordElementFallbacks(ranges: Range[]): void {\n\t\tif (typeof Element === \"undefined\") return;\n\t\tthis.highlightElementFallbacks(\n\t\t\tranges,\n\t\t\t\"data-pie-tts-word-element\",\n\t\t\tthis.ttsWordElementHighlights,\n\t\t\tHighlightCoordinator.WORD_FALLBACK_SELECTOR,\n\t\t);\n\t}\n\n\tprivate highlightElementFallbacks(\n\t\tranges: Range[],\n\t\tattributeName: string,\n\t\ttrackedElements: Set<Element>,\n\t\tselector: string,\n\t): void {\n\t\tfor (const range of ranges) {\n\t\t\tconst root =\n\t\t\t\trange.commonAncestorContainer.nodeType === Node.ELEMENT_NODE\n\t\t\t\t\t? (range.commonAncestorContainer as Element)\n\t\t\t\t\t: range.commonAncestorContainer.parentElement;\n\t\t\tif (!root) continue;\n\t\t\tconst candidates: Element[] = [];\n\t\t\tconst rangeStartElement =\n\t\t\t\trange.startContainer.nodeType === Node.ELEMENT_NODE\n\t\t\t\t\t? (range.startContainer as Element)\n\t\t\t\t\t: range.startContainer.parentElement;\n\t\t\tconst rangeEndElement =\n\t\t\t\trange.endContainer.nodeType === Node.ELEMENT_NODE\n\t\t\t\t\t? (range.endContainer as Element)\n\t\t\t\t\t: range.endContainer.parentElement;\n\t\t\tfor (const element of [rangeStartElement, rangeEndElement]) {\n\t\t\t\tlet current: Element | null = element;\n\t\t\t\twhile (current) {\n\t\t\t\t\tif (current.matches?.(selector)) candidates.push(current);\n\t\t\t\t\tcurrent = current.parentElement;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (root.matches?.(selector)) {\n\t\t\t\tcandidates.push(root);\n\t\t\t}\n\t\t\tcandidates.push(...Array.from(root.querySelectorAll(selector)));\n\t\t\tfor (const element of new Set(candidates)) {\n\t\t\t\ttry {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!range.intersectsNode(element) &&\n\t\t\t\t\t\t!element.contains(rangeStartElement) &&\n\t\t\t\t\t\t!element.contains(rangeEndElement)\n\t\t\t\t\t) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telement.setAttribute(attributeName, \"true\");\n\t\t\t\ttrackedElements.add(element);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Clear TTS word highlight\n\t */\n\tclearTTSWord(): void {\n\t\tif (!this.ttsWordHighlight) return;\n\t\tthis.ttsWordHighlight.clear();\n\t\tfor (const element of this.ttsWordElementHighlights) {\n\t\t\telement.removeAttribute(\"data-pie-tts-word-element\");\n\t\t}\n\t\tthis.ttsWordElementHighlights.clear();\n\t}\n\n\t/**\n\t * Clear TTS sentence highlight\n\t */\n\tclearTTSSentence(): void {\n\t\tif (!this.ttsSentenceHighlight) return;\n\t\tthis.ttsSentenceHighlight.clear();\n\t\tfor (const element of this.ttsSentenceElementHighlights) {\n\t\t\telement.removeAttribute(\"data-pie-tts-sentence-element\");\n\t\t}\n\t\tthis.ttsSentenceElementHighlights.clear();\n\t}\n\n\t/**\n\t * Clear all TTS highlights\n\t */\n\tclearTTS(): void {\n\t\tthis.clearTTSWord();\n\t\tthis.clearTTSSentence();\n\t}\n\n\t/**\n\t * Add an annotation highlight (persistent)\n\t *\n\t * @param range Text range to annotate\n\t * @param color Highlight color\n\t * @returns Annotation ID for later removal\n\t */\n\taddAnnotation(\n\t\trange: Range,\n\t\tcolor: HighlightColor = HighlightColor.YELLOW,\n\t): string {\n\t\tconst id = `annotation-${this.nextAnnotationId++}`;\n\n\t\t// Clone the range to store\n\t\tconst clonedRange = range.cloneRange();\n\n\t\t// Store annotation data\n\t\tconst annotation: Annotation = {\n\t\t\tid,\n\t\t\trange: clonedRange,\n\t\t\ttype: HighlightType.ANNOTATION,\n\t\t\tcolor,\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\t\tthis.annotations.set(id, annotation);\n\n\t\t// Add the SAME range object to the shared color highlight\n\t\t// This ensures we can later delete it by reference\n\t\tconst colorHighlight = this.colorHighlights.get(color);\n\t\tif (colorHighlight) {\n\t\t\tcolorHighlight.add(clonedRange);\n\t\t}\n\n\t\treturn id;\n\t}\n\n\t/**\n\t * Remove an annotation\n\t *\n\t * @param id Annotation ID\n\t */\n\tremoveAnnotation(id: string): void {\n\t\tconst annotation = this.annotations.get(id);\n\t\tif (!annotation) {\n\t\t\tconsole.warn(`[HighlightCoordinator] Annotation ${id} not found`);\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.log(\n\t\t\t`[HighlightCoordinator] Removing annotation ${id} (color: ${annotation.color})`,\n\t\t);\n\n\t\t// Remove range from the shared color highlight\n\t\tconst colorHighlight = this.colorHighlights.get(annotation.color);\n\t\tif (colorHighlight) {\n\t\t\tconst deleted = colorHighlight.delete(annotation.range);\n\t\t\tconsole.log(\n\t\t\t\t`[HighlightCoordinator] Highlight.delete() returned:`,\n\t\t\t\tdeleted,\n\t\t\t);\n\t\t}\n\n\t\t// Clean up\n\t\tthis.annotations.delete(id);\n\t\tconsole.log(\n\t\t\t`[HighlightCoordinator] Annotation ${id} removed from map. Remaining:`,\n\t\t\tthis.annotations.size,\n\t\t);\n\t}\n\n\t/**\n\t * Remove all annotations\n\t */\n\tclearAnnotations(): void {\n\t\t// Clear all color highlights\n\t\tfor (const colorHighlight of this.colorHighlights.values()) {\n\t\t\tcolorHighlight.clear();\n\t\t}\n\n\t\t// Clear annotation data\n\t\tthis.annotations.clear();\n\t}\n\n\t/**\n\t * Get all annotations\n\t */\n\tgetAnnotations(): Annotation[] {\n\t\treturn Array.from(this.annotations.values());\n\t}\n\n\t/**\n\t * Get annotation by ID\n\t */\n\tgetAnnotation(id: string): Annotation | null {\n\t\treturn this.annotations.get(id) ?? null;\n\t}\n\n\t/**\n\t * Change annotation color\n\t *\n\t * @param id Annotation ID\n\t * @param newColor New color\n\t */\n\tchangeAnnotationColor(id: string, newColor: HighlightColor): void {\n\t\tconst annotation = this.annotations.get(id);\n\t\tif (!annotation) return;\n\n\t\tconst oldColor = annotation.color;\n\t\tif (oldColor === newColor) return;\n\n\t\t// Remove from old color highlight\n\t\tconst oldColorHighlight = this.colorHighlights.get(oldColor);\n\t\tif (oldColorHighlight) {\n\t\t\toldColorHighlight.delete(annotation.range);\n\t\t}\n\n\t\t// Update color\n\t\tannotation.color = newColor;\n\n\t\t// Add to new color highlight\n\t\tconst newColorHighlight = this.colorHighlights.get(newColor);\n\t\tif (newColorHighlight) {\n\t\t\tnewColorHighlight.add(annotation.range);\n\t\t}\n\t}\n\n\t/**\n\t * Export annotations as serializable data for persistence.\n\t * Uses RangeSerializer for robust DOM range serialization.\n\t *\n\t * @param root Root element for serialization (typically document.body or content container)\n\t * @returns Array of serialized annotations\n\t */\n\texportAnnotations(\n\t\troot: Element = document.body,\n\t): Array<\n\t\tSerializedRange & { id: string; color: HighlightColor; timestamp: number }\n\t> {\n\t\treturn this.getAnnotations().map((annotation) => ({\n\t\t\t...this.rangeSerializer.serialize(annotation.range, root),\n\t\t\tid: annotation.id,\n\t\t\tcolor: annotation.color,\n\t\t\ttimestamp: annotation.timestamp,\n\t\t}));\n\t}\n\n\t/**\n\t * Import annotations from serialized data.\n\t * Restores annotations after page navigation or refresh.\n\t *\n\t * @param data Array of serialized annotations\n\t * @param root Root element for deserialization (same as used in export)\n\t * @returns Number of successfully restored annotations\n\t */\n\timportAnnotations(\n\t\tdata: Array<\n\t\t\tSerializedRange & {\n\t\t\t\tid?: string;\n\t\t\t\tcolor: HighlightColor;\n\t\t\t\ttimestamp?: number;\n\t\t\t}\n\t\t>,\n\t\troot: Element = document.body,\n\t): number {\n\t\tlet restored = 0;\n\n\t\tfor (const item of data) {\n\t\t\tconst range = this.rangeSerializer.deserialize(item, root);\n\t\t\tif (range) {\n\t\t\t\tthis.addAnnotation(range, item.color);\n\t\t\t\trestored++;\n\t\t\t}\n\t\t}\n\n\t\treturn restored;\n\t}\n\n\t// ============================================================================\n\t// HighlightCoordinatorApi interface implementation\n\t// ============================================================================\n\n\t/**\n\t * Highlight a text range (interface method)\n\t *\n\t * Generic method that adapts to the specific highlight type.\n\t * For more control, use the specific methods like highlightTTSWord().\n\t */\n\thighlightRange(\n\t\trange: Range,\n\t\ttype: HighlightType,\n\t\tcolor: HighlightColor = HighlightColor.YELLOW,\n\t): void {\n\t\tif (!this.supported) return;\n\n\t\tswitch (type) {\n\t\t\tcase HighlightType.TTS_WORD:\n\t\t\tcase HighlightType.TTS_SENTENCE: {\n\t\t\t\t// For TTS, add to appropriate highlight\n\t\t\t\tconst highlight =\n\t\t\t\t\ttype === HighlightType.TTS_WORD\n\t\t\t\t\t\t? this.ttsWordHighlight\n\t\t\t\t\t\t: this.ttsSentenceHighlight;\n\t\t\t\tif (highlight) {\n\t\t\t\t\thighlight.clear();\n\t\t\t\t\thighlight.add(range);\n\t\t\t\t\tif (type === HighlightType.TTS_WORD) {\n\t\t\t\t\t\tfor (const element of this.ttsWordElementHighlights) {\n\t\t\t\t\t\t\telement.removeAttribute(\"data-pie-tts-word-element\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.ttsWordElementHighlights.clear();\n\t\t\t\t\t\tthis.highlightTTSWordElementFallbacks([range]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase HighlightType.ANNOTATION:\n\t\t\t\t// For annotations, use the existing method\n\t\t\t\tthis.addAnnotation(range, color);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Clear highlights of a specific type (interface method)\n\t */\n\tclearHighlights(type: HighlightType): void {\n\t\tif (!this.supported) return;\n\n\t\tswitch (type) {\n\t\t\tcase HighlightType.TTS_WORD:\n\t\t\t\tthis.clearTTSWord();\n\t\t\t\tbreak;\n\t\t\tcase HighlightType.TTS_SENTENCE:\n\t\t\t\tthis.clearTTSSentence();\n\t\t\t\tbreak;\n\t\t\tcase HighlightType.ANNOTATION:\n\t\t\t\tthis.clearAnnotations();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Clear all highlights (interface method)\n\t */\n\tclearAll(): void {\n\t\tif (!this.supported) return;\n\t\tthis.clearTTS();\n\t\tthis.clearAnnotations();\n\t}\n\n\t/**\n\t * Check if CSS Highlight API is supported (interface method)\n\t */\n\tisSupported(): boolean {\n\t\treturn this.supported;\n\t}\n\n\t/**\n\t * Update TTS highlight style dynamically\n\t *\n\t * @param color CSS color value (e.g., '#ffeb3b')\n\t * @param opacity Opacity value (0.0 to 1.0)\n\t */\n\tupdateTTSHighlightStyle(color: string, opacity: number): void {\n\t\tif (!this.supported) return;\n\t\tif (typeof document === \"undefined\") return;\n\n\t\tconst styleEl = document.getElementById(\"pie-highlight-styles\");\n\t\tif (!styleEl) return;\n\n\t\t// Convert hex to rgba\n\t\tconst hexToRgb = (hex: string) => {\n\t\t\tconst result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n\t\t\treturn result\n\t\t\t\t? {\n\t\t\t\t\t\tr: parseInt(result[1], 16),\n\t\t\t\t\t\tg: parseInt(result[2], 16),\n\t\t\t\t\t\tb: parseInt(result[3], 16),\n\t\t\t\t\t}\n\t\t\t\t: null;\n\t\t};\n\n\t\tconst rgb = hexToRgb(color);\n\t\tif (!rgb) return;\n\n\t\tthis.explicitTTSColorOverride = {\n\t\t\tcolor: `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`,\n\t\t\topacity: Math.max(0.2, Math.min(0.95, opacity)),\n\t\t};\n\t\tthis.applyAdaptiveTTSStyle();\n\t}\n\n\t/**\n\t * Cleanup - remove all highlights\n\t */\n\tdestroy(): void {\n\t\tif (!this.supported) return;\n\n\t\tthis.clearTTS();\n\t\tthis.clearAnnotations();\n\n\t\t// Remove style element\n\t\tif (typeof document !== \"undefined\") {\n\t\t\tconst styleEl = document.getElementById(\"pie-highlight-styles\");\n\t\t\tif (styleEl) {\n\t\t\t\tstyleEl.remove();\n\t\t\t}\n\t\t}\n\t\tthis.themeObserver?.disconnect();\n\t\tthis.themeObserver = null;\n\t}\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"I18nService.js","sourceRoot":"","sources":["../../src/services/I18nService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * I18nService\n *\n * Internationalization service with hybrid loading strategy.\n * Manages translations, locale switching, and RTL/LTR direction.\n *\n * Features:\n * - Subscriber/listener pattern for reactive updates\n * - Hybrid loading: Bundle English, lazy-load other locales\n * - Interpolation: {variable} syntax\n * - Pluralization: ICU-style plural rules\n * - RTL detection: Automatic based on locale\n * - Fallback to English for missing keys\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type {\n\tI18nConfig,\n\tI18nServiceApi,\n\tPluralTranslation,\n\tTranslationBundle,\n} from \"@pie-players/pie-players-shared/i18n\";\nimport { createLogger } from \"../utils/logger.js\";\n\nconst log = createLogger(\"I18nService\");\n\n// Re-export types for convenience\nexport type {\n\tI18nConfig,\n\tI18nServiceApi,\n\tPluralTranslation,\n\tTranslationBundle,\n};\n\n/**\n * I18nService\n *\n * Manages internationalization with reactive state updates.\n */\nexport class I18nService implements I18nServiceApi {\n\tprivate locale: string = \"en\";\n\tprivate fallbackLocale: string = \"en\";\n\tprivate direction: \"ltr\" | \"rtl\" = \"ltr\";\n\tprivate translations = new Map<string, TranslationBundle>();\n\tprivate listeners = new Set<() => void>();\n\tprivate loadingPromises = new Map<string, Promise<void>>();\n\tprivate config: I18nConfig;\n\n\tconstructor(config: I18nConfig = {}) {\n\t\tthis.config = config;\n\t\tthis.fallbackLocale = config.fallbackLocale || \"en\";\n\n\t\tlog(\"I18nService initialized\", { fallbackLocale: this.fallbackLocale });\n\n\t\t// Load bundled translations (English by default)\n\t\tif (config.bundledTranslations) {\n\t\t\tfor (const [locale, bundle] of Object.entries(\n\t\t\t\tconfig.bundledTranslations,\n\t\t\t)) {\n\t\t\t\tthis.translations.set(locale, bundle);\n\t\t\t\tlog(`Loaded bundled translations for: ${locale}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Initialize i18n with locale and loading strategy\n\t */\n\tasync initialize(config: I18nConfig): Promise<void> {\n\t\tObject.assign(this.config, config);\n\n\t\tconst locale = config.locale || this.detectBrowserLocale();\n\t\tlog(`Initializing with locale: ${locale}`);\n\n\t\tawait this.setLocale(locale);\n\t}\n\n\t/**\n\t * Translate a key with optional interpolation\n\t *\n\t * @param key Translation key (e.g., 'common.save')\n\t * @param params Optional parameters for interpolation\n\t * @returns Translated string\n\t */\n\tt(key: string, params?: Record<string, any>): string {\n\t\tconst translation = this.getTranslation(key);\n\t\treturn this.interpolate(translation as string, params);\n\t}\n\n\t/**\n\t * Translate with pluralization\n\t *\n\t * @param key Translation key\n\t * @param count Count for pluralization\n\t * @param params Optional parameters for interpolation\n\t * @returns Translated string with plural form\n\t */\n\ttn(key: string, count: number, params?: Record<string, any>): string {\n\t\tconst translation = this.getTranslation(key);\n\n\t\tif (typeof translation === \"object\") {\n\t\t\tconst pluralForm = this.selectPluralForm(count, this.locale);\n\t\t\tconst text = translation[pluralForm] || translation.other;\n\t\t\treturn this.interpolate(text, { ...params, count });\n\t\t}\n\n\t\treturn this.interpolate(translation as string, { ...params, count });\n\t}\n\n\t/**\n\t * Get current locale\n\t */\n\tgetLocale(): string {\n\t\treturn this.locale;\n\t}\n\n\t/**\n\t * Change locale (triggers async loading if needed)\n\t *\n\t * @param locale Locale code (e.g., 'en', 'es', 'zh', 'ar')\n\t */\n\tasync setLocale(locale: string): Promise<void> {\n\t\tlog(`Setting locale to: ${locale}`);\n\n\t\t// Check if already loading\n\t\tif (this.loadingPromises.has(locale)) {\n\t\t\tlog(`Locale ${locale} is already loading, waiting...`);\n\t\t\tawait this.loadingPromises.get(locale);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if already loaded\n\t\tif (this.translations.has(locale)) {\n\t\t\tlog(`Locale ${locale} already loaded`);\n\t\t\tthis.applyLocale(locale);\n\t\t\treturn;\n\t\t}\n\n\t\t// Load translations\n\t\tconst loadingPromise = this.loadTranslationsForLocale(locale);\n\t\tthis.loadingPromises.set(locale, loadingPromise);\n\n\t\ttry {\n\t\t\tawait loadingPromise;\n\t\t\tthis.applyLocale(locale);\n\t\t} finally {\n\t\t\tthis.loadingPromises.delete(locale);\n\t\t}\n\t}\n\n\t/**\n\t * Get current text direction\n\t */\n\tgetDirection(): \"ltr\" | \"rtl\" {\n\t\treturn this.direction;\n\t}\n\n\t/**\n\t * Get available locales\n\t */\n\tgetAvailableLocales(): string[] {\n\t\treturn Array.from(this.translations.keys());\n\t}\n\n\t/**\n\t * Check if locale is loaded\n\t */\n\tisLocaleLoaded(locale: string): boolean {\n\t\treturn this.translations.has(locale);\n\t}\n\n\t/**\n\t * Subscribe to locale/translation changes\n\t * Returns unsubscribe function\n\t */\n\tsubscribe(listener: () => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\t/**\n\t * Check if a translation key exists\n\t */\n\thasKey(key: string): boolean {\n\t\tconst bundle = this.translations.get(this.locale);\n\t\treturn !!bundle?.translations[key];\n\t}\n\n\t/**\n\t * Notify all listeners of state change\n\t */\n\tprivate notifyListeners(): void {\n\t\tthis.listeners.forEach((listener) => listener());\n\t}\n\n\t/**\n\t * Apply locale and notify listeners\n\t */\n\tprivate applyLocale(locale: string): void {\n\t\tconst bundle = this.translations.get(locale);\n\t\tif (!bundle) {\n\t\t\tlog(`No bundle found for locale: ${locale}`, \"warn\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst previousLocale = this.locale;\n\t\tthis.locale = locale;\n\t\tthis.direction = bundle.direction;\n\n\t\tlog(`Applied locale: ${locale} (direction: ${this.direction})`);\n\n\t\tthis.applyDOMDirection();\n\t\tthis.notifyListeners();\n\t}\n\n\t/**\n\t * Apply direction to DOM\n\t */\n\tprivate applyDOMDirection(): void {\n\t\tif (typeof document === \"undefined\") return;\n\n\t\tdocument.documentElement.setAttribute(\"dir\", this.direction);\n\t\tdocument.documentElement.setAttribute(\"lang\", this.locale);\n\n\t\tlog(\n\t\t\t`Applied DOM attributes: dir=\"${this.direction}\" lang=\"${this.locale}\"`,\n\t\t);\n\t}\n\n\t/**\n\t * Load translations for a locale\n\t */\n\tprivate async loadTranslationsForLocale(locale: string): Promise<void> {\n\t\tif (!this.config.loadTranslations) {\n\t\t\tthrow new Error(`No translation loader configured for locale: ${locale}`);\n\t\t}\n\n\t\tlog(`Loading translations for: ${locale}`);\n\n\t\ttry {\n\t\t\tconst bundle = await this.config.loadTranslations(locale);\n\t\t\tthis.translations.set(locale, bundle);\n\t\t\tlog(\n\t\t\t\t`Loaded translations for: ${locale} (${Object.keys(bundle.translations).length} keys)`,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tlog(`Failed to load translations for: ${locale}`, \"error\");\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Get translation for a key\n\t */\n\tprivate getTranslation(key: string): string | PluralTranslation {\n\t\t// Try current locale\n\t\tconst currentBundle = this.translations.get(this.locale);\n\t\tif (currentBundle?.translations[key]) {\n\t\t\treturn currentBundle.translations[key];\n\t\t}\n\n\t\t// Try fallback locale\n\t\tconst fallbackBundle = this.translations.get(this.fallbackLocale);\n\t\tif (fallbackBundle?.translations[key]) {\n\t\t\tif (this.config.debug) {\n\t\t\t\tlog(`Using fallback translation for key: ${key}`);\n\t\t\t}\n\t\t\treturn fallbackBundle.translations[key];\n\t\t}\n\n\t\t// Missing key handling\n\t\tif (this.config.onMissingKey) {\n\t\t\tthis.config.onMissingKey(key, this.locale);\n\t\t}\n\n\t\tif (this.config.debug) {\n\t\t\tlog(`Missing translation key: ${key} (locale: ${this.locale})`, \"warn\");\n\t\t}\n\n\t\treturn key;\n\t}\n\n\t/**\n\t * Interpolate variables in translation string\n\t * Replaces {variable} with params.variable\n\t */\n\tprivate interpolate(text: string, params?: Record<string, any>): string {\n\t\tif (!params) return text;\n\n\t\treturn text.replace(/\\{(\\w+)\\}/g, (match, key) => {\n\t\t\treturn params[key]?.toString() || match;\n\t\t});\n\t}\n\n\t/**\n\t * Select plural form based on count and locale\n\t * Simplified implementation - real implementation would use Intl.PluralRules\n\t */\n\tprivate selectPluralForm(\n\t\tcount: number,\n\t\tlocale: string,\n\t): keyof PluralTranslation {\n\t\t// Use Intl.PluralRules if available\n\t\tif (typeof Intl !== \"undefined\" && Intl.PluralRules) {\n\t\t\ttry {\n\t\t\t\tconst rules = new Intl.PluralRules(locale);\n\t\t\t\tconst category = rules.select(count);\n\t\t\t\treturn category as keyof PluralTranslation;\n\t\t\t} catch (error) {\n\t\t\t\tlog(`Failed to use Intl.PluralRules for locale: ${locale}`, \"warn\");\n\t\t\t}\n\t\t}\n\n\t\t// Fallback to simple rules\n\t\tif (count === 0) return \"zero\";\n\t\tif (count === 1) return \"one\";\n\t\treturn \"other\";\n\t}\n\n\t/**\n\t * Detect browser locale\n\t */\n\tprivate detectBrowserLocale(): string {\n\t\tif (typeof navigator === \"undefined\") return \"en\";\n\n\t\tconst browserLang =\n\t\t\tnavigator.language ||\n\t\t\t(navigator.languages && navigator.languages[0]) ||\n\t\t\t\"en\";\n\n\t\t// Extract language code (e.g., 'en-US' -> 'en')\n\t\treturn browserLang.split(\"-\")[0];\n\t}\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RangeSerializer.js","sourceRoot":"","sources":["../../src/services/RangeSerializer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * RangeSerializer - Serialize and deserialize DOM Range objects\n *\n * Provides utilities for storing and restoring text ranges across sessions.\n * Used by both TTS (for timing data) and annotations (for persistence).\n *\n * Uses CSS selector paths and node indices for robust serialization that\n * survives content changes when possible.\n */\n\n/**\n * Serialized form of a Range suitable for storage.\n */\nexport interface SerializedRange {\n\t/** CSS selector path to start container */\n\tstartContainer: string;\n\n\t/** Character offset in start container */\n\tstartOffset: number;\n\n\t/** CSS selector path to end container */\n\tendContainer: string;\n\n\t/** Character offset in end container */\n\tendOffset: number;\n\n\t/** Original text for validation */\n\ttext: string;\n\n\t/** Optional metadata */\n\tmetadata?: {\n\t\t/** When this range was created */\n\t\ttimestamp?: number;\n\n\t\t/** User-provided label */\n\t\tlabel?: string;\n\n\t\t/** Custom data */\n\t\tcustom?: Record<string, unknown>;\n\t};\n}\n\n/**\n * RangeSerializer - Serialize and deserialize Range objects.\n */\nexport class RangeSerializer {\n\t/**\n\t * Serialize a Range to storable format.\n\t *\n\t * @param range - Range to serialize\n\t * @param root - Root element (typically document.body or content container)\n\t * @returns Serialized range data\n\t *\n\t * @example\n\t * ```typescript\n\t * const serializer = new RangeSerializer();\n\t * const range = window.getSelection()!.getRangeAt(0);\n\t * const serialized = serializer.serialize(range, document.body);\n\t * localStorage.setItem('savedRange', JSON.stringify(serialized));\n\t * ```\n\t */\n\tserialize(range: Range, root: Element): SerializedRange {\n\t\treturn {\n\t\t\tstartContainer: this.getNodePath(range.startContainer, root),\n\t\t\tstartOffset: range.startOffset,\n\t\t\tendContainer: this.getNodePath(range.endContainer, root),\n\t\t\tendOffset: range.endOffset,\n\t\t\ttext: range.toString(),\n\t\t};\n\t}\n\n\t/**\n\t * Deserialize range from storage.\n\t *\n\t * @param data - Serialized range data\n\t * @param root - Root element (same as used in serialize)\n\t * @returns Range object, or null if content changed\n\t *\n\t * @example\n\t * ```typescript\n\t * const serializer = new RangeSerializer();\n\t * const data = JSON.parse(localStorage.getItem('savedRange')!);\n\t * const range = serializer.deserialize(data, document.body);\n\t * if (range) {\n\t * // Range successfully restored\n\t * } else {\n\t * // Content changed, range invalid\n\t * }\n\t * ```\n\t */\n\tdeserialize(data: SerializedRange, root: Element): Range | null {\n\t\tconst startNode = this.findNodeByPath(data.startContainer, root);\n\t\tconst endNode = this.findNodeByPath(data.endContainer, root);\n\n\t\tif (!startNode || !endNode) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst range = new Range();\n\t\t\trange.setStart(startNode, data.startOffset);\n\t\t\trange.setEnd(endNode, data.endOffset);\n\n\t\t\t// Validate text hasn't changed\n\t\t\tif (range.toString() === data.text) {\n\t\t\t\treturn range;\n\t\t\t}\n\n\t\t\t// Text changed, range is invalid\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\t// Range construction failed (offsets invalid, etc.)\n\t\t\tconsole.warn(\"Failed to deserialize range:\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get a unique path to a node from root.\n\t *\n\t * Uses a hybrid approach:\n\t * - For element nodes: CSS selector path\n\t * - For text nodes: parent selector + text node index\n\t *\n\t * @param node - Node to get path for\n\t * @param root - Root element\n\t * @returns Path string\n\t */\n\tprivate getNodePath(node: Node, root: Element): string {\n\t\tif (node === root) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\t// Handle text nodes\n\t\tif (node.nodeType === Node.TEXT_NODE) {\n\t\t\tconst parent = node.parentElement;\n\t\t\tif (!parent) {\n\t\t\t\tthrow new Error(\"Text node has no parent\");\n\t\t\t}\n\n\t\t\t// Get index of this text node among its siblings\n\t\t\tconst textNodes = Array.from(parent.childNodes).filter(\n\t\t\t\t(n) => n.nodeType === Node.TEXT_NODE,\n\t\t\t);\n\t\t\tconst index = textNodes.indexOf(node as Text);\n\n\t\t\tconst parentPath = this.getElementPath(parent, root);\n\t\t\treturn `${parentPath}::text[${index}]`;\n\t\t}\n\n\t\t// Handle element nodes\n\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\treturn this.getElementPath(node as Element, root);\n\t\t}\n\n\t\tthrow new Error(`Unsupported node type: ${node.nodeType}`);\n\t}\n\n\t/**\n\t * Get CSS selector path to an element.\n\t *\n\t * @param element - Element to get path for\n\t * @param root - Root element\n\t * @returns CSS selector path\n\t */\n\tprivate getElementPath(element: Element, root: Element): string {\n\t\tif (element === root) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tconst path: string[] = [];\n\t\tlet current: Element | null = element;\n\n\t\twhile (current && current !== root) {\n\t\t\t// Use ID if available (more stable)\n\t\t\tif (current.id) {\n\t\t\t\tpath.unshift(`#${current.id}`);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Otherwise use tag + nth-of-type\n\t\t\tconst parent: Element | null = current.parentElement;\n\t\t\tif (!parent) break;\n\n\t\t\tconst siblings = Array.from(parent.children).filter(\n\t\t\t\t(el: Element) => el.tagName === current!.tagName,\n\t\t\t);\n\t\t\tconst index = siblings.indexOf(current);\n\t\t\tconst selector =\n\t\t\t\tsiblings.length > 1\n\t\t\t\t\t? `${current.tagName.toLowerCase()}:nth-of-type(${index + 1})`\n\t\t\t\t\t: current.tagName.toLowerCase();\n\n\t\t\tpath.unshift(selector);\n\t\t\tcurrent = parent;\n\t\t}\n\n\t\treturn path.join(\" > \");\n\t}\n\n\t/**\n\t * Find a node by its path from root.\n\t *\n\t * @param path - Path string from getNodePath\n\t * @param root - Root element\n\t * @returns Node, or null if not found\n\t */\n\tprivate findNodeByPath(path: string, root: Element): Node | null {\n\t\tif (path === \"\") {\n\t\t\treturn root;\n\t\t}\n\n\t\t// Handle text node paths\n\t\tif (path.includes(\"::text[\")) {\n\t\t\tconst [elementPath, textPart] = path.split(\"::text[\");\n\t\t\tconst textIndex = Number.parseInt(textPart.replace(\"]\", \"\"), 10);\n\n\t\t\t// Find parent element\n\t\t\tconst parent = elementPath ? root.querySelector(elementPath) : root;\n\t\t\tif (!parent) return null;\n\n\t\t\t// Find text node by index\n\t\t\tconst textNodes = Array.from(parent.childNodes).filter(\n\t\t\t\t(n) => n.nodeType === Node.TEXT_NODE,\n\t\t\t);\n\n\t\t\treturn textNodes[textIndex] || null;\n\t\t}\n\n\t\t// Handle element paths\n\t\ttry {\n\t\t\treturn root.querySelector(path);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Invalid selector path:\", path, error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Batch serialize multiple ranges.\n\t *\n\t * @param ranges - Ranges to serialize\n\t * @param root - Root element\n\t * @returns Array of serialized ranges\n\t */\n\tserializeMany(ranges: Range[], root: Element): SerializedRange[] {\n\t\treturn ranges.map((range) => this.serialize(range, root));\n\t}\n\n\t/**\n\t * Batch deserialize multiple ranges.\n\t *\n\t * @param data - Array of serialized ranges\n\t * @param root - Root element\n\t * @returns Array of ranges (nulls for invalid ranges)\n\t */\n\tdeserializeMany(data: SerializedRange[], root: Element): (Range | null)[] {\n\t\treturn data.map((item) => this.deserialize(item, root));\n\t}\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SSMLExtractor.js","sourceRoot":"","sources":["../../src/services/SSMLExtractor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SSMLExtractor\n *\n * Service for extracting embedded SSML from PIE item content and converting\n * it into QTI 3.0 accessibility catalogs.\n *\n * Authors can embed <speak> SSML tags directly in content for convenience.\n * The extractor automatically:\n * 1. Parses markup and finds all <speak> elements\n * 2. Extracts SSML content with language metadata\n * 3. Generates accessibility catalogs with unique IDs\n * 4. Cleans visual markup (removes SSML, adds data-catalog-idref)\n *\n * This enables proper pronunciation, emphasis, and pacing for TTS without\n * requiring authors to maintain separate catalog files.\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type {\n\tAccessibilityCatalog,\n\tConfigEntity,\n\tPieModel,\n} from \"@pie-players/pie-players-shared/types\";\n\n/**\n * Result from extracting SSML from markup\n */\ninterface MarkupExtractionResult {\n\tcatalogs: AccessibilityCatalog[];\n\tcleanedMarkup: string;\n}\n\n/**\n * Result from extracting SSML from entire item config\n */\nexport interface ExtractionResult {\n\tcatalogs: AccessibilityCatalog[];\n\tcleanedConfig: ConfigEntity;\n}\n\n/**\n * Allow-listed SSML elements (from the SSML 1.1 spec subset we forward to\n * TTS providers). Anything outside this list is unwrapped during\n * sanitization so we never leak author-supplied scripts, iframes, or\n * arbitrary HTML into the TTS pipeline.\n */\nconst SSML_ALLOWED_TAGS = new Set([\n\t\"speak\",\n\t\"break\",\n\t\"prosody\",\n\t\"emphasis\",\n\t\"say-as\",\n\t\"sub\",\n\t\"mark\",\n\t\"phoneme\",\n\t\"lang\",\n\t\"p\",\n\t\"s\",\n\t\"voice\",\n\t\"audio\",\n\t\"lexicon\",\n]);\n\n/**\n * Allow-listed attributes across SSML elements. Attribute allow-listing is\n * global (not per-tag) because the union is small and consistent across\n * providers.\n */\n/**\n * Tags whose content must be dropped entirely (rather than unwrapped to\n * preserve phrasing text). These are dangerous containers whose text\n * payload could be re-interpreted downstream.\n */\nconst SSML_DROP_FULLY_TAGS = new Set([\n\t\"script\",\n\t\"style\",\n\t\"iframe\",\n\t\"object\",\n\t\"embed\",\n\t\"foreignobject\",\n\t\"noscript\",\n\t\"template\",\n]);\n\nconst SSML_ALLOWED_ATTRS = new Set([\n\t\"alphabet\",\n\t\"ph\",\n\t\"time\",\n\t\"strength\",\n\t\"rate\",\n\t\"pitch\",\n\t\"volume\",\n\t\"name\",\n\t\"interpret-as\",\n\t\"format\",\n\t\"detail\",\n\t\"language\",\n\t\"xml:lang\",\n\t\"lang\",\n\t\"gender\",\n\t\"src\",\n]);\n\n/**\n * SSML elements that are empty in practice (no child content). Authors\n * typically write them as self-closing (`<break time=\"200ms\"/>`). HTML's\n * parser does NOT recognize them as void elements and silently drops the\n * `/`, then nests every subsequent sibling as a child — producing\n * malformed SSML like `<break>…rest of paragraph…</break>` when\n * round-tripped. Pre-processing self-closing forms to explicit\n * open+close pairs before HTML parsing avoids the reflow, and the\n * serializer re-emits them as self-closing so providers see valid SSML.\n *\n * `audio` is intentionally excluded — SSML <audio> may carry fallback\n * children (the spec's \"alternate content\"), so treating it as void\n * would lose author intent.\n */\nconst SSML_VOID_TAGS = new Set([\"break\", \"mark\", \"lexicon\"]);\n\n/**\n * Normalize self-closing forms of SSML void elements to explicit\n * open+close pairs BEFORE handing the string to an HTML parser. The\n * HTML5 parser ignores the `/` on non-void tags, so `<break/>` becomes\n * a plain `<break>` open tag that swallows every following sibling.\n * Rewriting to `<break></break>` preserves the empty-element shape\n * across the HTML round-trip.\n */\nfunction expandSelfClosingVoidTags(input: string): string {\n\tlet next = input;\n\tfor (const tag of SSML_VOID_TAGS) {\n\t\tconst selfClosing = new RegExp(`<${tag}\\\\b([^>/]*?)\\\\s*/>`, \"gi\");\n\t\tnext = next.replace(selfClosing, (_match, attrs: string) => {\n\t\t\tconst attrText = attrs ? attrs.replace(/\\s+$/, \"\") : \"\";\n\t\t\treturn `<${tag}${attrText}></${tag}>`;\n\t\t});\n\t}\n\treturn next;\n}\n\n/**\n * Hosts that must never appear inside an SSML `<audio src>` because they\n * would cause the server-side TTS provider to fetch a private / metadata\n * endpoint on behalf of the request. Mirrors the SchoolCity provider's\n * defense-in-depth check; the SSML-side block is cheap and belt-and-braces.\n */\nconst SSML_PRIVATE_AUDIO_HOSTS: RegExp[] = [\n\t/^localhost$/i,\n\t/^127(?:\\.\\d{1,3}){3}$/,\n\t/^0(?:\\.\\d{1,3}){3}$/,\n\t/^10(?:\\.\\d{1,3}){3}$/,\n\t/^192\\.168(?:\\.\\d{1,3}){2}$/,\n\t/^172\\.(?:1[6-9]|2\\d|3[01])(?:\\.\\d{1,3}){2}$/,\n\t/^169\\.254(?:\\.\\d{1,3}){2}$/,\n\t/^::1$/,\n\t/^fe80:/i,\n\t/^fc00:/i,\n\t/^fd[0-9a-f]{2}:/i,\n\t/^metadata\\.google\\.internal$/i,\n\t/^metadata\\.azure\\.internal$/i,\n\t/^metadata\\.packet\\.net$/i,\n];\n\nfunction isSafeSsmlAudioSrc(raw: string): boolean {\n\tif (typeof raw !== \"string\" || raw.length === 0) return false;\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(raw);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n\t\treturn false;\n\t}\n\tconst hostname = parsed.hostname.replace(/^\\[|\\]$/g, \"\").replace(/\\.$/, \"\");\n\treturn !SSML_PRIVATE_AUDIO_HOSTS.some((pattern) => pattern.test(hostname));\n}\n\n/**\n * Sanitize an SSML subtree in place: remove disallowed attributes and\n * replace disallowed elements with their text content (so phrasing order\n * is preserved but no scripts / iframes / unknown tags survive).\n */\nexport function sanitizeSsmlElement(element: Element): void {\n\t// Walk children snapshot first because we may replace nodes as we go.\n\tconst children = Array.from(element.children);\n\tfor (const child of children) {\n\t\tconst tagName = child.tagName.toLowerCase();\n\t\tif (SSML_DROP_FULLY_TAGS.has(tagName)) {\n\t\t\tchild.remove();\n\t\t\tcontinue;\n\t\t}\n\t\tsanitizeSsmlElement(child);\n\t\tif (!SSML_ALLOWED_TAGS.has(tagName)) {\n\t\t\tconst textNode = element.ownerDocument.createTextNode(\n\t\t\t\tchild.textContent ?? \"\",\n\t\t\t);\n\t\t\tchild.replaceWith(textNode);\n\t\t}\n\t}\n\t// Strip disallowed attributes on the surviving node.\n\tconst attrNames = Array.from(element.attributes).map((attr) => attr.name);\n\tfor (const attrName of attrNames) {\n\t\tif (!SSML_ALLOWED_ATTRS.has(attrName.toLowerCase())) {\n\t\t\telement.removeAttribute(attrName);\n\t\t}\n\t}\n\t// Belt-and-braces: even though `src` is allow-listed, SSML `<audio>`\n\t// sends its URL to a server-side TTS provider that will dereference\n\t// it. Refuse non-http(s) schemes and private / cloud-metadata hosts\n\t// at the SSML boundary so we never forward an SSRF primitive.\n\tif (element.tagName.toLowerCase() === \"audio\") {\n\t\tconst src = element.getAttribute(\"src\");\n\t\tif (src !== null && !isSafeSsmlAudioSrc(src)) {\n\t\t\telement.removeAttribute(\"src\");\n\t\t}\n\t}\n}\n\n/**\n * Serialize a sanitized `<speak>` element back to a minimal SSML string.\n * Uses a manual walker instead of `outerHTML` so that any namespace\n * quirks (e.g. `xmlns` attributes added by the HTML parser) are\n * normalised and disallowed descendants cannot reappear through quirks\n * in the serializer.\n */\nexport function serializeSsmlElement(element: Element): string {\n\tconst tagName = element.tagName.toLowerCase();\n\tconst attrs = Array.from(element.attributes)\n\t\t.filter((attr) => SSML_ALLOWED_ATTRS.has(attr.name.toLowerCase()))\n\t\t.map((attr) => ` ${attr.name}=\"${escapeXmlAttr(attr.value)}\"`)\n\t\t.join(\"\");\n\t// SSML void elements (e.g. <break>, <mark>) must be emitted as\n\t// self-closing so providers like AWS Polly accept them. Emitting\n\t// `<break></break>` parses fine on the SSML side but is wasteful;\n\t// emitting `<break>` (open without close) is invalid SSML.\n\tif (SSML_VOID_TAGS.has(tagName)) {\n\t\treturn `<${tagName}${attrs}/>`;\n\t}\n\tconst childPieces: string[] = [];\n\tfor (const node of Array.from(element.childNodes)) {\n\t\tif (node.nodeType === 3 /* text */) {\n\t\t\tchildPieces.push(escapeXmlText(node.nodeValue ?? \"\"));\n\t\t} else if (node.nodeType === 1 /* element */) {\n\t\t\tconst childElement = node as Element;\n\t\t\tconst childTag = childElement.tagName.toLowerCase();\n\t\t\tif (SSML_DROP_FULLY_TAGS.has(childTag)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (SSML_ALLOWED_TAGS.has(childTag)) {\n\t\t\t\tchildPieces.push(serializeSsmlElement(childElement));\n\t\t\t} else {\n\t\t\t\tchildPieces.push(escapeXmlText(childElement.textContent ?? \"\"));\n\t\t\t}\n\t\t}\n\t}\n\treturn `<${tagName}${attrs}>${childPieces.join(\"\")}</${tagName}>`;\n}\n\nexport function sanitizeSsmlString(input: string): string {\n\tconst trimmed = String(input || \"\").trim();\n\tif (!trimmed || !trimmed.includes(\"<\")) return input;\n\tif (typeof DOMParser === \"undefined\") return input;\n\tconst parser = new DOMParser();\n\tconst doc = parser.parseFromString(\n\t\texpandSelfClosingVoidTags(trimmed),\n\t\t\"text/html\",\n\t);\n\tconst speakElement = doc.querySelector(\"speak\");\n\tif (speakElement) {\n\t\tsanitizeSsmlElement(speakElement);\n\t\treturn serializeSsmlElement(speakElement);\n\t}\n\tconst wrapper = doc.createElement(\"speak\");\n\tfor (const node of Array.from(doc.body.childNodes)) {\n\t\twrapper.appendChild(node);\n\t}\n\tsanitizeSsmlElement(wrapper);\n\treturn Array.from(wrapper.childNodes)\n\t\t.map((node) => {\n\t\t\tif (node.nodeType === 3 /* text */) {\n\t\t\t\treturn escapeXmlText(node.nodeValue ?? \"\");\n\t\t\t}\n\t\t\tif (node.nodeType === 1 /* element */) {\n\t\t\t\tconst child = node as Element;\n\t\t\t\tif (SSML_ALLOWED_TAGS.has(child.tagName.toLowerCase())) {\n\t\t\t\t\treturn serializeSsmlElement(child);\n\t\t\t\t}\n\t\t\t\treturn escapeXmlText(child.textContent ?? \"\");\n\t\t\t}\n\t\t\treturn \"\";\n\t\t})\n\t\t.join(\"\");\n}\n\nfunction escapeXmlText(value: string): string {\n\treturn value\n\t\t.replace(/&/g, \"&\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\");\n}\n\nfunction escapeXmlAttr(value: string): string {\n\treturn value\n\t\t.replace(/&/g, \"&\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/\"/g, \""\");\n}\n\n/**\n * SSMLExtractor Service\n *\n * Extracts embedded SSML from PIE content and generates accessibility catalogs.\n */\nexport class SSMLExtractor {\n\tprivate catalogCounter = 0;\n\n\t/**\n\t * Extract SSML from entire item config (markup + models)\n\t *\n\t * @param config Item configuration with potential embedded SSML\n\t * @returns Extracted catalogs and cleaned config\n\t */\n\textractFromItemConfig(config: ConfigEntity): ExtractionResult {\n\t\tconst allCatalogs: AccessibilityCatalog[] = [];\n\n\t\t// Shallow clone of config + a fresh models array. The per-model/choice\n\t\t// objects are replaced (not mutated in place) by the `.map` below, so the\n\t\t// caller's original config and its models are never mutated.\n\t\tconst cleanedConfig: ConfigEntity = {\n\t\t\t...config,\n\t\t\tmodels: config.models ? [...config.models] : [],\n\t\t};\n\n\t\t// 1. Extract from top-level markup (passages)\n\t\tif (config.markup) {\n\t\t\tconst markupResult = this.extractFromMarkup(config.markup, \"markup\");\n\t\t\tallCatalogs.push(...markupResult.catalogs);\n\t\t\tcleanedConfig.markup = markupResult.cleanedMarkup;\n\t\t}\n\n\t\t// 2. Extract from models (prompts, choices, etc.)\n\t\tif (config.models && config.models.length > 0) {\n\t\t\tcleanedConfig.models = config.models.map((model) => {\n\t\t\t\tconst cleanedModel = { ...model };\n\t\t\t\tconst modelCatalogs: AccessibilityCatalog[] = [];\n\n\t\t\t\t// Extract from prompt\n\t\t\t\tif (model.prompt && typeof model.prompt === \"string\") {\n\t\t\t\t\tconst promptResult = this.extractFromMarkup(\n\t\t\t\t\t\tmodel.prompt,\n\t\t\t\t\t\t`prompt-${model.id}`,\n\t\t\t\t\t);\n\t\t\t\t\tmodelCatalogs.push(...promptResult.catalogs);\n\t\t\t\t\tcleanedModel.prompt = promptResult.cleanedMarkup;\n\t\t\t\t}\n\n\t\t\t\t// Extract from choices\n\t\t\t\tif (model.choices && Array.isArray(model.choices)) {\n\t\t\t\t\tcleanedModel.choices = model.choices.map((choice) => {\n\t\t\t\t\t\tif (choice.label && typeof choice.label === \"string\") {\n\t\t\t\t\t\t\tconst choiceResult = this.extractFromMarkup(\n\t\t\t\t\t\t\t\tchoice.label,\n\t\t\t\t\t\t\t\t`choice-${model.id}-${choice.value}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tmodelCatalogs.push(...choiceResult.catalogs);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t...choice,\n\t\t\t\t\t\t\t\tlabel: choiceResult.cleanedMarkup,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn choice;\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tallCatalogs.push(...modelCatalogs);\n\t\t\t\treturn cleanedModel;\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tcatalogs: allCatalogs,\n\t\t\tcleanedConfig,\n\t\t};\n\t}\n\n\t/**\n\t * Extract SSML from markup string\n\t *\n\t * @param markup HTML markup potentially containing <speak> elements\n\t * @param idPrefix Prefix for generating catalog IDs\n\t * @returns Extracted catalogs and cleaned markup\n\t */\n\tprivate extractFromMarkup(\n\t\tmarkup: string,\n\t\tidPrefix: string,\n\t): MarkupExtractionResult {\n\t\tif (!markup || markup.trim() === \"\") {\n\t\t\treturn { catalogs: [], cleanedMarkup: markup };\n\t\t}\n\n\t\t// Check if running in browser environment\n\t\tif (typeof window === \"undefined\" || typeof DOMParser === \"undefined\") {\n\t\t\tconsole.warn(\n\t\t\t\t\"[SSMLExtractor] DOMParser not available (SSR?), skipping extraction\",\n\t\t\t);\n\t\t\treturn { catalogs: [], cleanedMarkup: markup };\n\t\t}\n\n\t\tconst catalogs: AccessibilityCatalog[] = [];\n\n\t\ttry {\n\t\t\tconst parser = new DOMParser();\n\t\t\tconst doc = parser.parseFromString(\n\t\t\t\texpandSelfClosingVoidTags(markup),\n\t\t\t\t\"text/html\",\n\t\t\t);\n\n\t\t\t// Find all <speak> elements\n\t\t\tconst speakElements = Array.from(doc.querySelectorAll(\"speak\"));\n\n\t\t\tif (speakElements.length === 0) {\n\t\t\t\t// No SSML found, return original markup\n\t\t\t\treturn { catalogs: [], cleanedMarkup: markup };\n\t\t\t}\n\n\t\t\t// Process each <speak> element\n\t\t\tfor (const speakEl of speakElements) {\n\t\t\t\ttry {\n\t\t\t\t\t// Generate unique catalog ID\n\t\t\t\t\tconst catalogId = this.generateCatalogId(idPrefix);\n\n\t\t\t\t\t// Sanitize before serializing so the SSML forwarded to the TTS\n\t\t\t\t\t// provider only contains spec-allowed tags/attributes. This\n\t\t\t\t\t// closes an author-content -> TTS-server pipeline that would\n\t\t\t\t\t// otherwise accept arbitrary HTML.\n\t\t\t\t\tsanitizeSsmlElement(speakEl);\n\t\t\t\t\tconst ssmlContent = serializeSsmlElement(speakEl);\n\n\t\t\t\t\t// Extract language (check both xml:lang and lang attributes)\n\t\t\t\t\tconst language =\n\t\t\t\t\t\tspeakEl.getAttribute(\"xml:lang\") ||\n\t\t\t\t\t\tspeakEl.getAttribute(\"lang\") ||\n\t\t\t\t\t\t\"en-US\";\n\n\t\t\t\t\t// Get plain text for visual display\n\t\t\t\t\tconst plainText = speakEl.textContent || \"\";\n\n\t\t\t\t\t// Find parent element or create wrapper\n\t\t\t\t\tlet wrapper = speakEl.parentElement;\n\n\t\t\t\t\tif (!wrapper || wrapper.tagName === \"BODY\") {\n\t\t\t\t\t\t// SPEAK is at root level - create span wrapper\n\t\t\t\t\t\tconst span = doc.createElement(\"span\");\n\t\t\t\t\t\tspeakEl.parentNode?.insertBefore(span, speakEl);\n\t\t\t\t\t\tspan.appendChild(doc.createTextNode(plainText));\n\t\t\t\t\t\twrapper = span;\n\t\t\t\t\t\tspeakEl.remove();\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Just remove <speak> - visual content should be in sibling element\n\t\t\t\t\t\tspeakEl.remove();\n\t\t\t\t\t}\n\n\t\t\t\t\t// Tag the wrapper with the QTI-style catalog reference so the\n\t\t\t\t\t// runtime can resolve this region's spoken content. Same attribute\n\t\t\t\t\t// authored content uses (`data-catalog-idref`) — one canonical name.\n\t\t\t\t\tif (wrapper) {\n\t\t\t\t\t\twrapper.setAttribute(\"data-catalog-idref\", catalogId);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create catalog entry\n\t\t\t\t\tcatalogs.push({\n\t\t\t\t\t\tidentifier: catalogId,\n\t\t\t\t\t\tcards: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcatalog: \"spoken\",\n\t\t\t\t\t\t\t\tlanguage,\n\t\t\t\t\t\t\t\tcontent: ssmlContent,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"[SSMLExtractor] Error processing <speak> element:\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\t// Continue with other elements\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Return cleaned markup\n\t\t\tconst cleanedMarkup = doc.body.innerHTML;\n\n\t\t\treturn { catalogs, cleanedMarkup };\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[SSMLExtractor] Error parsing markup:\", error);\n\t\t\t// Return original markup if parsing fails\n\t\t\treturn { catalogs: [], cleanedMarkup: markup };\n\t\t}\n\t}\n\n\t/**\n\t * Generate unique catalog ID\n\t *\n\t * @param prefix Context prefix (e.g., 'prompt-q1', 'choice-q1-a')\n\t * @returns Unique catalog ID (e.g., 'auto-prompt-q1-0')\n\t */\n\tprivate generateCatalogId(prefix: string): string {\n\t\tconst id = `auto-${prefix}-${this.catalogCounter}`;\n\t\tthis.catalogCounter++;\n\t\treturn id;\n\t}\n\n\t/**\n\t * Reset counter (useful for testing or new extraction contexts)\n\t */\n\treset(): void {\n\t\tthis.catalogCounter = 0;\n\t}\n}\n"]}
|