@panelwave/player 1.6.0 → 1.7.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/fesm2022/panelwave-player.mjs +132 -9
- package/fesm2022/panelwave-player.mjs.map +1 -1
- package/index.d.ts +24 -0
- package/package.json +1 -1
|
@@ -5456,6 +5456,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
5456
5456
|
* Manages audio playback with Web Audio API
|
|
5457
5457
|
*/
|
|
5458
5458
|
const ROLES = ['ambient', 'music', 'voiceover', 'sfx'];
|
|
5459
|
+
/**
|
|
5460
|
+
* Whether an audio URL must be loaded with `crossOrigin = 'anonymous'`: an
|
|
5461
|
+
* http(s) file on another origin than the page. Same-origin files, data:/
|
|
5462
|
+
* blob: URLs and `file:` pages (the offline self-hosting package) stay in
|
|
5463
|
+
* no-CORS mode — CORS requests from a file: page fail.
|
|
5464
|
+
*/
|
|
5465
|
+
function needsCorsMode(url, pageHref = typeof location !== 'undefined' ? location.href : '') {
|
|
5466
|
+
try {
|
|
5467
|
+
const target = new URL(url, pageHref || undefined);
|
|
5468
|
+
if (target.protocol !== 'http:' && target.protocol !== 'https:') {
|
|
5469
|
+
return false;
|
|
5470
|
+
}
|
|
5471
|
+
const page = pageHref ? new URL(pageHref) : null;
|
|
5472
|
+
if (page && page.protocol !== 'http:' && page.protocol !== 'https:') {
|
|
5473
|
+
return false;
|
|
5474
|
+
}
|
|
5475
|
+
return !page || target.origin !== page.origin;
|
|
5476
|
+
}
|
|
5477
|
+
catch {
|
|
5478
|
+
return false;
|
|
5479
|
+
}
|
|
5480
|
+
}
|
|
5459
5481
|
/**
|
|
5460
5482
|
* Audio Engine Service
|
|
5461
5483
|
* Provides Web Audio API-based audio playback with multiple buses.
|
|
@@ -5859,8 +5881,15 @@ class AudioEngineService {
|
|
|
5859
5881
|
if (!bus) {
|
|
5860
5882
|
throw new Error(`Invalid role: ${track.role}`);
|
|
5861
5883
|
}
|
|
5862
|
-
// Create audio element
|
|
5863
|
-
|
|
5884
|
+
// Create audio element. A file from another origin (object storage, a
|
|
5885
|
+
// CDN) must be fetched in CORS mode: routed through Web Audio
|
|
5886
|
+
// (createMediaElementSource below), a no-CORS cross-origin element plays
|
|
5887
|
+
// but outputs silence. crossOrigin has to be set before the src.
|
|
5888
|
+
const audio = new Audio();
|
|
5889
|
+
if (needsCorsMode(track.url)) {
|
|
5890
|
+
audio.crossOrigin = 'anonymous';
|
|
5891
|
+
}
|
|
5892
|
+
audio.src = track.url;
|
|
5864
5893
|
audio.loop = track.loop || false;
|
|
5865
5894
|
const rate = typeof track.playbackRate === 'number' && isFinite(track.playbackRate) ? Math.max(0.25, Math.min(4, track.playbackRate)) : 1;
|
|
5866
5895
|
if (rate !== 1) {
|
|
@@ -20119,6 +20148,7 @@ class PlayerShellComponent {
|
|
|
20119
20148
|
}
|
|
20120
20149
|
// Set initial locale and configure translations
|
|
20121
20150
|
this.applyLocale();
|
|
20151
|
+
this.lastTrackedLocale = this.locale;
|
|
20122
20152
|
// Check if page view is available
|
|
20123
20153
|
const loadedManifest = this.manifestService.getManifest();
|
|
20124
20154
|
if (loadedManifest?.chapters) {
|
|
@@ -20340,6 +20370,16 @@ class PlayerShellComponent {
|
|
|
20340
20370
|
panelId,
|
|
20341
20371
|
previousPanelId: this.lastPanelChangeId,
|
|
20342
20372
|
});
|
|
20373
|
+
// One `transition` per move between two panels (the flow analytics
|
|
20374
|
+
// aggregate edges from it); the first panel of a session has none.
|
|
20375
|
+
if (panelId && this.lastPanelChangeId && this.lastPanelChangeId !== panelId) {
|
|
20376
|
+
this.trackingService.track('transition', {
|
|
20377
|
+
fromPanelId: this.lastPanelChangeId,
|
|
20378
|
+
toPanelId: panelId,
|
|
20379
|
+
panelId,
|
|
20380
|
+
chapterId: this.currentChapter.id,
|
|
20381
|
+
});
|
|
20382
|
+
}
|
|
20343
20383
|
this.lastPanelChangeId = panelId || undefined;
|
|
20344
20384
|
this.trackPanelView(panel, this.currentChapter);
|
|
20345
20385
|
this.updatePreloadTargets();
|
|
@@ -20353,7 +20393,17 @@ class PlayerShellComponent {
|
|
|
20353
20393
|
this.playerState.locale$
|
|
20354
20394
|
.pipe(skip(1), distinctUntilChanged$1(), takeUntil(this.destroy$))
|
|
20355
20395
|
.subscribe((locale) => {
|
|
20396
|
+
const fromLocale = this.lastTrackedLocale;
|
|
20397
|
+
this.lastTrackedLocale = locale;
|
|
20356
20398
|
this.localeChange.emit(locale);
|
|
20399
|
+
if (fromLocale && fromLocale !== locale) {
|
|
20400
|
+
this.trackingService.track('lang_change', {
|
|
20401
|
+
from: fromLocale,
|
|
20402
|
+
to: locale,
|
|
20403
|
+
panelId: this.getCurrentPanelId(),
|
|
20404
|
+
chapterId: this.currentChapter?.id,
|
|
20405
|
+
});
|
|
20406
|
+
}
|
|
20357
20407
|
});
|
|
20358
20408
|
}
|
|
20359
20409
|
/**
|
|
@@ -20590,6 +20640,13 @@ class PlayerShellComponent {
|
|
|
20590
20640
|
// A choice: its path appears in page view, the other options' panels stay placeholders.
|
|
20591
20641
|
this.madeChoices.set(evt.panelId, effect.to);
|
|
20592
20642
|
this.refreshBranchVisibility();
|
|
20643
|
+
if (this.currentChapter) {
|
|
20644
|
+
this.trackDecision(this.currentChapter.id, evt.panelId, effect.to, {
|
|
20645
|
+
choiceId: evt.hotspot.id,
|
|
20646
|
+
choiceLabel: this.localizedText(evt.hotspot.label),
|
|
20647
|
+
source: 'hotspot',
|
|
20648
|
+
});
|
|
20649
|
+
}
|
|
20593
20650
|
}
|
|
20594
20651
|
this.applyHotspotEffect(effect);
|
|
20595
20652
|
this.refreshVariableContext();
|
|
@@ -20794,6 +20851,24 @@ class PlayerShellComponent {
|
|
|
20794
20851
|
this.refreshVariableContext();
|
|
20795
20852
|
this.variableChange.emit({ key, value });
|
|
20796
20853
|
}
|
|
20854
|
+
/**
|
|
20855
|
+
* Host-side variable write after load (the CMS preview's live variable
|
|
20856
|
+
* editor, an embed bridge): the value lands in its definition's declared
|
|
20857
|
+
* scope, a read-only variable is seeded the privileged way
|
|
20858
|
+
* `initialVariables` uses, and conditional content reacts at once.
|
|
20859
|
+
* Emits `variableChange`; the story position is kept.
|
|
20860
|
+
*/
|
|
20861
|
+
seedVariable(key, value) {
|
|
20862
|
+
const def = this.variableDefinitions.find((d) => d.id === key);
|
|
20863
|
+
if (def?.readOnly) {
|
|
20864
|
+
this.variableStore.seed({ [key]: value });
|
|
20865
|
+
}
|
|
20866
|
+
else {
|
|
20867
|
+
this.variableStore.set(key, value, def?.scope ?? 'session', def ? this.scopeIdFor(def) : undefined);
|
|
20868
|
+
}
|
|
20869
|
+
this.refreshVariableContext();
|
|
20870
|
+
this.variableChange.emit({ key, value });
|
|
20871
|
+
}
|
|
20797
20872
|
/**
|
|
20798
20873
|
* Get variable
|
|
20799
20874
|
*/
|
|
@@ -21249,7 +21324,12 @@ class PlayerShellComponent {
|
|
|
21249
21324
|
entry.liked = this.liked;
|
|
21250
21325
|
store[this.workKey()] = entry;
|
|
21251
21326
|
this.writeSocialStore(store);
|
|
21252
|
-
this.trackingService.track('like', {
|
|
21327
|
+
this.trackingService.track('like', {
|
|
21328
|
+
workId: this.workKey(),
|
|
21329
|
+
liked: this.liked,
|
|
21330
|
+
panelId: this.getCurrentPanelId(),
|
|
21331
|
+
chapterId: this.currentChapter?.id,
|
|
21332
|
+
});
|
|
21253
21333
|
this.likeChange.emit({ workId: this.workKey(), liked: this.liked });
|
|
21254
21334
|
this.cdr.markForCheck();
|
|
21255
21335
|
}
|
|
@@ -21285,6 +21365,16 @@ class PlayerShellComponent {
|
|
|
21285
21365
|
this.shareLink = this.buildShareLink();
|
|
21286
21366
|
this.shareVisible = true;
|
|
21287
21367
|
}
|
|
21368
|
+
/** The reader copied or posted the link from the share dialog. */
|
|
21369
|
+
onShared(channel) {
|
|
21370
|
+
this.trackingService.track('share', {
|
|
21371
|
+
workId: this.workKey(),
|
|
21372
|
+
channel,
|
|
21373
|
+
panelId: this.getCurrentPanelId(),
|
|
21374
|
+
chapterId: this.currentChapter?.id,
|
|
21375
|
+
url: this.shareLink,
|
|
21376
|
+
});
|
|
21377
|
+
}
|
|
21288
21378
|
/**
|
|
21289
21379
|
* What to share: the host's `shareUrl`, else the current address with the
|
|
21290
21380
|
* reading position — the page in page view, the panel in panel view, the
|
|
@@ -22382,6 +22472,38 @@ class PlayerShellComponent {
|
|
|
22382
22472
|
openPaywall(gate) {
|
|
22383
22473
|
this.paywallGate = gate;
|
|
22384
22474
|
this.paywallVisible = true;
|
|
22475
|
+
this.trackingService.track('paywall_view', this.paywallEventPayload(gate));
|
|
22476
|
+
}
|
|
22477
|
+
/**
|
|
22478
|
+
* Analytics payload shared by `paywall_view` and `paywall_action`: the
|
|
22479
|
+
* gated panel (the gate's panel, else where the reader stands), the rule
|
|
22480
|
+
* that raised the gate and why.
|
|
22481
|
+
*/
|
|
22482
|
+
paywallEventPayload(gate) {
|
|
22483
|
+
const panelId = gate.scope === 'panel' && gate.refId ? gate.refId : this.getCurrentPanelId();
|
|
22484
|
+
return {
|
|
22485
|
+
panelId,
|
|
22486
|
+
chapterId: gate.scope === 'chapter' && gate.refId ? gate.refId : this.currentChapter?.id,
|
|
22487
|
+
ruleId: gate.ruleId,
|
|
22488
|
+
scope: gate.scope,
|
|
22489
|
+
refId: gate.refId,
|
|
22490
|
+
lockReason: gate.lockReason,
|
|
22491
|
+
requireEntitlement: gate.requireEntitlement,
|
|
22492
|
+
};
|
|
22493
|
+
}
|
|
22494
|
+
/**
|
|
22495
|
+
* Track a reader's choice between branches — the branch chooser's edge or
|
|
22496
|
+
* a `goTo` hotspot — as a `decision` (schema tracking vocabulary; the flow
|
|
22497
|
+
* analytics aggregate `choiceId` per `panelId`).
|
|
22498
|
+
*/
|
|
22499
|
+
trackDecision(chapterId, fromPanelId, toPanelId, choice) {
|
|
22500
|
+
this.trackingService.track('decision', {
|
|
22501
|
+
panelId: fromPanelId,
|
|
22502
|
+
chapterId,
|
|
22503
|
+
fromPanelId,
|
|
22504
|
+
toPanelId,
|
|
22505
|
+
...choice,
|
|
22506
|
+
});
|
|
22385
22507
|
}
|
|
22386
22508
|
/** Dismiss the overlay without changing entitlement. */
|
|
22387
22509
|
closePaywall() {
|
|
@@ -22397,6 +22519,7 @@ class PlayerShellComponent {
|
|
|
22397
22519
|
const gate = this.paywallGate;
|
|
22398
22520
|
const productId = action === 'purchase' || action === 'subscribe' ? this.paywallProductId : undefined;
|
|
22399
22521
|
if (gate) {
|
|
22522
|
+
this.trackingService.track('paywallAction', { ...this.paywallEventPayload(gate), action, productId });
|
|
22400
22523
|
this.paywallAction.emit({ action, gate, ...(productId ? { productId } : {}) });
|
|
22401
22524
|
}
|
|
22402
22525
|
this.paywallProductId = undefined;
|
|
@@ -22621,10 +22744,10 @@ class PlayerShellComponent {
|
|
|
22621
22744
|
return;
|
|
22622
22745
|
}
|
|
22623
22746
|
const settings = this.manifestService.getManifest()?.settings;
|
|
22624
|
-
this.
|
|
22625
|
-
|
|
22626
|
-
|
|
22627
|
-
|
|
22747
|
+
this.trackDecision(chapter.id, choice.edge.from, choice.edge.to, {
|
|
22748
|
+
choiceId: `${choice.edge.from}->${choice.edge.to}`,
|
|
22749
|
+
choiceLabel: choice.label,
|
|
22750
|
+
source: 'branch',
|
|
22628
22751
|
index: choice.index,
|
|
22629
22752
|
});
|
|
22630
22753
|
// Like a hotspot choice: page view reveals the chosen path.
|
|
@@ -22633,7 +22756,7 @@ class PlayerShellComponent {
|
|
|
22633
22756
|
await this.navigateToPanel(chapter.id, choice.edge.to, choice.edge.transition ?? this.flowEngine.getDefaultTransition(settings), choice.edge.cameraMove ?? this.flowEngine.getDefaultCameraMove(settings), choice.edge.action);
|
|
22634
22757
|
}
|
|
22635
22758
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: PlayerShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
22636
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: PlayerShellComponent, isStandalone: true, selector: "pw-player-shell", inputs: { manifest: "manifest", manifestUrl: "manifestUrl", entitlementAdapter: "entitlementAdapter", entitlementSnapshot: "entitlementSnapshot", entitlementEndpoint: "entitlementEndpoint", readerToken: "readerToken", initialVariables: "initialVariables", locale: "locale", initialChapterId: "initialChapterId", initialPanelId: "initialPanelId", initialPageId: "initialPageId", initialViewMode: "initialViewMode", shareUrl: "shareUrl", autoplay: "autoplay", secondsPerPanel: "secondsPerPanel", pageFormat: "pageFormat", showCover: "showCover", reducedMotion: "reducedMotion", showToolbar: "showToolbar", viewModeOverride: "viewModeOverride" }, outputs: { paywallAction: "paywallAction", ageVerified: "ageVerified", likeChange: "likeChange", bookmarkChange: "bookmarkChange", locationChange: "locationChange", ready: "ready", panelChange: "panelChange", chapterChange: "chapterChange", error: "error", localeChange: "localeChange", variableChange: "variableChange", navigationAttempt: "navigationAttempt", cameraChange: "cameraChange" }, host: { listeners: { "document:fullscreenchange": "onFullscreenChange()", "window:keydown": "handleKeyboard($event)", "window:resize": "onWindowResize()" } }, providers: [CanvasCameraService], usesOnChanges: true, ngImport: i0, template: "<div class=\"player-shell\" [class.has-error]=\"hasError\">\n <!-- Loading state -->\n @if (loading) {\n <div class=\"loading-container\">\n <div class=\"loading-spinner\"></div>\n <p class=\"loading-text\">{{ 'loading.text' | translate }}</p>\n </div>\n }\n\n <!-- Error state -->\n @if (hasError && !loading) {\n <div class=\"error-container\">\n <div class=\"error-icon\"><pw-icon name=\"lucideTriangleAlert\" /></div>\n <h2 class=\"error-title\">{{ 'error.title' | translate }}</h2>\n <p class=\"error-message\">{{ errorMessage }}</p>\n <button class=\"retry-button\" type=\"button\" (click)=\"reload()\">\n {{ 'error.retry' | translate }}\n </button>\n </div>\n }\n\n <!-- Main player -->\n @if (!loading && !hasError && currentPanel) {\n <div class=\"player-content\">\n <!-- Cover (before the first panel / page) -->\n @if (coverVisible) {\n <button\n class=\"pw-cover\"\n type=\"button\"\n [attr.aria-label]=\"'navigation.start_reading' | translate\"\n (click)=\"hideCover()\">\n <img class=\"pw-cover-image\" [src]=\"coverUrl\" [alt]=\"workTitle\" fetchpriority=\"high\" />\n </button>\n }\n\n <!-- Viewport (panel / page view) -->\n @if (!coverVisible && viewMode !== 'canvas') {\n <pw-viewport\n [transition]=\"viewportTransition\"\n [panel]=\"effectivePanel || currentPanel\"\n [page]=\"currentPage || null\"\n [pageBackground]=\"pageBackground\"\n [panels]=\"resolvedPanels\"\n [hiddenPanelIds]=\"hiddenPanelIds\"\n [graph]=\"currentChapter?.graph || null\"\n [currentPanelId]=\"getCurrentPanelId() || null\"\n [viewMode]=\"viewMode\"\n [reducedMotion]=\"motionReduced\"\n [locale]=\"locale\"\n [interactive]=\"true\"\n [showOverflowArrows]=\"true\"\n [speechEnabled]=\"speechEnabled\"\n [preload]=\"manifestPreloadSettings\"\n [preloadTargets]=\"preloadTargets\"\n [variableContext]=\"variableContext\"\n (viewportClick)=\"onViewportClick()\"\n (swipe)=\"onSwipe($event)\"\n (navigatePrevious)=\"onViewportPrevious()\"\n (navigateNext)=\"onViewportNext()\"\n (panelDoubleClick)=\"onPanelDoubleClick($event)\"\n (hotspotActivate)=\"onHotspotActivate($event)\"\n (deadClick)=\"onDeadClick($event)\"\n (lockedPanelActivate)=\"onLockedPanelActivate($event)\">\n </pw-viewport>\n }\n\n <!-- Canvas stage (infinite-canvas view, schema 1.4) -->\n @if (!coverVisible && viewMode === 'canvas') {\n <pw-canvas-stage\n [canvas]=\"currentChapter?.canvas || null\"\n [panels]=\"resolvedPanels\"\n [graph]=\"currentChapter?.graph || null\"\n [currentPanelId]=\"getCurrentPanelId() || null\"\n [visitedPanelIds]=\"visitedPanelIds\"\n [locale]=\"locale\"\n [variableContext]=\"variableContext\"\n [speechEnabled]=\"speechEnabled\"\n [workBalloonConfig]=\"manifestBalloonConfig\"\n [characters]=\"manifestCharacters\"\n [reducedMotion]=\"motionReduced\"\n [interactive]=\"true\"\n [preload]=\"manifestPreloadSettings\"\n (panelTap)=\"onCanvasPanelTap($event)\"\n (navigateNext)=\"navigateNext()\"\n (navigatePrevious)=\"navigatePrevious()\"\n (viewportClick)=\"onViewportClick()\"\n (hotspotActivate)=\"onHotspotActivate($event)\"\n (cameraChange)=\"cameraChange.emit($event)\">\n </pw-canvas-stage>\n }\n\n <!-- Full Toolbar -->\n <pw-toolbar\n [visible]=\"toolbarVisible\"\n [locale]=\"locale\"\n [availableLocales]=\"availableLocales\"\n [viewMode]=\"viewMode\"\n [pageViewAvailable]=\"pageViewAvailable\"\n [speechEnabled]=\"speechEnabled\"\n [audioEnabled]=\"audioEnabled\"\n [sfxEnabled]=\"sfxEnabled\"\n [autoplayEnabled]=\"autoplayEnabled\"\n [autoplayProgress]=\"autoplayProgress\"\n [secondsPerPanel]=\"autoplaySecondsDisplay\"\n [autoplayTiming]=\"autoplayTiming\"\n [thumbnailsVisible]=\"thumbnailsVisible\"\n [hasAlternatives]=\"!!(currentPanel && currentPanel.variants && currentPanel.variants.length)\"\n [hasBranches]=\"hasBranchesAhead\"\n [showSocial]=\"true\"\n [liked]=\"liked\"\n [bookmarked]=\"bookmarked\"\n [fullscreen]=\"isFullscreen\"\n [fullscreenAvailable]=\"fullscreenAvailable\"\n (toggleFullscreen)=\"toggleFullscreen()\"\n (toggleView)=\"onToggleView()\"\n (localeChange)=\"onLocaleChange($event)\"\n (toggleSpeech)=\"onToggleSpeech()\"\n (toggleAudio)=\"onToggleAudio()\"\n (toggleSfx)=\"onToggleSfx()\"\n (toggleAutoplay)=\"onToggleAutoplay()\"\n (secondsPerPanelChange)=\"onSecondsPerPanelChange($event)\"\n (autoplayTimingChange)=\"onAutoplayTimingChange($event)\"\n (toggleThumbnails)=\"onToggleThumbnails()\"\n (openToc)=\"onOpenToc()\"\n (openSettings)=\"onOpenSettings()\"\n (openLanguage)=\"languageModalVisible = true\"\n (openCharacters)=\"onOpenCharacters()\"\n (cycleAlternative)=\"onCycleAlternative()\"\n (showBranches)=\"onShowBranches()\"\n (openExtras)=\"onOpenExtras()\"\n (like)=\"onLike()\"\n (bookmark)=\"onBookmark()\"\n (share)=\"onShare()\"\n (openComments)=\"onOpenComments()\"\n (close)=\"toggleToolbar()\">\n </pw-toolbar>\n\n <!-- Thumbnail Strip -->\n <pw-thumbnail-strip\n [visible]=\"thumbnailsVisible\"\n [chapters]=\"loadedManifest?.chapters || []\"\n [currentChapterId]=\"currentChapter?.id\"\n [currentPanelId]=\"coverVisible ? undefined : getCurrentPanelId()\"\n [currentPanelIds]=\"currentPagePanelIds\"\n [coverThumbnail]=\"coverThumbUrl\"\n [coverCurrent]=\"coverVisible\"\n [locale]=\"locale\"\n [lockedPanels]=\"[]\"\n (navigate)=\"onThumbnailNavigate($event)\"\n (close)=\"onThumbnailsClose()\">\n </pw-thumbnail-strip>\n\n <!-- Active variant indicator -->\n @if (activeVariantId) {\n <div class=\"variant-active\" role=\"status\" aria-live=\"polite\"\n [attr.aria-label]=\"'Active panel variant: ' + activeVariantId\">\n {{ activeVariantId }}\n </div>\n }\n\n <!-- PanelWave icon: opens the toolbar -->\n @if (!toolbarVisible) {\n <button\n class=\"floating-icon\"\n type=\"button\"\n [attr.aria-label]=\"'toolbar.show' | translate\"\n [attr.title]=\"'toolbar.show' | translate\"\n (click)=\"toggleToolbar()\">\n <img class=\"floating-icon-image\" [src]=\"panelwaveIcon\" alt=\"\" />\n </button>\n }\n\n <!-- Modals -->\n @if (paywallVisible && paywallGate) {\n <pw-paywall-overlay\n [visible]=\"paywallVisible\"\n [gate]=\"paywallGate\"\n [purchaseOptions]=\"paywallGate.options || []\"\n [locale]=\"locale\"\n (purchase)=\"paywallProductId = $event\"\n (action)=\"onPaywallAction($event)\"\n (close)=\"closePaywall()\">\n </pw-paywall-overlay>\n }\n\n @if (ageGateVisible) {\n <pw-age-gate\n [visible]=\"ageGateVisible\"\n [minimumAge]=\"ageGateMinimumAge\"\n [locale]=\"locale\"\n [allowDismiss]=\"true\"\n (verify)=\"onAgeGateVerify($event)\"\n (close)=\"closeAgeGate()\">\n </pw-age-gate>\n }\n\n @if (branchChooserVisible) {\n <pw-branch-chooser\n [visible]=\"branchChooserVisible\"\n [locale]=\"locale\"\n [choices]=\"branchChoices\"\n (choose)=\"onBranchChosen($event)\"\n (close)=\"branchChooserVisible = false\">\n </pw-branch-chooser>\n }\n\n @if (tocVisible) {\n <pw-toc-overlay\n [visible]=\"tocVisible\"\n [manifest]=\"loadedManifest\"\n [currentChapterId]=\"currentChapter?.id\"\n [currentPanelId]=\"coverVisible ? undefined : getCurrentPanelId()\"\n [pageFormat]=\"activePageFormat\"\n [coverThumbnail]=\"coverThumbUrl\"\n [coverCurrent]=\"coverVisible\"\n [locale]=\"locale\"\n (navigate)=\"onTocNavigate($event)\"\n (close)=\"tocVisible = false\">\n </pw-toc-overlay>\n }\n\n @if (settingsVisible) {\n <pw-settings-modal\n [visible]=\"settingsVisible\"\n [variables]=\"variableDefinitions\"\n [variableValues]=\"settingsVariableValues\"\n [availableLocales]=\"availableLocales\"\n [locale]=\"locale\"\n [preferences]=\"settingsPreferences\"\n (preferencesChange)=\"onPreferencesChange($event)\"\n (variableChange)=\"onSettingsVariableChange($event)\"\n (close)=\"settingsVisible = false\">\n </pw-settings-modal>\n }\n\n @if (languageModalVisible) {\n <pw-language-modal\n [visible]=\"languageModalVisible\"\n [currentLocale]=\"locale\"\n [availableLocales]=\"availableLocales\"\n (localeSelected)=\"onLocaleChange($event)\"\n (closeModal)=\"languageModalVisible = false\">\n </pw-language-modal>\n }\n\n @if (charactersVisible) {\n <pw-character-roster\n [visible]=\"charactersVisible\"\n [characters]=\"characters\"\n [locale]=\"locale\"\n (close)=\"charactersVisible = false\">\n </pw-character-roster>\n }\n\n @if (extrasVisible) {\n <pw-extras-viewer\n [visible]=\"extrasVisible\"\n [extras]=\"extrasList\"\n [initialExtraId]=\"extrasInitialId\"\n [locale]=\"locale\"\n (close)=\"extrasVisible = false; extrasInitialId = undefined\">\n </pw-extras-viewer>\n }\n\n @if (actionModalVisible) {\n <pw-action-modal\n [visible]=\"actionModalVisible\"\n [locale]=\"locale\"\n [title]=\"actionModalTitle\"\n [content]=\"actionModalContent\"\n (close)=\"actionModalVisible = false\">\n </pw-action-modal>\n }\n\n @if (shareVisible) {\n <pw-share-modal\n [visible]=\"shareVisible\"\n [shareUrl]=\"shareLink\"\n [shareTitle]=\"workTitle\"\n [shareable]=\"currentPanel.shareable ?? true\"\n (close)=\"shareVisible = false\">\n </pw-share-modal>\n }\n\n @if (commentsVisible) {\n <pw-comments-drawer\n [visible]=\"commentsVisible\"\n (close)=\"commentsVisible = false\">\n </pw-comments-drawer>\n }\n </div>\n }\n</div>\n", styles: [":host{font-family:var(--pw-font-ui, \"Barlow\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif)}:host(:fullscreen){display:block;width:100vw;height:100vh;background:#000}:host ::ng-deep :is(button,input,select,textarea){font-family:inherit}.player-shell{position:relative;width:100%;height:100%;overflow:hidden;background:#000;color:#fff}.pw-cover{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:16px;margin:0;border:0;background:#000;cursor:pointer;box-sizing:border-box}.pw-cover-image{max-width:100%;max-height:100%;object-fit:contain;box-shadow:0 8px 40px #0009}.pw-cover:focus-visible{outline:2px solid rgba(0,120,215,.8);outline-offset:-4px}.player-shell.has-error{background:#1a1a1a}.loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:24px}.loading-spinner{width:48px;height:48px;border:4px solid rgba(255,255,255,.1);border-top-color:#fff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-text{font-size:16px;color:#ffffffb3;margin:0}.error-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:24px;text-align:center;gap:16px}.error-icon{font-size:64px;line-height:1}.error-title{font-size:24px;font-weight:600;margin:0;color:#fff}.error-message{font-size:16px;color:#ffffffb3;margin:0;max-width:500px}.retry-button{margin-top:16px;padding:12px 24px;font-size:16px;font-weight:500;color:#fff;background:#0078d7;border:none;border-radius:4px;cursor:pointer;transition:background .2s ease}.retry-button:hover{background:#005a9e}.retry-button:active{background:#004578}.player-content{position:relative;width:100%;height:100%}.toolbar-container{position:absolute;bottom:0;left:0;right:0;z-index:100;background:linear-gradient(to top,rgba(0,0,0,.8),transparent);padding:24px 16px 16px;animation:slideUp .3s ease-out}@keyframes slideUp{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}.toolbar{display:flex;justify-content:center;align-items:center;gap:12px;max-width:1200px;margin:0 auto}.toolbar-button{display:flex;align-items:center;gap:8px;padding:10px 16px;font-size:14px;font-weight:500;color:#fff;background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer;transition:all .2s ease;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.toolbar-button:hover{background:#fff3;border-color:#ffffff4d}.toolbar-button:active{background:#ffffff26;transform:scale(.98)}.button-icon{font-size:18px;line-height:1}.button-label{font-size:14px}.variant-active{position:absolute;bottom:20px;left:20px;z-index:50;padding:4px 10px;font-size:12px;font-weight:600;color:#fff;background:#0078d7d9;border-radius:12px;box-shadow:0 2px 8px #0000004d;pointer-events:none}.floating-icon{position:absolute;bottom:20px;right:20px;z-index:50;width:48px;height:48px;padding:0;display:flex;align-items:center;justify-content:center;background:transparent;border:none;border-radius:22%;box-shadow:0 4px 12px #0006;cursor:pointer;opacity:.85;transition:transform .2s ease,opacity .2s ease,box-shadow .2s ease}.floating-icon:hover{opacity:1;transform:scale(1.08);box-shadow:0 6px 16px #00000080}.floating-icon:active{transform:scale(1.02)}.floating-icon:focus-visible{outline:2px solid #fff;outline-offset:3px}.floating-icon-image{width:100%;height:100%;border-radius:inherit;display:block}@media (prefers-reduced-motion: reduce){.floating-icon{transition:none}}@media (max-width: 768px){.toolbar-container{padding:16px 12px 12px}.toolbar{gap:8px}.toolbar-button{padding:8px 12px;font-size:12px}.button-label{display:none}.button-icon{font-size:16px}.floating-icon{width:48px;height:48px;bottom:16px;right:16px}.floating-icon svg{width:20px;height:20px}}@media (prefers-reduced-motion: reduce){.toolbar-container{animation:none}.loading-spinner{animation:none;border-top-color:#ffffff4d}.toolbar-button:active,.floating-icon:hover{transform:none}}\n"], dependencies: [{ kind: "component", type: ViewportComponent, selector: "pw-viewport", inputs: ["panel", "page", "pageBackground", "panels", "hiddenPanelIds", "preloadTargets", "viewMode", "currentPanelId", "locale", "variableContext", "panX", "panY", "zoom", "reducedMotion", "transition", "interactive", "showOverflowArrows", "enableViewportCulling", "enableLazyLoading", "workBalloonConfig", "characters", "speechEnabled", "graph", "preload"], outputs: ["viewportClick", "performanceMetrics", "transformChange", "layerClick", "swipe", "navigatePrevious", "navigateNext", "panelDoubleClick", "panelFocus", "hotspotActivate", "deadClick", "lockedPanelActivate"] }, { kind: "component", type: CanvasStageComponent, selector: "pw-canvas-stage", inputs: ["canvas", "panels", "graph", "currentPanelId", "visitedPanelIds", "locale", "variableContext", "speechEnabled", "workBalloonConfig", "characters", "reducedMotion", "interactive", "preload"], outputs: ["panelTap", "navigateNext", "navigatePrevious", "viewportClick", "hotspotActivate", "cameraChange"] }, { kind: "component", type: ToolbarComponent, selector: "pw-toolbar", inputs: ["locale", "availableLocales", "viewMode", "pageViewAvailable", "speechEnabled", "audioEnabled", "sfxEnabled", "autoplayEnabled", "secondsPerPanel", "autoplayTiming", "autoplayProgress", "thumbnailsVisible", "hasAlternatives", "hasBranches", "liked", "bookmarked", "showSocial", "visible", "fullscreen", "fullscreenAvailable"], outputs: ["toggleFullscreen", "toggleView", "localeChange", "toggleSpeech", "toggleAudio", "toggleSfx", "toggleAutoplay", "secondsPerPanelChange", "autoplayTimingChange", "toggleThumbnails", "openToc", "openSettings", "openCharacters", "cycleAlternative", "showBranches", "openExtras", "like", "bookmark", "share", "openComments", "close", "openLanguage"] }, { kind: "component", type: ThumbnailStripComponent, selector: "pw-thumbnail-strip", inputs: ["chapters", "currentChapterId", "currentPanelId", "currentPanelIds", "coverThumbnail", "coverCurrent", "locale", "visible", "lockedPanels"], outputs: ["navigate", "close"] }, { kind: "component", type: TocOverlayComponent, selector: "pw-toc-overlay", inputs: ["manifest", "chapters", "currentChapterId", "currentPanelId", "locale", "pageFormat", "coverThumbnail", "coverCurrent", "visible"], outputs: ["navigate", "close"] }, { kind: "component", type: PaywallOverlayComponent, selector: "pw-paywall-overlay", inputs: ["visible", "gate", "purchaseOptions", "locale", "title", "message", "allowPreview", "showLogin"], outputs: ["action", "purchase", "close"] }, { kind: "component", type: SettingsModalComponent, selector: "pw-settings-modal", inputs: ["preferences", "availableLocales", "locale", "variables", "variableValues", "visible"], outputs: ["preferencesChange", "localeChange", "variableChange", "close"] }, { kind: "component", type: LanguageModalComponent, selector: "pw-language-modal", inputs: ["visible", "currentLocale", "availableLocales"], outputs: ["closeModal", "localeSelected"] }, { kind: "component", type: CharacterRosterComponent, selector: "pw-character-roster", inputs: ["characters", "locale", "visible"], outputs: ["characterSelect", "close"] }, { kind: "component", type: ExtrasViewerComponent, selector: "pw-extras-viewer", inputs: ["extras", "initialExtraId", "locale", "visible", "hasPremiumAccess"], outputs: ["purchase", "close"] }, { kind: "component", type: ActionModalComponent, selector: "pw-action-modal", inputs: ["visible", "locale", "title", "content"], outputs: ["close"] }, { kind: "component", type: AgeGateComponent, selector: "pw-age-gate", inputs: ["visible", "minimumAge", "locale", "warningMessage", "allowDismiss"], outputs: ["verify", "close"] }, { kind: "component", type: BranchChooserComponent, selector: "pw-branch-chooser", inputs: ["visible", "locale", "choices"], outputs: ["choose", "close"] }, { kind: "component", type: ShareModalComponent, selector: "pw-share-modal", inputs: ["shareUrl", "shareTitle", "shareDescription", "visible", "shareable"], outputs: ["close", "share"] }, { kind: "component", type: CommentsDrawerComponent, selector: "pw-comments-drawer", inputs: ["comments", "visible", "loading", "currentPage", "totalPages", "hasMore", "isAuthenticated"], outputs: ["postComment", "loadMore", "likeComment", "close"] }, { kind: "component", type: PwIconComponent, selector: "pw-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
22759
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: PlayerShellComponent, isStandalone: true, selector: "pw-player-shell", inputs: { manifest: "manifest", manifestUrl: "manifestUrl", entitlementAdapter: "entitlementAdapter", entitlementSnapshot: "entitlementSnapshot", entitlementEndpoint: "entitlementEndpoint", readerToken: "readerToken", initialVariables: "initialVariables", locale: "locale", initialChapterId: "initialChapterId", initialPanelId: "initialPanelId", initialPageId: "initialPageId", initialViewMode: "initialViewMode", shareUrl: "shareUrl", autoplay: "autoplay", secondsPerPanel: "secondsPerPanel", pageFormat: "pageFormat", showCover: "showCover", reducedMotion: "reducedMotion", showToolbar: "showToolbar", viewModeOverride: "viewModeOverride" }, outputs: { paywallAction: "paywallAction", ageVerified: "ageVerified", likeChange: "likeChange", bookmarkChange: "bookmarkChange", locationChange: "locationChange", ready: "ready", panelChange: "panelChange", chapterChange: "chapterChange", error: "error", localeChange: "localeChange", variableChange: "variableChange", navigationAttempt: "navigationAttempt", cameraChange: "cameraChange" }, host: { listeners: { "document:fullscreenchange": "onFullscreenChange()", "window:keydown": "handleKeyboard($event)", "window:resize": "onWindowResize()" } }, providers: [CanvasCameraService], usesOnChanges: true, ngImport: i0, template: "<div class=\"player-shell\" [class.has-error]=\"hasError\">\n <!-- Loading state -->\n @if (loading) {\n <div class=\"loading-container\">\n <div class=\"loading-spinner\"></div>\n <p class=\"loading-text\">{{ 'loading.text' | translate }}</p>\n </div>\n }\n\n <!-- Error state -->\n @if (hasError && !loading) {\n <div class=\"error-container\">\n <div class=\"error-icon\"><pw-icon name=\"lucideTriangleAlert\" /></div>\n <h2 class=\"error-title\">{{ 'error.title' | translate }}</h2>\n <p class=\"error-message\">{{ errorMessage }}</p>\n <button class=\"retry-button\" type=\"button\" (click)=\"reload()\">\n {{ 'error.retry' | translate }}\n </button>\n </div>\n }\n\n <!-- Main player -->\n @if (!loading && !hasError && currentPanel) {\n <div class=\"player-content\">\n <!-- Cover (before the first panel / page) -->\n @if (coverVisible) {\n <button\n class=\"pw-cover\"\n type=\"button\"\n [attr.aria-label]=\"'navigation.start_reading' | translate\"\n (click)=\"hideCover()\">\n <img class=\"pw-cover-image\" [src]=\"coverUrl\" [alt]=\"workTitle\" fetchpriority=\"high\" />\n </button>\n }\n\n <!-- Viewport (panel / page view) -->\n @if (!coverVisible && viewMode !== 'canvas') {\n <pw-viewport\n [transition]=\"viewportTransition\"\n [panel]=\"effectivePanel || currentPanel\"\n [page]=\"currentPage || null\"\n [pageBackground]=\"pageBackground\"\n [panels]=\"resolvedPanels\"\n [hiddenPanelIds]=\"hiddenPanelIds\"\n [graph]=\"currentChapter?.graph || null\"\n [currentPanelId]=\"getCurrentPanelId() || null\"\n [viewMode]=\"viewMode\"\n [reducedMotion]=\"motionReduced\"\n [locale]=\"locale\"\n [interactive]=\"true\"\n [showOverflowArrows]=\"true\"\n [speechEnabled]=\"speechEnabled\"\n [preload]=\"manifestPreloadSettings\"\n [preloadTargets]=\"preloadTargets\"\n [variableContext]=\"variableContext\"\n (viewportClick)=\"onViewportClick()\"\n (swipe)=\"onSwipe($event)\"\n (navigatePrevious)=\"onViewportPrevious()\"\n (navigateNext)=\"onViewportNext()\"\n (panelDoubleClick)=\"onPanelDoubleClick($event)\"\n (hotspotActivate)=\"onHotspotActivate($event)\"\n (deadClick)=\"onDeadClick($event)\"\n (lockedPanelActivate)=\"onLockedPanelActivate($event)\">\n </pw-viewport>\n }\n\n <!-- Canvas stage (infinite-canvas view, schema 1.4) -->\n @if (!coverVisible && viewMode === 'canvas') {\n <pw-canvas-stage\n [canvas]=\"currentChapter?.canvas || null\"\n [panels]=\"resolvedPanels\"\n [graph]=\"currentChapter?.graph || null\"\n [currentPanelId]=\"getCurrentPanelId() || null\"\n [visitedPanelIds]=\"visitedPanelIds\"\n [locale]=\"locale\"\n [variableContext]=\"variableContext\"\n [speechEnabled]=\"speechEnabled\"\n [workBalloonConfig]=\"manifestBalloonConfig\"\n [characters]=\"manifestCharacters\"\n [reducedMotion]=\"motionReduced\"\n [interactive]=\"true\"\n [preload]=\"manifestPreloadSettings\"\n (panelTap)=\"onCanvasPanelTap($event)\"\n (navigateNext)=\"navigateNext()\"\n (navigatePrevious)=\"navigatePrevious()\"\n (viewportClick)=\"onViewportClick()\"\n (hotspotActivate)=\"onHotspotActivate($event)\"\n (cameraChange)=\"cameraChange.emit($event)\">\n </pw-canvas-stage>\n }\n\n <!-- Full Toolbar -->\n <pw-toolbar\n [visible]=\"toolbarVisible\"\n [locale]=\"locale\"\n [availableLocales]=\"availableLocales\"\n [viewMode]=\"viewMode\"\n [pageViewAvailable]=\"pageViewAvailable\"\n [speechEnabled]=\"speechEnabled\"\n [audioEnabled]=\"audioEnabled\"\n [sfxEnabled]=\"sfxEnabled\"\n [autoplayEnabled]=\"autoplayEnabled\"\n [autoplayProgress]=\"autoplayProgress\"\n [secondsPerPanel]=\"autoplaySecondsDisplay\"\n [autoplayTiming]=\"autoplayTiming\"\n [thumbnailsVisible]=\"thumbnailsVisible\"\n [hasAlternatives]=\"!!(currentPanel && currentPanel.variants && currentPanel.variants.length)\"\n [hasBranches]=\"hasBranchesAhead\"\n [showSocial]=\"true\"\n [liked]=\"liked\"\n [bookmarked]=\"bookmarked\"\n [fullscreen]=\"isFullscreen\"\n [fullscreenAvailable]=\"fullscreenAvailable\"\n (toggleFullscreen)=\"toggleFullscreen()\"\n (toggleView)=\"onToggleView()\"\n (localeChange)=\"onLocaleChange($event)\"\n (toggleSpeech)=\"onToggleSpeech()\"\n (toggleAudio)=\"onToggleAudio()\"\n (toggleSfx)=\"onToggleSfx()\"\n (toggleAutoplay)=\"onToggleAutoplay()\"\n (secondsPerPanelChange)=\"onSecondsPerPanelChange($event)\"\n (autoplayTimingChange)=\"onAutoplayTimingChange($event)\"\n (toggleThumbnails)=\"onToggleThumbnails()\"\n (openToc)=\"onOpenToc()\"\n (openSettings)=\"onOpenSettings()\"\n (openLanguage)=\"languageModalVisible = true\"\n (openCharacters)=\"onOpenCharacters()\"\n (cycleAlternative)=\"onCycleAlternative()\"\n (showBranches)=\"onShowBranches()\"\n (openExtras)=\"onOpenExtras()\"\n (like)=\"onLike()\"\n (bookmark)=\"onBookmark()\"\n (share)=\"onShare()\"\n (openComments)=\"onOpenComments()\"\n (close)=\"toggleToolbar()\">\n </pw-toolbar>\n\n <!-- Thumbnail Strip -->\n <pw-thumbnail-strip\n [visible]=\"thumbnailsVisible\"\n [chapters]=\"loadedManifest?.chapters || []\"\n [currentChapterId]=\"currentChapter?.id\"\n [currentPanelId]=\"coverVisible ? undefined : getCurrentPanelId()\"\n [currentPanelIds]=\"currentPagePanelIds\"\n [coverThumbnail]=\"coverThumbUrl\"\n [coverCurrent]=\"coverVisible\"\n [locale]=\"locale\"\n [lockedPanels]=\"[]\"\n (navigate)=\"onThumbnailNavigate($event)\"\n (close)=\"onThumbnailsClose()\">\n </pw-thumbnail-strip>\n\n <!-- Active variant indicator -->\n @if (activeVariantId) {\n <div class=\"variant-active\" role=\"status\" aria-live=\"polite\"\n [attr.aria-label]=\"'Active panel variant: ' + activeVariantId\">\n {{ activeVariantId }}\n </div>\n }\n\n <!-- PanelWave icon: opens the toolbar -->\n @if (!toolbarVisible) {\n <button\n class=\"floating-icon\"\n type=\"button\"\n [attr.aria-label]=\"'toolbar.show' | translate\"\n [attr.title]=\"'toolbar.show' | translate\"\n (click)=\"toggleToolbar()\">\n <img class=\"floating-icon-image\" [src]=\"panelwaveIcon\" alt=\"\" />\n </button>\n }\n\n <!-- Modals -->\n @if (paywallVisible && paywallGate) {\n <pw-paywall-overlay\n [visible]=\"paywallVisible\"\n [gate]=\"paywallGate\"\n [purchaseOptions]=\"paywallGate.options || []\"\n [locale]=\"locale\"\n (purchase)=\"paywallProductId = $event\"\n (action)=\"onPaywallAction($event)\"\n (close)=\"closePaywall()\">\n </pw-paywall-overlay>\n }\n\n @if (ageGateVisible) {\n <pw-age-gate\n [visible]=\"ageGateVisible\"\n [minimumAge]=\"ageGateMinimumAge\"\n [locale]=\"locale\"\n [allowDismiss]=\"true\"\n (verify)=\"onAgeGateVerify($event)\"\n (close)=\"closeAgeGate()\">\n </pw-age-gate>\n }\n\n @if (branchChooserVisible) {\n <pw-branch-chooser\n [visible]=\"branchChooserVisible\"\n [locale]=\"locale\"\n [choices]=\"branchChoices\"\n (choose)=\"onBranchChosen($event)\"\n (close)=\"branchChooserVisible = false\">\n </pw-branch-chooser>\n }\n\n @if (tocVisible) {\n <pw-toc-overlay\n [visible]=\"tocVisible\"\n [manifest]=\"loadedManifest\"\n [currentChapterId]=\"currentChapter?.id\"\n [currentPanelId]=\"coverVisible ? undefined : getCurrentPanelId()\"\n [pageFormat]=\"activePageFormat\"\n [coverThumbnail]=\"coverThumbUrl\"\n [coverCurrent]=\"coverVisible\"\n [locale]=\"locale\"\n (navigate)=\"onTocNavigate($event)\"\n (close)=\"tocVisible = false\">\n </pw-toc-overlay>\n }\n\n @if (settingsVisible) {\n <pw-settings-modal\n [visible]=\"settingsVisible\"\n [variables]=\"variableDefinitions\"\n [variableValues]=\"settingsVariableValues\"\n [availableLocales]=\"availableLocales\"\n [locale]=\"locale\"\n [preferences]=\"settingsPreferences\"\n (preferencesChange)=\"onPreferencesChange($event)\"\n (variableChange)=\"onSettingsVariableChange($event)\"\n (close)=\"settingsVisible = false\">\n </pw-settings-modal>\n }\n\n @if (languageModalVisible) {\n <pw-language-modal\n [visible]=\"languageModalVisible\"\n [currentLocale]=\"locale\"\n [availableLocales]=\"availableLocales\"\n (localeSelected)=\"onLocaleChange($event)\"\n (closeModal)=\"languageModalVisible = false\">\n </pw-language-modal>\n }\n\n @if (charactersVisible) {\n <pw-character-roster\n [visible]=\"charactersVisible\"\n [characters]=\"characters\"\n [locale]=\"locale\"\n (close)=\"charactersVisible = false\">\n </pw-character-roster>\n }\n\n @if (extrasVisible) {\n <pw-extras-viewer\n [visible]=\"extrasVisible\"\n [extras]=\"extrasList\"\n [initialExtraId]=\"extrasInitialId\"\n [locale]=\"locale\"\n (close)=\"extrasVisible = false; extrasInitialId = undefined\">\n </pw-extras-viewer>\n }\n\n @if (actionModalVisible) {\n <pw-action-modal\n [visible]=\"actionModalVisible\"\n [locale]=\"locale\"\n [title]=\"actionModalTitle\"\n [content]=\"actionModalContent\"\n (close)=\"actionModalVisible = false\">\n </pw-action-modal>\n }\n\n @if (shareVisible) {\n <pw-share-modal\n [visible]=\"shareVisible\"\n [shareUrl]=\"shareLink\"\n [shareTitle]=\"workTitle\"\n [shareable]=\"currentPanel.shareable ?? true\"\n (share)=\"onShared($event)\"\n (close)=\"shareVisible = false\">\n </pw-share-modal>\n }\n\n @if (commentsVisible) {\n <pw-comments-drawer\n [visible]=\"commentsVisible\"\n (close)=\"commentsVisible = false\">\n </pw-comments-drawer>\n }\n </div>\n }\n</div>\n", styles: [":host{font-family:var(--pw-font-ui, \"Barlow\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif)}:host(:fullscreen){display:block;width:100vw;height:100vh;background:#000}:host ::ng-deep :is(button,input,select,textarea){font-family:inherit}.player-shell{position:relative;width:100%;height:100%;overflow:hidden;background:#000;color:#fff}.pw-cover{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:16px;margin:0;border:0;background:#000;cursor:pointer;box-sizing:border-box}.pw-cover-image{max-width:100%;max-height:100%;object-fit:contain;box-shadow:0 8px 40px #0009}.pw-cover:focus-visible{outline:2px solid rgba(0,120,215,.8);outline-offset:-4px}.player-shell.has-error{background:#1a1a1a}.loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:24px}.loading-spinner{width:48px;height:48px;border:4px solid rgba(255,255,255,.1);border-top-color:#fff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-text{font-size:16px;color:#ffffffb3;margin:0}.error-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:24px;text-align:center;gap:16px}.error-icon{font-size:64px;line-height:1}.error-title{font-size:24px;font-weight:600;margin:0;color:#fff}.error-message{font-size:16px;color:#ffffffb3;margin:0;max-width:500px}.retry-button{margin-top:16px;padding:12px 24px;font-size:16px;font-weight:500;color:#fff;background:#0078d7;border:none;border-radius:4px;cursor:pointer;transition:background .2s ease}.retry-button:hover{background:#005a9e}.retry-button:active{background:#004578}.player-content{position:relative;width:100%;height:100%}.toolbar-container{position:absolute;bottom:0;left:0;right:0;z-index:100;background:linear-gradient(to top,rgba(0,0,0,.8),transparent);padding:24px 16px 16px;animation:slideUp .3s ease-out}@keyframes slideUp{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}.toolbar{display:flex;justify-content:center;align-items:center;gap:12px;max-width:1200px;margin:0 auto}.toolbar-button{display:flex;align-items:center;gap:8px;padding:10px 16px;font-size:14px;font-weight:500;color:#fff;background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer;transition:all .2s ease;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.toolbar-button:hover{background:#fff3;border-color:#ffffff4d}.toolbar-button:active{background:#ffffff26;transform:scale(.98)}.button-icon{font-size:18px;line-height:1}.button-label{font-size:14px}.variant-active{position:absolute;bottom:20px;left:20px;z-index:50;padding:4px 10px;font-size:12px;font-weight:600;color:#fff;background:#0078d7d9;border-radius:12px;box-shadow:0 2px 8px #0000004d;pointer-events:none}.floating-icon{position:absolute;bottom:20px;right:20px;z-index:50;width:48px;height:48px;padding:0;display:flex;align-items:center;justify-content:center;background:transparent;border:none;border-radius:22%;box-shadow:0 4px 12px #0006;cursor:pointer;opacity:.85;transition:transform .2s ease,opacity .2s ease,box-shadow .2s ease}.floating-icon:hover{opacity:1;transform:scale(1.08);box-shadow:0 6px 16px #00000080}.floating-icon:active{transform:scale(1.02)}.floating-icon:focus-visible{outline:2px solid #fff;outline-offset:3px}.floating-icon-image{width:100%;height:100%;border-radius:inherit;display:block}@media (prefers-reduced-motion: reduce){.floating-icon{transition:none}}@media (max-width: 768px){.toolbar-container{padding:16px 12px 12px}.toolbar{gap:8px}.toolbar-button{padding:8px 12px;font-size:12px}.button-label{display:none}.button-icon{font-size:16px}.floating-icon{width:48px;height:48px;bottom:16px;right:16px}.floating-icon svg{width:20px;height:20px}}@media (prefers-reduced-motion: reduce){.toolbar-container{animation:none}.loading-spinner{animation:none;border-top-color:#ffffff4d}.toolbar-button:active,.floating-icon:hover{transform:none}}\n"], dependencies: [{ kind: "component", type: ViewportComponent, selector: "pw-viewport", inputs: ["panel", "page", "pageBackground", "panels", "hiddenPanelIds", "preloadTargets", "viewMode", "currentPanelId", "locale", "variableContext", "panX", "panY", "zoom", "reducedMotion", "transition", "interactive", "showOverflowArrows", "enableViewportCulling", "enableLazyLoading", "workBalloonConfig", "characters", "speechEnabled", "graph", "preload"], outputs: ["viewportClick", "performanceMetrics", "transformChange", "layerClick", "swipe", "navigatePrevious", "navigateNext", "panelDoubleClick", "panelFocus", "hotspotActivate", "deadClick", "lockedPanelActivate"] }, { kind: "component", type: CanvasStageComponent, selector: "pw-canvas-stage", inputs: ["canvas", "panels", "graph", "currentPanelId", "visitedPanelIds", "locale", "variableContext", "speechEnabled", "workBalloonConfig", "characters", "reducedMotion", "interactive", "preload"], outputs: ["panelTap", "navigateNext", "navigatePrevious", "viewportClick", "hotspotActivate", "cameraChange"] }, { kind: "component", type: ToolbarComponent, selector: "pw-toolbar", inputs: ["locale", "availableLocales", "viewMode", "pageViewAvailable", "speechEnabled", "audioEnabled", "sfxEnabled", "autoplayEnabled", "secondsPerPanel", "autoplayTiming", "autoplayProgress", "thumbnailsVisible", "hasAlternatives", "hasBranches", "liked", "bookmarked", "showSocial", "visible", "fullscreen", "fullscreenAvailable"], outputs: ["toggleFullscreen", "toggleView", "localeChange", "toggleSpeech", "toggleAudio", "toggleSfx", "toggleAutoplay", "secondsPerPanelChange", "autoplayTimingChange", "toggleThumbnails", "openToc", "openSettings", "openCharacters", "cycleAlternative", "showBranches", "openExtras", "like", "bookmark", "share", "openComments", "close", "openLanguage"] }, { kind: "component", type: ThumbnailStripComponent, selector: "pw-thumbnail-strip", inputs: ["chapters", "currentChapterId", "currentPanelId", "currentPanelIds", "coverThumbnail", "coverCurrent", "locale", "visible", "lockedPanels"], outputs: ["navigate", "close"] }, { kind: "component", type: TocOverlayComponent, selector: "pw-toc-overlay", inputs: ["manifest", "chapters", "currentChapterId", "currentPanelId", "locale", "pageFormat", "coverThumbnail", "coverCurrent", "visible"], outputs: ["navigate", "close"] }, { kind: "component", type: PaywallOverlayComponent, selector: "pw-paywall-overlay", inputs: ["visible", "gate", "purchaseOptions", "locale", "title", "message", "allowPreview", "showLogin"], outputs: ["action", "purchase", "close"] }, { kind: "component", type: SettingsModalComponent, selector: "pw-settings-modal", inputs: ["preferences", "availableLocales", "locale", "variables", "variableValues", "visible"], outputs: ["preferencesChange", "localeChange", "variableChange", "close"] }, { kind: "component", type: LanguageModalComponent, selector: "pw-language-modal", inputs: ["visible", "currentLocale", "availableLocales"], outputs: ["closeModal", "localeSelected"] }, { kind: "component", type: CharacterRosterComponent, selector: "pw-character-roster", inputs: ["characters", "locale", "visible"], outputs: ["characterSelect", "close"] }, { kind: "component", type: ExtrasViewerComponent, selector: "pw-extras-viewer", inputs: ["extras", "initialExtraId", "locale", "visible", "hasPremiumAccess"], outputs: ["purchase", "close"] }, { kind: "component", type: ActionModalComponent, selector: "pw-action-modal", inputs: ["visible", "locale", "title", "content"], outputs: ["close"] }, { kind: "component", type: AgeGateComponent, selector: "pw-age-gate", inputs: ["visible", "minimumAge", "locale", "warningMessage", "allowDismiss"], outputs: ["verify", "close"] }, { kind: "component", type: BranchChooserComponent, selector: "pw-branch-chooser", inputs: ["visible", "locale", "choices"], outputs: ["choose", "close"] }, { kind: "component", type: ShareModalComponent, selector: "pw-share-modal", inputs: ["shareUrl", "shareTitle", "shareDescription", "visible", "shareable"], outputs: ["close", "share"] }, { kind: "component", type: CommentsDrawerComponent, selector: "pw-comments-drawer", inputs: ["comments", "visible", "loading", "currentPage", "totalPages", "hasMore", "isAuthenticated"], outputs: ["postComment", "loadMore", "likeComment", "close"] }, { kind: "component", type: PwIconComponent, selector: "pw-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
22637
22760
|
}
|
|
22638
22761
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: PlayerShellComponent, decorators: [{
|
|
22639
22762
|
type: Component,
|
|
@@ -22655,7 +22778,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
22655
22778
|
ShareModalComponent,
|
|
22656
22779
|
CommentsDrawerComponent,
|
|
22657
22780
|
PwIconComponent
|
|
22658
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"player-shell\" [class.has-error]=\"hasError\">\n <!-- Loading state -->\n @if (loading) {\n <div class=\"loading-container\">\n <div class=\"loading-spinner\"></div>\n <p class=\"loading-text\">{{ 'loading.text' | translate }}</p>\n </div>\n }\n\n <!-- Error state -->\n @if (hasError && !loading) {\n <div class=\"error-container\">\n <div class=\"error-icon\"><pw-icon name=\"lucideTriangleAlert\" /></div>\n <h2 class=\"error-title\">{{ 'error.title' | translate }}</h2>\n <p class=\"error-message\">{{ errorMessage }}</p>\n <button class=\"retry-button\" type=\"button\" (click)=\"reload()\">\n {{ 'error.retry' | translate }}\n </button>\n </div>\n }\n\n <!-- Main player -->\n @if (!loading && !hasError && currentPanel) {\n <div class=\"player-content\">\n <!-- Cover (before the first panel / page) -->\n @if (coverVisible) {\n <button\n class=\"pw-cover\"\n type=\"button\"\n [attr.aria-label]=\"'navigation.start_reading' | translate\"\n (click)=\"hideCover()\">\n <img class=\"pw-cover-image\" [src]=\"coverUrl\" [alt]=\"workTitle\" fetchpriority=\"high\" />\n </button>\n }\n\n <!-- Viewport (panel / page view) -->\n @if (!coverVisible && viewMode !== 'canvas') {\n <pw-viewport\n [transition]=\"viewportTransition\"\n [panel]=\"effectivePanel || currentPanel\"\n [page]=\"currentPage || null\"\n [pageBackground]=\"pageBackground\"\n [panels]=\"resolvedPanels\"\n [hiddenPanelIds]=\"hiddenPanelIds\"\n [graph]=\"currentChapter?.graph || null\"\n [currentPanelId]=\"getCurrentPanelId() || null\"\n [viewMode]=\"viewMode\"\n [reducedMotion]=\"motionReduced\"\n [locale]=\"locale\"\n [interactive]=\"true\"\n [showOverflowArrows]=\"true\"\n [speechEnabled]=\"speechEnabled\"\n [preload]=\"manifestPreloadSettings\"\n [preloadTargets]=\"preloadTargets\"\n [variableContext]=\"variableContext\"\n (viewportClick)=\"onViewportClick()\"\n (swipe)=\"onSwipe($event)\"\n (navigatePrevious)=\"onViewportPrevious()\"\n (navigateNext)=\"onViewportNext()\"\n (panelDoubleClick)=\"onPanelDoubleClick($event)\"\n (hotspotActivate)=\"onHotspotActivate($event)\"\n (deadClick)=\"onDeadClick($event)\"\n (lockedPanelActivate)=\"onLockedPanelActivate($event)\">\n </pw-viewport>\n }\n\n <!-- Canvas stage (infinite-canvas view, schema 1.4) -->\n @if (!coverVisible && viewMode === 'canvas') {\n <pw-canvas-stage\n [canvas]=\"currentChapter?.canvas || null\"\n [panels]=\"resolvedPanels\"\n [graph]=\"currentChapter?.graph || null\"\n [currentPanelId]=\"getCurrentPanelId() || null\"\n [visitedPanelIds]=\"visitedPanelIds\"\n [locale]=\"locale\"\n [variableContext]=\"variableContext\"\n [speechEnabled]=\"speechEnabled\"\n [workBalloonConfig]=\"manifestBalloonConfig\"\n [characters]=\"manifestCharacters\"\n [reducedMotion]=\"motionReduced\"\n [interactive]=\"true\"\n [preload]=\"manifestPreloadSettings\"\n (panelTap)=\"onCanvasPanelTap($event)\"\n (navigateNext)=\"navigateNext()\"\n (navigatePrevious)=\"navigatePrevious()\"\n (viewportClick)=\"onViewportClick()\"\n (hotspotActivate)=\"onHotspotActivate($event)\"\n (cameraChange)=\"cameraChange.emit($event)\">\n </pw-canvas-stage>\n }\n\n <!-- Full Toolbar -->\n <pw-toolbar\n [visible]=\"toolbarVisible\"\n [locale]=\"locale\"\n [availableLocales]=\"availableLocales\"\n [viewMode]=\"viewMode\"\n [pageViewAvailable]=\"pageViewAvailable\"\n [speechEnabled]=\"speechEnabled\"\n [audioEnabled]=\"audioEnabled\"\n [sfxEnabled]=\"sfxEnabled\"\n [autoplayEnabled]=\"autoplayEnabled\"\n [autoplayProgress]=\"autoplayProgress\"\n [secondsPerPanel]=\"autoplaySecondsDisplay\"\n [autoplayTiming]=\"autoplayTiming\"\n [thumbnailsVisible]=\"thumbnailsVisible\"\n [hasAlternatives]=\"!!(currentPanel && currentPanel.variants && currentPanel.variants.length)\"\n [hasBranches]=\"hasBranchesAhead\"\n [showSocial]=\"true\"\n [liked]=\"liked\"\n [bookmarked]=\"bookmarked\"\n [fullscreen]=\"isFullscreen\"\n [fullscreenAvailable]=\"fullscreenAvailable\"\n (toggleFullscreen)=\"toggleFullscreen()\"\n (toggleView)=\"onToggleView()\"\n (localeChange)=\"onLocaleChange($event)\"\n (toggleSpeech)=\"onToggleSpeech()\"\n (toggleAudio)=\"onToggleAudio()\"\n (toggleSfx)=\"onToggleSfx()\"\n (toggleAutoplay)=\"onToggleAutoplay()\"\n (secondsPerPanelChange)=\"onSecondsPerPanelChange($event)\"\n (autoplayTimingChange)=\"onAutoplayTimingChange($event)\"\n (toggleThumbnails)=\"onToggleThumbnails()\"\n (openToc)=\"onOpenToc()\"\n (openSettings)=\"onOpenSettings()\"\n (openLanguage)=\"languageModalVisible = true\"\n (openCharacters)=\"onOpenCharacters()\"\n (cycleAlternative)=\"onCycleAlternative()\"\n (showBranches)=\"onShowBranches()\"\n (openExtras)=\"onOpenExtras()\"\n (like)=\"onLike()\"\n (bookmark)=\"onBookmark()\"\n (share)=\"onShare()\"\n (openComments)=\"onOpenComments()\"\n (close)=\"toggleToolbar()\">\n </pw-toolbar>\n\n <!-- Thumbnail Strip -->\n <pw-thumbnail-strip\n [visible]=\"thumbnailsVisible\"\n [chapters]=\"loadedManifest?.chapters || []\"\n [currentChapterId]=\"currentChapter?.id\"\n [currentPanelId]=\"coverVisible ? undefined : getCurrentPanelId()\"\n [currentPanelIds]=\"currentPagePanelIds\"\n [coverThumbnail]=\"coverThumbUrl\"\n [coverCurrent]=\"coverVisible\"\n [locale]=\"locale\"\n [lockedPanels]=\"[]\"\n (navigate)=\"onThumbnailNavigate($event)\"\n (close)=\"onThumbnailsClose()\">\n </pw-thumbnail-strip>\n\n <!-- Active variant indicator -->\n @if (activeVariantId) {\n <div class=\"variant-active\" role=\"status\" aria-live=\"polite\"\n [attr.aria-label]=\"'Active panel variant: ' + activeVariantId\">\n {{ activeVariantId }}\n </div>\n }\n\n <!-- PanelWave icon: opens the toolbar -->\n @if (!toolbarVisible) {\n <button\n class=\"floating-icon\"\n type=\"button\"\n [attr.aria-label]=\"'toolbar.show' | translate\"\n [attr.title]=\"'toolbar.show' | translate\"\n (click)=\"toggleToolbar()\">\n <img class=\"floating-icon-image\" [src]=\"panelwaveIcon\" alt=\"\" />\n </button>\n }\n\n <!-- Modals -->\n @if (paywallVisible && paywallGate) {\n <pw-paywall-overlay\n [visible]=\"paywallVisible\"\n [gate]=\"paywallGate\"\n [purchaseOptions]=\"paywallGate.options || []\"\n [locale]=\"locale\"\n (purchase)=\"paywallProductId = $event\"\n (action)=\"onPaywallAction($event)\"\n (close)=\"closePaywall()\">\n </pw-paywall-overlay>\n }\n\n @if (ageGateVisible) {\n <pw-age-gate\n [visible]=\"ageGateVisible\"\n [minimumAge]=\"ageGateMinimumAge\"\n [locale]=\"locale\"\n [allowDismiss]=\"true\"\n (verify)=\"onAgeGateVerify($event)\"\n (close)=\"closeAgeGate()\">\n </pw-age-gate>\n }\n\n @if (branchChooserVisible) {\n <pw-branch-chooser\n [visible]=\"branchChooserVisible\"\n [locale]=\"locale\"\n [choices]=\"branchChoices\"\n (choose)=\"onBranchChosen($event)\"\n (close)=\"branchChooserVisible = false\">\n </pw-branch-chooser>\n }\n\n @if (tocVisible) {\n <pw-toc-overlay\n [visible]=\"tocVisible\"\n [manifest]=\"loadedManifest\"\n [currentChapterId]=\"currentChapter?.id\"\n [currentPanelId]=\"coverVisible ? undefined : getCurrentPanelId()\"\n [pageFormat]=\"activePageFormat\"\n [coverThumbnail]=\"coverThumbUrl\"\n [coverCurrent]=\"coverVisible\"\n [locale]=\"locale\"\n (navigate)=\"onTocNavigate($event)\"\n (close)=\"tocVisible = false\">\n </pw-toc-overlay>\n }\n\n @if (settingsVisible) {\n <pw-settings-modal\n [visible]=\"settingsVisible\"\n [variables]=\"variableDefinitions\"\n [variableValues]=\"settingsVariableValues\"\n [availableLocales]=\"availableLocales\"\n [locale]=\"locale\"\n [preferences]=\"settingsPreferences\"\n (preferencesChange)=\"onPreferencesChange($event)\"\n (variableChange)=\"onSettingsVariableChange($event)\"\n (close)=\"settingsVisible = false\">\n </pw-settings-modal>\n }\n\n @if (languageModalVisible) {\n <pw-language-modal\n [visible]=\"languageModalVisible\"\n [currentLocale]=\"locale\"\n [availableLocales]=\"availableLocales\"\n (localeSelected)=\"onLocaleChange($event)\"\n (closeModal)=\"languageModalVisible = false\">\n </pw-language-modal>\n }\n\n @if (charactersVisible) {\n <pw-character-roster\n [visible]=\"charactersVisible\"\n [characters]=\"characters\"\n [locale]=\"locale\"\n (close)=\"charactersVisible = false\">\n </pw-character-roster>\n }\n\n @if (extrasVisible) {\n <pw-extras-viewer\n [visible]=\"extrasVisible\"\n [extras]=\"extrasList\"\n [initialExtraId]=\"extrasInitialId\"\n [locale]=\"locale\"\n (close)=\"extrasVisible = false; extrasInitialId = undefined\">\n </pw-extras-viewer>\n }\n\n @if (actionModalVisible) {\n <pw-action-modal\n [visible]=\"actionModalVisible\"\n [locale]=\"locale\"\n [title]=\"actionModalTitle\"\n [content]=\"actionModalContent\"\n (close)=\"actionModalVisible = false\">\n </pw-action-modal>\n }\n\n @if (shareVisible) {\n <pw-share-modal\n [visible]=\"shareVisible\"\n [shareUrl]=\"shareLink\"\n [shareTitle]=\"workTitle\"\n [shareable]=\"currentPanel.shareable ?? true\"\n (close)=\"shareVisible = false\">\n </pw-share-modal>\n }\n\n @if (commentsVisible) {\n <pw-comments-drawer\n [visible]=\"commentsVisible\"\n (close)=\"commentsVisible = false\">\n </pw-comments-drawer>\n }\n </div>\n }\n</div>\n", styles: [":host{font-family:var(--pw-font-ui, \"Barlow\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif)}:host(:fullscreen){display:block;width:100vw;height:100vh;background:#000}:host ::ng-deep :is(button,input,select,textarea){font-family:inherit}.player-shell{position:relative;width:100%;height:100%;overflow:hidden;background:#000;color:#fff}.pw-cover{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:16px;margin:0;border:0;background:#000;cursor:pointer;box-sizing:border-box}.pw-cover-image{max-width:100%;max-height:100%;object-fit:contain;box-shadow:0 8px 40px #0009}.pw-cover:focus-visible{outline:2px solid rgba(0,120,215,.8);outline-offset:-4px}.player-shell.has-error{background:#1a1a1a}.loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:24px}.loading-spinner{width:48px;height:48px;border:4px solid rgba(255,255,255,.1);border-top-color:#fff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-text{font-size:16px;color:#ffffffb3;margin:0}.error-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:24px;text-align:center;gap:16px}.error-icon{font-size:64px;line-height:1}.error-title{font-size:24px;font-weight:600;margin:0;color:#fff}.error-message{font-size:16px;color:#ffffffb3;margin:0;max-width:500px}.retry-button{margin-top:16px;padding:12px 24px;font-size:16px;font-weight:500;color:#fff;background:#0078d7;border:none;border-radius:4px;cursor:pointer;transition:background .2s ease}.retry-button:hover{background:#005a9e}.retry-button:active{background:#004578}.player-content{position:relative;width:100%;height:100%}.toolbar-container{position:absolute;bottom:0;left:0;right:0;z-index:100;background:linear-gradient(to top,rgba(0,0,0,.8),transparent);padding:24px 16px 16px;animation:slideUp .3s ease-out}@keyframes slideUp{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}.toolbar{display:flex;justify-content:center;align-items:center;gap:12px;max-width:1200px;margin:0 auto}.toolbar-button{display:flex;align-items:center;gap:8px;padding:10px 16px;font-size:14px;font-weight:500;color:#fff;background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer;transition:all .2s ease;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.toolbar-button:hover{background:#fff3;border-color:#ffffff4d}.toolbar-button:active{background:#ffffff26;transform:scale(.98)}.button-icon{font-size:18px;line-height:1}.button-label{font-size:14px}.variant-active{position:absolute;bottom:20px;left:20px;z-index:50;padding:4px 10px;font-size:12px;font-weight:600;color:#fff;background:#0078d7d9;border-radius:12px;box-shadow:0 2px 8px #0000004d;pointer-events:none}.floating-icon{position:absolute;bottom:20px;right:20px;z-index:50;width:48px;height:48px;padding:0;display:flex;align-items:center;justify-content:center;background:transparent;border:none;border-radius:22%;box-shadow:0 4px 12px #0006;cursor:pointer;opacity:.85;transition:transform .2s ease,opacity .2s ease,box-shadow .2s ease}.floating-icon:hover{opacity:1;transform:scale(1.08);box-shadow:0 6px 16px #00000080}.floating-icon:active{transform:scale(1.02)}.floating-icon:focus-visible{outline:2px solid #fff;outline-offset:3px}.floating-icon-image{width:100%;height:100%;border-radius:inherit;display:block}@media (prefers-reduced-motion: reduce){.floating-icon{transition:none}}@media (max-width: 768px){.toolbar-container{padding:16px 12px 12px}.toolbar{gap:8px}.toolbar-button{padding:8px 12px;font-size:12px}.button-label{display:none}.button-icon{font-size:16px}.floating-icon{width:48px;height:48px;bottom:16px;right:16px}.floating-icon svg{width:20px;height:20px}}@media (prefers-reduced-motion: reduce){.toolbar-container{animation:none}.loading-spinner{animation:none;border-top-color:#ffffff4d}.toolbar-button:active,.floating-icon:hover{transform:none}}\n"] }]
|
|
22781
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"player-shell\" [class.has-error]=\"hasError\">\n <!-- Loading state -->\n @if (loading) {\n <div class=\"loading-container\">\n <div class=\"loading-spinner\"></div>\n <p class=\"loading-text\">{{ 'loading.text' | translate }}</p>\n </div>\n }\n\n <!-- Error state -->\n @if (hasError && !loading) {\n <div class=\"error-container\">\n <div class=\"error-icon\"><pw-icon name=\"lucideTriangleAlert\" /></div>\n <h2 class=\"error-title\">{{ 'error.title' | translate }}</h2>\n <p class=\"error-message\">{{ errorMessage }}</p>\n <button class=\"retry-button\" type=\"button\" (click)=\"reload()\">\n {{ 'error.retry' | translate }}\n </button>\n </div>\n }\n\n <!-- Main player -->\n @if (!loading && !hasError && currentPanel) {\n <div class=\"player-content\">\n <!-- Cover (before the first panel / page) -->\n @if (coverVisible) {\n <button\n class=\"pw-cover\"\n type=\"button\"\n [attr.aria-label]=\"'navigation.start_reading' | translate\"\n (click)=\"hideCover()\">\n <img class=\"pw-cover-image\" [src]=\"coverUrl\" [alt]=\"workTitle\" fetchpriority=\"high\" />\n </button>\n }\n\n <!-- Viewport (panel / page view) -->\n @if (!coverVisible && viewMode !== 'canvas') {\n <pw-viewport\n [transition]=\"viewportTransition\"\n [panel]=\"effectivePanel || currentPanel\"\n [page]=\"currentPage || null\"\n [pageBackground]=\"pageBackground\"\n [panels]=\"resolvedPanels\"\n [hiddenPanelIds]=\"hiddenPanelIds\"\n [graph]=\"currentChapter?.graph || null\"\n [currentPanelId]=\"getCurrentPanelId() || null\"\n [viewMode]=\"viewMode\"\n [reducedMotion]=\"motionReduced\"\n [locale]=\"locale\"\n [interactive]=\"true\"\n [showOverflowArrows]=\"true\"\n [speechEnabled]=\"speechEnabled\"\n [preload]=\"manifestPreloadSettings\"\n [preloadTargets]=\"preloadTargets\"\n [variableContext]=\"variableContext\"\n (viewportClick)=\"onViewportClick()\"\n (swipe)=\"onSwipe($event)\"\n (navigatePrevious)=\"onViewportPrevious()\"\n (navigateNext)=\"onViewportNext()\"\n (panelDoubleClick)=\"onPanelDoubleClick($event)\"\n (hotspotActivate)=\"onHotspotActivate($event)\"\n (deadClick)=\"onDeadClick($event)\"\n (lockedPanelActivate)=\"onLockedPanelActivate($event)\">\n </pw-viewport>\n }\n\n <!-- Canvas stage (infinite-canvas view, schema 1.4) -->\n @if (!coverVisible && viewMode === 'canvas') {\n <pw-canvas-stage\n [canvas]=\"currentChapter?.canvas || null\"\n [panels]=\"resolvedPanels\"\n [graph]=\"currentChapter?.graph || null\"\n [currentPanelId]=\"getCurrentPanelId() || null\"\n [visitedPanelIds]=\"visitedPanelIds\"\n [locale]=\"locale\"\n [variableContext]=\"variableContext\"\n [speechEnabled]=\"speechEnabled\"\n [workBalloonConfig]=\"manifestBalloonConfig\"\n [characters]=\"manifestCharacters\"\n [reducedMotion]=\"motionReduced\"\n [interactive]=\"true\"\n [preload]=\"manifestPreloadSettings\"\n (panelTap)=\"onCanvasPanelTap($event)\"\n (navigateNext)=\"navigateNext()\"\n (navigatePrevious)=\"navigatePrevious()\"\n (viewportClick)=\"onViewportClick()\"\n (hotspotActivate)=\"onHotspotActivate($event)\"\n (cameraChange)=\"cameraChange.emit($event)\">\n </pw-canvas-stage>\n }\n\n <!-- Full Toolbar -->\n <pw-toolbar\n [visible]=\"toolbarVisible\"\n [locale]=\"locale\"\n [availableLocales]=\"availableLocales\"\n [viewMode]=\"viewMode\"\n [pageViewAvailable]=\"pageViewAvailable\"\n [speechEnabled]=\"speechEnabled\"\n [audioEnabled]=\"audioEnabled\"\n [sfxEnabled]=\"sfxEnabled\"\n [autoplayEnabled]=\"autoplayEnabled\"\n [autoplayProgress]=\"autoplayProgress\"\n [secondsPerPanel]=\"autoplaySecondsDisplay\"\n [autoplayTiming]=\"autoplayTiming\"\n [thumbnailsVisible]=\"thumbnailsVisible\"\n [hasAlternatives]=\"!!(currentPanel && currentPanel.variants && currentPanel.variants.length)\"\n [hasBranches]=\"hasBranchesAhead\"\n [showSocial]=\"true\"\n [liked]=\"liked\"\n [bookmarked]=\"bookmarked\"\n [fullscreen]=\"isFullscreen\"\n [fullscreenAvailable]=\"fullscreenAvailable\"\n (toggleFullscreen)=\"toggleFullscreen()\"\n (toggleView)=\"onToggleView()\"\n (localeChange)=\"onLocaleChange($event)\"\n (toggleSpeech)=\"onToggleSpeech()\"\n (toggleAudio)=\"onToggleAudio()\"\n (toggleSfx)=\"onToggleSfx()\"\n (toggleAutoplay)=\"onToggleAutoplay()\"\n (secondsPerPanelChange)=\"onSecondsPerPanelChange($event)\"\n (autoplayTimingChange)=\"onAutoplayTimingChange($event)\"\n (toggleThumbnails)=\"onToggleThumbnails()\"\n (openToc)=\"onOpenToc()\"\n (openSettings)=\"onOpenSettings()\"\n (openLanguage)=\"languageModalVisible = true\"\n (openCharacters)=\"onOpenCharacters()\"\n (cycleAlternative)=\"onCycleAlternative()\"\n (showBranches)=\"onShowBranches()\"\n (openExtras)=\"onOpenExtras()\"\n (like)=\"onLike()\"\n (bookmark)=\"onBookmark()\"\n (share)=\"onShare()\"\n (openComments)=\"onOpenComments()\"\n (close)=\"toggleToolbar()\">\n </pw-toolbar>\n\n <!-- Thumbnail Strip -->\n <pw-thumbnail-strip\n [visible]=\"thumbnailsVisible\"\n [chapters]=\"loadedManifest?.chapters || []\"\n [currentChapterId]=\"currentChapter?.id\"\n [currentPanelId]=\"coverVisible ? undefined : getCurrentPanelId()\"\n [currentPanelIds]=\"currentPagePanelIds\"\n [coverThumbnail]=\"coverThumbUrl\"\n [coverCurrent]=\"coverVisible\"\n [locale]=\"locale\"\n [lockedPanels]=\"[]\"\n (navigate)=\"onThumbnailNavigate($event)\"\n (close)=\"onThumbnailsClose()\">\n </pw-thumbnail-strip>\n\n <!-- Active variant indicator -->\n @if (activeVariantId) {\n <div class=\"variant-active\" role=\"status\" aria-live=\"polite\"\n [attr.aria-label]=\"'Active panel variant: ' + activeVariantId\">\n {{ activeVariantId }}\n </div>\n }\n\n <!-- PanelWave icon: opens the toolbar -->\n @if (!toolbarVisible) {\n <button\n class=\"floating-icon\"\n type=\"button\"\n [attr.aria-label]=\"'toolbar.show' | translate\"\n [attr.title]=\"'toolbar.show' | translate\"\n (click)=\"toggleToolbar()\">\n <img class=\"floating-icon-image\" [src]=\"panelwaveIcon\" alt=\"\" />\n </button>\n }\n\n <!-- Modals -->\n @if (paywallVisible && paywallGate) {\n <pw-paywall-overlay\n [visible]=\"paywallVisible\"\n [gate]=\"paywallGate\"\n [purchaseOptions]=\"paywallGate.options || []\"\n [locale]=\"locale\"\n (purchase)=\"paywallProductId = $event\"\n (action)=\"onPaywallAction($event)\"\n (close)=\"closePaywall()\">\n </pw-paywall-overlay>\n }\n\n @if (ageGateVisible) {\n <pw-age-gate\n [visible]=\"ageGateVisible\"\n [minimumAge]=\"ageGateMinimumAge\"\n [locale]=\"locale\"\n [allowDismiss]=\"true\"\n (verify)=\"onAgeGateVerify($event)\"\n (close)=\"closeAgeGate()\">\n </pw-age-gate>\n }\n\n @if (branchChooserVisible) {\n <pw-branch-chooser\n [visible]=\"branchChooserVisible\"\n [locale]=\"locale\"\n [choices]=\"branchChoices\"\n (choose)=\"onBranchChosen($event)\"\n (close)=\"branchChooserVisible = false\">\n </pw-branch-chooser>\n }\n\n @if (tocVisible) {\n <pw-toc-overlay\n [visible]=\"tocVisible\"\n [manifest]=\"loadedManifest\"\n [currentChapterId]=\"currentChapter?.id\"\n [currentPanelId]=\"coverVisible ? undefined : getCurrentPanelId()\"\n [pageFormat]=\"activePageFormat\"\n [coverThumbnail]=\"coverThumbUrl\"\n [coverCurrent]=\"coverVisible\"\n [locale]=\"locale\"\n (navigate)=\"onTocNavigate($event)\"\n (close)=\"tocVisible = false\">\n </pw-toc-overlay>\n }\n\n @if (settingsVisible) {\n <pw-settings-modal\n [visible]=\"settingsVisible\"\n [variables]=\"variableDefinitions\"\n [variableValues]=\"settingsVariableValues\"\n [availableLocales]=\"availableLocales\"\n [locale]=\"locale\"\n [preferences]=\"settingsPreferences\"\n (preferencesChange)=\"onPreferencesChange($event)\"\n (variableChange)=\"onSettingsVariableChange($event)\"\n (close)=\"settingsVisible = false\">\n </pw-settings-modal>\n }\n\n @if (languageModalVisible) {\n <pw-language-modal\n [visible]=\"languageModalVisible\"\n [currentLocale]=\"locale\"\n [availableLocales]=\"availableLocales\"\n (localeSelected)=\"onLocaleChange($event)\"\n (closeModal)=\"languageModalVisible = false\">\n </pw-language-modal>\n }\n\n @if (charactersVisible) {\n <pw-character-roster\n [visible]=\"charactersVisible\"\n [characters]=\"characters\"\n [locale]=\"locale\"\n (close)=\"charactersVisible = false\">\n </pw-character-roster>\n }\n\n @if (extrasVisible) {\n <pw-extras-viewer\n [visible]=\"extrasVisible\"\n [extras]=\"extrasList\"\n [initialExtraId]=\"extrasInitialId\"\n [locale]=\"locale\"\n (close)=\"extrasVisible = false; extrasInitialId = undefined\">\n </pw-extras-viewer>\n }\n\n @if (actionModalVisible) {\n <pw-action-modal\n [visible]=\"actionModalVisible\"\n [locale]=\"locale\"\n [title]=\"actionModalTitle\"\n [content]=\"actionModalContent\"\n (close)=\"actionModalVisible = false\">\n </pw-action-modal>\n }\n\n @if (shareVisible) {\n <pw-share-modal\n [visible]=\"shareVisible\"\n [shareUrl]=\"shareLink\"\n [shareTitle]=\"workTitle\"\n [shareable]=\"currentPanel.shareable ?? true\"\n (share)=\"onShared($event)\"\n (close)=\"shareVisible = false\">\n </pw-share-modal>\n }\n\n @if (commentsVisible) {\n <pw-comments-drawer\n [visible]=\"commentsVisible\"\n (close)=\"commentsVisible = false\">\n </pw-comments-drawer>\n }\n </div>\n }\n</div>\n", styles: [":host{font-family:var(--pw-font-ui, \"Barlow\", system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif)}:host(:fullscreen){display:block;width:100vw;height:100vh;background:#000}:host ::ng-deep :is(button,input,select,textarea){font-family:inherit}.player-shell{position:relative;width:100%;height:100%;overflow:hidden;background:#000;color:#fff}.pw-cover{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:16px;margin:0;border:0;background:#000;cursor:pointer;box-sizing:border-box}.pw-cover-image{max-width:100%;max-height:100%;object-fit:contain;box-shadow:0 8px 40px #0009}.pw-cover:focus-visible{outline:2px solid rgba(0,120,215,.8);outline-offset:-4px}.player-shell.has-error{background:#1a1a1a}.loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:24px}.loading-spinner{width:48px;height:48px;border:4px solid rgba(255,255,255,.1);border-top-color:#fff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-text{font-size:16px;color:#ffffffb3;margin:0}.error-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:24px;text-align:center;gap:16px}.error-icon{font-size:64px;line-height:1}.error-title{font-size:24px;font-weight:600;margin:0;color:#fff}.error-message{font-size:16px;color:#ffffffb3;margin:0;max-width:500px}.retry-button{margin-top:16px;padding:12px 24px;font-size:16px;font-weight:500;color:#fff;background:#0078d7;border:none;border-radius:4px;cursor:pointer;transition:background .2s ease}.retry-button:hover{background:#005a9e}.retry-button:active{background:#004578}.player-content{position:relative;width:100%;height:100%}.toolbar-container{position:absolute;bottom:0;left:0;right:0;z-index:100;background:linear-gradient(to top,rgba(0,0,0,.8),transparent);padding:24px 16px 16px;animation:slideUp .3s ease-out}@keyframes slideUp{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}.toolbar{display:flex;justify-content:center;align-items:center;gap:12px;max-width:1200px;margin:0 auto}.toolbar-button{display:flex;align-items:center;gap:8px;padding:10px 16px;font-size:14px;font-weight:500;color:#fff;background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer;transition:all .2s ease;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.toolbar-button:hover{background:#fff3;border-color:#ffffff4d}.toolbar-button:active{background:#ffffff26;transform:scale(.98)}.button-icon{font-size:18px;line-height:1}.button-label{font-size:14px}.variant-active{position:absolute;bottom:20px;left:20px;z-index:50;padding:4px 10px;font-size:12px;font-weight:600;color:#fff;background:#0078d7d9;border-radius:12px;box-shadow:0 2px 8px #0000004d;pointer-events:none}.floating-icon{position:absolute;bottom:20px;right:20px;z-index:50;width:48px;height:48px;padding:0;display:flex;align-items:center;justify-content:center;background:transparent;border:none;border-radius:22%;box-shadow:0 4px 12px #0006;cursor:pointer;opacity:.85;transition:transform .2s ease,opacity .2s ease,box-shadow .2s ease}.floating-icon:hover{opacity:1;transform:scale(1.08);box-shadow:0 6px 16px #00000080}.floating-icon:active{transform:scale(1.02)}.floating-icon:focus-visible{outline:2px solid #fff;outline-offset:3px}.floating-icon-image{width:100%;height:100%;border-radius:inherit;display:block}@media (prefers-reduced-motion: reduce){.floating-icon{transition:none}}@media (max-width: 768px){.toolbar-container{padding:16px 12px 12px}.toolbar{gap:8px}.toolbar-button{padding:8px 12px;font-size:12px}.button-label{display:none}.button-icon{font-size:16px}.floating-icon{width:48px;height:48px;bottom:16px;right:16px}.floating-icon svg{width:20px;height:20px}}@media (prefers-reduced-motion: reduce){.toolbar-container{animation:none}.loading-spinner{animation:none;border-top-color:#ffffff4d}.toolbar-button:active,.floating-icon:hover{transform:none}}\n"] }]
|
|
22659
22782
|
}], propDecorators: { manifest: [{
|
|
22660
22783
|
type: Input
|
|
22661
22784
|
}], manifestUrl: [{
|