@open-press/cli 0.8.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/README.md +33 -23
  2. package/dist/cli.js +320 -252
  3. package/package.json +9 -8
  4. package/template/core/AGENTS.md +0 -126
  5. package/template/core/CHANGELOG.md +0 -215
  6. package/template/core/README.md +0 -40
  7. package/template/core/engine/cli.mjs +0 -96
  8. package/template/core/engine/commands/_shared.mjs +0 -177
  9. package/template/core/engine/commands/deploy.mjs +0 -31
  10. package/template/core/engine/commands/dev.mjs +0 -49
  11. package/template/core/engine/commands/doctor.mjs +0 -229
  12. package/template/core/engine/commands/export.mjs +0 -8
  13. package/template/core/engine/commands/init.mjs +0 -24
  14. package/template/core/engine/commands/inspect.mjs +0 -35
  15. package/template/core/engine/commands/pdf.mjs +0 -26
  16. package/template/core/engine/commands/preview.mjs +0 -26
  17. package/template/core/engine/commands/render.mjs +0 -17
  18. package/template/core/engine/commands/replace.mjs +0 -41
  19. package/template/core/engine/commands/search.mjs +0 -33
  20. package/template/core/engine/commands/typecheck.mjs +0 -5
  21. package/template/core/engine/commands/upgrade.mjs +0 -159
  22. package/template/core/engine/commands/validate.mjs +0 -17
  23. package/template/core/engine/document-export.mjs +0 -15
  24. package/template/core/engine/init.mjs +0 -90
  25. package/template/core/engine/output/chrome-pdf.d.mts +0 -34
  26. package/template/core/engine/output/chrome-pdf.mjs +0 -358
  27. package/template/core/engine/output/deploy-sync.mjs +0 -15
  28. package/template/core/engine/output/fonts.mjs +0 -62
  29. package/template/core/engine/output/katex-assets.mjs +0 -45
  30. package/template/core/engine/output/page-block.mjs +0 -30
  31. package/template/core/engine/output/pdf-media.mjs +0 -45
  32. package/template/core/engine/output/public-assets.mjs +0 -19
  33. package/template/core/engine/output/static-server.mjs +0 -532
  34. package/template/core/engine/react/caption-numbering.mjs +0 -73
  35. package/template/core/engine/react/comment-endpoint.d.mts +0 -11
  36. package/template/core/engine/react/comment-endpoint.mjs +0 -102
  37. package/template/core/engine/react/comment-marker.mjs +0 -374
  38. package/template/core/engine/react/document-entry.mjs +0 -324
  39. package/template/core/engine/react/document-export.mjs +0 -373
  40. package/template/core/engine/react/http-json.mjs +0 -24
  41. package/template/core/engine/react/mdx-compile.mjs +0 -599
  42. package/template/core/engine/react/measurement-css.mjs +0 -136
  43. package/template/core/engine/react/object-entities.mjs +0 -119
  44. package/template/core/engine/react/pagination/allocator.mjs +0 -122
  45. package/template/core/engine/react/pagination/regions.mjs +0 -81
  46. package/template/core/engine/react/pagination-constants.mjs +0 -3
  47. package/template/core/engine/react/pagination.mjs +0 -9
  48. package/template/core/engine/react/pipeline/allocate.mjs +0 -251
  49. package/template/core/engine/react/pipeline/final-render.mjs +0 -94
  50. package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -302
  51. package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
  52. package/template/core/engine/react/project-asset-endpoint.d.mts +0 -10
  53. package/template/core/engine/react/project-asset-endpoint.mjs +0 -361
  54. package/template/core/engine/react/section-css.mjs +0 -56
  55. package/template/core/engine/react/source-edit-endpoint.d.mts +0 -10
  56. package/template/core/engine/react/source-edit-endpoint.mjs +0 -75
  57. package/template/core/engine/react/sources/heading-numbering.mjs +0 -132
  58. package/template/core/engine/react/sources/mdx-resolver.mjs +0 -439
  59. package/template/core/engine/react/style-discovery.mjs +0 -142
  60. package/template/core/engine/runtime/config.d.mts +0 -40
  61. package/template/core/engine/runtime/config.mjs +0 -175
  62. package/template/core/engine/runtime/file-utils.mjs +0 -106
  63. package/template/core/engine/runtime/file-walk.mjs +0 -22
  64. package/template/core/engine/runtime/inspection.mjs +0 -328
  65. package/template/core/engine/runtime/issue-report.mjs +0 -44
  66. package/template/core/engine/runtime/path-utils.mjs +0 -20
  67. package/template/core/engine/runtime/source-text-tools.d.mts +0 -102
  68. package/template/core/engine/runtime/source-text-tools.mjs +0 -832
  69. package/template/core/engine/runtime/source-workspace.mjs +0 -159
  70. package/template/core/engine/runtime/validation.mjs +0 -174
  71. package/template/core/index.html +0 -13
  72. package/template/core/openpress.config.mjs +0 -12
  73. package/template/core/package.json +0 -91
  74. package/template/core/src/main.tsx +0 -16
  75. package/template/core/src/openpress/app/OpenPressApp.tsx +0 -140
  76. package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -94
  77. package/template/core/src/openpress/app/index.ts +0 -2
  78. package/template/core/src/openpress/core/Frame.tsx +0 -78
  79. package/template/core/src/openpress/core/FrameContext.tsx +0 -24
  80. package/template/core/src/openpress/core/MdxArea.tsx +0 -34
  81. package/template/core/src/openpress/core/Press.tsx +0 -34
  82. package/template/core/src/openpress/core/cn.ts +0 -4
  83. package/template/core/src/openpress/core/index.tsx +0 -40
  84. package/template/core/src/openpress/core/primitives.tsx +0 -44
  85. package/template/core/src/openpress/core/types.ts +0 -191
  86. package/template/core/src/openpress/core/useSource.ts +0 -28
  87. package/template/core/src/openpress/document-model/anchorMapModel.ts +0 -27
  88. package/template/core/src/openpress/document-model/documentIndexes.ts +0 -329
  89. package/template/core/src/openpress/document-model/documentTypes.ts +0 -138
  90. package/template/core/src/openpress/document-model/index.ts +0 -6
  91. package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -51
  92. package/template/core/src/openpress/document-model/projectIdentityModel.ts +0 -15
  93. package/template/core/src/openpress/document-model/reactDocumentMetadataModel.ts +0 -27
  94. package/template/core/src/openpress/manuscript/index.tsx +0 -238
  95. package/template/core/src/openpress/mdx/index.ts +0 -88
  96. package/template/core/src/openpress/numbering/index.ts +0 -294
  97. package/template/core/src/openpress/reader/PublicReaderPage.tsx +0 -267
  98. package/template/core/src/openpress/reader/ReaderNavigationPanel.tsx +0 -123
  99. package/template/core/src/openpress/reader/index.ts +0 -10
  100. package/template/core/src/openpress/reader/pageViewportScaleModel.ts +0 -73
  101. package/template/core/src/openpress/reader/readerPageRegistry.ts +0 -41
  102. package/template/core/src/openpress/reader/readerPageRoute.ts +0 -21
  103. package/template/core/src/openpress/reader/readerScroll.ts +0 -92
  104. package/template/core/src/openpress/reader/readerStateModel.ts +0 -15
  105. package/template/core/src/openpress/reader/readerTypes.ts +0 -4
  106. package/template/core/src/openpress/reader/usePageViewportScale.ts +0 -119
  107. package/template/core/src/openpress/reader/usePanelState.ts +0 -56
  108. package/template/core/src/openpress/reader/useReaderHashSync.ts +0 -61
  109. package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +0 -48
  110. package/template/core/src/openpress/reader/useReaderRuntime.ts +0 -146
  111. package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +0 -64
  112. package/template/core/src/openpress/shared/Panel.tsx +0 -77
  113. package/template/core/src/openpress/shared/frameScheduler.ts +0 -32
  114. package/template/core/src/openpress/shared/index.ts +0 -4
  115. package/template/core/src/openpress/shared/numberUtils.ts +0 -3
  116. package/template/core/src/openpress/shared/runtimeMode.ts +0 -11
  117. package/template/core/src/openpress/workbench/Workbench.tsx +0 -407
  118. package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
  119. package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +0 -182
  120. package/template/core/src/openpress/workbench/actions/SearchControl.tsx +0 -345
  121. package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +0 -112
  122. package/template/core/src/openpress/workbench/actions/index.ts +0 -5
  123. package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +0 -136
  124. package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +0 -72
  125. package/template/core/src/openpress/workbench/dialog/index.ts +0 -1
  126. package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +0 -127
  127. package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +0 -207
  128. package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +0 -9
  129. package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +0 -34
  130. package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +0 -525
  131. package/template/core/src/openpress/workbench/document/index.ts +0 -10
  132. package/template/core/src/openpress/workbench/index.ts +0 -2
  133. package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +0 -459
  134. package/template/core/src/openpress/workbench/inspector/index.ts +0 -5
  135. package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +0 -125
  136. package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +0 -160
  137. package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +0 -408
  138. package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +0 -248
  139. package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +0 -41
  140. package/template/core/src/openpress/workbench/mentions/index.ts +0 -2
  141. package/template/core/src/openpress/workbench/mentions/useComposerMentions.ts +0 -185
  142. package/template/core/src/openpress/workbench/panels/Panel.tsx +0 -1
  143. package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +0 -76
  144. package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +0 -29
  145. package/template/core/src/openpress/workbench/panels/index.ts +0 -3
  146. package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +0 -523
  147. package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +0 -35
  148. package/template/core/src/openpress/workbench/project/index.ts +0 -2
  149. package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +0 -11
  150. package/template/core/src/openpress/workbench/project/projectSourceModel.ts +0 -24
  151. package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +0 -167
  152. package/template/core/src/openpress/workbench/shell/index.ts +0 -1
  153. package/template/core/src/openpress/workbench/workbenchFormatters.ts +0 -120
  154. package/template/core/src/openpress/workbench/workbenchTypes.ts +0 -35
  155. package/template/core/src/styles/openpress/app-shell.css +0 -251
  156. package/template/core/src/styles/openpress/media-workspace.css +0 -230
  157. package/template/core/src/styles/openpress/print-route.css +0 -184
  158. package/template/core/src/styles/openpress/project-preview-panel.css +0 -924
  159. package/template/core/src/styles/openpress/public-viewer.css +0 -688
  160. package/template/core/src/styles/openpress/reader-runtime.css +0 -980
  161. package/template/core/src/styles/openpress/responsive.css +0 -245
  162. package/template/core/src/styles/openpress/workbench-panels.css +0 -594
  163. package/template/core/src/styles/openpress/workbench.css +0 -1255
  164. package/template/core/src/styles/openpress.css +0 -14
  165. package/template/core/src/vite-env.d.ts +0 -9
  166. package/template/core/tsconfig.json +0 -40
  167. package/template/core/vite.config.ts +0 -584
  168. package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +0 -35
  169. package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +0 -50
  170. package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +0 -47
  171. package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +0 -26
  172. package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +0 -32
  173. package/template/packs/academic-paper/document/components/ChapterOpenerVisual/index.tsx +0 -76
  174. package/template/packs/academic-paper/document/components/Page.tsx +0 -60
  175. package/template/packs/academic-paper/document/components/TokenSwatchGrid/index.tsx +0 -46
  176. package/template/packs/academic-paper/document/components/TokenSwatchGrid/style.css +0 -63
  177. package/template/packs/academic-paper/document/components/TypeSpecimen/index.tsx +0 -38
  178. package/template/packs/academic-paper/document/components/TypeSpecimen/style.css +0 -111
  179. package/template/packs/academic-paper/document/design.md +0 -279
  180. package/template/packs/academic-paper/document/index.tsx +0 -123
  181. package/template/packs/academic-paper/document/media/README.md +0 -13
  182. package/template/packs/academic-paper/document/media/figure-placeholder.svg +0 -9
  183. package/template/packs/academic-paper/document/openpress.config.mjs +0 -26
  184. package/template/packs/academic-paper/document/theme/README.md +0 -11
  185. package/template/packs/academic-paper/document/theme/base/page-contract.css +0 -522
  186. package/template/packs/academic-paper/document/theme/base/print.css +0 -93
  187. package/template/packs/academic-paper/document/theme/base/typography.css +0 -333
  188. package/template/packs/academic-paper/document/theme/fonts.css +0 -3
  189. package/template/packs/academic-paper/document/theme/page-surfaces/back-cover.css +0 -43
  190. package/template/packs/academic-paper/document/theme/page-surfaces/chapter-opener.css +0 -205
  191. package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +0 -294
  192. package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +0 -149
  193. package/template/packs/academic-paper/document/theme/patterns/_chart-frame.css +0 -49
  194. package/template/packs/academic-paper/document/theme/patterns/figure-grid.css +0 -68
  195. package/template/packs/academic-paper/document/theme/patterns/table-utilities.css +0 -66
  196. package/template/packs/academic-paper/document/theme/shell/reader-controls.css +0 -761
  197. package/template/packs/academic-paper/document/theme/tokens.css +0 -80
  198. package/template/packs/academic-paper/openpress.config.mjs +0 -5
  199. package/template/packs/claude-document/document/chapters/01-document-shape/content/01-document-shape.mdx +0 -51
  200. package/template/packs/claude-document/document/chapters/02-review-loop/content/01-review-loop.mdx +0 -31
  201. package/template/packs/claude-document/document/components/ChapterOpenerVisual.tsx +0 -96
  202. package/template/packs/claude-document/document/components/Page.tsx +0 -37
  203. package/template/packs/claude-document/document/design.md +0 -142
  204. package/template/packs/claude-document/document/index.tsx +0 -94
  205. package/template/packs/claude-document/document/media/README.md +0 -13
  206. package/template/packs/claude-document/document/openpress.config.mjs +0 -26
  207. package/template/packs/claude-document/document/theme/README.md +0 -15
  208. package/template/packs/claude-document/document/theme/base/page-contract.css +0 -525
  209. package/template/packs/claude-document/document/theme/base/print.css +0 -93
  210. package/template/packs/claude-document/document/theme/base/typography.css +0 -612
  211. package/template/packs/claude-document/document/theme/fonts.css +0 -4
  212. package/template/packs/claude-document/document/theme/page-surfaces/back-cover.css +0 -72
  213. package/template/packs/claude-document/document/theme/page-surfaces/chapter-opener.css +0 -236
  214. package/template/packs/claude-document/document/theme/page-surfaces/cover.css +0 -309
  215. package/template/packs/claude-document/document/theme/page-surfaces/toc.css +0 -225
  216. package/template/packs/claude-document/document/theme/patterns/_chart-frame.css +0 -53
  217. package/template/packs/claude-document/document/theme/patterns/figure-grid.css +0 -68
  218. package/template/packs/claude-document/document/theme/patterns/table-utilities.css +0 -66
  219. package/template/packs/claude-document/document/theme/shell/reader-controls.css +0 -789
  220. package/template/packs/claude-document/document/theme/tokens.css +0 -89
  221. package/template/packs/claude-document/openpress.config.mjs +0 -5
  222. package/template/packs/editorial-monograph/document/chapters/01-product-and-use-cases/content/01-product-and-use-cases.mdx +0 -31
  223. package/template/packs/editorial-monograph/document/chapters/02-workflow/content/01-workflow.mdx +0 -89
  224. package/template/packs/editorial-monograph/document/chapters/03-agent-skills-contributors/content/01-agent-skills-contributors.mdx +0 -51
  225. package/template/packs/editorial-monograph/document/chapters/04-validation-deploy/content/01-validation-deploy.mdx +0 -39
  226. package/template/packs/editorial-monograph/document/components/ChapterOpenerVisual/index.tsx +0 -76
  227. package/template/packs/editorial-monograph/document/components/Page.tsx +0 -37
  228. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/index.tsx +0 -46
  229. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/style.css +0 -63
  230. package/template/packs/editorial-monograph/document/components/TypeSpecimen/index.tsx +0 -38
  231. package/template/packs/editorial-monograph/document/components/TypeSpecimen/style.css +0 -111
  232. package/template/packs/editorial-monograph/document/design.md +0 -279
  233. package/template/packs/editorial-monograph/document/index.tsx +0 -97
  234. package/template/packs/editorial-monograph/document/media/README.md +0 -13
  235. package/template/packs/editorial-monograph/document/openpress.config.mjs +0 -26
  236. package/template/packs/editorial-monograph/document/theme/README.md +0 -11
  237. package/template/packs/editorial-monograph/document/theme/base/page-contract.css +0 -505
  238. package/template/packs/editorial-monograph/document/theme/base/print.css +0 -93
  239. package/template/packs/editorial-monograph/document/theme/base/typography.css +0 -336
  240. package/template/packs/editorial-monograph/document/theme/fonts.css +0 -3
  241. package/template/packs/editorial-monograph/document/theme/page-surfaces/back-cover.css +0 -43
  242. package/template/packs/editorial-monograph/document/theme/page-surfaces/chapter-opener.css +0 -205
  243. package/template/packs/editorial-monograph/document/theme/page-surfaces/cover.css +0 -147
  244. package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +0 -149
  245. package/template/packs/editorial-monograph/document/theme/patterns/_chart-frame.css +0 -49
  246. package/template/packs/editorial-monograph/document/theme/patterns/figure-grid.css +0 -68
  247. package/template/packs/editorial-monograph/document/theme/patterns/table-utilities.css +0 -66
  248. package/template/packs/editorial-monograph/document/theme/shell/reader-controls.css +0 -761
  249. package/template/packs/editorial-monograph/document/theme/tokens.css +0 -80
  250. package/template/packs/editorial-monograph/openpress.config.mjs +0 -5
@@ -1,135 +0,0 @@
1
- // Layer 2 — Press Tree Expansion.
2
- //
3
- // SSR-renders the user's Press tree with a PressContext provider that
4
- // supplies resolved sources and (optionally) allocation hints. Output:
5
- // - rendered HTML (used by Layer 3 for measurement and Layer 5 for final)
6
- // - extracted frame metadata (frameKey, role, chrome, sequence position)
7
- // - per-frame MdxArea slots (chainId, sequence index within frame)
8
- //
9
- // Frames are discovered by parsing the rendered HTML for elements with the
10
- // `data-openpress-frame-key` attribute. This works because <Frame> renders
11
- // to a deterministic `<section>` with that attribute set.
12
-
13
- import React from "react";
14
- import { renderToStaticMarkup } from "react-dom/server";
15
-
16
- const FRAME_OPEN_RE = /<section\b([^>]*)\bdata-openpress-frame-key="([^"]+)"([^>]*)>/g;
17
- const ATTR_RE = (name) => new RegExp(`\\b${name}="([^"]*)"`);
18
-
19
- /**
20
- * Render the Press tree and extract frame structure.
21
- *
22
- * @param {object} opts
23
- * @param {React.ComponentType} opts.Press The user's default-exported Press component.
24
- * @param {object} opts.PressContext The PressContext from @open-press/core.
25
- * @param {Record<string, object>} opts.sources Resolved sources keyed by sourceId.
26
- * @param {object|null} opts.hints Allocation hints (or null on first pass).
27
- * @param {object|null} opts.allocation FrameAllocation map (or null for measurement).
28
- * @returns {{ html: string, frames: Array<FrameInstance> }}
29
- */
30
- export function expandPressTree({ Press: UserPress, PressContext, sources, hints = null, allocation = null, toc = null }) {
31
- const html = renderToStaticMarkup(
32
- React.createElement(
33
- PressContext.Provider,
34
- { value: { sources, allocation, hints, toc } },
35
- React.createElement(UserPress),
36
- ),
37
- );
38
-
39
- const frames = extractFrames(html);
40
- enforceUniqueFrameKeys(frames);
41
- return { html, frames };
42
- }
43
-
44
- function extractFrames(html) {
45
- const frames = [];
46
- let match;
47
- FRAME_OPEN_RE.lastIndex = 0;
48
- while ((match = FRAME_OPEN_RE.exec(html)) !== null) {
49
- const attrsBefore = match[1] ?? "";
50
- const frameKey = match[2];
51
- const attrsAfter = match[3] ?? "";
52
- const allAttrs = `${attrsBefore} ${attrsAfter}`;
53
- const role = pickAttr(allAttrs, "data-frame-role") || undefined;
54
- const chromeRaw = pickAttr(allAttrs, "data-frame-chrome");
55
- const chrome = chromeRaw === "false" ? false : true;
56
- const openIndex = match.index;
57
- const sectionHtml = sliceSection(html, openIndex);
58
- const mdxAreas = extractMdxAreas(sectionHtml);
59
- frames.push({
60
- frameKey,
61
- role,
62
- chrome,
63
- mdxAreas,
64
- htmlStart: openIndex,
65
- htmlEnd: openIndex + sectionHtml.length,
66
- html: sectionHtml,
67
- });
68
- }
69
- return frames;
70
- }
71
-
72
- const MDX_AREA_RE = /<([a-z][a-z0-9-]*)\b([^>]*)\bdata-openpress-mdx-area="true"([^>]*)>/gi;
73
-
74
- function extractMdxAreas(sectionHtml) {
75
- const areas = [];
76
- let match;
77
- MDX_AREA_RE.lastIndex = 0;
78
- while ((match = MDX_AREA_RE.exec(sectionHtml)) !== null) {
79
- const attrs = `${match[2] ?? ""} ${match[3] ?? ""}`;
80
- const chainId = pickAttr(attrs, "data-openpress-mdx-area-chain");
81
- const overflow = pickAttr(attrs, "data-openpress-mdx-area-overflow") || "extend";
82
- if (!chainId) continue;
83
- const indexInFrame = areas.filter((a) => a.chainId === chainId).length;
84
- areas.push({ chainId, overflow, indexInFrame, indexAcrossFrame: areas.length });
85
- }
86
- return areas;
87
- }
88
-
89
- function pickAttr(attrs, name) {
90
- const match = ATTR_RE(name).exec(attrs);
91
- return match ? match[1] : "";
92
- }
93
-
94
- // Find the end of a <section> opening at `start`, returning the full
95
- // `<section ...>...</section>` substring. Handles nested <section> elements
96
- // by depth-counting.
97
- function sliceSection(html, start) {
98
- const sectionOpen = /<section\b[^>]*>/g;
99
- const sectionClose = /<\/section\s*>/g;
100
- sectionOpen.lastIndex = start + 1;
101
- sectionClose.lastIndex = start + 1;
102
- let depth = 1;
103
- while (depth > 0) {
104
- const nextOpen = sectionOpen.exec(html);
105
- const nextClose = sectionClose.exec(html);
106
- if (!nextClose) {
107
- throw new Error(`Unterminated <section> in Press tree HTML near offset ${start}`);
108
- }
109
- if (nextOpen && nextOpen.index < nextClose.index) {
110
- depth += 1;
111
- sectionClose.lastIndex = nextOpen.index + 1;
112
- continue;
113
- }
114
- depth -= 1;
115
- if (depth === 0) {
116
- return html.slice(start, nextClose.index + nextClose[0].length);
117
- }
118
- sectionOpen.lastIndex = nextClose.index + 1;
119
- }
120
- throw new Error(`Section depth balance bug at offset ${start}`);
121
- }
122
-
123
- function enforceUniqueFrameKeys(frames) {
124
- const seen = new Map();
125
- for (const frame of frames) {
126
- if (seen.has(frame.frameKey)) {
127
- const prior = seen.get(frame.frameKey);
128
- throw new Error(
129
- `Duplicate frameKey "${frame.frameKey}" found in Press tree. ` +
130
- `First seen with role "${prior.role ?? "?"}", second with role "${frame.role ?? "?"}".`,
131
- );
132
- }
133
- seen.set(frame.frameKey, frame);
134
- }
135
- }
@@ -1,10 +0,0 @@
1
- import type { IncomingMessage, ServerResponse } from "node:http";
2
-
3
- export function handleProjectAssetRequest(
4
- req: IncomingMessage,
5
- res: ServerResponse,
6
- options?: {
7
- root?: string;
8
- timestamp?: string;
9
- },
10
- ): Promise<void>;
@@ -1,361 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { loadConfig } from "../runtime/config.mjs";
4
- import { collectSourceTextFiles } from "../runtime/source-text-tools.mjs";
5
- import { insertCommentMarker } from "./comment-marker.mjs";
6
- import { readJsonBody, writeJson } from "./http-json.mjs";
7
-
8
- export async function handleProjectAssetRequest(req, res, {
9
- root = ".",
10
- timestamp = undefined,
11
- } = {}) {
12
- if (req.method !== "POST") {
13
- writeJson(res, 405, { ok: false, message: "OpenPress project asset endpoint requires POST." });
14
- return;
15
- }
16
-
17
- try {
18
- const body = await readJsonBody(req, { bodyLabel: "Project asset request" });
19
- const config = await loadConfig(root);
20
- const action = stringValue(body?.action);
21
- const kind = stringValue(body?.kind);
22
- const name = stringValue(body?.name);
23
-
24
- if (kind !== "media" && kind !== "component") {
25
- throw new Error("Project asset kind must be `media` or `component`.");
26
- }
27
- if (!name) throw new Error("Project asset action requires a name.");
28
-
29
- if (action === "rename") {
30
- const result = await renameProjectAsset({
31
- config,
32
- kind,
33
- name,
34
- nextName: body?.nextName,
35
- });
36
- writeJson(res, 200, { ok: true, ...result });
37
- return;
38
- }
39
-
40
- if (action === "delete") {
41
- const result = await deleteProjectAsset({ config, kind, name });
42
- const status = result.needsReferenceCleanup ? 409 : 200;
43
- writeJson(res, status, { ok: !result.needsReferenceCleanup, ...result });
44
- return;
45
- }
46
-
47
- if (action === "comment") {
48
- const result = await createProjectAssetComment({
49
- config,
50
- kind,
51
- name,
52
- note: body?.note,
53
- commentTarget: body?.commentTarget,
54
- currentSource: body?.currentSource,
55
- objectEntity: body?.objectEntity,
56
- timestamp,
57
- });
58
- writeJson(res, 200, { ok: true, ...result });
59
- return;
60
- }
61
-
62
- throw new Error("Project asset action must be `rename`, `delete`, or `comment`.");
63
- } catch (error) {
64
- writeJson(res, 400, {
65
- ok: false,
66
- message: error instanceof Error ? error.message : String(error),
67
- });
68
- }
69
- }
70
-
71
- async function renameProjectAsset({ config, kind, name, nextName }) {
72
- const normalizedCurrentName = normalizeAssetName(kind, name);
73
- const normalizedNextName = normalizeAssetName(kind, stringValue(nextName), normalizedCurrentName);
74
- if (!normalizedNextName || normalizedNextName === normalizedCurrentName) {
75
- throw new Error("Rename requires a different valid name.");
76
- }
77
-
78
- const currentPath = resolveAssetPath(config, kind, normalizedCurrentName);
79
- const nextPath = resolveAssetPath(config, kind, normalizedNextName);
80
- await assertPathExists(currentPath, `${kind} asset not found: ${normalizedCurrentName}`);
81
- if (await fileExists(nextPath)) throw new Error(`${kind} asset already exists: ${normalizedNextName}`);
82
-
83
- await fs.rename(currentPath, nextPath);
84
- const referenceResult = await replaceProjectAssetReferences({
85
- config,
86
- kind,
87
- from: normalizedCurrentName,
88
- to: normalizedNextName,
89
- });
90
-
91
- return {
92
- action: "rename",
93
- kind,
94
- name: normalizedCurrentName,
95
- nextName: normalizedNextName,
96
- referenceCount: referenceResult.referenceCount,
97
- fileCount: referenceResult.fileCount,
98
- };
99
- }
100
-
101
- async function deleteProjectAsset({ config, kind, name }) {
102
- const normalizedName = normalizeAssetName(kind, name);
103
- const references = await findProjectAssetReferences({ config, kind, name: normalizedName });
104
- if (references.length > 0) {
105
- return {
106
- action: "delete",
107
- kind,
108
- name: normalizedName,
109
- needsReferenceCleanup: true,
110
- referenceCount: references.length,
111
- references: references.slice(0, 12),
112
- message: `Cannot delete ${kind} asset while ${references.length} reference(s) still exist.`,
113
- };
114
- }
115
-
116
- const targetPath = resolveAssetPath(config, kind, normalizedName);
117
- await assertPathExists(targetPath, `${kind} asset not found: ${normalizedName}`);
118
- await fs.rm(targetPath, { recursive: true, force: true });
119
-
120
- return {
121
- action: "delete",
122
- kind,
123
- name: normalizedName,
124
- needsReferenceCleanup: false,
125
- referenceCount: 0,
126
- };
127
- }
128
-
129
- async function createProjectAssetComment({
130
- config,
131
- kind,
132
- name,
133
- note,
134
- commentTarget,
135
- currentSource,
136
- objectEntity,
137
- timestamp,
138
- }) {
139
- const normalizedName = normalizeAssetName(kind, name);
140
- const noteText = stringValue(note);
141
- if (!noteText) throw new Error("Project asset comment requires a note.");
142
-
143
- const target = await resolveCommentTarget({
144
- config,
145
- kind,
146
- name: normalizedName,
147
- commentTarget: stringValue(commentTarget),
148
- currentSource,
149
- });
150
- const objectHint = stringValue(objectEntity?.id) ? ` object=${stringValue(objectEntity.id)}` : "";
151
-
152
- const result = await insertCommentMarker({
153
- root: config.root,
154
- path: target.path,
155
- source: { line: target.line, column: 1 },
156
- note: `${assetLabel(kind, normalizedName)}:${noteText}`,
157
- hint: `openpress-project-asset kind=${kind} action=comment target=${target.reason} asset=${normalizedName}${objectHint}`,
158
- timestamp,
159
- });
160
-
161
- return {
162
- action: "comment",
163
- kind,
164
- name: normalizedName,
165
- comment: {
166
- id: result.id,
167
- timestamp: result.timestamp,
168
- path: result.path,
169
- line: result.line,
170
- },
171
- };
172
- }
173
-
174
- async function resolveCommentTarget({ config, kind, name, commentTarget, currentSource }) {
175
- if (commentTarget === "current-page") {
176
- const currentPath = stringValue(currentSource?.path);
177
- if (currentPath) {
178
- return {
179
- path: currentPath,
180
- line: normalizePositiveInteger(currentSource?.line) ?? 1,
181
- reason: "current-page",
182
- };
183
- }
184
- }
185
-
186
- const references = await findProjectAssetReferences({ config, kind, name });
187
- const preferred = references.find((reference) => {
188
- if (kind === "component") return reference.preview.includes("data-openpress-component");
189
- return reference.path.includes("/content/") || reference.path.endsWith(".mdx");
190
- }) ?? references[0];
191
- if (!preferred) {
192
- throw new Error(`No editable reference found for ${kind} asset: ${name}`);
193
- }
194
- return {
195
- path: preferred.path,
196
- line: preferred.line,
197
- reason: "asset-reference",
198
- };
199
- }
200
-
201
- async function replaceProjectAssetReferences({ config, kind, from, to }) {
202
- const replacements = replacementPairs(kind, from, to);
203
- const files = await collectSourceTextFiles(config, { scope: "all" });
204
- let referenceCount = 0;
205
- let fileCount = 0;
206
-
207
- for (const file of files) {
208
- let text = file.text;
209
- let changed = false;
210
- for (const [fromText, toText] of replacements) {
211
- if (!fromText || fromText === toText || !text.includes(fromText)) continue;
212
- const count = text.split(fromText).length - 1;
213
- text = text.split(fromText).join(toText);
214
- referenceCount += count;
215
- changed = true;
216
- }
217
- if (!changed) continue;
218
- fileCount += 1;
219
- await fs.writeFile(file.absolutePath, text, "utf8");
220
- }
221
-
222
- return { referenceCount, fileCount };
223
- }
224
-
225
- async function findProjectAssetReferences({ config, kind, name }) {
226
- const tokens = referenceTokens(kind, name);
227
- const files = await collectSourceTextFiles(config, { scope: "all" });
228
- const references = [];
229
-
230
- for (const file of files) {
231
- const lines = file.text.split(/\r?\n/);
232
- lines.forEach((line, index) => {
233
- if (!tokens.some((token) => token && line.includes(token))) return;
234
- references.push({
235
- path: file.relativePath,
236
- line: index + 1,
237
- preview: line.trim().slice(0, 180),
238
- });
239
- });
240
- }
241
-
242
- return references;
243
- }
244
-
245
- function resolveAssetPath(config, kind, name) {
246
- const root = kind === "media" ? config.paths.mediaDir : config.paths.componentsDir;
247
- const target = path.resolve(root, name);
248
- const resolvedRoot = path.resolve(root);
249
- if (!target.startsWith(`${resolvedRoot}${path.sep}`) && target !== resolvedRoot) {
250
- throw new Error(`Project asset path escapes ${kind} directory: ${name}`);
251
- }
252
- return target;
253
- }
254
-
255
- function normalizeAssetName(kind, value, currentName = "") {
256
- if (kind === "media") return sanitizeMediaFileName(value, currentName);
257
- return sanitizeComponentName(value);
258
- }
259
-
260
- function sanitizeMediaFileName(value, currentName = "") {
261
- const rawName = stringValue(value);
262
- if (!rawName) return "";
263
- const currentExt = path.extname(currentName);
264
- const suppliedExt = path.extname(rawName);
265
- const baseName = path.basename(suppliedExt ? rawName : `${rawName}${currentExt}`).trim();
266
- if (!baseName) return "";
267
- const ext = path.extname(baseName);
268
- const stem = path.basename(baseName, ext)
269
- .replace(/[\\/:*?"<>|#%{}^~[\]`]/g, "-")
270
- .replace(/\s+/g, "-")
271
- .replace(/-+/g, "-")
272
- .replace(/^-|-$/g, "");
273
- if (!stem || !ext || !isAllowedMediaFile(`${stem}${ext}`)) return "";
274
- return `${stem}${ext.toLowerCase()}`;
275
- }
276
-
277
- function sanitizeComponentName(value) {
278
- const raw = stringValue(value).replaceAll("\\", "/").split("/").pop() ?? "";
279
- const normalized = raw
280
- .trim()
281
- .replace(/\s+/g, "-")
282
- .replace(/[^a-zA-Z0-9_-]/g, "-")
283
- .replace(/-+/g, "-")
284
- .replace(/^-|-$/g, "");
285
- if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(normalized)) return "";
286
- return normalized;
287
- }
288
-
289
- function isAllowedMediaFile(fileName) {
290
- return /\.(png|jpe?g|gif|svg|webp)$/i.test(fileName);
291
- }
292
-
293
- function replacementPairs(kind, from, to) {
294
- if (kind === "media") {
295
- return uniquePairs([
296
- [from, to],
297
- [encodeURIComponent(from), encodeURIComponent(to)],
298
- [`@media/${from}`, `@media/${to}`],
299
- ]);
300
- }
301
- return uniquePairs([
302
- [from, to],
303
- [`@component/${from}`, `@component/${to}`],
304
- ]);
305
- }
306
-
307
- function referenceTokens(kind, name) {
308
- if (kind === "media") {
309
- return uniqueValues([
310
- name,
311
- encodeURIComponent(name),
312
- `@media/${name}`,
313
- ]);
314
- }
315
- return uniqueValues([
316
- name,
317
- `@component/${name}`,
318
- `data-openpress-component="${name}"`,
319
- `data-openpress-component='${name}'`,
320
- ]);
321
- }
322
-
323
- function uniquePairs(pairs) {
324
- const seen = new Set();
325
- return pairs.filter(([from, to]) => {
326
- const key = `${from}\0${to}`;
327
- if (seen.has(key)) return false;
328
- seen.add(key);
329
- return true;
330
- });
331
- }
332
-
333
- function uniqueValues(values) {
334
- return Array.from(new Set(values.filter(Boolean)));
335
- }
336
-
337
- function assetLabel(kind, name) {
338
- return kind === "media" ? `Media ${name}` : `Component ${name}`;
339
- }
340
-
341
- function stringValue(value) {
342
- return typeof value === "string" ? value.trim() : "";
343
- }
344
-
345
- function normalizePositiveInteger(value) {
346
- const number = Number(value);
347
- return Number.isInteger(number) && number > 0 ? number : null;
348
- }
349
-
350
- async function assertPathExists(filePath, message) {
351
- if (!(await fileExists(filePath))) throw new Error(message);
352
- }
353
-
354
- async function fileExists(filePath) {
355
- try {
356
- await fs.access(filePath);
357
- return true;
358
- } catch {
359
- return false;
360
- }
361
- }
@@ -1,56 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import postcss from "postcss";
3
-
4
- const UNSCOPED_RULE_PARENTS = new Set(["keyframes", "-webkit-keyframes", "page"]);
5
-
6
- // Section-scoped CSS. Style files under `chapters/<slug>/styles/*.css` (in
7
- // the section-folders preset) are scoped to `[data-section-id="<slug>"]`.
8
- // Workspaces using other source presets do not get section-scoped CSS in v1.
9
- export async function buildSectionScopedCss(workspace) {
10
- const parts = [];
11
- for (const section of workspace.sections ?? workspace.chapters ?? []) {
12
- for (const styleFile of section.styleFiles ?? []) {
13
- const source = await fs.readFile(styleFile.absolutePath, "utf8");
14
- const scoped = await scopeSectionCss(source, {
15
- sectionSlug: section.slug,
16
- from: styleFile.absolutePath,
17
- });
18
- if (!scoped.trim()) continue;
19
- parts.push(`/* === ${styleFile.documentPath} === */`);
20
- parts.push(scoped.trimEnd());
21
- parts.push("");
22
- }
23
- }
24
- return parts.join("\n");
25
- }
26
-
27
- export async function scopeSectionCss(source, { sectionSlug, from = undefined } = {}) {
28
- if (typeof source !== "string") throw new Error("scopeSectionCss requires a CSS source string.");
29
- const slug = cssAttributeValue(sectionSlug);
30
- const scope = `[data-section-id="${slug}"]`;
31
- const root = postcss.parse(source, { from });
32
-
33
- root.walkRules((rule) => {
34
- if (isInsideUnscopedAtRule(rule)) return;
35
- rule.selector = `${scope} :where(${rule.selector})`;
36
- });
37
-
38
- return root.toString();
39
- }
40
-
41
- function isInsideUnscopedAtRule(node) {
42
- let current = node.parent;
43
- while (current) {
44
- if (current.type === "atrule" && UNSCOPED_RULE_PARENTS.has(current.name.toLowerCase())) {
45
- return true;
46
- }
47
- current = current.parent;
48
- }
49
- return false;
50
- }
51
-
52
- function cssAttributeValue(value) {
53
- return String(value ?? "")
54
- .replaceAll("\\", "\\\\")
55
- .replaceAll('"', '\\"');
56
- }
@@ -1,10 +0,0 @@
1
- import type { IncomingMessage, ServerResponse } from "node:http";
2
-
3
- export function handleSourceEditRequest(
4
- req: IncomingMessage,
5
- res: ServerResponse,
6
- options?: {
7
- root?: string;
8
- refreshDocument?: boolean;
9
- },
10
- ): Promise<void>;
@@ -1,75 +0,0 @@
1
- import { loadConfig } from "../runtime/config.mjs";
2
- import { applySourceBlockTextEdit, readSourceBlockText } from "../runtime/source-text-tools.mjs";
3
- import { exportReactDocument } from "./document-export.mjs";
4
- import { readJsonBody, writeJson } from "./http-json.mjs";
5
-
6
- export async function handleSourceEditRequest(req, res, {
7
- root = ".",
8
- refreshDocument = true,
9
- } = {}) {
10
- if (req.method === "GET") {
11
- try {
12
- const requestUrl = new URL(req.url ?? "/", "http://localhost");
13
- const config = await loadConfig(root);
14
- const sourceText = await readSourceBlockText({
15
- config,
16
- path: requestUrl.searchParams.get("path"),
17
- source: {
18
- line: Number(requestUrl.searchParams.get("line")),
19
- column: Number(requestUrl.searchParams.get("column") || 1),
20
- endLine: Number(requestUrl.searchParams.get("endLine") || requestUrl.searchParams.get("line")),
21
- endColumn: Number(requestUrl.searchParams.get("endColumn") || requestUrl.searchParams.get("column") || 1),
22
- },
23
- });
24
- writeJson(res, 200, { ok: true, source: sourceText });
25
- } catch (error) {
26
- writeJson(res, 400, {
27
- ok: false,
28
- message: error instanceof Error ? error.message : String(error),
29
- });
30
- }
31
- return;
32
- }
33
-
34
- if (req.method !== "POST") {
35
- writeJson(res, 405, { ok: false, message: "OpenPress source edit endpoint requires GET or POST." });
36
- return;
37
- }
38
-
39
- try {
40
- const body = await readJsonBody(req, {
41
- bodyLabel: "OpenPress source edit request",
42
- maxBytes: 256 * 1024,
43
- });
44
- const config = await loadConfig(root);
45
- const edit = await applySourceBlockTextEdit({
46
- config,
47
- path: body?.path,
48
- source: body?.source,
49
- text: body?.text,
50
- kind: body?.kind,
51
- name: body?.name,
52
- blockId: body?.blockId,
53
- sourceMode: body?.sourceMode === true,
54
- });
55
- const exported = refreshDocument && body?.refreshDocument !== false
56
- ? await exportReactDocument(root, { syncAssets: false })
57
- : null;
58
-
59
- writeJson(res, 200, {
60
- ok: true,
61
- edit,
62
- document: exported
63
- ? {
64
- path: exported.documentPath,
65
- pageCount: exported.pageCount,
66
- }
67
- : undefined,
68
- });
69
- } catch (error) {
70
- writeJson(res, 400, {
71
- ok: false,
72
- message: error instanceof Error ? error.message : String(error),
73
- });
74
- }
75
- }