@edrlab/thorium-web 1.3.1 → 1.4.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.
- package/dist/{ThPreferencesAdapter-D0rzsGRl.d.mts → ThPreferencesAdapter-_5AePKHa.d.mts} +26 -7
- package/dist/{ThSettingsWrapper-BXuRgdqp.d.mts → ThSettingsWrapper-B_9klYXH.d.mts} +1 -1
- package/dist/{actions-BLAr0oaM.d.mts → actions-CuRRM3rp.d.mts} +5 -2
- package/dist/{actionsReducer-XWTGGNUd.d.mts → actionsReducer-VFR42qgL.d.mts} +1 -1
- package/dist/{chunk-6EHFW43Y.mjs → chunk-2NCN2AG2.mjs} +5 -4
- package/dist/chunk-2NCN2AG2.mjs.map +1 -0
- package/dist/{chunk-L4XGZAZ5.mjs → chunk-2YRT7RNW.mjs} +20 -3
- package/dist/chunk-2YRT7RNW.mjs.map +1 -0
- package/dist/{chunk-5LUMM7FW.mjs → chunk-44PEO3DS.mjs} +2 -2
- package/dist/{chunk-5LUMM7FW.mjs.map → chunk-44PEO3DS.mjs.map} +1 -1
- package/dist/chunk-A575ZW4A.mjs +10 -0
- package/dist/chunk-A575ZW4A.mjs.map +1 -0
- package/dist/chunk-AE6P4KJB.mjs +13 -0
- package/dist/chunk-AE6P4KJB.mjs.map +1 -0
- package/dist/chunk-AQSJDL63.mjs +193 -0
- package/dist/chunk-AQSJDL63.mjs.map +1 -0
- package/dist/{chunk-NKO3K3QS.mjs → chunk-DQDOOTCE.mjs} +5 -5
- package/dist/chunk-DQDOOTCE.mjs.map +1 -0
- package/dist/{chunk-SAUOY37Q.mjs → chunk-E2JEGVVE.mjs} +15 -15
- package/dist/chunk-E2JEGVVE.mjs.map +1 -0
- package/dist/{chunk-DETZMFZ7.mjs → chunk-ETLIGONP.mjs} +39 -33
- package/dist/chunk-ETLIGONP.mjs.map +1 -0
- package/dist/chunk-GNROODJB.mjs +9 -0
- package/dist/chunk-GNROODJB.mjs.map +1 -0
- package/dist/{chunk-6BUN7DEA.mjs → chunk-KGSFTRCH.mjs} +69 -84
- package/dist/chunk-KGSFTRCH.mjs.map +1 -0
- package/dist/{chunk-LP3JFZ4A.mjs → chunk-MSHUPSBI.mjs} +718 -466
- package/dist/chunk-MSHUPSBI.mjs.map +1 -0
- package/dist/chunk-OD75GC5N.mjs +3953 -0
- package/dist/chunk-OD75GC5N.mjs.map +1 -0
- package/dist/{chunk-I4BKU5NN.mjs → chunk-RBEPH5E5.mjs} +100 -30
- package/dist/chunk-RBEPH5E5.mjs.map +1 -0
- package/dist/{chunk-DMZFSOHK.mjs → chunk-SI4FBFHM.mjs} +135 -46
- package/dist/chunk-SI4FBFHM.mjs.map +1 -0
- package/dist/{chunk-A3FZBEUL.mjs → chunk-SZAVAQ6S.mjs} +30 -6
- package/dist/chunk-SZAVAQ6S.mjs.map +1 -0
- package/dist/{chunk-ITDBOMY5.mjs → chunk-VENFFPK2.mjs} +3 -3
- package/dist/{chunk-ITDBOMY5.mjs.map → chunk-VENFFPK2.mjs.map} +1 -1
- package/dist/{chunk-2ORXUOH3.mjs → chunk-WF2UOYO7.mjs} +4 -4
- package/dist/{chunk-2ORXUOH3.mjs.map → chunk-WF2UOYO7.mjs.map} +1 -1
- package/dist/{chunk-EZG6SBSO.mjs → chunk-YEVLT3AV.mjs} +104 -29
- package/dist/chunk-YEVLT3AV.mjs.map +1 -0
- package/dist/components/Audio/index.css +2 -1
- package/dist/components/Audio/index.css.map +1 -1
- package/dist/components/Audio/index.d.mts +16 -15
- package/dist/components/Audio/index.mjs +16 -16
- package/dist/components/Epub/index.css +5 -4
- package/dist/components/Epub/index.css.map +1 -1
- package/dist/components/Epub/index.d.mts +13 -13
- package/dist/components/Epub/index.mjs +17 -17
- package/dist/components/Misc/index.mjs +5 -5
- package/dist/components/Reader/index.css +5 -4
- package/dist/components/Reader/index.css.map +1 -1
- package/dist/components/Reader/index.d.mts +11 -11
- package/dist/components/Reader/index.mjs +34 -30
- package/dist/components/Reader/index.mjs.map +1 -1
- package/dist/components/WebPub/index.css +5 -4
- package/dist/components/WebPub/index.css.map +1 -1
- package/dist/components/WebPub/index.d.mts +13 -13
- package/dist/components/WebPub/index.mjs +17 -17
- package/dist/core/Components/index.d.mts +12 -22
- package/dist/core/Components/index.mjs +2 -2
- package/dist/core/Helpers/index.d.mts +1 -1
- package/dist/core/Helpers/index.mjs +3 -4
- package/dist/core/Hooks/index.d.mts +12 -8
- package/dist/core/Hooks/index.mjs +1 -1
- package/dist/i18n/index.mjs +4 -7
- package/dist/lib/index.d.mts +56 -20
- package/dist/lib/index.mjs +3 -2
- package/dist/locales/da/thorium-shared.json +3 -0
- package/dist/locales/da/thorium-web.json +37 -2
- package/dist/locales/en/thorium-shared.json +24 -2
- package/dist/locales/en/thorium-web.json +2 -2
- package/dist/locales/es/thorium-shared.json +364 -0
- package/dist/locales/es/thorium-web.json +130 -0
- package/dist/locales/et/thorium-shared.json +121 -9
- package/dist/locales/et/thorium-web.json +32 -1
- package/dist/locales/fi/thorium-shared.json +42 -4
- package/dist/locales/fi/thorium-web.json +36 -2
- package/dist/locales/fr/thorium-shared.json +108 -1
- package/dist/locales/fr/thorium-web.json +121 -86
- package/dist/locales/it/thorium-shared.json +108 -1
- package/dist/locales/it/thorium-web.json +15 -2
- package/dist/locales/lt/thorium-web.json +36 -2
- package/dist/locales/pl/thorium-web.json +1 -1
- package/dist/locales/pt-BR/thorium-shared.json +6 -0
- package/dist/locales/pt-BR/thorium-web.json +88 -88
- package/dist/locales/pt-PT/thorium-shared.json +91 -0
- package/dist/locales/pt-PT/thorium-web.json +15 -3
- package/dist/locales/sv/thorium-shared.json +108 -2
- package/dist/locales/sv/thorium-web.json +15 -3
- package/dist/locales/tr/thorium-shared.json +42 -0
- package/dist/next-lib/index.mjs +1 -1
- package/dist/next-lib/index.mjs.map +1 -1
- package/dist/preferences/index.d.mts +59 -13
- package/dist/preferences/index.mjs +7 -7
- package/dist/{settingsReducer-Bu1zeveu.d.mts → settingsReducer-VqBhLq50.d.mts} +14 -2
- package/dist/{ui-nBv8gfr0.d.mts → ui-DnZZhozX.d.mts} +1 -1
- package/dist/{useAudioNavigator-C5aW4-eT.d.mts → useAudioNavigator-CWXyNWq1.d.mts} +3 -1
- package/dist/{useContrast-2t429O9O.d.mts → useContrast-Cbso7N1l.d.mts} +5 -1
- package/dist/{usePreferences-VaBf46eP.d.mts → usePreferences-9ZvbcbLW.d.mts} +6 -8
- package/dist/{useReaderTransitions-IBGdE7qi.d.mts → useReaderTransitions-0hKGCvMm.d.mts} +64 -12
- package/package.json +10 -9
- package/dist/chunk-6BUN7DEA.mjs.map +0 -1
- package/dist/chunk-6EHFW43Y.mjs.map +0 -1
- package/dist/chunk-7CGMWOZN.mjs +0 -20
- package/dist/chunk-7CGMWOZN.mjs.map +0 -1
- package/dist/chunk-A3FZBEUL.mjs.map +0 -1
- package/dist/chunk-B3WDMWCT.mjs +0 -9
- package/dist/chunk-B3WDMWCT.mjs.map +0 -1
- package/dist/chunk-DETZMFZ7.mjs.map +0 -1
- package/dist/chunk-DMZFSOHK.mjs.map +0 -1
- package/dist/chunk-DTPO3J2C.mjs +0 -1732
- package/dist/chunk-DTPO3J2C.mjs.map +0 -1
- package/dist/chunk-EZG6SBSO.mjs.map +0 -1
- package/dist/chunk-GPWW5OML.mjs +0 -1955
- package/dist/chunk-GPWW5OML.mjs.map +0 -1
- package/dist/chunk-I4BKU5NN.mjs.map +0 -1
- package/dist/chunk-L4XGZAZ5.mjs.map +0 -1
- package/dist/chunk-LP3JFZ4A.mjs.map +0 -1
- package/dist/chunk-MLEYTQGK.mjs +0 -60
- package/dist/chunk-MLEYTQGK.mjs.map +0 -1
- package/dist/chunk-NKO3K3QS.mjs.map +0 -1
- package/dist/chunk-SAUOY37Q.mjs.map +0 -1
|
@@ -0,0 +1,3953 @@
|
|
|
1
|
+
import { propsToCSSVars } from './chunk-2YRT7RNW.mjs';
|
|
2
|
+
import { useBreakpoints, useForcedColors, useMonochrome, useReducedMotion, useReducedTransparency } from './chunk-GFSLVQIG.mjs';
|
|
3
|
+
import { ThGlobalPreferencesContext } from './chunk-AE6P4KJB.mjs';
|
|
4
|
+
import { useAppSelector } from './chunk-A575ZW4A.mjs';
|
|
5
|
+
import { useColorScheme, useContrast } from './chunk-NQ2ZSGCX.mjs';
|
|
6
|
+
import { isSupportedLocale, supportedLocales } from './chunk-GNROODJB.mjs';
|
|
7
|
+
import { createContext, useMemo, useState, useCallback, useEffect, useContext, useRef } from 'react';
|
|
8
|
+
import { I18nProvider, useLocale } from 'react-aria';
|
|
9
|
+
import { jsx } from 'react/jsx-runtime';
|
|
10
|
+
import fontStacks from '@readium/css/css/vars/fontStacks.json';
|
|
11
|
+
import ReadiumCSSColors from '@readium/css/css/vars/colors.json';
|
|
12
|
+
|
|
13
|
+
// src/preferences/globalPreferences.ts
|
|
14
|
+
var createGlobalPreferences = (params) => {
|
|
15
|
+
if (params.locale) {
|
|
16
|
+
const languageCode = params.locale.split("-")[0];
|
|
17
|
+
if (!isSupportedLocale(languageCode)) {
|
|
18
|
+
console.warn(`Locale "${params.locale}" is not supported. Supported locales: ${supportedLocales.join(", ")}. Falling back to browser/OS language settings.`);
|
|
19
|
+
return { ...params, locale: void 0 };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return params;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/preferences/adapters/ThGlobalMemoryPreferencesAdapter.ts
|
|
26
|
+
var ThGlobalMemoryPreferencesAdapter = class {
|
|
27
|
+
preferences;
|
|
28
|
+
listeners = /* @__PURE__ */ new Set();
|
|
29
|
+
constructor(initialPreferences = {}) {
|
|
30
|
+
this.preferences = { ...initialPreferences };
|
|
31
|
+
}
|
|
32
|
+
getPreferences() {
|
|
33
|
+
return { ...this.preferences };
|
|
34
|
+
}
|
|
35
|
+
setPreferences(prefs) {
|
|
36
|
+
this.preferences = { ...prefs };
|
|
37
|
+
this.listeners.forEach((cb) => cb({ ...this.preferences }));
|
|
38
|
+
}
|
|
39
|
+
subscribe(callback) {
|
|
40
|
+
this.listeners.add(callback);
|
|
41
|
+
callback(this.getPreferences());
|
|
42
|
+
}
|
|
43
|
+
unsubscribe(callback) {
|
|
44
|
+
this.listeners.delete(callback);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var ThDirectionSetter = ({ children }) => {
|
|
48
|
+
const { direction } = useLocale();
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
document.documentElement.dir = direction;
|
|
51
|
+
}, [direction]);
|
|
52
|
+
return children;
|
|
53
|
+
};
|
|
54
|
+
function ThGlobalPreferencesProvider({ adapter, initialPreferences, children }) {
|
|
55
|
+
const effectiveAdapter = useMemo(
|
|
56
|
+
() => adapter || new ThGlobalMemoryPreferencesAdapter(initialPreferences),
|
|
57
|
+
[adapter, initialPreferences]
|
|
58
|
+
);
|
|
59
|
+
const [preferences, setPreferences] = useState(
|
|
60
|
+
() => createGlobalPreferences(initialPreferences ?? {})
|
|
61
|
+
);
|
|
62
|
+
const handlePreferenceChange = useCallback((newPrefs) => {
|
|
63
|
+
setPreferences((prev) => {
|
|
64
|
+
const validated = createGlobalPreferences(newPrefs);
|
|
65
|
+
return JSON.stringify(prev) === JSON.stringify(validated) ? prev : validated;
|
|
66
|
+
});
|
|
67
|
+
}, []);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
effectiveAdapter.subscribe(handlePreferenceChange);
|
|
70
|
+
return () => effectiveAdapter.unsubscribe(handlePreferenceChange);
|
|
71
|
+
}, [effectiveAdapter, handlePreferenceChange]);
|
|
72
|
+
const contextValue = useMemo(() => ({
|
|
73
|
+
preferences,
|
|
74
|
+
updatePreferences: (newPrefs) => {
|
|
75
|
+
effectiveAdapter.setPreferences(newPrefs);
|
|
76
|
+
}
|
|
77
|
+
}), [preferences, effectiveAdapter]);
|
|
78
|
+
return /* @__PURE__ */ jsx(ThGlobalPreferencesContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(I18nProvider, { locale: preferences.locale, children: /* @__PURE__ */ jsx(ThDirectionSetter, { children }) }) });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/preferences/models/actions.ts
|
|
82
|
+
var ThActionsKeys = /* @__PURE__ */ ((ThActionsKeys3) => {
|
|
83
|
+
ThActionsKeys3["fullscreen"] = "fullscreen";
|
|
84
|
+
ThActionsKeys3["jumpToPosition"] = "jumpToPosition";
|
|
85
|
+
ThActionsKeys3["settings"] = "settings";
|
|
86
|
+
ThActionsKeys3["toc"] = "toc";
|
|
87
|
+
return ThActionsKeys3;
|
|
88
|
+
})(ThActionsKeys || {});
|
|
89
|
+
var ThDockingKeys = /* @__PURE__ */ ((ThDockingKeys3) => {
|
|
90
|
+
ThDockingKeys3["start"] = "dockingStart";
|
|
91
|
+
ThDockingKeys3["end"] = "dockingEnd";
|
|
92
|
+
ThDockingKeys3["transient"] = "dockingTransient";
|
|
93
|
+
return ThDockingKeys3;
|
|
94
|
+
})(ThDockingKeys || {});
|
|
95
|
+
var ThDockingTypes = /* @__PURE__ */ ((ThDockingTypes2) => {
|
|
96
|
+
ThDockingTypes2["none"] = "none";
|
|
97
|
+
ThDockingTypes2["both"] = "both";
|
|
98
|
+
ThDockingTypes2["start"] = "start";
|
|
99
|
+
ThDockingTypes2["end"] = "end";
|
|
100
|
+
return ThDockingTypes2;
|
|
101
|
+
})(ThDockingTypes || {});
|
|
102
|
+
var ThSheetTypes = /* @__PURE__ */ ((ThSheetTypes3) => {
|
|
103
|
+
ThSheetTypes3["popover"] = "popover";
|
|
104
|
+
ThSheetTypes3["compactPopover"] = "compactPopover";
|
|
105
|
+
ThSheetTypes3["modal"] = "modal";
|
|
106
|
+
ThSheetTypes3["fullscreen"] = "fullscreen";
|
|
107
|
+
ThSheetTypes3["dockedStart"] = "docked start";
|
|
108
|
+
ThSheetTypes3["dockedEnd"] = "docked end";
|
|
109
|
+
ThSheetTypes3["bottomSheet"] = "bottomSheet";
|
|
110
|
+
return ThSheetTypes3;
|
|
111
|
+
})(ThSheetTypes || {});
|
|
112
|
+
var ThSheetHeaderVariant = /* @__PURE__ */ ((ThSheetHeaderVariant2) => {
|
|
113
|
+
ThSheetHeaderVariant2["close"] = "close";
|
|
114
|
+
ThSheetHeaderVariant2["previous"] = "previous";
|
|
115
|
+
return ThSheetHeaderVariant2;
|
|
116
|
+
})(ThSheetHeaderVariant || {});
|
|
117
|
+
var defaultActionKeysObject = {
|
|
118
|
+
visibility: "partially" /* partially */,
|
|
119
|
+
shortcut: null
|
|
120
|
+
};
|
|
121
|
+
var defaultSettingsAction = {
|
|
122
|
+
visibility: "partially" /* partially */,
|
|
123
|
+
shortcut: null,
|
|
124
|
+
// `${ UnstableShortcutMetaKeywords.shift }+${ ShortcutMetaKeywords.alt }+P`,
|
|
125
|
+
sheet: {
|
|
126
|
+
defaultSheet: "popover" /* popover */,
|
|
127
|
+
breakpoints: {
|
|
128
|
+
["compact" /* compact */]: "bottomSheet" /* bottomSheet */
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
docked: {
|
|
132
|
+
dockable: "none" /* none */,
|
|
133
|
+
width: 340
|
|
134
|
+
},
|
|
135
|
+
snapped: {
|
|
136
|
+
scrim: true,
|
|
137
|
+
peekHeight: 50,
|
|
138
|
+
minHeight: 30,
|
|
139
|
+
maxHeight: 100
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
var defaultFullscreenAction = {
|
|
143
|
+
visibility: "partially" /* partially */,
|
|
144
|
+
shortcut: null
|
|
145
|
+
};
|
|
146
|
+
var defaultTocAction = {
|
|
147
|
+
visibility: "partially" /* partially */,
|
|
148
|
+
shortcut: null,
|
|
149
|
+
// `${ UnstableShortcutMetaKeywords.shift }+${ ShortcutMetaKeywords.alt }+T`,
|
|
150
|
+
sheet: {
|
|
151
|
+
defaultSheet: "popover" /* popover */,
|
|
152
|
+
breakpoints: {
|
|
153
|
+
["compact" /* compact */]: "fullscreen" /* fullscreen */,
|
|
154
|
+
["medium" /* medium */]: "fullscreen" /* fullscreen */
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
docked: {
|
|
158
|
+
dockable: "both" /* both */,
|
|
159
|
+
dragIndicator: false,
|
|
160
|
+
width: 360,
|
|
161
|
+
minWidth: 320,
|
|
162
|
+
maxWidth: 450
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
var defaultJumpToPositionAction = {
|
|
166
|
+
visibility: "overflow" /* overflow */,
|
|
167
|
+
shortcut: null,
|
|
168
|
+
// `${ UnstableShortcutMetaKeywords.shift }+${ ShortcutMetaKeywords.alt }+J`,
|
|
169
|
+
sheet: {
|
|
170
|
+
defaultSheet: "popover" /* popover */,
|
|
171
|
+
breakpoints: {
|
|
172
|
+
["compact" /* compact */]: "bottomSheet" /* bottomSheet */
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
docked: {
|
|
176
|
+
dockable: "none" /* none */
|
|
177
|
+
},
|
|
178
|
+
snapped: {
|
|
179
|
+
scrim: true,
|
|
180
|
+
minHeight: "content-height"
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/preferences/models/settings.ts
|
|
185
|
+
var ThSettingsKeys = /* @__PURE__ */ ((ThSettingsKeys2) => {
|
|
186
|
+
ThSettingsKeys2["columns"] = "columns";
|
|
187
|
+
ThSettingsKeys2["fontFamily"] = "fontFamily";
|
|
188
|
+
ThSettingsKeys2["fontWeight"] = "fontWeight";
|
|
189
|
+
ThSettingsKeys2["hyphens"] = "hyphens";
|
|
190
|
+
ThSettingsKeys2["layout"] = "layout";
|
|
191
|
+
ThSettingsKeys2["letterSpacing"] = "letterSpacing";
|
|
192
|
+
ThSettingsKeys2["ligatures"] = "ligatures";
|
|
193
|
+
ThSettingsKeys2["lineHeight"] = "lineHeight";
|
|
194
|
+
ThSettingsKeys2["noRuby"] = "noRuby";
|
|
195
|
+
ThSettingsKeys2["paragraphIndent"] = "paragraphIndent";
|
|
196
|
+
ThSettingsKeys2["paragraphSpacing"] = "paragraphSpacing";
|
|
197
|
+
ThSettingsKeys2["publisherStyles"] = "publisherStyles";
|
|
198
|
+
ThSettingsKeys2["spacingGroup"] = "spacingGroup";
|
|
199
|
+
ThSettingsKeys2["spacingPresets"] = "spacingPresets";
|
|
200
|
+
ThSettingsKeys2["textAlign"] = "textAlign";
|
|
201
|
+
ThSettingsKeys2["textGroup"] = "textGroup";
|
|
202
|
+
ThSettingsKeys2["textNormalize"] = "textNormalize";
|
|
203
|
+
ThSettingsKeys2["theme"] = "theme";
|
|
204
|
+
ThSettingsKeys2["wordSpacing"] = "wordSpacing";
|
|
205
|
+
ThSettingsKeys2["zoom"] = "zoom";
|
|
206
|
+
return ThSettingsKeys2;
|
|
207
|
+
})(ThSettingsKeys || {});
|
|
208
|
+
var ThTextSettingsKeys = /* @__PURE__ */ ((ThTextSettingsKeys3) => {
|
|
209
|
+
ThTextSettingsKeys3["fontFamily"] = "fontFamily";
|
|
210
|
+
ThTextSettingsKeys3["fontWeight"] = "fontWeight";
|
|
211
|
+
ThTextSettingsKeys3["hyphens"] = "hyphens";
|
|
212
|
+
ThTextSettingsKeys3["ligatures"] = "ligatures";
|
|
213
|
+
ThTextSettingsKeys3["noRuby"] = "noRuby";
|
|
214
|
+
ThTextSettingsKeys3["textAlign"] = "textAlign";
|
|
215
|
+
ThTextSettingsKeys3["textNormalize"] = "textNormalize";
|
|
216
|
+
return ThTextSettingsKeys3;
|
|
217
|
+
})(ThTextSettingsKeys || {});
|
|
218
|
+
var ThSpacingSettingsKeys = /* @__PURE__ */ ((ThSpacingSettingsKeys3) => {
|
|
219
|
+
ThSpacingSettingsKeys3["letterSpacing"] = "letterSpacing";
|
|
220
|
+
ThSpacingSettingsKeys3["lineHeight"] = "lineHeight";
|
|
221
|
+
ThSpacingSettingsKeys3["paragraphIndent"] = "paragraphIndent";
|
|
222
|
+
ThSpacingSettingsKeys3["paragraphSpacing"] = "paragraphSpacing";
|
|
223
|
+
ThSpacingSettingsKeys3["publisherStyles"] = "publisherStyles";
|
|
224
|
+
ThSpacingSettingsKeys3["spacingPresets"] = "spacingPresets";
|
|
225
|
+
ThSpacingSettingsKeys3["wordSpacing"] = "wordSpacing";
|
|
226
|
+
return ThSpacingSettingsKeys3;
|
|
227
|
+
})(ThSpacingSettingsKeys || {});
|
|
228
|
+
var ThSettingsContainerKeys = /* @__PURE__ */ ((ThSettingsContainerKeys2) => {
|
|
229
|
+
ThSettingsContainerKeys2["initial"] = "initial";
|
|
230
|
+
ThSettingsContainerKeys2["text"] = "text";
|
|
231
|
+
ThSettingsContainerKeys2["spacing"] = "spacing";
|
|
232
|
+
return ThSettingsContainerKeys2;
|
|
233
|
+
})(ThSettingsContainerKeys || {});
|
|
234
|
+
var ThSettingsRangeVariant = /* @__PURE__ */ ((ThSettingsRangeVariant2) => {
|
|
235
|
+
ThSettingsRangeVariant2["slider"] = "slider";
|
|
236
|
+
ThSettingsRangeVariant2["incrementedSlider"] = "incrementedSlider";
|
|
237
|
+
ThSettingsRangeVariant2["numberField"] = "numberField";
|
|
238
|
+
ThSettingsRangeVariant2["sliderWithPresets"] = "sliderWithPresets";
|
|
239
|
+
ThSettingsRangeVariant2["presetsGroup"] = "presetsGroup";
|
|
240
|
+
return ThSettingsRangeVariant2;
|
|
241
|
+
})(ThSettingsRangeVariant || {});
|
|
242
|
+
var ThSettingsRangePlaceholder = /* @__PURE__ */ ((ThSettingsRangePlaceholder2) => {
|
|
243
|
+
ThSettingsRangePlaceholder2["range"] = "range";
|
|
244
|
+
ThSettingsRangePlaceholder2["none"] = "none";
|
|
245
|
+
return ThSettingsRangePlaceholder2;
|
|
246
|
+
})(ThSettingsRangePlaceholder || {});
|
|
247
|
+
var ThSpacingPresetKeys = /* @__PURE__ */ ((ThSpacingPresetKeys4) => {
|
|
248
|
+
ThSpacingPresetKeys4["publisher"] = "publisher";
|
|
249
|
+
ThSpacingPresetKeys4["tight"] = "tight";
|
|
250
|
+
ThSpacingPresetKeys4["balanced"] = "balanced";
|
|
251
|
+
ThSpacingPresetKeys4["loose"] = "loose";
|
|
252
|
+
ThSpacingPresetKeys4["accessible"] = "accessible";
|
|
253
|
+
ThSpacingPresetKeys4["custom"] = "custom";
|
|
254
|
+
return ThSpacingPresetKeys4;
|
|
255
|
+
})(ThSpacingPresetKeys || {});
|
|
256
|
+
var ThLayoutOptions = /* @__PURE__ */ ((ThLayoutOptions2) => {
|
|
257
|
+
ThLayoutOptions2["scroll"] = "scroll_option";
|
|
258
|
+
ThLayoutOptions2["paginated"] = "page_option";
|
|
259
|
+
return ThLayoutOptions2;
|
|
260
|
+
})(ThLayoutOptions || {});
|
|
261
|
+
var ThTextAlignOptions = /* @__PURE__ */ ((ThTextAlignOptions2) => {
|
|
262
|
+
ThTextAlignOptions2["publisher"] = "publisher";
|
|
263
|
+
ThTextAlignOptions2["start"] = "start";
|
|
264
|
+
ThTextAlignOptions2["justify"] = "justify";
|
|
265
|
+
return ThTextAlignOptions2;
|
|
266
|
+
})(ThTextAlignOptions || {});
|
|
267
|
+
var ThLineHeightOptions = /* @__PURE__ */ ((ThLineHeightOptions3) => {
|
|
268
|
+
ThLineHeightOptions3["publisher"] = "publisher";
|
|
269
|
+
ThLineHeightOptions3["small"] = "small";
|
|
270
|
+
ThLineHeightOptions3["medium"] = "medium";
|
|
271
|
+
ThLineHeightOptions3["large"] = "large";
|
|
272
|
+
return ThLineHeightOptions3;
|
|
273
|
+
})(ThLineHeightOptions || {});
|
|
274
|
+
var defaultTextSettingsMain = ["fontFamily" /* fontFamily */];
|
|
275
|
+
var defaultTextSettingsSubpanel = [
|
|
276
|
+
"fontFamily" /* fontFamily */,
|
|
277
|
+
"textAlign" /* textAlign */,
|
|
278
|
+
"hyphens" /* hyphens */,
|
|
279
|
+
"fontWeight" /* fontWeight */,
|
|
280
|
+
"textNormalize" /* textNormalize */,
|
|
281
|
+
"ligatures" /* ligatures */,
|
|
282
|
+
"noRuby" /* noRuby */
|
|
283
|
+
];
|
|
284
|
+
var defaultSpacingSettingsMain = [
|
|
285
|
+
"spacingPresets" /* spacingPresets */
|
|
286
|
+
];
|
|
287
|
+
var defaultSpacingSettingsSubpanel = [
|
|
288
|
+
"spacingPresets" /* spacingPresets */,
|
|
289
|
+
"lineHeight" /* lineHeight */,
|
|
290
|
+
"paragraphSpacing" /* paragraphSpacing */,
|
|
291
|
+
"paragraphIndent" /* paragraphIndent */,
|
|
292
|
+
"wordSpacing" /* wordSpacing */,
|
|
293
|
+
"letterSpacing" /* letterSpacing */
|
|
294
|
+
];
|
|
295
|
+
var defaultSpacingPresetsOrder = [
|
|
296
|
+
"publisher" /* publisher */,
|
|
297
|
+
"accessible" /* accessible */,
|
|
298
|
+
"custom" /* custom */,
|
|
299
|
+
"tight" /* tight */,
|
|
300
|
+
"balanced" /* balanced */,
|
|
301
|
+
"loose" /* loose */
|
|
302
|
+
];
|
|
303
|
+
var defaultParagraphSpacing = {
|
|
304
|
+
variant: "numberField" /* numberField */,
|
|
305
|
+
placeholder: "range" /* range */,
|
|
306
|
+
range: [0, 3],
|
|
307
|
+
step: 0.25
|
|
308
|
+
};
|
|
309
|
+
var defaultParagraphIndent = {
|
|
310
|
+
variant: "numberField" /* numberField */,
|
|
311
|
+
placeholder: "range" /* range */,
|
|
312
|
+
range: [0, 2],
|
|
313
|
+
step: 0.25
|
|
314
|
+
};
|
|
315
|
+
var defaultWordSpacing = {
|
|
316
|
+
variant: "numberField" /* numberField */,
|
|
317
|
+
placeholder: "range" /* range */,
|
|
318
|
+
range: [0, 1],
|
|
319
|
+
step: 0.1
|
|
320
|
+
};
|
|
321
|
+
var defaultLetterSpacing = {
|
|
322
|
+
variant: "numberField" /* numberField */,
|
|
323
|
+
placeholder: "range" /* range */,
|
|
324
|
+
range: [0, 0.5],
|
|
325
|
+
step: 0.05
|
|
326
|
+
};
|
|
327
|
+
var defaultLineHeights = {
|
|
328
|
+
["small" /* small */]: 1.35,
|
|
329
|
+
["medium" /* medium */]: 1.5,
|
|
330
|
+
["large" /* large */]: 1.75
|
|
331
|
+
};
|
|
332
|
+
var defaultZoom = {
|
|
333
|
+
variant: "numberField" /* numberField */,
|
|
334
|
+
placeholder: "range" /* range */,
|
|
335
|
+
range: [0.7, 4],
|
|
336
|
+
step: 0.05
|
|
337
|
+
};
|
|
338
|
+
var defaultSpacingPresets = {
|
|
339
|
+
["tight" /* tight */]: {
|
|
340
|
+
["lineHeight" /* lineHeight */]: "small" /* small */,
|
|
341
|
+
["paragraphSpacing" /* paragraphSpacing */]: 0.25,
|
|
342
|
+
["paragraphIndent" /* paragraphIndent */]: 1
|
|
343
|
+
},
|
|
344
|
+
["balanced" /* balanced */]: {
|
|
345
|
+
["lineHeight" /* lineHeight */]: "medium" /* medium */,
|
|
346
|
+
["paragraphSpacing" /* paragraphSpacing */]: 1,
|
|
347
|
+
["paragraphIndent" /* paragraphIndent */]: 1
|
|
348
|
+
},
|
|
349
|
+
["loose" /* loose */]: {
|
|
350
|
+
["lineHeight" /* lineHeight */]: "large" /* large */,
|
|
351
|
+
["paragraphSpacing" /* paragraphSpacing */]: 1.5,
|
|
352
|
+
["paragraphIndent" /* paragraphIndent */]: 1
|
|
353
|
+
},
|
|
354
|
+
["accessible" /* accessible */]: {
|
|
355
|
+
["lineHeight" /* lineHeight */]: "large" /* large */,
|
|
356
|
+
["paragraphSpacing" /* paragraphSpacing */]: 2.5,
|
|
357
|
+
["paragraphIndent" /* paragraphIndent */]: 1,
|
|
358
|
+
["letterSpacing" /* letterSpacing */]: 0.1,
|
|
359
|
+
["wordSpacing" /* wordSpacing */]: 0.3
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// src/preferences/models/audio.ts
|
|
364
|
+
var ThAudioActionKeys = /* @__PURE__ */ ((ThAudioActionKeys2) => {
|
|
365
|
+
ThAudioActionKeys2["toc"] = "audio.toc";
|
|
366
|
+
ThAudioActionKeys2["volume"] = "audio.volume";
|
|
367
|
+
ThAudioActionKeys2["playbackRate"] = "audio.playbackRate";
|
|
368
|
+
ThAudioActionKeys2["sleepTimer"] = "audio.sleepTimer";
|
|
369
|
+
ThAudioActionKeys2["remotePlayback"] = "audio.remotePlayback";
|
|
370
|
+
return ThAudioActionKeys2;
|
|
371
|
+
})(ThAudioActionKeys || {});
|
|
372
|
+
var ThAudioKeys = /* @__PURE__ */ ((ThAudioKeys2) => {
|
|
373
|
+
ThAudioKeys2["theme"] = "theme";
|
|
374
|
+
ThAudioKeys2["volume"] = "volume";
|
|
375
|
+
ThAudioKeys2["playbackRate"] = "playbackRate";
|
|
376
|
+
ThAudioKeys2["skipBackwardInterval"] = "skipBackwardInterval";
|
|
377
|
+
ThAudioKeys2["skipForwardInterval"] = "skipForwardInterval";
|
|
378
|
+
ThAudioKeys2["skipInterval"] = "skipInterval";
|
|
379
|
+
ThAudioKeys2["autoPlay"] = "autoPlay";
|
|
380
|
+
ThAudioKeys2["sleepTimer"] = "sleepTimer";
|
|
381
|
+
return ThAudioKeys2;
|
|
382
|
+
})(ThAudioKeys || {});
|
|
383
|
+
var ThSettingsTimerVariant = /* @__PURE__ */ ((ThSettingsTimerVariant2) => {
|
|
384
|
+
ThSettingsTimerVariant2["presetList"] = "presetList";
|
|
385
|
+
ThSettingsTimerVariant2["durationField"] = "durationField";
|
|
386
|
+
return ThSettingsTimerVariant2;
|
|
387
|
+
})(ThSettingsTimerVariant || {});
|
|
388
|
+
var defaultAudioVolume = {
|
|
389
|
+
variant: "slider" /* slider */,
|
|
390
|
+
range: [0, 1],
|
|
391
|
+
step: 0.1,
|
|
392
|
+
placeholder: "range" /* range */
|
|
393
|
+
};
|
|
394
|
+
var defaultAudioPlaybackRate = {
|
|
395
|
+
variant: "sliderWithPresets" /* sliderWithPresets */,
|
|
396
|
+
range: [0.5, 4],
|
|
397
|
+
step: 0.05,
|
|
398
|
+
placeholder: "range" /* range */,
|
|
399
|
+
presets: [0.75, 1, 1.25, 1.5, 1.75, 2]
|
|
400
|
+
};
|
|
401
|
+
var defaultAudioSkipBackwardInterval = {
|
|
402
|
+
variant: "presetsGroup" /* presetsGroup */,
|
|
403
|
+
range: [5, 60],
|
|
404
|
+
step: 5,
|
|
405
|
+
placeholder: "range" /* range */,
|
|
406
|
+
presets: [5, 10, 30]
|
|
407
|
+
};
|
|
408
|
+
var defaultAudioSkipForwardInterval = {
|
|
409
|
+
variant: "presetsGroup" /* presetsGroup */,
|
|
410
|
+
range: [5, 60],
|
|
411
|
+
step: 5,
|
|
412
|
+
placeholder: "range" /* range */,
|
|
413
|
+
presets: [5, 10, 30]
|
|
414
|
+
};
|
|
415
|
+
var defaultAudioSkipInterval = {
|
|
416
|
+
variant: "presetsGroup" /* presetsGroup */,
|
|
417
|
+
range: [5, 60],
|
|
418
|
+
step: 5,
|
|
419
|
+
placeholder: "range" /* range */,
|
|
420
|
+
presets: [5, 10, 30]
|
|
421
|
+
};
|
|
422
|
+
var defaultAudioSleepTimer = {
|
|
423
|
+
variant: "durationField" /* durationField */,
|
|
424
|
+
maxHours: 23
|
|
425
|
+
};
|
|
426
|
+
var defaultAudioSleepTimerPresetList = {
|
|
427
|
+
variant: "presetList" /* presetList */,
|
|
428
|
+
presets: [15, 30, 45, 60, 90, "endOfFragment", "endOfResource"]
|
|
429
|
+
};
|
|
430
|
+
var defaultAudioVolumeAction = {
|
|
431
|
+
visibility: "always" /* always */,
|
|
432
|
+
shortcut: null,
|
|
433
|
+
sheet: {
|
|
434
|
+
defaultSheet: "compactPopover" /* compactPopover */,
|
|
435
|
+
breakpoints: {}
|
|
436
|
+
},
|
|
437
|
+
docked: { dockable: "none" /* none */ }
|
|
438
|
+
};
|
|
439
|
+
var defaultAudioPlaybackRateAction = {
|
|
440
|
+
visibility: "always" /* always */,
|
|
441
|
+
shortcut: null,
|
|
442
|
+
sheet: {
|
|
443
|
+
defaultSheet: "compactPopover" /* compactPopover */,
|
|
444
|
+
breakpoints: { ["compact" /* compact */]: "bottomSheet" /* bottomSheet */ }
|
|
445
|
+
},
|
|
446
|
+
snapped: {
|
|
447
|
+
minHeight: "content-height"
|
|
448
|
+
},
|
|
449
|
+
docked: { dockable: "none" /* none */ }
|
|
450
|
+
};
|
|
451
|
+
var defaultAudioSleepTimerAction = {
|
|
452
|
+
visibility: "partially" /* partially */,
|
|
453
|
+
shortcut: null,
|
|
454
|
+
sheet: {
|
|
455
|
+
defaultSheet: "modal" /* modal */,
|
|
456
|
+
breakpoints: {
|
|
457
|
+
["compact" /* compact */]: "bottomSheet" /* bottomSheet */,
|
|
458
|
+
["medium" /* medium */]: "bottomSheet" /* bottomSheet */
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
snapped: {
|
|
462
|
+
minHeight: "content-height"
|
|
463
|
+
},
|
|
464
|
+
docked: { dockable: "none" /* none */ }
|
|
465
|
+
};
|
|
466
|
+
var defaultAudioRemotePlaybackAction = {
|
|
467
|
+
visibility: "always" /* always */,
|
|
468
|
+
shortcut: null
|
|
469
|
+
};
|
|
470
|
+
var defaultAudioTocAction = {
|
|
471
|
+
visibility: "partially" /* partially */,
|
|
472
|
+
shortcut: null,
|
|
473
|
+
sheet: {
|
|
474
|
+
defaultSheet: "modal" /* modal */,
|
|
475
|
+
breakpoints: {
|
|
476
|
+
["compact" /* compact */]: "fullscreen" /* fullscreen */,
|
|
477
|
+
["medium" /* medium */]: "fullscreen" /* fullscreen */
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
docked: {
|
|
481
|
+
dockable: "both" /* both */,
|
|
482
|
+
dragIndicator: false,
|
|
483
|
+
width: 360,
|
|
484
|
+
minWidth: 320,
|
|
485
|
+
maxWidth: 450
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// src/preferences/helpers/buildThemeObject.ts
|
|
490
|
+
var buildThemeObject = ({
|
|
491
|
+
theme,
|
|
492
|
+
themeKeys,
|
|
493
|
+
systemThemes,
|
|
494
|
+
colorScheme
|
|
495
|
+
}) => {
|
|
496
|
+
if (!theme) {
|
|
497
|
+
return {};
|
|
498
|
+
}
|
|
499
|
+
if (theme === "auto" && colorScheme && systemThemes) {
|
|
500
|
+
theme = colorScheme === "dark" /* dark */ ? systemThemes.dark : systemThemes.light;
|
|
501
|
+
}
|
|
502
|
+
let themeProps = {};
|
|
503
|
+
const themeToken = themeKeys[theme];
|
|
504
|
+
if (themeToken) {
|
|
505
|
+
themeProps = {
|
|
506
|
+
backgroundColor: themeToken.background,
|
|
507
|
+
textColor: themeToken.text,
|
|
508
|
+
linkColor: themeToken.link,
|
|
509
|
+
selectionBackgroundColor: themeToken.select,
|
|
510
|
+
selectionTextColor: themeToken.onSelect,
|
|
511
|
+
visitedColor: themeToken.visited
|
|
512
|
+
};
|
|
513
|
+
} else {
|
|
514
|
+
console.warn(`Theme key "${String(theme)}" not found in themeKeys.`);
|
|
515
|
+
themeProps = {
|
|
516
|
+
backgroundColor: null,
|
|
517
|
+
textColor: null,
|
|
518
|
+
linkColor: null,
|
|
519
|
+
selectionBackgroundColor: null,
|
|
520
|
+
selectionTextColor: null,
|
|
521
|
+
visitedColor: null
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
return themeProps;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
// src/preferences/helpers/fontPref/bunnyFonts.ts
|
|
528
|
+
var DEFAULT_FALLBACK = "sans-serif";
|
|
529
|
+
var createDefinitionsFromBunnyFonts = (params) => {
|
|
530
|
+
const { cssUrl, options } = params;
|
|
531
|
+
const { fallbacks, order, labels } = options || {};
|
|
532
|
+
const processedUrl = cssUrl.includes("@import") ? cssUrl.match(/@import\s+url\(['"]?([^'")]+)['"]?\)/i)?.[1] || cssUrl : cssUrl.includes("href=") ? cssUrl.match(/href=["']([^"']+)["']/)?.[1] || cssUrl : cssUrl;
|
|
533
|
+
const url = new URL(processedUrl);
|
|
534
|
+
if (!url.hostname.includes("fonts.bunny.net")) {
|
|
535
|
+
throw new Error("Invalid Bunny Fonts URL");
|
|
536
|
+
}
|
|
537
|
+
const familyParam = url.searchParams.get("family");
|
|
538
|
+
if (!familyParam) {
|
|
539
|
+
throw new Error("No family parameter found in Bunny Fonts URL");
|
|
540
|
+
}
|
|
541
|
+
const fontEntries = familyParam.split("|").map((familyStr) => {
|
|
542
|
+
const [familyName, weightsStr = ""] = familyStr.split(":");
|
|
543
|
+
if (!familyName) {
|
|
544
|
+
throw new Error(`Invalid font family format: ${familyStr}`);
|
|
545
|
+
}
|
|
546
|
+
const weightStyles = /* @__PURE__ */ new Map();
|
|
547
|
+
if (weightsStr) {
|
|
548
|
+
weightsStr.split(",").forEach((weightStr) => {
|
|
549
|
+
const isItalic = weightStr.endsWith("i");
|
|
550
|
+
const weightValue = parseInt(isItalic ? weightStr.slice(0, -1) : weightStr, 10);
|
|
551
|
+
if (!isNaN(weightValue)) {
|
|
552
|
+
if (!weightStyles.has(weightValue)) {
|
|
553
|
+
weightStyles.set(weightValue, /* @__PURE__ */ new Set());
|
|
554
|
+
}
|
|
555
|
+
weightStyles.get(weightValue)?.add(isItalic ? "italic" : "normal");
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
const weights = Array.from(weightStyles.keys()).sort((a, b) => a - b);
|
|
560
|
+
const hasItalic = Array.from(weightStyles.values()).some((styles2) => styles2.has("italic"));
|
|
561
|
+
const styles = hasItalic ? ["normal", "italic"] : ["normal"];
|
|
562
|
+
const fontId = familyName;
|
|
563
|
+
const familyDisplayName = familyName.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
564
|
+
return [
|
|
565
|
+
fontId,
|
|
566
|
+
{
|
|
567
|
+
id: fontId,
|
|
568
|
+
name: familyDisplayName,
|
|
569
|
+
...labels?.[fontId] && { label: labels[fontId] },
|
|
570
|
+
source: {
|
|
571
|
+
type: "custom",
|
|
572
|
+
provider: "bunny"
|
|
573
|
+
},
|
|
574
|
+
spec: {
|
|
575
|
+
family: familyDisplayName,
|
|
576
|
+
fallbacks: fallbacks?.[fontId] || [DEFAULT_FALLBACK],
|
|
577
|
+
weights: {
|
|
578
|
+
type: "static",
|
|
579
|
+
values: weights.length ? weights : [400]
|
|
580
|
+
},
|
|
581
|
+
styles
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
];
|
|
585
|
+
});
|
|
586
|
+
const result = Object.fromEntries(fontEntries);
|
|
587
|
+
if (order && order.length > 0) {
|
|
588
|
+
const orderedResult = {};
|
|
589
|
+
order.forEach((fontId) => {
|
|
590
|
+
if (result[fontId]) {
|
|
591
|
+
orderedResult[fontId] = result[fontId];
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
Object.entries(result).forEach(([fontId, definition]) => {
|
|
595
|
+
if (!orderedResult[fontId]) {
|
|
596
|
+
orderedResult[fontId] = definition;
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
return orderedResult;
|
|
600
|
+
}
|
|
601
|
+
return result;
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// src/preferences/helpers/fontPref/googleFonts.ts
|
|
605
|
+
var DEFAULT_FALLBACK2 = "sans-serif";
|
|
606
|
+
var DEFAULT_WIDTH_STEP = 20;
|
|
607
|
+
var DEFAULT_WEIGHT_STEP = 20;
|
|
608
|
+
var createDefinitionsFromGoogleFonts = (params) => {
|
|
609
|
+
const { cssUrl, options } = params;
|
|
610
|
+
const { widthStep = DEFAULT_WIDTH_STEP, weightStep = DEFAULT_WEIGHT_STEP, display, labels, fallbacks, order } = options || {};
|
|
611
|
+
const processedUrl = cssUrl.includes("@import") ? cssUrl.match(/@import\s+url\(['"]?([^'")]+)['"]?\)/i)?.[1] || cssUrl : cssUrl.includes("href=") ? cssUrl.match(/href=["']([^"']+)["']/)?.[1] || cssUrl : cssUrl;
|
|
612
|
+
const url = new URL(processedUrl);
|
|
613
|
+
if (!url.hostname.includes("fonts.googleapis.com")) {
|
|
614
|
+
throw new Error("Invalid Google Fonts URL");
|
|
615
|
+
}
|
|
616
|
+
const familyParams = url.searchParams.getAll("family");
|
|
617
|
+
if (familyParams.length === 0) {
|
|
618
|
+
throw new Error("No family parameter found in Google Fonts URL");
|
|
619
|
+
}
|
|
620
|
+
const families = familyParams.map((familyParam) => {
|
|
621
|
+
const decodedFamily = decodeURIComponent(familyParam);
|
|
622
|
+
const [familyName, axesStr] = decodedFamily.split(":");
|
|
623
|
+
if (!familyName) {
|
|
624
|
+
throw new Error(`Invalid family format: ${familyParam}`);
|
|
625
|
+
}
|
|
626
|
+
const family = {
|
|
627
|
+
name: familyName.replace(/\+/g, " "),
|
|
628
|
+
styles: ["normal"],
|
|
629
|
+
weights: { type: "static", values: [400] }
|
|
630
|
+
// Default weight
|
|
631
|
+
};
|
|
632
|
+
let hasExplicitWeights = false;
|
|
633
|
+
if (axesStr) {
|
|
634
|
+
const [axisNames, valuesStr] = axesStr.split("@");
|
|
635
|
+
if (axisNames && valuesStr) {
|
|
636
|
+
const axes = axisNames.split(",");
|
|
637
|
+
const variations = valuesStr.split(";");
|
|
638
|
+
variations.forEach((variation) => {
|
|
639
|
+
const values = variation.split(",");
|
|
640
|
+
axes.forEach((axis, index) => {
|
|
641
|
+
const value = values[index];
|
|
642
|
+
if (!value) return;
|
|
643
|
+
switch (axis) {
|
|
644
|
+
case "ital":
|
|
645
|
+
if (value === "1") {
|
|
646
|
+
family.styles = Array.from(/* @__PURE__ */ new Set([...family.styles, "italic"]));
|
|
647
|
+
}
|
|
648
|
+
break;
|
|
649
|
+
case "wght":
|
|
650
|
+
if (value.includes("..")) {
|
|
651
|
+
const [min, max] = value.split("..").map(Number);
|
|
652
|
+
if (!isNaN(min) && !isNaN(max)) {
|
|
653
|
+
family.weights = {
|
|
654
|
+
type: "variable",
|
|
655
|
+
min,
|
|
656
|
+
max,
|
|
657
|
+
step: weightStep
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
} else {
|
|
661
|
+
const weight = Number(value);
|
|
662
|
+
if (!isNaN(weight) && family.weights.type === "static") {
|
|
663
|
+
const currentWeights = family.weights.values;
|
|
664
|
+
const newWeights = !hasExplicitWeights ? [weight] : Array.from(/* @__PURE__ */ new Set([...currentWeights, weight])).sort((a, b) => a - b);
|
|
665
|
+
family.weights = {
|
|
666
|
+
type: "static",
|
|
667
|
+
values: newWeights
|
|
668
|
+
};
|
|
669
|
+
hasExplicitWeights = true;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
break;
|
|
673
|
+
case "wdth":
|
|
674
|
+
if (value.includes("..")) {
|
|
675
|
+
const [min, max] = value.split("..").map(Number);
|
|
676
|
+
if (!isNaN(min) && !isNaN(max)) {
|
|
677
|
+
family.widths = {
|
|
678
|
+
min,
|
|
679
|
+
max,
|
|
680
|
+
step: widthStep
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
break;
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return family;
|
|
691
|
+
});
|
|
692
|
+
const fontEntries = families.map((family) => {
|
|
693
|
+
const fontId = family.name.toLowerCase().replace(/\s+/g, "-");
|
|
694
|
+
return [
|
|
695
|
+
fontId,
|
|
696
|
+
{
|
|
697
|
+
id: fontId,
|
|
698
|
+
name: family.name,
|
|
699
|
+
...labels?.[fontId] && { label: labels[fontId] },
|
|
700
|
+
source: { type: "custom", provider: "google" },
|
|
701
|
+
spec: {
|
|
702
|
+
family: family.name,
|
|
703
|
+
fallbacks: fallbacks?.[fontId] || [DEFAULT_FALLBACK2],
|
|
704
|
+
weights: family.weights,
|
|
705
|
+
styles: family.styles,
|
|
706
|
+
...family.widths && { widths: family.widths },
|
|
707
|
+
...display && { display }
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
];
|
|
711
|
+
});
|
|
712
|
+
if (order && order.length > 0) {
|
|
713
|
+
const orderedEntries = [];
|
|
714
|
+
const fontMap = new Map(fontEntries);
|
|
715
|
+
for (const fontId of order) {
|
|
716
|
+
const fontEntry = fontMap.get(fontId);
|
|
717
|
+
if (fontEntry) {
|
|
718
|
+
orderedEntries.push([fontId, fontEntry]);
|
|
719
|
+
fontMap.delete(fontId);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
for (const [fontId, fontEntry] of fontMap.entries()) {
|
|
723
|
+
orderedEntries.push([fontId, fontEntry]);
|
|
724
|
+
}
|
|
725
|
+
return Object.fromEntries(orderedEntries);
|
|
726
|
+
}
|
|
727
|
+
return Object.fromEntries(fontEntries);
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
// src/preferences/helpers/fontPref/localFonts.ts
|
|
731
|
+
var createDefinitionFromStaticFonts = (params) => {
|
|
732
|
+
const { id, name, files, family, label, fallbacks = ["sans-serif"] } = params;
|
|
733
|
+
if (!files || files.length === 0) {
|
|
734
|
+
throw new Error("No files provided to infer font specification");
|
|
735
|
+
}
|
|
736
|
+
if (!files.every((file) => file.weight !== void 0)) {
|
|
737
|
+
throw new Error("All files must have explicit weights for static font specification inference");
|
|
738
|
+
}
|
|
739
|
+
const weights = Array.from(new Set(files.map((file) => file.weight))).sort((a, b) => a - b);
|
|
740
|
+
const styles = Array.from(new Set(files.map((file) => file.style)));
|
|
741
|
+
const source = {
|
|
742
|
+
type: "custom",
|
|
743
|
+
provider: "local",
|
|
744
|
+
variant: "static",
|
|
745
|
+
files
|
|
746
|
+
};
|
|
747
|
+
const spec = {
|
|
748
|
+
family: family || name,
|
|
749
|
+
fallbacks,
|
|
750
|
+
weights: {
|
|
751
|
+
type: "static",
|
|
752
|
+
values: weights
|
|
753
|
+
},
|
|
754
|
+
styles
|
|
755
|
+
};
|
|
756
|
+
return {
|
|
757
|
+
id,
|
|
758
|
+
name,
|
|
759
|
+
...label && { label },
|
|
760
|
+
source,
|
|
761
|
+
spec
|
|
762
|
+
};
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
// src/preferences/helpers/validateObjectKeys.ts
|
|
766
|
+
var validateObjectKeys = (orderArrays, keysObj, context, specialCase, fallback) => {
|
|
767
|
+
const allOrders = new Set(
|
|
768
|
+
orderArrays.flatMap((arr) => {
|
|
769
|
+
if (!specialCase) return arr;
|
|
770
|
+
return arr.filter((k) => {
|
|
771
|
+
if (Array.isArray(specialCase)) {
|
|
772
|
+
return !specialCase.includes(k);
|
|
773
|
+
} else {
|
|
774
|
+
return k !== specialCase;
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
})
|
|
778
|
+
);
|
|
779
|
+
const availableKeys = Object.keys(keysObj);
|
|
780
|
+
allOrders.forEach((key) => {
|
|
781
|
+
if (!availableKeys.includes(key)) {
|
|
782
|
+
if (fallback) keysObj[key] = fallback;
|
|
783
|
+
console.warn(
|
|
784
|
+
`Key "${key}" in ${context} order arrays not found in ${context}.keys.${fallback ? `
|
|
785
|
+
Using fallback: ${JSON.stringify(fallback)}` : ""}`
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
// src/preferences/models/fonts.ts
|
|
792
|
+
var readiumCSSFontCollection = {
|
|
793
|
+
oldStyle: {
|
|
794
|
+
id: "oldStyle",
|
|
795
|
+
name: "Old Style",
|
|
796
|
+
label: "reader.preferences.fontFamily.oldStyle.descriptive",
|
|
797
|
+
source: { type: "system" },
|
|
798
|
+
spec: {
|
|
799
|
+
family: fontStacks.RS__oldStyleTf,
|
|
800
|
+
weights: { type: "static", values: [400, 700] },
|
|
801
|
+
fallbacks: []
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
modern: {
|
|
805
|
+
id: "modern",
|
|
806
|
+
name: "Modern",
|
|
807
|
+
label: "reader.preferences.fontFamily.modern.descriptive",
|
|
808
|
+
source: { type: "system" },
|
|
809
|
+
spec: {
|
|
810
|
+
family: fontStacks.RS__modernTf,
|
|
811
|
+
weights: { type: "static", values: [400, 700] },
|
|
812
|
+
fallbacks: []
|
|
813
|
+
}
|
|
814
|
+
},
|
|
815
|
+
sans: {
|
|
816
|
+
id: "sans",
|
|
817
|
+
name: "Sans",
|
|
818
|
+
label: "reader.preferences.fontFamily.sans",
|
|
819
|
+
source: { type: "system" },
|
|
820
|
+
spec: {
|
|
821
|
+
family: fontStacks.RS__sansTf,
|
|
822
|
+
weights: { type: "static", values: [400, 700] },
|
|
823
|
+
fallbacks: []
|
|
824
|
+
}
|
|
825
|
+
},
|
|
826
|
+
humanist: {
|
|
827
|
+
id: "humanist",
|
|
828
|
+
name: "Humanist",
|
|
829
|
+
label: "reader.preferences.fontFamily.humanist.descriptive",
|
|
830
|
+
source: { type: "system" },
|
|
831
|
+
spec: {
|
|
832
|
+
family: fontStacks.RS__humanistTf,
|
|
833
|
+
weights: { type: "static", values: [400, 700] },
|
|
834
|
+
fallbacks: []
|
|
835
|
+
}
|
|
836
|
+
},
|
|
837
|
+
monospace: {
|
|
838
|
+
id: "monospace",
|
|
839
|
+
name: "Monospace",
|
|
840
|
+
label: "reader.preferences.fontFamily.monospace",
|
|
841
|
+
source: { type: "system" },
|
|
842
|
+
spec: {
|
|
843
|
+
family: fontStacks.RS__monospaceTf,
|
|
844
|
+
weights: { type: "static", values: [400, 700] },
|
|
845
|
+
fallbacks: []
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
var defaultFontCollection = {
|
|
850
|
+
...createDefinitionsFromGoogleFonts({
|
|
851
|
+
cssUrl: "https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&family=Literata:ital,opsz,wght@0,7..72,200..900;1,7..72,200..900",
|
|
852
|
+
options: {
|
|
853
|
+
order: ["literata", "atkinson-hyperlegible-next"],
|
|
854
|
+
fallbacks: {
|
|
855
|
+
"literata": ["serif"],
|
|
856
|
+
"atkinson-hyperlegible-next": ["sans-serif"]
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}),
|
|
860
|
+
luciole: createDefinitionFromStaticFonts({
|
|
861
|
+
id: "luciole",
|
|
862
|
+
name: "Luciole",
|
|
863
|
+
files: [
|
|
864
|
+
{ path: "/fonts/Luciole/Luciole-Regular.woff2", weight: 400, style: "normal" },
|
|
865
|
+
{ path: "/fonts/Luciole/Luciole-Italic.woff2", weight: 400, style: "italic" },
|
|
866
|
+
{ path: "/fonts/Luciole/Luciole-Bold.woff2", weight: 700, style: "normal" },
|
|
867
|
+
{ path: "/fonts/Luciole/Luciole-BoldItalic.woff2", weight: 700, style: "italic" }
|
|
868
|
+
]
|
|
869
|
+
}),
|
|
870
|
+
...readiumCSSFontCollection,
|
|
871
|
+
iAWriterDuo: createDefinitionFromStaticFonts({
|
|
872
|
+
id: "iAWriterDuo",
|
|
873
|
+
name: "iA Writer Duo",
|
|
874
|
+
label: "iA Writer Duospace",
|
|
875
|
+
fallbacks: ["monospace"],
|
|
876
|
+
files: [
|
|
877
|
+
{ path: "/fonts/iAWriterDuo/iAWriterDuoS-Regular.woff2", weight: 400, style: "normal" },
|
|
878
|
+
{ path: "/fonts/iAWriterDuo/iAWriterDuoS-Bold.woff2", weight: 700, style: "normal" },
|
|
879
|
+
{ path: "/fonts/iAWriterDuo/iAWriterDuoS-Italic.woff2", weight: 400, style: "italic" },
|
|
880
|
+
{ path: "/fonts/iAWriterDuo/iAWriterDuoS-BoldItalic.woff2", weight: 700, style: "italic" }
|
|
881
|
+
]
|
|
882
|
+
}),
|
|
883
|
+
openDyslexic: createDefinitionFromStaticFonts({
|
|
884
|
+
id: "openDyslexic",
|
|
885
|
+
name: "Open Dyslexic",
|
|
886
|
+
files: [
|
|
887
|
+
{ path: "/fonts/OpenDyslexic/OpenDyslexic-Regular.otf", weight: 400, style: "normal" },
|
|
888
|
+
{ path: "/fonts/OpenDyslexic/OpenDyslexic-Italic.otf", weight: 400, style: "italic" },
|
|
889
|
+
{ path: "/fonts/OpenDyslexic/OpenDyslexic-Bold.otf", weight: 700, style: "normal" },
|
|
890
|
+
{ path: "/fonts/OpenDyslexic/OpenDyslexic-BoldItalic.otf", weight: 700, style: "italic" }
|
|
891
|
+
]
|
|
892
|
+
}),
|
|
893
|
+
accessibleDfA: createDefinitionFromStaticFonts({
|
|
894
|
+
id: "accessibleDfA",
|
|
895
|
+
name: "Accessible DfA",
|
|
896
|
+
files: [
|
|
897
|
+
{ path: "/fonts/AccessibleDfA/AccessibleDfA-Regular.woff2", weight: 400, style: "normal" },
|
|
898
|
+
{ path: "/fonts/AccessibleDfA/AccessibleDfA-Italic.woff2", weight: 400, style: "italic" },
|
|
899
|
+
{ path: "/fonts/AccessibleDfA/AccessibleDfA-Bold.woff2", weight: 700, style: "normal" }
|
|
900
|
+
]
|
|
901
|
+
})
|
|
902
|
+
};
|
|
903
|
+
var tamilCollection = {
|
|
904
|
+
...createDefinitionsFromGoogleFonts({
|
|
905
|
+
cssUrl: "https://fonts.googleapis.com/css2?family=Anek+Tamil:wght@100..800&family=Catamaran:wght@100..900&family=Hind+Madurai:wght@400;700&family=Mukta+Malar:wght@400;700&family=Noto+Sans+Tamil:wght@100..900&family=Noto+Serif+Tamil:ital,wght@0,100..900;1,100..900",
|
|
906
|
+
options: {
|
|
907
|
+
order: ["noto-sans-tamil", "noto-serif-tamil", "anek-tamil", "catamaran", "hind-madurai", "mukta-malar"],
|
|
908
|
+
labels: {
|
|
909
|
+
"noto-sans-tamil": "Noto Sans",
|
|
910
|
+
"noto-serif-tamil": "Noto Serif",
|
|
911
|
+
"anek-tamil": "\u0B85\u0BA9\u0BC7\u0B95\u0BCD \u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
|
|
912
|
+
"catamaran": "\u0B95\u0B9F\u0BCD\u0B9F\u0BC1\u0BAE\u0BB0\u0BA9\u0BCD",
|
|
913
|
+
"mukta-malar": "\u0BAE\u0BC1\u0B95\u0BCD\u0BA4 \u0BAE\u0BB2\u0BB0\u0BCD"
|
|
914
|
+
},
|
|
915
|
+
fallbacks: {
|
|
916
|
+
"noto-serif-tamil": ["serif"]
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
})
|
|
920
|
+
};
|
|
921
|
+
var sysFontDef = (id, name, family) => ({
|
|
922
|
+
id,
|
|
923
|
+
name,
|
|
924
|
+
source: { type: "system" },
|
|
925
|
+
spec: { family, fallbacks: [], weights: { type: "static", values: [400, 700] } }
|
|
926
|
+
});
|
|
927
|
+
var arabicFarsiCollection = {
|
|
928
|
+
...createDefinitionsFromGoogleFonts({
|
|
929
|
+
cssUrl: "https://fonts.googleapis.com/css2?family=Noto+Sans+Arabic:wght@100..900&family=Noto+Naskh+Arabic:wght@400..700&family=Scheherazade+New:wght@400..700",
|
|
930
|
+
options: {
|
|
931
|
+
order: ["noto-sans-arabic", "noto-naskh-arabic", "scheherazade-new"],
|
|
932
|
+
labels: {
|
|
933
|
+
"noto-sans-arabic": "Noto Sans",
|
|
934
|
+
"noto-naskh-arabic": "Noto Naskh"
|
|
935
|
+
},
|
|
936
|
+
fallbacks: {
|
|
937
|
+
"noto-naskh-arabic": ["serif"],
|
|
938
|
+
"scheherazade-new": ["serif"]
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
})
|
|
942
|
+
};
|
|
943
|
+
var hebrewCollection = {
|
|
944
|
+
...createDefinitionsFromGoogleFonts({
|
|
945
|
+
cssUrl: "https://fonts.googleapis.com/css2?family=Noto+Sans+Hebrew:wght@100..900&family=Frank+Ruhl+Libre:wght@300..900&family=Heebo:wght@100..800",
|
|
946
|
+
options: {
|
|
947
|
+
order: ["noto-sans-hebrew", "frank-ruhl-libre", "heebo"],
|
|
948
|
+
labels: {
|
|
949
|
+
"frank-ruhl-libre": "Frank R\xFChl Libre"
|
|
950
|
+
},
|
|
951
|
+
fallbacks: {
|
|
952
|
+
"frank-ruhl-libre": ["serif"]
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
})
|
|
956
|
+
};
|
|
957
|
+
var chineseSimplifiedCollection = {
|
|
958
|
+
"chinese-hans-sans": sysFontDef("chinese-hans-sans", "Sans-Serif", '"PingFang SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif'),
|
|
959
|
+
"chinese-hans-serif": sysFontDef("chinese-hans-serif", "Serif", '"STSong", "SimSun", "AR PL UMing CN", serif'),
|
|
960
|
+
...createDefinitionsFromGoogleFonts({
|
|
961
|
+
cssUrl: "https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@100..900&family=Noto+Serif+SC:wght@200..900",
|
|
962
|
+
options: {
|
|
963
|
+
labels: {
|
|
964
|
+
"noto-sans-sc": "Noto Sans SC",
|
|
965
|
+
"noto-serif-sc": "Noto Serif SC"
|
|
966
|
+
},
|
|
967
|
+
fallbacks: {
|
|
968
|
+
"noto-serif-sc": ["serif"]
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
})
|
|
972
|
+
};
|
|
973
|
+
var chineseTraditionalCollection = {
|
|
974
|
+
"chinese-hant-sans": sysFontDef("chinese-hant-sans", "Sans-Serif", '"PingFang TC", "Microsoft JhengHei", "WenQuanYi Micro Hei", sans-serif'),
|
|
975
|
+
"chinese-hant-serif": sysFontDef("chinese-hant-serif", "Serif", '"MingLiU", "AR PL UMing TW", serif'),
|
|
976
|
+
...createDefinitionsFromGoogleFonts({
|
|
977
|
+
cssUrl: "https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100..900&family=Noto+Serif+TC:wght@200..900",
|
|
978
|
+
options: {
|
|
979
|
+
labels: {
|
|
980
|
+
"noto-sans-tc": "Noto Sans TC",
|
|
981
|
+
"noto-serif-tc": "Noto Serif TC"
|
|
982
|
+
},
|
|
983
|
+
fallbacks: {
|
|
984
|
+
"noto-serif-tc": ["serif"]
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
})
|
|
988
|
+
};
|
|
989
|
+
var japaneseCollection = {
|
|
990
|
+
"japanese-sans": sysFontDef("japanese-sans", "Sans-Serif", '"Hiragino Kaku Gothic ProN", "Yu Gothic", "Meiryo", sans-serif'),
|
|
991
|
+
"japanese-serif": sysFontDef("japanese-serif", "Serif", '"Hiragino Mincho ProN", "Yu Mincho", "MS PMincho", serif'),
|
|
992
|
+
...createDefinitionsFromGoogleFonts({
|
|
993
|
+
cssUrl: "https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100..900&family=Noto+Serif+JP:wght@200..900",
|
|
994
|
+
options: {
|
|
995
|
+
labels: {
|
|
996
|
+
"noto-sans-jp": "Noto Sans JP",
|
|
997
|
+
"noto-serif-jp": "Noto Serif JP"
|
|
998
|
+
},
|
|
999
|
+
fallbacks: {
|
|
1000
|
+
"noto-serif-jp": ["serif"]
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
})
|
|
1004
|
+
};
|
|
1005
|
+
var japaneseVerticalCollection = {
|
|
1006
|
+
"japanese-v-serif": sysFontDef("japanese-v-serif", "Serif", '"Hiragino Mincho ProN", "Yu Mincho", "MS PMincho", serif'),
|
|
1007
|
+
"japanese-v-sans": sysFontDef("japanese-v-sans", "Sans-Serif", '"Hiragino Kaku Gothic ProN", "Yu Gothic", "Meiryo", sans-serif'),
|
|
1008
|
+
...createDefinitionsFromGoogleFonts({
|
|
1009
|
+
cssUrl: "https://fonts.googleapis.com/css2?family=Noto+Serif+JP:wght@200..900&family=Shippori+Mincho:wght@400..800&family=Noto+Sans+JP:wght@100..900",
|
|
1010
|
+
options: {
|
|
1011
|
+
order: ["noto-serif-jp", "shippori-mincho", "noto-sans-jp"],
|
|
1012
|
+
labels: {
|
|
1013
|
+
"noto-serif-jp": "Noto Serif JP",
|
|
1014
|
+
"noto-sans-jp": "Noto Sans JP"
|
|
1015
|
+
},
|
|
1016
|
+
fallbacks: {
|
|
1017
|
+
"noto-serif-jp": ["serif"],
|
|
1018
|
+
"shippori-mincho": ["serif"]
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
})
|
|
1022
|
+
};
|
|
1023
|
+
var koreanCollection = {
|
|
1024
|
+
"korean-sans": sysFontDef("korean-sans", "Sans-Serif", '"Apple SD Gothic Neo", "Malgun Gothic", "Dotum", sans-serif'),
|
|
1025
|
+
"korean-serif": sysFontDef("korean-serif", "Serif", '"Batang", "Gungsuh", "Apple Myungjo", serif'),
|
|
1026
|
+
...createDefinitionsFromGoogleFonts({
|
|
1027
|
+
cssUrl: "https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100..900&family=Noto+Serif+KR:wght@200..900&family=Nanum+Myeongjo:wght@400;700;800",
|
|
1028
|
+
options: {
|
|
1029
|
+
order: ["noto-sans-kr", "noto-serif-kr", "nanum-myeongjo"],
|
|
1030
|
+
labels: {
|
|
1031
|
+
"noto-sans-kr": "Noto Sans KR",
|
|
1032
|
+
"noto-serif-kr": "Noto Serif KR"
|
|
1033
|
+
},
|
|
1034
|
+
fallbacks: {
|
|
1035
|
+
"noto-serif-kr": ["serif"],
|
|
1036
|
+
"nanum-myeongjo": ["serif"]
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
})
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
// src/preferences/models/protection.ts
|
|
1043
|
+
var resolveContentProtectionConfig = (contentProtection, t) => {
|
|
1044
|
+
if (!contentProtection) return void 0;
|
|
1045
|
+
let resolvedWatermark;
|
|
1046
|
+
if (contentProtection.protectPrinting?.watermark) {
|
|
1047
|
+
if (typeof contentProtection.protectPrinting.watermark === "object" && "key" in contentProtection.protectPrinting.watermark) {
|
|
1048
|
+
resolvedWatermark = t(contentProtection.protectPrinting.watermark.key, {
|
|
1049
|
+
defaultValue: contentProtection.protectPrinting.watermark.fallback
|
|
1050
|
+
});
|
|
1051
|
+
} else if (typeof contentProtection.protectPrinting.watermark === "string") {
|
|
1052
|
+
resolvedWatermark = t(contentProtection.protectPrinting.watermark);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
const resolved = {
|
|
1056
|
+
protectCopy: contentProtection.protectCopy,
|
|
1057
|
+
disableContextMenu: contentProtection.disableContextMenu,
|
|
1058
|
+
disableDragAndDrop: contentProtection.disableDragAndDrop,
|
|
1059
|
+
protectPrinting: contentProtection.protectPrinting?.disable ? {
|
|
1060
|
+
disable: true,
|
|
1061
|
+
watermark: resolvedWatermark
|
|
1062
|
+
} : void 0,
|
|
1063
|
+
disableSelectAll: contentProtection.disableSelectAll,
|
|
1064
|
+
disableSave: contentProtection.disableSave,
|
|
1065
|
+
monitorDevTools: contentProtection.monitorDevTools
|
|
1066
|
+
// TODO: When we implement it in non-audio navigators, uncomment
|
|
1067
|
+
// disableRemotePlayback: contentProtection.disableRemotePlayback
|
|
1068
|
+
};
|
|
1069
|
+
return resolved;
|
|
1070
|
+
};
|
|
1071
|
+
var resolveAudioContentProtectionConfig = (contentProtection, t) => {
|
|
1072
|
+
if (!contentProtection) return void 0;
|
|
1073
|
+
let resolvedWatermark;
|
|
1074
|
+
if (contentProtection.protectPrinting?.watermark) {
|
|
1075
|
+
if (typeof contentProtection.protectPrinting.watermark === "object" && "key" in contentProtection.protectPrinting.watermark) {
|
|
1076
|
+
resolvedWatermark = t(contentProtection.protectPrinting.watermark.key, {
|
|
1077
|
+
defaultValue: contentProtection.protectPrinting.watermark.fallback
|
|
1078
|
+
});
|
|
1079
|
+
} else if (typeof contentProtection.protectPrinting.watermark === "string") {
|
|
1080
|
+
resolvedWatermark = t(contentProtection.protectPrinting.watermark);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return {
|
|
1084
|
+
protectCopy: contentProtection.protectCopy,
|
|
1085
|
+
disableContextMenu: contentProtection.disableContextMenu,
|
|
1086
|
+
disableDragAndDrop: contentProtection.disableDragAndDrop,
|
|
1087
|
+
protectPrinting: contentProtection.protectPrinting?.disable ? {
|
|
1088
|
+
disable: true,
|
|
1089
|
+
watermark: resolvedWatermark
|
|
1090
|
+
} : void 0,
|
|
1091
|
+
disableSelectAll: contentProtection.disableSelectAll,
|
|
1092
|
+
disableSave: contentProtection.disableSave,
|
|
1093
|
+
monitorDevTools: contentProtection.monitorDevTools,
|
|
1094
|
+
disableRemotePlayback: contentProtection.disableRemotePlayback
|
|
1095
|
+
};
|
|
1096
|
+
};
|
|
1097
|
+
var defaultContentProtectionConfig = {
|
|
1098
|
+
protectCopy: false,
|
|
1099
|
+
disableContextMenu: false,
|
|
1100
|
+
disableDragAndDrop: false,
|
|
1101
|
+
protectPrinting: {
|
|
1102
|
+
disable: false,
|
|
1103
|
+
watermark: "reader.app.printingDisabled"
|
|
1104
|
+
},
|
|
1105
|
+
disableSelectAll: false,
|
|
1106
|
+
disableSave: false,
|
|
1107
|
+
monitorDevTools: false
|
|
1108
|
+
};
|
|
1109
|
+
var defaultAudioContentProtectionConfig = {
|
|
1110
|
+
protectCopy: false,
|
|
1111
|
+
disableContextMenu: false,
|
|
1112
|
+
disableDragAndDrop: false,
|
|
1113
|
+
protectPrinting: {
|
|
1114
|
+
disable: false,
|
|
1115
|
+
watermark: "reader.app.printingDisabled"
|
|
1116
|
+
},
|
|
1117
|
+
disableSelectAll: false,
|
|
1118
|
+
disableSave: false,
|
|
1119
|
+
monitorDevTools: false,
|
|
1120
|
+
disableRemotePlayback: false
|
|
1121
|
+
};
|
|
1122
|
+
var devContentProtectionConfig = {
|
|
1123
|
+
protectCopy: false,
|
|
1124
|
+
disableContextMenu: false,
|
|
1125
|
+
disableDragAndDrop: false,
|
|
1126
|
+
protectPrinting: {
|
|
1127
|
+
disable: false
|
|
1128
|
+
},
|
|
1129
|
+
disableSelectAll: false,
|
|
1130
|
+
disableSave: false,
|
|
1131
|
+
monitorDevTools: false,
|
|
1132
|
+
disableRemotePlayback: false
|
|
1133
|
+
};
|
|
1134
|
+
var ThThemeKeys = /* @__PURE__ */ ((ThThemeKeys3) => {
|
|
1135
|
+
ThThemeKeys3["light"] = "light";
|
|
1136
|
+
ThThemeKeys3["sepia"] = "sepia";
|
|
1137
|
+
ThThemeKeys3["dark"] = "dark";
|
|
1138
|
+
ThThemeKeys3["paper"] = "paper";
|
|
1139
|
+
ThThemeKeys3["contrast1"] = "contrast1";
|
|
1140
|
+
ThThemeKeys3["contrast2"] = "contrast2";
|
|
1141
|
+
ThThemeKeys3["contrast3"] = "contrast3";
|
|
1142
|
+
return ThThemeKeys3;
|
|
1143
|
+
})(ThThemeKeys || {});
|
|
1144
|
+
var lightTheme = {
|
|
1145
|
+
background: ReadiumCSSColors.RS__backgroundColor,
|
|
1146
|
+
// Color of background
|
|
1147
|
+
text: ReadiumCSSColors.RS__textColor,
|
|
1148
|
+
// Color of text
|
|
1149
|
+
link: "#0000ee",
|
|
1150
|
+
// Color of links
|
|
1151
|
+
visited: "#551a8b",
|
|
1152
|
+
// Color of visited links
|
|
1153
|
+
subdue: "#808080",
|
|
1154
|
+
// Color of subdued elements
|
|
1155
|
+
disable: "#808080",
|
|
1156
|
+
// color for :disabled
|
|
1157
|
+
hover: "#d9d9d9",
|
|
1158
|
+
// color of background for :hover
|
|
1159
|
+
onHover: ReadiumCSSColors.RS__textColor,
|
|
1160
|
+
// color of text for :hover
|
|
1161
|
+
select: "#b4d8fe",
|
|
1162
|
+
// color of selected background
|
|
1163
|
+
onSelect: "inherit",
|
|
1164
|
+
// color of selected text
|
|
1165
|
+
focus: "#0067f4",
|
|
1166
|
+
// color of :focus-visible
|
|
1167
|
+
elevate: "0px 0px 2px #808080",
|
|
1168
|
+
// drop shadow of containers
|
|
1169
|
+
immerse: "0.6"
|
|
1170
|
+
// opacity of immersive mode
|
|
1171
|
+
};
|
|
1172
|
+
var darkTheme = {
|
|
1173
|
+
background: "#000000",
|
|
1174
|
+
text: "#FEFEFE",
|
|
1175
|
+
link: "#63caff",
|
|
1176
|
+
visited: "#0099E5",
|
|
1177
|
+
subdue: "#808080",
|
|
1178
|
+
disable: "#808080",
|
|
1179
|
+
hover: "#404040",
|
|
1180
|
+
onHover: "#FEFEFE",
|
|
1181
|
+
select: "#b4d8fe",
|
|
1182
|
+
onSelect: "inherit",
|
|
1183
|
+
focus: "#0067f4",
|
|
1184
|
+
elevate: "0px 0px 2px #808080",
|
|
1185
|
+
immerse: "0.4"
|
|
1186
|
+
};
|
|
1187
|
+
var paperTheme = {
|
|
1188
|
+
background: "#faf4e8",
|
|
1189
|
+
text: "#121212",
|
|
1190
|
+
link: "#0000EE",
|
|
1191
|
+
visited: "#551A8B",
|
|
1192
|
+
subdue: "#8c8c8c",
|
|
1193
|
+
disable: "#8c8c8c",
|
|
1194
|
+
hover: "#edd7ab",
|
|
1195
|
+
onHover: "#121212",
|
|
1196
|
+
select: "#b4d8fe",
|
|
1197
|
+
onSelect: "inherit",
|
|
1198
|
+
focus: "#0067f4",
|
|
1199
|
+
elevate: "0px 0px 2px #8c8c8c",
|
|
1200
|
+
immerse: "0.5"
|
|
1201
|
+
};
|
|
1202
|
+
var sepiaTheme = {
|
|
1203
|
+
background: "#e9ddc8",
|
|
1204
|
+
text: "#000000",
|
|
1205
|
+
link: "#0000EE",
|
|
1206
|
+
visited: "#551A8B",
|
|
1207
|
+
subdue: "#8c8c8c",
|
|
1208
|
+
disable: "#8c8c8c",
|
|
1209
|
+
hover: "#ccb07f",
|
|
1210
|
+
onHover: "#000000",
|
|
1211
|
+
select: "#b4d8fe",
|
|
1212
|
+
onSelect: "inherit",
|
|
1213
|
+
focus: "#004099",
|
|
1214
|
+
elevate: "0px 0px 2px #8c8c8c",
|
|
1215
|
+
immerse: "0.45"
|
|
1216
|
+
};
|
|
1217
|
+
var contrast1Theme = {
|
|
1218
|
+
background: "#000000",
|
|
1219
|
+
text: "#ffff00",
|
|
1220
|
+
link: "#63caff",
|
|
1221
|
+
visited: "#0099E5",
|
|
1222
|
+
subdue: "#808000",
|
|
1223
|
+
disable: "#808000",
|
|
1224
|
+
hover: "#404040",
|
|
1225
|
+
onHover: "#ffff00",
|
|
1226
|
+
select: "#b4d8fe",
|
|
1227
|
+
onSelect: "inherit",
|
|
1228
|
+
focus: "#0067f4",
|
|
1229
|
+
elevate: "0px 0px 2px #808000",
|
|
1230
|
+
immerse: "0.4"
|
|
1231
|
+
};
|
|
1232
|
+
var contrast2Theme = {
|
|
1233
|
+
background: "#181842",
|
|
1234
|
+
text: "#ffffff",
|
|
1235
|
+
link: "#adcfff",
|
|
1236
|
+
visited: "#7ab2ff",
|
|
1237
|
+
subdue: "#808080",
|
|
1238
|
+
disable: "#808080",
|
|
1239
|
+
hover: "#4444bb",
|
|
1240
|
+
onHover: "#ffffff",
|
|
1241
|
+
select: "#b4d8fe",
|
|
1242
|
+
onSelect: "inherit",
|
|
1243
|
+
focus: "#6BA9FF",
|
|
1244
|
+
elevate: "0px 0px 2px #808080",
|
|
1245
|
+
immerse: "0.4"
|
|
1246
|
+
};
|
|
1247
|
+
var contrast3Theme = {
|
|
1248
|
+
background: "#c5e7cd",
|
|
1249
|
+
text: "#000000",
|
|
1250
|
+
link: "#0000EE",
|
|
1251
|
+
visited: "#551A8B",
|
|
1252
|
+
subdue: "#8c8c8c",
|
|
1253
|
+
disable: "#8c8c8c",
|
|
1254
|
+
hover: "#6fc383",
|
|
1255
|
+
onHover: "#000000",
|
|
1256
|
+
select: "#b4d8fe",
|
|
1257
|
+
onSelect: "inherit",
|
|
1258
|
+
focus: "#004099",
|
|
1259
|
+
elevate: "0px 0px 2px #8c8c8c",
|
|
1260
|
+
immerse: "0.45"
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
// src/preferences/audioPreferences.ts
|
|
1264
|
+
var ThAudioAffordance = /* @__PURE__ */ ((ThAudioAffordance2) => {
|
|
1265
|
+
ThAudioAffordance2["timeline"] = "timeline";
|
|
1266
|
+
ThAudioAffordance2["readingOrder"] = "readingOrder";
|
|
1267
|
+
ThAudioAffordance2["toc"] = "toc";
|
|
1268
|
+
return ThAudioAffordance2;
|
|
1269
|
+
})(ThAudioAffordance || {});
|
|
1270
|
+
var validateRangePresets = (pref, context) => {
|
|
1271
|
+
if (pref.variant !== "sliderWithPresets" /* sliderWithPresets */ || !pref.presets?.length) return;
|
|
1272
|
+
const [min, max] = [Math.min(...pref.range), Math.max(...pref.range)];
|
|
1273
|
+
const step = pref.step;
|
|
1274
|
+
const tolerance = step * 1e-9;
|
|
1275
|
+
const invalid = pref.presets.filter((p) => {
|
|
1276
|
+
if (p < min || p > max) return true;
|
|
1277
|
+
const offset = (p - min) / step;
|
|
1278
|
+
return Math.abs(offset - Math.round(offset)) > tolerance;
|
|
1279
|
+
});
|
|
1280
|
+
if (invalid.length > 0) {
|
|
1281
|
+
console.warn(
|
|
1282
|
+
`${context}: presets [${invalid.join(", ")}] are not reachable with range=[${min}, ${max}] and step=${step}.`
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
var createAudioPreferences = (params) => {
|
|
1287
|
+
if (params.actions?.secondary) {
|
|
1288
|
+
validateObjectKeys(
|
|
1289
|
+
[params.actions.secondary.displayOrder],
|
|
1290
|
+
params.actions.secondary.keys,
|
|
1291
|
+
"actions.secondary"
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
if (params.settings?.order) {
|
|
1295
|
+
const order = params.settings.order;
|
|
1296
|
+
const hasSkipInterval = order.includes("skipInterval" /* skipInterval */);
|
|
1297
|
+
const hasSplitIntervals = order.includes("skipBackwardInterval" /* skipBackwardInterval */) || order.includes("skipForwardInterval" /* skipForwardInterval */);
|
|
1298
|
+
if (hasSkipInterval && hasSplitIntervals) {
|
|
1299
|
+
console.warn(
|
|
1300
|
+
`settings.order contains both "${"skipInterval" /* skipInterval */}" and split interval keys. Use one or the other.`
|
|
1301
|
+
);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
if (params.theming?.themes) {
|
|
1305
|
+
validateObjectKeys(
|
|
1306
|
+
[params.theming.themes.audioOrder],
|
|
1307
|
+
params.theming.themes.keys,
|
|
1308
|
+
"theming.themes",
|
|
1309
|
+
"auto"
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
if (params.theming?.layout?.publicationMetadata?.order) {
|
|
1313
|
+
const order = params.theming.layout.publicationMetadata.order;
|
|
1314
|
+
const titleVariants = [
|
|
1315
|
+
"title" /* title */,
|
|
1316
|
+
"titleWithSubtitle" /* titleWithSubtitle */,
|
|
1317
|
+
"subtitleWithTitle" /* subtitleWithTitle */
|
|
1318
|
+
];
|
|
1319
|
+
const titleVariantsInOrder = order.filter((c) => titleVariants.includes(c));
|
|
1320
|
+
if (titleVariantsInOrder.length > 1) {
|
|
1321
|
+
console.warn(
|
|
1322
|
+
`publicationMetadata.order contains multiple title variants [${titleVariantsInOrder.join(", ")}]. Using first one only.`
|
|
1323
|
+
);
|
|
1324
|
+
const firstTitleIndex = order.findIndex((c) => titleVariants.includes(c));
|
|
1325
|
+
params.theming.layout.publicationMetadata.order = order.filter((component, index) => {
|
|
1326
|
+
if (component === "authors" /* authors */) return true;
|
|
1327
|
+
return index === firstTitleIndex;
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
Object.entries(params.settings?.keys ?? {}).forEach(([key, pref]) => {
|
|
1332
|
+
if (pref && typeof pref === "object" && "variant" in pref) {
|
|
1333
|
+
validateRangePresets(pref, `settings.keys.${key}`);
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1336
|
+
return params;
|
|
1337
|
+
};
|
|
1338
|
+
|
|
1339
|
+
// src/preferences/defaultAudioPreferences.ts
|
|
1340
|
+
var defaultAudioPreferences = createAudioPreferences({
|
|
1341
|
+
theming: {
|
|
1342
|
+
header: {
|
|
1343
|
+
backLink: {
|
|
1344
|
+
variant: "arrow" /* arrow */,
|
|
1345
|
+
visibility: "partially",
|
|
1346
|
+
href: "/"
|
|
1347
|
+
}
|
|
1348
|
+
},
|
|
1349
|
+
icon: {
|
|
1350
|
+
size: 24,
|
|
1351
|
+
tooltipOffset: 10
|
|
1352
|
+
},
|
|
1353
|
+
layout: {
|
|
1354
|
+
compact: {
|
|
1355
|
+
order: [
|
|
1356
|
+
"cover" /* cover */,
|
|
1357
|
+
"metadata" /* metadata */,
|
|
1358
|
+
"playbackControls" /* playbackControls */,
|
|
1359
|
+
"progressBar" /* progressBar */,
|
|
1360
|
+
"mediaActions" /* mediaActions */
|
|
1361
|
+
]
|
|
1362
|
+
},
|
|
1363
|
+
expanded: {
|
|
1364
|
+
start: [
|
|
1365
|
+
"cover" /* cover */,
|
|
1366
|
+
"metadata" /* metadata */
|
|
1367
|
+
],
|
|
1368
|
+
end: [
|
|
1369
|
+
"playbackControls" /* playbackControls */,
|
|
1370
|
+
"progressBar" /* progressBar */,
|
|
1371
|
+
"mediaActions" /* mediaActions */
|
|
1372
|
+
]
|
|
1373
|
+
},
|
|
1374
|
+
publicationMetadata: {
|
|
1375
|
+
order: [
|
|
1376
|
+
"titleWithSubtitle" /* titleWithSubtitle */
|
|
1377
|
+
]
|
|
1378
|
+
},
|
|
1379
|
+
radius: 5,
|
|
1380
|
+
spacing: 20,
|
|
1381
|
+
progressBar: {
|
|
1382
|
+
variant: "segmented" /* segmented */
|
|
1383
|
+
},
|
|
1384
|
+
defaults: {
|
|
1385
|
+
dockingWidth: 340,
|
|
1386
|
+
scrim: "rgba(0, 0, 0, 0.2)"
|
|
1387
|
+
},
|
|
1388
|
+
constraints: {
|
|
1389
|
+
["bottomSheet" /* bottomSheet */]: 600,
|
|
1390
|
+
["popover" /* popover */]: 600,
|
|
1391
|
+
["modal" /* modal */]: 600,
|
|
1392
|
+
cover: 300
|
|
1393
|
+
}
|
|
1394
|
+
},
|
|
1395
|
+
breakpoints: {
|
|
1396
|
+
["compact" /* compact */]: 600,
|
|
1397
|
+
["medium" /* medium */]: 840,
|
|
1398
|
+
["expanded" /* expanded */]: 1200,
|
|
1399
|
+
["large" /* large */]: 1600,
|
|
1400
|
+
["xLarge" /* xLarge */]: null
|
|
1401
|
+
},
|
|
1402
|
+
themes: {
|
|
1403
|
+
audioOrder: [
|
|
1404
|
+
"auto",
|
|
1405
|
+
"light" /* light */,
|
|
1406
|
+
"dark" /* dark */
|
|
1407
|
+
],
|
|
1408
|
+
systemThemes: {
|
|
1409
|
+
light: "light" /* light */,
|
|
1410
|
+
dark: "dark" /* dark */
|
|
1411
|
+
},
|
|
1412
|
+
keys: {
|
|
1413
|
+
["light" /* light */]: lightTheme,
|
|
1414
|
+
["dark" /* dark */]: darkTheme
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
},
|
|
1418
|
+
actions: {
|
|
1419
|
+
primary: {
|
|
1420
|
+
displayOrder: [
|
|
1421
|
+
"audio.volume" /* volume */,
|
|
1422
|
+
"audio.playbackRate" /* playbackRate */,
|
|
1423
|
+
"audio.toc" /* toc */,
|
|
1424
|
+
"audio.sleepTimer" /* sleepTimer */
|
|
1425
|
+
],
|
|
1426
|
+
keys: {
|
|
1427
|
+
["audio.volume" /* volume */]: defaultAudioVolumeAction,
|
|
1428
|
+
["audio.playbackRate" /* playbackRate */]: defaultAudioPlaybackRateAction,
|
|
1429
|
+
["audio.toc" /* toc */]: defaultAudioTocAction,
|
|
1430
|
+
["audio.sleepTimer" /* sleepTimer */]: defaultAudioSleepTimerAction
|
|
1431
|
+
}
|
|
1432
|
+
},
|
|
1433
|
+
secondary: {
|
|
1434
|
+
displayOrder: [
|
|
1435
|
+
"audio.remotePlayback" /* remotePlayback */,
|
|
1436
|
+
"settings" /* settings */
|
|
1437
|
+
],
|
|
1438
|
+
collapse: {
|
|
1439
|
+
["compact" /* compact */]: 2,
|
|
1440
|
+
["medium" /* medium */]: 3
|
|
1441
|
+
},
|
|
1442
|
+
keys: {
|
|
1443
|
+
["audio.remotePlayback" /* remotePlayback */]: defaultAudioRemotePlaybackAction,
|
|
1444
|
+
["settings" /* settings */]: defaultSettingsAction
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
settings: {
|
|
1449
|
+
order: [
|
|
1450
|
+
"theme" /* theme */,
|
|
1451
|
+
"skipBackwardInterval" /* skipBackwardInterval */,
|
|
1452
|
+
"skipForwardInterval" /* skipForwardInterval */,
|
|
1453
|
+
"autoPlay" /* autoPlay */
|
|
1454
|
+
],
|
|
1455
|
+
keys: {
|
|
1456
|
+
["volume" /* volume */]: defaultAudioVolume,
|
|
1457
|
+
["playbackRate" /* playbackRate */]: defaultAudioPlaybackRate,
|
|
1458
|
+
["skipBackwardInterval" /* skipBackwardInterval */]: defaultAudioSkipBackwardInterval,
|
|
1459
|
+
["skipForwardInterval" /* skipForwardInterval */]: defaultAudioSkipForwardInterval,
|
|
1460
|
+
["sleepTimer" /* sleepTimer */]: defaultAudioSleepTimer
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
contentProtection: defaultAudioContentProtectionConfig,
|
|
1464
|
+
affordances: {
|
|
1465
|
+
previous: "toc" /* toc */,
|
|
1466
|
+
next: "toc" /* toc */
|
|
1467
|
+
},
|
|
1468
|
+
shortcuts: {
|
|
1469
|
+
representation: "symbol" /* symbol */,
|
|
1470
|
+
joiner: "+"
|
|
1471
|
+
},
|
|
1472
|
+
docking: {
|
|
1473
|
+
displayOrder: [
|
|
1474
|
+
"dockingTransient" /* transient */,
|
|
1475
|
+
"dockingStart" /* start */,
|
|
1476
|
+
"dockingEnd" /* end */
|
|
1477
|
+
],
|
|
1478
|
+
// Only toc is dockable; others have dockable:none so dock panels are TOC-only
|
|
1479
|
+
// Matches EPUB config: no docking on compact/medium (mobile/tablet portrait)
|
|
1480
|
+
dock: {
|
|
1481
|
+
["compact" /* compact */]: "none" /* none */,
|
|
1482
|
+
["medium" /* medium */]: "none" /* none */,
|
|
1483
|
+
["expanded" /* expanded */]: "start" /* start */,
|
|
1484
|
+
["large" /* large */]: "both" /* both */,
|
|
1485
|
+
["xLarge" /* xLarge */]: "both" /* both */
|
|
1486
|
+
},
|
|
1487
|
+
collapse: true,
|
|
1488
|
+
keys: {
|
|
1489
|
+
["dockingStart" /* start */]: { visibility: "overflow" /* overflow */, shortcut: null },
|
|
1490
|
+
["dockingEnd" /* end */]: { visibility: "overflow" /* overflow */, shortcut: null },
|
|
1491
|
+
["dockingTransient" /* transient */]: { visibility: "overflow" /* overflow */, shortcut: null }
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
});
|
|
1495
|
+
var ThAudioPreferencesContext = createContext(null);
|
|
1496
|
+
var defaultAudioPreferencesContextValue = {
|
|
1497
|
+
preferences: defaultAudioPreferences,
|
|
1498
|
+
updatePreferences: () => {
|
|
1499
|
+
throw new Error("updatePreferences must be used within a ThAudioPreferencesProvider");
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
|
|
1503
|
+
// src/preferences/adapters/ThAudioMemoryPreferencesAdapter.ts
|
|
1504
|
+
var ThAudioMemoryPreferencesAdapter = class {
|
|
1505
|
+
currentPreferences;
|
|
1506
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1507
|
+
constructor(initialPreferences) {
|
|
1508
|
+
this.currentPreferences = { ...initialPreferences };
|
|
1509
|
+
}
|
|
1510
|
+
getPreferences() {
|
|
1511
|
+
return { ...this.currentPreferences };
|
|
1512
|
+
}
|
|
1513
|
+
setPreferences(prefs) {
|
|
1514
|
+
this.currentPreferences = { ...prefs };
|
|
1515
|
+
this.notifyListeners(this.currentPreferences);
|
|
1516
|
+
}
|
|
1517
|
+
subscribe(listener) {
|
|
1518
|
+
this.listeners.add(listener);
|
|
1519
|
+
}
|
|
1520
|
+
unsubscribe(listener) {
|
|
1521
|
+
this.listeners.delete(listener);
|
|
1522
|
+
}
|
|
1523
|
+
notifyListeners(prefs) {
|
|
1524
|
+
this.listeners.forEach((listener) => listener({ ...prefs }));
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
function ThAudioPreferencesProvider({
|
|
1528
|
+
adapter,
|
|
1529
|
+
initialPreferences,
|
|
1530
|
+
devMode,
|
|
1531
|
+
children
|
|
1532
|
+
}) {
|
|
1533
|
+
const effectiveAdapter = useMemo(() => {
|
|
1534
|
+
let fallback = defaultAudioPreferencesContextValue.preferences;
|
|
1535
|
+
if (devMode && !initialPreferences) {
|
|
1536
|
+
fallback = { ...fallback, contentProtection: devContentProtectionConfig };
|
|
1537
|
+
}
|
|
1538
|
+
return adapter || new ThAudioMemoryPreferencesAdapter(
|
|
1539
|
+
initialPreferences || fallback
|
|
1540
|
+
);
|
|
1541
|
+
}, [adapter, initialPreferences, devMode]);
|
|
1542
|
+
const [preferences, setPreferences] = useState(
|
|
1543
|
+
(() => {
|
|
1544
|
+
let fallback = defaultAudioPreferencesContextValue.preferences;
|
|
1545
|
+
if (devMode && !initialPreferences) {
|
|
1546
|
+
fallback = { ...fallback, contentProtection: devContentProtectionConfig };
|
|
1547
|
+
}
|
|
1548
|
+
return initialPreferences || fallback;
|
|
1549
|
+
})()
|
|
1550
|
+
);
|
|
1551
|
+
const handlePreferenceChange = useCallback((newPrefs) => {
|
|
1552
|
+
setPreferences(
|
|
1553
|
+
(prev) => JSON.stringify(prev) === JSON.stringify(newPrefs) ? prev : newPrefs
|
|
1554
|
+
);
|
|
1555
|
+
}, []);
|
|
1556
|
+
useEffect(() => {
|
|
1557
|
+
effectiveAdapter.subscribe(handlePreferenceChange);
|
|
1558
|
+
return () => {
|
|
1559
|
+
effectiveAdapter.unsubscribe(handlePreferenceChange);
|
|
1560
|
+
};
|
|
1561
|
+
}, [effectiveAdapter, handlePreferenceChange]);
|
|
1562
|
+
const contextValue = useMemo(() => ({
|
|
1563
|
+
preferences,
|
|
1564
|
+
updatePreferences: (newPrefs) => {
|
|
1565
|
+
effectiveAdapter.setPreferences(newPrefs);
|
|
1566
|
+
}
|
|
1567
|
+
}), [preferences, effectiveAdapter]);
|
|
1568
|
+
return /* @__PURE__ */ jsx(ThAudioPreferencesContext.Provider, { value: contextValue, children });
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// src/preferences/preferences.ts
|
|
1572
|
+
var createPreferences = (params) => {
|
|
1573
|
+
if (params.actions) {
|
|
1574
|
+
validateObjectKeys(
|
|
1575
|
+
[
|
|
1576
|
+
params.actions.reflowOrder,
|
|
1577
|
+
params.actions.fxlOrder,
|
|
1578
|
+
params.actions.webPubOrder
|
|
1579
|
+
],
|
|
1580
|
+
params.actions.keys,
|
|
1581
|
+
"actions"
|
|
1582
|
+
);
|
|
1583
|
+
}
|
|
1584
|
+
if (params.theming?.themes) {
|
|
1585
|
+
validateObjectKeys(
|
|
1586
|
+
[params.theming.themes.reflowOrder, params.theming.themes.fxlOrder],
|
|
1587
|
+
params.theming.themes.keys,
|
|
1588
|
+
"theming.themes",
|
|
1589
|
+
"auto"
|
|
1590
|
+
// Special case for themes
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
if (params.settings.spacing?.presets) {
|
|
1594
|
+
validateObjectKeys(
|
|
1595
|
+
[params.settings.spacing.presets.reflowOrder],
|
|
1596
|
+
params.settings.spacing.presets.keys,
|
|
1597
|
+
"settings.spacing.presets",
|
|
1598
|
+
["publisher", "custom"]
|
|
1599
|
+
);
|
|
1600
|
+
}
|
|
1601
|
+
if (params.settings?.spacing?.presets?.keys && params.settings?.keys) {
|
|
1602
|
+
const spacingSettings = params.settings.spacing.presets.keys;
|
|
1603
|
+
const spacingThemes = params.settings.spacing.presets.keys;
|
|
1604
|
+
const adjustSpacingValue = (key, value, context) => {
|
|
1605
|
+
const settingKey = Object.values(ThSettingsKeys).find((k) => k === key);
|
|
1606
|
+
if (!settingKey) {
|
|
1607
|
+
return value;
|
|
1608
|
+
}
|
|
1609
|
+
const setting = spacingSettings[settingKey];
|
|
1610
|
+
if (!setting) {
|
|
1611
|
+
return value;
|
|
1612
|
+
}
|
|
1613
|
+
let range;
|
|
1614
|
+
let step;
|
|
1615
|
+
if (setting && typeof setting === "object" && "range" in setting) {
|
|
1616
|
+
range = setting.range;
|
|
1617
|
+
step = setting.step;
|
|
1618
|
+
} else if (setting && typeof setting === "object") {
|
|
1619
|
+
return value;
|
|
1620
|
+
}
|
|
1621
|
+
let adjustedValue = value;
|
|
1622
|
+
if (range) {
|
|
1623
|
+
const [min, max] = range;
|
|
1624
|
+
if (adjustedValue < min) {
|
|
1625
|
+
console.warn(`Adjusting value ${value} for ${context.join(".")} to minimum allowed value ${min}`);
|
|
1626
|
+
adjustedValue = min;
|
|
1627
|
+
} else if (adjustedValue > max) {
|
|
1628
|
+
console.warn(`Adjusting value ${value} for ${context.join(".")} to maximum allowed value ${max}`);
|
|
1629
|
+
adjustedValue = max;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
if (step && range) {
|
|
1633
|
+
const [min] = range;
|
|
1634
|
+
const steps = Math.round((adjustedValue - min) / step);
|
|
1635
|
+
const steppedValue = parseFloat((min + steps * step).toFixed(10));
|
|
1636
|
+
const finalValue = Math.min(Math.max(steppedValue, range[0]), range[1]);
|
|
1637
|
+
if (Math.abs(finalValue - adjustedValue) > Number.EPSILON) {
|
|
1638
|
+
console.warn(`Adjusting value ${value} for ${context.join(".")} to nearest step value ${finalValue}`);
|
|
1639
|
+
adjustedValue = finalValue;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
return adjustedValue;
|
|
1643
|
+
};
|
|
1644
|
+
for (const [themeName, spacingTheme] of Object.entries(spacingThemes)) {
|
|
1645
|
+
if (spacingTheme && typeof spacingTheme === "object") {
|
|
1646
|
+
const adjustedTheme = {};
|
|
1647
|
+
let hasAdjustedValues = false;
|
|
1648
|
+
for (const [key, value] of Object.entries(spacingTheme)) {
|
|
1649
|
+
if (typeof value === "number") {
|
|
1650
|
+
const context = ["theming", "spacing", "keys", themeName, key];
|
|
1651
|
+
const adjustedValue = adjustSpacingValue(key, value, context);
|
|
1652
|
+
adjustedTheme[key] = adjustedValue;
|
|
1653
|
+
if (adjustedValue !== value) {
|
|
1654
|
+
hasAdjustedValues = true;
|
|
1655
|
+
}
|
|
1656
|
+
} else {
|
|
1657
|
+
adjustedTheme[key] = value;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
if (hasAdjustedValues) {
|
|
1661
|
+
spacingThemes[themeName] = adjustedTheme;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
if (params.settings?.keys?.fontFamily) {
|
|
1667
|
+
const fontFamilyPref = params.settings.keys.fontFamily;
|
|
1668
|
+
const languageMap = /* @__PURE__ */ new Map();
|
|
1669
|
+
Object.entries(fontFamilyPref).forEach(([collectionName, collectionData]) => {
|
|
1670
|
+
if (collectionName === "default") return;
|
|
1671
|
+
const supportedLangs = "supportedLanguages" in collectionData ? collectionData.supportedLanguages : null;
|
|
1672
|
+
if (supportedLangs && Array.isArray(supportedLangs)) {
|
|
1673
|
+
supportedLangs.forEach((lang) => {
|
|
1674
|
+
if (!languageMap.has(lang)) {
|
|
1675
|
+
languageMap.set(lang, []);
|
|
1676
|
+
}
|
|
1677
|
+
languageMap.get(lang).push(collectionName);
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
languageMap.forEach((collections, language) => {
|
|
1682
|
+
if (collections.length > 1) {
|
|
1683
|
+
console.warn(`Language "${language}" is supported by multiple font collections: ${collections.join(", ")}. This may cause ambiguous font selection. Consider consolidating to a single collection per language.`);
|
|
1684
|
+
}
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
const validateRangePresets2 = (pref, context) => {
|
|
1688
|
+
if (pref.variant !== "sliderWithPresets" /* sliderWithPresets */ || !pref.presets?.length) return;
|
|
1689
|
+
const [min, max] = [Math.min(...pref.range), Math.max(...pref.range)];
|
|
1690
|
+
const step = pref.step;
|
|
1691
|
+
const tolerance = step * 1e-9;
|
|
1692
|
+
const invalid = pref.presets.filter((p) => {
|
|
1693
|
+
if (p < min || p > max) return true;
|
|
1694
|
+
const offset = (p - min) / step;
|
|
1695
|
+
return Math.abs(offset - Math.round(offset)) > tolerance;
|
|
1696
|
+
});
|
|
1697
|
+
if (invalid.length > 0) {
|
|
1698
|
+
console.warn(`${context}: presets [${invalid.join(", ")}] are not reachable with range=[${min}, ${max}] and step=${step}.`);
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
Object.entries(params.settings?.keys ?? {}).forEach(([key, pref]) => {
|
|
1702
|
+
if (pref && typeof pref === "object" && "variant" in pref) {
|
|
1703
|
+
validateRangePresets2(pref, `settings.keys.${key}`);
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
return params;
|
|
1707
|
+
};
|
|
1708
|
+
|
|
1709
|
+
// src/preferences/defaultPreferences.ts
|
|
1710
|
+
var defaultPreferences = createPreferences({
|
|
1711
|
+
// direction: ThLayoutDirection.ltr,
|
|
1712
|
+
// locale: "en",
|
|
1713
|
+
experiments: {
|
|
1714
|
+
reflow: ["experimentalHeaderFiltering", "experimentalZoom"],
|
|
1715
|
+
webPub: ["experimentalHeaderFiltering", "experimentalZoom"]
|
|
1716
|
+
},
|
|
1717
|
+
metadata: {
|
|
1718
|
+
documentTitle: {
|
|
1719
|
+
format: "title" /* title */
|
|
1720
|
+
}
|
|
1721
|
+
},
|
|
1722
|
+
typography: {
|
|
1723
|
+
minimalLineLength: 40,
|
|
1724
|
+
// undefined | null | number of characters. If 2 cols will switch to 1 based on this
|
|
1725
|
+
optimalLineLength: 55,
|
|
1726
|
+
// number of characters. If auto layout, picks colCount based on this
|
|
1727
|
+
maximalLineLength: 70,
|
|
1728
|
+
// undefined | null | number of characters.
|
|
1729
|
+
pageGutter: 20
|
|
1730
|
+
},
|
|
1731
|
+
theming: {
|
|
1732
|
+
header: {
|
|
1733
|
+
backLink: {
|
|
1734
|
+
variant: "arrow" /* arrow */,
|
|
1735
|
+
visibility: "partially",
|
|
1736
|
+
href: "/"
|
|
1737
|
+
},
|
|
1738
|
+
runningHead: {
|
|
1739
|
+
format: {
|
|
1740
|
+
reflow: {
|
|
1741
|
+
default: {
|
|
1742
|
+
variants: "chapter" /* chapter */,
|
|
1743
|
+
displayInImmersive: true,
|
|
1744
|
+
displayInFullscreen: false
|
|
1745
|
+
},
|
|
1746
|
+
breakpoints: {
|
|
1747
|
+
["compact" /* compact */]: {
|
|
1748
|
+
variants: "chapter" /* chapter */,
|
|
1749
|
+
displayInImmersive: false,
|
|
1750
|
+
displayInFullscreen: false
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
},
|
|
1754
|
+
fxl: {
|
|
1755
|
+
default: {
|
|
1756
|
+
variants: "title" /* title */,
|
|
1757
|
+
displayInImmersive: true,
|
|
1758
|
+
displayInFullscreen: true
|
|
1759
|
+
},
|
|
1760
|
+
breakpoints: {
|
|
1761
|
+
["compact" /* compact */]: {
|
|
1762
|
+
variants: "title" /* title */,
|
|
1763
|
+
displayInImmersive: false,
|
|
1764
|
+
displayInFullscreen: true
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
},
|
|
1768
|
+
webPub: {
|
|
1769
|
+
default: {
|
|
1770
|
+
variants: "chapter" /* chapter */,
|
|
1771
|
+
displayInImmersive: true,
|
|
1772
|
+
displayInFullscreen: true
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
},
|
|
1778
|
+
progression: {
|
|
1779
|
+
format: {
|
|
1780
|
+
reflow: {
|
|
1781
|
+
default: {
|
|
1782
|
+
variants: [
|
|
1783
|
+
"positionsPercentOfTotal" /* positionsPercentOfTotal */,
|
|
1784
|
+
"progressionOfResource" /* progressionOfResource */
|
|
1785
|
+
],
|
|
1786
|
+
displayInImmersive: true,
|
|
1787
|
+
displayInFullscreen: false
|
|
1788
|
+
},
|
|
1789
|
+
breakpoints: {
|
|
1790
|
+
["compact" /* compact */]: {
|
|
1791
|
+
variants: [
|
|
1792
|
+
"positionsOfTotal" /* positionsOfTotal */,
|
|
1793
|
+
"resourceProgression" /* resourceProgression */
|
|
1794
|
+
],
|
|
1795
|
+
displayInImmersive: false,
|
|
1796
|
+
displayInFullscreen: false
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
},
|
|
1800
|
+
fxl: {
|
|
1801
|
+
default: {
|
|
1802
|
+
variants: [
|
|
1803
|
+
"positionsOfTotal" /* positionsOfTotal */,
|
|
1804
|
+
"overallProgression" /* overallProgression */,
|
|
1805
|
+
"none" /* none */
|
|
1806
|
+
],
|
|
1807
|
+
displayInImmersive: true,
|
|
1808
|
+
displayInFullscreen: true
|
|
1809
|
+
},
|
|
1810
|
+
breakpoints: {
|
|
1811
|
+
["compact" /* compact */]: {
|
|
1812
|
+
variants: [
|
|
1813
|
+
"positions" /* positions */,
|
|
1814
|
+
"overallProgression" /* overallProgression */,
|
|
1815
|
+
"none" /* none */
|
|
1816
|
+
],
|
|
1817
|
+
displayInImmersive: false,
|
|
1818
|
+
displayInFullscreen: true
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
},
|
|
1822
|
+
webPub: {
|
|
1823
|
+
default: {
|
|
1824
|
+
variants: [
|
|
1825
|
+
"readingOrderIndex" /* readingOrderIndex */,
|
|
1826
|
+
"none" /* none */
|
|
1827
|
+
],
|
|
1828
|
+
displayInImmersive: true,
|
|
1829
|
+
displayInFullscreen: true
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
},
|
|
1834
|
+
arrow: {
|
|
1835
|
+
size: 40,
|
|
1836
|
+
// Size of the left and right arrows in px
|
|
1837
|
+
offset: 5
|
|
1838
|
+
// offset of the arrows from the edges in px
|
|
1839
|
+
},
|
|
1840
|
+
icon: {
|
|
1841
|
+
size: 24,
|
|
1842
|
+
// Size of icons in px
|
|
1843
|
+
tooltipOffset: 10
|
|
1844
|
+
// offset of tooltip in px
|
|
1845
|
+
},
|
|
1846
|
+
layout: {
|
|
1847
|
+
ui: {
|
|
1848
|
+
reflow: "layered-ui" /* layered */,
|
|
1849
|
+
fxl: "layered-ui" /* layered */,
|
|
1850
|
+
webPub: "stacked-ui" /* stacked */
|
|
1851
|
+
},
|
|
1852
|
+
radius: 5,
|
|
1853
|
+
// border-radius of containers
|
|
1854
|
+
spacing: 20,
|
|
1855
|
+
// padding of containers/sheets
|
|
1856
|
+
defaults: {
|
|
1857
|
+
dockingWidth: 340,
|
|
1858
|
+
// default width of resizable panels
|
|
1859
|
+
scrim: "rgba(0, 0, 0, 0.2)"
|
|
1860
|
+
// default scrim/underlay bg-color
|
|
1861
|
+
},
|
|
1862
|
+
constraints: {
|
|
1863
|
+
["bottomSheet" /* bottomSheet */]: 600,
|
|
1864
|
+
// Max-width of all bottom sheets
|
|
1865
|
+
["popover" /* popover */]: 600,
|
|
1866
|
+
// Max-width of all popover sheets
|
|
1867
|
+
["modal" /* modal */]: 600,
|
|
1868
|
+
// Max-width of all modal sheets
|
|
1869
|
+
pagination: 1024,
|
|
1870
|
+
// Max-width of pagination component
|
|
1871
|
+
dropdown: 250
|
|
1872
|
+
// Max-height of main UI dropdowns
|
|
1873
|
+
}
|
|
1874
|
+
},
|
|
1875
|
+
breakpoints: {
|
|
1876
|
+
// See https://m3.material.io/foundations/layout/applying-layout/window-size-classes
|
|
1877
|
+
["compact" /* compact */]: 600,
|
|
1878
|
+
// Phone in portrait
|
|
1879
|
+
["medium" /* medium */]: 840,
|
|
1880
|
+
// Tablet in portrait, Foldable in portrait (unfolded)
|
|
1881
|
+
["expanded" /* expanded */]: 1200,
|
|
1882
|
+
// Phone in landscape, Tablet in landscape, Foldable in landscape (unfolded), Desktop
|
|
1883
|
+
["large" /* large */]: 1600,
|
|
1884
|
+
// Desktop
|
|
1885
|
+
["xLarge" /* xLarge */]: null
|
|
1886
|
+
// Desktop Ultra-wide
|
|
1887
|
+
},
|
|
1888
|
+
themes: {
|
|
1889
|
+
reflowOrder: [
|
|
1890
|
+
"auto",
|
|
1891
|
+
"light" /* light */,
|
|
1892
|
+
"paper" /* paper */,
|
|
1893
|
+
"sepia" /* sepia */,
|
|
1894
|
+
"dark" /* dark */,
|
|
1895
|
+
"contrast1" /* contrast1 */,
|
|
1896
|
+
"contrast2" /* contrast2 */,
|
|
1897
|
+
"contrast3" /* contrast3 */
|
|
1898
|
+
],
|
|
1899
|
+
fxlOrder: [
|
|
1900
|
+
"auto",
|
|
1901
|
+
"light" /* light */,
|
|
1902
|
+
"dark" /* dark */
|
|
1903
|
+
],
|
|
1904
|
+
systemThemes: {
|
|
1905
|
+
light: "light" /* light */,
|
|
1906
|
+
dark: "dark" /* dark */
|
|
1907
|
+
},
|
|
1908
|
+
keys: {
|
|
1909
|
+
["light" /* light */]: lightTheme,
|
|
1910
|
+
["dark" /* dark */]: darkTheme,
|
|
1911
|
+
["paper" /* paper */]: paperTheme,
|
|
1912
|
+
["sepia" /* sepia */]: sepiaTheme,
|
|
1913
|
+
["contrast1" /* contrast1 */]: contrast1Theme,
|
|
1914
|
+
["contrast2" /* contrast2 */]: contrast2Theme,
|
|
1915
|
+
["contrast3" /* contrast3 */]: contrast3Theme
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
},
|
|
1919
|
+
contentProtection: defaultContentProtectionConfig,
|
|
1920
|
+
affordances: {
|
|
1921
|
+
scroll: {
|
|
1922
|
+
hintInImmersive: true,
|
|
1923
|
+
toggleOnMiddlePointer: ["tap", "click"],
|
|
1924
|
+
hideOnForwardScroll: true,
|
|
1925
|
+
showOnBackwardScroll: true
|
|
1926
|
+
},
|
|
1927
|
+
paginated: {
|
|
1928
|
+
reflow: {
|
|
1929
|
+
default: {
|
|
1930
|
+
variant: "layered" /* layered */,
|
|
1931
|
+
discard: ["navigation"],
|
|
1932
|
+
hint: ["layoutChange"]
|
|
1933
|
+
},
|
|
1934
|
+
breakpoints: {
|
|
1935
|
+
["large" /* large */]: {
|
|
1936
|
+
variant: "stacked" /* stacked */
|
|
1937
|
+
},
|
|
1938
|
+
["xLarge" /* xLarge */]: {
|
|
1939
|
+
variant: "stacked" /* stacked */
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
},
|
|
1943
|
+
fxl: {
|
|
1944
|
+
// Note FXL arrows are always layered
|
|
1945
|
+
// FXL navigator is using the window width to calculate the layout
|
|
1946
|
+
// so we need to force the layered variant to prevent layout issues
|
|
1947
|
+
default: {
|
|
1948
|
+
variant: "layered" /* layered */,
|
|
1949
|
+
discard: ["navigation"],
|
|
1950
|
+
hint: "none"
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
},
|
|
1955
|
+
shortcuts: {
|
|
1956
|
+
representation: "symbol" /* symbol */,
|
|
1957
|
+
joiner: "+"
|
|
1958
|
+
},
|
|
1959
|
+
actions: {
|
|
1960
|
+
reflowOrder: [
|
|
1961
|
+
"settings" /* settings */,
|
|
1962
|
+
"toc" /* toc */,
|
|
1963
|
+
"fullscreen" /* fullscreen */,
|
|
1964
|
+
"jumpToPosition" /* jumpToPosition */
|
|
1965
|
+
],
|
|
1966
|
+
fxlOrder: [
|
|
1967
|
+
"settings" /* settings */,
|
|
1968
|
+
"toc" /* toc */,
|
|
1969
|
+
"fullscreen" /* fullscreen */,
|
|
1970
|
+
"jumpToPosition" /* jumpToPosition */
|
|
1971
|
+
],
|
|
1972
|
+
webPubOrder: [
|
|
1973
|
+
"settings" /* settings */,
|
|
1974
|
+
"toc" /* toc */,
|
|
1975
|
+
"fullscreen" /* fullscreen */
|
|
1976
|
+
],
|
|
1977
|
+
collapse: true,
|
|
1978
|
+
keys: {
|
|
1979
|
+
["settings" /* settings */]: defaultSettingsAction,
|
|
1980
|
+
["fullscreen" /* fullscreen */]: defaultFullscreenAction,
|
|
1981
|
+
["toc" /* toc */]: defaultTocAction,
|
|
1982
|
+
["jumpToPosition" /* jumpToPosition */]: defaultJumpToPositionAction
|
|
1983
|
+
}
|
|
1984
|
+
},
|
|
1985
|
+
docking: {
|
|
1986
|
+
displayOrder: [
|
|
1987
|
+
"dockingTransient" /* transient */,
|
|
1988
|
+
"dockingStart" /* start */,
|
|
1989
|
+
"dockingEnd" /* end */
|
|
1990
|
+
],
|
|
1991
|
+
dock: {
|
|
1992
|
+
["compact" /* compact */]: "none" /* none */,
|
|
1993
|
+
["medium" /* medium */]: "none" /* none */,
|
|
1994
|
+
["expanded" /* expanded */]: "start" /* start */,
|
|
1995
|
+
["large" /* large */]: "both" /* both */,
|
|
1996
|
+
["xLarge" /* xLarge */]: "both" /* both */
|
|
1997
|
+
},
|
|
1998
|
+
collapse: true,
|
|
1999
|
+
keys: {
|
|
2000
|
+
["dockingStart" /* start */]: {
|
|
2001
|
+
visibility: "overflow" /* overflow */,
|
|
2002
|
+
shortcut: null
|
|
2003
|
+
},
|
|
2004
|
+
["dockingEnd" /* end */]: {
|
|
2005
|
+
visibility: "overflow" /* overflow */,
|
|
2006
|
+
shortcut: null
|
|
2007
|
+
},
|
|
2008
|
+
["dockingTransient" /* transient */]: {
|
|
2009
|
+
visibility: "overflow" /* overflow */,
|
|
2010
|
+
shortcut: null
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
},
|
|
2014
|
+
settings: {
|
|
2015
|
+
reflowOrder: [
|
|
2016
|
+
"zoom" /* zoom */,
|
|
2017
|
+
"textGroup" /* textGroup */,
|
|
2018
|
+
"theme" /* theme */,
|
|
2019
|
+
"spacingGroup" /* spacingGroup */,
|
|
2020
|
+
"layout" /* layout */,
|
|
2021
|
+
"columns" /* columns */
|
|
2022
|
+
],
|
|
2023
|
+
fxlOrder: [
|
|
2024
|
+
"theme" /* theme */,
|
|
2025
|
+
"columns" /* columns */
|
|
2026
|
+
],
|
|
2027
|
+
webPubOrder: [
|
|
2028
|
+
"zoom" /* zoom */,
|
|
2029
|
+
"textGroup" /* textGroup */,
|
|
2030
|
+
"spacingGroup" /* spacingGroup */
|
|
2031
|
+
],
|
|
2032
|
+
keys: {
|
|
2033
|
+
["fontFamily" /* fontFamily */]: {
|
|
2034
|
+
default: defaultFontCollection,
|
|
2035
|
+
arabic: { supportedLanguages: ["ar", "fa"], fonts: arabicFarsiCollection },
|
|
2036
|
+
hebrew: { supportedLanguages: ["he"], fonts: hebrewCollection },
|
|
2037
|
+
"chinese-simplified": { supportedLanguages: ["zh", "zh-hans", "zh-cn"], fonts: chineseSimplifiedCollection },
|
|
2038
|
+
"chinese-traditional": { supportedLanguages: ["zh-hant", "zh-tw", "zh-hk"], fonts: chineseTraditionalCollection },
|
|
2039
|
+
japanese: { supportedLanguages: ["ja"], fonts: japaneseCollection },
|
|
2040
|
+
"japanese-vertical": { supportedLanguages: ["ja-v"], fonts: japaneseVerticalCollection },
|
|
2041
|
+
korean: { supportedLanguages: ["ko"], fonts: koreanCollection },
|
|
2042
|
+
tamil: { supportedLanguages: ["ta"], fonts: tamilCollection }
|
|
2043
|
+
},
|
|
2044
|
+
["letterSpacing" /* letterSpacing */]: defaultLetterSpacing,
|
|
2045
|
+
["lineHeight" /* lineHeight */]: {
|
|
2046
|
+
allowUnset: false,
|
|
2047
|
+
keys: defaultLineHeights
|
|
2048
|
+
},
|
|
2049
|
+
["paragraphIndent" /* paragraphIndent */]: defaultParagraphIndent,
|
|
2050
|
+
["paragraphSpacing" /* paragraphSpacing */]: defaultParagraphSpacing,
|
|
2051
|
+
["wordSpacing" /* wordSpacing */]: defaultWordSpacing,
|
|
2052
|
+
["zoom" /* zoom */]: defaultZoom
|
|
2053
|
+
},
|
|
2054
|
+
text: {
|
|
2055
|
+
header: "previous" /* previous */,
|
|
2056
|
+
main: defaultTextSettingsMain,
|
|
2057
|
+
subPanel: defaultTextSettingsSubpanel
|
|
2058
|
+
},
|
|
2059
|
+
spacing: {
|
|
2060
|
+
header: "previous" /* previous */,
|
|
2061
|
+
main: defaultSpacingSettingsMain,
|
|
2062
|
+
subPanel: defaultSpacingSettingsSubpanel,
|
|
2063
|
+
presets: {
|
|
2064
|
+
reflowOrder: defaultSpacingPresetsOrder,
|
|
2065
|
+
webPubOrder: defaultSpacingPresetsOrder,
|
|
2066
|
+
keys: defaultSpacingPresets
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
});
|
|
2071
|
+
|
|
2072
|
+
// src/preferences/ThPreferencesContext.ts
|
|
2073
|
+
var ThPreferencesContext = createContext(null);
|
|
2074
|
+
var defaultPreferencesContextValue = {
|
|
2075
|
+
preferences: defaultPreferences,
|
|
2076
|
+
updatePreferences: () => {
|
|
2077
|
+
throw new Error("updatePreferences must be used within a ThPreferencesProvider with an adapter");
|
|
2078
|
+
}
|
|
2079
|
+
};
|
|
2080
|
+
|
|
2081
|
+
// src/preferences/adapters/ThMemoryPreferencesAdapter.ts
|
|
2082
|
+
var ThMemoryPreferencesAdapter = class {
|
|
2083
|
+
currentPreferences;
|
|
2084
|
+
listeners = /* @__PURE__ */ new Set();
|
|
2085
|
+
constructor(initialPreferences) {
|
|
2086
|
+
this.currentPreferences = { ...initialPreferences };
|
|
2087
|
+
}
|
|
2088
|
+
getPreferences() {
|
|
2089
|
+
return { ...this.currentPreferences };
|
|
2090
|
+
}
|
|
2091
|
+
setPreferences(prefs) {
|
|
2092
|
+
this.currentPreferences = { ...prefs };
|
|
2093
|
+
this.notifyListeners(this.currentPreferences);
|
|
2094
|
+
}
|
|
2095
|
+
subscribe(listener) {
|
|
2096
|
+
this.listeners.add(listener);
|
|
2097
|
+
}
|
|
2098
|
+
unsubscribe(listener) {
|
|
2099
|
+
this.listeners.delete(listener);
|
|
2100
|
+
}
|
|
2101
|
+
notifyListeners(prefs) {
|
|
2102
|
+
this.listeners.forEach((listener) => listener({ ...prefs }));
|
|
2103
|
+
}
|
|
2104
|
+
};
|
|
2105
|
+
function ThPreferencesProvider({
|
|
2106
|
+
adapter,
|
|
2107
|
+
initialPreferences,
|
|
2108
|
+
devMode,
|
|
2109
|
+
children
|
|
2110
|
+
}) {
|
|
2111
|
+
const effectiveAdapter = useMemo(() => {
|
|
2112
|
+
let fallbackPreferences = defaultPreferencesContextValue.preferences;
|
|
2113
|
+
if (devMode && !initialPreferences) {
|
|
2114
|
+
fallbackPreferences = {
|
|
2115
|
+
...fallbackPreferences,
|
|
2116
|
+
contentProtection: devContentProtectionConfig
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
return adapter || new ThMemoryPreferencesAdapter(
|
|
2120
|
+
initialPreferences || fallbackPreferences
|
|
2121
|
+
);
|
|
2122
|
+
}, [adapter, initialPreferences, devMode]);
|
|
2123
|
+
const [preferences, setPreferences] = useState(
|
|
2124
|
+
(() => {
|
|
2125
|
+
let fallbackPreferences = defaultPreferencesContextValue.preferences;
|
|
2126
|
+
if (devMode && !initialPreferences) {
|
|
2127
|
+
fallbackPreferences = {
|
|
2128
|
+
...fallbackPreferences,
|
|
2129
|
+
contentProtection: devContentProtectionConfig
|
|
2130
|
+
};
|
|
2131
|
+
}
|
|
2132
|
+
return initialPreferences || fallbackPreferences;
|
|
2133
|
+
})()
|
|
2134
|
+
);
|
|
2135
|
+
const handlePreferenceChange = useCallback((newPrefs) => {
|
|
2136
|
+
setPreferences((prev) => {
|
|
2137
|
+
return JSON.stringify(prev) === JSON.stringify(newPrefs) ? prev : newPrefs;
|
|
2138
|
+
});
|
|
2139
|
+
}, []);
|
|
2140
|
+
useEffect(() => {
|
|
2141
|
+
effectiveAdapter.subscribe(handlePreferenceChange);
|
|
2142
|
+
return () => {
|
|
2143
|
+
effectiveAdapter.unsubscribe(handlePreferenceChange);
|
|
2144
|
+
};
|
|
2145
|
+
}, [effectiveAdapter, handlePreferenceChange]);
|
|
2146
|
+
const contextValue = useMemo(() => ({
|
|
2147
|
+
preferences,
|
|
2148
|
+
updatePreferences: (newPrefs) => {
|
|
2149
|
+
effectiveAdapter.setPreferences(newPrefs);
|
|
2150
|
+
}
|
|
2151
|
+
}), [preferences, effectiveAdapter]);
|
|
2152
|
+
return /* @__PURE__ */ jsx(ThPreferencesContext.Provider, { value: contextValue, children });
|
|
2153
|
+
}
|
|
2154
|
+
var useActionsPreferences = () => {
|
|
2155
|
+
const audioCtx = useContext(ThAudioPreferencesContext);
|
|
2156
|
+
const readerCtx = useContext(ThPreferencesContext);
|
|
2157
|
+
const audioPrimaryKeys = audioCtx?.preferences.actions.primary.keys;
|
|
2158
|
+
const audioSecondaryKeys = audioCtx?.preferences.actions.secondary.keys;
|
|
2159
|
+
const audioDocking = audioCtx?.preferences.docking;
|
|
2160
|
+
const audioActionsKeys = useMemo(() => {
|
|
2161
|
+
if (!audioPrimaryKeys && !audioSecondaryKeys) return null;
|
|
2162
|
+
return { ...audioPrimaryKeys, ...audioSecondaryKeys };
|
|
2163
|
+
}, [audioPrimaryKeys, audioSecondaryKeys]);
|
|
2164
|
+
const audioResult = useMemo(() => {
|
|
2165
|
+
if (!audioCtx || !audioActionsKeys || !audioDocking) return null;
|
|
2166
|
+
return { docking: audioDocking, actionsKeys: audioActionsKeys };
|
|
2167
|
+
}, [audioCtx, audioDocking, audioActionsKeys]);
|
|
2168
|
+
const readerResult = useMemo(() => {
|
|
2169
|
+
if (!readerCtx) return null;
|
|
2170
|
+
return {
|
|
2171
|
+
docking: readerCtx.preferences.docking,
|
|
2172
|
+
actionsKeys: readerCtx.preferences.actions.keys
|
|
2173
|
+
};
|
|
2174
|
+
}, [readerCtx]);
|
|
2175
|
+
if (audioResult) return audioResult;
|
|
2176
|
+
if (readerResult) return readerResult;
|
|
2177
|
+
throw new Error("useActionsPreferences must be used within a ThPreferencesProvider or ThAudioPreferencesProvider");
|
|
2178
|
+
};
|
|
2179
|
+
var useAudioActionsPreferences = () => {
|
|
2180
|
+
const audioCtx = useContext(ThAudioPreferencesContext);
|
|
2181
|
+
if (!audioCtx) {
|
|
2182
|
+
throw new Error("useAudioActionsPreferences must be used within a ThAudioPreferencesProvider");
|
|
2183
|
+
}
|
|
2184
|
+
return {
|
|
2185
|
+
docking: audioCtx.preferences.docking,
|
|
2186
|
+
primaryActionsKeys: audioCtx.preferences.actions.primary.keys,
|
|
2187
|
+
secondaryActionsKeys: audioCtx.preferences.actions.secondary.keys
|
|
2188
|
+
};
|
|
2189
|
+
};
|
|
2190
|
+
function useAudioPreferences() {
|
|
2191
|
+
const context = useContext(ThAudioPreferencesContext);
|
|
2192
|
+
if (!context) {
|
|
2193
|
+
throw new Error("useAudioPreferences must be used within a ThAudioPreferencesProvider");
|
|
2194
|
+
}
|
|
2195
|
+
return {
|
|
2196
|
+
preferences: context.preferences,
|
|
2197
|
+
updatePreferences: context.updatePreferences
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// src/preferences/services/createBunnyFontResources.ts
|
|
2202
|
+
var buildBunnyFontsUrl = ({
|
|
2203
|
+
family,
|
|
2204
|
+
weights,
|
|
2205
|
+
styles = ["normal"]
|
|
2206
|
+
}) => {
|
|
2207
|
+
if (weights.type !== "static") {
|
|
2208
|
+
throw new Error("Bunny Fonts only supports static fonts");
|
|
2209
|
+
}
|
|
2210
|
+
const weightValues = weights.values;
|
|
2211
|
+
const variants = /* @__PURE__ */ new Set();
|
|
2212
|
+
for (const weight of weightValues) {
|
|
2213
|
+
variants.add(weight.toString());
|
|
2214
|
+
if (styles.includes("italic")) {
|
|
2215
|
+
variants.add(`${weight}i`);
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
const variantList = Array.from(variants).sort();
|
|
2219
|
+
const familyParam = family.replace(/ /g, "-").toLowerCase();
|
|
2220
|
+
const variantParam = variantList.join(",");
|
|
2221
|
+
return `https://fonts.bunny.net/css?family=${familyParam}:${variantParam}`;
|
|
2222
|
+
};
|
|
2223
|
+
var createBunnyFontResources = (font) => {
|
|
2224
|
+
if (font.source.type !== "custom" || font.source.provider !== "bunny" || font.spec.weights.type !== "static") {
|
|
2225
|
+
return null;
|
|
2226
|
+
}
|
|
2227
|
+
const { family, weights, styles } = font.spec;
|
|
2228
|
+
const url = buildBunnyFontsUrl({
|
|
2229
|
+
family,
|
|
2230
|
+
weights,
|
|
2231
|
+
styles
|
|
2232
|
+
});
|
|
2233
|
+
return {
|
|
2234
|
+
as: "link",
|
|
2235
|
+
rel: "stylesheet",
|
|
2236
|
+
url
|
|
2237
|
+
};
|
|
2238
|
+
};
|
|
2239
|
+
|
|
2240
|
+
// src/preferences/services/createGoogleFontResources.ts
|
|
2241
|
+
var buildGoogleFontsV2Url = ({
|
|
2242
|
+
family,
|
|
2243
|
+
weights,
|
|
2244
|
+
styles = ["normal"],
|
|
2245
|
+
widths,
|
|
2246
|
+
display = "block",
|
|
2247
|
+
text
|
|
2248
|
+
}) => {
|
|
2249
|
+
if (text) {
|
|
2250
|
+
return `https://fonts.googleapis.com/css2?family=${family.replace(/ /g, "+")}&text=${encodeURIComponent(text)}`;
|
|
2251
|
+
}
|
|
2252
|
+
const hasItalic = styles.includes("italic");
|
|
2253
|
+
const hasWidth = !!widths;
|
|
2254
|
+
const weightValues = weights.type === "static" ? weights.values.join(",") : `${weights.min}..${weights.max}`;
|
|
2255
|
+
const widthValues = hasWidth && widths ? `${widths.min}..${widths.max}` : void 0;
|
|
2256
|
+
const familyParam = family.replace(/ /g, "+");
|
|
2257
|
+
let axesParam;
|
|
2258
|
+
if (hasItalic && hasWidth) {
|
|
2259
|
+
const variants = [
|
|
2260
|
+
`0,${widthValues},${weightValues}`,
|
|
2261
|
+
// normal
|
|
2262
|
+
`1,${widthValues},${weightValues}`
|
|
2263
|
+
// italic
|
|
2264
|
+
];
|
|
2265
|
+
axesParam = `:ital,wdth,wght@${variants.join(";")}`;
|
|
2266
|
+
} else if (hasItalic) {
|
|
2267
|
+
const variants = [
|
|
2268
|
+
`0,${weightValues}`,
|
|
2269
|
+
// normal
|
|
2270
|
+
`1,${weightValues}`
|
|
2271
|
+
// italic
|
|
2272
|
+
];
|
|
2273
|
+
axesParam = `:ital,wght@${variants.join(";")}`;
|
|
2274
|
+
} else if (hasWidth) {
|
|
2275
|
+
axesParam = `:wdth,wght@${widthValues},${weightValues}`;
|
|
2276
|
+
} else {
|
|
2277
|
+
axesParam = `:wght@${weightValues}`;
|
|
2278
|
+
}
|
|
2279
|
+
const displayParam = display ? `&display=${display}` : "";
|
|
2280
|
+
return `https://fonts.googleapis.com/css2?family=${familyParam}${axesParam}${displayParam}`;
|
|
2281
|
+
};
|
|
2282
|
+
var createGoogleFontResources = (font, text) => {
|
|
2283
|
+
if (font.source.type !== "custom" || font.source.provider !== "google") {
|
|
2284
|
+
return null;
|
|
2285
|
+
}
|
|
2286
|
+
const { family, weights, display, styles, widths } = font.spec;
|
|
2287
|
+
const url = buildGoogleFontsV2Url({
|
|
2288
|
+
family,
|
|
2289
|
+
weights,
|
|
2290
|
+
display,
|
|
2291
|
+
styles,
|
|
2292
|
+
widths,
|
|
2293
|
+
text
|
|
2294
|
+
});
|
|
2295
|
+
return {
|
|
2296
|
+
as: "link",
|
|
2297
|
+
rel: "stylesheet",
|
|
2298
|
+
url
|
|
2299
|
+
};
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
// src/preferences/services/createLocalFontResources.ts
|
|
2303
|
+
var getFontFormat = (path) => {
|
|
2304
|
+
const ext = path.split(".").pop()?.toLowerCase();
|
|
2305
|
+
switch (ext) {
|
|
2306
|
+
case "woff":
|
|
2307
|
+
return "woff";
|
|
2308
|
+
case "woff2":
|
|
2309
|
+
return "woff2";
|
|
2310
|
+
case "ttf":
|
|
2311
|
+
return "truetype";
|
|
2312
|
+
case "otf":
|
|
2313
|
+
return "opentype";
|
|
2314
|
+
case "eot":
|
|
2315
|
+
return "embedded-opentype";
|
|
2316
|
+
case "svg":
|
|
2317
|
+
return "svg";
|
|
2318
|
+
default:
|
|
2319
|
+
return "woff2";
|
|
2320
|
+
}
|
|
2321
|
+
};
|
|
2322
|
+
var createLocalFontResources = (font) => {
|
|
2323
|
+
if (font.source.type !== "custom" || font.source.provider !== "local") {
|
|
2324
|
+
return null;
|
|
2325
|
+
}
|
|
2326
|
+
const { family, weights, display, widths } = font.spec;
|
|
2327
|
+
const fontFiles = font.source.files || [];
|
|
2328
|
+
const cssContent = fontFiles.map((fontFile) => {
|
|
2329
|
+
const format = getFontFormat(fontFile.path);
|
|
2330
|
+
const fontUrl = new URL(fontFile.path, window.location.origin).toString();
|
|
2331
|
+
const isVariable = font.source.type === "custom" && font.source.provider === "local" && "variant" in font.source && font.source.variant === "variable";
|
|
2332
|
+
const rules = [
|
|
2333
|
+
`@font-face {`,
|
|
2334
|
+
` font-family: "${family}";`,
|
|
2335
|
+
` src: url("${fontUrl}") format("${format}");`
|
|
2336
|
+
];
|
|
2337
|
+
if (isVariable && weights.type === "variable") {
|
|
2338
|
+
rules.push(` font-weight: ${weights.min} ${weights.max};`);
|
|
2339
|
+
} else if ("weight" in fontFile) {
|
|
2340
|
+
rules.push(` font-weight: ${fontFile.weight};`);
|
|
2341
|
+
}
|
|
2342
|
+
if ("style" in fontFile) {
|
|
2343
|
+
rules.push(` font-style: ${fontFile.style};`);
|
|
2344
|
+
}
|
|
2345
|
+
if (isVariable && widths) {
|
|
2346
|
+
rules.push(` font-stretch: ${widths.min}% ${widths.max}%;`);
|
|
2347
|
+
}
|
|
2348
|
+
if (display) {
|
|
2349
|
+
rules.push(` font-display: ${display};`);
|
|
2350
|
+
} else {
|
|
2351
|
+
rules.push(` font-display: block;`);
|
|
2352
|
+
}
|
|
2353
|
+
return rules.join("\n") + "\n}";
|
|
2354
|
+
}).filter(Boolean).join("\n\n");
|
|
2355
|
+
const blob = new Blob([cssContent], { type: "text/css" });
|
|
2356
|
+
return {
|
|
2357
|
+
as: "link",
|
|
2358
|
+
rel: "stylesheet",
|
|
2359
|
+
blob
|
|
2360
|
+
};
|
|
2361
|
+
};
|
|
2362
|
+
|
|
2363
|
+
// src/preferences/services/fonts.ts
|
|
2364
|
+
var createFontService = (fontFamilyPref) => {
|
|
2365
|
+
const allSupportedLanguages = [];
|
|
2366
|
+
const parsedFonts = /* @__PURE__ */ new Map();
|
|
2367
|
+
const bunnyFonts = /* @__PURE__ */ new Map();
|
|
2368
|
+
const googleFonts = /* @__PURE__ */ new Map();
|
|
2369
|
+
const localFonts = /* @__PURE__ */ new Map();
|
|
2370
|
+
const resolveFontLanguage = (bcp47Tag, scriptMode = "ltr") => {
|
|
2371
|
+
if (!bcp47Tag) return "default";
|
|
2372
|
+
if (allSupportedLanguages.includes(bcp47Tag)) {
|
|
2373
|
+
return bcp47Tag;
|
|
2374
|
+
}
|
|
2375
|
+
const parts = bcp47Tag.split(/[-_]/);
|
|
2376
|
+
const language = parts[0].toLowerCase();
|
|
2377
|
+
const scriptOrRegion = parts[1]?.toLowerCase();
|
|
2378
|
+
if (scriptOrRegion) {
|
|
2379
|
+
const langScriptOrRegion = `${language}-${scriptOrRegion}`;
|
|
2380
|
+
if (allSupportedLanguages.includes(langScriptOrRegion)) {
|
|
2381
|
+
return langScriptOrRegion;
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
if (language === "ja" && !scriptOrRegion) {
|
|
2385
|
+
if (scriptMode === "cjk-vertical" && allSupportedLanguages.includes("ja-v")) {
|
|
2386
|
+
return "ja-v";
|
|
2387
|
+
}
|
|
2388
|
+
if (allSupportedLanguages.includes("ja")) {
|
|
2389
|
+
return "ja";
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
const shouldFilter = language === "mn" && (scriptOrRegion === "mong" || scriptOrRegion === "cyrl") || language === "zh" && (scriptOrRegion === "hant" || scriptOrRegion === "tw" || scriptOrRegion === "hk");
|
|
2393
|
+
if (!shouldFilter && allSupportedLanguages.includes(language)) {
|
|
2394
|
+
return language;
|
|
2395
|
+
}
|
|
2396
|
+
return "default";
|
|
2397
|
+
};
|
|
2398
|
+
Object.entries(fontFamilyPref).forEach(([collectionName, collectionData]) => {
|
|
2399
|
+
const fontCollection = "fonts" in collectionData ? collectionData.fonts : collectionData;
|
|
2400
|
+
if ("supportedLanguages" in collectionData) {
|
|
2401
|
+
const reducedLanguages = collectionData.supportedLanguages.map((lang) => {
|
|
2402
|
+
const parts = lang.split(/[-_]/);
|
|
2403
|
+
const language = parts[0].toLowerCase();
|
|
2404
|
+
const scriptOrRegion = parts[1]?.toLowerCase();
|
|
2405
|
+
return scriptOrRegion ? `${language}-${scriptOrRegion}` : language;
|
|
2406
|
+
});
|
|
2407
|
+
allSupportedLanguages.push(...reducedLanguages);
|
|
2408
|
+
}
|
|
2409
|
+
bunnyFonts.set(collectionName, []);
|
|
2410
|
+
googleFonts.set(collectionName, []);
|
|
2411
|
+
localFonts.set(collectionName, []);
|
|
2412
|
+
const collectionBunnyFonts = bunnyFonts.get(collectionName);
|
|
2413
|
+
const collectionGoogleFonts = googleFonts.get(collectionName);
|
|
2414
|
+
const collectionLocalFonts = localFonts.get(collectionName);
|
|
2415
|
+
Object.entries(fontCollection).forEach(([id, font]) => {
|
|
2416
|
+
const fontFamily = font.spec.family;
|
|
2417
|
+
let fontStack = fontFamily;
|
|
2418
|
+
if (font.source.type === "custom") {
|
|
2419
|
+
switch (font.source.provider) {
|
|
2420
|
+
case "bunny":
|
|
2421
|
+
collectionBunnyFonts.push(font);
|
|
2422
|
+
break;
|
|
2423
|
+
case "google":
|
|
2424
|
+
collectionGoogleFonts.push(font);
|
|
2425
|
+
break;
|
|
2426
|
+
case "local":
|
|
2427
|
+
collectionLocalFonts.push(font);
|
|
2428
|
+
break;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
const wrapIfNeeded = (name) => {
|
|
2432
|
+
const trimmed = name.trim();
|
|
2433
|
+
if (!trimmed) return "";
|
|
2434
|
+
if (trimmed.includes(" ") && !/^['"].*['"]$/.test(trimmed)) {
|
|
2435
|
+
return `"${trimmed}"`;
|
|
2436
|
+
}
|
|
2437
|
+
return trimmed;
|
|
2438
|
+
};
|
|
2439
|
+
const wrappedFontFamily = wrapIfNeeded(fontFamily);
|
|
2440
|
+
if (font.spec.fallbacks?.length) {
|
|
2441
|
+
const uniqueFallbacks = [...new Set(
|
|
2442
|
+
font.spec.fallbacks.filter((fallback) => fallback.toLowerCase() !== fontFamily.toLowerCase()).map(wrapIfNeeded)
|
|
2443
|
+
)];
|
|
2444
|
+
if (uniqueFallbacks.length > 0) {
|
|
2445
|
+
fontStack = [wrappedFontFamily, ...uniqueFallbacks].join(", ");
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
parsedFonts.set(id, {
|
|
2449
|
+
fontStack: fontStack || wrappedFontFamily,
|
|
2450
|
+
fontFamily: wrappedFontFamily,
|
|
2451
|
+
weights: font.spec.weights || null,
|
|
2452
|
+
widths: font.spec.widths || null
|
|
2453
|
+
});
|
|
2454
|
+
});
|
|
2455
|
+
});
|
|
2456
|
+
const defaultBunnyFonts = bunnyFonts.get("default") || [];
|
|
2457
|
+
const defaultGoogleFonts = googleFonts.get("default") || [];
|
|
2458
|
+
const defaultLocalFonts = localFonts.get("default") || [];
|
|
2459
|
+
const processFonts = (bunnyFontsList, googleFontsList, localFontsList, optimize = false) => {
|
|
2460
|
+
const result = {
|
|
2461
|
+
allowedDomains: [],
|
|
2462
|
+
prepend: [],
|
|
2463
|
+
append: []
|
|
2464
|
+
};
|
|
2465
|
+
const bunnyResources = bunnyFontsList.map((font) => createBunnyFontResources(font)).filter((resource) => resource !== null);
|
|
2466
|
+
if (bunnyResources.length > 0) {
|
|
2467
|
+
result.allowedDomains.push(
|
|
2468
|
+
"https://fonts.bunny.net"
|
|
2469
|
+
);
|
|
2470
|
+
result.prepend.push(
|
|
2471
|
+
{
|
|
2472
|
+
as: "link",
|
|
2473
|
+
rel: "preconnect",
|
|
2474
|
+
url: "https://fonts.bunny.net"
|
|
2475
|
+
}
|
|
2476
|
+
);
|
|
2477
|
+
result.append.push(...bunnyResources);
|
|
2478
|
+
}
|
|
2479
|
+
const googleResources = googleFontsList.map((font) => createGoogleFontResources(font, optimize ? font.name : void 0)).filter((resource) => resource !== null);
|
|
2480
|
+
if (googleResources.length > 0) {
|
|
2481
|
+
result.allowedDomains.push(
|
|
2482
|
+
"https://fonts.googleapis.com",
|
|
2483
|
+
"https://fonts.gstatic.com"
|
|
2484
|
+
);
|
|
2485
|
+
result.prepend.push(
|
|
2486
|
+
{
|
|
2487
|
+
as: "link",
|
|
2488
|
+
rel: "preconnect",
|
|
2489
|
+
url: "https://fonts.googleapis.com"
|
|
2490
|
+
},
|
|
2491
|
+
{
|
|
2492
|
+
as: "link",
|
|
2493
|
+
rel: "preconnect",
|
|
2494
|
+
url: "https://fonts.gstatic.com",
|
|
2495
|
+
attributes: { crossOrigin: "anonymous" }
|
|
2496
|
+
}
|
|
2497
|
+
);
|
|
2498
|
+
result.append.push(...googleResources);
|
|
2499
|
+
}
|
|
2500
|
+
const localResources = localFontsList.map(createLocalFontResources).filter((resource) => resource !== null);
|
|
2501
|
+
if (localResources.length > 0) {
|
|
2502
|
+
result.allowedDomains.push(window.location.origin);
|
|
2503
|
+
result.append.push(...localResources);
|
|
2504
|
+
}
|
|
2505
|
+
return result.append.length > 0 ? result : null;
|
|
2506
|
+
};
|
|
2507
|
+
const getInjectables = (options, optimize = false) => {
|
|
2508
|
+
if (options && "key" in options) {
|
|
2509
|
+
const { key } = options;
|
|
2510
|
+
if (!key || !(key in fontFamilyPref)) {
|
|
2511
|
+
return null;
|
|
2512
|
+
}
|
|
2513
|
+
return processFonts(bunnyFonts.get(key) || [], googleFonts.get(key) || [], localFonts.get(key) || [], optimize);
|
|
2514
|
+
}
|
|
2515
|
+
if (options && "language" in options) {
|
|
2516
|
+
const { language: publicationLanguage } = options;
|
|
2517
|
+
for (const [collectionName, collectionData] of Object.entries(fontFamilyPref)) {
|
|
2518
|
+
if (collectionName === "default") continue;
|
|
2519
|
+
const supportedLangs = "supportedLanguages" in collectionData ? collectionData.supportedLanguages : null;
|
|
2520
|
+
if (supportedLangs && Array.isArray(supportedLangs) && publicationLanguage && supportedLangs.includes(publicationLanguage)) {
|
|
2521
|
+
return processFonts(bunnyFonts.get(collectionName) || [], googleFonts.get(collectionName) || [], localFonts.get(collectionName) || [], optimize);
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
return processFonts(defaultBunnyFonts, defaultGoogleFonts, defaultLocalFonts, optimize);
|
|
2526
|
+
};
|
|
2527
|
+
const getFontMetadata = (fontId) => {
|
|
2528
|
+
const parsed = parsedFonts.get(fontId);
|
|
2529
|
+
return parsed || { fontStack: null, fontFamily: null, weights: null, widths: null };
|
|
2530
|
+
};
|
|
2531
|
+
const getFontCollection = (options) => {
|
|
2532
|
+
if (options && "key" in options) {
|
|
2533
|
+
const { key } = options;
|
|
2534
|
+
if (!key || !(key in fontFamilyPref)) {
|
|
2535
|
+
return fontFamilyPref.default;
|
|
2536
|
+
}
|
|
2537
|
+
if (key === "default") {
|
|
2538
|
+
return fontFamilyPref.default;
|
|
2539
|
+
}
|
|
2540
|
+
const prefRecord = fontFamilyPref;
|
|
2541
|
+
const collection = prefRecord[key];
|
|
2542
|
+
if (collection && "fonts" in collection) {
|
|
2543
|
+
return collection.fonts;
|
|
2544
|
+
}
|
|
2545
|
+
return fontFamilyPref.default;
|
|
2546
|
+
}
|
|
2547
|
+
if (options && "language" in options) {
|
|
2548
|
+
const { language: publicationLanguage } = options;
|
|
2549
|
+
for (const [collectionName, collectionData] of Object.entries(fontFamilyPref)) {
|
|
2550
|
+
if (collectionName === "default") continue;
|
|
2551
|
+
const collection = "fonts" in collectionData ? collectionData : { fonts: collectionData };
|
|
2552
|
+
const supportedLangs = "supportedLanguages" in collection ? collection.supportedLanguages : null;
|
|
2553
|
+
if (supportedLangs?.includes(publicationLanguage)) {
|
|
2554
|
+
return collection.fonts;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
return fontFamilyPref.default;
|
|
2558
|
+
}
|
|
2559
|
+
return fontFamilyPref.default;
|
|
2560
|
+
};
|
|
2561
|
+
return {
|
|
2562
|
+
getInjectables,
|
|
2563
|
+
getFontMetadata,
|
|
2564
|
+
getFontCollection,
|
|
2565
|
+
resolveFontLanguage
|
|
2566
|
+
};
|
|
2567
|
+
};
|
|
2568
|
+
|
|
2569
|
+
// src/preferences/hooks/usePreferences.ts
|
|
2570
|
+
function usePreferences() {
|
|
2571
|
+
const context = useContext(ThPreferencesContext);
|
|
2572
|
+
if (!context) {
|
|
2573
|
+
throw new Error("usePreferences must be used within a ThPreferencesProvider");
|
|
2574
|
+
}
|
|
2575
|
+
const fontService = createFontService(context.preferences.settings.keys.fontFamily);
|
|
2576
|
+
return {
|
|
2577
|
+
preferences: context.preferences,
|
|
2578
|
+
updatePreferences: context.updatePreferences,
|
|
2579
|
+
getFontInjectables: (options, optimize) => {
|
|
2580
|
+
return fontService.getInjectables(options, optimize);
|
|
2581
|
+
},
|
|
2582
|
+
getFontsList: (options) => {
|
|
2583
|
+
return fontService.getFontCollection(options);
|
|
2584
|
+
},
|
|
2585
|
+
getFontMetadata: (fontId) => {
|
|
2586
|
+
return fontService.getFontMetadata(fontId);
|
|
2587
|
+
},
|
|
2588
|
+
resolveFontLanguage: (bcp47Tag, scriptMode) => {
|
|
2589
|
+
return fontService.resolveFontLanguage(bcp47Tag, scriptMode);
|
|
2590
|
+
}
|
|
2591
|
+
};
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
// src/preferences/hooks/usePreferenceKeys.ts
|
|
2595
|
+
var usePreferenceKeys = () => {
|
|
2596
|
+
const { preferences } = usePreferences();
|
|
2597
|
+
const reflowActionKeys = preferences.actions.reflowOrder;
|
|
2598
|
+
const fxlActionKeys = preferences.actions.fxlOrder;
|
|
2599
|
+
const webPubActionKeys = preferences.actions.webPubOrder;
|
|
2600
|
+
const reflowThemeKeys = preferences.theming.themes.reflowOrder;
|
|
2601
|
+
const fxlThemeKeys = preferences.theming.themes.fxlOrder;
|
|
2602
|
+
const reflowSettingsKeys = preferences.settings.reflowOrder;
|
|
2603
|
+
const fxlSettingsKeys = preferences.settings.fxlOrder;
|
|
2604
|
+
const webPubSettingsKeys = preferences.settings.webPubOrder;
|
|
2605
|
+
const mainTextSettingsKeys = preferences.settings.text?.main ?? defaultTextSettingsMain;
|
|
2606
|
+
const subPanelTextSettingsKeys = preferences.settings.text?.subPanel ?? defaultTextSettingsSubpanel;
|
|
2607
|
+
const mainSpacingSettingsKeys = preferences.settings.spacing?.main ?? defaultSpacingSettingsMain;
|
|
2608
|
+
const subPanelSpacingSettingsKeys = preferences.settings.spacing?.subPanel ?? defaultSpacingSettingsSubpanel;
|
|
2609
|
+
const reflowSpacingPresetKeys = preferences.settings.spacing?.presets?.reflowOrder ?? defaultSpacingPresetsOrder;
|
|
2610
|
+
const fxlSpacingPresetKeys = [];
|
|
2611
|
+
const webPubSpacingPresetKeys = preferences.settings.spacing?.presets?.webPubOrder ?? defaultSpacingPresetsOrder;
|
|
2612
|
+
return {
|
|
2613
|
+
reflowActionKeys,
|
|
2614
|
+
fxlActionKeys,
|
|
2615
|
+
webPubActionKeys,
|
|
2616
|
+
reflowThemeKeys,
|
|
2617
|
+
fxlThemeKeys,
|
|
2618
|
+
reflowSettingsKeys,
|
|
2619
|
+
fxlSettingsKeys,
|
|
2620
|
+
webPubSettingsKeys,
|
|
2621
|
+
mainTextSettingsKeys,
|
|
2622
|
+
subPanelTextSettingsKeys,
|
|
2623
|
+
mainSpacingSettingsKeys,
|
|
2624
|
+
subPanelSpacingSettingsKeys,
|
|
2625
|
+
reflowSpacingPresetKeys,
|
|
2626
|
+
fxlSpacingPresetKeys,
|
|
2627
|
+
webPubSpacingPresetKeys
|
|
2628
|
+
};
|
|
2629
|
+
};
|
|
2630
|
+
var EXCLUDED_CJK = [
|
|
2631
|
+
"textAlign" /* textAlign */,
|
|
2632
|
+
"hyphens" /* hyphens */,
|
|
2633
|
+
"ligatures" /* ligatures */,
|
|
2634
|
+
"paragraphIndent" /* paragraphIndent */,
|
|
2635
|
+
"wordSpacing" /* wordSpacing */,
|
|
2636
|
+
"textNormalize" /* textNormalize */
|
|
2637
|
+
];
|
|
2638
|
+
var EXCLUDED_BY_SCRIPT_MODE = {
|
|
2639
|
+
"ltr": [
|
|
2640
|
+
"noRuby" /* noRuby */
|
|
2641
|
+
],
|
|
2642
|
+
"rtl": [
|
|
2643
|
+
"hyphens" /* hyphens */,
|
|
2644
|
+
"letterSpacing" /* letterSpacing */,
|
|
2645
|
+
"textNormalize" /* textNormalize */,
|
|
2646
|
+
"noRuby" /* noRuby */
|
|
2647
|
+
],
|
|
2648
|
+
"cjk-horizontal": EXCLUDED_CJK,
|
|
2649
|
+
"cjk-vertical": [...EXCLUDED_CJK, "layout" /* layout */],
|
|
2650
|
+
"mongolian-vertical": [...EXCLUDED_CJK, "layout" /* layout */]
|
|
2651
|
+
};
|
|
2652
|
+
var useFilteredPreferenceKeys = () => {
|
|
2653
|
+
const keys = usePreferenceKeys();
|
|
2654
|
+
const scriptMode = useAppSelector((state) => state.publication.scriptMode);
|
|
2655
|
+
const isFXL = useAppSelector((state) => state.publication.isFXL);
|
|
2656
|
+
return useMemo(() => {
|
|
2657
|
+
const excluded = [
|
|
2658
|
+
...EXCLUDED_BY_SCRIPT_MODE[scriptMode] ?? [],
|
|
2659
|
+
...(scriptMode === "cjk-vertical" || scriptMode === "mongolian-vertical") && !isFXL ? ["columns" /* columns */] : []
|
|
2660
|
+
];
|
|
2661
|
+
if (excluded.length === 0) return keys;
|
|
2662
|
+
const filter = (arr) => arr.filter((k) => !excluded.includes(k));
|
|
2663
|
+
return {
|
|
2664
|
+
...keys,
|
|
2665
|
+
reflowSettingsKeys: filter(keys.reflowSettingsKeys),
|
|
2666
|
+
fxlSettingsKeys: filter(keys.fxlSettingsKeys),
|
|
2667
|
+
webPubSettingsKeys: filter(keys.webPubSettingsKeys),
|
|
2668
|
+
mainTextSettingsKeys: filter(keys.mainTextSettingsKeys),
|
|
2669
|
+
subPanelTextSettingsKeys: filter(keys.subPanelTextSettingsKeys),
|
|
2670
|
+
mainSpacingSettingsKeys: filter(keys.mainSpacingSettingsKeys),
|
|
2671
|
+
subPanelSpacingSettingsKeys: filter(keys.subPanelSpacingSettingsKeys)
|
|
2672
|
+
};
|
|
2673
|
+
}, [keys, scriptMode, isFXL]);
|
|
2674
|
+
};
|
|
2675
|
+
|
|
2676
|
+
// src/core/Helpers/prefixString.ts
|
|
2677
|
+
var PREFIXES = {
|
|
2678
|
+
short: "th",
|
|
2679
|
+
full: "thorium_web"
|
|
2680
|
+
};
|
|
2681
|
+
var prefixString = (str, variant = "short") => {
|
|
2682
|
+
return `${PREFIXES[variant]}-${str}`;
|
|
2683
|
+
};
|
|
2684
|
+
|
|
2685
|
+
// node_modules/.pnpm/colorthief@3.3.1_sharp@0.34.5/node_modules/colorthief/dist/index.js
|
|
2686
|
+
var __defProp = Object.defineProperty;
|
|
2687
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2688
|
+
var __esm = (fn, res) => function __init() {
|
|
2689
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
2690
|
+
};
|
|
2691
|
+
var __export = (target, all) => {
|
|
2692
|
+
for (var name in all)
|
|
2693
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
2694
|
+
};
|
|
2695
|
+
function srgbToLinear(c) {
|
|
2696
|
+
const s = c / 255;
|
|
2697
|
+
return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
2698
|
+
}
|
|
2699
|
+
function linearToSrgb(c) {
|
|
2700
|
+
const s = c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
2701
|
+
return Math.round(Math.max(0, Math.min(255, s * 255)));
|
|
2702
|
+
}
|
|
2703
|
+
function rgbToOklch(r, g, b) {
|
|
2704
|
+
const lr = srgbToLinear(r);
|
|
2705
|
+
const lg = srgbToLinear(g);
|
|
2706
|
+
const lb = srgbToLinear(b);
|
|
2707
|
+
const l_ = 0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb;
|
|
2708
|
+
const m_ = 0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb;
|
|
2709
|
+
const s_ = 0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb;
|
|
2710
|
+
const l3 = Math.cbrt(l_);
|
|
2711
|
+
const m3 = Math.cbrt(m_);
|
|
2712
|
+
const s3 = Math.cbrt(s_);
|
|
2713
|
+
const L = 0.2104542553 * l3 + 0.793617785 * m3 - 0.0040720468 * s3;
|
|
2714
|
+
const a = 1.9779984951 * l3 - 2.428592205 * m3 + 0.4505937099 * s3;
|
|
2715
|
+
const bLab = 0.0259040371 * l3 + 0.7827717662 * m3 - 0.808675766 * s3;
|
|
2716
|
+
const C = Math.sqrt(a * a + bLab * bLab);
|
|
2717
|
+
let H = Math.atan2(bLab, a) * (180 / Math.PI);
|
|
2718
|
+
if (H < 0) H += 360;
|
|
2719
|
+
return { l: L, c: C, h: H };
|
|
2720
|
+
}
|
|
2721
|
+
function oklchToRgb(l, c, h) {
|
|
2722
|
+
const hRad = h * (Math.PI / 180);
|
|
2723
|
+
const a = c * Math.cos(hRad);
|
|
2724
|
+
const bLab = c * Math.sin(hRad);
|
|
2725
|
+
const l3 = l + 0.3963377774 * a + 0.2158037573 * bLab;
|
|
2726
|
+
const m3 = l - 0.1055613458 * a - 0.0638541728 * bLab;
|
|
2727
|
+
const s3 = l - 0.0894841775 * a - 1.291485548 * bLab;
|
|
2728
|
+
const l_ = l3 * l3 * l3;
|
|
2729
|
+
const m_ = m3 * m3 * m3;
|
|
2730
|
+
const s_ = s3 * s3 * s3;
|
|
2731
|
+
const lr = 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_;
|
|
2732
|
+
const lg = -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_;
|
|
2733
|
+
const lb = -0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_;
|
|
2734
|
+
return [linearToSrgb(lr), linearToSrgb(lg), linearToSrgb(lb)];
|
|
2735
|
+
}
|
|
2736
|
+
function pixelsRgbToOklchScaled(pixels) {
|
|
2737
|
+
const out = new Array(pixels.length);
|
|
2738
|
+
for (let i = 0; i < pixels.length; i++) {
|
|
2739
|
+
const [r, g, b] = pixels[i];
|
|
2740
|
+
const { l, c, h } = rgbToOklch(r, g, b);
|
|
2741
|
+
out[i] = [
|
|
2742
|
+
Math.round(l * 255),
|
|
2743
|
+
Math.round(c / 0.4 * 255),
|
|
2744
|
+
Math.round(h / 360 * 255)
|
|
2745
|
+
];
|
|
2746
|
+
}
|
|
2747
|
+
return out;
|
|
2748
|
+
}
|
|
2749
|
+
function paletteOklchScaledToRgb(colors) {
|
|
2750
|
+
return colors.map(({ color: [ls, cs, hs], population }) => {
|
|
2751
|
+
const l = ls / 255;
|
|
2752
|
+
const c = cs / 255 * 0.4;
|
|
2753
|
+
const h = hs / 255 * 360;
|
|
2754
|
+
return { color: oklchToRgb(l, c, h), population };
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
var init_color_space = __esm({
|
|
2758
|
+
"src/color-space.ts"() {
|
|
2759
|
+
}
|
|
2760
|
+
});
|
|
2761
|
+
function rgbToHsl(r, g, b) {
|
|
2762
|
+
const r1 = r / 255;
|
|
2763
|
+
const g1 = g / 255;
|
|
2764
|
+
const b1 = b / 255;
|
|
2765
|
+
const max = Math.max(r1, g1, b1);
|
|
2766
|
+
const min = Math.min(r1, g1, b1);
|
|
2767
|
+
const l = (max + min) / 2;
|
|
2768
|
+
let h = 0;
|
|
2769
|
+
let s = 0;
|
|
2770
|
+
if (max !== min) {
|
|
2771
|
+
const d = max - min;
|
|
2772
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
2773
|
+
if (max === r1) {
|
|
2774
|
+
h = ((g1 - b1) / d + (g1 < b1 ? 6 : 0)) / 6;
|
|
2775
|
+
} else if (max === g1) {
|
|
2776
|
+
h = ((b1 - r1) / d + 2) / 6;
|
|
2777
|
+
} else {
|
|
2778
|
+
h = ((r1 - g1) / d + 4) / 6;
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
return {
|
|
2782
|
+
h: Math.round(h * 360),
|
|
2783
|
+
s: Math.round(s * 100),
|
|
2784
|
+
l: Math.round(l * 100)
|
|
2785
|
+
};
|
|
2786
|
+
}
|
|
2787
|
+
function relativeLuminance(r, g, b) {
|
|
2788
|
+
const toLinear = (c) => {
|
|
2789
|
+
const s = c / 255;
|
|
2790
|
+
return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
2791
|
+
};
|
|
2792
|
+
return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
|
|
2793
|
+
}
|
|
2794
|
+
function contrastRatio(l1, l2) {
|
|
2795
|
+
const lighter = Math.max(l1, l2);
|
|
2796
|
+
const darker = Math.min(l1, l2);
|
|
2797
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
2798
|
+
}
|
|
2799
|
+
function createColor(r, g, b, population, proportion = 0) {
|
|
2800
|
+
return new ColorImpl(r, g, b, population, proportion);
|
|
2801
|
+
}
|
|
2802
|
+
var ColorImpl;
|
|
2803
|
+
var init_color = __esm({
|
|
2804
|
+
"src/color.ts"() {
|
|
2805
|
+
init_color_space();
|
|
2806
|
+
ColorImpl = class {
|
|
2807
|
+
constructor(r, g, b, population, proportion) {
|
|
2808
|
+
this._r = r;
|
|
2809
|
+
this._g = g;
|
|
2810
|
+
this._b = b;
|
|
2811
|
+
this.population = population;
|
|
2812
|
+
this.proportion = proportion;
|
|
2813
|
+
}
|
|
2814
|
+
rgb() {
|
|
2815
|
+
return { r: this._r, g: this._g, b: this._b };
|
|
2816
|
+
}
|
|
2817
|
+
hex() {
|
|
2818
|
+
const toHex = (n) => n.toString(16).padStart(2, "0");
|
|
2819
|
+
return `#${toHex(this._r)}${toHex(this._g)}${toHex(this._b)}`;
|
|
2820
|
+
}
|
|
2821
|
+
hsl() {
|
|
2822
|
+
if (!this._hsl) {
|
|
2823
|
+
this._hsl = rgbToHsl(this._r, this._g, this._b);
|
|
2824
|
+
}
|
|
2825
|
+
return this._hsl;
|
|
2826
|
+
}
|
|
2827
|
+
oklch() {
|
|
2828
|
+
if (!this._oklch) {
|
|
2829
|
+
this._oklch = rgbToOklch(this._r, this._g, this._b);
|
|
2830
|
+
}
|
|
2831
|
+
return this._oklch;
|
|
2832
|
+
}
|
|
2833
|
+
css(format = "rgb") {
|
|
2834
|
+
switch (format) {
|
|
2835
|
+
case "hsl": {
|
|
2836
|
+
const { h, s, l } = this.hsl();
|
|
2837
|
+
return `hsl(${h}, ${s}%, ${l}%)`;
|
|
2838
|
+
}
|
|
2839
|
+
case "oklch": {
|
|
2840
|
+
const { l, c, h } = this.oklch();
|
|
2841
|
+
return `oklch(${l.toFixed(3)} ${c.toFixed(3)} ${h.toFixed(1)})`;
|
|
2842
|
+
}
|
|
2843
|
+
case "rgb":
|
|
2844
|
+
default:
|
|
2845
|
+
return `rgb(${this._r}, ${this._g}, ${this._b})`;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
array() {
|
|
2849
|
+
return [this._r, this._g, this._b];
|
|
2850
|
+
}
|
|
2851
|
+
toString() {
|
|
2852
|
+
return this.hex();
|
|
2853
|
+
}
|
|
2854
|
+
get textColor() {
|
|
2855
|
+
return this.isDark ? "#ffffff" : "#000000";
|
|
2856
|
+
}
|
|
2857
|
+
get luminance() {
|
|
2858
|
+
if (this._luminance === void 0) {
|
|
2859
|
+
this._luminance = relativeLuminance(this._r, this._g, this._b);
|
|
2860
|
+
}
|
|
2861
|
+
return this._luminance;
|
|
2862
|
+
}
|
|
2863
|
+
get isDark() {
|
|
2864
|
+
return this.luminance <= 0.179;
|
|
2865
|
+
}
|
|
2866
|
+
get isLight() {
|
|
2867
|
+
return !this.isDark;
|
|
2868
|
+
}
|
|
2869
|
+
get contrast() {
|
|
2870
|
+
if (!this._contrast) {
|
|
2871
|
+
const lum = this.luminance;
|
|
2872
|
+
const white = contrastRatio(lum, 1);
|
|
2873
|
+
const black = contrastRatio(lum, 0);
|
|
2874
|
+
const foreground = this.isDark ? createColor(255, 255, 255, 0, 0) : createColor(0, 0, 0, 0, 0);
|
|
2875
|
+
this._contrast = {
|
|
2876
|
+
white: Math.round(white * 100) / 100,
|
|
2877
|
+
black: Math.round(black * 100) / 100,
|
|
2878
|
+
foreground
|
|
2879
|
+
};
|
|
2880
|
+
}
|
|
2881
|
+
return this._contrast;
|
|
2882
|
+
}
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
});
|
|
2886
|
+
var pipeline_exports = {};
|
|
2887
|
+
__export(pipeline_exports, {
|
|
2888
|
+
computeFallbackColor: () => computeFallbackColor,
|
|
2889
|
+
createPixelArray: () => createPixelArray,
|
|
2890
|
+
extractPalette: () => extractPalette,
|
|
2891
|
+
validateOptions: () => validateOptions
|
|
2892
|
+
});
|
|
2893
|
+
function validateOptions(options) {
|
|
2894
|
+
let { colorCount, quality } = options;
|
|
2895
|
+
if (typeof colorCount === "undefined" || !Number.isInteger(colorCount)) {
|
|
2896
|
+
colorCount = 10;
|
|
2897
|
+
} else if (colorCount === 1) {
|
|
2898
|
+
throw new Error(
|
|
2899
|
+
"colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()"
|
|
2900
|
+
);
|
|
2901
|
+
} else {
|
|
2902
|
+
colorCount = Math.max(colorCount, 2);
|
|
2903
|
+
colorCount = Math.min(colorCount, 20);
|
|
2904
|
+
}
|
|
2905
|
+
if (typeof quality === "undefined" || !Number.isInteger(quality) || quality < 1) {
|
|
2906
|
+
quality = 10;
|
|
2907
|
+
}
|
|
2908
|
+
const ignoreWhite = options.ignoreWhite !== void 0 ? !!options.ignoreWhite : true;
|
|
2909
|
+
const whiteThreshold = typeof options.whiteThreshold === "number" ? options.whiteThreshold : 250;
|
|
2910
|
+
const alphaThreshold = typeof options.alphaThreshold === "number" ? options.alphaThreshold : 125;
|
|
2911
|
+
const minSaturation = typeof options.minSaturation === "number" ? Math.max(0, Math.min(1, options.minSaturation)) : 0;
|
|
2912
|
+
const colorSpace = options.colorSpace ?? "oklch";
|
|
2913
|
+
return {
|
|
2914
|
+
colorCount,
|
|
2915
|
+
quality,
|
|
2916
|
+
ignoreWhite,
|
|
2917
|
+
whiteThreshold,
|
|
2918
|
+
alphaThreshold,
|
|
2919
|
+
minSaturation,
|
|
2920
|
+
colorSpace
|
|
2921
|
+
};
|
|
2922
|
+
}
|
|
2923
|
+
function createPixelArray(data, pixelCount, quality, filterOptions) {
|
|
2924
|
+
const {
|
|
2925
|
+
ignoreWhite = true,
|
|
2926
|
+
whiteThreshold = 250,
|
|
2927
|
+
alphaThreshold = 125,
|
|
2928
|
+
minSaturation = 0
|
|
2929
|
+
} = filterOptions;
|
|
2930
|
+
const pixelArray = [];
|
|
2931
|
+
for (let i = 0; i < pixelCount; i += quality) {
|
|
2932
|
+
const offset = i * 4;
|
|
2933
|
+
const r = data[offset];
|
|
2934
|
+
const g = data[offset + 1];
|
|
2935
|
+
const b = data[offset + 2];
|
|
2936
|
+
const a = data[offset + 3];
|
|
2937
|
+
if (a !== void 0 && a < alphaThreshold) continue;
|
|
2938
|
+
if (ignoreWhite && r > whiteThreshold && g > whiteThreshold && b > whiteThreshold)
|
|
2939
|
+
continue;
|
|
2940
|
+
if (minSaturation > 0) {
|
|
2941
|
+
const max = Math.max(r, g, b);
|
|
2942
|
+
if (max === 0 || (max - Math.min(r, g, b)) / max < minSaturation)
|
|
2943
|
+
continue;
|
|
2944
|
+
}
|
|
2945
|
+
pixelArray.push([r, g, b]);
|
|
2946
|
+
}
|
|
2947
|
+
return pixelArray;
|
|
2948
|
+
}
|
|
2949
|
+
function computeFallbackColor(data, pixelCount, quality) {
|
|
2950
|
+
let rTotal = 0;
|
|
2951
|
+
let gTotal = 0;
|
|
2952
|
+
let bTotal = 0;
|
|
2953
|
+
let count = 0;
|
|
2954
|
+
for (let i = 0; i < pixelCount; i += quality) {
|
|
2955
|
+
const offset = i * 4;
|
|
2956
|
+
rTotal += data[offset];
|
|
2957
|
+
gTotal += data[offset + 1];
|
|
2958
|
+
bTotal += data[offset + 2];
|
|
2959
|
+
count++;
|
|
2960
|
+
}
|
|
2961
|
+
if (count === 0) return null;
|
|
2962
|
+
return [
|
|
2963
|
+
Math.round(rTotal / count),
|
|
2964
|
+
Math.round(gTotal / count),
|
|
2965
|
+
Math.round(bTotal / count)
|
|
2966
|
+
];
|
|
2967
|
+
}
|
|
2968
|
+
function extractPalette(data, width, height, opts, quantizer) {
|
|
2969
|
+
const pixelCount = width * height;
|
|
2970
|
+
const filterOptions = {
|
|
2971
|
+
ignoreWhite: opts.ignoreWhite,
|
|
2972
|
+
whiteThreshold: opts.whiteThreshold,
|
|
2973
|
+
alphaThreshold: opts.alphaThreshold,
|
|
2974
|
+
minSaturation: opts.minSaturation
|
|
2975
|
+
};
|
|
2976
|
+
let pixelArray = createPixelArray(data, pixelCount, opts.quality, filterOptions);
|
|
2977
|
+
if (pixelArray.length === 0) {
|
|
2978
|
+
pixelArray = createPixelArray(data, pixelCount, opts.quality, {
|
|
2979
|
+
...filterOptions,
|
|
2980
|
+
ignoreWhite: false
|
|
2981
|
+
});
|
|
2982
|
+
}
|
|
2983
|
+
if (pixelArray.length === 0) {
|
|
2984
|
+
pixelArray = createPixelArray(data, pixelCount, opts.quality, {
|
|
2985
|
+
...filterOptions,
|
|
2986
|
+
ignoreWhite: false,
|
|
2987
|
+
alphaThreshold: 0
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2990
|
+
let quantized;
|
|
2991
|
+
if (opts.colorSpace === "oklch") {
|
|
2992
|
+
const scaled = pixelsRgbToOklchScaled(pixelArray);
|
|
2993
|
+
quantized = paletteOklchScaledToRgb(
|
|
2994
|
+
quantizer.quantize(scaled, opts.colorCount)
|
|
2995
|
+
);
|
|
2996
|
+
} else {
|
|
2997
|
+
quantized = quantizer.quantize(pixelArray, opts.colorCount);
|
|
2998
|
+
}
|
|
2999
|
+
if (quantized.length > 0) {
|
|
3000
|
+
const totalPopulation = quantized.reduce((sum, q) => sum + q.population, 0);
|
|
3001
|
+
return quantized.map(
|
|
3002
|
+
({ color: [r, g, b], population }) => createColor(r, g, b, population, totalPopulation > 0 ? population / totalPopulation : 0)
|
|
3003
|
+
);
|
|
3004
|
+
}
|
|
3005
|
+
const fallback = computeFallbackColor(data, pixelCount, opts.quality);
|
|
3006
|
+
return fallback ? [createColor(fallback[0], fallback[1], fallback[2], 1, 1)] : null;
|
|
3007
|
+
}
|
|
3008
|
+
var init_pipeline = __esm({
|
|
3009
|
+
"src/pipeline.ts"() {
|
|
3010
|
+
init_color();
|
|
3011
|
+
init_color_space();
|
|
3012
|
+
}
|
|
3013
|
+
});
|
|
3014
|
+
var browser_exports = {};
|
|
3015
|
+
__export(browser_exports, {
|
|
3016
|
+
BrowserPixelLoader: () => BrowserPixelLoader
|
|
3017
|
+
});
|
|
3018
|
+
var BrowserPixelLoader;
|
|
3019
|
+
var init_browser = __esm({
|
|
3020
|
+
"src/loaders/browser.ts"() {
|
|
3021
|
+
BrowserPixelLoader = class {
|
|
3022
|
+
async load(source) {
|
|
3023
|
+
if (typeof HTMLImageElement !== "undefined" && source instanceof HTMLImageElement) {
|
|
3024
|
+
return this.loadFromImage(source);
|
|
3025
|
+
}
|
|
3026
|
+
if (typeof HTMLCanvasElement !== "undefined" && source instanceof HTMLCanvasElement) {
|
|
3027
|
+
return this.loadFromCanvas(source);
|
|
3028
|
+
}
|
|
3029
|
+
if (typeof ImageData !== "undefined" && source instanceof ImageData) {
|
|
3030
|
+
return {
|
|
3031
|
+
data: source.data,
|
|
3032
|
+
width: source.width,
|
|
3033
|
+
height: source.height
|
|
3034
|
+
};
|
|
3035
|
+
}
|
|
3036
|
+
if (typeof HTMLVideoElement !== "undefined" && source instanceof HTMLVideoElement) {
|
|
3037
|
+
return this.loadFromVideo(source);
|
|
3038
|
+
}
|
|
3039
|
+
if (typeof ImageBitmap !== "undefined" && source instanceof ImageBitmap) {
|
|
3040
|
+
return this.loadFromImageBitmap(source);
|
|
3041
|
+
}
|
|
3042
|
+
if (typeof OffscreenCanvas !== "undefined" && source instanceof OffscreenCanvas) {
|
|
3043
|
+
return this.loadFromOffscreenCanvas(source);
|
|
3044
|
+
}
|
|
3045
|
+
throw new Error(
|
|
3046
|
+
"Unsupported source type. Expected HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, ImageData, ImageBitmap, or OffscreenCanvas."
|
|
3047
|
+
);
|
|
3048
|
+
}
|
|
3049
|
+
loadFromImage(img) {
|
|
3050
|
+
if (!img.complete) {
|
|
3051
|
+
throw new Error(
|
|
3052
|
+
'Image has not finished loading. Wait for the "load" event before calling getColor/getPalette.'
|
|
3053
|
+
);
|
|
3054
|
+
}
|
|
3055
|
+
if (!img.naturalWidth) {
|
|
3056
|
+
throw new Error(
|
|
3057
|
+
"Image has no dimensions. It may not have loaded successfully."
|
|
3058
|
+
);
|
|
3059
|
+
}
|
|
3060
|
+
const canvas = document.createElement("canvas");
|
|
3061
|
+
const ctx = canvas.getContext("2d");
|
|
3062
|
+
const width = canvas.width = img.naturalWidth;
|
|
3063
|
+
const height = canvas.height = img.naturalHeight;
|
|
3064
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
3065
|
+
try {
|
|
3066
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
3067
|
+
return { data: imageData.data, width, height };
|
|
3068
|
+
} catch (e) {
|
|
3069
|
+
if (e instanceof DOMException && e.name === "SecurityError") {
|
|
3070
|
+
const err = new Error(
|
|
3071
|
+
'Image is tainted by cross-origin data. Add crossorigin="anonymous" to the <img> tag and ensure the server sends appropriate CORS headers.'
|
|
3072
|
+
);
|
|
3073
|
+
err.cause = e;
|
|
3074
|
+
throw err;
|
|
3075
|
+
}
|
|
3076
|
+
throw e;
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
loadFromCanvas(canvas) {
|
|
3080
|
+
const ctx = canvas.getContext("2d");
|
|
3081
|
+
const { width, height } = canvas;
|
|
3082
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
3083
|
+
return { data: imageData.data, width, height };
|
|
3084
|
+
}
|
|
3085
|
+
loadFromVideo(video) {
|
|
3086
|
+
if (video.readyState < 2) {
|
|
3087
|
+
throw new Error(
|
|
3088
|
+
'Video is not ready. Wait for the "loadeddata" or "canplay" event before calling getColor/getPalette.'
|
|
3089
|
+
);
|
|
3090
|
+
}
|
|
3091
|
+
const width = video.videoWidth;
|
|
3092
|
+
const height = video.videoHeight;
|
|
3093
|
+
if (!width || !height) {
|
|
3094
|
+
throw new Error(
|
|
3095
|
+
"Video has no dimensions. It may not have loaded successfully."
|
|
3096
|
+
);
|
|
3097
|
+
}
|
|
3098
|
+
const canvas = document.createElement("canvas");
|
|
3099
|
+
const ctx = canvas.getContext("2d");
|
|
3100
|
+
canvas.width = width;
|
|
3101
|
+
canvas.height = height;
|
|
3102
|
+
ctx.drawImage(video, 0, 0, width, height);
|
|
3103
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
3104
|
+
return { data: imageData.data, width, height };
|
|
3105
|
+
}
|
|
3106
|
+
loadFromOffscreenCanvas(canvas) {
|
|
3107
|
+
const ctx = canvas.getContext("2d");
|
|
3108
|
+
if (!ctx) {
|
|
3109
|
+
throw new Error(
|
|
3110
|
+
"Could not get 2D context from OffscreenCanvas."
|
|
3111
|
+
);
|
|
3112
|
+
}
|
|
3113
|
+
const { width, height } = canvas;
|
|
3114
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
3115
|
+
return { data: imageData.data, width, height };
|
|
3116
|
+
}
|
|
3117
|
+
loadFromImageBitmap(bitmap) {
|
|
3118
|
+
const canvas = document.createElement("canvas");
|
|
3119
|
+
const ctx = canvas.getContext("2d");
|
|
3120
|
+
canvas.width = bitmap.width;
|
|
3121
|
+
canvas.height = bitmap.height;
|
|
3122
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
3123
|
+
const imageData = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
|
|
3124
|
+
return { data: imageData.data, width: bitmap.width, height: bitmap.height };
|
|
3125
|
+
}
|
|
3126
|
+
};
|
|
3127
|
+
}
|
|
3128
|
+
});
|
|
3129
|
+
var node_exports = {};
|
|
3130
|
+
__export(node_exports, {
|
|
3131
|
+
NodePixelLoader: () => NodePixelLoader,
|
|
3132
|
+
createNodeLoader: () => createNodeLoader
|
|
3133
|
+
});
|
|
3134
|
+
function createNodeLoader(options) {
|
|
3135
|
+
return new NodePixelLoader(options);
|
|
3136
|
+
}
|
|
3137
|
+
var NodePixelLoader;
|
|
3138
|
+
var WORKER_SOURCE;
|
|
3139
|
+
var manager_exports = {};
|
|
3140
|
+
__export(manager_exports, {
|
|
3141
|
+
extractInWorker: () => extractInWorker,
|
|
3142
|
+
isWorkerSupported: () => isWorkerSupported,
|
|
3143
|
+
terminateWorker: () => terminateWorker
|
|
3144
|
+
});
|
|
3145
|
+
function isWorkerSupported() {
|
|
3146
|
+
return typeof Worker !== "undefined";
|
|
3147
|
+
}
|
|
3148
|
+
function getOrCreateWorker() {
|
|
3149
|
+
if (worker) return worker;
|
|
3150
|
+
if (!isWorkerSupported()) {
|
|
3151
|
+
throw new Error("Web Workers are not supported in this environment.");
|
|
3152
|
+
}
|
|
3153
|
+
blobUrl = URL.createObjectURL(
|
|
3154
|
+
new Blob([WORKER_SOURCE], { type: "application/javascript" })
|
|
3155
|
+
);
|
|
3156
|
+
worker = new Worker(blobUrl);
|
|
3157
|
+
worker.onmessage = (e) => {
|
|
3158
|
+
const { id, palette, error } = e.data;
|
|
3159
|
+
const entry = pending.get(id);
|
|
3160
|
+
if (!entry) return;
|
|
3161
|
+
pending.delete(id);
|
|
3162
|
+
if (error) {
|
|
3163
|
+
entry.reject(new Error(error));
|
|
3164
|
+
} else {
|
|
3165
|
+
const raw = palette;
|
|
3166
|
+
const totalPopulation = raw.reduce((sum, q) => sum + q.population, 0);
|
|
3167
|
+
const colors = raw.map(({ color: [r, g, b], population }) => createColor(r, g, b, population, totalPopulation > 0 ? population / totalPopulation : 0));
|
|
3168
|
+
entry.resolve(colors);
|
|
3169
|
+
}
|
|
3170
|
+
};
|
|
3171
|
+
worker.onerror = (e) => {
|
|
3172
|
+
for (const [, entry] of pending) {
|
|
3173
|
+
entry.reject(new Error(e.message));
|
|
3174
|
+
}
|
|
3175
|
+
pending.clear();
|
|
3176
|
+
};
|
|
3177
|
+
return worker;
|
|
3178
|
+
}
|
|
3179
|
+
function extractInWorker(pixels, maxColors, signal) {
|
|
3180
|
+
return new Promise((resolve, reject) => {
|
|
3181
|
+
if (signal?.aborted) {
|
|
3182
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
3183
|
+
return;
|
|
3184
|
+
}
|
|
3185
|
+
const id = nextId++;
|
|
3186
|
+
pending.set(id, { resolve, reject });
|
|
3187
|
+
const onAbort = () => {
|
|
3188
|
+
pending.delete(id);
|
|
3189
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
3190
|
+
};
|
|
3191
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3192
|
+
try {
|
|
3193
|
+
const w = getOrCreateWorker();
|
|
3194
|
+
w.postMessage({ id, pixels, maxColors });
|
|
3195
|
+
} catch (err) {
|
|
3196
|
+
pending.delete(id);
|
|
3197
|
+
signal?.removeEventListener("abort", onAbort);
|
|
3198
|
+
reject(err);
|
|
3199
|
+
}
|
|
3200
|
+
});
|
|
3201
|
+
}
|
|
3202
|
+
function terminateWorker() {
|
|
3203
|
+
if (worker) {
|
|
3204
|
+
worker.terminate();
|
|
3205
|
+
worker = null;
|
|
3206
|
+
}
|
|
3207
|
+
if (blobUrl) {
|
|
3208
|
+
URL.revokeObjectURL(blobUrl);
|
|
3209
|
+
blobUrl = null;
|
|
3210
|
+
}
|
|
3211
|
+
for (const [, entry] of pending) {
|
|
3212
|
+
entry.reject(new Error("Worker terminated"));
|
|
3213
|
+
}
|
|
3214
|
+
pending.clear();
|
|
3215
|
+
}
|
|
3216
|
+
var worker;
|
|
3217
|
+
var blobUrl;
|
|
3218
|
+
var nextId;
|
|
3219
|
+
var pending;
|
|
3220
|
+
init_pipeline();
|
|
3221
|
+
init_pipeline();
|
|
3222
|
+
init_color();
|
|
3223
|
+
createColor(255, 255, 255, 0);
|
|
3224
|
+
createColor(0, 0, 0, 0);
|
|
3225
|
+
var SIGBITS = 5;
|
|
3226
|
+
var RSHIFT = 8 - SIGBITS;
|
|
3227
|
+
var MAX_ITERATIONS = 1e3;
|
|
3228
|
+
var FRACT_BY_POPULATIONS = 0.75;
|
|
3229
|
+
var HISTO_SIZE = 1 << 3 * SIGBITS;
|
|
3230
|
+
function getColorIndex(r, g, b) {
|
|
3231
|
+
return (r << 2 * SIGBITS) + (g << SIGBITS) + b;
|
|
3232
|
+
}
|
|
3233
|
+
var VBox = class _VBox {
|
|
3234
|
+
constructor(r1, r2, g1, g2, b1, b2, histo) {
|
|
3235
|
+
this.r1 = r1;
|
|
3236
|
+
this.r2 = r2;
|
|
3237
|
+
this.g1 = g1;
|
|
3238
|
+
this.g2 = g2;
|
|
3239
|
+
this.b1 = b1;
|
|
3240
|
+
this.b2 = b2;
|
|
3241
|
+
this.histo = histo;
|
|
3242
|
+
}
|
|
3243
|
+
volume(force = false) {
|
|
3244
|
+
if (this._volume === void 0 || force) {
|
|
3245
|
+
this._volume = (this.r2 - this.r1 + 1) * (this.g2 - this.g1 + 1) * (this.b2 - this.b1 + 1);
|
|
3246
|
+
}
|
|
3247
|
+
return this._volume;
|
|
3248
|
+
}
|
|
3249
|
+
count(force = false) {
|
|
3250
|
+
if (this._count === void 0 || force) {
|
|
3251
|
+
let npix = 0;
|
|
3252
|
+
for (let i = this.r1; i <= this.r2; i++) {
|
|
3253
|
+
for (let j = this.g1; j <= this.g2; j++) {
|
|
3254
|
+
for (let k = this.b1; k <= this.b2; k++) {
|
|
3255
|
+
npix += this.histo[getColorIndex(i, j, k)] || 0;
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
this._count = npix;
|
|
3260
|
+
}
|
|
3261
|
+
return this._count;
|
|
3262
|
+
}
|
|
3263
|
+
copy() {
|
|
3264
|
+
return new _VBox(this.r1, this.r2, this.g1, this.g2, this.b1, this.b2, this.histo);
|
|
3265
|
+
}
|
|
3266
|
+
avg(force = false) {
|
|
3267
|
+
if (this._avg === void 0 || force) {
|
|
3268
|
+
const mult = 1 << RSHIFT;
|
|
3269
|
+
if (this.r1 === this.r2 && this.g1 === this.g2 && this.b1 === this.b2) {
|
|
3270
|
+
this._avg = [
|
|
3271
|
+
this.r1 << RSHIFT,
|
|
3272
|
+
this.g1 << RSHIFT,
|
|
3273
|
+
this.b1 << RSHIFT
|
|
3274
|
+
];
|
|
3275
|
+
} else {
|
|
3276
|
+
let ntot = 0;
|
|
3277
|
+
let rsum = 0;
|
|
3278
|
+
let gsum = 0;
|
|
3279
|
+
let bsum = 0;
|
|
3280
|
+
for (let i = this.r1; i <= this.r2; i++) {
|
|
3281
|
+
for (let j = this.g1; j <= this.g2; j++) {
|
|
3282
|
+
for (let k = this.b1; k <= this.b2; k++) {
|
|
3283
|
+
const hval = this.histo[getColorIndex(i, j, k)] || 0;
|
|
3284
|
+
ntot += hval;
|
|
3285
|
+
rsum += hval * (i + 0.5) * mult;
|
|
3286
|
+
gsum += hval * (j + 0.5) * mult;
|
|
3287
|
+
bsum += hval * (k + 0.5) * mult;
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
if (ntot) {
|
|
3292
|
+
this._avg = [
|
|
3293
|
+
~~(rsum / ntot),
|
|
3294
|
+
~~(gsum / ntot),
|
|
3295
|
+
~~(bsum / ntot)
|
|
3296
|
+
];
|
|
3297
|
+
} else {
|
|
3298
|
+
this._avg = [
|
|
3299
|
+
~~(mult * (this.r1 + this.r2 + 1) / 2),
|
|
3300
|
+
~~(mult * (this.g1 + this.g2 + 1) / 2),
|
|
3301
|
+
~~(mult * (this.b1 + this.b2 + 1) / 2)
|
|
3302
|
+
];
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
return this._avg;
|
|
3307
|
+
}
|
|
3308
|
+
};
|
|
3309
|
+
var PQueue = class {
|
|
3310
|
+
constructor(comparator) {
|
|
3311
|
+
this.comparator = comparator;
|
|
3312
|
+
this.contents = [];
|
|
3313
|
+
this.sorted = false;
|
|
3314
|
+
}
|
|
3315
|
+
sort() {
|
|
3316
|
+
this.contents.sort(this.comparator);
|
|
3317
|
+
this.sorted = true;
|
|
3318
|
+
}
|
|
3319
|
+
push(item) {
|
|
3320
|
+
this.contents.push(item);
|
|
3321
|
+
this.sorted = false;
|
|
3322
|
+
}
|
|
3323
|
+
peek(index) {
|
|
3324
|
+
if (!this.sorted) this.sort();
|
|
3325
|
+
return this.contents[index ?? this.contents.length - 1];
|
|
3326
|
+
}
|
|
3327
|
+
pop() {
|
|
3328
|
+
if (!this.sorted) this.sort();
|
|
3329
|
+
return this.contents.pop();
|
|
3330
|
+
}
|
|
3331
|
+
size() {
|
|
3332
|
+
return this.contents.length;
|
|
3333
|
+
}
|
|
3334
|
+
map(fn) {
|
|
3335
|
+
return this.contents.map(fn);
|
|
3336
|
+
}
|
|
3337
|
+
};
|
|
3338
|
+
function getHisto(pixels) {
|
|
3339
|
+
const histo = new Uint32Array(HISTO_SIZE);
|
|
3340
|
+
for (const pixel of pixels) {
|
|
3341
|
+
const rval = pixel[0] >> RSHIFT;
|
|
3342
|
+
const gval = pixel[1] >> RSHIFT;
|
|
3343
|
+
const bval = pixel[2] >> RSHIFT;
|
|
3344
|
+
histo[getColorIndex(rval, gval, bval)]++;
|
|
3345
|
+
}
|
|
3346
|
+
return histo;
|
|
3347
|
+
}
|
|
3348
|
+
function vboxFromPixels(pixels, histo) {
|
|
3349
|
+
let rmin = 1e6;
|
|
3350
|
+
let rmax = 0;
|
|
3351
|
+
let gmin = 1e6;
|
|
3352
|
+
let gmax = 0;
|
|
3353
|
+
let bmin = 1e6;
|
|
3354
|
+
let bmax = 0;
|
|
3355
|
+
for (const pixel of pixels) {
|
|
3356
|
+
const rval = pixel[0] >> RSHIFT;
|
|
3357
|
+
const gval = pixel[1] >> RSHIFT;
|
|
3358
|
+
const bval = pixel[2] >> RSHIFT;
|
|
3359
|
+
if (rval < rmin) rmin = rval;
|
|
3360
|
+
else if (rval > rmax) rmax = rval;
|
|
3361
|
+
if (gval < gmin) gmin = gval;
|
|
3362
|
+
else if (gval > gmax) gmax = gval;
|
|
3363
|
+
if (bval < bmin) bmin = bval;
|
|
3364
|
+
else if (bval > bmax) bmax = bval;
|
|
3365
|
+
}
|
|
3366
|
+
return new VBox(rmin, rmax, gmin, gmax, bmin, bmax, histo);
|
|
3367
|
+
}
|
|
3368
|
+
function medianCutApply(histo, vbox) {
|
|
3369
|
+
if (!vbox.count()) return void 0;
|
|
3370
|
+
if (vbox.count() === 1) return [vbox.copy(), null];
|
|
3371
|
+
const rw = vbox.r2 - vbox.r1 + 1;
|
|
3372
|
+
const gw = vbox.g2 - vbox.g1 + 1;
|
|
3373
|
+
const bw = vbox.b2 - vbox.b1 + 1;
|
|
3374
|
+
const maxw = Math.max(rw, gw, bw);
|
|
3375
|
+
let total = 0;
|
|
3376
|
+
const partialsum = [];
|
|
3377
|
+
const lookaheadsum = [];
|
|
3378
|
+
if (maxw === rw) {
|
|
3379
|
+
for (let i = vbox.r1; i <= vbox.r2; i++) {
|
|
3380
|
+
let sum = 0;
|
|
3381
|
+
for (let j = vbox.g1; j <= vbox.g2; j++) {
|
|
3382
|
+
for (let k = vbox.b1; k <= vbox.b2; k++) {
|
|
3383
|
+
sum += histo[getColorIndex(i, j, k)] || 0;
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
total += sum;
|
|
3387
|
+
partialsum[i] = total;
|
|
3388
|
+
}
|
|
3389
|
+
} else if (maxw === gw) {
|
|
3390
|
+
for (let i = vbox.g1; i <= vbox.g2; i++) {
|
|
3391
|
+
let sum = 0;
|
|
3392
|
+
for (let j = vbox.r1; j <= vbox.r2; j++) {
|
|
3393
|
+
for (let k = vbox.b1; k <= vbox.b2; k++) {
|
|
3394
|
+
sum += histo[getColorIndex(j, i, k)] || 0;
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
total += sum;
|
|
3398
|
+
partialsum[i] = total;
|
|
3399
|
+
}
|
|
3400
|
+
} else {
|
|
3401
|
+
for (let i = vbox.b1; i <= vbox.b2; i++) {
|
|
3402
|
+
let sum = 0;
|
|
3403
|
+
for (let j = vbox.r1; j <= vbox.r2; j++) {
|
|
3404
|
+
for (let k = vbox.g1; k <= vbox.g2; k++) {
|
|
3405
|
+
sum += histo[getColorIndex(j, k, i)] || 0;
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
total += sum;
|
|
3409
|
+
partialsum[i] = total;
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
partialsum.forEach((d, i) => {
|
|
3413
|
+
lookaheadsum[i] = total - d;
|
|
3414
|
+
});
|
|
3415
|
+
function doCut(color) {
|
|
3416
|
+
const dim1 = color + "1";
|
|
3417
|
+
const dim2 = color + "2";
|
|
3418
|
+
for (let i = vbox[dim1]; i <= vbox[dim2]; i++) {
|
|
3419
|
+
if (partialsum[i] > total / 2) {
|
|
3420
|
+
const vbox1 = vbox.copy();
|
|
3421
|
+
const vbox2 = vbox.copy();
|
|
3422
|
+
const left = i - vbox[dim1];
|
|
3423
|
+
const right = vbox[dim2] - i;
|
|
3424
|
+
let d2;
|
|
3425
|
+
if (left <= right) {
|
|
3426
|
+
d2 = Math.min(vbox[dim2] - 1, ~~(i + right / 2));
|
|
3427
|
+
} else {
|
|
3428
|
+
d2 = Math.max(vbox[dim1], ~~(i - 1 - left / 2));
|
|
3429
|
+
}
|
|
3430
|
+
while (!partialsum[d2]) d2++;
|
|
3431
|
+
let count2 = lookaheadsum[d2];
|
|
3432
|
+
while (!count2 && partialsum[d2 - 1]) count2 = lookaheadsum[--d2];
|
|
3433
|
+
vbox1[dim2] = d2;
|
|
3434
|
+
vbox2[dim1] = vbox1[dim2] + 1;
|
|
3435
|
+
return [vbox1, vbox2];
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
return void 0;
|
|
3439
|
+
}
|
|
3440
|
+
if (maxw === rw) return doCut("r");
|
|
3441
|
+
if (maxw === gw) return doCut("g");
|
|
3442
|
+
return doCut("b");
|
|
3443
|
+
}
|
|
3444
|
+
function iterate(pq, target, histo) {
|
|
3445
|
+
let ncolors = pq.size();
|
|
3446
|
+
let niters = 0;
|
|
3447
|
+
while (niters < MAX_ITERATIONS) {
|
|
3448
|
+
if (ncolors >= target) return;
|
|
3449
|
+
niters++;
|
|
3450
|
+
const vbox = pq.pop();
|
|
3451
|
+
if (!vbox.count()) {
|
|
3452
|
+
pq.push(vbox);
|
|
3453
|
+
continue;
|
|
3454
|
+
}
|
|
3455
|
+
const result = medianCutApply(histo, vbox);
|
|
3456
|
+
if (!result || !result[0]) return;
|
|
3457
|
+
pq.push(result[0]);
|
|
3458
|
+
if (result[1]) {
|
|
3459
|
+
pq.push(result[1]);
|
|
3460
|
+
ncolors++;
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
function quantize(pixels, maxColors) {
|
|
3465
|
+
if (!pixels.length || maxColors < 2 || maxColors > 256) return [];
|
|
3466
|
+
const seenColors = /* @__PURE__ */ new Set();
|
|
3467
|
+
const uniqueColors = [];
|
|
3468
|
+
for (const color of pixels) {
|
|
3469
|
+
const key = color.join(",");
|
|
3470
|
+
if (!seenColors.has(key)) {
|
|
3471
|
+
seenColors.add(key);
|
|
3472
|
+
uniqueColors.push(color);
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
if (uniqueColors.length <= maxColors) {
|
|
3476
|
+
const countMap = /* @__PURE__ */ new Map();
|
|
3477
|
+
for (const color of pixels) {
|
|
3478
|
+
const key = color.join(",");
|
|
3479
|
+
countMap.set(key, (countMap.get(key) || 0) + 1);
|
|
3480
|
+
}
|
|
3481
|
+
return uniqueColors.map((color) => ({
|
|
3482
|
+
color,
|
|
3483
|
+
population: countMap.get(color.join(","))
|
|
3484
|
+
}));
|
|
3485
|
+
}
|
|
3486
|
+
const histo = getHisto(pixels);
|
|
3487
|
+
const vbox = vboxFromPixels(pixels, histo);
|
|
3488
|
+
const pq = new PQueue((a, b) => a.count() - b.count());
|
|
3489
|
+
pq.push(vbox);
|
|
3490
|
+
iterate(pq, FRACT_BY_POPULATIONS * maxColors, histo);
|
|
3491
|
+
const pq2 = new PQueue((a, b) => a.count() * a.volume() - b.count() * b.volume());
|
|
3492
|
+
while (pq.size()) {
|
|
3493
|
+
pq2.push(pq.pop());
|
|
3494
|
+
}
|
|
3495
|
+
iterate(pq2, maxColors, histo);
|
|
3496
|
+
const results = [];
|
|
3497
|
+
while (pq2.size()) {
|
|
3498
|
+
const box = pq2.pop();
|
|
3499
|
+
results.push({
|
|
3500
|
+
color: box.avg(),
|
|
3501
|
+
population: box.count()
|
|
3502
|
+
});
|
|
3503
|
+
}
|
|
3504
|
+
return results;
|
|
3505
|
+
}
|
|
3506
|
+
var MmcqQuantizer = class {
|
|
3507
|
+
async init() {
|
|
3508
|
+
}
|
|
3509
|
+
quantize(pixels, maxColors) {
|
|
3510
|
+
return quantize(pixels, maxColors);
|
|
3511
|
+
}
|
|
3512
|
+
};
|
|
3513
|
+
init_browser();
|
|
3514
|
+
init_pipeline();
|
|
3515
|
+
new BrowserPixelLoader();
|
|
3516
|
+
var defaultQuantizer = new MmcqQuantizer();
|
|
3517
|
+
function getColorSync(source, options) {
|
|
3518
|
+
const palette = getPaletteSync(source, { colorCount: 5, ...options });
|
|
3519
|
+
return palette ? palette[0] : null;
|
|
3520
|
+
}
|
|
3521
|
+
function getPaletteSync(source, options) {
|
|
3522
|
+
const opts = validateOptions(options ?? {});
|
|
3523
|
+
const quantizer = options?.quantizer ?? defaultQuantizer;
|
|
3524
|
+
const pixels = loadPixelsSync(source);
|
|
3525
|
+
return extractPalette(
|
|
3526
|
+
pixels.data,
|
|
3527
|
+
pixels.width,
|
|
3528
|
+
pixels.height,
|
|
3529
|
+
opts,
|
|
3530
|
+
quantizer
|
|
3531
|
+
);
|
|
3532
|
+
}
|
|
3533
|
+
function loadPixelsSync(source) {
|
|
3534
|
+
if (typeof HTMLImageElement !== "undefined" && source instanceof HTMLImageElement) {
|
|
3535
|
+
return loadFromImage(source);
|
|
3536
|
+
}
|
|
3537
|
+
if (typeof HTMLCanvasElement !== "undefined" && source instanceof HTMLCanvasElement) {
|
|
3538
|
+
return loadFromCanvas(source);
|
|
3539
|
+
}
|
|
3540
|
+
if (typeof ImageData !== "undefined" && source instanceof ImageData) {
|
|
3541
|
+
return { data: source.data, width: source.width, height: source.height };
|
|
3542
|
+
}
|
|
3543
|
+
if (typeof HTMLVideoElement !== "undefined" && source instanceof HTMLVideoElement) {
|
|
3544
|
+
return loadFromVideo(source);
|
|
3545
|
+
}
|
|
3546
|
+
if (typeof ImageBitmap !== "undefined" && source instanceof ImageBitmap) {
|
|
3547
|
+
return loadFromImageBitmap(source);
|
|
3548
|
+
}
|
|
3549
|
+
if (typeof OffscreenCanvas !== "undefined" && source instanceof OffscreenCanvas) {
|
|
3550
|
+
return loadFromOffscreenCanvas(source);
|
|
3551
|
+
}
|
|
3552
|
+
throw new Error(
|
|
3553
|
+
"Unsupported source type. Expected HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, ImageData, ImageBitmap, or OffscreenCanvas."
|
|
3554
|
+
);
|
|
3555
|
+
}
|
|
3556
|
+
function loadFromImage(img) {
|
|
3557
|
+
if (!img.complete) {
|
|
3558
|
+
throw new Error(
|
|
3559
|
+
'Image has not finished loading. Wait for the "load" event before calling getColorSync/getPaletteSync.'
|
|
3560
|
+
);
|
|
3561
|
+
}
|
|
3562
|
+
if (!img.naturalWidth) {
|
|
3563
|
+
throw new Error(
|
|
3564
|
+
"Image has no dimensions. It may not have loaded successfully."
|
|
3565
|
+
);
|
|
3566
|
+
}
|
|
3567
|
+
const canvas = document.createElement("canvas");
|
|
3568
|
+
const ctx = canvas.getContext("2d");
|
|
3569
|
+
const width = canvas.width = img.naturalWidth;
|
|
3570
|
+
const height = canvas.height = img.naturalHeight;
|
|
3571
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
3572
|
+
try {
|
|
3573
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
3574
|
+
return { data: imageData.data, width, height };
|
|
3575
|
+
} catch (e) {
|
|
3576
|
+
if (e instanceof DOMException && e.name === "SecurityError") {
|
|
3577
|
+
const err = new Error(
|
|
3578
|
+
'Image is tainted by cross-origin data. Add crossorigin="anonymous" to the <img> tag and ensure the server sends appropriate CORS headers.'
|
|
3579
|
+
);
|
|
3580
|
+
err.cause = e;
|
|
3581
|
+
throw err;
|
|
3582
|
+
}
|
|
3583
|
+
throw e;
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
function loadFromCanvas(canvas) {
|
|
3587
|
+
const ctx = canvas.getContext("2d");
|
|
3588
|
+
const { width, height } = canvas;
|
|
3589
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
3590
|
+
return { data: imageData.data, width, height };
|
|
3591
|
+
}
|
|
3592
|
+
function loadFromVideo(video) {
|
|
3593
|
+
if (video.readyState < 2) {
|
|
3594
|
+
throw new Error(
|
|
3595
|
+
'Video is not ready. Wait for the "loadeddata" or "canplay" event before calling getColorSync/getPaletteSync.'
|
|
3596
|
+
);
|
|
3597
|
+
}
|
|
3598
|
+
const width = video.videoWidth;
|
|
3599
|
+
const height = video.videoHeight;
|
|
3600
|
+
if (!width || !height) {
|
|
3601
|
+
throw new Error(
|
|
3602
|
+
"Video has no dimensions. It may not have loaded successfully."
|
|
3603
|
+
);
|
|
3604
|
+
}
|
|
3605
|
+
const canvas = document.createElement("canvas");
|
|
3606
|
+
const ctx = canvas.getContext("2d");
|
|
3607
|
+
canvas.width = width;
|
|
3608
|
+
canvas.height = height;
|
|
3609
|
+
ctx.drawImage(video, 0, 0, width, height);
|
|
3610
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
3611
|
+
return { data: imageData.data, width, height };
|
|
3612
|
+
}
|
|
3613
|
+
function loadFromOffscreenCanvas(canvas) {
|
|
3614
|
+
const ctx = canvas.getContext("2d");
|
|
3615
|
+
if (!ctx) {
|
|
3616
|
+
throw new Error(
|
|
3617
|
+
"Could not get 2D context from OffscreenCanvas."
|
|
3618
|
+
);
|
|
3619
|
+
}
|
|
3620
|
+
const { width, height } = canvas;
|
|
3621
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
3622
|
+
return { data: imageData.data, width, height };
|
|
3623
|
+
}
|
|
3624
|
+
function loadFromImageBitmap(bitmap) {
|
|
3625
|
+
const canvas = document.createElement("canvas");
|
|
3626
|
+
const ctx = canvas.getContext("2d");
|
|
3627
|
+
canvas.width = bitmap.width;
|
|
3628
|
+
canvas.height = bitmap.height;
|
|
3629
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
3630
|
+
const imageData = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
|
|
3631
|
+
return { data: imageData.data, width: bitmap.width, height: bitmap.height };
|
|
3632
|
+
}
|
|
3633
|
+
init_color();
|
|
3634
|
+
|
|
3635
|
+
// src/preferences/helpers/getDominantColor.ts
|
|
3636
|
+
var getDominantColor = (img) => {
|
|
3637
|
+
return new Promise((resolve, reject) => {
|
|
3638
|
+
if (!img.complete || img.naturalWidth === 0) {
|
|
3639
|
+
reject(new Error("Image not loaded"));
|
|
3640
|
+
return;
|
|
3641
|
+
}
|
|
3642
|
+
try {
|
|
3643
|
+
const color = getColorSync(img);
|
|
3644
|
+
if (color) {
|
|
3645
|
+
const hex = color.hex();
|
|
3646
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(hex)) {
|
|
3647
|
+
reject(new Error(`Invalid color extracted: ${hex}`));
|
|
3648
|
+
} else {
|
|
3649
|
+
resolve(hex);
|
|
3650
|
+
}
|
|
3651
|
+
} else {
|
|
3652
|
+
reject(new Error("No color extracted"));
|
|
3653
|
+
}
|
|
3654
|
+
} catch (error) {
|
|
3655
|
+
reject(new Error(`Color extraction failed: ${error.message}`));
|
|
3656
|
+
}
|
|
3657
|
+
});
|
|
3658
|
+
};
|
|
3659
|
+
|
|
3660
|
+
// src/preferences/helpers/themeGeneration.ts
|
|
3661
|
+
var hexToHsl = (hex) => {
|
|
3662
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
3663
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
3664
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
3665
|
+
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
3666
|
+
const l = (max + min) / 2;
|
|
3667
|
+
if (max === min) return [0, 0, l];
|
|
3668
|
+
const d = max - min;
|
|
3669
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
3670
|
+
const h = max === r ? ((g - b) / d + (g < b ? 6 : 0)) / 6 : max === g ? ((b - r) / d + 2) / 6 : ((r - g) / d + 4) / 6;
|
|
3671
|
+
return [h, s, l];
|
|
3672
|
+
};
|
|
3673
|
+
var hslToHex = (h, s, l) => {
|
|
3674
|
+
const a = s * Math.min(l, 1 - l);
|
|
3675
|
+
const f = (n) => {
|
|
3676
|
+
const k = (n + h * 12) % 12;
|
|
3677
|
+
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
|
3678
|
+
return Math.round(255 * color).toString(16).padStart(2, "0");
|
|
3679
|
+
};
|
|
3680
|
+
return `#${f(0)}${f(8)}${f(4)}`;
|
|
3681
|
+
};
|
|
3682
|
+
var luminance = (hex) => {
|
|
3683
|
+
const toLinear = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
3684
|
+
const r = toLinear(parseInt(hex.slice(1, 3), 16) / 255);
|
|
3685
|
+
const g = toLinear(parseInt(hex.slice(3, 5), 16) / 255);
|
|
3686
|
+
const b = toLinear(parseInt(hex.slice(5, 7), 16) / 255);
|
|
3687
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
3688
|
+
};
|
|
3689
|
+
var contrastRatio2 = (a, b) => {
|
|
3690
|
+
const l1 = luminance(a), l2 = luminance(b);
|
|
3691
|
+
return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
|
|
3692
|
+
};
|
|
3693
|
+
var isLightColor = (hexColor) => luminance(hexColor) > 0.179;
|
|
3694
|
+
var shadeColor = (hex, isLight) => {
|
|
3695
|
+
const [h, s] = hexToHsl(hex);
|
|
3696
|
+
return isLight ? hslToHex(h, Math.min(s, 0.25), 0.93) : hslToHex(h, Math.min(s, 0.3), 0.13);
|
|
3697
|
+
};
|
|
3698
|
+
var generateThemeFromColor = (color) => {
|
|
3699
|
+
const [h, s] = hexToHsl(color);
|
|
3700
|
+
const isLight = isLightColor(color);
|
|
3701
|
+
const background = shadeColor(color, isLight);
|
|
3702
|
+
const text = contrastRatio2(background, "#ffffff") >= contrastRatio2(background, "#000000") ? "#ffffff" : "#000000";
|
|
3703
|
+
const subdue = isLight ? hslToHex(h, Math.min(s, 0.15), 0.55) : hslToHex(h, Math.min(s, 0.15), 0.55);
|
|
3704
|
+
const hover = isLight ? hslToHex(h, Math.min(s, 0.2), 0.83) : hslToHex(h, Math.min(s, 0.2), 0.22);
|
|
3705
|
+
const elevate = `0px 0px 2px ${isLight ? hslToHex(h, 0.1, 0.7) : hslToHex(h, 0.1, 0.08)}`;
|
|
3706
|
+
const link = isLight ? hslToHex(h, 0.7, 0.35) : hslToHex(h, 0.8, 0.72);
|
|
3707
|
+
const visited = isLight ? hslToHex(h, 0.5, 0.28) : hslToHex(h, 0.55, 0.6);
|
|
3708
|
+
const select = hslToHex(h, 0.6, isLight ? 0.8 : 0.35);
|
|
3709
|
+
const focus = hslToHex(h, 0.9, isLight ? 0.4 : 0.65);
|
|
3710
|
+
return {
|
|
3711
|
+
background,
|
|
3712
|
+
text,
|
|
3713
|
+
link,
|
|
3714
|
+
visited,
|
|
3715
|
+
subdue,
|
|
3716
|
+
disable: subdue,
|
|
3717
|
+
hover,
|
|
3718
|
+
onHover: text,
|
|
3719
|
+
select,
|
|
3720
|
+
onSelect: "inherit",
|
|
3721
|
+
focus,
|
|
3722
|
+
elevate,
|
|
3723
|
+
immerse: isLight ? "0.6" : "0.4"
|
|
3724
|
+
};
|
|
3725
|
+
};
|
|
3726
|
+
var extractThemeFromImage = async (imageUrl) => {
|
|
3727
|
+
const img = new Image();
|
|
3728
|
+
img.crossOrigin = "anonymous";
|
|
3729
|
+
await new Promise((resolve, reject) => {
|
|
3730
|
+
img.onload = () => {
|
|
3731
|
+
if (img.naturalWidth === 0) {
|
|
3732
|
+
reject(new Error("Image loaded but has no content (blocked or corrupt)"));
|
|
3733
|
+
} else {
|
|
3734
|
+
resolve();
|
|
3735
|
+
}
|
|
3736
|
+
};
|
|
3737
|
+
img.onerror = () => reject(new Error(`Failed to load image: ${imageUrl}`));
|
|
3738
|
+
img.src = imageUrl;
|
|
3739
|
+
});
|
|
3740
|
+
const dominantColor = await getDominantColor(img);
|
|
3741
|
+
return generateThemeFromColor(dominantColor);
|
|
3742
|
+
};
|
|
3743
|
+
|
|
3744
|
+
// src/helpers/proxyUrl.ts
|
|
3745
|
+
var proxyUrl = (url) => {
|
|
3746
|
+
if (!url) return void 0;
|
|
3747
|
+
try {
|
|
3748
|
+
const parsed = new URL(url);
|
|
3749
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
3750
|
+
return `/api/proxy?url=${encodeURIComponent(url)}`;
|
|
3751
|
+
}
|
|
3752
|
+
} catch {
|
|
3753
|
+
}
|
|
3754
|
+
return url;
|
|
3755
|
+
};
|
|
3756
|
+
var initRanges = (prefs) => {
|
|
3757
|
+
const ranges = {};
|
|
3758
|
+
let prev = null;
|
|
3759
|
+
Object.entries(prefs).forEach(([key, value]) => {
|
|
3760
|
+
if (value && !isNaN(value)) {
|
|
3761
|
+
const max = value;
|
|
3762
|
+
const min = prev ? prev + 1 : null;
|
|
3763
|
+
ranges[key] = { min, max };
|
|
3764
|
+
prev = value;
|
|
3765
|
+
} else if (!value && key === "xLarge" /* xLarge */ && prev) {
|
|
3766
|
+
ranges[key] = { min: prev + 1, max: null };
|
|
3767
|
+
}
|
|
3768
|
+
});
|
|
3769
|
+
return ranges;
|
|
3770
|
+
};
|
|
3771
|
+
var resolveBreakpoint = (width, ranges) => {
|
|
3772
|
+
for (const [key, range] of Object.entries(ranges)) {
|
|
3773
|
+
const { min, max } = range;
|
|
3774
|
+
if (min !== null && width < min) continue;
|
|
3775
|
+
if (max !== null && width > max) continue;
|
|
3776
|
+
return key;
|
|
3777
|
+
}
|
|
3778
|
+
return null;
|
|
3779
|
+
};
|
|
3780
|
+
var useContainerBreakpoints = (map, onChange) => {
|
|
3781
|
+
const [containerEl, setContainerEl] = useState(null);
|
|
3782
|
+
useEffect(() => {
|
|
3783
|
+
if (!containerEl) return;
|
|
3784
|
+
const ranges = initRanges(map);
|
|
3785
|
+
const observer = new ResizeObserver((entries) => {
|
|
3786
|
+
const entry = entries[0];
|
|
3787
|
+
if (!entry) return;
|
|
3788
|
+
const width = entry.contentRect.width;
|
|
3789
|
+
const breakpoint = resolveBreakpoint(width, ranges);
|
|
3790
|
+
onChange?.(breakpoint);
|
|
3791
|
+
});
|
|
3792
|
+
observer.observe(containerEl);
|
|
3793
|
+
return () => observer.disconnect();
|
|
3794
|
+
}, [containerEl, map, onChange]);
|
|
3795
|
+
return useCallback((el) => {
|
|
3796
|
+
setContainerEl(el);
|
|
3797
|
+
}, []);
|
|
3798
|
+
};
|
|
3799
|
+
|
|
3800
|
+
// src/preferences/hooks/useTheming.ts
|
|
3801
|
+
var useTheming = ({
|
|
3802
|
+
theme,
|
|
3803
|
+
systemKeys,
|
|
3804
|
+
themeKeys,
|
|
3805
|
+
breakpointsMap,
|
|
3806
|
+
initProps,
|
|
3807
|
+
coverUrl,
|
|
3808
|
+
autoThemeSource,
|
|
3809
|
+
onBreakpointChange,
|
|
3810
|
+
onColorSchemeChange,
|
|
3811
|
+
onContrastChange,
|
|
3812
|
+
onForcedColorsChange,
|
|
3813
|
+
onMonochromeChange,
|
|
3814
|
+
onReducedMotionChange,
|
|
3815
|
+
onReducedTransparencyChange,
|
|
3816
|
+
onCoverThemeGenerated,
|
|
3817
|
+
onContainerBreakpointChange
|
|
3818
|
+
}) => {
|
|
3819
|
+
const [coverThemeTokens, setCoverThemeTokens] = useState(null);
|
|
3820
|
+
const [coverThemeFailed, setCoverThemeFailed] = useState(false);
|
|
3821
|
+
const breakpoints = useBreakpoints(breakpointsMap, onBreakpointChange);
|
|
3822
|
+
const setContainerRef = useContainerBreakpoints(breakpointsMap, onContainerBreakpointChange);
|
|
3823
|
+
const colorScheme = useColorScheme(onColorSchemeChange);
|
|
3824
|
+
const colorSchemeRef = useRef(colorScheme);
|
|
3825
|
+
const contrast = useContrast(onContrastChange);
|
|
3826
|
+
const forcedColors = useForcedColors(onForcedColorsChange);
|
|
3827
|
+
const monochrome = useMonochrome(onMonochromeChange);
|
|
3828
|
+
const reducedMotion = useReducedMotion(onReducedMotionChange);
|
|
3829
|
+
const reducedTransparency = useReducedTransparency(onReducedTransparencyChange);
|
|
3830
|
+
useEffect(() => {
|
|
3831
|
+
if (autoThemeSource === "cover" && coverUrl && !coverThemeTokens) {
|
|
3832
|
+
const extractTheme = async () => {
|
|
3833
|
+
try {
|
|
3834
|
+
const themeTokens = await extractThemeFromImage(proxyUrl(coverUrl) ?? coverUrl);
|
|
3835
|
+
setCoverThemeTokens(themeTokens);
|
|
3836
|
+
onCoverThemeGenerated?.(themeTokens);
|
|
3837
|
+
} catch (error) {
|
|
3838
|
+
console.warn("Failed to extract cover theme:", error);
|
|
3839
|
+
setCoverThemeFailed(true);
|
|
3840
|
+
}
|
|
3841
|
+
};
|
|
3842
|
+
extractTheme();
|
|
3843
|
+
}
|
|
3844
|
+
}, [autoThemeSource, coverUrl, coverThemeTokens, onCoverThemeGenerated]);
|
|
3845
|
+
const updateThemeColorMetaTag = useCallback((color) => {
|
|
3846
|
+
if (typeof document === "undefined") return;
|
|
3847
|
+
let metaTag = document.querySelector("meta[name='theme-color']");
|
|
3848
|
+
if (!metaTag) {
|
|
3849
|
+
metaTag = document.createElement("meta");
|
|
3850
|
+
metaTag.setAttribute("name", "theme-color");
|
|
3851
|
+
document.head.appendChild(metaTag);
|
|
3852
|
+
}
|
|
3853
|
+
metaTag.setAttribute("content", color);
|
|
3854
|
+
}, []);
|
|
3855
|
+
const initThemingCustomProps = useCallback(() => {
|
|
3856
|
+
for (let p in initProps) {
|
|
3857
|
+
document.documentElement.style.setProperty(p, initProps[p]);
|
|
3858
|
+
}
|
|
3859
|
+
}, [initProps]);
|
|
3860
|
+
const inferThemeAuto = useCallback(() => {
|
|
3861
|
+
if (autoThemeSource === "cover") {
|
|
3862
|
+
if (coverThemeTokens) return "cover";
|
|
3863
|
+
if (!coverThemeFailed) return void 0;
|
|
3864
|
+
}
|
|
3865
|
+
return colorSchemeRef.current === "dark" /* dark */ ? systemKeys?.dark : systemKeys?.light;
|
|
3866
|
+
}, [systemKeys, autoThemeSource, coverThemeTokens, coverThemeFailed]);
|
|
3867
|
+
const setThemeCustomProps = useCallback((t) => {
|
|
3868
|
+
if (!t) {
|
|
3869
|
+
return;
|
|
3870
|
+
}
|
|
3871
|
+
if (t === "auto") {
|
|
3872
|
+
const autoTheme = inferThemeAuto();
|
|
3873
|
+
if (!autoTheme) {
|
|
3874
|
+
return;
|
|
3875
|
+
}
|
|
3876
|
+
t = autoTheme;
|
|
3877
|
+
}
|
|
3878
|
+
let themeTokens;
|
|
3879
|
+
if (t === "cover" && coverThemeTokens) {
|
|
3880
|
+
themeTokens = coverThemeTokens;
|
|
3881
|
+
} else {
|
|
3882
|
+
themeTokens = themeKeys[t];
|
|
3883
|
+
}
|
|
3884
|
+
if (!themeTokens) {
|
|
3885
|
+
return;
|
|
3886
|
+
}
|
|
3887
|
+
const props = propsToCSSVars(themeTokens, { prefix: prefixString("theme") });
|
|
3888
|
+
for (let p in props) {
|
|
3889
|
+
document.documentElement.style.setProperty(p, props[p]);
|
|
3890
|
+
}
|
|
3891
|
+
updateThemeColorMetaTag(themeTokens.background);
|
|
3892
|
+
}, [inferThemeAuto, updateThemeColorMetaTag, themeKeys, coverThemeTokens]);
|
|
3893
|
+
useEffect(() => {
|
|
3894
|
+
initThemingCustomProps();
|
|
3895
|
+
}, [initThemingCustomProps]);
|
|
3896
|
+
useEffect(() => {
|
|
3897
|
+
colorSchemeRef.current = colorScheme;
|
|
3898
|
+
setThemeCustomProps(theme);
|
|
3899
|
+
}, [setThemeCustomProps, theme, colorScheme]);
|
|
3900
|
+
useEffect(() => {
|
|
3901
|
+
if (!coverThemeTokens || theme !== "auto") return;
|
|
3902
|
+
const props = propsToCSSVars(coverThemeTokens, { prefix: prefixString("theme") });
|
|
3903
|
+
for (let p in props) {
|
|
3904
|
+
document.documentElement.style.setProperty(p, props[p]);
|
|
3905
|
+
}
|
|
3906
|
+
updateThemeColorMetaTag(coverThemeTokens.background);
|
|
3907
|
+
}, [coverThemeTokens, theme, updateThemeColorMetaTag]);
|
|
3908
|
+
const themeResolved = autoThemeSource !== "cover" || !coverUrl || !!coverThemeTokens || coverThemeFailed;
|
|
3909
|
+
return {
|
|
3910
|
+
inferThemeAuto,
|
|
3911
|
+
theme,
|
|
3912
|
+
breakpoints,
|
|
3913
|
+
colorScheme,
|
|
3914
|
+
contrast,
|
|
3915
|
+
forcedColors,
|
|
3916
|
+
monochrome,
|
|
3917
|
+
reducedMotion,
|
|
3918
|
+
reducedTransparency,
|
|
3919
|
+
coverThemeTokens,
|
|
3920
|
+
themeResolved,
|
|
3921
|
+
setContainerRef
|
|
3922
|
+
};
|
|
3923
|
+
};
|
|
3924
|
+
var useSharedPreferences = () => {
|
|
3925
|
+
const audioCtx = useContext(ThAudioPreferencesContext);
|
|
3926
|
+
const readerCtx = useContext(ThPreferencesContext);
|
|
3927
|
+
const ctx = audioCtx ?? readerCtx;
|
|
3928
|
+
if (!ctx) throw new Error("useSharedPreferences must be used within a ThPreferencesProvider or ThAudioPreferencesProvider");
|
|
3929
|
+
const prefs = ctx.preferences;
|
|
3930
|
+
return {
|
|
3931
|
+
shortcuts: prefs.shortcuts,
|
|
3932
|
+
docking: prefs.docking,
|
|
3933
|
+
theming: {
|
|
3934
|
+
icon: prefs.theming.icon,
|
|
3935
|
+
header: prefs.theming.header ? { backLink: prefs.theming.header.backLink } : void 0,
|
|
3936
|
+
themes: {
|
|
3937
|
+
systemThemes: prefs.theming.themes.systemThemes,
|
|
3938
|
+
keys: prefs.theming.themes.keys,
|
|
3939
|
+
audioOrder: audioCtx?.preferences.theming.themes.audioOrder,
|
|
3940
|
+
reflowOrder: readerCtx?.preferences.theming.themes.reflowOrder,
|
|
3941
|
+
fxlOrder: readerCtx?.preferences.theming.themes.fxlOrder
|
|
3942
|
+
},
|
|
3943
|
+
layout: {
|
|
3944
|
+
defaults: prefs.theming.layout.defaults
|
|
3945
|
+
},
|
|
3946
|
+
breakpoints: prefs.theming.breakpoints
|
|
3947
|
+
}
|
|
3948
|
+
};
|
|
3949
|
+
};
|
|
3950
|
+
|
|
3951
|
+
export { ThActionsKeys, ThAudioActionKeys, ThAudioAffordance, ThAudioKeys, ThAudioMemoryPreferencesAdapter, ThAudioPreferencesContext, ThAudioPreferencesProvider, ThDockingKeys, ThDockingTypes, ThGlobalMemoryPreferencesAdapter, ThGlobalPreferencesProvider, ThLayoutOptions, ThLineHeightOptions, ThMemoryPreferencesAdapter, ThPreferencesContext, ThPreferencesProvider, ThSettingsContainerKeys, ThSettingsKeys, ThSettingsRangePlaceholder, ThSettingsRangeVariant, ThSettingsTimerVariant, ThSheetHeaderVariant, ThSheetTypes, ThSpacingPresetKeys, ThSpacingSettingsKeys, ThTextAlignOptions, ThTextSettingsKeys, ThThemeKeys, arabicFarsiCollection, buildThemeObject, chineseSimplifiedCollection, chineseTraditionalCollection, contrast1Theme, contrast2Theme, contrast3Theme, createAudioPreferences, createDefinitionFromStaticFonts, createDefinitionsFromBunnyFonts, createDefinitionsFromGoogleFonts, createGlobalPreferences, createPreferences, darkTheme, defaultActionKeysObject, defaultAudioContentProtectionConfig, defaultAudioPlaybackRate, defaultAudioPlaybackRateAction, defaultAudioPreferences, defaultAudioPreferencesContextValue, defaultAudioRemotePlaybackAction, defaultAudioSkipBackwardInterval, defaultAudioSkipForwardInterval, defaultAudioSkipInterval, defaultAudioSleepTimer, defaultAudioSleepTimerAction, defaultAudioSleepTimerPresetList, defaultAudioTocAction, defaultAudioVolume, defaultAudioVolumeAction, defaultContentProtectionConfig, defaultFontCollection, defaultFullscreenAction, defaultJumpToPositionAction, defaultLetterSpacing, defaultLineHeights, defaultParagraphIndent, defaultParagraphSpacing, defaultPreferences, defaultPreferencesContextValue, defaultSettingsAction, defaultSpacingPresets, defaultSpacingPresetsOrder, defaultSpacingSettingsMain, defaultSpacingSettingsSubpanel, defaultTextSettingsMain, defaultTextSettingsSubpanel, defaultTocAction, defaultWordSpacing, defaultZoom, devContentProtectionConfig, hebrewCollection, japaneseCollection, japaneseVerticalCollection, koreanCollection, lightTheme, paperTheme, prefixString, proxyUrl, readiumCSSFontCollection, resolveAudioContentProtectionConfig, resolveContentProtectionConfig, sepiaTheme, tamilCollection, useActionsPreferences, useAudioActionsPreferences, useAudioPreferences, useFilteredPreferenceKeys, usePreferenceKeys, usePreferences, useSharedPreferences, useTheming, validateObjectKeys };
|
|
3952
|
+
//# sourceMappingURL=chunk-OD75GC5N.mjs.map
|
|
3953
|
+
//# sourceMappingURL=chunk-OD75GC5N.mjs.map
|