aksos-react-native-epub-reader 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +338 -2
- package/lib/EpubReader.d.ts +63 -0
- package/lib/EpubReader.js +199 -0
- package/lib/EpubReaderView.d.ts +24 -0
- package/lib/EpubReaderView.js +504 -0
- package/lib/assets/page-turn-1.wav +0 -0
- package/lib/assets/page-turn-2.wav +0 -0
- package/lib/assets/page-turn-3.wav +0 -0
- package/lib/assets/page-turn-4.wav +0 -0
- package/lib/engine/html.d.ts +1 -0
- package/lib/engine/html.js +5 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +30 -0
- package/lib/sound.d.ts +1 -0
- package/lib/sound.js +71 -0
- package/lib/themes.d.ts +56 -0
- package/lib/themes.js +166 -0
- package/lib/types.d.ts +133 -0
- package/lib/types.js +2 -0
- package/lib/ui/AppearanceSheet.d.ts +15 -0
- package/lib/ui/AppearanceSheet.js +127 -0
- package/lib/ui/ContentsSheet.d.ts +27 -0
- package/lib/ui/ContentsSheet.js +87 -0
- package/lib/ui/CustomizeSheet.d.ts +16 -0
- package/lib/ui/CustomizeSheet.js +139 -0
- package/lib/ui/Glass.d.ts +15 -0
- package/lib/ui/Glass.js +27 -0
- package/lib/ui/HighlightMenu.d.ts +25 -0
- package/lib/ui/HighlightMenu.js +43 -0
- package/lib/ui/Icons.d.ts +17 -0
- package/lib/ui/Icons.js +193 -0
- package/lib/ui/LineGuide.d.ts +21 -0
- package/lib/ui/LineGuide.js +92 -0
- package/lib/ui/NoteEditor.d.ts +12 -0
- package/lib/ui/NoteEditor.js +61 -0
- package/lib/ui/Scrubber.d.ts +15 -0
- package/lib/ui/Scrubber.js +86 -0
- package/lib/ui/SearchSheet.d.ts +14 -0
- package/lib/ui/SearchSheet.js +67 -0
- package/lib/ui/Sheet.d.ts +19 -0
- package/lib/ui/Sheet.js +38 -0
- package/lib/ui/Slider.d.ts +14 -0
- package/lib/ui/Slider.js +53 -0
- package/package.json +75 -5
- package/src/EpubReader.tsx +256 -0
- package/src/EpubReaderView.tsx +721 -0
- package/src/assets/page-turn-1.wav +0 -0
- package/src/assets/page-turn-2.wav +0 -0
- package/src/assets/page-turn-3.wav +0 -0
- package/src/assets/page-turn-4.wav +0 -0
- package/src/engine/html.ts +2 -0
- package/src/engine/reader.js +1696 -0
- package/src/index.ts +6 -0
- package/src/sound.ts +66 -0
- package/src/themes.ts +188 -0
- package/src/types.ts +138 -0
- package/src/ui/AppearanceSheet.tsx +167 -0
- package/src/ui/ContentsSheet.tsx +144 -0
- package/src/ui/CustomizeSheet.tsx +189 -0
- package/src/ui/Glass.tsx +37 -0
- package/src/ui/HighlightMenu.tsx +64 -0
- package/src/ui/Icons.tsx +246 -0
- package/src/ui/LineGuide.tsx +87 -0
- package/src/ui/NoteEditor.tsx +55 -0
- package/src/ui/Scrubber.tsx +78 -0
- package/src/ui/SearchSheet.tsx +79 -0
- package/src/ui/Sheet.tsx +69 -0
- package/src/ui/Slider.tsx +30 -0
package/lib/themes.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { HighlightColor, ReaderFont, ReaderSettings, ReaderTheme, ThemeColors } from './types';
|
|
2
|
+
export declare const THEMES: ReaderTheme[];
|
|
3
|
+
/** Font stacks. iOS ships most of these, on Android the stacks fall back to the system serif / sans. */
|
|
4
|
+
export declare const FONTS: ReaderFont[];
|
|
5
|
+
export declare const HIGHLIGHT_COLORS: Record<HighlightColor, string>;
|
|
6
|
+
export declare const DEFAULT_SETTINGS: ReaderSettings;
|
|
7
|
+
export declare const getTheme: (id: string) => ReaderTheme;
|
|
8
|
+
export declare const getFont: (id: string) => ReaderFont;
|
|
9
|
+
export declare const serifFont: string;
|
|
10
|
+
export declare function isDark(settings: ReaderSettings, systemDark: boolean): boolean;
|
|
11
|
+
export declare function themeColors(settings: ReaderSettings, systemDark: boolean): ThemeColors;
|
|
12
|
+
/** Settings object understood by the WebView engine. */
|
|
13
|
+
export declare function toEngineSettings(settings: ReaderSettings, systemDark: boolean, insets: {
|
|
14
|
+
top: number;
|
|
15
|
+
bottom: number;
|
|
16
|
+
}): {
|
|
17
|
+
bg: string;
|
|
18
|
+
fg: string;
|
|
19
|
+
link: string;
|
|
20
|
+
bold: boolean;
|
|
21
|
+
fontFamily: string;
|
|
22
|
+
fontSize: number;
|
|
23
|
+
lineHeight: number;
|
|
24
|
+
letterSpacing: number;
|
|
25
|
+
wordSpacing: number;
|
|
26
|
+
marginX: number;
|
|
27
|
+
columns: import("./types").ColumnsSetting;
|
|
28
|
+
justify: boolean;
|
|
29
|
+
mode: import("./types").PageTurn;
|
|
30
|
+
direction: "auto" | "ltr" | "rtl";
|
|
31
|
+
insets: {
|
|
32
|
+
top: number;
|
|
33
|
+
bottom: number;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export interface ChromeColors {
|
|
37
|
+
/** translucent control background (pills, circles), tinted with the page colour */
|
|
38
|
+
glass: string;
|
|
39
|
+
glassBorder: string;
|
|
40
|
+
/** solid sheet colour and a translucent one for sheets that float over the book */
|
|
41
|
+
sheet: string;
|
|
42
|
+
sheetGlass: string;
|
|
43
|
+
/** rows / cards inside a sheet: a neutral wash that works on any theme */
|
|
44
|
+
card: string;
|
|
45
|
+
text: string;
|
|
46
|
+
subtext: string;
|
|
47
|
+
separator: string;
|
|
48
|
+
/** highlight for the current chapter / selected items */
|
|
49
|
+
accent: string;
|
|
50
|
+
dark: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Chrome (buttons, menus, sheets) is frosted glass tinted with the page colour, so a Calm page gets warm glass and a
|
|
54
|
+
* black page gets dark glass. Text and icons use the neutral text colour of the light / dark variant.
|
|
55
|
+
*/
|
|
56
|
+
export declare function chromeColors(colors: ThemeColors, dark: boolean): ChromeColors;
|
package/lib/themes.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serifFont = exports.getFont = exports.getTheme = exports.DEFAULT_SETTINGS = exports.HIGHLIGHT_COLORS = exports.FONTS = exports.THEMES = void 0;
|
|
4
|
+
exports.isDark = isDark;
|
|
5
|
+
exports.themeColors = themeColors;
|
|
6
|
+
exports.toEngineSettings = toEngineSettings;
|
|
7
|
+
exports.chromeColors = chromeColors;
|
|
8
|
+
const react_native_1 = require("react-native");
|
|
9
|
+
exports.THEMES = [
|
|
10
|
+
{
|
|
11
|
+
id: 'original',
|
|
12
|
+
name: 'Original',
|
|
13
|
+
light: { bg: '#ffffff', fg: '#1c1c1e', link: '#0a60ff' },
|
|
14
|
+
dark: { bg: '#000000', fg: '#f2f2f7', link: '#64a8ff' }
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'quiet',
|
|
18
|
+
name: 'Quiet',
|
|
19
|
+
light: { bg: '#e8e8ea', fg: '#58585c', link: '#3a6ea5' },
|
|
20
|
+
dark: { bg: '#121212', fg: '#8e8e93', link: '#6c9bd1' }
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'paper',
|
|
24
|
+
name: 'Paper',
|
|
25
|
+
light: { bg: '#f6f0e1', fg: '#3b3226', link: '#8a5a2b' },
|
|
26
|
+
dark: { bg: '#1d1d1f', fg: '#e9e0cd', link: '#d6a86a' }
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'bold',
|
|
30
|
+
name: 'Bold',
|
|
31
|
+
bold: true,
|
|
32
|
+
light: { bg: '#ffffff', fg: '#000000', link: '#0a60ff' },
|
|
33
|
+
dark: { bg: '#000000', fg: '#ffffff', link: '#64a8ff' }
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'calm',
|
|
37
|
+
name: 'Calm',
|
|
38
|
+
light: { bg: '#ece1c9', fg: '#4a3f2c', link: '#7a5a1f' },
|
|
39
|
+
dark: { bg: '#3d372d', fg: '#f0e7d3', link: '#e0b66f' }
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'focus',
|
|
43
|
+
name: 'Focus',
|
|
44
|
+
light: { bg: '#f3efe2', fg: '#2d2a24', link: '#9a4b1c' },
|
|
45
|
+
dark: { bg: '#1a1913', fg: '#e8e2d0', link: '#d98f5a' }
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
const SERIF = react_native_1.Platform.OS === 'ios' ? 'Georgia' : 'serif';
|
|
49
|
+
/** Font stacks. iOS ships most of these, on Android the stacks fall back to the system serif / sans. */
|
|
50
|
+
exports.FONTS = [
|
|
51
|
+
{ id: 'original', name: 'Original', css: 'original' },
|
|
52
|
+
{ id: 'system', name: 'System', css: "-apple-system, system-ui, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif" },
|
|
53
|
+
{ id: 'newyork', name: 'New York', css: "ui-serif, 'New York', Georgia, serif" },
|
|
54
|
+
{ id: 'athelas', name: 'Athelas', css: "Athelas, Georgia, serif" },
|
|
55
|
+
{ id: 'charter', name: 'Charter', css: "Charter, 'Bitstream Charter', Georgia, serif" },
|
|
56
|
+
{ id: 'georgia', name: 'Georgia', css: "Georgia, 'Times New Roman', serif" },
|
|
57
|
+
{ id: 'iowan', name: 'Iowan', css: "'Iowan Old Style', Georgia, serif" },
|
|
58
|
+
{ id: 'palatino', name: 'Palatino', css: "Palatino, 'Palatino Linotype', 'Book Antiqua', serif" },
|
|
59
|
+
{ id: 'seravek', name: 'Seravek', css: "Seravek, 'Avenir Next', -apple-system, sans-serif" },
|
|
60
|
+
{ id: 'times', name: 'Times New Roman', css: "'Times New Roman', Times, serif" },
|
|
61
|
+
{ id: 'naskh', name: 'Naskh (عربي / پښتو)', css: "'Noto Naskh Arabic', 'Geeza Pro', 'Droid Arabic Naskh', 'Scheherazade New', serif" }
|
|
62
|
+
];
|
|
63
|
+
exports.HIGHLIGHT_COLORS = {
|
|
64
|
+
yellow: '#ffd60a',
|
|
65
|
+
green: '#34c759',
|
|
66
|
+
blue: '#0a84ff',
|
|
67
|
+
pink: '#ff375f',
|
|
68
|
+
purple: '#bf5af2'
|
|
69
|
+
};
|
|
70
|
+
exports.DEFAULT_SETTINGS = {
|
|
71
|
+
themeId: 'original',
|
|
72
|
+
appearance: 'device',
|
|
73
|
+
fontId: 'original',
|
|
74
|
+
fontSize: 19,
|
|
75
|
+
bold: false,
|
|
76
|
+
customize: false,
|
|
77
|
+
lineHeight: 1.5,
|
|
78
|
+
letterSpacing: 0,
|
|
79
|
+
wordSpacing: 0,
|
|
80
|
+
margins: 0,
|
|
81
|
+
columns: 'auto',
|
|
82
|
+
justify: false,
|
|
83
|
+
pageTurn: 'slide',
|
|
84
|
+
pageSound: true,
|
|
85
|
+
brightness: 1,
|
|
86
|
+
direction: 'auto'
|
|
87
|
+
};
|
|
88
|
+
const getTheme = (id) => exports.THEMES.find((t) => t.id === id) || exports.THEMES[0];
|
|
89
|
+
exports.getTheme = getTheme;
|
|
90
|
+
const getFont = (id) => exports.FONTS.find((f) => f.id === id) || exports.FONTS[0];
|
|
91
|
+
exports.getFont = getFont;
|
|
92
|
+
exports.serifFont = SERIF;
|
|
93
|
+
function isDark(settings, systemDark) {
|
|
94
|
+
return settings.appearance === 'device' ? systemDark : settings.appearance === 'dark';
|
|
95
|
+
}
|
|
96
|
+
function themeColors(settings, systemDark) {
|
|
97
|
+
const t = (0, exports.getTheme)(settings.themeId);
|
|
98
|
+
return isDark(settings, systemDark) ? t.dark : t.light;
|
|
99
|
+
}
|
|
100
|
+
const BASE_MARGIN = 28;
|
|
101
|
+
/** Settings object understood by the WebView engine. */
|
|
102
|
+
function toEngineSettings(settings, systemDark, insets) {
|
|
103
|
+
const theme = (0, exports.getTheme)(settings.themeId);
|
|
104
|
+
const c = themeColors(settings, systemDark);
|
|
105
|
+
const custom = settings.customize;
|
|
106
|
+
return {
|
|
107
|
+
bg: c.bg,
|
|
108
|
+
fg: c.fg,
|
|
109
|
+
link: c.link,
|
|
110
|
+
bold: !!theme.bold || settings.bold,
|
|
111
|
+
fontFamily: (0, exports.getFont)(settings.fontId).css,
|
|
112
|
+
fontSize: settings.fontSize,
|
|
113
|
+
lineHeight: custom ? settings.lineHeight : 1.5,
|
|
114
|
+
letterSpacing: custom ? (settings.letterSpacing / 100) * 0.25 : 0,
|
|
115
|
+
wordSpacing: custom ? (settings.wordSpacing / 100) * 0.5 : 0,
|
|
116
|
+
marginX: Math.round(BASE_MARGIN * (1 + (custom ? settings.margins : 0) / 100)),
|
|
117
|
+
columns: settings.columns,
|
|
118
|
+
justify: settings.justify,
|
|
119
|
+
mode: settings.pageTurn,
|
|
120
|
+
direction: settings.direction,
|
|
121
|
+
insets: { top: insets.top + 40, bottom: insets.bottom + 40 }
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function mix(hex, other, amount) {
|
|
125
|
+
const p = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16));
|
|
126
|
+
const a = p(hex);
|
|
127
|
+
const b = p(other);
|
|
128
|
+
const m = a.map((v, i) => Math.round(v + (b[i] - v) * amount));
|
|
129
|
+
return '#' + m.map((v) => v.toString(16).padStart(2, '0')).join('');
|
|
130
|
+
}
|
|
131
|
+
function rgba(hex, alpha) {
|
|
132
|
+
const v = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16));
|
|
133
|
+
return `rgba(${v[0]},${v[1]},${v[2]},${alpha})`;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Chrome (buttons, menus, sheets) is frosted glass tinted with the page colour, so a Calm page gets warm glass and a
|
|
137
|
+
* black page gets dark glass. Text and icons use the neutral text colour of the light / dark variant.
|
|
138
|
+
*/
|
|
139
|
+
function chromeColors(colors, dark) {
|
|
140
|
+
if (dark) {
|
|
141
|
+
return {
|
|
142
|
+
glass: rgba(mix(colors.bg, '#ffffff', 0.22), 0.4),
|
|
143
|
+
glassBorder: 'rgba(255,255,255,0.16)',
|
|
144
|
+
sheet: mix(colors.bg, '#ffffff', 0.1),
|
|
145
|
+
sheetGlass: rgba(mix(colors.bg, '#ffffff', 0.1), 0.84),
|
|
146
|
+
card: 'rgba(255,255,255,0.1)',
|
|
147
|
+
text: '#ffffff',
|
|
148
|
+
subtext: '#a09d96',
|
|
149
|
+
separator: 'rgba(255,255,255,0.14)',
|
|
150
|
+
accent: '#ffb340',
|
|
151
|
+
dark
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
glass: rgba(mix(colors.bg, '#ffffff', 0.55), 0.5),
|
|
156
|
+
glassBorder: 'rgba(0,0,0,0.1)',
|
|
157
|
+
sheet: mix(colors.bg, '#ffffff', 0.5),
|
|
158
|
+
sheetGlass: rgba(mix(colors.bg, '#ffffff', 0.5), 0.86),
|
|
159
|
+
card: 'rgba(0,0,0,0.06)',
|
|
160
|
+
text: '#000000',
|
|
161
|
+
subtext: '#6c6c70',
|
|
162
|
+
separator: 'rgba(0,0,0,0.14)',
|
|
163
|
+
accent: '#ff9500',
|
|
164
|
+
dark
|
|
165
|
+
};
|
|
166
|
+
}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export type HighlightColor = 'yellow' | 'green' | 'blue' | 'pink' | 'purple';
|
|
2
|
+
export type HighlightStyle = 'highlight' | 'underline';
|
|
3
|
+
/** Position in the book. Offsets are text-character offsets inside the spine item, so they survive font/size changes. */
|
|
4
|
+
export interface BookLocation {
|
|
5
|
+
spine: number;
|
|
6
|
+
offset: number;
|
|
7
|
+
}
|
|
8
|
+
export interface Highlight {
|
|
9
|
+
id: string;
|
|
10
|
+
spine: number;
|
|
11
|
+
start: number;
|
|
12
|
+
end: number;
|
|
13
|
+
text: string;
|
|
14
|
+
color: HighlightColor;
|
|
15
|
+
style?: HighlightStyle;
|
|
16
|
+
note?: string;
|
|
17
|
+
chapter?: string;
|
|
18
|
+
createdAt: number;
|
|
19
|
+
}
|
|
20
|
+
export interface Bookmark {
|
|
21
|
+
id: string;
|
|
22
|
+
spine: number;
|
|
23
|
+
offset: number;
|
|
24
|
+
chapter: string;
|
|
25
|
+
excerpt: string;
|
|
26
|
+
progress: number;
|
|
27
|
+
createdAt: number;
|
|
28
|
+
}
|
|
29
|
+
export interface TocItem {
|
|
30
|
+
label: string;
|
|
31
|
+
spine: number;
|
|
32
|
+
fragment: string;
|
|
33
|
+
level: number;
|
|
34
|
+
}
|
|
35
|
+
export interface BookMetadata {
|
|
36
|
+
title: string;
|
|
37
|
+
author: string;
|
|
38
|
+
language: string;
|
|
39
|
+
publisher: string;
|
|
40
|
+
}
|
|
41
|
+
export interface RelocateEvent {
|
|
42
|
+
spine: number;
|
|
43
|
+
/** offset of the first character on the page */
|
|
44
|
+
offset: number;
|
|
45
|
+
/** offset just past the last character on the page */
|
|
46
|
+
end: number;
|
|
47
|
+
page: number;
|
|
48
|
+
pageCount: number;
|
|
49
|
+
spineCount: number;
|
|
50
|
+
/** number of characters in this spine item */
|
|
51
|
+
textLength: number;
|
|
52
|
+
/** 0..1 across the whole book (exact once `indexed` is true) */
|
|
53
|
+
progress: number;
|
|
54
|
+
chapter: string;
|
|
55
|
+
excerpt: string;
|
|
56
|
+
indexed: boolean;
|
|
57
|
+
}
|
|
58
|
+
export interface SelectionEvent {
|
|
59
|
+
spine: number;
|
|
60
|
+
start: number;
|
|
61
|
+
end: number;
|
|
62
|
+
text: string;
|
|
63
|
+
rect: {
|
|
64
|
+
x: number;
|
|
65
|
+
y: number;
|
|
66
|
+
width: number;
|
|
67
|
+
height: number;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export interface SearchResult {
|
|
71
|
+
spine: number;
|
|
72
|
+
offset: number;
|
|
73
|
+
length: number;
|
|
74
|
+
chapter: string;
|
|
75
|
+
before: string;
|
|
76
|
+
match: string;
|
|
77
|
+
after: string;
|
|
78
|
+
}
|
|
79
|
+
export interface ThemeColors {
|
|
80
|
+
/** page background */
|
|
81
|
+
bg: string;
|
|
82
|
+
/** body text */
|
|
83
|
+
fg: string;
|
|
84
|
+
link: string;
|
|
85
|
+
}
|
|
86
|
+
/** A theme has a light and a dark variant, like the themes in Apple Books. */
|
|
87
|
+
export interface ReaderTheme {
|
|
88
|
+
id: string;
|
|
89
|
+
name: string;
|
|
90
|
+
light: ThemeColors;
|
|
91
|
+
dark: ThemeColors;
|
|
92
|
+
bold?: boolean;
|
|
93
|
+
}
|
|
94
|
+
export type Appearance = 'light' | 'dark' | 'device';
|
|
95
|
+
export type PageTurn = 'slide' | 'curl' | 'fade' | 'scroll';
|
|
96
|
+
export type ColumnsSetting = 'auto' | 1 | 2;
|
|
97
|
+
export interface ReaderFont {
|
|
98
|
+
id: string;
|
|
99
|
+
name: string;
|
|
100
|
+
/** CSS font-family, or 'original' to keep the book's own fonts */
|
|
101
|
+
css: string;
|
|
102
|
+
}
|
|
103
|
+
export interface ReaderSettings {
|
|
104
|
+
themeId: string;
|
|
105
|
+
appearance: Appearance;
|
|
106
|
+
fontId: string;
|
|
107
|
+
fontSize: number;
|
|
108
|
+
/** extra bold weight on top of the theme's own */
|
|
109
|
+
bold: boolean;
|
|
110
|
+
/** when false the spacing values below are ignored (defaults are used) */
|
|
111
|
+
customize: boolean;
|
|
112
|
+
lineHeight: number;
|
|
113
|
+
/** percent, 0 = default */
|
|
114
|
+
letterSpacing: number;
|
|
115
|
+
/** percent, 0 = default */
|
|
116
|
+
wordSpacing: number;
|
|
117
|
+
/** percent change of the side margins, 0 = default */
|
|
118
|
+
margins: number;
|
|
119
|
+
columns: ColumnsSetting;
|
|
120
|
+
justify: boolean;
|
|
121
|
+
pageTurn: PageTurn;
|
|
122
|
+
/** paper sound when a page is turned (Curl) */
|
|
123
|
+
pageSound: boolean;
|
|
124
|
+
/** 0.3 .. 1, dims the page like the Books brightness slider */
|
|
125
|
+
brightness: number;
|
|
126
|
+
/** 'auto' follows the book's page-progression-direction / language */
|
|
127
|
+
direction: 'auto' | 'ltr' | 'rtl';
|
|
128
|
+
}
|
|
129
|
+
export type EpubSource = {
|
|
130
|
+
uri: string;
|
|
131
|
+
} | {
|
|
132
|
+
base64: string;
|
|
133
|
+
};
|
package/lib/types.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ChromeColors } from '../themes';
|
|
3
|
+
import type { ReaderSettings } from '../types';
|
|
4
|
+
interface Props {
|
|
5
|
+
visible: boolean;
|
|
6
|
+
colors: ChromeColors;
|
|
7
|
+
settings: ReaderSettings;
|
|
8
|
+
systemDark: boolean;
|
|
9
|
+
onChange: (patch: Partial<ReaderSettings>) => void;
|
|
10
|
+
onCustomize: () => void;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
}
|
|
13
|
+
/** "Themes & Settings" sheet. */
|
|
14
|
+
export declare function AppearanceSheet({ visible, colors, settings: s, systemDark, onChange, onCustomize, onClose }: Props): React.JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AppearanceSheet = AppearanceSheet;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
const themes_1 = require("../themes");
|
|
40
|
+
const Icons_1 = require("./Icons");
|
|
41
|
+
const Sheet_1 = require("./Sheet");
|
|
42
|
+
const Slider_1 = require("./Slider");
|
|
43
|
+
const SIZE = { min: 12, max: 40, step: 2 };
|
|
44
|
+
const TURNS = [
|
|
45
|
+
{ id: 'slide', label: 'Slide', icon: 'pageSlide' },
|
|
46
|
+
{ id: 'curl', label: 'Curl', icon: 'pageCurl' },
|
|
47
|
+
{ id: 'fade', label: 'Fast Fade', icon: 'pageFade' },
|
|
48
|
+
{ id: 'scroll', label: 'Scroll', icon: 'pageScroll' }
|
|
49
|
+
];
|
|
50
|
+
const APPEARANCES = [
|
|
51
|
+
{ id: 'light', label: 'Light', icon: 'sun', key: 'light' },
|
|
52
|
+
{ id: 'dark', label: 'Dark', icon: 'moon', key: 'dark' },
|
|
53
|
+
{ id: 'device', label: 'Match Device', icon: 'contrast', key: 'device' },
|
|
54
|
+
{ id: 'device', label: 'Match Surroundings', icon: 'sunLow', key: 'surroundings' }
|
|
55
|
+
];
|
|
56
|
+
/** Icon of the current appearance choice: the button always shows the state it is in. */
|
|
57
|
+
const appearanceIcon = (a) => (a === 'light' ? 'sun' : a === 'dark' ? 'moon' : 'contrast');
|
|
58
|
+
/** "Themes & Settings" sheet. */
|
|
59
|
+
function AppearanceSheet({ visible, colors, settings: s, systemDark, onChange, onCustomize, onClose }) {
|
|
60
|
+
const [menu, setMenu] = (0, react_1.useState)(null);
|
|
61
|
+
const dark = (0, themes_1.isDark)(s, systemDark);
|
|
62
|
+
const turn = TURNS.find((t) => t.id === s.pageTurn) || TURNS[0];
|
|
63
|
+
const menuView = menu && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
64
|
+
react_1.default.createElement(react_native_1.Pressable, { style: react_native_1.StyleSheet.absoluteFill, onPress: () => setMenu(null) }),
|
|
65
|
+
react_1.default.createElement(react_native_1.View, { style: [styles.menu, { backgroundColor: colors.dark ? '#4a4742' : '#ffffff', borderColor: colors.glassBorder }] }, menu === 'turn'
|
|
66
|
+
? TURNS.map((t) => (react_1.default.createElement(react_native_1.Pressable, { key: t.id, style: styles.menuItem, onPress: () => {
|
|
67
|
+
onChange({ pageTurn: t.id });
|
|
68
|
+
setMenu(null);
|
|
69
|
+
} },
|
|
70
|
+
react_1.default.createElement(react_native_1.View, { style: styles.check }, s.pageTurn === t.id && react_1.default.createElement(Icons_1.Icon, { name: "check", color: colors.text, size: 20 })),
|
|
71
|
+
react_1.default.createElement(Icons_1.Icon, { name: t.icon, color: colors.text, size: 24 }),
|
|
72
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.menuText, { color: colors.text }] }, t.label))))
|
|
73
|
+
: APPEARANCES.map((a) => (react_1.default.createElement(react_native_1.Pressable, { key: a.key, style: styles.menuItem, onPress: () => {
|
|
74
|
+
onChange({ appearance: a.id });
|
|
75
|
+
setMenu(null);
|
|
76
|
+
} },
|
|
77
|
+
react_1.default.createElement(react_native_1.View, { style: styles.check }, s.appearance === a.id && a.key !== 'surroundings' && react_1.default.createElement(Icons_1.Icon, { name: "check", color: colors.text, size: 20 })),
|
|
78
|
+
react_1.default.createElement(Icons_1.Icon, { name: a.icon, color: colors.text, size: 24 }),
|
|
79
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.menuText, { color: colors.text }] }, a.label)))))));
|
|
80
|
+
return (react_1.default.createElement(Sheet_1.Sheet, { visible: visible, colors: colors, onClose: onClose, title: "Themes & Settings", height: "auto", overlay: menuView || undefined },
|
|
81
|
+
react_1.default.createElement(react_native_1.View, { style: styles.body },
|
|
82
|
+
react_1.default.createElement(react_native_1.View, { style: styles.row },
|
|
83
|
+
react_1.default.createElement(react_native_1.View, { style: [styles.pill, { flex: 1, backgroundColor: colors.card }] },
|
|
84
|
+
react_1.default.createElement(react_native_1.Pressable, { style: styles.half, onPress: () => onChange({ fontSize: Math.max(SIZE.min, s.fontSize - SIZE.step) }), accessibilityLabel: "Smaller text" },
|
|
85
|
+
react_1.default.createElement(react_native_1.Text, { style: { color: colors.text, fontSize: 15, fontWeight: '500' } }, "A")),
|
|
86
|
+
react_1.default.createElement(react_native_1.View, { style: [styles.divider, { backgroundColor: colors.separator }] }),
|
|
87
|
+
react_1.default.createElement(react_native_1.Pressable, { style: styles.half, onPress: () => onChange({ fontSize: Math.min(SIZE.max, s.fontSize + SIZE.step) }), accessibilityLabel: "Larger text" },
|
|
88
|
+
react_1.default.createElement(react_native_1.Text, { style: { color: colors.text, fontSize: 26, fontWeight: '500' } }, "A"))),
|
|
89
|
+
react_1.default.createElement(react_native_1.View, { style: [styles.pill, { width: 150, backgroundColor: colors.card }] },
|
|
90
|
+
react_1.default.createElement(react_native_1.Pressable, { style: [styles.half, menu === 'turn' && { backgroundColor: colors.separator }], onPress: () => setMenu(menu === 'turn' ? null : 'turn'), accessibilityLabel: `Page turn style: ${turn.label}` },
|
|
91
|
+
react_1.default.createElement(Icons_1.Icon, { name: turn.icon, color: colors.text, size: 26 })),
|
|
92
|
+
react_1.default.createElement(react_native_1.Pressable, { style: [styles.half, menu === 'appearance' && { backgroundColor: colors.separator }], onPress: () => setMenu(menu === 'appearance' ? null : 'appearance'), accessibilityLabel: "Appearance" },
|
|
93
|
+
react_1.default.createElement(Icons_1.Icon, { name: appearanceIcon(s.appearance), color: colors.text, size: 26 })))),
|
|
94
|
+
react_1.default.createElement(react_native_1.View, { style: styles.brightness },
|
|
95
|
+
react_1.default.createElement(Icons_1.Icon, { name: "sunLow", color: colors.subtext, size: 20 }),
|
|
96
|
+
react_1.default.createElement(react_native_1.View, { style: { flex: 1, marginHorizontal: 10 } },
|
|
97
|
+
react_1.default.createElement(Slider_1.Slider, { value: s.brightness, min: 0.3, max: 1, step: 0.01, onChange: (v) => onChange({ brightness: v }), color: colors.text, track: colors.separator, thumbColor: "#ffffff" })),
|
|
98
|
+
react_1.default.createElement(Icons_1.Icon, { name: "sun", color: colors.subtext, size: 26 })),
|
|
99
|
+
react_1.default.createElement(react_native_1.View, { style: styles.grid }, themes_1.THEMES.map((t) => {
|
|
100
|
+
const c = dark ? t.dark : t.light;
|
|
101
|
+
const on = t.id === s.themeId;
|
|
102
|
+
return (react_1.default.createElement(react_native_1.Pressable, { key: t.id, onPress: () => onChange({ themeId: t.id }), style: [styles.tile, { backgroundColor: c.bg, borderColor: on ? colors.text : colors.separator, borderWidth: on ? 3 : react_native_1.StyleSheet.hairlineWidth }], accessibilityRole: "radio", accessibilityState: { selected: on } },
|
|
103
|
+
react_1.default.createElement(react_native_1.Text, { style: { color: c.fg, fontSize: 30, fontFamily: themes_1.serifFont, fontWeight: t.bold ? '800' : '400' } }, "Aa"),
|
|
104
|
+
react_1.default.createElement(react_native_1.Text, { style: { color: c.fg, fontSize: 15, fontFamily: themes_1.serifFont, fontWeight: t.bold ? '700' : '400' } }, t.name)));
|
|
105
|
+
})),
|
|
106
|
+
react_1.default.createElement(react_native_1.Pressable, { onPress: onCustomize, style: [styles.customize, { backgroundColor: colors.card }], accessibilityRole: "button" },
|
|
107
|
+
react_1.default.createElement(Icons_1.Icon, { name: "sliders", color: colors.text, size: 22 }),
|
|
108
|
+
react_1.default.createElement(react_native_1.Text, { style: { color: colors.text, fontSize: 18, fontWeight: '600', marginStart: 10 } }, "Customize")))));
|
|
109
|
+
}
|
|
110
|
+
const styles = react_native_1.StyleSheet.create({
|
|
111
|
+
body: { paddingHorizontal: 16, paddingBottom: 28 },
|
|
112
|
+
row: { flexDirection: 'row', gap: 10 },
|
|
113
|
+
pill: { height: 58, borderRadius: 29, flexDirection: 'row', alignItems: 'center', overflow: 'hidden' },
|
|
114
|
+
half: { flex: 1, height: '100%', alignItems: 'center', justifyContent: 'center' },
|
|
115
|
+
divider: { width: react_native_1.StyleSheet.hairlineWidth, height: 24 },
|
|
116
|
+
brightness: { flexDirection: 'row', alignItems: 'center', marginTop: 14, marginBottom: 8, paddingHorizontal: 4 },
|
|
117
|
+
grid: { flexDirection: 'row', flexWrap: 'wrap', gap: 10, marginTop: 6 },
|
|
118
|
+
tile: { width: '31.6%', height: 92, borderRadius: 22, alignItems: 'center', justifyContent: 'center' },
|
|
119
|
+
customize: { marginTop: 12, height: 58, borderRadius: 29, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' },
|
|
120
|
+
menu: {
|
|
121
|
+
position: 'absolute', top: 80, right: 16, width: 250, borderRadius: 24, paddingVertical: 8, borderWidth: react_native_1.StyleSheet.hairlineWidth,
|
|
122
|
+
shadowColor: '#000', shadowOpacity: 0.3, shadowRadius: 16, shadowOffset: { width: 0, height: 6 }, elevation: 12
|
|
123
|
+
},
|
|
124
|
+
menuItem: { flexDirection: 'row', alignItems: 'center', paddingVertical: 12, paddingHorizontal: 14 },
|
|
125
|
+
check: { width: 26, alignItems: 'center' },
|
|
126
|
+
menuText: { fontSize: 18, marginStart: 14, flexShrink: 1 }
|
|
127
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ChromeColors } from '../themes';
|
|
3
|
+
import type { Bookmark, Highlight, TocItem } from '../types';
|
|
4
|
+
export type ContentsTab = 'contents' | 'bookmarks' | 'notes';
|
|
5
|
+
interface Props {
|
|
6
|
+
visible: boolean;
|
|
7
|
+
colors: ChromeColors;
|
|
8
|
+
title: string;
|
|
9
|
+
/** e.g. "Page 100 of 219" */
|
|
10
|
+
subtitle: string;
|
|
11
|
+
toc: TocItem[];
|
|
12
|
+
currentSpine: number;
|
|
13
|
+
/** estimated page number in the whole book for a spine item + character offset */
|
|
14
|
+
pageOf: (spine: number, offset: number) => number;
|
|
15
|
+
bookmarks: Bookmark[];
|
|
16
|
+
highlights: Highlight[];
|
|
17
|
+
initialTab?: ContentsTab;
|
|
18
|
+
onClose: () => void;
|
|
19
|
+
onGoToc: (item: TocItem) => void;
|
|
20
|
+
onGoBookmark: (b: Bookmark) => void;
|
|
21
|
+
onGoHighlight: (h: Highlight) => void;
|
|
22
|
+
onDeleteBookmark: (id: string) => void;
|
|
23
|
+
onDeleteHighlight: (id: string) => void;
|
|
24
|
+
}
|
|
25
|
+
/** Contents sheet: Chapters | Bookmarks | Highlights, each row with its page number. */
|
|
26
|
+
export declare function ContentsSheet(p: Props): React.JSX.Element;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ContentsSheet = ContentsSheet;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
const themes_1 = require("../themes");
|
|
40
|
+
const Sheet_1 = require("./Sheet");
|
|
41
|
+
const TABS = [
|
|
42
|
+
{ id: 'contents', label: 'Chapters' },
|
|
43
|
+
{ id: 'bookmarks', label: 'Bookmarks' },
|
|
44
|
+
{ id: 'notes', label: 'Highlights' }
|
|
45
|
+
];
|
|
46
|
+
/** Contents sheet: Chapters | Bookmarks | Highlights, each row with its page number. */
|
|
47
|
+
function ContentsSheet(p) {
|
|
48
|
+
const { colors } = p;
|
|
49
|
+
const [tab, setTab] = (0, react_1.useState)(p.initialTab || 'contents');
|
|
50
|
+
const confirmDelete = (what, fn) => react_native_1.Alert.alert(`Delete ${what}?`, undefined, [
|
|
51
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
52
|
+
{ text: 'Delete', style: 'destructive', onPress: fn }
|
|
53
|
+
]);
|
|
54
|
+
const empty = (text) => react_1.default.createElement(react_native_1.Text, { style: [styles.empty, { color: colors.subtext }] }, text);
|
|
55
|
+
const rowBorder = { borderColor: colors.separator };
|
|
56
|
+
return (react_1.default.createElement(Sheet_1.Sheet, { visible: p.visible, colors: colors, onClose: p.onClose, closeIcon: "check", title: p.title, subtitle: p.subtitle, height: "90%" },
|
|
57
|
+
react_1.default.createElement(react_native_1.View, { style: [styles.segment, { backgroundColor: colors.card }] }, TABS.map((t) => (react_1.default.createElement(react_native_1.Pressable, { key: t.id, onPress: () => setTab(t.id), style: [styles.segItem, tab === t.id && { backgroundColor: colors.dark ? 'rgba(255,255,255,0.22)' : '#ffffff' }], accessibilityRole: "tab", accessibilityState: { selected: tab === t.id } },
|
|
58
|
+
react_1.default.createElement(react_native_1.Text, { style: { color: colors.text, fontWeight: tab === t.id ? '600' : '400', fontSize: 16 } }, t.label))))),
|
|
59
|
+
tab === 'contents' && (react_1.default.createElement(react_native_1.FlatList, { data: p.toc, keyExtractor: (_, i) => String(i), ListEmptyComponent: empty('No table of contents'), renderItem: ({ item }) => {
|
|
60
|
+
const active = item.spine === p.currentSpine && !item.fragment;
|
|
61
|
+
return (react_1.default.createElement(react_native_1.Pressable, { onPress: () => p.onGoToc(item), style: [styles.row, rowBorder, { paddingStart: 20 + item.level * 18 }] },
|
|
62
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.rowText, { color: active ? colors.accent : colors.text, flex: 1 }], numberOfLines: 2 }, item.label),
|
|
63
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.page, { color: colors.subtext }] }, p.pageOf(item.spine, 0))));
|
|
64
|
+
} })),
|
|
65
|
+
tab === 'bookmarks' && (react_1.default.createElement(react_native_1.FlatList, { data: p.bookmarks.slice().sort((a, b) => a.progress - b.progress), keyExtractor: (b) => b.id, ListEmptyComponent: empty('No bookmarks yet. Tap the bookmark button while reading.'), renderItem: ({ item }) => (react_1.default.createElement(react_native_1.Pressable, { onPress: () => p.onGoBookmark(item), onLongPress: () => confirmDelete('bookmark', () => p.onDeleteBookmark(item.id)), style: [styles.row, rowBorder] },
|
|
66
|
+
react_1.default.createElement(react_native_1.View, { style: { flex: 1 } },
|
|
67
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.meta, { color: colors.accent }] }, item.chapter),
|
|
68
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.rowText, { color: colors.text }], numberOfLines: 2 }, item.excerpt)),
|
|
69
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.page, { color: colors.subtext }] }, p.pageOf(item.spine, item.offset)))) })),
|
|
70
|
+
tab === 'notes' && (react_1.default.createElement(react_native_1.FlatList, { data: p.highlights.slice().sort((a, b) => a.spine - b.spine || a.start - b.start), keyExtractor: (h) => h.id, ListEmptyComponent: empty('No highlights or notes yet. Select text to add one.'), renderItem: ({ item }) => (react_1.default.createElement(react_native_1.Pressable, { onPress: () => p.onGoHighlight(item), onLongPress: () => confirmDelete('highlight', () => p.onDeleteHighlight(item.id)), style: [styles.row, rowBorder] },
|
|
71
|
+
react_1.default.createElement(react_native_1.View, { style: [styles.bar, { backgroundColor: themes_1.HIGHLIGHT_COLORS[item.color] }] }),
|
|
72
|
+
react_1.default.createElement(react_native_1.View, { style: { flex: 1 } },
|
|
73
|
+
!!item.note && react_1.default.createElement(react_native_1.Text, { style: [styles.rowText, { color: colors.text, fontWeight: '600' }] }, item.note),
|
|
74
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.rowText, { color: item.note ? colors.subtext : colors.text }], numberOfLines: 3 }, item.text),
|
|
75
|
+
!!item.chapter && react_1.default.createElement(react_native_1.Text, { style: [styles.meta, { color: colors.subtext }] }, item.chapter)),
|
|
76
|
+
react_1.default.createElement(react_native_1.Text, { style: [styles.page, { color: colors.subtext }] }, p.pageOf(item.spine, item.start)))) }))));
|
|
77
|
+
}
|
|
78
|
+
const styles = react_native_1.StyleSheet.create({
|
|
79
|
+
segment: { flexDirection: 'row', marginHorizontal: 16, marginTop: 34, marginBottom: 6, borderRadius: 18, padding: 3 },
|
|
80
|
+
segItem: { flex: 1, alignItems: 'center', paddingVertical: 8, borderRadius: 15 },
|
|
81
|
+
row: { flexDirection: 'row', alignItems: 'center', paddingVertical: 17, paddingHorizontal: 20, marginHorizontal: 0, borderBottomWidth: react_native_1.StyleSheet.hairlineWidth },
|
|
82
|
+
rowText: { fontSize: 18, lineHeight: 24 },
|
|
83
|
+
page: { fontSize: 18, marginStart: 12, minWidth: 34, textAlign: 'right' },
|
|
84
|
+
meta: { fontSize: 13, marginBottom: 2, fontWeight: '600' },
|
|
85
|
+
bar: { width: 3, alignSelf: 'stretch', borderRadius: 2, marginEnd: 12 },
|
|
86
|
+
empty: { textAlign: 'center', padding: 32, fontSize: 16 }
|
|
87
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ChromeColors } from '../themes';
|
|
3
|
+
import type { ReaderSettings, ThemeColors } from '../types';
|
|
4
|
+
interface Props {
|
|
5
|
+
visible: boolean;
|
|
6
|
+
colors: ChromeColors;
|
|
7
|
+
page: ThemeColors;
|
|
8
|
+
settings: ReaderSettings;
|
|
9
|
+
sample: string;
|
|
10
|
+
onChange: (patch: Partial<ReaderSettings>) => void;
|
|
11
|
+
onReset: () => void;
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
}
|
|
14
|
+
/** "Customize Theme" sheet: font, bold, spacing, margins, columns, justify, sound. */
|
|
15
|
+
export declare function CustomizeSheet({ visible, colors, page, settings: s, sample, onChange, onReset, onClose }: Props): React.JSX.Element;
|
|
16
|
+
export {};
|