open-index/open-npm huggingface dataset:
npm package: @pie-players/math-renderer-core
Description: Core types and provider for pluggable math rendering in PIE players
Latest version: 0.3.2

# @pie-players/math-renderer-core

Core types and provider for pluggable math rendering in PIE players.

## Overview

This package provides the foundation for pluggable math rendering. Actual rendering is done by adapter packages:

- **@pie-players/math-renderer-mathjax** - Full-featured MathJax renderer (~2.7MB)
- **@pie-players/math-renderer-katex** - Fast, lightweight KaTeX renderer (~100KB)

## Installation

```bash
npm install @pie-players/math-renderer-core
# Also install an adapter:
npm install @pie-players/math-renderer-mathjax
# or
npm install @pie-players/math-renderer-katex
```

## Usage

### Basic Usage with Provider

```typescript
import { mathRendererProvider } from '@pie-players/math-renderer-core';
import { createMathjaxRenderer } from '@pie-players/math-renderer-mathjax';

// Set the global renderer
const renderer = await createMathjaxRenderer();
mathRendererProvider.setRenderer(renderer);

// Later, get the renderer
const current = mathRendererProvider.getRenderer();
if (current) {
  await current.renderMath(document.body);
}
```

### Creating Custom Renderers

```typescript
import type { MathRenderer } from '@pie-players/math-renderer-core';

const customRenderer: MathRenderer = async (element) => {
  // Find math elements
  const mathElements = element.querySelectorAll('[data-latex]');

  // Render each element
  mathElements.forEach(el => {
    const latex = el.getAttribute('data-latex');
    // ... your rendering logic
  });
};

// Use with provider
mathRendererProvider.setRenderer(customRenderer);
```

## API

### Types

#### `MathRenderer`

```typescript
type MathRenderer = (element: HTMLElement) => void | Promise<void>;
```

Function that renders math within an HTML element.

#### `MathRenderingAPI`

```typescript
interface MathRenderingAPI {
  renderMath: MathRenderer;
  wrapMath?: (latex: string) => string;
  unWrapMath?: (wrapped: string) => string;
  mmlToLatex?: (mathml: string) => string;
}
```

Complete API that PIE elements expect on window globals.

### Provider

#### `mathRendererProvider`

Global singleton for managing the active math renderer.

**Methods:**

- `setRenderer(renderer: MathRenderingAPI): void` - Set the active renderer
- `getRenderer(): MathRenderingAPI | null` - Get the current renderer
- `isInitialized(): boolean` - Check if a renderer is set

## Architecture

See [MATH-RENDERER-ARCHITECTURE.md](../../docs/MATH-RENDERER-ARCHITECTURE.md) for the complete unified architecture documentation.

## License

MIT
npm package: @pie-players/math-renderer-mathjax
Description: MathJax adapter for PIE math rendering
Latest version: 0.3.2

# @pie-players/math-renderer-mathjax

MathJax adapter for PIE math rendering (~2.7MB).

## Overview

Full-featured LaTeX and MathML rendering with accessibility support. This adapter wraps the upstream `@pie-lib/math-rendering-module` package.

## Installation

```bash
npm install @pie-players/math-renderer-mathjax
npm install @pie-players/math-renderer-core
```

## Usage

### With PIE Players

```typescript
import { createMathjaxRenderer } from '@pie-players/math-renderer-mathjax';
import { setMathRenderer } from '@pie-players/pie-players-shared/pie';

// Create and set renderer before loading PIE elements
const renderer = await createMathjaxRenderer({
  accessibility: true
});
setMathRenderer(renderer);

// Now load PIE elements - they'll use MathJax
await loader.load(config, document, needsControllers);
```

### Standalone Usage

```typescript
import { createMathjaxRenderer } from '@pie-players/math-renderer-mathjax';

const renderer = await createMathjaxRenderer();

// Render math in any element
await renderer.renderMath(document.body);
```

### With Provider

```typescript
import { mathRendererProvider } from '@pie-players/math-renderer-core';
import { createMathjaxRenderer } from '@pie-players/math-renderer-mathjax';

const renderer = await createMathjaxRenderer();
mathRendererProvider.setRenderer(renderer);
```

## API

### `createMathjaxRenderer(options?)`

Creates a MathJax renderer adapter.

**Parameters:**

```typescript
interface MathjaxRendererOptions {
  /** Enable accessibility features (speech output, etc.) */
  accessibility?: boolean;

  /** Auto-load MathJax fonts */
  loadFonts?: boolean;
}
```

**Returns:** `Promise<MathRenderingAPI>`

## Features

- ✅ Full LaTeX support
- ✅ Complete MathML support
- ✅ Accessibility features (speech output, aria-labels)
- ✅ Auto-handles delimiters natively
- ✅ Server-side rendering compatible (with proper setup)
- ✅ ~2.7MB bundle size

## Comparison: MathJax vs KaTeX

| Feature | MathJax | KaTeX |
|---------|---------|-------|
| Bundle Size | ~2.7MB | ~100KB |
| Speed | Slower | ~100x faster |
| LaTeX Coverage | 99% | 95% |
| MathML Support | Complete | Via conversion |
| Accessibility | Built-in | Limited |

## Related Packages

- [@pie-players/math-renderer-core](../math-renderer-core) - Core types and provider
- [@pie-players/math-renderer-katex](../math-renderer-katex) - KaTeX adapter (faster, smaller)

## License

MIT
npm package: @pie-players/pie-context
Description: Framework-agnostic Context Protocol helpers for PIE web components
Latest version: 0.3.29

# @pie-players/pie-context

Framework-agnostic Context Protocol helpers for PIE web components.

This package implements the Web Components community `context-request` protocol
so orchestration/runtime dependencies can be shared without prop drilling.

## Included APIs

- `createContext<T>(key)` for typed context keys
- `ContextRequestEvent` and `ContextProviderEvent`
- `ContextProvider` and `provideContext(...)`
- `ContextConsumer`, `consumeContext(...)`, and `requestContext(...)`
- `ContextRoot` for late-provider replay of pending subscribing requests

## Design notes

- Events are emitted with `bubbles: true` and `composed: true`.
- Provider matching uses strict key identity (`===`).
- `ContextRequestEvent.subscribe` defaults to `false` (one-shot request).
- Providers call `stopPropagation()` when they satisfy a matching request.
- Providers re-dispatch existing subscriptions when a nested provider for the
  same context announces itself.
- Subscriptions are opt-in (`subscribe: true`) and include unsubscribe callbacks.
- `ContextRoot` only tracks subscribing requests to avoid unnecessary retention.
- `ContextRoot` dedupes pending replay by `(requestor, callback)` pair.

See `LIT_PARITY_CHECKLIST.md` for the parity checklist used to align semantics
with `../lit/packages/context` core APIs.

## Svelte usage pattern

Keep Svelte reactivity for internal state and use this package for ambient
runtime dependencies between components:

```ts
import { onMount } from "svelte";
import { ContextConsumer, createContext } from "@pie-players/pie-context";

const toolkitContext = createContext<{ assessmentId: string }>(
  Symbol("toolkit-runtime"),
);

let host: HTMLElement;
let assessmentId = "";
let consumer: ContextConsumer<typeof toolkitContext>;

onMount(() => {
  consumer = new ContextConsumer(host, {
    context: toolkitContext,
    subscribe: true,
    onValue: (value) => {
      assessmentId = value.assessmentId;
    },
  });
  consumer.connect();
  return () => consumer.disconnect();
});
```
npm package: @pie-players/pie-default-tool-loaders
Description: Default lazy module loaders for PIE assessment tools
Latest version: 0.3.29

# PIE Default Tool Loaders

Default lazy module loader mappings for built-in PIE assessment tools.

This package intentionally owns concrete `pie-tool-*` dependencies so toolkit core can remain dependency-light and cycle-safe.

## Usage

```ts
import { createDefaultToolRegistry } from "@pie-players/pie-assessment-toolkit";
import {
	DEFAULT_TOOL_MODULE_LOADERS,
	registerSectionToolModuleLoaders,
} from "@pie-players/pie-default-tool-loaders";

// Full default loaders (item + section tools)
const registry = createDefaultToolRegistry({
	toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
});

// Section-only loaders (for section toolbar bootstrap points)
registerSectionToolModuleLoaders(registry);
```
npm package: @pie-players/pie-theme-daisyui
Description: DaisyUI token bridge for PIE theme variables
Latest version: 0.3.29

# @pie-players/pie-theme-daisyui

Bridge DaisyUI theme tokens to PIE `--pie-*` variables.

When using `@pie-players/pie-theme`, Daisy token translation is applied automatically by the built-in Daisy provider in `pie-theme`.

This package is token-focused: it maps DaisyUI CSS variables to PIE theme variables only.
Shared `pie-*` component class styling (for example debugger overlays or answer-eliminator classes) belongs in `@pie-players/pie-theme/components.css`.

## Usage

```ts
import "@pie-players/pie-theme-daisyui/bridge.css";
```

Or apply variables with JavaScript:

```ts
import { applyDaisyThemeToElement } from "@pie-players/pie-theme-daisyui";
```

Register the explicit Daisy provider adapter (optional, mostly useful for custom bootstraps):

```ts
import { registerDaisyThemeProvider } from "@pie-players/pie-theme-daisyui";

registerDaisyThemeProvider();
```

You can also read resolved Daisy tokens from an element and map them:

```ts
import {
	readDaisyThemeTokensFromElement,
	mapResolvedDaisyThemeToPieVariables,
	daisyThemeProviderAdapter
} from "@pie-players/pie-theme-daisyui";
```
npm package: @pie-players/pie-tool-line-reader
Description: Reading guide overlay tool for PIE assessment player
Latest version: 0.3.29

# @pie-players/pie-tool-line-reader

Draggable reading guide overlay tool for PIE assessment players.

## Overview

Provides a movable, resizable colored overlay strip that helps students focus on specific lines of text during assessments. Supports both highlight mode (colored strip) and obscure/masking mode (darkens everything except the reading window).

## Custom Element

Tag: `pie-tool-line-reader`

| Attribute | Type | Default | Description |
|---|---|---|---|
| `visible` | `boolean` | `false` | Controls visibility |
| `tool-id` | `string` | `'lineReader'` | Identifier used by the ToolCoordinator |

## Features

- Drag to reposition; bottom handle to resize height
- Two modes: **highlight** (colored overlay) and **obscure** (masks surrounding content)
- Five color options (yellow, blue, pink, green, orange) with adjustable opacity
- Full keyboard support:
  - Arrow keys to move
  - `+`/`-` to resize height
  - `C` to cycle colors
  - `[`/`]` to adjust opacity
  - `M` to toggle highlight/masking mode
- Screen reader announcements for all state changes
- Connects to `AssessmentToolkitRuntimeContext` for ToolCoordinator integration

## Integration

Registered through the `ToolRegistry` as `lineReader`. Managed by `ToolkitCoordinator` via `tools.placement`.

## License

MIT
npm package: @pie-players/pie-tool-periodic-table
Description: Interactive periodic table reference tool for PIE assessment player
Latest version: 0.3.29

# @pie-players/pie-tool-periodic-table

Interactive periodic table reference tool for PIE assessment players.

## Overview

Provides a full periodic table with element details, category filtering, and keyboard navigation. Rendered as a custom element with shadow DOM.

## Custom Element

Tag: `pie-tool-periodic-table`

| Attribute | Type | Default | Description |
|---|---|---|---|
| `visible` | `boolean` | `false` | Controls visibility |
| `tool-id` | `string` | `'periodicTable'` | Identifier used by the ToolCoordinator |

## Features

- Full 118-element periodic table laid out on an 18x10 CSS grid
- Element detail panel showing symbol, name, atomic mass, atomic number, electron configuration, and phase
- Category filter badges (Alkali Metal, Transition Metal, Noble Gas, etc.)
- Category-based color coding
- Keyboard accessible: focusable cells with Enter/Space to select

## Integration

Registered through the `ToolRegistry` as `periodicTable`. Managed by `ToolkitCoordinator` via `tools.placement`.

## License

MIT
npm package: @pie-players/pie-tool-ruler
Description: Ruler measurement tool for PIE assessment player
Latest version: 0.3.29

# Ruler Tool

A draggable and rotatable ruler measurement tool for the PIEoneer assessment player.

## Features

- **Draggable**: Click and drag anywhere on the ruler to move it
- **Rotatable**: Use the green rotation handle or keyboard shortcuts
- **Unit Toggle**: Switch between inches and centimeters
- **Keyboard Navigation**:
  - Arrow keys: Move the ruler
  - Shift + Arrow keys: Rotate the ruler
  - PageUp/PageDown: Fine rotation control
  - U key: Toggle between inches and centimeters
- **Accessibility**: Full ARIA support and screen reader announcements

## Usage

```svelte
<script>
  // Consumers should import the CE entrypoint from the package exports:
  import '@pie-players/pie-tool-ruler/components/tool-ruler-element';

  let showRuler = $state(false);
</script>

<pie-tool-ruler visible={showRuler} toolId="ruler" />
```

## Props

- `visible` (boolean): Controls visibility of the tool
- `toolId` (string): Unique identifier for tool coordination (default: 'ruler')

## Based On

This implementation is based on production ruler tool patterns, adapted for the PIE architecture.
npm package: @pie-players/pie-tool-theme
Description: Theme tool for PIE assessment player - provides accessible themes and color schemes
Latest version: 0.3.29

# @pie-players/pie-tool-theme

Accessible color scheme selection tool for PIE assessment players.

## Overview

Provides a theme/color scheme picker that applies accessible color schemes via the `<pie-theme>` provider. Addresses WCAG 2.1 Level AA criteria 1.4.1, 1.4.3, and 1.4.11.

## Custom Element

Tag: `pie-tool-theme`

| Attribute | Type | Default | Description |
|---|---|---|---|
| `visible` | `boolean` | `false` | Controls visibility |
| `tool-id` | `string` | `'theme'` | Identifier used by the ToolCoordinator |
| `schemes` | `string` | `''` | JSON string of custom scheme definitions |

| JS Property | Type | Description |
|---|---|---|
| `schemeCatalog` | `object[]` | Array of scheme objects (alternative to the `schemes` attribute) |

## Features

- Dropdown scheme selector with color previews
- Discovers available schemes from `<pie-theme>` provider, `schemeCatalog` property, or `schemes` JSON attribute
- Persists selection to `localStorage` (key: `pie-color-scheme`)
- Applies scheme by setting the `scheme` attribute on the nearest `<pie-theme>` element
- Focus trap when visible; keyboard navigation with arrow keys and Escape
- Connects to `AssessmentToolkitRuntimeContext` for ToolCoordinator integration

## Integration

Registered through the `ToolRegistry` as `theme`. Managed by `ToolkitCoordinator` via `tools.placement`.

## License

MIT
npm package: @pie-players/pie-tts
Description: TTS interfaces and types for PIE Assessment Toolkit - No UI dependencies
Author: PIE Framework
Latest version: 0.3.29

# @pie-players/pie-tts

TTS interfaces and types for PIE Assessment Toolkit - Pure TypeScript with no UI dependencies.

## Purpose

This package provides the foundational interfaces and types for building TTS (Text-to-Speech) providers in the PIE ecosystem. It has **zero dependencies** and no UI framework requirements, making it suitable for:

- Implementing custom TTS providers
- Type-safe TTS integration
- Framework-agnostic TTS solutions

## What's Included

### Interfaces

- **`ITTSProvider`** - Stateless factory for creating TTS implementations
- **`ITTSProviderImplementation`** - Actual TTS playback implementation
- **`TTSProviderCapabilities`** - Feature support description
- **`TTSConfig`** - Provider configuration

### Types

- **`TTSFeature`** - Union type of supported features
- Configuration and capability types

## Installation

```bash
npm install @pie-players/pie-tts
# or
bun add @pie-players/pie-tts
```

## Usage

### Implementing a Custom TTS Provider

```typescript
import type {
  ITTSProvider,
  ITTSProviderImplementation,
  TTSConfig,
  TTSProviderCapabilities,
  TTSFeature
} from '@pie-players/pie-tts';

class MyTTSImplementation implements ITTSProviderImplementation {
  async speak(text: string): Promise<void> {
    // Your implementation
  }

  pause(): void { /* ... */ }
  resume(): void { /* ... */ }
  stop(): void { /* ... */ }
  isPlaying(): boolean { return false; }
  isPaused(): boolean { return false; }
}

export class MyTTSProvider implements ITTSProvider {
  readonly providerId = 'my-tts';
  readonly providerName = 'My TTS Provider';
  readonly version = '1.0.0';

  async initialize(config: TTSConfig): Promise<ITTSProviderImplementation> {
    return new MyTTSImplementation(config);
  }

  supportsFeature(feature: TTSFeature): boolean {
    return feature === 'pause' || feature === 'resume';
  }

  getCapabilities(): TTSProviderCapabilities {
    return {
      supportsPause: true,
      supportsResume: true,
      supportsWordBoundary: false,
      supportsVoiceSelection: true,
      supportsRateControl: true,
      supportsPitchControl: false,
    };
  }

  destroy(): void {
    // Cleanup if needed
  }
}
```

## Official Implementations

- **Browser TTS** (in `@pie-players/pie-assessment-toolkit`) - Uses Web Speech API, always available as fallback
- **Server TTS** (`@pie-players/tts-client-server`) - High-quality server-backed voices (Polly/Google/etc.) with speech marks

## Design Philosophy

This core package intentionally:
- ✅ Has **zero runtime dependencies**
- ✅ Contains **only TypeScript interfaces and types**
- ✅ Is **framework-agnostic** (no React, Svelte, Vue, etc.)
- ✅ Supports **pluggable architecture**
- ✅ Enables **type-safe TTS implementations**

## License

MIT

## Related Packages

- [@pie-players/pie-assessment-toolkit](../assessment-toolkit) - Includes TTSService and BrowserTTSProvider
- [@pie-players/tts-client-server](../tts-client-server) - Server-backed TTS provider client
npm package: @pie-players/tts-server-sc
Description: SchoolCity-backed provider for server-side TTS with speech marks support
Author: PIE Framework
Latest version: 0.3.29

# @pie-players/tts-server-sc

SchoolCity-backed server-side TTS provider for PIE projects.

This package uses SchoolCity as an internal Renaissance-backed reference implementation
for custom server-side TTS integrations. The intent is to demonstrate a reusable
"custom backend adapter" pattern you can apply to your own TTS service.

## What it does

- Signs short-lived JWT credentials for SchoolCity TTS.
- Calls SchoolCity synthesis endpoint.
- Fetches and normalizes word marks.
- Rebases offsets so marks align to original request text.
- Exposes a provider API compatible with the `@pie-players/tts-server-*` pattern.

## Positioning

- `@pie-players/tts-server-sc` is a host/server integration package.
- It is not a toolkit default option by itself.
- Toolkit defaults remain browser-backed until a host app explicitly configures server/custom TTS.

## Install

```bash
bun add @pie-players/tts-server-sc
```

## Required configuration

- `baseUrl`: SchoolCity TTS endpoint
- `apiKey`: signing key used to mint bearer JWT
- `issuer`: JWT `iss` claim

## Basic usage

```ts
import { SchoolCityServerProvider } from "@pie-players/tts-server-sc";

const provider = new SchoolCityServerProvider();
await provider.initialize({
  baseUrl: process.env.TTS_SCHOOLCITY_URL!,
  apiKey: process.env.TTS_SCHOOLCITY_API_KEY!,
  issuer: process.env.TTS_SCHOOLCITY_ISS!,
  defaultLanguage: "en-US",
  defaultSpeedRate: "medium",
  defaultCache: true,
});

const result = await provider.synthesize({
  text: "Hello world",
  language: "en-US",
  includeSpeechMarks: true,
});
```

## Dogfood adapter example (section-demos shape)

If you need to keep legacy response shape (`audioContent`, `word`) while reusing provider logic:

```ts
const assets = await provider.synthesizeWithAssets({
  text,
  language: lang_id,
  providerOptions: { speedRate, cache },
});

return json({
  audioContent: assets.audioContent,
  word: assets.word,
  speechMarks: assets.speechMarks,
});
```

## Bring-your-own backend mapping

To adapt this package pattern to another custom provider, replace:

- **Auth signer**: `toBearerToken()` strategy (JWT/API key/OAuth as needed)
- **Upstream request shape**: request payload builder (`text`, language, speed/rate fields)
- **Asset contract**: response extraction (`audioContent`, marks URL, or inline marks)
- **Marks parser**: JSONL/JSON parsing and normalization rules
- **Offset normalization**: rebasing logic if upstream marks are not indexed to original text

Keep the provider surface (`initialize`, `synthesize`, `synthesizeWithAssets`) so host
routes can stay thin and consistent.

## Notes on SSML

- The provider accepts plain text and SSML fragments.
- If full `<speak>...</speak>` is provided, it is normalized to avoid nested `<speak>` wrappers when SC applies its own wrapping behavior.
- Mark rebasing is best-effort and intentionally deterministic to keep highlighting stable.
npm package: @pie-qti/element-schemas
Description: JSON schemas for PIE element validation
Latest version: 0.1.4

# @pie-qti/element-schemas

JSON schemas for PIE element validation. This package contains schemas extracted from PIE element packages for use in validation without requiring the full element packages as dependencies.

## Usage

```typescript
import { loadSchema, loadAllSchemas } from '@pie-qti/element-schemas';

// Load a specific schema
const mcSchema = await loadSchema('multiple-choice');

// Load all schemas
const allSchemas = await loadAllSchemas();
// Returns Map: '@pie-element/multiple-choice' => schema object
```

## Available Schemas

- `multiple-choice` - Multiple choice questions
- `extended-text-entry` - Extended text entry questions

## Keeping Schemas Up-to-Date

Schemas are synced from the [pie-elements](https://github.com/pie-framework/pie-elements) repository.

### Prerequisites

Ensure `pie-elements` is checked out as a sibling directory:

```bash
cd /path/to/projects
git clone https://github.com/pie-framework/pie-elements.git
```

### Sync Schemas

To update schemas from pie-elements:

```bash
bun --filter @pie-qti/element-schemas sync
```

This will:
1. Read schemas from `pie-elements/packages/*/docs/pie-schema.json`
2. Copy them to `src/schemas/`
3. Update `schema-versions.json` with version metadata
4. Skip schemas that haven't changed (based on content hash)

### Check for Updates

To check if local schemas are up-to-date:

```bash
bun --filter @pie-qti/element-schemas check-updates
```

This is useful in CI to detect schema drift.

## Schema Versions

The `schema-versions.json` file tracks:
- Source package version
- Content hash (SHA-256)
- Sync timestamp
- Source file path

Example:
```json
{
  "multiple-choice": {
    "version": "7.2.1",
    "schemaHash": "abc123def456",
    "syncedAt": "2025-12-24T10:00:00Z",
    "sourcePath": "/path/to/pie-elements/packages/multiple-choice/docs/pie-schema.json"
  }
}
```

## Adding New Elements

To add a new PIE element schema:

1. Add the element name to `ELEMENTS_TO_SYNC` in `scripts/sync-schemas.ts`
2. Add the element type to `AVAILABLE_SCHEMAS` in `src/index.ts`
3. Run `bun --filter @pie-qti/element-schemas sync` to pull the schema
4. Commit the new schema file and updated metadata

## CI Integration

Example GitHub Action to check for schema drift:

```yaml
name: Check PIE Schemas
on: [push, pull_request]
jobs:
  check-schemas:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: oven-sh/setup-bun@v1
      - run: bun install
      - name: Clone pie-elements
        run: git clone --depth 1 https://github.com/pie-framework/pie-elements.git ../pie-elements
      - name: Check schema versions
        run: bun --filter @pie-qti/element-schemas check-updates
```
npm package: @pie-qti/ims-cp-core
Description: Core IMS Content Package manifest parsing utilities (browser + Node.js)
Latest version: 0.1.4

# @pie-qti/ims-cp-core

Core IMS Content Package utilities for manifest parsing and resource resolution.

**100% browser-compatible** - works in both browser and Node.js environments.

## Features

- **Manifest Parsing**: Parse IMS Content Package manifest files (imsmanifest.xml)
- **Localized Resources**: Extract and manage multilingual content variants
- **Passage Reusability**: Detect and deduplicate reusable passage content
- **Universal Hashing**: SHA-256 hashing using Web Crypto API (browser) or Node.js crypto

## Installation

```bash
bun add @pie-qti/ims-cp-core
```

## Usage

### Manifest Parsing

```typescript
import { parseManifest } from '@pie-qti/ims-cp-core';

const manifestXml = `<?xml version="1.0"?>
<manifest identifier="package-1" ...>
  ...
</manifest>`;

const manifest = parseManifest(manifestXml, '/path/to/package');
console.log(manifest.items); // Array of QTI items
console.log(manifest.tests); // Array of QTI tests
```

### Localized Resources

```typescript
import { buildLocalizedManifest } from '@pie-qti/ims-cp-core/localized-resources';

const localized = buildLocalizedManifest(manifest, 'en-US');
console.log(localized.availableLocales); // Set of all locales
```

### Passage Reusability

```typescript
import { generateStablePassageId, PassageRegistry } from '@pie-qti/ims-cp-core/passage-reusability';

// Generate stable ID from content (async)
const id = await generateStablePassageId({
  content: '<p>The Industrial Revolution...</p>'
});

// Track passage references across a package
const registry = new PassageRegistry();
registry.registerReference('item-1', {
  id: 'passage-123',
  source: 'file',
  isReusable: false
});
```

## API

### Breaking Changes from to-pie

- `generateStablePassageId()` is now **async** (returns `Promise<string>`)
- `PassageRegistry.detectAndMergeDuplicates()` is now **async**

## License

MIT
npm package: @pie-qti/player-elements
Description: QTI (2.x and 3.0) player web components (custom elements) for framework-agnostic embedding
Latest version: 0.1.4

# @pie-qti/player-elements

Framework-agnostic **Web Components (Custom Elements)** for the QTI 2.x players in this repo.

## Elements

- `pie-qti-item-player`
- `pie-qti-assessment-player`

## Install

```bash
npm install @pie-qti/player-elements
```

## Register elements

```js
import '@pie-qti/player-elements/register';
```

## `pie-qti-item-player`

### Pass QTI

Prefer setting the XML via property (attributes are awkward for large XML strings):

```js
import '@pie-qti/player-elements/register';

const el = document.querySelector('pie-qti-item-player');
el.itemXml = qtiItemXmlString;
el.identifier = 'item-1';
el.title = 'My Item';
el.role = 'candidate'; // QTI 2.x standard role
```

### Listen for interactions/responses

```js
el.addEventListener('response-change', (e) => {
  // e.detail = { responseId, value, responses }
  console.log(e.detail.responseId, e.detail.value);
});
```

## `pie-qti-assessment-player`

### Standard (B): pass `assessmentTest` XML + base URL for `href` item resolution

```js
import '@pie-qti/player-elements/register';

const el = document.querySelector('pie-qti-assessment-player');
el.assessmentTestXml = assessmentTestXmlString; // QTI <assessmentTest>...</assessmentTest>
el.itemBaseUrl = 'https://my-cdn.example.com/qti/'; // used to resolve assessmentItemRef@href
```

### Fallback/convenience (A): pass `assessmentTest` XML + an in-memory item map

```js
import '@pie-qti/player-elements/register';

const el = document.querySelector('pie-qti-assessment-player');
el.assessmentTestXml = assessmentTestXmlString;

// Map keys can be either the assessmentItemRef@href value OR the assessmentItemRef@identifier
el.items = {
  'items/item-1.xml': item1Xml,
  'ITEM-2': item2Xml
};
```

### (Optional) config

```js
el.config = {
  role: 'candidate', // QTI 2.x standard role
  navigationMode: 'nonlinear', // QTI 2.x navigation mode
  showSections: true
};
```

### Listen for lifecycle + state events

```js
el.addEventListener('item-change', (e) => console.log('item', e.detail));
el.addEventListener('section-change', (e) => console.log('section', e.detail));
el.addEventListener('response-change', (e) => console.log('responses', e.detail.responses));
el.addEventListener('submit', (e) => console.log('results', e.detail.results));
el.addEventListener('complete', () => console.log('complete'));

// Optional loading lifecycle (useful if you rely on base-url fetching)
el.addEventListener('load-start', () => console.log('loading...'));
el.addEventListener('load-end', () => console.log('loaded'));
el.addEventListener('load-error', (e) => console.error(e.detail.message));
```

### Imperative API

```js
await el.next();
await el.previous();
await el.navigateTo(0);
await el.navigateToSection('section-1');
const results = await el.submit();
const state = el.getState();
el.restoreState(state);
```