@open-press/cli 1.0.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 +11 -12
- package/dist/cli.js +298 -79
- package/package.json +9 -7
- package/template/core/AGENTS.md +0 -130
- package/template/core/CHANGELOG.md +0 -218
- package/template/core/README.md +0 -43
- package/template/core/engine/cli.mjs +0 -96
- package/template/core/engine/commands/_shared.mjs +0 -199
- 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/image.mjs +0 -29
- 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/skills-sync.mjs +0 -71
- package/template/core/engine/commands/typecheck.mjs +0 -67
- 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/output/chrome-pdf.d.mts +0 -34
- package/template/core/engine/output/chrome-pdf.mjs +0 -450
- 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 -571
- 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 -331
- package/template/core/engine/react/document-export.mjs +0 -512
- package/template/core/engine/react/http-json.mjs +0 -24
- package/template/core/engine/react/mdx-compile.mjs +0 -629
- package/template/core/engine/react/measurement-css.mjs +0 -157
- package/template/core/engine/react/object-entities.mjs +0 -204
- package/template/core/engine/react/pagination/allocator.mjs +0 -167
- 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 -217
- package/template/core/engine/react/pipeline/final-render.mjs +0 -94
- package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -306
- package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
- package/template/core/engine/react/press-tree-inspection.mjs +0 -172
- 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 -160
- package/template/core/engine/runtime/config.d.mts +0 -48
- package/template/core/engine/runtime/config.mjs +0 -172
- package/template/core/engine/runtime/file-utils.mjs +0 -114
- 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/page-geometry.mjs +0 -131
- 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 -168
- package/template/core/engine/runtime/validation.mjs +0 -183
- package/template/core/index.html +0 -13
- package/template/core/openpress.config.mjs +0 -8
- package/template/core/package.json +0 -89
- package/template/core/src/main.tsx +0 -16
- package/template/core/src/openpress/app/OpenPressApp.tsx +0 -296
- package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -102
- package/template/core/src/openpress/app/WorkspaceGalleryPage.tsx +0 -219
- package/template/core/src/openpress/app/index.ts +0 -2
- package/template/core/src/openpress/core/Frame.tsx +0 -91
- package/template/core/src/openpress/core/FrameContext.tsx +0 -26
- package/template/core/src/openpress/core/MdxArea.tsx +0 -34
- package/template/core/src/openpress/core/Press.tsx +0 -55
- package/template/core/src/openpress/core/Workspace.tsx +0 -36
- package/template/core/src/openpress/core/cn.ts +0 -4
- package/template/core/src/openpress/core/index.tsx +0 -47
- package/template/core/src/openpress/core/primitives.tsx +0 -91
- package/template/core/src/openpress/core/types.ts +0 -236
- 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 -147
- package/template/core/src/openpress/document-model/index.ts +0 -7
- package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -55
- 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/document-model/workspaceManifestModel.ts +0 -57
- package/template/core/src/openpress/manuscript/index.tsx +0 -238
- package/template/core/src/openpress/mdx/index.ts +0 -96
- package/template/core/src/openpress/numbering/index.ts +0 -294
- package/template/core/src/openpress/reader/PageThumbnailsPanel.tsx +0 -168
- 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 -11
- 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 -506
- package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
- package/template/core/src/openpress/workbench/actions/ExportImageControl.tsx +0 -96
- 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 -6
- 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 -254
- 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 -80
- 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 -525
- 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 -989
- package/template/core/src/styles/openpress/responsive.css +0 -245
- package/template/core/src/styles/openpress/workbench-panels.css +0 -707
- package/template/core/src/styles/openpress/workbench.css +0 -1255
- package/template/core/src/styles/openpress/workspace-gallery.css +0 -300
- package/template/core/src/styles/openpress.css +0 -15
- 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
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
// @open-press/core/manuscript — long-form section-flow helpers.
|
|
2
|
-
//
|
|
3
|
-
// Manuscript helpers cover the "paper / report / book / monograph" pattern:
|
|
4
|
-
// a source resolves into a sequence of sections, each section becomes one
|
|
5
|
-
// content frame (with overflow auto-cloning across pages), and a TOC frame
|
|
6
|
-
// summarizes the outline.
|
|
7
|
-
//
|
|
8
|
-
// These helpers are conventions only. Core never knows about them. Any
|
|
9
|
-
// document type that wants section flow imports from here; documents that
|
|
10
|
-
// do not (slides, folios, calendars) skip this module entirely.
|
|
11
|
-
|
|
12
|
-
import { Fragment, useContext, type ComponentType, type ReactNode } from "react";
|
|
13
|
-
import { Frame, FrameContext, MdxArea, PressContext, useSource } from "../core";
|
|
14
|
-
import type { MdxAreaOverflow, ResolvedSource } from "../core";
|
|
15
|
-
import { createMdxAreaObjectEntityId } from "../document-model/objectEntityModel";
|
|
16
|
-
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
// <Sections>
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
|
|
21
|
-
export interface SectionsPageProps {
|
|
22
|
-
frameKey: string;
|
|
23
|
-
chainId: string;
|
|
24
|
-
pageIndex: number;
|
|
25
|
-
totalPages: number;
|
|
26
|
-
sectionSlug: string;
|
|
27
|
-
sectionTitle: string;
|
|
28
|
-
sectionTone?: string;
|
|
29
|
-
sectionMeta: Record<string, unknown>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface SectionsOpenerProps {
|
|
33
|
-
frameKey: string;
|
|
34
|
-
sectionSlug: string;
|
|
35
|
-
sectionTitle: string;
|
|
36
|
-
sectionIndex: number;
|
|
37
|
-
sectionMeta: Record<string, unknown>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface SectionsProps {
|
|
41
|
-
source: string;
|
|
42
|
-
page?: ComponentType<SectionsPageProps>;
|
|
43
|
-
opener?: ComponentType<SectionsOpenerProps>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function Sections({ source: sourceId, page: Page = DefaultSectionPage, opener: Opener }: SectionsProps) {
|
|
47
|
-
const source = useSource(sourceId);
|
|
48
|
-
const press = useContext(PressContext);
|
|
49
|
-
const hints = press?.hints ?? null;
|
|
50
|
-
return (
|
|
51
|
-
<Fragment>
|
|
52
|
-
{source.tree.map((section, index) => {
|
|
53
|
-
const chainId = `${sourceId}:${section.slug}`;
|
|
54
|
-
const meta = (section.meta ?? {}) as Record<string, unknown>;
|
|
55
|
-
const tone = typeof meta.tone === "string" ? meta.tone : undefined;
|
|
56
|
-
const totalPages = Math.max(1, hints?.totalPagesPerChain?.[chainId] ?? 1);
|
|
57
|
-
const pages: ReactNode[] = [];
|
|
58
|
-
for (let i = 0; i < totalPages; i++) {
|
|
59
|
-
pages.push(
|
|
60
|
-
<Page
|
|
61
|
-
key={i}
|
|
62
|
-
frameKey={`${sourceId}:${section.slug}:content:${i}`}
|
|
63
|
-
chainId={chainId}
|
|
64
|
-
pageIndex={i}
|
|
65
|
-
totalPages={totalPages}
|
|
66
|
-
sectionSlug={section.slug}
|
|
67
|
-
sectionTitle={section.title}
|
|
68
|
-
sectionTone={tone}
|
|
69
|
-
sectionMeta={meta}
|
|
70
|
-
/>,
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
return (
|
|
74
|
-
<Fragment key={section.slug}>
|
|
75
|
-
{Opener ? (
|
|
76
|
-
<Opener
|
|
77
|
-
frameKey={`${sourceId}:${section.slug}:opener`}
|
|
78
|
-
sectionSlug={section.slug}
|
|
79
|
-
sectionTitle={section.title}
|
|
80
|
-
sectionIndex={index}
|
|
81
|
-
sectionMeta={meta}
|
|
82
|
-
/>
|
|
83
|
-
) : null}
|
|
84
|
-
{pages}
|
|
85
|
-
</Fragment>
|
|
86
|
-
);
|
|
87
|
-
})}
|
|
88
|
-
</Fragment>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Compatibility alias for chapter vocabulary.
|
|
93
|
-
export const Chapters = Sections;
|
|
94
|
-
export type ChaptersProps = SectionsProps;
|
|
95
|
-
|
|
96
|
-
export function DefaultSectionPage({
|
|
97
|
-
frameKey,
|
|
98
|
-
chainId,
|
|
99
|
-
pageIndex,
|
|
100
|
-
totalPages,
|
|
101
|
-
sectionSlug,
|
|
102
|
-
sectionTitle,
|
|
103
|
-
sectionTone,
|
|
104
|
-
}: SectionsPageProps) {
|
|
105
|
-
return (
|
|
106
|
-
<Frame
|
|
107
|
-
frameKey={frameKey}
|
|
108
|
-
role="manuscript.content"
|
|
109
|
-
className="reader-page--content"
|
|
110
|
-
data-page-index={pageIndex}
|
|
111
|
-
data-total-pages={totalPages}
|
|
112
|
-
data-section-id={sectionSlug}
|
|
113
|
-
data-chapter-tone={sectionTone}
|
|
114
|
-
>
|
|
115
|
-
<div className="page-frame">
|
|
116
|
-
<header className="page-header" aria-hidden="true" />
|
|
117
|
-
<main className="page-body">
|
|
118
|
-
<MdxArea chainId={chainId} />
|
|
119
|
-
</main>
|
|
120
|
-
<footer className="page-footer" aria-hidden="true">
|
|
121
|
-
<span className="footer-left">{sectionTitle}</span>
|
|
122
|
-
<span className="footer-right">
|
|
123
|
-
{totalPages > 1 ? `${pageIndex + 1}/${totalPages}` : pageIndex + 1}
|
|
124
|
-
</span>
|
|
125
|
-
</footer>
|
|
126
|
-
</div>
|
|
127
|
-
</Frame>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// ---------------------------------------------------------------------------
|
|
132
|
-
// <Toc>
|
|
133
|
-
// ---------------------------------------------------------------------------
|
|
134
|
-
|
|
135
|
-
export interface TocProps {
|
|
136
|
-
source: string;
|
|
137
|
-
className?: string;
|
|
138
|
-
heading?: ReactNode;
|
|
139
|
-
frameKey?: string;
|
|
140
|
-
maxLevel?: 2 | 3;
|
|
141
|
-
overflow?: MdxAreaOverflow;
|
|
142
|
-
page?: React.ComponentType<TocPageProps>;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export interface TocPageProps {
|
|
146
|
-
frameKey: string;
|
|
147
|
-
chainId: string;
|
|
148
|
-
pageIndex: number;
|
|
149
|
-
totalPages: number;
|
|
150
|
-
sourceId: string;
|
|
151
|
-
heading?: ReactNode;
|
|
152
|
-
className?: string;
|
|
153
|
-
maxLevel?: 2 | 3;
|
|
154
|
-
overflow?: MdxAreaOverflow;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export interface TocAreaProps {
|
|
158
|
-
chainId: string;
|
|
159
|
-
maxLevel?: 2 | 3;
|
|
160
|
-
overflow?: MdxAreaOverflow;
|
|
161
|
-
className?: string;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function Toc({ source: sourceId, className, heading, frameKey = "toc", maxLevel = 3, overflow = "extend", page: Page = DefaultTocPage }: TocProps) {
|
|
165
|
-
useSource(sourceId) as ResolvedSource;
|
|
166
|
-
const press = useContext(PressContext);
|
|
167
|
-
const chainId = maxLevel <= 2 ? `toc:${sourceId}:h2` : `toc:${sourceId}`;
|
|
168
|
-
const totalPages = Math.max(1, press?.hints?.totalPagesPerChain?.[chainId] ?? 1);
|
|
169
|
-
const pages: ReactNode[] = [];
|
|
170
|
-
for (let i = 0; i < totalPages; i++) {
|
|
171
|
-
pages.push(
|
|
172
|
-
<Page
|
|
173
|
-
key={i}
|
|
174
|
-
frameKey={i === 0 ? frameKey : `${frameKey}:page:${i}`}
|
|
175
|
-
chainId={chainId}
|
|
176
|
-
pageIndex={i}
|
|
177
|
-
totalPages={totalPages}
|
|
178
|
-
sourceId={sourceId}
|
|
179
|
-
heading={heading}
|
|
180
|
-
className={className}
|
|
181
|
-
maxLevel={maxLevel}
|
|
182
|
-
overflow={overflow}
|
|
183
|
-
/>,
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
return <Fragment>{pages}</Fragment>;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function DefaultTocPage({ frameKey, chainId, pageIndex, totalPages, heading, className, maxLevel, overflow }: TocPageProps) {
|
|
190
|
-
const isContinuation = pageIndex > 0;
|
|
191
|
-
const tocClassName = ["reader-page--toc", isContinuation ? "toc-continuation" : null, className].filter(Boolean).join(" ") || undefined;
|
|
192
|
-
return (
|
|
193
|
-
<Frame
|
|
194
|
-
frameKey={frameKey}
|
|
195
|
-
role="manuscript.toc"
|
|
196
|
-
chrome={false}
|
|
197
|
-
className={tocClassName}
|
|
198
|
-
>
|
|
199
|
-
<div className="page-frame">
|
|
200
|
-
<header className="page-header toc-header">
|
|
201
|
-
{heading ?? (
|
|
202
|
-
<h2 className={isContinuation ? "toc-heading toc-heading--continuation" : "toc-heading"} id={isContinuation ? `${frameKey}-title` : "toc-title"}>
|
|
203
|
-
{isContinuation ? "目錄續" : "目錄"}
|
|
204
|
-
</h2>
|
|
205
|
-
)}
|
|
206
|
-
</header>
|
|
207
|
-
<main className="page-body">
|
|
208
|
-
<TocArea chainId={chainId} maxLevel={maxLevel} overflow={overflow} />
|
|
209
|
-
</main>
|
|
210
|
-
</div>
|
|
211
|
-
</Frame>
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export function TocArea({ chainId, maxLevel, overflow = "extend", className }: TocAreaProps) {
|
|
216
|
-
const frame = useContext(FrameContext);
|
|
217
|
-
const consumed = frame?.consumeArea(chainId) ?? null;
|
|
218
|
-
const blocks = consumed?.blocks ?? null;
|
|
219
|
-
const objectId = frame && consumed
|
|
220
|
-
? createMdxAreaObjectEntityId(frame.frameKey, chainId, consumed.indexInFrame)
|
|
221
|
-
: undefined;
|
|
222
|
-
return (
|
|
223
|
-
<div
|
|
224
|
-
className="openpress-mdx-area openpress-toc-area"
|
|
225
|
-
data-openpress-mdx-area="true"
|
|
226
|
-
data-openpress-mdx-area-chain={chainId}
|
|
227
|
-
data-openpress-mdx-area-index={consumed?.indexInFrame}
|
|
228
|
-
data-openpress-object-id={objectId}
|
|
229
|
-
data-openpress-toc-max-level={maxLevel}
|
|
230
|
-
data-openpress-mdx-area-overflow={overflow}
|
|
231
|
-
data-openpress-mdx-area-empty={blocks == null ? "true" : "false"}
|
|
232
|
-
>
|
|
233
|
-
<ol className={["toc-list", className].filter(Boolean).join(" ") || undefined}>
|
|
234
|
-
{blocks}
|
|
235
|
-
</ol>
|
|
236
|
-
</div>
|
|
237
|
-
);
|
|
238
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// @open-press/core/mdx — pure source descriptor factories.
|
|
2
|
-
//
|
|
3
|
-
// These factories MUST stay pure: they construct descriptor objects only.
|
|
4
|
-
// They must not touch the filesystem, fetch the network, or execute
|
|
5
|
-
// workspace logic at module load. Resolution happens in Layer 1 inside the
|
|
6
|
-
// engine, where IO is allowed.
|
|
7
|
-
|
|
8
|
-
import type {
|
|
9
|
-
MdxSourceDescriptor,
|
|
10
|
-
MdxSourceDescriptorFileList,
|
|
11
|
-
MdxSourceDescriptorSectionFiles,
|
|
12
|
-
MdxSourceDescriptorSectionFolders,
|
|
13
|
-
} from "../core/types";
|
|
14
|
-
|
|
15
|
-
export type {
|
|
16
|
-
MdxSourceDescriptor,
|
|
17
|
-
MdxSourceDescriptorFileList,
|
|
18
|
-
MdxSourceDescriptorSectionFiles,
|
|
19
|
-
MdxSourceDescriptorSectionFolders,
|
|
20
|
-
} from "../core/types";
|
|
21
|
-
|
|
22
|
-
export type {
|
|
23
|
-
OutlineItem,
|
|
24
|
-
ResolvedSource,
|
|
25
|
-
SourceBlock,
|
|
26
|
-
SourceFileRecord,
|
|
27
|
-
SourceNode,
|
|
28
|
-
} from "../core/types";
|
|
29
|
-
|
|
30
|
-
// All presets accept an optional `id` for the 1.0 contract where sources
|
|
31
|
-
// are an array passed via <Press sources>. In v0.x the id came from the
|
|
32
|
-
// record key in `export const sources = { story: mdxSource(...) }`.
|
|
33
|
-
type MdxSourceOptions =
|
|
34
|
-
| { id?: string; preset: "section-folders"; root?: string }
|
|
35
|
-
| { id?: string; preset: "section-files"; root?: string }
|
|
36
|
-
| { id?: string; preset: "file-list"; files: string[] };
|
|
37
|
-
|
|
38
|
-
const VALID_PRESETS = new Set(["section-folders", "section-files", "file-list"]);
|
|
39
|
-
|
|
40
|
-
export function mdxSource(options: MdxSourceOptions): MdxSourceDescriptor & { id?: string } {
|
|
41
|
-
if (!options || typeof options !== "object") {
|
|
42
|
-
throw new Error("mdxSource() requires an options object.");
|
|
43
|
-
}
|
|
44
|
-
if (!VALID_PRESETS.has(options.preset)) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
`mdxSource() preset must be one of: section-folders, section-files, file-list. Got "${
|
|
47
|
-
(options as { preset?: unknown }).preset
|
|
48
|
-
}".`,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const id = typeof options.id === "string" && options.id.trim() ? options.id.trim() : undefined;
|
|
53
|
-
|
|
54
|
-
if (options.preset === "section-folders") {
|
|
55
|
-
const desc = normalizeRooted("section-folders", options.root, "chapters") as MdxSourceDescriptorSectionFolders;
|
|
56
|
-
return id ? { ...desc, id } : desc;
|
|
57
|
-
}
|
|
58
|
-
if (options.preset === "section-files") {
|
|
59
|
-
const desc = normalizeRooted("section-files", options.root, "content") as MdxSourceDescriptorSectionFiles;
|
|
60
|
-
return id ? { ...desc, id } : desc;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// file-list
|
|
64
|
-
if (!Array.isArray(options.files)) {
|
|
65
|
-
throw new Error('mdxSource({ preset: "file-list" }) requires `files: string[]`.');
|
|
66
|
-
}
|
|
67
|
-
const files: string[] = [];
|
|
68
|
-
for (const raw of options.files) {
|
|
69
|
-
if (typeof raw !== "string") {
|
|
70
|
-
throw new Error('mdxSource({ preset: "file-list" }) `files` entries must be strings.');
|
|
71
|
-
}
|
|
72
|
-
const trimmed = raw.trim();
|
|
73
|
-
if (!trimmed) continue;
|
|
74
|
-
files.push(trimmed);
|
|
75
|
-
}
|
|
76
|
-
if (files.length === 0) {
|
|
77
|
-
throw new Error('mdxSource({ preset: "file-list" }) requires at least one file.');
|
|
78
|
-
}
|
|
79
|
-
const desc: MdxSourceDescriptor = { type: "mdx", preset: "file-list", files };
|
|
80
|
-
return id ? { ...desc, id } : desc;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function normalizeRooted(
|
|
84
|
-
preset: "section-folders" | "section-files",
|
|
85
|
-
root: string | undefined,
|
|
86
|
-
defaultRoot: string,
|
|
87
|
-
): MdxSourceDescriptor {
|
|
88
|
-
if (root !== undefined && typeof root !== "string") {
|
|
89
|
-
throw new Error(`mdxSource() \`root\` must be a string if provided. Got ${typeof root}.`);
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
type: "mdx",
|
|
93
|
-
preset,
|
|
94
|
-
root: (root ?? defaultRoot).trim() || defaultRoot,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
// @open-press/core/numbering — label formatters for AI authoring layer.
|
|
2
|
-
//
|
|
3
|
-
// Core stays neutral about how chapter / section / topic numbers should look.
|
|
4
|
-
// Source resolution exposes raw integers via outline items
|
|
5
|
-
// (`chapterNumber`, `sectionIndex`, `topicIndex`). This module is a toolbox
|
|
6
|
-
// of pure functions that turn those integers into human-readable labels —
|
|
7
|
-
// Chinese numerals, Roman numerals, alphabet letters, padded decimals, etc.
|
|
8
|
-
//
|
|
9
|
-
// Workspaces import what they need and compose them inside their own
|
|
10
|
-
// `<Page>` / `<Toc>` components. The core engine never calls these.
|
|
11
|
-
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
// CJK numeral systems
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
|
-
|
|
16
|
-
const CJK_INFORMAL = ["〇", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
|
|
17
|
-
const CJK_FORMAL = ["零", "壹", "貳", "參", "肆", "伍", "陸", "柒", "捌", "玖"];
|
|
18
|
-
const CJK_HEAVENLY_STEMS = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"];
|
|
19
|
-
const CJK_EARTHLY_BRANCHES = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"];
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Informal CJK numerals (一二三...). Handles 0 to 9999.
|
|
23
|
-
*
|
|
24
|
-
* toCjk(1) → "一"
|
|
25
|
-
* toCjk(10) → "十"
|
|
26
|
-
* toCjk(15) → "十五"
|
|
27
|
-
* toCjk(23) → "二十三"
|
|
28
|
-
* toCjk(100) → "一百"
|
|
29
|
-
* toCjk(108) → "一百零八"
|
|
30
|
-
* toCjk(2024) → "二千零二十四"
|
|
31
|
-
*/
|
|
32
|
-
export function toCjk(n: number): string {
|
|
33
|
-
if (!Number.isFinite(n) || n < 0) return String(n);
|
|
34
|
-
return formatCjkDigits(Math.floor(n), CJK_INFORMAL);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Formal CJK numerals (壹貳參...). Used in legal / financial contexts.
|
|
39
|
-
*
|
|
40
|
-
* toCjkFormal(1) → "壹"
|
|
41
|
-
* toCjkFormal(100) → "壹佰"
|
|
42
|
-
*/
|
|
43
|
-
export function toCjkFormal(n: number): string {
|
|
44
|
-
if (!Number.isFinite(n) || n < 0) return String(n);
|
|
45
|
-
return formatCjkDigits(Math.floor(n), CJK_FORMAL, { formal: true });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Heavenly Stems cycle (甲乙丙丁戊己庚辛壬癸 — 10 only).
|
|
50
|
-
* Cycles for n > 10; n=11 → 甲 again.
|
|
51
|
-
*
|
|
52
|
-
* toCjkHeavenlyStem(1) → "甲"
|
|
53
|
-
* toCjkHeavenlyStem(10) → "癸"
|
|
54
|
-
* toCjkHeavenlyStem(11) → "甲"
|
|
55
|
-
*/
|
|
56
|
-
export function toCjkHeavenlyStem(n: number): string {
|
|
57
|
-
if (!Number.isFinite(n) || n < 1) return String(n);
|
|
58
|
-
return CJK_HEAVENLY_STEMS[(Math.floor(n) - 1) % 10]!;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Earthly Branches cycle (子丑寅卯辰巳午未申酉戌亥 — 12 only).
|
|
63
|
-
* Cycles for n > 12.
|
|
64
|
-
*/
|
|
65
|
-
export function toCjkEarthlyBranch(n: number): string {
|
|
66
|
-
if (!Number.isFinite(n) || n < 1) return String(n);
|
|
67
|
-
return CJK_EARTHLY_BRANCHES[(Math.floor(n) - 1) % 12]!;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
// Roman numerals
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
|
|
74
|
-
const ROMAN_TABLE: Array<[number, string]> = [
|
|
75
|
-
[1000, "M"], [900, "CM"], [500, "D"], [400, "CD"],
|
|
76
|
-
[100, "C"], [90, "XC"], [50, "L"], [40, "XL"],
|
|
77
|
-
[10, "X"], [9, "IX"], [5, "V"], [4, "IV"],
|
|
78
|
-
[1, "I"],
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Roman numerals (I, II, III, …, MMXX). Uppercase by default.
|
|
83
|
-
* Pass `{ upper: false }` for lowercase (typical front-matter page numbering).
|
|
84
|
-
*
|
|
85
|
-
* toRoman(1) → "I"
|
|
86
|
-
* toRoman(4) → "IV"
|
|
87
|
-
* toRoman(2024) → "MMXXIV"
|
|
88
|
-
* toRoman(3, { upper: false }) → "iii"
|
|
89
|
-
*/
|
|
90
|
-
export function toRoman(n: number, opts: { upper?: boolean } = {}): string {
|
|
91
|
-
if (!Number.isFinite(n) || n < 1) return String(n);
|
|
92
|
-
let value = Math.floor(n);
|
|
93
|
-
let out = "";
|
|
94
|
-
for (const [num, sym] of ROMAN_TABLE) {
|
|
95
|
-
while (value >= num) {
|
|
96
|
-
out += sym;
|
|
97
|
-
value -= num;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return opts.upper === false ? out.toLowerCase() : out;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
|
-
// Latin alphabet (a, b, c, …, z, aa, ab, …)
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Spreadsheet-column-style alphabet labels.
|
|
109
|
-
*
|
|
110
|
-
* toAlpha(1) → "a"
|
|
111
|
-
* toAlpha(26) → "z"
|
|
112
|
-
* toAlpha(27) → "aa"
|
|
113
|
-
* toAlpha(53) → "ba"
|
|
114
|
-
* toAlpha(1, { upper: true }) → "A"
|
|
115
|
-
*/
|
|
116
|
-
export function toAlpha(n: number, opts: { upper?: boolean } = {}): string {
|
|
117
|
-
if (!Number.isFinite(n) || n < 1) return String(n);
|
|
118
|
-
let value = Math.floor(n);
|
|
119
|
-
let out = "";
|
|
120
|
-
while (value > 0) {
|
|
121
|
-
const rem = (value - 1) % 26;
|
|
122
|
-
out = String.fromCharCode(97 + rem) + out;
|
|
123
|
-
value = Math.floor((value - 1) / 26);
|
|
124
|
-
}
|
|
125
|
-
return opts.upper ? out.toUpperCase() : out;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ---------------------------------------------------------------------------
|
|
129
|
-
// Zero-padded decimal
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Zero-padded decimal — `width` is the minimum number of digits.
|
|
134
|
-
*
|
|
135
|
-
* toPadded(1) → "01"
|
|
136
|
-
* toPadded(1, { width: 3 }) → "001"
|
|
137
|
-
* toPadded(123) → "123"
|
|
138
|
-
*/
|
|
139
|
-
export function toPadded(n: number, opts: { width?: number } = {}): string {
|
|
140
|
-
if (!Number.isFinite(n)) return String(n);
|
|
141
|
-
const width = Math.max(1, opts.width ?? 2);
|
|
142
|
-
return String(Math.floor(n)).padStart(width, "0");
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// ---------------------------------------------------------------------------
|
|
146
|
-
// Composable label templates
|
|
147
|
-
// ---------------------------------------------------------------------------
|
|
148
|
-
|
|
149
|
-
export type NumberFormat =
|
|
150
|
-
| "decimal"
|
|
151
|
-
| "decimal-padded"
|
|
152
|
-
| "cjk"
|
|
153
|
-
| "cjk-formal"
|
|
154
|
-
| "cjk-heavenly-stem"
|
|
155
|
-
| "cjk-earthly-branch"
|
|
156
|
-
| "roman"
|
|
157
|
-
| "roman-lower"
|
|
158
|
-
| "alpha"
|
|
159
|
-
| "alpha-upper";
|
|
160
|
-
|
|
161
|
-
export interface ChapterLabelOptions {
|
|
162
|
-
format?: NumberFormat;
|
|
163
|
-
prefix?: string;
|
|
164
|
-
suffix?: string;
|
|
165
|
-
width?: number;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Compose a chapter label like "第一章" / "Chapter 1" / "01" / "Part IV".
|
|
170
|
-
*
|
|
171
|
-
* chapterLabel(1)
|
|
172
|
-
* → "第一章" (default: cjk informal, prefix 第, suffix 章)
|
|
173
|
-
* chapterLabel(1, { format: "decimal-padded", prefix: "Chapter " })
|
|
174
|
-
* → "Chapter 01"
|
|
175
|
-
* chapterLabel(4, { format: "roman", prefix: "Part " })
|
|
176
|
-
* → "Part IV"
|
|
177
|
-
* chapterLabel(2, { prefix: "", suffix: "" })
|
|
178
|
-
* → "二"
|
|
179
|
-
*/
|
|
180
|
-
export function chapterLabel(n: number, opts: ChapterLabelOptions = {}): string {
|
|
181
|
-
const { format = "cjk", prefix = "第", suffix = "章", width } = opts;
|
|
182
|
-
return `${prefix}${formatNumber(n, format, width)}${suffix}`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export interface SectionLabelOptions {
|
|
186
|
-
format?: NumberFormat;
|
|
187
|
-
separator?: string;
|
|
188
|
-
prefix?: string;
|
|
189
|
-
suffix?: string;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Compose a section label like "1.1" / "1.1.2" / "一-1".
|
|
194
|
-
*
|
|
195
|
-
* Accepts variadic counters for nesting:
|
|
196
|
-
* sectionLabel(1, 2)
|
|
197
|
-
* → "1.2"
|
|
198
|
-
* sectionLabel(1, 2, 3)
|
|
199
|
-
* → "1.2.3"
|
|
200
|
-
* sectionLabel(1, 2, { format: "cjk", separator: "之" })
|
|
201
|
-
* ⚠ note: pass options as last argument when using variadic counters;
|
|
202
|
-
* prefer `sectionLabelOf([1, 2], { … })` below for clarity.
|
|
203
|
-
*/
|
|
204
|
-
export function sectionLabel(...counters: number[]): string {
|
|
205
|
-
return sectionLabelOf(counters);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Explicit-array variant of `sectionLabel` so options can be passed cleanly.
|
|
210
|
-
*
|
|
211
|
-
* sectionLabelOf([1, 2], { separator: "-" }) → "1-2"
|
|
212
|
-
* sectionLabelOf([1, 2, 3], { format: "alpha" }) → "a.b.c"
|
|
213
|
-
* sectionLabelOf([1, 1], { format: "cjk", separator: "-" }) → "一-一"
|
|
214
|
-
*/
|
|
215
|
-
export function sectionLabelOf(counters: number[], opts: SectionLabelOptions = {}): string {
|
|
216
|
-
const { format = "decimal", separator = ".", prefix = "", suffix = "" } = opts;
|
|
217
|
-
const parts = counters.map((c) => formatNumber(c, format));
|
|
218
|
-
return `${prefix}${parts.join(separator)}${suffix}`;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// ---------------------------------------------------------------------------
|
|
222
|
-
// Generic dispatch — `format` -> formatter
|
|
223
|
-
// ---------------------------------------------------------------------------
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Format a single number using one of the named formats. Useful when the
|
|
227
|
-
* caller already knows which format string to use (e.g. from config).
|
|
228
|
-
*
|
|
229
|
-
* formatNumber(7, "cjk") → "七"
|
|
230
|
-
* formatNumber(7, "roman-lower") → "vii"
|
|
231
|
-
* formatNumber(3, "decimal-padded", 3) → "003"
|
|
232
|
-
*/
|
|
233
|
-
export function formatNumber(n: number, format: NumberFormat, width?: number): string {
|
|
234
|
-
switch (format) {
|
|
235
|
-
case "decimal": return String(Math.floor(n));
|
|
236
|
-
case "decimal-padded": return toPadded(n, { width });
|
|
237
|
-
case "cjk": return toCjk(n);
|
|
238
|
-
case "cjk-formal": return toCjkFormal(n);
|
|
239
|
-
case "cjk-heavenly-stem": return toCjkHeavenlyStem(n);
|
|
240
|
-
case "cjk-earthly-branch": return toCjkEarthlyBranch(n);
|
|
241
|
-
case "roman": return toRoman(n, { upper: true });
|
|
242
|
-
case "roman-lower": return toRoman(n, { upper: false });
|
|
243
|
-
case "alpha": return toAlpha(n);
|
|
244
|
-
case "alpha-upper": return toAlpha(n, { upper: true });
|
|
245
|
-
default: return String(n);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// ---------------------------------------------------------------------------
|
|
250
|
-
// CJK digit formatter (internal)
|
|
251
|
-
// ---------------------------------------------------------------------------
|
|
252
|
-
|
|
253
|
-
const CJK_UNITS = ["", "十", "百", "千"];
|
|
254
|
-
const CJK_UNITS_FORMAL = ["", "拾", "佰", "仟"];
|
|
255
|
-
|
|
256
|
-
// Gap-zero indicator used between non-zero digits when reading aloud
|
|
257
|
-
// (e.g. 一百零八). Distinct from 〇/零 as a literal zero digit.
|
|
258
|
-
const CJK_GAP_ZERO = "零";
|
|
259
|
-
|
|
260
|
-
function formatCjkDigits(n: number, digits: string[], opts: { formal?: boolean } = {}): string {
|
|
261
|
-
if (n === 0) return digits[0]!;
|
|
262
|
-
if (n < 0) return `負${formatCjkDigits(-n, digits, opts)}`;
|
|
263
|
-
if (n >= 10000) {
|
|
264
|
-
const wan = Math.floor(n / 10000);
|
|
265
|
-
const rest = n % 10000;
|
|
266
|
-
if (rest === 0) return `${formatCjkDigits(wan, digits, opts)}萬`;
|
|
267
|
-
const restStr = formatCjkDigits(rest, digits, opts);
|
|
268
|
-
const padded = rest < 1000 ? `${CJK_GAP_ZERO}${restStr}` : restStr;
|
|
269
|
-
return `${formatCjkDigits(wan, digits, opts)}萬${padded}`;
|
|
270
|
-
}
|
|
271
|
-
const units = opts.formal ? CJK_UNITS_FORMAL : CJK_UNITS;
|
|
272
|
-
const str = String(n);
|
|
273
|
-
let out = "";
|
|
274
|
-
let zeroPending = false;
|
|
275
|
-
for (let i = 0; i < str.length; i++) {
|
|
276
|
-
const digit = Number(str[i]);
|
|
277
|
-
const unit = str.length - 1 - i;
|
|
278
|
-
if (digit === 0) {
|
|
279
|
-
zeroPending = true;
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
if (zeroPending && out.length > 0) {
|
|
283
|
-
out += CJK_GAP_ZERO;
|
|
284
|
-
zeroPending = false;
|
|
285
|
-
}
|
|
286
|
-
// Special case: leading "一十" usually written as just "十" in informal CJK
|
|
287
|
-
if (!opts.formal && digit === 1 && unit === 1 && i === 0) {
|
|
288
|
-
out += units[unit];
|
|
289
|
-
} else {
|
|
290
|
-
out += digits[digit] + units[unit];
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return out;
|
|
294
|
-
}
|