promptslide 0.3.11 → 0.3.12
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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/commands/clone.mjs +231 -0
- package/src/commands/create.mjs +46 -70
- package/src/index.mjs +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.12](https://github.com/prompticeu/promptslide/compare/promptslide-v0.3.11...promptslide-v0.3.12) (2026-03-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add clone command, make create --from always template ([#87](https://github.com/prompticeu/promptslide/issues/87)) ([769fa7e](https://github.com/prompticeu/promptslide/commit/769fa7ed0ee77f7473f49f2eb608ca996142f3fd))
|
|
9
|
+
|
|
3
10
|
## [0.3.11](https://github.com/prompticeu/promptslide/compare/promptslide-v0.3.10...promptslide-v0.3.11) (2026-03-25)
|
|
4
11
|
|
|
5
12
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { existsSync, cpSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"
|
|
2
|
+
import { join, resolve, dirname } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
|
|
5
|
+
import { bold, green, cyan, red, dim, yellow } from "../utils/ansi.mjs"
|
|
6
|
+
import { requireAuth } from "../utils/auth.mjs"
|
|
7
|
+
import { hexToOklch } from "../utils/colors.mjs"
|
|
8
|
+
import { closePrompts } from "../utils/prompts.mjs"
|
|
9
|
+
import { fetchRegistryItem, resolveRegistryDependencies, writeLockfile } from "../utils/registry.mjs"
|
|
10
|
+
import { toPascalCase, replaceDeckConfig } from "../utils/deck-config.mjs"
|
|
11
|
+
import { ensureTsConfig } from "../utils/tsconfig.mjs"
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
14
|
+
const __dirname = dirname(__filename)
|
|
15
|
+
const CLI_ROOT = join(__dirname, "..", "..")
|
|
16
|
+
const TEMPLATE_DIR = join(CLI_ROOT, "templates", "default")
|
|
17
|
+
const CLI_VERSION = JSON.parse(readFileSync(join(CLI_ROOT, "package.json"), "utf-8")).version
|
|
18
|
+
|
|
19
|
+
function replaceInFile(filePath, replacements) {
|
|
20
|
+
let content = readFileSync(filePath, "utf-8")
|
|
21
|
+
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
22
|
+
content = content.replaceAll(placeholder, value)
|
|
23
|
+
}
|
|
24
|
+
writeFileSync(filePath, content, "utf-8")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function clone(args) {
|
|
28
|
+
console.log()
|
|
29
|
+
console.log(` ${bold("promptslide")} ${dim("clone")}`)
|
|
30
|
+
console.log()
|
|
31
|
+
|
|
32
|
+
if (args[0] === "--help" || args[0] === "-h") {
|
|
33
|
+
console.log(` ${bold("Usage:")} promptslide clone ${dim("<deck-slug>")}`)
|
|
34
|
+
console.log()
|
|
35
|
+
console.log(` Clone a published deck to continue working on it locally.`)
|
|
36
|
+
console.log(` The deck stays linked for pull/publish.`)
|
|
37
|
+
console.log()
|
|
38
|
+
console.log(` ${bold("Examples:")}`)
|
|
39
|
+
console.log(` promptslide clone statworx-slide-master`)
|
|
40
|
+
console.log()
|
|
41
|
+
process.exit(0)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const slug = args[0]
|
|
45
|
+
|
|
46
|
+
if (!slug) {
|
|
47
|
+
console.error(` ${red("Error:")} Please provide a deck slug.`)
|
|
48
|
+
console.error(` ${dim("Usage:")} promptslide clone ${dim("<deck-slug>")}`)
|
|
49
|
+
console.log()
|
|
50
|
+
process.exit(1)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 1. Authenticate and fetch deck
|
|
54
|
+
const auth = requireAuth()
|
|
55
|
+
|
|
56
|
+
let item
|
|
57
|
+
try {
|
|
58
|
+
item = await fetchRegistryItem(slug, auth)
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error(` ${red("Error:")} ${err.message}`)
|
|
61
|
+
process.exit(1)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (item.type !== "deck") {
|
|
65
|
+
console.error(` ${red("Error:")} "${slug}" is a ${item.type}, not a deck. Only decks can be cloned.`)
|
|
66
|
+
process.exit(1)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const versionTag = item.version ? ` ${dim(`v${item.version}`)}` : ""
|
|
70
|
+
console.log(` Cloning ${bold(item.title || item.name)}${versionTag}`)
|
|
71
|
+
|
|
72
|
+
if (item.promptslideVersion) {
|
|
73
|
+
const pubParts = item.promptslideVersion.match(/^(\d+)\.(\d+)/)
|
|
74
|
+
const localParts = CLI_VERSION.match(/^(\d+)\.(\d+)/)
|
|
75
|
+
if (pubParts && localParts && pubParts[2] !== localParts[2]) {
|
|
76
|
+
console.log()
|
|
77
|
+
console.log(` ${yellow("⚠")} This deck was published with promptslide ${bold(`v${item.promptslideVersion}`)}`)
|
|
78
|
+
console.log(` You have ${bold(`v${CLI_VERSION}`)} installed — some slides may need updating.`)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log()
|
|
83
|
+
|
|
84
|
+
// 2. Use slug as directory name
|
|
85
|
+
const dirName = slug
|
|
86
|
+
const targetDir = resolve(process.cwd(), dirName)
|
|
87
|
+
|
|
88
|
+
if (existsSync(targetDir)) {
|
|
89
|
+
console.error(` ${red("Error:")} Directory "${dirName}" already exists.`)
|
|
90
|
+
process.exit(1)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 3. Scaffold base template
|
|
94
|
+
cpSync(TEMPLATE_DIR, targetDir, { recursive: true })
|
|
95
|
+
|
|
96
|
+
const projectName = item.title || slug
|
|
97
|
+
const primaryOklch = hexToOklch("#3B82F6")
|
|
98
|
+
|
|
99
|
+
const replacements = [
|
|
100
|
+
{
|
|
101
|
+
path: join(targetDir, "package.json"),
|
|
102
|
+
values: { "{{PROJECT_SLUG}}": dirName, "{{PROJECT_NAME}}": projectName, "{{PROMPTSLIDE_VERSION}}": `^${CLI_VERSION}` }
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
path: join(targetDir, "src", "theme.ts"),
|
|
106
|
+
values: { "{{PROJECT_NAME}}": projectName }
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
path: join(targetDir, "src", "slides", "slide-title.tsx"),
|
|
110
|
+
values: { "{{PROJECT_NAME}}": projectName }
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
path: join(targetDir, "README.md"),
|
|
114
|
+
values: { "{{PROJECT_NAME}}": projectName }
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
path: join(targetDir, "src", "globals.css"),
|
|
118
|
+
values: { "{{PRIMARY_COLOR}}": primaryOklch }
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
for (const { path, values } of replacements) {
|
|
123
|
+
replaceInFile(path, values)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 4. Create lockfile linked to the original deck
|
|
127
|
+
writeLockfile(targetDir, {
|
|
128
|
+
deckSlug: slug,
|
|
129
|
+
deckMeta: { title: "", description: "", tags: [] },
|
|
130
|
+
items: {}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// 5. Resolve dependencies and write deck files
|
|
134
|
+
let resolved
|
|
135
|
+
try {
|
|
136
|
+
resolved = await resolveRegistryDependencies(item, auth, targetDir)
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error(` ${red("Error:")} ${err.message}`)
|
|
139
|
+
process.exit(1)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const regItem of resolved.items) {
|
|
143
|
+
if (!regItem.files?.length) continue
|
|
144
|
+
for (const file of regItem.files) {
|
|
145
|
+
const targetPath = join(targetDir, file.target, file.path)
|
|
146
|
+
const targetFileDir = dirname(targetPath)
|
|
147
|
+
mkdirSync(targetFileDir, { recursive: true })
|
|
148
|
+
|
|
149
|
+
const dataUriPrefix = file.content.match(/^data:[^;]+;base64,/)
|
|
150
|
+
if (dataUriPrefix) {
|
|
151
|
+
writeFileSync(targetPath, Buffer.from(file.content.slice(dataUriPrefix[0].length), "base64"))
|
|
152
|
+
} else {
|
|
153
|
+
writeFileSync(targetPath, file.content, "utf-8")
|
|
154
|
+
}
|
|
155
|
+
console.log(` ${green("✓")} Added ${cyan(file.target + file.path)}`)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 6. Generate deck-config.ts
|
|
160
|
+
if (item.meta?.slides) {
|
|
161
|
+
const slides = item.meta.slides.map(s => ({
|
|
162
|
+
componentName: toPascalCase(s.slug),
|
|
163
|
+
importPath: `@/slides/${s.slug}`,
|
|
164
|
+
steps: s.steps,
|
|
165
|
+
section: s.section
|
|
166
|
+
}))
|
|
167
|
+
replaceDeckConfig(targetDir, slides, {
|
|
168
|
+
transition: item.meta.transition,
|
|
169
|
+
directionalTransition: item.meta.directionalTransition
|
|
170
|
+
})
|
|
171
|
+
console.log(` ${green("✓")} Generated ${cyan("deck-config.ts")} ${dim(`(${slides.length} slides)`)}`)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 7. Add npm dependencies
|
|
175
|
+
if (Object.keys(resolved.npmDeps).length > 0) {
|
|
176
|
+
const pkgList = Object.entries(resolved.npmDeps).map(([name, ver]) => `${name}@${ver}`)
|
|
177
|
+
const pkgPath = join(targetDir, "package.json")
|
|
178
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"))
|
|
179
|
+
for (const [name, ver] of Object.entries(resolved.npmDeps)) {
|
|
180
|
+
pkg.dependencies = pkg.dependencies || {}
|
|
181
|
+
pkg.dependencies[name] = ver
|
|
182
|
+
}
|
|
183
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8")
|
|
184
|
+
console.log(` ${green("✓")} Added ${dim(pkgList.join(", "))} to package.json`)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 8. Fetch annotations
|
|
188
|
+
if (item.id) {
|
|
189
|
+
try {
|
|
190
|
+
const annotationsRes = await fetch(`${auth.registry}/api/items/${item.id}/annotations`, {
|
|
191
|
+
headers: { Authorization: `Bearer ${auth.token}`, ...(auth.organizationId ? { "X-Organization-Id": auth.organizationId } : {}) }
|
|
192
|
+
})
|
|
193
|
+
if (annotationsRes.ok) {
|
|
194
|
+
const data = await annotationsRes.json()
|
|
195
|
+
const annotations = data.annotations ?? []
|
|
196
|
+
if (annotations.length > 0) {
|
|
197
|
+
const annotationsFile = { version: 1, annotations: annotations.map(a => ({
|
|
198
|
+
id: a.id,
|
|
199
|
+
slideIndex: a.slideIndex,
|
|
200
|
+
slideTitle: a.slideTitle,
|
|
201
|
+
target: a.target,
|
|
202
|
+
body: a.body,
|
|
203
|
+
createdAt: a.createdAt,
|
|
204
|
+
status: a.status,
|
|
205
|
+
...(a.resolution ? { resolution: a.resolution } : {})
|
|
206
|
+
})) }
|
|
207
|
+
writeFileSync(join(targetDir, "annotations.json"), JSON.stringify(annotationsFile, null, 2) + "\n", "utf-8")
|
|
208
|
+
console.log(` ${green("✓")} Pulled ${cyan("annotations.json")} ${dim(`(${annotations.length} annotation${annotations.length === 1 ? "" : "s"})`)}`)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch {
|
|
212
|
+
// Annotations are non-critical; don't fail the clone
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 9. Generate tsconfig.json
|
|
217
|
+
ensureTsConfig(targetDir)
|
|
218
|
+
|
|
219
|
+
// 10. Success output
|
|
220
|
+
console.log()
|
|
221
|
+
console.log(` ${green("✓")} Cloned ${bold(projectName)} in ${cyan(dirName)}/`)
|
|
222
|
+
console.log()
|
|
223
|
+
console.log(` ${bold("Next steps:")}`)
|
|
224
|
+
console.log()
|
|
225
|
+
console.log(` cd ${dirName}`)
|
|
226
|
+
console.log(` bun install`)
|
|
227
|
+
console.log(` bun run dev`)
|
|
228
|
+
console.log()
|
|
229
|
+
|
|
230
|
+
closePrompts()
|
|
231
|
+
}
|
package/src/commands/create.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { bold, green, cyan, red, dim, yellow } from "../utils/ansi.mjs"
|
|
|
7
7
|
import { requireAuth } from "../utils/auth.mjs"
|
|
8
8
|
import { hexToOklch, isValidHex } from "../utils/colors.mjs"
|
|
9
9
|
import { prompt, confirm, closePrompts } from "../utils/prompts.mjs"
|
|
10
|
-
import { fetchRegistryItem, resolveRegistryDependencies,
|
|
10
|
+
import { fetchRegistryItem, resolveRegistryDependencies, writeLockfile } from "../utils/registry.mjs"
|
|
11
11
|
import { toPascalCase, replaceDeckConfig } from "../utils/deck-config.mjs"
|
|
12
12
|
import { ensureTsConfig } from "../utils/tsconfig.mjs"
|
|
13
13
|
|
|
@@ -48,7 +48,7 @@ export async function create(args) {
|
|
|
48
48
|
let dirName = filteredArgs[0]
|
|
49
49
|
|
|
50
50
|
if (dirName === "--help" || dirName === "-h") {
|
|
51
|
-
console.log(` ${bold("Usage:")} promptslide create ${dim("
|
|
51
|
+
console.log(` ${bold("Usage:")} promptslide create ${dim("[project-directory]")} ${dim("[options]")}`)
|
|
52
52
|
console.log()
|
|
53
53
|
console.log(` Scaffolds a new PromptSlide slide deck project.`)
|
|
54
54
|
console.log()
|
|
@@ -59,11 +59,47 @@ export async function create(args) {
|
|
|
59
59
|
console.log(` ${bold("Examples:")}`)
|
|
60
60
|
console.log(` promptslide create my-pitch-deck`)
|
|
61
61
|
console.log(` promptslide create my-pitch-deck --yes`)
|
|
62
|
-
console.log(` promptslide create
|
|
62
|
+
console.log(` promptslide create --from promptic-pitch-deck`)
|
|
63
63
|
console.log()
|
|
64
64
|
process.exit(0)
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// If --from is specified, fetch the deck info before the directory prompt
|
|
68
|
+
let fromItem = null
|
|
69
|
+
let fromAuth = null
|
|
70
|
+
if (fromSlug) {
|
|
71
|
+
fromAuth = requireAuth()
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
fromItem = await fetchRegistryItem(fromSlug, fromAuth)
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error(` ${red("Error:")} ${err.message}`)
|
|
77
|
+
closePrompts()
|
|
78
|
+
process.exit(1)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (fromItem.type !== "deck") {
|
|
82
|
+
console.error(` ${red("Error:")} "${fromSlug}" is a ${fromItem.type}, not a deck. Use --from with a published deck.`)
|
|
83
|
+
closePrompts()
|
|
84
|
+
process.exit(1)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const versionTag = fromItem.version ? ` ${dim(`v${fromItem.version}`)}` : ""
|
|
88
|
+
console.log(` Using deck ${bold(fromItem.title || fromItem.name)}${versionTag} as template`)
|
|
89
|
+
|
|
90
|
+
if (fromItem.promptslideVersion) {
|
|
91
|
+
const pubParts = fromItem.promptslideVersion.match(/^(\d+)\.(\d+)/)
|
|
92
|
+
const localParts = CLI_VERSION.match(/^(\d+)\.(\d+)/)
|
|
93
|
+
if (pubParts && localParts && pubParts[2] !== localParts[2]) {
|
|
94
|
+
console.log()
|
|
95
|
+
console.log(` ${yellow("⚠")} This deck was published with promptslide ${bold(`v${fromItem.promptslideVersion}`)}`)
|
|
96
|
+
console.log(` You have ${bold(`v${CLI_VERSION}`)} installed — some slides may need updating.`)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log()
|
|
101
|
+
}
|
|
102
|
+
|
|
67
103
|
if (!dirName) {
|
|
68
104
|
if (useDefaults) {
|
|
69
105
|
console.error(` ${red("Error:")} Please provide a project directory name when using --yes.`)
|
|
@@ -156,40 +192,9 @@ export async function create(args) {
|
|
|
156
192
|
|
|
157
193
|
// 7. Overlay deck files if --from was specified
|
|
158
194
|
if (fromSlug) {
|
|
159
|
-
const auth = requireAuth()
|
|
160
|
-
|
|
161
|
-
let item
|
|
162
|
-
try {
|
|
163
|
-
item = await fetchRegistryItem(fromSlug, auth)
|
|
164
|
-
} catch (err) {
|
|
165
|
-
console.error(` ${red("Error:")} ${err.message}`)
|
|
166
|
-
closePrompts()
|
|
167
|
-
process.exit(1)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (item.type !== "deck") {
|
|
171
|
-
console.error(` ${red("Error:")} "${fromSlug}" is a ${item.type}, not a deck. Use --from with a published deck.`)
|
|
172
|
-
closePrompts()
|
|
173
|
-
process.exit(1)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const versionTag = item.version ? ` ${dim(`v${item.version}`)}` : ""
|
|
177
|
-
console.log(` Using deck ${bold(item.title || item.name)}${versionTag}`)
|
|
178
|
-
|
|
179
|
-
// Warn if the deck was published with a different minor version
|
|
180
|
-
if (item.promptslideVersion) {
|
|
181
|
-
const pubParts = item.promptslideVersion.match(/^(\d+)\.(\d+)/)
|
|
182
|
-
const localParts = CLI_VERSION.match(/^(\d+)\.(\d+)/)
|
|
183
|
-
if (pubParts && localParts && pubParts[2] !== localParts[2]) {
|
|
184
|
-
console.log()
|
|
185
|
-
console.log(` ${yellow("⚠")} This deck was published with promptslide ${bold(`v${item.promptslideVersion}`)}`)
|
|
186
|
-
console.log(` You have ${bold(`v${CLI_VERSION}`)} installed — some slides may need updating.`)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
195
|
let resolved
|
|
191
196
|
try {
|
|
192
|
-
resolved = await resolveRegistryDependencies(
|
|
197
|
+
resolved = await resolveRegistryDependencies(fromItem, fromAuth, targetDir)
|
|
193
198
|
} catch (err) {
|
|
194
199
|
console.error(` ${red("Error:")} ${err.message}`)
|
|
195
200
|
closePrompts()
|
|
@@ -215,16 +220,16 @@ export async function create(args) {
|
|
|
215
220
|
}
|
|
216
221
|
|
|
217
222
|
// Reconstruct deck-config.ts from deckConfig metadata
|
|
218
|
-
if (
|
|
219
|
-
const slides =
|
|
223
|
+
if (fromItem.meta?.slides) {
|
|
224
|
+
const slides = fromItem.meta.slides.map(s => ({
|
|
220
225
|
componentName: toPascalCase(s.slug),
|
|
221
226
|
importPath: `@/slides/${s.slug}`,
|
|
222
227
|
steps: s.steps,
|
|
223
228
|
section: s.section
|
|
224
229
|
}))
|
|
225
230
|
replaceDeckConfig(targetDir, slides, {
|
|
226
|
-
transition:
|
|
227
|
-
directionalTransition:
|
|
231
|
+
transition: fromItem.meta.transition,
|
|
232
|
+
directionalTransition: fromItem.meta.directionalTransition
|
|
228
233
|
})
|
|
229
234
|
console.log(` ${green("✓")} Generated ${cyan("deck-config.ts")} ${dim(`(${slides.length} slides)`)}`)
|
|
230
235
|
}
|
|
@@ -242,37 +247,8 @@ export async function create(args) {
|
|
|
242
247
|
console.log(` ${green("✓")} Added ${dim(pkgList.join(", "))} to package.json`)
|
|
243
248
|
}
|
|
244
249
|
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// Fetch annotations from registry
|
|
249
|
-
if (item.id) {
|
|
250
|
-
try {
|
|
251
|
-
const annotationsRes = await fetch(`${auth.registry}/api/items/${item.id}/annotations`, {
|
|
252
|
-
headers: { Authorization: `Bearer ${auth.token}`, ...(auth.organizationId ? { "X-Organization-Id": auth.organizationId } : {}) }
|
|
253
|
-
})
|
|
254
|
-
if (annotationsRes.ok) {
|
|
255
|
-
const data = await annotationsRes.json()
|
|
256
|
-
const annotations = data.annotations ?? []
|
|
257
|
-
if (annotations.length > 0) {
|
|
258
|
-
const annotationsFile = { version: 1, annotations: annotations.map(a => ({
|
|
259
|
-
id: a.id,
|
|
260
|
-
slideIndex: a.slideIndex,
|
|
261
|
-
slideTitle: a.slideTitle,
|
|
262
|
-
target: a.target,
|
|
263
|
-
body: a.body,
|
|
264
|
-
createdAt: a.createdAt,
|
|
265
|
-
status: a.status,
|
|
266
|
-
...(a.resolution ? { resolution: a.resolution } : {})
|
|
267
|
-
})) }
|
|
268
|
-
writeFileSync(join(targetDir, "annotations.json"), JSON.stringify(annotationsFile, null, 2) + "\n", "utf-8")
|
|
269
|
-
console.log(` ${green("✓")} Pulled ${cyan("annotations.json")} ${dim(`(${annotations.length} annotation${annotations.length === 1 ? "" : "s"})`)}`)
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
} catch {
|
|
273
|
-
// Annotations are non-critical; don't fail the create
|
|
274
|
-
}
|
|
275
|
-
}
|
|
250
|
+
// New deck identity — deckSlug stays as dirName (set in initial lockfile scaffold)
|
|
251
|
+
console.log(` ${dim("Deck slug set to")} ${bold(dirName)} ${dim("(publish will create a new deck)")}`)
|
|
276
252
|
|
|
277
253
|
console.log()
|
|
278
254
|
}
|
package/src/index.mjs
CHANGED
|
@@ -20,6 +20,7 @@ function printHelp() {
|
|
|
20
20
|
console.log()
|
|
21
21
|
console.log(` ${bold("Commands:")}`)
|
|
22
22
|
console.log(` create ${dim("<dir>")} Scaffold a new slide deck project`)
|
|
23
|
+
console.log(` clone ${dim("<slug>")} Clone a published deck to work on it`)
|
|
23
24
|
console.log(` studio Start the development studio`)
|
|
24
25
|
console.log(` build Build for production`)
|
|
25
26
|
console.log(` preview Preview the production build`)
|
|
@@ -55,6 +56,11 @@ switch (command) {
|
|
|
55
56
|
await create(args)
|
|
56
57
|
break
|
|
57
58
|
}
|
|
59
|
+
case "clone": {
|
|
60
|
+
const { clone } = await import("./commands/clone.mjs")
|
|
61
|
+
await clone(args)
|
|
62
|
+
break
|
|
63
|
+
}
|
|
58
64
|
case "studio": {
|
|
59
65
|
const { studio } = await import("./commands/studio.mjs")
|
|
60
66
|
await studio(args)
|