@open-press/cli 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/core/CHANGELOG.md +67 -1
- package/template/core/README.md +9 -5
- package/template/core/engine/cli.mjs +2 -5
- package/template/core/engine/commands/_shared.mjs +4 -4
- package/template/core/engine/commands/deploy.mjs +1 -1
- package/template/core/engine/commands/inspect.mjs +3 -3
- package/template/core/engine/commands/replace.mjs +1 -1
- package/template/core/engine/commands/search.mjs +1 -1
- package/template/core/engine/commands/validate.mjs +2 -2
- package/template/core/engine/document-export.mjs +1 -1
- package/template/core/engine/{chrome-pdf.mjs → output/chrome-pdf.mjs} +1 -2
- package/template/core/engine/{deploy-sync.mjs → output/deploy-sync.mjs} +2 -2
- package/template/core/engine/{fonts.mjs → output/fonts.mjs} +1 -1
- package/template/core/engine/{public-assets.mjs → output/public-assets.mjs} +2 -2
- package/template/core/engine/{static-server.mjs → output/static-server.mjs} +2 -2
- package/template/core/engine/react/caption-numbering.mjs +73 -0
- package/template/core/engine/react/comment-marker.mjs +54 -10
- package/template/core/engine/react/document-entry.mjs +124 -64
- package/template/core/engine/react/document-export.mjs +252 -311
- package/template/core/engine/react/mdx-compile.mjs +123 -3
- package/template/core/engine/react/measurement-css.mjs +3 -3
- package/template/core/engine/react/pagination/allocator.mjs +122 -0
- package/template/core/engine/react/pagination/regions.mjs +81 -0
- package/template/core/engine/react/pagination.mjs +9 -121
- package/template/core/engine/react/pipeline/allocate.mjs +248 -0
- package/template/core/engine/react/pipeline/final-render.mjs +94 -0
- package/template/core/engine/react/pipeline/frame-measurement.mjs +271 -0
- package/template/core/engine/react/pipeline/press-tree.mjs +135 -0
- package/template/core/engine/react/project-asset-endpoint.mjs +2 -2
- package/template/core/engine/react/{chapter-css.mjs → section-css.mjs} +12 -9
- package/template/core/engine/react/sources/heading-numbering.mjs +132 -0
- package/template/core/engine/react/sources/mdx-resolver.mjs +441 -0
- package/template/core/engine/react/{workspace-discovery.mjs → style-discovery.mjs} +29 -40
- package/template/core/engine/{config.mjs → runtime/config.mjs} +15 -0
- package/template/core/engine/{file-utils.mjs → runtime/file-utils.mjs} +1 -1
- package/template/core/engine/{inspection.mjs → runtime/inspection.mjs} +3 -4
- package/template/core/engine/{source-text-tools.mjs → runtime/source-text-tools.mjs} +24 -7
- package/template/core/engine/runtime/source-workspace.mjs +186 -0
- package/template/core/engine/{validation.mjs → runtime/validation.mjs} +19 -17
- package/template/core/package.json +5 -2
- package/template/core/src/openpress/anchorMap.ts +27 -0
- package/template/core/src/openpress/core/Frame.tsx +80 -0
- package/template/core/src/openpress/core/FrameContext.tsx +19 -0
- package/template/core/src/openpress/core/MdxArea.tsx +35 -0
- package/template/core/src/openpress/core/Press.tsx +34 -0
- package/template/core/src/openpress/core/index.tsx +34 -15
- package/template/core/src/openpress/core/primitives.tsx +23 -0
- package/template/core/src/openpress/core/types.ts +131 -19
- package/template/core/src/openpress/core/useSource.ts +28 -0
- package/template/core/src/openpress/manuscript/index.tsx +196 -0
- package/template/core/src/openpress/mdx/index.ts +88 -0
- package/template/core/src/openpress/numbering/index.ts +294 -0
- package/template/core/src/openpress/publicPage.tsx +4 -186
- package/template/core/src/openpress/reactDocumentMetadata.ts +2 -16
- package/template/core/src/openpress/types.ts +0 -16
- package/template/core/src/openpress/workbench.tsx +2 -36
- package/template/core/src/styles/openpress/responsive.css +0 -14
- package/template/core/tsconfig.json +4 -1
- package/template/core/vite.config.ts +10 -3
- package/template/packs/academic-paper/document/components/Page.tsx +24 -14
- package/template/packs/academic-paper/document/design.md +2 -2
- package/template/packs/academic-paper/document/index.tsx +98 -74
- package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +19 -9
- package/template/packs/claude-document/document/components/Page.tsx +24 -14
- package/template/packs/claude-document/document/design.md +2 -2
- package/template/packs/claude-document/document/index.tsx +67 -62
- package/template/packs/claude-document/document/theme/page-surfaces/toc.css +19 -7
- package/template/packs/editorial-monograph/document/components/Page.tsx +24 -14
- package/template/packs/editorial-monograph/document/design.md +2 -2
- package/template/packs/editorial-monograph/document/index.tsx +71 -47
- package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +19 -9
- package/template/core/engine/commands/migrate-to-react.mjs +0 -27
- package/template/core/engine/page-renderer.mjs +0 -217
- package/template/core/engine/react/migrate-to-react.mjs +0 -355
- package/template/core/engine/source-workspace.mjs +0 -76
- package/template/core/src/openpress/core/basePages.tsx +0 -87
- package/template/core/src/openpress/pagination.ts +0 -845
- package/template/packs/claude-document/document/chapters/01-document-shape/chapter.tsx +0 -30
- package/template/packs/claude-document/document/chapters/02-review-loop/chapter.tsx +0 -30
- /package/template/core/engine/{chrome-pdf.d.mts → output/chrome-pdf.d.mts} +0 -0
- /package/template/core/engine/{katex-assets.mjs → output/katex-assets.mjs} +0 -0
- /package/template/core/engine/{page-block.mjs → output/page-block.mjs} +0 -0
- /package/template/core/engine/{pdf-media.mjs → output/pdf-media.mjs} +0 -0
- /package/template/core/engine/{config.d.mts → runtime/config.d.mts} +0 -0
- /package/template/core/engine/{issue-report.mjs → runtime/issue-report.mjs} +0 -0
package/package.json
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# @open-press/core
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 718d2d1: **Press Tree render architecture** — full refresh of the React export pipeline (clean break, no v0.5 compatibility).
|
|
8
|
+
|
|
9
|
+
The render kernel no longer knows about `cover`, `toc`, `chapter`, or `back-cover` as built-in concepts. Workspaces describe their document as a React tree using three primitives:
|
|
10
|
+
|
|
11
|
+
- `Press` — root composition boundary.
|
|
12
|
+
- `Frame` — a single fixed-layout surface (replaces `BasePage` and friends).
|
|
13
|
+
- `MdxArea` — a measurable slot consuming a content chain, with `overflow="extend|truncate|error"` control.
|
|
14
|
+
|
|
15
|
+
Sources are now declarative descriptors:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
export const sources = {
|
|
19
|
+
story: mdxSource({ preset: "section-folders", root: "chapters" }),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default function MyPress() {
|
|
23
|
+
return (
|
|
24
|
+
<Press>
|
|
25
|
+
<Cover />
|
|
26
|
+
<Toc source="story" />
|
|
27
|
+
<Sections source="story" page={Page} />
|
|
28
|
+
<BackCover />
|
|
29
|
+
</Press>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Three `mdxSource()` presets: `section-folders` (existing convention), `section-files` (flat file-per-section), `file-list` (explicit ordering).
|
|
35
|
+
|
|
36
|
+
Manuscript helpers (`Toc`, `Sections`, `Chapters` alias) ship in `@open-press/core/manuscript`. `mdxSource()` lives in `@open-press/core/mdx`. Subpath exports keep the public surface tight without committing to separate npm packages.
|
|
37
|
+
|
|
38
|
+
`Toc` is implemented as a manuscript helper, not a core kernel special case. Registered sources generate a synthetic `toc:<sourceId>` chain; `TocArea` consumes it with the same allocation path as `MdxArea`.
|
|
39
|
+
|
|
40
|
+
Reader-side pagination is removed. The export pipeline writes final frame HTML into `document.json`; the reader displays that HTML and no longer mutates headings/captions, injects footers, or reflows pages at runtime. Page shell belongs to workspace components.
|
|
41
|
+
|
|
42
|
+
MDX source resolution now derives manuscript TOC entries from actual `##` / `###` headings, not folder slugs. Heading numbering is written during export as `data-chapter="01"` / `data-section="1.1"` attributes so themes can render numbering with CSS without reader-side mutation.
|
|
43
|
+
|
|
44
|
+
**Removed (no compatibility layer):**
|
|
45
|
+
|
|
46
|
+
- `BasePage`, `BaseCoverPage`, `BaseTocPage`, `BaseContentPage`, `BaseBackCoverPage`.
|
|
47
|
+
- Legacy named exports (`cover`, `toc`, `backCover`) from `document/index.tsx`.
|
|
48
|
+
- The `migrate-to-react` CLI command.
|
|
49
|
+
- Implicit chapter discovery as the only source mechanism.
|
|
50
|
+
- Legacy `chapter.tsx` meta/opener auto-discovery. Section openers are explicit workspace components in the Press tree.
|
|
51
|
+
|
|
52
|
+
The top-level purity gate remains: `config` must be data, `sources` must be pure `mdxSource()` descriptors, and filesystem/network/process side effects are rejected before module execution. Default-exported function bodies can contain normal React component logic, including hooks and `.map()`.
|
|
53
|
+
|
|
54
|
+
All `<Frame>` instances require a stable `frameKey`; source roots and file-list entries must stay inside `document/`.
|
|
55
|
+
|
|
56
|
+
**Workspace data attributes:**
|
|
57
|
+
|
|
58
|
+
- `data-frame-role` (new, opaque role like `"manuscript.content"`).
|
|
59
|
+
- `data-page-kind` (derived from role's last segment — reader CSS keeps using this).
|
|
60
|
+
- `data-section-id` replaces `data-chapter-slug` for section-scoped CSS.
|
|
61
|
+
|
|
62
|
+
**Migration:** Workspaces written for v0.5.x must rewrite `document/index.tsx` to default-export a Press component. Pre-1.0 minor bump is acceptable per repo policy; no production deployments exist to break.
|
|
63
|
+
|
|
64
|
+
See `docs/superpowers/specs/2026-05-23-press-tree-render-architecture-design.md` for full design rationale.
|
|
65
|
+
|
|
3
66
|
## 0.6.0
|
|
4
67
|
|
|
5
68
|
### Minor Changes
|
|
@@ -40,6 +103,7 @@
|
|
|
40
103
|
- 0169cba: Agent-driven upgrade flow.
|
|
41
104
|
|
|
42
105
|
**New commands:**
|
|
106
|
+
|
|
43
107
|
- `npx open-press doctor` — diagnose workspace against latest framework state. Reports `@open-press/core` version vs npm latest, installed skill count, and any pending `docs/migrations/<version>.md` notes between current and latest. `--json` for machine-readable output, `--no-cache` to bypass the 24h cache. Always exits 0 (informational only).
|
|
44
108
|
|
|
45
109
|
- `npx open-press upgrade` — orchestrate the upgrade. Runs `npm update @open-press/core` (when the workspace declares the dep) and `npx skills upgrade`, then surfaces the list of migration notes for the agent to read. **Does not auto-edit `document/` content** — the agent reads the surfaced `docs/migrations/<version>.md` notes and proposes edits to the user with confirmation. Use `--dry-run` to preview, `--no-deps` / `--no-skills` to target one layer.
|
|
@@ -49,6 +113,7 @@
|
|
|
49
113
|
`open-press dev` now runs `doctor` before starting Vite. When the workspace is behind, a single line prints: `○ open-press: @open-press/core 0.4.0 → 0.5.0 · 1 migration note(s) — run npx open-press doctor for details.` Cached for 24h, network failure is silent, never blocks dev.
|
|
50
114
|
|
|
51
115
|
**Migration docs:**
|
|
116
|
+
|
|
52
117
|
- New `docs/migrations/_template.md` — each release with breaking changes ships a `docs/migrations/<version>.md` file with sections the agent reads.
|
|
53
118
|
- New `docs/migrations/0.4.0.md` — backfilled. Documents the SKILL fold (no document or CLI changes).
|
|
54
119
|
|
|
@@ -65,6 +130,7 @@
|
|
|
65
130
|
### Minor Changes
|
|
66
131
|
|
|
67
132
|
- 3cb4939: Consolidate internal skills (13 → 11).
|
|
133
|
+
|
|
68
134
|
- `openpress-update` folded into `openpress` as an "Updating An Existing Workspace" section. The release-upgrade flow, pre-flight checks, breaking-change reference, and do-not list are now part of the system-operation skill where they naturally belong.
|
|
69
135
|
- `openpress-document-hierarchy` folded into `openpress-writing` as a "Hierarchy" section. Hierarchy decisions (H2/H3/H4 model, TOC depth, appendix placement, H4 granularity) and prose decisions happen in the same workflow; one skill, one routing decision.
|
|
70
136
|
- `references/data-structures-outline.md` moved from the hierarchy skill into `openpress-writing/references/`.
|
|
@@ -81,4 +147,4 @@
|
|
|
81
147
|
|
|
82
148
|
**@open-press/cli** (new): scaffolder for open-press workspaces. Run `npx @open-press/cli init <target> --pack <pack>` to create a fixed-layout document workspace from a bundled template. Supports `editorial-monograph` and `claude-document` style packs, metadata flags, and AI-agent skill installation under `.claude/skills/` and `.agents/skills/`.
|
|
83
149
|
|
|
84
|
-
**@open-press/core** (new): framework runtime, CLI engine, render pipeline, and
|
|
150
|
+
**@open-press/core** (new): framework runtime, CLI engine, render pipeline, and document primitives. Consumed transitively by workspaces scaffolded via `@open-press/cli`. Exposes the `open-press` bin (dev / build / preview / validate / pdf / deploy / export).
|
package/template/core/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @open-press/core
|
|
2
2
|
|
|
3
|
-
Framework runtime, CLI engine, and
|
|
3
|
+
Framework runtime, CLI engine, and Press Tree primitives for [open-press](https://github.com/quan0715/open-press) — an AI-first fixed-layout document workspace.
|
|
4
4
|
|
|
5
5
|
Most users do **not** install this package directly. Instead, scaffold a workspace with the CLI:
|
|
6
6
|
|
|
@@ -20,15 +20,19 @@ npm install @open-press/core
|
|
|
20
20
|
|
|
21
21
|
```tsx
|
|
22
22
|
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
BaseBackCoverPage,
|
|
23
|
+
Press,
|
|
24
|
+
Frame,
|
|
25
|
+
MdxArea,
|
|
27
26
|
BaseFigure,
|
|
28
27
|
BaseCallout,
|
|
29
28
|
} from "@open-press/core";
|
|
29
|
+
|
|
30
|
+
import { mdxSource } from "@open-press/core/mdx";
|
|
31
|
+
import { Sections, Toc } from "@open-press/core/manuscript";
|
|
30
32
|
```
|
|
31
33
|
|
|
34
|
+
`document/index.tsx` default-exports a `<Press>` tree. `Frame` marks fixed-layout pages, `MdxArea` receives measured MDX blocks, and `mdxSource()` declares which MDX files participate in the render pipeline.
|
|
35
|
+
|
|
32
36
|
The CLI bin (`open-press`) supports dev / build / preview / validate / pdf / deploy / export commands. It requires a workspace with `openpress.config.mjs` and the surrounding framework files (which the scaffolder installs).
|
|
33
37
|
|
|
34
38
|
## License
|
|
@@ -6,7 +6,6 @@ import * as doctorCmd from "./commands/doctor.mjs";
|
|
|
6
6
|
import * as exportCmd from "./commands/export.mjs";
|
|
7
7
|
import * as initCmd from "./commands/init.mjs";
|
|
8
8
|
import * as inspectCmd from "./commands/inspect.mjs";
|
|
9
|
-
import * as migrateToReactCmd from "./commands/migrate-to-react.mjs";
|
|
10
9
|
import * as pdfCmd from "./commands/pdf.mjs";
|
|
11
10
|
import * as previewCmd from "./commands/preview.mjs";
|
|
12
11
|
import * as replaceCmd from "./commands/replace.mjs";
|
|
@@ -16,13 +15,12 @@ import * as typecheckCmd from "./commands/typecheck.mjs";
|
|
|
16
15
|
import * as upgradeCmd from "./commands/upgrade.mjs";
|
|
17
16
|
import * as validateCmd from "./commands/validate.mjs";
|
|
18
17
|
import { parseOptions } from "./commands/_shared.mjs";
|
|
19
|
-
import { loadConfig } from "./config.mjs";
|
|
18
|
+
import { loadConfig } from "./runtime/config.mjs";
|
|
20
19
|
import { listStylePackSkills } from "./init.mjs";
|
|
21
|
-
import { discoverWorkspace } from "./validation.mjs";
|
|
20
|
+
import { discoverWorkspace } from "./runtime/validation.mjs";
|
|
22
21
|
|
|
23
22
|
const COMMANDS = {
|
|
24
23
|
init: initCmd,
|
|
25
|
-
"migrate-to-react": migrateToReactCmd,
|
|
26
24
|
validate: validateCmd,
|
|
27
25
|
inspect: inspectCmd,
|
|
28
26
|
search: searchCmd,
|
|
@@ -79,7 +77,6 @@ async function printHelp() {
|
|
|
79
77
|
|
|
80
78
|
Commands:
|
|
81
79
|
init <target> [--skill <name>] [--force]
|
|
82
|
-
migrate-to-react [path] [--dry-run] [--force] [--json]
|
|
83
80
|
validate
|
|
84
81
|
inspect [--json] [--no-build] [--dry-run]
|
|
85
82
|
search [path] <query> [--json] [--scope content|all]
|
|
@@ -2,14 +2,14 @@ import { spawn, spawnSync } from "node:child_process";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { printUrlToPdf, stopChildProcess, waitForPrintReady } from "../chrome-pdf.mjs";
|
|
6
|
-
import { loadConfig, publicPdfHref } from "../config.mjs";
|
|
5
|
+
import { printUrlToPdf, stopChildProcess, waitForPrintReady } from "../output/chrome-pdf.mjs";
|
|
6
|
+
import { loadConfig, publicPdfHref } from "../runtime/config.mjs";
|
|
7
7
|
import { exportDocument } from "../document-export.mjs";
|
|
8
|
-
import { optimizePdfMediaForStaticRoot } from "../pdf-media.mjs";
|
|
8
|
+
import { optimizePdfMediaForStaticRoot } from "../output/pdf-media.mjs";
|
|
9
9
|
|
|
10
10
|
export const ENGINE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
11
|
export const CLI_ENTRY = path.join(ENGINE_DIR, "cli.mjs");
|
|
12
|
-
export const STATIC_SERVER = path.join(ENGINE_DIR, "static-server.mjs");
|
|
12
|
+
export const STATIC_SERVER = path.join(ENGINE_DIR, "output", "static-server.mjs");
|
|
13
13
|
|
|
14
14
|
export function parseOptions(argv) {
|
|
15
15
|
const options = {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { deploySync } from "../deploy-sync.mjs";
|
|
2
|
+
import { deploySync } from "../output/deploy-sync.mjs";
|
|
3
3
|
import { CLI_ENTRY, buildReactPdf, formatNodeScriptCommand, runCommand, writePdfStageDeployConfig } from "./_shared.mjs";
|
|
4
4
|
|
|
5
5
|
export async function run({ root, config, options, recurse }) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { inspectWorkspace } from "../inspection.mjs";
|
|
2
|
-
import { exitCodeForIssueReport } from "../issue-report.mjs";
|
|
1
|
+
import { inspectWorkspace } from "../runtime/inspection.mjs";
|
|
2
|
+
import { exitCodeForIssueReport } from "../runtime/issue-report.mjs";
|
|
3
3
|
|
|
4
4
|
export async function run({ root, config, options, recurse }) {
|
|
5
5
|
const host = options.host ?? "127.0.0.1";
|
|
@@ -10,7 +10,7 @@ export async function run({ root, config, options, recurse }) {
|
|
|
10
10
|
if (!options.noBuild) {
|
|
11
11
|
console.log("Command: node engine/cli.mjs render . --renderer react");
|
|
12
12
|
}
|
|
13
|
-
console.log(`Command: node engine/static-server.mjs ${config.outputDir} --host ${host} --port ${port} --workspace .`);
|
|
13
|
+
console.log(`Command: node engine/output/static-server.mjs ${config.outputDir} --host ${host} --port ${port} --workspace .`);
|
|
14
14
|
console.log(`Chrome inspection URL: ${url}`);
|
|
15
15
|
return 0;
|
|
16
16
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { validateWorkspace } from "../validation.mjs";
|
|
2
|
-
import { exitCodeForIssueReport } from "../issue-report.mjs";
|
|
1
|
+
import { validateWorkspace } from "../runtime/validation.mjs";
|
|
2
|
+
import { exitCodeForIssueReport } from "../runtime/issue-report.mjs";
|
|
3
3
|
|
|
4
4
|
export async function run({ root, options }) {
|
|
5
5
|
const report = await validateWorkspace(root);
|
|
@@ -10,6 +10,6 @@ export async function exportDocument(root = ROOT) {
|
|
|
10
10
|
if (reactResult) return reactResult;
|
|
11
11
|
|
|
12
12
|
throw new Error(
|
|
13
|
-
"React/MDX document entry not found. Expected document/index.tsx
|
|
13
|
+
"React/MDX document entry not found. Expected document/index.tsx with a Press default export before exporting.",
|
|
14
14
|
);
|
|
15
15
|
}
|
|
@@ -203,8 +203,7 @@ export async function waitForPrintReady(client) {
|
|
|
203
203
|
awaitPromise: true,
|
|
204
204
|
expression: `Promise.resolve().then(async () => {
|
|
205
205
|
const root = document.querySelector('[data-openpress-print-document="true"]');
|
|
206
|
-
|
|
207
|
-
if (!ready) return 0;
|
|
206
|
+
if (!root || root.querySelectorAll('.openpress-html-page').length === 0) return 0;
|
|
208
207
|
|
|
209
208
|
await document.fonts?.ready;
|
|
210
209
|
await Promise.all(Array.from(document.images).map(async (img) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { loadConfig } from "
|
|
4
|
-
import { copyDirectory } from "
|
|
3
|
+
import { loadConfig } from "../runtime/config.mjs";
|
|
4
|
+
import { copyDirectory } from "../runtime/file-utils.mjs";
|
|
5
5
|
|
|
6
6
|
export async function deploySync(root, sourceDir, deployDir) {
|
|
7
7
|
const config = await loadConfig(root);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { copyDirectory } from "
|
|
3
|
+
import { copyDirectory } from "../runtime/file-utils.mjs";
|
|
4
4
|
|
|
5
5
|
export async function copyThemeFonts(root, publicOutputDir, config) {
|
|
6
6
|
const themeDir = config?.paths?.themeDir ?? path.join(path.resolve(root), "theme");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { loadConfig } from "
|
|
4
|
-
import { copyDirectory, writeComponentsCss, writeContentCss } from "
|
|
3
|
+
import { loadConfig } from "../runtime/config.mjs";
|
|
4
|
+
import { copyDirectory, writeComponentsCss, writeContentCss } from "../runtime/file-utils.mjs";
|
|
5
5
|
import { copyThemeFonts } from "./fonts.mjs";
|
|
6
6
|
import { copyKatexFonts } from "./katex-assets.mjs";
|
|
7
7
|
|
|
@@ -2,8 +2,8 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import http from "node:http";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
|
-
import { loadConfig, publicPdfHref } from "
|
|
6
|
-
import { handleProjectAssetRequest } from "
|
|
5
|
+
import { loadConfig, publicPdfHref } from "../runtime/config.mjs";
|
|
6
|
+
import { handleProjectAssetRequest } from "../react/project-asset-endpoint.mjs";
|
|
7
7
|
|
|
8
8
|
const [rootArg = "dist", ...rest] = process.argv.slice(2);
|
|
9
9
|
const host = valueAfter(rest, "--host") ?? "127.0.0.1";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const DEFAULT_CAPTION_NUMBERING = {
|
|
2
|
+
figure: "Figure",
|
|
3
|
+
table: "Table",
|
|
4
|
+
separator: " ",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function normalizeCaptionNumbering(value = {}) {
|
|
8
|
+
const input = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
9
|
+
return {
|
|
10
|
+
figure: stringOption(input.figure, DEFAULT_CAPTION_NUMBERING.figure),
|
|
11
|
+
table: stringOption(input.table, DEFAULT_CAPTION_NUMBERING.table),
|
|
12
|
+
separator: typeof input.separator === "string" ? input.separator : DEFAULT_CAPTION_NUMBERING.separator,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createCaptionNumberingState() {
|
|
17
|
+
return {
|
|
18
|
+
figure: 0,
|
|
19
|
+
table: 0,
|
|
20
|
+
seenTables: new Set(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function numberCaptionsInHtml(html, numbering, state = createCaptionNumberingState()) {
|
|
25
|
+
if (!html) return html;
|
|
26
|
+
const options = normalizeCaptionNumbering(numbering);
|
|
27
|
+
let out = String(html);
|
|
28
|
+
out = numberTableCaptions(out, options, state);
|
|
29
|
+
out = numberFigureCaptions(out, options, state);
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function numberTableCaptions(html, options, state) {
|
|
34
|
+
return html.replace(/<table\b([^>]*)>([\s\S]*?<caption\b([^>]*)>)([\s\S]*?)(<\/caption>[\s\S]*?<\/table>)/g, (match, tableAttrs, beforeCaptionText, captionAttrs, captionText, afterCaptionText) => {
|
|
35
|
+
if (captionText.includes("data-openpress-caption-label=")) return match;
|
|
36
|
+
const tableId = attrValue(tableAttrs, "data-openpress-table-id");
|
|
37
|
+
if (tableId && state.seenTables.has(tableId)) return match;
|
|
38
|
+
if (tableId) state.seenTables.add(tableId);
|
|
39
|
+
state.table += 1;
|
|
40
|
+
const label = captionLabel(options.table, state.table, options.separator);
|
|
41
|
+
return `<table${tableAttrs}>${beforeCaptionText}${captionLabelSpan("table", state.table, label)} ${captionText}${afterCaptionText}`;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function numberFigureCaptions(html, options, state) {
|
|
46
|
+
return html.replace(/<figure\b([^>]*)>([\s\S]*?<figcaption\b([^>]*)>)([\s\S]*?)(<\/figcaption>[\s\S]*?<\/figure>)/g, (match, figureAttrs, beforeCaptionText, captionAttrs, captionText, afterCaptionText) => {
|
|
47
|
+
if (captionText.includes("data-openpress-caption-label=")) return match;
|
|
48
|
+
state.figure += 1;
|
|
49
|
+
const label = captionLabel(options.figure, state.figure, options.separator);
|
|
50
|
+
return `<figure${figureAttrs}>${beforeCaptionText}${captionLabelSpan("figure", state.figure, label)} ${captionText}${afterCaptionText}`;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function captionLabel(noun, number, separator) {
|
|
55
|
+
return `${noun}${separator}${number}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function captionLabelSpan(kind, number, label) {
|
|
59
|
+
return `<span class="openpress-caption-label" data-openpress-caption-label="${kind}" data-openpress-caption-number="${number}">${escapeHtml(label)}</span>`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function attrValue(attrs, name) {
|
|
63
|
+
const pattern = new RegExp(`${name}=(["'])(.*?)\\1`);
|
|
64
|
+
return attrs.match(pattern)?.[2] ?? "";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function stringOption(value, fallback) {
|
|
68
|
+
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function escapeHtml(value) {
|
|
72
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
73
|
+
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { loadConfig } from "../config.mjs";
|
|
5
|
-
import { collectSourceTextFiles } from "../source-text-tools.mjs";
|
|
6
|
-
|
|
4
|
+
import { loadConfig } from "../runtime/config.mjs";
|
|
5
|
+
import { collectSourceTextFiles } from "../runtime/source-text-tools.mjs";
|
|
6
|
+
|
|
7
|
+
// Any `.mdx` or `.tsx` file under `document/` is a legal comment target.
|
|
8
|
+
// The Press Tree allows arbitrary source layouts — `section-folders`,
|
|
9
|
+
// `section-files`, `file-list`, custom `root` paths, etc. — so we no
|
|
10
|
+
// longer hardcode `document/chapters/<slug>/content/*.mdx`. The boundary
|
|
11
|
+
// is "inside the workspace's authored `document/` directory" and "looks
|
|
12
|
+
// like an editable React/MDX source" by extension.
|
|
7
13
|
const EDITABLE_COMMENT_SOURCE_PATTERNS = [
|
|
8
|
-
/^document
|
|
9
|
-
/^document
|
|
10
|
-
/^document\/chapters\/[^/]+\/chapter\.tsx$/,
|
|
11
|
-
/^document\/chapters\/[^/]+\/components\/.+\.tsx$/,
|
|
12
|
-
/^document\/components\/.+\.tsx$/,
|
|
14
|
+
/^document\/.+\.mdx$/,
|
|
15
|
+
/^document\/.+\.tsx$/,
|
|
13
16
|
];
|
|
14
17
|
const COMMENT_MARKER_RE = /\{\/\*\s*@openpress-comment\b(?<attrs>[^*]*)\*\/\}/g;
|
|
15
18
|
const COMMENT_LINE_RE = /^\s*\{\/\*\s*@openpress-comment\b[^*]*\*\/\}\s*$/;
|
|
@@ -138,8 +141,14 @@ export function assertEditableCommentPath(relativePath) {
|
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
|
|
144
|
+
// Strict check against workspace-relative paths. Callers walking the
|
|
145
|
+
// workspace (`applyCommentMarker` via `collectSourceTextFiles`) already
|
|
146
|
+
// receive paths with the `document/` prefix and must not have system
|
|
147
|
+
// paths silently mapped into the editable set.
|
|
141
148
|
export function isEditableCommentPath(relativePath) {
|
|
142
|
-
|
|
149
|
+
if (typeof relativePath !== "string" || !relativePath) return false;
|
|
150
|
+
const trimmed = relativePath.trim().replaceAll("\\", "/").replace(/^\.\//, "");
|
|
151
|
+
return EDITABLE_COMMENT_SOURCE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
143
152
|
}
|
|
144
153
|
|
|
145
154
|
function normalizeEditableSourcePath(value) {
|
|
@@ -150,7 +159,42 @@ function normalizeEditableSourcePath(value) {
|
|
|
150
159
|
if (path.posix.isAbsolute(normalized) || normalized.includes("\0") || normalized === "." || normalized.startsWith("../")) {
|
|
151
160
|
throw new Error(`OpenPress comment target path is invalid: ${value}`);
|
|
152
161
|
}
|
|
153
|
-
|
|
162
|
+
const posix = path.posix.normalize(normalized);
|
|
163
|
+
// The Press Tree source resolver emits paths relative to `document/`
|
|
164
|
+
// (e.g. "chapters/01-start/content/01-start.mdx"). The comment marker
|
|
165
|
+
// works in workspace-relative paths (with the `document/` prefix). If
|
|
166
|
+
// the incoming path is documentRoot-relative, prepend `document/`.
|
|
167
|
+
if (!posix.startsWith("document/") && looksDocumentRelative(posix)) {
|
|
168
|
+
return `document/${posix}`;
|
|
169
|
+
}
|
|
170
|
+
return posix;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Identify paths the Press Tree source resolver emits — those are relative
|
|
174
|
+
// to `document/`. Match `.mdx` / `.tsx` files that don't already have the
|
|
175
|
+
// `document/` prefix and don't look like system / engine paths. The check
|
|
176
|
+
// is intentionally tight so we never silently rewrite engine internals
|
|
177
|
+
// (e.g. `src/openpress/...`) into "editable" workspace paths.
|
|
178
|
+
const SYSTEM_PATH_PREFIXES = [
|
|
179
|
+
"document/",
|
|
180
|
+
"src/",
|
|
181
|
+
"engine/",
|
|
182
|
+
"dist/",
|
|
183
|
+
"dist-react/",
|
|
184
|
+
"node_modules/",
|
|
185
|
+
"tests/",
|
|
186
|
+
"public/",
|
|
187
|
+
"packages/",
|
|
188
|
+
".openpress/",
|
|
189
|
+
".deploy/",
|
|
190
|
+
".changeset/",
|
|
191
|
+
".github/",
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
function looksDocumentRelative(posixPath) {
|
|
195
|
+
if (!/\.(mdx|tsx)$/.test(posixPath)) return false;
|
|
196
|
+
if (SYSTEM_PATH_PREFIXES.some((prefix) => posixPath.startsWith(prefix))) return false;
|
|
197
|
+
return true;
|
|
154
198
|
}
|
|
155
199
|
|
|
156
200
|
function normalizeLineNumber(value) {
|