@open-press/cli 0.6.0 → 0.7.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 (86) hide show
  1. package/package.json +1 -1
  2. package/template/core/CHANGELOG.md +67 -1
  3. package/template/core/README.md +9 -5
  4. package/template/core/engine/cli.mjs +2 -5
  5. package/template/core/engine/commands/_shared.mjs +4 -4
  6. package/template/core/engine/commands/deploy.mjs +1 -1
  7. package/template/core/engine/commands/inspect.mjs +3 -3
  8. package/template/core/engine/commands/replace.mjs +1 -1
  9. package/template/core/engine/commands/search.mjs +1 -1
  10. package/template/core/engine/commands/validate.mjs +2 -2
  11. package/template/core/engine/document-export.mjs +1 -1
  12. package/template/core/engine/{chrome-pdf.mjs → output/chrome-pdf.mjs} +1 -2
  13. package/template/core/engine/{deploy-sync.mjs → output/deploy-sync.mjs} +2 -2
  14. package/template/core/engine/{fonts.mjs → output/fonts.mjs} +1 -1
  15. package/template/core/engine/{public-assets.mjs → output/public-assets.mjs} +2 -2
  16. package/template/core/engine/{static-server.mjs → output/static-server.mjs} +2 -2
  17. package/template/core/engine/react/caption-numbering.mjs +73 -0
  18. package/template/core/engine/react/comment-marker.mjs +54 -10
  19. package/template/core/engine/react/document-entry.mjs +124 -64
  20. package/template/core/engine/react/document-export.mjs +252 -311
  21. package/template/core/engine/react/mdx-compile.mjs +123 -3
  22. package/template/core/engine/react/measurement-css.mjs +3 -3
  23. package/template/core/engine/react/pagination/allocator.mjs +122 -0
  24. package/template/core/engine/react/pagination/regions.mjs +81 -0
  25. package/template/core/engine/react/pagination.mjs +9 -121
  26. package/template/core/engine/react/pipeline/allocate.mjs +248 -0
  27. package/template/core/engine/react/pipeline/final-render.mjs +94 -0
  28. package/template/core/engine/react/pipeline/frame-measurement.mjs +271 -0
  29. package/template/core/engine/react/pipeline/press-tree.mjs +135 -0
  30. package/template/core/engine/react/project-asset-endpoint.mjs +2 -2
  31. package/template/core/engine/react/{chapter-css.mjs → section-css.mjs} +12 -9
  32. package/template/core/engine/react/sources/heading-numbering.mjs +132 -0
  33. package/template/core/engine/react/sources/mdx-resolver.mjs +441 -0
  34. package/template/core/engine/react/{workspace-discovery.mjs → style-discovery.mjs} +29 -40
  35. package/template/core/engine/{config.mjs → runtime/config.mjs} +15 -0
  36. package/template/core/engine/{file-utils.mjs → runtime/file-utils.mjs} +1 -1
  37. package/template/core/engine/{inspection.mjs → runtime/inspection.mjs} +3 -4
  38. package/template/core/engine/{source-text-tools.mjs → runtime/source-text-tools.mjs} +24 -7
  39. package/template/core/engine/runtime/source-workspace.mjs +186 -0
  40. package/template/core/engine/{validation.mjs → runtime/validation.mjs} +19 -17
  41. package/template/core/package.json +5 -2
  42. package/template/core/src/openpress/anchorMap.ts +27 -0
  43. package/template/core/src/openpress/core/Frame.tsx +80 -0
  44. package/template/core/src/openpress/core/FrameContext.tsx +19 -0
  45. package/template/core/src/openpress/core/MdxArea.tsx +35 -0
  46. package/template/core/src/openpress/core/Press.tsx +34 -0
  47. package/template/core/src/openpress/core/index.tsx +34 -15
  48. package/template/core/src/openpress/core/primitives.tsx +23 -0
  49. package/template/core/src/openpress/core/types.ts +131 -19
  50. package/template/core/src/openpress/core/useSource.ts +28 -0
  51. package/template/core/src/openpress/manuscript/index.tsx +196 -0
  52. package/template/core/src/openpress/mdx/index.ts +88 -0
  53. package/template/core/src/openpress/numbering/index.ts +294 -0
  54. package/template/core/src/openpress/publicPage.tsx +4 -186
  55. package/template/core/src/openpress/reactDocumentMetadata.ts +2 -16
  56. package/template/core/src/openpress/types.ts +0 -16
  57. package/template/core/src/openpress/workbench.tsx +2 -36
  58. package/template/core/src/styles/openpress/responsive.css +0 -14
  59. package/template/core/tsconfig.json +4 -1
  60. package/template/core/vite.config.ts +10 -3
  61. package/template/packs/academic-paper/document/components/Page.tsx +24 -14
  62. package/template/packs/academic-paper/document/design.md +2 -2
  63. package/template/packs/academic-paper/document/index.tsx +98 -74
  64. package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +19 -9
  65. package/template/packs/claude-document/document/components/Page.tsx +24 -14
  66. package/template/packs/claude-document/document/design.md +2 -2
  67. package/template/packs/claude-document/document/index.tsx +67 -62
  68. package/template/packs/claude-document/document/theme/page-surfaces/toc.css +19 -7
  69. package/template/packs/editorial-monograph/document/components/Page.tsx +24 -14
  70. package/template/packs/editorial-monograph/document/design.md +2 -2
  71. package/template/packs/editorial-monograph/document/index.tsx +71 -47
  72. package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +19 -9
  73. package/template/core/engine/commands/migrate-to-react.mjs +0 -27
  74. package/template/core/engine/page-renderer.mjs +0 -217
  75. package/template/core/engine/react/migrate-to-react.mjs +0 -355
  76. package/template/core/engine/source-workspace.mjs +0 -76
  77. package/template/core/src/openpress/core/basePages.tsx +0 -87
  78. package/template/core/src/openpress/pagination.ts +0 -845
  79. package/template/packs/claude-document/document/chapters/01-document-shape/chapter.tsx +0 -30
  80. package/template/packs/claude-document/document/chapters/02-review-loop/chapter.tsx +0 -30
  81. /package/template/core/engine/{chrome-pdf.d.mts → output/chrome-pdf.d.mts} +0 -0
  82. /package/template/core/engine/{katex-assets.mjs → output/katex-assets.mjs} +0 -0
  83. /package/template/core/engine/{page-block.mjs → output/page-block.mjs} +0 -0
  84. /package/template/core/engine/{pdf-media.mjs → output/pdf-media.mjs} +0 -0
  85. /package/template/core/engine/{config.d.mts → runtime/config.d.mts} +0 -0
  86. /package/template/core/engine/{issue-report.mjs → runtime/issue-report.mjs} +0 -0
@@ -33,18 +33,14 @@ import {
33
33
  ProjectEntryPanel,
34
34
  type ProjectMentionItem,
35
35
  } from "./projectWorkspace";
36
- import { paginateSourcePages, type PaginatedPage } from "./pagination";
36
+ import { createAnchorPageMap, resolveAnchorPageIndex } from "./anchorMap";
37
37
  import { scheduleBrowserFrame } from "./frameScheduler";
38
38
  import {
39
- createAnchorPageMap,
40
- numberSourceHeadings,
41
39
  PUBLIC_DRAWER_BREAKPOINT,
42
40
  PublicPage,
43
- resolveAnchorPageIndex,
44
41
  useViewMode,
45
42
  } from "./publicPage";
46
43
  import { getProjectIdentity } from "./projectIdentity";
47
- import { hasBuildTimePagination } from "./reactDocumentMetadata";
48
44
  import { buildPublicPreviewHref, isLocalWorkspaceHost } from "./runtimeMode";
49
45
  import { useReaderRuntime } from "./readerRuntime";
50
46
  import type { DeploymentInfo, ReaderDocument, HtmlPageBlock, SourceBlock } from "./types";
@@ -123,14 +119,9 @@ export function HtmlWorkbench({
123
119
  deploymentInfo: DeploymentInfo;
124
120
  }) {
125
121
  const sourceContainerRef = useRef<HTMLDivElement | null>(null);
126
- const numberedPages = useMemo(() => numberSourceHeadings(pages), [pages]);
122
+ const displayPages = pages;
127
123
  const viewModeState = useViewMode();
128
124
  const { viewMode } = viewModeState;
129
- const buildTimePaginated = hasBuildTimePagination(document);
130
- const [paginatedPages, setPaginatedPages] = useState<PaginatedPage[] | null>(null);
131
- const displayPages: DisplayPage[] = viewMode === "paged" && !buildTimePaginated
132
- ? (paginatedPages ?? numberedPages)
133
- : numberedPages;
134
125
  const mediaAssets = useMemo(() => collectMediaAssetIndex(displayPages), [displayPages]);
135
126
  const anchorPageMap = useMemo(() => createAnchorPageMap(displayPages), [displayPages]);
136
127
  const projectComponentUsages = useMemo(() => createProjectComponentUsages(displayPages), [displayPages]);
@@ -163,7 +154,6 @@ export function HtmlWorkbench({
163
154
  const pdfButtonText = workbenchPdfButtonText(localDeployEnabled, pdfActionStatus, staticPdfHref);
164
155
  const pdfStatusMessage = workbenchPdfStatusMessage(localDeployEnabled, pdfActionStatus);
165
156
  const pdfButtonDisabled = localDeployEnabled ? pdfActionStatus === "generating" || pdfActionStatus === "opening" : !staticPdfHref;
166
- const activePaginatedReady = viewMode === "reading" || buildTimePaginated || Boolean(paginatedPages);
167
157
  const inspectorSelectionLabel = formatInspectorSelection(inspector.selectedBlock);
168
158
  const activeInlineSavedComment = getInlineSavedCommentForTarget(inlineSavedComment, inspector.selectedTarget);
169
159
  const inspectorCommentDisabled = !inspector.selectedBlock || !inspectorCommentText.trim() || inspectorCommentStatus === "submitting";
@@ -392,10 +382,6 @@ export function HtmlWorkbench({
392
382
  scheduleBrowserFrame(() => reader.setPage(reader.currentPageIndex, { behavior: "auto" }));
393
383
  };
394
384
 
395
- useLayoutEffect(() => {
396
- setPaginatedPages(null);
397
- }, [numberedPages]);
398
-
399
385
  useEffect(() => {
400
386
  setInspectorCommentStatus("idle");
401
387
  setInspectorCommentError("");
@@ -407,24 +393,6 @@ export function HtmlWorkbench({
407
393
  void refreshPendingComments();
408
394
  }, [devMode, refreshPendingComments, workspaceView]);
409
395
 
410
- useLayoutEffect(() => {
411
- if (buildTimePaginated) return undefined;
412
- if (viewMode !== "paged" || paginatedPages) return undefined;
413
- const sourceContainer = sourceContainerRef.current;
414
- if (!sourceContainer) return undefined;
415
-
416
- let cancelled = false;
417
- const cancelFrame = scheduleBrowserFrame(() => {
418
- const nextPages = paginateSourcePages(sourceContainer, numberedPages);
419
- if (!cancelled) setPaginatedPages(nextPages);
420
- });
421
-
422
- return () => {
423
- cancelled = true;
424
- cancelFrame();
425
- };
426
- }, [buildTimePaginated, numberedPages, paginatedPages, viewMode]);
427
-
428
396
  const actionSection = (
429
397
  <section className="openpress-public-action-section" aria-label="輸出">
430
398
  <span className="openpress-public-action-heading">輸出</span>
@@ -512,7 +480,6 @@ export function HtmlWorkbench({
512
480
  className={`reader-app openpress-reader-app openpress-public-viewer openpress-dev-public-viewer is-ready${reader.rightPanelOpen ? "" : " is-closed-right"}`}
513
481
  data-openpress-react-runtime="true"
514
482
  data-openpress-view-mode={viewMode}
515
- data-openpress-pagination={activePaginatedReady ? "ready" : "pending"}
516
483
  data-openpress-inspector-mode={inspector.inspectorMode ? "on" : "off"}
517
484
  data-active-workspace={workspaceView}
518
485
  >
@@ -533,7 +500,6 @@ export function HtmlWorkbench({
533
500
  pages={displayPages}
534
501
  currentPageIndex={reader.currentPageIndex}
535
502
  devMode={devMode}
536
- paginatedReady={activePaginatedReady}
537
503
  sourceContainerRef={sourceContainerRef}
538
504
  registerPage={reader.registerPage}
539
505
  exposeSourceData={devMode}
@@ -269,12 +269,6 @@
269
269
  box-shadow: none;
270
270
  }
271
271
 
272
- .openpress-public-viewer.openpress-reader-app[data-openpress-pagination="pending"] .openpress-public-navigation {
273
- transform: translateX(-110%);
274
- pointer-events: none;
275
- box-shadow: none;
276
- }
277
-
278
272
  .openpress-public-viewer.openpress-reader-app .openpress-public-scrim {
279
273
  position: fixed;
280
274
  inset: 0;
@@ -309,14 +303,6 @@
309
303
  display: block;
310
304
  }
311
305
 
312
- .openpress-public-viewer.openpress-reader-app[data-openpress-pagination="pending"] .openpress-public-scrim {
313
- display: none;
314
- }
315
-
316
- .openpress-public-viewer.openpress-reader-app[data-openpress-pagination="pending"] .openpress-public-fab {
317
- display: flex;
318
- }
319
-
320
306
  .openpress-public-viewer[data-openpress-view-mode="paged"] .reader-pages {
321
307
  --openpress-public-page-width: min(
322
308
  var(--openpress-page-width),
@@ -17,7 +17,10 @@
17
17
  "noEmit": true,
18
18
  "jsx": "react-jsx",
19
19
  "paths": {
20
- "@openpress/core": ["./src/openpress/core/index.tsx"],
20
+ "@open-press/core": ["./src/openpress/core/index.tsx"],
21
+ "@open-press/core/mdx": ["./src/openpress/mdx/index.ts"],
22
+ "@open-press/core/manuscript": ["./src/openpress/manuscript/index.tsx"],
23
+ "@open-press/core/numbering": ["./src/openpress/numbering/index.ts"],
21
24
  "@/components": ["./document/components/index.ts", "./document/components/index.tsx"],
22
25
  "@/components/*": ["./document/components/*"],
23
26
  "@/*": ["./src/*"]
@@ -5,7 +5,7 @@ import path from "node:path";
5
5
  import type { IncomingMessage, ServerResponse } from "node:http";
6
6
  import { defineConfig } from "vite";
7
7
  import react from "@vitejs/plugin-react";
8
- import { loadConfig, publicPdfHref } from "./engine/config.mjs";
8
+ import { loadConfig, publicPdfHref } from "./engine/runtime/config.mjs";
9
9
  import { handleCommentRequest } from "./engine/react/comment-endpoint.mjs";
10
10
  import { handleProjectAssetRequest } from "./engine/react/project-asset-endpoint.mjs";
11
11
 
@@ -15,8 +15,11 @@ const workspaceRoot = process.env.OPENPRESS_WORKSPACE_ROOT
15
15
  : frameworkRoot;
16
16
  const sourceRoot = path.join(frameworkRoot, "src");
17
17
  const openpressCliPath = path.join(frameworkRoot, "engine", "cli.mjs");
18
- const staticServerPath = path.join(frameworkRoot, "engine", "static-server.mjs");
18
+ const staticServerPath = path.join(frameworkRoot, "engine", "output", "static-server.mjs");
19
19
  const openpressCoreEntry = path.join(frameworkRoot, "src", "openpress", "core", "index.tsx");
20
+ const openpressMdxEntry = path.join(frameworkRoot, "src", "openpress", "mdx", "index.ts");
21
+ const openpressManuscriptEntry = path.join(frameworkRoot, "src", "openpress", "manuscript", "index.tsx");
22
+ const openpressNumberingEntry = path.join(frameworkRoot, "src", "openpress", "numbering", "index.ts");
20
23
  const openpressConfig = await loadConfig(workspaceRoot);
21
24
  const outputDir = openpressConfig.paths.outputDir;
22
25
  const reactDocumentRoot = openpressConfig.paths.documentRoot;
@@ -55,7 +58,11 @@ export default defineConfig({
55
58
  resolve: {
56
59
  dedupe: ["react", "react-dom", "@mdx-js/react"],
57
60
  alias: {
58
- "@openpress/core": openpressCoreEntry,
61
+ // Subpaths must come before the base path so resolution matches longest first.
62
+ "@open-press/core/mdx": openpressMdxEntry,
63
+ "@open-press/core/manuscript": openpressManuscriptEntry,
64
+ "@open-press/core/numbering": openpressNumberingEntry,
65
+ "@open-press/core": openpressCoreEntry,
59
66
  "@/components": reactDocumentComponentsRoot,
60
67
  "@": sourceRoot,
61
68
  ...workspaceAliases,
@@ -1,27 +1,37 @@
1
- import type { PageProps } from "@openpress/core";
1
+ import { Frame, MdxArea } from "@open-press/core";
2
+ import type { SectionsPageProps } from "@open-press/core/manuscript";
2
3
 
3
4
  export default function Page({
5
+ frameKey,
6
+ chainId,
4
7
  pageIndex,
5
8
  totalPages,
6
- chapterSlug,
7
- chapterTone,
8
- children,
9
- }: PageProps) {
9
+ sectionSlug,
10
+ sectionTitle,
11
+ sectionTone,
12
+ }: SectionsPageProps) {
10
13
  return (
11
- <section
12
- className="reader-page reader-page--content"
13
- data-page-kind="content"
14
- data-page-footer="true"
14
+ <Frame
15
+ frameKey={frameKey}
16
+ role="manuscript.content"
17
+ className="reader-page--content"
15
18
  data-page-index={pageIndex}
16
19
  data-total-pages={totalPages}
17
- data-chapter-slug={chapterSlug}
18
- data-chapter-tone={chapterTone}
20
+ data-section-id={sectionSlug}
21
+ data-chapter-tone={sectionTone}
19
22
  >
20
23
  <div className="page-frame">
21
24
  <header className="page-header" aria-hidden="true" />
22
- <main className="page-body">{children}</main>
23
- <footer className="page-footer" aria-hidden="true" />
25
+ <main className="page-body">
26
+ <MdxArea chainId={chainId} />
27
+ </main>
28
+ <footer className="page-footer" aria-hidden="true">
29
+ <span className="footer-left">{sectionTitle}</span>
30
+ <span className="footer-right">
31
+ {totalPages > 1 ? `${pageIndex + 1}/${totalPages}` : pageIndex + 1}
32
+ </span>
33
+ </footer>
24
34
  </div>
25
- </section>
35
+ </Frame>
26
36
  );
27
37
  }
@@ -112,7 +112,7 @@ Block-level 強調(callout / warning / note)目前不在 design system 範圍;
112
112
 
113
113
  ### Chapter & Section Numbering
114
114
 
115
- Engine 不在 h2/h3 內容前注入任何編號或前綴;pagination runtime 計數後寫入 `data-chapter` / `data-section` attribute(值是 `01`、`1.1` 這種兩位數阿拉伯格式)。實際顯示樣式由 theme 的 `::before content` 決定,這讓不同文件能挑不同 numbering vocabulary 而不動 engine
115
+ Engine 不在 h2/h3 內容前注入任何編號或前綴;export pipeline 會在 build-time 寫入 `data-chapter` / `data-section` attribute(值是 `01`、`1.1` 這種兩位數阿拉伯格式)。實際顯示樣式由 theme 的 `::before content` 決定,這讓不同文件能挑不同 numbering vocabulary 而不動 reader runtime
116
116
 
117
117
  預設(editorial-monograph 起手樣式):
118
118
 
@@ -258,7 +258,7 @@ body 內容區的 page padding(content margin)由 `.reader-page` 內的 `--page-
258
258
  | 資料歸資料 | chart data 透過 React props 或鄰近的 typed data module 傳入,不寫死在 CSS |
259
259
  | 樣式歸樣式 | chart frame 等通用規則放在 `document/theme/patterns/`;變體 CSS 放在元件自己的 `style.css` |
260
260
  | 元件歸元件 | 一個視覺 = 一個 `document/components/ComponentName/` 包;MDX 只保留 `<ComponentName />` 呼叫 |
261
- | Caption 必填 | caption 只寫說明文字,不手寫圖號或表號;runtime 會替 content pages 的 figure/table 自動編號 |
261
+ | Caption 必填 | caption 只寫說明文字,不手寫圖號或表號;export pipeline 會替 content pages 的 figure/table 自動編號 |
262
262
  | PDF-safe | 圖表需 `break-inside: avoid`,高度不可壓到 footer |
263
263
 
264
264
  如果使用者要求新的視覺元件,Agent 應先更新本檔說明,再改對應的 `document/components/<name>/style.css` 或 `document/theme/` CSS,最後用 reader 預覽檢查 PDF 輸出。
@@ -1,5 +1,8 @@
1
- import type { Manifest } from "@openpress/core";
2
- import { BaseBackCoverPage, BaseCoverPage, BaseTocPage } from "@openpress/core";
1
+ import { Frame, Press } from "@open-press/core";
2
+ import type { Manifest } from "@open-press/core";
3
+ import { mdxSource } from "@open-press/core/mdx";
4
+ import { Sections, Toc } from "@open-press/core/manuscript";
5
+ import Page from "./components/Page";
3
6
 
4
7
  export const config: Manifest = {
5
8
  title: "Paper Title",
@@ -24,84 +27,105 @@ export const config: Manifest = {
24
27
  },
25
28
  };
26
29
 
30
+ export const sources = {
31
+ story: mdxSource({ preset: "section-folders", root: "chapters" }),
32
+ };
33
+
27
34
  /**
28
35
  * The cover renders the academic title block: paper title, author grid,
29
36
  * abstract band, and index terms. Replace the placeholders with your own.
30
37
  */
31
- export const cover = (
32
- <BaseCoverPage data-page-title="Title page" aria-labelledby="paper-title">
33
- <div className="paper-cover">
34
- <h1 id="paper-title" className="paper-title">
35
- Conference Paper Title
36
- </h1>
37
- <p className="paper-subtitle">
38
- Sub-title (optional). Not captured by indexing services like IEEE Xplore.
39
- </p>
38
+ function Cover() {
39
+ return (
40
+ <Frame
41
+ frameKey="cover"
42
+ role="manuscript.cover"
43
+ chrome={false}
44
+ className="reader-page--cover"
45
+ data-page-title="Title page"
46
+ aria-labelledby="paper-title"
47
+ >
48
+ <div className="paper-cover">
49
+ <h1 id="paper-title" className="paper-title">
50
+ Conference Paper Title
51
+ </h1>
52
+ <p className="paper-subtitle">
53
+ Sub-title (optional). Not captured by indexing services like IEEE Xplore.
54
+ </p>
40
55
 
41
- <ol className="paper-authors" aria-label="Authors">
42
- <li className="paper-author">
43
- <p className="paper-author-name">First Author Surname</p>
44
- <p className="paper-author-affiliation">dept. of organization</p>
45
- <p className="paper-author-affiliation">name of organization</p>
46
- <p className="paper-author-location">City, Country</p>
47
- <p className="paper-author-contact">email or ORCID</p>
48
- </li>
49
- <li className="paper-author">
50
- <p className="paper-author-name">Second Author Surname</p>
51
- <p className="paper-author-affiliation">dept. of organization</p>
52
- <p className="paper-author-affiliation">name of organization</p>
53
- <p className="paper-author-location">City, Country</p>
54
- <p className="paper-author-contact">email or ORCID</p>
55
- </li>
56
- <li className="paper-author">
57
- <p className="paper-author-name">Third Author Surname</p>
58
- <p className="paper-author-affiliation">dept. of organization</p>
59
- <p className="paper-author-affiliation">name of organization</p>
60
- <p className="paper-author-location">City, Country</p>
61
- <p className="paper-author-contact">email or ORCID</p>
62
- </li>
63
- </ol>
56
+ <ol className="paper-authors" aria-label="Authors">
57
+ <li className="paper-author">
58
+ <p className="paper-author-name">First Author Surname</p>
59
+ <p className="paper-author-affiliation">dept. of organization</p>
60
+ <p className="paper-author-affiliation">name of organization</p>
61
+ <p className="paper-author-location">City, Country</p>
62
+ <p className="paper-author-contact">email or ORCID</p>
63
+ </li>
64
+ <li className="paper-author">
65
+ <p className="paper-author-name">Second Author Surname</p>
66
+ <p className="paper-author-affiliation">dept. of organization</p>
67
+ <p className="paper-author-affiliation">name of organization</p>
68
+ <p className="paper-author-location">City, Country</p>
69
+ <p className="paper-author-contact">email or ORCID</p>
70
+ </li>
71
+ <li className="paper-author">
72
+ <p className="paper-author-name">Third Author Surname</p>
73
+ <p className="paper-author-affiliation">dept. of organization</p>
74
+ <p className="paper-author-affiliation">name of organization</p>
75
+ <p className="paper-author-location">City, Country</p>
76
+ <p className="paper-author-contact">email or ORCID</p>
77
+ </li>
78
+ </ol>
64
79
 
65
- <section className="paper-abstract" aria-label="Abstract">
66
- <p>
67
- <span className="paper-abstract-label">Abstract</span>—This document
68
- is a model and starting point for an academic paper drafted in
69
- open-press. Replace this abstract with your own — keep it under
70
- 250 words. Do not use abbreviations, symbols, footnotes, or math
71
- in the abstract.
72
- </p>
73
- </section>
80
+ <section className="paper-abstract" aria-label="Abstract">
81
+ <p>
82
+ <span className="paper-abstract-label">Abstract</span>—This document
83
+ is a model and starting point for an academic paper drafted in
84
+ open-press. Replace this abstract with your own — keep it under
85
+ 250 words. Do not use abbreviations, symbols, footnotes, or math
86
+ in the abstract.
87
+ </p>
88
+ </section>
74
89
 
75
- <section className="paper-index-terms" aria-label="Index terms">
76
- <p>
77
- <span className="paper-abstract-label">Index Terms</span>—keyword
78
- one, keyword two, keyword three, keyword four
79
- </p>
80
- </section>
81
- </div>
82
- </BaseCoverPage>
83
- );
90
+ <section className="paper-index-terms" aria-label="Index terms">
91
+ <p>
92
+ <span className="paper-abstract-label">Index Terms</span>—keyword
93
+ one, keyword two, keyword three, keyword four
94
+ </p>
95
+ </section>
96
+ </div>
97
+ </Frame>
98
+ );
99
+ }
84
100
 
85
- export const toc = (
86
- <BaseTocPage data-page-title="Contents" id="toc">
87
- <div className="page-frame">
88
- <header className="page-header" aria-hidden="true"></header>
89
- <main className="page-body">
90
- <h2 id="toc-title" className="toc-heading">Contents</h2>
91
- </main>
92
- </div>
93
- </BaseTocPage>
94
- );
101
+ function BackCover() {
102
+ return (
103
+ <Frame
104
+ frameKey="back-cover"
105
+ role="manuscript.back-cover"
106
+ chrome={false}
107
+ className="reader-page--back-cover"
108
+ data-page-title="Back cover"
109
+ >
110
+ <div className="paper-back-cover">
111
+ <p className="paper-back-kicker">open-press · academic-paper</p>
112
+ <p className="paper-back-statement">
113
+ Draft built with open-press. When the paper is ready for submission,
114
+ port the prose into the publisher's LaTeX class (IEEEtran, acmart,
115
+ etc.). open-press is the iteration loop, not the camera-ready output.
116
+ </p>
117
+ </div>
118
+ </Frame>
119
+ );
120
+ }
95
121
 
96
- export const backCover = (
97
- <BaseBackCoverPage data-page-title="Back cover">
98
- <div className="paper-back-cover">
99
- <p className="paper-back-kicker">open-press · academic-paper</p>
100
- <p className="paper-back-statement">
101
- Draft built with open-press. When the paper is ready for submission,
102
- port the prose into the publisher's LaTeX class (IEEEtran, acmart,
103
- etc.). open-press is the iteration loop, not the camera-ready output.
104
- </p>
105
- </div>
106
- </BaseBackCoverPage>
107
- );
122
+ export default function AcademicPaperPress() {
123
+ return (
124
+ <Press>
125
+ <Cover />
126
+ <Toc source="story" heading={<h2 id="toc-title" className="toc-heading">Contents</h2>} />
127
+ <Sections source="story" page={Page} />
128
+ <BackCover />
129
+ </Press>
130
+ );
131
+ }
@@ -1,12 +1,26 @@
1
1
  /* page-surfaces / toc
2
- * Table-of-contents page surface routed by `kind: toc` and built by the
3
- * engine's renderToc(). Layout uses a 3-column grid (index / title / page).
2
+ * Table-of-contents page surface rendered by the manuscript <Toc> helper.
3
+ * Entries flow through the generated toc:<sourceId> chain and TocArea.
4
4
  */
5
5
 
6
6
  .reader-page--toc {
7
7
  padding: 0;
8
8
  }
9
9
 
10
+ .reader-page--toc .page-frame {
11
+ grid-template-rows: auto minmax(0, 1fr);
12
+ }
13
+
14
+ .reader-page--toc .toc-header {
15
+ display: block;
16
+ overflow: visible;
17
+ opacity: 1;
18
+ }
19
+
20
+ .reader-page--toc .openpress-toc-area {
21
+ height: 100%;
22
+ }
23
+
10
24
  .reader-page--toc h2 {
11
25
  margin-top: 0;
12
26
  font-family: var(--openpress-font-serif);
@@ -18,13 +32,7 @@
18
32
  }
19
33
 
20
34
  .reader-page--toc h2.toc-heading--continuation {
21
- margin-bottom: 0;
22
- color: var(--openpress-color-muted);
23
- font-family: var(--openpress-font-mono);
24
- font-size: calc(8.5pt - var(--openpress-type-step-down));
25
- font-weight: 400;
26
- letter-spacing: 0.08em;
27
- text-align: right;
35
+ display: none;
28
36
  }
29
37
 
30
38
  .toc-list {
@@ -51,6 +59,7 @@
51
59
  align-items: baseline;
52
60
  color: var(--openpress-color-ink);
53
61
  text-decoration: none;
62
+ font-family: var(--openpress-font-serif);
54
63
  padding: 1.6mm 0;
55
64
  font-weight: 400;
56
65
  line-height: 1.38;
@@ -117,6 +126,7 @@
117
126
  gap: 3mm;
118
127
  align-items: baseline;
119
128
  color: var(--openpress-color-ink);
129
+ font-family: var(--openpress-font-serif);
120
130
  }
121
131
 
122
132
  .toc-title::after {
@@ -1,27 +1,37 @@
1
- import type { PageProps } from "@openpress/core";
1
+ import { Frame, MdxArea } from "@open-press/core";
2
+ import type { SectionsPageProps } from "@open-press/core/manuscript";
2
3
 
3
4
  export default function Page({
5
+ frameKey,
6
+ chainId,
4
7
  pageIndex,
5
8
  totalPages,
6
- chapterSlug,
7
- chapterTone,
8
- children,
9
- }: PageProps) {
9
+ sectionSlug,
10
+ sectionTitle,
11
+ sectionTone,
12
+ }: SectionsPageProps) {
10
13
  return (
11
- <section
12
- className="reader-page reader-page--content"
13
- data-page-kind="content"
14
- data-page-footer="true"
14
+ <Frame
15
+ frameKey={frameKey}
16
+ role="manuscript.content"
17
+ className="reader-page--content"
15
18
  data-page-index={pageIndex}
16
19
  data-total-pages={totalPages}
17
- data-chapter-slug={chapterSlug}
18
- data-chapter-tone={chapterTone}
20
+ data-section-id={sectionSlug}
21
+ data-chapter-tone={sectionTone}
19
22
  >
20
23
  <div className="page-frame">
21
24
  <header className="page-header" aria-hidden="true" />
22
- <main className="page-body">{children}</main>
23
- <footer className="page-footer" aria-hidden="true" />
25
+ <main className="page-body">
26
+ <MdxArea chainId={chainId} />
27
+ </main>
28
+ <footer className="page-footer" aria-hidden="true">
29
+ <span className="footer-left">{sectionTitle}</span>
30
+ <span className="footer-right">
31
+ {totalPages > 1 ? `${pageIndex + 1}/${totalPages}` : pageIndex + 1}
32
+ </span>
33
+ </footer>
24
34
  </div>
25
- </section>
35
+ </Frame>
26
36
  );
27
37
  }
@@ -102,7 +102,7 @@ Claude Document 以 sans 作為主要閱讀字體,讓正文、表格與長段落
102
102
 
103
103
  ### Tables
104
104
 
105
- Markdown table 前使用 `<TableCaption>...</TableCaption>`,內容只寫說明文字;runtime 會輸出 `表 N:...`。不要手寫表號,也不要使用舊 `表:...` marker。
105
+ Markdown table 前使用 `<TableCaption>...</TableCaption>`,內容只寫說明文字;export pipeline 會輸出 `表 N:...`。不要手寫表號,也不要使用舊 `表:...` marker。
106
106
 
107
107
  <TableCaption>表格使用規則</TableCaption>
108
108
 
@@ -114,7 +114,7 @@ Markdown table 前使用 `<TableCaption>...</TableCaption>`,內容只寫說明
114
114
 
115
115
  ### Figures
116
116
 
117
- Figure component 應輸出標準 `<figure><figcaption>...</figcaption></figure>`。`figcaption` 只放 caption 文字,不放手寫圖號;runtime 會依 content pages 的 DOM 順序產生可見的 `圖 N:` / `表 N:` 前綴,並加上統一的 `data-openpress-caption="true"` 與 `data-openpress-caption-label`。
117
+ Figure component 應輸出標準 `<figure><figcaption>...</figcaption></figure>`。`figcaption` 只放 caption 文字,不放手寫圖號;export pipeline 會依 content pages 的 DOM 順序產生可見的 `圖 N:` / `表 N:` 前綴,並加上統一的 `data-openpress-caption="true"` 與 `data-openpress-caption-label`。
118
118
 
119
119
  ### Diagram Rules
120
120