@gui-chat-plugin/sequence 0.5.0 → 0.5.2
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 +21 -0
- package/README.md +3 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/slideHtml.d.ts +6 -0
- package/dist/core-BsHwn1O3.cjs +236 -0
- package/dist/{core-g9C3DDiR.js → core-BsZHVy5S.js} +334 -199
- package/dist/index.cjs +1 -1
- package/dist/index.js +2 -2
- package/dist/style.css +1 -1
- package/dist/vue.cjs +1 -1
- package/dist/vue.js +43 -43
- package/package.json +1 -1
- package/dist/core-DNUfOXPo.cjs +0 -232
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.2
|
|
4
|
+
|
|
5
|
+
- **The model explains the whole slide before the next one.** Grok Voice said one or two
|
|
6
|
+
sentences a slide and went on: asked for "at least two sentences", it said two, whatever the
|
|
7
|
+
slide held, and asked to call the next slide "in this same reply", it often said only the
|
|
8
|
+
slide's first sentence, word for word, before the call. The slide was replaced half explained.
|
|
9
|
+
The instructions now ask it to cover every section, point and number, say that the next slide
|
|
10
|
+
replaces this one as soon as it stops talking, and that the call is the last thing in the reply.
|
|
11
|
+
Checked on Grok Voice with a Tesla vs Waymo robotaxi slideshow: one-sentence slides went from
|
|
12
|
+
10 in 21 to none in 20. The prompt also asks for slides that hold only what the model will say.
|
|
13
|
+
|
|
14
|
+
## 0.5.1
|
|
15
|
+
|
|
16
|
+
- **Markdown slides fit in movies too.** A dense Markdown slide shrank to fit in the View, whose page
|
|
17
|
+
runs the fitting, but a movie made from it (MulmoChat's makeMovie: an `html_tailwind` beat) cut
|
|
18
|
+
it off at the top and bottom. `SLIDE_FIT_SCRIPT` is the same fitting, for the host to give the
|
|
19
|
+
movie's page (MulmoCast's html_tailwind beat takes it as its `script`). The Markdown slide's box
|
|
20
|
+
no longer shrinks as a flex item: MulmoCast's page makes the body a flex column, where it did,
|
|
21
|
+
and its text was fitted into the shrunken box, much too small. Checked in a MulmoChat movie: a
|
|
22
|
+
14-item slide with a formula on every line fits, its title and last line in view.
|
|
23
|
+
|
|
3
24
|
## 0.5.0
|
|
4
25
|
|
|
5
26
|
- **Chart slides**: `presentSlide` takes `chart`, a Chart.js configuration (`{ type, data,
|
package/README.md
CHANGED
|
@@ -81,7 +81,9 @@ model wrote it in `markdown`. Math is MathML (KaTeX's `mathml` output), which th
|
|
|
81
81
|
no stylesheet or font to load: it works offline and in a movie as on the screen. Function names
|
|
82
82
|
(`\log`, `\sin`) get TeX's thin spaces, and matrix cells padding, which Chrome's MathML doesn't give
|
|
83
83
|
them. TeX that KaTeX can't read is shown as written, in red. The slide's type is sized by how much it
|
|
84
|
-
says, and the page
|
|
84
|
+
says, and the page shrinks it until it fits. A page that shows the slide's HTML without the View's
|
|
85
|
+
page (a movie's) fits it with `SLIDE_FIT_SCRIPT`: MulmoCast's `html_tailwind` beat takes it as its
|
|
86
|
+
`script`.
|
|
85
87
|
|
|
86
88
|
### Chart slides
|
|
87
89
|
|
package/dist/core/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare const presentSlidePluginCore: SequencePluginCore;
|
|
|
4
4
|
export declare const defineStoryboardPluginCore: SequencePluginCore;
|
|
5
5
|
export declare const presentPanelPluginCore: SequencePluginCore;
|
|
6
6
|
export * from "./definitions";
|
|
7
|
-
export { MAX_SLIDE_HTML, SLIDE_ANIMATION_KINDS, SLIDE_AUTO_END_SECONDS, SLIDE_CSS_ANIMATIONS_FINISHED, SLIDE_HEIGHT, SLIDE_WIDTH, slideHtmlDocument, } from "./slideHtml";
|
|
7
|
+
export { MAX_SLIDE_HTML, SLIDE_ANIMATION_KINDS, SLIDE_AUTO_END_SECONDS, SLIDE_CSS_ANIMATIONS_FINISHED, SLIDE_FIT_SCRIPT, SLIDE_HEIGHT, SLIDE_WIDTH, slideHtmlDocument, } from "./slideHtml";
|
|
8
8
|
export { SLIDE_CHART_TYPES, chartSlideDocument, type SlideChart, } from "./chartSlide";
|
|
9
9
|
export { markdownSlideHtml } from "./markdownSlide";
|
|
10
10
|
export { SLIDESHOWS_DIR, STORYBOARDS_DIR, loadRecord } from "./records";
|
package/dist/core/slideHtml.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ export declare const SLIDE_ANIMATION_KINDS: readonly ["animate", "counter", "typ
|
|
|
11
11
|
* it runs to the end of its beat, which the narration decides and the View
|
|
12
12
|
* doesn't know. */
|
|
13
13
|
export declare const SLIDE_AUTO_END_SECONDS = 8;
|
|
14
|
+
/** A script that fits a slide's data-fit boxes, as the View's page does, for
|
|
15
|
+
* a page that shows the slide's HTML without it: a host making a movie of a
|
|
16
|
+
* Markdown slide runs it in the movie's page (MulmoCast's html_tailwind
|
|
17
|
+
* beat takes it as its script), or dense text is cut off there. It fits at
|
|
18
|
+
* once and again when the page has loaded. */
|
|
19
|
+
export declare const SLIDE_FIT_SCRIPT = "(() => {\n const fit = () => {\n for (const el of document.querySelectorAll(\"[data-fit]\")) {\n let size = parseFloat(getComputedStyle(el).fontSize);\n while (size > 12 && (el.scrollHeight > el.clientHeight + 1 || el.scrollWidth > el.clientWidth + 1)) {\n size -= 1;\n el.style.fontSize = size + \"px\";\n }\n }\n };\n fit();\n addEventListener(\"load\", fit);\n})();";
|
|
14
20
|
/** PAGE_SCRIPT's hash, which the policy allows (checked by a test). */
|
|
15
21
|
export declare const PAGE_SCRIPT_HASH = "sha256-bNBxv6ehYc2PHOgmTReFSYsrxiGvq6aqqg9m5RwS7Lc=";
|
|
16
22
|
/** Text as HTML text or an attribute's value. */
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
let e=require("gui-chat-protocol");var t=1280,n=720,r=6e4,i=`https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3/dist/index.global.js`,a=`sha384-2ql948lIdLcGEE0/qxNiudyTjgauA3RDJERu5xW75kFCvSl5a9odyQYCb6tEjnmB`,o=[`animate`,`counter`,`typewriter`],s=8,c=`() => {
|
|
2
|
+
for (const el of document.querySelectorAll("[data-fit]")) {
|
|
3
|
+
let size = parseFloat(getComputedStyle(el).fontSize);
|
|
4
|
+
while (size > 12 && (el.scrollHeight > el.clientHeight + 1 || el.scrollWidth > el.clientWidth + 1)) {
|
|
5
|
+
size -= 1;
|
|
6
|
+
el.style.fontSize = size + "px";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}`,l=`(() => {
|
|
10
|
+
const fit = ${c};
|
|
11
|
+
fit();
|
|
12
|
+
addEventListener("load", fit);
|
|
13
|
+
})();`,u=`
|
|
14
|
+
(() => {
|
|
15
|
+
const EASE = {
|
|
16
|
+
linear: (t) => t,
|
|
17
|
+
easeIn: (t) => t * t,
|
|
18
|
+
easeOut: (t) => 1 - (1 - t) * (1 - t),
|
|
19
|
+
easeInOut: (t) => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2),
|
|
20
|
+
};
|
|
21
|
+
const AUTO_END = 8;
|
|
22
|
+
const TRANSFORMS = [
|
|
23
|
+
["data-translate-x", "translateX", "px"],
|
|
24
|
+
["data-translate-y", "translateY", "px"],
|
|
25
|
+
["data-scale", "scale", ""],
|
|
26
|
+
["data-rotate", "rotate", "deg"],
|
|
27
|
+
["data-rotate-x", "rotateX", "deg"],
|
|
28
|
+
["data-rotate-y", "rotateY", "deg"],
|
|
29
|
+
["data-rotate-z", "rotateZ", "deg"],
|
|
30
|
+
];
|
|
31
|
+
const STYLES = [
|
|
32
|
+
["data-opacity", "opacity", ""],
|
|
33
|
+
["data-width", "width", "px"],
|
|
34
|
+
["data-height", "height", "px"],
|
|
35
|
+
];
|
|
36
|
+
const number = (value, fallback) => {
|
|
37
|
+
const n = Number(value);
|
|
38
|
+
return value !== null && value.trim() !== "" && Number.isFinite(n) ? n : fallback;
|
|
39
|
+
};
|
|
40
|
+
const range = (value, unit) => {
|
|
41
|
+
if (value === null) return null;
|
|
42
|
+
const parts = value.split(",").map((part) => part.trim());
|
|
43
|
+
const from = Number(parts[0]);
|
|
44
|
+
const to = Number(parts[1]);
|
|
45
|
+
if (!Number.isFinite(from) || !Number.isFinite(to)) return null;
|
|
46
|
+
return { from, to, unit: parts[2] || unit };
|
|
47
|
+
};
|
|
48
|
+
const ranges = (el, table) =>
|
|
49
|
+
table
|
|
50
|
+
.map(([attr, name, unit]) => [name, range(el.getAttribute(attr), unit)])
|
|
51
|
+
.filter(([, r]) => r);
|
|
52
|
+
let entries = [];
|
|
53
|
+
const collect = () => {
|
|
54
|
+
entries = [...document.querySelectorAll("[data-animation]")].map((el) => {
|
|
55
|
+
const kind = el.getAttribute("data-animation");
|
|
56
|
+
const end = el.getAttribute("data-end");
|
|
57
|
+
const entry = {
|
|
58
|
+
el,
|
|
59
|
+
kind,
|
|
60
|
+
start: number(el.getAttribute("data-start"), 0),
|
|
61
|
+
end: end === null || end.trim() === "auto" ? null : number(end, null),
|
|
62
|
+
ease: EASE[el.getAttribute("data-easing")] || EASE.linear,
|
|
63
|
+
};
|
|
64
|
+
if (kind === "animate") {
|
|
65
|
+
entry.transforms = ranges(el, TRANSFORMS);
|
|
66
|
+
entry.styles = ranges(el, STYLES);
|
|
67
|
+
} else if (kind === "counter") {
|
|
68
|
+
entry.from = number(el.getAttribute("data-from"), 0);
|
|
69
|
+
entry.to = number(el.getAttribute("data-to"), 0);
|
|
70
|
+
entry.decimals = Math.max(0, Math.round(number(el.getAttribute("data-decimals"), 0)));
|
|
71
|
+
entry.prefix = el.getAttribute("data-prefix") || "";
|
|
72
|
+
entry.suffix = el.getAttribute("data-suffix") || "";
|
|
73
|
+
} else if (kind === "typewriter") {
|
|
74
|
+
entry.text = el.getAttribute("data-text") ?? el.textContent;
|
|
75
|
+
} else {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return entry;
|
|
79
|
+
}).filter(Boolean);
|
|
80
|
+
};
|
|
81
|
+
const seek = (t) => {
|
|
82
|
+
for (const e of entries) {
|
|
83
|
+
const end = e.end ?? AUTO_END;
|
|
84
|
+
const span = end - e.start;
|
|
85
|
+
const p = span > 0 ? Math.min(1, Math.max(0, (t - e.start) / span)) : t >= e.start ? 1 : 0;
|
|
86
|
+
const k = e.ease(p);
|
|
87
|
+
const at = (r) => r.from + (r.to - r.from) * k;
|
|
88
|
+
if (e.kind === "animate") {
|
|
89
|
+
if (e.transforms.length) {
|
|
90
|
+
e.el.style.transform = e.transforms.map(([name, r]) => name + "(" + at(r) + r.unit + ")").join(" ");
|
|
91
|
+
}
|
|
92
|
+
for (const [name, r] of e.styles) e.el.style[name] = at(r) + r.unit;
|
|
93
|
+
} else if (e.kind === "counter") {
|
|
94
|
+
e.el.textContent = e.prefix + (e.from + (e.to - e.from) * k).toFixed(e.decimals) + e.suffix;
|
|
95
|
+
} else {
|
|
96
|
+
e.el.textContent = e.text.slice(0, Math.floor(e.text.length * k));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
window.__slideSeek = seek;
|
|
101
|
+
const fit = ${c};
|
|
102
|
+
const play = () => {
|
|
103
|
+
const last = Math.max(0, ...entries.map((e) => e.end ?? AUTO_END));
|
|
104
|
+
const begun = performance.now();
|
|
105
|
+
const tick = (now) => {
|
|
106
|
+
const t = (now - begun) / 1000;
|
|
107
|
+
seek(t);
|
|
108
|
+
if (t <= last) requestAnimationFrame(tick);
|
|
109
|
+
};
|
|
110
|
+
requestAnimationFrame(tick);
|
|
111
|
+
};
|
|
112
|
+
// Collected once the body is parsed: at DOMContentLoaded, or earlier when
|
|
113
|
+
// the fallback shows the slide while a slow Tailwind still holds
|
|
114
|
+
// DOMContentLoaded back (the body is parsed by then: readyState is
|
|
115
|
+
// "interactive" before deferred scripts run). Played once both are done.
|
|
116
|
+
let collected = false;
|
|
117
|
+
let shown = false;
|
|
118
|
+
const init = () => {
|
|
119
|
+
if (collected || document.readyState === "loading") return;
|
|
120
|
+
collected = true;
|
|
121
|
+
collect();
|
|
122
|
+
seek(0);
|
|
123
|
+
if (shown) play();
|
|
124
|
+
};
|
|
125
|
+
const show = () => {
|
|
126
|
+
if (shown) return;
|
|
127
|
+
init();
|
|
128
|
+
fit();
|
|
129
|
+
shown = true;
|
|
130
|
+
document.documentElement.classList.add("ready");
|
|
131
|
+
if (collected) play();
|
|
132
|
+
};
|
|
133
|
+
const ready = () => requestAnimationFrame(() => requestAnimationFrame(show));
|
|
134
|
+
const check = () =>
|
|
135
|
+
document.querySelector("style:not([type]):not(#slide-base)") ? ready() : setTimeout(check, 30);
|
|
136
|
+
addEventListener("DOMContentLoaded", () => {
|
|
137
|
+
init();
|
|
138
|
+
check();
|
|
139
|
+
});
|
|
140
|
+
setTimeout(show, 1000);
|
|
141
|
+
addEventListener("click", (event) => {
|
|
142
|
+
if (event.target instanceof Element && event.target.closest("a, area")) event.preventDefault();
|
|
143
|
+
}, true);
|
|
144
|
+
})();`,d=[`default-src 'none'`,`script-src 'sha256-bNBxv6ehYc2PHOgmTReFSYsrxiGvq6aqqg9m5RwS7Lc=' ${i}`,`style-src 'unsafe-inline' https://fonts.googleapis.com`,`font-src https://fonts.gstatic.com data:`,`img-src data: blob:`,`media-src data: blob:`,`connect-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `),f={"&":`&`,"<":`<`,">":`>`,'"':`"`},p=e=>e.replace(/[&<>"]/g,e=>f[e]??e),ee=e=>e.replace(/<(?=(meta|base|link)\b)/gi,`<`).replace(/shadowroot/gi,`data-no-shadow-root`),m=`*, *::before, *::after { animation-play-state: paused !important; animation-duration: 0s !important; animation-delay: 0s !important; animation-iteration-count: 1 !important; animation-fill-mode: both !important; transition: none !important; }`,te=`
|
|
145
|
+
html, body { margin: 0; width: ${t}px; height: 720px; overflow: hidden; }
|
|
146
|
+
html:not(.ready) body { visibility: hidden; }
|
|
147
|
+
${m}`;function ne(e){return`<!doctype html>
|
|
148
|
+
<html lang="en">
|
|
149
|
+
<head>
|
|
150
|
+
<meta charset="utf-8">
|
|
151
|
+
<meta http-equiv="Content-Security-Policy" content="${d}">
|
|
152
|
+
<style id="slide-base">${te}</style>
|
|
153
|
+
<script>${u}<\/script>
|
|
154
|
+
<script defer src="${i}" integrity="${a}" crossorigin="anonymous"><\/script>
|
|
155
|
+
</head>
|
|
156
|
+
<body class="bg-white text-slate-900 font-sans antialiased">
|
|
157
|
+
${ee(e)}
|
|
158
|
+
</body>
|
|
159
|
+
</html>`}var h=[`bar`,`line`,`pie`,`doughnut`,`radar`,`polarArea`,`scatter`,`bubble`],g=`https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js`,re=`sha384-jb8JQMbMoBUzgWatfe6COACi2ljcDdZQ2OxczGA3bGNeWe+6DChMTBJemed7ZnvJ`,ie=`
|
|
160
|
+
(() => {
|
|
161
|
+
const fail = (why) => {
|
|
162
|
+
document.getElementById("chart-error").textContent = why;
|
|
163
|
+
document.documentElement.classList.add("failed");
|
|
164
|
+
};
|
|
165
|
+
addEventListener("DOMContentLoaded", () => {
|
|
166
|
+
if (!window.Chart) return fail("The chart couldn't be drawn: Chart.js didn't load.");
|
|
167
|
+
let config;
|
|
168
|
+
try {
|
|
169
|
+
config = JSON.parse(document.getElementById("chart-config").textContent);
|
|
170
|
+
} catch {
|
|
171
|
+
return fail("The chart couldn't be drawn: its configuration isn't valid.");
|
|
172
|
+
}
|
|
173
|
+
Chart.defaults.font.size = 22;
|
|
174
|
+
Chart.defaults.color = "#334155";
|
|
175
|
+
Chart.defaults.animation.duration = 1200;
|
|
176
|
+
config.options = { ...config.options, responsive: true, maintainAspectRatio: false };
|
|
177
|
+
try {
|
|
178
|
+
new Chart(document.getElementById("chart"), config);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
fail("The chart couldn't be drawn: " + (error && error.message ? error.message : error));
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
})();`,ae=[`default-src 'none'`,`script-src 'sha256-TfZZxJX+yWgzYLy7zqsMa85OJEaC+zNLIpKLDZVnUqA=' ${g}`,`style-src 'unsafe-inline'`,`img-src data: blob:`,`connect-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `),oe=`
|
|
184
|
+
html, body { margin: 0; width: ${t}px; height: 720px; overflow: hidden; background: #ffffff; color: #0f172a; font-family: ui-sans-serif, system-ui, sans-serif; }
|
|
185
|
+
body { box-sizing: border-box; padding: 40px 64px 48px; display: flex; flex-direction: column; gap: 24px; }
|
|
186
|
+
h1 { margin: 0; font-size: 52px; font-weight: 800; color: #1e3a8a; line-height: 1.15; text-align: center; }
|
|
187
|
+
.chart { position: relative; flex: 1; min-height: 0; }
|
|
188
|
+
#chart-error { display: none; margin: auto; font-size: 28px; color: #b91c1c; }
|
|
189
|
+
.failed .chart { display: none; }
|
|
190
|
+
.failed #chart-error { display: block; }`,se=e=>JSON.stringify(e).replace(/</g,`\\u003c`);function ce(e,t){return`<!doctype html>
|
|
191
|
+
<html lang="en">
|
|
192
|
+
<head>
|
|
193
|
+
<meta charset="utf-8">
|
|
194
|
+
<meta http-equiv="Content-Security-Policy" content="${ae}">
|
|
195
|
+
<style>${oe}</style>
|
|
196
|
+
<script>${ie}<\/script>
|
|
197
|
+
<script defer src="${g}" integrity="${re}" crossorigin="anonymous"><\/script>
|
|
198
|
+
<script type="application/json" id="chart-config">${se(t)}<\/script>
|
|
199
|
+
</head>
|
|
200
|
+
<body>
|
|
201
|
+
${e?`<h1>${p(e)}</h1>`:``}
|
|
202
|
+
<div class="chart"><canvas id="chart"></canvas></div>
|
|
203
|
+
<p id="chart-error"></p>
|
|
204
|
+
</body>
|
|
205
|
+
</html>`}var _=`presentSlide`,le=`defineStoryboard`,ue=`presentPanel`,de=4,fe=12,pe=3,v=/^[0-9a-f]{12}$/,y=e=>typeof e==`object`&&!!e&&!Array.isArray(e),b=e=>typeof e==`string`?e.trim():``,x=(e,t)=>e.toLowerCase()===t.toLowerCase(),S=e=>typeof e==`number`&&Number.isInteger(e),C=`presentSlide needs slide and totalSlides (whole numbers, slide from 1 to totalSlides), and one of imagePrompt, html, markdown or chart`,w=`presentSlide's slide is too long: keep its html, markdown or chart under ${r} characters`,T=`presentSlide's chart must be a Chart.js configuration: an object with type (${h.join(`, `)}), data ({ labels, datasets }) and optionally options`;function me(e){let t=e;if(typeof t==`string`)try{t=JSON.parse(t)}catch{return null}if(!y(t)||!y(t.data))return null;let n=t.type;return!h.some(e=>e===n)||t.options!==void 0&&!y(t.options)?null:t}function E(e){let{slide:t,totalSlides:n,title:r,imagePrompt:i}=e,a=b(e.html),o=a?``:b(e.markdown),s=!a&&!o&&e.chart!==void 0&&e.chart!==null;if(!S(t)||!S(n)||t<1||n<t||!a&&!o&&!s&&!b(i))return C;let c=s?me(e.chart):null;return s&&!c?T:Math.max(a.length,o.length,c?JSON.stringify(c).length:0)>6e4?w:{slide:t,totalSlides:n,title:typeof r==`string`?r:``,imagePrompt:!a&&!o&&!c?String(i):``,...a&&{html:a},...o&&{markdown:o},...c&&{chart:c},mode:e.mode===`steps`?`steps`:`presentation`}}var D=`teach its subject as a presenter speaking to the audience, and cover everything on it: every section, point and number, in order, each in a sentence or more. The slide is replaced by the next one as soon as you stop talking, so a point you leave out is never explained. Begin with the content itself, never with the slide or your plan: no "This slide is about…", "The first slide shows…", "Let's unpack this one", "Next, I'll…". A line about the slide is not an explanation.`,O=({slide:e,totalSlides:t,title:n,mode:r})=>{let i=n?` ("${n}")`:``;if(r===`steps`){let n=`Step ${e} of ${t}${i}`;return e<t?`${n} is now on the screen. Say what to do in this step, directly ("Crack two eggs into a bowl…", not "This step is about eggs"), then stop and wait while the user does it. When they say they're ready (next, done, go on), call presentSlide for step ${e+1} with mode "steps". If they ask to go back or to see a step again, call presentSlide for that step.`:`${n}, the last one, is now on the screen. Say what to do, directly, then wrap up the guide once the user is done.`}let a=`Slide ${e} of ${t}${i}`;return e<t?`${a} is now on the screen. Explain it: ${D} Then, in this same reply and without waiting for the user, call presentSlide for slide ${e+1}: the call is the last thing in your reply, after your whole explanation, and ends it. Nothing you meant to say after it is heard, so a sentence and then the call leaves the slide unexplained. If the user has asked you to stop, or asked something else, since the slideshow began, answer them instead of going on.`:`${a}, the last one, is now on the screen. Explain it: ${D} Then wrap up the slideshow.`},he=[`For an HTML slide, instead of imagePrompt: the slide's content, the inside of <body> only (no <html>, <head> or <body> tags), for a ${t}x720 px canvas, which it should fill (start with a <div class="w-full h-full ...">). Style it with Tailwind CSS v4 classes, which are loaded for you: a clear layout, a large title (text-6xl or so), few words in large type, a color scheme, and inline SVG or emoji for icons and diagrams. There is no network: no <img> from URLs, no links to follow.`,`Animate it so it builds up as you explain it, with MulmoCast's data-animation attributes (the same ones make it move in a movie of the slideshow): data-animation="animate" with data-opacity="0,1", data-translate-x, data-translate-y (px), data-scale, data-rotate (deg), data-width or data-height ("0,80,%"), each "from,to"; data-animation="counter" with data-from, data-to, data-decimals, data-prefix, data-suffix; data-animation="typewriter" (its text, or data-text). Give every one data-start and data-end in seconds, and optionally data-easing (linear, easeIn, easeOut, easeInOut); stagger the parts with later data-start values so they appear one after another over a few seconds (for example 0, 0.6, 1.2). Set the first frame inline too (style="opacity:0" on an element that fades in).`,`CSS animations, transitions and scripts don't play: animate only with data-animation. A <style> for layout is allowed (and Google Fonts by @import).`].join(` `),ge=`For a Markdown slide, instead of imagePrompt: the slide in Markdown (GitHub's: headings, lists, bold, tables, code), with math in TeX between $...$ inline or $$...$$ for a displayed equation (on lines of its own). Start with a # heading, and keep it to what fits on one slide: a heading and a few lines, a list or an equation or two. Write a dollar sign that isn't math as \\$.`,_e=`For a chart slide, instead of imagePrompt: a Chart.js configuration as JSON text, {"type": ..., "data": ...}: { type, data: { labels, datasets: [{ label, data, backgroundColor }] }, options }. type is one of ${h.join(`, `)}. It is drawn under the slide's title, so leave out options.plugins.title; give the datasets colors, and the axes titles (options.scales.x.title) when their units aren't obvious. JSON values only: no functions.`,k=`When the user asks for a slideshow (or to explain something with slides), plan four to six slides, then show them one at a time with presentSlide: slide 1 first, and each next slide only after you have explained the one on the screen. Explain the subject, not the slides: say what the slide teaches, not that a slide is about it, and explain everything on a slide before showing the next; put on a slide only what you will explain. Go on to the last slide without asking whether to continue. A slide is a generated picture (imagePrompt), a chart (chart), a text slide in Markdown (markdown) or a designed slide in HTML (html): use a picture for a scene, an object or a place; a chart for numbers to compare, a trend or proportions; Markdown for equations and formulas (TeX math), definitions, short lists and small tables; and HTML for designed layouts, comparisons, timelines and diagrams. A picture model draws words, numbers and math badly, so they go on the other kinds; a slideshow can mix them. When the user asks for HTML slides (slides in HTML, an HTML presentation), this is the tool: make them HTML slides with presentSlide, one call per slide, not one HTML page. When the user wants to be shown how to do something they will do along with you (cooking, folding, fixing, an exercise), make it a step-by-step guide instead: mode "steps", one slide per step, and after each step wait for the user to say they are ready. Use generateImage for a single picture, not for slides.`,A={type:`function`,name:_,prompt:k,description:`Show one slide of a slideshow, full screen: a picture generated from imagePrompt, a chart (chart), a slide written in Markdown with TeX math (markdown), or a slide written in HTML (html). Give one of the four. Call it once per slide, in order, starting with slide 1. With mode "steps" it is a step-by-step guide: one slide per step, and the next step waits for the user.`,parameters:{type:`object`,properties:{slide:{type:`integer`,description:`This slide's number, starting at 1.`},totalSlides:{type:`integer`,description:`How many slides the slideshow has. Keep it the same for every slide.`},title:{type:`string`,description:`The slide's title.`},imagePrompt:{type:`string`,description:`For a picture slide: the picture, a clear illustration of its point, with the title as its only large text. Be concrete. For a step, show the action: hands, tools and the object. Leave it out for a chart, Markdown or HTML slide.`},html:{type:`string`,description:he},markdown:{type:`string`,description:ge},chart:{type:`string`,description:_e},mode:{type:`string`,enum:[`presentation`,`steps`],description:`"presentation" (the default) goes on by itself; "steps" is a how-to the user does along with you, and waits for them after each step. Keep it the same for every slide.`}},required:[`slide`,`totalSlides`,`title`]}};function j(e){let{totalPanels:t}=e;if(!S(t)||t<1||t>12)return`totalPanels must be a whole number from 1 to 12`;let n=[];for(let t of Array.isArray(e.characters)?e.characters:[]){let e=y(t)?b(t.name):``,r=y(t)?b(t.description):``;if(!e||!r)return`every character needs a name and a description`;if(n.some(t=>x(t.name,e)))return`two characters are named ${e}`;n.push({name:e,description:r})}return n.length<1||n.length>4?`a storyboard has 1 to 4 characters`:{title:b(e.title),style:b(e.style),totalPanels:t,characters:n,interactive:e.interactive===!0}}var M=({title:e,id:t})=>`The cast of "${e}" is now on the screen. Introduce the characters briefly, then, in this same reply and without waiting for the user, call presentPanel for panel 1 with storyboardId "${t}". If the user has asked you to stop, or asked something else, answer them instead of going on.`,N=`presentPanel needs a storyboardId, a panel number (from 1) and an imagePrompt`;function P(e){let{panel:t}=e,n=b(e.storyboardId),r=b(e.imagePrompt);if(!n||!r||!S(t)||t<1)return null;let i=e=>(Array.isArray(e)?e:[]).map(b).filter(Boolean);return{storyboardId:n,panel:t,characters:i(e.characters),imagePrompt:r,caption:b(e.caption),choices:i(e.choices).slice(0,3)}}function F(e,t,n){let{id:r,totalPanels:i}=e;return n.length?`Panel ${t} of ${i} is now on the screen, with the user's choices: ${n.map((e,t)=>`${t+1}. ${e}`).join(` `)} Tell this part of the story, then read the choices out and ask the user which one they pick, and wait for their answer. Then call presentPanel for panel ${t+1} with storyboardId "${r}", going on the way they chose.`:t<i?`Panel ${t} of ${i} is now on the screen. Tell this part of the story, then, in this same reply and without waiting for the user, call presentPanel for panel ${t+1} with storyboardId "${r}". If the user has asked you to stop, or asked something else, since the story began, answer them instead of going on.`:`Panel ${t} of ${i}, the last one, is now on the screen. Tell this part of the story, then bring it to an end.`}var ve=`When the user asks for a story, a picture book, a comic or a storyboard, tell it in pictures: first call defineStoryboard with the art style and the characters who appear more than once (it draws a reference sheet for each, so they look the same in every panel), then show the panels one at a time with presentPanel, naming the characters in each, and tell each part of the story when its panel appears. Go on to the last panel without asking whether to continue, unless the story is interactive. When the user wants to decide what happens (an adventure, a story for a child who wants to choose), set interactive, and give two or three choices on two or three of the panels: the story waits there for the user's pick and goes on their way; it still ends at the last panel whichever way it goes. Use presentSlide to explain a topic, not to tell a story.`,I={type:`function`,name:le,prompt:ve,description:`Start a story told in pictures: set its art style and draw a reference sheet for each recurring character, shown as the cast. Then call presentPanel for each panel, in order.`,parameters:{type:`object`,properties:{title:{type:`string`,description:`The story's title.`},style:{type:`string`,description:`The art style of every picture, in one sentence (for example: soft watercolor, children's picture book).`},totalPanels:{type:`integer`,description:`How many panels the story has, at most 12; four to eight is usual.`},interactive:{type:`boolean`,description:`The user chooses what happens: panels can offer choices, and the story waits for the pick.`},characters:{type:`array`,description:`The characters who appear in more than one panel, 1 to 4.`,items:{type:`object`,properties:{name:{type:`string`,description:`A short name, used in presentPanel.`},description:{type:`string`,description:`How they look: age, build, face, hair, clothes and colors. Be concrete: it is drawn once and reused in every panel.`}},required:[`name`,`description`]}}},required:[`title`,`style`,`totalPanels`,`characters`]}},L={type:`function`,name:ue,description:`Show one panel of a storyboard made with defineStoryboard: a picture of the scene with its characters drawn as in their reference sheets, and a caption. Call it once per panel, in order, starting with panel 1.`,parameters:{type:`object`,properties:{storyboardId:{type:`string`,description:`The storyboardId defineStoryboard returned.`},panel:{type:`integer`,description:`This panel's number, starting at 1.`},characters:{type:`array`,items:{type:`string`},description:`The names (as in defineStoryboard) of the characters in this panel.`},imagePrompt:{type:`string`,description:`What the panel shows: the setting, what happens, expressions and the camera angle. Refer to characters by name; don't describe their looks again.`},caption:{type:`string`,description:`One short line shown under the picture.`},choices:{type:`array`,items:{type:`string`},description:`Only in an interactive story, and not on the last panel: two or three short options for what happens next. The story waits for the user's pick.`}},required:[`storyboardId`,`panel`,`characters`,`imagePrompt`]}},R=e=>typeof e==`object`&&!!e&&!Array.isArray(e),ye=e=>R(e)&&typeof e.message==`string`;function z(e){let t=R(e?.data)?e.data:{};return{...typeof t.imageData==`string`&&{imageData:t.imageData},...typeof t.imagePath==`string`&&{imagePath:t.imagePath}}}function B(e){let t=R(e?.data)?e.data:{};return typeof t.html==`string`?t.html:``}function V(e){let t=R(e?.data)?e.data:{};return R(t.chart)?JSON.stringify(t.chart):``}var be=e=>!!z(e).imageData||!!B(e)||!!V(e);function xe(e,t){let n=z(e),r=z(t);if(n.imagePath&&r.imagePath)return n.imagePath===r.imagePath;if(n.imageData)return n.imageData===r.imageData;let i=B(e);if(i)return i===B(t);let a=V(e);return!!a&&a===V(t)}var H=(e,t)=>e.userSpokeAt===void 0||e.userSpokeAt>t,U=()=>crypto.randomUUID().replace(/-/g,``).slice(0,12),Se=`artifacts/`;async function W(e,t){let n=e.files?.artifacts;if(!e.app?.editImages||!n)return[];let r=[];for(let e of t)if(e.startsWith(Se))try{await n.exists(e.slice(10))&&r.push(e)}catch{}return r}async function G(e,t,n=[]){let r=e.app,i=r?.editImages,a=r?.generateImage;try{let e;if(n.length&&i)e=await i(t,[...n]);else if(a)e=await a(t);else return{message:`image generation isn't available`};return ye(e)?e:{message:`image generation returned an unrecognized result`}}catch(e){return{message:`image generation failed: ${e instanceof Error?e.message:String(e)}`}}}var Ce=50;function K(e){let t=new Map;return n=>{let r=n.conversationId??``,i=t.get(r);if(i===void 0?i=e():t.delete(r),t.set(r,i),t.size>Ce){let e=t.keys().next().value;e!==void 0&&t.delete(e)}return i}}var q=`slide-md`,we=`
|
|
206
|
+
.${q} { box-sizing: border-box; flex-shrink: 0; width: ${t}px; height: 720px; padding: 56px 80px; overflow: hidden; display: flex; flex-direction: column; justify-content: center; gap: 0.5em; background: #ffffff; color: #0f172a; font-family: ui-sans-serif, system-ui, sans-serif; line-height: 1.35; }
|
|
207
|
+
.${q} > * { margin: 0; }
|
|
208
|
+
.${q} h1 { font-size: 1.9em; font-weight: 800; color: #1e3a8a; line-height: 1.15; }
|
|
209
|
+
.${q} h2 { font-size: 1.5em; font-weight: 700; color: #1e40af; line-height: 1.2; }
|
|
210
|
+
.${q} h3 { font-size: 1.2em; font-weight: 700; color: #334155; }
|
|
211
|
+
.${q} ul, .${q} ol { padding-left: 1.3em; display: flex; flex-direction: column; gap: 0.3em; }
|
|
212
|
+
.${q} ul { list-style: disc; }
|
|
213
|
+
.${q} ol { list-style: decimal; }
|
|
214
|
+
.${q} li > ul, .${q} li > ol { margin-top: 0.3em; font-size: 0.9em; }
|
|
215
|
+
.${q} li::marker { color: #2563eb; }
|
|
216
|
+
.${q} strong { font-weight: 700; color: #1e3a8a; }
|
|
217
|
+
.${q} em { font-style: italic; }
|
|
218
|
+
.${q} a { color: #2563eb; text-decoration: underline; }
|
|
219
|
+
.${q} code { font-family: ui-monospace, monospace; font-size: 0.85em; background: #f1f5f9; border-radius: 0.2em; padding: 0.05em 0.3em; }
|
|
220
|
+
.${q} pre { background: #0f172a; color: #e2e8f0; border-radius: 0.4em; padding: 0.6em 0.8em; font-size: 0.75em; overflow: hidden; }
|
|
221
|
+
.${q} pre code { background: none; padding: 0; color: inherit; }
|
|
222
|
+
.${q} blockquote { border-left: 0.2em solid #93c5fd; padding-left: 0.8em; color: #475569; font-style: italic; }
|
|
223
|
+
.${q} table { border-collapse: collapse; font-size: 0.85em; align-self: flex-start; }
|
|
224
|
+
.${q} th, .${q} td { border: 1px solid #cbd5e1; padding: 0.25em 0.7em; text-align: left; }
|
|
225
|
+
.${q} th { background: #eff6ff; font-weight: 700; }
|
|
226
|
+
.${q} hr { border: 0; border-top: 2px solid #e2e8f0; }
|
|
227
|
+
.${q} img { max-width: 100%; max-height: 60%; }
|
|
228
|
+
.${q} .math-display { display: block; text-align: center; font-size: 1.15em; }
|
|
229
|
+
.${q} math { font-family: "Latin Modern Math", "STIX Two Math", "Cambria Math", math; }
|
|
230
|
+
.${q} mtd { padding: 0.1em 0.4em; }
|
|
231
|
+
.${q} mtd:first-child { padding-left: 0; }
|
|
232
|
+
.${q} mtd:last-child { padding-right: 0; }
|
|
233
|
+
.${q} .math-error { color: #b91c1c; font-family: ui-monospace, monospace; }`;function Te(e){let t=e.split(`
|
|
234
|
+
`).filter(e=>e.trim()).length,n=e.length;return t<=5&&n<=250?40:t<=8&&n<=450?34:t<=12&&n<=700?28:24}var Ee=/^[ \t]*(?:\$\$([\s\S]+?)\$\$|\\\[([\s\S]+?)\\\])[ \t]*(?:\n|$)/,De=/^(?:\$\$(.+?)\$\$|\$(?!\s)((?:\\.|[^\\$\n])+?)(?<!\s)\$(?!\d)|\\\((.+?)\\\))/;function Oe(e){return[{name:`blockMath`,level:`block`,start:e=>{let t=/(^|\n)(?=[ \t]*(?:\$\$|\\\[))/.exec(e);return t?t.index+(t[1]??``).length:void 0},tokenizer(e){let t=Ee.exec(e);if(t)return{type:`blockMath`,raw:t[0],text:(t[1]??t[2]??``).trim()}},renderer:t=>`<div class="math-display">${e(String(t.text),!0)}</div>\n`},{name:`inlineMath`,level:`inline`,start:e=>e.match(/(?<!\\)\$|\\\(/)?.index,tokenizer(e){let t=De.exec(e);if(t)return{type:`inlineMath`,raw:t[0],text:(t[1]??t[2]??t[3]??``).trim(),display:t[1]!==void 0}},renderer:t=>e(String(t.text),t.display===!0)}]}var ke=/(<\/mi>|<\/mn>|<\/mrow>|<mo[^>]*>[)\]]<\/mo>)?(<mi(?: mathvariant="normal")?>[A-Za-z]{2,}<\/mi>)<mo>\u2061<\/mo>/g,Ae=`<mspace width="0.1667em"></mspace>`,je=e=>e.replace(ke,(e,t,n)=>`${t?t+Ae:``}${n}<mo lspace="0" rspace="0.1667em">\u2061</mo>`);async function J(e){let[{Marked:t},{default:n}]=await Promise.all([import(`marked`),import(`katex`)]),r=(e,t)=>{try{return je(n.renderToString(e,{output:`mathml`,displayMode:t,throwOnError:!0}))}catch{return`<code class="math-error">${p(e)}</code>`}},i=new t({gfm:!0,breaks:!1});i.use({extensions:Oe(r)});let a=await i.parse(e);return`<style>${we}</style>
|
|
235
|
+
<div class="${q}" data-fit style="font-size: ${Te(e)}px">
|
|
236
|
+
${a}</div>`}var Y=`slideshows`,X=`storyboards`,Me={},Ne=new WeakMap,Pe=new WeakMap;function Fe(e){let t=Ne.get(e);return t||Ne.set(e,t=new Map),t}function Ie(t,n){let r=Pe.get(t);r||Pe.set(t,r=new Map);let i=r.get(n);return i||r.set(n,i=(0,e.createSerialLock)()),i}var Le=(e,t)=>`${e}/${t}.json`;async function Re(e,t){let n=Fe(e??Me),r=n.get(t);if(r)return r;if(!e)return null;try{let r=JSON.parse(await e.read(t));return n.set(t,r),r}catch{return null}}async function ze(e,t,n){return v.test(n)?Re(e,Le(t,n)):null}async function Z(e,t,n,r){let i=Le(t,n),a=e??Me;return Ie(a,i)(async()=>{let t=r(await Re(e,i));if(Fe(a).set(i,t),!e)return!1;try{return await e.write(i,JSON.stringify(t,null,2)),!0}catch(e){return console.warn(`[sequence] could not save`,i,e),!1}})}var Q=e=>`the ${e} could not be saved, so it lasts only until the app or server restarts`,Be=6e4;function Ve(){let e=new Map;return{claim:t=>{let n=Date.now();for(let[t,r]of e)n-r.at>Be&&e.delete(t);let r=e.get(t);if(r)return{earlier:r.shown};let i=()=>void 0,a=new Promise(e=>i=e);return e.set(t,{at:n,shown:a}),{settle:n=>{!n&&e.get(t)?.shown===a&&e.delete(t),i(n)}}}}}function He({slide:e,totalSlides:t,mode:n}){return n===`steps`?{step:e,total:t,kind:`guide`,label:`Step ${e} of ${t}`,onShown:`explain it`,nextCall:`call presentSlide for step ${e+1} of ${t} with mode "steps"`,waitsForUser:!0}:{step:e,total:t,kind:`slideshow`,label:`Slide ${e} of ${t}`,onShown:`explain it`,nextCall:`call presentSlide for slide ${e+1} of ${t}`}}var Ue=(e,{slide:t,totalSlides:n,title:r,imagePrompt:i,html:a,markdown:o,chart:s})=>JSON.stringify([e,t,n,r,i,a,o,s]),We=K(()=>({latest:{presentation:null,steps:null},lastShown:null,repeats:Ve()}));function Ge(e,t){let n=e.latest[t.mode],r=n?.totalSlides===t.totalSlides&&(t.slide!==1||n.firstTitle===t.title);if(n&&r)return n;let i={id:U(),mode:t.mode,totalSlides:t.totalSlides,firstTitle:t.slide===1?t.title:``,slides:new Map,current:0,shownAt:-1/0};return e.latest[t.mode]=i,i}function Ke(e,t,n,r){t.current=n.slide,t.shownAt=Date.now(),e.lastShown=t;let i=n.mode===`steps`?`step`:`slide`,{uuid:a,...o}=r;return{...o,message:`${i} ${n.slide} of ${n.totalSlides} is on the screen again, as it was`,instructions:O(n),instructionsRequired:!0,sequence:He(n)}}function qe(e,t){if(e.mode!==`steps`)return e.title?`A presentation slide titled "${e.title}". ${e.imagePrompt}`:e.imagePrompt;let n=e.title?`, "${e.title}"`:``;return[`An instructional illustration of step ${e.slide} of ${e.totalSlides}${n}. ${e.imagePrompt}`,t?`The reference image is the previous step: keep the same objects, hands, tools and setting, and show what this step changes.`:``,`Show the action clearly, with little or no text.`].filter(Boolean).join(` `)}async function Je(e,t,n){if(n.html)return{html:n.html,prompt:``};if(n.chart)return{chart:n.chart,prompt:``};if(n.markdown)return{html:await J(n.markdown),markdown:n.markdown,prompt:``};let r=t.slides.get(n.slide-1),i=n.mode===`steps`?z(r).imagePath:void 0,a=i?await W(e,[i]):[],o=qe(n,a.length>0),s=await G(e,o,a),{imageData:c,imagePath:l}=z(s);return c?{imageData:c,...l&&{imagePath:l},prompt:o}:{failed:{...s,sequence:null}}}async function Ye(e,t,n){let r=await Je(e,t,n);if(`failed`in r)return r.failed;let{imageData:i,imagePath:a,html:o,markdown:s,chart:c,prompt:l}=r,u=t.id,d=await Z(e.files?.artifacts,Y,u,e=>{let t=e??{id:u,title:``,mode:n.mode,totalSlides:n.totalSlides,slides:{}};return n.slide===1&&(t.title=n.title),t.slides[String(n.slide)]={title:n.title,imagePrompt:n.imagePrompt,...a&&{imagePath:a},...o&&{html:o},...s&&{markdown:s},...c&&{chart:c}},t}),f={...i&&{imageData:i},...a&&{imagePath:a},...o&&{html:o},...s&&{markdown:s},...c&&{chart:c},prompt:l,slideshowId:u,slide:n.slide,totalSlides:n.totalSlides,title:n.title,mode:n.mode},p=n.mode===`steps`?`step`:`slide`;return{data:f,title:n.title||`Slide ${n.slide}`,message:[`${p} ${n.slide} of ${n.totalSlides} of slideshow "${u}" is on the screen`,a?`saved to ${a}`:``,d?``:Q(`slideshow`)].filter(Boolean).join(`; `),instructions:O(n),instructionsRequired:!0,sequence:He(n)}}async function Xe(e,t){let n=E(t);if(typeof n==`string`)return{message:n,sequence:null};let r=We(e),i=Ge(r,n),a=n.mode===`steps`;if(a&&n.slide>i.current&&i.current>0&&!H(e,i.shownAt)){console.info(`[sequence] holding step ${n.slide} for the user`);let e=i.shownAt===1/0?`is still being drawn`:`is still on the screen`;return{message:`step ${n.slide} was not shown: the user hasn't said they're ready yet, and step ${i.current} ${e}. Wait for them before calling presentSlide for step ${n.slide}.`,cancelled:!0}}let o=a?i.slides.get(n.slide):void 0;if(o)return xe(e.currentResult,o)?{message:`step ${n.slide} of ${n.totalSlides} is already on the screen`,cancelled:!0}:Ke(r,i,n,o);let s=r.repeats.claim(Ue(i.id,n));if(`earlier`in s){if(!await s.earlier)return Xe(e,t);let a=i.slides.get(n.slide);return a&&r.lastShown!==i?Ke(r,i,n,a):{message:`slide ${n.slide} of ${n.totalSlides} is already on the screen`,cancelled:!0}}let{settle:c}=s,l={current:i.current,shownAt:i.shownAt};i.current=n.slide,i.shownAt=1/0;let u=!1;try{let t=await Ye(e,i,n);return u=be(t),u&&(i.slides.set(n.slide,t),i.current=n.slide,i.shownAt=Date.now(),r.lastShown=i),t}finally{c(u),!u&&i.current===n.slide&&(i.current=l.current,i.shownAt=l.shownAt)}}var Ze=({id:e,title:t,totalPanels:n})=>({step:0,total:n,kind:`story`,label:`The cast of "${t}"`,onShown:`introduce the characters`,nextCall:`call presentPanel for panel 1 of ${n} with storyboardId "${e}"`}),Qe=({id:e,totalPanels:t},n,r)=>({step:n,total:t,kind:`story`,label:`Panel ${n} of ${t}`,onShown:`tell that part of the story`,nextCall:`call presentPanel for panel ${n+1} of ${t} with storyboardId "${e}"`,...r.length>0&&{waitsForUser:!0}}),$e=(e,t)=>e?`${e}. ${t}`:t,et=(e,{name:t,description:n})=>$e(e,`Character reference sheet of ${t}: ${n}. Full body, front, side and back views, on a plain white background. No text or labels.`);async function tt(e,t){let n=j(t);if(typeof n==`string`)return{message:n,sequence:null};let r=await Promise.all(n.characters.map(t=>G(e,et(n.style,t)))),i=r.map(z),a=r.find((e,t)=>!i[t]?.imageData);if(i.every(({imageData:e})=>!e)&&a)return{...a,message:`the character sheets couldn't be made: ${a.message}`,sequence:null};let o={id:U(),...n,characters:n.characters.map((e,t)=>{let n=i[t]?.imagePath;return{...e,...n&&{imagePath:n}}}),panels:{}},s=await Z(e.files?.artifacts,X,o.id,()=>o),c=o.characters.filter(e=>e.imagePath).map(e=>`${e.name}: ${e.imagePath}`),l=n.characters.map(({name:e},t)=>i[t]?.imageData?null:`${e} (${r[t]?.message??``})`).filter(e=>!!e),u=i.find(e=>e.imageData)?.imageData;return{data:{storyboardId:o.id,title:o.title,totalPanels:o.totalPanels,...u&&{imageData:u},characters:o.characters.map(({name:e,imagePath:t},n)=>{let r=i[n]?.imageData;return{name:e,...r&&{imageData:r},...t&&{imagePath:t}}})},title:o.title,message:[`storyboard "${o.id}" is ready with ${o.totalPanels} panels; the cast is on the screen`,c.length?`character sheets saved to ${c.join(`, `)}`:``,l.length?`no sheet for ${l.join(`; `)}: they will be drawn from their description`:``,s?``:Q(`storyboard`)].filter(Boolean).join(`; `),instructions:M(o),instructionsRequired:!0,sequence:Ze(o)}}function nt(e,t,n){let r=[$e(e.style,`Panel ${t.panel} of ${e.totalPanels} of the illustrated story "${e.title}". ${t.imagePrompt}`)],i=0,a=n.map(({character:e,hasSheet:t})=>t?`${e.name} is the character in reference image ${++i}`:`${e.name}: ${e.description}`);return a.length&&r.push(`${a.join(`. `)}.`),i&&r.push(`Draw each character exactly as in their reference image: the same face, hair, clothes and colors. Use the reference images only for how the characters look, not for their pose or the background.`),r.push(`No captions, speech bubbles or other text in the picture.`),r.join(` `)}async function rt(e,t,n){let r=[],i=[];for(let a of n.characters){let n=t.characters.find(e=>x(e.name,a));if(!n)r.push(a);else if(!i.some(e=>e.character===n)){let[t]=n.imagePath?await W(e,[n.imagePath]):[];i.push({character:n,sheet:t??null})}}let a=i.map(({sheet:e})=>e).filter(e=>!!e),o=nt(t,n,i.map(({character:e,sheet:t})=>({character:e,hasSheet:!!t}))),s=await G(e,o,a),{imageData:c,imagePath:l}=z(s);if(!c)return{...s,sequence:null};let u=n.panel<t.totalPanels&&n.choices.length>=2?n.choices:[],d=await Z(e.files?.artifacts,X,t.id,e=>{let r=e??t;return u.length&&(r.interactive=!0),r.panels[String(n.panel)]={caption:n.caption,characters:i.map(({character:e})=>e.name),imagePrompt:n.imagePrompt,...l&&{imagePath:l},...u.length&&{choices:u}},r});return{data:{imageData:c,...l&&{imagePath:l},prompt:o,storyboardId:t.id,panel:n.panel,totalPanels:t.totalPanels,caption:n.caption,...u.length&&{choices:u}},title:n.caption||`Panel ${n.panel}`,message:[`panel ${n.panel} of ${t.totalPanels} is on the screen`,l?`saved to ${l}`:``,r.length?`not in the cast, so drawn from the prompt alone: ${r.join(`, `)}`:``,d?``:Q(`storyboard`)].filter(Boolean).join(`; `),instructions:F(t,n.panel,u),instructionsRequired:!0,sequence:Qe(t,n.panel,u)}}var it=K(()=>({repeats:Ve(),awaitingChoice:new Map}));async function $(e,t){let n=P(t);if(!n)return{message:N,sequence:null};let{storyboardId:r,panel:i}=n,{repeats:a,awaitingChoice:o}=it(e),s=o.get(r);if(s&&i>s.panel&&!H(e,s.shownAt))return console.info(`[sequence] holding panel ${i} for the user`),{message:s.shownAt===1/0?`panel ${i} was not shown: panel ${s.panel} is still being drawn. Wait for it before going on.`:`panel ${i} was not shown: the user hasn't picked a choice yet, and panel ${s.panel} is still on the screen. Wait for their answer.`,cancelled:!0};let c=a.claim(JSON.stringify(n));if(`earlier`in c)return await c.earlier?{message:`panel ${i} is already on the screen`,cancelled:!0}:$(e,t);let{settle:l}=c,u={panel:i,shownAt:1/0},d=o.get(r);o.set(r,u);let f=()=>o.get(r)===u,p=!1;try{let t=await ze(e.files?.artifacts,X,r);if(!t)return{message:`there is no storyboard "${r}"; call defineStoryboard first`,sequence:null};if(i>t.totalPanels)return{message:`storyboard "${t.id}" has ${t.totalPanels} panels`,sequence:null};let a=await rt(e,t,n);p=!!z(a).imageData;let s=a.data;return p&&f()&&(s?.choices?.length?o.set(r,{panel:i,shownAt:Date.now()}):o.delete(r)),a}finally{l(p),!p&&f()&&(d?o.set(r,d):o.delete(r))}}var at={toolDefinition:A,generatingMessage:`Making the slide...`,isEnabled:()=>!0,execute:(e,t)=>Xe(e,t)},ot={toolDefinition:I,generatingMessage:`Drawing the characters...`,isEnabled:()=>!0,execute:(e,t)=>tt(e,t)},st={toolDefinition:L,generatingMessage:`Drawing the panel...`,isEnabled:()=>!0,execute:(e,t)=>$(e,t)};Object.defineProperty(exports,"A",{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,"B",{enumerable:!0,get:function(){return ne}}),Object.defineProperty(exports,"C",{enumerable:!0,get:function(){return ve}}),Object.defineProperty(exports,"D",{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,"E",{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,"F",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"I",{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,"L",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"M",{enumerable:!0,get:function(){return ce}}),Object.defineProperty(exports,"N",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"O",{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,"P",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"R",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"S",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"T",{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return X}}),Object.defineProperty(exports,"b",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return le}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return pe}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return fe}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return Y}}),Object.defineProperty(exports,"j",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"k",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return ue}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return st}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return ze}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return at}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return J}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return ot}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return de}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,"w",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"x",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"y",{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,"z",{enumerable:!0,get:function(){return t}});
|