@open-press/cli 0.7.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -13
- package/dist/cli.js +44 -195
- package/package.json +4 -5
- package/template/core/AGENTS.md +18 -14
- package/template/core/CHANGELOG.md +57 -9
- package/template/core/README.md +6 -3
- package/template/core/engine/cli.mjs +8 -8
- package/template/core/engine/commands/_shared.mjs +37 -15
- package/template/core/engine/commands/dev.mjs +2 -2
- package/template/core/engine/commands/image.mjs +29 -0
- package/template/core/engine/commands/skills-sync.mjs +71 -0
- package/template/core/engine/commands/typecheck.mjs +63 -1
- package/template/core/engine/commands/upgrade.mjs +3 -3
- package/template/core/engine/document-export.mjs +1 -1
- package/template/core/engine/output/chrome-pdf.mjs +110 -3
- package/template/core/engine/output/static-server.mjs +87 -9
- package/template/core/engine/react/comment-endpoint.mjs +13 -39
- package/template/core/engine/react/comment-marker.mjs +43 -19
- package/template/core/engine/react/document-entry.mjs +46 -28
- package/template/core/engine/react/document-export.mjs +328 -164
- package/template/core/engine/react/http-json.mjs +24 -0
- package/template/core/engine/react/mdx-compile.mjs +126 -3
- package/template/core/engine/react/measurement-css.mjs +114 -1
- package/template/core/engine/react/object-entities.mjs +204 -0
- package/template/core/engine/react/pagination/allocator.mjs +48 -3
- package/template/core/engine/react/pagination.mjs +1 -1
- package/template/core/engine/react/pipeline/allocate.mjs +41 -72
- package/template/core/engine/react/pipeline/frame-measurement.mjs +6 -0
- package/template/core/engine/react/press-tree-inspection.mjs +172 -0
- package/template/core/engine/react/project-asset-endpoint.mjs +6 -24
- package/template/core/engine/react/source-edit-endpoint.d.mts +10 -0
- package/template/core/engine/react/source-edit-endpoint.mjs +75 -0
- package/template/core/engine/react/sources/mdx-resolver.mjs +13 -15
- package/template/core/engine/react/style-discovery.mjs +23 -8
- package/template/core/engine/runtime/config.d.mts +8 -0
- package/template/core/engine/runtime/config.mjs +57 -60
- package/template/core/engine/runtime/file-utils.mjs +9 -1
- package/template/core/engine/runtime/file-walk.mjs +22 -0
- package/template/core/engine/runtime/inspection.mjs +1 -20
- package/template/core/engine/runtime/page-geometry.mjs +131 -0
- package/template/core/engine/runtime/path-utils.mjs +20 -0
- package/template/core/engine/runtime/source-text-tools.d.mts +102 -0
- package/template/core/engine/runtime/source-text-tools.mjs +551 -16
- package/template/core/engine/runtime/source-workspace.mjs +16 -34
- package/template/core/engine/runtime/validation.mjs +19 -10
- package/template/core/openpress.config.mjs +3 -7
- package/template/core/package.json +3 -5
- package/template/core/src/main.tsx +2 -2
- package/template/core/src/openpress/app/OpenPressApp.tsx +296 -0
- package/template/core/src/openpress/{renderer.tsx → app/OpenPressRuntime.tsx} +20 -9
- package/template/core/src/openpress/app/WorkspaceGalleryPage.tsx +219 -0
- package/template/core/src/openpress/app/index.ts +2 -0
- package/template/core/src/openpress/core/Frame.tsx +26 -15
- package/template/core/src/openpress/core/FrameContext.tsx +10 -3
- package/template/core/src/openpress/core/MdxArea.tsx +11 -12
- package/template/core/src/openpress/core/Press.tsx +25 -4
- package/template/core/src/openpress/core/Workspace.tsx +36 -0
- package/template/core/src/openpress/core/cn.ts +4 -0
- package/template/core/src/openpress/core/index.tsx +11 -3
- package/template/core/src/openpress/core/primitives.tsx +74 -6
- package/template/core/src/openpress/core/types.ts +94 -41
- package/template/core/src/openpress/core/useSource.ts +1 -1
- package/template/core/src/openpress/{anchorMap.ts → document-model/anchorMapModel.ts} +1 -1
- package/template/core/src/openpress/{indexes.ts → document-model/documentIndexes.ts} +1 -1
- package/template/core/src/openpress/{types.ts → document-model/documentTypes.ts} +51 -0
- package/template/core/src/openpress/document-model/index.ts +7 -0
- package/template/core/src/openpress/document-model/objectEntityModel.ts +55 -0
- package/template/core/src/openpress/{projectIdentity.ts → document-model/projectIdentityModel.ts} +1 -1
- package/template/core/src/openpress/{reactDocumentMetadata.ts → document-model/reactDocumentMetadataModel.ts} +1 -1
- package/template/core/src/openpress/document-model/workspaceManifestModel.ts +57 -0
- package/template/core/src/openpress/manuscript/index.tsx +49 -7
- package/template/core/src/openpress/mdx/index.ts +15 -7
- package/template/core/src/openpress/reader/PageThumbnailsPanel.tsx +168 -0
- package/template/core/src/openpress/{publicPage.tsx → reader/PublicReaderPage.tsx} +31 -51
- package/template/core/src/openpress/{workbenchPanels.tsx → reader/ReaderNavigationPanel.tsx} +6 -5
- package/template/core/src/openpress/reader/index.ts +11 -0
- package/template/core/src/openpress/reader/pageViewportScaleModel.ts +73 -0
- package/template/core/src/openpress/reader/readerTypes.ts +4 -0
- package/template/core/src/openpress/reader/usePageViewportScale.ts +119 -0
- package/template/core/src/openpress/reader/usePanelState.ts +56 -0
- package/template/core/src/openpress/reader/useReaderHashSync.ts +61 -0
- package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +48 -0
- package/template/core/src/openpress/reader/useReaderRuntime.ts +146 -0
- package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +64 -0
- package/template/core/src/openpress/shared/Panel.tsx +77 -0
- package/template/core/src/openpress/shared/index.ts +4 -0
- package/template/core/src/openpress/shared/numberUtils.ts +3 -0
- package/template/core/src/openpress/{runtimeMode.ts → shared/runtimeMode.ts} +0 -11
- package/template/core/src/openpress/workbench/Workbench.tsx +506 -0
- package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +157 -0
- package/template/core/src/openpress/workbench/actions/ExportImageControl.tsx +96 -0
- package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +182 -0
- package/template/core/src/openpress/workbench/actions/SearchControl.tsx +345 -0
- package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +112 -0
- package/template/core/src/openpress/workbench/actions/index.ts +6 -0
- package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +136 -0
- package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +72 -0
- package/template/core/src/openpress/workbench/dialog/index.ts +1 -0
- package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +127 -0
- package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +207 -0
- package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +9 -0
- package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +34 -0
- package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +525 -0
- package/template/core/src/openpress/workbench/document/index.ts +10 -0
- package/template/core/src/openpress/workbench/index.ts +2 -0
- package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +459 -0
- package/template/core/src/openpress/workbench/inspector/index.ts +5 -0
- package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +125 -0
- package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +160 -0
- package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +408 -0
- package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +254 -0
- package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +41 -0
- package/template/core/src/openpress/workbench/mentions/index.ts +2 -0
- package/template/core/src/openpress/{composerMentions.ts → workbench/mentions/useComposerMentions.ts} +1 -4
- package/template/core/src/openpress/workbench/panels/Panel.tsx +1 -0
- package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +80 -0
- package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +29 -0
- package/template/core/src/openpress/workbench/panels/index.ts +3 -0
- package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +525 -0
- package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +35 -0
- package/template/core/src/openpress/workbench/project/index.ts +2 -0
- package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +11 -0
- package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +167 -0
- package/template/core/src/openpress/workbench/shell/index.ts +1 -0
- package/template/core/src/openpress/workbench/workbenchFormatters.ts +120 -0
- package/template/core/src/openpress/workbench/workbenchTypes.ts +35 -0
- package/template/core/src/styles/openpress/print-route.css +0 -2
- package/template/core/src/styles/openpress/{project-workspace.css → project-preview-panel.css} +13 -407
- package/template/core/src/styles/openpress/public-viewer.css +25 -320
- package/template/core/src/styles/openpress/reader-runtime.css +252 -55
- package/template/core/src/styles/openpress/responsive.css +145 -270
- package/template/core/src/styles/openpress/workbench-panels.css +327 -178
- package/template/core/src/styles/openpress/workbench.css +986 -451
- package/template/core/src/styles/openpress/workspace-gallery.css +300 -0
- package/template/core/src/styles/openpress.css +2 -1
- package/template/core/tsconfig.json +1 -1
- package/template/core/vite.config.ts +50 -0
- package/template/core/engine/commands/init.mjs +0 -24
- package/template/core/engine/init.mjs +0 -90
- package/template/core/src/openpress/App.tsx +0 -127
- package/template/core/src/openpress/inspector.ts +0 -282
- package/template/core/src/openpress/projectWorkspace.tsx +0 -919
- package/template/core/src/openpress/readerRuntime.ts +0 -230
- package/template/core/src/openpress/workbench.tsx +0 -1265
- package/template/core/src/openpress/workbenchTypes.ts +0 -4
- package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +0 -35
- package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +0 -50
- package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +0 -47
- package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +0 -26
- package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +0 -32
- package/template/packs/academic-paper/document/components/ChapterOpenerVisual/index.tsx +0 -76
- package/template/packs/academic-paper/document/components/Page.tsx +0 -60
- package/template/packs/academic-paper/document/components/TokenSwatchGrid/index.tsx +0 -46
- package/template/packs/academic-paper/document/components/TokenSwatchGrid/style.css +0 -63
- package/template/packs/academic-paper/document/components/TypeSpecimen/index.tsx +0 -38
- package/template/packs/academic-paper/document/components/TypeSpecimen/style.css +0 -111
- package/template/packs/academic-paper/document/design.md +0 -279
- package/template/packs/academic-paper/document/index.tsx +0 -123
- package/template/packs/academic-paper/document/media/README.md +0 -13
- package/template/packs/academic-paper/document/media/figure-placeholder.svg +0 -9
- package/template/packs/academic-paper/document/openpress.config.mjs +0 -26
- package/template/packs/academic-paper/document/theme/README.md +0 -11
- package/template/packs/academic-paper/document/theme/base/page-contract.css +0 -522
- package/template/packs/academic-paper/document/theme/base/print.css +0 -93
- package/template/packs/academic-paper/document/theme/base/typography.css +0 -333
- package/template/packs/academic-paper/document/theme/fonts.css +0 -3
- package/template/packs/academic-paper/document/theme/page-surfaces/back-cover.css +0 -43
- package/template/packs/academic-paper/document/theme/page-surfaces/chapter-opener.css +0 -205
- package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +0 -294
- package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +0 -149
- package/template/packs/academic-paper/document/theme/patterns/_chart-frame.css +0 -49
- package/template/packs/academic-paper/document/theme/patterns/figure-grid.css +0 -68
- package/template/packs/academic-paper/document/theme/patterns/table-utilities.css +0 -66
- package/template/packs/academic-paper/document/theme/shell/reader-controls.css +0 -761
- package/template/packs/academic-paper/document/theme/tokens.css +0 -80
- package/template/packs/academic-paper/openpress.config.mjs +0 -5
- package/template/packs/claude-document/document/chapters/01-document-shape/content/01-document-shape.mdx +0 -51
- package/template/packs/claude-document/document/chapters/02-review-loop/content/01-review-loop.mdx +0 -31
- package/template/packs/claude-document/document/components/ChapterOpenerVisual.tsx +0 -96
- package/template/packs/claude-document/document/components/Page.tsx +0 -37
- package/template/packs/claude-document/document/design.md +0 -142
- package/template/packs/claude-document/document/index.tsx +0 -94
- package/template/packs/claude-document/document/media/README.md +0 -13
- package/template/packs/claude-document/document/openpress.config.mjs +0 -26
- package/template/packs/claude-document/document/theme/README.md +0 -15
- package/template/packs/claude-document/document/theme/base/page-contract.css +0 -525
- package/template/packs/claude-document/document/theme/base/print.css +0 -93
- package/template/packs/claude-document/document/theme/base/typography.css +0 -612
- package/template/packs/claude-document/document/theme/fonts.css +0 -4
- package/template/packs/claude-document/document/theme/page-surfaces/back-cover.css +0 -72
- package/template/packs/claude-document/document/theme/page-surfaces/chapter-opener.css +0 -236
- package/template/packs/claude-document/document/theme/page-surfaces/cover.css +0 -309
- package/template/packs/claude-document/document/theme/page-surfaces/toc.css +0 -225
- package/template/packs/claude-document/document/theme/patterns/_chart-frame.css +0 -53
- package/template/packs/claude-document/document/theme/patterns/figure-grid.css +0 -68
- package/template/packs/claude-document/document/theme/patterns/table-utilities.css +0 -66
- package/template/packs/claude-document/document/theme/shell/reader-controls.css +0 -789
- package/template/packs/claude-document/document/theme/tokens.css +0 -89
- package/template/packs/claude-document/openpress.config.mjs +0 -5
- package/template/packs/editorial-monograph/document/chapters/01-product-and-use-cases/content/01-product-and-use-cases.mdx +0 -31
- package/template/packs/editorial-monograph/document/chapters/02-workflow/content/01-workflow.mdx +0 -89
- package/template/packs/editorial-monograph/document/chapters/03-agent-skills-contributors/content/01-agent-skills-contributors.mdx +0 -51
- package/template/packs/editorial-monograph/document/chapters/04-validation-deploy/content/01-validation-deploy.mdx +0 -39
- package/template/packs/editorial-monograph/document/components/ChapterOpenerVisual/index.tsx +0 -76
- package/template/packs/editorial-monograph/document/components/Page.tsx +0 -37
- package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/index.tsx +0 -46
- package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/style.css +0 -63
- package/template/packs/editorial-monograph/document/components/TypeSpecimen/index.tsx +0 -38
- package/template/packs/editorial-monograph/document/components/TypeSpecimen/style.css +0 -111
- package/template/packs/editorial-monograph/document/design.md +0 -279
- package/template/packs/editorial-monograph/document/index.tsx +0 -97
- package/template/packs/editorial-monograph/document/media/README.md +0 -13
- package/template/packs/editorial-monograph/document/openpress.config.mjs +0 -26
- package/template/packs/editorial-monograph/document/theme/README.md +0 -11
- package/template/packs/editorial-monograph/document/theme/base/page-contract.css +0 -505
- package/template/packs/editorial-monograph/document/theme/base/print.css +0 -93
- package/template/packs/editorial-monograph/document/theme/base/typography.css +0 -336
- package/template/packs/editorial-monograph/document/theme/fonts.css +0 -3
- package/template/packs/editorial-monograph/document/theme/page-surfaces/back-cover.css +0 -43
- package/template/packs/editorial-monograph/document/theme/page-surfaces/chapter-opener.css +0 -205
- package/template/packs/editorial-monograph/document/theme/page-surfaces/cover.css +0 -147
- package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +0 -149
- package/template/packs/editorial-monograph/document/theme/patterns/_chart-frame.css +0 -49
- package/template/packs/editorial-monograph/document/theme/patterns/figure-grid.css +0 -68
- package/template/packs/editorial-monograph/document/theme/patterns/table-utilities.css +0 -66
- package/template/packs/editorial-monograph/document/theme/shell/reader-controls.css +0 -761
- package/template/packs/editorial-monograph/document/theme/tokens.css +0 -80
- package/template/packs/editorial-monograph/openpress.config.mjs +0 -5
- /package/template/core/src/openpress/{readerPageRegistry.ts → reader/readerPageRegistry.ts} +0 -0
- /package/template/core/src/openpress/{pageRoute.ts → reader/readerPageRoute.ts} +0 -0
- /package/template/core/src/openpress/{readerScroll.ts → reader/readerScroll.ts} +0 -0
- /package/template/core/src/openpress/{readerState.ts → reader/readerStateModel.ts} +0 -0
- /package/template/core/src/openpress/{frameScheduler.ts → shared/frameScheduler.ts} +0 -0
- /package/template/core/src/openpress/{projectSources.ts → workbench/project/projectSourceModel.ts} +0 -0
package/README.md
CHANGED
|
@@ -2,16 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Scaffolder for [open-press](https://github.com/quan0715/open-press) — an AI-first fixed-layout document workspace.
|
|
4
4
|
|
|
5
|
+
## Prerequisite
|
|
6
|
+
|
|
7
|
+
Node.js 20 or newer with `npm` / `npx`.
|
|
8
|
+
|
|
5
9
|
## Quick start
|
|
6
10
|
|
|
7
11
|
```bash
|
|
8
|
-
npx @open-press/cli init my-doc
|
|
12
|
+
npx @open-press/cli init my-doc
|
|
9
13
|
cd my-doc
|
|
10
14
|
npm run dev
|
|
11
15
|
```
|
|
12
16
|
|
|
13
17
|
Then open the local URL printed by Vite (typically `http://127.0.0.1:5173/?dev=1`).
|
|
14
18
|
|
|
19
|
+
The CLI creates the OpenPress runtime workspace only. Starters and examples live in skills, installed separately with `npx skills add <owner/repo>`. The starter-bearing skills in the framework repo are just skills; agents can read and use them directly.
|
|
20
|
+
|
|
15
21
|
## Usage
|
|
16
22
|
|
|
17
23
|
```
|
|
@@ -20,39 +26,49 @@ npx @open-press/cli init <target> [flags]
|
|
|
20
26
|
|
|
21
27
|
| Flag | Description |
|
|
22
28
|
| -------------------- | --------------------------------------------------------------------------- |
|
|
23
|
-
| `--
|
|
24
|
-
| `--title <s>` | Document title (written to `openpress.config.mjs`) |
|
|
29
|
+
| `--title <s>` | Document title (written to workspace config) |
|
|
25
30
|
| `--subtitle <s>` | Document subtitle |
|
|
26
31
|
| `--organization <s>` | Organization name |
|
|
27
32
|
| `--author <s>` | Author name |
|
|
28
|
-
| `--no-git` | Skip `git init`
|
|
29
|
-
| `--no-install` | Skip `npm install`
|
|
30
|
-
| `--force` | Allow scaffolding into a non-empty target |
|
|
33
|
+
| `--no-git` | Skip `git init` + initial commit (use when scaffolding inside an existing repo) |
|
|
34
|
+
| `--no-install` | Skip `npm install` (offline, or you'll run pnpm/bun yourself) |
|
|
31
35
|
| `--help` | Print help |
|
|
32
36
|
|
|
37
|
+
> The target must be empty. A `.git/` directory or other harmless dotfiles (`.gitignore`, `.gitkeep`, `.DS_Store`) are ignored — common when scaffolding into a fresh repo.
|
|
38
|
+
|
|
39
|
+
To use an opinionated starter, install a skill and let the agent read that skill's files:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx -y skills@latest add quan0715/openpress-social-card-skill
|
|
43
|
+
```
|
|
44
|
+
|
|
33
45
|
## What it creates
|
|
34
46
|
|
|
35
47
|
A self-contained workspace with:
|
|
36
48
|
|
|
37
49
|
- `engine/`, `src/`, `vite.config.ts` — the open-press framework (snapshot of `@open-press/core`)
|
|
38
|
-
- `document/` — your content (populated from the chosen style pack)
|
|
39
50
|
- `.claude/skills/` and `.agents/skills/` — agent skill files for Claude Code, Codex, Cursor, Copilot, etc.
|
|
40
51
|
- `openpress.config.mjs` — workspace metadata (title, subtitle, organization, author)
|
|
41
52
|
- `AGENTS.md` — agent contract
|
|
42
53
|
|
|
54
|
+
The `press/` or transitional `document/` source tree is added by a skill, user-authored code, or a project-specific workflow after init.
|
|
55
|
+
|
|
43
56
|
## After init
|
|
44
57
|
|
|
45
58
|
Workspace commands (run via `npm run` or `node engine/cli.mjs`):
|
|
46
59
|
|
|
47
60
|
```
|
|
48
|
-
npm run dev
|
|
49
|
-
npm run build
|
|
50
|
-
npm run preview
|
|
51
|
-
npm run
|
|
52
|
-
npm run openpress:
|
|
53
|
-
npm run openpress:
|
|
61
|
+
npm run dev # start workbench
|
|
62
|
+
npm run build # validate + render dist-react/
|
|
63
|
+
npm run preview # preview production build
|
|
64
|
+
npm run typecheck # tsc --noEmit
|
|
65
|
+
npm run openpress:image # render one PNG per page
|
|
66
|
+
npm run openpress:pdf # render PDF
|
|
67
|
+
npm run openpress:deploy:dry-run # preview deploy without publishing
|
|
54
68
|
```
|
|
55
69
|
|
|
70
|
+
Full reference: <https://open-press.dev/docs/cli>
|
|
71
|
+
|
|
56
72
|
## License
|
|
57
73
|
|
|
58
74
|
MIT — see [LICENSE](https://github.com/quan0715/open-press/blob/main/LICENSE).
|
package/dist/cli.js
CHANGED
|
@@ -6,61 +6,21 @@ import process2 from "process";
|
|
|
6
6
|
// src/init.ts
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
8
|
import { existsSync } from "fs";
|
|
9
|
-
import { cp, mkdir
|
|
10
|
-
import
|
|
9
|
+
import { cp, mkdir } from "fs/promises";
|
|
10
|
+
import path from "path";
|
|
11
11
|
import process from "process";
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
13
|
|
|
14
|
-
// src/
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import path from "path";
|
|
19
|
-
import { Readable } from "stream";
|
|
20
|
-
import { pipeline } from "stream/promises";
|
|
21
|
-
import { x as extract } from "tar";
|
|
22
|
-
async function degit({ owner, repo, ref = "main", dest, subdir }) {
|
|
23
|
-
const url = `https://codeload.github.com/${owner}/${repo}/tar.gz/refs/heads/${ref}`;
|
|
24
|
-
const tmpDir = await mkdir(path.join(tmpdir(), `open-press-degit-${Date.now()}`), { recursive: true });
|
|
25
|
-
const tarballPath = path.join(tmpDir, "repo.tar.gz");
|
|
26
|
-
try {
|
|
27
|
-
await fetchTo(url, tarballPath);
|
|
28
|
-
await mkdir(dest, { recursive: true });
|
|
29
|
-
const subdirSegments = subdir ? subdir.split("/").filter(Boolean).length : 0;
|
|
30
|
-
const totalStrip = 1 + subdirSegments;
|
|
31
|
-
const filterPrefix = subdir ? subdir.replace(/\/$/, "") + "/" : null;
|
|
32
|
-
await extract({
|
|
33
|
-
file: tarballPath,
|
|
34
|
-
cwd: dest,
|
|
35
|
-
strip: totalStrip,
|
|
36
|
-
filter: (filePath) => {
|
|
37
|
-
const segments = filePath.split("/");
|
|
38
|
-
const inside = segments.slice(1).join("/");
|
|
39
|
-
if (filterPrefix) {
|
|
40
|
-
return inside.startsWith(filterPrefix);
|
|
41
|
-
}
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
} finally {
|
|
46
|
-
await rm(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async function fetchTo(url, destFile) {
|
|
51
|
-
const res = await fetch(url, { redirect: "follow" });
|
|
52
|
-
if (!res.ok || !res.body) {
|
|
53
|
-
throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
|
|
54
|
-
}
|
|
55
|
-
await pipeline(Readable.fromWeb(res.body), createWriteStream(destFile));
|
|
56
|
-
}
|
|
57
|
-
async function pathIsEmpty(target) {
|
|
14
|
+
// src/path-is-empty.ts
|
|
15
|
+
import { readdir, stat } from "fs/promises";
|
|
16
|
+
var HARMLESS_TARGET_ENTRIES = /* @__PURE__ */ new Set([".git", ".gitignore", ".gitkeep", ".DS_Store"]);
|
|
17
|
+
async function pathIsEmpty(target, options = {}) {
|
|
58
18
|
try {
|
|
59
19
|
const s = await stat(target);
|
|
60
20
|
if (!s.isDirectory()) return false;
|
|
61
|
-
const { readdir } = await import("fs/promises");
|
|
62
21
|
const entries = await readdir(target);
|
|
63
|
-
return entries.length === 0;
|
|
22
|
+
if (!options.ignoreHarmless) return entries.length === 0;
|
|
23
|
+
return entries.every((entry) => HARMLESS_TARGET_ENTRIES.has(entry));
|
|
64
24
|
} catch {
|
|
65
25
|
return true;
|
|
66
26
|
}
|
|
@@ -68,20 +28,17 @@ async function pathIsEmpty(target) {
|
|
|
68
28
|
|
|
69
29
|
// src/metadata.ts
|
|
70
30
|
import { readFile, writeFile } from "fs/promises";
|
|
71
|
-
async function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
source = source.replace(/(export\s+default\s*\{)/, `$1
|
|
81
|
-
${key}: "${escaped}",`);
|
|
82
|
-
}
|
|
31
|
+
async function patchPressTitle(entryPath, title) {
|
|
32
|
+
const source = await readFile(entryPath, "utf8");
|
|
33
|
+
const escaped = escapeStringForJs(title);
|
|
34
|
+
const existing = /(<Press\b[^>]*\btitle\s*=\s*)("[^"]*"|'[^']*'|\{`[^`]*`\})/;
|
|
35
|
+
let next;
|
|
36
|
+
if (existing.test(source)) {
|
|
37
|
+
next = source.replace(existing, `$1"${escaped}"`);
|
|
38
|
+
} else {
|
|
39
|
+
next = source.replace(/<Press\b/, `<Press title="${escaped}"`);
|
|
83
40
|
}
|
|
84
|
-
await writeFile(
|
|
41
|
+
await writeFile(entryPath, next);
|
|
85
42
|
}
|
|
86
43
|
function escapeStringForJs(value) {
|
|
87
44
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
@@ -115,69 +72,27 @@ async function patchPackageJsonName(packagePath, newName) {
|
|
|
115
72
|
}
|
|
116
73
|
|
|
117
74
|
// src/init.ts
|
|
118
|
-
var BUNDLED_PACKS = ["editorial-monograph", "claude-document", "academic-paper"];
|
|
119
75
|
var FRAMEWORK_SKILLS_SOURCE = "quan0715/open-press";
|
|
120
|
-
var __dirname =
|
|
121
|
-
var TEMPLATE_ROOT =
|
|
122
|
-
var TEMPLATE_CORE =
|
|
123
|
-
var TEMPLATE_PACKS = path2.join(TEMPLATE_ROOT, "packs");
|
|
76
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
77
|
+
var TEMPLATE_ROOT = path.resolve(__dirname, "..", "template");
|
|
78
|
+
var TEMPLATE_CORE = path.join(TEMPLATE_ROOT, "core");
|
|
124
79
|
async function init(options) {
|
|
125
|
-
const packSpec = options.pack ? parsePackSpec(options.pack) : null;
|
|
126
80
|
ensureTemplateBundled();
|
|
127
|
-
const target =
|
|
128
|
-
await ensureTarget(target
|
|
81
|
+
const target = path.resolve(process.cwd(), options.target);
|
|
82
|
+
await ensureTarget(target);
|
|
129
83
|
log(`Creating open-press workspace at ${target}`);
|
|
130
84
|
log("Copying framework (engine + runtime + config)\u2026");
|
|
131
85
|
await cp(TEMPLATE_CORE, target, { recursive: true });
|
|
132
|
-
const
|
|
133
|
-
await rm2(docDest, { recursive: true, force: true });
|
|
134
|
-
await mkdir2(docDest, { recursive: true });
|
|
135
|
-
if (packSpec?.kind === "bundled") {
|
|
136
|
-
log(`Applying bundled style pack: ${packSpec.name}`);
|
|
137
|
-
const packStarter = path2.join(TEMPLATE_PACKS, packSpec.name, "document");
|
|
138
|
-
if (!existsSync(packStarter)) {
|
|
139
|
-
throw new Error(`Bundled style pack starter not found: ${packStarter}`);
|
|
140
|
-
}
|
|
141
|
-
await cp(packStarter, docDest, { recursive: true });
|
|
142
|
-
} else if (packSpec?.kind === "github") {
|
|
143
|
-
log(`Fetching style pack from github:${packSpec.owner}/${packSpec.repo}${packSpec.ref ? `#${packSpec.ref}` : ""}\u2026`);
|
|
144
|
-
try {
|
|
145
|
-
await degit({
|
|
146
|
-
owner: packSpec.owner,
|
|
147
|
-
repo: packSpec.repo,
|
|
148
|
-
ref: packSpec.ref,
|
|
149
|
-
dest: docDest,
|
|
150
|
-
subdir: "starter/document"
|
|
151
|
-
});
|
|
152
|
-
} catch (err) {
|
|
153
|
-
throw new Error(
|
|
154
|
-
`Failed to fetch pack from github:${packSpec.owner}/${packSpec.repo}: ${err instanceof Error ? err.message : String(err)}`
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
if (await pathIsEmpty(docDest)) {
|
|
158
|
-
throw new Error(
|
|
159
|
-
`github:${packSpec.owner}/${packSpec.repo} doesn't contain starter/document/ at the repo root.
|
|
160
|
-
Third-party pack repos should follow this layout:
|
|
161
|
-
<repo>/
|
|
162
|
-
\u251C\u2500\u2500 starter/
|
|
163
|
-
\u2502 \u2514\u2500\u2500 document/ \u2190 cli copies this into your workspace's document/
|
|
164
|
-
\u2514\u2500\u2500 skills/<pack>/SKILL.md \u2190 npx skills add picks this up`
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
const pkgPath = path2.join(target, "package.json");
|
|
86
|
+
const pkgPath = path.join(target, "package.json");
|
|
169
87
|
if (existsSync(pkgPath)) {
|
|
170
|
-
await patchPackageJsonName(pkgPath,
|
|
88
|
+
await patchPackageJsonName(pkgPath, path.basename(target));
|
|
171
89
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
organization: options.organization,
|
|
179
|
-
author: options.author
|
|
180
|
-
});
|
|
90
|
+
if (options.title) {
|
|
91
|
+
const pressEntry = path.join(target, "press", "index.tsx");
|
|
92
|
+
if (existsSync(pressEntry)) {
|
|
93
|
+
log("Writing title into <Press> in press/index.tsx");
|
|
94
|
+
await patchPressTitle(pressEntry, options.title);
|
|
95
|
+
}
|
|
181
96
|
}
|
|
182
97
|
log(`Installing framework skills via \`npx skills add ${FRAMEWORK_SKILLS_SOURCE}\`\u2026`);
|
|
183
98
|
try {
|
|
@@ -186,16 +101,6 @@ Third-party pack repos should follow this layout:
|
|
|
186
101
|
log(`(framework skills install failed; retry: npx skills add ${FRAMEWORK_SKILLS_SOURCE})`);
|
|
187
102
|
log(` reason: ${err instanceof Error ? err.message : String(err)}`);
|
|
188
103
|
}
|
|
189
|
-
if (packSpec?.kind === "github") {
|
|
190
|
-
const packSource = `${packSpec.owner}/${packSpec.repo}`;
|
|
191
|
-
log(`Installing pack skills via \`npx skills add ${packSource}\`\u2026`);
|
|
192
|
-
try {
|
|
193
|
-
await runInTarget(target, "npx", ["-y", "skills@latest", "add", packSource]);
|
|
194
|
-
} catch (err) {
|
|
195
|
-
log(`(pack skills install failed; retry: npx skills add ${packSource})`);
|
|
196
|
-
log(` reason: ${err instanceof Error ? err.message : String(err)}`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
104
|
if (options.install) {
|
|
200
105
|
log("Installing dependencies (npm install)\u2026");
|
|
201
106
|
await runInTarget(target, "npm", ["install"]);
|
|
@@ -216,46 +121,24 @@ Third-party pack repos should follow this layout:
|
|
|
216
121
|
}
|
|
217
122
|
printNextSteps(target, options);
|
|
218
123
|
}
|
|
219
|
-
function parsePackSpec(spec) {
|
|
220
|
-
if (spec.startsWith("github:")) {
|
|
221
|
-
const rest = spec.slice("github:".length);
|
|
222
|
-
const [pathPart, ref] = rest.split("#");
|
|
223
|
-
const segments = pathPart.split("/").filter(Boolean);
|
|
224
|
-
if (segments.length !== 2) {
|
|
225
|
-
throw new Error(
|
|
226
|
-
`Invalid --pack spec: "${spec}". Use github:owner/repo or github:owner/repo#ref.`
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
const [owner, repo] = segments;
|
|
230
|
-
return { kind: "github", owner, repo, ref: ref?.trim() || void 0 };
|
|
231
|
-
}
|
|
232
|
-
if (!BUNDLED_PACKS.includes(spec)) {
|
|
233
|
-
throw new Error(
|
|
234
|
-
`Unknown style pack: "${spec}". Bundled packs: ${BUNDLED_PACKS.join(", ")}. For third-party packs use github:owner/repo (e.g. github:quan0715/openpress-pack-nycu-thesis).`
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
return { kind: "bundled", name: spec };
|
|
238
|
-
}
|
|
239
124
|
function ensureTemplateBundled() {
|
|
240
|
-
if (!existsSync(TEMPLATE_CORE)
|
|
125
|
+
if (!existsSync(TEMPLATE_CORE)) {
|
|
241
126
|
throw new Error(
|
|
242
127
|
`Template not bundled at ${TEMPLATE_ROOT}. If running from source, run \`pnpm sync:template\` in packages/cli first.`
|
|
243
128
|
);
|
|
244
129
|
}
|
|
245
130
|
}
|
|
246
|
-
async function ensureTarget(target
|
|
131
|
+
async function ensureTarget(target) {
|
|
247
132
|
if (existsSync(target)) {
|
|
248
|
-
|
|
249
|
-
const empty = await pathIsEmpty(target);
|
|
133
|
+
const empty = await pathIsEmpty(target, { ignoreHarmless: true });
|
|
250
134
|
if (!empty) {
|
|
251
|
-
throw new Error(
|
|
135
|
+
throw new Error(
|
|
136
|
+
`Target ${target} is not empty. Remove existing files first, or scaffold into a different directory.`
|
|
137
|
+
);
|
|
252
138
|
}
|
|
253
139
|
return;
|
|
254
140
|
}
|
|
255
|
-
await
|
|
256
|
-
}
|
|
257
|
-
function hasMetadata(options) {
|
|
258
|
-
return Boolean(options.title || options.subtitle || options.organization || options.author);
|
|
141
|
+
await mkdir(target, { recursive: true });
|
|
259
142
|
}
|
|
260
143
|
async function runInTarget(cwd, command, args, opts = {}) {
|
|
261
144
|
return new Promise((resolve, reject) => {
|
|
@@ -276,7 +159,7 @@ function log(message) {
|
|
|
276
159
|
`);
|
|
277
160
|
}
|
|
278
161
|
function printNextSteps(target, options) {
|
|
279
|
-
const rel =
|
|
162
|
+
const rel = path.relative(process.cwd(), target) || ".";
|
|
280
163
|
const lines = [
|
|
281
164
|
"",
|
|
282
165
|
"\u2713 Done. Your open-press workspace is ready.",
|
|
@@ -297,7 +180,7 @@ function printNextSteps(target, options) {
|
|
|
297
180
|
"Agent skills installed under .agents/skills/ (universal \u2014 read by Claude Code,",
|
|
298
181
|
`Cursor, Codex, Gemini CLI, etc.). Update later with: npx skills upgrade`,
|
|
299
182
|
"",
|
|
300
|
-
"
|
|
183
|
+
"Use an OpenPress-ready skill to add or adapt the press/document source tree.",
|
|
301
184
|
""
|
|
302
185
|
);
|
|
303
186
|
process.stdout.write(lines.join("\n"));
|
|
@@ -310,28 +193,14 @@ Usage:
|
|
|
310
193
|
npx @open-press/cli init <target> [flags]
|
|
311
194
|
|
|
312
195
|
Flags:
|
|
313
|
-
--
|
|
314
|
-
\u2022 a bundled name \u2014 editorial-monograph | claude-document | academic-paper
|
|
315
|
-
\u2022 github:owner/repo (third-party pack)
|
|
316
|
-
\u2022 github:owner/repo#branch-or-tag
|
|
317
|
-
--title <s> Document title (written to openpress.config.mjs)
|
|
318
|
-
--subtitle <s> Document subtitle
|
|
319
|
-
--organization <s> Organization name
|
|
320
|
-
--author <s> Author name
|
|
196
|
+
--title <s> Document title (written into <Press title="..."> in press/index.tsx)
|
|
321
197
|
--no-git Skip git init
|
|
322
198
|
--no-install Skip npm install
|
|
323
|
-
--force Allow non-empty target
|
|
324
199
|
--help Show this help
|
|
325
200
|
|
|
326
201
|
Examples:
|
|
327
|
-
|
|
328
|
-
npx @open-press/cli init my-
|
|
329
|
-
npx @open-press/cli init my-brief --pack claude-document --title "Q2 Brief" --author Quan
|
|
330
|
-
npx @open-press/cli init my-paper --pack academic-paper --title "Paper Title" --author "First Author"
|
|
331
|
-
|
|
332
|
-
# Third-party (any GitHub repo with starter/document/ at the root)
|
|
333
|
-
npx @open-press/cli init my-thesis --pack github:quan0715/openpress-pack-nycu-thesis
|
|
334
|
-
npx @open-press/cli init my-paper --pack github:foo/their-pack#v1.2
|
|
202
|
+
npx @open-press/cli init my-doc
|
|
203
|
+
npx @open-press/cli init my-brief --title "Q2 Brief"
|
|
335
204
|
`;
|
|
336
205
|
async function main(argv) {
|
|
337
206
|
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
@@ -368,33 +237,16 @@ function parseInitArgs(args) {
|
|
|
368
237
|
}
|
|
369
238
|
const options = {
|
|
370
239
|
target: "",
|
|
371
|
-
pack: void 0,
|
|
372
240
|
title: void 0,
|
|
373
|
-
subtitle: void 0,
|
|
374
|
-
organization: void 0,
|
|
375
|
-
author: void 0,
|
|
376
241
|
git: true,
|
|
377
|
-
install: true
|
|
378
|
-
force: false
|
|
242
|
+
install: true
|
|
379
243
|
};
|
|
380
244
|
for (let i = 0; i < args.length; i++) {
|
|
381
245
|
const arg = args[i];
|
|
382
246
|
switch (arg) {
|
|
383
|
-
case "--pack":
|
|
384
|
-
options.pack = args[++i];
|
|
385
|
-
break;
|
|
386
247
|
case "--title":
|
|
387
248
|
options.title = args[++i];
|
|
388
249
|
break;
|
|
389
|
-
case "--subtitle":
|
|
390
|
-
options.subtitle = args[++i];
|
|
391
|
-
break;
|
|
392
|
-
case "--organization":
|
|
393
|
-
options.organization = args[++i];
|
|
394
|
-
break;
|
|
395
|
-
case "--author":
|
|
396
|
-
options.author = args[++i];
|
|
397
|
-
break;
|
|
398
250
|
case "--no-git":
|
|
399
251
|
options.git = false;
|
|
400
252
|
break;
|
|
@@ -407,9 +259,6 @@ function parseInitArgs(args) {
|
|
|
407
259
|
case "--install":
|
|
408
260
|
options.install = true;
|
|
409
261
|
break;
|
|
410
|
-
case "--force":
|
|
411
|
-
options.force = true;
|
|
412
|
-
break;
|
|
413
262
|
default:
|
|
414
263
|
if (arg.startsWith("--")) {
|
|
415
264
|
process2.stderr.write(`Unknown flag: ${arg}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-press/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Scaffolder for open-press — AI-first fixed-layout document workspaces.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,9 +40,7 @@
|
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=20"
|
|
42
42
|
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"tar": "^7.5.0"
|
|
45
|
-
},
|
|
43
|
+
"dependencies": {},
|
|
46
44
|
"devDependencies": {
|
|
47
45
|
"@types/node": "^25.8.0",
|
|
48
46
|
"tsup": "^8.5.0",
|
|
@@ -52,6 +50,7 @@
|
|
|
52
50
|
"sync:template": "node scripts/sync-template.mjs",
|
|
53
51
|
"build": "pnpm sync:template && tsup",
|
|
54
52
|
"dev": "tsup --watch",
|
|
55
|
-
"typecheck": "tsc --noEmit"
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"test": "pnpm build && node --test tests/*.test.mjs"
|
|
56
55
|
}
|
|
57
56
|
}
|
package/template/core/AGENTS.md
CHANGED
|
@@ -16,15 +16,19 @@ will rewrite it.
|
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npm run dev # workbench at http://127.0.0.1:5173/?dev=1
|
|
19
|
-
npm run
|
|
20
|
-
npm run openpress:
|
|
19
|
+
npm run build # validate + render dist-react/
|
|
20
|
+
npm run openpress:image # write dist-react/images/page-*.png
|
|
21
21
|
npm run openpress:pdf # write document.pdf
|
|
22
|
-
npm run openpress:export # write public/openpress/document.json
|
|
23
22
|
npm run openpress:deploy # deploy via the configured adapter
|
|
24
23
|
npx open-press doctor # current vs latest version + pending migrations
|
|
25
24
|
npx open-press upgrade # apply the upgrade flow (see below)
|
|
26
25
|
```
|
|
27
26
|
|
|
27
|
+
The intermediate engine steps live behind `node engine/cli.mjs` if you
|
|
28
|
+
need them directly (rarely): `validate` (source-level checks only,
|
|
29
|
+
fast), `export` (write `public/openpress/document.json` without
|
|
30
|
+
bundling), `inspect` (post-render geometry + comment markers).
|
|
31
|
+
|
|
28
32
|
## When the user asks to upgrade
|
|
29
33
|
|
|
30
34
|
Triggers: "升級 / 套件更新 / upgrade open-press / apply latest design /
|
|
@@ -44,8 +48,7 @@ update to vX.Y.Z" etc.
|
|
|
44
48
|
Apply to `document/` with user confirmation.
|
|
45
49
|
5. Verify:
|
|
46
50
|
```bash
|
|
47
|
-
npm run
|
|
48
|
-
npm run openpress:render
|
|
51
|
+
npm run build
|
|
49
52
|
```
|
|
50
53
|
Fix anything broken using the migration notes.
|
|
51
54
|
6. Report to the user: starting version → ending version, what was
|
|
@@ -73,32 +76,33 @@ regenerate `document.json`. So edits to:
|
|
|
73
76
|
the change:
|
|
74
77
|
|
|
75
78
|
```bash
|
|
76
|
-
npm run
|
|
79
|
+
npm run build # validate + render (includes the export step)
|
|
77
80
|
# then refresh the browser
|
|
81
|
+
# — or, for the inner export only, without the full Vite bundle step:
|
|
82
|
+
node engine/cli.mjs export .
|
|
78
83
|
```
|
|
79
84
|
|
|
80
85
|
Quick rules of thumb:
|
|
81
86
|
|
|
82
87
|
- Pure CSS edits under `document/theme/` that don't move blocks → HMR
|
|
83
88
|
is enough (CSS is hot-replaced).
|
|
84
|
-
- Anything that affects content, pagination, or metadata → re-
|
|
85
|
-
|
|
86
|
-
works to refresh the JSON.
|
|
89
|
+
- Anything that affects content, pagination, or metadata → re-build (or
|
|
90
|
+
call `node engine/cli.mjs export .` for just the JSON refresh).
|
|
87
91
|
|
|
88
92
|
**Agent SOP**: after applying any non-CSS edit to `document/`, run
|
|
89
|
-
`npm run
|
|
90
|
-
|
|
93
|
+
`npm run build` before telling the user "done". If they ask "why
|
|
94
|
+
didn't my change show up?", check whether `document.json` was
|
|
91
95
|
regenerated since the edit.
|
|
92
96
|
|
|
93
97
|
## When the user reports a render / paginate issue
|
|
94
98
|
|
|
95
99
|
Press Tree paginates at build time. Common things to check:
|
|
96
100
|
|
|
97
|
-
1. `npm run
|
|
101
|
+
1. `npm run build` then inspect
|
|
98
102
|
`public/openpress/document.json` for `source.warnings` (chain
|
|
99
103
|
overflow, missing chains, etc.).
|
|
100
|
-
2. `
|
|
101
|
-
(missing entries, broken anchors).
|
|
104
|
+
2. `node engine/cli.mjs validate .` for structural issues
|
|
105
|
+
(missing entries, broken anchors) — faster than a full build.
|
|
102
106
|
3. `npm run dev` and use the workbench inspector to find which MDX
|
|
103
107
|
block / Frame element is misbehaving — comments and inline
|
|
104
108
|
annotations work directly from there.
|