promptslide 0.2.4 → 0.2.6
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/dist/index.d.ts +39 -1
- package/dist/index.js +241 -130
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/add.mjs +7 -0
- package/src/commands/create.mjs +10 -37
- package/src/commands/login.mjs +6 -1
- package/src/commands/org.mjs +2 -2
- package/src/commands/publish.mjs +54 -17
- package/src/commands/pull.mjs +228 -0
- package/src/commands/studio.mjs +8 -1
- package/src/core/index.ts +7 -0
- package/src/core/slide-deck.tsx +34 -82
- package/src/core/slide-embed.tsx +141 -0
- package/src/core/slide-renderer.tsx +91 -0
- package/src/index.mjs +6 -0
- package/src/utils/auth.mjs +5 -3
- package/src/utils/prompts.mjs +70 -0
- package/src/utils/registry.mjs +12 -0
- package/src/vite/plugin.mjs +54 -0
- package/templates/default/package.json +1 -1
- package/templates/default/src/layouts/slide-layout-centered.tsx +1 -1
package/src/commands/publish.mjs
CHANGED
|
@@ -4,11 +4,16 @@ import { join, basename, relative, extname } from "node:path"
|
|
|
4
4
|
import { bold, green, cyan, red, dim } from "../utils/ansi.mjs"
|
|
5
5
|
import { requireAuth } from "../utils/auth.mjs"
|
|
6
6
|
import { captureSlideAsDataUri, isPlaywrightAvailable } from "../utils/export.mjs"
|
|
7
|
-
import { publishToRegistry, registryItemExists, searchRegistry, updateLockfileItem, readLockfile, hashContent, detectPackageManager, requestUploadTokens, uploadBinaryToBlob, assetFileToSlug, detectAssetDepsInContent } from "../utils/registry.mjs"
|
|
8
|
-
import { prompt, confirm, closePrompts } from "../utils/prompts.mjs"
|
|
7
|
+
import { publishToRegistry, registryItemExists, searchRegistry, updateLockfileItem, updateLockfilePublishConfig, readLockfile, hashContent, detectPackageManager, requestUploadTokens, uploadBinaryToBlob, assetFileToSlug, detectAssetDepsInContent } from "../utils/registry.mjs"
|
|
8
|
+
import { prompt, confirm, select, closePrompts } from "../utils/prompts.mjs"
|
|
9
9
|
import { parseDeckConfig } from "../utils/deck-config.mjs"
|
|
10
10
|
|
|
11
11
|
function readDeckPrefix(cwd) {
|
|
12
|
+
// Prefer stored prefix from lockfile (user's previous choice)
|
|
13
|
+
const lock = readLockfile(cwd)
|
|
14
|
+
if (lock.deckPrefix) return lock.deckPrefix
|
|
15
|
+
|
|
16
|
+
// Fall back to package.json name
|
|
12
17
|
try {
|
|
13
18
|
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"))
|
|
14
19
|
return (pkg.name || "").toLowerCase()
|
|
@@ -402,22 +407,17 @@ export async function publish(args) {
|
|
|
402
407
|
process.exit(1)
|
|
403
408
|
}
|
|
404
409
|
|
|
405
|
-
console.log(` ${bold("
|
|
406
|
-
available.forEach((f, i) => {
|
|
407
|
-
console.log(` ${dim(`${i + 1}.`)} ${f.target}${f.path}`)
|
|
408
|
-
})
|
|
409
|
-
console.log(` ${dim("─".repeat(30))}`)
|
|
410
|
-
console.log(` ${dim(`${available.length + 1}.`)} ${bold("Entire deck")}`)
|
|
410
|
+
console.log(` ${bold("What do you want to publish?")}`)
|
|
411
411
|
console.log()
|
|
412
412
|
|
|
413
|
-
const
|
|
414
|
-
|
|
413
|
+
const options = [
|
|
414
|
+
...available.map(f => `${f.target}${f.path}`),
|
|
415
|
+
"Entire deck"
|
|
416
|
+
]
|
|
417
|
+
const idx = await select(options, options.length - 1)
|
|
415
418
|
|
|
416
419
|
if (idx === available.length) {
|
|
417
420
|
typeOverride = "deck"
|
|
418
|
-
} else if (idx < 0 || idx >= available.length) {
|
|
419
|
-
console.error(` ${red("Error:")} Invalid selection.`)
|
|
420
|
-
process.exit(1)
|
|
421
421
|
} else {
|
|
422
422
|
filePath = relative(cwd, available[idx].fullPath)
|
|
423
423
|
}
|
|
@@ -432,10 +432,34 @@ export async function publish(args) {
|
|
|
432
432
|
process.exit(1)
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
-
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
|
|
435
|
+
// Check if a deck slug already exists in the lockfile
|
|
436
|
+
const existingLock = readLockfile(cwd)
|
|
437
|
+
const existingSlug = existingLock.deckSlug
|
|
438
|
+
let deckPrefix, deckSlug
|
|
439
|
+
|
|
440
|
+
if (existingSlug) {
|
|
441
|
+
console.log(` ${dim("Previously published as")} ${cyan(existingSlug)}`)
|
|
442
|
+
console.log()
|
|
443
|
+
const reuse = await confirm(` Publish to ${bold(existingSlug)}?`)
|
|
444
|
+
|
|
445
|
+
if (reuse) {
|
|
446
|
+
deckSlug = existingSlug
|
|
447
|
+
deckPrefix = existingSlug.split("/")[0]
|
|
448
|
+
} else {
|
|
449
|
+
deckPrefix = await promptDeckPrefix(cwd, true)
|
|
450
|
+
const dirName = basename(cwd)
|
|
451
|
+
const deckBaseSlug = dirName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")
|
|
452
|
+
deckSlug = `${deckPrefix}/${deckBaseSlug}`
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
deckPrefix = await promptDeckPrefix(cwd, true)
|
|
456
|
+
const dirName = basename(cwd)
|
|
457
|
+
const deckBaseSlug = dirName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")
|
|
458
|
+
deckSlug = `${deckPrefix}/${deckBaseSlug}`
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Persist prefix for next time (slug is saved after successful publish)
|
|
462
|
+
updateLockfilePublishConfig(cwd, { deckPrefix })
|
|
439
463
|
|
|
440
464
|
// Walk public/ to collect assets and build reference set
|
|
441
465
|
const publicDir = join(cwd, "public")
|
|
@@ -721,6 +745,7 @@ export async function publish(args) {
|
|
|
721
745
|
const assetSlugs = publicAssets.map(a => assetFileToSlug(deckPrefix, a.relativePath))
|
|
722
746
|
const allDeckDeps = [...slideSlugs, ...assetSlugs]
|
|
723
747
|
|
|
748
|
+
let deckItemId = null
|
|
724
749
|
try {
|
|
725
750
|
const result = await publishToRegistry({
|
|
726
751
|
type: "deck",
|
|
@@ -736,6 +761,8 @@ export async function publish(args) {
|
|
|
736
761
|
}, auth)
|
|
737
762
|
console.log(` [${itemIndex}/${totalItems}] ${green("✓")} deck ${cyan(deckSlug)} ${dim(`v${result.version}`)}`)
|
|
738
763
|
updateLockfileItem(cwd, deckSlug, result.version ?? 0, {})
|
|
764
|
+
updateLockfilePublishConfig(cwd, { deckSlug })
|
|
765
|
+
deckItemId = result.id
|
|
739
766
|
published++
|
|
740
767
|
} catch (err) {
|
|
741
768
|
console.log(` [${itemIndex}/${totalItems}] ${red("✗")} deck ${dim(deckSlug)}: ${err.message}`)
|
|
@@ -744,6 +771,9 @@ export async function publish(args) {
|
|
|
744
771
|
|
|
745
772
|
console.log()
|
|
746
773
|
console.log(` ${bold("Done:")} ${green(`${published} published`)}${skipped ? `, ${skipped} unchanged` : ""}${failed ? `, ${red(`${failed} failed`)}` : ""}`)
|
|
774
|
+
if (auth.organizationSlug && deckItemId) {
|
|
775
|
+
console.log(` View: ${cyan(`${auth.registry}/${auth.organizationSlug}/items/${deckItemId}`)}`)
|
|
776
|
+
}
|
|
747
777
|
console.log(` Install: ${cyan(`promptslide add ${deckSlug}`)}`)
|
|
748
778
|
console.log()
|
|
749
779
|
closePrompts()
|
|
@@ -782,6 +812,10 @@ export async function publish(args) {
|
|
|
782
812
|
// Prompt for deck prefix (required)
|
|
783
813
|
const deckPrefix = await promptDeckPrefix(cwd, true)
|
|
784
814
|
const slug = `${deckPrefix}/${baseSlug}`
|
|
815
|
+
|
|
816
|
+
// Persist prefix for next time
|
|
817
|
+
updateLockfilePublishConfig(cwd, { deckPrefix })
|
|
818
|
+
|
|
785
819
|
console.log(` Slug: ${cyan(slug)}`)
|
|
786
820
|
console.log()
|
|
787
821
|
|
|
@@ -919,6 +953,9 @@ export async function publish(args) {
|
|
|
919
953
|
const verTag = result.version ? ` v${result.version}` : ""
|
|
920
954
|
console.log(` ${green("✓")} Published ${bold(slug)}${verTag} to ${auth.organizationName || "registry"}`)
|
|
921
955
|
console.log(` Status: ${result.status || "published"}`)
|
|
956
|
+
if (auth.organizationSlug && result.id) {
|
|
957
|
+
console.log(` View: ${cyan(`${auth.registry}/${auth.organizationSlug}/items/${result.id}`)}`)
|
|
958
|
+
}
|
|
922
959
|
console.log(` Install: ${cyan(`promptslide add ${slug}`)}`)
|
|
923
960
|
|
|
924
961
|
// Track in lockfile
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"
|
|
2
|
+
import { execFileSync } from "node:child_process"
|
|
3
|
+
import { join, dirname, resolve, sep } from "node:path"
|
|
4
|
+
|
|
5
|
+
import { bold, green, cyan, red, yellow, dim } from "../utils/ansi.mjs"
|
|
6
|
+
import { requireAuth } from "../utils/auth.mjs"
|
|
7
|
+
import {
|
|
8
|
+
fetchRegistryItem,
|
|
9
|
+
resolveRegistryDependencies,
|
|
10
|
+
detectPackageManager,
|
|
11
|
+
getInstallCommand,
|
|
12
|
+
readLockfile,
|
|
13
|
+
updateLockfileItem,
|
|
14
|
+
hashContent,
|
|
15
|
+
hashFile,
|
|
16
|
+
isFileDirty
|
|
17
|
+
} from "../utils/registry.mjs"
|
|
18
|
+
import {
|
|
19
|
+
toPascalCase,
|
|
20
|
+
addSlideToDeckConfig,
|
|
21
|
+
replaceDeckConfig
|
|
22
|
+
} from "../utils/deck-config.mjs"
|
|
23
|
+
import { confirm, closePrompts } from "../utils/prompts.mjs"
|
|
24
|
+
|
|
25
|
+
export async function pull(args) {
|
|
26
|
+
const cwd = process.cwd()
|
|
27
|
+
|
|
28
|
+
console.log()
|
|
29
|
+
console.log(` ${bold("promptslide")} ${dim("pull")}`)
|
|
30
|
+
console.log()
|
|
31
|
+
|
|
32
|
+
if (args[0] === "--help" || args[0] === "-h") {
|
|
33
|
+
console.log(` ${bold("Usage:")} promptslide pull ${dim("[--force]")}`)
|
|
34
|
+
console.log()
|
|
35
|
+
console.log(` Pull the latest version of your deck from the registry.`)
|
|
36
|
+
console.log(` Reads the deck slug from the lockfile (set during publish).`)
|
|
37
|
+
console.log()
|
|
38
|
+
console.log(` ${bold("Options:")}`)
|
|
39
|
+
console.log(` --force Overwrite locally modified files without prompting`)
|
|
40
|
+
console.log()
|
|
41
|
+
process.exit(0)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const force = args.includes("--force")
|
|
45
|
+
const auth = requireAuth()
|
|
46
|
+
|
|
47
|
+
// Read deckSlug from lockfile
|
|
48
|
+
const lock = readLockfile(cwd)
|
|
49
|
+
const deckSlug = lock.deckSlug
|
|
50
|
+
|
|
51
|
+
if (!deckSlug) {
|
|
52
|
+
console.error(` ${red("Error:")} No deck slug found in lockfile.`)
|
|
53
|
+
console.error(` ${dim("Run")} ${cyan("promptslide publish --type deck")} ${dim("first to set it.")}`)
|
|
54
|
+
console.log()
|
|
55
|
+
process.exit(1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(` Pulling deck ${bold(deckSlug)}...`)
|
|
59
|
+
console.log()
|
|
60
|
+
|
|
61
|
+
// Fetch the deck manifest from the registry
|
|
62
|
+
let deckItem
|
|
63
|
+
try {
|
|
64
|
+
deckItem = await fetchRegistryItem(deckSlug, auth)
|
|
65
|
+
} catch (err) {
|
|
66
|
+
console.error(` ${red("Error:")} ${err.message}`)
|
|
67
|
+
process.exit(1)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const versionTag = deckItem.version ? ` ${dim(`v${deckItem.version}`)}` : ""
|
|
71
|
+
console.log(` Found ${bold(deckItem.title || deckItem.name)} ${dim(`(${deckItem.type})`)}${versionTag}`)
|
|
72
|
+
|
|
73
|
+
// Resolve all dependencies (slides, layouts, themes, assets)
|
|
74
|
+
let resolved
|
|
75
|
+
try {
|
|
76
|
+
resolved = await resolveRegistryDependencies(deckItem, auth, cwd)
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error(` ${red("Error resolving dependencies:")} ${err.message}`)
|
|
79
|
+
process.exit(1)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const itemCount = resolved.items.length
|
|
83
|
+
const fileCount = resolved.items.reduce((sum, i) => sum + (i.files?.length || 0), 0)
|
|
84
|
+
console.log(` Resolved ${bold(String(itemCount))} items with ${bold(String(fileCount))} files`)
|
|
85
|
+
console.log()
|
|
86
|
+
|
|
87
|
+
// Write files
|
|
88
|
+
const written = []
|
|
89
|
+
const writtenByItem = new Map()
|
|
90
|
+
|
|
91
|
+
for (const regItem of resolved.items) {
|
|
92
|
+
if (!regItem.files?.length) continue
|
|
93
|
+
|
|
94
|
+
const fileHashes = {}
|
|
95
|
+
|
|
96
|
+
for (const file of regItem.files) {
|
|
97
|
+
const targetPath = resolve(cwd, file.target, file.path)
|
|
98
|
+
if (!targetPath.startsWith(cwd + sep)) {
|
|
99
|
+
console.log(` ${red("Error:")} Invalid file path: ${file.target}${file.path}`)
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
const targetDir = dirname(targetPath)
|
|
103
|
+
const relativePath = file.target + file.path
|
|
104
|
+
const newHash = hashContent(file.content)
|
|
105
|
+
|
|
106
|
+
if (existsSync(targetPath)) {
|
|
107
|
+
// Check if content is identical
|
|
108
|
+
const dataUriMatch = file.content.match(/^data:[^;]+;base64,/)
|
|
109
|
+
let identical
|
|
110
|
+
if (dataUriMatch) {
|
|
111
|
+
const newBuf = Buffer.from(file.content.slice(dataUriMatch[0].length), "base64")
|
|
112
|
+
const existingBuf = readFileSync(targetPath)
|
|
113
|
+
identical = newBuf.equals(existingBuf)
|
|
114
|
+
} else {
|
|
115
|
+
identical = hashFile(targetPath) === newHash
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (identical) {
|
|
119
|
+
fileHashes[relativePath] = newHash
|
|
120
|
+
continue
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Check if file has local modifications
|
|
124
|
+
const lockEntry = lock.items[regItem.name]
|
|
125
|
+
const storedHash = lockEntry?.files?.[relativePath]
|
|
126
|
+
if (!force) {
|
|
127
|
+
if (storedHash && isFileDirty(cwd, relativePath, storedHash)) {
|
|
128
|
+
// File was tracked and has been locally modified
|
|
129
|
+
const overwrite = await confirm(
|
|
130
|
+
` ${yellow("!")} ${relativePath} has local changes. Overwrite?`,
|
|
131
|
+
false
|
|
132
|
+
)
|
|
133
|
+
if (!overwrite) {
|
|
134
|
+
fileHashes[relativePath] = storedHash
|
|
135
|
+
console.log(` ${dim("Skipped")} ${relativePath}`)
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
} else if (!storedHash) {
|
|
139
|
+
// File exists but not in lockfile (first pull or untracked)
|
|
140
|
+
const overwrite = await confirm(` Overwrite ${relativePath}? ${dim("(local changes will be lost)")}`, false)
|
|
141
|
+
if (!overwrite) {
|
|
142
|
+
console.log(` ${dim("Skipped")} ${relativePath}`)
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Write the file
|
|
150
|
+
mkdirSync(targetDir, { recursive: true })
|
|
151
|
+
const dataUriPrefix = file.content.match(/^data:[^;]+;base64,/)
|
|
152
|
+
if (dataUriPrefix) {
|
|
153
|
+
writeFileSync(targetPath, Buffer.from(file.content.slice(dataUriPrefix[0].length), "base64"))
|
|
154
|
+
} else {
|
|
155
|
+
writeFileSync(targetPath, file.content, "utf-8")
|
|
156
|
+
}
|
|
157
|
+
fileHashes[relativePath] = newHash
|
|
158
|
+
written.push({ item: regItem, file })
|
|
159
|
+
console.log(` ${green("+")} ${cyan(relativePath)}`)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (Object.keys(fileHashes).length > 0) {
|
|
163
|
+
writtenByItem.set(regItem.name, { regItem, fileHashes })
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Update lockfile for all resolved items
|
|
168
|
+
for (const [name, { regItem, fileHashes }] of writtenByItem) {
|
|
169
|
+
updateLockfileItem(cwd, name, regItem.version ?? 0, fileHashes)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Also update the deck manifest entry itself (has no files)
|
|
173
|
+
updateLockfileItem(cwd, deckSlug, deckItem.version ?? 0, {})
|
|
174
|
+
|
|
175
|
+
// Update deck-config.ts
|
|
176
|
+
if (deckItem.type === "deck" && deckItem.meta?.slides) {
|
|
177
|
+
const shouldReplace = await confirm(" Replace deck-config.ts with pulled deck config?", true)
|
|
178
|
+
if (shouldReplace) {
|
|
179
|
+
const slides = deckItem.meta.slides.map(s => ({
|
|
180
|
+
componentName: toPascalCase(s.slug),
|
|
181
|
+
importPath: `@/slides/${s.slug}`,
|
|
182
|
+
steps: s.steps,
|
|
183
|
+
section: s.section
|
|
184
|
+
}))
|
|
185
|
+
replaceDeckConfig(cwd, slides, {
|
|
186
|
+
transition: deckItem.meta.transition,
|
|
187
|
+
directionalTransition: deckItem.meta.directionalTransition
|
|
188
|
+
})
|
|
189
|
+
console.log(` ${green("+")} Replaced ${cyan("deck-config.ts")} ${dim(`(${slides.length} slides)`)}`)
|
|
190
|
+
} else {
|
|
191
|
+
// Append individual slides
|
|
192
|
+
for (const s of deckItem.meta.slides) {
|
|
193
|
+
const componentName = toPascalCase(s.slug)
|
|
194
|
+
const importPath = `@/slides/${s.slug}`
|
|
195
|
+
const updated = addSlideToDeckConfig(cwd, { componentName, importPath, steps: s.steps, section: s.section })
|
|
196
|
+
if (updated) {
|
|
197
|
+
console.log(` ${green("+")} Added ${componentName} to ${cyan("deck-config.ts")}`)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Install npm dependencies
|
|
204
|
+
if (Object.keys(resolved.npmDeps).length > 0 && existsSync(join(cwd, "package.json"))) {
|
|
205
|
+
const pm = detectPackageManager(cwd)
|
|
206
|
+
const pkgList = Object.entries(resolved.npmDeps).map(([name, ver]) => `${name}@${ver}`)
|
|
207
|
+
const { cmd, args: installArgs, display } = getInstallCommand(pm, pkgList)
|
|
208
|
+
console.log()
|
|
209
|
+
console.log(` ${dim(`Installing dependencies: ${display}`)}`)
|
|
210
|
+
try {
|
|
211
|
+
execFileSync(cmd, installArgs, { cwd, stdio: "inherit" })
|
|
212
|
+
console.log(` ${green("+")} Dependencies installed`)
|
|
213
|
+
} catch {
|
|
214
|
+
console.log(` ${red("!")} Dependency installation failed. Run manually:`)
|
|
215
|
+
console.log(` ${display}`)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Summary
|
|
220
|
+
console.log()
|
|
221
|
+
if (written.length === 0) {
|
|
222
|
+
console.log(` ${green("+")} Everything is up to date.`)
|
|
223
|
+
} else {
|
|
224
|
+
console.log(` ${green("+")} Pulled ${bold(String(written.length))} file(s) from ${bold(deckSlug)}`)
|
|
225
|
+
}
|
|
226
|
+
console.log()
|
|
227
|
+
closePrompts()
|
|
228
|
+
}
|
package/src/commands/studio.mjs
CHANGED
|
@@ -8,6 +8,9 @@ export async function studio(args) {
|
|
|
8
8
|
const cwd = process.cwd()
|
|
9
9
|
const portArg = args.find(a => a.startsWith("--port="))
|
|
10
10
|
const port = portArg ? parseInt(portArg.split("=")[1], 10) : 5173
|
|
11
|
+
const hasHost = args.includes("--host") || args.some(a => a.startsWith("--host="))
|
|
12
|
+
const hostArg = args.find(a => a.startsWith("--host="))
|
|
13
|
+
const host = hasHost ? (hostArg ? hostArg.split("=")[1] || "0.0.0.0" : "0.0.0.0") : undefined
|
|
11
14
|
|
|
12
15
|
ensureTsConfig(cwd)
|
|
13
16
|
|
|
@@ -18,7 +21,11 @@ export async function studio(args) {
|
|
|
18
21
|
const config = createViteConfig({ cwd, mode: "development" })
|
|
19
22
|
const server = await createServer({
|
|
20
23
|
...config,
|
|
21
|
-
server: {
|
|
24
|
+
server: {
|
|
25
|
+
port,
|
|
26
|
+
strictPort: false,
|
|
27
|
+
...(host && { host, allowedHosts: true })
|
|
28
|
+
}
|
|
22
29
|
})
|
|
23
30
|
|
|
24
31
|
await server.listen()
|
package/src/core/index.ts
CHANGED
|
@@ -63,6 +63,13 @@ export type { ThemeConfig } from "./types"
|
|
|
63
63
|
// Shared Footer
|
|
64
64
|
export { SlideFooter } from "./layouts/shared-footer"
|
|
65
65
|
|
|
66
|
+
// SlideRenderer
|
|
67
|
+
export { SlideRenderer } from "./slide-renderer"
|
|
68
|
+
export type { SlideRendererProps } from "./slide-renderer"
|
|
69
|
+
|
|
70
|
+
// SlideEmbed
|
|
71
|
+
export { SlideEmbed } from "./slide-embed"
|
|
72
|
+
|
|
66
73
|
// SlideDeck
|
|
67
74
|
export { SlideDeck } from "./slide-deck"
|
|
68
75
|
|
package/src/core/slide-deck.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LayoutGroup } from "framer-motion"
|
|
2
2
|
import { ChevronLeft, ChevronRight, Download, Grid3X3, List, Maximize, Monitor } from "lucide-react"
|
|
3
3
|
import { useCallback, useEffect, useRef, useState } from "react"
|
|
4
4
|
|
|
5
5
|
import type { SlideTransitionType } from "./transitions"
|
|
6
6
|
import type { SlideConfig } from "./types"
|
|
7
7
|
|
|
8
|
-
import { SLIDE_DIMENSIONS
|
|
8
|
+
import { SLIDE_DIMENSIONS } from "./animation-config"
|
|
9
9
|
import { AnimationProvider } from "./animation-context"
|
|
10
10
|
import { SlideErrorBoundary } from "./slide-error-boundary"
|
|
11
|
-
import {
|
|
11
|
+
import { SlideRenderer } from "./slide-renderer"
|
|
12
12
|
import { useSlideNavigation } from "./use-slide-navigation"
|
|
13
13
|
import { cn } from "./utils"
|
|
14
14
|
|
|
@@ -177,18 +177,6 @@ export function SlideDeck({ slides, transition, directionalTransition }: SlideDe
|
|
|
177
177
|
return () => window.removeEventListener("keydown", handleKeyDown)
|
|
178
178
|
}, [advance, goBack, viewMode, togglePresentationMode])
|
|
179
179
|
|
|
180
|
-
// Per-slide transition resolution
|
|
181
|
-
const currentSlideTransition = slides[currentSlide]?.transition
|
|
182
|
-
const transitionType = currentSlideTransition ?? transition ?? DEFAULT_SLIDE_TRANSITION
|
|
183
|
-
const isDirectional = directionalTransition ?? false
|
|
184
|
-
|
|
185
|
-
const slideVariants = getSlideVariants(
|
|
186
|
-
{ type: transitionType, directional: isDirectional },
|
|
187
|
-
direction
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
const CurrentSlideComponent = slides[currentSlide]!.component
|
|
191
|
-
|
|
192
180
|
return (
|
|
193
181
|
<div className="min-h-screen w-full bg-neutral-950 text-foreground">
|
|
194
182
|
<style>{`
|
|
@@ -299,73 +287,31 @@ export function SlideDeck({ slides, transition, directionalTransition }: SlideDe
|
|
|
299
287
|
transformOrigin: "center center"
|
|
300
288
|
}}
|
|
301
289
|
>
|
|
302
|
-
<
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
}}
|
|
315
|
-
className="absolute inset-0 h-full w-full"
|
|
316
|
-
>
|
|
317
|
-
<AnimationProvider
|
|
318
|
-
currentStep={animationStep}
|
|
319
|
-
totalSteps={totalSteps}
|
|
320
|
-
showAllAnimations={showAllAnimations}
|
|
321
|
-
>
|
|
322
|
-
<SlideErrorBoundary
|
|
323
|
-
slideIndex={currentSlide}
|
|
324
|
-
slideTitle={slides[currentSlide]?.title}
|
|
325
|
-
>
|
|
326
|
-
<CurrentSlideComponent
|
|
327
|
-
slideNumber={currentSlide + 1}
|
|
328
|
-
totalSlides={slides.length}
|
|
329
|
-
/>
|
|
330
|
-
</SlideErrorBoundary>
|
|
331
|
-
</AnimationProvider>
|
|
332
|
-
</motion.div>
|
|
333
|
-
</AnimatePresence>
|
|
290
|
+
<SlideRenderer
|
|
291
|
+
slides={slides}
|
|
292
|
+
currentSlide={currentSlide}
|
|
293
|
+
animationStep={animationStep}
|
|
294
|
+
totalSteps={totalSteps}
|
|
295
|
+
direction={direction}
|
|
296
|
+
showAllAnimations={showAllAnimations}
|
|
297
|
+
transition={transition}
|
|
298
|
+
directionalTransition={directionalTransition}
|
|
299
|
+
onTransitionComplete={onTransitionComplete}
|
|
300
|
+
/>
|
|
334
301
|
</div>
|
|
335
302
|
) : (
|
|
336
303
|
<div className="relative aspect-video w-full max-w-7xl overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-2xl">
|
|
337
|
-
<
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
}}
|
|
350
|
-
className="absolute inset-0 h-full w-full"
|
|
351
|
-
>
|
|
352
|
-
<AnimationProvider
|
|
353
|
-
currentStep={animationStep}
|
|
354
|
-
totalSteps={totalSteps}
|
|
355
|
-
showAllAnimations={showAllAnimations}
|
|
356
|
-
>
|
|
357
|
-
<SlideErrorBoundary
|
|
358
|
-
slideIndex={currentSlide}
|
|
359
|
-
slideTitle={slides[currentSlide]?.title}
|
|
360
|
-
>
|
|
361
|
-
<CurrentSlideComponent
|
|
362
|
-
slideNumber={currentSlide + 1}
|
|
363
|
-
totalSlides={slides.length}
|
|
364
|
-
/>
|
|
365
|
-
</SlideErrorBoundary>
|
|
366
|
-
</AnimationProvider>
|
|
367
|
-
</motion.div>
|
|
368
|
-
</AnimatePresence>
|
|
304
|
+
<SlideRenderer
|
|
305
|
+
slides={slides}
|
|
306
|
+
currentSlide={currentSlide}
|
|
307
|
+
animationStep={animationStep}
|
|
308
|
+
totalSteps={totalSteps}
|
|
309
|
+
direction={direction}
|
|
310
|
+
showAllAnimations={showAllAnimations}
|
|
311
|
+
transition={transition}
|
|
312
|
+
directionalTransition={directionalTransition}
|
|
313
|
+
onTransitionComplete={onTransitionComplete}
|
|
314
|
+
/>
|
|
369
315
|
</div>
|
|
370
316
|
)}
|
|
371
317
|
</LayoutGroup>
|
|
@@ -427,9 +373,15 @@ export function SlideDeck({ slides, transition, directionalTransition }: SlideDe
|
|
|
427
373
|
className="h-full w-full origin-top-left scale-[0.25]"
|
|
428
374
|
style={{ width: "400%", height: "400%" }}
|
|
429
375
|
>
|
|
430
|
-
<
|
|
431
|
-
|
|
432
|
-
|
|
376
|
+
<AnimationProvider
|
|
377
|
+
currentStep={slideConfig.steps}
|
|
378
|
+
totalSteps={slideConfig.steps}
|
|
379
|
+
showAllAnimations={true}
|
|
380
|
+
>
|
|
381
|
+
<SlideErrorBoundary slideIndex={index} slideTitle={slideConfig.title}>
|
|
382
|
+
<SlideComponent slideNumber={index + 1} totalSlides={slides.length} />
|
|
383
|
+
</SlideErrorBoundary>
|
|
384
|
+
</AnimationProvider>
|
|
433
385
|
</div>
|
|
434
386
|
<div className="absolute inset-0 bg-black/0 transition-colors group-hover:bg-black/20" />
|
|
435
387
|
<div className="absolute bottom-2 left-2 rounded bg-black/70 px-2 py-1 text-xs font-medium text-white">
|