@panelwave/player 1.0.1 → 1.2.0
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/README.md +66 -20
- package/fesm2022/panelwave-player.mjs +511 -167
- package/fesm2022/panelwave-player.mjs.map +1 -1
- package/index.d.ts +212 -27
- package/package.json +4 -4
- package/src/assets/i18n/de.json +26 -1
- package/src/assets/i18n/en.json +26 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ story variables, multilingual text and paywalls, all in one component.
|
|
|
7
7
|
|
|
8
8
|
[](https://www.npmjs.com/package/@panelwave/player)
|
|
9
9
|
[](https://github.com/panelwave/player/blob/master/LICENSE)
|
|
10
|
-
[](https://angular.dev)
|
|
10
|
+
[](https://angular.dev)
|
|
11
11
|
|
|
12
12
|
**[Live demo](https://panelwave.github.io/player/)** ·
|
|
13
13
|
**[Documentation](https://docs.panelwave.org/player/overview)** ·
|
|
@@ -53,8 +53,10 @@ it into a complete reading experience:
|
|
|
53
53
|
|
|
54
54
|
Stories are authored in the PanelWave CMS or written by hand against the
|
|
55
55
|
[open JSON Schema](https://github.com/panelwave/schema). The player supports
|
|
56
|
-
|
|
57
|
-
typography style presets
|
|
56
|
+
manifests up to format 1.6 (video panels, edge-transition inheritance,
|
|
57
|
+
typography style presets, the infinite canvas, paywall rules unlocked by any
|
|
58
|
+
of several products, each Buy option named and priced from `paywall.products`; 1.5's asset folders and localization blocks are
|
|
59
|
+
authoring metadata the player doesn't need).
|
|
58
60
|
|
|
59
61
|
## Installation
|
|
60
62
|
|
|
@@ -62,8 +64,16 @@ typography style presets and the infinite canvas.
|
|
|
62
64
|
npm install @panelwave/player @ngx-translate/core
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
Peer dependencies: `@angular/core` and `@angular/common` **^20**,
|
|
66
|
-
`@ngx-translate/core` **^17**, `rxjs` **^7.8**.
|
|
67
|
+
Peer dependencies: `@angular/core` and `@angular/common` **^20, ^21 or ^22**,
|
|
68
|
+
`@ngx-translate/core` **^17 or ^18**, `rxjs` **^7.8**.
|
|
69
|
+
|
|
70
|
+
**Angular compatibility.** The package is compiled with Angular 20 in partial
|
|
71
|
+
(linker) mode, so newer Angular versions link it at build time. Every CI run
|
|
72
|
+
installs the packed library into a fresh `ng new` app on Angular 20, 21 and
|
|
73
|
+
22 with ngx-translate 18, and on Angular 20 and 22 with ngx-translate 17
|
|
74
|
+
(strict peer resolution, production build, headless render of a sample
|
|
75
|
+
manifest); the player works with zone.js and zoneless change detection (the
|
|
76
|
+
default for new Angular 21+ apps).
|
|
67
77
|
|
|
68
78
|
## Setup
|
|
69
79
|
|
|
@@ -72,13 +82,13 @@ for its UI strings:
|
|
|
72
82
|
|
|
73
83
|
```typescript
|
|
74
84
|
// app.config.ts
|
|
75
|
-
import { ApplicationConfig,
|
|
85
|
+
import { ApplicationConfig, inject } from '@angular/core';
|
|
76
86
|
import { HttpClient, provideHttpClient } from '@angular/common/http';
|
|
77
|
-
import { TranslateLoader,
|
|
87
|
+
import { TranslateLoader, TranslationObject, provideTranslateService } from '@ngx-translate/core';
|
|
78
88
|
import { Observable } from 'rxjs';
|
|
79
89
|
|
|
80
90
|
class PlayerTranslateLoader implements TranslateLoader {
|
|
81
|
-
|
|
91
|
+
private readonly http = inject(HttpClient);
|
|
82
92
|
getTranslation(lang: string): Observable<TranslationObject> {
|
|
83
93
|
return this.http.get<TranslationObject>(`./assets/i18n/${lang}.json`);
|
|
84
94
|
}
|
|
@@ -87,15 +97,21 @@ class PlayerTranslateLoader implements TranslateLoader {
|
|
|
87
97
|
export const appConfig: ApplicationConfig = {
|
|
88
98
|
providers: [
|
|
89
99
|
provideHttpClient(),
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}),
|
|
94
|
-
),
|
|
100
|
+
provideTranslateService({
|
|
101
|
+
loader: { provide: TranslateLoader, useClass: PlayerTranslateLoader },
|
|
102
|
+
}),
|
|
95
103
|
],
|
|
96
104
|
};
|
|
97
105
|
```
|
|
98
106
|
|
|
107
|
+
This works with ngx-translate 17 and 18. (`TranslateModule.forRoot()` still
|
|
108
|
+
works on 17 but no longer exists in 18.) Pass the loader as an explicit
|
|
109
|
+
provider as shown: on 18, `provideTranslateLoader(PlayerTranslateLoader)`
|
|
110
|
+
fails in production builds for a plain (non-`@Injectable`) class, because
|
|
111
|
+
18 tells classes from factory functions by their source text, and the
|
|
112
|
+
minified class no longer looks like one ("Class constructor … cannot be
|
|
113
|
+
invoked without 'new'").
|
|
114
|
+
|
|
99
115
|
**2. Assets and fonts.** Copy the player's UI translations into your app,
|
|
100
116
|
and include the balloon lettering fonts:
|
|
101
117
|
|
|
@@ -115,11 +131,15 @@ Caveat, Anton and 10 more). The commercial Blambot font **Ames Pro** is not
|
|
|
115
131
|
included. If your works use it, license it from [blambot.com](https://blambot.com)
|
|
116
132
|
and add your own `@font-face`. Otherwise balloons fall back to Comic Neue.
|
|
117
133
|
|
|
134
|
+
The player depends on `json-logic-js`, a CommonJS module. To silence the
|
|
135
|
+
build warning about it, add `"allowedCommonJsDependencies": ["json-logic-js"]`
|
|
136
|
+
to the same build options.
|
|
137
|
+
|
|
118
138
|
## Usage
|
|
119
139
|
|
|
120
140
|
```typescript
|
|
121
141
|
import { Component } from '@angular/core';
|
|
122
|
-
import { PlayerShellComponent } from '@panelwave/player';
|
|
142
|
+
import { PlayerShellComponent, type PlayerPanelChangeEvent } from '@panelwave/player';
|
|
123
143
|
|
|
124
144
|
@Component({
|
|
125
145
|
selector: 'app-reader',
|
|
@@ -129,13 +149,15 @@ import { PlayerShellComponent } from '@panelwave/player';
|
|
|
129
149
|
manifestUrl="/stories/my-story/panelwave.json"
|
|
130
150
|
locale="en-US"
|
|
131
151
|
[showToolbar]="true"
|
|
132
|
-
(panelChange)="onPanel($event
|
|
152
|
+
(panelChange)="onPanel($event)"
|
|
133
153
|
(error)="onError($event)" />
|
|
134
154
|
`,
|
|
135
155
|
styles: `:host { display: block; height: 100dvh; }`,
|
|
136
156
|
})
|
|
137
157
|
export class ReaderComponent {
|
|
138
|
-
onPanel(
|
|
158
|
+
onPanel(e: PlayerPanelChangeEvent) {
|
|
159
|
+
console.log(`panel ${e.panelId} in chapter ${e.chapter.id} (came from ${e.previousPanelId ?? 'start'})`);
|
|
160
|
+
}
|
|
139
161
|
onError(err: Error) { console.error(err); }
|
|
140
162
|
}
|
|
141
163
|
```
|
|
@@ -153,24 +175,31 @@ You can also pass a manifest object instead of a URL: `[manifest]="manifest"`
|
|
|
153
175
|
| `initialChapterId` / `initialPanelId` | `string` | | Start at a specific position (otherwise: bookmark, then chapter entry) |
|
|
154
176
|
| `initialVariables` | `Record<string, unknown>` | | Seed story variables once at start. May set `readOnly` variables, e.g. a verified `user.age` from your account system |
|
|
155
177
|
| `showToolbar` | `boolean` | `false` | Show the reader toolbar |
|
|
156
|
-
| `autoplay`
|
|
178
|
+
| `autoplay` | `boolean` | `false` | Start auto-advancing; the reader's toolbar toggle takes over afterwards |
|
|
179
|
+
| `secondsPerPanel` | `number` | `5` | Autoplay interval |
|
|
157
180
|
| `reducedMotion` | `boolean` | `false` | Force reduced motion (the OS setting and the reader's preference also apply) |
|
|
158
181
|
| `viewModeOverride` | `'auto' \| 'panel' \| 'canvas'` | `'auto'` | Force panel or infinite-canvas view |
|
|
159
182
|
| `entitlementSnapshot` | `EntitlementSnapshot` | anonymous | What the reader owns; turns the manifest's `paywall.rules` on |
|
|
160
183
|
| `entitlementEndpoint` / `readerToken` | `string` | | Let the player fetch the snapshot itself (`{workId}` is substituted) |
|
|
161
184
|
|
|
185
|
+
Inputs may change after init: a new `manifest` / `manifestUrl` reloads the
|
|
186
|
+
work (position, non-persistent variables, open overlays and autoplay reset),
|
|
187
|
+
a new `entitlementSnapshot` re-evaluates the gates, and `locale`,
|
|
188
|
+
`viewModeOverride`, `showToolbar`, `reducedMotion`, `secondsPerPanel` and
|
|
189
|
+
`autoplay` apply live.
|
|
190
|
+
|
|
162
191
|
### Outputs
|
|
163
192
|
|
|
164
193
|
| Output | Payload | Fires when |
|
|
165
194
|
|---|---|---|
|
|
166
195
|
| `ready` | `void` | The manifest is loaded and the player is ready |
|
|
167
|
-
| `panelChange` | `{ panel, chapter }` | The reader moves to a new panel |
|
|
196
|
+
| `panelChange` | `PlayerPanelChangeEvent`: `{ panel, chapter, panelId, previousPanelId? }` | The reader moves to a new panel. `panelId` is the panel's key in `chapter.panels` (read the id from here: `panel.id` is optional in the format and usually absent); `previousPanelId` is the panel before it, unset for the first panel after a (re)load |
|
|
168
197
|
| `chapterChange` | `Chapter` | A chapter boundary is crossed |
|
|
169
198
|
| `variableChange` | `{ key, value }` | A story variable changes |
|
|
170
199
|
| `localeChange` | `LocaleCode` | The language is switched |
|
|
171
200
|
| `navigationAttempt` | `{ direction, target? }` | Any navigation attempt, allowed or not |
|
|
172
201
|
| `cameraChange` | `CameraState` | The canvas-view camera moves |
|
|
173
|
-
| `paywallAction` | `{ action, gate }` | The reader clicks purchase
|
|
202
|
+
| `paywallAction` | `{ action, gate, productId? }` | The reader clicks Buy (`purchase`), Subscribe (`subscribe`), Sign in (`login`) or Maybe later (`dismiss`) on a paywall; `productId` is the chosen product id or tier |
|
|
174
203
|
| `ageVerified` | `AgeVerificationResult` | The reader answers an age gate |
|
|
175
204
|
| `likeChange` / `bookmarkChange` | `{ workId, … }` | Like or bookmark toggled (also stored locally) |
|
|
176
205
|
| `error` | `Error` | A loading or runtime error happens |
|
|
@@ -184,12 +213,29 @@ reader owns, and handle checkout yourself:
|
|
|
184
213
|
<pw-player-shell
|
|
185
214
|
[manifestUrl]="url"
|
|
186
215
|
[entitlementSnapshot]="{ subscriptionTier: null, purchasedProductIds: ['chapter-2'], ageVerified: false }"
|
|
187
|
-
(paywallAction)="
|
|
216
|
+
(paywallAction)="onPaywall($event)" />
|
|
188
217
|
```
|
|
189
218
|
|
|
190
219
|
Readers without access see the free preview and a paywall overlay, and
|
|
191
220
|
navigation stops at the gate. Works without paywall rules aren't affected.
|
|
192
221
|
|
|
222
|
+
- **Scopes:** `work` rules gate everything after their free preview;
|
|
223
|
+
`chapter` rules gate only the chapter named by `refId`, with the preview
|
|
224
|
+
counted within that chapter; `panel` rules gate exactly their panels;
|
|
225
|
+
`extras` rules lock an extras block, never panels.
|
|
226
|
+
- **Age:** `ageGate` / `minimumAge` is checked on top of the rule's
|
|
227
|
+
entitlement. The reader answers the age gate first, then gets the paywall
|
|
228
|
+
if the purchase or subscription is still missing.
|
|
229
|
+
- **Buy / Subscribe:** the overlay offers a Buy option per product of a
|
|
230
|
+
purchase rule and a Subscribe option per tier of a subscription rule (name,
|
|
231
|
+
description and price from the rule), next to Sign in and Maybe later.
|
|
232
|
+
`paywallAction` emits `{ action: 'purchase' | 'subscribe' | 'login' |
|
|
233
|
+
'dismiss', gate, productId? }`; the options are also on `gate.options`.
|
|
234
|
+
- **After checkout:** call `refreshEntitlements(snapshot)` on the shell or
|
|
235
|
+
pass a new `entitlementSnapshot`; the overlay closes once the reader is
|
|
236
|
+
through. Story keys and swipes do nothing while the paywall or the age
|
|
237
|
+
gate is open.
|
|
238
|
+
|
|
193
239
|
### Going further
|
|
194
240
|
|
|
195
241
|
The package also exports the services and building blocks behind the shell:
|