@ankhorage/reader 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/features/reader/ReaderViewport.d.ts +4 -0
- package/dist/features/reader/ReaderViewport.d.ts.map +1 -0
- package/dist/features/reader/ReaderViewport.js +210 -0
- package/dist/features/reader/ReaderViewport.js.map +1 -0
- package/dist/features/reader/adapters/ReaderView.d.ts +4 -0
- package/dist/features/reader/adapters/ReaderView.d.ts.map +1 -0
- package/dist/features/reader/adapters/ReaderView.js +5 -0
- package/dist/features/reader/adapters/ReaderView.js.map +1 -0
- package/dist/features/reader/adapters/ReaderView.native.d.ts +5 -0
- package/dist/features/reader/adapters/ReaderView.native.d.ts.map +1 -0
- package/dist/features/reader/adapters/ReaderView.native.js +22 -0
- package/dist/features/reader/adapters/ReaderView.native.js.map +1 -0
- package/dist/features/reader/adapters/ReaderView.web.d.ts +5 -0
- package/dist/features/reader/adapters/ReaderView.web.d.ts.map +1 -0
- package/dist/features/reader/adapters/ReaderView.web.js +16 -0
- package/dist/features/reader/adapters/ReaderView.web.js.map +1 -0
- package/dist/features/reader/drivers/EpubReaderDriver.d.ts +20 -0
- package/dist/features/reader/drivers/EpubReaderDriver.d.ts.map +1 -0
- package/dist/features/reader/drivers/EpubReaderDriver.js +259 -0
- package/dist/features/reader/drivers/EpubReaderDriver.js.map +1 -0
- package/dist/features/reader/drivers/PdfReaderDriver.d.ts +18 -0
- package/dist/features/reader/drivers/PdfReaderDriver.d.ts.map +1 -0
- package/dist/features/reader/drivers/PdfReaderDriver.js +198 -0
- package/dist/features/reader/drivers/PdfReaderDriver.js.map +1 -0
- package/dist/features/reader/epub/EpubArchive.d.ts +11 -0
- package/dist/features/reader/epub/EpubArchive.d.ts.map +1 -0
- package/dist/features/reader/epub/EpubArchive.js +65 -0
- package/dist/features/reader/epub/EpubArchive.js.map +1 -0
- package/dist/features/reader/epub/EpubArchiveFetcher.d.ts +11 -0
- package/dist/features/reader/epub/EpubArchiveFetcher.d.ts.map +1 -0
- package/dist/features/reader/epub/EpubArchiveFetcher.js +44 -0
- package/dist/features/reader/epub/EpubArchiveFetcher.js.map +1 -0
- package/dist/features/reader/epub/mediaTypes.d.ts +3 -0
- package/dist/features/reader/epub/mediaTypes.d.ts.map +1 -0
- package/dist/features/reader/epub/mediaTypes.js +26 -0
- package/dist/features/reader/epub/mediaTypes.js.map +1 -0
- package/dist/features/reader/epub/parseEpubPackage.d.ts +22 -0
- package/dist/features/reader/epub/parseEpubPackage.d.ts.map +1 -0
- package/dist/features/reader/epub/parseEpubPackage.js +98 -0
- package/dist/features/reader/epub/parseEpubPackage.js.map +1 -0
- package/dist/features/reader/epub/path.d.ts +4 -0
- package/dist/features/reader/epub/path.d.ts.map +1 -0
- package/dist/features/reader/epub/path.js +28 -0
- package/dist/features/reader/epub/path.js.map +1 -0
- package/dist/features/reader/epub/sanitizeEpubDocument.d.ts +3 -0
- package/dist/features/reader/epub/sanitizeEpubDocument.d.ts.map +1 -0
- package/dist/features/reader/epub/sanitizeEpubDocument.js +90 -0
- package/dist/features/reader/epub/sanitizeEpubDocument.js.map +1 -0
- package/dist/features/reader/epub/validateEpubArchiveEntriesAsync.d.ts +4 -0
- package/dist/features/reader/epub/validateEpubArchiveEntriesAsync.d.ts.map +1 -0
- package/dist/features/reader/epub/validateEpubArchiveEntriesAsync.js +50 -0
- package/dist/features/reader/epub/validateEpubArchiveEntriesAsync.js.map +1 -0
- package/dist/features/reader/errors/ReaderDocumentError.d.ts +6 -0
- package/dist/features/reader/errors/ReaderDocumentError.d.ts.map +1 -0
- package/dist/features/reader/errors/ReaderDocumentError.js +9 -0
- package/dist/features/reader/errors/ReaderDocumentError.js.map +1 -0
- package/dist/features/reader/loadReaderSourceAsync.d.ts +2 -0
- package/dist/features/reader/loadReaderSourceAsync.d.ts.map +1 -0
- package/dist/features/reader/loadReaderSourceAsync.js +49 -0
- package/dist/features/reader/loadReaderSourceAsync.js.map +1 -0
- package/dist/features/reader/navigation/resolveReaderKeyboardIntent.d.ts +9 -0
- package/dist/features/reader/navigation/resolveReaderKeyboardIntent.d.ts.map +1 -0
- package/dist/features/reader/navigation/resolveReaderKeyboardIntent.js +19 -0
- package/dist/features/reader/navigation/resolveReaderKeyboardIntent.js.map +1 -0
- package/dist/features/reader/navigation/resolveReaderNavigationIntent.d.ts +11 -0
- package/dist/features/reader/navigation/resolveReaderNavigationIntent.d.ts.map +1 -0
- package/dist/features/reader/navigation/resolveReaderNavigationIntent.js +16 -0
- package/dist/features/reader/navigation/resolveReaderNavigationIntent.js.map +1 -0
- package/dist/features/reader/readerViewportPresentation.d.ts +4 -0
- package/dist/features/reader/readerViewportPresentation.d.ts.map +1 -0
- package/dist/features/reader/readerViewportPresentation.js +25 -0
- package/dist/features/reader/readerViewportPresentation.js.map +1 -0
- package/dist/features/reader/toReaderViewportProps.d.ts +4 -0
- package/dist/features/reader/toReaderViewportProps.d.ts.map +1 -0
- package/dist/features/reader/toReaderViewportProps.js +24 -0
- package/dist/features/reader/toReaderViewportProps.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.native.d.ts +3 -0
- package/dist/index.native.d.ts.map +1 -0
- package/dist/index.native.js +2 -0
- package/dist/index.native.js.map +1 -0
- package/dist/index.web.d.ts +3 -0
- package/dist/index.web.d.ts.map +1 -0
- package/dist/index.web.js +2 -0
- package/dist/index.web.js.map +1 -0
- package/dist/types/reader.d.ts +93 -0
- package/dist/types/reader.d.ts.map +1 -0
- package/dist/types/reader.js +2 -0
- package/dist/types/reader.js.map +1 -0
- package/package.json +105 -4
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ankhorage
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReaderViewport.d.ts","sourceRoot":"","sources":["../../../src/features/reader/ReaderViewport.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAIV,mBAAmB,EAEpB,MAAM,uBAAuB,CAAC;AAY/B,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,KAAK,EAAE,mBAAmB,qBAoBhE"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
'use dom';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { EpubReaderDriver } from './drivers/EpubReaderDriver.js';
|
|
5
|
+
import { PdfReaderDriver } from './drivers/PdfReaderDriver.js';
|
|
6
|
+
import { ReaderDocumentError } from './errors/ReaderDocumentError.js';
|
|
7
|
+
import { loadReaderSourceAsync } from './loadReaderSourceAsync.js';
|
|
8
|
+
import { resolveReaderKeyboardIntent } from './navigation/resolveReaderKeyboardIntent.js';
|
|
9
|
+
import { resolveReaderNavigationIntent } from './navigation/resolveReaderNavigationIntent.js';
|
|
10
|
+
import { LOADING_STATE, READER_STYLES } from './readerViewportPresentation.js';
|
|
11
|
+
export default function ReaderViewport(props) {
|
|
12
|
+
const containerRef = React.useRef(null);
|
|
13
|
+
const runtime = useReaderLifecycle(props, containerRef);
|
|
14
|
+
useReaderCommand(props.command, props.sourceUri, runtime.driverRef, runtime.emitState);
|
|
15
|
+
const input = useReaderInput(runtime.driverRef, runtime.driverStateRef, runtime.emitState);
|
|
16
|
+
return (_jsxs("div", { "aria-label": "Reader viewport", className: `reader-root reader-${props.appearance.colorScheme}`, onKeyDown: input.handleKeyDown, onTouchEnd: input.handleTouchEnd, onTouchStart: input.handleTouchStart, role: "document", tabIndex: 0, children: [_jsx("style", { children: READER_STYLES }), _jsx("div", { className: "reader-content", ref: containerRef })] }));
|
|
17
|
+
}
|
|
18
|
+
function useReaderLifecycle(props, containerRef) {
|
|
19
|
+
const driverRef = React.useRef(undefined);
|
|
20
|
+
const driverStateRef = React.useRef(undefined);
|
|
21
|
+
const emitState = useEmitReaderState(props, driverStateRef);
|
|
22
|
+
useReaderDriverEffect(props, containerRef, driverRef, driverStateRef, emitState);
|
|
23
|
+
return { driverRef, driverStateRef, emitState };
|
|
24
|
+
}
|
|
25
|
+
function useEmitReaderState(props, stateRef) {
|
|
26
|
+
const { format, onStateChange } = props;
|
|
27
|
+
return React.useCallback((state, trigger) => {
|
|
28
|
+
stateRef.current = state;
|
|
29
|
+
void onStateChange(toViewportState(state, format, trigger));
|
|
30
|
+
}, [format, onStateChange, stateRef]);
|
|
31
|
+
}
|
|
32
|
+
function useReaderDriverEffect(props, containerRef, driverRef, driverStateRef, emitState) {
|
|
33
|
+
const { appearance, format, onError, onOpenExternalLink, onStateChange, sourceUri } = props;
|
|
34
|
+
const { colorScheme, fontScale, lineHeight } = appearance;
|
|
35
|
+
const [initialLocation] = React.useState(props.initialLocation);
|
|
36
|
+
React.useEffect(() => mountReaderDriver({
|
|
37
|
+
container: containerRef.current,
|
|
38
|
+
driverRef,
|
|
39
|
+
driverStateRef,
|
|
40
|
+
emitState,
|
|
41
|
+
onError,
|
|
42
|
+
onStateChange,
|
|
43
|
+
reader: {
|
|
44
|
+
appearance: { colorScheme, fontScale, lineHeight },
|
|
45
|
+
format,
|
|
46
|
+
...(initialLocation === undefined ? {} : { initialLocation }),
|
|
47
|
+
onError,
|
|
48
|
+
onOpenExternalLink,
|
|
49
|
+
sourceUri,
|
|
50
|
+
},
|
|
51
|
+
}), [
|
|
52
|
+
colorScheme,
|
|
53
|
+
containerRef,
|
|
54
|
+
driverRef,
|
|
55
|
+
driverStateRef,
|
|
56
|
+
emitState,
|
|
57
|
+
format,
|
|
58
|
+
fontScale,
|
|
59
|
+
initialLocation,
|
|
60
|
+
lineHeight,
|
|
61
|
+
onError,
|
|
62
|
+
onOpenExternalLink,
|
|
63
|
+
onStateChange,
|
|
64
|
+
sourceUri,
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
function mountReaderDriver(args) {
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
const holder = {};
|
|
70
|
+
args.container?.replaceChildren();
|
|
71
|
+
void args.onStateChange(LOADING_STATE);
|
|
72
|
+
if (args.container) {
|
|
73
|
+
void initializeReaderDriver(args.reader, args.container, controller.signal, args.emitState)
|
|
74
|
+
.then(async (driver) => {
|
|
75
|
+
if (controller.signal.aborted)
|
|
76
|
+
return driver.destroy();
|
|
77
|
+
holder.driver = driver;
|
|
78
|
+
args.driverRef.current = driver;
|
|
79
|
+
args.emitState(driver.getState(), 'location');
|
|
80
|
+
})
|
|
81
|
+
.catch((error) => {
|
|
82
|
+
if (!controller.signal.aborted)
|
|
83
|
+
void args.onError(normalizeReaderError(error, args.reader.format));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return () => {
|
|
87
|
+
controller.abort();
|
|
88
|
+
args.driverRef.current = undefined;
|
|
89
|
+
args.driverStateRef.current = undefined;
|
|
90
|
+
if (holder.driver)
|
|
91
|
+
void holder.driver.destroy();
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
async function initializeReaderDriver(props, container, signal, emitState) {
|
|
95
|
+
const bytes = await loadReaderSourceAsync(props.sourceUri, signal);
|
|
96
|
+
if (props.format === 'pdf') {
|
|
97
|
+
return PdfReaderDriver.create({
|
|
98
|
+
bytes,
|
|
99
|
+
container,
|
|
100
|
+
...(props.initialLocation === undefined ? {} : { initialLocation: props.initialLocation }),
|
|
101
|
+
onExternalLink: (url) => void props.onOpenExternalLink({ url }),
|
|
102
|
+
onRenderError: (error) => void props.onError(normalizeReaderError(error, props.format)),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return EpubReaderDriver.create({
|
|
106
|
+
appearance: props.appearance,
|
|
107
|
+
bytes,
|
|
108
|
+
container,
|
|
109
|
+
...(props.initialLocation === undefined ? {} : { initialLocation: props.initialLocation }),
|
|
110
|
+
onExternalLink: (url) => void props.onOpenExternalLink({ url }),
|
|
111
|
+
onStateChange: (state) => emitState(state, 'location'),
|
|
112
|
+
signal,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function useReaderCommand(command, sourceUri, driverRef, emitState) {
|
|
116
|
+
const lastCommandIdRef = React.useRef(0);
|
|
117
|
+
React.useEffect(() => {
|
|
118
|
+
if (command?.sourceUri !== sourceUri || command.id <= lastCommandIdRef.current)
|
|
119
|
+
return;
|
|
120
|
+
lastCommandIdRef.current = command.id;
|
|
121
|
+
const trigger = command.type === 'next' ? 'nextControl' : 'previousControl';
|
|
122
|
+
void navigate(driverRef.current, command.type, trigger, emitState);
|
|
123
|
+
}, [command, driverRef, emitState, sourceUri]);
|
|
124
|
+
}
|
|
125
|
+
function useReaderInput(driverRef, stateRef, emitState) {
|
|
126
|
+
const touchStartRef = React.useRef(undefined);
|
|
127
|
+
const handleTouchStart = React.useCallback((event) => {
|
|
128
|
+
const touch = event.changedTouches[0];
|
|
129
|
+
if (touch) {
|
|
130
|
+
touchStartRef.current = {
|
|
131
|
+
interactive: isInteractiveTarget(event.target),
|
|
132
|
+
x: touch.clientX,
|
|
133
|
+
y: touch.clientY,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}, []);
|
|
137
|
+
const handleTouchEnd = React.useCallback((event) => {
|
|
138
|
+
handleSwipe(event, touchStartRef.current, driverRef.current, stateRef.current, emitState);
|
|
139
|
+
touchStartRef.current = undefined;
|
|
140
|
+
}, [driverRef, emitState, stateRef]);
|
|
141
|
+
const handleKeyDown = React.useCallback((event) => handleKeyboard(event, driverRef.current, stateRef.current, emitState), [driverRef, emitState, stateRef]);
|
|
142
|
+
return { handleKeyDown, handleTouchEnd, handleTouchStart };
|
|
143
|
+
}
|
|
144
|
+
function handleSwipe(event, start, driver, state, emitState) {
|
|
145
|
+
const touch = event.changedTouches[0];
|
|
146
|
+
if (!start || !touch || !driver || !state)
|
|
147
|
+
return;
|
|
148
|
+
const intent = resolveReaderNavigationIntent({
|
|
149
|
+
canGoNext: state.canGoNext,
|
|
150
|
+
canGoPrevious: state.canGoPrevious,
|
|
151
|
+
deltaX: touch.clientX - start.x,
|
|
152
|
+
deltaY: touch.clientY - start.y,
|
|
153
|
+
direction: driver.direction,
|
|
154
|
+
hasSelection: !window.getSelection()?.isCollapsed,
|
|
155
|
+
isInteractiveTarget: start.interactive,
|
|
156
|
+
viewportWidth: event.currentTarget.clientWidth,
|
|
157
|
+
});
|
|
158
|
+
if (intent)
|
|
159
|
+
void navigate(driver, intent, 'swipe', emitState);
|
|
160
|
+
}
|
|
161
|
+
function handleKeyboard(event, driver, state, emitState) {
|
|
162
|
+
if (!driver || !state)
|
|
163
|
+
return;
|
|
164
|
+
const intent = resolveReaderKeyboardIntent({
|
|
165
|
+
canGoNext: state.canGoNext,
|
|
166
|
+
canGoPrevious: state.canGoPrevious,
|
|
167
|
+
direction: driver.direction,
|
|
168
|
+
hasModifier: event.altKey || event.ctrlKey || event.metaKey || event.shiftKey,
|
|
169
|
+
isInteractiveTarget: isInteractiveTarget(event.target),
|
|
170
|
+
key: event.key,
|
|
171
|
+
});
|
|
172
|
+
if (!intent)
|
|
173
|
+
return;
|
|
174
|
+
event.preventDefault();
|
|
175
|
+
void navigate(driver, intent, 'keyboard', emitState);
|
|
176
|
+
}
|
|
177
|
+
async function navigate(driver, intent, trigger, emitState) {
|
|
178
|
+
if (!driver)
|
|
179
|
+
return;
|
|
180
|
+
if (intent === 'next')
|
|
181
|
+
await driver.goNext(trigger);
|
|
182
|
+
else
|
|
183
|
+
await driver.goPrevious(trigger);
|
|
184
|
+
emitState(driver.getState(), trigger);
|
|
185
|
+
}
|
|
186
|
+
function toViewportState(state, format, trigger) {
|
|
187
|
+
return {
|
|
188
|
+
canGoNext: state.canGoNext,
|
|
189
|
+
canGoPrevious: state.canGoPrevious,
|
|
190
|
+
location: { format, trigger, ...state },
|
|
191
|
+
page: state.page,
|
|
192
|
+
...(state.pageCount === undefined ? {} : { pageCount: state.pageCount }),
|
|
193
|
+
progress: state.progression,
|
|
194
|
+
status: 'ready',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function isInteractiveTarget(target) {
|
|
198
|
+
return (target instanceof Element &&
|
|
199
|
+
target.closest('a, button, input, select, textarea, [contenteditable="true"], [role="button"]') !== null);
|
|
200
|
+
}
|
|
201
|
+
function normalizeReaderError(error, format) {
|
|
202
|
+
if (error instanceof ReaderDocumentError)
|
|
203
|
+
return { code: error.code, format, message: error.message };
|
|
204
|
+
return {
|
|
205
|
+
code: 'load-failed',
|
|
206
|
+
format,
|
|
207
|
+
message: error instanceof Error ? error.message : 'The document could not be loaded.',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=ReaderViewport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReaderViewport.js","sourceRoot":"","sources":["../../../src/features/reader/ReaderViewport.tsx"],"names":[],"mappings":"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","sourcesContent":["'use dom';\n\nimport React from 'react';\n\nimport type {\n ReaderDriver,\n ReaderDriverState,\n ReaderTouchStart,\n ReaderViewportProps,\n ReaderViewportState,\n} from '../../types/reader.js';\nimport { EpubReaderDriver } from './drivers/EpubReaderDriver.js';\nimport { PdfReaderDriver } from './drivers/PdfReaderDriver.js';\nimport { ReaderDocumentError } from './errors/ReaderDocumentError.js';\nimport { loadReaderSourceAsync } from './loadReaderSourceAsync.js';\nimport { resolveReaderKeyboardIntent } from './navigation/resolveReaderKeyboardIntent.js';\nimport { resolveReaderNavigationIntent } from './navigation/resolveReaderNavigationIntent.js';\nimport { LOADING_STATE, READER_STYLES } from './readerViewportPresentation.js';\n\ntype ReaderTrigger = NonNullable<ReaderViewportState['location']>['trigger'];\ntype EmitReaderState = (state: ReaderDriverState, trigger: ReaderTrigger) => void;\n\nexport default function ReaderViewport(props: ReaderViewportProps) {\n const containerRef = React.useRef<HTMLDivElement>(null);\n const runtime = useReaderLifecycle(props, containerRef);\n useReaderCommand(props.command, props.sourceUri, runtime.driverRef, runtime.emitState);\n const input = useReaderInput(runtime.driverRef, runtime.driverStateRef, runtime.emitState);\n\n return (\n <div\n aria-label=\"Reader viewport\"\n className={`reader-root reader-${props.appearance.colorScheme}`}\n onKeyDown={input.handleKeyDown}\n onTouchEnd={input.handleTouchEnd}\n onTouchStart={input.handleTouchStart}\n role=\"document\"\n tabIndex={0}\n >\n <style>{READER_STYLES}</style>\n <div className=\"reader-content\" ref={containerRef} />\n </div>\n );\n}\n\nfunction useReaderLifecycle(\n props: ReaderViewportProps,\n containerRef: React.RefObject<HTMLDivElement | null>,\n) {\n const driverRef = React.useRef<ReaderDriver | undefined>(undefined);\n const driverStateRef = React.useRef<ReaderDriverState | undefined>(undefined);\n const emitState = useEmitReaderState(props, driverStateRef);\n useReaderDriverEffect(props, containerRef, driverRef, driverStateRef, emitState);\n return { driverRef, driverStateRef, emitState };\n}\n\nfunction useEmitReaderState(\n props: ReaderViewportProps,\n stateRef: React.RefObject<ReaderDriverState | undefined>,\n): EmitReaderState {\n const { format, onStateChange } = props;\n return React.useCallback<EmitReaderState>(\n (state, trigger) => {\n stateRef.current = state;\n void onStateChange(toViewportState(state, format, trigger));\n },\n [format, onStateChange, stateRef],\n );\n}\n\nfunction useReaderDriverEffect(\n props: ReaderViewportProps,\n containerRef: React.RefObject<HTMLDivElement | null>,\n driverRef: React.RefObject<ReaderDriver | undefined>,\n driverStateRef: React.RefObject<ReaderDriverState | undefined>,\n emitState: EmitReaderState,\n): void {\n const { appearance, format, onError, onOpenExternalLink, onStateChange, sourceUri } = props;\n const { colorScheme, fontScale, lineHeight } = appearance;\n const [initialLocation] = React.useState(props.initialLocation);\n\n React.useEffect(\n () =>\n mountReaderDriver({\n container: containerRef.current,\n driverRef,\n driverStateRef,\n emitState,\n onError,\n onStateChange,\n reader: {\n appearance: { colorScheme, fontScale, lineHeight },\n format,\n ...(initialLocation === undefined ? {} : { initialLocation }),\n onError,\n onOpenExternalLink,\n sourceUri,\n },\n }),\n [\n colorScheme,\n containerRef,\n driverRef,\n driverStateRef,\n emitState,\n format,\n fontScale,\n initialLocation,\n lineHeight,\n onError,\n onOpenExternalLink,\n onStateChange,\n sourceUri,\n ],\n );\n}\n\nfunction mountReaderDriver(args: MountReaderDriverArgs): () => void {\n const controller = new AbortController();\n const holder: { driver?: ReaderDriver } = {};\n args.container?.replaceChildren();\n void args.onStateChange(LOADING_STATE);\n if (args.container) {\n void initializeReaderDriver(args.reader, args.container, controller.signal, args.emitState)\n .then(async (driver) => {\n if (controller.signal.aborted) return driver.destroy();\n holder.driver = driver;\n args.driverRef.current = driver;\n args.emitState(driver.getState(), 'location');\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n void args.onError(normalizeReaderError(error, args.reader.format));\n });\n }\n return () => {\n controller.abort();\n args.driverRef.current = undefined;\n args.driverStateRef.current = undefined;\n if (holder.driver) void holder.driver.destroy();\n };\n}\n\nasync function initializeReaderDriver(\n props: Pick<\n ReaderViewportProps,\n 'appearance' | 'format' | 'initialLocation' | 'onError' | 'onOpenExternalLink' | 'sourceUri'\n >,\n container: HTMLElement,\n signal: AbortSignal,\n emitState: EmitReaderState,\n): Promise<ReaderDriver> {\n const bytes = await loadReaderSourceAsync(props.sourceUri, signal);\n if (props.format === 'pdf') {\n return PdfReaderDriver.create({\n bytes,\n container,\n ...(props.initialLocation === undefined ? {} : { initialLocation: props.initialLocation }),\n onExternalLink: (url) => void props.onOpenExternalLink({ url }),\n onRenderError: (error) => void props.onError(normalizeReaderError(error, props.format)),\n });\n }\n return EpubReaderDriver.create({\n appearance: props.appearance,\n bytes,\n container,\n ...(props.initialLocation === undefined ? {} : { initialLocation: props.initialLocation }),\n onExternalLink: (url) => void props.onOpenExternalLink({ url }),\n onStateChange: (state) => emitState(state, 'location'),\n signal,\n });\n}\n\ninterface MountReaderDriverArgs {\n readonly container: HTMLElement | null;\n readonly driverRef: React.RefObject<ReaderDriver | undefined>;\n readonly driverStateRef: React.RefObject<ReaderDriverState | undefined>;\n readonly emitState: EmitReaderState;\n readonly onError: ReaderViewportProps['onError'];\n readonly onStateChange: ReaderViewportProps['onStateChange'];\n readonly reader: Pick<\n ReaderViewportProps,\n 'appearance' | 'format' | 'initialLocation' | 'onError' | 'onOpenExternalLink' | 'sourceUri'\n >;\n}\n\nfunction useReaderCommand(\n command: ReaderViewportProps['command'],\n sourceUri: string,\n driverRef: React.RefObject<ReaderDriver | undefined>,\n emitState: EmitReaderState,\n): void {\n const lastCommandIdRef = React.useRef(0);\n React.useEffect(() => {\n if (command?.sourceUri !== sourceUri || command.id <= lastCommandIdRef.current) return;\n lastCommandIdRef.current = command.id;\n const trigger = command.type === 'next' ? 'nextControl' : 'previousControl';\n void navigate(driverRef.current, command.type, trigger, emitState);\n }, [command, driverRef, emitState, sourceUri]);\n}\n\nfunction useReaderInput(\n driverRef: React.RefObject<ReaderDriver | undefined>,\n stateRef: React.RefObject<ReaderDriverState | undefined>,\n emitState: EmitReaderState,\n) {\n const touchStartRef = React.useRef<ReaderTouchStart | undefined>(undefined);\n const handleTouchStart = React.useCallback((event: React.TouchEvent<HTMLDivElement>) => {\n const touch = event.changedTouches[0];\n if (touch) {\n touchStartRef.current = {\n interactive: isInteractiveTarget(event.target),\n x: touch.clientX,\n y: touch.clientY,\n };\n }\n }, []);\n const handleTouchEnd = React.useCallback(\n (event: React.TouchEvent<HTMLDivElement>) => {\n handleSwipe(event, touchStartRef.current, driverRef.current, stateRef.current, emitState);\n touchStartRef.current = undefined;\n },\n [driverRef, emitState, stateRef],\n );\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) =>\n handleKeyboard(event, driverRef.current, stateRef.current, emitState),\n [driverRef, emitState, stateRef],\n );\n return { handleKeyDown, handleTouchEnd, handleTouchStart };\n}\n\nfunction handleSwipe(\n event: React.TouchEvent<HTMLDivElement>,\n start: ReaderTouchStart | undefined,\n driver: ReaderDriver | undefined,\n state: ReaderDriverState | undefined,\n emitState: EmitReaderState,\n): void {\n const touch = event.changedTouches[0];\n if (!start || !touch || !driver || !state) return;\n const intent = resolveReaderNavigationIntent({\n canGoNext: state.canGoNext,\n canGoPrevious: state.canGoPrevious,\n deltaX: touch.clientX - start.x,\n deltaY: touch.clientY - start.y,\n direction: driver.direction,\n hasSelection: !window.getSelection()?.isCollapsed,\n isInteractiveTarget: start.interactive,\n viewportWidth: event.currentTarget.clientWidth,\n });\n if (intent) void navigate(driver, intent, 'swipe', emitState);\n}\n\nfunction handleKeyboard(\n event: React.KeyboardEvent<HTMLDivElement>,\n driver: ReaderDriver | undefined,\n state: ReaderDriverState | undefined,\n emitState: EmitReaderState,\n): void {\n if (!driver || !state) return;\n const intent = resolveReaderKeyboardIntent({\n canGoNext: state.canGoNext,\n canGoPrevious: state.canGoPrevious,\n direction: driver.direction,\n hasModifier: event.altKey || event.ctrlKey || event.metaKey || event.shiftKey,\n isInteractiveTarget: isInteractiveTarget(event.target),\n key: event.key,\n });\n if (!intent) return;\n event.preventDefault();\n void navigate(driver, intent, 'keyboard', emitState);\n}\n\nasync function navigate(\n driver: ReaderDriver | undefined,\n intent: 'next' | 'previous',\n trigger: ReaderTrigger,\n emitState: EmitReaderState,\n): Promise<void> {\n if (!driver) return;\n if (intent === 'next') await driver.goNext(trigger);\n else await driver.goPrevious(trigger);\n emitState(driver.getState(), trigger);\n}\n\nfunction toViewportState(\n state: ReaderDriverState,\n format: ReaderViewportProps['format'],\n trigger: ReaderTrigger,\n): ReaderViewportState {\n return {\n canGoNext: state.canGoNext,\n canGoPrevious: state.canGoPrevious,\n location: { format, trigger, ...state },\n page: state.page,\n ...(state.pageCount === undefined ? {} : { pageCount: state.pageCount }),\n progress: state.progression,\n status: 'ready',\n };\n}\n\nfunction isInteractiveTarget(target: EventTarget): boolean {\n return (\n target instanceof Element &&\n target.closest(\n 'a, button, input, select, textarea, [contenteditable=\"true\"], [role=\"button\"]',\n ) !== null\n );\n}\n\nfunction normalizeReaderError(error: unknown, format: ReaderViewportProps['format']) {\n if (error instanceof ReaderDocumentError)\n return { code: error.code, format, message: error.message };\n return {\n code: 'load-failed' as const,\n format,\n message: error instanceof Error ? error.message : 'The document could not be loaded.',\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReaderView.d.ts","sourceRoot":"","sources":["../../../../src/features/reader/adapters/ReaderView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,KAAK,CAIzD"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/*** Fail clearly when an entrypoint without web/native platform resolution is used. */
|
|
2
|
+
export function ReaderView(_props) {
|
|
3
|
+
throw new Error('@ankhorage/reader ReaderView requires the browser or react-native package export condition.');
|
|
4
|
+
}
|
|
5
|
+
//# sourceMappingURL=ReaderView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReaderView.js","sourceRoot":"","sources":["../../../../src/features/reader/adapters/ReaderView.tsx"],"names":[],"mappings":"AAEA,uFAAuF;AACvF,MAAM,UAAU,UAAU,CAAC,MAAuB;IAChD,MAAM,IAAI,KAAK,CACb,6FAA6F,CAC9F,CAAC;AACJ,CAAC","sourcesContent":["import type { ReaderViewProps } from '../../../types/reader.js';\n\n/*** Fail clearly when an entrypoint without web/native platform resolution is used. */\nexport function ReaderView(_props: ReaderViewProps): never {\n throw new Error(\n '@ankhorage/reader ReaderView requires the browser or react-native package export condition.',\n );\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ReaderViewProps } from '../../../types/reader.js';
|
|
3
|
+
/*** Host the same EPUB/PDF DOM viewport in Expo's isolated native DOM WebView. */
|
|
4
|
+
export declare function ReaderView(props: ReaderViewProps): React.ReactElement;
|
|
5
|
+
//# sourceMappingURL=ReaderView.native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReaderView.native.d.ts","sourceRoot":"","sources":["../../../../src/features/reader/adapters/ReaderView.native.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAkBhE,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CAMrE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { StyleSheet, View } from 'react-native';
|
|
4
|
+
import ReaderViewport from '../ReaderViewport.js';
|
|
5
|
+
import { toReaderViewportProps } from '../toReaderViewportProps.js';
|
|
6
|
+
const styles = StyleSheet.create({
|
|
7
|
+
container: { flex: 1, minHeight: 360 },
|
|
8
|
+
});
|
|
9
|
+
const DOM_CONFIGURATION = {
|
|
10
|
+
allowsInlineMediaPlayback: false,
|
|
11
|
+
javaScriptCanOpenWindowsAutomatically: false,
|
|
12
|
+
mediaPlaybackRequiresUserAction: true,
|
|
13
|
+
originWhitelist: ['about:blank', 'blob:*'],
|
|
14
|
+
scrollEnabled: false,
|
|
15
|
+
style: styles.container,
|
|
16
|
+
unstable_useExpoModulesBridge: false,
|
|
17
|
+
};
|
|
18
|
+
/*** Host the same EPUB/PDF DOM viewport in Expo's isolated native DOM WebView. */
|
|
19
|
+
export function ReaderView(props) {
|
|
20
|
+
return (_jsx(View, { style: props.style ?? styles.container, children: _jsx(ReaderViewport, { ...toReaderViewportProps(props), dom: DOM_CONFIGURATION }) }));
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=ReaderView.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReaderView.native.js","sourceRoot":"","sources":["../../../../src/features/reader/adapters/ReaderView.native.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAEpE,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE;CACvC,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG;IACxB,yBAAyB,EAAE,KAAK;IAChC,qCAAqC,EAAE,KAAK;IAC5C,+BAA+B,EAAE,IAAI;IACrC,eAAe,EAAE,CAAC,aAAa,EAAE,QAAQ,CAAC;IAC1C,aAAa,EAAE,KAAK;IACpB,KAAK,EAAE,MAAM,CAAC,SAAS;IACvB,6BAA6B,EAAE,KAAK;CACrC,CAAC;AAEF,kFAAkF;AAClF,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,OAAO,CACL,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,SAAS,YAC1C,KAAC,cAAc,OAAK,qBAAqB,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,iBAAiB,GAAI,GACvE,CACR,CAAC;AACJ,CAAC","sourcesContent":["import React from 'react';\nimport { StyleSheet, View } from 'react-native';\n\nimport type { ReaderViewProps } from '../../../types/reader.js';\nimport ReaderViewport from '../ReaderViewport.js';\nimport { toReaderViewportProps } from '../toReaderViewportProps.js';\n\nconst styles = StyleSheet.create({\n container: { flex: 1, minHeight: 360 },\n});\n\nconst DOM_CONFIGURATION = {\n allowsInlineMediaPlayback: false,\n javaScriptCanOpenWindowsAutomatically: false,\n mediaPlaybackRequiresUserAction: true,\n originWhitelist: ['about:blank', 'blob:*'],\n scrollEnabled: false,\n style: styles.container,\n unstable_useExpoModulesBridge: false,\n};\n\n/*** Host the same EPUB/PDF DOM viewport in Expo's isolated native DOM WebView. */\nexport function ReaderView(props: ReaderViewProps): React.ReactElement {\n return (\n <View style={props.style ?? styles.container}>\n <ReaderViewport {...toReaderViewportProps(props)} dom={DOM_CONFIGURATION} />\n </View>\n );\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ReaderViewProps } from '../../../types/reader.js';
|
|
3
|
+
/*** Render the standalone EPUB/PDF viewport directly in a browser-owned DOM region. */
|
|
4
|
+
export declare function ReaderView(props: ReaderViewProps): React.ReactElement;
|
|
5
|
+
//# sourceMappingURL=ReaderView.web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReaderView.web.d.ts","sourceRoot":"","sources":["../../../../src/features/reader/adapters/ReaderView.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAWhE,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CASrE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import ReaderViewport from '../ReaderViewport.js';
|
|
4
|
+
import { toReaderViewportProps } from '../toReaderViewportProps.js';
|
|
5
|
+
const DEFAULT_WEB_STYLE = {
|
|
6
|
+
height: '100%',
|
|
7
|
+
minHeight: 360,
|
|
8
|
+
position: 'relative',
|
|
9
|
+
width: '100%',
|
|
10
|
+
};
|
|
11
|
+
/*** Render the standalone EPUB/PDF viewport directly in a browser-owned DOM region. */
|
|
12
|
+
export function ReaderView(props) {
|
|
13
|
+
const style = props.style === undefined ? DEFAULT_WEB_STYLE : { ...DEFAULT_WEB_STYLE, ...props.style };
|
|
14
|
+
return (_jsx("div", { style: style, children: _jsx(ReaderViewport, { ...toReaderViewportProps(props) }) }));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=ReaderView.web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReaderView.web.js","sourceRoot":"","sources":["../../../../src/features/reader/adapters/ReaderView.web.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAEpE,MAAM,iBAAiB,GAAwB;IAC7C,MAAM,EAAE,MAAM;IACd,SAAS,EAAE,GAAG;IACd,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,MAAM;CACd,CAAC;AAEF,uFAAuF;AACvF,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,MAAM,KAAK,GACT,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,GAAG,iBAAiB,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAE3F,OAAO,CACL,cAAK,KAAK,EAAE,KAAK,YACf,KAAC,cAAc,OAAK,qBAAqB,CAAC,KAAK,CAAC,GAAI,GAChD,CACP,CAAC;AACJ,CAAC","sourcesContent":["import React from 'react';\n\nimport type { ReaderViewProps } from '../../../types/reader.js';\nimport ReaderViewport from '../ReaderViewport.js';\nimport { toReaderViewportProps } from '../toReaderViewportProps.js';\n\nconst DEFAULT_WEB_STYLE: React.CSSProperties = {\n height: '100%',\n minHeight: 360,\n position: 'relative',\n width: '100%',\n};\n\n/*** Render the standalone EPUB/PDF viewport directly in a browser-owned DOM region. */\nexport function ReaderView(props: ReaderViewProps): React.ReactElement {\n const style =\n props.style === undefined ? DEFAULT_WEB_STYLE : { ...DEFAULT_WEB_STYLE, ...props.style };\n\n return (\n <div style={style}>\n <ReaderViewport {...toReaderViewportProps(props)} />\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReaderAppearance, ReaderDriver, ReaderDriverState } from '../../../types/reader.js';
|
|
2
|
+
export declare class EpubReaderDriver implements ReaderDriver {
|
|
3
|
+
#private;
|
|
4
|
+
readonly direction: 'ltr' | 'rtl';
|
|
5
|
+
private constructor();
|
|
6
|
+
static create(args: {
|
|
7
|
+
appearance: ReaderAppearance;
|
|
8
|
+
bytes: Uint8Array;
|
|
9
|
+
container: HTMLElement;
|
|
10
|
+
initialLocation?: string;
|
|
11
|
+
onExternalLink: (url: string) => void;
|
|
12
|
+
onStateChange: (state: ReaderDriverState) => void;
|
|
13
|
+
signal: AbortSignal;
|
|
14
|
+
}): Promise<EpubReaderDriver>;
|
|
15
|
+
getState(): ReaderDriverState;
|
|
16
|
+
goNext(_trigger: Parameters<ReaderDriver['goNext']>[0]): Promise<void>;
|
|
17
|
+
goPrevious(_trigger: Parameters<ReaderDriver['goPrevious']>[0]): Promise<void>;
|
|
18
|
+
destroy(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=EpubReaderDriver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EpubReaderDriver.d.ts","sourceRoot":"","sources":["../../../../src/features/reader/drivers/EpubReaderDriver.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAOlG,qBAAa,gBAAiB,YAAW,YAAY;;IACnD,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC;IAWlC,OAAO;WAoBM,MAAM,CAAC,IAAI,EAAE;QACxB,UAAU,EAAE,gBAAgB,CAAC;QAC7B,KAAK,EAAE,UAAU,CAAC;QAClB,SAAS,EAAE,WAAW,CAAC;QACvB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QACtC,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;QAClD,MAAM,EAAE,WAAW,CAAC;KACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqE7B,QAAQ,IAAI,iBAAiB;IAuBvB,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtE,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAW9E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAS/B"}
|