npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pie-players/pie-section-player

v0.3.29

Published

Web component for rendering QTI 3.0 assessment sections with passages and items

Readme

@pie-players/pie-section-player

Section rendering package with layout custom elements:

  • pie-section-player-splitpane
  • pie-section-player-vertical

The package no longer exposes the legacy pie-section-player layout orchestration API.

Install

npm install @pie-players/pie-section-player

Runtime boundary and migration

  • Browser-only package: @pie-players/pie-section-player registers custom elements and is intended for browser/DOM hosts, not plain Node runtime imports.
  • Node-import-safe packages (for server/runtime utilities) are documented in docs/setup/library-packaging-strategy.md.
  • Migration direction: prefer the stable default entry for side-effect registration:
import "@pie-players/pie-section-player";

If hosts need explicit registration control, keep using documented component entrypoints under @pie-players/pie-section-player/components/*.

Standalone browser variants for this package are intentionally deferred; the current supported contract is the default bundler entrypoints under dist.

Usage

Import the custom-element registration entrypoint in consumers:

import '@pie-players/pie-section-player/components/section-player-splitpane-element';
import '@pie-players/pie-section-player/components/section-player-vertical-element';
import '@pie-players/pie-section-player/components/section-player-item-card-element';
import '@pie-players/pie-section-player/components/section-player-passage-card-element';

Render in HTML/Svelte/JSX:

<pie-section-player-splitpane></pie-section-player-splitpane>

Set complex values (runtime, section, env) as JS properties.

Runtime Inputs

Both layout elements support:

  • runtime (object): primary coordinator/tools/player runtime bundle
  • section (object): assessment section payload
  • env (object): optional top-level override for { mode, role }
  • debug (boolean-like): verbose debug logging control ("true" enables, "false"/"0" disables)
  • toolbar-position (string): top|right|bottom|left|none
  • narrow-layout-breakpoint (number, optional): viewport width in px below which the layout collapses (split pane: single column; vertical: toolbar moves to top). Clamped to 400–2000; default 1100.
  • show-toolbar (boolean-like): accepts true/false and common string forms ("true", "false", "1", "0", "yes", "no")
  • Host extension props (JS properties only): toolRegistry, sectionHostButtons, itemHostButtons, passageHostButtons, hooks

When viewport width is within the collapsed range (~1100px and below), inline section toolbar positions (left/right) normalize to top so controls remain horizontally laid out and easier to access.

hooks.cardTitleFormatter remains active across responsive splitpane transitions (split -> stacked and stacked -> split), because title rendering is provided through shared card context rather than layout-specific state.

API direction: CE defaults first, JS customization for advanced cases

The intended usage model is:

  • CE props for default/standard flows (roughly 90% use cases):
    • assessment-id, section, section-id, attempt-id, debug
    • show-toolbar, toolbar-position, narrow-layout-breakpoint, enabled-tools, item-toolbar-tools, passage-toolbar-tools
  • JS API for advanced customization:
    • Get the controller handle via getSectionController() or waitForSectionController()
    • Listen to section-controller-ready
    • Apply custom policy/gating in host code (for example, domain-specific canNext based on controller events like section-items-complete-changed)
    • Compose forward/backward eligibility in host code using selectNavigation() + host state; there is intentionally no separate parallel CE gating API for this
    • Inject custom toolbar tooling with toolRegistry and optional host button arrays (sectionHostButtons, itemHostButtons, passageHostButtons)
    • Register host callbacks via hooks (for example hooks.cardTitleFormatter)

Example:

const host = document.querySelector("pie-section-player-splitpane") as any;
host.hooks = {
  cardTitleFormatter: (context: any) => {
    if (context.kind === "item") {
      return `Question ${context.itemIndex + 1}: ${context.item?.name || context.defaultTitle}`;
    }
    return context.passage?.name || context.defaultTitle;
  },
};

Advanced CE props are still supported as escape hatches (runtime, coordinator, createSectionController, etc.), but hosts should prefer JS/controller composition for non-standard behavior.

Navigation signals

  • item-selected: item-level navigation change within the current section in the SectionController broadcast stream (itemIndex, currentItemId, totalItems).
  • section-navigation-change: section-level navigation/selection change in the SectionController broadcast stream (previousSectionId, currentSectionId, reason).

Runtime precedence is explicit:

  • runtime values are primary for runtime fields (assessmentId, playerType, player, lazyInit, tools, accessibility, coordinator, createSectionController, isolation, env).
  • Top-level runtime-like props remain compatibility inputs and are merged with runtime values. For player, top-level values are merged first, then runtime.player overrides. Nested loaderOptions and loaderConfig are also merged with the same precedence.
  • Toolbar placement overrides (enabled-tools, item-toolbar-tools, passage-toolbar-tools) are normalized on top of the runtime tools config.
  • Tool configuration validation is canonical in toolkit initialization (pie-assessment-toolkit), including toolbar overlays. Use runtime.toolConfigStrictness (off | warn | error) to control warning-only vs fail-fast behavior.
  • TTS provider config must use tools.providers.textToSpeech (canonical). tools.providers.tts is rejected by validation.
  • Host tool overrides are additive:
    • toolRegistry overrides the default toolbar registry when provided
    • host buttons are appended per toolbar scope via sectionHostButtons, itemHostButtons, passageHostButtons

Debug logging can be controlled per section-player host:

  • Enable: <pie-section-player-splitpane debug="true">
  • Disable: <pie-section-player-splitpane debug="false"> (or debug="0")

You can also disable globally via window.PIE_DEBUG = false.

See the progressive demo routes in apps/section-demos/src/routes/(demos) (for example single-question/+page.svelte and session-hydrate-db/+page.svelte) for end-to-end host integrations.

Data flow and stability guarantees

Section-player follows a unidirectional flow model:

  • Inputs flow downward (runtime, section, env, toolbar options) into base/toolkit/layout/card render paths.
  • State updates flow upward as events (runtime-*, session-changed, controller change events) and are reconciled by runtime owners.
  • Layout/card components should not create competing sources of truth for composition/session.

Stability guarantees

For non-structural updates, section-player guarantees behavior stability:

  • Item/passage shell identity remains stable (no remount churn for response-only updates).
  • Pane-local scroll position remains stable in splitpane and vertical layouts.

Non-structural updates include:

  • response/session updates
  • tool toggles/config updates
  • runtime config changes that do not alter composition identity

Structural composition changes (new/removed/reordered entities) may legitimately re-render/remount affected nodes.

Custom layout authoring

For section layout authors, pie-section-player-shell is the primary abstraction:

  • Use pie-section-player-shell to place the section toolbar around your layout body.
  • Keep your custom layout logic focused on passages/items and layout UI.
  • Treat pie-section-player-base as internal runtime plumbing that wraps the shell.
  • Use pie-section-player-item-card and pie-section-player-passage-card as reusable card primitives.
  • Prefer shared context for cross-cutting card render plumbing (resolved player tag/action) over repeated prop drilling.

Minimal pattern for package layout components:

<pie-section-player-base runtime={effectiveRuntime} {section} section-id={sectionId} attempt-id={attemptId}>
  <pie-section-player-shell
    show-toolbar={showToolbar}
    toolbar-position={toolbarPosition}
    enabled-tools={enabledTools}
    toolRegistry={toolRegistry}
    sectionHostButtons={sectionHostButtons}
  >
    <!-- layout-specific body -->
    <pie-section-player-passage-card
      passage={passage}
      playerParams={passagePlayerParams}
      passageToolbarTools={passageToolbarTools}
      toolRegistry={toolRegistry}
      hostButtons={passageHostButtons}
    ></pie-section-player-passage-card>
    <pie-section-player-item-card
      item={item}
      canonicalItemId={canonicalItemId}
      playerParams={itemPlayerParams}
      itemToolbarTools={itemToolbarTools}
      toolRegistry={toolRegistry}
      hostButtons={itemHostButtons}
    ></pie-section-player-item-card>
  </pie-section-player-shell>
</pie-section-player-base>

JS API example for advanced host policy

const host = document.querySelector("pie-section-player-splitpane") as any;
const controller = await host.waitForSectionController?.(5000);
let sectionComplete = false;

const unsubscribe = controller?.subscribe?.((event: any) => {
  if (event?.type === "section-items-complete-changed") {
    sectionComplete = event.complete === true;
  }
});

function canAdvance() {
  const nav = host.selectNavigation?.();
  return Boolean(nav?.canNext && sectionComplete);
}

If you already have a ToolkitCoordinator from toolkit-ready, prefer helper subscriptions for host logic:

const unsubscribeItem = coordinator.subscribeItemEvents({
  sectionId,
  attemptId,
  listener: (event: any) => {
    // item-scoped stream
  },
});

const unsubscribeSection = coordinator.subscribeSectionLifecycleEvents({
  sectionId,
  attemptId,
  listener: (event: any) => {
    // section-loading-complete / section-items-complete-changed / section-error / section-navigation-change
  },
});

Use subscribeSectionEvents(...) only for advanced mixed filtering requirements.

Item-level observability configuration

Item-level resource observability is configured on the embedded pie-item-player via loaderConfig. In section-player integrations, pass this through runtime.player.loaderConfig.

import { ConsoleInstrumentationProvider } from "@pie-players/pie-players-shared";

const provider = new ConsoleInstrumentationProvider({ useColors: true });
await provider.initialize({ debug: true });

sectionPlayerEl.runtime = {
  playerType: "esm",
  player: {
    loaderConfig: {
      trackPageActions: true,
      instrumentationProvider: provider,
      maxResourceRetries: 3,
      resourceRetryDelay: 500,
    },
    loaderOptions: {
      esmCdnUrl: "https://cdn.jsdelivr.net/npm",
    },
  },
};

Important:

  • loaderOptions controls bundle loading. loaderConfig controls runtime resource monitoring.
  • Custom providers (functions/instances) must be passed as JS properties (runtime object), not serialized string attributes.

Instrumentation ownership and semantics

Section-player instrumentation is provider-agnostic and uses the shared InstrumentationProvider contract.

  • Canonical provider path: runtime.player.loaderConfig.instrumentationProvider
  • With trackPageActions: true, missing/undefined providers use the default New Relic provider path.
  • instrumentationProvider: null explicitly disables instrumentation.
  • Invalid provider objects are ignored (optional debug warning), also no-op.
  • Existing item-player behavior is preserved.
  • For local debug overlays, compose providers (for example NewRelicInstrumentationProvider + DebugPanelInstrumentationProvider) through CompositeInstrumentationProvider.
  • Toolkit telemetry forwarding uses the same provider path, so tool/backend operational events are visible alongside section events when toolkit is mounted.

Section-player owned canonical stream:

  • pie-section-readiness-change
  • pie-section-interaction-ready
  • pie-section-ready
  • pie-section-controller-ready
  • pie-section-session-changed
  • pie-section-composition-changed
  • pie-section-runtime-error

Framework boundary stream (from toolkit, re-emitted through section-player wrappers):

  • framework-error (canonical)
  • runtime-error (compatibility signal)

If toolkit is mounted, toolkit lifecycle events are emitted on a separate pie-toolkit-* stream. This separation avoids semantic overlap; bridge dedupe is a defensive safety net only.

Toolkit tool/backend operational stream:

  • pie-tool-init-start|success|error
  • pie-tool-backend-call-start|success|error
  • pie-tool-library-load-start|success|error

Item session management

Section session data can be managed either through persistence hooks or directly through the controller API.

const host = document.querySelector("pie-section-player-splitpane") as any;
const controller = await host.waitForSectionController?.(5000);

// Read current section session snapshot.
const currentSession = controller?.getSession?.();

// Replace section session state (resume from backend snapshot).
await controller?.applySession?.({
  currentItemIndex: 0,
  visitedItemIdentifiers: ["q1"],
  itemSessions: {
    q1: {
      itemIdentifier: "q1",
      pieSessionId: "q1-session",
      session: { id: "q1-session", data: [{ id: "choice", value: "a" }] }
    }
  }
}, { mode: "replace" });

// Update a single item session directly.
await controller?.updateItemSession?.("q1", {
  session: { id: "q1-session", data: [{ id: "choice", value: "b" }] },
  complete: true,
});

The same controller snapshot is what the persistence strategy saves/loads. When a controller is reused for the same sectionId/attemptId, updateInput() refreshes composition input while preserving in-memory section session data.

Exports

Published exports are intentionally minimal:

  • @pie-players/pie-section-player
  • @pie-players/pie-section-player/components/section-player-splitpane-element
  • @pie-players/pie-section-player/components/section-player-vertical-element
  • @pie-players/pie-section-player/components/section-player-kernel-host-element
  • @pie-players/pie-section-player/components/section-player-shell-element
  • @pie-players/pie-section-player/components/section-player-item-card-element
  • @pie-players/pie-section-player/components/section-player-passage-card-element
  • @pie-players/pie-section-player/contracts/layout-contract
  • @pie-players/pie-section-player/contracts/public-events
  • @pie-players/pie-section-player/contracts/runtime-host-contract
  • @pie-players/pie-section-player/contracts/layout-parity-metadata
  • @pie-players/pie-section-player/contracts/host-hooks
  • @pie-players/pie-section-player/policies

Development

bun run --cwd packages/section-player dev
bun run --cwd packages/section-player check
bun run --cwd packages/section-player build