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/src/index.mjs
CHANGED
|
@@ -40,6 +40,7 @@ function printHelp() {
|
|
|
40
40
|
console.log()
|
|
41
41
|
console.log(` ${bold("Tools:")}`)
|
|
42
42
|
console.log(` to-image ${dim("<slide>")} Export a slide as a PNG image`)
|
|
43
|
+
console.log(` to-pdf Export the full deck as a PDF`)
|
|
43
44
|
console.log()
|
|
44
45
|
console.log(` ${bold("Options:")}`)
|
|
45
46
|
console.log(` --help, -h Show this help message`)
|
|
@@ -136,6 +137,11 @@ switch (command) {
|
|
|
136
137
|
await toImage(args)
|
|
137
138
|
break
|
|
138
139
|
}
|
|
140
|
+
case "to-pdf": {
|
|
141
|
+
const { toPdf } = await import("./commands/to-pdf.mjs")
|
|
142
|
+
await toPdf(args)
|
|
143
|
+
break
|
|
144
|
+
}
|
|
139
145
|
case "--help":
|
|
140
146
|
case "-h":
|
|
141
147
|
case undefined:
|
|
@@ -33,9 +33,9 @@ export function deriveImportPath(target, filePath) {
|
|
|
33
33
|
* Uses string manipulation to preserve existing content.
|
|
34
34
|
*
|
|
35
35
|
* @param {string} cwd - Project root directory
|
|
36
|
-
* @param {{ componentName: string, importPath: string, steps: number }} opts
|
|
36
|
+
* @param {{ componentName: string, importPath: string, steps: number, id?: string }} opts
|
|
37
37
|
*/
|
|
38
|
-
export function addSlideToDeckConfig(cwd, { componentName, importPath, steps }) {
|
|
38
|
+
export function addSlideToDeckConfig(cwd, { componentName, importPath, steps, id }) {
|
|
39
39
|
const configPath = join(cwd, DECK_CONFIG_PATH)
|
|
40
40
|
if (!existsSync(configPath)) {
|
|
41
41
|
console.warn(` Warning: ${DECK_CONFIG_PATH} not found. Skipping deck-config update.`)
|
|
@@ -51,7 +51,13 @@ export function addSlideToDeckConfig(cwd, { componentName, importPath, steps })
|
|
|
51
51
|
|
|
52
52
|
// Build import and slide entry strings
|
|
53
53
|
const importLine = `import { ${componentName} } from "${importPath}"`
|
|
54
|
-
const
|
|
54
|
+
const slideId =
|
|
55
|
+
id ||
|
|
56
|
+
importPath
|
|
57
|
+
.split("/")
|
|
58
|
+
.pop()
|
|
59
|
+
.replace(/^slide-/, "")
|
|
60
|
+
const slideEntry = ` { id: ${JSON.stringify(slideId)}, component: ${componentName}, steps: ${steps} },`
|
|
55
61
|
|
|
56
62
|
// Find insertion point for import:
|
|
57
63
|
// Look for the last import from "@/slides/" or "@/layouts/"
|
|
@@ -70,11 +76,12 @@ export function addSlideToDeckConfig(cwd, { componentName, importPath, steps })
|
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
// Insert import after last slide import, or after last import, or at top
|
|
73
|
-
const importInsertIdx =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
const importInsertIdx =
|
|
80
|
+
lastSlideImportIdx >= 0
|
|
81
|
+
? lastSlideImportIdx + 1
|
|
82
|
+
: lastAnyImportIdx >= 0
|
|
83
|
+
? lastAnyImportIdx + 1
|
|
84
|
+
: 0
|
|
78
85
|
|
|
79
86
|
importLines.splice(importInsertIdx, 0, importLine)
|
|
80
87
|
content = importLines.join("\n")
|
|
@@ -169,7 +176,7 @@ export function removeSlideFromDeckConfig(cwd, componentName) {
|
|
|
169
176
|
* Replace the entire deck-config.ts with a new slide manifest (for deck type).
|
|
170
177
|
*
|
|
171
178
|
* @param {string} cwd - Project root directory
|
|
172
|
-
* @param {{ componentName: string, importPath: string, steps: number, section?: string }[]} slides
|
|
179
|
+
* @param {{ componentName: string, importPath: string, steps: number, id?: string, section?: string }[]} slides
|
|
173
180
|
* @param {{ transition?: string, directionalTransition?: boolean }} opts
|
|
174
181
|
*/
|
|
175
182
|
export function replaceDeckConfig(cwd, slides, opts = {}) {
|
|
@@ -182,7 +189,17 @@ export function replaceDeckConfig(cwd, slides, opts = {}) {
|
|
|
182
189
|
|
|
183
190
|
const slideEntries = slides
|
|
184
191
|
.map(s => {
|
|
185
|
-
const
|
|
192
|
+
const id =
|
|
193
|
+
s.id ||
|
|
194
|
+
s.importPath
|
|
195
|
+
.split("/")
|
|
196
|
+
.pop()
|
|
197
|
+
.replace(/^slide-/, "")
|
|
198
|
+
const parts = [
|
|
199
|
+
`id: ${JSON.stringify(id)}`,
|
|
200
|
+
`component: ${s.componentName}`,
|
|
201
|
+
`steps: ${s.steps}`
|
|
202
|
+
]
|
|
186
203
|
if (s.section) parts.push(`section: "${s.section}"`)
|
|
187
204
|
return ` { ${parts.join(", ")} },`
|
|
188
205
|
})
|
|
@@ -213,7 +230,7 @@ export function replaceDeckConfig(cwd, slides, opts = {}) {
|
|
|
213
230
|
/**
|
|
214
231
|
* Parse deck-config.ts and extract the deckConfig structure for publishing.
|
|
215
232
|
* Returns { transition?, directionalTransition?, slides } where slides is
|
|
216
|
-
* an array of { slug, steps, section? }.
|
|
233
|
+
* an array of { slug, id?, steps, section? }.
|
|
217
234
|
*
|
|
218
235
|
* @param {string} cwd - Project root directory
|
|
219
236
|
* @returns {{ transition?: string, directionalTransition?: boolean, slides: { slug: string, steps: number, section?: string }[] } | null}
|
|
@@ -280,6 +297,8 @@ export function parseDeckConfig(cwd) {
|
|
|
280
297
|
if (!slug) continue // layout or unknown import — skip
|
|
281
298
|
const stepsMatch = body.match(/steps:\s*(\d+)/)
|
|
282
299
|
const entry = { slug, steps: stepsMatch ? parseInt(stepsMatch[1], 10) : 0 }
|
|
300
|
+
const idMatch = body.match(/\bid:\s*["']([^"']+)["']/)
|
|
301
|
+
if (idMatch) entry.id = idMatch[1]
|
|
283
302
|
// Preserve original component name so pull can reconstruct the correct export
|
|
284
303
|
if (slugToComponent[slug]) entry.componentName = slugToComponent[slug]
|
|
285
304
|
const sectionMatch = body.match(/section:\s*["']([^"']+)["']/)
|
package/src/utils/export.mjs
CHANGED
|
@@ -4,8 +4,9 @@ import { fileURLToPath } from "node:url"
|
|
|
4
4
|
|
|
5
5
|
import { createServer } from "vite"
|
|
6
6
|
|
|
7
|
-
import { ensureTsConfig } from "./tsconfig.mjs"
|
|
8
7
|
import { createViteConfig } from "../vite/config.mjs"
|
|
8
|
+
import { projectIdentity, readStudioServer } from "./studio-discovery.mjs"
|
|
9
|
+
import { ensureTsConfig } from "./tsconfig.mjs"
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Check if Playwright is available.
|
|
@@ -40,12 +41,88 @@ async function ensureChromium(chromium) {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/** Find a Studio process serving this project; other projects must not be reused. */
|
|
45
|
+
async function findStudioServer(cwd, preferredPort) {
|
|
46
|
+
const candidates = preferredPort
|
|
47
|
+
? [`http://127.0.0.1:${preferredPort}`, `http://localhost:${preferredPort}`]
|
|
48
|
+
: [
|
|
49
|
+
readStudioServer(cwd),
|
|
50
|
+
...Array.from({ length: 11 }, (_, i) => `http://127.0.0.1:${5173 + i}`)
|
|
51
|
+
]
|
|
52
|
+
const results = await Promise.all(
|
|
53
|
+
candidates.filter(Boolean).map(async baseUrl => {
|
|
54
|
+
try {
|
|
55
|
+
const response = await fetch(`${baseUrl}/__promptslide_info`, {
|
|
56
|
+
signal: AbortSignal.timeout(350)
|
|
57
|
+
})
|
|
58
|
+
if (!response.ok) return null
|
|
59
|
+
const info = await response.json()
|
|
60
|
+
return info.projectId === projectIdentity(cwd) ? baseUrl : null
|
|
61
|
+
} catch {
|
|
62
|
+
return null
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
return results.find(Boolean) || null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function getCaptureServer(cwd, preferredPort) {
|
|
70
|
+
const studio = await findStudioServer(cwd, preferredPort)
|
|
71
|
+
if (studio) return { baseUrl: studio, close: async () => {} }
|
|
72
|
+
|
|
73
|
+
ensureTsConfig(cwd)
|
|
74
|
+
const config = createViteConfig({ cwd, mode: "development" })
|
|
75
|
+
const server = await createServer({
|
|
76
|
+
...config,
|
|
77
|
+
server: { port: 0, strictPort: true },
|
|
78
|
+
logLevel: "silent"
|
|
79
|
+
})
|
|
80
|
+
await server.listen()
|
|
81
|
+
const address = server.httpServer.address()
|
|
82
|
+
const port = typeof address === "object" ? address.port : 0
|
|
83
|
+
return { baseUrl: `http://127.0.0.1:${port}`, close: () => server.close() }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function waitForExport(page, errors) {
|
|
87
|
+
try {
|
|
88
|
+
const handle = await page.waitForFunction(
|
|
89
|
+
() => {
|
|
90
|
+
const overlay = document.querySelector("vite-error-overlay")
|
|
91
|
+
if (overlay) {
|
|
92
|
+
const root = overlay.shadowRoot
|
|
93
|
+
return {
|
|
94
|
+
error:
|
|
95
|
+
root?.querySelector(".message-body")?.textContent ||
|
|
96
|
+
root?.textContent ||
|
|
97
|
+
"Vite compilation failed"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (document.querySelector("[data-export-ready='true']")) return { ready: true }
|
|
101
|
+
return false
|
|
102
|
+
},
|
|
103
|
+
null,
|
|
104
|
+
{ timeout: 15000 }
|
|
105
|
+
)
|
|
106
|
+
const result = await handle.jsonValue()
|
|
107
|
+
if (result.error) throw new Error(`Slide compile error: ${result.error.trim()}`)
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (errors.length) throw new Error(`${err.message}\nBrowser errors:\n ${errors.join("\n ")}`)
|
|
110
|
+
throw err
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
43
114
|
/**
|
|
44
115
|
* Capture a screenshot of a specific slide.
|
|
45
116
|
* @param {{ cwd: string, slidePath: string, width?: number, height?: number }} opts
|
|
46
117
|
* @returns {Promise<Buffer | null>} PNG buffer, or null if Playwright is not installed
|
|
47
118
|
*/
|
|
48
|
-
export async function captureSlideScreenshot({
|
|
119
|
+
export async function captureSlideScreenshot({
|
|
120
|
+
cwd,
|
|
121
|
+
slidePath,
|
|
122
|
+
width = 1280,
|
|
123
|
+
height = 720,
|
|
124
|
+
studioPort
|
|
125
|
+
}) {
|
|
49
126
|
let chromium
|
|
50
127
|
try {
|
|
51
128
|
const pw = await import("playwright")
|
|
@@ -56,19 +133,8 @@ export async function captureSlideScreenshot({ cwd, slidePath, width = 1280, hei
|
|
|
56
133
|
|
|
57
134
|
await ensureChromium(chromium)
|
|
58
135
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const config = createViteConfig({ cwd, mode: "development" })
|
|
62
|
-
const server = await createServer({
|
|
63
|
-
...config,
|
|
64
|
-
server: { port: 0, strictPort: false },
|
|
65
|
-
logLevel: "silent"
|
|
66
|
-
})
|
|
67
|
-
await server.listen()
|
|
68
|
-
|
|
69
|
-
const address = server.httpServer.address()
|
|
70
|
-
const port = typeof address === "object" ? address.port : 0
|
|
71
|
-
const url = `http://localhost:${port}/?export=true&slidePath=${encodeURIComponent(slidePath)}`
|
|
136
|
+
const server = await getCaptureServer(cwd, studioPort)
|
|
137
|
+
const url = `${server.baseUrl}/?export=true&slidePath=${encodeURIComponent(slidePath)}`
|
|
72
138
|
|
|
73
139
|
let browser
|
|
74
140
|
try {
|
|
@@ -78,14 +144,10 @@ export async function captureSlideScreenshot({ cwd, slidePath, width = 1280, hei
|
|
|
78
144
|
const errors = []
|
|
79
145
|
page.on("pageerror", err => errors.push(err.message))
|
|
80
146
|
|
|
81
|
-
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 15000 })
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
throw err
|
|
87
|
-
})
|
|
88
|
-
await page.waitForTimeout(200)
|
|
147
|
+
const response = await page.goto(url, { waitUntil: "domcontentloaded", timeout: 15000 })
|
|
148
|
+
if (!response?.ok())
|
|
149
|
+
throw new Error(`Slide export failed: ${response?.status()} ${await response?.text()}`)
|
|
150
|
+
await waitForExport(page, errors)
|
|
89
151
|
|
|
90
152
|
const element = await page.$("[data-export-ready='true']")
|
|
91
153
|
const screenshot = await element.screenshot({ type: "png" })
|
|
@@ -105,7 +167,7 @@ export async function captureSlideScreenshot({ cwd, slidePath, width = 1280, hei
|
|
|
105
167
|
* @returns {Promise<{ capture: (slidePath: string) => Promise<string | null>, close: () => Promise<void> } | null>}
|
|
106
168
|
* null if Playwright is not available
|
|
107
169
|
*/
|
|
108
|
-
export async function createCaptureSession({ cwd, width = 1280, height = 720 }) {
|
|
170
|
+
export async function createCaptureSession({ cwd, width = 1280, height = 720, studioPort }) {
|
|
109
171
|
let chromium
|
|
110
172
|
try {
|
|
111
173
|
const pw = await import("playwright")
|
|
@@ -115,36 +177,21 @@ export async function createCaptureSession({ cwd, width = 1280, height = 720 })
|
|
|
115
177
|
}
|
|
116
178
|
|
|
117
179
|
await ensureChromium(chromium)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const config = createViteConfig({ cwd, mode: "development" })
|
|
121
|
-
const server = await createServer({
|
|
122
|
-
...config,
|
|
123
|
-
server: { port: 0, strictPort: false },
|
|
124
|
-
logLevel: "silent"
|
|
125
|
-
})
|
|
126
|
-
await server.listen()
|
|
127
|
-
|
|
128
|
-
const address = server.httpServer.address()
|
|
129
|
-
const port = typeof address === "object" ? address.port : 0
|
|
130
|
-
|
|
180
|
+
const server = await getCaptureServer(cwd, studioPort)
|
|
131
181
|
const browser = await chromium.launch({ headless: true })
|
|
182
|
+
const page = await browser.newPage({ viewport: { width, height } })
|
|
183
|
+
let pending = Promise.resolve()
|
|
132
184
|
|
|
133
|
-
async function
|
|
134
|
-
const url =
|
|
135
|
-
const
|
|
185
|
+
async function captureOne(slidePath) {
|
|
186
|
+
const url = `${server.baseUrl}/?export=true&slidePath=${encodeURIComponent(slidePath)}`
|
|
187
|
+
const errors = []
|
|
188
|
+
const onPageError = err => errors.push(err.message)
|
|
189
|
+
page.on("pageerror", onPageError)
|
|
136
190
|
try {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
await page
|
|
141
|
-
await page.waitForSelector("[data-export-ready='true']", { timeout: 15000 }).catch(err => {
|
|
142
|
-
if (errors.length) {
|
|
143
|
-
throw new Error(`${err.message}\n Browser errors:\n ${errors.join("\n ")}`)
|
|
144
|
-
}
|
|
145
|
-
throw err
|
|
146
|
-
})
|
|
147
|
-
await page.waitForTimeout(200)
|
|
191
|
+
const response = await page.goto(url, { waitUntil: "domcontentloaded", timeout: 15000 })
|
|
192
|
+
if (!response?.ok())
|
|
193
|
+
throw new Error(`Slide export failed: ${response?.status()} ${await response?.text()}`)
|
|
194
|
+
await waitForExport(page, errors)
|
|
148
195
|
|
|
149
196
|
const element = await page.$("[data-export-ready='true']")
|
|
150
197
|
const screenshot = await element.screenshot({ type: "png" })
|
|
@@ -153,11 +200,22 @@ export async function createCaptureSession({ cwd, width = 1280, height = 720 })
|
|
|
153
200
|
console.error(` Screenshot error: ${err.message}`)
|
|
154
201
|
return null
|
|
155
202
|
} finally {
|
|
156
|
-
|
|
203
|
+
page.off("pageerror", onPageError)
|
|
157
204
|
}
|
|
158
205
|
}
|
|
159
206
|
|
|
207
|
+
function capture(slidePath) {
|
|
208
|
+
const result = pending.then(() => captureOne(slidePath))
|
|
209
|
+
pending = result.then(
|
|
210
|
+
() => {},
|
|
211
|
+
() => {}
|
|
212
|
+
)
|
|
213
|
+
return result
|
|
214
|
+
}
|
|
215
|
+
|
|
160
216
|
async function close() {
|
|
217
|
+
await pending
|
|
218
|
+
await page.close().catch(() => {})
|
|
161
219
|
await browser.close().catch(() => {})
|
|
162
220
|
await server.close()
|
|
163
221
|
}
|
|
@@ -181,3 +239,44 @@ export async function captureSlideAsDataUri({ cwd, slidePath }) {
|
|
|
181
239
|
return null
|
|
182
240
|
}
|
|
183
241
|
}
|
|
242
|
+
|
|
243
|
+
/** Render the whole deck at its design size through an existing Studio server. */
|
|
244
|
+
export async function captureDeckPdfFromServer(baseUrl) {
|
|
245
|
+
const { chromium } = await import("playwright")
|
|
246
|
+
await ensureChromium(chromium)
|
|
247
|
+
const browser = await chromium.launch({ headless: true })
|
|
248
|
+
try {
|
|
249
|
+
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } })
|
|
250
|
+
const errors = []
|
|
251
|
+
page.on("pageerror", error => errors.push(error.message))
|
|
252
|
+
const response = await page.goto(`${baseUrl}/?pdf=true`, {
|
|
253
|
+
waitUntil: "domcontentloaded",
|
|
254
|
+
timeout: 15000
|
|
255
|
+
})
|
|
256
|
+
if (!response?.ok()) throw new Error(`Deck PDF export failed: ${response?.status()}`)
|
|
257
|
+
try {
|
|
258
|
+
await page.waitForSelector("[data-pdf-ready='true']", { timeout: 15000 })
|
|
259
|
+
} catch (error) {
|
|
260
|
+
if (errors.length)
|
|
261
|
+
throw new Error(`${error.message}\nBrowser errors:\n ${errors.join("\n ")}`)
|
|
262
|
+
throw error
|
|
263
|
+
}
|
|
264
|
+
return await page.pdf({
|
|
265
|
+
width: "1280px",
|
|
266
|
+
height: "720px",
|
|
267
|
+
printBackground: true,
|
|
268
|
+
preferCSSPageSize: true
|
|
269
|
+
})
|
|
270
|
+
} finally {
|
|
271
|
+
await browser.close()
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export async function captureDeckPdf({ cwd, studioPort }) {
|
|
276
|
+
const server = await getCaptureServer(cwd, studioPort)
|
|
277
|
+
try {
|
|
278
|
+
return await captureDeckPdfFromServer(server.baseUrl)
|
|
279
|
+
} finally {
|
|
280
|
+
await server.close()
|
|
281
|
+
}
|
|
282
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createHash } from "node:crypto"
|
|
2
|
+
import { readFileSync, writeFileSync, unlinkSync } from "node:fs"
|
|
3
|
+
import { tmpdir } from "node:os"
|
|
4
|
+
import { join, resolve } from "node:path"
|
|
5
|
+
|
|
6
|
+
export function projectIdentity(cwd) {
|
|
7
|
+
return createHash("sha256").update(resolve(cwd)).digest("hex").slice(0, 16)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function discoveryPath(cwd) {
|
|
11
|
+
return join(tmpdir(), `promptslide-studio-${projectIdentity(cwd)}.json`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function registerStudioServer(cwd, baseUrl) {
|
|
15
|
+
const path = discoveryPath(cwd)
|
|
16
|
+
writeFileSync(
|
|
17
|
+
path,
|
|
18
|
+
JSON.stringify({ root: resolve(cwd), baseUrl: baseUrl.replace(/\/$/, ""), pid: process.pid })
|
|
19
|
+
)
|
|
20
|
+
process.on("exit", () => {
|
|
21
|
+
try {
|
|
22
|
+
const current = JSON.parse(readFileSync(path, "utf8"))
|
|
23
|
+
if (current.pid === process.pid) unlinkSync(path)
|
|
24
|
+
} catch {
|
|
25
|
+
/* Another process may have removed the file. */
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function readStudioServer(cwd) {
|
|
31
|
+
try {
|
|
32
|
+
const info = JSON.parse(readFileSync(discoveryPath(cwd), "utf8"))
|
|
33
|
+
return info.root === resolve(cwd) ? info.baseUrl : null
|
|
34
|
+
} catch {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
}
|
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()]
|