@open-press/cli 0.6.0 → 0.7.1
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/AGENTS.md +126 -0
- package/template/core/CHANGELOG.md +87 -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/upgrade.mjs +47 -5
- 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 +266 -310
- package/template/core/engine/react/mdx-compile.mjs +214 -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 +300 -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/chapters/01-introduction/content/01-introduction.mdx +26 -12
- package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +37 -17
- package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +34 -16
- package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +22 -8
- package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +20 -15
- package/template/packs/academic-paper/document/components/Page.tsx +48 -15
- package/template/packs/academic-paper/document/design.md +2 -2
- package/template/packs/academic-paper/document/index.tsx +96 -80
- package/template/packs/academic-paper/document/media/figure-placeholder.svg +9 -0
- package/template/packs/academic-paper/document/theme/base/page-contract.css +30 -13
- package/template/packs/academic-paper/document/theme/base/typography.css +30 -33
- package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +74 -47
- 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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Working on this open-press workspace
|
|
2
|
+
|
|
3
|
+
This directory is an **open-press workspace** scaffolded by
|
|
4
|
+
`@open-press/cli`. You (the agent) own:
|
|
5
|
+
|
|
6
|
+
- `document/` — chapters (MDX), components, theme, media. The actual content.
|
|
7
|
+
- `package.json` / `openpress.config.mjs` — project metadata + build config.
|
|
8
|
+
- `.agents/skills/` — agent skills installed by `npx skills` (auto-refreshed via `npx open-press upgrade`).
|
|
9
|
+
|
|
10
|
+
The rest of the tree is the open-press framework copied at scaffold time
|
|
11
|
+
(`engine/`, `src/`, `vite.config.ts`, etc). Treat it as vendored — don't
|
|
12
|
+
hand-edit unless the user explicitly asks; the next `npx open-press upgrade`
|
|
13
|
+
will rewrite it.
|
|
14
|
+
|
|
15
|
+
## Quick reference
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm run dev # workbench at http://127.0.0.1:5173/?dev=1
|
|
19
|
+
npm run openpress:validate # structural checks
|
|
20
|
+
npm run openpress:render # full render through chromium
|
|
21
|
+
npm run openpress:pdf # write document.pdf
|
|
22
|
+
npm run openpress:export # write public/openpress/document.json
|
|
23
|
+
npm run openpress:deploy # deploy via the configured adapter
|
|
24
|
+
npx open-press doctor # current vs latest version + pending migrations
|
|
25
|
+
npx open-press upgrade # apply the upgrade flow (see below)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## When the user asks to upgrade
|
|
29
|
+
|
|
30
|
+
Triggers: "升級 / 套件更新 / upgrade open-press / apply latest design /
|
|
31
|
+
update to vX.Y.Z" etc.
|
|
32
|
+
|
|
33
|
+
**Follow the upgrade SOP, do NOT manually diff template versions:**
|
|
34
|
+
|
|
35
|
+
1. `npx open-press doctor` — confirm current vs latest, count pending
|
|
36
|
+
migrations.
|
|
37
|
+
2. Ask the user "go ahead?" — briefly mention what will change (deps,
|
|
38
|
+
skills, migrations to read).
|
|
39
|
+
3. `npx open-press upgrade` — refreshes `@open-press/core`, refreshes
|
|
40
|
+
installed skills, **fetches migration notes for each pending version
|
|
41
|
+
into `.openpress/migrations/<version>.md`** and lists the paths.
|
|
42
|
+
4. For each migration file listed, read it fully. Each has a
|
|
43
|
+
`Document-level changes` section with `rg` find + rewrite rules.
|
|
44
|
+
Apply to `document/` with user confirmation.
|
|
45
|
+
5. Verify:
|
|
46
|
+
```bash
|
|
47
|
+
npm run openpress:validate
|
|
48
|
+
npm run openpress:render
|
|
49
|
+
```
|
|
50
|
+
Fix anything broken using the migration notes.
|
|
51
|
+
6. Report to the user: starting version → ending version, what was
|
|
52
|
+
applied, anything that needed manual judgement.
|
|
53
|
+
|
|
54
|
+
**Anti-pattern**: running `npx @open-press/cli@latest init` somewhere
|
|
55
|
+
and manually diffing against the workspace. The migration notes are the
|
|
56
|
+
authoritative source for what changed; fresh templates ship default
|
|
57
|
+
content that does not apply to a customised workspace.
|
|
58
|
+
|
|
59
|
+
## When the user says "I changed X but the page didn't update"
|
|
60
|
+
|
|
61
|
+
Common cause: **the reader renders from a static `public/openpress/document.json`,
|
|
62
|
+
not from your live MDX / theme files.** Vite Hot Reload covers React UI
|
|
63
|
+
chrome (workbench panels, inspector, navigation) but it does **not**
|
|
64
|
+
regenerate `document.json`. So edits to:
|
|
65
|
+
|
|
66
|
+
- `document/chapters/**/*.mdx` (prose)
|
|
67
|
+
- `document/index.tsx` (Press tree, Cover/BackCover JSX)
|
|
68
|
+
- `document/components/**/*.tsx` (Page, openers, custom components)
|
|
69
|
+
- `document/theme/**` style files that affect pagination capacity
|
|
70
|
+
- `openpress.config.mjs` metadata (title, captionNumbering, …)
|
|
71
|
+
|
|
72
|
+
…all need a re-export before the workbench / public viewer reflect
|
|
73
|
+
the change:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm run openpress:export # regenerate public/openpress/document.json
|
|
77
|
+
# then refresh the browser
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Quick rules of thumb:
|
|
81
|
+
|
|
82
|
+
- Pure CSS edits under `document/theme/` that don't move blocks → HMR
|
|
83
|
+
is enough (CSS is hot-replaced).
|
|
84
|
+
- Anything that affects content, pagination, or metadata → re-export.
|
|
85
|
+
- `npm run openpress:render` is `export` + extra asset sync; either
|
|
86
|
+
works to refresh the JSON.
|
|
87
|
+
|
|
88
|
+
**Agent SOP**: after applying any non-CSS edit to `document/`, run
|
|
89
|
+
`npm run openpress:export` before telling the user "done". If they ask
|
|
90
|
+
"why didn't my change show up?", check whether `document.json` was
|
|
91
|
+
regenerated since the edit.
|
|
92
|
+
|
|
93
|
+
## When the user reports a render / paginate issue
|
|
94
|
+
|
|
95
|
+
Press Tree paginates at build time. Common things to check:
|
|
96
|
+
|
|
97
|
+
1. `npm run openpress:export` then inspect
|
|
98
|
+
`public/openpress/document.json` for `source.warnings` (chain
|
|
99
|
+
overflow, missing chains, etc.).
|
|
100
|
+
2. `npm run openpress:validate` for structural issues
|
|
101
|
+
(missing entries, broken anchors).
|
|
102
|
+
3. `npm run dev` and use the workbench inspector to find which MDX
|
|
103
|
+
block / Frame element is misbehaving — comments and inline
|
|
104
|
+
annotations work directly from there.
|
|
105
|
+
|
|
106
|
+
## Skills
|
|
107
|
+
|
|
108
|
+
This workspace ships agent skills under `.agents/skills/`. If your
|
|
109
|
+
platform supports skills (Claude Code, Cursor, Codex, Cline, Gemini
|
|
110
|
+
CLI, …), prefer invoking them over re-reading this file:
|
|
111
|
+
|
|
112
|
+
- `openpress` — operate the workspace (CLI, validate, export, render,
|
|
113
|
+
PDF, deploy, search/replace, comments, upgrades, routing).
|
|
114
|
+
- `openpress-writing` — writing-time rules for MDX prose.
|
|
115
|
+
- `openpress-design` — theme tokens, layout, page surfaces.
|
|
116
|
+
- `openpress-deploy` — deployment workflows.
|
|
117
|
+
- `openpress-init` — scaffolding new workspaces.
|
|
118
|
+
- Plus any style-pack-specific skills installed by the user.
|
|
119
|
+
|
|
120
|
+
Skills are kept in sync by `npx skills upgrade` (run automatically
|
|
121
|
+
inside `npx open-press upgrade`).
|
|
122
|
+
|
|
123
|
+
## Reporting issues
|
|
124
|
+
|
|
125
|
+
- Issues / questions: https://github.com/quan0715/open-press/issues
|
|
126
|
+
- Source: https://github.com/quan0715/open-press
|
|
@@ -1,5 +1,88 @@
|
|
|
1
1
|
# @open-press/core
|
|
2
2
|
|
|
3
|
+
## 0.7.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Measurement pipeline + pagination fixes:
|
|
8
|
+
|
|
9
|
+
- **Measurement**: wait on `document.fonts.ready`, image `load`/`error` + `decode()`,
|
|
10
|
+
and two `requestAnimationFrame` ticks before sampling block heights so figures
|
|
11
|
+
no longer under-measure on cold loads.
|
|
12
|
+
- **Measurement**: inline relative `media/`, `./media/`, and `/openpress/media/`
|
|
13
|
+
image sources during the SSR measurement pass (previously only the absolute
|
|
14
|
+
`/openpress/media/...` form was rewritten, leaving relative refs as broken).
|
|
15
|
+
- **MDX compile**: split bullet/numbered lists into per-item paginable blocks
|
|
16
|
+
so long lists can break across pages without losing ordered numbering.
|
|
17
|
+
- **Debug**: new `OPENPRESS_DEBUG_ALLOC` env var prints per-iteration allocator
|
|
18
|
+
state (mdxArea capacities, block heights, pagination hints, warnings).
|
|
19
|
+
- **Academic-paper starter**: `<MdxArea overflow="extend">` on the body and the
|
|
20
|
+
single-column `.reader-page--content .page-body` override removed so content
|
|
21
|
+
paginates naturally with the new allocator.
|
|
22
|
+
|
|
23
|
+
## 0.7.0
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- 718d2d1: **Press Tree render architecture** — full refresh of the React export pipeline (clean break, no v0.5 compatibility).
|
|
28
|
+
|
|
29
|
+
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:
|
|
30
|
+
|
|
31
|
+
- `Press` — root composition boundary.
|
|
32
|
+
- `Frame` — a single fixed-layout surface (replaces `BasePage` and friends).
|
|
33
|
+
- `MdxArea` — a measurable slot consuming a content chain, with `overflow="extend|truncate|error"` control.
|
|
34
|
+
|
|
35
|
+
Sources are now declarative descriptors:
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
export const sources = {
|
|
39
|
+
story: mdxSource({ preset: "section-folders", root: "chapters" }),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default function MyPress() {
|
|
43
|
+
return (
|
|
44
|
+
<Press>
|
|
45
|
+
<Cover />
|
|
46
|
+
<Toc source="story" />
|
|
47
|
+
<Sections source="story" page={Page} />
|
|
48
|
+
<BackCover />
|
|
49
|
+
</Press>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Three `mdxSource()` presets: `section-folders` (existing convention), `section-files` (flat file-per-section), `file-list` (explicit ordering).
|
|
55
|
+
|
|
56
|
+
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.
|
|
57
|
+
|
|
58
|
+
`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`.
|
|
59
|
+
|
|
60
|
+
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.
|
|
61
|
+
|
|
62
|
+
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.
|
|
63
|
+
|
|
64
|
+
**Removed (no compatibility layer):**
|
|
65
|
+
|
|
66
|
+
- `BasePage`, `BaseCoverPage`, `BaseTocPage`, `BaseContentPage`, `BaseBackCoverPage`.
|
|
67
|
+
- Legacy named exports (`cover`, `toc`, `backCover`) from `document/index.tsx`.
|
|
68
|
+
- The `migrate-to-react` CLI command.
|
|
69
|
+
- Implicit chapter discovery as the only source mechanism.
|
|
70
|
+
- Legacy `chapter.tsx` meta/opener auto-discovery. Section openers are explicit workspace components in the Press tree.
|
|
71
|
+
|
|
72
|
+
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()`.
|
|
73
|
+
|
|
74
|
+
All `<Frame>` instances require a stable `frameKey`; source roots and file-list entries must stay inside `document/`.
|
|
75
|
+
|
|
76
|
+
**Workspace data attributes:**
|
|
77
|
+
|
|
78
|
+
- `data-frame-role` (new, opaque role like `"manuscript.content"`).
|
|
79
|
+
- `data-page-kind` (derived from role's last segment — reader CSS keeps using this).
|
|
80
|
+
- `data-section-id` replaces `data-chapter-slug` for section-scoped CSS.
|
|
81
|
+
|
|
82
|
+
**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.
|
|
83
|
+
|
|
84
|
+
See `docs/superpowers/specs/2026-05-23-press-tree-render-architecture-design.md` for full design rationale.
|
|
85
|
+
|
|
3
86
|
## 0.6.0
|
|
4
87
|
|
|
5
88
|
### Minor Changes
|
|
@@ -40,6 +123,7 @@
|
|
|
40
123
|
- 0169cba: Agent-driven upgrade flow.
|
|
41
124
|
|
|
42
125
|
**New commands:**
|
|
126
|
+
|
|
43
127
|
- `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
128
|
|
|
45
129
|
- `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 +133,7 @@
|
|
|
49
133
|
`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
134
|
|
|
51
135
|
**Migration docs:**
|
|
136
|
+
|
|
52
137
|
- New `docs/migrations/_template.md` — each release with breaking changes ships a `docs/migrations/<version>.md` file with sections the agent reads.
|
|
53
138
|
- New `docs/migrations/0.4.0.md` — backfilled. Documents the SKILL fold (no document or CLI changes).
|
|
54
139
|
|
|
@@ -65,6 +150,7 @@
|
|
|
65
150
|
### Minor Changes
|
|
66
151
|
|
|
67
152
|
- 3cb4939: Consolidate internal skills (13 → 11).
|
|
153
|
+
|
|
68
154
|
- `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
155
|
- `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
156
|
- `references/data-structures-outline.md` moved from the hierarchy skill into `openpress-writing/references/`.
|
|
@@ -81,4 +167,4 @@
|
|
|
81
167
|
|
|
82
168
|
**@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
169
|
|
|
84
|
-
**@open-press/core** (new): framework runtime, CLI engine, render pipeline, and
|
|
170
|
+
**@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,9 +1,15 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { diagnose } from "./doctor.mjs";
|
|
5
5
|
import { runCommand } from "./_shared.mjs";
|
|
6
6
|
|
|
7
|
+
// Migration notes live in the framework repo, not in scaffolded workspaces.
|
|
8
|
+
// `npx open-press upgrade` fetches the notes for each pending version and
|
|
9
|
+
// caches them under `.openpress/migrations/` so agents can read locally.
|
|
10
|
+
const MIGRATION_REMOTE_BASE = "https://raw.githubusercontent.com/quan0715/open-press/main/docs/migrations";
|
|
11
|
+
const MIGRATION_CACHE_DIR = path.join(".openpress", "migrations");
|
|
12
|
+
|
|
7
13
|
export async function run({ root, options }) {
|
|
8
14
|
const dryRun = Boolean(options?.dryRun);
|
|
9
15
|
const skipSkills = Boolean(options?.noSkills);
|
|
@@ -85,7 +91,11 @@ export async function run({ root, options }) {
|
|
|
85
91
|
process.stdout.write(" (no migration docs in this version range)\n\n");
|
|
86
92
|
} else {
|
|
87
93
|
for (const m of migrationContents) {
|
|
88
|
-
|
|
94
|
+
if (m.path) {
|
|
95
|
+
process.stdout.write(` ─ ${m.path}${m.fetched ? " (fetched from github)" : ""}\n`);
|
|
96
|
+
} else {
|
|
97
|
+
process.stdout.write(` ─ ${m.version}.md (not found locally or on github — check the repo manually)\n`);
|
|
98
|
+
}
|
|
89
99
|
}
|
|
90
100
|
process.stdout.write(
|
|
91
101
|
"\nAgent: open each file, identify document-level changes, grep document/ for affected patterns, propose edits before applying.\n",
|
|
@@ -107,11 +117,43 @@ async function hasCoreDep(root) {
|
|
|
107
117
|
|
|
108
118
|
async function loadMigrations(root, versions) {
|
|
109
119
|
const results = [];
|
|
120
|
+
const cacheDir = path.join(root, MIGRATION_CACHE_DIR);
|
|
121
|
+
await mkdir(cacheDir, { recursive: true });
|
|
122
|
+
|
|
110
123
|
for (const v of versions) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
124
|
+
// Framework repo has docs/migrations/ at root — prefer local if present
|
|
125
|
+
// (covers the open-press framework repo itself acting as a workspace).
|
|
126
|
+
const localDocsPath = path.join(root, "docs", "migrations", `${v}.md`);
|
|
127
|
+
if (existsSync(localDocsPath)) {
|
|
128
|
+
results.push({ version: v, path: path.relative(root, localDocsPath), fetched: false });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Otherwise fetch from GitHub raw and cache to .openpress/migrations/.
|
|
133
|
+
const cachedPath = path.join(cacheDir, `${v}.md`);
|
|
134
|
+
if (existsSync(cachedPath)) {
|
|
135
|
+
results.push({ version: v, path: path.relative(root, cachedPath), fetched: false });
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const body = await fetchMigration(v);
|
|
140
|
+
if (body) {
|
|
141
|
+
await writeFile(cachedPath, body, "utf8");
|
|
142
|
+
results.push({ version: v, path: path.relative(root, cachedPath), fetched: true });
|
|
143
|
+
} else {
|
|
144
|
+
results.push({ version: v, path: null, fetched: false });
|
|
114
145
|
}
|
|
115
146
|
}
|
|
116
147
|
return results;
|
|
117
148
|
}
|
|
149
|
+
|
|
150
|
+
async function fetchMigration(version) {
|
|
151
|
+
const url = `${MIGRATION_REMOTE_BASE}/${version}.md`;
|
|
152
|
+
try {
|
|
153
|
+
const res = await fetch(url, { headers: { Accept: "text/plain" } });
|
|
154
|
+
if (!res.ok) return null;
|
|
155
|
+
return await res.text();
|
|
156
|
+
} catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -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
|
+
}
|