promptslide 0.3.16 → 0.3.18
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 +14 -0
- package/README.md +16 -12
- package/dist/index.d.ts +17 -7
- package/dist/index.js +1072 -225
- package/dist/index.js.map +1 -1
- package/package.json +31 -31
- package/src/commands/clone.mjs +54 -21
- package/src/commands/pull.mjs +57 -27
- package/src/commands/studio.mjs +7 -3
- package/src/commands/to-image.mjs +18 -5
- package/src/commands/to-pdf.mjs +27 -0
- package/src/core/animated.tsx +3 -3
- package/src/core/annotations/adapters/http.ts +8 -0
- package/src/core/annotations/annotation-overlay.tsx +77 -32
- package/src/core/annotations/annotation-panel.tsx +184 -51
- package/src/core/annotations/types.ts +5 -0
- package/src/core/annotations/use-annotations.ts +57 -7
- package/src/core/slide-deck.tsx +740 -116
- package/src/core/slide-renderer.tsx +3 -8
- package/src/core/transitions.ts +5 -3
- package/src/core/types.ts +2 -0
- package/src/core/use-slide-navigation.ts +93 -3
- package/src/editor/README.md +35 -0
- package/src/editor/geometry.ts +76 -0
- package/src/editor/mount.ts +24 -0
- package/src/editor/plugin.mjs +189 -0
- package/src/editor/source.mjs +363 -0
- package/src/editor/studio.css +1004 -0
- package/src/editor/studio.tsx +1948 -0
- package/src/index.mjs +6 -0
- package/src/utils/deck-config.mjs +30 -11
- package/src/utils/export.mjs +151 -52
- package/src/utils/studio-discovery.mjs +37 -0
- package/src/vite/config.mjs +23 -2
- package/src/vite/plugin.mjs +147 -16
- package/templates/default/AGENTS.md +3 -2
- package/templates/default/src/deck-config.ts +2 -2
package/package.json
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "promptslide",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.18",
|
|
4
4
|
"description": "CLI and slide engine for PromptSlide presentations",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"presentations",
|
|
8
|
+
"promptslide",
|
|
9
|
+
"react",
|
|
10
|
+
"slides",
|
|
11
|
+
"vite"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "prompticeu",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/prompticeu/promptslide.git"
|
|
14
18
|
},
|
|
15
19
|
"bin": {
|
|
16
20
|
"promptslide": "src/index.mjs"
|
|
@@ -22,47 +26,43 @@
|
|
|
22
26
|
"CHANGELOG.md",
|
|
23
27
|
"README.md"
|
|
24
28
|
],
|
|
29
|
+
"type": "module",
|
|
30
|
+
"main": "./dist/index.js",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"import": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./src/commands/*": "./src/commands/*"
|
|
38
|
+
},
|
|
25
39
|
"scripts": {
|
|
26
40
|
"build": "tsup",
|
|
27
41
|
"test": "node --test test/*.test.mjs",
|
|
28
42
|
"prepublishOnly": "npm run build"
|
|
29
43
|
},
|
|
30
|
-
"license": "MIT",
|
|
31
|
-
"repository": {
|
|
32
|
-
"type": "git",
|
|
33
|
-
"url": "git+https://github.com/prompticeu/promptslide.git"
|
|
34
|
-
},
|
|
35
|
-
"author": "prompticeu",
|
|
36
|
-
"keywords": [
|
|
37
|
-
"slides",
|
|
38
|
-
"presentations",
|
|
39
|
-
"cli",
|
|
40
|
-
"react",
|
|
41
|
-
"promptslide",
|
|
42
|
-
"vite"
|
|
43
|
-
],
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@tailwindcss/postcss": "^4.1.13",
|
|
46
46
|
"@vitejs/plugin-react": "^4.5.2",
|
|
47
47
|
"clsx": "^2.1.1",
|
|
48
48
|
"lucide-react": "^0.552.0",
|
|
49
|
+
"playwright": "^1.50.0",
|
|
49
50
|
"postcss": "^8.5.6",
|
|
50
51
|
"tailwind-merge": "^3.3.1",
|
|
51
52
|
"tailwindcss": "^4.1.13",
|
|
52
|
-
"
|
|
53
|
+
"typescript": "^5.9.3",
|
|
53
54
|
"vite": "^6.3.5"
|
|
54
55
|
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/react": "^19.2.14",
|
|
58
|
+
"@types/react-dom": "^19.2.3",
|
|
59
|
+
"tsup": "^8.5.1"
|
|
60
|
+
},
|
|
55
61
|
"peerDependencies": {
|
|
56
62
|
"framer-motion": "^12.0.0",
|
|
57
63
|
"react": "^19.0.0",
|
|
58
64
|
"react-dom": "^19.0.0"
|
|
59
65
|
},
|
|
60
|
-
"devDependencies": {
|
|
61
|
-
"@types/react": "^19.2.14",
|
|
62
|
-
"@types/react-dom": "^19.2.3",
|
|
63
|
-
"tsup": "^8.5.1",
|
|
64
|
-
"typescript": "^5.9.3"
|
|
65
|
-
},
|
|
66
66
|
"engines": {
|
|
67
67
|
"node": ">=18.0.0"
|
|
68
68
|
}
|
package/src/commands/clone.mjs
CHANGED
|
@@ -5,10 +5,14 @@ import { fileURLToPath } from "node:url"
|
|
|
5
5
|
import { bold, green, cyan, red, dim, yellow } from "../utils/ansi.mjs"
|
|
6
6
|
import { requireAuth } from "../utils/auth.mjs"
|
|
7
7
|
import { hexToOklch } from "../utils/colors.mjs"
|
|
8
|
-
import { closePrompts } from "../utils/prompts.mjs"
|
|
9
|
-
import { fetchRegistryItem, resolveRegistryDependencies, writeLockfile } from "../utils/registry.mjs"
|
|
10
8
|
import { toPascalCase, replaceDeckConfig } from "../utils/deck-config.mjs"
|
|
11
9
|
import { collectRegistrySlideSlugs, partitionDeckSlides } from "../utils/deck-sync.mjs"
|
|
10
|
+
import { closePrompts } from "../utils/prompts.mjs"
|
|
11
|
+
import {
|
|
12
|
+
fetchRegistryItem,
|
|
13
|
+
resolveRegistryDependencies,
|
|
14
|
+
writeLockfile
|
|
15
|
+
} from "../utils/registry.mjs"
|
|
12
16
|
import { ensureTsConfig } from "../utils/tsconfig.mjs"
|
|
13
17
|
|
|
14
18
|
const __filename = fileURLToPath(import.meta.url)
|
|
@@ -63,7 +67,9 @@ export async function clone(args) {
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
if (item.type !== "deck") {
|
|
66
|
-
console.error(
|
|
70
|
+
console.error(
|
|
71
|
+
` ${red("Error:")} "${slug}" is a ${item.type}, not a deck. Only decks can be cloned.`
|
|
72
|
+
)
|
|
67
73
|
process.exit(1)
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -75,8 +81,12 @@ export async function clone(args) {
|
|
|
75
81
|
const localParts = CLI_VERSION.match(/^(\d+)\.(\d+)/)
|
|
76
82
|
if (pubParts && localParts && pubParts[2] !== localParts[2]) {
|
|
77
83
|
console.log()
|
|
78
|
-
console.log(
|
|
79
|
-
|
|
84
|
+
console.log(
|
|
85
|
+
` ${yellow("⚠")} This deck was published with promptslide ${bold(`v${item.promptslideVersion}`)}`
|
|
86
|
+
)
|
|
87
|
+
console.log(
|
|
88
|
+
` You have ${bold(`v${CLI_VERSION}`)} installed — some slides may need updating.`
|
|
89
|
+
)
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
92
|
|
|
@@ -100,7 +110,11 @@ export async function clone(args) {
|
|
|
100
110
|
const replacements = [
|
|
101
111
|
{
|
|
102
112
|
path: join(targetDir, "package.json"),
|
|
103
|
-
values: {
|
|
113
|
+
values: {
|
|
114
|
+
"{{PROJECT_SLUG}}": dirName,
|
|
115
|
+
"{{PROJECT_NAME}}": projectName,
|
|
116
|
+
"{{PROMPTSLIDE_VERSION}}": `^${CLI_VERSION}`
|
|
117
|
+
}
|
|
104
118
|
},
|
|
105
119
|
{
|
|
106
120
|
path: join(targetDir, "src", "theme.ts"),
|
|
@@ -149,7 +163,10 @@ export async function clone(args) {
|
|
|
149
163
|
|
|
150
164
|
const dataUriPrefix = file.content.match(/^data:[^;]+;base64,/)
|
|
151
165
|
if (dataUriPrefix) {
|
|
152
|
-
writeFileSync(
|
|
166
|
+
writeFileSync(
|
|
167
|
+
targetPath,
|
|
168
|
+
Buffer.from(file.content.slice(dataUriPrefix[0].length), "base64")
|
|
169
|
+
)
|
|
153
170
|
} else {
|
|
154
171
|
writeFileSync(targetPath, file.content, "utf-8")
|
|
155
172
|
}
|
|
@@ -168,6 +185,7 @@ export async function clone(args) {
|
|
|
168
185
|
const slides = available.map(s => ({
|
|
169
186
|
componentName: s.componentName || toPascalCase(s.slug),
|
|
170
187
|
importPath: `@/slides/${s.slug}`,
|
|
188
|
+
id: s.id,
|
|
171
189
|
steps: s.steps,
|
|
172
190
|
section: s.section
|
|
173
191
|
}))
|
|
@@ -175,7 +193,9 @@ export async function clone(args) {
|
|
|
175
193
|
transition: item.meta.transition,
|
|
176
194
|
directionalTransition: item.meta.directionalTransition
|
|
177
195
|
})
|
|
178
|
-
console.log(
|
|
196
|
+
console.log(
|
|
197
|
+
` ${green("✓")} Generated ${cyan("deck-config.ts")} ${dim(`(${slides.length} slides)`)}`
|
|
198
|
+
)
|
|
179
199
|
}
|
|
180
200
|
|
|
181
201
|
// 7. Add npm dependencies
|
|
@@ -195,24 +215,37 @@ export async function clone(args) {
|
|
|
195
215
|
if (item.id) {
|
|
196
216
|
try {
|
|
197
217
|
const annotationsRes = await fetch(`${auth.registry}/api/items/${item.id}/annotations`, {
|
|
198
|
-
headers: {
|
|
218
|
+
headers: {
|
|
219
|
+
Authorization: `Bearer ${auth.token}`,
|
|
220
|
+
...(auth.organizationId ? { "X-Organization-Id": auth.organizationId } : {})
|
|
221
|
+
}
|
|
199
222
|
})
|
|
200
223
|
if (annotationsRes.ok) {
|
|
201
224
|
const data = await annotationsRes.json()
|
|
202
225
|
const annotations = data.annotations ?? []
|
|
203
226
|
if (annotations.length > 0) {
|
|
204
|
-
const annotationsFile = {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
227
|
+
const annotationsFile = {
|
|
228
|
+
version: 1,
|
|
229
|
+
annotations: annotations.map(a => ({
|
|
230
|
+
id: a.id,
|
|
231
|
+
slideIndex: a.slideIndex,
|
|
232
|
+
...(a.slideId ? { slideId: a.slideId } : {}),
|
|
233
|
+
slideTitle: a.slideTitle,
|
|
234
|
+
target: a.target,
|
|
235
|
+
body: a.body,
|
|
236
|
+
createdAt: a.createdAt,
|
|
237
|
+
status: a.status,
|
|
238
|
+
...(a.resolution ? { resolution: a.resolution } : {})
|
|
239
|
+
}))
|
|
240
|
+
}
|
|
241
|
+
writeFileSync(
|
|
242
|
+
join(targetDir, "annotations.json"),
|
|
243
|
+
JSON.stringify(annotationsFile, null, 2) + "\n",
|
|
244
|
+
"utf-8"
|
|
245
|
+
)
|
|
246
|
+
console.log(
|
|
247
|
+
` ${green("✓")} Pulled ${cyan("annotations.json")} ${dim(`(${annotations.length} annotation${annotations.length === 1 ? "" : "s"})`)}`
|
|
248
|
+
)
|
|
216
249
|
}
|
|
217
250
|
}
|
|
218
251
|
} catch {
|
package/src/commands/pull.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"
|
|
2
1
|
import { execFileSync } from "node:child_process"
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"
|
|
3
3
|
import { join, dirname, resolve, sep } from "node:path"
|
|
4
4
|
|
|
5
5
|
import { bold, green, cyan, red, yellow, dim } from "../utils/ansi.mjs"
|
|
6
6
|
import { requireAuth } from "../utils/auth.mjs"
|
|
7
|
+
import { toPascalCase, addSlideToDeckConfig, replaceDeckConfig } from "../utils/deck-config.mjs"
|
|
8
|
+
import { confirm, closePrompts } from "../utils/prompts.mjs"
|
|
7
9
|
import {
|
|
8
10
|
fetchRegistryItem,
|
|
9
11
|
resolveRegistryDependencies,
|
|
@@ -15,13 +17,7 @@ import {
|
|
|
15
17
|
hashFile,
|
|
16
18
|
isFileDirty
|
|
17
19
|
} from "../utils/registry.mjs"
|
|
18
|
-
import {
|
|
19
|
-
toPascalCase,
|
|
20
|
-
addSlideToDeckConfig,
|
|
21
|
-
replaceDeckConfig
|
|
22
|
-
} from "../utils/deck-config.mjs"
|
|
23
20
|
import { collectRegistrySlideSlugs, partitionDeckSlides } from "../utils/deck-sync.mjs"
|
|
24
|
-
import { confirm, closePrompts } from "../utils/prompts.mjs"
|
|
25
21
|
|
|
26
22
|
export async function pull(args) {
|
|
27
23
|
const cwd = process.cwd()
|
|
@@ -51,7 +47,9 @@ export async function pull(args) {
|
|
|
51
47
|
|
|
52
48
|
if (!deckSlug) {
|
|
53
49
|
console.error(` ${red("Error:")} No deck slug found in lockfile.`)
|
|
54
|
-
console.error(
|
|
50
|
+
console.error(
|
|
51
|
+
` ${dim("Run")} ${cyan("promptslide publish --type deck")} ${dim("first to set it.")}`
|
|
52
|
+
)
|
|
55
53
|
console.log()
|
|
56
54
|
process.exit(1)
|
|
57
55
|
}
|
|
@@ -69,7 +67,9 @@ export async function pull(args) {
|
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
const versionTag = deckItem.version ? ` ${dim(`v${deckItem.version}`)}` : ""
|
|
72
|
-
console.log(
|
|
70
|
+
console.log(
|
|
71
|
+
` Found ${bold(deckItem.title || deckItem.name)} ${dim(`(${deckItem.type})`)}${versionTag}`
|
|
72
|
+
)
|
|
73
73
|
|
|
74
74
|
// Resolve all dependencies (slides, layouts, themes, assets)
|
|
75
75
|
let resolved
|
|
@@ -138,7 +138,10 @@ export async function pull(args) {
|
|
|
138
138
|
}
|
|
139
139
|
} else if (!storedHash) {
|
|
140
140
|
// File exists but not in lockfile (first pull or untracked)
|
|
141
|
-
const overwrite = await confirm(
|
|
141
|
+
const overwrite = await confirm(
|
|
142
|
+
` Overwrite ${relativePath}? ${dim("(local changes will be lost)")}`,
|
|
143
|
+
false
|
|
144
|
+
)
|
|
142
145
|
if (!overwrite) {
|
|
143
146
|
console.log(` ${dim("Skipped")} ${relativePath}`)
|
|
144
147
|
continue
|
|
@@ -151,7 +154,10 @@ export async function pull(args) {
|
|
|
151
154
|
mkdirSync(targetDir, { recursive: true })
|
|
152
155
|
const dataUriPrefix = file.content.match(/^data:[^;]+;base64,/)
|
|
153
156
|
if (dataUriPrefix) {
|
|
154
|
-
writeFileSync(
|
|
157
|
+
writeFileSync(
|
|
158
|
+
targetPath,
|
|
159
|
+
Buffer.from(file.content.slice(dataUriPrefix[0].length), "base64")
|
|
160
|
+
)
|
|
155
161
|
} else {
|
|
156
162
|
writeFileSync(targetPath, file.content, "utf-8")
|
|
157
163
|
}
|
|
@@ -186,6 +192,7 @@ export async function pull(args) {
|
|
|
186
192
|
const slides = available.map(s => ({
|
|
187
193
|
componentName: s.componentName || toPascalCase(s.slug),
|
|
188
194
|
importPath: `@/slides/${s.slug}`,
|
|
195
|
+
id: s.id,
|
|
189
196
|
steps: s.steps,
|
|
190
197
|
section: s.section
|
|
191
198
|
}))
|
|
@@ -193,13 +200,21 @@ export async function pull(args) {
|
|
|
193
200
|
transition: deckItem.meta.transition,
|
|
194
201
|
directionalTransition: deckItem.meta.directionalTransition
|
|
195
202
|
})
|
|
196
|
-
console.log(
|
|
203
|
+
console.log(
|
|
204
|
+
` ${green("+")} Replaced ${cyan("deck-config.ts")} ${dim(`(${slides.length} slides)`)}`
|
|
205
|
+
)
|
|
197
206
|
} else {
|
|
198
207
|
// Append individual slides
|
|
199
208
|
for (const s of available) {
|
|
200
209
|
const componentName = s.componentName || toPascalCase(s.slug)
|
|
201
210
|
const importPath = `@/slides/${s.slug}`
|
|
202
|
-
const updated = addSlideToDeckConfig(cwd, {
|
|
211
|
+
const updated = addSlideToDeckConfig(cwd, {
|
|
212
|
+
componentName,
|
|
213
|
+
importPath,
|
|
214
|
+
steps: s.steps,
|
|
215
|
+
id: s.id,
|
|
216
|
+
section: s.section
|
|
217
|
+
})
|
|
203
218
|
if (updated) {
|
|
204
219
|
console.log(` ${green("+")} Added ${componentName} to ${cyan("deck-config.ts")}`)
|
|
205
220
|
}
|
|
@@ -227,24 +242,37 @@ export async function pull(args) {
|
|
|
227
242
|
if (deckItem.id) {
|
|
228
243
|
try {
|
|
229
244
|
const annotationsRes = await fetch(`${auth.registry}/api/items/${deckItem.id}/annotations`, {
|
|
230
|
-
headers: {
|
|
245
|
+
headers: {
|
|
246
|
+
Authorization: `Bearer ${auth.token}`,
|
|
247
|
+
...(auth.organizationId ? { "X-Organization-Id": auth.organizationId } : {})
|
|
248
|
+
}
|
|
231
249
|
})
|
|
232
250
|
if (annotationsRes.ok) {
|
|
233
251
|
const data = await annotationsRes.json()
|
|
234
252
|
const annotations = data.annotations ?? []
|
|
235
253
|
if (annotations.length > 0) {
|
|
236
|
-
const annotationsFile = {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
254
|
+
const annotationsFile = {
|
|
255
|
+
version: 1,
|
|
256
|
+
annotations: annotations.map(a => ({
|
|
257
|
+
id: a.id,
|
|
258
|
+
slideIndex: a.slideIndex,
|
|
259
|
+
...(a.slideId ? { slideId: a.slideId } : {}),
|
|
260
|
+
slideTitle: a.slideTitle,
|
|
261
|
+
target: a.target,
|
|
262
|
+
body: a.body,
|
|
263
|
+
createdAt: a.createdAt,
|
|
264
|
+
status: a.status,
|
|
265
|
+
...(a.resolution ? { resolution: a.resolution } : {})
|
|
266
|
+
}))
|
|
267
|
+
}
|
|
268
|
+
writeFileSync(
|
|
269
|
+
join(cwd, "annotations.json"),
|
|
270
|
+
JSON.stringify(annotationsFile, null, 2) + "\n",
|
|
271
|
+
"utf-8"
|
|
272
|
+
)
|
|
273
|
+
console.log(
|
|
274
|
+
` ${green("+")} ${cyan("annotations.json")} ${dim(`(${annotations.length} annotation${annotations.length === 1 ? "" : "s"})`)}`
|
|
275
|
+
)
|
|
248
276
|
}
|
|
249
277
|
}
|
|
250
278
|
} catch {
|
|
@@ -257,7 +285,9 @@ export async function pull(args) {
|
|
|
257
285
|
if (written.length === 0) {
|
|
258
286
|
console.log(` ${green("+")} Everything is up to date.`)
|
|
259
287
|
} else {
|
|
260
|
-
console.log(
|
|
288
|
+
console.log(
|
|
289
|
+
` ${green("+")} Pulled ${bold(String(written.length))} file(s) from ${bold(deckSlug)}`
|
|
290
|
+
)
|
|
261
291
|
}
|
|
262
292
|
console.log()
|
|
263
293
|
closePrompts()
|
package/src/commands/studio.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createServer } from "vite"
|
|
2
2
|
|
|
3
3
|
import { bold, dim } from "../utils/ansi.mjs"
|
|
4
|
+
import { registerStudioServer } from "../utils/studio-discovery.mjs"
|
|
4
5
|
import { ensureTsConfig } from "../utils/tsconfig.mjs"
|
|
5
6
|
import { createViteConfig } from "../vite/config.mjs"
|
|
6
7
|
|
|
@@ -10,7 +11,7 @@ export async function studio(args) {
|
|
|
10
11
|
const port = portArg ? parseInt(portArg.split("=")[1], 10) : 5173
|
|
11
12
|
const hasHost = args.includes("--host") || args.some(a => a.startsWith("--host="))
|
|
12
13
|
const hostArg = args.find(a => a.startsWith("--host="))
|
|
13
|
-
const host = hasHost ? (hostArg ? hostArg.split("=")[1] || "0.0.0.0" : "0.0.0.0") :
|
|
14
|
+
const host = hasHost ? (hostArg ? hostArg.split("=")[1] || "0.0.0.0" : "0.0.0.0") : "127.0.0.1"
|
|
14
15
|
|
|
15
16
|
ensureTsConfig(cwd)
|
|
16
17
|
|
|
@@ -18,17 +19,20 @@ export async function studio(args) {
|
|
|
18
19
|
console.log(` ${bold("promptslide")} ${dim("studio")}`)
|
|
19
20
|
console.log()
|
|
20
21
|
|
|
21
|
-
const config = createViteConfig({ cwd, mode: "development" })
|
|
22
|
+
const config = createViteConfig({ cwd, mode: "development", editor: !hasHost })
|
|
22
23
|
const server = await createServer({
|
|
23
24
|
...config,
|
|
24
25
|
server: {
|
|
25
26
|
port,
|
|
27
|
+
host,
|
|
26
28
|
strictPort: false,
|
|
27
|
-
...(
|
|
29
|
+
...(hasHost && { allowedHosts: true })
|
|
28
30
|
}
|
|
29
31
|
})
|
|
30
32
|
|
|
31
33
|
await server.listen()
|
|
34
|
+
const baseUrl = server.resolvedUrls?.local[0]
|
|
35
|
+
if (baseUrl) registerStudioServer(cwd, baseUrl)
|
|
32
36
|
server.printUrls()
|
|
33
37
|
server.bindCLIShortcuts({ print: true })
|
|
34
38
|
}
|
|
@@ -8,6 +8,8 @@ export async function toImage(args) {
|
|
|
8
8
|
const slidePath = args[0]
|
|
9
9
|
const outputIndex = args.indexOf("-o")
|
|
10
10
|
const output = outputIndex !== -1 ? args[outputIndex + 1] : null
|
|
11
|
+
const portArg = args.find(arg => arg.startsWith("--port="))
|
|
12
|
+
const studioPort = portArg ? Number(portArg.slice(7)) : undefined
|
|
11
13
|
|
|
12
14
|
if (!slidePath || slidePath === "--help" || slidePath === "-h") {
|
|
13
15
|
console.log()
|
|
@@ -15,6 +17,7 @@ export async function toImage(args) {
|
|
|
15
17
|
console.log()
|
|
16
18
|
console.log(` ${bold("Options:")}`)
|
|
17
19
|
console.log(` -o <file> Output file path (default: <slide-name>.png)`)
|
|
20
|
+
console.log(` --port=N Studio port to reuse (default: auto-discover)`)
|
|
18
21
|
console.log()
|
|
19
22
|
console.log(` ${bold("Examples:")}`)
|
|
20
23
|
console.log(` ${dim("promptslide to-image src/slides/slide-title.tsx")}`)
|
|
@@ -26,7 +29,9 @@ export async function toImage(args) {
|
|
|
26
29
|
if (!(await isPlaywrightAvailable())) {
|
|
27
30
|
console.error()
|
|
28
31
|
console.error(` ${red("Error:")} Playwright is required for image export.`)
|
|
29
|
-
console.error(
|
|
32
|
+
console.error(
|
|
33
|
+
` Install it with: ${bold("npm install -D playwright && npx playwright install chromium")}`
|
|
34
|
+
)
|
|
30
35
|
console.error()
|
|
31
36
|
process.exit(1)
|
|
32
37
|
}
|
|
@@ -36,10 +41,18 @@ export async function toImage(args) {
|
|
|
36
41
|
console.log()
|
|
37
42
|
console.log(` ${dim("Capturing screenshot...")}`)
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
let buffer
|
|
45
|
+
try {
|
|
46
|
+
buffer = await captureSlideScreenshot({
|
|
47
|
+
cwd: process.cwd(),
|
|
48
|
+
slidePath,
|
|
49
|
+
studioPort
|
|
50
|
+
})
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(` ${red("Error:")} ${error.message}`)
|
|
53
|
+
process.exitCode = 1
|
|
54
|
+
return
|
|
55
|
+
}
|
|
43
56
|
|
|
44
57
|
if (!buffer) {
|
|
45
58
|
console.error(` ${red("Error:")} Failed to capture screenshot.`)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs"
|
|
2
|
+
import { resolve } from "node:path"
|
|
3
|
+
|
|
4
|
+
import { bold, green, dim, red } from "../utils/ansi.mjs"
|
|
5
|
+
import { captureDeckPdf } from "../utils/export.mjs"
|
|
6
|
+
|
|
7
|
+
export async function toPdf(args) {
|
|
8
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
9
|
+
console.log(` ${bold("Usage:")} promptslide to-pdf [-o slides.pdf] [--port=N]`)
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
const outputIndex = args.indexOf("-o")
|
|
13
|
+
const output = resolve(outputIndex >= 0 ? args[outputIndex + 1] : "slides.pdf")
|
|
14
|
+
const portArg = args.find(arg => arg.startsWith("--port="))
|
|
15
|
+
const studioPort = portArg ? Number(portArg.slice(7)) : undefined
|
|
16
|
+
console.log(` ${dim("Rendering deck PDF...")}`)
|
|
17
|
+
let pdf
|
|
18
|
+
try {
|
|
19
|
+
pdf = await captureDeckPdf({ cwd: process.cwd(), studioPort })
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error(` ${red("Error:")} ${error.message}`)
|
|
22
|
+
process.exitCode = 1
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
writeFileSync(output, pdf)
|
|
26
|
+
console.log(` ${green("✓")} Saved to ${bold(output)}`)
|
|
27
|
+
}
|
package/src/core/animated.tsx
CHANGED
|
@@ -48,7 +48,7 @@ const animationVariants: Record<AnimationType, Variants> = {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
interface AnimatedProps {
|
|
51
|
-
/** Which step reveals this content (
|
|
51
|
+
/** Which step reveals this content (0 = visible from slide entry) */
|
|
52
52
|
step: number
|
|
53
53
|
/** Animation type */
|
|
54
54
|
animation?: AnimationType
|
|
@@ -83,7 +83,7 @@ export function Animated({
|
|
|
83
83
|
|
|
84
84
|
return (
|
|
85
85
|
<motion.div
|
|
86
|
-
initial="hidden"
|
|
86
|
+
initial={isVisible ? false : "hidden"}
|
|
87
87
|
animate={isVisible ? "visible" : "hidden"}
|
|
88
88
|
variants={animationVariants[animation]}
|
|
89
89
|
transition={{
|
|
@@ -138,7 +138,7 @@ export function AnimatedGroup({
|
|
|
138
138
|
|
|
139
139
|
return (
|
|
140
140
|
<motion.div
|
|
141
|
-
initial="hidden"
|
|
141
|
+
initial={isVisible ? false : "hidden"}
|
|
142
142
|
animate={isVisible ? "visible" : "hidden"}
|
|
143
143
|
variants={containerVariants}
|
|
144
144
|
className={className}
|
|
@@ -38,6 +38,14 @@ export function createHttpAdapter(): AnnotationStorageAdapter {
|
|
|
38
38
|
async remove(id) {
|
|
39
39
|
cache = cache.filter(a => a.id !== id)
|
|
40
40
|
await saveAll(cache)
|
|
41
|
+
},
|
|
42
|
+
async update(id, patch) {
|
|
43
|
+
cache = cache.map(a => (a.id === id ? { ...a, ...patch } : a))
|
|
44
|
+
await saveAll(cache)
|
|
45
|
+
},
|
|
46
|
+
async replaceAll(annotations) {
|
|
47
|
+
cache = annotations
|
|
48
|
+
await saveAll(cache)
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
}
|