@cuppacue/cli 0.1.0
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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/ai/__tests__/generate.test.d.ts +2 -0
- package/dist/ai/__tests__/generate.test.d.ts.map +1 -0
- package/dist/ai/__tests__/generate.test.js +129 -0
- package/dist/ai/__tests__/generate.test.js.map +1 -0
- package/dist/ai/__tests__/images.test.d.ts +2 -0
- package/dist/ai/__tests__/images.test.d.ts.map +1 -0
- package/dist/ai/__tests__/images.test.js +186 -0
- package/dist/ai/__tests__/images.test.js.map +1 -0
- package/dist/ai/__tests__/prompt.test.d.ts +2 -0
- package/dist/ai/__tests__/prompt.test.d.ts.map +1 -0
- package/dist/ai/__tests__/prompt.test.js +98 -0
- package/dist/ai/__tests__/prompt.test.js.map +1 -0
- package/dist/ai/__tests__/refine.test.d.ts +2 -0
- package/dist/ai/__tests__/refine.test.d.ts.map +1 -0
- package/dist/ai/__tests__/refine.test.js +87 -0
- package/dist/ai/__tests__/refine.test.js.map +1 -0
- package/dist/ai/client.d.ts +4 -0
- package/dist/ai/client.d.ts.map +1 -0
- package/dist/ai/client.js +36 -0
- package/dist/ai/client.js.map +1 -0
- package/dist/ai/example.d.ts +6 -0
- package/dist/ai/example.d.ts.map +1 -0
- package/dist/ai/example.js +284 -0
- package/dist/ai/example.js.map +1 -0
- package/dist/ai/generate.d.ts +15 -0
- package/dist/ai/generate.d.ts.map +1 -0
- package/dist/ai/generate.js +120 -0
- package/dist/ai/generate.js.map +1 -0
- package/dist/ai/images.d.ts +8 -0
- package/dist/ai/images.d.ts.map +1 -0
- package/dist/ai/images.js +71 -0
- package/dist/ai/images.js.map +1 -0
- package/dist/ai/index.d.ts +7 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +7 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/prompt.d.ts +10 -0
- package/dist/ai/prompt.d.ts.map +1 -0
- package/dist/ai/prompt.js +119 -0
- package/dist/ai/prompt.js.map +1 -0
- package/dist/ai/refine.d.ts +6 -0
- package/dist/ai/refine.d.ts.map +1 -0
- package/dist/ai/refine.js +22 -0
- package/dist/ai/refine.js.map +1 -0
- package/dist/ai/schema.d.ts +5 -0
- package/dist/ai/schema.d.ts.map +1 -0
- package/dist/ai/schema.js +292 -0
- package/dist/ai/schema.js.map +1 -0
- package/dist/commands/__tests__/backstage.test.d.ts +2 -0
- package/dist/commands/__tests__/backstage.test.d.ts.map +1 -0
- package/dist/commands/__tests__/backstage.test.js +45 -0
- package/dist/commands/__tests__/backstage.test.js.map +1 -0
- package/dist/commands/__tests__/export.test.d.ts +2 -0
- package/dist/commands/__tests__/export.test.d.ts.map +1 -0
- package/dist/commands/__tests__/export.test.js +50 -0
- package/dist/commands/__tests__/export.test.js.map +1 -0
- package/dist/commands/ai.d.ts +2 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +113 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +121 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/export-pdf.d.ts +9 -0
- package/dist/commands/export-pdf.d.ts.map +1 -0
- package/dist/commands/export-pdf.js +109 -0
- package/dist/commands/export-pdf.js.map +1 -0
- package/dist/commands/export.d.ts +2 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +147 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +54 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/preview.d.ts +2 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +78 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/serve.d.ts +7 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +773 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/validate.d.ts +2 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +39 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/github/__tests__/fetcher.test.d.ts +2 -0
- package/dist/github/__tests__/fetcher.test.d.ts.map +1 -0
- package/dist/github/__tests__/fetcher.test.js +102 -0
- package/dist/github/__tests__/fetcher.test.js.map +1 -0
- package/dist/github/__tests__/rewrite-images.test.d.ts +2 -0
- package/dist/github/__tests__/rewrite-images.test.d.ts.map +1 -0
- package/dist/github/__tests__/rewrite-images.test.js +79 -0
- package/dist/github/__tests__/rewrite-images.test.js.map +1 -0
- package/dist/github/__tests__/url-parser.test.d.ts +2 -0
- package/dist/github/__tests__/url-parser.test.d.ts.map +1 -0
- package/dist/github/__tests__/url-parser.test.js +96 -0
- package/dist/github/__tests__/url-parser.test.js.map +1 -0
- package/dist/github/fetcher.d.ts +10 -0
- package/dist/github/fetcher.d.ts.map +1 -0
- package/dist/github/fetcher.js +53 -0
- package/dist/github/fetcher.js.map +1 -0
- package/dist/github/index.d.ts +5 -0
- package/dist/github/index.d.ts.map +1 -0
- package/dist/github/index.js +4 -0
- package/dist/github/index.js.map +1 -0
- package/dist/github/rewrite-images.d.ts +7 -0
- package/dist/github/rewrite-images.d.ts.map +1 -0
- package/dist/github/rewrite-images.js +26 -0
- package/dist/github/rewrite-images.js.map +1 -0
- package/dist/github/url-parser.d.ts +12 -0
- package/dist/github/url-parser.d.ts.map +1 -0
- package/dist/github/url-parser.js +60 -0
- package/dist/github/url-parser.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown/__tests__/auto-slide.test.d.ts +2 -0
- package/dist/markdown/__tests__/auto-slide.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/auto-slide.test.js +325 -0
- package/dist/markdown/__tests__/auto-slide.test.js.map +1 -0
- package/dist/markdown/__tests__/compiler.test.d.ts +2 -0
- package/dist/markdown/__tests__/compiler.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/compiler.test.js +142 -0
- package/dist/markdown/__tests__/compiler.test.js.map +1 -0
- package/dist/markdown/__tests__/custom-parsers.test.d.ts +2 -0
- package/dist/markdown/__tests__/custom-parsers.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/custom-parsers.test.js +182 -0
- package/dist/markdown/__tests__/custom-parsers.test.js.map +1 -0
- package/dist/markdown/__tests__/frontmatter.test.d.ts +2 -0
- package/dist/markdown/__tests__/frontmatter.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/frontmatter.test.js +162 -0
- package/dist/markdown/__tests__/frontmatter.test.js.map +1 -0
- package/dist/markdown/__tests__/layout.test.d.ts +2 -0
- package/dist/markdown/__tests__/layout.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/layout.test.js +85 -0
- package/dist/markdown/__tests__/layout.test.js.map +1 -0
- package/dist/markdown/__tests__/parser.test.d.ts +2 -0
- package/dist/markdown/__tests__/parser.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/parser.test.js +646 -0
- package/dist/markdown/__tests__/parser.test.js.map +1 -0
- package/dist/markdown/__tests__/timesheet-gen.test.d.ts +2 -0
- package/dist/markdown/__tests__/timesheet-gen.test.d.ts.map +1 -0
- package/dist/markdown/__tests__/timesheet-gen.test.js +90 -0
- package/dist/markdown/__tests__/timesheet-gen.test.js.map +1 -0
- package/dist/markdown/auto-slide.d.ts +16 -0
- package/dist/markdown/auto-slide.d.ts.map +1 -0
- package/dist/markdown/auto-slide.js +144 -0
- package/dist/markdown/auto-slide.js.map +1 -0
- package/dist/markdown/compiler.d.ts +12 -0
- package/dist/markdown/compiler.d.ts.map +1 -0
- package/dist/markdown/compiler.js +47 -0
- package/dist/markdown/compiler.js.map +1 -0
- package/dist/markdown/custom-parsers.d.ts +69 -0
- package/dist/markdown/custom-parsers.d.ts.map +1 -0
- package/dist/markdown/custom-parsers.js +227 -0
- package/dist/markdown/custom-parsers.js.map +1 -0
- package/dist/markdown/frontmatter.d.ts +33 -0
- package/dist/markdown/frontmatter.d.ts.map +1 -0
- package/dist/markdown/frontmatter.js +83 -0
- package/dist/markdown/frontmatter.js.map +1 -0
- package/dist/markdown/id.d.ts +4 -0
- package/dist/markdown/id.d.ts.map +1 -0
- package/dist/markdown/id.js +15 -0
- package/dist/markdown/id.js.map +1 -0
- package/dist/markdown/index.d.ts +8 -0
- package/dist/markdown/index.d.ts.map +1 -0
- package/dist/markdown/index.js +6 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/markdown/parser.d.ts +35 -0
- package/dist/markdown/parser.d.ts.map +1 -0
- package/dist/markdown/parser.js +605 -0
- package/dist/markdown/parser.js.map +1 -0
- package/dist/markdown/timesheet-gen.d.ts +6 -0
- package/dist/markdown/timesheet-gen.d.ts.map +1 -0
- package/dist/markdown/timesheet-gen.js +223 -0
- package/dist/markdown/timesheet-gen.js.map +1 -0
- package/dist/player/CuePlayer.d.ts +58 -0
- package/dist/player/CuePlayer.d.ts.map +1 -0
- package/dist/player/NavBar.d.ts +31 -0
- package/dist/player/NavBar.d.ts.map +1 -0
- package/dist/player/PresenterView.d.ts +25 -0
- package/dist/player/PresenterView.d.ts.map +1 -0
- package/dist/player/SlideOverview.d.ts +16 -0
- package/dist/player/SlideOverview.d.ts.map +1 -0
- package/dist/player/__mocks__/chart.d.ts +29 -0
- package/dist/player/__mocks__/chart.d.ts.map +1 -0
- package/dist/player/__mocks__/mermaid.d.ts +8 -0
- package/dist/player/__mocks__/mermaid.d.ts.map +1 -0
- package/dist/player/__tests__/animator.test.d.ts +2 -0
- package/dist/player/__tests__/animator.test.d.ts.map +1 -0
- package/dist/player/__tests__/layout.test.d.ts +2 -0
- package/dist/player/__tests__/layout.test.d.ts.map +1 -0
- package/dist/player/__tests__/overview.test.d.ts +2 -0
- package/dist/player/__tests__/overview.test.d.ts.map +1 -0
- package/dist/player/__tests__/timeline.test.d.ts +2 -0
- package/dist/player/__tests__/timeline.test.d.ts.map +1 -0
- package/dist/player/animator.d.ts +4 -0
- package/dist/player/animator.d.ts.map +1 -0
- package/dist/player/cuppacue-player.css +1 -0
- package/dist/player/cuppacue-player.js +3247 -0
- package/dist/player/index.d.ts +14 -0
- package/dist/player/index.d.ts.map +1 -0
- package/dist/player/loader.d.ts +3 -0
- package/dist/player/loader.d.ts.map +1 -0
- package/dist/player/main.d.ts +2 -0
- package/dist/player/main.d.ts.map +1 -0
- package/dist/player/navigator.d.ts +27 -0
- package/dist/player/navigator.d.ts.map +1 -0
- package/dist/player/renderer.d.ts +9 -0
- package/dist/player/renderer.d.ts.map +1 -0
- package/dist/player/renderers/cards.d.ts +3 -0
- package/dist/player/renderers/cards.d.ts.map +1 -0
- package/dist/player/renderers/chart.d.ts +3 -0
- package/dist/player/renderers/chart.d.ts.map +1 -0
- package/dist/player/renderers/icons.d.ts +6 -0
- package/dist/player/renderers/icons.d.ts.map +1 -0
- package/dist/player/renderers/mermaid.d.ts +3 -0
- package/dist/player/renderers/mermaid.d.ts.map +1 -0
- package/dist/player/renderers/stats.d.ts +3 -0
- package/dist/player/renderers/stats.d.ts.map +1 -0
- package/dist/player/renderers/terminal.d.ts +3 -0
- package/dist/player/renderers/terminal.d.ts.map +1 -0
- package/dist/player/themes/dark.d.ts +2 -0
- package/dist/player/themes/dark.d.ts.map +1 -0
- package/dist/player/themes/light.d.ts +2 -0
- package/dist/player/themes/light.d.ts.map +1 -0
- package/dist/player/timeline.d.ts +43 -0
- package/dist/player/timeline.d.ts.map +1 -0
- package/dist/serve.d.ts +2 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +171 -0
- package/dist/serve.js.map +1 -0
- package/dist/templates/discover.d.ts +20 -0
- package/dist/templates/discover.d.ts.map +1 -0
- package/dist/templates/discover.js +86 -0
- package/dist/templates/discover.js.map +1 -0
- package/dist/templates/loader.d.ts +17 -0
- package/dist/templates/loader.d.ts.map +1 -0
- package/dist/templates/loader.js +40 -0
- package/dist/templates/loader.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
function getProvider() {
|
|
2
|
+
const pexelsKey = process.env.PEXELS_API_KEY;
|
|
3
|
+
if (pexelsKey)
|
|
4
|
+
return { name: "pexels", key: pexelsKey };
|
|
5
|
+
const unsplashKey = process.env.UNSPLASH_ACCESS_KEY;
|
|
6
|
+
if (unsplashKey)
|
|
7
|
+
return { name: "unsplash", key: unsplashKey };
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve placeholder images to real photos via Pexels or Unsplash.
|
|
12
|
+
* Checks PEXELS_API_KEY first, then UNSPLASH_ACCESS_KEY.
|
|
13
|
+
* When neither is set, placeholders remain and render nicely in the player.
|
|
14
|
+
*/
|
|
15
|
+
export async function resolveImages(cup) {
|
|
16
|
+
const placeholders = [];
|
|
17
|
+
for (const scene of cup.content?.scenes ?? []) {
|
|
18
|
+
for (const el of scene.elements ?? []) {
|
|
19
|
+
if (el.type === "image" && el.placeholder) {
|
|
20
|
+
placeholders.push(el);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (placeholders.length === 0)
|
|
25
|
+
return;
|
|
26
|
+
const provider = getProvider();
|
|
27
|
+
if (!provider) {
|
|
28
|
+
console.log("Tip: Set PEXELS_API_KEY or UNSPLASH_ACCESS_KEY to resolve image placeholders to real photos");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
await Promise.allSettled(placeholders.map((img) => resolveOne(img, provider)));
|
|
32
|
+
}
|
|
33
|
+
function sanitizeQuery(alt) {
|
|
34
|
+
return alt.replace(/[^\w\s-]/g, " ").trim().slice(0, 100);
|
|
35
|
+
}
|
|
36
|
+
async function resolveOne(img, provider) {
|
|
37
|
+
const query = sanitizeQuery(img.alt ?? "abstract background");
|
|
38
|
+
if (provider.name === "pexels") {
|
|
39
|
+
await resolveViaPexels(img, query, provider.key);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
await resolveViaUnsplash(img, query, provider.key);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function resolveViaPexels(img, query, apiKey) {
|
|
46
|
+
const url = `https://api.pexels.com/v1/search?query=${encodeURIComponent(query)}&per_page=1&orientation=landscape`;
|
|
47
|
+
const res = await fetch(url, {
|
|
48
|
+
headers: { Authorization: apiKey },
|
|
49
|
+
});
|
|
50
|
+
if (!res.ok)
|
|
51
|
+
return;
|
|
52
|
+
const data = (await res.json());
|
|
53
|
+
if (data.photos?.length > 0) {
|
|
54
|
+
img.src = data.photos[0].src.large2x;
|
|
55
|
+
img.placeholder = false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function resolveViaUnsplash(img, query, accessKey) {
|
|
59
|
+
const url = `https://api.unsplash.com/search/photos?query=${encodeURIComponent(query)}&per_page=1&orientation=landscape`;
|
|
60
|
+
const res = await fetch(url, {
|
|
61
|
+
headers: { Authorization: `Client-ID ${accessKey}` },
|
|
62
|
+
});
|
|
63
|
+
if (!res.ok)
|
|
64
|
+
return;
|
|
65
|
+
const data = (await res.json());
|
|
66
|
+
if (data.results?.length > 0) {
|
|
67
|
+
img.src = data.results[0].urls.regular;
|
|
68
|
+
img.placeholder = false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=images.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"images.js","sourceRoot":"","sources":["../../src/ai/images.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { FORMAT_SCHEMA } from "./schema.js";
|
|
2
|
+
export { EXAMPLE_CUPFILE } from "./example.js";
|
|
3
|
+
export { buildGeneratePrompt, buildRefinePrompt } from "./prompt.js";
|
|
4
|
+
export { createClient, callClaude } from "./client.js";
|
|
5
|
+
export { generate } from "./generate.js";
|
|
6
|
+
export { refine } from "./refine.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/ai/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { FORMAT_SCHEMA } from "./schema.js";
|
|
2
|
+
export { EXAMPLE_CUPFILE } from "./example.js";
|
|
3
|
+
export { buildGeneratePrompt, buildRefinePrompt } from "./prompt.js";
|
|
4
|
+
export { createClient, callClaude } from "./client.js";
|
|
5
|
+
export { generate } from "./generate.js";
|
|
6
|
+
export { refine } from "./refine.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CupFile } from "@cuppacue/format";
|
|
2
|
+
export declare function buildGeneratePrompt(userPrompt: string, sourceContent?: string): {
|
|
3
|
+
system: string;
|
|
4
|
+
user: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function buildRefinePrompt(currentCupFile: CupFile, instruction: string): {
|
|
7
|
+
system: string;
|
|
8
|
+
user: string;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/ai/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAuFhD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAoBlC;AAED,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,OAAO,EACvB,WAAW,EAAE,MAAM,GAClB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAkBlC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { FORMAT_SCHEMA } from "./schema.js";
|
|
2
|
+
import { EXAMPLE_CUPFILE } from "./example.js";
|
|
3
|
+
const SYSTEM_ROLE = `You are an expert presentation designer. You create visually appealing, well-structured presentations in the CuppaCue JSON format.`;
|
|
4
|
+
const GENERATE_GUIDELINES = `
|
|
5
|
+
## Guidelines
|
|
6
|
+
- Create 5-8 scenes (slides) unless the user specifies otherwise
|
|
7
|
+
- Keep text concise — bullet points over paragraphs
|
|
8
|
+
- ALWAYS set layout on every scene — never rely on position fields
|
|
9
|
+
- Do NOT set position fields — the layout engine handles positioning automatically
|
|
10
|
+
- Use meaningful animations: fadeInUp for titles, revealDown for code, fadeInLeft for lists, bounceIn for cards, zoomIn for images/charts/stats, blurIn for mermaid/captions, slideUp for terminals
|
|
11
|
+
- Add "pause" markers on complex scenes (code blocks, dense content) so presenters can explain
|
|
12
|
+
- Use real language tags on code blocks (e.g. "typescript", "python", not "code")
|
|
13
|
+
- Give every element a unique, descriptive ID (e.g. "intro-title", "arch-code")
|
|
14
|
+
- Include a timesheet with staggered animations (400ms apart typically)
|
|
15
|
+
- Scene duration should accommodate all animations plus 1500ms padding
|
|
16
|
+
- Set manifest.sceneCount to match the actual number of scenes
|
|
17
|
+
|
|
18
|
+
## Slide Structure
|
|
19
|
+
- Start with a title slide using "intro" layout (H1 title + subtitle + caption)
|
|
20
|
+
- Use "section" layout for topic dividers between major sections
|
|
21
|
+
- Use "header-body" for most content slides (lists, tables, cards, stats)
|
|
22
|
+
- Use "split" for text + code/image side-by-side, "code-focus" for code walkthroughs
|
|
23
|
+
- Use "quote" for key quotes or value propositions
|
|
24
|
+
- End with a closing slide using "end" or "intro" layout
|
|
25
|
+
- Use varied transitions: "fade" for section dividers, "slide" for content, "zoom" for emphasis, "morph" for smooth flow, "reveal" for dramatic reveals, "flip" for fun/playful
|
|
26
|
+
|
|
27
|
+
## Rich Elements
|
|
28
|
+
- Use CardsElement for feature overviews (3-4 cards with icon/title/description)
|
|
29
|
+
- Use StatsElement for key metrics (3-4 stats with value + label)
|
|
30
|
+
- Use TerminalElement for CLI demos (command + output pairs)
|
|
31
|
+
- Use TableElement for comparisons or structured data
|
|
32
|
+
- Use MermaidElement for architecture or flow diagrams (flowchart/sequence/graph syntax)
|
|
33
|
+
- Use ChartElement for data visualization (bar/line/pie with labels + datasets)
|
|
34
|
+
- Place rich elements in "header-body" layout with a heading above
|
|
35
|
+
|
|
36
|
+
## Images
|
|
37
|
+
- Include 1-3 ImageElements per presentation where visuals add value
|
|
38
|
+
- Use placeholder: true and src: "placeholder" — real images are resolved automatically
|
|
39
|
+
- Write descriptive alt text for image search (e.g. "modern server room with blue lighting")
|
|
40
|
+
- Place images in "header-body", "split", "image-left", or "image-right" layouts
|
|
41
|
+
|
|
42
|
+
## References
|
|
43
|
+
- Add a "References" or "Learn More" scene before the closing slide
|
|
44
|
+
- Use a ListElement with 3-6 relevant URLs (docs, articles, repos)
|
|
45
|
+
- Use layout "header-body" with a heading
|
|
46
|
+
|
|
47
|
+
## Theme Selection
|
|
48
|
+
- Pick a built-in theme that matches the presentation topic and tone:
|
|
49
|
+
- "dark" — technical talks, developer content (default)
|
|
50
|
+
- "light" — general purpose, business
|
|
51
|
+
- "minimal" — startup pitches, product demos, clean aesthetic
|
|
52
|
+
- "corporate" — business presentations, proposals
|
|
53
|
+
- "elegant" — creative talks, storytelling, design
|
|
54
|
+
- "bold" — keynotes, marketing, announcements
|
|
55
|
+
- "forest" — nature, sustainability, green topics
|
|
56
|
+
- "ocean" — technology, data, research
|
|
57
|
+
- Use the theme colors/fonts from the built-in preset — do not invent custom colors
|
|
58
|
+
- Auto-select typography preset matching the theme:
|
|
59
|
+
- "elegant" theme → elegant typography (serif headings)
|
|
60
|
+
- "bold" theme → bold typography (heavy weights)
|
|
61
|
+
- "minimal" theme → minimal typography (light weights)
|
|
62
|
+
- Others → default typography (no typography field needed)
|
|
63
|
+
|
|
64
|
+
## Speaker Notes
|
|
65
|
+
- Add brief speaker notes to scenes with complex content (code blocks, dense lists, key arguments)
|
|
66
|
+
- Notes help presenters remember key talking points — keep them concise (1-3 sentences)
|
|
67
|
+
- Set the "notes" field on scenes, not on elements
|
|
68
|
+
- Title and closing slides typically don't need notes
|
|
69
|
+
`.trim();
|
|
70
|
+
const REFINE_GUIDELINES = `
|
|
71
|
+
## Guidelines
|
|
72
|
+
- Modify the existing presentation — do not regenerate from scratch
|
|
73
|
+
- Preserve the parts the user did not ask to change
|
|
74
|
+
- Keep the same theme unless asked to change it
|
|
75
|
+
- Maintain valid element IDs and scene IDs
|
|
76
|
+
- ALWAYS set layout on every scene — never rely on position fields
|
|
77
|
+
- Do NOT set position fields — the layout engine handles positioning automatically
|
|
78
|
+
- Update the timesheet to match any content changes
|
|
79
|
+
- Update manifest.sceneCount if scenes were added or removed
|
|
80
|
+
- When adding images, use placeholder: true and src: "placeholder" with descriptive alt text
|
|
81
|
+
- A references scene with URLs is recommended if not already present
|
|
82
|
+
`.trim();
|
|
83
|
+
export function buildGeneratePrompt(userPrompt, sourceContent) {
|
|
84
|
+
const system = `${SYSTEM_ROLE}
|
|
85
|
+
|
|
86
|
+
${FORMAT_SCHEMA}
|
|
87
|
+
|
|
88
|
+
## Example CupFile
|
|
89
|
+
\`\`\`json
|
|
90
|
+
${EXAMPLE_CUPFILE}
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
${GENERATE_GUIDELINES}
|
|
94
|
+
|
|
95
|
+
Respond with ONLY valid JSON — a single CupFile object. No explanation, no markdown fences.`;
|
|
96
|
+
let user = userPrompt;
|
|
97
|
+
if (sourceContent) {
|
|
98
|
+
user = `${userPrompt}\n\n## Source Content\n${sourceContent}`;
|
|
99
|
+
}
|
|
100
|
+
return { system, user };
|
|
101
|
+
}
|
|
102
|
+
export function buildRefinePrompt(currentCupFile, instruction) {
|
|
103
|
+
const system = `${SYSTEM_ROLE}
|
|
104
|
+
|
|
105
|
+
${FORMAT_SCHEMA}
|
|
106
|
+
|
|
107
|
+
${REFINE_GUIDELINES}
|
|
108
|
+
|
|
109
|
+
Respond with ONLY valid JSON — the complete modified CupFile object. No explanation, no markdown fences.`;
|
|
110
|
+
const user = `## Current Presentation
|
|
111
|
+
\`\`\`json
|
|
112
|
+
${JSON.stringify(currentCupFile, null, 2)}
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
## Instruction
|
|
116
|
+
${instruction}`;
|
|
117
|
+
return { system, user };
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/ai/prompt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,WAAW,GAAG,oIAAoI,CAAC;AAEzJ,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiE3B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,iBAAiB,GAAG;;;;;;;;;;;;CAYzB,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,UAAU,mBAAmB,CACjC,UAAkB,EAClB,aAAsB;IAEtB,MAAM,MAAM,GAAG,GAAG,WAAW;;EAE7B,aAAa;;;;EAIb,eAAe;;;EAGf,mBAAmB;;4FAEuE,CAAC;IAE3F,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,GAAG,GAAG,UAAU,0BAA0B,aAAa,EAAE,CAAC;IAChE,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,cAAuB,EACvB,WAAmB;IAEnB,MAAM,MAAM,GAAG,GAAG,WAAW;;EAE7B,aAAa;;EAEb,iBAAiB;;yGAEsF,CAAC;IAExG,MAAM,IAAI,GAAG;;EAEb,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;;;;EAIvC,WAAW,EAAE,CAAC;IAEd,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refine.d.ts","sourceRoot":"","sources":["../../src/ai/refine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAOhD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,MAAM,CAC1B,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,OAAO,CAAC,CAiBlB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { validateCupFile } from "@cuppacue/format";
|
|
2
|
+
import { buildRefinePrompt } from "./prompt.js";
|
|
3
|
+
import { callClaude } from "./client.js";
|
|
4
|
+
import { extractJson, autoFix } from "./generate.js";
|
|
5
|
+
import { resolveImages } from "./images.js";
|
|
6
|
+
export async function refine(cupFile, instruction, opts = {}) {
|
|
7
|
+
const { system, user } = buildRefinePrompt(cupFile, instruction);
|
|
8
|
+
const response = await callClaude(system, user, opts.model);
|
|
9
|
+
const jsonStr = extractJson(response);
|
|
10
|
+
const data = JSON.parse(jsonStr);
|
|
11
|
+
const cup = autoFix(data);
|
|
12
|
+
await resolveImages(cup);
|
|
13
|
+
const result = validateCupFile(cup);
|
|
14
|
+
if (!result.valid) {
|
|
15
|
+
console.error("Warning: Refined presentation has validation issues:");
|
|
16
|
+
for (const err of result.errors) {
|
|
17
|
+
console.error(` ${err.path}: ${err.message}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return cup;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=refine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refine.js","sourceRoot":"","sources":["../../src/ai/refine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAM5C,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,OAAgB,EAChB,WAAmB,EACnB,OAAsB,EAAE;IAExB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA4B,CAAC;IAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;IAEzB,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QACtE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAChC,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/ai/schema.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,aAAa,QA+RlB,CAAC"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format schema description for the AI system prompt.
|
|
3
|
+
*/
|
|
4
|
+
export const FORMAT_SCHEMA = `
|
|
5
|
+
# CuppaCue Presentation Format (.cup)
|
|
6
|
+
|
|
7
|
+
A presentation is a CupFile JSON object with four top-level keys:
|
|
8
|
+
\`manifest\`, \`content\`, \`timesheet\`, \`theme\`.
|
|
9
|
+
|
|
10
|
+
## Manifest
|
|
11
|
+
\`\`\`
|
|
12
|
+
{
|
|
13
|
+
"version": 1, // always 1
|
|
14
|
+
"title": string, // presentation title
|
|
15
|
+
"author"?: string,
|
|
16
|
+
"description"?: string,
|
|
17
|
+
"sceneCount": number // must match content.scenes.length
|
|
18
|
+
}
|
|
19
|
+
\`\`\`
|
|
20
|
+
|
|
21
|
+
## Content
|
|
22
|
+
\`\`\`
|
|
23
|
+
{
|
|
24
|
+
"scenes": Scene[]
|
|
25
|
+
}
|
|
26
|
+
\`\`\`
|
|
27
|
+
|
|
28
|
+
### Scene
|
|
29
|
+
\`\`\`
|
|
30
|
+
{
|
|
31
|
+
"id": string, // unique scene ID (e.g. "intro", "problem")
|
|
32
|
+
"elements": Element[],
|
|
33
|
+
"layout"?: LayoutType,
|
|
34
|
+
"background"?: string, // CSS color, gradient, or image path (e.g. "resources/images/hero.jpg")
|
|
35
|
+
"transition"?: { "type": "fade" | "slide" | "none" | "zoom" | "reveal" | "flip" | "morph", "duration"?: number },
|
|
36
|
+
"class"?: string, // extra CSS class for custom styling
|
|
37
|
+
"notes"?: string, // speaker notes (not rendered on slide, shown in presenter view)
|
|
38
|
+
"accentBar"?: { "color": string, "position"?: "top" | "bottom", "height"?: string },
|
|
39
|
+
// thin colored bar at top/bottom of slide for visual rhythm
|
|
40
|
+
"sceneTemplate"?: string, // scene template ID (e.g. "tech-talk/intro") — overrides layout with designed composition
|
|
41
|
+
"templateData"?: Record<string, string>
|
|
42
|
+
// template chrome data, e.g. { "badge": "TECH TALK", "footerKeywords": "Fast · Reliable" }
|
|
43
|
+
}
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
### Layout (ALWAYS SET ON EVERY SCENE)
|
|
47
|
+
|
|
48
|
+
13 layouts available. Scene-level layout controls element positioning via CSS Grid:
|
|
49
|
+
|
|
50
|
+
**Title layouts:**
|
|
51
|
+
- **"intro"** — Vertically centered, left-aligned. Optional image in right column. Best for: opening title slides.
|
|
52
|
+
- **"section"** — Large centered heading with accent underline. Best for: section dividers between topics.
|
|
53
|
+
- **"end"** — Same as intro (left-aligned, optional right image). Best for: closing/thank-you slides.
|
|
54
|
+
- **"quote"** — Centered, large italic text with decorative quote mark. Best for: key quotes, value propositions.
|
|
55
|
+
|
|
56
|
+
**Content layouts:**
|
|
57
|
+
- **"center"** — All elements centered. Best for: single quotes, key takeaways.
|
|
58
|
+
- **"header-body"** — Heading on top, content below. Best for: most content slides (lists, tables, cards, stats).
|
|
59
|
+
- **"split"** — Two columns with optional header. Text/lists go left, code/images go right. Best for: comparisons, text + code.
|
|
60
|
+
- **"code-focus"** — Small heading + large code area. Best for: code walkthroughs, demos.
|
|
61
|
+
|
|
62
|
+
**Media layouts:**
|
|
63
|
+
- **"image-left"** — Image on left (2fr), text on right (3fr). Best for: visual + explanation.
|
|
64
|
+
- **"image-right"** — Text on left (3fr), image on right (2fr). Best for: explanation + visual.
|
|
65
|
+
- **"image-full"** — Full-bleed background image with text overlay at bottom. Best for: dramatic hero images.
|
|
66
|
+
|
|
67
|
+
**Special layouts:**
|
|
68
|
+
- **"full"** — Single area filling viewport. Best for: large diagrams, charts.
|
|
69
|
+
- **"blank"** — No padding, complete custom control.
|
|
70
|
+
|
|
71
|
+
When using a layout, elements are automatically placed into grid slots based on their type/role.
|
|
72
|
+
You do NOT need to set position fields when using a layout.
|
|
73
|
+
|
|
74
|
+
**Suggested patterns:**
|
|
75
|
+
- Title slide → "intro"
|
|
76
|
+
- Section divider → "section"
|
|
77
|
+
- Content with heading + bullets/table → "header-body"
|
|
78
|
+
- Content with heading + cards/stats → "header-body"
|
|
79
|
+
- Code with explanation → "split" or "code-focus"
|
|
80
|
+
- Key quote or value proposition → "quote"
|
|
81
|
+
- Closing/thanks slide → "end" or "intro"
|
|
82
|
+
|
|
83
|
+
### Background
|
|
84
|
+
|
|
85
|
+
The \`background\` field accepts:
|
|
86
|
+
- CSS colors: \`"#1a1a2e"\`, \`"rgb(26,26,46)"\`
|
|
87
|
+
- CSS gradients: \`"linear-gradient(135deg, #0f172a, #1e293b)"\`
|
|
88
|
+
- Image paths: \`"resources/images/hero.jpg"\` (resolved from .cup bundle)
|
|
89
|
+
|
|
90
|
+
Background images display as \`background-size: cover; background-position: center\`.
|
|
91
|
+
Combine with text elements for dramatic effect on "intro", "quote", or "section" layouts.
|
|
92
|
+
|
|
93
|
+
### Transition
|
|
94
|
+
|
|
95
|
+
Per-scene transitions control animation between slides:
|
|
96
|
+
- **"fade"** (default) — Cross-fade between scenes (400ms default)
|
|
97
|
+
- **"slide"** — Direction-aware slide (forward: left-to-right, backward: right-to-left)
|
|
98
|
+
- **"zoom"** — Zoom in new scene, zoom out old scene
|
|
99
|
+
- **"reveal"** — Clip-path wipe left-to-right
|
|
100
|
+
- **"flip"** — 3D card flip (rotateY)
|
|
101
|
+
- **"morph"** — Scale + blur transition (smooth, modern)
|
|
102
|
+
- **"none"** — Instant swap
|
|
103
|
+
|
|
104
|
+
Example: \`"transition": { "type": "zoom", "duration": 500 }\`
|
|
105
|
+
|
|
106
|
+
### Element Types
|
|
107
|
+
|
|
108
|
+
All elements have: \`id\` (unique string), \`type\`, optional \`position\`, optional \`style\`.
|
|
109
|
+
|
|
110
|
+
**TextElement**: \`{ type: "text", content: string, role?: "title" | "subtitle" | "heading" | "body" | "caption" | "footer" }\`
|
|
111
|
+
- Use "footer" role for persistent slide footer text (company name, date, etc.)
|
|
112
|
+
- Content supports inline HTML: \`<strong>\`, \`<em>\`, \`<code>\`, \`<del>\`, \`<a href="...">\`
|
|
113
|
+
|
|
114
|
+
**CodeElement**: \`{ type: "code", content: string, language: string, highlightLines?: number[] }\`
|
|
115
|
+
|
|
116
|
+
**ListElement**: \`{ type: "list", items: string[], ordered?: boolean }\`
|
|
117
|
+
- Items support inline HTML
|
|
118
|
+
|
|
119
|
+
**ImageElement**: \`{ type: "image", src: string, alt?: string, width?: string, height?: string, placeholder?: boolean }\`
|
|
120
|
+
|
|
121
|
+
**TableElement**: \`{ type: "table", headers: string[], rows: string[][], alignments?: ("left" | "center" | "right" | null)[] }\`
|
|
122
|
+
- Cells support inline HTML
|
|
123
|
+
|
|
124
|
+
**DividerElement**: \`{ type: "divider" }\`
|
|
125
|
+
|
|
126
|
+
**CardsElement**: \`{ type: "cards", cards: [{ title: string, icon?: string, description?: string, accent?: string }] }\`
|
|
127
|
+
- accent: optional hex color for a colored top border stripe (e.g. "#38bdf8"). Use different accents per card to differentiate features.
|
|
128
|
+
- Icons: "code", "brain", "shield", "globe", "zap", "layout", "lock", "terminal", "sparkles", "search", "settings", "link", "target", "layers", "cpu", "wifi", "git-branch", "message-circle", "package", "trending-up", "activity", "download", "upload", "alert-triangle", "info", "check-circle", "file-text", "help-circle", "eye", "refresh", "rocket", "star", "heart", "database", "server", "cloud", "key", "bar-chart", "users", "mail", "clock", "arrow-right"
|
|
129
|
+
- Best in "header-body" layout with a heading
|
|
130
|
+
|
|
131
|
+
**StatsElement**: \`{ type: "stats", stats: [{ value: string, label: string }] }\`
|
|
132
|
+
- Animated counters for key metrics
|
|
133
|
+
- Best in "header-body" layout with a heading
|
|
134
|
+
|
|
135
|
+
**TerminalElement**: \`{ type: "terminal", lines: [{ type: "command" | "output", text: string }] }\`
|
|
136
|
+
- Simulated terminal with prompt styling
|
|
137
|
+
- Best in "split" or "code-focus" layout
|
|
138
|
+
|
|
139
|
+
**MermaidElement**: \`{ type: "mermaid", definition: string }\`
|
|
140
|
+
- Mermaid diagram syntax
|
|
141
|
+
- Best in "full" or "header-body" layout
|
|
142
|
+
|
|
143
|
+
**ChartElement**: \`{ type: "chart", chartType: "bar" | "line" | "pie", labels: string[], datasets: [{ label: string, data: number[] }] }\`
|
|
144
|
+
- Chart.js-powered charts
|
|
145
|
+
- Best in "full" or "header-body" layout
|
|
146
|
+
|
|
147
|
+
### Position (legacy — only for custom layouts without a layout field)
|
|
148
|
+
\`\`\`
|
|
149
|
+
{
|
|
150
|
+
"x"?: string, // e.g. "50%", "8%"
|
|
151
|
+
"y"?: string, // e.g. "35%", "50%"
|
|
152
|
+
"anchor"?: "center" | "top-left" | "top-center" | "top-right" |
|
|
153
|
+
"center-left" | "center-right" |
|
|
154
|
+
"bottom-left" | "bottom-center" | "bottom-right",
|
|
155
|
+
"width"?: string,
|
|
156
|
+
"maxWidth"?: string // e.g. "70%", "45%"
|
|
157
|
+
}
|
|
158
|
+
\`\`\`
|
|
159
|
+
Do NOT use position fields when using a layout — the layout engine handles positioning automatically.
|
|
160
|
+
|
|
161
|
+
### ElementStyle (optional)
|
|
162
|
+
\`\`\`
|
|
163
|
+
{
|
|
164
|
+
"fontSize"?: string,
|
|
165
|
+
"color"?: string,
|
|
166
|
+
"fontWeight"?: string,
|
|
167
|
+
"fontFamily"?: string,
|
|
168
|
+
"textAlign"?: string,
|
|
169
|
+
"opacity"?: number,
|
|
170
|
+
"padding"?: string
|
|
171
|
+
}
|
|
172
|
+
\`\`\`
|
|
173
|
+
|
|
174
|
+
## Timesheet
|
|
175
|
+
\`\`\`
|
|
176
|
+
{
|
|
177
|
+
"timeline": TimelineEntry[], // one per scene, same order as scenes
|
|
178
|
+
"defaults"?: { "sceneDuration"?: number, "animationDuration"?: number }
|
|
179
|
+
}
|
|
180
|
+
\`\`\`
|
|
181
|
+
|
|
182
|
+
### TimelineEntry
|
|
183
|
+
\`\`\`
|
|
184
|
+
{
|
|
185
|
+
"sceneId": string, // must match a scene ID
|
|
186
|
+
"duration": number, // scene duration in ms
|
|
187
|
+
"events": TimelineEvent[],
|
|
188
|
+
"markers"?: Marker[]
|
|
189
|
+
}
|
|
190
|
+
\`\`\`
|
|
191
|
+
|
|
192
|
+
### TimelineEvent
|
|
193
|
+
\`\`\`
|
|
194
|
+
{
|
|
195
|
+
"elementId": string, // must match an element ID in that scene
|
|
196
|
+
"animation": AnimationName,
|
|
197
|
+
"at": number, // start time in ms (relative to scene start)
|
|
198
|
+
"duration"?: number // animation duration in ms
|
|
199
|
+
}
|
|
200
|
+
\`\`\`
|
|
201
|
+
|
|
202
|
+
### AnimationName (one of):
|
|
203
|
+
- \`fadeIn\` — general entrance, good for headings
|
|
204
|
+
- \`fadeOut\` — exit animation
|
|
205
|
+
- \`fadeInUp\` — entrance with upward motion, good for titles and body text
|
|
206
|
+
- \`fadeInDown\` — entrance with downward motion
|
|
207
|
+
- \`fadeInLeft\` — entrance from left, good for lists
|
|
208
|
+
- \`fadeInRight\` — entrance from right, good for side-by-side layouts
|
|
209
|
+
- \`typewriter\` — left-to-right character reveal, good for single-line text
|
|
210
|
+
- \`revealDown\` — top-to-bottom reveal, ideal for code blocks (line-by-line appearance)
|
|
211
|
+
- \`scale\` — scale up entrance, good for emphasis/closing titles
|
|
212
|
+
- \`highlight\` — attention pulse, good for key points
|
|
213
|
+
- \`bounceIn\` — bouncy scale entrance, great for cards and feature highlights
|
|
214
|
+
- \`blurIn\` — blur-to-focus entrance, great for mermaid diagrams and captions
|
|
215
|
+
- \`rotateIn\` — rotation entrance, great for emphasis elements
|
|
216
|
+
- \`slideUp\` — slide from bottom, great for terminal elements
|
|
217
|
+
- \`slideDown\` — slide from top, great for headers
|
|
218
|
+
- \`zoomIn\` — zoom from zero, great for images, charts, and stats
|
|
219
|
+
|
|
220
|
+
### Marker
|
|
221
|
+
\`\`\`
|
|
222
|
+
{
|
|
223
|
+
"at": number, // time in ms
|
|
224
|
+
"label"?: string, // optional description
|
|
225
|
+
"action": "pause" | "auto"
|
|
226
|
+
}
|
|
227
|
+
\`\`\`
|
|
228
|
+
Use "pause" markers on complex scenes (code, many elements) so the presenter can explain.
|
|
229
|
+
|
|
230
|
+
## Theme
|
|
231
|
+
|
|
232
|
+
8 built-in themes are available. Pick one by name — use the theme object as shown.
|
|
233
|
+
|
|
234
|
+
| Theme | Style | Best For |
|
|
235
|
+
|-------|-------|----------|
|
|
236
|
+
| dark | Cyan on dark slate | Technical talks, developer content |
|
|
237
|
+
| light | Blue on white | General purpose, corporate |
|
|
238
|
+
| minimal | Gray on off-white, light weights | Startup pitches, product demos |
|
|
239
|
+
| corporate | Deep blue on white, structured | Business presentations, proposals |
|
|
240
|
+
| elegant | Gold on charcoal, serif headings | Creative talks, storytelling, design |
|
|
241
|
+
| bold | Yellow on black, heavy weights | Keynotes, marketing, announcements |
|
|
242
|
+
| forest | Green on dark green | Nature/sustainability topics |
|
|
243
|
+
| ocean | Teal on deep navy | Technology, data, research |
|
|
244
|
+
|
|
245
|
+
### Theme Object Structure
|
|
246
|
+
\`\`\`
|
|
247
|
+
{
|
|
248
|
+
"name": string, // theme name
|
|
249
|
+
"colors": {
|
|
250
|
+
"background": string, // hex color
|
|
251
|
+
"foreground": string,
|
|
252
|
+
"primary": string,
|
|
253
|
+
"secondary": string,
|
|
254
|
+
"accent": string,
|
|
255
|
+
"codeBackground": string,
|
|
256
|
+
"codeForeground": string
|
|
257
|
+
},
|
|
258
|
+
"fonts": {
|
|
259
|
+
"heading": string, // CSS font-family
|
|
260
|
+
"body": string,
|
|
261
|
+
"code": string
|
|
262
|
+
},
|
|
263
|
+
"codeTheme"?: string, // "vitesse-dark" for dark themes, "vitesse-light" for light themes
|
|
264
|
+
"typography"?: Typography, // per-role overrides (see below)
|
|
265
|
+
"fontImports"?: string[] // Google Fonts URLs for custom fonts
|
|
266
|
+
}
|
|
267
|
+
\`\`\`
|
|
268
|
+
|
|
269
|
+
### Typography (optional)
|
|
270
|
+
Per-role overrides for font sizing and style. Available presets: "default", "elegant", "minimal", "bold".
|
|
271
|
+
|
|
272
|
+
\`\`\`
|
|
273
|
+
{
|
|
274
|
+
"title"?: TypographyRole,
|
|
275
|
+
"subtitle"?: TypographyRole,
|
|
276
|
+
"heading"?: TypographyRole,
|
|
277
|
+
"body"?: TypographyRole,
|
|
278
|
+
"caption"?: TypographyRole
|
|
279
|
+
}
|
|
280
|
+
\`\`\`
|
|
281
|
+
|
|
282
|
+
**TypographyRole**: \`{ fontSize?: string, fontWeight?: string, lineHeight?: string, letterSpacing?: string, textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" }\`
|
|
283
|
+
|
|
284
|
+
**Preset: "elegant"** — Playfair Display serif headings + Lato body. Use fonts: \`{ heading: "'Playfair Display', Georgia, serif", body: "'Lato', system-ui, sans-serif", code: "'JetBrains Mono', 'Fira Code', monospace" }\` with fontImports: \`["https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600;700&family=Lato:wght@300;400;700&display=swap"]\`
|
|
285
|
+
|
|
286
|
+
**Preset: "minimal"** — Inter with lighter weights (300/400), wider letter-spacing.
|
|
287
|
+
|
|
288
|
+
**Preset: "bold"** — Inter with heavier weights (700/800), tighter letter-spacing, uppercase captions.
|
|
289
|
+
|
|
290
|
+
**Preset: "default"** — Inter, standard sizes/weights. No typography field needed.
|
|
291
|
+
`.trim();
|
|
292
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/ai/schema.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+R5B,CAAC,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backstage.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/backstage.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { applyNavAction } from "../serve.js";
|
|
3
|
+
function state(currentIndex, totalScenes) {
|
|
4
|
+
return { currentIndex, totalScenes };
|
|
5
|
+
}
|
|
6
|
+
describe("applyNavAction", () => {
|
|
7
|
+
it("advances to next scene", () => {
|
|
8
|
+
expect(applyNavAction(state(0, 5), "next")).toBe(1);
|
|
9
|
+
expect(applyNavAction(state(2, 5), "next")).toBe(3);
|
|
10
|
+
});
|
|
11
|
+
it("clamps next at last scene", () => {
|
|
12
|
+
expect(applyNavAction(state(4, 5), "next")).toBe(4);
|
|
13
|
+
expect(applyNavAction(state(9, 10), "next")).toBe(9);
|
|
14
|
+
});
|
|
15
|
+
it("goes to previous scene", () => {
|
|
16
|
+
expect(applyNavAction(state(3, 5), "prev")).toBe(2);
|
|
17
|
+
expect(applyNavAction(state(1, 5), "prev")).toBe(0);
|
|
18
|
+
});
|
|
19
|
+
it("clamps prev at first scene", () => {
|
|
20
|
+
expect(applyNavAction(state(0, 5), "prev")).toBe(0);
|
|
21
|
+
});
|
|
22
|
+
it("jumps to a specific scene with goto", () => {
|
|
23
|
+
expect(applyNavAction(state(0, 10), "goto", 5)).toBe(5);
|
|
24
|
+
expect(applyNavAction(state(7, 10), "goto", 2)).toBe(2);
|
|
25
|
+
});
|
|
26
|
+
it("clamps goto within bounds", () => {
|
|
27
|
+
expect(applyNavAction(state(0, 5), "goto", 99)).toBe(4);
|
|
28
|
+
expect(applyNavAction(state(3, 5), "goto", -1)).toBe(0);
|
|
29
|
+
});
|
|
30
|
+
it("ignores goto without an index", () => {
|
|
31
|
+
expect(applyNavAction(state(3, 5), "goto")).toBe(3);
|
|
32
|
+
});
|
|
33
|
+
it("returns current index for unknown actions", () => {
|
|
34
|
+
expect(applyNavAction(state(2, 5), "unknown")).toBe(2);
|
|
35
|
+
});
|
|
36
|
+
it("handles single-scene presentation", () => {
|
|
37
|
+
expect(applyNavAction(state(0, 1), "next")).toBe(0);
|
|
38
|
+
expect(applyNavAction(state(0, 1), "prev")).toBe(0);
|
|
39
|
+
});
|
|
40
|
+
it("handles empty presentation gracefully", () => {
|
|
41
|
+
expect(applyNavAction(state(0, 0), "next")).toBe(0);
|
|
42
|
+
expect(applyNavAction(state(0, 0), "prev")).toBe(0);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=backstage.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backstage.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/backstage.test.ts"],"names":[],"mappings":"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"}
|