flipbookengine 0.1.6 → 0.2.1

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.
@@ -0,0 +1,20 @@
1
+ import { FlipbookEngineOptions } from '../engine';
2
+
3
+ export declare class PageFlipAdapter {
4
+ private pageFlip;
5
+ private bookContainer;
6
+ private options;
7
+ private unsubs;
8
+ private audioEl;
9
+ private isLibraryFlipping;
10
+ private autoPlayTimer;
11
+ constructor(bookContainer: HTMLElement, options: FlipbookEngineOptions);
12
+ init(viewportWidth: number, viewportHeight: number): void;
13
+ private bindEvents;
14
+ private setupSignalSync;
15
+ turnToNextPage(): void;
16
+ turnToPrevPage(): void;
17
+ playSound(): void;
18
+ update(): void;
19
+ destroy(): void;
20
+ }
@@ -0,0 +1,18 @@
1
+ import { PageFlipAdapter } from '../adapters/PageFlipAdapter';
2
+ import { InteractionManager } from '../core/InteractionManager';
3
+
4
+ interface AppProps {
5
+ pageFlipAdapterRef: {
6
+ current: PageFlipAdapter | null;
7
+ };
8
+ interactionManagerRef: {
9
+ current: InteractionManager | null;
10
+ };
11
+ bookWrapperRef: (el: HTMLElement) => void;
12
+ bookSizerRef: (el: HTMLElement) => void;
13
+ bookContainerRef: (el: HTMLElement) => void;
14
+ className?: string;
15
+ onDownload: () => void;
16
+ }
17
+ export declare function App(props: AppProps): import('@serenity-is/domwise').JSXElement;
18
+ export {};
@@ -0,0 +1,5 @@
1
+ interface ThumbnailsProps {
2
+ onThumbClick: (pageIndex: number) => void;
3
+ }
4
+ export declare function Thumbnails(props: ThumbnailsProps): import('@serenity-is/domwise').JSXElement;
5
+ export {};
@@ -0,0 +1,14 @@
1
+ interface ToolbarProps {
2
+ onToggleThumbs: () => void;
3
+ onToggleSingleMode: () => void;
4
+ onZoomIn: () => void;
5
+ onZoomOut: () => void;
6
+ onDownload?: () => void;
7
+ onSoundToggle: () => void;
8
+ onNextPage: () => void;
9
+ onPrevPage: () => void;
10
+ onToggleAutoPlay: () => void;
11
+ onToggleFullscreen: () => void;
12
+ }
13
+ export declare function Toolbar(props: ToolbarProps): import('@serenity-is/domwise').JSXElement;
14
+ export {};
@@ -0,0 +1,7 @@
1
+ interface ViewerProps {
2
+ bookWrapperRef: (el: HTMLElement) => void;
3
+ bookSizerRef: (el: HTMLElement) => void;
4
+ bookContainerRef: (el: HTMLElement) => void;
5
+ }
6
+ export declare function Viewer(props: ViewerProps): import('@serenity-is/domwise').JSXElement;
7
+ export {};
@@ -0,0 +1,18 @@
1
+ import { PageFlipAdapter } from '../adapters/PageFlipAdapter';
2
+
3
+ export declare class InteractionManager {
4
+ private container;
5
+ private pageFlipAdapter;
6
+ private touchStartX;
7
+ private unsubs;
8
+ constructor(container: HTMLElement, pageFlipAdapter: PageFlipAdapter);
9
+ init(): void;
10
+ private setupStateSync;
11
+ private bindWheel;
12
+ private bindMouseDrag;
13
+ private bindTouch;
14
+ zoomIn(): void;
15
+ zoomOut(): void;
16
+ private updateZoomPan;
17
+ destroy(): void;
18
+ }
@@ -0,0 +1,19 @@
1
+ export declare class LayoutManager {
2
+ private wrapperElement;
3
+ private sizerElement;
4
+ private pageViewport;
5
+ private resizeObserver;
6
+ private orientationMediaQuery;
7
+ private unsubs;
8
+ onResizeCallback?: () => void;
9
+ constructor(pageViewport: {
10
+ width: number;
11
+ height: number;
12
+ });
13
+ init(wrapperElement: HTMLElement, sizerElement: HTMLElement): void;
14
+ private setupStateSync;
15
+ private setupResizeObserver;
16
+ private setupOrientationMedia;
17
+ updateBookAspectRatio(): void;
18
+ destroy(): void;
19
+ }
package/dist/engine.d.ts CHANGED
@@ -1,6 +1,6 @@
1
+ import { FlipbookPageAsset } from './model/pages';
1
2
  import { FlipbookThemeMode } from './theme/theme';
2
3
  import { FlipbookLocale, PartialFlipbookMessages } from './i18n/service';
3
- import { FlipbookPageAsset } from './model/pages';
4
4
 
5
5
  export interface FlipbookEngineOptions {
6
6
  allowDownload?: boolean;
@@ -20,6 +20,10 @@ export interface FlipbookEngineOptions {
20
20
  className?: string;
21
21
  cssVariables?: Record<string, string>;
22
22
  onDownload?: (url: string) => void;
23
+ singleMode?: boolean;
24
+ isSingleMode?: boolean;
25
+ autoPlay?: boolean;
26
+ autoPlayInterval?: number;
23
27
  }
24
28
  export interface PageImages extends FlipbookPageAsset {
25
29
  pageNumber: number;
@@ -56,78 +60,26 @@ export type FlipbookEngineEventName = keyof FlipbookEngineEventMap;
56
60
  type FlipbookEventHandler<T extends FlipbookEngineEventName> = (payload: FlipbookEngineEventMap[T]) => void;
57
61
  export declare class FlipbookEngine {
58
62
  private selector;
59
- private options;
60
63
  private static readonly DEFAULT_PAGE_VIEWPORT;
61
64
  private container;
62
- private ui;
63
- private pageFlip;
64
- private sourceAssets;
65
- private pages;
66
- private downloadUrl;
67
- private totalPages;
68
- private pageViewport;
65
+ private options;
66
+ private pageFlipAdapter;
67
+ private layoutManager;
68
+ private interactionManager;
69
69
  private pdfDoc;
70
- private pdfPagesCache;
71
- private currentZoom;
72
- private zoomState;
73
- private messages;
74
70
  private listeners;
75
- private domEventController;
76
- private orientationMediaQuery;
77
- private orientationChangeHandler;
78
- private initialized;
79
- private resizeObserver;
80
- private state;
81
- private touchStartX;
82
- private flipState;
83
71
  constructor(selector: string | HTMLElement, options?: FlipbookEngineOptions);
84
72
  init(pdfUrl: string, imageList?: Array<PageImages | FlipbookPageAsset>): Promise<void>;
85
- on<T extends FlipbookEngineEventName>(eventName: T, handler: FlipbookEventHandler<T>): () => void;
86
- off<T extends FlipbookEngineEventName>(eventName: T, handler: FlipbookEventHandler<T>): void;
87
- getCurrentPage(): number;
88
- getTotalPages(): number;
89
- getLocale(): string;
90
- goToPage(targetIdx: number): void;
91
- getZoom(): number;
92
- setZoom(zoomLevel: number): void;
93
- toggleFullscreen(): void;
94
- destroy(clearMarkup?: boolean): void;
95
- updateOptions(nextOptions: Partial<FlipbookEngineOptions>): void;
96
- setLocale(locale: FlipbookLocale | string, messages?: PartialFlipbookMessages): void;
97
- setPages(imageList: Array<PageImages | FlipbookPageAsset>, pdfUrl?: string): void;
98
- private buildUI;
99
- private mountViewer;
100
- private renderEmptyState;
101
- private teardownMountedViewer;
102
- private setResolvedPages;
103
- private resolvePageViewport;
104
73
  private loadImageSize;
105
- private setupOrientationHandling;
106
- private teardownRuntimeBindings;
107
- private getEventSignal;
108
- private updateBookAspectRatio;
109
- private playSound;
110
- private applyThemeConfiguration;
111
- private renderPagesIntoDOM;
112
- private initPageFlip;
113
- private createPageImage;
114
- private createThumbnailImage;
115
- private ensurePageSrc;
116
- private getPageImageClassName;
117
- private getThumbnailImageClassName;
74
+ goToPage(targetIdx: number): void;
75
+ nextPage(): void;
76
+ prevPage(): void;
118
77
  setSingleMode(isSingle: boolean): void;
119
- private updateSingleView;
120
- private syncUI;
121
- private goNext;
122
- private goPrev;
123
- private loadHighResForVisiblePages;
124
- private bindEvents;
125
- private handleDragStart;
126
- private handleDragMove;
127
- private handleDragEnd;
128
- private updateTransform;
78
+ updateOptions(options: Partial<FlipbookEngineOptions>): void;
79
+ setPages(imageList: Array<PageImages | FlipbookPageAsset>, pdfUrl?: string): void;
80
+ destroy(clearMarkup?: boolean): void;
81
+ on<T extends FlipbookEngineEventName>(eventName: T, handler: FlipbookEventHandler<T>): () => void;
82
+ off<T extends FlipbookEngineEventName>(eventName: T, handler: FlipbookEventHandler<T>): void;
129
83
  private emit;
130
- private emitPageChange;
131
- private emitZoomChange;
132
84
  }
133
85
  export {};
@@ -1 +1 @@
1
- :root{--flipbook-bg-main: var(--bs-body-bg, #f8f9fa);--flipbook-bg-surface: var(--bs-secondary-bg, #ffffff);--flipbook-bg-surface-alt: var(--bs-tertiary-bg, #f1f3f5);--flipbook-bg-panel: var(--bs-dark, #212529);--flipbook-bg-panel-soft: color-mix(in srgb, var(--flipbook-bg-panel) 88%, white 12%);--flipbook-text-main: var(--bs-body-color, #212529);--flipbook-text-muted: var(--bs-secondary-color, #6c757d);--flipbook-toolbar-text: #f8f9fa;--flipbook-toolbar-button-text: #f8f9fa;--flipbook-border: var(--bs-border-color, rgba(0, 0, 0, .15));--flipbook-accent: var(--bs-primary, #0d6efd);--flipbook-accent-text: #ffffff;--flipbook-thumbrail-bg: color-mix(in srgb, var(--flipbook-bg-panel) 95%, black 5%);--flipbook-thumb-bg: #ffffff;--flipbook-thumb-text: #ffffff;--flipbook-thumbscroll-track: rgba(0, 0, 0, .18);--flipbook-thumbscroll-thumb: rgba(255, 255, 255, .22);--flipbook-navbutton-bg: rgba(0, 0, 0, .6);--flipbook-navbutton-text: #ffffff;--flipbook-shadow: rgba(0, 0, 0, .35);--flipbook-bookpage-bg: #ffffff;--flipbook-emptycover-bg: var(--flipbook-bg-panel);--flipbook-download-bg: #198754;--flipbook-download-border: #198754;--flipbook-download-text: #ffffff;--flipbook-settings-bg: var(--flipbook-bg-surface);--flipbook-settings-text: var(--flipbook-text-main);--flipbook-radius-sm: .375rem;--flipbook-radius-md: .5rem;--flipbook-toolbar-height: 60px;--flipbook-thumbs-height: 110px;--flipbook-navbutton-size: 50px;--flipbook-page-padding: 20px;--flipbook-font-family: var(--bs-body-font-family, system-ui, -apple-system, "Segoe UI", sans-serif)}[data-bs-theme=dark]{--flipbook-bg-main: var(--bs-body-bg, #121826);--flipbook-bg-surface: var(--bs-secondary-bg, #1b2232);--flipbook-bg-surface-alt: var(--bs-tertiary-bg, #222b3c);--flipbook-bg-panel: #141927;--flipbook-bg-panel-soft: #1d2434;--flipbook-text-main: var(--bs-body-color, #f8f9fa);--flipbook-text-muted: var(--bs-secondary-color, #adb5bd);--flipbook-toolbar-text: #f8f9fa;--flipbook-toolbar-button-text: #f8f9fa;--flipbook-border: var(--bs-border-color, rgba(255, 255, 255, .12));--flipbook-thumbrail-bg: #101522;--flipbook-thumb-bg: #ffffff;--flipbook-settings-bg: #1d2434;--flipbook-settings-text: #f8f9fa;--flipbook-emptycover-bg: #101522;--flipbook-shadow: rgba(0, 0, 0, .5)}.bk-theme-light{color-scheme:light;--flipbook-bg-main: #f8fafc;--flipbook-bg-surface: #ffffff;--flipbook-bg-surface-alt: #f1f5f9;--flipbook-bg-panel: #e2e8f0;--flipbook-bg-panel-soft: #f1f5f9;--flipbook-text-main: #0f172a;--flipbook-text-muted: #64748b;--flipbook-toolbar-text: #0f172a;--flipbook-toolbar-button-text: #334155;--flipbook-border: rgba(15, 23, 42, .1);--flipbook-accent: #4f46e5;--flipbook-accent-text: #ffffff;--flipbook-thumbrail-bg: #f8fafc;--flipbook-thumb-bg: #ffffff;--flipbook-thumb-text: #0f172a;--flipbook-thumbscroll-track: rgba(15, 23, 42, .08);--flipbook-thumbscroll-thumb: rgba(15, 23, 42, .18);--flipbook-navbutton-bg: rgba(15, 23, 42, .65);--flipbook-navbutton-text: #ffffff;--flipbook-shadow: rgba(15, 23, 42, .08);--flipbook-bookpage-bg: #ffffff;--flipbook-emptycover-bg: #e2e8f0;--flipbook-download-bg: #10b981;--flipbook-download-border: #10b981;--flipbook-download-text: #ffffff;--flipbook-settings-bg: #ffffff;--flipbook-settings-text: #0f172a}.bk-theme-dark{color-scheme:dark;--flipbook-bg-main: #0b0f19;--flipbook-bg-surface: #111827;--flipbook-bg-surface-alt: #1f2937;--flipbook-bg-panel: #111827;--flipbook-bg-panel-soft: #1f2937;--flipbook-text-main: #f9fafb;--flipbook-text-muted: #9ca3af;--flipbook-toolbar-text: #f9fafb;--flipbook-toolbar-button-text: #e5e7eb;--flipbook-border: rgba(255, 255, 255, .08);--flipbook-accent: #7367f0;--flipbook-accent-text: #ffffff;--flipbook-thumbrail-bg: #0b0f19;--flipbook-thumb-bg: #1f2937;--flipbook-thumb-text: #f9fafb;--flipbook-thumbscroll-track: rgba(255, 255, 255, .08);--flipbook-thumbscroll-thumb: rgba(255, 255, 255, .18);--flipbook-navbutton-bg: rgba(0, 0, 0, .6);--flipbook-navbutton-text: #ffffff;--flipbook-shadow: rgba(0, 0, 0, .5);--flipbook-bookpage-bg: #ffffff;--flipbook-emptycover-bg: #101522;--flipbook-download-bg: #10b981;--flipbook-download-border: #10b981;--flipbook-download-text: #ffffff;--flipbook-settings-bg: #1f2937;--flipbook-settings-text: #f9fafb}.bk-container,.bk-container *{box-sizing:border-box}.bk-container{color:var(--flipbook-text-main);font-family:var(--flipbook-font-family)}.bk-wrapper{position:relative;width:100%;height:100%;display:flex;flex-direction:column;background:var(--flipbook-bg-main);color:var(--flipbook-text-main);overflow:hidden;font-family:var(--flipbook-font-family);border-radius:var(--flipbook-radius-md)}.bk-main-area{flex:1;position:relative;overflow:hidden;width:100%;min-height:0;background:var(--flipbook-bg-main)}#bk-book-wrapper{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--flipbook-page-padding);display:flex;justify-content:center;align-items:center;transition:transform .3s ease;transform-origin:center center}#bk-book-wrapper.zoomed{transition:none;cursor:grab}#bk-book-wrapper.zoomed:active{cursor:grabbing}#bk-book-wrapper.zoomed #bk-book{pointer-events:none}#bk-book-sizer{position:relative;margin:0 auto;transition:transform .4s ease}#bk-book-wrapper.bk-is-cover #bk-book-sizer{transform:translate(-25%)}#bk-book-wrapper.bk-is-back-cover #bk-book-sizer{transform:translate(25%)}.bk-container.double-mode:not(.bk-is-cover):not(.bk-is-back-cover) .stf__block{background-color:#00000080}#bk-book{width:100%;height:100%;position:relative}#bk-single-view{display:none;width:100%;height:100%;justify-content:center;align-items:center;overflow:hidden}#bk-single-img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;box-shadow:0 5px 20px var(--flipbook-shadow);background:var(--flipbook-bookpage-bg);transition:transform .3s ease,opacity .3s ease;pointer-events:none}.slide-left{transform:translate(-100%);opacity:0}.slide-right{transform:translate(100%);opacity:0}.bz-page{background-color:var(--flipbook-bg-main);overflow:hidden;box-shadow:0 0 15px var(--flipbook-shadow)}.page-shadow{position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;z-index:5}.bz-page--left .page-shadow{background:linear-gradient(to right,#0000001a,#0000 3%,#0000 85%,#0006)}.bz-page--right .page-shadow{background:linear-gradient(to right,#0006,#0000 15%,#0000 97%,#0000001a)}.bz-page--cover .page-shadow{display:none!important}.page-content{width:100%;height:100%;object-fit:contain;pointer-events:none;background:var(--flipbook-bookpage-bg);display:block}.page-content--split{width:100%;max-width:none;height:100%;object-fit:cover;object-position:left center}.page-content--left{object-position:left center}.page-content--right{object-position:right center}#bk-single-img.page-content--split{width:100%;height:100%;max-width:none;max-height:none;object-fit:cover}.bk-toolbar{height:var(--flipbook-toolbar-height);background:var(--flipbook-bg-panel-soft);color:var(--flipbook-toolbar-text);display:flex;align-items:center;justify-content:space-between;padding:0 15px;z-index:10;flex-shrink:0;border-top:1px solid var(--flipbook-border);gap:.75rem;overflow-x:auto}.bk-toolbar::-webkit-scrollbar{height:4px}.bk-toolbar::-webkit-scrollbar-thumb{background:var(--flipbook-border);border-radius:999px}.bk-btn-group{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.bk-btn-group--center{flex-grow:1;justify-content:center}.bk-btn{background:color-mix(in srgb,var(--flipbook-bg-surface) 18%,transparent 82%);border:1px solid color-mix(in srgb,var(--flipbook-border) 80%,transparent 20%);color:var(--flipbook-toolbar-button-text);padding:0 15px;height:38px;border-radius:var(--flipbook-radius-sm);cursor:pointer;font-size:15px;display:flex;align-items:center;justify-content:center;transition:background-color .2s ease,border-color .2s ease,color .2s ease}.bk-btn:hover,.bk-btn.active{background:var(--flipbook-accent);border-color:var(--flipbook-accent);color:var(--flipbook-accent-text)}.bk-btn:focus-visible,.thumb-item:focus-visible,.nav-float:focus-visible{outline:2px solid var(--flipbook-accent);outline-offset:2px}.bk-btn--download{background:var(--flipbook-download-bg);border-color:var(--flipbook-download-border);color:var(--flipbook-download-text)}#bk-page-info{color:var(--flipbook-toolbar-text);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:14px;min-width:70px;text-align:center;font-weight:700}.bk-thumbs{height:var(--flipbook-thumbs-height);background:var(--flipbook-thumbrail-bg);overflow-x:auto;padding:15px;gap:15px;z-index:10;flex-shrink:0;align-items:center;border-top:1px solid var(--flipbook-border)}.bk-thumbs::-webkit-scrollbar{height:6px}.bk-thumbs::-webkit-scrollbar-track{background:var(--flipbook-thumbscroll-track);border-radius:999px}.bk-thumbs::-webkit-scrollbar-thumb{background:var(--flipbook-thumbscroll-thumb);border-radius:999px;transition:background-color .2s ease}.bk-thumbs::-webkit-scrollbar-thumb:hover{background:var(--flipbook-accent)}.thumb-item{display:flex;flex-direction:column;align-items:center;cursor:pointer;opacity:.68;transition:opacity .2s ease,transform .2s ease;flex-shrink:0;color:var(--flipbook-thumb-text)}.thumb-item:hover,.thumb-item.active{opacity:1}.thumb-item.active img{border-color:var(--flipbook-accent);transform:scale(1.05);box-shadow:0 0 15px color-mix(in srgb,var(--flipbook-accent) 45%,transparent 55%)}.thumb-img{height:60px;width:auto;border:2px solid transparent;border-radius:var(--flipbook-radius-sm);background:var(--flipbook-thumb-bg);transition:border-color .2s ease,transform .2s ease,box-shadow .2s ease}.thumb-img--split{width:96px;max-width:none;object-fit:cover}.thumb-img--left{object-position:left center}.thumb-img--right{object-position:right center}.thumb-item span{color:var(--flipbook-thumb-text);font-size:11px;margin-top:6px;font-weight:700}.nav-float{position:absolute;z-index:10;background:var(--flipbook-navbutton-bg);border:none;border-radius:999px;width:var(--flipbook-navbutton-size);height:var(--flipbook-navbutton-size);color:var(--flipbook-navbutton-text);font-size:20px;cursor:pointer;transition:background-color .2s ease;top:50%;transform:translateY(-50%)}.nav-float:hover{background:var(--flipbook-accent)}#nav-prev{left:20px}#nav-next{right:20px}.bk-settings-panel{position:absolute;bottom:70px;right:15px;background:var(--flipbook-settings-bg);color:var(--flipbook-settings-text);border:1px solid var(--flipbook-border);border-radius:var(--flipbook-radius-md);padding:15px;display:none;z-index:20;min-width:200px;box-shadow:0 10px 30px var(--flipbook-shadow)}.setting-item{display:flex;align-items:center;justify-content:space-between;gap:1rem;font-size:14px;margin-bottom:10px}.setting-item:last-child{margin-bottom:0}.bk-empty-state{display:grid;place-items:center;width:100%;min-height:240px;padding:1.5rem;background:var(--flipbook-bg-main);color:var(--flipbook-text-main);border:1px dashed var(--flipbook-border);border-radius:var(--flipbook-radius-md)}.bk-empty-state__title{margin:0;font-size:1.125rem;font-weight:600}.empty-cover{background-color:var(--flipbook-emptycover-bg)!important;box-shadow:none!important}.bk-attribution{position:absolute;bottom:15px;left:15px;background:#0f172abf;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#fff;padding:6px 12px;border-radius:var(--flipbook-radius-sm);font-size:11px;text-decoration:none;z-index:20;display:flex;align-items:center;gap:6px;box-shadow:0 4px 12px #0006;border:1px solid rgba(255,255,255,.15);transition:all .2s ease;opacity:.85}.bk-attribution:hover{opacity:1;transform:translateY(-2px);box-shadow:0 6px 16px #0009;border-color:var(--flipbook-accent);color:#fff}.bk-attribution-icon{font-size:13px}.bk-attribution-img{height:24px;width:auto;object-fit:contain;display:block}@media (max-width: 768px){:root{--flipbook-toolbar-height: 50px;--flipbook-thumbs-height: 85px;--flipbook-navbutton-size: 35px;--flipbook-page-padding: 12px}.bk-toolbar{padding:0 5px}.bk-btn{padding:0 8px;font-size:12px;height:32px}.bk-btn-group{gap:4px}#bk-page-info{min-width:50px;font-size:12px}.nav-float{font-size:16px}#nav-prev{left:10px}#nav-next{right:10px}.bk-thumbs{padding:10px}.thumb-img{height:45px}.bk-settings-panel{bottom:60px}}.bk-btn.muted{opacity:.5;position:relative}.bk-btn.muted:after{content:"";position:absolute;width:80%;height:2px;background:currentColor;top:50%;left:10%;transform:rotate(-45deg);border-radius:1px}
1
+ :root{--flipbook-bg-main: var(--bs-body-bg, #f8f9fa);--flipbook-bg-surface: var(--bs-secondary-bg, #ffffff);--flipbook-bg-surface-alt: var(--bs-tertiary-bg, #f1f3f5);--flipbook-bg-panel: var(--bs-dark, #212529);--flipbook-bg-panel-soft: color-mix(in srgb, var(--flipbook-bg-panel) 88%, white 12%);--flipbook-text-main: var(--bs-body-color, #212529);--flipbook-text-muted: var(--bs-secondary-color, #6c757d);--flipbook-toolbar-text: #f8f9fa;--flipbook-toolbar-button-text: #f8f9fa;--flipbook-border: var(--bs-border-color, rgba(0, 0, 0, .15));--flipbook-accent: var(--bs-primary, #0d6efd);--flipbook-accent-text: #ffffff;--flipbook-thumbrail-bg: color-mix(in srgb, var(--flipbook-bg-panel) 95%, black 5%);--flipbook-thumb-bg: #ffffff;--flipbook-thumb-text: #ffffff;--flipbook-thumbscroll-track: rgba(0, 0, 0, .18);--flipbook-thumbscroll-thumb: rgba(255, 255, 255, .22);--flipbook-navbutton-bg: rgba(0, 0, 0, .6);--flipbook-navbutton-text: #ffffff;--flipbook-shadow: rgba(0, 0, 0, .35);--flipbook-bookpage-bg: #ffffff;--flipbook-emptycover-bg: var(--flipbook-bg-panel);--flipbook-download-bg: #198754;--flipbook-download-border: #198754;--flipbook-download-text: #ffffff;--flipbook-settings-bg: var(--flipbook-bg-surface);--flipbook-settings-text: var(--flipbook-text-main);--flipbook-radius-sm: .375rem;--flipbook-radius-md: .5rem;--flipbook-toolbar-height: 60px;--flipbook-thumbs-height: 110px;--flipbook-navbutton-size: 50px;--flipbook-page-padding: 20px;--flipbook-font-family: var(--bs-body-font-family, system-ui, -apple-system, "Segoe UI", sans-serif)}[data-bs-theme=dark]{--flipbook-bg-main: var(--bs-body-bg, #121826);--flipbook-bg-surface: var(--bs-secondary-bg, #1b2232);--flipbook-bg-surface-alt: var(--bs-tertiary-bg, #222b3c);--flipbook-bg-panel: #141927;--flipbook-bg-panel-soft: #1d2434;--flipbook-text-main: var(--bs-body-color, #f8f9fa);--flipbook-text-muted: var(--bs-secondary-color, #adb5bd);--flipbook-toolbar-text: #f8f9fa;--flipbook-toolbar-button-text: #f8f9fa;--flipbook-border: var(--bs-border-color, rgba(255, 255, 255, .12));--flipbook-thumbrail-bg: #101522;--flipbook-thumb-bg: #ffffff;--flipbook-settings-bg: #1d2434;--flipbook-settings-text: #f8f9fa;--flipbook-emptycover-bg: #101522;--flipbook-shadow: rgba(0, 0, 0, .5)}.bk-theme-light{color-scheme:light;--flipbook-bg-main: #f8fafc;--flipbook-bg-surface: #ffffff;--flipbook-bg-surface-alt: #f1f5f9;--flipbook-bg-panel: #e2e8f0;--flipbook-bg-panel-soft: #f1f5f9;--flipbook-text-main: #0f172a;--flipbook-text-muted: #64748b;--flipbook-toolbar-text: #0f172a;--flipbook-toolbar-button-text: #334155;--flipbook-border: rgba(15, 23, 42, .1);--flipbook-accent: #4f46e5;--flipbook-accent-text: #ffffff;--flipbook-thumbrail-bg: #f8fafc;--flipbook-thumb-bg: #ffffff;--flipbook-thumb-text: #0f172a;--flipbook-thumbscroll-track: rgba(15, 23, 42, .08);--flipbook-thumbscroll-thumb: rgba(15, 23, 42, .18);--flipbook-navbutton-bg: rgba(15, 23, 42, .65);--flipbook-navbutton-text: #ffffff;--flipbook-shadow: rgba(15, 23, 42, .08);--flipbook-bookpage-bg: #ffffff;--flipbook-emptycover-bg: #e2e8f0;--flipbook-download-bg: #10b981;--flipbook-download-border: #10b981;--flipbook-download-text: #ffffff;--flipbook-settings-bg: #ffffff;--flipbook-settings-text: #0f172a}.bk-theme-dark{color-scheme:dark;--flipbook-bg-main: #0b0f19;--flipbook-bg-surface: #111827;--flipbook-bg-surface-alt: #1f2937;--flipbook-bg-panel: #111827;--flipbook-bg-panel-soft: #1f2937;--flipbook-text-main: #f9fafb;--flipbook-text-muted: #9ca3af;--flipbook-toolbar-text: #f9fafb;--flipbook-toolbar-button-text: #e5e7eb;--flipbook-border: rgba(255, 255, 255, .08);--flipbook-accent: #7367f0;--flipbook-accent-text: #ffffff;--flipbook-thumbrail-bg: #0b0f19;--flipbook-thumb-bg: #1f2937;--flipbook-thumb-text: #f9fafb;--flipbook-thumbscroll-track: rgba(255, 255, 255, .08);--flipbook-thumbscroll-thumb: rgba(255, 255, 255, .18);--flipbook-navbutton-bg: rgba(0, 0, 0, .6);--flipbook-navbutton-text: #ffffff;--flipbook-shadow: rgba(0, 0, 0, .5);--flipbook-bookpage-bg: #ffffff;--flipbook-emptycover-bg: #101522;--flipbook-download-bg: #10b981;--flipbook-download-border: #10b981;--flipbook-download-text: #ffffff;--flipbook-settings-bg: #1f2937;--flipbook-settings-text: #f9fafb}.bk-container,.bk-container *{box-sizing:border-box}.bk-container{color:var(--flipbook-text-main);font-family:var(--flipbook-font-family);flex:1;align-self:stretch}.bk-wrapper{position:relative;width:100%;height:100%;display:flex;flex-direction:column;background:var(--flipbook-bg-main);color:var(--flipbook-text-main);overflow:hidden;font-family:var(--flipbook-font-family);border-radius:var(--flipbook-radius-md);flex:1;align-self:stretch}.bk-main-area{flex:1;position:relative;overflow:hidden;width:100%;min-height:0;background:var(--flipbook-bg-main)}.bk-container.single-mode .bk-main-area{overflow-y:auto;overflow-x:hidden}#bk-book-wrapper{position:absolute;inset:var(--flipbook-page-padding);padding:0;display:flex;justify-content:center;align-items:center;transition:transform .3s ease;transform-origin:center center}.bk-container.single-mode #bk-book-wrapper{position:relative;min-height:100%;inset:auto;padding:var(--flipbook-page-padding)}#bk-book-wrapper.zoomed{transition:none;cursor:grab}#bk-book-wrapper.zoomed:active{cursor:grabbing}#bk-book-wrapper.zoomed #bk-book{pointer-events:none}#bk-book-sizer{position:relative;margin:0 auto;transition:transform .4s ease}#bk-book-wrapper.bk-is-cover #bk-book-sizer{transform:translate(-25%)}#bk-book-wrapper.bk-is-back-cover #bk-book-sizer{transform:translate(25%)}.bk-container.double-mode:not(.bk-is-cover):not(.bk-is-back-cover) .stf__block{background-color:#00000080}#bk-book{width:100%;height:100%;position:relative}#bk-single-view{display:none;width:100%;height:100%;justify-content:center;align-items:center;overflow:hidden}#bk-single-img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;box-shadow:0 5px 20px var(--flipbook-shadow);background:var(--flipbook-bookpage-bg);transition:transform .3s ease,opacity .3s ease;pointer-events:none}.slide-left{transform:translate(-100%);opacity:0}.slide-right{transform:translate(100%);opacity:0}.bz-page{background-color:var(--flipbook-bg-main);overflow:hidden;box-shadow:0 0 15px var(--flipbook-shadow)}.page-shadow{position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;z-index:5}.bz-page--left .page-shadow{background:linear-gradient(to right,#0000001a,#0000 3%,#0000 85%,#0006)}.bz-page--right .page-shadow{background:linear-gradient(to right,#0006,#0000 15%,#0000 97%,#0000001a)}.bz-page--cover .page-shadow{display:none!important}.page-content{width:100%;height:100%;object-fit:contain;pointer-events:none;background:var(--flipbook-bookpage-bg);display:block}.page-content--split{width:100%;max-width:none;height:100%;object-fit:cover;object-position:left center}.page-content--left{object-position:left center}.page-content--right{object-position:right center}#bk-single-img.page-content--split{width:100%;height:100%;max-width:none;max-height:none;object-fit:cover}.bk-toolbar{height:var(--flipbook-toolbar-height);background:var(--flipbook-bg-panel-soft);color:var(--flipbook-toolbar-text);display:flex;align-items:center;justify-content:space-between;padding:0 15px;z-index:10;flex-shrink:0;border-top:1px solid var(--flipbook-border);gap:.75rem;overflow-x:auto;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.bk-toolbar::-webkit-scrollbar{height:4px}.bk-toolbar::-webkit-scrollbar-thumb{background:var(--flipbook-border);border-radius:999px}.bk-btn-group{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.bk-btn-group--center{flex-grow:1;justify-content:center}.bk-btn{background:transparent;border:1px solid transparent;color:var(--flipbook-toolbar-button-text);width:38px;height:38px;padding:0;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s cubic-bezier(.4,0,.2,1)}.bk-btn:hover{background:color-mix(in srgb,var(--flipbook-text-main) 10%,transparent);transform:translateY(-1px)}.bk-btn:active{transform:translateY(1px)}.bk-btn.active{background:color-mix(in srgb,var(--flipbook-accent) 15%,transparent);color:var(--flipbook-accent)}.bk-btn:focus-visible,.thumb-item:focus-visible,.nav-float:focus-visible{outline:2px solid var(--flipbook-accent);outline-offset:2px}.bk-btn--download{color:var(--flipbook-download-bg)}.bk-btn--download:hover{background:color-mix(in srgb,var(--flipbook-download-bg) 15%,transparent)}#bk-page-info{color:var(--flipbook-toolbar-text);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:14px;min-width:70px;text-align:center;font-weight:700}.bk-thumbs{height:var(--flipbook-thumbs-height);background:var(--flipbook-thumbrail-bg);overflow-x:auto;padding:15px;gap:15px;z-index:10;display:flex;flex-shrink:0;align-items:center;border-top:1px solid var(--flipbook-border);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.bk-watermark{position:absolute;bottom:12px;right:15px;font-size:11px;color:var(--flipbook-text-muted);background:color-mix(in srgb,var(--flipbook-bg-surface) 60%,transparent);padding:4px 10px;border-radius:99px;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border:1px solid var(--flipbook-border);z-index:100;box-shadow:0 2px 10px var(--flipbook-shadow);transition:all .2s ease;pointer-events:auto}.bk-watermark:hover{background:var(--flipbook-bg-surface);color:var(--flipbook-text-main);transform:translateY(-2px)}.bk-watermark a{color:inherit;font-weight:600;text-decoration:none}.bk-thumbs::-webkit-scrollbar{height:6px}.bk-thumbs::-webkit-scrollbar-track{background:var(--flipbook-thumbscroll-track);border-radius:999px}.bk-thumbs::-webkit-scrollbar-thumb{background:var(--flipbook-thumbscroll-thumb);border-radius:999px;transition:background-color .2s ease}.bk-thumbs::-webkit-scrollbar-thumb:hover{background:var(--flipbook-accent)}.thumb-item{display:flex;flex-direction:column;align-items:center;cursor:pointer;opacity:.7;transition:all .2s ease;min-width:60px;flex-shrink:0;gap:5px}.thumb-item:hover{opacity:1;transform:translateY(-2px)}.thumb-item.active{opacity:1}.thumb-item.active .thumb-img-wrapper{border-color:var(--flipbook-accent);box-shadow:0 0 0 2px var(--flipbook-accent),0 4px 12px var(--flipbook-shadow)}.thumb-item.active span{color:var(--flipbook-accent);font-weight:700}.thumb-img-wrapper{width:50px;height:65px;background:var(--flipbook-bg-surface);border:1px solid var(--flipbook-border);border-radius:var(--flipbook-radius-sm);display:flex;align-items:center;justify-content:center;overflow:hidden;box-shadow:0 2px 5px var(--flipbook-shadow);transition:all .2s ease}.thumb-img{width:100%;height:100%;object-fit:cover}.thumb-img--split{width:96px;max-width:none;object-fit:cover}.thumb-img--left{object-position:left center}.thumb-img--right{object-position:right center}.thumb-item span{color:var(--flipbook-thumb-text);font-size:11px;margin-top:6px;font-weight:700}.nav-float{position:absolute;z-index:10;background:var(--flipbook-navbutton-bg);border:none;border-radius:999px;width:var(--flipbook-navbutton-size);height:var(--flipbook-navbutton-size);color:var(--flipbook-navbutton-text);font-size:20px;cursor:pointer;transition:background-color .2s ease;top:50%;transform:translateY(-50%)}.nav-float:hover{background:var(--flipbook-accent)}#nav-prev{left:20px}#nav-next{right:20px}.bk-settings-panel{position:absolute;bottom:70px;right:15px;background:var(--flipbook-settings-bg);color:var(--flipbook-settings-text);border:1px solid var(--flipbook-border);border-radius:var(--flipbook-radius-md);padding:15px;display:none;z-index:20;min-width:200px;box-shadow:0 10px 30px var(--flipbook-shadow)}.setting-item{display:flex;align-items:center;justify-content:space-between;gap:1rem;font-size:14px;margin-bottom:10px}.setting-item:last-child{margin-bottom:0}.bk-empty-state{display:grid;place-items:center;width:100%;min-height:240px;padding:1.5rem;background:var(--flipbook-bg-main);color:var(--flipbook-text-main);border:1px dashed var(--flipbook-border);border-radius:var(--flipbook-radius-md)}.bk-empty-state__title{margin:0;font-size:1.125rem;font-weight:600}.empty-cover{background-color:var(--flipbook-emptycover-bg)!important;box-shadow:none!important}.bk-attribution{position:absolute;bottom:15px;left:15px;background:#0f172abf;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#fff;padding:6px 12px;border-radius:var(--flipbook-radius-sm);font-size:11px;text-decoration:none;z-index:20;display:flex;align-items:center;gap:6px;box-shadow:0 4px 12px #0006;border:1px solid rgba(255,255,255,.15);transition:all .2s ease;opacity:.85}.bk-attribution:hover{opacity:1;transform:translateY(-2px);box-shadow:0 6px 16px #0009;border-color:var(--flipbook-accent);color:#fff}.bk-attribution-icon{font-size:13px}.bk-attribution-img{height:24px;width:auto;object-fit:contain;display:block}@media (max-width: 768px){:root{--flipbook-toolbar-height: 50px;--flipbook-thumbs-height: 85px;--flipbook-navbutton-size: 35px;--flipbook-page-padding: 12px}.bk-toolbar{padding:0 5px}.bk-btn{padding:0 8px;font-size:12px;height:32px}.bk-btn-group{gap:4px}#bk-page-info{min-width:50px;font-size:12px}.nav-float{font-size:16px}#nav-prev{left:10px}#nav-next{right:10px}.bk-thumbs{padding:10px}.thumb-img{height:45px}.bk-settings-panel{bottom:60px}}.bk-btn.muted{opacity:.5;position:relative}.bk-btn.muted:after{content:"";position:absolute;width:80%;height:2px;background:currentColor;top:50%;left:10%;transform:rotate(-45deg);border-radius:1px}