@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.
Files changed (250) hide show
  1. package/README.md +33 -23
  2. package/dist/cli.js +320 -252
  3. package/package.json +9 -8
  4. package/template/core/AGENTS.md +0 -126
  5. package/template/core/CHANGELOG.md +0 -215
  6. package/template/core/README.md +0 -40
  7. package/template/core/engine/cli.mjs +0 -96
  8. package/template/core/engine/commands/_shared.mjs +0 -177
  9. package/template/core/engine/commands/deploy.mjs +0 -31
  10. package/template/core/engine/commands/dev.mjs +0 -49
  11. package/template/core/engine/commands/doctor.mjs +0 -229
  12. package/template/core/engine/commands/export.mjs +0 -8
  13. package/template/core/engine/commands/init.mjs +0 -24
  14. package/template/core/engine/commands/inspect.mjs +0 -35
  15. package/template/core/engine/commands/pdf.mjs +0 -26
  16. package/template/core/engine/commands/preview.mjs +0 -26
  17. package/template/core/engine/commands/render.mjs +0 -17
  18. package/template/core/engine/commands/replace.mjs +0 -41
  19. package/template/core/engine/commands/search.mjs +0 -33
  20. package/template/core/engine/commands/typecheck.mjs +0 -5
  21. package/template/core/engine/commands/upgrade.mjs +0 -159
  22. package/template/core/engine/commands/validate.mjs +0 -17
  23. package/template/core/engine/document-export.mjs +0 -15
  24. package/template/core/engine/init.mjs +0 -90
  25. package/template/core/engine/output/chrome-pdf.d.mts +0 -34
  26. package/template/core/engine/output/chrome-pdf.mjs +0 -358
  27. package/template/core/engine/output/deploy-sync.mjs +0 -15
  28. package/template/core/engine/output/fonts.mjs +0 -62
  29. package/template/core/engine/output/katex-assets.mjs +0 -45
  30. package/template/core/engine/output/page-block.mjs +0 -30
  31. package/template/core/engine/output/pdf-media.mjs +0 -45
  32. package/template/core/engine/output/public-assets.mjs +0 -19
  33. package/template/core/engine/output/static-server.mjs +0 -532
  34. package/template/core/engine/react/caption-numbering.mjs +0 -73
  35. package/template/core/engine/react/comment-endpoint.d.mts +0 -11
  36. package/template/core/engine/react/comment-endpoint.mjs +0 -102
  37. package/template/core/engine/react/comment-marker.mjs +0 -374
  38. package/template/core/engine/react/document-entry.mjs +0 -324
  39. package/template/core/engine/react/document-export.mjs +0 -373
  40. package/template/core/engine/react/http-json.mjs +0 -24
  41. package/template/core/engine/react/mdx-compile.mjs +0 -599
  42. package/template/core/engine/react/measurement-css.mjs +0 -136
  43. package/template/core/engine/react/object-entities.mjs +0 -119
  44. package/template/core/engine/react/pagination/allocator.mjs +0 -122
  45. package/template/core/engine/react/pagination/regions.mjs +0 -81
  46. package/template/core/engine/react/pagination-constants.mjs +0 -3
  47. package/template/core/engine/react/pagination.mjs +0 -9
  48. package/template/core/engine/react/pipeline/allocate.mjs +0 -251
  49. package/template/core/engine/react/pipeline/final-render.mjs +0 -94
  50. package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -302
  51. package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
  52. package/template/core/engine/react/project-asset-endpoint.d.mts +0 -10
  53. package/template/core/engine/react/project-asset-endpoint.mjs +0 -361
  54. package/template/core/engine/react/section-css.mjs +0 -56
  55. package/template/core/engine/react/source-edit-endpoint.d.mts +0 -10
  56. package/template/core/engine/react/source-edit-endpoint.mjs +0 -75
  57. package/template/core/engine/react/sources/heading-numbering.mjs +0 -132
  58. package/template/core/engine/react/sources/mdx-resolver.mjs +0 -439
  59. package/template/core/engine/react/style-discovery.mjs +0 -142
  60. package/template/core/engine/runtime/config.d.mts +0 -40
  61. package/template/core/engine/runtime/config.mjs +0 -175
  62. package/template/core/engine/runtime/file-utils.mjs +0 -106
  63. package/template/core/engine/runtime/file-walk.mjs +0 -22
  64. package/template/core/engine/runtime/inspection.mjs +0 -328
  65. package/template/core/engine/runtime/issue-report.mjs +0 -44
  66. package/template/core/engine/runtime/path-utils.mjs +0 -20
  67. package/template/core/engine/runtime/source-text-tools.d.mts +0 -102
  68. package/template/core/engine/runtime/source-text-tools.mjs +0 -832
  69. package/template/core/engine/runtime/source-workspace.mjs +0 -159
  70. package/template/core/engine/runtime/validation.mjs +0 -174
  71. package/template/core/index.html +0 -13
  72. package/template/core/openpress.config.mjs +0 -12
  73. package/template/core/package.json +0 -91
  74. package/template/core/src/main.tsx +0 -16
  75. package/template/core/src/openpress/app/OpenPressApp.tsx +0 -140
  76. package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -94
  77. package/template/core/src/openpress/app/index.ts +0 -2
  78. package/template/core/src/openpress/core/Frame.tsx +0 -78
  79. package/template/core/src/openpress/core/FrameContext.tsx +0 -24
  80. package/template/core/src/openpress/core/MdxArea.tsx +0 -34
  81. package/template/core/src/openpress/core/Press.tsx +0 -34
  82. package/template/core/src/openpress/core/cn.ts +0 -4
  83. package/template/core/src/openpress/core/index.tsx +0 -40
  84. package/template/core/src/openpress/core/primitives.tsx +0 -44
  85. package/template/core/src/openpress/core/types.ts +0 -191
  86. package/template/core/src/openpress/core/useSource.ts +0 -28
  87. package/template/core/src/openpress/document-model/anchorMapModel.ts +0 -27
  88. package/template/core/src/openpress/document-model/documentIndexes.ts +0 -329
  89. package/template/core/src/openpress/document-model/documentTypes.ts +0 -138
  90. package/template/core/src/openpress/document-model/index.ts +0 -6
  91. package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -51
  92. package/template/core/src/openpress/document-model/projectIdentityModel.ts +0 -15
  93. package/template/core/src/openpress/document-model/reactDocumentMetadataModel.ts +0 -27
  94. package/template/core/src/openpress/manuscript/index.tsx +0 -238
  95. package/template/core/src/openpress/mdx/index.ts +0 -88
  96. package/template/core/src/openpress/numbering/index.ts +0 -294
  97. package/template/core/src/openpress/reader/PublicReaderPage.tsx +0 -267
  98. package/template/core/src/openpress/reader/ReaderNavigationPanel.tsx +0 -123
  99. package/template/core/src/openpress/reader/index.ts +0 -10
  100. package/template/core/src/openpress/reader/pageViewportScaleModel.ts +0 -73
  101. package/template/core/src/openpress/reader/readerPageRegistry.ts +0 -41
  102. package/template/core/src/openpress/reader/readerPageRoute.ts +0 -21
  103. package/template/core/src/openpress/reader/readerScroll.ts +0 -92
  104. package/template/core/src/openpress/reader/readerStateModel.ts +0 -15
  105. package/template/core/src/openpress/reader/readerTypes.ts +0 -4
  106. package/template/core/src/openpress/reader/usePageViewportScale.ts +0 -119
  107. package/template/core/src/openpress/reader/usePanelState.ts +0 -56
  108. package/template/core/src/openpress/reader/useReaderHashSync.ts +0 -61
  109. package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +0 -48
  110. package/template/core/src/openpress/reader/useReaderRuntime.ts +0 -146
  111. package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +0 -64
  112. package/template/core/src/openpress/shared/Panel.tsx +0 -77
  113. package/template/core/src/openpress/shared/frameScheduler.ts +0 -32
  114. package/template/core/src/openpress/shared/index.ts +0 -4
  115. package/template/core/src/openpress/shared/numberUtils.ts +0 -3
  116. package/template/core/src/openpress/shared/runtimeMode.ts +0 -11
  117. package/template/core/src/openpress/workbench/Workbench.tsx +0 -407
  118. package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
  119. package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +0 -182
  120. package/template/core/src/openpress/workbench/actions/SearchControl.tsx +0 -345
  121. package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +0 -112
  122. package/template/core/src/openpress/workbench/actions/index.ts +0 -5
  123. package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +0 -136
  124. package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +0 -72
  125. package/template/core/src/openpress/workbench/dialog/index.ts +0 -1
  126. package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +0 -127
  127. package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +0 -207
  128. package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +0 -9
  129. package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +0 -34
  130. package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +0 -525
  131. package/template/core/src/openpress/workbench/document/index.ts +0 -10
  132. package/template/core/src/openpress/workbench/index.ts +0 -2
  133. package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +0 -459
  134. package/template/core/src/openpress/workbench/inspector/index.ts +0 -5
  135. package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +0 -125
  136. package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +0 -160
  137. package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +0 -408
  138. package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +0 -248
  139. package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +0 -41
  140. package/template/core/src/openpress/workbench/mentions/index.ts +0 -2
  141. package/template/core/src/openpress/workbench/mentions/useComposerMentions.ts +0 -185
  142. package/template/core/src/openpress/workbench/panels/Panel.tsx +0 -1
  143. package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +0 -76
  144. package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +0 -29
  145. package/template/core/src/openpress/workbench/panels/index.ts +0 -3
  146. package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +0 -523
  147. package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +0 -35
  148. package/template/core/src/openpress/workbench/project/index.ts +0 -2
  149. package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +0 -11
  150. package/template/core/src/openpress/workbench/project/projectSourceModel.ts +0 -24
  151. package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +0 -167
  152. package/template/core/src/openpress/workbench/shell/index.ts +0 -1
  153. package/template/core/src/openpress/workbench/workbenchFormatters.ts +0 -120
  154. package/template/core/src/openpress/workbench/workbenchTypes.ts +0 -35
  155. package/template/core/src/styles/openpress/app-shell.css +0 -251
  156. package/template/core/src/styles/openpress/media-workspace.css +0 -230
  157. package/template/core/src/styles/openpress/print-route.css +0 -184
  158. package/template/core/src/styles/openpress/project-preview-panel.css +0 -924
  159. package/template/core/src/styles/openpress/public-viewer.css +0 -688
  160. package/template/core/src/styles/openpress/reader-runtime.css +0 -980
  161. package/template/core/src/styles/openpress/responsive.css +0 -245
  162. package/template/core/src/styles/openpress/workbench-panels.css +0 -594
  163. package/template/core/src/styles/openpress/workbench.css +0 -1255
  164. package/template/core/src/styles/openpress.css +0 -14
  165. package/template/core/src/vite-env.d.ts +0 -9
  166. package/template/core/tsconfig.json +0 -40
  167. package/template/core/vite.config.ts +0 -584
  168. package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +0 -35
  169. package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +0 -50
  170. package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +0 -47
  171. package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +0 -26
  172. package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +0 -32
  173. package/template/packs/academic-paper/document/components/ChapterOpenerVisual/index.tsx +0 -76
  174. package/template/packs/academic-paper/document/components/Page.tsx +0 -60
  175. package/template/packs/academic-paper/document/components/TokenSwatchGrid/index.tsx +0 -46
  176. package/template/packs/academic-paper/document/components/TokenSwatchGrid/style.css +0 -63
  177. package/template/packs/academic-paper/document/components/TypeSpecimen/index.tsx +0 -38
  178. package/template/packs/academic-paper/document/components/TypeSpecimen/style.css +0 -111
  179. package/template/packs/academic-paper/document/design.md +0 -279
  180. package/template/packs/academic-paper/document/index.tsx +0 -123
  181. package/template/packs/academic-paper/document/media/README.md +0 -13
  182. package/template/packs/academic-paper/document/media/figure-placeholder.svg +0 -9
  183. package/template/packs/academic-paper/document/openpress.config.mjs +0 -26
  184. package/template/packs/academic-paper/document/theme/README.md +0 -11
  185. package/template/packs/academic-paper/document/theme/base/page-contract.css +0 -522
  186. package/template/packs/academic-paper/document/theme/base/print.css +0 -93
  187. package/template/packs/academic-paper/document/theme/base/typography.css +0 -333
  188. package/template/packs/academic-paper/document/theme/fonts.css +0 -3
  189. package/template/packs/academic-paper/document/theme/page-surfaces/back-cover.css +0 -43
  190. package/template/packs/academic-paper/document/theme/page-surfaces/chapter-opener.css +0 -205
  191. package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +0 -294
  192. package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +0 -149
  193. package/template/packs/academic-paper/document/theme/patterns/_chart-frame.css +0 -49
  194. package/template/packs/academic-paper/document/theme/patterns/figure-grid.css +0 -68
  195. package/template/packs/academic-paper/document/theme/patterns/table-utilities.css +0 -66
  196. package/template/packs/academic-paper/document/theme/shell/reader-controls.css +0 -761
  197. package/template/packs/academic-paper/document/theme/tokens.css +0 -80
  198. package/template/packs/academic-paper/openpress.config.mjs +0 -5
  199. package/template/packs/claude-document/document/chapters/01-document-shape/content/01-document-shape.mdx +0 -51
  200. package/template/packs/claude-document/document/chapters/02-review-loop/content/01-review-loop.mdx +0 -31
  201. package/template/packs/claude-document/document/components/ChapterOpenerVisual.tsx +0 -96
  202. package/template/packs/claude-document/document/components/Page.tsx +0 -37
  203. package/template/packs/claude-document/document/design.md +0 -142
  204. package/template/packs/claude-document/document/index.tsx +0 -94
  205. package/template/packs/claude-document/document/media/README.md +0 -13
  206. package/template/packs/claude-document/document/openpress.config.mjs +0 -26
  207. package/template/packs/claude-document/document/theme/README.md +0 -15
  208. package/template/packs/claude-document/document/theme/base/page-contract.css +0 -525
  209. package/template/packs/claude-document/document/theme/base/print.css +0 -93
  210. package/template/packs/claude-document/document/theme/base/typography.css +0 -612
  211. package/template/packs/claude-document/document/theme/fonts.css +0 -4
  212. package/template/packs/claude-document/document/theme/page-surfaces/back-cover.css +0 -72
  213. package/template/packs/claude-document/document/theme/page-surfaces/chapter-opener.css +0 -236
  214. package/template/packs/claude-document/document/theme/page-surfaces/cover.css +0 -309
  215. package/template/packs/claude-document/document/theme/page-surfaces/toc.css +0 -225
  216. package/template/packs/claude-document/document/theme/patterns/_chart-frame.css +0 -53
  217. package/template/packs/claude-document/document/theme/patterns/figure-grid.css +0 -68
  218. package/template/packs/claude-document/document/theme/patterns/table-utilities.css +0 -66
  219. package/template/packs/claude-document/document/theme/shell/reader-controls.css +0 -789
  220. package/template/packs/claude-document/document/theme/tokens.css +0 -89
  221. package/template/packs/claude-document/openpress.config.mjs +0 -5
  222. package/template/packs/editorial-monograph/document/chapters/01-product-and-use-cases/content/01-product-and-use-cases.mdx +0 -31
  223. package/template/packs/editorial-monograph/document/chapters/02-workflow/content/01-workflow.mdx +0 -89
  224. package/template/packs/editorial-monograph/document/chapters/03-agent-skills-contributors/content/01-agent-skills-contributors.mdx +0 -51
  225. package/template/packs/editorial-monograph/document/chapters/04-validation-deploy/content/01-validation-deploy.mdx +0 -39
  226. package/template/packs/editorial-monograph/document/components/ChapterOpenerVisual/index.tsx +0 -76
  227. package/template/packs/editorial-monograph/document/components/Page.tsx +0 -37
  228. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/index.tsx +0 -46
  229. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/style.css +0 -63
  230. package/template/packs/editorial-monograph/document/components/TypeSpecimen/index.tsx +0 -38
  231. package/template/packs/editorial-monograph/document/components/TypeSpecimen/style.css +0 -111
  232. package/template/packs/editorial-monograph/document/design.md +0 -279
  233. package/template/packs/editorial-monograph/document/index.tsx +0 -97
  234. package/template/packs/editorial-monograph/document/media/README.md +0 -13
  235. package/template/packs/editorial-monograph/document/openpress.config.mjs +0 -26
  236. package/template/packs/editorial-monograph/document/theme/README.md +0 -11
  237. package/template/packs/editorial-monograph/document/theme/base/page-contract.css +0 -505
  238. package/template/packs/editorial-monograph/document/theme/base/print.css +0 -93
  239. package/template/packs/editorial-monograph/document/theme/base/typography.css +0 -336
  240. package/template/packs/editorial-monograph/document/theme/fonts.css +0 -3
  241. package/template/packs/editorial-monograph/document/theme/page-surfaces/back-cover.css +0 -43
  242. package/template/packs/editorial-monograph/document/theme/page-surfaces/chapter-opener.css +0 -205
  243. package/template/packs/editorial-monograph/document/theme/page-surfaces/cover.css +0 -147
  244. package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +0 -149
  245. package/template/packs/editorial-monograph/document/theme/patterns/_chart-frame.css +0 -49
  246. package/template/packs/editorial-monograph/document/theme/patterns/figure-grid.css +0 -68
  247. package/template/packs/editorial-monograph/document/theme/patterns/table-utilities.css +0 -66
  248. package/template/packs/editorial-monograph/document/theme/shell/reader-controls.css +0 -761
  249. package/template/packs/editorial-monograph/document/theme/tokens.css +0 -80
  250. 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
- }
@@ -1,4 +0,0 @@
1
- import type { IndexedHtmlPage } from "../document-model";
2
- import type { HtmlPageBlock } from "../document-model";
3
-
4
- export type DisplayPage = IndexedHtmlPage & Pick<HtmlPageBlock, "source" | "frameKey">;