@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.
Files changed (175) hide show
  1. package/README.md +11 -12
  2. package/dist/cli.js +298 -79
  3. package/package.json +9 -7
  4. package/template/core/AGENTS.md +0 -130
  5. package/template/core/CHANGELOG.md +0 -218
  6. package/template/core/README.md +0 -43
  7. package/template/core/engine/cli.mjs +0 -96
  8. package/template/core/engine/commands/_shared.mjs +0 -199
  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/image.mjs +0 -29
  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/skills-sync.mjs +0 -71
  21. package/template/core/engine/commands/typecheck.mjs +0 -67
  22. package/template/core/engine/commands/upgrade.mjs +0 -159
  23. package/template/core/engine/commands/validate.mjs +0 -17
  24. package/template/core/engine/document-export.mjs +0 -15
  25. package/template/core/engine/output/chrome-pdf.d.mts +0 -34
  26. package/template/core/engine/output/chrome-pdf.mjs +0 -450
  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 -571
  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 -331
  39. package/template/core/engine/react/document-export.mjs +0 -512
  40. package/template/core/engine/react/http-json.mjs +0 -24
  41. package/template/core/engine/react/mdx-compile.mjs +0 -629
  42. package/template/core/engine/react/measurement-css.mjs +0 -157
  43. package/template/core/engine/react/object-entities.mjs +0 -204
  44. package/template/core/engine/react/pagination/allocator.mjs +0 -167
  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 -217
  49. package/template/core/engine/react/pipeline/final-render.mjs +0 -94
  50. package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -306
  51. package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
  52. package/template/core/engine/react/press-tree-inspection.mjs +0 -172
  53. package/template/core/engine/react/project-asset-endpoint.d.mts +0 -10
  54. package/template/core/engine/react/project-asset-endpoint.mjs +0 -361
  55. package/template/core/engine/react/section-css.mjs +0 -56
  56. package/template/core/engine/react/source-edit-endpoint.d.mts +0 -10
  57. package/template/core/engine/react/source-edit-endpoint.mjs +0 -75
  58. package/template/core/engine/react/sources/heading-numbering.mjs +0 -132
  59. package/template/core/engine/react/sources/mdx-resolver.mjs +0 -439
  60. package/template/core/engine/react/style-discovery.mjs +0 -160
  61. package/template/core/engine/runtime/config.d.mts +0 -48
  62. package/template/core/engine/runtime/config.mjs +0 -172
  63. package/template/core/engine/runtime/file-utils.mjs +0 -114
  64. package/template/core/engine/runtime/file-walk.mjs +0 -22
  65. package/template/core/engine/runtime/inspection.mjs +0 -328
  66. package/template/core/engine/runtime/issue-report.mjs +0 -44
  67. package/template/core/engine/runtime/page-geometry.mjs +0 -131
  68. package/template/core/engine/runtime/path-utils.mjs +0 -20
  69. package/template/core/engine/runtime/source-text-tools.d.mts +0 -102
  70. package/template/core/engine/runtime/source-text-tools.mjs +0 -832
  71. package/template/core/engine/runtime/source-workspace.mjs +0 -168
  72. package/template/core/engine/runtime/validation.mjs +0 -183
  73. package/template/core/index.html +0 -13
  74. package/template/core/openpress.config.mjs +0 -8
  75. package/template/core/package.json +0 -89
  76. package/template/core/src/main.tsx +0 -16
  77. package/template/core/src/openpress/app/OpenPressApp.tsx +0 -296
  78. package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -102
  79. package/template/core/src/openpress/app/WorkspaceGalleryPage.tsx +0 -219
  80. package/template/core/src/openpress/app/index.ts +0 -2
  81. package/template/core/src/openpress/core/Frame.tsx +0 -91
  82. package/template/core/src/openpress/core/FrameContext.tsx +0 -26
  83. package/template/core/src/openpress/core/MdxArea.tsx +0 -34
  84. package/template/core/src/openpress/core/Press.tsx +0 -55
  85. package/template/core/src/openpress/core/Workspace.tsx +0 -36
  86. package/template/core/src/openpress/core/cn.ts +0 -4
  87. package/template/core/src/openpress/core/index.tsx +0 -47
  88. package/template/core/src/openpress/core/primitives.tsx +0 -91
  89. package/template/core/src/openpress/core/types.ts +0 -236
  90. package/template/core/src/openpress/core/useSource.ts +0 -28
  91. package/template/core/src/openpress/document-model/anchorMapModel.ts +0 -27
  92. package/template/core/src/openpress/document-model/documentIndexes.ts +0 -329
  93. package/template/core/src/openpress/document-model/documentTypes.ts +0 -147
  94. package/template/core/src/openpress/document-model/index.ts +0 -7
  95. package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -55
  96. package/template/core/src/openpress/document-model/projectIdentityModel.ts +0 -15
  97. package/template/core/src/openpress/document-model/reactDocumentMetadataModel.ts +0 -27
  98. package/template/core/src/openpress/document-model/workspaceManifestModel.ts +0 -57
  99. package/template/core/src/openpress/manuscript/index.tsx +0 -238
  100. package/template/core/src/openpress/mdx/index.ts +0 -96
  101. package/template/core/src/openpress/numbering/index.ts +0 -294
  102. package/template/core/src/openpress/reader/PageThumbnailsPanel.tsx +0 -168
  103. package/template/core/src/openpress/reader/PublicReaderPage.tsx +0 -267
  104. package/template/core/src/openpress/reader/ReaderNavigationPanel.tsx +0 -123
  105. package/template/core/src/openpress/reader/index.ts +0 -11
  106. package/template/core/src/openpress/reader/pageViewportScaleModel.ts +0 -73
  107. package/template/core/src/openpress/reader/readerPageRegistry.ts +0 -41
  108. package/template/core/src/openpress/reader/readerPageRoute.ts +0 -21
  109. package/template/core/src/openpress/reader/readerScroll.ts +0 -92
  110. package/template/core/src/openpress/reader/readerStateModel.ts +0 -15
  111. package/template/core/src/openpress/reader/readerTypes.ts +0 -4
  112. package/template/core/src/openpress/reader/usePageViewportScale.ts +0 -119
  113. package/template/core/src/openpress/reader/usePanelState.ts +0 -56
  114. package/template/core/src/openpress/reader/useReaderHashSync.ts +0 -61
  115. package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +0 -48
  116. package/template/core/src/openpress/reader/useReaderRuntime.ts +0 -146
  117. package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +0 -64
  118. package/template/core/src/openpress/shared/Panel.tsx +0 -77
  119. package/template/core/src/openpress/shared/frameScheduler.ts +0 -32
  120. package/template/core/src/openpress/shared/index.ts +0 -4
  121. package/template/core/src/openpress/shared/numberUtils.ts +0 -3
  122. package/template/core/src/openpress/shared/runtimeMode.ts +0 -11
  123. package/template/core/src/openpress/workbench/Workbench.tsx +0 -506
  124. package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
  125. package/template/core/src/openpress/workbench/actions/ExportImageControl.tsx +0 -96
  126. package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +0 -182
  127. package/template/core/src/openpress/workbench/actions/SearchControl.tsx +0 -345
  128. package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +0 -112
  129. package/template/core/src/openpress/workbench/actions/index.ts +0 -6
  130. package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +0 -136
  131. package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +0 -72
  132. package/template/core/src/openpress/workbench/dialog/index.ts +0 -1
  133. package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +0 -127
  134. package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +0 -207
  135. package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +0 -9
  136. package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +0 -34
  137. package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +0 -525
  138. package/template/core/src/openpress/workbench/document/index.ts +0 -10
  139. package/template/core/src/openpress/workbench/index.ts +0 -2
  140. package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +0 -459
  141. package/template/core/src/openpress/workbench/inspector/index.ts +0 -5
  142. package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +0 -125
  143. package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +0 -160
  144. package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +0 -408
  145. package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +0 -254
  146. package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +0 -41
  147. package/template/core/src/openpress/workbench/mentions/index.ts +0 -2
  148. package/template/core/src/openpress/workbench/mentions/useComposerMentions.ts +0 -185
  149. package/template/core/src/openpress/workbench/panels/Panel.tsx +0 -1
  150. package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +0 -80
  151. package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +0 -29
  152. package/template/core/src/openpress/workbench/panels/index.ts +0 -3
  153. package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +0 -525
  154. package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +0 -35
  155. package/template/core/src/openpress/workbench/project/index.ts +0 -2
  156. package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +0 -11
  157. package/template/core/src/openpress/workbench/project/projectSourceModel.ts +0 -24
  158. package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +0 -167
  159. package/template/core/src/openpress/workbench/shell/index.ts +0 -1
  160. package/template/core/src/openpress/workbench/workbenchFormatters.ts +0 -120
  161. package/template/core/src/openpress/workbench/workbenchTypes.ts +0 -35
  162. package/template/core/src/styles/openpress/app-shell.css +0 -251
  163. package/template/core/src/styles/openpress/media-workspace.css +0 -230
  164. package/template/core/src/styles/openpress/print-route.css +0 -184
  165. package/template/core/src/styles/openpress/project-preview-panel.css +0 -924
  166. package/template/core/src/styles/openpress/public-viewer.css +0 -688
  167. package/template/core/src/styles/openpress/reader-runtime.css +0 -989
  168. package/template/core/src/styles/openpress/responsive.css +0 -245
  169. package/template/core/src/styles/openpress/workbench-panels.css +0 -707
  170. package/template/core/src/styles/openpress/workbench.css +0 -1255
  171. package/template/core/src/styles/openpress/workspace-gallery.css +0 -300
  172. package/template/core/src/styles/openpress.css +0 -15
  173. package/template/core/src/vite-env.d.ts +0 -9
  174. package/template/core/tsconfig.json +0 -40
  175. package/template/core/vite.config.ts +0 -584
@@ -1,629 +0,0 @@
1
- import path from "node:path";
2
- import { pathToFileURL } from "node:url";
3
- import { evaluate } from "@mdx-js/mdx";
4
- import React from "react";
5
- import * as jsxRuntime from "react/jsx-runtime";
6
- import rehypeKatex from "rehype-katex";
7
- import remarkGfm from "remark-gfm";
8
- import remarkMath from "remark-math";
9
-
10
- const PAGINABLE_TAGS = new Set([
11
- "p",
12
- "h1",
13
- "h2",
14
- "h3",
15
- "h4",
16
- "h5",
17
- "h6",
18
- "ul",
19
- "ol",
20
- "pre",
21
- "blockquote",
22
- "figure",
23
- "table",
24
- ]);
25
- const TABLE_CAPTION_COMPONENT_NAME = "TableCaption";
26
- const LEGACY_TABLE_CAPTION_MARKER_RE = /^\s*表\s*(?:[\d一二三四五六七八九十百千〇零]+(?:[--..][\d一二三四五六七八九十百千〇零]+)?)?\s*[::、..]\s*(.+?)\s*$/u;
27
-
28
- export async function compileMdx({
29
- source,
30
- filePath,
31
- components = {},
32
- chapterSlug = "document",
33
- includeBlockIds = null,
34
- blockAttributes = null,
35
- } = {}) {
36
- if (typeof source !== "string") throw new Error("compileMdx requires a string `source`.");
37
- if (typeof filePath !== "string" || !filePath.trim()) throw new Error("compileMdx requires `filePath`.");
38
- assertNoImports(source, filePath);
39
- const mdxSource = normalizeSingleLineDisplayMath(source);
40
-
41
- const blocks = [];
42
- const remarkPlugins = [[remarkMath, { singleDollarTextMath: true }], remarkGfm, [remarkBlockOnlyMdx, { filePath }]];
43
- const rehypePlugins = [rehypeKatex, rehypeTableCaptions, [rehypeBlockIds, { blocks, filePath, chapterSlug, includeBlockIds, blockAttributes }]];
44
- const mod = await evaluate(mdxSource, {
45
- ...jsxRuntime,
46
- baseUrl: pathToFileURL(filePath).href,
47
- remarkPlugins,
48
- rehypePlugins,
49
- });
50
- const MdxContent = mod.default;
51
- const mdxComponents = wrapMdxComponents(components);
52
-
53
- function MdxContentWrapper(props = {}) {
54
- return React.createElement(MdxContent, {
55
- ...props,
56
- components: {
57
- ...mdxComponents,
58
- ...(props.components ?? {}),
59
- },
60
- });
61
- }
62
-
63
- return {
64
- Content: MdxContentWrapper,
65
- blocks,
66
- exports: mod,
67
- };
68
- }
69
-
70
- export function rehypeTableCaptions() {
71
- return (tree) => {
72
- normalizeTableCaptions(tree);
73
- };
74
- }
75
-
76
- export function rehypeBlockIds(options = {}) {
77
- const blocks = Array.isArray(options.blocks) ? options.blocks : [];
78
- const filePath = String(options.filePath ?? "document.mdx");
79
- const chapterSlug = slugPart(options.chapterSlug ?? "document");
80
- const sourceSlug = slugPart(path.basename(filePath, path.extname(filePath)));
81
- const includeBlockIds = Array.isArray(options.includeBlockIds) ? new Set(options.includeBlockIds) : null;
82
- const blockAttributes = normalizeBlockAttributes(options.blockAttributes);
83
- let counter = 0;
84
-
85
- return (tree) => {
86
- filterTree(tree, (node) => {
87
- const block = blockInfo(node);
88
- if (!block) return true;
89
-
90
- const id = `b-${chapterSlug}-${sourceSlug}-${counter}`;
91
- counter += 1;
92
- if (block.name === "table") {
93
- return applyTableRowBlocks({
94
- node,
95
- id,
96
- blocks,
97
- filePath,
98
- chapterSlug,
99
- includeBlockIds,
100
- });
101
- }
102
- if (block.name === "ul" || block.name === "ol") {
103
- return applyListItemBlocks({
104
- node,
105
- id,
106
- blocks,
107
- filePath,
108
- chapterSlug,
109
- includeBlockIds,
110
- });
111
- }
112
- if (includeBlockIds && !includeBlockIds.has(id)) return false;
113
-
114
- setDataAttribute(node, "data-openpress-block-id", id);
115
- setDataAttribute(node, "data-openpress-object-id", createBlockObjectEntityId(id));
116
- const extraAttributes = blockAttributes.get(id);
117
- if (extraAttributes) {
118
- for (const [name, value] of Object.entries(extraAttributes)) {
119
- if (value == null || value === "") continue;
120
- setDataAttribute(node, name, String(value));
121
- }
122
- }
123
- blocks.push({
124
- id,
125
- kind: block.kind,
126
- name: block.name,
127
- text: block.text,
128
- filePath,
129
- chapterSlug,
130
- source: sourcePosition(node.position),
131
- });
132
- return "skip";
133
- });
134
- };
135
- }
136
-
137
- function applyTableRowBlocks({
138
- node,
139
- id,
140
- blocks,
141
- filePath,
142
- chapterSlug,
143
- includeBlockIds,
144
- }) {
145
- const rows = tableBodyRows(node);
146
- const header = tableHeaderRow(node);
147
- const caption = tableCaption(node);
148
- const captionRecord = caption ? { id: `${id}-caption`, node: caption } : null;
149
- const headerRecord = header ? { id: `${id}-h0`, node: header } : null;
150
- const selectedCaption = captionRecord && (!includeBlockIds || includeBlockIds.has(captionRecord.id));
151
- const selectedHeader = headerRecord && (!includeBlockIds || includeBlockIds.has(headerRecord.id));
152
- const firstSelectedRowIndex = selectedFirstTableRowIndex(rows, includeBlockIds, id);
153
- const renderCaption = selectedCaption || (captionRecord && includeBlockIds && firstSelectedRowIndex === 0);
154
- const renderHeader = Boolean(headerRecord && (!includeBlockIds || firstSelectedRowIndex === 0 || selectedHeader));
155
- if (rows.length === 0) {
156
- if (includeBlockIds && !includeBlockIds.has(id)) return false;
157
- setDataAttribute(node, "data-openpress-block-id", id);
158
- setDataAttribute(node, "data-openpress-object-id", createBlockObjectEntityId(id));
159
- blocks.push({
160
- id,
161
- kind: "element",
162
- name: "table",
163
- text: textContent(node).trim() || undefined,
164
- filePath,
165
- chapterSlug,
166
- source: sourcePosition(node.position),
167
- });
168
- return "skip";
169
- }
170
-
171
- const rowRecords = rows.map((row, index) => ({
172
- id: `${id}-r${index}`,
173
- node: row,
174
- index,
175
- }));
176
- const selected = includeBlockIds
177
- ? rowRecords.filter((row) => includeBlockIds.has(row.id))
178
- : rowRecords;
179
- if (selected.length === 0 && !selectedCaption && !selectedHeader) return false;
180
-
181
- setDataAttribute(node, "data-openpress-table-id", id);
182
- if (headerRecord && renderHeader) {
183
- setDataAttribute(headerRecord.node, "data-openpress-block-id", headerRecord.id);
184
- setDataAttribute(headerRecord.node, "data-openpress-object-id", createBlockObjectEntityId(headerRecord.id));
185
- setDataAttribute(headerRecord.node, "data-openpress-block-layout", "attached");
186
- annotateTableCells(headerRecord.node, headerRecord.id);
187
- }
188
- if (captionRecord) {
189
- if (renderCaption) {
190
- setDataAttribute(captionRecord.node, "data-openpress-block-id", captionRecord.id);
191
- setDataAttribute(captionRecord.node, "data-openpress-object-id", createBlockObjectEntityId(captionRecord.id));
192
- if (selectedCaption) {
193
- blocks.push({
194
- id: captionRecord.id,
195
- kind: "element",
196
- name: "caption",
197
- text: textContent(captionRecord.node).trim() || undefined,
198
- filePath,
199
- chapterSlug,
200
- tableId: id,
201
- layout: "attached",
202
- source: sourcePosition(captionRecord.node.position ?? node.position),
203
- });
204
- }
205
- } else {
206
- removeTableCaption(node);
207
- }
208
- }
209
- if (headerRecord && selectedHeader) {
210
- blocks.push({
211
- id: headerRecord.id,
212
- kind: "table-row",
213
- name: "table-header-row",
214
- text: textContent(headerRecord.node).trim() || undefined,
215
- filePath,
216
- chapterSlug,
217
- tableId: id,
218
- rowIndex: -1,
219
- layout: "attached",
220
- source: sourcePosition(headerRecord.node.position ?? node.position),
221
- });
222
- }
223
- const selectedNodes = new Set(selected.map((row) => row.node));
224
- pruneUnselectedTableRows(node, new Set(rowRecords.map((row) => row.node)), selectedNodes);
225
- if (!renderHeader) stripTableHeader(node);
226
-
227
- for (const row of selected) {
228
- setDataAttribute(row.node, "data-openpress-block-id", row.id);
229
- setDataAttribute(row.node, "data-openpress-object-id", createBlockObjectEntityId(row.id));
230
- // Bake cell-level object ids into every <td>/<th>. The inspector resolves
231
- // a clicked target via `closest("[data-openpress-object-id]")` — without
232
- // this, a click inside a cell would walk up to the row and a comment
233
- // would target the entire row. With the cell-precision id present in the
234
- // static HTML the inspector targets the individual cell, matching the
235
- // engine's per-cell source-edit pipeline (`cellIndex`).
236
- annotateTableCells(row.node, row.id);
237
- blocks.push({
238
- id: row.id,
239
- kind: "table-row",
240
- name: "table-row",
241
- text: textContent(row.node).trim() || undefined,
242
- filePath,
243
- chapterSlug,
244
- tableId: id,
245
- rowIndex: row.index,
246
- source: sourcePosition(row.node.position ?? node.position),
247
- });
248
- }
249
- return "skip";
250
- }
251
-
252
- function annotateTableCells(rowNode, rowBlockId) {
253
- const children = Array.isArray(rowNode?.children) ? rowNode.children : [];
254
- let cellIndex = 0;
255
- for (const child of children) {
256
- if (child?.type !== "element") continue;
257
- if (child.tagName !== "td" && child.tagName !== "th") continue;
258
- // Inherit the row's block id so `findObjectSelection` can resolve the
259
- // cell's underlying SourceBlock (which lives on the row). The
260
- // cell-precision `data-openpress-object-id` + cellIndex still let the
261
- // inspector / source-edit pipeline target a single cell within that row.
262
- // `data-openpress-inherited-block-id="true"` keeps the same convention
263
- // the inline editor uses for caption / cell descendants, so block
264
- // measurement (which queries `[data-openpress-block-id]`) can skip
265
- // these and not double-count the row's height across N cells.
266
- setDataAttribute(child, "data-openpress-block-id", rowBlockId);
267
- setDataAttribute(child, "data-openpress-inherited-block-id", "true");
268
- setDataAttribute(child, "data-openpress-object-id", `${createBlockObjectEntityId(rowBlockId)}:cell:${cellIndex}`);
269
- setDataAttribute(child, "data-openpress-table-cell-index", String(cellIndex));
270
- cellIndex += 1;
271
- }
272
- }
273
-
274
- export function remarkBlockOnlyMdx(options = {}) {
275
- const filePath = String(options.filePath ?? "document.mdx");
276
-
277
- return (tree) => {
278
- visit(tree, (node) => {
279
- if (node?.type !== "mdxJsxTextElement") return;
280
- const position = node.position?.start;
281
- const suffix = position ? `:${position.line}:${position.column}` : "";
282
- throw new Error(`MDX JSX components must be block-only in OpenPress chapter prose: ${filePath}${suffix}`);
283
- });
284
- };
285
- }
286
-
287
- function normalizeTableCaptions(node) {
288
- if (!Array.isArray(node?.children)) return;
289
-
290
- for (let index = 0; index < node.children.length; index += 1) {
291
- const child = node.children[index];
292
- normalizeTableCaptions(child);
293
-
294
- const legacyCaptionText = legacyTableCaptionText(child);
295
- if (legacyCaptionText) {
296
- throw new Error(`Legacy table caption markers are not supported. Use <TableCaption>${legacyCaptionText}</TableCaption> before the table.`);
297
- }
298
-
299
- const captionText = tableCaptionText(child);
300
- if (!captionText) continue;
301
-
302
- const tableIndex = nextElementIndex(node.children, index + 1);
303
- const table = tableIndex === -1 ? null : node.children[tableIndex];
304
- if (!table || table.type !== "element" || table.tagName !== "table") {
305
- throw new Error(`<${TABLE_CAPTION_COMPONENT_NAME}> must appear immediately before a Markdown table.`);
306
- }
307
-
308
- if (!table.children?.some((item) => item.type === "element" && item.tagName === "caption")) {
309
- table.children ??= [];
310
- table.children.unshift({
311
- type: "element",
312
- tagName: "caption",
313
- properties: {},
314
- position: child.position,
315
- children: [{ type: "text", value: captionText }],
316
- });
317
- }
318
-
319
- node.children.splice(index, tableIndex - index);
320
- index -= 1;
321
- }
322
- }
323
-
324
- function legacyTableCaptionText(node) {
325
- if (node?.type !== "element" || node.tagName !== "p") return "";
326
- const match = textContent(node).match(LEGACY_TABLE_CAPTION_MARKER_RE);
327
- return match?.[1]?.trim() ?? "";
328
- }
329
-
330
- function tableCaptionText(node) {
331
- if (node?.type !== "mdxJsxFlowElement" || node.name !== TABLE_CAPTION_COMPONENT_NAME) return "";
332
- const caption = textContent(node).trim();
333
- if (!caption) throw new Error(`<${TABLE_CAPTION_COMPONENT_NAME}> requires caption text.`);
334
- return caption;
335
- }
336
-
337
- function nextElementIndex(children, start) {
338
- for (let index = start; index < children.length; index += 1) {
339
- const child = children[index];
340
- if (child?.type === "text" && !String(child.value ?? "").trim()) continue;
341
- return child?.type === "element" ? index : -1;
342
- }
343
- return -1;
344
- }
345
-
346
- function textContent(node) {
347
- if (node?.type === "text") return String(node.value ?? "");
348
- if (!Array.isArray(node?.children)) return "";
349
- return node.children.map(textContent).join("");
350
- }
351
-
352
- function wrapMdxComponents(components) {
353
- const wrapped = {};
354
- for (const [name, Component] of Object.entries(components ?? {})) {
355
- if (typeof Component !== "function") continue;
356
- wrapped[name] = function ComponentBlock(props = {}) {
357
- const blockId = props["data-openpress-block-id"];
358
- const objectId = props["data-openpress-object-id"] || (blockId ? createBlockObjectEntityId(blockId) : undefined);
359
- const rest = { ...props };
360
- delete rest["data-openpress-block-id"];
361
- delete rest["data-openpress-object-id"];
362
-
363
- if (!blockId) return React.createElement(Component, rest);
364
-
365
- return React.createElement(
366
- "div",
367
- {
368
- "data-openpress-block-id": blockId,
369
- "data-openpress-object-id": objectId,
370
- "data-openpress-component-block": name,
371
- },
372
- React.createElement(Component, rest),
373
- );
374
- };
375
- }
376
- return wrapped;
377
- }
378
-
379
- function assertNoImports(source, filePath) {
380
- if (/^\s*import\s/m.test(source)) {
381
- throw new Error(`MDX imports are not supported in OpenPress chapter prose: ${filePath}`);
382
- }
383
- }
384
-
385
- function normalizeSingleLineDisplayMath(source) {
386
- const fences = [];
387
- const withoutFences = source.replace(/(```[\s\S]*?```|~~~[\s\S]*?~~~)/g, (match) => {
388
- const token = `@@MDX_FENCE_${fences.length}@@`;
389
- fences.push(match);
390
- return token;
391
- });
392
-
393
- const normalized = withoutFences.replace(/^([ \t]*)\$\$([^\n]+?)\$\$[ \t]*$/gm, (_match, indent, math) => (
394
- `${indent}$$\n${indent}${math.trim()}\n${indent}$$`
395
- ));
396
-
397
- return normalized.replace(/@@MDX_FENCE_(\d+)@@/g, (_match, index) => fences[Number(index)] ?? "");
398
- }
399
-
400
- function blockInfo(node) {
401
- if (node?.type === "element" && PAGINABLE_TAGS.has(node.tagName)) {
402
- return { kind: "element", name: node.tagName, text: headingText(node) };
403
- }
404
- if (node?.type === "element" && node.tagName === "span" && hasClassName(node, "katex-display")) {
405
- return { kind: "element", name: "math" };
406
- }
407
- if (node?.type === "mdxJsxFlowElement" && typeof node.name === "string" && node.name) {
408
- return { kind: "component", name: node.name };
409
- }
410
- return null;
411
- }
412
-
413
- function applyListItemBlocks({
414
- node,
415
- id,
416
- blocks,
417
- filePath,
418
- chapterSlug,
419
- includeBlockIds,
420
- }) {
421
- const items = listItems(node);
422
- if (items.length === 0) {
423
- if (includeBlockIds && !includeBlockIds.has(id)) return false;
424
- setDataAttribute(node, "data-openpress-block-id", id);
425
- setDataAttribute(node, "data-openpress-object-id", createBlockObjectEntityId(id));
426
- blocks.push({
427
- id,
428
- kind: "element",
429
- name: node.tagName,
430
- text: textContent(node).trim() || undefined,
431
- filePath,
432
- chapterSlug,
433
- source: sourcePosition(node.position),
434
- });
435
- return "skip";
436
- }
437
-
438
- const itemRecords = items.map((item, index) => ({
439
- id: `${id}-i${index}`,
440
- node: item,
441
- index,
442
- }));
443
- const selected = includeBlockIds
444
- ? itemRecords.filter((item) => includeBlockIds.has(item.id))
445
- : itemRecords;
446
- if (selected.length === 0) return false;
447
-
448
- setDataAttribute(node, "data-openpress-list-id", id);
449
-
450
- // For ordered lists, continuation pages must keep numbering picking up
451
- // from the first surviving item. `start` is the 1-based number of the
452
- // first `<li>` rendered, so if the original list had `start="5"` and we
453
- // dropped the first three items, continuation starts at 5 + 3 = 8.
454
- if (node.tagName === "ol" && selected[0]?.index > 0) {
455
- const baseStart = Number(node.properties?.start ?? 1);
456
- const continuationStart = baseStart + selected[0].index;
457
- node.properties = { ...node.properties, start: continuationStart };
458
- }
459
-
460
- const selectedNodes = new Set(selected.map((item) => item.node));
461
- pruneUnselectedListItems(node, new Set(itemRecords.map((item) => item.node)), selectedNodes);
462
-
463
- for (const item of selected) {
464
- setDataAttribute(item.node, "data-openpress-block-id", item.id);
465
- setDataAttribute(item.node, "data-openpress-object-id", createBlockObjectEntityId(item.id));
466
- blocks.push({
467
- id: item.id,
468
- kind: "list-item",
469
- name: "list-item",
470
- text: textContent(item.node).trim() || undefined,
471
- filePath,
472
- chapterSlug,
473
- listId: id,
474
- listTag: node.tagName,
475
- itemIndex: item.index,
476
- source: sourcePosition(item.node.position ?? node.position),
477
- });
478
- }
479
- return "skip";
480
- }
481
-
482
- function listItems(list) {
483
- if (list?.type !== "element") return [];
484
- if (list.tagName !== "ul" && list.tagName !== "ol") return [];
485
- return (list.children ?? []).filter((child) => child?.type === "element" && child.tagName === "li");
486
- }
487
-
488
- function pruneUnselectedListItems(node, itemNodes, selectedNodes) {
489
- if (!Array.isArray(node?.children)) return;
490
- node.children = node.children.filter((child) => {
491
- if (!itemNodes.has(child)) return true;
492
- return selectedNodes.has(child);
493
- });
494
- }
495
-
496
- function tableBodyRows(table) {
497
- if (table?.type !== "element" || table.tagName !== "table") return [];
498
- const rows = [];
499
- for (const child of table.children ?? []) {
500
- if (child?.type === "element" && child.tagName === "tbody") {
501
- for (const row of child.children ?? []) {
502
- if (row?.type === "element" && row.tagName === "tr") rows.push(row);
503
- }
504
- }
505
- }
506
- if (rows.length > 0) return rows;
507
- return (table.children ?? []).filter((child) => child?.type === "element" && child.tagName === "tr");
508
- }
509
-
510
- function tableHeaderRow(table) {
511
- if (table?.type !== "element" || table.tagName !== "table") return null;
512
- for (const child of table.children ?? []) {
513
- if (child?.type !== "element" || child.tagName !== "thead") continue;
514
- return (child.children ?? []).find((row) => row?.type === "element" && row.tagName === "tr") ?? null;
515
- }
516
- return null;
517
- }
518
-
519
- function selectedFirstTableRowIndex(rows, includeBlockIds, tableId) {
520
- if (!includeBlockIds) return 0;
521
- for (let index = 0; index < rows.length; index += 1) {
522
- if (includeBlockIds.has(`${tableId}-r${index}`)) return index;
523
- }
524
- return -1;
525
- }
526
-
527
- function tableCaption(table) {
528
- if (table?.type !== "element" || table.tagName !== "table") return null;
529
- return (table.children ?? []).find((child) => child?.type === "element" && child.tagName === "caption") ?? null;
530
- }
531
-
532
- function pruneUnselectedTableRows(node, rowNodes, selectedNodes) {
533
- if (!Array.isArray(node?.children)) return;
534
- node.children = node.children.filter((child) => {
535
- if (!rowNodes.has(child)) return true;
536
- return selectedNodes.has(child);
537
- });
538
- for (const child of node.children) pruneUnselectedTableRows(child, rowNodes, selectedNodes);
539
- }
540
-
541
- function stripTableHeader(table) {
542
- if (!Array.isArray(table?.children)) return;
543
- table.children = table.children.filter((child) => {
544
- if (child?.type !== "element") return true;
545
- return child.tagName !== "thead";
546
- });
547
- }
548
-
549
- function removeTableCaption(table) {
550
- if (!Array.isArray(table?.children)) return;
551
- table.children = table.children.filter((child) => child?.type !== "element" || child.tagName !== "caption");
552
- }
553
-
554
- function headingText(node) {
555
- if (!/^h[1-6]$/.test(String(node?.tagName ?? ""))) return undefined;
556
- return textContent(node).trim() || undefined;
557
- }
558
-
559
- function normalizeBlockAttributes(value) {
560
- if (!value) return new Map();
561
- if (value instanceof Map) return value;
562
- if (typeof value === "object") return new Map(Object.entries(value));
563
- return new Map();
564
- }
565
-
566
- function hasClassName(node, className) {
567
- const raw = node?.properties?.className;
568
- if (Array.isArray(raw)) return raw.includes(className);
569
- if (typeof raw === "string") return raw.split(/\s+/).includes(className);
570
- return false;
571
- }
572
-
573
- function setDataAttribute(node, name, value) {
574
- if (node.type === "mdxJsxFlowElement") {
575
- node.attributes ??= [];
576
- node.attributes.push({
577
- type: "mdxJsxAttribute",
578
- name,
579
- value,
580
- });
581
- return;
582
- }
583
-
584
- node.properties ??= {};
585
- node.properties[name] = value;
586
- }
587
-
588
- function createObjectEntityId(kind, ...parts) {
589
- return [kind, ...parts.map((part) => encodeURIComponent(String(part)))].join(":");
590
- }
591
-
592
- function createBlockObjectEntityId(blockId) {
593
- return createObjectEntityId("mdx-block", blockId);
594
- }
595
-
596
- function visit(node, visitor) {
597
- visitor(node);
598
- if (!Array.isArray(node?.children)) return;
599
- for (const child of node.children) visit(child, visitor);
600
- }
601
-
602
- function filterTree(node, visitor) {
603
- const keep = visitor(node);
604
- if (!keep) return false;
605
- if (keep === "skip") return true;
606
- if (!Array.isArray(node?.children)) return true;
607
- node.children = node.children.filter((child) => filterTree(child, visitor));
608
- return true;
609
- }
610
-
611
- function sourcePosition(position) {
612
- if (!position?.start || !position?.end) return undefined;
613
- return {
614
- line: position.start.line,
615
- column: position.start.column,
616
- endLine: position.end.line,
617
- endColumn: position.end.column,
618
- };
619
- }
620
-
621
- function slugPart(value) {
622
- const slug = String(value)
623
- .trim()
624
- .replace(/\.[^.]+$/, "")
625
- .replace(/[^A-Za-z0-9_-]+/g, "-")
626
- .replace(/^-+|-+$/g, "")
627
- .toLowerCase();
628
- return slug || "document";
629
- }