@gui-chat-plugin/sequence 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +45 -11
- package/dist/core/chartSlide.d.ts +14 -0
- package/dist/core/definitions.d.ts +24 -9
- package/dist/core/host.d.ts +5 -2
- package/dist/core/index.d.ts +2 -0
- package/dist/core/markdownSlide.d.ts +9 -0
- package/dist/core/slideHtml.d.ts +3 -1
- package/dist/core-DNUfOXPo.cjs +232 -0
- package/dist/{core-Dsp7rj6a.js → core-g9C3DDiR.js} +342 -157
- 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 +48 -48
- package/package.json +5 -1
- package/dist/core-Bl4BX0HI.cjs +0 -145
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
- **Chart slides**: `presentSlide` takes `chart`, a Chart.js configuration (`{ type, data,
|
|
6
|
+
options }`, as MulmoCast's chart beats take it), for numbers to compare, a trend or proportions.
|
|
7
|
+
The View draws it under the slide's title on a sandboxed page of its own
|
|
8
|
+
(`chartSlideDocument`), with the same kind of policy as an HTML slide's: Chart.js from jsDelivr,
|
|
9
|
+
checked by its hash, and the configuration as data. Chart.js's own types only
|
|
10
|
+
(`SLIDE_CHART_TYPES`). The parameter is JSON text: as an object without properties, Gemini
|
|
11
|
+
Live used it for 2 charts in 4 asked for, drawing the others in HTML; as JSON text, 3 in 3. An
|
|
12
|
+
object is read too.
|
|
13
|
+
- **Markdown slides with TeX math**: `presentSlide` takes `markdown`, with math between `$…$` or
|
|
14
|
+
`$$…$$`, for equations, definitions, short lists and small tables. `execute()` makes it an HTML
|
|
15
|
+
slide (`markdownSlideHtml`), so Views and movies show it as one; the record and the result keep
|
|
16
|
+
the text too, in `markdown`. Math is MathML, drawn by the browser with nothing to load, with the
|
|
17
|
+
thin spaces around function names and the matrix column gaps Chrome's MathML leaves out. The
|
|
18
|
+
slide's type shrinks until it fits. New dependencies: marked and KaTeX, imported only when a
|
|
19
|
+
Markdown slide is made.
|
|
20
|
+
- The prompt tells the model which kind a slide should be: a picture for a scene, a chart for
|
|
21
|
+
numbers, Markdown for math and text, HTML for designed layouts and diagrams.
|
|
22
|
+
- An HTML slide's page shrinks a box marked `data-fit` until it fits (its script's hash changed).
|
|
23
|
+
|
|
3
24
|
## 0.4.1
|
|
4
25
|
|
|
5
26
|
- **Slides are explained, not announced.** Asked only to "explain" the slide on the screen, OpenAI's
|
package/README.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
Tools that show things one step at a time, as pictures, for
|
|
4
4
|
[GUI Chat Protocol](https://github.com/receptron/gui-chat-protocol) hosts:
|
|
5
5
|
|
|
6
|
-
| Tool | Shows
|
|
7
|
-
| ------------------ |
|
|
8
|
-
| `presentSlide` | One slide of a spoken slideshow (mode `"presentation"`), or one step of a step-by-step guide the user follows along with (mode `"steps"`): a generated picture, or a slide in HTML |
|
|
9
|
-
| `defineStoryboard` | A story's cast: a reference sheet for each recurring character
|
|
10
|
-
| `presentPanel` | One panel of the story, with its characters drawn as in their sheets; an interactive story offers choices
|
|
6
|
+
| Tool | Shows |
|
|
7
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
8
|
+
| `presentSlide` | One slide of a spoken slideshow (mode `"presentation"`), or one step of a step-by-step guide the user follows along with (mode `"steps"`): a generated picture, a chart, or a slide in Markdown (with TeX math) or HTML |
|
|
9
|
+
| `defineStoryboard` | A story's cast: a reference sheet for each recurring character |
|
|
10
|
+
| `presentPanel` | One panel of the story, with its characters drawn as in their sheets; an interactive story offers choices |
|
|
11
11
|
|
|
12
12
|
From MulmoChat and MulmoGlass, tested there by voice on OpenAI Realtime, Gemini Live and Grok, and
|
|
13
13
|
in text chat.
|
|
@@ -60,11 +60,45 @@ overlap (a host that runs `execute()` on a server gets them as concurrent reques
|
|
|
60
60
|
claimed before anything is awaited, so an identical one waits for it and a later step is held while
|
|
61
61
|
it is drawn.
|
|
62
62
|
|
|
63
|
-
##
|
|
64
|
-
|
|
65
|
-
A slide is a generated picture (`imagePrompt`)
|
|
66
|
-
picture for a scene or an object
|
|
67
|
-
|
|
63
|
+
## Slides that aren't pictures
|
|
64
|
+
|
|
65
|
+
A slide is a generated picture (`imagePrompt`), a chart (`chart`), Markdown (`markdown`) or HTML
|
|
66
|
+
(`html`), the model's choice per slide: a picture for a scene or an object; a chart for numbers to
|
|
67
|
+
compare, a trend or proportions; Markdown for equations (TeX math), definitions, short lists and
|
|
68
|
+
small tables; HTML for designed layouts, comparisons, timelines and diagrams. Picture models draw
|
|
69
|
+
words, numbers and math badly. A slide sent with more than one is one kind: `html` first, then
|
|
70
|
+
`markdown`, then `chart`, then the picture.
|
|
71
|
+
|
|
72
|
+
### Markdown slides
|
|
73
|
+
|
|
74
|
+
`markdown` is GitHub-flavored Markdown with TeX math between `$…$` (inline) or `$$…$$` (a
|
|
75
|
+
displayed equation), `\(…\)` and `\[…\]` too. A dollar starts math only before a non-space and
|
|
76
|
+
ends it only after one, not before a digit, so "$5 and $10" stays money; `\$` is a dollar sign.
|
|
77
|
+
`execute()` makes it into an HTML slide (`markdownSlideHtml`, with [marked](https://marked.js.org)
|
|
78
|
+
and [KaTeX](https://katex.org), imported only when a Markdown slide is made), so it is shown, saved
|
|
79
|
+
and made into a movie as one: the result and the record have the HTML in `html` and the text as the
|
|
80
|
+
model wrote it in `markdown`. Math is MathML (KaTeX's `mathml` output), which the browser draws with
|
|
81
|
+
no stylesheet or font to load: it works offline and in a movie as on the screen. Function names
|
|
82
|
+
(`\log`, `\sin`) get TeX's thin spaces, and matrix cells padding, which Chrome's MathML doesn't give
|
|
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 (the View's; a movie keeps the first size) shrinks it until it fits.
|
|
85
|
+
|
|
86
|
+
### Chart slides
|
|
87
|
+
|
|
88
|
+
`chart` is a [Chart.js](https://www.chartjs.org) configuration, `{ type, data, options }`, as
|
|
89
|
+
MulmoCast's `chart` beats take it (their `chartData`), of one of Chart.js's own types
|
|
90
|
+
(`SLIDE_CHART_TYPES`: bar, line, pie, doughnut, radar, polarArea, scatter, bubble). The tool advertises it as JSON text (an object without properties, Gemini Live
|
|
91
|
+
used for 2 charts in 4 asked for; JSON text for 3 in 3), and an object is read too; one of another type, or without `data`, is refused, and the model is told
|
|
92
|
+
why. The record and the result keep it in `chart`. `chartSlideDocument(title, chart)` (core entry)
|
|
93
|
+
makes the page: the slide's title over the chart, which Chart.js (from jsDelivr, one version,
|
|
94
|
+
checked by its hash) draws growing into place. The configuration is in a JSON data block, so it
|
|
95
|
+
can't run anything, and its policy, like an HTML slide's, lets the page send nothing out and run no
|
|
96
|
+
script but its own and Chart.js. Offline, the page says the chart couldn't be drawn.
|
|
97
|
+
|
|
98
|
+
### HTML slides
|
|
99
|
+
|
|
100
|
+
The model writes the inside of `<body>` for a 1280x720 canvas, with
|
|
101
|
+
Tailwind CSS classes, and animates it with [MulmoCast](https://github.com/receptron/mulmocast-cli)'s The model writes the inside of `<body>` for a 1280x720 canvas, with
|
|
68
102
|
Tailwind CSS classes, and animates it with [MulmoCast](https://github.com/receptron/mulmocast-cli)'s
|
|
69
103
|
declarative `data-animation` attributes: `animate` (`data-opacity`, `data-translate-x/y`,
|
|
70
104
|
`data-scale`, `data-rotate`, `data-width`/`data-height`), `counter` and `typewriter`, each with
|
|
@@ -94,7 +128,7 @@ that an HTML step is on the screen; a guide step asked for again is then shown a
|
|
|
94
128
|
|
|
95
129
|
Saved through `files.artifacts`, with their pictures' paths:
|
|
96
130
|
|
|
97
|
-
- `slideshows/<id>.json`: `{ id, title, mode, totalSlides, slides: { "1": { title, imagePrompt, imagePath } } }`;
|
|
131
|
+
- `slideshows/<id>.json`: `{ id, title, mode, totalSlides, slides: { "1": { title, imagePrompt, imagePath } } }`; a slide that isn't a picture has an `imagePrompt` of `""` and no `imagePath`, and has `html` (an HTML slide), `html` and `markdown` (a Markdown slide) or `chart` (a chart slide)
|
|
98
132
|
- `storyboards/<id>.json`: `{ id, title, style, totalPanels, interactive, characters: [{ name, description, imagePath }], panels: { "1": { caption, characters, imagePrompt, imagePath, choices } } }`
|
|
99
133
|
|
|
100
134
|
IDs are 12 hex digits. The results name them, so a later tool call can refer to a slideshow or a
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The chart types a slide can have: Chart.js's own, with no plugin. */
|
|
2
|
+
export declare const SLIDE_CHART_TYPES: readonly ["bar", "line", "pie", "doughnut", "radar", "polarArea", "scatter", "bubble"];
|
|
3
|
+
/** A Chart.js configuration: `type`, `data` and optionally `options`. */
|
|
4
|
+
export type SlideChart = Record<string, unknown> & {
|
|
5
|
+
type: (typeof SLIDE_CHART_TYPES)[number];
|
|
6
|
+
data: Record<string, unknown>;
|
|
7
|
+
};
|
|
8
|
+
/** CHART_PAGE_SCRIPT's hash, which the policy allows (checked by a test). */
|
|
9
|
+
export declare const CHART_PAGE_SCRIPT_HASH = "sha256-TfZZxJX+yWgzYLy7zqsMa85OJEaC+zNLIpKLDZVnUqA=";
|
|
10
|
+
/** JSON that can sit in a <script> data block: no "<", so no "</script>"
|
|
11
|
+
* and no "<!--" can end it early. */
|
|
12
|
+
export declare const scriptSafeJson: (value: unknown) => string;
|
|
13
|
+
/** The page a chart slide is shown as. */
|
|
14
|
+
export declare function chartSlideDocument(title: string, chart: SlideChart): string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
|
+
import { type SlideChart } from "./chartSlide";
|
|
2
3
|
export declare const PRESENT_SLIDE = "presentSlide";
|
|
3
4
|
export declare const DEFINE_STORYBOARD = "defineStoryboard";
|
|
4
5
|
export declare const PRESENT_PANEL = "presentPanel";
|
|
@@ -13,16 +14,22 @@ export interface SlideArgs {
|
|
|
13
14
|
slide: number;
|
|
14
15
|
totalSlides: number;
|
|
15
16
|
title: string;
|
|
16
|
-
/** The picture's prompt; "" for
|
|
17
|
+
/** The picture's prompt; "" for a slide that isn't a picture. */
|
|
17
18
|
imagePrompt: string;
|
|
18
|
-
/** An HTML slide's body
|
|
19
|
+
/** An HTML slide's body. */
|
|
19
20
|
html?: string;
|
|
21
|
+
/** A Markdown slide's text, with TeX math. */
|
|
22
|
+
markdown?: string;
|
|
23
|
+
/** A chart slide's Chart.js configuration. */
|
|
24
|
+
chart?: SlideChart;
|
|
20
25
|
mode: SlideMode;
|
|
21
26
|
}
|
|
22
|
-
export declare const SLIDE_ARGS_ERROR = "presentSlide needs slide and totalSlides (whole numbers, slide from 1 to totalSlides), and
|
|
23
|
-
export declare const SLIDE_HTML_TOO_LONG = "presentSlide's
|
|
27
|
+
export declare const SLIDE_ARGS_ERROR = "presentSlide needs slide and totalSlides (whole numbers, slide from 1 to totalSlides), and one of imagePrompt, html, markdown or chart";
|
|
28
|
+
export declare const SLIDE_HTML_TOO_LONG = "presentSlide's slide is too long: keep its html, markdown or chart under 60000 characters";
|
|
29
|
+
export declare const SLIDE_CHART_INVALID: string;
|
|
24
30
|
/** The slide arguments, or what is wrong with them (said to the model). A
|
|
25
|
-
* slide
|
|
31
|
+
* slide is one kind: html first, then markdown, then chart, then a
|
|
32
|
+
* picture, whatever else was sent with it. */
|
|
26
33
|
export declare function parseSlideArgs(args: Record<string, unknown>): SlideArgs | string;
|
|
27
34
|
/** What the model does once a slide is on the screen. */
|
|
28
35
|
export declare const slideShownInstructions: ({ slide, totalSlides, title, mode, }: SlideArgs) => string;
|
|
@@ -35,19 +42,27 @@ export interface Slideshow {
|
|
|
35
42
|
totalSlides: number;
|
|
36
43
|
slides: Record<string, {
|
|
37
44
|
title: string;
|
|
38
|
-
/** "" for
|
|
45
|
+
/** "" for a slide that isn't a picture. */
|
|
39
46
|
imagePrompt: string;
|
|
40
47
|
imagePath?: string;
|
|
41
|
-
/** An HTML slide's body (slideHtmlDocument makes it a page)
|
|
48
|
+
/** An HTML slide's body (slideHtmlDocument makes it a page); for a
|
|
49
|
+
* Markdown slide, the HTML made of it. */
|
|
42
50
|
html?: string;
|
|
51
|
+
/** A Markdown slide's text, as the model wrote it. */
|
|
52
|
+
markdown?: string;
|
|
53
|
+
/** A chart slide's Chart.js configuration (chartSlideDocument). */
|
|
54
|
+
chart?: SlideChart;
|
|
43
55
|
}>;
|
|
44
56
|
}
|
|
45
|
-
/** A shown slide's result data: a picture,
|
|
57
|
+
/** A shown slide's result data: a picture, an HTML slide's body (a
|
|
58
|
+
* Markdown slide's included, with its markdown), or a chart. */
|
|
46
59
|
export interface SlideData {
|
|
47
60
|
imageData?: string;
|
|
48
61
|
imagePath?: string;
|
|
49
62
|
html?: string;
|
|
50
|
-
|
|
63
|
+
markdown?: string;
|
|
64
|
+
chart?: SlideChart;
|
|
65
|
+
/** The picture's prompt; "" for a slide that isn't a picture. */
|
|
51
66
|
prompt: string;
|
|
52
67
|
slideshowId: string;
|
|
53
68
|
slide: number;
|
package/dist/core/host.d.ts
CHANGED
|
@@ -13,11 +13,14 @@ export declare function imageOf(result: ToolResult | null | undefined): {
|
|
|
13
13
|
};
|
|
14
14
|
/** An HTML slide's body, when the result is one. */
|
|
15
15
|
export declare function htmlOf(result: ToolResult | null | undefined): string;
|
|
16
|
-
/**
|
|
16
|
+
/** A chart slide's configuration as text, when the result is one. */
|
|
17
|
+
export declare function chartOf(result: ToolResult | null | undefined): string;
|
|
18
|
+
/** Whether a result shows something: a picture, an HTML slide (a Markdown
|
|
19
|
+
* slide is one) or a chart. */
|
|
17
20
|
export declare const shownContent: (result: ToolResult) => boolean;
|
|
18
21
|
/** Whether two results show the same picture: by its saved path when both
|
|
19
22
|
* have one (a host may send the current result without its image data),
|
|
20
|
-
* else by the picture itself, or the same HTML slide. A host that sends the
|
|
23
|
+
* else by the picture itself, or the same HTML slide or chart. A host that sends the
|
|
21
24
|
* current result by its path only can't tell an HTML slide is on the
|
|
22
25
|
* screen: one asked for again is shown again. */
|
|
23
26
|
export declare function samePicture(a: ToolResult | null | undefined, b: ToolResult | null | undefined): boolean;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -5,5 +5,7 @@ export declare const defineStoryboardPluginCore: SequencePluginCore;
|
|
|
5
5
|
export declare const presentPanelPluginCore: SequencePluginCore;
|
|
6
6
|
export * from "./definitions";
|
|
7
7
|
export { MAX_SLIDE_HTML, SLIDE_ANIMATION_KINDS, SLIDE_AUTO_END_SECONDS, SLIDE_CSS_ANIMATIONS_FINISHED, SLIDE_HEIGHT, SLIDE_WIDTH, slideHtmlDocument, } from "./slideHtml";
|
|
8
|
+
export { SLIDE_CHART_TYPES, chartSlideDocument, type SlideChart, } from "./chartSlide";
|
|
9
|
+
export { markdownSlideHtml } from "./markdownSlide";
|
|
8
10
|
export { SLIDESHOWS_DIR, STORYBOARDS_DIR, loadRecord } from "./records";
|
|
9
11
|
export type { SequenceContext } from "./host";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** The box's first font size, in px, from how much the slide says: a few
|
|
2
|
+
* lines are large, many smaller. The page shrinks it further when the slide
|
|
3
|
+
* still doesn't fit; a movie, which doesn't run the page's script, has this
|
|
4
|
+
* one. */
|
|
5
|
+
export declare function markdownFontSize(markdown: string): number;
|
|
6
|
+
/** KaTeX's MathML, spaced as TeX spaces it where Chrome doesn't. */
|
|
7
|
+
export declare const spaceFunctionNames: (mathml: string) => string;
|
|
8
|
+
/** A Markdown slide's HTML: the body of an HTML slide. */
|
|
9
|
+
export declare function markdownSlideHtml(markdown: string): Promise<string>;
|
package/dist/core/slideHtml.d.ts
CHANGED
|
@@ -12,7 +12,9 @@ export declare const SLIDE_ANIMATION_KINDS: readonly ["animate", "counter", "typ
|
|
|
12
12
|
* doesn't know. */
|
|
13
13
|
export declare const SLIDE_AUTO_END_SECONDS = 8;
|
|
14
14
|
/** PAGE_SCRIPT's hash, which the policy allows (checked by a test). */
|
|
15
|
-
export declare const PAGE_SCRIPT_HASH = "sha256-
|
|
15
|
+
export declare const PAGE_SCRIPT_HASH = "sha256-bNBxv6ehYc2PHOgmTReFSYsrxiGvq6aqqg9m5RwS7Lc=";
|
|
16
|
+
/** Text as HTML text or an attribute's value. */
|
|
17
|
+
export declare const escapeHtml: (text: string) => string;
|
|
16
18
|
/** The slide's HTML, made safe to show:
|
|
17
19
|
* - the tags that act on the page from outside the body are plain text:
|
|
18
20
|
* <meta> (a refresh navigates the frame, a policy of its own), <base> and
|
|
@@ -0,0 +1,232 @@
|
|
|
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
|
+
(() => {
|
|
3
|
+
const EASE = {
|
|
4
|
+
linear: (t) => t,
|
|
5
|
+
easeIn: (t) => t * t,
|
|
6
|
+
easeOut: (t) => 1 - (1 - t) * (1 - t),
|
|
7
|
+
easeInOut: (t) => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2),
|
|
8
|
+
};
|
|
9
|
+
const AUTO_END = 8;
|
|
10
|
+
const TRANSFORMS = [
|
|
11
|
+
["data-translate-x", "translateX", "px"],
|
|
12
|
+
["data-translate-y", "translateY", "px"],
|
|
13
|
+
["data-scale", "scale", ""],
|
|
14
|
+
["data-rotate", "rotate", "deg"],
|
|
15
|
+
["data-rotate-x", "rotateX", "deg"],
|
|
16
|
+
["data-rotate-y", "rotateY", "deg"],
|
|
17
|
+
["data-rotate-z", "rotateZ", "deg"],
|
|
18
|
+
];
|
|
19
|
+
const STYLES = [
|
|
20
|
+
["data-opacity", "opacity", ""],
|
|
21
|
+
["data-width", "width", "px"],
|
|
22
|
+
["data-height", "height", "px"],
|
|
23
|
+
];
|
|
24
|
+
const number = (value, fallback) => {
|
|
25
|
+
const n = Number(value);
|
|
26
|
+
return value !== null && value.trim() !== "" && Number.isFinite(n) ? n : fallback;
|
|
27
|
+
};
|
|
28
|
+
const range = (value, unit) => {
|
|
29
|
+
if (value === null) return null;
|
|
30
|
+
const parts = value.split(",").map((part) => part.trim());
|
|
31
|
+
const from = Number(parts[0]);
|
|
32
|
+
const to = Number(parts[1]);
|
|
33
|
+
if (!Number.isFinite(from) || !Number.isFinite(to)) return null;
|
|
34
|
+
return { from, to, unit: parts[2] || unit };
|
|
35
|
+
};
|
|
36
|
+
const ranges = (el, table) =>
|
|
37
|
+
table
|
|
38
|
+
.map(([attr, name, unit]) => [name, range(el.getAttribute(attr), unit)])
|
|
39
|
+
.filter(([, r]) => r);
|
|
40
|
+
let entries = [];
|
|
41
|
+
const collect = () => {
|
|
42
|
+
entries = [...document.querySelectorAll("[data-animation]")].map((el) => {
|
|
43
|
+
const kind = el.getAttribute("data-animation");
|
|
44
|
+
const end = el.getAttribute("data-end");
|
|
45
|
+
const entry = {
|
|
46
|
+
el,
|
|
47
|
+
kind,
|
|
48
|
+
start: number(el.getAttribute("data-start"), 0),
|
|
49
|
+
end: end === null || end.trim() === "auto" ? null : number(end, null),
|
|
50
|
+
ease: EASE[el.getAttribute("data-easing")] || EASE.linear,
|
|
51
|
+
};
|
|
52
|
+
if (kind === "animate") {
|
|
53
|
+
entry.transforms = ranges(el, TRANSFORMS);
|
|
54
|
+
entry.styles = ranges(el, STYLES);
|
|
55
|
+
} else if (kind === "counter") {
|
|
56
|
+
entry.from = number(el.getAttribute("data-from"), 0);
|
|
57
|
+
entry.to = number(el.getAttribute("data-to"), 0);
|
|
58
|
+
entry.decimals = Math.max(0, Math.round(number(el.getAttribute("data-decimals"), 0)));
|
|
59
|
+
entry.prefix = el.getAttribute("data-prefix") || "";
|
|
60
|
+
entry.suffix = el.getAttribute("data-suffix") || "";
|
|
61
|
+
} else if (kind === "typewriter") {
|
|
62
|
+
entry.text = el.getAttribute("data-text") ?? el.textContent;
|
|
63
|
+
} else {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return entry;
|
|
67
|
+
}).filter(Boolean);
|
|
68
|
+
};
|
|
69
|
+
const seek = (t) => {
|
|
70
|
+
for (const e of entries) {
|
|
71
|
+
const end = e.end ?? AUTO_END;
|
|
72
|
+
const span = end - e.start;
|
|
73
|
+
const p = span > 0 ? Math.min(1, Math.max(0, (t - e.start) / span)) : t >= e.start ? 1 : 0;
|
|
74
|
+
const k = e.ease(p);
|
|
75
|
+
const at = (r) => r.from + (r.to - r.from) * k;
|
|
76
|
+
if (e.kind === "animate") {
|
|
77
|
+
if (e.transforms.length) {
|
|
78
|
+
e.el.style.transform = e.transforms.map(([name, r]) => name + "(" + at(r) + r.unit + ")").join(" ");
|
|
79
|
+
}
|
|
80
|
+
for (const [name, r] of e.styles) e.el.style[name] = at(r) + r.unit;
|
|
81
|
+
} else if (e.kind === "counter") {
|
|
82
|
+
e.el.textContent = e.prefix + (e.from + (e.to - e.from) * k).toFixed(e.decimals) + e.suffix;
|
|
83
|
+
} else {
|
|
84
|
+
e.el.textContent = e.text.slice(0, Math.floor(e.text.length * k));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
window.__slideSeek = seek;
|
|
89
|
+
const fit = () => {
|
|
90
|
+
for (const el of document.querySelectorAll("[data-fit]")) {
|
|
91
|
+
let size = parseFloat(getComputedStyle(el).fontSize);
|
|
92
|
+
while (size > 12 && (el.scrollHeight > el.clientHeight + 1 || el.scrollWidth > el.clientWidth + 1)) {
|
|
93
|
+
size -= 1;
|
|
94
|
+
el.style.fontSize = size + "px";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const play = () => {
|
|
99
|
+
const last = Math.max(0, ...entries.map((e) => e.end ?? AUTO_END));
|
|
100
|
+
const begun = performance.now();
|
|
101
|
+
const tick = (now) => {
|
|
102
|
+
const t = (now - begun) / 1000;
|
|
103
|
+
seek(t);
|
|
104
|
+
if (t <= last) requestAnimationFrame(tick);
|
|
105
|
+
};
|
|
106
|
+
requestAnimationFrame(tick);
|
|
107
|
+
};
|
|
108
|
+
// Collected once the body is parsed: at DOMContentLoaded, or earlier when
|
|
109
|
+
// the fallback shows the slide while a slow Tailwind still holds
|
|
110
|
+
// DOMContentLoaded back (the body is parsed by then: readyState is
|
|
111
|
+
// "interactive" before deferred scripts run). Played once both are done.
|
|
112
|
+
let collected = false;
|
|
113
|
+
let shown = false;
|
|
114
|
+
const init = () => {
|
|
115
|
+
if (collected || document.readyState === "loading") return;
|
|
116
|
+
collected = true;
|
|
117
|
+
collect();
|
|
118
|
+
seek(0);
|
|
119
|
+
if (shown) play();
|
|
120
|
+
};
|
|
121
|
+
const show = () => {
|
|
122
|
+
if (shown) return;
|
|
123
|
+
init();
|
|
124
|
+
fit();
|
|
125
|
+
shown = true;
|
|
126
|
+
document.documentElement.classList.add("ready");
|
|
127
|
+
if (collected) play();
|
|
128
|
+
};
|
|
129
|
+
const ready = () => requestAnimationFrame(() => requestAnimationFrame(show));
|
|
130
|
+
const check = () =>
|
|
131
|
+
document.querySelector("style:not([type]):not(#slide-base)") ? ready() : setTimeout(check, 30);
|
|
132
|
+
addEventListener("DOMContentLoaded", () => {
|
|
133
|
+
init();
|
|
134
|
+
check();
|
|
135
|
+
});
|
|
136
|
+
setTimeout(show, 1000);
|
|
137
|
+
addEventListener("click", (event) => {
|
|
138
|
+
if (event.target instanceof Element && event.target.closest("a, area")) event.preventDefault();
|
|
139
|
+
}, true);
|
|
140
|
+
})();`,l=[`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(`; `),u={"&":`&`,"<":`<`,">":`>`,'"':`"`},d=e=>e.replace(/[&<>"]/g,e=>u[e]??e),f=e=>e.replace(/<(?=(meta|base|link)\b)/gi,`<`).replace(/shadowroot/gi,`data-no-shadow-root`),p=`*, *::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; }`,ee=`
|
|
141
|
+
html, body { margin: 0; width: ${t}px; height: 720px; overflow: hidden; }
|
|
142
|
+
html:not(.ready) body { visibility: hidden; }
|
|
143
|
+
${p}`;function te(e){return`<!doctype html>
|
|
144
|
+
<html lang="en">
|
|
145
|
+
<head>
|
|
146
|
+
<meta charset="utf-8">
|
|
147
|
+
<meta http-equiv="Content-Security-Policy" content="${l}">
|
|
148
|
+
<style id="slide-base">${ee}</style>
|
|
149
|
+
<script>${c}<\/script>
|
|
150
|
+
<script defer src="${i}" integrity="${a}" crossorigin="anonymous"><\/script>
|
|
151
|
+
</head>
|
|
152
|
+
<body class="bg-white text-slate-900 font-sans antialiased">
|
|
153
|
+
${f(e)}
|
|
154
|
+
</body>
|
|
155
|
+
</html>`}var m=[`bar`,`line`,`pie`,`doughnut`,`radar`,`polarArea`,`scatter`,`bubble`],h=`https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js`,ne=`sha384-jb8JQMbMoBUzgWatfe6COACi2ljcDdZQ2OxczGA3bGNeWe+6DChMTBJemed7ZnvJ`,re=`
|
|
156
|
+
(() => {
|
|
157
|
+
const fail = (why) => {
|
|
158
|
+
document.getElementById("chart-error").textContent = why;
|
|
159
|
+
document.documentElement.classList.add("failed");
|
|
160
|
+
};
|
|
161
|
+
addEventListener("DOMContentLoaded", () => {
|
|
162
|
+
if (!window.Chart) return fail("The chart couldn't be drawn: Chart.js didn't load.");
|
|
163
|
+
let config;
|
|
164
|
+
try {
|
|
165
|
+
config = JSON.parse(document.getElementById("chart-config").textContent);
|
|
166
|
+
} catch {
|
|
167
|
+
return fail("The chart couldn't be drawn: its configuration isn't valid.");
|
|
168
|
+
}
|
|
169
|
+
Chart.defaults.font.size = 22;
|
|
170
|
+
Chart.defaults.color = "#334155";
|
|
171
|
+
Chart.defaults.animation.duration = 1200;
|
|
172
|
+
config.options = { ...config.options, responsive: true, maintainAspectRatio: false };
|
|
173
|
+
try {
|
|
174
|
+
new Chart(document.getElementById("chart"), config);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
fail("The chart couldn't be drawn: " + (error && error.message ? error.message : error));
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
})();`,ie=[`default-src 'none'`,`script-src 'sha256-TfZZxJX+yWgzYLy7zqsMa85OJEaC+zNLIpKLDZVnUqA=' ${h}`,`style-src 'unsafe-inline'`,`img-src data: blob:`,`connect-src 'none'`,`form-action 'none'`,`base-uri 'none'`].join(`; `),ae=`
|
|
180
|
+
html, body { margin: 0; width: ${t}px; height: 720px; overflow: hidden; background: #ffffff; color: #0f172a; font-family: ui-sans-serif, system-ui, sans-serif; }
|
|
181
|
+
body { box-sizing: border-box; padding: 40px 64px 48px; display: flex; flex-direction: column; gap: 24px; }
|
|
182
|
+
h1 { margin: 0; font-size: 52px; font-weight: 800; color: #1e3a8a; line-height: 1.15; text-align: center; }
|
|
183
|
+
.chart { position: relative; flex: 1; min-height: 0; }
|
|
184
|
+
#chart-error { display: none; margin: auto; font-size: 28px; color: #b91c1c; }
|
|
185
|
+
.failed .chart { display: none; }
|
|
186
|
+
.failed #chart-error { display: block; }`,oe=e=>JSON.stringify(e).replace(/</g,`\\u003c`);function se(e,t){return`<!doctype html>
|
|
187
|
+
<html lang="en">
|
|
188
|
+
<head>
|
|
189
|
+
<meta charset="utf-8">
|
|
190
|
+
<meta http-equiv="Content-Security-Policy" content="${ie}">
|
|
191
|
+
<style>${ae}</style>
|
|
192
|
+
<script>${re}<\/script>
|
|
193
|
+
<script defer src="${h}" integrity="${ne}" crossorigin="anonymous"><\/script>
|
|
194
|
+
<script type="application/json" id="chart-config">${oe(t)}<\/script>
|
|
195
|
+
</head>
|
|
196
|
+
<body>
|
|
197
|
+
${e?`<h1>${d(e)}</h1>`:``}
|
|
198
|
+
<div class="chart"><canvas id="chart"></canvas></div>
|
|
199
|
+
<p id="chart-error"></p>
|
|
200
|
+
</body>
|
|
201
|
+
</html>`}var g=`presentSlide`,_=`defineStoryboard`,v=`presentPanel`,ce=4,le=12,ue=3,de=/^[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),fe=`presentSlide needs slide and totalSlides (whole numbers, slide from 1 to totalSlides), and one of imagePrompt, html, markdown or chart`,C=`presentSlide's slide is too long: keep its html, markdown or chart under ${r} characters`,w=`presentSlide's chart must be a Chart.js configuration: an object with type (${m.join(`, `)}), data ({ labels, datasets }) and optionally options`;function pe(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!m.some(e=>e===n)||t.options!==void 0&&!y(t.options)?null:t}function me(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 fe;let c=s?pe(e.chart):null;return s&&!c?w:Math.max(a.length,o.length,c?JSON.stringify(c).length:0)>6e4?C:{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 he=`teach its subject in at least two sentences, as a presenter speaking to the audience. 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.`,T=({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: ${he} Then, in this same reply and without waiting for the user, call presentSlide for slide ${e+1}. 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: ${he} Then wrap up the slideshow.`},ge=[`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(` `),_e=`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 \\$.`,ve=`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 ${m.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.`,ye=`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. 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.`,E={type:`function`,name:g,prompt:ye,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:ge},markdown:{type:`string`,description:_e},chart:{type:`string`,description:ve},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 D(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 O=({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.`,k=`presentPanel needs a storyboardId, a panel number (from 1) and an imagePrompt`;function A(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 j(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 M=`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.`,N={type:`function`,name:_,prompt:M,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`]}},P={type:`function`,name:v,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`]}},F=e=>typeof e==`object`&&!!e&&!Array.isArray(e),be=e=>F(e)&&typeof e.message==`string`;function I(e){let t=F(e?.data)?e.data:{};return{...typeof t.imageData==`string`&&{imageData:t.imageData},...typeof t.imagePath==`string`&&{imagePath:t.imagePath}}}function L(e){let t=F(e?.data)?e.data:{};return typeof t.html==`string`?t.html:``}function R(e){let t=F(e?.data)?e.data:{};return F(t.chart)?JSON.stringify(t.chart):``}var xe=e=>!!I(e).imageData||!!L(e)||!!R(e);function Se(e,t){let n=I(e),r=I(t);if(n.imagePath&&r.imagePath)return n.imagePath===r.imagePath;if(n.imageData)return n.imageData===r.imageData;let i=L(e);if(i)return i===L(t);let a=R(e);return!!a&&a===R(t)}var z=(e,t)=>e.userSpokeAt===void 0||e.userSpokeAt>t,B=()=>crypto.randomUUID().replace(/-/g,``).slice(0,12),Ce=`artifacts/`;async function V(e,t){let n=e.files?.artifacts;if(!e.app?.editImages||!n)return[];let r=[];for(let e of t)if(e.startsWith(Ce))try{await n.exists(e.slice(10))&&r.push(e)}catch{}return r}async function H(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 be(e)?e:{message:`image generation returned an unrecognized result`}}catch(e){return{message:`image generation failed: ${e instanceof Error?e.message:String(e)}`}}}var we=50;function U(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>we){let e=t.keys().next().value;e!==void 0&&t.delete(e)}return i}}var W=`slide-md`,Te=`
|
|
202
|
+
.${W} { box-sizing: border-box; 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; }
|
|
203
|
+
.${W} > * { margin: 0; }
|
|
204
|
+
.${W} h1 { font-size: 1.9em; font-weight: 800; color: #1e3a8a; line-height: 1.15; }
|
|
205
|
+
.${W} h2 { font-size: 1.5em; font-weight: 700; color: #1e40af; line-height: 1.2; }
|
|
206
|
+
.${W} h3 { font-size: 1.2em; font-weight: 700; color: #334155; }
|
|
207
|
+
.${W} ul, .${W} ol { padding-left: 1.3em; display: flex; flex-direction: column; gap: 0.3em; }
|
|
208
|
+
.${W} ul { list-style: disc; }
|
|
209
|
+
.${W} ol { list-style: decimal; }
|
|
210
|
+
.${W} li > ul, .${W} li > ol { margin-top: 0.3em; font-size: 0.9em; }
|
|
211
|
+
.${W} li::marker { color: #2563eb; }
|
|
212
|
+
.${W} strong { font-weight: 700; color: #1e3a8a; }
|
|
213
|
+
.${W} em { font-style: italic; }
|
|
214
|
+
.${W} a { color: #2563eb; text-decoration: underline; }
|
|
215
|
+
.${W} code { font-family: ui-monospace, monospace; font-size: 0.85em; background: #f1f5f9; border-radius: 0.2em; padding: 0.05em 0.3em; }
|
|
216
|
+
.${W} pre { background: #0f172a; color: #e2e8f0; border-radius: 0.4em; padding: 0.6em 0.8em; font-size: 0.75em; overflow: hidden; }
|
|
217
|
+
.${W} pre code { background: none; padding: 0; color: inherit; }
|
|
218
|
+
.${W} blockquote { border-left: 0.2em solid #93c5fd; padding-left: 0.8em; color: #475569; font-style: italic; }
|
|
219
|
+
.${W} table { border-collapse: collapse; font-size: 0.85em; align-self: flex-start; }
|
|
220
|
+
.${W} th, .${W} td { border: 1px solid #cbd5e1; padding: 0.25em 0.7em; text-align: left; }
|
|
221
|
+
.${W} th { background: #eff6ff; font-weight: 700; }
|
|
222
|
+
.${W} hr { border: 0; border-top: 2px solid #e2e8f0; }
|
|
223
|
+
.${W} img { max-width: 100%; max-height: 60%; }
|
|
224
|
+
.${W} .math-display { display: block; text-align: center; font-size: 1.15em; }
|
|
225
|
+
.${W} math { font-family: "Latin Modern Math", "STIX Two Math", "Cambria Math", math; }
|
|
226
|
+
.${W} mtd { padding: 0.1em 0.4em; }
|
|
227
|
+
.${W} mtd:first-child { padding-left: 0; }
|
|
228
|
+
.${W} mtd:last-child { padding-right: 0; }
|
|
229
|
+
.${W} .math-error { color: #b91c1c; font-family: ui-monospace, monospace; }`;function Ee(e){let t=e.split(`
|
|
230
|
+
`).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 De=/^[ \t]*(?:\$\$([\s\S]+?)\$\$|\\\[([\s\S]+?)\\\])[ \t]*(?:\n|$)/,Oe=/^(?:\$\$(.+?)\$\$|\$(?!\s)((?:\\.|[^\\$\n])+?)(?<!\s)\$(?!\d)|\\\((.+?)\\\))/;function ke(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=De.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=Oe.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 Ae=/(<\/mi>|<\/mn>|<\/mrow>|<mo[^>]*>[)\]]<\/mo>)?(<mi(?: mathvariant="normal")?>[A-Za-z]{2,}<\/mi>)<mo>\u2061<\/mo>/g,je=`<mspace width="0.1667em"></mspace>`,Me=e=>e.replace(Ae,(e,t,n)=>`${t?t+je:``}${n}<mo lspace="0" rspace="0.1667em">\u2061</mo>`);async function G(e){let[{Marked:t},{default:n}]=await Promise.all([import(`marked`),import(`katex`)]),r=(e,t)=>{try{return Me(n.renderToString(e,{output:`mathml`,displayMode:t,throwOnError:!0}))}catch{return`<code class="math-error">${d(e)}</code>`}},i=new t({gfm:!0,breaks:!1});i.use({extensions:ke(r)});let a=await i.parse(e);return`<style>${Te}</style>
|
|
231
|
+
<div class="${W}" data-fit style="font-size: ${Ee(e)}px">
|
|
232
|
+
${a}</div>`}var K=`slideshows`,q=`storyboards`,J={},Y=new WeakMap,X=new WeakMap;function Ne(e){let t=Y.get(e);return t||Y.set(e,t=new Map),t}function Pe(t,n){let r=X.get(t);r||X.set(t,r=new Map);let i=r.get(n);return i||r.set(n,i=(0,e.createSerialLock)()),i}var Fe=(e,t)=>`${e}/${t}.json`;async function Ie(e,t){let n=Ne(e??J),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 Le(e,t,n){return de.test(n)?Ie(e,Fe(t,n)):null}async function Z(e,t,n,r){let i=Fe(t,n),a=e??J;return Pe(a,i)(async()=>{let t=r(await Ie(e,i));if(Ne(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`,Re=6e4;function ze(){let e=new Map;return{claim:t=>{let n=Date.now();for(let[t,r]of e)n-r.at>Re&&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 Be({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 Ve=(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]),He=U(()=>({latest:{presentation:null,steps:null},lastShown:null,repeats:ze()}));function Ue(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:B(),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 $(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:T(n),instructionsRequired:!0,sequence:Be(n)}}function We(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 Ge(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 G(n.markdown),markdown:n.markdown,prompt:``};let r=t.slides.get(n.slide-1),i=n.mode===`steps`?I(r).imagePath:void 0,a=i?await V(e,[i]):[],o=We(n,a.length>0),s=await H(e,o,a),{imageData:c,imagePath:l}=I(s);return c?{imageData:c,...l&&{imagePath:l},prompt:o}:{failed:{...s,sequence:null}}}async function Ke(e,t,n){let r=await Ge(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,K,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:T(n),instructionsRequired:!0,sequence:Be(n)}}async function qe(e,t){let n=me(t);if(typeof n==`string`)return{message:n,sequence:null};let r=He(e),i=Ue(r,n),a=n.mode===`steps`;if(a&&n.slide>i.current&&i.current>0&&!z(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 Se(e.currentResult,o)?{message:`step ${n.slide} of ${n.totalSlides} is already on the screen`,cancelled:!0}:$(r,i,n,o);let s=r.repeats.claim(Ve(i.id,n));if(`earlier`in s){if(!await s.earlier)return qe(e,t);let a=i.slides.get(n.slide);return a&&r.lastShown!==i?$(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 Ke(e,i,n);return u=xe(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 Je=({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}"`}),Ye=({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}}),Xe=(e,t)=>e?`${e}. ${t}`:t,Ze=(e,{name:t,description:n})=>Xe(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 Qe(e,t){let n=D(t);if(typeof n==`string`)return{message:n,sequence:null};let r=await Promise.all(n.characters.map(t=>H(e,Ze(n.style,t)))),i=r.map(I),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:B(),...n,characters:n.characters.map((e,t)=>{let n=i[t]?.imagePath;return{...e,...n&&{imagePath:n}}}),panels:{}},s=await Z(e.files?.artifacts,q,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:O(o),instructionsRequired:!0,sequence:Je(o)}}function $e(e,t,n){let r=[Xe(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 et(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 V(e,[n.imagePath]):[];i.push({character:n,sheet:t??null})}}let a=i.map(({sheet:e})=>e).filter(e=>!!e),o=$e(t,n,i.map(({character:e,sheet:t})=>({character:e,hasSheet:!!t}))),s=await H(e,o,a),{imageData:c,imagePath:l}=I(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,q,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:j(t,n.panel,u),instructionsRequired:!0,sequence:Ye(t,n.panel,u)}}var tt=U(()=>({repeats:ze(),awaitingChoice:new Map}));async function nt(e,t){let n=A(t);if(!n)return{message:k,sequence:null};let{storyboardId:r,panel:i}=n,{repeats:a,awaitingChoice:o}=tt(e),s=o.get(r);if(s&&i>s.panel&&!z(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}:nt(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 Le(e.files?.artifacts,q,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 et(e,t,n);p=!!I(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 rt={toolDefinition:E,generatingMessage:`Making the slide...`,isEnabled:()=>!0,execute:(e,t)=>qe(e,t)},it={toolDefinition:N,generatingMessage:`Drawing the characters...`,isEnabled:()=>!0,execute:(e,t)=>Qe(e,t)},at={toolDefinition:P,generatingMessage:`Drawing the panel...`,isEnabled:()=>!0,execute:(e,t)=>nt(e,t)};Object.defineProperty(exports,"A",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"C",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"D",{enumerable:!0,get:function(){return me}}),Object.defineProperty(exports,"E",{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,"F",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"I",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"L",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"M",{enumerable:!0,get:function(){return se}}),Object.defineProperty(exports,"N",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"O",{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,"P",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"R",{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,"S",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"T",{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return q}}),Object.defineProperty(exports,"b",{enumerable:!0,get:function(){return fe}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return ue}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return le}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return K}}),Object.defineProperty(exports,"j",{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,"k",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return at}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return Le}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return rt}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return G}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return it}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return ce}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return ye}}),Object.defineProperty(exports,"w",{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,"x",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"y",{enumerable:!0,get:function(){return de}}),Object.defineProperty(exports,"z",{enumerable:!0,get:function(){return te}});
|