Skip to content

Latest commit

 

History

History
252 lines (191 loc) · 8.76 KB

File metadata and controls

252 lines (191 loc) · 8.76 KB

Migration Guide: 1.3.0

This release adds the audio player as a first-class package. It also includes one breaking change for all existing StatefulReaderWrapper users.


New: Audio Player Support

StatefulReaderWrapper and usePublication now detect and handle the "audio" profile automatically. No changes are required to existing EPUB or WebPub integrations.

const { publication, profile, localDataKey } = usePublication({ url });
// profile may now be "epub" | "webPub" | "audio" | null

<StatefulReaderWrapper
  profile={ profile }
  publication={ publication }
  localDataKey={ localDataKey }
/>

Plugins

ReaderPlugins now accepts an audio factory alongside epub and webPub. Use createAudioDefaultPlugin to get the built-in audio plugin set, the same way createDefaultPlugin works for EPUB and WebPub:

import { createDefaultPlugin, createAudioDefaultPlugin } from "@edrlab/thorium-web/reader";

<StatefulReaderWrapper
  profile={ profile }
  publication={ publication }
  localDataKey={ localDataKey }
  plugins={{
    epub: () => [createDefaultPlugin()],
    audio: () => [createAudioDefaultPlugin()]
  }}
/>

The wrapper resolves only the factory matching the active profile — unused factories are never called.

Preferences

StatefulReaderWrapper accepts a preferences prop to pass initial preferences or a custom adapter to the underlying provider. The shape is profile-specific:

import { createAudioPreferences } from "@edrlab/thorium-web/audio";
import { createPreferences } from "@edrlab/thorium-web/reader";

// Audio
<StatefulReaderWrapper
  profile="audio"
  publication={ publication }
  localDataKey={ localDataKey }
  preferences={{
    initialPreferences: createAudioPreferences({ /* ... */ }),
    adapter: myAudioAdapter  // optional
  }}
/>

// EPUB / WebPub
<StatefulReaderWrapper
  profile="epub"
  publication={ publication }
  localDataKey={ localDataKey }
  preferences={{
    initialPreferences: createPreferences({ /* ... */ }),
    adapter: myAdapter  // optional
  }}
/>

Using StatefulPlayer directly

If you use StatefulPlayer from @edrlab/thorium-web/audio instead of StatefulReaderWrapper, you are responsible for wrapping it with the required providers. ThAudioPreferencesProvider is separate from ThPreferencesProvider — do not use the EPUB/WebPub one for audio.

import {
  StatefulPlayer,
  ThStoreProvider,
  ThAudioPreferencesProvider,
  ThI18nProvider,
  createAudioPreferences
} from "@edrlab/thorium-web/audio";

const myPreferences = createAudioPreferences({ /* ... */ });

<ThStoreProvider>
  <ThAudioPreferencesProvider initialPreferences={ myPreferences }>
    <ThI18nProvider>
      <StatefulPlayer
        publication={ publication }
        localDataKey={ localDataKey }
      />
    </ThI18nProvider>
  </ThAudioPreferencesProvider>
</ThStoreProvider>

Caution

All components from @edrlab/thorium-web/audio must use the providers from that same path. Using providers from @edrlab/thorium-web/reader or @edrlab/thorium-web/epub will create a separate context that the audio components cannot access.

See the Audio package documentation for the full guide.


Breaking: StatefulReaderWrapper now manages ThPreferencesProvider and ThI18nProvider internally

StatefulReaderWrapper now mounts the appropriate preferences provider (ThPreferencesProvider for EPUB/WebPub, ThAudioPreferencesProvider for audio) and ThI18nProvider internally. Remove these from your outer component tree. Keeping them will result in nested duplicate providers.

Before (1.2.0):

<ThStoreProvider>
  <ThPreferencesProvider initialPreferences={ myPreferences }>
    <ThI18nProvider>
      <StatefulReaderWrapper
        profile={ profile }
        publication={ publication }
        localDataKey={ localDataKey }
      />
    </ThI18nProvider>
  </ThPreferencesProvider>
</ThStoreProvider>

After (1.3.0):

<ThStoreProvider>
  <StatefulReaderWrapper
    profile={ profile }
    publication={ publication }
    localDataKey={ localDataKey }
    preferences={{ initialPreferences: myPreferences }}
  />
</ThStoreProvider>

Configuring ThI18nProvider through the wrapper

If you need to pass i18next options (extra namespaces, a custom backend path, etc.) that you previously passed directly to <ThI18nProvider>, use the i18n prop on StatefulReaderWrapper:

<StatefulReaderWrapper
  profile={ profile }
  publication={ publication }
  localDataKey={ localDataKey }
  i18n={{
    ns: ["thorium-shared", "thorium-web", "my-app"],
    defaultNS: ["my-app", "thorium-web", "thorium-shared"]
  }}
/>

Important

Array fields like ns and defaultNS are replaced, not merged with defaults. Always include "thorium-shared" and "thorium-web" when you extend them.


Breaking: useNavigator now returns a namespaced object

useNavigator no longer returns a flat navigator interface. The return value is now an object with three namespaced accessors — visual, media, and unified — reflecting the introduction of the audio navigator as a first-class citizen alongside the existing visual (EPUB/WebPub) navigator.

Before (1.2.0)

const { submitPreferences, getSetting, goLink, play, pause, ... } = useNavigator();

All methods were returned on a single flat object regardless of navigator type.

After (1.3.0)

const navigator = useNavigator();

// Visual navigator (EPUB / WebPub) — reading system settings, preferences, frames
const { submitPreferences, getSetting, preferencesEditor, getCframes, ... } = navigator.visual;

// Media navigator (Audio) — playback, seeking, track navigation
const { play, pause, seek, skipForward, skipBackward, submitPreferences, ... } = navigator.media;

// Unified interface — works across both navigator types
const { go, goLink, goForward, goBackward, currentLocator, previousLocator, nextLocator, isVisual } = navigator.unified;

Accessing .visual inside an audio player context (or .media inside an EPUB reader context) will throw at runtime. Use .unified for components that are shared across both profiles.

Which accessor to use

Situation Accessor
EPUB/WebPub-only component (settings, spacing, themes, zoom…) .visual
Audio-only component (play/pause, progress bar, volume, sleep timer…) .media
Component shared across both profiles (TOC navigation, position jumping…) .unified

unified interface

.unified prefers the visual navigator when both are present, falling back to media. It exposes the lowest-common-denominator API:

interface UnifiedNavigator {
  go(locator: Locator, animated: boolean, callback: (ok: boolean) => void): void;
  goLink(link: Link, animated: boolean, callback: (ok: boolean) => void): void;
  goForward(animated: boolean, callback: (ok: boolean) => void): void;
  goBackward(animated: boolean, callback: (ok: boolean) => void): void;
  currentLocator(): Locator | undefined;
  previousLocator(): Locator | null;
  nextLocator(): Locator | null;
  isVisual(): boolean;
  getCframes?(): (FrameManager | FXLFrameManager | WebPubFrameManager | undefined)[] | undefined;
  underlying: VisualNavigator | MediaNavigator; // escape hatch to the raw navigator
}

Quick migration reference

Before After
useNavigator().submitPreferences(…) useNavigator().visual.submitPreferences(…)
useNavigator().getSetting(key) useNavigator().visual.getSetting(key)
useNavigator().preferencesEditor useNavigator().visual.preferencesEditor
useNavigator().getCframes() useNavigator().visual.getCframes()
useNavigator().goLink(…) useNavigator().unified.goLink(…)
useNavigator().go(…) useNavigator().unified.go(…)
useNavigator().currentLocator() useNavigator().unified.currentLocator()

New: useTheming additions

useTheming has been extended with cover-based automatic theme generation. All new properties are optional.

New input props:

  • coverUrl: cover image URL to extract a theme from
  • autoThemeSource: "cover" or "system"
  • onCoverThemeGenerated: callback fired when cover theme tokens are ready

New return values:

  • coverThemeTokens: the generated ThemeTokens, or null if not yet ready
  • themeResolved: false while cover theme extraction is in progress — use this to defer rendering

New: ThSliderWithPresets and StatefulSliderWithPresets

A new core component combining a slider with a preset radio group, used internally by the audio player and available for custom integrations.

import { ThSliderWithPresets } from "@edrlab/thorium-web/core/components";

See Core Settings Components for the full API.