@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.
Files changed (247) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -0
  3. package/dist/ai/__tests__/generate.test.d.ts +2 -0
  4. package/dist/ai/__tests__/generate.test.d.ts.map +1 -0
  5. package/dist/ai/__tests__/generate.test.js +129 -0
  6. package/dist/ai/__tests__/generate.test.js.map +1 -0
  7. package/dist/ai/__tests__/images.test.d.ts +2 -0
  8. package/dist/ai/__tests__/images.test.d.ts.map +1 -0
  9. package/dist/ai/__tests__/images.test.js +186 -0
  10. package/dist/ai/__tests__/images.test.js.map +1 -0
  11. package/dist/ai/__tests__/prompt.test.d.ts +2 -0
  12. package/dist/ai/__tests__/prompt.test.d.ts.map +1 -0
  13. package/dist/ai/__tests__/prompt.test.js +98 -0
  14. package/dist/ai/__tests__/prompt.test.js.map +1 -0
  15. package/dist/ai/__tests__/refine.test.d.ts +2 -0
  16. package/dist/ai/__tests__/refine.test.d.ts.map +1 -0
  17. package/dist/ai/__tests__/refine.test.js +87 -0
  18. package/dist/ai/__tests__/refine.test.js.map +1 -0
  19. package/dist/ai/client.d.ts +4 -0
  20. package/dist/ai/client.d.ts.map +1 -0
  21. package/dist/ai/client.js +36 -0
  22. package/dist/ai/client.js.map +1 -0
  23. package/dist/ai/example.d.ts +6 -0
  24. package/dist/ai/example.d.ts.map +1 -0
  25. package/dist/ai/example.js +284 -0
  26. package/dist/ai/example.js.map +1 -0
  27. package/dist/ai/generate.d.ts +15 -0
  28. package/dist/ai/generate.d.ts.map +1 -0
  29. package/dist/ai/generate.js +120 -0
  30. package/dist/ai/generate.js.map +1 -0
  31. package/dist/ai/images.d.ts +8 -0
  32. package/dist/ai/images.d.ts.map +1 -0
  33. package/dist/ai/images.js +71 -0
  34. package/dist/ai/images.js.map +1 -0
  35. package/dist/ai/index.d.ts +7 -0
  36. package/dist/ai/index.d.ts.map +1 -0
  37. package/dist/ai/index.js +7 -0
  38. package/dist/ai/index.js.map +1 -0
  39. package/dist/ai/prompt.d.ts +10 -0
  40. package/dist/ai/prompt.d.ts.map +1 -0
  41. package/dist/ai/prompt.js +119 -0
  42. package/dist/ai/prompt.js.map +1 -0
  43. package/dist/ai/refine.d.ts +6 -0
  44. package/dist/ai/refine.d.ts.map +1 -0
  45. package/dist/ai/refine.js +22 -0
  46. package/dist/ai/refine.js.map +1 -0
  47. package/dist/ai/schema.d.ts +5 -0
  48. package/dist/ai/schema.d.ts.map +1 -0
  49. package/dist/ai/schema.js +292 -0
  50. package/dist/ai/schema.js.map +1 -0
  51. package/dist/commands/__tests__/backstage.test.d.ts +2 -0
  52. package/dist/commands/__tests__/backstage.test.d.ts.map +1 -0
  53. package/dist/commands/__tests__/backstage.test.js +45 -0
  54. package/dist/commands/__tests__/backstage.test.js.map +1 -0
  55. package/dist/commands/__tests__/export.test.d.ts +2 -0
  56. package/dist/commands/__tests__/export.test.d.ts.map +1 -0
  57. package/dist/commands/__tests__/export.test.js +50 -0
  58. package/dist/commands/__tests__/export.test.js.map +1 -0
  59. package/dist/commands/ai.d.ts +2 -0
  60. package/dist/commands/ai.d.ts.map +1 -0
  61. package/dist/commands/ai.js +113 -0
  62. package/dist/commands/ai.js.map +1 -0
  63. package/dist/commands/build.d.ts +2 -0
  64. package/dist/commands/build.d.ts.map +1 -0
  65. package/dist/commands/build.js +121 -0
  66. package/dist/commands/build.js.map +1 -0
  67. package/dist/commands/export-pdf.d.ts +9 -0
  68. package/dist/commands/export-pdf.d.ts.map +1 -0
  69. package/dist/commands/export-pdf.js +109 -0
  70. package/dist/commands/export-pdf.js.map +1 -0
  71. package/dist/commands/export.d.ts +2 -0
  72. package/dist/commands/export.d.ts.map +1 -0
  73. package/dist/commands/export.js +147 -0
  74. package/dist/commands/export.js.map +1 -0
  75. package/dist/commands/init.d.ts +2 -0
  76. package/dist/commands/init.d.ts.map +1 -0
  77. package/dist/commands/init.js +54 -0
  78. package/dist/commands/init.js.map +1 -0
  79. package/dist/commands/preview.d.ts +2 -0
  80. package/dist/commands/preview.d.ts.map +1 -0
  81. package/dist/commands/preview.js +78 -0
  82. package/dist/commands/preview.js.map +1 -0
  83. package/dist/commands/serve.d.ts +7 -0
  84. package/dist/commands/serve.d.ts.map +1 -0
  85. package/dist/commands/serve.js +773 -0
  86. package/dist/commands/serve.js.map +1 -0
  87. package/dist/commands/validate.d.ts +2 -0
  88. package/dist/commands/validate.d.ts.map +1 -0
  89. package/dist/commands/validate.js +39 -0
  90. package/dist/commands/validate.js.map +1 -0
  91. package/dist/github/__tests__/fetcher.test.d.ts +2 -0
  92. package/dist/github/__tests__/fetcher.test.d.ts.map +1 -0
  93. package/dist/github/__tests__/fetcher.test.js +102 -0
  94. package/dist/github/__tests__/fetcher.test.js.map +1 -0
  95. package/dist/github/__tests__/rewrite-images.test.d.ts +2 -0
  96. package/dist/github/__tests__/rewrite-images.test.d.ts.map +1 -0
  97. package/dist/github/__tests__/rewrite-images.test.js +79 -0
  98. package/dist/github/__tests__/rewrite-images.test.js.map +1 -0
  99. package/dist/github/__tests__/url-parser.test.d.ts +2 -0
  100. package/dist/github/__tests__/url-parser.test.d.ts.map +1 -0
  101. package/dist/github/__tests__/url-parser.test.js +96 -0
  102. package/dist/github/__tests__/url-parser.test.js.map +1 -0
  103. package/dist/github/fetcher.d.ts +10 -0
  104. package/dist/github/fetcher.d.ts.map +1 -0
  105. package/dist/github/fetcher.js +53 -0
  106. package/dist/github/fetcher.js.map +1 -0
  107. package/dist/github/index.d.ts +5 -0
  108. package/dist/github/index.d.ts.map +1 -0
  109. package/dist/github/index.js +4 -0
  110. package/dist/github/index.js.map +1 -0
  111. package/dist/github/rewrite-images.d.ts +7 -0
  112. package/dist/github/rewrite-images.d.ts.map +1 -0
  113. package/dist/github/rewrite-images.js +26 -0
  114. package/dist/github/rewrite-images.js.map +1 -0
  115. package/dist/github/url-parser.d.ts +12 -0
  116. package/dist/github/url-parser.d.ts.map +1 -0
  117. package/dist/github/url-parser.js +60 -0
  118. package/dist/github/url-parser.js.map +1 -0
  119. package/dist/index.d.ts +3 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +95 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/markdown/__tests__/auto-slide.test.d.ts +2 -0
  124. package/dist/markdown/__tests__/auto-slide.test.d.ts.map +1 -0
  125. package/dist/markdown/__tests__/auto-slide.test.js +325 -0
  126. package/dist/markdown/__tests__/auto-slide.test.js.map +1 -0
  127. package/dist/markdown/__tests__/compiler.test.d.ts +2 -0
  128. package/dist/markdown/__tests__/compiler.test.d.ts.map +1 -0
  129. package/dist/markdown/__tests__/compiler.test.js +142 -0
  130. package/dist/markdown/__tests__/compiler.test.js.map +1 -0
  131. package/dist/markdown/__tests__/custom-parsers.test.d.ts +2 -0
  132. package/dist/markdown/__tests__/custom-parsers.test.d.ts.map +1 -0
  133. package/dist/markdown/__tests__/custom-parsers.test.js +182 -0
  134. package/dist/markdown/__tests__/custom-parsers.test.js.map +1 -0
  135. package/dist/markdown/__tests__/frontmatter.test.d.ts +2 -0
  136. package/dist/markdown/__tests__/frontmatter.test.d.ts.map +1 -0
  137. package/dist/markdown/__tests__/frontmatter.test.js +162 -0
  138. package/dist/markdown/__tests__/frontmatter.test.js.map +1 -0
  139. package/dist/markdown/__tests__/layout.test.d.ts +2 -0
  140. package/dist/markdown/__tests__/layout.test.d.ts.map +1 -0
  141. package/dist/markdown/__tests__/layout.test.js +85 -0
  142. package/dist/markdown/__tests__/layout.test.js.map +1 -0
  143. package/dist/markdown/__tests__/parser.test.d.ts +2 -0
  144. package/dist/markdown/__tests__/parser.test.d.ts.map +1 -0
  145. package/dist/markdown/__tests__/parser.test.js +646 -0
  146. package/dist/markdown/__tests__/parser.test.js.map +1 -0
  147. package/dist/markdown/__tests__/timesheet-gen.test.d.ts +2 -0
  148. package/dist/markdown/__tests__/timesheet-gen.test.d.ts.map +1 -0
  149. package/dist/markdown/__tests__/timesheet-gen.test.js +90 -0
  150. package/dist/markdown/__tests__/timesheet-gen.test.js.map +1 -0
  151. package/dist/markdown/auto-slide.d.ts +16 -0
  152. package/dist/markdown/auto-slide.d.ts.map +1 -0
  153. package/dist/markdown/auto-slide.js +144 -0
  154. package/dist/markdown/auto-slide.js.map +1 -0
  155. package/dist/markdown/compiler.d.ts +12 -0
  156. package/dist/markdown/compiler.d.ts.map +1 -0
  157. package/dist/markdown/compiler.js +47 -0
  158. package/dist/markdown/compiler.js.map +1 -0
  159. package/dist/markdown/custom-parsers.d.ts +69 -0
  160. package/dist/markdown/custom-parsers.d.ts.map +1 -0
  161. package/dist/markdown/custom-parsers.js +227 -0
  162. package/dist/markdown/custom-parsers.js.map +1 -0
  163. package/dist/markdown/frontmatter.d.ts +33 -0
  164. package/dist/markdown/frontmatter.d.ts.map +1 -0
  165. package/dist/markdown/frontmatter.js +83 -0
  166. package/dist/markdown/frontmatter.js.map +1 -0
  167. package/dist/markdown/id.d.ts +4 -0
  168. package/dist/markdown/id.d.ts.map +1 -0
  169. package/dist/markdown/id.js +15 -0
  170. package/dist/markdown/id.js.map +1 -0
  171. package/dist/markdown/index.d.ts +8 -0
  172. package/dist/markdown/index.d.ts.map +1 -0
  173. package/dist/markdown/index.js +6 -0
  174. package/dist/markdown/index.js.map +1 -0
  175. package/dist/markdown/parser.d.ts +35 -0
  176. package/dist/markdown/parser.d.ts.map +1 -0
  177. package/dist/markdown/parser.js +605 -0
  178. package/dist/markdown/parser.js.map +1 -0
  179. package/dist/markdown/timesheet-gen.d.ts +6 -0
  180. package/dist/markdown/timesheet-gen.d.ts.map +1 -0
  181. package/dist/markdown/timesheet-gen.js +223 -0
  182. package/dist/markdown/timesheet-gen.js.map +1 -0
  183. package/dist/player/CuePlayer.d.ts +58 -0
  184. package/dist/player/CuePlayer.d.ts.map +1 -0
  185. package/dist/player/NavBar.d.ts +31 -0
  186. package/dist/player/NavBar.d.ts.map +1 -0
  187. package/dist/player/PresenterView.d.ts +25 -0
  188. package/dist/player/PresenterView.d.ts.map +1 -0
  189. package/dist/player/SlideOverview.d.ts +16 -0
  190. package/dist/player/SlideOverview.d.ts.map +1 -0
  191. package/dist/player/__mocks__/chart.d.ts +29 -0
  192. package/dist/player/__mocks__/chart.d.ts.map +1 -0
  193. package/dist/player/__mocks__/mermaid.d.ts +8 -0
  194. package/dist/player/__mocks__/mermaid.d.ts.map +1 -0
  195. package/dist/player/__tests__/animator.test.d.ts +2 -0
  196. package/dist/player/__tests__/animator.test.d.ts.map +1 -0
  197. package/dist/player/__tests__/layout.test.d.ts +2 -0
  198. package/dist/player/__tests__/layout.test.d.ts.map +1 -0
  199. package/dist/player/__tests__/overview.test.d.ts +2 -0
  200. package/dist/player/__tests__/overview.test.d.ts.map +1 -0
  201. package/dist/player/__tests__/timeline.test.d.ts +2 -0
  202. package/dist/player/__tests__/timeline.test.d.ts.map +1 -0
  203. package/dist/player/animator.d.ts +4 -0
  204. package/dist/player/animator.d.ts.map +1 -0
  205. package/dist/player/cuppacue-player.css +1 -0
  206. package/dist/player/cuppacue-player.js +3247 -0
  207. package/dist/player/index.d.ts +14 -0
  208. package/dist/player/index.d.ts.map +1 -0
  209. package/dist/player/loader.d.ts +3 -0
  210. package/dist/player/loader.d.ts.map +1 -0
  211. package/dist/player/main.d.ts +2 -0
  212. package/dist/player/main.d.ts.map +1 -0
  213. package/dist/player/navigator.d.ts +27 -0
  214. package/dist/player/navigator.d.ts.map +1 -0
  215. package/dist/player/renderer.d.ts +9 -0
  216. package/dist/player/renderer.d.ts.map +1 -0
  217. package/dist/player/renderers/cards.d.ts +3 -0
  218. package/dist/player/renderers/cards.d.ts.map +1 -0
  219. package/dist/player/renderers/chart.d.ts +3 -0
  220. package/dist/player/renderers/chart.d.ts.map +1 -0
  221. package/dist/player/renderers/icons.d.ts +6 -0
  222. package/dist/player/renderers/icons.d.ts.map +1 -0
  223. package/dist/player/renderers/mermaid.d.ts +3 -0
  224. package/dist/player/renderers/mermaid.d.ts.map +1 -0
  225. package/dist/player/renderers/stats.d.ts +3 -0
  226. package/dist/player/renderers/stats.d.ts.map +1 -0
  227. package/dist/player/renderers/terminal.d.ts +3 -0
  228. package/dist/player/renderers/terminal.d.ts.map +1 -0
  229. package/dist/player/themes/dark.d.ts +2 -0
  230. package/dist/player/themes/dark.d.ts.map +1 -0
  231. package/dist/player/themes/light.d.ts +2 -0
  232. package/dist/player/themes/light.d.ts.map +1 -0
  233. package/dist/player/timeline.d.ts +43 -0
  234. package/dist/player/timeline.d.ts.map +1 -0
  235. package/dist/serve.d.ts +2 -0
  236. package/dist/serve.d.ts.map +1 -0
  237. package/dist/serve.js +171 -0
  238. package/dist/serve.js.map +1 -0
  239. package/dist/templates/discover.d.ts +20 -0
  240. package/dist/templates/discover.d.ts.map +1 -0
  241. package/dist/templates/discover.js +86 -0
  242. package/dist/templates/discover.js.map +1 -0
  243. package/dist/templates/loader.d.ts +17 -0
  244. package/dist/templates/loader.d.ts.map +1 -0
  245. package/dist/templates/loader.js +40 -0
  246. package/dist/templates/loader.js.map +1 -0
  247. 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"}
@@ -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,6 @@
1
+ import type { CupFile } from "@cuppacue/format";
2
+ export interface RefineOptions {
3
+ model?: string;
4
+ }
5
+ export declare function refine(cupFile: CupFile, instruction: string, opts?: RefineOptions): Promise<CupFile>;
6
+ //# sourceMappingURL=refine.d.ts.map
@@ -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,5 @@
1
+ /**
2
+ * Format schema description for the AI system prompt.
3
+ */
4
+ export declare const FORMAT_SCHEMA: string;
5
+ //# sourceMappingURL=schema.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=backstage.test.d.ts.map
@@ -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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"}