@open-press/create 2.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 +13 -0
- package/dist/index.js +692 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @open-press/create
|
|
2
|
+
|
|
3
|
+
Bootstrap a new OpenPress workspace.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create @open-press my-deck -- --type slides
|
|
7
|
+
cd my-deck
|
|
8
|
+
npm run dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This package writes the workspace `package.json`, `.gitignore`, and a minimal folder-per-slide Press under `press/<name>/`. It can install dependencies, sync OpenPress skills, and initialize git unless those steps are skipped with flags. The scaffold uses the OpenPress 2.0 slides folder contract and Tailwind-ready runtime.
|
|
12
|
+
|
|
13
|
+
Page-based scaffolding is intentionally handled by the OpenPress skills layer. The create package keeps the installable workspace bootstrap small and delegates richer document structure to agents that can read the current skills.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import path3 from "path";
|
|
5
|
+
import process2 from "process";
|
|
6
|
+
import prompts from "prompts";
|
|
7
|
+
|
|
8
|
+
// src/slides-template.ts
|
|
9
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
10
|
+
import path from "path";
|
|
11
|
+
async function writeSlidesPress(pressRoot, opts) {
|
|
12
|
+
const { pressName, title } = opts;
|
|
13
|
+
const folder = folderName(pressName);
|
|
14
|
+
const component = componentName(folder);
|
|
15
|
+
const escapedTitle = escapeJsxAttr(title);
|
|
16
|
+
await mkdir(path.join(pressRoot, "slides", "intro"), { recursive: true });
|
|
17
|
+
await mkdir(path.join(pressRoot, "components"), { recursive: true });
|
|
18
|
+
await mkdir(path.join(pressRoot, "layouts"), { recursive: true });
|
|
19
|
+
await mkdir(path.join(pressRoot, "themes"), { recursive: true });
|
|
20
|
+
await writeFile(
|
|
21
|
+
path.join(pressRoot, "press.tsx"),
|
|
22
|
+
`import { Press, Slide } from "@open-press/core";
|
|
23
|
+
|
|
24
|
+
export default function ${component}Press() {
|
|
25
|
+
return (
|
|
26
|
+
<Press
|
|
27
|
+
slug="${folder}"
|
|
28
|
+
title="${escapedTitle}"
|
|
29
|
+
type="slides"
|
|
30
|
+
page="slide-16-9"
|
|
31
|
+
componentsDir="./components"
|
|
32
|
+
>
|
|
33
|
+
<Slide id="intro" />
|
|
34
|
+
</Press>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
`,
|
|
38
|
+
"utf8"
|
|
39
|
+
);
|
|
40
|
+
await writeFile(
|
|
41
|
+
path.join(pressRoot, "components", "DeckSlide.tsx"),
|
|
42
|
+
`import { PageFolio, Slide } from "@open-press/core";
|
|
43
|
+
import type { ReactNode } from "react";
|
|
44
|
+
|
|
45
|
+
export type DeckSlideVariant = "cover" | "agenda" | "content" | "process" | "closing";
|
|
46
|
+
|
|
47
|
+
const SLIDE_PAGE_CLASS = "op-slide-page bg-bg text-text [font-family:var(--font-body)]";
|
|
48
|
+
const SLIDE_SHELL_CLASS = "op-slide-shell relative h-full w-full overflow-hidden bg-bg";
|
|
49
|
+
const CHROME_HEADER_CLASS =
|
|
50
|
+
"op-slide-chrome-header absolute left-0 right-0 top-0 z-10 grid h-[66px] items-center border-b border-border bg-bg/95 pl-op-sm text-op-caption text-text-muted [grid-template-columns:minmax(0,1fr)_1px_218px]";
|
|
51
|
+
const CHROME_RULE_CLASS = "op-slide-chrome-rule h-[42px] w-px bg-border";
|
|
52
|
+
const WORDMARK_CLASS =
|
|
53
|
+
"op-slide-wordmark justify-self-center font-mono text-op-caption font-bold text-text";
|
|
54
|
+
const SLIDE_MAIN_CLASS = "op-slide-main absolute left-0 right-0 z-[1] top-[66px] bottom-[64px]";
|
|
55
|
+
const CHROME_FOOTER_CLASS =
|
|
56
|
+
"op-slide-chrome-footer absolute bottom-0 left-0 right-0 z-10 grid h-[64px] items-center border-t border-border bg-bg/95 pl-op-sm text-op-caption text-text-muted [grid-template-columns:minmax(0,1fr)_62px]";
|
|
57
|
+
const FOOTER_LABEL_CLASS =
|
|
58
|
+
"op-slide-footer-label overflow-hidden text-ellipsis whitespace-nowrap text-op-caption text-text-muted";
|
|
59
|
+
const FOOTER_NUMBER_CLASS =
|
|
60
|
+
"op-slide-footer-number grid h-[64px] place-items-center border-l border-border text-op-caption text-text-muted [font-variant-numeric:tabular-nums]";
|
|
61
|
+
|
|
62
|
+
// Edit this file to customise the chrome for your deck:
|
|
63
|
+
// - Change the header title / wordmark
|
|
64
|
+
// - Swap the footer label
|
|
65
|
+
// - Adjust header/footer height constants above
|
|
66
|
+
export function DeckSlide({
|
|
67
|
+
id,
|
|
68
|
+
variant = "content",
|
|
69
|
+
children,
|
|
70
|
+
}: {
|
|
71
|
+
id: string;
|
|
72
|
+
variant?: DeckSlideVariant;
|
|
73
|
+
children: ReactNode;
|
|
74
|
+
}) {
|
|
75
|
+
return (
|
|
76
|
+
<Slide id={id} className={SLIDE_PAGE_CLASS}>
|
|
77
|
+
<div className={SLIDE_SHELL_CLASS} data-variant={variant}>
|
|
78
|
+
<header className={CHROME_HEADER_CLASS}>
|
|
79
|
+
<span>${escapedTitle}</span>
|
|
80
|
+
<span className={CHROME_RULE_CLASS} />
|
|
81
|
+
<span className={WORDMARK_CLASS}>open-press</span>
|
|
82
|
+
</header>
|
|
83
|
+
<main className={SLIDE_MAIN_CLASS}>{children}</main>
|
|
84
|
+
<footer className={CHROME_FOOTER_CLASS}>
|
|
85
|
+
<span className={FOOTER_LABEL_CLASS}>${escapedTitle}</span>
|
|
86
|
+
<PageFolio currentFormat="plain" className={FOOTER_NUMBER_CLASS} />
|
|
87
|
+
</footer>
|
|
88
|
+
</div>
|
|
89
|
+
</Slide>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default DeckSlide;
|
|
94
|
+
`,
|
|
95
|
+
"utf8"
|
|
96
|
+
);
|
|
97
|
+
await writeFile(
|
|
98
|
+
path.join(pressRoot, "layouts", "SlideProtocol.tsx"),
|
|
99
|
+
SLIDE_PROTOCOL_SOURCE,
|
|
100
|
+
"utf8"
|
|
101
|
+
);
|
|
102
|
+
await writeFile(
|
|
103
|
+
path.join(pressRoot, "slides", "intro", "slide.tsx"),
|
|
104
|
+
`import type { SlideMeta } from "@open-press/core";
|
|
105
|
+
import { BlankSlide } from "../layouts/SlideProtocol";
|
|
106
|
+
|
|
107
|
+
export const meta = {
|
|
108
|
+
layout: "blank",
|
|
109
|
+
description: "Intro slide \u2014 replace with TitleSlide or another layout.",
|
|
110
|
+
} satisfies SlideMeta;
|
|
111
|
+
|
|
112
|
+
export const notes = "First slide. Edit this file to get started.";
|
|
113
|
+
|
|
114
|
+
export default function Slide() {
|
|
115
|
+
return (
|
|
116
|
+
<BlankSlide id="intro">
|
|
117
|
+
<BlankSlide.Kicker>${escapedTitle}</BlankSlide.Kicker>
|
|
118
|
+
<BlankSlide.Title>Start here.</BlankSlide.Title>
|
|
119
|
+
<BlankSlide.Body>Replace this with your content.</BlankSlide.Body>
|
|
120
|
+
</BlankSlide>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
`,
|
|
124
|
+
"utf8"
|
|
125
|
+
);
|
|
126
|
+
await writeFile(path.join(pressRoot, "themes", "default.css"), `/* ${folder} theme */
|
|
127
|
+
`, "utf8");
|
|
128
|
+
}
|
|
129
|
+
var SLIDE_PROTOCOL_SOURCE = `import { Text, type TextProps } from "@open-press/core";
|
|
130
|
+
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
131
|
+
import { DeckSlide, type DeckSlideVariant } from "../components/DeckSlide";
|
|
132
|
+
|
|
133
|
+
type SlideRootProps = {
|
|
134
|
+
id: string;
|
|
135
|
+
children: ReactNode;
|
|
136
|
+
} & Omit<ComponentPropsWithoutRef<"section">, "id" | "children">;
|
|
137
|
+
|
|
138
|
+
type SlotProps = TextProps & { children?: ReactNode };
|
|
139
|
+
type BoxEl = "div" | "ol" | "li" | "figure" | "figcaption" | "article";
|
|
140
|
+
type BoxProps<T extends BoxEl = "div"> = ComponentPropsWithoutRef<T> & { children?: ReactNode };
|
|
141
|
+
|
|
142
|
+
function cx(...parts: Array<string | false | null | undefined>) {
|
|
143
|
+
return parts.filter(Boolean).join(" ");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function SlotText({ as = "p", className, children, ...rest }: SlotProps) {
|
|
147
|
+
return <Text {...rest} as={as} className={className}>{children}</Text>;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function ProtocolSlide({ id, variant, children }: SlideRootProps & { variant: DeckSlideVariant }) {
|
|
151
|
+
return <DeckSlide id={id} variant={variant}>{children}</DeckSlide>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// \u2500\u2500\u2500 TitleSlide \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
155
|
+
|
|
156
|
+
function TitleSlideRoot({ id, className, children, ...rest }: SlideRootProps) {
|
|
157
|
+
return (
|
|
158
|
+
<ProtocolSlide id={id} variant="cover">
|
|
159
|
+
<section {...rest} className={cx("op-slide-title-layout grid h-full items-end gap-op-xl px-op-xl pb-op-xl pt-op-lg [grid-template-columns:minmax(0,1fr)_500px]", className)}>
|
|
160
|
+
{children}
|
|
161
|
+
</section>
|
|
162
|
+
</ProtocolSlide>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function TitleSlideContent({ className, children, ...rest }: BoxProps) {
|
|
167
|
+
return <div {...rest} className={cx("op-slide-title-copy max-w-[960px] pb-op-xs", className)}>{children}</div>;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function TitleSlideKicker({ className, children, ...rest }: SlotProps) {
|
|
171
|
+
return <SlotText {...rest} as="p" className={cx("op-kicker mb-op-sm", className)}>{children}</SlotText>;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function TitleSlideTitle({ className, children, ...rest }: SlotProps) {
|
|
175
|
+
return <SlotText {...rest} as="h1" className={cx("op-display max-w-[960px]", className)}>{children}</SlotText>;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function TitleSlideSubtitle({ className, children, ...rest }: SlotProps) {
|
|
179
|
+
return <SlotText {...rest} as="p" className={cx("op-lead mt-op-sm max-w-[900px] font-semibold", className)}>{children}</SlotText>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function TitleSlideMedia({ className, children, ...rest }: BoxProps<"figure">) {
|
|
183
|
+
return <figure {...rest} className={cx("op-slide-title-media relative h-[660px] w-[500px] self-center overflow-hidden rounded-op-panel border border-border bg-surface-muted shadow-op-card", className)}>{children}</figure>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function TitleSlideImage({ className, ...rest }: ComponentPropsWithoutRef<"img">) {
|
|
187
|
+
return <img {...rest} className={cx("h-full w-full object-cover object-[58%_50%]", className)} />;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function TitleSlideMediaCaption({ className, children, ...rest }: BoxProps<"figcaption">) {
|
|
191
|
+
return <figcaption {...rest} className={cx("absolute bottom-op-sm left-op-sm inline-flex items-center rounded-op-pill border border-text-muted bg-surface-inverse px-op-sm py-op-xs text-op-caption font-medium text-text-inverse", className)}>{children}</figcaption>;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// \u2500\u2500\u2500 StatementSlide \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
195
|
+
|
|
196
|
+
function StatementSlideRoot({ id, className, children, ...rest }: SlideRootProps) {
|
|
197
|
+
return (
|
|
198
|
+
<ProtocolSlide id={id} variant="closing">
|
|
199
|
+
<section {...rest} className={cx("op-slide-statement-layout grid h-full content-center gap-op-xl px-op-lg py-op-lg [grid-template-columns:minmax(0,1fr)_520px]", className)}>
|
|
200
|
+
{children}
|
|
201
|
+
</section>
|
|
202
|
+
</ProtocolSlide>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function StatementSlideKicker({ className, children, ...rest }: SlotProps) {
|
|
207
|
+
return <SlotText {...rest} as="p" className={cx("op-kicker col-span-2", className)}>{children}</SlotText>;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function StatementSlideStatement({ className, children, ...rest }: SlotProps) {
|
|
211
|
+
return <SlotText {...rest} as="h2" className={cx("op-section max-w-[850px]", className)}>{children}</SlotText>;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function StatementSlideSupport({ className, children, ...rest }: BoxProps) {
|
|
215
|
+
return <div {...rest} className={cx("op-card-muted grid self-center gap-op-sm", className)}>{children}</div>;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function StatementSlideSupportText({ className, children, ...rest }: SlotProps) {
|
|
219
|
+
return <SlotText {...rest} as="p" className={cx("op-body font-bold", className)}>{children}</SlotText>;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// \u2500\u2500\u2500 BlankSlide \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
223
|
+
|
|
224
|
+
function BlankSlideRoot({ id, className, children, ...rest }: SlideRootProps) {
|
|
225
|
+
return (
|
|
226
|
+
<ProtocolSlide id={id} variant="content">
|
|
227
|
+
<section {...rest} className={cx("op-slide-blank-layout grid h-full place-items-center bg-bg px-op-xl py-op-xl text-center text-text", className)}>
|
|
228
|
+
<div className="op-slide-blank-copy max-w-[900px]">{children}</div>
|
|
229
|
+
</section>
|
|
230
|
+
</ProtocolSlide>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function BlankSlideKicker({ className, children, ...rest }: SlotProps) {
|
|
235
|
+
return <SlotText {...rest} as="p" className={cx("op-kicker mb-op-sm", className)}>{children}</SlotText>;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function BlankSlideTitle({ className, children, ...rest }: SlotProps) {
|
|
239
|
+
return <SlotText {...rest} as="h1" className={cx("op-display", className)}>{children}</SlotText>;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function BlankSlideBody({ className, children, ...rest }: SlotProps) {
|
|
243
|
+
return <SlotText {...rest} as="p" className={cx("op-lead mt-op-sm", className)}>{children}</SlotText>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// \u2500\u2500\u2500 TwoColumnSlide \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
247
|
+
|
|
248
|
+
function TwoColumnSlideRoot({ id, className, children, ...rest }: SlideRootProps) {
|
|
249
|
+
return (
|
|
250
|
+
<ProtocolSlide id={id} variant="agenda">
|
|
251
|
+
<section {...rest} className={cx("op-slide-two-column-layout grid h-full items-start gap-op-xl px-op-xl py-op-xl [grid-template-columns:470px_minmax(0,1fr)]", className)}>
|
|
252
|
+
{children}
|
|
253
|
+
</section>
|
|
254
|
+
</ProtocolSlide>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function TwoColumnSlideKicker({ className, children, ...rest }: SlotProps) {
|
|
259
|
+
return <SlotText {...rest} as="p" className={cx("op-kicker mb-op-sm", className)}>{children}</SlotText>;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function TwoColumnSlideTitle({ className, children, ...rest }: SlotProps) {
|
|
263
|
+
return <SlotText {...rest} as="h2" className={cx("op-section", className)}>{children}</SlotText>;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function TwoColumnSlideLeft({ className, children, ...rest }: BoxProps) {
|
|
267
|
+
return <div {...rest} className={cx("min-w-0", className)}>{children}</div>;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function TwoColumnSlideRight({ className, children, ...rest }: BoxProps) {
|
|
271
|
+
return <div {...rest} className={cx("min-w-0", className)}>{children}</div>;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function TwoColumnSlideList({ className, children, ...rest }: BoxProps<"ol">) {
|
|
275
|
+
return <ol {...rest} className={cx("m-0 grid list-none gap-op-sm p-0", className)}>{children}</ol>;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function TwoColumnSlideItem({ className, children, ...rest }: BoxProps<"li">) {
|
|
279
|
+
return <li {...rest} className={cx("grid gap-op-sm border-t border-border py-op-sm [grid-template-columns:96px_minmax(0,1fr)]", className)}>{children}</li>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function TwoColumnSlideItemNumber({ className, children, ...rest }: SlotProps) {
|
|
283
|
+
return <SlotText {...rest} as="span" className={cx("font-heading text-op-title text-accent", className)}>{children}</SlotText>;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function TwoColumnSlideItemCopy({ className, children, ...rest }: BoxProps) {
|
|
287
|
+
return <div {...rest} className={className}>{children}</div>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function TwoColumnSlideItemTitle({ className, children, ...rest }: SlotProps) {
|
|
291
|
+
return <SlotText {...rest} as="h3" className={cx("op-lead font-bold text-text", className)}>{children}</SlotText>;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function TwoColumnSlideItemBody({ className, children, ...rest }: SlotProps) {
|
|
295
|
+
return <SlotText {...rest} as="p" className={cx("op-body mt-op-xs", className)}>{children}</SlotText>;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// \u2500\u2500\u2500 CardGridSlide \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
299
|
+
|
|
300
|
+
function CardGridSlideRoot({ id, className, children, ...rest }: SlideRootProps) {
|
|
301
|
+
return (
|
|
302
|
+
<ProtocolSlide id={id} variant="content">
|
|
303
|
+
<section {...rest} className={cx("h-full px-op-xl py-op-xl", className)}>{children}</section>
|
|
304
|
+
</ProtocolSlide>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function CardGridSlideHeading({ className, children, ...rest }: BoxProps) {
|
|
309
|
+
return <div {...rest} className={cx("max-w-[1120px]", className)}>{children}</div>;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function CardGridSlideKicker({ className, children, ...rest }: SlotProps) {
|
|
313
|
+
return <SlotText {...rest} as="p" className={cx("op-kicker mb-op-sm", className)}>{children}</SlotText>;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function CardGridSlideTitle({ className, children, ...rest }: SlotProps) {
|
|
317
|
+
return <SlotText {...rest} as="h2" className={cx("op-section", className)}>{children}</SlotText>;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function CardGridSlideGrid({ className, children, ...rest }: BoxProps) {
|
|
321
|
+
return <div {...rest} className={cx("mt-op-lg grid grid-cols-3 gap-op-sm", className)}>{children}</div>;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function CardGridSlideCard({ className, children, ...rest }: BoxProps<"article">) {
|
|
325
|
+
return <article {...rest} className={cx("op-card-muted min-h-[230px] border-t-4 border-t-text", className)}>{children}</article>;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function CardGridSlideLabel({ className, children, ...rest }: SlotProps) {
|
|
329
|
+
return <SlotText {...rest} as="span" className={cx("op-kicker mb-op-sm block", className)}>{children}</SlotText>;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function CardGridSlideCardTitle({ className, children, ...rest }: SlotProps) {
|
|
333
|
+
return <SlotText {...rest} as="h3" className={cx("op-lead font-bold text-text", className)}>{children}</SlotText>;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function CardGridSlideCardBody({ className, children, ...rest }: SlotProps) {
|
|
337
|
+
return <SlotText {...rest} as="p" className={cx("op-body mt-op-xs", className)}>{children}</SlotText>;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// \u2500\u2500\u2500 ProcessSlide \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
341
|
+
|
|
342
|
+
function ProcessSlideRoot({ id, className, children, ...rest }: SlideRootProps) {
|
|
343
|
+
return (
|
|
344
|
+
<ProtocolSlide id={id} variant="process">
|
|
345
|
+
<section {...rest} className={cx("grid h-full gap-op-lg px-op-lg py-op-lg [grid-template-rows:auto_minmax(0,1fr)]", className)}>
|
|
346
|
+
{children}
|
|
347
|
+
</section>
|
|
348
|
+
</ProtocolSlide>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function ProcessSlideHeading({ className, children, ...rest }: BoxProps) {
|
|
353
|
+
return <div {...rest} className={cx("max-w-[980px]", className)}>{children}</div>;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function ProcessSlideKicker({ className, children, ...rest }: SlotProps) {
|
|
357
|
+
return <SlotText {...rest} as="p" className={cx("op-kicker mb-op-sm", className)}>{children}</SlotText>;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function ProcessSlideTitle({ className, children, ...rest }: SlotProps) {
|
|
361
|
+
return <SlotText {...rest} as="h2" className={cx("op-section", className)}>{children}</SlotText>;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function ProcessSlideMap({ className, children, ...rest }: BoxProps) {
|
|
365
|
+
return <div {...rest} className={cx("grid grid-cols-4 items-start gap-op-sm bg-surface-muted p-op-lg", className)}>{children}</div>;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function ProcessSlideStep({ className, children, ...rest }: BoxProps<"article">) {
|
|
369
|
+
return <article {...rest} className={cx("min-h-[286px] rounded-op-card border border-border bg-surface-muted p-op-sm", className)}>{children}</article>;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function ProcessSlideStepNumber({ className, children, ...rest }: SlotProps) {
|
|
373
|
+
return <SlotText {...rest} as="span" className={cx("op-title text-accent italic", className)}>{children}</SlotText>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function ProcessSlideStepTitle({ className, children, ...rest }: SlotProps) {
|
|
377
|
+
return <SlotText {...rest} as="h3" className={cx("op-lead mt-op-sm font-bold text-text", className)}>{children}</SlotText>;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function ProcessSlideStepBody({ className, children, ...rest }: SlotProps) {
|
|
381
|
+
return <SlotText {...rest} as="p" className={cx("op-body mt-op-xs", className)}>{children}</SlotText>;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// \u2500\u2500\u2500 Exports \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
385
|
+
|
|
386
|
+
export const TitleSlide = Object.assign(TitleSlideRoot, {
|
|
387
|
+
Content: TitleSlideContent, Image: TitleSlideImage, Kicker: TitleSlideKicker,
|
|
388
|
+
Media: TitleSlideMedia, MediaCaption: TitleSlideMediaCaption,
|
|
389
|
+
Subtitle: TitleSlideSubtitle, Title: TitleSlideTitle,
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
export const StatementSlide = Object.assign(StatementSlideRoot, {
|
|
393
|
+
Kicker: StatementSlideKicker, Statement: StatementSlideStatement,
|
|
394
|
+
Support: StatementSlideSupport, SupportText: StatementSlideSupportText,
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
export const BlankSlide = Object.assign(BlankSlideRoot, {
|
|
398
|
+
Body: BlankSlideBody, Kicker: BlankSlideKicker, Title: BlankSlideTitle,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
export const TwoColumnSlide = Object.assign(TwoColumnSlideRoot, {
|
|
402
|
+
Item: TwoColumnSlideItem, ItemBody: TwoColumnSlideItemBody,
|
|
403
|
+
ItemCopy: TwoColumnSlideItemCopy, ItemNumber: TwoColumnSlideItemNumber,
|
|
404
|
+
ItemTitle: TwoColumnSlideItemTitle, Kicker: TwoColumnSlideKicker,
|
|
405
|
+
Left: TwoColumnSlideLeft, List: TwoColumnSlideList,
|
|
406
|
+
Right: TwoColumnSlideRight, Title: TwoColumnSlideTitle,
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
export const CardGridSlide = Object.assign(CardGridSlideRoot, {
|
|
410
|
+
Body: CardGridSlideCardBody, Card: CardGridSlideCard,
|
|
411
|
+
CardTitle: CardGridSlideCardTitle, Grid: CardGridSlideGrid,
|
|
412
|
+
Heading: CardGridSlideHeading, Kicker: CardGridSlideKicker,
|
|
413
|
+
Label: CardGridSlideLabel, Title: CardGridSlideTitle,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
export const ProcessSlide = Object.assign(ProcessSlideRoot, {
|
|
417
|
+
Body: ProcessSlideStepBody, Heading: ProcessSlideHeading,
|
|
418
|
+
Kicker: ProcessSlideKicker, Map: ProcessSlideMap,
|
|
419
|
+
Step: ProcessSlideStep, StepNumber: ProcessSlideStepNumber,
|
|
420
|
+
StepTitle: ProcessSlideStepTitle, Title: ProcessSlideTitle,
|
|
421
|
+
});
|
|
422
|
+
`;
|
|
423
|
+
function folderName(name) {
|
|
424
|
+
const base = path.basename(name);
|
|
425
|
+
return base.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "press";
|
|
426
|
+
}
|
|
427
|
+
function componentName(folder) {
|
|
428
|
+
return folder.split(/[-_]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join("") || "OpenPress";
|
|
429
|
+
}
|
|
430
|
+
function escapeJsxAttr(value) {
|
|
431
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// src/workspace.ts
|
|
435
|
+
import { spawn } from "child_process";
|
|
436
|
+
import { existsSync } from "fs";
|
|
437
|
+
import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
|
|
438
|
+
import path2 from "path";
|
|
439
|
+
import process from "process";
|
|
440
|
+
import { fileURLToPath } from "url";
|
|
441
|
+
|
|
442
|
+
// src/path-is-empty.ts
|
|
443
|
+
import { readdir, stat } from "fs/promises";
|
|
444
|
+
var HARMLESS_TARGET_ENTRIES = /* @__PURE__ */ new Set([".git", ".gitignore", ".gitkeep", ".DS_Store"]);
|
|
445
|
+
async function pathIsEmpty(target, options = {}) {
|
|
446
|
+
try {
|
|
447
|
+
const s = await stat(target);
|
|
448
|
+
if (!s.isDirectory()) return false;
|
|
449
|
+
const entries = await readdir(target);
|
|
450
|
+
if (!options.ignoreHarmless) return entries.length === 0;
|
|
451
|
+
return entries.every((entry) => HARMLESS_TARGET_ENTRIES.has(entry));
|
|
452
|
+
} catch {
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/workspace.ts
|
|
458
|
+
var __dirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
459
|
+
var OWN_PACKAGE_JSON = path2.resolve(__dirname, "..", "package.json");
|
|
460
|
+
async function ensureTarget(target) {
|
|
461
|
+
if (existsSync(target)) {
|
|
462
|
+
const empty = await pathIsEmpty(target, { ignoreHarmless: true });
|
|
463
|
+
if (!empty) {
|
|
464
|
+
throw new Error(
|
|
465
|
+
`Target ${target} is not empty. Remove existing files first, or scaffold into a different directory.`
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
await mkdir2(target, { recursive: true });
|
|
471
|
+
}
|
|
472
|
+
async function writeWorkspaceFiles(target, workspaceName) {
|
|
473
|
+
const version = await readOwnVersion();
|
|
474
|
+
await writeWorkspacePackageJson(target, workspaceName, version);
|
|
475
|
+
await writeWorkspaceGitignore(target);
|
|
476
|
+
await writeWorkspaceDesignDoc(target, workspaceName);
|
|
477
|
+
}
|
|
478
|
+
async function readOwnVersion() {
|
|
479
|
+
const pkg = JSON.parse(await readFile(OWN_PACKAGE_JSON, "utf8"));
|
|
480
|
+
return typeof pkg.version === "string" && pkg.version ? pkg.version : "latest";
|
|
481
|
+
}
|
|
482
|
+
async function writeWorkspacePackageJson(target, workspaceName, version) {
|
|
483
|
+
const pkg = {
|
|
484
|
+
name: workspaceName,
|
|
485
|
+
version: "0.0.0",
|
|
486
|
+
private: true,
|
|
487
|
+
type: "module",
|
|
488
|
+
description: `open-press workspace: ${workspaceName}`,
|
|
489
|
+
scripts: {
|
|
490
|
+
dev: "open-press dev . --renderer react",
|
|
491
|
+
build: "open-press render . --renderer react",
|
|
492
|
+
preview: "open-press preview . --renderer react",
|
|
493
|
+
typecheck: "open-press typecheck .",
|
|
494
|
+
"openpress:image": "open-press image .",
|
|
495
|
+
"openpress:pdf": "open-press pdf .",
|
|
496
|
+
"openpress:deploy": "open-press deploy .",
|
|
497
|
+
"openpress:deploy:dry-run": "open-press deploy . --confirm --dry-run",
|
|
498
|
+
"openpress:skills": "open-press skills:sync"
|
|
499
|
+
},
|
|
500
|
+
dependencies: {
|
|
501
|
+
"@open-press/core": version
|
|
502
|
+
},
|
|
503
|
+
devDependencies: {
|
|
504
|
+
"@open-press/cli": version,
|
|
505
|
+
"@types/node": "^25.8.0",
|
|
506
|
+
"@types/react": "^19.2.14",
|
|
507
|
+
"@types/react-dom": "^19.2.3",
|
|
508
|
+
typescript: "^6.0.3"
|
|
509
|
+
},
|
|
510
|
+
openpress: {
|
|
511
|
+
pdf: { filename: "document.pdf" },
|
|
512
|
+
deploy: {
|
|
513
|
+
adapter: "cloudflare-pages",
|
|
514
|
+
source: ".deploy/openpress",
|
|
515
|
+
projectName: null,
|
|
516
|
+
commitDirty: false,
|
|
517
|
+
requiresConfirmation: true
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
await writeFile2(path2.join(target, "package.json"), `${JSON.stringify(pkg, null, 2)}
|
|
522
|
+
`, "utf8");
|
|
523
|
+
}
|
|
524
|
+
async function writeWorkspaceGitignore(target) {
|
|
525
|
+
const content = [
|
|
526
|
+
"node_modules/",
|
|
527
|
+
".DS_Store",
|
|
528
|
+
"*.log",
|
|
529
|
+
"",
|
|
530
|
+
"# OpenPress generated artifacts",
|
|
531
|
+
".openpress/",
|
|
532
|
+
".deploy/",
|
|
533
|
+
".turbo/",
|
|
534
|
+
"dist/",
|
|
535
|
+
"dist-react/",
|
|
536
|
+
"public/openpress/",
|
|
537
|
+
"output/",
|
|
538
|
+
""
|
|
539
|
+
].join("\n");
|
|
540
|
+
await writeFile2(path2.join(target, ".gitignore"), content, "utf8");
|
|
541
|
+
}
|
|
542
|
+
async function writeWorkspaceDesignDoc(target, workspaceName) {
|
|
543
|
+
const pressRoot = path2.join(target, "press");
|
|
544
|
+
await mkdir2(pressRoot, { recursive: true });
|
|
545
|
+
const content = `# ${workspaceName} design
|
|
546
|
+
|
|
547
|
+
This workspace uses source-based slide authoring.
|
|
548
|
+
|
|
549
|
+
- Keep \`press.tsx\` as the ordered index of self-closing \`<Slide id />\` markers.
|
|
550
|
+
- Put slide content in \`press/${workspaceName}/slides/<id>/slide.tsx\`.
|
|
551
|
+
- Put reusable slide UI in \`press/${workspaceName}/components/\` or \`press/shared/\`.
|
|
552
|
+
- Update this file when visual rules, layout conventions, or agent constraints change.
|
|
553
|
+
`;
|
|
554
|
+
await writeFile2(path2.join(pressRoot, "design.md"), content, "utf8");
|
|
555
|
+
}
|
|
556
|
+
async function runInTarget(cwd, command, args, opts = {}) {
|
|
557
|
+
return new Promise((resolve, reject) => {
|
|
558
|
+
const child = spawn(command, args, {
|
|
559
|
+
cwd,
|
|
560
|
+
stdio: opts.silent ? ["ignore", "ignore", "ignore"] : "inherit",
|
|
561
|
+
shell: process.platform === "win32"
|
|
562
|
+
});
|
|
563
|
+
child.once("error", reject);
|
|
564
|
+
child.once("close", (code) => {
|
|
565
|
+
if (code === 0) resolve();
|
|
566
|
+
else reject(new Error(`${command} ${args.join(" ")} exited with code ${code}`));
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// src/index.ts
|
|
572
|
+
var FRAMEWORK_SKILLS_SOURCE = "quan0715/open-press";
|
|
573
|
+
async function main(argv) {
|
|
574
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
575
|
+
printHelp();
|
|
576
|
+
return 0;
|
|
577
|
+
}
|
|
578
|
+
let target = argv.find((a) => !a.startsWith("--")) ?? "";
|
|
579
|
+
let type = parseFlag(argv, "--type");
|
|
580
|
+
const title = parseFlag(argv, "--title");
|
|
581
|
+
const noInstall = argv.includes("--no-install");
|
|
582
|
+
const noSkills = argv.includes("--no-skills");
|
|
583
|
+
const noGit = argv.includes("--no-git");
|
|
584
|
+
if (!target) {
|
|
585
|
+
const res = await prompts({
|
|
586
|
+
type: "text",
|
|
587
|
+
name: "target",
|
|
588
|
+
message: "Project name:",
|
|
589
|
+
validate: (value) => value.trim() ? true : "Name is required"
|
|
590
|
+
});
|
|
591
|
+
if (!res.target) return 1;
|
|
592
|
+
target = res.target;
|
|
593
|
+
}
|
|
594
|
+
if (!type) {
|
|
595
|
+
const res = await prompts({
|
|
596
|
+
type: "select",
|
|
597
|
+
name: "type",
|
|
598
|
+
message: "Press type:",
|
|
599
|
+
choices: [
|
|
600
|
+
{ title: "Slides (16:9 deck)", value: "slides" },
|
|
601
|
+
{ title: "Pages (not yet supported)", value: "pages", disabled: true }
|
|
602
|
+
]
|
|
603
|
+
});
|
|
604
|
+
if (!res.type) return 1;
|
|
605
|
+
type = res.type;
|
|
606
|
+
}
|
|
607
|
+
if (type === "pages") {
|
|
608
|
+
process2.stderr.write("--type pages is not yet supported. Use --type slides.\n");
|
|
609
|
+
return 1;
|
|
610
|
+
}
|
|
611
|
+
const opts = {
|
|
612
|
+
target,
|
|
613
|
+
type,
|
|
614
|
+
title,
|
|
615
|
+
install: !noInstall,
|
|
616
|
+
skills: !noSkills,
|
|
617
|
+
git: !noGit
|
|
618
|
+
};
|
|
619
|
+
return run(opts);
|
|
620
|
+
}
|
|
621
|
+
async function run(opts) {
|
|
622
|
+
const targetPath = path3.resolve(process2.cwd(), opts.target);
|
|
623
|
+
const workspaceName = path3.basename(targetPath);
|
|
624
|
+
const pressRoot = path3.join(targetPath, "press", workspaceName);
|
|
625
|
+
log(`Creating open-press workspace at ${targetPath}`);
|
|
626
|
+
await ensureTarget(targetPath);
|
|
627
|
+
await writeWorkspaceFiles(targetPath, workspaceName);
|
|
628
|
+
await writeSlidesPress(pressRoot, {
|
|
629
|
+
pressName: workspaceName,
|
|
630
|
+
title: opts.title ?? workspaceName
|
|
631
|
+
});
|
|
632
|
+
if (opts.skills) {
|
|
633
|
+
log("Installing framework skills...");
|
|
634
|
+
try {
|
|
635
|
+
await runInTarget(targetPath, "npx", ["-y", "skills@latest", "add", FRAMEWORK_SKILLS_SOURCE]);
|
|
636
|
+
} catch (err) {
|
|
637
|
+
log("(skills install failed; retry later: open-press skills:sync)");
|
|
638
|
+
log(` ${err instanceof Error ? err.message : String(err)}`);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (opts.install) {
|
|
642
|
+
log("Installing dependencies (npm install)...");
|
|
643
|
+
await runInTarget(targetPath, "npm", ["install"]);
|
|
644
|
+
}
|
|
645
|
+
if (opts.git) {
|
|
646
|
+
log("Initializing git repository...");
|
|
647
|
+
try {
|
|
648
|
+
await runInTarget(targetPath, "git", ["init"]);
|
|
649
|
+
await runInTarget(targetPath, "git", ["add", "-A"]);
|
|
650
|
+
await runInTarget(targetPath, "git", ["commit", "-m", "Initial commit from @open-press/create"], {
|
|
651
|
+
silent: true
|
|
652
|
+
});
|
|
653
|
+
} catch {
|
|
654
|
+
log("(git not available; skipping)");
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
printNextSteps(targetPath, opts);
|
|
658
|
+
return 0;
|
|
659
|
+
}
|
|
660
|
+
function parseFlag(argv, flag) {
|
|
661
|
+
const i = argv.indexOf(flag);
|
|
662
|
+
return i !== -1 ? argv[i + 1] : void 0;
|
|
663
|
+
}
|
|
664
|
+
function log(msg) {
|
|
665
|
+
process2.stdout.write(`> ${msg}
|
|
666
|
+
`);
|
|
667
|
+
}
|
|
668
|
+
function printHelp() {
|
|
669
|
+
process2.stdout.write(`npm create @open-press <target> -- --type slides [options]
|
|
670
|
+
|
|
671
|
+
Options:
|
|
672
|
+
--type slides Press type. The create package scaffolds slides; page projects are skill-authored.
|
|
673
|
+
--title <s> Document title
|
|
674
|
+
--no-install Skip npm install
|
|
675
|
+
--no-skills Skip agent skill installation
|
|
676
|
+
--no-git Skip git init
|
|
677
|
+
--help Show this help
|
|
678
|
+
`);
|
|
679
|
+
}
|
|
680
|
+
function printNextSteps(target, opts) {
|
|
681
|
+
const rel = path3.relative(process2.cwd(), target) || ".";
|
|
682
|
+
const lines = ["", "Done. Your open-press workspace is ready.", "", "Next steps:", ` cd ${rel}`];
|
|
683
|
+
if (!opts.install) lines.push(" npm install");
|
|
684
|
+
if (!opts.skills) lines.push(" open-press skills:sync");
|
|
685
|
+
lines.push("", " npm run dev", "", "Then open the local URL printed by Vite.", "");
|
|
686
|
+
process2.stdout.write(lines.join("\n"));
|
|
687
|
+
}
|
|
688
|
+
main(process2.argv.slice(2)).then((code) => process2.exit(code)).catch((err) => {
|
|
689
|
+
process2.stderr.write(`${err instanceof Error ? err.stack ?? err.message : String(err)}
|
|
690
|
+
`);
|
|
691
|
+
process2.exit(1);
|
|
692
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@open-press/create",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Bootstrap a new OpenPress workspace.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "quan0715",
|
|
8
|
+
"homepage": "https://github.com/quan0715/open-press#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/quan0715/open-press.git",
|
|
12
|
+
"directory": "packages/create"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/quan0715/open-press/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"document",
|
|
19
|
+
"mdx",
|
|
20
|
+
"react",
|
|
21
|
+
"pdf",
|
|
22
|
+
"fixed-layout",
|
|
23
|
+
"ai-first",
|
|
24
|
+
"scaffold",
|
|
25
|
+
"create"
|
|
26
|
+
],
|
|
27
|
+
"bin": {
|
|
28
|
+
"create-open-press": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"dev": "tsup --watch",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "pnpm build && node --test tests/*.test.mjs",
|
|
42
|
+
"prepack": "pnpm build"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"prompts": "^2.4.2"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^25.8.0",
|
|
52
|
+
"@types/prompts": "^2.4.9",
|
|
53
|
+
"tsup": "^8.5.0",
|
|
54
|
+
"typescript": "^6.0.3"
|
|
55
|
+
}
|
|
56
|
+
}
|