@open-press/cli 0.8.0 → 1.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/README.md +33 -23
- package/dist/cli.js +320 -252
- package/package.json +9 -8
- package/template/core/AGENTS.md +0 -126
- package/template/core/CHANGELOG.md +0 -215
- package/template/core/README.md +0 -40
- package/template/core/engine/cli.mjs +0 -96
- package/template/core/engine/commands/_shared.mjs +0 -177
- package/template/core/engine/commands/deploy.mjs +0 -31
- package/template/core/engine/commands/dev.mjs +0 -49
- package/template/core/engine/commands/doctor.mjs +0 -229
- package/template/core/engine/commands/export.mjs +0 -8
- package/template/core/engine/commands/init.mjs +0 -24
- package/template/core/engine/commands/inspect.mjs +0 -35
- package/template/core/engine/commands/pdf.mjs +0 -26
- package/template/core/engine/commands/preview.mjs +0 -26
- package/template/core/engine/commands/render.mjs +0 -17
- package/template/core/engine/commands/replace.mjs +0 -41
- package/template/core/engine/commands/search.mjs +0 -33
- package/template/core/engine/commands/typecheck.mjs +0 -5
- package/template/core/engine/commands/upgrade.mjs +0 -159
- package/template/core/engine/commands/validate.mjs +0 -17
- package/template/core/engine/document-export.mjs +0 -15
- package/template/core/engine/init.mjs +0 -90
- package/template/core/engine/output/chrome-pdf.d.mts +0 -34
- package/template/core/engine/output/chrome-pdf.mjs +0 -358
- package/template/core/engine/output/deploy-sync.mjs +0 -15
- package/template/core/engine/output/fonts.mjs +0 -62
- package/template/core/engine/output/katex-assets.mjs +0 -45
- package/template/core/engine/output/page-block.mjs +0 -30
- package/template/core/engine/output/pdf-media.mjs +0 -45
- package/template/core/engine/output/public-assets.mjs +0 -19
- package/template/core/engine/output/static-server.mjs +0 -532
- package/template/core/engine/react/caption-numbering.mjs +0 -73
- package/template/core/engine/react/comment-endpoint.d.mts +0 -11
- package/template/core/engine/react/comment-endpoint.mjs +0 -102
- package/template/core/engine/react/comment-marker.mjs +0 -374
- package/template/core/engine/react/document-entry.mjs +0 -324
- package/template/core/engine/react/document-export.mjs +0 -373
- package/template/core/engine/react/http-json.mjs +0 -24
- package/template/core/engine/react/mdx-compile.mjs +0 -599
- package/template/core/engine/react/measurement-css.mjs +0 -136
- package/template/core/engine/react/object-entities.mjs +0 -119
- package/template/core/engine/react/pagination/allocator.mjs +0 -122
- package/template/core/engine/react/pagination/regions.mjs +0 -81
- package/template/core/engine/react/pagination-constants.mjs +0 -3
- package/template/core/engine/react/pagination.mjs +0 -9
- package/template/core/engine/react/pipeline/allocate.mjs +0 -251
- package/template/core/engine/react/pipeline/final-render.mjs +0 -94
- package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -302
- package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
- package/template/core/engine/react/project-asset-endpoint.d.mts +0 -10
- package/template/core/engine/react/project-asset-endpoint.mjs +0 -361
- package/template/core/engine/react/section-css.mjs +0 -56
- package/template/core/engine/react/source-edit-endpoint.d.mts +0 -10
- package/template/core/engine/react/source-edit-endpoint.mjs +0 -75
- package/template/core/engine/react/sources/heading-numbering.mjs +0 -132
- package/template/core/engine/react/sources/mdx-resolver.mjs +0 -439
- package/template/core/engine/react/style-discovery.mjs +0 -142
- package/template/core/engine/runtime/config.d.mts +0 -40
- package/template/core/engine/runtime/config.mjs +0 -175
- package/template/core/engine/runtime/file-utils.mjs +0 -106
- package/template/core/engine/runtime/file-walk.mjs +0 -22
- package/template/core/engine/runtime/inspection.mjs +0 -328
- package/template/core/engine/runtime/issue-report.mjs +0 -44
- package/template/core/engine/runtime/path-utils.mjs +0 -20
- package/template/core/engine/runtime/source-text-tools.d.mts +0 -102
- package/template/core/engine/runtime/source-text-tools.mjs +0 -832
- package/template/core/engine/runtime/source-workspace.mjs +0 -159
- package/template/core/engine/runtime/validation.mjs +0 -174
- package/template/core/index.html +0 -13
- package/template/core/openpress.config.mjs +0 -12
- package/template/core/package.json +0 -91
- package/template/core/src/main.tsx +0 -16
- package/template/core/src/openpress/app/OpenPressApp.tsx +0 -140
- package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -94
- package/template/core/src/openpress/app/index.ts +0 -2
- package/template/core/src/openpress/core/Frame.tsx +0 -78
- package/template/core/src/openpress/core/FrameContext.tsx +0 -24
- package/template/core/src/openpress/core/MdxArea.tsx +0 -34
- package/template/core/src/openpress/core/Press.tsx +0 -34
- package/template/core/src/openpress/core/cn.ts +0 -4
- package/template/core/src/openpress/core/index.tsx +0 -40
- package/template/core/src/openpress/core/primitives.tsx +0 -44
- package/template/core/src/openpress/core/types.ts +0 -191
- package/template/core/src/openpress/core/useSource.ts +0 -28
- package/template/core/src/openpress/document-model/anchorMapModel.ts +0 -27
- package/template/core/src/openpress/document-model/documentIndexes.ts +0 -329
- package/template/core/src/openpress/document-model/documentTypes.ts +0 -138
- package/template/core/src/openpress/document-model/index.ts +0 -6
- package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -51
- package/template/core/src/openpress/document-model/projectIdentityModel.ts +0 -15
- package/template/core/src/openpress/document-model/reactDocumentMetadataModel.ts +0 -27
- package/template/core/src/openpress/manuscript/index.tsx +0 -238
- package/template/core/src/openpress/mdx/index.ts +0 -88
- package/template/core/src/openpress/numbering/index.ts +0 -294
- package/template/core/src/openpress/reader/PublicReaderPage.tsx +0 -267
- package/template/core/src/openpress/reader/ReaderNavigationPanel.tsx +0 -123
- package/template/core/src/openpress/reader/index.ts +0 -10
- package/template/core/src/openpress/reader/pageViewportScaleModel.ts +0 -73
- package/template/core/src/openpress/reader/readerPageRegistry.ts +0 -41
- package/template/core/src/openpress/reader/readerPageRoute.ts +0 -21
- package/template/core/src/openpress/reader/readerScroll.ts +0 -92
- package/template/core/src/openpress/reader/readerStateModel.ts +0 -15
- package/template/core/src/openpress/reader/readerTypes.ts +0 -4
- package/template/core/src/openpress/reader/usePageViewportScale.ts +0 -119
- package/template/core/src/openpress/reader/usePanelState.ts +0 -56
- package/template/core/src/openpress/reader/useReaderHashSync.ts +0 -61
- package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +0 -48
- package/template/core/src/openpress/reader/useReaderRuntime.ts +0 -146
- package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +0 -64
- package/template/core/src/openpress/shared/Panel.tsx +0 -77
- package/template/core/src/openpress/shared/frameScheduler.ts +0 -32
- package/template/core/src/openpress/shared/index.ts +0 -4
- package/template/core/src/openpress/shared/numberUtils.ts +0 -3
- package/template/core/src/openpress/shared/runtimeMode.ts +0 -11
- package/template/core/src/openpress/workbench/Workbench.tsx +0 -407
- package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
- package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +0 -182
- package/template/core/src/openpress/workbench/actions/SearchControl.tsx +0 -345
- package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +0 -112
- package/template/core/src/openpress/workbench/actions/index.ts +0 -5
- package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +0 -136
- package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +0 -72
- package/template/core/src/openpress/workbench/dialog/index.ts +0 -1
- package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +0 -127
- package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +0 -207
- package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +0 -9
- package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +0 -34
- package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +0 -525
- package/template/core/src/openpress/workbench/document/index.ts +0 -10
- package/template/core/src/openpress/workbench/index.ts +0 -2
- package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +0 -459
- package/template/core/src/openpress/workbench/inspector/index.ts +0 -5
- package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +0 -125
- package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +0 -160
- package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +0 -408
- package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +0 -248
- package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +0 -41
- package/template/core/src/openpress/workbench/mentions/index.ts +0 -2
- package/template/core/src/openpress/workbench/mentions/useComposerMentions.ts +0 -185
- package/template/core/src/openpress/workbench/panels/Panel.tsx +0 -1
- package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +0 -76
- package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +0 -29
- package/template/core/src/openpress/workbench/panels/index.ts +0 -3
- package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +0 -523
- package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +0 -35
- package/template/core/src/openpress/workbench/project/index.ts +0 -2
- package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +0 -11
- package/template/core/src/openpress/workbench/project/projectSourceModel.ts +0 -24
- package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +0 -167
- package/template/core/src/openpress/workbench/shell/index.ts +0 -1
- package/template/core/src/openpress/workbench/workbenchFormatters.ts +0 -120
- package/template/core/src/openpress/workbench/workbenchTypes.ts +0 -35
- package/template/core/src/styles/openpress/app-shell.css +0 -251
- package/template/core/src/styles/openpress/media-workspace.css +0 -230
- package/template/core/src/styles/openpress/print-route.css +0 -184
- package/template/core/src/styles/openpress/project-preview-panel.css +0 -924
- package/template/core/src/styles/openpress/public-viewer.css +0 -688
- package/template/core/src/styles/openpress/reader-runtime.css +0 -980
- package/template/core/src/styles/openpress/responsive.css +0 -245
- package/template/core/src/styles/openpress/workbench-panels.css +0 -594
- package/template/core/src/styles/openpress/workbench.css +0 -1255
- package/template/core/src/styles/openpress.css +0 -14
- package/template/core/src/vite-env.d.ts +0 -9
- package/template/core/tsconfig.json +0 -40
- package/template/core/vite.config.ts +0 -584
- package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +0 -35
- package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +0 -50
- package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +0 -47
- package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +0 -26
- package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +0 -32
- package/template/packs/academic-paper/document/components/ChapterOpenerVisual/index.tsx +0 -76
- package/template/packs/academic-paper/document/components/Page.tsx +0 -60
- package/template/packs/academic-paper/document/components/TokenSwatchGrid/index.tsx +0 -46
- package/template/packs/academic-paper/document/components/TokenSwatchGrid/style.css +0 -63
- package/template/packs/academic-paper/document/components/TypeSpecimen/index.tsx +0 -38
- package/template/packs/academic-paper/document/components/TypeSpecimen/style.css +0 -111
- package/template/packs/academic-paper/document/design.md +0 -279
- package/template/packs/academic-paper/document/index.tsx +0 -123
- package/template/packs/academic-paper/document/media/README.md +0 -13
- package/template/packs/academic-paper/document/media/figure-placeholder.svg +0 -9
- package/template/packs/academic-paper/document/openpress.config.mjs +0 -26
- package/template/packs/academic-paper/document/theme/README.md +0 -11
- package/template/packs/academic-paper/document/theme/base/page-contract.css +0 -522
- package/template/packs/academic-paper/document/theme/base/print.css +0 -93
- package/template/packs/academic-paper/document/theme/base/typography.css +0 -333
- package/template/packs/academic-paper/document/theme/fonts.css +0 -3
- package/template/packs/academic-paper/document/theme/page-surfaces/back-cover.css +0 -43
- package/template/packs/academic-paper/document/theme/page-surfaces/chapter-opener.css +0 -205
- package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +0 -294
- package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +0 -149
- package/template/packs/academic-paper/document/theme/patterns/_chart-frame.css +0 -49
- package/template/packs/academic-paper/document/theme/patterns/figure-grid.css +0 -68
- package/template/packs/academic-paper/document/theme/patterns/table-utilities.css +0 -66
- package/template/packs/academic-paper/document/theme/shell/reader-controls.css +0 -761
- package/template/packs/academic-paper/document/theme/tokens.css +0 -80
- package/template/packs/academic-paper/openpress.config.mjs +0 -5
- package/template/packs/claude-document/document/chapters/01-document-shape/content/01-document-shape.mdx +0 -51
- package/template/packs/claude-document/document/chapters/02-review-loop/content/01-review-loop.mdx +0 -31
- package/template/packs/claude-document/document/components/ChapterOpenerVisual.tsx +0 -96
- package/template/packs/claude-document/document/components/Page.tsx +0 -37
- package/template/packs/claude-document/document/design.md +0 -142
- package/template/packs/claude-document/document/index.tsx +0 -94
- package/template/packs/claude-document/document/media/README.md +0 -13
- package/template/packs/claude-document/document/openpress.config.mjs +0 -26
- package/template/packs/claude-document/document/theme/README.md +0 -15
- package/template/packs/claude-document/document/theme/base/page-contract.css +0 -525
- package/template/packs/claude-document/document/theme/base/print.css +0 -93
- package/template/packs/claude-document/document/theme/base/typography.css +0 -612
- package/template/packs/claude-document/document/theme/fonts.css +0 -4
- package/template/packs/claude-document/document/theme/page-surfaces/back-cover.css +0 -72
- package/template/packs/claude-document/document/theme/page-surfaces/chapter-opener.css +0 -236
- package/template/packs/claude-document/document/theme/page-surfaces/cover.css +0 -309
- package/template/packs/claude-document/document/theme/page-surfaces/toc.css +0 -225
- package/template/packs/claude-document/document/theme/patterns/_chart-frame.css +0 -53
- package/template/packs/claude-document/document/theme/patterns/figure-grid.css +0 -68
- package/template/packs/claude-document/document/theme/patterns/table-utilities.css +0 -66
- package/template/packs/claude-document/document/theme/shell/reader-controls.css +0 -789
- package/template/packs/claude-document/document/theme/tokens.css +0 -89
- package/template/packs/claude-document/openpress.config.mjs +0 -5
- package/template/packs/editorial-monograph/document/chapters/01-product-and-use-cases/content/01-product-and-use-cases.mdx +0 -31
- package/template/packs/editorial-monograph/document/chapters/02-workflow/content/01-workflow.mdx +0 -89
- package/template/packs/editorial-monograph/document/chapters/03-agent-skills-contributors/content/01-agent-skills-contributors.mdx +0 -51
- package/template/packs/editorial-monograph/document/chapters/04-validation-deploy/content/01-validation-deploy.mdx +0 -39
- package/template/packs/editorial-monograph/document/components/ChapterOpenerVisual/index.tsx +0 -76
- package/template/packs/editorial-monograph/document/components/Page.tsx +0 -37
- package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/index.tsx +0 -46
- package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/style.css +0 -63
- package/template/packs/editorial-monograph/document/components/TypeSpecimen/index.tsx +0 -38
- package/template/packs/editorial-monograph/document/components/TypeSpecimen/style.css +0 -111
- package/template/packs/editorial-monograph/document/design.md +0 -279
- package/template/packs/editorial-monograph/document/index.tsx +0 -97
- package/template/packs/editorial-monograph/document/media/README.md +0 -13
- package/template/packs/editorial-monograph/document/openpress.config.mjs +0 -26
- package/template/packs/editorial-monograph/document/theme/README.md +0 -11
- package/template/packs/editorial-monograph/document/theme/base/page-contract.css +0 -505
- package/template/packs/editorial-monograph/document/theme/base/print.css +0 -93
- package/template/packs/editorial-monograph/document/theme/base/typography.css +0 -336
- package/template/packs/editorial-monograph/document/theme/fonts.css +0 -3
- package/template/packs/editorial-monograph/document/theme/page-surfaces/back-cover.css +0 -43
- package/template/packs/editorial-monograph/document/theme/page-surfaces/chapter-opener.css +0 -205
- package/template/packs/editorial-monograph/document/theme/page-surfaces/cover.css +0 -147
- package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +0 -149
- package/template/packs/editorial-monograph/document/theme/patterns/_chart-frame.css +0 -49
- package/template/packs/editorial-monograph/document/theme/patterns/figure-grid.css +0 -68
- package/template/packs/editorial-monograph/document/theme/patterns/table-utilities.css +0 -66
- package/template/packs/editorial-monograph/document/theme/shell/reader-controls.css +0 -761
- package/template/packs/editorial-monograph/document/theme/tokens.css +0 -80
- package/template/packs/editorial-monograph/openpress.config.mjs +0 -5
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useMemo,
|
|
3
|
-
useRef,
|
|
4
|
-
type CSSProperties,
|
|
5
|
-
type MouseEvent as ReactMouseEvent,
|
|
6
|
-
type RefCallback,
|
|
7
|
-
type RefObject,
|
|
8
|
-
} from "react";
|
|
9
|
-
import { BookOpen, ExternalLink, X } from "lucide-react";
|
|
10
|
-
import {
|
|
11
|
-
collectBookmarkIndex,
|
|
12
|
-
createAnchorPageMap,
|
|
13
|
-
createPageObjectEntityId,
|
|
14
|
-
getProjectIdentity,
|
|
15
|
-
resolveAnchorPageIndex,
|
|
16
|
-
type DeploymentInfo,
|
|
17
|
-
type HtmlPageBlock,
|
|
18
|
-
type ReaderDocument,
|
|
19
|
-
} from "../document-model";
|
|
20
|
-
import type { InspectorState } from "../workbench/inspector";
|
|
21
|
-
import { useReaderRuntime } from "./useReaderRuntime";
|
|
22
|
-
import { Bookmarks, CurrentPagePanel } from "./ReaderNavigationPanel";
|
|
23
|
-
import type { DisplayPage } from "./readerTypes";
|
|
24
|
-
import { usePageViewportScale } from "./usePageViewportScale";
|
|
25
|
-
import type { PageLayoutMode } from "./pageViewportScaleModel";
|
|
26
|
-
|
|
27
|
-
export const PUBLIC_DRAWER_BREAKPOINT = 1185;
|
|
28
|
-
export type ViewMode = "paged";
|
|
29
|
-
export type PageInspector = Pick<InspectorState, "enabled" | "handleClick">;
|
|
30
|
-
|
|
31
|
-
export function PublicViewer({
|
|
32
|
-
document,
|
|
33
|
-
pages,
|
|
34
|
-
style,
|
|
35
|
-
deploymentInfo = { online: false },
|
|
36
|
-
}: {
|
|
37
|
-
document: ReaderDocument;
|
|
38
|
-
pages: Array<HtmlPageBlock>;
|
|
39
|
-
style: CSSProperties;
|
|
40
|
-
deploymentInfo?: DeploymentInfo;
|
|
41
|
-
}) {
|
|
42
|
-
const sourceContainerRef = useRef<HTMLDivElement | null>(null);
|
|
43
|
-
const displayPages = pages;
|
|
44
|
-
const viewModeState = useViewMode();
|
|
45
|
-
const { viewMode } = viewModeState;
|
|
46
|
-
const bookmarks = collectBookmarkIndex(displayPages);
|
|
47
|
-
const anchorPageMap = useMemo(() => createAnchorPageMap(displayPages), [displayPages]);
|
|
48
|
-
const reader = useReaderRuntime({
|
|
49
|
-
pageCount: displayPages.length,
|
|
50
|
-
rightPanelBreakpoint: PUBLIC_DRAWER_BREAKPOINT,
|
|
51
|
-
});
|
|
52
|
-
usePageViewportScale({
|
|
53
|
-
stageRef: reader.stageRef,
|
|
54
|
-
pageContainerRef: sourceContainerRef,
|
|
55
|
-
pageCount: displayPages.length,
|
|
56
|
-
layoutMode: "single",
|
|
57
|
-
});
|
|
58
|
-
const currentPage = displayPages[reader.currentPageIndex];
|
|
59
|
-
const staticPdfHref = deploymentInfo.pdf;
|
|
60
|
-
const projectIdentity = getProjectIdentity(document.meta);
|
|
61
|
-
|
|
62
|
-
const drawerOpen = reader.rightPanelOpen;
|
|
63
|
-
|
|
64
|
-
const selectPublicPage = (pageIndex: number, options?: { behavior?: ScrollBehavior }) => {
|
|
65
|
-
reader.setPage(pageIndex, options);
|
|
66
|
-
if (window.innerWidth < PUBLIC_DRAWER_BREAKPOINT && drawerOpen) reader.toggleRightPanel();
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const selectPublicAnchor = (anchorId: string, pageIndex?: number) => {
|
|
70
|
-
const targetPageIndex = resolveAnchorPageIndex(anchorPageMap, displayPages.length, anchorId, pageIndex);
|
|
71
|
-
if (targetPageIndex === null) return false;
|
|
72
|
-
selectPublicPage(targetPageIndex, { behavior: "smooth" });
|
|
73
|
-
return true;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const appClassName = [
|
|
77
|
-
"reader-app openpress-reader-app openpress-public-viewer is-ready",
|
|
78
|
-
drawerOpen ? "" : "is-closed-right",
|
|
79
|
-
].filter(Boolean).join(" ");
|
|
80
|
-
|
|
81
|
-
const handleOpenStaticPdf = () => {
|
|
82
|
-
if (!staticPdfHref) return;
|
|
83
|
-
window.open(staticPdfHref, "_blank", "noopener,noreferrer");
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
<main className="openpress-workbench openpress-public-shell" style={style} data-openpress-public-viewer="true" aria-label={`${document.meta.title} 公開頁`}>
|
|
88
|
-
<div
|
|
89
|
-
className={appClassName}
|
|
90
|
-
data-openpress-react-runtime="true"
|
|
91
|
-
data-openpress-view-mode={viewMode}
|
|
92
|
-
>
|
|
93
|
-
{drawerOpen && (
|
|
94
|
-
<div className="openpress-public-scrim" aria-hidden="true" onClick={reader.toggleRightPanel} />
|
|
95
|
-
)}
|
|
96
|
-
<button type="button" className="openpress-public-fab" aria-label="開啟目錄" onClick={reader.toggleRightPanel}>
|
|
97
|
-
<BookOpen size={20} aria-hidden="true" />
|
|
98
|
-
</button>
|
|
99
|
-
|
|
100
|
-
<section className="openpress-workbench__stage openpress-public-viewer__stage" aria-label="公開文件頁面">
|
|
101
|
-
<main className="reader-stage" tabIndex={-1} ref={reader.stageRef}>
|
|
102
|
-
<PublicPage
|
|
103
|
-
pages={displayPages}
|
|
104
|
-
currentPageIndex={reader.currentPageIndex}
|
|
105
|
-
devMode={false}
|
|
106
|
-
sourceContainerRef={sourceContainerRef}
|
|
107
|
-
registerPage={reader.registerPage}
|
|
108
|
-
onInternalAnchorNavigate={selectPublicAnchor}
|
|
109
|
-
/>
|
|
110
|
-
</main>
|
|
111
|
-
</section>
|
|
112
|
-
|
|
113
|
-
<aside className="reader-side-nav openpress-workspace-panel openpress-public-navigation" aria-label="文件導覽">
|
|
114
|
-
<button type="button" className="openpress-public-drawer-close" aria-label="關閉目錄" onClick={reader.toggleRightPanel}>
|
|
115
|
-
<X size={16} aria-hidden="true" />
|
|
116
|
-
</button>
|
|
117
|
-
<section className="openpress-public-identity" aria-label="文件資訊">
|
|
118
|
-
<strong>
|
|
119
|
-
<span className="openpress-public-title-main">{projectIdentity.name}</span>
|
|
120
|
-
{projectIdentity.subtitle ? <span className="openpress-public-title-sub">{projectIdentity.subtitle}</span> : null}
|
|
121
|
-
</strong>
|
|
122
|
-
{projectIdentity.label ? <span>{projectIdentity.label}</span> : null}
|
|
123
|
-
</section>
|
|
124
|
-
<div className="openpress-public-actions" aria-label="文件操作">
|
|
125
|
-
<button
|
|
126
|
-
type="button"
|
|
127
|
-
className="openpress-public-export-button"
|
|
128
|
-
data-openpress-public-export
|
|
129
|
-
disabled={!staticPdfHref}
|
|
130
|
-
onClick={handleOpenStaticPdf}
|
|
131
|
-
>
|
|
132
|
-
<ExternalLink aria-hidden="true" />
|
|
133
|
-
{!staticPdfHref ? "PDF 未部署" : "開啟 PDF"}
|
|
134
|
-
</button>
|
|
135
|
-
</div>
|
|
136
|
-
<section id="openpress-bookmarks" className="openpress-panel-section openpress-panel-section--bookmarks" aria-label="章節書籤">
|
|
137
|
-
<nav className="reader-bookmarks" aria-label="章節導覽" data-openpress-react-bookmarks="true">
|
|
138
|
-
<div className="reader-bookmarks-rail" aria-hidden="true" />
|
|
139
|
-
<Bookmarks items={bookmarks} currentPageIndex={reader.currentPageIndex} onSelectPage={selectPublicPage} />
|
|
140
|
-
</nav>
|
|
141
|
-
</section>
|
|
142
|
-
<CurrentPagePanel
|
|
143
|
-
currentPageLabel={reader.currentPageLabel}
|
|
144
|
-
totalPageLabel={reader.totalPageLabel}
|
|
145
|
-
progressPercent={reader.progressPercent}
|
|
146
|
-
title={currentPage?.title || document.meta.title}
|
|
147
|
-
pageLabelPrefix="頁"
|
|
148
|
-
showHeading={false}
|
|
149
|
-
showTitle={false}
|
|
150
|
-
/>
|
|
151
|
-
</aside>
|
|
152
|
-
</div>
|
|
153
|
-
</main>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function useViewMode(): { viewMode: ViewMode } {
|
|
158
|
-
return { viewMode: "paged" };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function PrintDocument({
|
|
162
|
-
document,
|
|
163
|
-
pages,
|
|
164
|
-
style,
|
|
165
|
-
}: {
|
|
166
|
-
document: ReaderDocument;
|
|
167
|
-
pages: Array<HtmlPageBlock>;
|
|
168
|
-
style: CSSProperties;
|
|
169
|
-
}) {
|
|
170
|
-
const sourceContainerRef = useRef<HTMLDivElement | null>(null);
|
|
171
|
-
const displayPages = pages;
|
|
172
|
-
const registerPage = () => () => undefined;
|
|
173
|
-
|
|
174
|
-
return (
|
|
175
|
-
<main
|
|
176
|
-
className="openpress-print-document"
|
|
177
|
-
style={style}
|
|
178
|
-
data-openpress-print-document="true"
|
|
179
|
-
aria-label={`${document.meta.title} PDF 輸出`}
|
|
180
|
-
>
|
|
181
|
-
<PublicPage
|
|
182
|
-
pages={displayPages}
|
|
183
|
-
currentPageIndex={0}
|
|
184
|
-
devMode={false}
|
|
185
|
-
sourceContainerRef={sourceContainerRef}
|
|
186
|
-
registerPage={registerPage}
|
|
187
|
-
exposeSourceData
|
|
188
|
-
/>
|
|
189
|
-
</main>
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export function PublicPage({
|
|
194
|
-
pages,
|
|
195
|
-
currentPageIndex,
|
|
196
|
-
devMode,
|
|
197
|
-
sourceContainerRef,
|
|
198
|
-
registerPage,
|
|
199
|
-
exposeSourceData = false,
|
|
200
|
-
inspector,
|
|
201
|
-
onInternalAnchorNavigate,
|
|
202
|
-
pageLayoutMode = "single",
|
|
203
|
-
}: {
|
|
204
|
-
pages: DisplayPage[];
|
|
205
|
-
currentPageIndex: number;
|
|
206
|
-
devMode: boolean;
|
|
207
|
-
sourceContainerRef: RefObject<HTMLDivElement | null>;
|
|
208
|
-
registerPage: (pageIndex: number) => RefCallback<HTMLElement>;
|
|
209
|
-
exposeSourceData?: boolean;
|
|
210
|
-
inspector?: PageInspector;
|
|
211
|
-
onInternalAnchorNavigate?: (anchorId: string, pageIndex?: number) => boolean;
|
|
212
|
-
pageLayoutMode?: PageLayoutMode;
|
|
213
|
-
}) {
|
|
214
|
-
const handlePageClick = (event: ReactMouseEvent<HTMLDivElement>) => {
|
|
215
|
-
if (inspector?.enabled && inspector.handleClick(event)) return;
|
|
216
|
-
if (!onInternalAnchorNavigate || event.defaultPrevented || event.button !== 0) return;
|
|
217
|
-
if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
|
|
218
|
-
if (!(event.target instanceof Element)) return;
|
|
219
|
-
|
|
220
|
-
const link = event.target.closest<HTMLAnchorElement>('a[href^="#"]');
|
|
221
|
-
if (!link) return;
|
|
222
|
-
|
|
223
|
-
const href = link.getAttribute("href") ?? "";
|
|
224
|
-
const anchorId = link.dataset.openpressAnchor || safeDecodeAnchor(href.slice(1));
|
|
225
|
-
if (!anchorId) return;
|
|
226
|
-
|
|
227
|
-
const pageIndex = Number.parseInt(link.dataset.openpressTargetPageIndex ?? "", 10);
|
|
228
|
-
const handled = onInternalAnchorNavigate(anchorId, Number.isFinite(pageIndex) ? pageIndex : undefined);
|
|
229
|
-
if (handled) event.preventDefault();
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
return (
|
|
233
|
-
<div
|
|
234
|
-
className="reader-pages openpress-public-page"
|
|
235
|
-
ref={sourceContainerRef}
|
|
236
|
-
data-openpress-public-page="true"
|
|
237
|
-
data-openpress-page-layout={pageLayoutMode}
|
|
238
|
-
onClick={handlePageClick}
|
|
239
|
-
>
|
|
240
|
-
{pages.map((page) => (
|
|
241
|
-
<div
|
|
242
|
-
key={page.id}
|
|
243
|
-
ref={registerPage(page.pageNumber - 1)}
|
|
244
|
-
id={`page-${String(page.pageNumber).padStart(2, "0")}`}
|
|
245
|
-
className="openpress-html-page"
|
|
246
|
-
data-openpress-object-id={page.frameKey ? createPageObjectEntityId(page.frameKey) : undefined}
|
|
247
|
-
data-openpress-page-index={page.pageNumber - 1}
|
|
248
|
-
data-openpress-page-spread-side={pageLayoutMode === "spread" ? ((page.pageNumber - 1) % 2 === 0 ? "left" : "right") : undefined}
|
|
249
|
-
data-openpress-active={currentPageIndex === page.pageNumber - 1 ? "true" : "false"}
|
|
250
|
-
data-source-path={exposeSourceData ? page.source?.path : undefined}
|
|
251
|
-
data-source-file={exposeSourceData ? page.source?.file : undefined}
|
|
252
|
-
>
|
|
253
|
-
<div className="openpress-html-page__html" dangerouslySetInnerHTML={{ __html: page.html }} />
|
|
254
|
-
</div>
|
|
255
|
-
))}
|
|
256
|
-
</div>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function safeDecodeAnchor(value: string) {
|
|
261
|
-
if (!value) return "";
|
|
262
|
-
try {
|
|
263
|
-
return decodeURIComponent(value);
|
|
264
|
-
} catch {
|
|
265
|
-
return value;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { type CSSProperties, type MouseEvent as ReactMouseEvent } from "react";
|
|
2
|
-
import type { BookmarkItem } from "../document-model";
|
|
3
|
-
import { Panel } from "../shared";
|
|
4
|
-
|
|
5
|
-
type BookmarkSelectOptions = {
|
|
6
|
-
behavior?: ScrollBehavior;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export function Bookmarks({
|
|
10
|
-
items,
|
|
11
|
-
currentPageIndex,
|
|
12
|
-
onSelectPage,
|
|
13
|
-
}: {
|
|
14
|
-
items: BookmarkItem[];
|
|
15
|
-
currentPageIndex: number;
|
|
16
|
-
onSelectPage: (pageIndex: number, options?: BookmarkSelectOptions) => void;
|
|
17
|
-
}) {
|
|
18
|
-
const goToPage = (event: ReactMouseEvent<HTMLButtonElement>, pageIndex: number) => {
|
|
19
|
-
event.preventDefault();
|
|
20
|
-
onSelectPage(pageIndex, { behavior: "smooth" });
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
if (items.length === 0) {
|
|
24
|
-
return <Panel.Empty className="openpress-asset-empty" role="status">尚無書籤</Panel.Empty>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<>
|
|
29
|
-
{items.map((item, index) => {
|
|
30
|
-
const groupActive = currentPageIndex >= item.pageIndex && currentPageIndex <= item.endPageIndex;
|
|
31
|
-
const activeSub = item.subs.find((sub) => currentPageIndex >= sub.pageIndex && currentPageIndex <= sub.endPageIndex);
|
|
32
|
-
const h2SelfActive = groupActive && !activeSub;
|
|
33
|
-
const itemLabel = item.label ?? String(index + 1).padStart(2, "0");
|
|
34
|
-
return (
|
|
35
|
-
<div className={`bookmark-group${groupActive ? " is-open" : ""}`} key={item.id}>
|
|
36
|
-
<button
|
|
37
|
-
type="button"
|
|
38
|
-
className={`bookmark-item bookmark-h2${h2SelfActive ? " is-active" : ""}`}
|
|
39
|
-
data-openpress-page-index={item.pageIndex}
|
|
40
|
-
onClick={(event) => goToPage(event, item.pageIndex)}
|
|
41
|
-
>
|
|
42
|
-
<span className="bookmark-index">{itemLabel}</span>
|
|
43
|
-
<span className="bookmark-title">{item.title}</span>
|
|
44
|
-
</button>
|
|
45
|
-
<div className="bookmark-subs">
|
|
46
|
-
{item.subs.map((sub, subIndex) => {
|
|
47
|
-
const subActive = currentPageIndex >= sub.pageIndex && currentPageIndex <= sub.endPageIndex;
|
|
48
|
-
const activeTopic = sub.subs.find((topic) => currentPageIndex >= topic.pageIndex && currentPageIndex <= topic.endPageIndex);
|
|
49
|
-
const subSelfActive = subActive && !activeTopic;
|
|
50
|
-
const subLabel = sub.label ?? `${itemLabel}.${subIndex + 1}`;
|
|
51
|
-
return (
|
|
52
|
-
<div className="bookmark-subgroup" key={sub.id}>
|
|
53
|
-
<button
|
|
54
|
-
type="button"
|
|
55
|
-
className={`bookmark-item bookmark-h3${subSelfActive ? " is-active" : ""}`}
|
|
56
|
-
data-openpress-page-index={sub.pageIndex}
|
|
57
|
-
onClick={(event) => goToPage(event, sub.pageIndex)}
|
|
58
|
-
>
|
|
59
|
-
<span className="bookmark-index">{subLabel}</span>
|
|
60
|
-
<span className="bookmark-title">{sub.title}</span>
|
|
61
|
-
</button>
|
|
62
|
-
{sub.subs.map((topic, topicIndex) => {
|
|
63
|
-
const topicActive = currentPageIndex >= topic.pageIndex && currentPageIndex <= topic.endPageIndex;
|
|
64
|
-
const topicLabel = topic.label ?? `${subLabel}.${topicIndex + 1}`;
|
|
65
|
-
return (
|
|
66
|
-
<button
|
|
67
|
-
type="button"
|
|
68
|
-
className={`bookmark-item bookmark-h4${topicActive ? " is-active" : ""}`}
|
|
69
|
-
data-openpress-page-index={topic.pageIndex}
|
|
70
|
-
onClick={(event) => goToPage(event, topic.pageIndex)}
|
|
71
|
-
key={topic.id}
|
|
72
|
-
>
|
|
73
|
-
<span className="bookmark-index">{topicLabel}</span>
|
|
74
|
-
<span className="bookmark-title">{topic.title}</span>
|
|
75
|
-
</button>
|
|
76
|
-
);
|
|
77
|
-
})}
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
80
|
-
})}
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
83
|
-
);
|
|
84
|
-
})}
|
|
85
|
-
</>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function CurrentPagePanel({
|
|
90
|
-
currentPageLabel,
|
|
91
|
-
totalPageLabel,
|
|
92
|
-
progressPercent,
|
|
93
|
-
title,
|
|
94
|
-
pageLabelPrefix,
|
|
95
|
-
showHeading = true,
|
|
96
|
-
showTitle = true,
|
|
97
|
-
}: {
|
|
98
|
-
currentPageLabel: string;
|
|
99
|
-
totalPageLabel: string;
|
|
100
|
-
progressPercent: number;
|
|
101
|
-
title: string;
|
|
102
|
-
pageLabelPrefix?: string;
|
|
103
|
-
showHeading?: boolean;
|
|
104
|
-
showTitle?: boolean;
|
|
105
|
-
}) {
|
|
106
|
-
return (
|
|
107
|
-
<Panel.Section className="openpress-panel-section--current" aria-label="目前頁面">
|
|
108
|
-
{showHeading ? <Panel.SectionTitle className="openpress-panel-heading">目前頁面</Panel.SectionTitle> : null}
|
|
109
|
-
<div className="openpress-current-page-card">
|
|
110
|
-
<div className="openpress-current-page-card__number" aria-label="目前頁數">
|
|
111
|
-
{pageLabelPrefix ? <span className="openpress-current-page-card__prefix">{pageLabelPrefix}</span> : null}
|
|
112
|
-
<span data-openpress-current-page>{currentPageLabel}</span>
|
|
113
|
-
<span className="sep">/</span>
|
|
114
|
-
<span data-openpress-total-pages>{totalPageLabel}</span>
|
|
115
|
-
</div>
|
|
116
|
-
{showTitle ? <div className="openpress-current-page-card__title">{title}</div> : null}
|
|
117
|
-
<div className="openpress-current-page-card__progress" aria-hidden="true">
|
|
118
|
-
<span style={{ "--progress": `${progressPercent}%` } as CSSProperties} />
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
</Panel.Section>
|
|
122
|
-
);
|
|
123
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export * from "./PublicReaderPage";
|
|
2
|
-
export * from "./ReaderNavigationPanel";
|
|
3
|
-
export * from "./pageViewportScaleModel";
|
|
4
|
-
export * from "./readerPageRegistry";
|
|
5
|
-
export * from "./readerPageRoute";
|
|
6
|
-
export * from "./readerScroll";
|
|
7
|
-
export * from "./readerStateModel";
|
|
8
|
-
export * from "./readerTypes";
|
|
9
|
-
export * from "./usePageViewportScale";
|
|
10
|
-
export * from "./useReaderRuntime";
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
export type PageLayoutMode = "single" | "spread";
|
|
2
|
-
|
|
3
|
-
export type PageViewportScaleMode =
|
|
4
|
-
| "fit-width"
|
|
5
|
-
| "fit-page"
|
|
6
|
-
| "scale-25"
|
|
7
|
-
| "scale-50"
|
|
8
|
-
| "scale-75"
|
|
9
|
-
| "scale-100"
|
|
10
|
-
| "scale-125"
|
|
11
|
-
| "scale-150"
|
|
12
|
-
| "scale-200";
|
|
13
|
-
|
|
14
|
-
export const PAGE_VIEWPORT_SCALE_OPTIONS: Array<{
|
|
15
|
-
value: PageViewportScaleMode;
|
|
16
|
-
label: string;
|
|
17
|
-
}> = [
|
|
18
|
-
{ value: "scale-25", label: "25%" },
|
|
19
|
-
{ value: "scale-50", label: "50%" },
|
|
20
|
-
{ value: "scale-75", label: "75%" },
|
|
21
|
-
{ value: "scale-100", label: "100%" },
|
|
22
|
-
{ value: "scale-125", label: "125%" },
|
|
23
|
-
{ value: "scale-150", label: "150%" },
|
|
24
|
-
{ value: "scale-200", label: "200%" },
|
|
25
|
-
{ value: "fit-width", label: "符合頁面寬度" },
|
|
26
|
-
{ value: "fit-page", label: "符合全開頁面" },
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
const MIN_PAGE_VIEWPORT_SCALE = 0.12;
|
|
30
|
-
const MAX_FIT_PAGE_VIEWPORT_SCALE = 1;
|
|
31
|
-
const MAX_FIXED_PAGE_VIEWPORT_SCALE = 2;
|
|
32
|
-
|
|
33
|
-
export function resolvePageViewportScale({
|
|
34
|
-
mode,
|
|
35
|
-
fitWidthScale,
|
|
36
|
-
fitPageScale,
|
|
37
|
-
}: {
|
|
38
|
-
mode: PageViewportScaleMode;
|
|
39
|
-
fitWidthScale: number;
|
|
40
|
-
fitPageScale: number;
|
|
41
|
-
}) {
|
|
42
|
-
if (mode === "fit-width") return clampPageViewportScale(fitWidthScale, MAX_FIT_PAGE_VIEWPORT_SCALE);
|
|
43
|
-
if (mode === "fit-page") return clampPageViewportScale(fitPageScale, MAX_FIT_PAGE_VIEWPORT_SCALE);
|
|
44
|
-
return scaleModeToFixedValue(mode);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function formatPageViewportScaleLabel(mode: PageViewportScaleMode, scale: number) {
|
|
48
|
-
void mode;
|
|
49
|
-
return formatPageViewportScalePercent(scale);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function formatPageViewportScalePercent(scale: number) {
|
|
53
|
-
return `${Math.round(clampPageViewportScale(scale, MAX_FIXED_PAGE_VIEWPORT_SCALE) * 100)}%`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function formatPageViewportScaleValue(scale: number) {
|
|
57
|
-
return clampPageViewportScale(scale, MAX_FIXED_PAGE_VIEWPORT_SCALE)
|
|
58
|
-
.toFixed(4)
|
|
59
|
-
.replace(/0+$/, "")
|
|
60
|
-
.replace(/\.$/, "");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function scaleModeToFixedValue(mode: PageViewportScaleMode) {
|
|
64
|
-
const match = /^scale-(\d+)$/.exec(mode);
|
|
65
|
-
if (!match) return 1;
|
|
66
|
-
return clampPageViewportScale(Number.parseInt(match[1] ?? "100", 10) / 100, MAX_FIXED_PAGE_VIEWPORT_SCALE);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function clampPageViewportScale(value: number, maxScale: number) {
|
|
70
|
-
if (!Number.isFinite(value)) return 1;
|
|
71
|
-
const safeMaxScale = Number.isFinite(maxScale) && maxScale > 0 ? maxScale : MAX_FIXED_PAGE_VIEWPORT_SCALE;
|
|
72
|
-
return Math.min(Math.max(value, MIN_PAGE_VIEWPORT_SCALE), safeMaxScale);
|
|
73
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export interface ReaderPageRegistry<TNode> {
|
|
2
|
-
refs: Array<TNode | null>;
|
|
3
|
-
registerPage: (pageIndex: number) => (node: TNode | null) => void;
|
|
4
|
-
trim: (pageCount: number) => void;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function createReaderPageRegistry<TNode = HTMLElement>(
|
|
8
|
-
onChange: (version: number) => void,
|
|
9
|
-
): ReaderPageRegistry<TNode> {
|
|
10
|
-
const refs: Array<TNode | null> = [];
|
|
11
|
-
const callbacks = new Map<number, (node: TNode | null) => void>();
|
|
12
|
-
let version = 0;
|
|
13
|
-
|
|
14
|
-
const bump = () => {
|
|
15
|
-
version += 1;
|
|
16
|
-
onChange(version);
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
refs,
|
|
21
|
-
registerPage(pageIndex: number) {
|
|
22
|
-
const existing = callbacks.get(pageIndex);
|
|
23
|
-
if (existing) return existing;
|
|
24
|
-
|
|
25
|
-
const callback = (node: TNode | null) => {
|
|
26
|
-
if (refs[pageIndex] === node) return;
|
|
27
|
-
refs[pageIndex] = node;
|
|
28
|
-
bump();
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
callbacks.set(pageIndex, callback);
|
|
32
|
-
return callback;
|
|
33
|
-
},
|
|
34
|
-
trim(pageCount: number) {
|
|
35
|
-
refs.length = Math.max(pageCount, 0);
|
|
36
|
-
for (const pageIndex of callbacks.keys()) {
|
|
37
|
-
if (pageIndex >= pageCount) callbacks.delete(pageIndex);
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
const PAGE_HASH_PATTERN = /^#page-(\d+)$/;
|
|
2
|
-
|
|
3
|
-
export function pageHashFromIndex(pageIndex: number) {
|
|
4
|
-
return `#page-${String(Math.max(1, pageIndex + 1)).padStart(2, "0")}`;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function pageIndexFromHash(hash: string, pageCount: number) {
|
|
8
|
-
const match = hash.match(PAGE_HASH_PATTERN);
|
|
9
|
-
if (!match) return null;
|
|
10
|
-
|
|
11
|
-
const pageNumber = Number.parseInt(match[1], 10);
|
|
12
|
-
if (!Number.isFinite(pageNumber) || pageNumber < 1 || pageNumber > pageCount) return null;
|
|
13
|
-
return pageNumber - 1;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function replacePageRoute(pageIndex: number) {
|
|
17
|
-
if (typeof window === "undefined") return;
|
|
18
|
-
const hash = pageHashFromIndex(pageIndex);
|
|
19
|
-
if (window.location.hash === hash) return;
|
|
20
|
-
window.history.replaceState(null, "", hash);
|
|
21
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
// Single place that touches scrollIntoView and IntersectionObserver. Keeping
|
|
2
|
-
// these together makes it obvious which DOM APIs the reader depends on and
|
|
3
|
-
// keeps the React runtime free of imperative scroll bookkeeping.
|
|
4
|
-
|
|
5
|
-
const DEBOUNCE_MS = 100;
|
|
6
|
-
|
|
7
|
-
const OBSERVER_THRESHOLDS = [0, 0.25, 0.5, 0.75, 1];
|
|
8
|
-
|
|
9
|
-
export function scrollToPage(
|
|
10
|
-
refs: Array<HTMLElement | null>,
|
|
11
|
-
pageIndex: number,
|
|
12
|
-
behavior: ScrollBehavior = "smooth",
|
|
13
|
-
root?: HTMLElement | null,
|
|
14
|
-
) {
|
|
15
|
-
const target = refs[pageIndex];
|
|
16
|
-
if (!target) return false;
|
|
17
|
-
|
|
18
|
-
if (root && root.contains(target) && typeof root.scrollTo === "function") {
|
|
19
|
-
const rootRect = root.getBoundingClientRect();
|
|
20
|
-
const targetRect = target.getBoundingClientRect();
|
|
21
|
-
const scrollMarginTop = readScrollMarginTop(target);
|
|
22
|
-
root.scrollTo({
|
|
23
|
-
top: Math.max(0, root.scrollTop + targetRect.top - rootRect.top - scrollMarginTop),
|
|
24
|
-
behavior,
|
|
25
|
-
});
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
target.scrollIntoView({ behavior, block: "start" });
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function readScrollMarginTop(target: HTMLElement) {
|
|
34
|
-
if (typeof window === "undefined") return 0;
|
|
35
|
-
const value = Number.parseFloat(window.getComputedStyle(target).scrollMarginTop);
|
|
36
|
-
return Number.isFinite(value) ? value : 0;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface PageVisibilityObserver {
|
|
40
|
-
observe: (element: Element) => void;
|
|
41
|
-
disconnect: () => void;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function createPageVisibilityObserver(
|
|
45
|
-
root: Element,
|
|
46
|
-
onVisiblePageChange: (pageIndex: number) => void,
|
|
47
|
-
): PageVisibilityObserver | null {
|
|
48
|
-
if (typeof IntersectionObserver === "undefined") return null;
|
|
49
|
-
|
|
50
|
-
const ratios = new Map<Element, number>();
|
|
51
|
-
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
52
|
-
|
|
53
|
-
const flush = () => {
|
|
54
|
-
debounceTimer = null;
|
|
55
|
-
let bestEl: Element | null = null;
|
|
56
|
-
let bestRatio = -1;
|
|
57
|
-
for (const [el, ratio] of ratios) {
|
|
58
|
-
if (ratio > bestRatio) {
|
|
59
|
-
bestRatio = ratio;
|
|
60
|
-
bestEl = el;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (!bestEl || bestRatio <= 0) return;
|
|
64
|
-
const raw = bestEl.getAttribute("data-openpress-page-index");
|
|
65
|
-
if (raw === null) return;
|
|
66
|
-
const parsed = Number.parseInt(raw, 10);
|
|
67
|
-
if (Number.isFinite(parsed)) onVisiblePageChange(parsed);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const observer = new IntersectionObserver(
|
|
71
|
-
(entries) => {
|
|
72
|
-
for (const entry of entries) {
|
|
73
|
-
ratios.set(entry.target, entry.isIntersecting ? entry.intersectionRatio : 0);
|
|
74
|
-
}
|
|
75
|
-
if (debounceTimer !== null) clearTimeout(debounceTimer);
|
|
76
|
-
debounceTimer = setTimeout(flush, DEBOUNCE_MS);
|
|
77
|
-
},
|
|
78
|
-
{ root, threshold: OBSERVER_THRESHOLDS },
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
observe: (element) => observer.observe(element),
|
|
83
|
-
disconnect: () => {
|
|
84
|
-
observer.disconnect();
|
|
85
|
-
if (debounceTimer !== null) {
|
|
86
|
-
clearTimeout(debounceTimer);
|
|
87
|
-
debounceTimer = null;
|
|
88
|
-
}
|
|
89
|
-
ratios.clear();
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export function clampReaderPageIndex(value: number, pageCount: number) {
|
|
2
|
-
const normalizedPageCount = normalizeReaderPageCount(pageCount);
|
|
3
|
-
if (normalizedPageCount <= 0) return 0;
|
|
4
|
-
if (!Number.isFinite(value)) return 0;
|
|
5
|
-
return Math.min(Math.max(Math.trunc(value), 0), normalizedPageCount - 1);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function formatReaderPageNumber(value: number) {
|
|
9
|
-
return String(Math.max(Math.trunc(value), 1)).padStart(2, "0");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function normalizeReaderPageCount(value: number) {
|
|
13
|
-
if (!Number.isFinite(value)) return 0;
|
|
14
|
-
return Math.max(Math.trunc(value), 0);
|
|
15
|
-
}
|