@gui-chat-plugin/sequence 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +72 -0
- package/dist/core/definitions.d.ts +126 -0
- package/dist/core/host.d.ts +27 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/presentSlide.d.ts +6 -0
- package/dist/core/records.d.ts +17 -0
- package/dist/core/repeatGuard.d.ts +14 -0
- package/dist/core/storyboard.d.ts +4 -0
- package/dist/core-55UuKHJb.cjs +1 -0
- package/dist/core-D0F2WGN6.js +619 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/style.css +3 -0
- package/dist/vue/index.d.ts +9 -0
- package/dist/vue.cjs +1 -0
- package/dist/vue.js +77 -0
- package/package.json +58 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
First release: `presentSlide`, `defineStoryboard` and `presentPanel`, from MulmoChat and
|
|
6
|
+
MulmoGlass, as one host-agnostic package. Results carry gui-chat-protocol 2.1's `sequence`; the
|
|
7
|
+
holds read `context.userSpokeAt`; references are drawn with `context.app.editImages`.
|
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @gui-chat-plugin/sequence
|
|
2
|
+
|
|
3
|
+
Tools that show things one step at a time, as pictures, for
|
|
4
|
+
[GUI Chat Protocol](https://github.com/receptron/gui-chat-protocol) hosts:
|
|
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 |
|
|
11
|
+
|
|
12
|
+
From MulmoChat and MulmoGlass, tested there by voice on OpenAI Realtime, Gemini Live and Grok, and
|
|
13
|
+
in text chat.
|
|
14
|
+
|
|
15
|
+
## Using it in a host
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { plugins } from "@gui-chat-plugin/sequence/vue"; // presentSlide, defineStoryboard, presentPanel
|
|
19
|
+
import "@gui-chat-plugin/sequence/style.css";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The core entry (`@gui-chat-plugin/sequence`) has the same tools without Views, for a host that runs
|
|
23
|
+
`execute()` on a server, plus the record types and `loadRecord`, `SLIDESHOWS_DIR` and
|
|
24
|
+
`STORYBOARDS_DIR` for tools that read the records (MulmoChat's makeMovie).
|
|
25
|
+
|
|
26
|
+
### What `execute()` needs from its context
|
|
27
|
+
|
|
28
|
+
| | Used for | Without it |
|
|
29
|
+
| ------------------------------------ | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
30
|
+
| `app.generateImage(prompt)` | every picture | "image generation isn't available" |
|
|
31
|
+
| `app.editImages(prompt, imagePaths)` | a panel drawn from its characters' sheets, a guide step drawn from the step before | drawn from the prompt alone |
|
|
32
|
+
| `files.artifacts` (`FileOps`) | the records, and checking that a reference picture is still there | records are kept in memory only (the results say they weren't saved); no references |
|
|
33
|
+
| `userSpokeAt` | holding a guide's next step, or a story's next panel after a choice, until the user has spoken | nothing is held |
|
|
34
|
+
| `currentResult` | telling "show step 2 again" (it isn't on the screen) from a repeated call (it is) | a step asked for again is shown again |
|
|
35
|
+
|
|
36
|
+
`generateImage` and `editImages` are gui-chat-protocol's `context.app` conventions: they save each
|
|
37
|
+
picture as `artifacts/images/…` and return its path in `data.imagePath`. `currentResult` is compared
|
|
38
|
+
by `data.imagePath` when it has one, so a host that runs `execute()` on a server can send the
|
|
39
|
+
current result without its picture.
|
|
40
|
+
|
|
41
|
+
### Keeping a sequence going
|
|
42
|
+
|
|
43
|
+
Every shown step's result carries `sequence` (gui-chat-protocol 2.1): where the sequence is, or
|
|
44
|
+
`null` when a step couldn't be shown. A host that wires gui-chat-protocol's `createSequenceKeeper`
|
|
45
|
+
asks the model to go on when it ends a reply mid-slideshow, and passes `keeper.userSpokeAt()` as
|
|
46
|
+
`context.userSpokeAt`. Without it the tools still work: each step asks for the next in its
|
|
47
|
+
instructions, which models usually follow.
|
|
48
|
+
|
|
49
|
+
Step results also set `instructionsRequired`, so a turn-based host (text chat) takes a turn for the
|
|
50
|
+
model to explain the step.
|
|
51
|
+
|
|
52
|
+
### State
|
|
53
|
+
|
|
54
|
+
The holds, the repeat guard and each slideshow's shown steps are kept in memory where `execute()`
|
|
55
|
+
runs, as one conversation's. A host that runs `execute()` for several users in one process would
|
|
56
|
+
mix them. Calls may overlap (a host that runs `execute()` on a server gets them as concurrent
|
|
57
|
+
requests): a call is claimed before anything is awaited, so an identical one waits for it and a
|
|
58
|
+
later step is held while it is drawn.
|
|
59
|
+
|
|
60
|
+
## Records
|
|
61
|
+
|
|
62
|
+
Saved through `files.artifacts`, with their pictures' paths:
|
|
63
|
+
|
|
64
|
+
- `slideshows/<id>.json`: `{ id, title, mode, totalSlides, slides: { "1": { title, imagePrompt, imagePath } } }`
|
|
65
|
+
- `storyboards/<id>.json`: `{ id, title, style, totalPanels, interactive, characters: [{ name, description, imagePath }], panels: { "1": { caption, characters, imagePrompt, imagePath, choices } } }`
|
|
66
|
+
|
|
67
|
+
IDs are 12 hex digits. The results name them, so a later tool call can refer to a slideshow or a
|
|
68
|
+
story (MulmoChat's makeMovie makes a narrated movie from one).
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
|
+
export declare const PRESENT_SLIDE = "presentSlide";
|
|
3
|
+
export declare const DEFINE_STORYBOARD = "defineStoryboard";
|
|
4
|
+
export declare const PRESENT_PANEL = "presentPanel";
|
|
5
|
+
export declare const MAX_CHARACTERS = 4;
|
|
6
|
+
export declare const MAX_PANELS = 12;
|
|
7
|
+
export declare const MAX_CHOICES = 3;
|
|
8
|
+
/** A slideshow's or storyboard's ID: 12 hex digits, also its file name. */
|
|
9
|
+
export declare const SEQUENCE_ID: RegExp;
|
|
10
|
+
export declare const sameName: (a: string, b: string) => boolean;
|
|
11
|
+
export type SlideMode = "presentation" | "steps";
|
|
12
|
+
export interface SlideArgs {
|
|
13
|
+
slide: number;
|
|
14
|
+
totalSlides: number;
|
|
15
|
+
title: string;
|
|
16
|
+
imagePrompt: string;
|
|
17
|
+
mode: SlideMode;
|
|
18
|
+
}
|
|
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
|
+
/** What the model does once a slide is on the screen. */
|
|
23
|
+
export declare const slideShownInstructions: ({ slide, totalSlides, title, mode, }: SlideArgs) => string;
|
|
24
|
+
/** A slideshow as saved in artifacts/slideshows/<id>.json. */
|
|
25
|
+
export interface Slideshow {
|
|
26
|
+
id: string;
|
|
27
|
+
/** The first slide's title. */
|
|
28
|
+
title: string;
|
|
29
|
+
mode: SlideMode;
|
|
30
|
+
totalSlides: number;
|
|
31
|
+
slides: Record<string, {
|
|
32
|
+
title: string;
|
|
33
|
+
imagePrompt: string;
|
|
34
|
+
imagePath?: string;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
/** A shown slide's result data. */
|
|
38
|
+
export interface SlideData {
|
|
39
|
+
imageData: string;
|
|
40
|
+
imagePath?: string;
|
|
41
|
+
prompt: string;
|
|
42
|
+
slideshowId: string;
|
|
43
|
+
slide: number;
|
|
44
|
+
totalSlides: number;
|
|
45
|
+
title: string;
|
|
46
|
+
mode: SlideMode;
|
|
47
|
+
}
|
|
48
|
+
export declare const PRESENT_SLIDE_PROMPT: string;
|
|
49
|
+
export declare const PRESENT_SLIDE_DEFINITION: ToolDefinition;
|
|
50
|
+
export interface Character {
|
|
51
|
+
name: string;
|
|
52
|
+
description: string;
|
|
53
|
+
/** Its reference sheet, when one was made and saved. */
|
|
54
|
+
imagePath?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface Panel {
|
|
57
|
+
caption: string;
|
|
58
|
+
characters: string[];
|
|
59
|
+
imagePrompt: string;
|
|
60
|
+
imagePath?: string;
|
|
61
|
+
/** In an interactive story, what the user chose between after it. */
|
|
62
|
+
choices?: string[];
|
|
63
|
+
}
|
|
64
|
+
/** A storyboard as saved in artifacts/storyboards/<id>.json. It maps onto a
|
|
65
|
+
* MulmoScript: each character (name and sheet) an entry in
|
|
66
|
+
* imageParams.images, each panel a beat, a panel's characters its
|
|
67
|
+
* imageNames. */
|
|
68
|
+
export interface Storyboard {
|
|
69
|
+
id: string;
|
|
70
|
+
title: string;
|
|
71
|
+
style: string;
|
|
72
|
+
totalPanels: number;
|
|
73
|
+
characters: Character[];
|
|
74
|
+
/** The user chooses what happens at some panels; the panels saved are the
|
|
75
|
+
* path the story took. */
|
|
76
|
+
interactive: boolean;
|
|
77
|
+
panels: Record<string, Panel>;
|
|
78
|
+
}
|
|
79
|
+
export interface StoryboardArgs {
|
|
80
|
+
title: string;
|
|
81
|
+
style: string;
|
|
82
|
+
totalPanels: number;
|
|
83
|
+
characters: Character[];
|
|
84
|
+
interactive: boolean;
|
|
85
|
+
}
|
|
86
|
+
/** The arguments, or what is wrong with them (said to the model). */
|
|
87
|
+
export declare function parseStoryboardArgs(args: Record<string, unknown>): StoryboardArgs | string;
|
|
88
|
+
export declare const castShownInstructions: ({ title, id }: Storyboard) => string;
|
|
89
|
+
/** The cast's result data. */
|
|
90
|
+
export interface CastData {
|
|
91
|
+
storyboardId: string;
|
|
92
|
+
title: string;
|
|
93
|
+
totalPanels: number;
|
|
94
|
+
/** The first sheet, for the preview. */
|
|
95
|
+
imageData?: string;
|
|
96
|
+
characters: {
|
|
97
|
+
name: string;
|
|
98
|
+
imageData?: string;
|
|
99
|
+
imagePath?: string;
|
|
100
|
+
}[];
|
|
101
|
+
}
|
|
102
|
+
export interface PanelArgs {
|
|
103
|
+
storyboardId: string;
|
|
104
|
+
panel: number;
|
|
105
|
+
characters: string[];
|
|
106
|
+
imagePrompt: string;
|
|
107
|
+
caption: string;
|
|
108
|
+
choices: string[];
|
|
109
|
+
}
|
|
110
|
+
export declare const PANEL_ARGS_ERROR = "presentPanel needs a storyboardId, a panel number (from 1) and an imagePrompt";
|
|
111
|
+
export declare function parsePanelArgs(args: Record<string, unknown>): PanelArgs | null;
|
|
112
|
+
export declare function panelShownInstructions(storyboard: Pick<Storyboard, "id" | "totalPanels">, panel: number, choices: string[]): string;
|
|
113
|
+
/** A shown panel's result data. */
|
|
114
|
+
export interface PanelData {
|
|
115
|
+
imageData: string;
|
|
116
|
+
imagePath?: string;
|
|
117
|
+
prompt: string;
|
|
118
|
+
storyboardId: string;
|
|
119
|
+
panel: number;
|
|
120
|
+
totalPanels: number;
|
|
121
|
+
caption: string;
|
|
122
|
+
choices?: string[];
|
|
123
|
+
}
|
|
124
|
+
export declare const STORYBOARD_PROMPT = "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.";
|
|
125
|
+
export declare const DEFINE_STORYBOARD_DEFINITION: ToolDefinition;
|
|
126
|
+
export declare const PRESENT_PANEL_DEFINITION: ToolDefinition;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { FileOps, ToolContext, ToolResult } from "gui-chat-protocol";
|
|
2
|
+
/** The context execute() is called with. */
|
|
3
|
+
export type SequenceContext = ToolContext & {
|
|
4
|
+
files?: {
|
|
5
|
+
artifacts: FileOps;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
|
|
9
|
+
/** The picture a result carries, and where it was saved. */
|
|
10
|
+
export declare function imageOf(result: ToolResult | null | undefined): {
|
|
11
|
+
imageData?: string;
|
|
12
|
+
imagePath?: string;
|
|
13
|
+
};
|
|
14
|
+
/** Whether two results show the same picture: by its saved path when both
|
|
15
|
+
* have one (a host may send the current result without its image data),
|
|
16
|
+
* else by the picture itself. */
|
|
17
|
+
export declare function samePicture(a: ToolResult | null | undefined, b: ToolResult | null | undefined): boolean;
|
|
18
|
+
/** When the user spoke after `time` (ms since the epoch). A host that doesn't
|
|
19
|
+
* say when the user spoke holds nothing. */
|
|
20
|
+
export declare const userSpokeSince: (context: SequenceContext, time: number) => boolean;
|
|
21
|
+
export declare const newSequenceId: () => string;
|
|
22
|
+
/** The saved pictures among `paths` that the host can draw from: under
|
|
23
|
+
* artifacts/, still there, and only when the host takes references. */
|
|
24
|
+
export declare function usableReferences(context: SequenceContext, paths: readonly string[]): Promise<string[]>;
|
|
25
|
+
/** A picture from the host: from `references` (saved paths) when there are
|
|
26
|
+
* any, else from the prompt alone. A failure is a result saying why. */
|
|
27
|
+
export declare function drawPicture(context: SequenceContext, prompt: string, references?: readonly string[]): Promise<ToolResult>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ToolPluginCore } from "gui-chat-protocol";
|
|
2
|
+
type SequencePluginCore = ToolPluginCore<unknown, unknown, Record<string, unknown>>;
|
|
3
|
+
export declare const presentSlidePluginCore: SequencePluginCore;
|
|
4
|
+
export declare const defineStoryboardPluginCore: SequencePluginCore;
|
|
5
|
+
export declare const presentPanelPluginCore: SequencePluginCore;
|
|
6
|
+
export * from "./definitions";
|
|
7
|
+
export { SLIDESHOWS_DIR, STORYBOARDS_DIR, loadRecord } from "./records";
|
|
8
|
+
export type { SequenceContext } from "./host";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SequenceStep, ToolResult } from "gui-chat-protocol";
|
|
2
|
+
import { type SlideArgs } from "./definitions";
|
|
3
|
+
import { type SequenceContext } from "./host";
|
|
4
|
+
/** Where the slideshow is after a slide was shown. */
|
|
5
|
+
export declare function slideSequenceStep({ slide: step, totalSlides: total, mode, }: SlideArgs): SequenceStep;
|
|
6
|
+
export declare function presentSlide(context: SequenceContext, args: Record<string, unknown>): Promise<ToolResult>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type FileOps } from "gui-chat-protocol";
|
|
2
|
+
import { type Slideshow, type Storyboard } from "./definitions";
|
|
3
|
+
export declare const SLIDESHOWS_DIR = "slideshows";
|
|
4
|
+
export declare const STORYBOARDS_DIR = "storyboards";
|
|
5
|
+
type SequenceRecord = Slideshow | Storyboard;
|
|
6
|
+
/** A saved slideshow or storyboard, or null. `id` is checked first, so a
|
|
7
|
+
* model-written ID can't name another file. */
|
|
8
|
+
export declare function loadRecord<T extends SequenceRecord>(files: FileOps | undefined, dir: string, id: string): Promise<T | null>;
|
|
9
|
+
/**
|
|
10
|
+
* Change a record (null when there is none yet) and save it. Resolves to
|
|
11
|
+
* whether it was saved: one that wasn't (no files, or the write failed)
|
|
12
|
+
* still works while the host runs, and the result says so, as for a picture
|
|
13
|
+
* that wasn't saved.
|
|
14
|
+
*/
|
|
15
|
+
export declare function updateRecord<T extends SequenceRecord>(files: FileOps | undefined, dir: string, id: string, change: (record: T | null) => T): Promise<boolean>;
|
|
16
|
+
export declare const NOT_SAVED: (what: string) => string;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A request claimed: either this call makes it (`settle` says whether it was
|
|
3
|
+
* shown), or an identical one made in the last minute does (`earlier`
|
|
4
|
+
* resolves to whether that one was shown).
|
|
5
|
+
*/
|
|
6
|
+
export type Claim = {
|
|
7
|
+
settle: (shown: boolean) => void;
|
|
8
|
+
} | {
|
|
9
|
+
earlier: Promise<boolean>;
|
|
10
|
+
};
|
|
11
|
+
/** Drops a repeat of a request made in the last minute. */
|
|
12
|
+
export declare function createRepeatGuard(): {
|
|
13
|
+
claim: (key: string) => Claim;
|
|
14
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ToolResult } from "gui-chat-protocol";
|
|
2
|
+
import { type SequenceContext } from "./host";
|
|
3
|
+
export declare function defineStoryboard(context: SequenceContext, args: Record<string, unknown>): Promise<ToolResult>;
|
|
4
|
+
export declare function presentPanel(context: SequenceContext, args: Record<string, unknown>): Promise<ToolResult>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let e=require("gui-chat-protocol");var t=`presentSlide`,n=`defineStoryboard`,r=`presentPanel`,i=4,a=12,o=3,s=/^[0-9a-f]{12}$/,c=e=>typeof e==`object`&&!!e&&!Array.isArray(e),l=e=>typeof e==`string`?e.trim():``,u=(e,t)=>e.toLowerCase()===t.toLowerCase(),d=e=>typeof e==`number`&&Number.isInteger(e),f=`presentSlide needs slide and totalSlides (whole numbers, slide from 1 to totalSlides) and an imagePrompt`;function p(e){let{slide:t,totalSlides:n,title:r,imagePrompt:i}=e;return!d(t)||!d(n)||t<1||n<t||typeof i!=`string`||!i.trim()?null:{slide:t,totalSlides:n,title:typeof r==`string`?r:``,imagePrompt:i,mode:e.mode===`steps`?`steps`:`presentation`}}var m=({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. Explain what to do in this step, 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. Explain what to do, 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, 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, then wrap up the slideshow.`},h=`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. Go on to the last slide without asking whether to continue. 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.`,g={type:`function`,name:t,prompt:h,description:`Show one slide of a slideshow: a picture generated from imagePrompt, full screen. 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:`The picture for this slide: a clear illustration or diagram of its point, with the title as its only large text. Be concrete. For a step, show the action: hands, tools and the object.`},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`,`imagePrompt`]}};function _(e){let{totalPanels:t}=e;if(!d(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=c(t)?l(t.name):``,r=c(t)?l(t.description):``;if(!e||!r)return`every character needs a name and a description`;if(n.some(t=>u(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:l(e.title),style:l(e.style),totalPanels:t,characters:n,interactive:e.interactive===!0}}var v=({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.`,y=`presentPanel needs a storyboardId, a panel number (from 1) and an imagePrompt`;function b(e){let{panel:t}=e,n=l(e.storyboardId),r=l(e.imagePrompt);if(!n||!r||!d(t)||t<1)return null;let i=e=>(Array.isArray(e)?e:[]).map(l).filter(Boolean);return{storyboardId:n,panel:t,characters:i(e.characters),imagePrompt:r,caption:l(e.caption),choices:i(e.choices).slice(0,3)}}function x(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 S=`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.`,C={type:`function`,name:n,prompt:S,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`]}},w={type:`function`,name:r,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`]}},T=e=>typeof e==`object`&&!!e&&!Array.isArray(e),ee=e=>T(e)&&typeof e.message==`string`;function E(e){let t=T(e?.data)?e.data:{};return{...typeof t.imageData==`string`&&{imageData:t.imageData},...typeof t.imagePath==`string`&&{imagePath:t.imagePath}}}function te(e,t){let n=E(e),r=E(t);return n.imagePath&&r.imagePath?n.imagePath===r.imagePath:!!n.imageData&&n.imageData===r.imageData}var D=(e,t)=>e.userSpokeAt===void 0||e.userSpokeAt>t,O=()=>crypto.randomUUID().replace(/-/g,``).slice(0,12),k=`artifacts/`;async function A(e,t){let n=e.files?.artifacts;if(!e.app?.editImages||!n)return[];let r=[];for(let e of t)if(e.startsWith(k))try{await n.exists(e.slice(10))&&r.push(e)}catch{}return r}async function j(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 ee(e)?e:{message:`image generation returned an unrecognized result`}}catch(e){return{message:`image generation failed: ${e instanceof Error?e.message:String(e)}`}}}var M=`slideshows`,N=`storyboards`,P={},F=new WeakMap,I=new WeakMap;function L(e){let t=F.get(e);return t||F.set(e,t=new Map),t}function R(t,n){let r=I.get(t);r||I.set(t,r=new Map);let i=r.get(n);return i||r.set(n,i=(0,e.createSerialLock)()),i}var z=(e,t)=>`${e}/${t}.json`;async function B(e,t){let n=L(e??P),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 V(e,t,n){return s.test(n)?B(e,z(t,n)):null}async function H(e,t,n,r){let i=z(t,n),a=e??P;return R(a,i)(async()=>{let t=r(await B(e,i));if(L(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 U=e=>`the ${e} could not be saved, so it lasts only until the app or server restarts`,ne=6e4;function W(){let e=new Map;return{claim:t=>{let n=Date.now();for(let[t,r]of e)n-r.at>ne&&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 G({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 re=W(),ie=(e,{slide:t,totalSlides:n,title:r,imagePrompt:i})=>JSON.stringify([e,t,n,r,i]),K={presentation:null,steps:null},q=null;function J(e){let t=K[e.mode],n=t?.totalSlides===e.totalSlides&&(e.slide!==1||t.firstTitle===e.title);if(t&&n)return t;let r={id:O(),mode:e.mode,totalSlides:e.totalSlides,firstTitle:e.slide===1?e.title:``,slides:new Map,current:0,shownAt:-1/0};return K[e.mode]=r,r}function Y(e,t,n){e.current=t.slide,e.shownAt=Date.now(),q=e;let r=t.mode===`steps`?`step`:`slide`,{uuid:i,...a}=n;return{...a,message:`${r} ${t.slide} of ${t.totalSlides} is on the screen again, as it was`,instructions:m(t),instructionsRequired:!0,sequence:G(t)}}function ae(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 oe(e,t,n){let r=t.slides.get(n.slide-1),i=n.mode===`steps`?E(r).imagePath:void 0,a=i?await A(e,[i]):[],o=ae(n,a.length>0),s=await j(e,o,a),{imageData:c,imagePath:l}=E(s);if(!c)return{...s,sequence:null};let u=t.id,d=await H(e.files?.artifacts,M,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,...l&&{imagePath:l}},t}),f={imageData:c,...l&&{imagePath:l},prompt:o,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`,l?`saved to ${l}`:``,d?``:U(`slideshow`)].filter(Boolean).join(`; `),instructions:m(n),instructionsRequired:!0,sequence:G(n)}}async function X(e,t){let n=p(t);if(!n)return{message:f,sequence:null};let r=J(n),i=n.mode===`steps`;if(i&&n.slide>r.current&&r.current>0&&!D(e,r.shownAt)){console.info(`[sequence] holding step ${n.slide} for the user`);let e=r.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 ${r.current} ${e}. Wait for them before calling presentSlide for step ${n.slide}.`,cancelled:!0}}let a=i?r.slides.get(n.slide):void 0;if(a)return te(e.currentResult,a)?{message:`step ${n.slide} of ${n.totalSlides} is already on the screen`,cancelled:!0}:Y(r,n,a);let o=re.claim(ie(r.id,n));if(`earlier`in o){if(!await o.earlier)return X(e,t);let i=r.slides.get(n.slide);return i&&q!==r?Y(r,n,i):{message:`slide ${n.slide} of ${n.totalSlides} is already on the screen`,cancelled:!0}}let{settle:s}=o,c={current:r.current,shownAt:r.shownAt};r.current=n.slide,r.shownAt=1/0;let l=!1;try{let t=await oe(e,r,n);return l=!!E(t).imageData,l&&(r.slides.set(n.slide,t),r.current=n.slide,r.shownAt=Date.now(),q=r),t}finally{s(l),!l&&r.current===n.slide&&(r.current=c.current,r.shownAt=c.shownAt)}}var se=({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}"`}),ce=({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}}),Z=(e,t)=>e?`${e}. ${t}`:t,le=(e,{name:t,description:n})=>Z(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 ue(e,t){let n=_(t);if(typeof n==`string`)return{message:n,sequence:null};let r=await Promise.all(n.characters.map(t=>j(e,le(n.style,t)))),i=r.map(E),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:O(),...n,characters:n.characters.map((e,t)=>{let n=i[t]?.imagePath;return{...e,...n&&{imagePath:n}}}),panels:{}},s=await H(e.files?.artifacts,N,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?``:U(`storyboard`)].filter(Boolean).join(`; `),instructions:v(o),instructionsRequired:!0,sequence:se(o)}}function de(e,t,n){let r=[Z(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 fe(e,t,n){let r=[],i=[];for(let a of n.characters){let n=t.characters.find(e=>u(e.name,a));if(!n)r.push(a);else if(!i.some(e=>e.character===n)){let[t]=n.imagePath?await A(e,[n.imagePath]):[];i.push({character:n,sheet:t??null})}}let a=i.map(({sheet:e})=>e).filter(e=>!!e),o=de(t,n,i.map(({character:e,sheet:t})=>({character:e,hasSheet:!!t}))),s=await j(e,o,a),{imageData:c,imagePath:l}=E(s);if(!c)return{...s,sequence:null};let d=n.panel<t.totalPanels&&n.choices.length>=2?n.choices:[],f=await H(e.files?.artifacts,N,t.id,e=>{let r=e??t;return d.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},...d.length&&{choices:d}},r});return{data:{imageData:c,...l&&{imagePath:l},prompt:o,storyboardId:t.id,panel:n.panel,totalPanels:t.totalPanels,caption:n.caption,...d.length&&{choices:d}},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(`, `)}`:``,f?``:U(`storyboard`)].filter(Boolean).join(`; `),instructions:x(t,n.panel,d),instructionsRequired:!0,sequence:ce(t,n.panel,d)}}var pe=W(),Q=new Map;async function $(e,t){let n=b(t);if(!n)return{message:y,sequence:null};let{storyboardId:r,panel:i}=n,a=Q.get(r);if(a&&i>a.panel&&!D(e,a.shownAt))return console.info(`[sequence] holding panel ${i} for the user`),{message:a.shownAt===1/0?`panel ${i} was not shown: panel ${a.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 ${a.panel} is still on the screen. Wait for their answer.`,cancelled:!0};let o=pe.claim(JSON.stringify(n));if(`earlier`in o)return await o.earlier?{message:`panel ${i} is already on the screen`,cancelled:!0}:$(e,t);let{settle:s}=o,c={panel:i,shownAt:1/0},l=Q.get(r);Q.set(r,c);let u=()=>Q.get(r)===c,d=!1;try{let t=await V(e.files?.artifacts,N,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 fe(e,t,n);d=!!E(a).imageData;let o=a.data;return d&&u()&&(o?.choices?.length?Q.set(r,{panel:i,shownAt:Date.now()}):Q.delete(r)),a}finally{s(d),!d&&u()&&(l?Q.set(r,l):Q.delete(r))}}var me={toolDefinition:g,generatingMessage:`Making the slide...`,isEnabled:()=>!0,execute:(e,t)=>X(e,t)},he={toolDefinition:C,generatingMessage:`Drawing the characters...`,isEnabled:()=>!0,execute:(e,t)=>ue(e,t)},ge={toolDefinition:w,generatingMessage:`Drawing the panel...`,isEnabled:()=>!0,execute:(e,t)=>$(e,t)};Object.defineProperty(exports,"C",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"D",{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,"E",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"S",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"T",{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,"_",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"b",{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"g",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return ge}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return me}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return he}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"v",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"w",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"x",{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,"y",{enumerable:!0,get:function(){return f}});
|
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
import { createSerialLock as e } from "gui-chat-protocol";
|
|
2
|
+
//#region src/core/definitions.ts
|
|
3
|
+
var t = "presentSlide", n = "defineStoryboard", r = "presentPanel", i = 4, a = 12, o = 3, s = /^[0-9a-f]{12}$/, c = (e) => typeof e == "object" && !!e && !Array.isArray(e), l = (e) => typeof e == "string" ? e.trim() : "", u = (e, t) => e.toLowerCase() === t.toLowerCase(), d = (e) => typeof e == "number" && Number.isInteger(e), f = "presentSlide needs slide and totalSlides (whole numbers, slide from 1 to totalSlides) and an imagePrompt";
|
|
4
|
+
function p(e) {
|
|
5
|
+
let { slide: t, totalSlides: n, title: r, imagePrompt: i } = e;
|
|
6
|
+
return !d(t) || !d(n) || t < 1 || n < t || typeof i != "string" || !i.trim() ? null : {
|
|
7
|
+
slide: t,
|
|
8
|
+
totalSlides: n,
|
|
9
|
+
title: typeof r == "string" ? r : "",
|
|
10
|
+
imagePrompt: i,
|
|
11
|
+
mode: e.mode === "steps" ? "steps" : "presentation"
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
var m = ({ slide: e, totalSlides: t, title: n, mode: r }) => {
|
|
15
|
+
let i = n ? ` ("${n}")` : "";
|
|
16
|
+
if (r === "steps") {
|
|
17
|
+
let n = `Step ${e} of ${t}${i}`;
|
|
18
|
+
return e < t ? `${n} is now on the screen. Explain what to do in this step, 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. Explain what to do, then wrap up the guide once the user is done.`;
|
|
19
|
+
}
|
|
20
|
+
let a = `Slide ${e} of ${t}${i}`;
|
|
21
|
+
return e < t ? `${a} is now on the screen. Explain it, 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, then wrap up the slideshow.`;
|
|
22
|
+
}, h = "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. Go on to the last slide without asking whether to continue. 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.", g = {
|
|
23
|
+
type: "function",
|
|
24
|
+
name: t,
|
|
25
|
+
prompt: h,
|
|
26
|
+
description: "Show one slide of a slideshow: a picture generated from imagePrompt, full screen. 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.",
|
|
27
|
+
parameters: {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
slide: {
|
|
31
|
+
type: "integer",
|
|
32
|
+
description: "This slide's number, starting at 1."
|
|
33
|
+
},
|
|
34
|
+
totalSlides: {
|
|
35
|
+
type: "integer",
|
|
36
|
+
description: "How many slides the slideshow has. Keep it the same for every slide."
|
|
37
|
+
},
|
|
38
|
+
title: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "The slide's title."
|
|
41
|
+
},
|
|
42
|
+
imagePrompt: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "The picture for this slide: a clear illustration or diagram of its point, with the title as its only large text. Be concrete. For a step, show the action: hands, tools and the object."
|
|
45
|
+
},
|
|
46
|
+
mode: {
|
|
47
|
+
type: "string",
|
|
48
|
+
enum: ["presentation", "steps"],
|
|
49
|
+
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."
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
required: [
|
|
53
|
+
"slide",
|
|
54
|
+
"totalSlides",
|
|
55
|
+
"title",
|
|
56
|
+
"imagePrompt"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
function _(e) {
|
|
61
|
+
let { totalPanels: t } = e;
|
|
62
|
+
if (!d(t) || t < 1 || t > 12) return "totalPanels must be a whole number from 1 to 12";
|
|
63
|
+
let n = [];
|
|
64
|
+
for (let t of Array.isArray(e.characters) ? e.characters : []) {
|
|
65
|
+
let e = c(t) ? l(t.name) : "", r = c(t) ? l(t.description) : "";
|
|
66
|
+
if (!e || !r) return "every character needs a name and a description";
|
|
67
|
+
if (n.some((t) => u(t.name, e))) return `two characters are named ${e}`;
|
|
68
|
+
n.push({
|
|
69
|
+
name: e,
|
|
70
|
+
description: r
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return n.length < 1 || n.length > 4 ? "a storyboard has 1 to 4 characters" : {
|
|
74
|
+
title: l(e.title),
|
|
75
|
+
style: l(e.style),
|
|
76
|
+
totalPanels: t,
|
|
77
|
+
characters: n,
|
|
78
|
+
interactive: e.interactive === !0
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
var v = ({ 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.`, y = "presentPanel needs a storyboardId, a panel number (from 1) and an imagePrompt";
|
|
82
|
+
function b(e) {
|
|
83
|
+
let { panel: t } = e, n = l(e.storyboardId), r = l(e.imagePrompt);
|
|
84
|
+
if (!n || !r || !d(t) || t < 1) return null;
|
|
85
|
+
let i = (e) => (Array.isArray(e) ? e : []).map(l).filter(Boolean);
|
|
86
|
+
return {
|
|
87
|
+
storyboardId: n,
|
|
88
|
+
panel: t,
|
|
89
|
+
characters: i(e.characters),
|
|
90
|
+
imagePrompt: r,
|
|
91
|
+
caption: l(e.caption),
|
|
92
|
+
choices: i(e.choices).slice(0, 3)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function x(e, t, n) {
|
|
96
|
+
let { id: r, totalPanels: i } = e;
|
|
97
|
+
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.`;
|
|
98
|
+
}
|
|
99
|
+
var S = "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.", C = {
|
|
100
|
+
type: "function",
|
|
101
|
+
name: n,
|
|
102
|
+
prompt: S,
|
|
103
|
+
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.",
|
|
104
|
+
parameters: {
|
|
105
|
+
type: "object",
|
|
106
|
+
properties: {
|
|
107
|
+
title: {
|
|
108
|
+
type: "string",
|
|
109
|
+
description: "The story's title."
|
|
110
|
+
},
|
|
111
|
+
style: {
|
|
112
|
+
type: "string",
|
|
113
|
+
description: "The art style of every picture, in one sentence (for example: soft watercolor, children's picture book)."
|
|
114
|
+
},
|
|
115
|
+
totalPanels: {
|
|
116
|
+
type: "integer",
|
|
117
|
+
description: "How many panels the story has, at most 12; four to eight is usual."
|
|
118
|
+
},
|
|
119
|
+
interactive: {
|
|
120
|
+
type: "boolean",
|
|
121
|
+
description: "The user chooses what happens: panels can offer choices, and the story waits for the pick."
|
|
122
|
+
},
|
|
123
|
+
characters: {
|
|
124
|
+
type: "array",
|
|
125
|
+
description: "The characters who appear in more than one panel, 1 to 4.",
|
|
126
|
+
items: {
|
|
127
|
+
type: "object",
|
|
128
|
+
properties: {
|
|
129
|
+
name: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: "A short name, used in presentPanel."
|
|
132
|
+
},
|
|
133
|
+
description: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "How they look: age, build, face, hair, clothes and colors. Be concrete: it is drawn once and reused in every panel."
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
required: ["name", "description"]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
required: [
|
|
143
|
+
"title",
|
|
144
|
+
"style",
|
|
145
|
+
"totalPanels",
|
|
146
|
+
"characters"
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
}, w = {
|
|
150
|
+
type: "function",
|
|
151
|
+
name: r,
|
|
152
|
+
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.",
|
|
153
|
+
parameters: {
|
|
154
|
+
type: "object",
|
|
155
|
+
properties: {
|
|
156
|
+
storyboardId: {
|
|
157
|
+
type: "string",
|
|
158
|
+
description: "The storyboardId defineStoryboard returned."
|
|
159
|
+
},
|
|
160
|
+
panel: {
|
|
161
|
+
type: "integer",
|
|
162
|
+
description: "This panel's number, starting at 1."
|
|
163
|
+
},
|
|
164
|
+
characters: {
|
|
165
|
+
type: "array",
|
|
166
|
+
items: { type: "string" },
|
|
167
|
+
description: "The names (as in defineStoryboard) of the characters in this panel."
|
|
168
|
+
},
|
|
169
|
+
imagePrompt: {
|
|
170
|
+
type: "string",
|
|
171
|
+
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."
|
|
172
|
+
},
|
|
173
|
+
caption: {
|
|
174
|
+
type: "string",
|
|
175
|
+
description: "One short line shown under the picture."
|
|
176
|
+
},
|
|
177
|
+
choices: {
|
|
178
|
+
type: "array",
|
|
179
|
+
items: { type: "string" },
|
|
180
|
+
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."
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
required: [
|
|
184
|
+
"storyboardId",
|
|
185
|
+
"panel",
|
|
186
|
+
"characters",
|
|
187
|
+
"imagePrompt"
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
}, T = (e) => typeof e == "object" && !!e && !Array.isArray(e), ee = (e) => T(e) && typeof e.message == "string";
|
|
191
|
+
function E(e) {
|
|
192
|
+
let t = T(e?.data) ? e.data : {};
|
|
193
|
+
return {
|
|
194
|
+
...typeof t.imageData == "string" && { imageData: t.imageData },
|
|
195
|
+
...typeof t.imagePath == "string" && { imagePath: t.imagePath }
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function te(e, t) {
|
|
199
|
+
let n = E(e), r = E(t);
|
|
200
|
+
return n.imagePath && r.imagePath ? n.imagePath === r.imagePath : !!n.imageData && n.imageData === r.imageData;
|
|
201
|
+
}
|
|
202
|
+
var D = (e, t) => e.userSpokeAt === void 0 || e.userSpokeAt > t, O = () => crypto.randomUUID().replace(/-/g, "").slice(0, 12), k = "artifacts/";
|
|
203
|
+
async function A(e, t) {
|
|
204
|
+
let n = e.files?.artifacts;
|
|
205
|
+
if (!e.app?.editImages || !n) return [];
|
|
206
|
+
let r = [];
|
|
207
|
+
for (let e of t) if (e.startsWith(k)) try {
|
|
208
|
+
await n.exists(e.slice(10)) && r.push(e);
|
|
209
|
+
} catch {}
|
|
210
|
+
return r;
|
|
211
|
+
}
|
|
212
|
+
async function j(e, t, n = []) {
|
|
213
|
+
let r = e.app, i = r?.editImages, a = r?.generateImage;
|
|
214
|
+
try {
|
|
215
|
+
let e;
|
|
216
|
+
if (n.length && i) e = await i(t, [...n]);
|
|
217
|
+
else if (a) e = await a(t);
|
|
218
|
+
else return { message: "image generation isn't available" };
|
|
219
|
+
return ee(e) ? e : { message: "image generation returned an unrecognized result" };
|
|
220
|
+
} catch (e) {
|
|
221
|
+
return { message: `image generation failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/core/records.ts
|
|
226
|
+
var M = "slideshows", N = "storyboards", P = {}, F = /* @__PURE__ */ new WeakMap(), I = /* @__PURE__ */ new WeakMap();
|
|
227
|
+
function L(e) {
|
|
228
|
+
let t = F.get(e);
|
|
229
|
+
return t || F.set(e, t = /* @__PURE__ */ new Map()), t;
|
|
230
|
+
}
|
|
231
|
+
function R(t, n) {
|
|
232
|
+
let r = I.get(t);
|
|
233
|
+
r || I.set(t, r = /* @__PURE__ */ new Map());
|
|
234
|
+
let i = r.get(n);
|
|
235
|
+
return i || r.set(n, i = e()), i;
|
|
236
|
+
}
|
|
237
|
+
var z = (e, t) => `${e}/${t}.json`;
|
|
238
|
+
async function B(e, t) {
|
|
239
|
+
let n = L(e ?? P), r = n.get(t);
|
|
240
|
+
if (r) return r;
|
|
241
|
+
if (!e) return null;
|
|
242
|
+
try {
|
|
243
|
+
let r = JSON.parse(await e.read(t));
|
|
244
|
+
return n.set(t, r), r;
|
|
245
|
+
} catch {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async function V(e, t, n) {
|
|
250
|
+
return s.test(n) ? B(e, z(t, n)) : null;
|
|
251
|
+
}
|
|
252
|
+
async function H(e, t, n, r) {
|
|
253
|
+
let i = z(t, n), a = e ?? P;
|
|
254
|
+
return R(a, i)(async () => {
|
|
255
|
+
let t = r(await B(e, i));
|
|
256
|
+
if (L(a).set(i, t), !e) return !1;
|
|
257
|
+
try {
|
|
258
|
+
return await e.write(i, JSON.stringify(t, null, 2)), !0;
|
|
259
|
+
} catch (e) {
|
|
260
|
+
return console.warn("[sequence] could not save", i, e), !1;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
var U = (e) => `the ${e} could not be saved, so it lasts only until the app or server restarts`, ne = 6e4;
|
|
265
|
+
function W() {
|
|
266
|
+
let e = /* @__PURE__ */ new Map();
|
|
267
|
+
return { claim: (t) => {
|
|
268
|
+
let n = Date.now();
|
|
269
|
+
for (let [t, r] of e) n - r.at > ne && e.delete(t);
|
|
270
|
+
let r = e.get(t);
|
|
271
|
+
if (r) return { earlier: r.shown };
|
|
272
|
+
let i = () => void 0, a = new Promise((e) => i = e);
|
|
273
|
+
return e.set(t, {
|
|
274
|
+
at: n,
|
|
275
|
+
shown: a
|
|
276
|
+
}), { settle: (n) => {
|
|
277
|
+
!n && e.get(t)?.shown === a && e.delete(t), i(n);
|
|
278
|
+
} };
|
|
279
|
+
} };
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/core/presentSlide.ts
|
|
283
|
+
function G({ slide: e, totalSlides: t, mode: n }) {
|
|
284
|
+
return n === "steps" ? {
|
|
285
|
+
step: e,
|
|
286
|
+
total: t,
|
|
287
|
+
kind: "guide",
|
|
288
|
+
label: `Step ${e} of ${t}`,
|
|
289
|
+
onShown: "explain it",
|
|
290
|
+
nextCall: `call presentSlide for step ${e + 1} of ${t} with mode "steps"`,
|
|
291
|
+
waitsForUser: !0
|
|
292
|
+
} : {
|
|
293
|
+
step: e,
|
|
294
|
+
total: t,
|
|
295
|
+
kind: "slideshow",
|
|
296
|
+
label: `Slide ${e} of ${t}`,
|
|
297
|
+
onShown: "explain it",
|
|
298
|
+
nextCall: `call presentSlide for slide ${e + 1} of ${t}`
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
var re = W(), ie = (e, { slide: t, totalSlides: n, title: r, imagePrompt: i }) => JSON.stringify([
|
|
302
|
+
e,
|
|
303
|
+
t,
|
|
304
|
+
n,
|
|
305
|
+
r,
|
|
306
|
+
i
|
|
307
|
+
]), K = {
|
|
308
|
+
presentation: null,
|
|
309
|
+
steps: null
|
|
310
|
+
}, q = null;
|
|
311
|
+
function J(e) {
|
|
312
|
+
let t = K[e.mode], n = t?.totalSlides === e.totalSlides && (e.slide !== 1 || t.firstTitle === e.title);
|
|
313
|
+
if (t && n) return t;
|
|
314
|
+
let r = {
|
|
315
|
+
id: O(),
|
|
316
|
+
mode: e.mode,
|
|
317
|
+
totalSlides: e.totalSlides,
|
|
318
|
+
firstTitle: e.slide === 1 ? e.title : "",
|
|
319
|
+
slides: /* @__PURE__ */ new Map(),
|
|
320
|
+
current: 0,
|
|
321
|
+
shownAt: -Infinity
|
|
322
|
+
};
|
|
323
|
+
return K[e.mode] = r, r;
|
|
324
|
+
}
|
|
325
|
+
function Y(e, t, n) {
|
|
326
|
+
e.current = t.slide, e.shownAt = Date.now(), q = e;
|
|
327
|
+
let r = t.mode === "steps" ? "step" : "slide", { uuid: i, ...a } = n;
|
|
328
|
+
return {
|
|
329
|
+
...a,
|
|
330
|
+
message: `${r} ${t.slide} of ${t.totalSlides} is on the screen again, as it was`,
|
|
331
|
+
instructions: m(t),
|
|
332
|
+
instructionsRequired: !0,
|
|
333
|
+
sequence: G(t)
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function ae(e, t) {
|
|
337
|
+
if (e.mode !== "steps") return e.title ? `A presentation slide titled "${e.title}". ${e.imagePrompt}` : e.imagePrompt;
|
|
338
|
+
let n = e.title ? `, "${e.title}"` : "";
|
|
339
|
+
return [
|
|
340
|
+
`An instructional illustration of step ${e.slide} of ${e.totalSlides}${n}. ${e.imagePrompt}`,
|
|
341
|
+
t ? "The reference image is the previous step: keep the same objects, hands, tools and setting, and show what this step changes." : "",
|
|
342
|
+
"Show the action clearly, with little or no text."
|
|
343
|
+
].filter(Boolean).join(" ");
|
|
344
|
+
}
|
|
345
|
+
async function oe(e, t, n) {
|
|
346
|
+
let r = t.slides.get(n.slide - 1), i = n.mode === "steps" ? E(r).imagePath : void 0, a = i ? await A(e, [i]) : [], o = ae(n, a.length > 0), s = await j(e, o, a), { imageData: c, imagePath: l } = E(s);
|
|
347
|
+
if (!c) return {
|
|
348
|
+
...s,
|
|
349
|
+
sequence: null
|
|
350
|
+
};
|
|
351
|
+
let u = t.id, d = await H(e.files?.artifacts, M, u, (e) => {
|
|
352
|
+
let t = e ?? {
|
|
353
|
+
id: u,
|
|
354
|
+
title: "",
|
|
355
|
+
mode: n.mode,
|
|
356
|
+
totalSlides: n.totalSlides,
|
|
357
|
+
slides: {}
|
|
358
|
+
};
|
|
359
|
+
return n.slide === 1 && (t.title = n.title), t.slides[String(n.slide)] = {
|
|
360
|
+
title: n.title,
|
|
361
|
+
imagePrompt: n.imagePrompt,
|
|
362
|
+
...l && { imagePath: l }
|
|
363
|
+
}, t;
|
|
364
|
+
}), f = {
|
|
365
|
+
imageData: c,
|
|
366
|
+
...l && { imagePath: l },
|
|
367
|
+
prompt: o,
|
|
368
|
+
slideshowId: u,
|
|
369
|
+
slide: n.slide,
|
|
370
|
+
totalSlides: n.totalSlides,
|
|
371
|
+
title: n.title,
|
|
372
|
+
mode: n.mode
|
|
373
|
+
}, p = n.mode === "steps" ? "step" : "slide";
|
|
374
|
+
return {
|
|
375
|
+
data: f,
|
|
376
|
+
title: n.title || `Slide ${n.slide}`,
|
|
377
|
+
message: [
|
|
378
|
+
`${p} ${n.slide} of ${n.totalSlides} of slideshow "${u}" is on the screen`,
|
|
379
|
+
l ? `saved to ${l}` : "",
|
|
380
|
+
d ? "" : U("slideshow")
|
|
381
|
+
].filter(Boolean).join("; "),
|
|
382
|
+
instructions: m(n),
|
|
383
|
+
instructionsRequired: !0,
|
|
384
|
+
sequence: G(n)
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
async function X(e, t) {
|
|
388
|
+
let n = p(t);
|
|
389
|
+
if (!n) return {
|
|
390
|
+
message: f,
|
|
391
|
+
sequence: null
|
|
392
|
+
};
|
|
393
|
+
let r = J(n), i = n.mode === "steps";
|
|
394
|
+
if (i && n.slide > r.current && r.current > 0 && !D(e, r.shownAt)) {
|
|
395
|
+
console.info(`[sequence] holding step ${n.slide} for the user`);
|
|
396
|
+
let e = r.shownAt === Infinity ? "is still being drawn" : "is still on the screen";
|
|
397
|
+
return {
|
|
398
|
+
message: `step ${n.slide} was not shown: the user hasn't said they're ready yet, and step ${r.current} ${e}. Wait for them before calling presentSlide for step ${n.slide}.`,
|
|
399
|
+
cancelled: !0
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
let a = i ? r.slides.get(n.slide) : void 0;
|
|
403
|
+
if (a) return te(e.currentResult, a) ? {
|
|
404
|
+
message: `step ${n.slide} of ${n.totalSlides} is already on the screen`,
|
|
405
|
+
cancelled: !0
|
|
406
|
+
} : Y(r, n, a);
|
|
407
|
+
let o = re.claim(ie(r.id, n));
|
|
408
|
+
if ("earlier" in o) {
|
|
409
|
+
if (!await o.earlier) return X(e, t);
|
|
410
|
+
let i = r.slides.get(n.slide);
|
|
411
|
+
return i && q !== r ? Y(r, n, i) : {
|
|
412
|
+
message: `slide ${n.slide} of ${n.totalSlides} is already on the screen`,
|
|
413
|
+
cancelled: !0
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
let { settle: s } = o, c = {
|
|
417
|
+
current: r.current,
|
|
418
|
+
shownAt: r.shownAt
|
|
419
|
+
};
|
|
420
|
+
r.current = n.slide, r.shownAt = Infinity;
|
|
421
|
+
let l = !1;
|
|
422
|
+
try {
|
|
423
|
+
let t = await oe(e, r, n);
|
|
424
|
+
return l = !!E(t).imageData, l && (r.slides.set(n.slide, t), r.current = n.slide, r.shownAt = Date.now(), q = r), t;
|
|
425
|
+
} finally {
|
|
426
|
+
s(l), !l && r.current === n.slide && (r.current = c.current, r.shownAt = c.shownAt);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/core/storyboard.ts
|
|
431
|
+
var se = ({ id: e, title: t, totalPanels: n }) => ({
|
|
432
|
+
step: 0,
|
|
433
|
+
total: n,
|
|
434
|
+
kind: "story",
|
|
435
|
+
label: `The cast of "${t}"`,
|
|
436
|
+
onShown: "introduce the characters",
|
|
437
|
+
nextCall: `call presentPanel for panel 1 of ${n} with storyboardId "${e}"`
|
|
438
|
+
}), ce = ({ id: e, totalPanels: t }, n, r) => ({
|
|
439
|
+
step: n,
|
|
440
|
+
total: t,
|
|
441
|
+
kind: "story",
|
|
442
|
+
label: `Panel ${n} of ${t}`,
|
|
443
|
+
onShown: "tell that part of the story",
|
|
444
|
+
nextCall: `call presentPanel for panel ${n + 1} of ${t} with storyboardId "${e}"`,
|
|
445
|
+
...r.length > 0 && { waitsForUser: !0 }
|
|
446
|
+
}), Z = (e, t) => e ? `${e}. ${t}` : t, le = (e, { name: t, description: n }) => Z(e, `Character reference sheet of ${t}: ${n}. Full body, front, side and back views, on a plain white background. No text or labels.`);
|
|
447
|
+
async function ue(e, t) {
|
|
448
|
+
let n = _(t);
|
|
449
|
+
if (typeof n == "string") return {
|
|
450
|
+
message: n,
|
|
451
|
+
sequence: null
|
|
452
|
+
};
|
|
453
|
+
let r = await Promise.all(n.characters.map((t) => j(e, le(n.style, t)))), i = r.map(E), a = r.find((e, t) => !i[t]?.imageData);
|
|
454
|
+
if (i.every(({ imageData: e }) => !e) && a) return {
|
|
455
|
+
...a,
|
|
456
|
+
message: `the character sheets couldn't be made: ${a.message}`,
|
|
457
|
+
sequence: null
|
|
458
|
+
};
|
|
459
|
+
let o = {
|
|
460
|
+
id: O(),
|
|
461
|
+
...n,
|
|
462
|
+
characters: n.characters.map((e, t) => {
|
|
463
|
+
let n = i[t]?.imagePath;
|
|
464
|
+
return {
|
|
465
|
+
...e,
|
|
466
|
+
...n && { imagePath: n }
|
|
467
|
+
};
|
|
468
|
+
}),
|
|
469
|
+
panels: {}
|
|
470
|
+
}, s = await H(e.files?.artifacts, N, 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;
|
|
471
|
+
return {
|
|
472
|
+
data: {
|
|
473
|
+
storyboardId: o.id,
|
|
474
|
+
title: o.title,
|
|
475
|
+
totalPanels: o.totalPanels,
|
|
476
|
+
...u && { imageData: u },
|
|
477
|
+
characters: o.characters.map(({ name: e, imagePath: t }, n) => {
|
|
478
|
+
let r = i[n]?.imageData;
|
|
479
|
+
return {
|
|
480
|
+
name: e,
|
|
481
|
+
...r && { imageData: r },
|
|
482
|
+
...t && { imagePath: t }
|
|
483
|
+
};
|
|
484
|
+
})
|
|
485
|
+
},
|
|
486
|
+
title: o.title,
|
|
487
|
+
message: [
|
|
488
|
+
`storyboard "${o.id}" is ready with ${o.totalPanels} panels; the cast is on the screen`,
|
|
489
|
+
c.length ? `character sheets saved to ${c.join(", ")}` : "",
|
|
490
|
+
l.length ? `no sheet for ${l.join("; ")}: they will be drawn from their description` : "",
|
|
491
|
+
s ? "" : U("storyboard")
|
|
492
|
+
].filter(Boolean).join("; "),
|
|
493
|
+
instructions: v(o),
|
|
494
|
+
instructionsRequired: !0,
|
|
495
|
+
sequence: se(o)
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function de(e, t, n) {
|
|
499
|
+
let r = [Z(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}`);
|
|
500
|
+
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(" ");
|
|
501
|
+
}
|
|
502
|
+
async function fe(e, t, n) {
|
|
503
|
+
let r = [], i = [];
|
|
504
|
+
for (let a of n.characters) {
|
|
505
|
+
let n = t.characters.find((e) => u(e.name, a));
|
|
506
|
+
if (!n) r.push(a);
|
|
507
|
+
else if (!i.some((e) => e.character === n)) {
|
|
508
|
+
let [t] = n.imagePath ? await A(e, [n.imagePath]) : [];
|
|
509
|
+
i.push({
|
|
510
|
+
character: n,
|
|
511
|
+
sheet: t ?? null
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
let a = i.map(({ sheet: e }) => e).filter((e) => !!e), o = de(t, n, i.map(({ character: e, sheet: t }) => ({
|
|
516
|
+
character: e,
|
|
517
|
+
hasSheet: !!t
|
|
518
|
+
}))), s = await j(e, o, a), { imageData: c, imagePath: l } = E(s);
|
|
519
|
+
if (!c) return {
|
|
520
|
+
...s,
|
|
521
|
+
sequence: null
|
|
522
|
+
};
|
|
523
|
+
let d = n.panel < t.totalPanels && n.choices.length >= 2 ? n.choices : [], f = await H(e.files?.artifacts, N, t.id, (e) => {
|
|
524
|
+
let r = e ?? t;
|
|
525
|
+
return d.length && (r.interactive = !0), r.panels[String(n.panel)] = {
|
|
526
|
+
caption: n.caption,
|
|
527
|
+
characters: i.map(({ character: e }) => e.name),
|
|
528
|
+
imagePrompt: n.imagePrompt,
|
|
529
|
+
...l && { imagePath: l },
|
|
530
|
+
...d.length && { choices: d }
|
|
531
|
+
}, r;
|
|
532
|
+
});
|
|
533
|
+
return {
|
|
534
|
+
data: {
|
|
535
|
+
imageData: c,
|
|
536
|
+
...l && { imagePath: l },
|
|
537
|
+
prompt: o,
|
|
538
|
+
storyboardId: t.id,
|
|
539
|
+
panel: n.panel,
|
|
540
|
+
totalPanels: t.totalPanels,
|
|
541
|
+
caption: n.caption,
|
|
542
|
+
...d.length && { choices: d }
|
|
543
|
+
},
|
|
544
|
+
title: n.caption || `Panel ${n.panel}`,
|
|
545
|
+
message: [
|
|
546
|
+
`panel ${n.panel} of ${t.totalPanels} is on the screen`,
|
|
547
|
+
l ? `saved to ${l}` : "",
|
|
548
|
+
r.length ? `not in the cast, so drawn from the prompt alone: ${r.join(", ")}` : "",
|
|
549
|
+
f ? "" : U("storyboard")
|
|
550
|
+
].filter(Boolean).join("; "),
|
|
551
|
+
instructions: x(t, n.panel, d),
|
|
552
|
+
instructionsRequired: !0,
|
|
553
|
+
sequence: ce(t, n.panel, d)
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
var pe = W(), Q = /* @__PURE__ */ new Map();
|
|
557
|
+
async function $(e, t) {
|
|
558
|
+
let n = b(t);
|
|
559
|
+
if (!n) return {
|
|
560
|
+
message: y,
|
|
561
|
+
sequence: null
|
|
562
|
+
};
|
|
563
|
+
let { storyboardId: r, panel: i } = n, a = Q.get(r);
|
|
564
|
+
if (a && i > a.panel && !D(e, a.shownAt)) return console.info(`[sequence] holding panel ${i} for the user`), {
|
|
565
|
+
message: a.shownAt === Infinity ? `panel ${i} was not shown: panel ${a.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 ${a.panel} is still on the screen. Wait for their answer.`,
|
|
566
|
+
cancelled: !0
|
|
567
|
+
};
|
|
568
|
+
let o = pe.claim(JSON.stringify(n));
|
|
569
|
+
if ("earlier" in o) return await o.earlier ? {
|
|
570
|
+
message: `panel ${i} is already on the screen`,
|
|
571
|
+
cancelled: !0
|
|
572
|
+
} : $(e, t);
|
|
573
|
+
let { settle: s } = o, c = {
|
|
574
|
+
panel: i,
|
|
575
|
+
shownAt: Infinity
|
|
576
|
+
}, l = Q.get(r);
|
|
577
|
+
Q.set(r, c);
|
|
578
|
+
let u = () => Q.get(r) === c, d = !1;
|
|
579
|
+
try {
|
|
580
|
+
let t = await V(e.files?.artifacts, N, r);
|
|
581
|
+
if (!t) return {
|
|
582
|
+
message: `there is no storyboard "${r}"; call defineStoryboard first`,
|
|
583
|
+
sequence: null
|
|
584
|
+
};
|
|
585
|
+
if (i > t.totalPanels) return {
|
|
586
|
+
message: `storyboard "${t.id}" has ${t.totalPanels} panels`,
|
|
587
|
+
sequence: null
|
|
588
|
+
};
|
|
589
|
+
let a = await fe(e, t, n);
|
|
590
|
+
d = !!E(a).imageData;
|
|
591
|
+
let o = a.data;
|
|
592
|
+
return d && u() && (o?.choices?.length ? Q.set(r, {
|
|
593
|
+
panel: i,
|
|
594
|
+
shownAt: Date.now()
|
|
595
|
+
}) : Q.delete(r)), a;
|
|
596
|
+
} finally {
|
|
597
|
+
s(d), !d && u() && (l ? Q.set(r, l) : Q.delete(r));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
//#endregion
|
|
601
|
+
//#region src/core/index.ts
|
|
602
|
+
var me = {
|
|
603
|
+
toolDefinition: g,
|
|
604
|
+
generatingMessage: "Making the slide...",
|
|
605
|
+
isEnabled: () => !0,
|
|
606
|
+
execute: (e, t) => X(e, t)
|
|
607
|
+
}, he = {
|
|
608
|
+
toolDefinition: C,
|
|
609
|
+
generatingMessage: "Drawing the characters...",
|
|
610
|
+
isEnabled: () => !0,
|
|
611
|
+
execute: (e, t) => ue(e, t)
|
|
612
|
+
}, ge = {
|
|
613
|
+
toolDefinition: w,
|
|
614
|
+
generatingMessage: "Drawing the panel...",
|
|
615
|
+
isEnabled: () => !0,
|
|
616
|
+
execute: (e, t) => $(e, t)
|
|
617
|
+
};
|
|
618
|
+
//#endregion
|
|
619
|
+
export { b as C, m as D, u as E, x as S, _ as T, h as _, N as a, S as b, C as c, a as d, y as f, g, t as h, M as i, i as l, w as m, ge as n, V as o, r as p, me as r, n as s, he as t, o as u, s as v, p as w, v as x, f as y };
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core-55UuKHJb.cjs");exports.DEFINE_STORYBOARD=e.s,exports.DEFINE_STORYBOARD_DEFINITION=e.c,exports.MAX_CHARACTERS=e.l,exports.MAX_CHOICES=e.u,exports.MAX_PANELS=e.d,exports.PANEL_ARGS_ERROR=e.f,exports.PRESENT_PANEL=e.p,exports.PRESENT_PANEL_DEFINITION=e.m,exports.PRESENT_SLIDE=e.h,exports.PRESENT_SLIDE_DEFINITION=e.g,exports.PRESENT_SLIDE_PROMPT=e._,exports.SEQUENCE_ID=e.v,exports.SLIDESHOWS_DIR=e.i,exports.SLIDE_ARGS_ERROR=e.y,exports.STORYBOARDS_DIR=e.a,exports.STORYBOARD_PROMPT=e.b,exports.castShownInstructions=e.x,exports.defineStoryboardPluginCore=e.t,exports.loadRecord=e.o,exports.panelShownInstructions=e.S,exports.parsePanelArgs=e.C,exports.parseSlideArgs=e.w,exports.parseStoryboardArgs=e.T,exports.presentPanelPluginCore=e.n,exports.presentSlidePluginCore=e.r,exports.sameName=e.E,exports.slideShownInstructions=e.D;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./core/index";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as e, D as t, E as n, S as r, T as i, _ as a, a as o, b as s, c, d as l, f as u, g as d, h as f, i as p, l as m, m as h, n as g, o as _, p as v, r as y, s as b, t as x, u as S, v as C, w, x as T, y as E } from "./core-D0F2WGN6.js";
|
|
2
|
+
export { b as DEFINE_STORYBOARD, c as DEFINE_STORYBOARD_DEFINITION, m as MAX_CHARACTERS, S as MAX_CHOICES, l as MAX_PANELS, u as PANEL_ARGS_ERROR, v as PRESENT_PANEL, h as PRESENT_PANEL_DEFINITION, f as PRESENT_SLIDE, d as PRESENT_SLIDE_DEFINITION, a as PRESENT_SLIDE_PROMPT, C as SEQUENCE_ID, p as SLIDESHOWS_DIR, E as SLIDE_ARGS_ERROR, o as STORYBOARDS_DIR, s as STORYBOARD_PROMPT, T as castShownInstructions, x as defineStoryboardPluginCore, _ as loadRecord, r as panelShownInstructions, e as parsePanelArgs, w as parseSlideArgs, i as parseStoryboardArgs, g as presentPanelPluginCore, y as presentSlidePluginCore, n as sameName, t as slideShownInstructions };
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-indigo-600:oklch(51.1% .262 276.966);--color-gray-800:oklch(27.8% .033 256.848);--color-white:#fff;--spacing:.25rem;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.mt-1{margin-top:var(--spacing)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.flex{display:flex}.grid{display:grid}.h-full{height:100%}.max-h-full{max-height:100%}.min-h-0{min-height:0}.w-full{width:100%}.max-w-full{max-width:100%}.flex-1{flex:1}.auto-rows-fr{grid-auto-rows:minmax(0,1fr)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.rounded-full{border-radius:2147483647px}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-white{background-color:var(--color-white)}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.py-2{padding-block:calc(var(--spacing) * 2)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-gray-800{color:var(--color-gray-800)}.text-white{color:var(--color-white)}}@property --tw-font-weight{syntax:"*";inherits:false}
|
|
3
|
+
/*$vite$:1*/
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import "../style.css";
|
|
2
|
+
import type { ToolPlugin } from "gui-chat-protocol/vue";
|
|
3
|
+
type SequencePlugin = ToolPlugin<unknown, unknown, Record<string, unknown>>;
|
|
4
|
+
export declare const presentSlidePlugin: SequencePlugin;
|
|
5
|
+
export declare const defineStoryboardPlugin: SequencePlugin;
|
|
6
|
+
export declare const presentPanelPlugin: SequencePlugin;
|
|
7
|
+
/** All three, as a host registers them. */
|
|
8
|
+
export declare const plugins: SequencePlugin[];
|
|
9
|
+
export * from "../core/index";
|
package/dist/vue.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core-55UuKHJb.cjs");let t=require("vue"),n=require("@mulmochat-plugin/ui-image");function r({caption:e,choices:n}){let r=[];return e&&r.push((0,t.h)(`p`,{class:`text-center text-lg text-gray-800 px-4`},e)),n?.length&&r.push((0,t.h)(`ol`,{class:`flex flex-wrap justify-center gap-3 px-4`},n.map((e,n)=>(0,t.h)(`li`,{class:`rounded-full bg-indigo-600 text-white text-xl px-5 py-2 font-medium`},`${n+1}. ${e}`)))),r.length?(0,t.h)(`div`,{class:`flex flex-col gap-2 pb-2`},r):null}function i(e,n=()=>({})){return(0,t.markRaw)((0,t.defineComponent)({name:e,props:{selectedResult:{type:Object,required:!0}},setup(e){return()=>{let i=e.selectedResult.data;return i?.imageData?(0,t.h)(`div`,{class:`h-full w-full flex flex-col bg-white p-2 gap-2`},[(0,t.h)(`div`,{class:`flex-1 min-h-0 flex items-center justify-center`},[(0,t.h)(`img`,{src:i.imageData,alt:i.prompt??``,class:`max-w-full max-h-full object-contain`})]),r(n(i))]):(0,t.h)(`div`,{class:`h-full bg-white`})}}}))}function a(e){return(0,t.markRaw)((0,t.defineComponent)({name:e,props:{result:{type:Object,required:!0}},setup(e){return()=>(0,t.h)(n.ImagePreview,{result:e.result})}}))}var o=(0,t.markRaw)((0,t.defineComponent)({name:`StoryboardCastView`,props:{selectedResult:{type:Object,required:!0}},setup(e){return()=>{let n=e.selectedResult.data,r=n?.characters??[];return(0,t.h)(`div`,{class:`h-full w-full flex flex-col bg-white p-4`},[(0,t.h)(`h2`,{class:`text-center text-xl font-semibold text-gray-800 mb-3`},n?.title??``),(0,t.h)(`div`,{class:`flex-1 min-h-0 grid gap-3 ${r.length>1?`grid-cols-2`:`grid-cols-1`} auto-rows-fr`},r.map(e=>(0,t.h)(`figure`,{class:`min-h-0 flex flex-col items-center`},[e.imageData?(0,t.h)(`img`,{src:e.imageData,alt:e.name,class:`flex-1 min-h-0 max-w-full object-contain`}):(0,t.h)(`div`,{class:`flex-1`}),(0,t.h)(`figcaption`,{class:`text-lg text-gray-800 mt-1`},e.name)])))])}}})),s=e=>Array.isArray(e)?e.filter(e=>typeof e==`string`):[],c={...e.r,viewComponent:i(`PresentSlideView`),previewComponent:a(`PresentSlidePreview`)},l={...e.t,viewComponent:o,previewComponent:a(`StoryboardCastPreview`)},u={...e.n,viewComponent:i(`PresentPanelView`,e=>({caption:typeof e.caption==`string`?e.caption:``,choices:s(e.choices)})),previewComponent:a(`PresentPanelPreview`)},d=[c,l,u];exports.DEFINE_STORYBOARD=e.s,exports.DEFINE_STORYBOARD_DEFINITION=e.c,exports.MAX_CHARACTERS=e.l,exports.MAX_CHOICES=e.u,exports.MAX_PANELS=e.d,exports.PANEL_ARGS_ERROR=e.f,exports.PRESENT_PANEL=e.p,exports.PRESENT_PANEL_DEFINITION=e.m,exports.PRESENT_SLIDE=e.h,exports.PRESENT_SLIDE_DEFINITION=e.g,exports.PRESENT_SLIDE_PROMPT=e._,exports.SEQUENCE_ID=e.v,exports.SLIDESHOWS_DIR=e.i,exports.SLIDE_ARGS_ERROR=e.y,exports.STORYBOARDS_DIR=e.a,exports.STORYBOARD_PROMPT=e.b,exports.castShownInstructions=e.x,exports.defineStoryboardPlugin=l,exports.defineStoryboardPluginCore=e.t,exports.loadRecord=e.o,exports.panelShownInstructions=e.S,exports.parsePanelArgs=e.C,exports.parseSlideArgs=e.w,exports.parseStoryboardArgs=e.T,exports.plugins=d,exports.presentPanelPlugin=u,exports.presentPanelPluginCore=e.n,exports.presentSlidePlugin=c,exports.presentSlidePluginCore=e.r,exports.sameName=e.E,exports.slideShownInstructions=e.D;
|
package/dist/vue.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { C as e, D as t, E as n, S as r, T as i, _ as a, a as o, b as s, c, d as l, f as u, g as d, h as f, i as p, l as m, m as h, n as g, o as _, p as v, r as y, s as b, t as x, u as S, v as C, w, x as T, y as E } from "./core-D0F2WGN6.js";
|
|
2
|
+
import { defineComponent as D, h as O, markRaw as k } from "vue";
|
|
3
|
+
import { ImagePreview as A } from "@mulmochat-plugin/ui-image";
|
|
4
|
+
//#region src/vue/index.ts
|
|
5
|
+
function j({ caption: e, choices: t }) {
|
|
6
|
+
let n = [];
|
|
7
|
+
return e && n.push(O("p", { class: "text-center text-lg text-gray-800 px-4" }, e)), t?.length && n.push(O("ol", { class: "flex flex-wrap justify-center gap-3 px-4" }, t.map((e, t) => O("li", { class: "rounded-full bg-indigo-600 text-white text-xl px-5 py-2 font-medium" }, `${t + 1}. ${e}`)))), n.length ? O("div", { class: "flex flex-col gap-2 pb-2" }, n) : null;
|
|
8
|
+
}
|
|
9
|
+
function M(e, t = () => ({})) {
|
|
10
|
+
return k(D({
|
|
11
|
+
name: e,
|
|
12
|
+
props: { selectedResult: {
|
|
13
|
+
type: Object,
|
|
14
|
+
required: !0
|
|
15
|
+
} },
|
|
16
|
+
setup(e) {
|
|
17
|
+
return () => {
|
|
18
|
+
let n = e.selectedResult.data;
|
|
19
|
+
return n?.imageData ? O("div", { class: "h-full w-full flex flex-col bg-white p-2 gap-2" }, [O("div", { class: "flex-1 min-h-0 flex items-center justify-center" }, [O("img", {
|
|
20
|
+
src: n.imageData,
|
|
21
|
+
alt: n.prompt ?? "",
|
|
22
|
+
class: "max-w-full max-h-full object-contain"
|
|
23
|
+
})]), j(t(n))]) : O("div", { class: "h-full bg-white" });
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
function N(e) {
|
|
29
|
+
return k(D({
|
|
30
|
+
name: e,
|
|
31
|
+
props: { result: {
|
|
32
|
+
type: Object,
|
|
33
|
+
required: !0
|
|
34
|
+
} },
|
|
35
|
+
setup(e) {
|
|
36
|
+
return () => O(A, { result: e.result });
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
var P = k(D({
|
|
41
|
+
name: "StoryboardCastView",
|
|
42
|
+
props: { selectedResult: {
|
|
43
|
+
type: Object,
|
|
44
|
+
required: !0
|
|
45
|
+
} },
|
|
46
|
+
setup(e) {
|
|
47
|
+
return () => {
|
|
48
|
+
let t = e.selectedResult.data, n = t?.characters ?? [];
|
|
49
|
+
return O("div", { class: "h-full w-full flex flex-col bg-white p-4" }, [O("h2", { class: "text-center text-xl font-semibold text-gray-800 mb-3" }, t?.title ?? ""), O("div", { class: `flex-1 min-h-0 grid gap-3 ${n.length > 1 ? "grid-cols-2" : "grid-cols-1"} auto-rows-fr` }, n.map((e) => O("figure", { class: "min-h-0 flex flex-col items-center" }, [e.imageData ? O("img", {
|
|
50
|
+
src: e.imageData,
|
|
51
|
+
alt: e.name,
|
|
52
|
+
class: "flex-1 min-h-0 max-w-full object-contain"
|
|
53
|
+
}) : O("div", { class: "flex-1" }), O("figcaption", { class: "text-lg text-gray-800 mt-1" }, e.name)])))]);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
})), F = (e) => Array.isArray(e) ? e.filter((e) => typeof e == "string") : [], I = {
|
|
57
|
+
...y,
|
|
58
|
+
viewComponent: M("PresentSlideView"),
|
|
59
|
+
previewComponent: N("PresentSlidePreview")
|
|
60
|
+
}, L = {
|
|
61
|
+
...x,
|
|
62
|
+
viewComponent: P,
|
|
63
|
+
previewComponent: N("StoryboardCastPreview")
|
|
64
|
+
}, R = {
|
|
65
|
+
...g,
|
|
66
|
+
viewComponent: M("PresentPanelView", (e) => ({
|
|
67
|
+
caption: typeof e.caption == "string" ? e.caption : "",
|
|
68
|
+
choices: F(e.choices)
|
|
69
|
+
})),
|
|
70
|
+
previewComponent: N("PresentPanelPreview")
|
|
71
|
+
}, z = [
|
|
72
|
+
I,
|
|
73
|
+
L,
|
|
74
|
+
R
|
|
75
|
+
];
|
|
76
|
+
//#endregion
|
|
77
|
+
export { b as DEFINE_STORYBOARD, c as DEFINE_STORYBOARD_DEFINITION, m as MAX_CHARACTERS, S as MAX_CHOICES, l as MAX_PANELS, u as PANEL_ARGS_ERROR, v as PRESENT_PANEL, h as PRESENT_PANEL_DEFINITION, f as PRESENT_SLIDE, d as PRESENT_SLIDE_DEFINITION, a as PRESENT_SLIDE_PROMPT, C as SEQUENCE_ID, p as SLIDESHOWS_DIR, E as SLIDE_ARGS_ERROR, o as STORYBOARDS_DIR, s as STORYBOARD_PROMPT, T as castShownInstructions, L as defineStoryboardPlugin, x as defineStoryboardPluginCore, _ as loadRecord, r as panelShownInstructions, e as parsePanelArgs, w as parseSlideArgs, i as parseStoryboardArgs, z as plugins, R as presentPanelPlugin, g as presentPanelPluginCore, I as presentSlidePlugin, y as presentSlidePluginCore, n as sameName, t as slideShownInstructions };
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gui-chat-plugin/sequence",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Slideshows, step-by-step guides and stories told in pictures (presentSlide, defineStoryboard, presentPanel) for GUI Chat Protocol hosts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./vue": {
|
|
16
|
+
"types": "./dist/vue/index.d.ts",
|
|
17
|
+
"import": "./dist/vue.js",
|
|
18
|
+
"require": "./dist/vue.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./style.css": "./dist/style.css"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"CHANGELOG.md"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "vite build && tsc -p tsconfig.build.json",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"test": "tsx --test test/test_*.ts"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@mulmochat-plugin/ui-image": "^0.4.1",
|
|
36
|
+
"gui-chat-protocol": "^2.1.0",
|
|
37
|
+
"vue": "^3.5.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@mulmochat-plugin/ui-image": "^0.4.1",
|
|
41
|
+
"gui-chat-protocol": "^2.1.0",
|
|
42
|
+
"vue": "^3.5.43"
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"homepage": "https://github.com/receptron/gui-chat-plugins/tree/main/packages/sequence#readme",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/receptron/gui-chat-plugins.git",
|
|
49
|
+
"directory": "packages/sequence"
|
|
50
|
+
},
|
|
51
|
+
"keywords": [
|
|
52
|
+
"gui-chat",
|
|
53
|
+
"gui-chat-protocol",
|
|
54
|
+
"plugin",
|
|
55
|
+
"slideshow",
|
|
56
|
+
"storyboard"
|
|
57
|
+
]
|
|
58
|
+
}
|