@gui-chat-plugin/sequence 0.2.0 → 0.3.1

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1
4
+
5
+ - **"HTML slides" go to presentSlide**: the prompt says that when the user asks for HTML slides,
6
+ presentSlide is the tool, one HTML slide per call, not one HTML page. OpenAI Realtime, asked to
7
+ "explain this article in HTML slides", wrote a single presentHtml page with its own slide
8
+ navigation (found in MulmoChat). With this and the hosts' presentHtml prompt, 6 of 6 runs in
9
+ MulmoChat and 3 of 3 in MulmoGlass made presentSlide HTML slides.
10
+
11
+ ## 0.3.0
12
+
13
+ - **HTML slides**: `presentSlide` takes `html` instead of `imagePrompt` for a slide designed in HTML
14
+ (words, numbers, lists, comparisons, diagrams), styled with Tailwind CSS v4 and animated, so it
15
+ builds up while the model explains it. The model picks per slide; a slideshow can mix them. The
16
+ View shows it at 1280x720, scaled to fit, in a sandboxed iframe (`sandbox="allow-scripts"`, no
17
+ network: `connect-src 'none'`, images from `data:`/`blob:` only; the slide's own scripts, event
18
+ handlers, `<meta>` refreshes and links don't run, since they could navigate the frame to a URL
19
+ carrying the slide). Nothing is drawn, so it needs
20
+ no image backend. `slideHtmlDocument(html)` makes the page, for a host that shows one elsewhere.
21
+ Records and results carry `html`; `imagePrompt` is `""` and `imagePath` absent for these slides.
22
+
3
23
  ## 0.2.0
4
24
 
5
25
  - **State per conversation**: the slideshow in progress, the steps and panels waiting for the
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"`) |
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, 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 |
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,34 @@ 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
+ ## HTML slides
64
+
65
+ A slide is a generated picture (`imagePrompt`) or HTML (`html`), the model's choice per slide: a
66
+ picture for a scene or an object, HTML for words, numbers, lists, comparisons and diagrams, which
67
+ picture models draw badly. The model writes the inside of `<body>` for a 1280x720 canvas, with
68
+ Tailwind CSS v4 classes and these entrance animations: `animate-fade-in`, `animate-fade-up`,
69
+ `animate-fade-down`, `animate-slide-in-left`, `animate-slide-in-right`, `animate-zoom-in`,
70
+ `animate-pop` and `animate-draw` (an SVG path drawing itself), staggered with
71
+ `[animation-delay:…]`. The slide's own scripts don't run: its animations are CSS.
72
+
73
+ `slideHtmlDocument(html)` (core entry) makes the page: Tailwind's browser build from jsDelivr (one
74
+ version, checked by its hash), the animations, and a Content Security Policy that lets it send
75
+ nothing out: `connect-src 'none'`; images, media and fonts only from `data:`/`blob:` URLs and
76
+ Google Fonts; and no script but the page's own and Tailwind's, so nothing can navigate the frame to
77
+ a URL carrying the page (a sandbox allows that, and CSP can't forbid it). The slide's `<meta>`,
78
+ `<base>` and `<link>` tags are made plain text (a refresh would navigate), and a link doesn't
79
+ navigate when clicked. The View shows it in an iframe with `sandbox="allow-scripts"` and no
80
+ `allow-same-origin`, so the page can't reach the host's pages, storage or cookies, scaled to fit. Tailwind is loaded from the network: offline, the slide
81
+ appears unstyled.
82
+
83
+ A host that sends `currentResult` by its saved picture's path only (as MulmoChat does) can't tell
84
+ that an HTML step is on the screen; a guide step asked for again is then shown again.
85
+
63
86
  ## Records
64
87
 
65
88
  Saved through `files.artifacts`, with their pictures' paths:
66
89
 
67
- - `slideshows/<id>.json`: `{ id, title, mode, totalSlides, slides: { "1": { title, imagePrompt, imagePath } } }`
90
+ - `slideshows/<id>.json`: `{ id, title, mode, totalSlides, slides: { "1": { title, imagePrompt, imagePath } } }`; an HTML slide has `html`, an `imagePrompt` of `""` and no `imagePath`
68
91
  - `storyboards/<id>.json`: `{ id, title, style, totalPanels, interactive, characters: [{ name, description, imagePath }], panels: { "1": { caption, characters, imagePrompt, imagePath, choices } } }`
69
92
 
70
93
  IDs are 12 hex digits. The results name them, so a later tool call can refer to a slideshow or a
@@ -13,12 +13,17 @@ export interface SlideArgs {
13
13
  slide: number;
14
14
  totalSlides: number;
15
15
  title: string;
16
+ /** The picture's prompt; "" for an HTML slide. */
16
17
  imagePrompt: string;
18
+ /** An HTML slide's body; absent for a picture. */
19
+ html?: string;
17
20
  mode: SlideMode;
18
21
  }
19
- export declare const SLIDE_ARGS_ERROR = "presentSlide needs slide and totalSlides (whole numbers, slide from 1 to totalSlides) and an imagePrompt";
20
- /** The slide arguments, or null when the model sent something else. */
21
- export declare function parseSlideArgs(args: Record<string, unknown>): SlideArgs | null;
22
+ export declare const SLIDE_ARGS_ERROR = "presentSlide needs slide and totalSlides (whole numbers, slide from 1 to totalSlides), and either an imagePrompt or html";
23
+ export declare const SLIDE_HTML_TOO_LONG = "presentSlide's html is too long: keep a slide under 60000 characters";
24
+ /** The slide arguments, or what is wrong with them (said to the model). A
25
+ * slide with html is an HTML slide, whatever imagePrompt says. */
26
+ export declare function parseSlideArgs(args: Record<string, unknown>): SlideArgs | string;
22
27
  /** What the model does once a slide is on the screen. */
23
28
  export declare const slideShownInstructions: ({ slide, totalSlides, title, mode, }: SlideArgs) => string;
24
29
  /** A slideshow as saved in artifacts/slideshows/<id>.json. */
@@ -30,14 +35,19 @@ export interface Slideshow {
30
35
  totalSlides: number;
31
36
  slides: Record<string, {
32
37
  title: string;
38
+ /** "" for an HTML slide. */
33
39
  imagePrompt: string;
34
40
  imagePath?: string;
41
+ /** An HTML slide's body (slideHtmlDocument makes it a page). */
42
+ html?: string;
35
43
  }>;
36
44
  }
37
- /** A shown slide's result data. */
45
+ /** A shown slide's result data: a picture, or an HTML slide's body. */
38
46
  export interface SlideData {
39
- imageData: string;
47
+ imageData?: string;
40
48
  imagePath?: string;
49
+ html?: string;
50
+ /** The picture's prompt; "" for an HTML slide. */
41
51
  prompt: string;
42
52
  slideshowId: string;
43
53
  slide: number;
@@ -11,9 +11,15 @@ export declare function imageOf(result: ToolResult | null | undefined): {
11
11
  imageData?: string;
12
12
  imagePath?: string;
13
13
  };
14
+ /** An HTML slide's body, when the result is one. */
15
+ export declare function htmlOf(result: ToolResult | null | undefined): string;
16
+ /** Whether a result shows something: a picture or an HTML slide. */
17
+ export declare const shownContent: (result: ToolResult) => boolean;
14
18
  /** Whether two results show the same picture: by its saved path when both
15
19
  * have one (a host may send the current result without its image data),
16
- * else by the picture itself. */
20
+ * else by the picture itself, or the same HTML slide. A host that sends the
21
+ * current result by its path only can't tell an HTML slide is on the
22
+ * screen: one asked for again is shown again. */
17
23
  export declare function samePicture(a: ToolResult | null | undefined, b: ToolResult | null | undefined): boolean;
18
24
  /** When the user spoke after `time` (ms since the epoch). A host that doesn't
19
25
  * say when the user spoke holds nothing. */
@@ -4,5 +4,6 @@ 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_ANIMATIONS, SLIDE_HEIGHT, SLIDE_WIDTH, slideHtmlDocument, } from "./slideHtml";
7
8
  export { SLIDESHOWS_DIR, STORYBOARDS_DIR, loadRecord } from "./records";
8
9
  export type { SequenceContext } from "./host";
@@ -0,0 +1,23 @@
1
+ export declare const SLIDE_WIDTH = 1280;
2
+ export declare const SLIDE_HEIGHT = 720;
3
+ /** The most HTML a slide may have, in characters. */
4
+ export declare const MAX_SLIDE_HTML = 60000;
5
+ /** The animations every slide has, as `animate-<name>` classes. They keep
6
+ * their first frame while delayed (fill mode both), so an element with a
7
+ * delay stays hidden until its turn. */
8
+ export declare const SLIDE_ANIMATIONS: readonly ["fade-in", "fade-up", "fade-down", "slide-in-left", "slide-in-right", "zoom-in", "pop", "draw"];
9
+ /** PAGE_SCRIPT's hash, which the policy allows (checked by a test). */
10
+ export declare const PAGE_SCRIPT_HASH = "sha256-MZyL5rvWmIVxl3WUuDU5BqzPJGR2/fV/YPh01MNyUoM=";
11
+ /** The slide's HTML, made safe to show:
12
+ * - the tags that act on the page from outside the body are plain text:
13
+ * <meta> (a refresh navigates the frame, a policy of its own), <base> and
14
+ * <link> (prefetches). No tag can be rebuilt by the replacement, which only
15
+ * adds "&lt;".
16
+ * - no declarative shadow root (<template shadowrootmode>, the only way to
17
+ * make one without a script): a click inside one reaches the page's link
18
+ * guard retargeted to its host, so a link there would navigate. Every
19
+ * "shadowroot" is renamed, which leaves no attribute that makes one.
20
+ * Everything stays in the light DOM, where the guard sees it. */
21
+ export declare const neutralizeSlideHtml: (html: string) => string;
22
+ /** The page an HTML slide is shown as. */
23
+ export declare function slideHtmlDocument(html: string): string;