promptslide 0.3.17 → 0.3.19
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 +76 -0
- package/dist/index.d.ts +29 -4
- package/dist/index.js +1427 -359
- package/dist/index.js.map +1 -1
- package/package.json +31 -31
- package/src/commands/build.mjs +4 -1
- package/src/commands/clone.mjs +5 -2
- package/src/commands/create.mjs +45 -14
- package/src/commands/pull.mjs +6 -3
- package/src/commands/studio.mjs +4 -3
- package/src/core/annotations/annotation-panel.tsx +18 -12
- package/src/core/index.ts +11 -5
- package/src/core/inspector.tsx +155 -0
- package/src/core/loop/config.d.mts +13 -0
- package/src/core/loop/config.mjs +65 -0
- package/src/core/loop/context.tsx +20 -0
- package/src/core/loop/loop-settings.tsx +292 -0
- package/src/core/loop/use-loop-playback.ts +115 -0
- package/src/core/slide-deck.tsx +459 -125
- package/src/core/slide-renderer.tsx +5 -4
- package/src/core/toolbar-action.tsx +38 -0
- package/src/core/types.ts +14 -0
- package/src/core/use-slide-navigation.ts +27 -0
- package/src/core/view-menu.tsx +130 -0
- package/src/editor/README.md +45 -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 +978 -0
- package/src/editor/studio.tsx +1964 -0
- package/src/utils/deck-config.mjs +6 -0
- package/src/utils/loop-config.mjs +70 -0
- package/src/vite/config.mjs +23 -2
- package/src/vite/plugin.mjs +21 -4
- package/templates/default/src/deck-config.ts +2 -2
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, existsSync } from "node:fs"
|
|
2
2
|
import { join } from "node:path"
|
|
3
3
|
|
|
4
|
+
import { DEFAULT_LOOP_CONFIG, readLoopConfig, writeLoopConfig } from "./loop-config.mjs"
|
|
5
|
+
|
|
4
6
|
const DECK_CONFIG_PATH = "src/deck-config.ts"
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -224,6 +226,8 @@ export function replaceDeckConfig(cwd, slides, opts = {}) {
|
|
|
224
226
|
].join("\n")
|
|
225
227
|
|
|
226
228
|
writeFileSync(configPath, content, "utf-8")
|
|
229
|
+
if (opts.loopConfig !== undefined) writeLoopConfig(cwd, opts.loopConfig)
|
|
230
|
+
else if (!existsSync(join(cwd, "loop-config.json"))) writeLoopConfig(cwd, DEFAULT_LOOP_CONFIG)
|
|
227
231
|
return true
|
|
228
232
|
}
|
|
229
233
|
|
|
@@ -310,6 +314,8 @@ export function parseDeckConfig(cwd) {
|
|
|
310
314
|
if (slides.length === 0) return null
|
|
311
315
|
|
|
312
316
|
const result = { slides }
|
|
317
|
+
const loopConfig = readLoopConfig(cwd)
|
|
318
|
+
if (loopConfig) result.loopConfig = loopConfig
|
|
313
319
|
if (transition) result.transition = transition
|
|
314
320
|
if (directionalTransition !== undefined) result.directionalTransition = directionalTransition
|
|
315
321
|
return result
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto"
|
|
2
|
+
import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync, lstatSync } from "node:fs"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_LOOP_CONFIG, validateLoopConfig } from "../core/loop/config.mjs"
|
|
6
|
+
|
|
7
|
+
export function readLoopConfig(root) {
|
|
8
|
+
const path = join(root, "loop-config.json")
|
|
9
|
+
if (!existsSync(path)) return undefined
|
|
10
|
+
if (lstatSync(path).isSymbolicLink()) throw new Error("loop-config.json must be a regular file")
|
|
11
|
+
return validateLoopConfig(JSON.parse(readFileSync(path, "utf8")))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function writeLoopConfig(root, config) {
|
|
15
|
+
const value = validateLoopConfig(config)
|
|
16
|
+
const path = join(root, "loop-config.json")
|
|
17
|
+
if (existsSync(path) && lstatSync(path).isSymbolicLink())
|
|
18
|
+
throw new Error("loop-config.json must be a regular file")
|
|
19
|
+
const temporary = join(root, `.loop-config-${randomUUID()}.tmp`)
|
|
20
|
+
try {
|
|
21
|
+
writeFileSync(temporary, JSON.stringify(value, null, 2) + "\n", { flag: "wx" })
|
|
22
|
+
renameSync(temporary, path)
|
|
23
|
+
} finally {
|
|
24
|
+
if (existsSync(temporary)) unlinkSync(temporary)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function loopConfigMiddleware(root, onSaved = () => {}) {
|
|
29
|
+
return (req, res, next) => {
|
|
30
|
+
if (req.url !== "/__promptslide_loop") return next()
|
|
31
|
+
if (req.method !== "POST") {
|
|
32
|
+
res.statusCode = 405
|
|
33
|
+
return res.end()
|
|
34
|
+
}
|
|
35
|
+
const host = req.headers.host || ""
|
|
36
|
+
const remote = req.socket.remoteAddress
|
|
37
|
+
if (
|
|
38
|
+
!/^(localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/.test(host) ||
|
|
39
|
+
!["127.0.0.1", "::1", "::ffff:127.0.0.1"].includes(remote) ||
|
|
40
|
+
req.headers.origin !== `http://${host}` ||
|
|
41
|
+
!req.headers["content-type"]?.startsWith("application/json")
|
|
42
|
+
) {
|
|
43
|
+
res.statusCode = 403
|
|
44
|
+
return res.end("Loop settings can only be saved from local Studio.")
|
|
45
|
+
}
|
|
46
|
+
let body = ""
|
|
47
|
+
req.on("data", chunk => {
|
|
48
|
+
body += chunk
|
|
49
|
+
if (Buffer.byteLength(body) > 65536) {
|
|
50
|
+
res.statusCode = 413
|
|
51
|
+
res.end()
|
|
52
|
+
req.destroy()
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
req.on("end", () => {
|
|
56
|
+
if (res.writableEnded) return
|
|
57
|
+
try {
|
|
58
|
+
writeLoopConfig(root, JSON.parse(body))
|
|
59
|
+
onSaved()
|
|
60
|
+
res.statusCode = 204
|
|
61
|
+
res.end()
|
|
62
|
+
} catch {
|
|
63
|
+
res.statusCode = 400
|
|
64
|
+
res.end("Invalid loop settings or project file could not be written.")
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { DEFAULT_LOOP_CONFIG }
|
package/src/vite/config.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url"
|
|
|
5
5
|
import tailwindcss from "@tailwindcss/postcss"
|
|
6
6
|
import react from "@vitejs/plugin-react"
|
|
7
7
|
|
|
8
|
+
import { editorPlugin } from "../editor/plugin.mjs"
|
|
8
9
|
import { promptslidePlugin } from "./plugin.mjs"
|
|
9
10
|
|
|
10
11
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
@@ -17,13 +18,20 @@ const tailwindPath = resolve(dirname(require.resolve("tailwindcss")), "..")
|
|
|
17
18
|
// Resolve promptslide core from source so Vite uses TS directly without a build step
|
|
18
19
|
const promptslidePath = resolve(__dirname, "../core/index.ts")
|
|
19
20
|
|
|
20
|
-
export function createViteConfig({ cwd, mode = "development" }) {
|
|
21
|
+
export function createViteConfig({ cwd, mode = "development", editor = true }) {
|
|
21
22
|
return {
|
|
22
23
|
configFile: false,
|
|
23
24
|
root: cwd,
|
|
24
25
|
mode,
|
|
25
|
-
plugins: [
|
|
26
|
+
plugins: [
|
|
27
|
+
...(mode === "development" && editor ? [editorPlugin({ root: cwd })] : []),
|
|
28
|
+
react(),
|
|
29
|
+
promptslidePlugin({ root: cwd })
|
|
30
|
+
],
|
|
26
31
|
resolve: {
|
|
32
|
+
// The CLI's core/editor and the deck must share one React dispatcher,
|
|
33
|
+
// including when the CLI is installed or linked outside the deck root.
|
|
34
|
+
dedupe: ["react", "react-dom"],
|
|
27
35
|
alias: {
|
|
28
36
|
"@": resolve(cwd, "src"),
|
|
29
37
|
promptslide: promptslidePath,
|
|
@@ -31,6 +39,19 @@ export function createViteConfig({ cwd, mode = "development" }) {
|
|
|
31
39
|
tailwindcss: tailwindPath
|
|
32
40
|
}
|
|
33
41
|
},
|
|
42
|
+
// These imports live in virtual entry modules, which dependency scanning
|
|
43
|
+
// cannot discover before the first page loads. Optimize them together so
|
|
44
|
+
// the viewer and editor never start with different dependency generations.
|
|
45
|
+
optimizeDeps: {
|
|
46
|
+
include: [
|
|
47
|
+
"react",
|
|
48
|
+
"react-dom/client",
|
|
49
|
+
"lucide-react",
|
|
50
|
+
"framer-motion",
|
|
51
|
+
"clsx",
|
|
52
|
+
"tailwind-merge"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
34
55
|
css: {
|
|
35
56
|
postcss: {
|
|
36
57
|
plugins: [tailwindcss()]
|
package/src/vite/plugin.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { readFileSync, writeFileSync, existsSync } from "node:fs"
|
|
|
2
2
|
import { join, resolve, sep, extname } from "node:path"
|
|
3
3
|
|
|
4
4
|
import { bold, dim } from "../utils/ansi.mjs"
|
|
5
|
+
import { readLoopConfig, DEFAULT_LOOP_CONFIG, loopConfigMiddleware } from "../utils/loop-config.mjs"
|
|
5
6
|
import { projectIdentity } from "../utils/studio-discovery.mjs"
|
|
6
7
|
|
|
7
8
|
/** Normalize a path to forward slashes so it can be used in ES module import strings on Windows. */
|
|
@@ -84,9 +85,11 @@ import { StrictMode, createElement } from "react"
|
|
|
84
85
|
import { createRoot } from "react-dom/client"
|
|
85
86
|
import "${r}/src/globals.css"
|
|
86
87
|
import App from "${r}/src/App"
|
|
88
|
+
import { LoopConfigProvider } from "promptslide"
|
|
89
|
+
const loopConfig = ${JSON.stringify(readLoopConfig(root) ?? DEFAULT_LOOP_CONFIG)}
|
|
87
90
|
|
|
88
91
|
createRoot(document.getElementById("root")).render(
|
|
89
|
-
createElement(StrictMode, null, createElement(App))
|
|
92
|
+
createElement(StrictMode, null, createElement(LoopConfigProvider, { config: loopConfig }, createElement(App)))
|
|
90
93
|
)
|
|
91
94
|
`
|
|
92
95
|
}
|
|
@@ -256,7 +259,7 @@ export function promptslidePlugin({ root: initialRoot } = {}) {
|
|
|
256
259
|
return {
|
|
257
260
|
server: {
|
|
258
261
|
watch: {
|
|
259
|
-
ignored: ["**/annotations.json"]
|
|
262
|
+
ignored: ["**/annotations.json", "**/.loop-config-*.tmp"]
|
|
260
263
|
}
|
|
261
264
|
}
|
|
262
265
|
}
|
|
@@ -284,6 +287,19 @@ export function promptslidePlugin({ root: initialRoot } = {}) {
|
|
|
284
287
|
},
|
|
285
288
|
|
|
286
289
|
configureServer(server) {
|
|
290
|
+
const invalidateLoopConfig = () => {
|
|
291
|
+
const entry = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ENTRY_ID)
|
|
292
|
+
if (entry) server.moduleGraph.invalidateModule(entry)
|
|
293
|
+
}
|
|
294
|
+
const loopFileChanged = path => {
|
|
295
|
+
if (resolve(path) === resolve(root, "loop-config.json")) invalidateLoopConfig()
|
|
296
|
+
}
|
|
297
|
+
// New page loads see the saved file without interrupting the current presentation.
|
|
298
|
+
server.watcher
|
|
299
|
+
.on("change", loopFileChanged)
|
|
300
|
+
.on("add", loopFileChanged)
|
|
301
|
+
.on("unlink", loopFileChanged)
|
|
302
|
+
server.middlewares.use(loopConfigMiddleware(root, invalidateLoopConfig))
|
|
287
303
|
server.middlewares.use(async (req, res, next) => {
|
|
288
304
|
if (req.method !== "GET" || req.url !== "/__promptslide_pdf") return next()
|
|
289
305
|
try {
|
|
@@ -384,7 +400,7 @@ export function promptslidePlugin({ root: initialRoot } = {}) {
|
|
|
384
400
|
server.middlewares.use(async (req, res, next) => {
|
|
385
401
|
const url = new URL(req.url, "http://localhost")
|
|
386
402
|
if (url.searchParams.get("pdf") === "true") {
|
|
387
|
-
const html = await server.transformIndexHtml("/index.html", getPdfHtmlTemplate())
|
|
403
|
+
const html = await server.transformIndexHtml("/index.html", getPdfHtmlTemplate(), req.url)
|
|
388
404
|
res.setHeader("Content-Type", "text/html")
|
|
389
405
|
res.statusCode = 200
|
|
390
406
|
res.end(html)
|
|
@@ -402,7 +418,8 @@ export function promptslidePlugin({ root: initialRoot } = {}) {
|
|
|
402
418
|
}
|
|
403
419
|
const html = await server.transformIndexHtml(
|
|
404
420
|
"/index.html",
|
|
405
|
-
getExportHtmlTemplate(slidePath)
|
|
421
|
+
getExportHtmlTemplate(slidePath),
|
|
422
|
+
req.url
|
|
406
423
|
)
|
|
407
424
|
res.setHeader("Content-Type", "text/html")
|
|
408
425
|
res.statusCode = 200
|
|
@@ -3,6 +3,6 @@ import { SlideTitle } from "@/slides/slide-title";
|
|
|
3
3
|
import { SlideExample } from "@/slides/slide-example";
|
|
4
4
|
|
|
5
5
|
export const slides: SlideConfig[] = [
|
|
6
|
-
{ id: "title", component: SlideTitle, steps: 0 },
|
|
7
|
-
{ id: "example", component: SlideExample, steps: 2 },
|
|
6
|
+
{ id: "title", title: "Introduction", component: SlideTitle, steps: 0 },
|
|
7
|
+
{ id: "example", title: "Example", component: SlideExample, steps: 2 },
|
|
8
8
|
];
|